@dujavi/ai-md 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -24
- package/bin/ai-md.js +346 -99
- package/docs/architecture.html +316 -0
- package/docs/architecture.png +0 -0
- package/lib/config.js +110 -8
- package/lib/scripts.js +104 -0
- package/lib/status.js +11 -1
- package/package.json +9 -6
- package/scripts/sync-config.sh +2 -2
- package/scripts/ensure-agent-tools.sh +0 -114
package/bin/ai-md.js
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
const { spawnSync } = require("child_process");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
resolveConfig,
|
|
8
|
+
applyEnvFromConfig,
|
|
9
|
+
writeMachineConfig,
|
|
10
|
+
readMachineConfig,
|
|
11
|
+
expandHome,
|
|
12
|
+
} = require("../lib/config");
|
|
7
13
|
const { emit, fail } = require("../lib/output");
|
|
8
14
|
const { collectStatus, statusHelp } = require("../lib/status");
|
|
9
15
|
const {
|
|
@@ -14,14 +20,77 @@ const {
|
|
|
14
20
|
ensureCursorLinks,
|
|
15
21
|
ensureAgentSkillLinks,
|
|
16
22
|
} = require("../lib/commands");
|
|
23
|
+
const { runScript, runScripts } = require("../lib/scripts");
|
|
17
24
|
|
|
18
25
|
const scriptsDir = path.join(__dirname, "..", "scripts");
|
|
19
|
-
const cfg = resolveConfig();
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
function applyFlag(out, flag, args) {
|
|
28
|
+
switch (flag) {
|
|
29
|
+
case "--json":
|
|
30
|
+
out.json = true;
|
|
31
|
+
return true;
|
|
32
|
+
case "--full":
|
|
33
|
+
out.full = true;
|
|
34
|
+
return true;
|
|
35
|
+
case "--force":
|
|
36
|
+
out.force = true;
|
|
37
|
+
return true;
|
|
38
|
+
case "--dry-run":
|
|
39
|
+
out.dryRun = true;
|
|
40
|
+
return true;
|
|
41
|
+
case "--fix":
|
|
42
|
+
out.fix = true;
|
|
43
|
+
out.force = true;
|
|
44
|
+
return true;
|
|
45
|
+
case "-m":
|
|
46
|
+
case "--message":
|
|
47
|
+
out.message = args.shift();
|
|
48
|
+
return true;
|
|
49
|
+
case "--repo":
|
|
50
|
+
out.repo = args.shift();
|
|
51
|
+
return true;
|
|
52
|
+
case "--name":
|
|
53
|
+
out.name = args.shift();
|
|
54
|
+
return true;
|
|
55
|
+
case "--project":
|
|
56
|
+
out.project = args.shift();
|
|
57
|
+
return true;
|
|
58
|
+
case "--from":
|
|
59
|
+
out.from = args.shift();
|
|
60
|
+
return true;
|
|
61
|
+
case "--remote":
|
|
62
|
+
out.remote = args.shift();
|
|
63
|
+
return true;
|
|
64
|
+
case "--dir":
|
|
65
|
+
out.dir = expandHome(args.shift());
|
|
66
|
+
return true;
|
|
67
|
+
case "--agents":
|
|
68
|
+
out.agents = String(args.shift() || "cursor")
|
|
69
|
+
.split(",")
|
|
70
|
+
.map((s) => s.trim())
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
return true;
|
|
73
|
+
case "--script": {
|
|
74
|
+
const name = args.shift();
|
|
75
|
+
if (!name) {
|
|
76
|
+
fail("--script requires a name", {
|
|
77
|
+
exitCode: 2,
|
|
78
|
+
json: out.json,
|
|
79
|
+
help: ["ai-md setup --script ensure-tools"],
|
|
80
|
+
});
|
|
81
|
+
process.exit(2);
|
|
82
|
+
}
|
|
83
|
+
out.scripts.push(name);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
case "-h":
|
|
87
|
+
case "--help":
|
|
88
|
+
out.cmdHelp = true;
|
|
89
|
+
return true;
|
|
90
|
+
default:
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
25
94
|
|
|
26
95
|
function parseArgs(argv) {
|
|
27
96
|
const out = {
|
|
@@ -36,7 +105,12 @@ function parseArgs(argv) {
|
|
|
36
105
|
name: null,
|
|
37
106
|
project: null,
|
|
38
107
|
from: "base",
|
|
108
|
+
remote: null,
|
|
109
|
+
dir: null,
|
|
39
110
|
agents: ["cursor"],
|
|
111
|
+
scripts: [],
|
|
112
|
+
scriptName: null,
|
|
113
|
+
scriptArgs: [],
|
|
40
114
|
rest: [],
|
|
41
115
|
};
|
|
42
116
|
const args = [...argv];
|
|
@@ -50,58 +124,26 @@ function parseArgs(argv) {
|
|
|
50
124
|
return out;
|
|
51
125
|
}
|
|
52
126
|
if (first.startsWith("-")) {
|
|
53
|
-
// flags before command → treat as status with flags
|
|
54
127
|
out.cmd = "status";
|
|
55
128
|
} else {
|
|
56
129
|
out.cmd = args.shift();
|
|
57
130
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
out.fix = true;
|
|
75
|
-
out.force = true;
|
|
76
|
-
break;
|
|
77
|
-
case "-m":
|
|
78
|
-
case "--message":
|
|
79
|
-
out.message = args.shift();
|
|
80
|
-
break;
|
|
81
|
-
case "--repo":
|
|
82
|
-
out.repo = args.shift();
|
|
83
|
-
break;
|
|
84
|
-
case "--name":
|
|
85
|
-
out.name = args.shift();
|
|
86
|
-
break;
|
|
87
|
-
case "--project":
|
|
88
|
-
out.project = args.shift();
|
|
89
|
-
break;
|
|
90
|
-
case "--from":
|
|
91
|
-
out.from = args.shift();
|
|
92
|
-
break;
|
|
93
|
-
case "--agents":
|
|
94
|
-
out.agents = String(args.shift() || "cursor")
|
|
95
|
-
.split(",")
|
|
96
|
-
.map((s) => s.trim())
|
|
97
|
-
.filter(Boolean);
|
|
98
|
-
break;
|
|
99
|
-
case "-h":
|
|
100
|
-
case "--help":
|
|
101
|
-
out.cmdHelp = true;
|
|
102
|
-
break;
|
|
103
|
-
default:
|
|
104
|
-
if (a.startsWith("-")) {
|
|
131
|
+
|
|
132
|
+
// `script <name> [--] [args...]` — ai-md flags only before the name
|
|
133
|
+
if (out.cmd === "script" || out.cmd === "run-script") {
|
|
134
|
+
while (args.length) {
|
|
135
|
+
const a = args[0];
|
|
136
|
+
if (a === "--") {
|
|
137
|
+
fail("script name required before `--`", {
|
|
138
|
+
exitCode: 2,
|
|
139
|
+
json: out.json,
|
|
140
|
+
help: ["ai-md script ensure-tools -- --dry-run"],
|
|
141
|
+
});
|
|
142
|
+
process.exit(2);
|
|
143
|
+
}
|
|
144
|
+
if (a.startsWith("-")) {
|
|
145
|
+
args.shift();
|
|
146
|
+
if (!applyFlag(out, a, args)) {
|
|
105
147
|
fail(`unknown flag: ${a}`, {
|
|
106
148
|
exitCode: 2,
|
|
107
149
|
json: out.json,
|
|
@@ -109,9 +151,46 @@ function parseArgs(argv) {
|
|
|
109
151
|
});
|
|
110
152
|
process.exit(2);
|
|
111
153
|
}
|
|
112
|
-
|
|
113
|
-
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
out.scriptName = args.shift();
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (!out.scriptName) {
|
|
160
|
+
fail("script requires a name", {
|
|
161
|
+
exitCode: 2,
|
|
162
|
+
json: out.json,
|
|
163
|
+
help: ["ai-md script <name> [--] [args...]"],
|
|
164
|
+
});
|
|
165
|
+
process.exit(2);
|
|
166
|
+
}
|
|
167
|
+
if (args[0] === "--") args.shift();
|
|
168
|
+
out.scriptArgs = args;
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
while (args.length) {
|
|
173
|
+
const a = args.shift();
|
|
174
|
+
if (a === "--") {
|
|
175
|
+
out.scriptArgs = args;
|
|
176
|
+
break;
|
|
114
177
|
}
|
|
178
|
+
if (a === "set" || a === "show") {
|
|
179
|
+
out.rest.push(a);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (a.startsWith("-")) {
|
|
183
|
+
if (!applyFlag(out, a, args)) {
|
|
184
|
+
fail(`unknown flag: ${a}`, {
|
|
185
|
+
exitCode: 2,
|
|
186
|
+
json: out.json,
|
|
187
|
+
help: ["Run `ai-md --help`"],
|
|
188
|
+
});
|
|
189
|
+
process.exit(2);
|
|
190
|
+
}
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
out.rest.push(a);
|
|
115
194
|
}
|
|
116
195
|
return out;
|
|
117
196
|
}
|
|
@@ -127,44 +206,63 @@ Layout:
|
|
|
127
206
|
~/.ai-md/skills, rules System (global) base — linked to ~/.cursor
|
|
128
207
|
~/.ai-md/templates/<type> Project-type starters (default: base)
|
|
129
208
|
~/.ai-md/projects/<name> Per-app overlays (repo .cursor → here)
|
|
209
|
+
~/.ai-md/scripts/<name> Private machine scripts (ai-md script / setup --script)
|
|
210
|
+
|
|
211
|
+
Machine config (persisted):
|
|
212
|
+
~/.config/ai-md/config.json dir + remote (override with AI_MD_CONFIG)
|
|
213
|
+
Precedence: --flag > env > config file > defaults
|
|
214
|
+
|
|
215
|
+
Supported harnesses (--agents):
|
|
216
|
+
cursor ~/.cursor/skills (+ ~/.cursor/rules via install) [default]
|
|
217
|
+
claude ~/.claude/skills
|
|
218
|
+
agents ~/.agents/skills
|
|
130
219
|
|
|
131
220
|
Commands:
|
|
221
|
+
setup First-time machine setup: save config, install, optional --script
|
|
222
|
+
config Show persisted config (or: config set --remote/--dir)
|
|
132
223
|
status Snapshot (default when no command) [AXI]
|
|
133
224
|
doctor Diagnose links/projects; --fix repairs
|
|
134
225
|
install Clone remote if needed; link ~/.cursor + optional agents
|
|
135
226
|
pull | push Sync private git repo
|
|
136
|
-
|
|
227
|
+
script Run ~/.ai-md/scripts/<name> (alias: run-script)
|
|
137
228
|
init-project Seed projects/<name> from templates/<from> + link .cursor/
|
|
138
229
|
apply-template Merge missing files from a template into a project
|
|
139
230
|
link-project Link repo .cursor/ without seeding (alias: link)
|
|
140
231
|
help Show this help
|
|
141
232
|
|
|
142
233
|
Options:
|
|
143
|
-
--
|
|
234
|
+
--remote <url> Private content git remote (persisted by setup/config set/install)
|
|
235
|
+
--dir <path> Local AI_MD_DIR (default ~/.ai-md; persisted same way)
|
|
236
|
+
--json JSON instead of TOON
|
|
144
237
|
--full Include paths and drift details
|
|
145
|
-
--agents <list> Skill link
|
|
238
|
+
--agents <list> Skill link harnesses: cursor,claude,agents (default: cursor)
|
|
239
|
+
--script <name> With setup/install: run private script (repeatable)
|
|
146
240
|
--repo <path> App repository root
|
|
147
|
-
--name <id> Project id under projects/
|
|
241
|
+
--name <id> Project id under projects/
|
|
148
242
|
--project <id> Target project for apply-template
|
|
149
243
|
--from <id> Template under templates/ (default: base)
|
|
150
|
-
--force Replace non-symlink
|
|
151
|
-
--dry-run Preview without writing
|
|
244
|
+
--force Replace non-symlink paths
|
|
245
|
+
--dry-run Preview without writing (before script name / before --)
|
|
152
246
|
--fix doctor: repair symlinks
|
|
153
247
|
-m, --message push commit message
|
|
248
|
+
-- End of ai-md options; remaining args go to private scripts
|
|
154
249
|
|
|
155
250
|
Examples:
|
|
156
|
-
ai-md
|
|
157
|
-
ai-md
|
|
158
|
-
ai-md
|
|
159
|
-
ai-md
|
|
251
|
+
ai-md setup --remote https://github.com/<you>/.ai-md.git --script ensure-tools
|
|
252
|
+
ai-md setup --remote <url> --script ensure-tools -- --dry-run
|
|
253
|
+
ai-md script ensure-tools
|
|
254
|
+
ai-md script ensure-tools -- --dry-run
|
|
255
|
+
ai-md config set --remote https://github.com/<you>/.ai-md.git --dir ~/.ai-md
|
|
256
|
+
ai-md install --remote https://github.com/<you>/.ai-md.git
|
|
160
257
|
ai-md doctor --fix --agents cursor,claude
|
|
258
|
+
ai-md init-project --repo ~/presenter --from base
|
|
161
259
|
`);
|
|
162
260
|
}
|
|
163
261
|
|
|
164
|
-
function runBash(script, args) {
|
|
262
|
+
function runBash(script, args, env) {
|
|
165
263
|
const result = spawnSync("bash", [path.join(scriptsDir, script), ...args], {
|
|
166
264
|
stdio: "inherit",
|
|
167
|
-
env
|
|
265
|
+
env,
|
|
168
266
|
});
|
|
169
267
|
if (result.error) {
|
|
170
268
|
fail(result.error.message, { exitCode: 1 });
|
|
@@ -173,6 +271,37 @@ function runBash(script, args) {
|
|
|
173
271
|
process.exit(result.status === null ? 1 : result.status);
|
|
174
272
|
}
|
|
175
273
|
|
|
274
|
+
function persistIfRequested(opts) {
|
|
275
|
+
if (opts.remote == null && opts.dir == null) return null;
|
|
276
|
+
return writeMachineConfig(
|
|
277
|
+
{ dir: opts.dir, remote: opts.remote },
|
|
278
|
+
process.env,
|
|
279
|
+
{ dryRun: opts.dryRun }
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function runInstall(cfg, opts) {
|
|
284
|
+
const bashArgs = ["install"];
|
|
285
|
+
if (opts.force) bashArgs.push("--force");
|
|
286
|
+
if (opts.dryRun) bashArgs.push("--dry-run");
|
|
287
|
+
const result = spawnSync(
|
|
288
|
+
"bash",
|
|
289
|
+
[path.join(scriptsDir, "sync-config.sh"), ...bashArgs],
|
|
290
|
+
{ stdio: "inherit", env: process.env }
|
|
291
|
+
);
|
|
292
|
+
if (result.status !== 0) {
|
|
293
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
294
|
+
}
|
|
295
|
+
const links = [
|
|
296
|
+
...ensureCursorLinks(cfg, { force: opts.force, dryRun: opts.dryRun }),
|
|
297
|
+
...ensureAgentSkillLinks(cfg, opts.agents, {
|
|
298
|
+
force: opts.force,
|
|
299
|
+
dryRun: opts.dryRun,
|
|
300
|
+
}),
|
|
301
|
+
];
|
|
302
|
+
return links;
|
|
303
|
+
}
|
|
304
|
+
|
|
176
305
|
function main() {
|
|
177
306
|
const opts = parseArgs(process.argv.slice(2));
|
|
178
307
|
|
|
@@ -181,8 +310,116 @@ function main() {
|
|
|
181
310
|
return;
|
|
182
311
|
}
|
|
183
312
|
|
|
313
|
+
let cfg = resolveConfig(process.env, {
|
|
314
|
+
dir: opts.dir || undefined,
|
|
315
|
+
remote: opts.remote || undefined,
|
|
316
|
+
});
|
|
317
|
+
applyEnvFromConfig(cfg);
|
|
318
|
+
|
|
184
319
|
try {
|
|
185
320
|
switch (opts.cmd) {
|
|
321
|
+
case "config": {
|
|
322
|
+
const sub = opts.rest[0] || "show";
|
|
323
|
+
if (sub === "set") {
|
|
324
|
+
if (opts.remote == null && opts.dir == null) {
|
|
325
|
+
fail("config set requires --remote and/or --dir", {
|
|
326
|
+
exitCode: 2,
|
|
327
|
+
json: opts.json,
|
|
328
|
+
help: [
|
|
329
|
+
"ai-md config set --remote https://github.com/<you>/.ai-md.git --dir ~/.ai-md",
|
|
330
|
+
],
|
|
331
|
+
});
|
|
332
|
+
process.exit(2);
|
|
333
|
+
}
|
|
334
|
+
const saved = writeMachineConfig(
|
|
335
|
+
{ dir: opts.dir, remote: opts.remote },
|
|
336
|
+
process.env,
|
|
337
|
+
{ dryRun: opts.dryRun }
|
|
338
|
+
);
|
|
339
|
+
cfg = resolveConfig(process.env);
|
|
340
|
+
emit({
|
|
341
|
+
data: {
|
|
342
|
+
...saved,
|
|
343
|
+
resolved: {
|
|
344
|
+
dir: cfg.dir,
|
|
345
|
+
remote: cfg.remote,
|
|
346
|
+
sources: cfg.sources,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
json: opts.json,
|
|
350
|
+
help: [
|
|
351
|
+
"Run `ai-md install` if ~/.ai-md is not cloned yet",
|
|
352
|
+
"Run `ai-md setup --remote <url> --script ensure-tools` for first-time bootstrap",
|
|
353
|
+
],
|
|
354
|
+
});
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
const stored = readMachineConfig();
|
|
358
|
+
cfg = resolveConfig(process.env);
|
|
359
|
+
emit({
|
|
360
|
+
data: {
|
|
361
|
+
path: stored.path,
|
|
362
|
+
stored: stored.raw,
|
|
363
|
+
resolved: {
|
|
364
|
+
dir: cfg.dir,
|
|
365
|
+
remote: cfg.remote,
|
|
366
|
+
sources: cfg.sources,
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
json: opts.json,
|
|
370
|
+
help: [
|
|
371
|
+
"ai-md config set --remote <url> --dir ~/.ai-md",
|
|
372
|
+
"Flags and env override the config file",
|
|
373
|
+
],
|
|
374
|
+
});
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case "setup": {
|
|
378
|
+
const saved = writeMachineConfig(
|
|
379
|
+
{
|
|
380
|
+
dir: opts.dir || cfg.dir,
|
|
381
|
+
remote: opts.remote || cfg.remote,
|
|
382
|
+
},
|
|
383
|
+
process.env,
|
|
384
|
+
{ dryRun: opts.dryRun }
|
|
385
|
+
);
|
|
386
|
+
cfg = resolveConfig(process.env, {
|
|
387
|
+
dir: opts.dir || undefined,
|
|
388
|
+
remote: opts.remote || undefined,
|
|
389
|
+
});
|
|
390
|
+
applyEnvFromConfig(cfg);
|
|
391
|
+
const links = opts.dryRun ? [] : runInstall(cfg, opts);
|
|
392
|
+
const scripts =
|
|
393
|
+
opts.scripts.length === 0
|
|
394
|
+
? []
|
|
395
|
+
: runScripts(cfg, opts.scripts, opts.scriptArgs, {
|
|
396
|
+
dryRun: opts.dryRun,
|
|
397
|
+
});
|
|
398
|
+
const failed = scripts.find((s) => s.exitCode !== 0);
|
|
399
|
+
const data = collectStatus({
|
|
400
|
+
full: opts.full,
|
|
401
|
+
agents: opts.agents,
|
|
402
|
+
from: opts.from,
|
|
403
|
+
});
|
|
404
|
+
emit({
|
|
405
|
+
data: {
|
|
406
|
+
setup: "ok",
|
|
407
|
+
config: saved,
|
|
408
|
+
links,
|
|
409
|
+
scripts,
|
|
410
|
+
...data,
|
|
411
|
+
},
|
|
412
|
+
json: opts.json,
|
|
413
|
+
help: [
|
|
414
|
+
opts.scripts.length
|
|
415
|
+
? "Run `ai-md status` to verify"
|
|
416
|
+
: "Run `ai-md script <name>` for private machine scripts (e.g. ensure-tools)",
|
|
417
|
+
"Run `ai-md init-project --repo <path> --from base` for a new app",
|
|
418
|
+
],
|
|
419
|
+
});
|
|
420
|
+
if (failed) process.exit(failed.exitCode);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
186
423
|
case "status": {
|
|
187
424
|
const data = collectStatus({
|
|
188
425
|
full: opts.full,
|
|
@@ -260,55 +497,65 @@ function main() {
|
|
|
260
497
|
break;
|
|
261
498
|
}
|
|
262
499
|
case "install": {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
{ stdio: "inherit", env: process.env }
|
|
271
|
-
);
|
|
272
|
-
if (result.status !== 0) {
|
|
273
|
-
process.exit(result.status === null ? 1 : result.status);
|
|
500
|
+
const saved = persistIfRequested(opts);
|
|
501
|
+
if (saved) {
|
|
502
|
+
cfg = resolveConfig(process.env, {
|
|
503
|
+
dir: opts.dir || undefined,
|
|
504
|
+
remote: opts.remote || undefined,
|
|
505
|
+
});
|
|
506
|
+
applyEnvFromConfig(cfg);
|
|
274
507
|
}
|
|
275
|
-
const links =
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
508
|
+
const links = runInstall(cfg, opts);
|
|
509
|
+
const scripts =
|
|
510
|
+
opts.scripts.length === 0
|
|
511
|
+
? []
|
|
512
|
+
: runScripts(cfg, opts.scripts, opts.scriptArgs, {
|
|
513
|
+
dryRun: opts.dryRun,
|
|
514
|
+
});
|
|
515
|
+
const failed = scripts.find((s) => s.exitCode !== 0);
|
|
282
516
|
const data = collectStatus({ full: opts.full, agents: opts.agents });
|
|
283
517
|
emit({
|
|
284
|
-
data: { install: "ok", links, ...data },
|
|
518
|
+
data: { install: "ok", config: saved, links, scripts, ...data },
|
|
285
519
|
json: opts.json,
|
|
286
520
|
help: [
|
|
287
|
-
"Run `ai-md
|
|
521
|
+
"Run `ai-md script <name>` for private machine scripts",
|
|
288
522
|
"Run `ai-md init-project --repo <path>` for a new app",
|
|
289
523
|
],
|
|
290
524
|
});
|
|
525
|
+
if (failed) process.exit(failed.exitCode);
|
|
291
526
|
break;
|
|
292
527
|
}
|
|
293
528
|
case "pull":
|
|
294
|
-
runBash(
|
|
295
|
-
"
|
|
296
|
-
...(opts.dryRun ? ["--dry-run"] : []),
|
|
297
|
-
|
|
529
|
+
runBash(
|
|
530
|
+
"sync-config.sh",
|
|
531
|
+
["pull", ...(opts.dryRun ? ["--dry-run"] : [])],
|
|
532
|
+
process.env
|
|
533
|
+
);
|
|
298
534
|
break;
|
|
299
535
|
case "push": {
|
|
300
536
|
const args = ["push"];
|
|
301
537
|
if (opts.message) args.push("-m", opts.message);
|
|
302
538
|
if (opts.dryRun) args.push("--dry-run");
|
|
303
|
-
runBash("sync-config.sh", args);
|
|
539
|
+
runBash("sync-config.sh", args, process.env);
|
|
304
540
|
break;
|
|
305
541
|
}
|
|
306
|
-
case "
|
|
307
|
-
case "
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
542
|
+
case "script":
|
|
543
|
+
case "run-script": {
|
|
544
|
+
const result = runScript(cfg, opts.scriptName, opts.scriptArgs, {
|
|
545
|
+
dryRun: opts.dryRun,
|
|
546
|
+
});
|
|
547
|
+
emit({
|
|
548
|
+
data: { scripts: [result] },
|
|
549
|
+
json: opts.json,
|
|
550
|
+
help: [
|
|
551
|
+
"Scripts live in ~/.ai-md/scripts/ (private content repo)",
|
|
552
|
+
"ai-md script <name> -- [args...]",
|
|
553
|
+
"ai-md setup --script <name> -- [args...]",
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
process.exit(result.exitCode);
|
|
311
557
|
break;
|
|
558
|
+
}
|
|
312
559
|
default:
|
|
313
560
|
fail(`unknown command: ${opts.cmd}`, {
|
|
314
561
|
exitCode: 2,
|
|
@@ -319,7 +566,7 @@ function main() {
|
|
|
319
566
|
}
|
|
320
567
|
} catch (err) {
|
|
321
568
|
fail(err.message || String(err), {
|
|
322
|
-
exitCode: err.code === "EINVAL" ? 2 : 1,
|
|
569
|
+
exitCode: err.code === "EINVAL" || err.code === "ENOENT" ? 2 : 1,
|
|
323
570
|
json: opts.json,
|
|
324
571
|
help: ["Run `ai-md --help`"],
|
|
325
572
|
});
|