@lazyingart/agintiflow 0.20.49 → 0.20.50

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.
@@ -0,0 +1,742 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import fsSync from "node:fs";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+
7
+ const execFileAsync = promisify(execFile);
8
+
9
+ const SKIP_DIRS = new Set([
10
+ ".git",
11
+ ".aginti",
12
+ ".aginti-sessions",
13
+ ".agintiflow",
14
+ ".aaps-work",
15
+ ".sessions",
16
+ "artifacts",
17
+ "build",
18
+ "coverage",
19
+ "dist",
20
+ "node_modules",
21
+ "out",
22
+ "runtime",
23
+ "target",
24
+ "vendor",
25
+ "__pycache__",
26
+ ]);
27
+
28
+ const DEFAULT_TIMEOUT_MS = 45_000;
29
+ const LONG_TIMEOUT_MS = 120_000;
30
+ const JSON_ACTIONS = new Set(["parse", "validate", "compile", "compile-project", "missing", "prepare-setup", "plan", "check", "run"]);
31
+ const ACTIONS_REQUIRING_FILE = new Set(["plan", "check", "run", "dry-run"]);
32
+ const ALLOWED_ACTIONS = new Set([
33
+ "status",
34
+ "files",
35
+ "init",
36
+ "install",
37
+ "parse",
38
+ "validate",
39
+ "compile",
40
+ "compile-project",
41
+ "missing",
42
+ "prepare-setup",
43
+ "plan",
44
+ "check",
45
+ "run",
46
+ "dry-run",
47
+ "studio",
48
+ "help",
49
+ ]);
50
+ const COMPILE_MODES = new Set(["check", "suggest", "apply", "interactive", "force"]);
51
+
52
+ function toProjectPath(value = "") {
53
+ return String(value || "").split(path.sep).join("/");
54
+ }
55
+
56
+ function safeRelative(projectDir, value, label = "path", { allowEmpty = false } = {}) {
57
+ const text = String(value || "").trim();
58
+ if (!text) {
59
+ if (allowEmpty) return "";
60
+ throw new Error(`${label} is required.`);
61
+ }
62
+ if (text.includes("\0")) throw new Error(`${label} contains an invalid NUL byte.`);
63
+ const normalized = path.normalize(text);
64
+ if (path.isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${path.sep}`)) {
65
+ throw new Error(`${label} must be project-relative: ${value}`);
66
+ }
67
+ const resolved = path.resolve(projectDir, normalized);
68
+ const relative = path.relative(projectDir, resolved);
69
+ if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
70
+ throw new Error(`${label} escapes project root: ${value}`);
71
+ }
72
+ return toProjectPath(relative);
73
+ }
74
+
75
+ function maybeStat(file) {
76
+ try {
77
+ return fsSync.statSync(file);
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ function isExecutableFile(file) {
84
+ const stat = maybeStat(file);
85
+ return Boolean(stat?.isFile());
86
+ }
87
+
88
+ async function commandExists(command) {
89
+ try {
90
+ await execFileAsync("command", ["-v", command], {
91
+ shell: true,
92
+ timeout: 5000,
93
+ maxBuffer: 64 * 1024,
94
+ });
95
+ return true;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ function devSiblingAapsScript(packageDir = "") {
102
+ const candidates = [
103
+ path.resolve(packageDir || process.cwd(), "..", "..", "AAPS", "scripts", "aaps.js"),
104
+ path.resolve(process.cwd(), "..", "AAPS", "scripts", "aaps.js"),
105
+ "/home/lachlan/ProjectsLFS/AAPS/scripts/aaps.js",
106
+ ];
107
+ return candidates.find((candidate) => isExecutableFile(candidate)) || "";
108
+ }
109
+
110
+ function envAapsCandidate() {
111
+ const value = String(process.env.AAPS_BIN || process.env.AGINTI_AAPS_BIN || "").trim();
112
+ if (!value) return null;
113
+ if (value.includes("/") || value.includes(path.sep)) {
114
+ const resolved = path.resolve(value);
115
+ if (!isExecutableFile(resolved)) {
116
+ return {
117
+ ok: false,
118
+ error: `Configured AAPS_BIN does not exist or is not a file: ${resolved}`,
119
+ };
120
+ }
121
+ if (path.basename(resolved) === "aaps.js") {
122
+ return {
123
+ ok: true,
124
+ candidate: {
125
+ source: "env",
126
+ label: resolved,
127
+ command: process.execPath,
128
+ prefixArgs: [resolved],
129
+ },
130
+ };
131
+ }
132
+ return {
133
+ ok: true,
134
+ candidate: {
135
+ source: "env",
136
+ label: resolved,
137
+ command: resolved,
138
+ prefixArgs: [],
139
+ },
140
+ };
141
+ }
142
+ return {
143
+ ok: true,
144
+ candidate: {
145
+ source: "env",
146
+ label: value,
147
+ command: value,
148
+ prefixArgs: [],
149
+ },
150
+ };
151
+ }
152
+
153
+ export async function discoverAaps({ cwd = process.cwd(), packageDir = "", allowNpx = false } = {}) {
154
+ const projectDir = path.resolve(cwd || process.cwd());
155
+ const envCandidate = envAapsCandidate();
156
+ if (envCandidate?.ok === false) {
157
+ return {
158
+ found: false,
159
+ projectDir,
160
+ source: "env",
161
+ error: envCandidate.error,
162
+ checked: [envCandidate.error],
163
+ };
164
+ }
165
+ if (envCandidate?.candidate) {
166
+ return { found: true, projectDir, ...envCandidate.candidate, checked: ["AAPS_BIN"] };
167
+ }
168
+
169
+ const checked = [];
170
+ const localBin = path.join(projectDir, "node_modules", ".bin", process.platform === "win32" ? "aaps.cmd" : "aaps");
171
+ checked.push(localBin);
172
+ if (isExecutableFile(localBin)) {
173
+ return {
174
+ found: true,
175
+ projectDir,
176
+ source: "project",
177
+ label: localBin,
178
+ command: localBin,
179
+ prefixArgs: [],
180
+ checked,
181
+ };
182
+ }
183
+
184
+ if (await commandExists("aaps")) {
185
+ checked.push("PATH:aaps");
186
+ return {
187
+ found: true,
188
+ projectDir,
189
+ source: "path",
190
+ label: "aaps",
191
+ command: "aaps",
192
+ prefixArgs: [],
193
+ checked,
194
+ };
195
+ }
196
+ checked.push("PATH:aaps");
197
+
198
+ const devScript = process.env.AGINTIFLOW_DEV_AAPS === "0" ? "" : devSiblingAapsScript(packageDir);
199
+ if (devScript) {
200
+ checked.push(devScript);
201
+ return {
202
+ found: true,
203
+ projectDir,
204
+ source: "dev-sibling",
205
+ label: devScript,
206
+ command: process.execPath,
207
+ prefixArgs: [devScript],
208
+ checked,
209
+ };
210
+ }
211
+
212
+ if (allowNpx) {
213
+ checked.push("npx -y @lazyingart/aaps");
214
+ return {
215
+ found: true,
216
+ projectDir,
217
+ source: "npx",
218
+ label: "npx -y @lazyingart/aaps",
219
+ command: "npx",
220
+ prefixArgs: ["-y", "@lazyingart/aaps"],
221
+ checked,
222
+ };
223
+ }
224
+
225
+ return {
226
+ found: false,
227
+ projectDir,
228
+ source: "",
229
+ label: "",
230
+ command: "",
231
+ prefixArgs: [],
232
+ checked,
233
+ error: "AAPS was not found. Run `aginti aaps install`, `npm install -g @lazyingart/aaps`, or set AAPS_BIN.",
234
+ };
235
+ }
236
+
237
+ async function readJsonIfExists(file) {
238
+ try {
239
+ return JSON.parse(await fs.readFile(file, "utf8"));
240
+ } catch {
241
+ return null;
242
+ }
243
+ }
244
+
245
+ async function walkAapsFiles(dir, root, files, { limit = 200, depth = 0 } = {}) {
246
+ if (files.length >= limit || depth > 12) return;
247
+ let entries = [];
248
+ try {
249
+ entries = await fs.readdir(dir, { withFileTypes: true });
250
+ } catch {
251
+ return;
252
+ }
253
+ for (const entry of entries) {
254
+ if (files.length >= limit) return;
255
+ const full = path.join(dir, entry.name);
256
+ if (entry.isDirectory()) {
257
+ if (!SKIP_DIRS.has(entry.name)) await walkAapsFiles(full, root, files, { limit, depth: depth + 1 });
258
+ } else if (entry.isFile() && entry.name.endsWith(".aaps")) {
259
+ files.push(toProjectPath(path.relative(root, full)));
260
+ }
261
+ }
262
+ }
263
+
264
+ export async function listAapsFiles({ cwd = process.cwd(), limit = 200 } = {}) {
265
+ const projectDir = path.resolve(cwd || process.cwd());
266
+ const files = [];
267
+ await walkAapsFiles(projectDir, projectDir, files, { limit });
268
+ return files.sort();
269
+ }
270
+
271
+ export async function readAapsManifest({ cwd = process.cwd() } = {}) {
272
+ const projectDir = path.resolve(cwd || process.cwd());
273
+ const manifestPath = path.join(projectDir, "aaps.project.json");
274
+ const manifest = await readJsonIfExists(manifestPath);
275
+ if (!manifest) return null;
276
+ return {
277
+ ...manifest,
278
+ path: "aaps.project.json",
279
+ activeFile: typeof manifest.activeFile === "string" ? manifest.activeFile : "",
280
+ defaultMain: typeof manifest.defaultMain === "string" ? manifest.defaultMain : "",
281
+ };
282
+ }
283
+
284
+ export async function resolveAapsEntry({ cwd = process.cwd(), file = "" } = {}) {
285
+ const projectDir = path.resolve(cwd || process.cwd());
286
+ const manifest = await readAapsManifest({ cwd: projectDir });
287
+ const candidate = String(file || manifest?.activeFile || manifest?.defaultMain || "").trim();
288
+ if (!candidate) return "";
289
+ return safeRelative(projectDir, candidate, "AAPS file");
290
+ }
291
+
292
+ async function inferAapsPackage(discovery) {
293
+ if (!discovery?.found) return null;
294
+ const script = discovery.prefixArgs?.[0] || "";
295
+ if (!script || path.basename(script) !== "aaps.js") return null;
296
+ const pkg = await readJsonIfExists(path.resolve(path.dirname(script), "..", "package.json"));
297
+ if (!pkg) return null;
298
+ return {
299
+ name: pkg.name || "@lazyingart/aaps",
300
+ version: pkg.version || "",
301
+ path: path.resolve(path.dirname(script), "..", "package.json"),
302
+ };
303
+ }
304
+
305
+ export async function aapsStatus({ cwd = process.cwd(), packageDir = "" } = {}) {
306
+ const projectDir = path.resolve(cwd || process.cwd());
307
+ const [discovery, files, manifest] = await Promise.all([
308
+ discoverAaps({ cwd: projectDir, packageDir }),
309
+ listAapsFiles({ cwd: projectDir }),
310
+ readAapsManifest({ cwd: projectDir }),
311
+ ]);
312
+ const pkg = await inferAapsPackage(discovery);
313
+ const entry = await resolveAapsEntry({ cwd: projectDir }).catch(() => "");
314
+ return {
315
+ ok: true,
316
+ projectDir,
317
+ found: discovery.found,
318
+ source: discovery.source || "",
319
+ command: discovery.label || "",
320
+ error: discovery.error || "",
321
+ checked: discovery.checked || [],
322
+ package: pkg,
323
+ manifest: manifest
324
+ ? {
325
+ name: manifest.name || path.basename(projectDir),
326
+ path: manifest.path,
327
+ activeFile: manifest.activeFile || "",
328
+ defaultMain: manifest.defaultMain || "",
329
+ }
330
+ : null,
331
+ entry,
332
+ files,
333
+ };
334
+ }
335
+
336
+ function splitCommandLine(value = "") {
337
+ return String(value || "")
338
+ .split(/\s+/)
339
+ .map((item) => item.trim())
340
+ .filter(Boolean);
341
+ }
342
+
343
+ function normalizeAction(action = "") {
344
+ const value = String(action || "status").trim().toLowerCase();
345
+ if (value === "on" || value === "auto") return value;
346
+ if (value === "ls" || value === "list") return "files";
347
+ if (value === "dryrun") return "dry-run";
348
+ if (value === "compile-project") return "compile-project";
349
+ return value || "status";
350
+ }
351
+
352
+ function extractMode(args = []) {
353
+ let mode = "";
354
+ const rest = [];
355
+ for (let index = 0; index < args.length; index += 1) {
356
+ const item = args[index];
357
+ if (item === "--mode") {
358
+ mode = String(args[index + 1] || "");
359
+ index += 1;
360
+ continue;
361
+ }
362
+ if (String(item || "").startsWith("--mode=")) {
363
+ mode = String(item).slice("--mode=".length);
364
+ continue;
365
+ }
366
+ if (!mode && COMPILE_MODES.has(String(item || "").toLowerCase())) {
367
+ mode = String(item).toLowerCase();
368
+ continue;
369
+ }
370
+ rest.push(item);
371
+ }
372
+ return { mode: mode || "check", rest };
373
+ }
374
+
375
+ function buildAapsArgs(action, args = [], { cwd = process.cwd() } = {}) {
376
+ const projectDir = path.resolve(cwd || process.cwd());
377
+ const normalized = normalizeAction(action);
378
+ if (!ALLOWED_ACTIONS.has(normalized)) {
379
+ throw new Error(`Unknown AAPS action: ${action}. Use status, files, init, parse, validate, compile, check, run, dry-run, or studio.`);
380
+ }
381
+ if (normalized === "help") return { action: normalized, cliArgs: ["help"], timeoutMs: DEFAULT_TIMEOUT_MS };
382
+ if (normalized === "status" || normalized === "files" || normalized === "init" || normalized === "install") {
383
+ return { action: normalized, cliArgs: [], timeoutMs: DEFAULT_TIMEOUT_MS };
384
+ }
385
+ if (normalized === "studio") {
386
+ const allowed = [];
387
+ for (let index = 0; index < args.length; index += 1) {
388
+ const item = args[index];
389
+ if (["--host", "--port"].includes(item)) {
390
+ allowed.push(item, String(args[index + 1] || ""));
391
+ index += 1;
392
+ } else if (item === "--mock-codex") {
393
+ allowed.push(item);
394
+ } else if (/^\d+$/.test(String(item || ""))) {
395
+ allowed.push("--port", String(item));
396
+ } else {
397
+ throw new Error(`Unsupported AAPS studio argument: ${item}`);
398
+ }
399
+ }
400
+ return { action: normalized, cliArgs: ["studio", ...allowed], timeoutMs: LONG_TIMEOUT_MS };
401
+ }
402
+
403
+ const { mode, rest } = normalized === "compile" || normalized === "compile-project" ? extractMode(args) : { mode: "", rest: args };
404
+ if (mode && !COMPILE_MODES.has(mode)) throw new Error(`Invalid AAPS compile mode: ${mode}`);
405
+ const fileArg = rest.find((item) => !String(item || "").startsWith("--")) || "";
406
+ const file = fileArg ? safeRelative(projectDir, fileArg, "AAPS file") : "";
407
+ const cliArgs = [];
408
+ if (normalized === "dry-run") cliArgs.push("run");
409
+ else cliArgs.push(normalized);
410
+ if (file) cliArgs.push(file);
411
+ if (ACTIONS_REQUIRING_FILE.has(normalized) && !file) {
412
+ throw new Error(`AAPS ${normalized} needs a .aaps file or activeFile/defaultMain in aaps.project.json.`);
413
+ }
414
+ cliArgs.push("--project", ".");
415
+ if (normalized === "compile") cliArgs.push("--mode", mode || "check");
416
+ if (normalized === "compile-project") cliArgs.push("--mode", mode || "check");
417
+ if (normalized === "dry-run") cliArgs.push("--dry-run");
418
+ if (JSON_ACTIONS.has(normalized === "dry-run" ? "run" : normalized)) cliArgs.push("--json");
419
+ return { action: normalized, cliArgs, timeoutMs: normalized === "run" || normalized === "dry-run" ? LONG_TIMEOUT_MS : DEFAULT_TIMEOUT_MS };
420
+ }
421
+
422
+ function parseJsonMaybe(stdout = "") {
423
+ const text = String(stdout || "").trim();
424
+ if (!text) return null;
425
+ try {
426
+ return JSON.parse(text);
427
+ } catch {
428
+ const start = text.indexOf("{");
429
+ const end = text.lastIndexOf("}");
430
+ if (start >= 0 && end > start) {
431
+ try {
432
+ return JSON.parse(text.slice(start, end + 1));
433
+ } catch {
434
+ return null;
435
+ }
436
+ }
437
+ return null;
438
+ }
439
+ }
440
+
441
+ function compactOutput(value = "", limit = 12_000) {
442
+ const text = String(value || "");
443
+ if (text.length <= limit) return text;
444
+ return `${text.slice(0, limit).trimEnd()}\n... [truncated ${text.length - limit} chars]`;
445
+ }
446
+
447
+ async function runDiscoveredAaps(discovery, cliArgs, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
448
+ const fullArgs = [...(discovery.prefixArgs || []), ...cliArgs];
449
+ try {
450
+ const result = await execFileAsync(discovery.command, fullArgs, {
451
+ cwd: discovery.projectDir,
452
+ timeout: timeoutMs,
453
+ maxBuffer: 4 * 1024 * 1024,
454
+ env: {
455
+ ...process.env,
456
+ AAPS_PROJECT_ROOT: discovery.projectDir,
457
+ },
458
+ });
459
+ return {
460
+ ok: true,
461
+ exitCode: 0,
462
+ stdout: compactOutput(result.stdout || ""),
463
+ stderr: compactOutput(result.stderr || ""),
464
+ json: parseJsonMaybe(result.stdout || ""),
465
+ };
466
+ } catch (error) {
467
+ return {
468
+ ok: false,
469
+ exitCode: Number.isInteger(error?.code) ? error.code : 1,
470
+ stdout: compactOutput(error?.stdout || ""),
471
+ stderr: compactOutput(error?.stderr || error?.message || String(error)),
472
+ json: parseJsonMaybe(error?.stdout || ""),
473
+ };
474
+ }
475
+ }
476
+
477
+ async function createAapsStarterProject({ cwd = process.cwd(), name = "" } = {}) {
478
+ const projectDir = path.resolve(cwd || process.cwd());
479
+ const projectName = String(name || path.basename(projectDir) || "AgInTiFlow AAPS Project").trim();
480
+ const workflowDir = path.join(projectDir, "workflows");
481
+ const reportsDir = path.join(projectDir, "reports");
482
+ const runsDir = path.join(projectDir, "runs");
483
+ const artifactsDir = path.join(projectDir, "artifacts");
484
+ await Promise.all([fs.mkdir(workflowDir, { recursive: true }), fs.mkdir(reportsDir, { recursive: true }), fs.mkdir(runsDir, { recursive: true }), fs.mkdir(artifactsDir, { recursive: true })]);
485
+
486
+ const manifestPath = path.join(projectDir, "aaps.project.json");
487
+ const workflowPath = path.join(workflowDir, "main.aaps");
488
+ const created = [];
489
+ if (!fsSync.existsSync(manifestPath)) {
490
+ const now = new Date().toISOString();
491
+ const manifest = {
492
+ schema: "aaps_project/0.1",
493
+ name: projectName,
494
+ path: ".",
495
+ description: "Project-local AAPS workflows for large agentic tasks.",
496
+ domain: "agentic-workflows",
497
+ tags: ["agintiflow", "aaps"],
498
+ defaultMain: "workflows/main.aaps",
499
+ activeFile: "workflows/main.aaps",
500
+ created: now,
501
+ updated: now,
502
+ paths: {
503
+ workflows: "workflows",
504
+ blocks: "blocks",
505
+ skills: "skills",
506
+ modules: "modules",
507
+ subworkflows: "workflows",
508
+ data: "data",
509
+ artifacts: "artifacts",
510
+ runs: "runs",
511
+ reports: "reports",
512
+ notes: "notes",
513
+ },
514
+ artifactRoot: "artifacts",
515
+ runDatabase: "runs/aaps-runs.jsonl",
516
+ tools: ["node", "python3", "git", "aginti"],
517
+ models: ["deepseek-v4-flash", "deepseek-v4-pro"],
518
+ files: {
519
+ workflows: ["workflows/main.aaps"],
520
+ blocks: [],
521
+ skills: [],
522
+ modules: [],
523
+ subworkflows: [],
524
+ drafts: [],
525
+ archives: [],
526
+ references: [],
527
+ },
528
+ notes: ["Keep all AAPS paths project-relative.", "Use `aginti aaps validate` before running long workflows."],
529
+ };
530
+ await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
531
+ created.push("aaps.project.json");
532
+ }
533
+ if (!fsSync.existsSync(workflowPath)) {
534
+ const source = `pipeline "${projectName} Starter" {
535
+ subtitle "A project-local AAPS workflow for AgInTiFlow"
536
+ version "0.2"
537
+ domain "agentic-workflows"
538
+ tags "agintiflow, aaps, starter"
539
+ artifact_dir "artifacts"
540
+ database "runs/aaps-runs.jsonl"
541
+ goal "Turn a high-level request into planned, checked, durable project work."
542
+ output plan: markdown = "reports/aaps-plan.md"
543
+
544
+ agent planner {
545
+ role "Plan project work in small, verifiable phases."
546
+ model "deepseek-v4-pro"
547
+ tools "filesystem, shell, git, aginti"
548
+ }
549
+
550
+ task draft_plan {
551
+ uses planner
552
+ prompt """
553
+ Inspect this project and produce a concise phased plan with acceptance checks.
554
+ Keep paths project-relative and avoid secrets.
555
+ """
556
+ output plan: markdown = "reports/aaps-plan.md"
557
+ verify "The plan names concrete files, commands, risks, and acceptance checks."
558
+ }
559
+ }
560
+ `;
561
+ await fs.writeFile(workflowPath, source, "utf8");
562
+ created.push("workflows/main.aaps");
563
+ }
564
+ return {
565
+ ok: true,
566
+ projectDir,
567
+ created,
568
+ manifest: "aaps.project.json",
569
+ entry: "workflows/main.aaps",
570
+ };
571
+ }
572
+
573
+ async function installAaps({ cwd = process.cwd(), scope = "" } = {}) {
574
+ const projectDir = path.resolve(cwd || process.cwd());
575
+ const normalizedScope = String(scope || "").trim().toLowerCase();
576
+ const hasPackageJson = fsSync.existsSync(path.join(projectDir, "package.json"));
577
+ if (!hasPackageJson && normalizedScope !== "global") {
578
+ return {
579
+ ok: false,
580
+ command: "",
581
+ stdout: "",
582
+ stderr:
583
+ "No package.json found for a project-local install. Run `npm init -y` first, or explicitly use `aginti aaps install global` / `npm install -g @lazyingart/aaps`.",
584
+ };
585
+ }
586
+ const args =
587
+ normalizedScope === "global"
588
+ ? ["install", "-g", "@lazyingart/aaps"]
589
+ : ["install", "--save-dev", "@lazyingart/aaps"];
590
+ try {
591
+ const result = await execFileAsync("npm", args, {
592
+ cwd: projectDir,
593
+ timeout: LONG_TIMEOUT_MS,
594
+ maxBuffer: 4 * 1024 * 1024,
595
+ });
596
+ return {
597
+ ok: true,
598
+ command: `npm ${args.join(" ")}`,
599
+ stdout: compactOutput(result.stdout || "", 8000),
600
+ stderr: compactOutput(result.stderr || "", 8000),
601
+ };
602
+ } catch (error) {
603
+ return {
604
+ ok: false,
605
+ command: `npm ${args.join(" ")}`,
606
+ stdout: compactOutput(error?.stdout || "", 8000),
607
+ stderr: compactOutput(error?.stderr || error?.message || String(error), 8000),
608
+ };
609
+ }
610
+ }
611
+
612
+ export async function runAapsAction(action = "status", rawArgs = [], { cwd = process.cwd(), packageDir = "" } = {}) {
613
+ const projectDir = path.resolve(cwd || process.cwd());
614
+ const args = Array.isArray(rawArgs) ? rawArgs.map(String).filter(Boolean) : splitCommandLine(rawArgs);
615
+ const normalized = normalizeAction(action);
616
+
617
+ if (normalized === "status") return await aapsStatus({ cwd: projectDir, packageDir });
618
+ if (normalized === "files") {
619
+ const files = await listAapsFiles({ cwd: projectDir });
620
+ const manifest = await readAapsManifest({ cwd: projectDir });
621
+ return { ok: true, action: "files", projectDir, manifest, files };
622
+ }
623
+ if (normalized === "init") return await createAapsStarterProject({ cwd: projectDir, name: args.join(" ") });
624
+ if (normalized === "install") return await installAaps({ cwd: projectDir, scope: args[0] || "" });
625
+
626
+ const discovery = await discoverAaps({ cwd: projectDir, packageDir });
627
+ if (!discovery.found) {
628
+ return {
629
+ ok: false,
630
+ action: normalized,
631
+ projectDir,
632
+ error: discovery.error,
633
+ checked: discovery.checked || [],
634
+ };
635
+ }
636
+
637
+ let commandArgs = args;
638
+ if (["compile", "plan", "check", "run", "dry-run"].includes(normalized)) {
639
+ const rest = normalized === "compile" ? extractMode(args).rest : args;
640
+ const hasExplicitFile = rest.some((item) => !String(item || "").startsWith("--"));
641
+ if (!hasExplicitFile) {
642
+ const entry = await resolveAapsEntry({ cwd: projectDir });
643
+ if (entry) commandArgs = [...args, entry];
644
+ }
645
+ }
646
+
647
+ const built = buildAapsArgs(normalized, commandArgs, { cwd: projectDir });
648
+ const result = await runDiscoveredAaps(discovery, built.cliArgs, { timeoutMs: built.timeoutMs });
649
+ return {
650
+ ...result,
651
+ action: built.action,
652
+ projectDir,
653
+ source: discovery.source,
654
+ command: [discovery.label, ...built.cliArgs].join(" "),
655
+ cliArgs: built.cliArgs,
656
+ };
657
+ }
658
+
659
+ function summarizeJson(action, json) {
660
+ if (!json || typeof json !== "object") return "";
661
+ if (action === "parse") {
662
+ const pipeline = json.pipeline || {};
663
+ return `pipeline=${pipeline.name || "(unnamed)"} tasks=${pipeline.tasks?.length || 0} agents=${pipeline.agents?.length || 0} diagnostics=${json.diagnostics?.length || 0}`;
664
+ }
665
+ if (action === "validate") {
666
+ return `ok=${Boolean(json.ok)} files=${json.files ?? ""} diagnostics=${json.diagnostics?.length || 0}`;
667
+ }
668
+ if (action === "compile" || action === "compile-project" || action === "missing" || action === "prepare-setup") {
669
+ return `mode=${json.mode || ""} missing=${json.missingComponents?.length || 0} generated=${json.generatedFiles?.length || 0} compileDir=${json.compileDir || ""}`;
670
+ }
671
+ if (action === "check" || action === "plan" || action === "run" || action === "dry-run") {
672
+ const ok = json.ok ?? json.ready ?? "";
673
+ const steps = json.plan?.steps?.length ?? json.steps?.length ?? "";
674
+ const runDir = json.runDir || json.runRoot || "";
675
+ return `ok=${ok} steps=${steps} runDir=${runDir}`;
676
+ }
677
+ return "";
678
+ }
679
+
680
+ export function formatAapsResult(result = {}) {
681
+ if (!result || typeof result !== "object") return "AAPS returned no result.";
682
+ if (result.action === "files") {
683
+ const lines = [
684
+ `AAPS files in ${result.projectDir}: ${result.files?.length || 0}`,
685
+ result.manifest ? `manifest=${result.manifest.path || "aaps.project.json"} active=${result.manifest.activeFile || result.manifest.defaultMain || ""}` : "manifest=missing",
686
+ ...(result.files || []).slice(0, 40).map((file) => `- ${file}`),
687
+ ];
688
+ if ((result.files || []).length > 40) lines.push(`... ${(result.files || []).length - 40} more`);
689
+ return lines.join("\n");
690
+ }
691
+ if (result.found !== undefined) {
692
+ return [
693
+ `AAPS adapter: ${result.found ? "available" : "missing"}`,
694
+ `project=${result.projectDir || ""}`,
695
+ result.found ? `source=${result.source} command=${result.command}` : `error=${result.error}`,
696
+ result.package ? `package=${result.package.name}@${result.package.version}` : "",
697
+ result.manifest ? `manifest=${result.manifest.path} active=${result.manifest.activeFile || result.manifest.defaultMain || ""}` : "manifest=missing",
698
+ `files=${result.files?.length || 0}${result.entry ? ` entry=${result.entry}` : ""}`,
699
+ !result.found && result.checked?.length ? `checked=${result.checked.join(", ")}` : "",
700
+ ]
701
+ .filter(Boolean)
702
+ .join("\n");
703
+ }
704
+ if (result.manifest && result.entry && Array.isArray(result.created)) {
705
+ return [
706
+ `AAPS project initialized in ${result.projectDir}`,
707
+ result.created.length ? `created=${result.created.join(", ")}` : "created=none; existing files preserved",
708
+ `entry=${result.entry}`,
709
+ "Next: /aaps validate or aginti aaps validate",
710
+ ].join("\n");
711
+ }
712
+ if (result.command?.startsWith("npm ")) {
713
+ return [
714
+ `AAPS install ${result.ok ? "completed" : "failed"}`,
715
+ `command=${result.command}`,
716
+ result.stdout ? `stdout\n${result.stdout.trim()}` : "",
717
+ result.stderr ? `stderr\n${result.stderr.trim()}` : "",
718
+ ]
719
+ .filter(Boolean)
720
+ .join("\n");
721
+ }
722
+ const summary = summarizeJson(result.action, result.json);
723
+ return [
724
+ `AAPS ${result.action || "command"} ${result.ok ? "ok" : "failed"}`,
725
+ result.command ? `command=${result.command}` : "",
726
+ summary ? `summary=${summary}` : "",
727
+ result.stdout ? `stdout\n${result.stdout.trim()}` : "",
728
+ result.stderr ? `stderr\n${result.stderr.trim()}` : "",
729
+ result.error ? `error=${result.error}` : "",
730
+ result.checked?.length ? `checked=${result.checked.join(", ")}` : "",
731
+ ]
732
+ .filter(Boolean)
733
+ .join("\n");
734
+ }
735
+
736
+ export async function handleAapsCliCommand(argv = [], { cwd = process.cwd(), packageDir = "" } = {}) {
737
+ const [action = "status", ...args] = argv;
738
+ const result = await runAapsAction(action, args, { cwd, packageDir });
739
+ console.log(formatAapsResult(result));
740
+ if (result.ok === false) process.exitCode = 1;
741
+ return result;
742
+ }