main: --config/--help arg parsing, vendor on package.path, REPL start

Phase 0 entry point per PHASE0.md §4, §10.

Resolves the §10 config search:
  --config <path>          (explicit; failure if not openable, no fallback)
  $AISH_CONFIG
  ~/.config/aish/config.lua
  ./config.lua

The explicit form now hard-fails instead of silently falling through to
the next candidate — caught in smoke (`--config /nonexistent` was loading
./config.lua).

Pre-pends `./?.lua;./vendor/?.lua` to package.path so `require("dkjson")`
finds vendor/dkjson.lua and project requires resolve from the repo root.
Run from the repo root; cwd-independent resolution lands later.

`--help` prints the usage block. Unrecognized arg exits 2 with a
diagnostic on stderr.

Phase 0 done-criteria (PHASE0.md §2):
  ✓ shell command execution with framed output
  ✓ :meta commands (full §5.2 set)
  ✓ in-memory conversation history with sliding-window eviction
  ✓ codebase layout matches §4 — every module name stable for Phase 1+
   live AI exchange — structurally wired; live test deferred per
     issue #12 (broker endpoint hostname not resolvable from noether)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 17:12:25 +00:00
parent e0e69f839b
commit a18e530c03
+61 -16
View File
@@ -2,31 +2,76 @@
-- Phase 0: arg parsing, config load, REPL start.
-- See docs/PHASE0.md §4, §10.
local function load_config()
-- Resolution order per PHASE0.md §10:
-- 1. --config <path> 2. $AISH_CONFIG
-- 3. ~/.config/aish/config.lua 4. ./config.lua
-- Phase 0 stub: pick the first existing path; no CLI parsing yet.
-- Make project modules and the vendored dkjson resolvable from the repo root.
-- Run aish with the repo root as cwd; PTY-relative resolution lands later.
package.path = "./?.lua;./vendor/?.lua;" .. package.path
local USAGE = [[
aish — AI-augmented conversational shell.
Usage: luajit main.lua [--config <path>] [--help]
Config resolution order (PHASE0.md §10):
1. --config <path>
2. $AISH_CONFIG
3. ~/.config/aish/config.lua
4. ./config.lua
]]
local function parse_args(argv)
local out = {}
local i = 1
while i <= #argv do
local a = argv[i]
if a == "--config" then
out.config = argv[i + 1]
i = i + 2
elseif a == "--help" or a == "-h" then
out.help = true
i = i + 1
else
io.stderr:write("aish: unrecognized argument: " .. a .. "\n")
os.exit(2)
end
end
return out
end
local function load_config(opts)
-- --config is explicit: use exactly that path or fail. No silent fallback.
if opts.config then
local f = io.open(opts.config, "r")
if not f then
error("aish: --config " .. opts.config .. ": cannot open")
end
f:close()
return dofile(opts.config), opts.config
end
local home = os.getenv("HOME") or ""
local candidates = {
os.getenv("AISH_CONFIG"),
home .. "/.config/aish/config.lua",
"./config.lua",
}
local candidates = {}
local function push(p) if p and p ~= "" then candidates[#candidates + 1] = p end end
push(os.getenv("AISH_CONFIG"))
push(home .. "/.config/aish/config.lua")
push("./config.lua")
for _, path in ipairs(candidates) do
if path then
local f = io.open(path, "r")
if f then f:close(); return dofile(path), path end
end
end
error("aish: no config.lua found in any standard location")
error("aish: no config.lua found (tried: "
.. table.concat(candidates, ", ") .. ")")
end
local function main()
local config, config_path = load_config()
local function main(argv)
local opts = parse_args(argv or {})
if opts.help then io.write(USAGE); return end
local config, config_path = load_config(opts)
io.stderr:write(("aish: loaded config from %s\n"):format(config_path))
local repl = require("repl")
repl.run(config)
end
main()
main(arg)