@felixdotgo/aidlc-kit 0.1.1 → 0.3.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.
Files changed (198) hide show
  1. package/DEVELOPMENT.md +14 -13
  2. package/README.md +37 -43
  3. package/dist/assets/.agents/aidlc/docker/Dockerfile.claude +7 -0
  4. package/dist/assets/.agents/aidlc/docker/Dockerfile.codex +9 -0
  5. package/dist/assets/.agents/aidlc/docker/Dockerfile.opencode +8 -0
  6. package/dist/assets/.agents/aidlc/orchestrator.md +2 -2
  7. package/dist/assets/.agents/aidlc/phase-clarify.md +1 -1
  8. package/dist/assets/.agents/aidlc/phase-plan.md +2 -2
  9. package/dist/assets/.agents/aidlc/schemas/config.schema.json +2 -1
  10. package/dist/assets/.agents/aidlc/schemas/state.schema.json +2 -0
  11. package/dist/assets/.agents/aidlc/scripts/lib/assignment-store.mjs +94 -48
  12. package/dist/assets/.agents/aidlc/scripts/lib/context-runtime.mjs +5 -4
  13. package/dist/assets/.agents/aidlc/scripts/lib/loop-authority.mjs +261 -0
  14. package/dist/assets/.agents/aidlc/scripts/lib/loop-commit-message.mjs +45 -0
  15. package/dist/assets/.agents/aidlc/scripts/lib/loop-daemon.mjs +206 -0
  16. package/dist/assets/.agents/aidlc/scripts/lib/loop-drivers.mjs +100 -14
  17. package/dist/assets/.agents/aidlc/scripts/lib/loop-env.mjs +279 -0
  18. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-cli.mjs +105 -32
  19. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-remote.mjs +17 -1
  20. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-supervisor.mjs +24 -7
  21. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet.mjs +42 -16
  22. package/dist/assets/.agents/aidlc/scripts/lib/loop-git.mjs +20 -6
  23. package/dist/assets/.agents/aidlc/scripts/lib/loop-holder.mjs +376 -0
  24. package/dist/assets/.agents/aidlc/scripts/lib/loop-intake.mjs +33 -7
  25. package/dist/assets/.agents/aidlc/scripts/lib/loop-lease.mjs +67 -61
  26. package/dist/assets/.agents/aidlc/scripts/lib/loop-leases.mjs +268 -0
  27. package/dist/assets/.agents/aidlc/scripts/lib/loop-local-workers.mjs +116 -28
  28. package/dist/assets/.agents/aidlc/scripts/lib/loop-runtime.mjs +114 -12
  29. package/dist/assets/.agents/aidlc/scripts/lib/loop-stream.mjs +117 -0
  30. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-local.mjs +16 -56
  31. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-run-local.mjs +149 -6
  32. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor.mjs +9 -1
  33. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-docker.mjs +44 -6
  34. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-image.mjs +64 -0
  35. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-log.mjs +158 -0
  36. package/dist/assets/.agents/aidlc/scripts/lib/repo-head.mjs +22 -0
  37. package/dist/assets/.agents/aidlc/scripts/lib/store.mjs +68 -2
  38. package/dist/assets/.agents/aidlc/scripts/lib/task-runtime-migrate.mjs +75 -0
  39. package/dist/assets/.agents/aidlc/scripts/lib/task-runtime.mjs +97 -0
  40. package/dist/assets/.agents/aidlc/scripts/loop-entry.mjs +168 -73
  41. package/dist/assets/.agents/aidlc/scripts/loop-gate.mjs +19 -7
  42. package/dist/assets/.agents/aidlc/scripts/loop.cmd +101 -0
  43. package/dist/assets/.agents/aidlc/scripts/loop.mjs +2 -1
  44. package/dist/assets/.agents/aidlc/scripts/loop.sh +71 -0
  45. package/dist/assets/.agents/aidlc/scripts/remote-state.mjs +10 -4
  46. package/dist/assets/.agents/aidlc/scripts/state.mjs +16 -8
  47. package/dist/assets/.agents/aidlc/templates/design.md +9 -0
  48. package/dist/assets/.agents/aidlc/templates/intent.md +4 -0
  49. package/dist/assets/.agents/aidlc/templates/workplan.md +1 -1
  50. package/dist/assets/.agents/skills/aidlc-plan/SKILL.md +1 -1
  51. package/dist/assets/ui/bulma.min.css +3 -0
  52. package/dist/assets/ui/changes.js +83 -0
  53. package/dist/assets/ui/common.js +664 -0
  54. package/dist/assets/ui/dashboard.css +636 -0
  55. package/dist/assets/ui/dashboard.js +77 -0
  56. package/dist/assets/ui/detail.js +159 -0
  57. package/dist/assets/ui/live-log.js +332 -0
  58. package/dist/services/aidlc-coordination/Dockerfile +11 -3
  59. package/dist/services/aidlc-coordination/compose.yaml +12 -0
  60. package/dist/services/aidlc-coordination/package-lock.json +3 -1
  61. package/dist/services/aidlc-coordination/package.json +9 -4
  62. package/dist/services/aidlc-coordination/src/lifecycle-core.mjs +68 -2
  63. package/dist/services/aidlc-coordination/src/repository.mjs +66 -0
  64. package/dist/services/aidlc-coordination/src/server.mjs +9 -3
  65. package/dist/services/aidlc-coordination/test/fleet.test.mjs +21 -0
  66. package/dist/services/aidlc-coordination/test/server-health-auth.test.mjs +67 -0
  67. package/dist/src/adapters.d.ts +0 -3
  68. package/dist/src/adapters.js +3 -5
  69. package/dist/src/cli/argv.d.ts +12 -0
  70. package/dist/src/cli/argv.js +68 -0
  71. package/dist/src/cli/commands.d.ts +8 -0
  72. package/dist/src/cli/commands.js +159 -0
  73. package/dist/src/cli/loop-launcher.d.ts +47 -0
  74. package/dist/src/cli/loop-launcher.js +322 -0
  75. package/dist/src/cli/options.d.ts +25 -0
  76. package/dist/src/cli/options.js +32 -0
  77. package/dist/src/cli/prompt.d.ts +9 -0
  78. package/dist/src/cli/prompt.js +66 -0
  79. package/dist/src/cli.js +28 -191
  80. package/dist/src/gate.js +3 -2
  81. package/dist/src/installer.d.ts +14 -7
  82. package/dist/src/installer.js +42 -143
  83. package/dist/src/legacy.js +1 -0
  84. package/dist/src/managed-content.d.ts +7 -0
  85. package/dist/src/managed-content.js +15 -4
  86. package/dist/src/managed-planning.d.ts +65 -0
  87. package/dist/src/managed-planning.js +219 -0
  88. package/dist/src/mcp-setup.js +3 -1
  89. package/dist/src/model/config-contracts.d.ts +65 -0
  90. package/dist/src/model/config-contracts.js +1 -0
  91. package/dist/src/model/install-contracts.d.ts +29 -0
  92. package/dist/src/model/install-contracts.js +1 -0
  93. package/dist/src/model/lifecycle-enums.d.ts +13 -0
  94. package/dist/src/model/lifecycle-enums.js +1 -0
  95. package/dist/src/model/loop.d.ts +20 -0
  96. package/dist/src/model/loop.js +1 -0
  97. package/dist/src/model/manifest.d.ts +16 -0
  98. package/dist/src/model/manifest.js +1 -0
  99. package/dist/src/model/records.d.ts +142 -0
  100. package/dist/src/model/records.js +1 -0
  101. package/dist/src/model.d.ts +11 -264
  102. package/dist/src/model.js +1 -1
  103. package/dist/src/profiles.d.ts +2 -0
  104. package/dist/src/profiles.js +53 -9
  105. package/dist/src/state/archive-record.d.ts +9 -0
  106. package/dist/src/state/archive-record.js +26 -0
  107. package/dist/src/state/commands.d.ts +18 -0
  108. package/dist/src/state/commands.js +27 -0
  109. package/dist/src/state/domain.d.ts +9 -0
  110. package/dist/src/state/domain.js +9 -0
  111. package/dist/src/state/guards.d.ts +7 -0
  112. package/dist/src/state/guards.js +17 -0
  113. package/dist/src/state/io.d.ts +10 -0
  114. package/dist/src/state/io.js +35 -0
  115. package/dist/src/state/items.d.ts +3 -4
  116. package/dist/src/state/items.js +58 -57
  117. package/dist/src/state/lessons.js +14 -7
  118. package/dist/src/state/lifecycle.js +2 -2
  119. package/dist/src/state/lock.js +7 -6
  120. package/dist/src/state/memory.js +24 -12
  121. package/dist/src/state/{archive.js → migrate.js} +7 -7
  122. package/dist/src/state/partition.d.ts +15 -0
  123. package/dist/src/state/partition.js +90 -0
  124. package/dist/src/state/paths.d.ts +11 -0
  125. package/dist/src/state/paths.js +12 -0
  126. package/dist/src/state/render.js +9 -6
  127. package/dist/src/state/store.js +9 -15
  128. package/dist/src/state/transitions.js +9 -7
  129. package/dist/src/state/validate.js +55 -7
  130. package/dist/src/state.d.ts +16 -11
  131. package/dist/src/state.js +16 -11
  132. package/dist/src/task-ui.d.ts +33 -0
  133. package/dist/src/task-ui.js +33 -0
  134. package/dist/src/ui/assets.d.ts +14 -0
  135. package/dist/src/ui/assets.js +45 -0
  136. package/dist/src/ui/changes.d.ts +83 -0
  137. package/dist/src/ui/changes.js +498 -0
  138. package/dist/src/ui/containers.d.ts +71 -0
  139. package/dist/src/ui/containers.js +214 -0
  140. package/dist/src/ui/daemon.d.ts +75 -0
  141. package/dist/src/ui/daemon.js +232 -0
  142. package/dist/src/ui/data.d.ts +42 -0
  143. package/dist/src/ui/data.js +89 -0
  144. package/dist/src/ui/docs.d.ts +13 -0
  145. package/dist/src/ui/docs.js +39 -0
  146. package/dist/src/ui/files.d.ts +3 -0
  147. package/dist/src/ui/files.js +20 -0
  148. package/dist/src/ui/http.d.ts +14 -0
  149. package/dist/src/ui/http.js +80 -0
  150. package/dist/src/ui/live.d.ts +5 -0
  151. package/dist/src/ui/live.js +43 -0
  152. package/dist/src/ui/markdown.d.ts +2 -0
  153. package/dist/src/ui/markdown.js +127 -0
  154. package/dist/src/ui/options.d.ts +48 -0
  155. package/dist/src/ui/options.js +81 -0
  156. package/dist/src/ui/security.d.ts +34 -0
  157. package/dist/src/ui/security.js +38 -0
  158. package/dist/src/ui/server.d.ts +39 -0
  159. package/dist/src/ui/server.js +347 -0
  160. package/dist/src/ui/sse.d.ts +14 -0
  161. package/dist/src/ui/sse.js +281 -0
  162. package/dist/src/ui/views/board.d.ts +7 -0
  163. package/dist/src/ui/views/board.js +131 -0
  164. package/dist/src/ui/views/changes.d.ts +19 -0
  165. package/dist/src/ui/views/changes.js +154 -0
  166. package/dist/src/ui/views/components.d.ts +56 -0
  167. package/dist/src/ui/views/components.js +113 -0
  168. package/dist/src/ui/views/docs.d.ts +15 -0
  169. package/dist/src/ui/views/docs.js +64 -0
  170. package/dist/src/ui/views/layout.d.ts +44 -0
  171. package/dist/src/ui/views/layout.js +233 -0
  172. package/dist/src/ui/views/task.d.ts +25 -0
  173. package/dist/src/ui/views/task.js +320 -0
  174. package/dist/src/upgrade-board.js +10 -10
  175. package/dist/src/upgrade.d.ts +0 -1
  176. package/dist/src/upgrade.js +24 -85
  177. package/dist/src/workflow.d.ts +4 -0
  178. package/dist/src/workflow.js +36 -4
  179. package/docs/README.md +41 -22
  180. package/docs/architecture.md +101 -37
  181. package/docs/command-reference.md +289 -52
  182. package/docs/configuration.md +70 -30
  183. package/docs/coordination-service.md +91 -30
  184. package/docs/design-decisions.md +87 -0
  185. package/docs/development.md +124 -49
  186. package/docs/loop-local-run.md +228 -0
  187. package/docs/loop-runbook.md +214 -0
  188. package/docs/operating-workflow.md +55 -42
  189. package/docs/testing-and-release.md +72 -49
  190. package/docs/troubleshooting.md +221 -0
  191. package/package.json +4 -1
  192. package/dist/src/ledger.d.ts +0 -22
  193. package/dist/src/ledger.js +0 -119
  194. package/dist/src/state/shared.d.ts +0 -29
  195. package/dist/src/state/shared.js +0 -71
  196. package/dist/src/tracker.d.ts +0 -36
  197. package/dist/src/tracker.js +0 -44
  198. /package/dist/src/state/{archive.d.ts → migrate.d.ts} +0 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Interactive layer: TTY detection, dynamic @inquirer/prompts loading, and the
3
+ * prompting flows (loop configuration, MCP setup selections, confirmations).
4
+ * Parsing of argv into options happens in ./argv.ts and ./options.ts.
5
+ */
6
+ import { stdin, stdout } from "node:process";
7
+ import { LOOP_PROVIDERS } from "../model.js";
8
+ import { validateLoopConfig } from "../profiles.js";
9
+ import { loopSelection, mcpArgSelections, parseWorkerMapping } from "./options.js";
10
+ export const interactive = () => Boolean(stdin.isTTY && stdout.isTTY);
11
+ const prompts = async () => import("@inquirer/prompts");
12
+ export const chooseLoop = async (args, upgrade) => {
13
+ let { mode, lead: leadArg, workers: workersArg } = loopSelection(args);
14
+ if (!mode && (leadArg || workersArg))
15
+ throw new Error("--loop-lead and --loop-workers require --loop-mode");
16
+ if (!mode && interactive()) {
17
+ const { select } = await prompts();
18
+ mode = await select({ message: "Loop execution mode", choices: [
19
+ ...(upgrade ? [{ name: "Keep existing loop configuration", value: "keep" }] : []),
20
+ { name: "Off", value: "off" }, { name: "Local", value: "local" }, { name: "Fleet", value: "fleet" }
21
+ ] });
22
+ }
23
+ if (!mode || mode === "keep")
24
+ return undefined;
25
+ if (mode === "off") {
26
+ if (leadArg || workersArg)
27
+ throw new Error("Disabled loop cannot specify agents");
28
+ return validateLoopConfig({ mode });
29
+ }
30
+ let lead = leadArg, rawWorkers = workersArg;
31
+ if (interactive() && (!lead || !rawWorkers)) {
32
+ const { select, input } = await prompts();
33
+ lead ??= await select({ message: "Lead agent", choices: LOOP_PROVIDERS.map((name) => ({ name, value: name })) });
34
+ rawWorkers ??= await input({ message: "Workers (id=provider, comma separated)", default: "codex-worker=codex,claude-worker=claude" });
35
+ }
36
+ if (!lead || !rawWorkers)
37
+ throw new Error("An enabled loop requires --loop-lead and --loop-workers");
38
+ return validateLoopConfig({ mode, agents: { lead, workers: parseWorkerMapping(rawWorkers) } });
39
+ };
40
+ export const mcpSetupSelections = async (args) => {
41
+ const selected = mcpArgSelections(args);
42
+ if ((!selected.deployment || !selected.storage || !selected.bind || !selected.workspace) && !interactive())
43
+ throw new Error("mcp setup outside a TTY requires --deployment, --storage, --bind and --workspace");
44
+ if (interactive()) {
45
+ const { input, select, checkbox } = await prompts();
46
+ selected.deployment ??= await select({ message: "State service deployment", choices: [{ name: "Docker on this machine", value: "docker" }, { name: "Remote service (configure endpoint later)", value: "remote" }] });
47
+ selected.storage ??= await select({ message: "Database", choices: [{ name: "SQLite (local Docker volume)", value: "sqlite" }, { name: "PostgreSQL (multi-host production)", value: "postgres" }] });
48
+ selected.bind ??= await select({ message: "Network binding", choices: [{ name: "localhost only", value: "localhost" }, { name: "network (requires token/TLS)", value: "network" }] });
49
+ selected.workspace ??= await input({ message: "Workspace ID", default: "default", required: true });
50
+ if (!selected.providers.length)
51
+ selected.providers = await checkbox({ message: "Project-management connectors to configure", choices: ["jira", "trello", "github-issues"].map((value) => ({ name: value, value })) });
52
+ }
53
+ if (!Number.isInteger(selected.pollMs) || selected.pollMs < 1_000 || selected.pollMs > 3_600_000)
54
+ throw new Error("--poll-ms must be an integer from 1000 to 3600000");
55
+ return selected;
56
+ };
57
+ export const confirmGeneral = async (options, operation) => {
58
+ if (options.dryRun)
59
+ return false;
60
+ if (options.yes)
61
+ return true;
62
+ if (!interactive())
63
+ return false;
64
+ const { confirm } = await prompts();
65
+ return confirm({ message: `${operation} this plan?`, default: false });
66
+ };
package/dist/src/cli.js CHANGED
@@ -1,96 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { stdin, stdout } from "node:process";
3
- import { spawnSync } from "node:child_process";
4
- import { resolve, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- import { installableAdapters } from "./adapters.js";
7
- import { applyPlan, detectedAgents, doctor, formatPlan, planInit, planUninstall, selectAgents, status } from "./installer.js";
8
- import { LOOP_PROVIDERS } from "./model.js";
9
- import { loadProjectConfig, resolveProfiles, validateLoopConfig } from "./profiles.js";
2
+ import { resolvedAdapters } from "./adapters.js";
3
+ import { assertKnownOptions, flags, INIT_FLAGS, rootArg, UNINSTALL_FLAGS, UPGRADE_FLAGS, usage } from "./cli/argv.js";
4
+ import { runDoctor, runLoopCommand, runMcp, runProfile, runStatus, runUi } from "./cli/commands.js";
5
+ import { recordInstallerCli } from "./cli/loop-launcher.js";
6
+ import { initOptions } from "./cli/options.js";
7
+ import { chooseLoop, confirmGeneral, interactive } from "./cli/prompt.js";
8
+ import { applyPlan, detectedAgents, formatPlan, planInit, planUninstall, selectAgents } from "./installer.js";
10
9
  import { applyUpgrade, planUpgrade } from "./upgrade.js";
11
10
  import { packageVersion } from "./workflow.js";
12
- import { applyMcpSetup, planMcpSetup } from "./mcp-setup.js";
13
- const usage = `Usage:
14
- npx @felixdotgo/aidlc-kit init [path] [--agent <name[,name]> | --all] [--loop-mode off|local|fleet] [--loop-lead ${LOOP_PROVIDERS.join("|")}] [--loop-workers <id=provider,...>] [--yes] [--dry-run] [--force]
15
- npx @felixdotgo/aidlc-kit upgrade [path] [--loop-mode off|local|fleet] [--loop-lead ${LOOP_PROVIDERS.join("|")}] [--loop-workers <id=provider,...>] [--dry-run]
16
- npx @felixdotgo/aidlc-kit uninstall [path] [--yes] [--dry-run]
17
- npx @felixdotgo/aidlc-kit status [path]
18
- npx @felixdotgo/aidlc-kit doctor [path] [--strict]
19
- npx @felixdotgo/aidlc-kit profile validate [path]
20
- npx @felixdotgo/aidlc-kit loop start|status|resume [--task-id ID] [--root PATH] [--mode local|fleet]
21
- npx @felixdotgo/aidlc-kit mcp setup [path] [--dry-run] [--yes] [--storage sqlite|postgres] [--deployment docker|remote] [--bind localhost|network] [--workspace <id>] [--poll-ms <n>] [--enable]`;
22
- const interactive = () => Boolean(stdin.isTTY && stdout.isTTY);
11
+ /* Kept in this file: test/unit/cli.test.ts pins these prompt sources (and the upgrade boundary strings below) to src/cli.ts; the shared interactive flows live in ./cli/prompt.ts. */
23
12
  const prompts = async () => import("@inquirer/prompts");
24
- /* --- Argv helpers --------------------------------------------------------- */
25
- const value = (args, name) => {
26
- const index = args.indexOf(name);
27
- return index < 0 ? undefined : args[index + 1];
28
- };
29
- const flags = (args) => new Set(args.filter((item) => item.startsWith("--")));
30
- const valueFlags = new Set(["--agent", "--storage", "--deployment", "--bind", "--workspace", "--poll-ms", "--token-env", "--providers", "--loop-mode", "--loop-lead", "--loop-workers"]);
31
- const COMMAND_KEYWORDS = ["init", "upgrade", "uninstall", "status", "doctor", "validate", "run", "list"];
32
- const positional = (args) => {
33
- const result = [];
34
- for (let index = 0; index < args.length; index += 1) {
35
- if (valueFlags.has(args[index])) {
36
- index += 1;
37
- continue;
38
- }
39
- if (!args[index].startsWith("--"))
40
- result.push(args[index]);
41
- }
42
- return result;
43
- };
44
- const rootArg = (args, fallback = ".") => positional(args).find((item) => !COMMAND_KEYWORDS.includes(item)) ?? fallback;
45
- /* --- Loop configuration --------------------------------------------------- */
46
- const parseWorkerMapping = (rawWorkers) => {
47
- const workers = Object.create(null);
48
- for (const entry of rawWorkers.split(",")) {
49
- const parts = entry.split("=");
50
- if (parts.length !== 2 || !parts[0] || !parts[1] || Object.hasOwn(workers, parts[0]))
51
- throw new Error("Invalid or duplicate loop worker mapping");
52
- workers[parts[0]] = parts[1];
53
- }
54
- return workers;
55
- };
56
- const chooseLoop = async (args, upgrade) => {
57
- let mode = value(args, "--loop-mode");
58
- const leadArg = value(args, "--loop-lead"), workersArg = value(args, "--loop-workers");
59
- if (!mode && (leadArg || workersArg))
60
- throw new Error("--loop-lead and --loop-workers require --loop-mode");
61
- if (!mode && interactive()) {
62
- const { select } = await prompts();
63
- mode = await select({ message: "Loop execution mode", choices: [
64
- ...(upgrade ? [{ name: "Keep existing loop configuration", value: "keep" }] : []),
65
- { name: "Off", value: "off" }, { name: "Local", value: "local" }, { name: "Fleet", value: "fleet" }
66
- ] });
67
- }
68
- if (!mode || mode === "keep")
69
- return undefined;
70
- if (mode === "off") {
71
- if (leadArg || workersArg)
72
- throw new Error("Disabled loop cannot specify agents");
73
- return validateLoopConfig({ mode });
74
- }
75
- let lead = leadArg, rawWorkers = workersArg;
76
- if (interactive() && (!lead || !rawWorkers)) {
77
- const { select, input } = await prompts();
78
- lead ??= await select({ message: "Lead agent", choices: LOOP_PROVIDERS.map((name) => ({ name, value: name })) });
79
- rawWorkers ??= await input({ message: "Workers (id=provider, comma separated)", default: "codex-worker=codex,claude-worker=claude" });
80
- }
81
- if (!lead || !rawWorkers)
82
- throw new Error("An enabled loop requires --loop-lead and --loop-workers");
83
- return validateLoopConfig({ mode, agents: { lead, workers: parseWorkerMapping(rawWorkers) } });
84
- };
85
- /* --- Option builders and shared flows -------------------------------------- */
86
- const initOptions = (args) => ({
87
- root: rootArg(args),
88
- agents: (value(args, "--agent") ?? "").split(",").filter(Boolean),
89
- all: flags(args).has("--all"),
90
- dryRun: flags(args).has("--dry-run"),
91
- yes: flags(args).has("--yes"),
92
- force: flags(args).has("--force")
93
- });
13
+ /* --- Agent selection and plan preview -------------------------------------- */
94
14
  const chooseAgents = async (options) => {
95
15
  const selected = selectAgents(options);
96
16
  if (selected.length)
@@ -101,7 +21,7 @@ const chooseAgents = async (options) => {
101
21
  if (!interactive())
102
22
  throw new Error(`Unable to select an agent automatically. Use --agent <name> or --all. Detected: ${detected.join(", ") || "none"}`);
103
23
  const { checkbox } = await prompts();
104
- const choices = installableAdapters();
24
+ const choices = resolvedAdapters();
105
25
  const agents = await checkbox({
106
26
  message: "Select one or more agents",
107
27
  choices: choices.map((adapter) => ({
@@ -116,67 +36,9 @@ const preview = (operation, plan) => {
116
36
  console.log(`◆ ${operation} preview — changes have NOT been applied`);
117
37
  console.log(formatPlan(plan));
118
38
  };
119
- const confirmGeneral = async (options, operation) => {
120
- if (options.dryRun)
121
- return false;
122
- if (options.yes)
123
- return true;
124
- if (!interactive())
125
- return false;
126
- const { confirm } = await prompts();
127
- return confirm({ message: `${operation} this plan?`, default: false });
128
- };
129
- /* --- MCP setup options ----------------------------------------------------- */
130
- const mcpSetupSelections = async (args) => {
131
- const selected = {
132
- deployment: value(args, "--deployment"),
133
- storage: value(args, "--storage"),
134
- bind: value(args, "--bind"),
135
- workspace: value(args, "--workspace"),
136
- pollMs: Number(value(args, "--poll-ms") ?? 30_000),
137
- tokenEnv: value(args, "--token-env") ?? "AIDLC_MCP_TOKEN",
138
- providers: (value(args, "--providers") ?? "").split(",").filter(Boolean)
139
- };
140
- if ((!selected.deployment || !selected.storage || !selected.bind || !selected.workspace) && !interactive())
141
- throw new Error("mcp setup outside a TTY requires --deployment, --storage, --bind and --workspace");
142
- if (interactive()) {
143
- const { input, select, checkbox } = await prompts();
144
- selected.deployment ??= await select({ message: "State service deployment", choices: [{ name: "Docker on this machine", value: "docker" }, { name: "Remote service (configure endpoint later)", value: "remote" }] });
145
- selected.storage ??= await select({ message: "Database", choices: [{ name: "SQLite (local Docker volume)", value: "sqlite" }, { name: "PostgreSQL (multi-host production)", value: "postgres" }] });
146
- selected.bind ??= await select({ message: "Network binding", choices: [{ name: "localhost only", value: "localhost" }, { name: "network (requires token/TLS)", value: "network" }] });
147
- selected.workspace ??= await input({ message: "Workspace ID", default: "default", required: true });
148
- if (!selected.providers.length)
149
- selected.providers = await checkbox({ message: "Project-management connectors to configure", choices: ["jira", "trello", "github-issues"].map((value) => ({ name: value, value })) });
150
- }
151
- if (!Number.isInteger(selected.pollMs) || selected.pollMs < 1_000 || selected.pollMs > 3_600_000)
152
- throw new Error("--poll-ms must be an integer from 1000 to 3600000");
153
- return selected;
154
- };
155
- const mcpSetupOptions = async (args) => {
156
- const selected = await mcpSetupSelections(args);
157
- return { root: rootArg(args), template: join(fileURLToPath(new URL("../services/aidlc-coordination", import.meta.url))), dryRun: flags(args).has("--dry-run"), deployment: selected.deployment, storage: selected.storage, bind: selected.bind, workspace: selected.workspace, pollMs: selected.pollMs, tokenEnv: selected.tokenEnv, providers: selected.providers, enable: flags(args).has("--enable") };
158
- };
159
- /* --- Command handlers ------------------------------------------------------ */
160
- const runLoopCommand = (args) => {
161
- const action = args[0];
162
- if (!["start", "status", "resume"].includes(action))
163
- throw new Error("Expected loop start, status, or resume");
164
- const parsed = new Map();
165
- for (let index = 1; index < args.length; index += 2) {
166
- const key = args[index], entry = args[index + 1];
167
- if (!["--task-id", "--root", "--mode"].includes(key) || !entry || entry.startsWith("--") || parsed.has(key))
168
- throw new Error("Invalid or duplicate loop option");
169
- parsed.set(key, entry);
170
- }
171
- const root = resolve(parsed.get("--root") ?? ".");
172
- const script = join(root, ".agents", "aidlc", "scripts", "loop-entry.mjs");
173
- const child = spawnSync(process.execPath, [script, action, parsed.get("--task-id") ?? "", root, parsed.get("--mode") ?? ""], { stdio: "inherit" });
174
- if (child.error)
175
- throw child.error;
176
- if (child.status !== 0)
177
- process.exitCode = child.status ?? 1;
178
- };
39
+ /* --- Command handlers that consume the pinned prompts above ----------------- */
179
40
  const runInit = async (args) => {
41
+ assertKnownOptions(args, INIT_FLAGS);
180
42
  const options = { ...await chooseAgents(initOptions(args)), loop: await chooseLoop(args, false) };
181
43
  const plan = planInit(options);
182
44
  preview("INIT", plan);
@@ -192,8 +54,10 @@ const runInit = async (args) => {
192
54
  applyPlan(options.root, plan);
193
55
  console.log("Installed local AI-DLC workflow assets.");
194
56
  }
57
+ recordInstallerCli(options.root);
195
58
  };
196
59
  const runUninstall = async (args) => {
60
+ assertKnownOptions(args, UNINSTALL_FLAGS);
197
61
  const options = initOptions(args);
198
62
  const plan = planUninstall(options.root);
199
63
  preview("UNINSTALL", plan);
@@ -203,6 +67,7 @@ const runUninstall = async (args) => {
203
67
  console.log("Removed eligible managed assets; project config and state were preserved.");
204
68
  };
205
69
  const runUpgrade = async (args) => {
70
+ assertKnownOptions(args, UPGRADE_FLAGS);
206
71
  if (flags(args).has("--yes") || flags(args).has("--force"))
207
72
  throw new Error("upgrade does not support --yes or --force; apply requires a human interactive TTY confirmation");
208
73
  const root = rootArg(args);
@@ -220,49 +85,21 @@ const runUpgrade = async (args) => {
220
85
  throw new Error("Upgrade confirmation did not match the target version");
221
86
  const result = applyUpgrade(root, plan);
222
87
  console.log(`Upgrade complete. Backup: ${result.backup}; changed: ${result.changed}`);
88
+ recordInstallerCli(root);
223
89
  };
224
- const runMcpSetup = async (args) => {
225
- const options = await mcpSetupOptions(args.slice(1));
226
- const plan = planMcpSetup(options);
227
- console.log(`◆ MCP SETUP preview — ${options.dryRun ? "no files will be written" : "review before applying"}`);
228
- console.log(JSON.stringify(plan, null, 2));
229
- if (options.dryRun)
230
- return;
231
- if (!await confirmGeneral({ ...initOptions(args), root: options.root }, "Apply MCP setup"))
232
- return;
233
- applyMcpSetup(options);
234
- console.log(`MCP setup written to ${plan.target}. MCP remains ${options.enable ? "enabled" : "disabled"}; start Docker explicitly with docker compose --env-file .env -f ${join(plan.target, "compose.yaml")} up -d.`);
235
- };
90
+ const runHelp = () => console.log(usage);
91
+ const runVersion = () => console.log(packageVersion());
92
+ const REGISTRY = { init: runInit, upgrade: runUpgrade, uninstall: runUninstall, status: runStatus, doctor: runDoctor, loop: runLoopCommand, ui: runUi, mcp: runMcp, profile: runProfile, version: runVersion, help: runHelp };
236
93
  const main = async () => {
237
- const [command = "help", ...args] = process.argv.slice(2);
238
- if (command === "help" || command === "--help")
94
+ const argv = process.argv.slice(2);
95
+ if (argv.includes("--version") || argv.includes("-v"))
96
+ return void console.log(packageVersion());
97
+ if (argv.includes("--help"))
239
98
  return void console.log(usage);
240
- if (command === "loop")
241
- return void runLoopCommand(args);
242
- if (command === "status")
243
- return void console.log(status(rootArg(args)));
244
- if (command === "doctor") {
245
- const result = doctor(rootArg(args), flags(args).has("--strict"));
246
- console.log(result);
247
- if (result.startsWith("ERROR:"))
248
- process.exitCode = 1;
249
- return;
250
- }
251
- if (command === "init")
252
- return void await runInit(args);
253
- if (command === "uninstall")
254
- return void await runUninstall(args);
255
- if (command === "upgrade")
256
- return void await runUpgrade(args);
257
- if (command === "mcp" && args[0] === "setup")
258
- return void await runMcpSetup(args);
259
- if (command === "profile" && args[0] === "validate") {
260
- const root = rootArg(args.slice(1));
261
- const config = loadProjectConfig(root);
262
- const profiles = resolveProfiles(root, config.extends);
263
- console.log(`OK: ${profiles.map((item) => item.id).join(" → ")}`);
264
- return;
265
- }
266
- throw new Error(`Unknown command: ${command}`);
99
+ const [command = "help", ...args] = argv;
100
+ const handler = Object.hasOwn(REGISTRY, command) ? REGISTRY[command] : undefined;
101
+ if (!handler)
102
+ throw new Error(`Unknown command: ${command}`);
103
+ return void await handler(args);
267
104
  };
268
105
  main().catch((error) => { console.error(`aidlc-kit: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; });
package/dist/src/gate.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolveProjectPathWithoutSymlinks } from "./project-path.js";
3
- import { hasAreaVerification, hasReview, isLegacyG2Wait, nextAction, repairBounds, transitionDiagnostics, transitionTask } from "./state.js";
3
+ import { hasAreaVerification, hasReview, isLegacyG2Wait, loopPartitionStrict, nextAction, partitionDiagnostics, repairBounds, transitionDiagnostics, transitionTask } from "./state.js";
4
4
  const safeArtifactTarget = (root, path) => resolveProjectPathWithoutSymlinks(root, path, "Unsafe artifact path", "Artifact path crosses a symlink");
5
5
  const artifactContent = (root, path) => {
6
6
  if (!path)
@@ -40,6 +40,7 @@ const reviewGateDiagnostics = (root, task) => {
40
40
  if (content !== undefined)
41
41
  for (const heading of missingHeadings(content, ["## 🧩 Solution per affected area", "## 📌 Spec traceability", "## 🔗 Cross-service contracts", "## ⚠️ Risks / edge cases"]))
42
42
  diagnostics.push({ level: "ERROR", code: "DESIGN_HEADING", message: `Design is missing ${heading}` });
43
+ diagnostics.push(...partitionDiagnostics(task, { strict: loopPartitionStrict(root) }));
43
44
  return diagnostics;
44
45
  };
45
46
  const codeReviewGateDiagnostics = (root, task) => {
@@ -82,7 +83,7 @@ export const checkGate = (root, state, taskId, gate) => {
82
83
  diagnostics.push(...reviewGateDiagnostics(root, task));
83
84
  if (gate === "G2_codereview")
84
85
  diagnostics.push(...codeReviewGateDiagnostics(root, task));
85
- if (!diagnostics.length)
86
+ if (!diagnostics.some((item) => item.level === "ERROR"))
86
87
  diagnostics.push({ level: "INFO", code: "GATE_OK", message: `${gate} checks passed for ${taskId}` });
87
88
  return diagnostics;
88
89
  };
@@ -1,12 +1,8 @@
1
- import type { AgentId, FileSpec, InitOptions, LoopConfig, ManifestInventoryItem, PlannedWrite, WorkflowManifest } from "./model.js";
2
- /** Managed prompt blocks share a path across owners, so inventory keys carry the owner. */
3
- export declare const installedHashFor: (files: Record<string, ManifestInventoryItem> | undefined, spec: Pick<FileSpec, "path" | "owner" | "strategy">) => string | undefined;
4
- export declare const managedContentHash: (spec: Pick<FileSpec, "owner" | "strategy">, content: string) => string | undefined;
5
- export declare const readManifest: (root: string) => WorkflowManifest | undefined;
1
+ import type { AgentId, InitOptions, PlannedWrite } from "./model.js";
2
+ /** Manifest reading lives in the managed-planning core; this export path stays stable for existing importers. */
3
+ export { readManifest } from "./managed-planning.js";
6
4
  export declare const detectedAgents: (root: string) => AgentId[];
7
5
  export declare const selectAgents: (options: Pick<InitOptions, "root" | "agents" | "all">) => AgentId[];
8
- /** Include an explicitly selected loop config in the same preview/transaction as setup. */
9
- export declare const planLoopSelection: (plan: PlannedWrite[], selection?: LoopConfig) => void;
10
6
  /** Plans an init transaction: managed workflow files, manifest, and project-owned files. */
11
7
  export declare const planInit: (options: InitOptions) => PlannedWrite[];
12
8
  /** Applies a planned init/uninstall transaction to disk. */
@@ -14,7 +10,18 @@ export declare const applyPlan: (root: string, plan: PlannedWrite[]) => void;
14
10
  export declare const formatPlan: (plan: PlannedWrite[]) => string;
15
11
  /** Plans an uninstall transaction that removes only unchanged managed assets. */
16
12
  export declare const planUninstall: (root: string) => PlannedWrite[];
13
+ export declare const statusData: (root: string) => {
14
+ workflow: string;
15
+ installedVersion: string;
16
+ schema: string;
17
+ detectedAgents: string[];
18
+ };
17
19
  /** Reports installed workflow version, schema, and detected agents. */
18
20
  export declare const status: (root: string) => string;
19
21
  /** Validates the installed workflow manifest and local config/state. */
20
22
  export declare const doctor: (root: string, strict?: boolean) => string;
23
+ export declare const doctorData: (root: string, strict?: boolean) => {
24
+ level: "ok" | "warn" | "error";
25
+ message: string;
26
+ strict: boolean;
27
+ };