@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.
- package/DEVELOPMENT.md +14 -13
- package/README.md +37 -43
- package/dist/assets/.agents/aidlc/docker/Dockerfile.claude +7 -0
- package/dist/assets/.agents/aidlc/docker/Dockerfile.codex +9 -0
- package/dist/assets/.agents/aidlc/docker/Dockerfile.opencode +8 -0
- package/dist/assets/.agents/aidlc/orchestrator.md +2 -2
- package/dist/assets/.agents/aidlc/phase-clarify.md +1 -1
- package/dist/assets/.agents/aidlc/phase-plan.md +2 -2
- package/dist/assets/.agents/aidlc/schemas/config.schema.json +2 -1
- package/dist/assets/.agents/aidlc/schemas/state.schema.json +2 -0
- package/dist/assets/.agents/aidlc/scripts/lib/assignment-store.mjs +94 -48
- package/dist/assets/.agents/aidlc/scripts/lib/context-runtime.mjs +5 -4
- package/dist/assets/.agents/aidlc/scripts/lib/loop-authority.mjs +261 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-commit-message.mjs +45 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-daemon.mjs +206 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-drivers.mjs +100 -14
- package/dist/assets/.agents/aidlc/scripts/lib/loop-env.mjs +279 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-cli.mjs +105 -32
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-remote.mjs +17 -1
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-supervisor.mjs +24 -7
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet.mjs +42 -16
- package/dist/assets/.agents/aidlc/scripts/lib/loop-git.mjs +20 -6
- package/dist/assets/.agents/aidlc/scripts/lib/loop-holder.mjs +376 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-intake.mjs +33 -7
- package/dist/assets/.agents/aidlc/scripts/lib/loop-lease.mjs +67 -61
- package/dist/assets/.agents/aidlc/scripts/lib/loop-leases.mjs +268 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-local-workers.mjs +116 -28
- package/dist/assets/.agents/aidlc/scripts/lib/loop-runtime.mjs +114 -12
- package/dist/assets/.agents/aidlc/scripts/lib/loop-stream.mjs +117 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-local.mjs +16 -56
- package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-run-local.mjs +149 -6
- package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor.mjs +9 -1
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-docker.mjs +44 -6
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-image.mjs +64 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-log.mjs +158 -0
- package/dist/assets/.agents/aidlc/scripts/lib/repo-head.mjs +22 -0
- package/dist/assets/.agents/aidlc/scripts/lib/store.mjs +68 -2
- package/dist/assets/.agents/aidlc/scripts/lib/task-runtime-migrate.mjs +75 -0
- package/dist/assets/.agents/aidlc/scripts/lib/task-runtime.mjs +97 -0
- package/dist/assets/.agents/aidlc/scripts/loop-entry.mjs +168 -73
- package/dist/assets/.agents/aidlc/scripts/loop-gate.mjs +19 -7
- package/dist/assets/.agents/aidlc/scripts/loop.cmd +101 -0
- package/dist/assets/.agents/aidlc/scripts/loop.mjs +2 -1
- package/dist/assets/.agents/aidlc/scripts/loop.sh +71 -0
- package/dist/assets/.agents/aidlc/scripts/remote-state.mjs +10 -4
- package/dist/assets/.agents/aidlc/scripts/state.mjs +16 -8
- package/dist/assets/.agents/aidlc/templates/design.md +9 -0
- package/dist/assets/.agents/aidlc/templates/intent.md +4 -0
- package/dist/assets/.agents/aidlc/templates/workplan.md +1 -1
- package/dist/assets/.agents/skills/aidlc-plan/SKILL.md +1 -1
- package/dist/assets/ui/bulma.min.css +3 -0
- package/dist/assets/ui/changes.js +83 -0
- package/dist/assets/ui/common.js +664 -0
- package/dist/assets/ui/dashboard.css +636 -0
- package/dist/assets/ui/dashboard.js +77 -0
- package/dist/assets/ui/detail.js +159 -0
- package/dist/assets/ui/live-log.js +332 -0
- package/dist/services/aidlc-coordination/Dockerfile +11 -3
- package/dist/services/aidlc-coordination/compose.yaml +12 -0
- package/dist/services/aidlc-coordination/package-lock.json +3 -1
- package/dist/services/aidlc-coordination/package.json +9 -4
- package/dist/services/aidlc-coordination/src/lifecycle-core.mjs +68 -2
- package/dist/services/aidlc-coordination/src/repository.mjs +66 -0
- package/dist/services/aidlc-coordination/src/server.mjs +9 -3
- package/dist/services/aidlc-coordination/test/fleet.test.mjs +21 -0
- package/dist/services/aidlc-coordination/test/server-health-auth.test.mjs +67 -0
- package/dist/src/adapters.d.ts +0 -3
- package/dist/src/adapters.js +3 -5
- package/dist/src/cli/argv.d.ts +12 -0
- package/dist/src/cli/argv.js +68 -0
- package/dist/src/cli/commands.d.ts +8 -0
- package/dist/src/cli/commands.js +159 -0
- package/dist/src/cli/loop-launcher.d.ts +47 -0
- package/dist/src/cli/loop-launcher.js +322 -0
- package/dist/src/cli/options.d.ts +25 -0
- package/dist/src/cli/options.js +32 -0
- package/dist/src/cli/prompt.d.ts +9 -0
- package/dist/src/cli/prompt.js +66 -0
- package/dist/src/cli.js +28 -191
- package/dist/src/gate.js +3 -2
- package/dist/src/installer.d.ts +14 -7
- package/dist/src/installer.js +42 -143
- package/dist/src/legacy.js +1 -0
- package/dist/src/managed-content.d.ts +7 -0
- package/dist/src/managed-content.js +15 -4
- package/dist/src/managed-planning.d.ts +65 -0
- package/dist/src/managed-planning.js +219 -0
- package/dist/src/mcp-setup.js +3 -1
- package/dist/src/model/config-contracts.d.ts +65 -0
- package/dist/src/model/config-contracts.js +1 -0
- package/dist/src/model/install-contracts.d.ts +29 -0
- package/dist/src/model/install-contracts.js +1 -0
- package/dist/src/model/lifecycle-enums.d.ts +13 -0
- package/dist/src/model/lifecycle-enums.js +1 -0
- package/dist/src/model/loop.d.ts +20 -0
- package/dist/src/model/loop.js +1 -0
- package/dist/src/model/manifest.d.ts +16 -0
- package/dist/src/model/manifest.js +1 -0
- package/dist/src/model/records.d.ts +142 -0
- package/dist/src/model/records.js +1 -0
- package/dist/src/model.d.ts +11 -264
- package/dist/src/model.js +1 -1
- package/dist/src/profiles.d.ts +2 -0
- package/dist/src/profiles.js +53 -9
- package/dist/src/state/archive-record.d.ts +9 -0
- package/dist/src/state/archive-record.js +26 -0
- package/dist/src/state/commands.d.ts +18 -0
- package/dist/src/state/commands.js +27 -0
- package/dist/src/state/domain.d.ts +9 -0
- package/dist/src/state/domain.js +9 -0
- package/dist/src/state/guards.d.ts +7 -0
- package/dist/src/state/guards.js +17 -0
- package/dist/src/state/io.d.ts +10 -0
- package/dist/src/state/io.js +35 -0
- package/dist/src/state/items.d.ts +3 -4
- package/dist/src/state/items.js +58 -57
- package/dist/src/state/lessons.js +14 -7
- package/dist/src/state/lifecycle.js +2 -2
- package/dist/src/state/lock.js +7 -6
- package/dist/src/state/memory.js +24 -12
- package/dist/src/state/{archive.js → migrate.js} +7 -7
- package/dist/src/state/partition.d.ts +15 -0
- package/dist/src/state/partition.js +90 -0
- package/dist/src/state/paths.d.ts +11 -0
- package/dist/src/state/paths.js +12 -0
- package/dist/src/state/render.js +9 -6
- package/dist/src/state/store.js +9 -15
- package/dist/src/state/transitions.js +9 -7
- package/dist/src/state/validate.js +55 -7
- package/dist/src/state.d.ts +16 -11
- package/dist/src/state.js +16 -11
- package/dist/src/task-ui.d.ts +33 -0
- package/dist/src/task-ui.js +33 -0
- package/dist/src/ui/assets.d.ts +14 -0
- package/dist/src/ui/assets.js +45 -0
- package/dist/src/ui/changes.d.ts +83 -0
- package/dist/src/ui/changes.js +498 -0
- package/dist/src/ui/containers.d.ts +71 -0
- package/dist/src/ui/containers.js +214 -0
- package/dist/src/ui/daemon.d.ts +75 -0
- package/dist/src/ui/daemon.js +232 -0
- package/dist/src/ui/data.d.ts +42 -0
- package/dist/src/ui/data.js +89 -0
- package/dist/src/ui/docs.d.ts +13 -0
- package/dist/src/ui/docs.js +39 -0
- package/dist/src/ui/files.d.ts +3 -0
- package/dist/src/ui/files.js +20 -0
- package/dist/src/ui/http.d.ts +14 -0
- package/dist/src/ui/http.js +80 -0
- package/dist/src/ui/live.d.ts +5 -0
- package/dist/src/ui/live.js +43 -0
- package/dist/src/ui/markdown.d.ts +2 -0
- package/dist/src/ui/markdown.js +127 -0
- package/dist/src/ui/options.d.ts +48 -0
- package/dist/src/ui/options.js +81 -0
- package/dist/src/ui/security.d.ts +34 -0
- package/dist/src/ui/security.js +38 -0
- package/dist/src/ui/server.d.ts +39 -0
- package/dist/src/ui/server.js +347 -0
- package/dist/src/ui/sse.d.ts +14 -0
- package/dist/src/ui/sse.js +281 -0
- package/dist/src/ui/views/board.d.ts +7 -0
- package/dist/src/ui/views/board.js +131 -0
- package/dist/src/ui/views/changes.d.ts +19 -0
- package/dist/src/ui/views/changes.js +154 -0
- package/dist/src/ui/views/components.d.ts +56 -0
- package/dist/src/ui/views/components.js +113 -0
- package/dist/src/ui/views/docs.d.ts +15 -0
- package/dist/src/ui/views/docs.js +64 -0
- package/dist/src/ui/views/layout.d.ts +44 -0
- package/dist/src/ui/views/layout.js +233 -0
- package/dist/src/ui/views/task.d.ts +25 -0
- package/dist/src/ui/views/task.js +320 -0
- package/dist/src/upgrade-board.js +10 -10
- package/dist/src/upgrade.d.ts +0 -1
- package/dist/src/upgrade.js +24 -85
- package/dist/src/workflow.d.ts +4 -0
- package/dist/src/workflow.js +36 -4
- package/docs/README.md +41 -22
- package/docs/architecture.md +101 -37
- package/docs/command-reference.md +289 -52
- package/docs/configuration.md +70 -30
- package/docs/coordination-service.md +91 -30
- package/docs/design-decisions.md +87 -0
- package/docs/development.md +124 -49
- package/docs/loop-local-run.md +228 -0
- package/docs/loop-runbook.md +214 -0
- package/docs/operating-workflow.md +55 -42
- package/docs/testing-and-release.md +72 -49
- package/docs/troubleshooting.md +221 -0
- package/package.json +4 -1
- package/dist/src/ledger.d.ts +0 -22
- package/dist/src/ledger.js +0 -119
- package/dist/src/state/shared.d.ts +0 -29
- package/dist/src/state/shared.js +0 -71
- package/dist/src/tracker.d.ts +0 -36
- package/dist/src/tracker.js +0 -44
- /package/dist/src/state/{archive.d.ts → migrate.d.ts} +0 -0
|
@@ -41,18 +41,30 @@ async function main() {
|
|
|
41
41
|
console.log(review.view.trimEnd());
|
|
42
42
|
console.log(`Review identity: ${JSON.stringify({ taskId, phase: review.phase, gate: review.gate, taskHash: review.taskHash, artifacts: review.artifacts, integrationCommit: review.commit })}`);
|
|
43
43
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
44
|
-
let answer; try { answer = await rl.question("Type approve to approve this exact review: "); } finally { rl.close(); }
|
|
45
|
-
|
|
44
|
+
let answer; try { answer = await rl.question("Type approve to approve this exact review, or type feedback to send guidance back to the loop: "); } finally { rl.close(); }
|
|
45
|
+
const trimmed = answer.trim();
|
|
46
|
+
if (!trimmed) throw new Error("Review was not approved");
|
|
46
47
|
release = acquireStateLock(root);
|
|
47
48
|
try {
|
|
48
49
|
const state = loadState(root), fresh = snapshot(root, taskId, state);
|
|
49
50
|
if (digest({ ...fresh, view: undefined }) !== digest({ ...review, view: undefined })) throw new Error("Review changed while waiting for approval; inspect the new review");
|
|
50
|
-
const
|
|
51
|
+
const task = state.tasks[taskId];
|
|
51
52
|
const reviewHash = digest({ ...review, view: undefined });
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if (trimmed === "approve") {
|
|
54
|
+
const checkpoint = readLocalGateCheckpoint(root, task);
|
|
55
|
+
const outcome = approveAndAdvance(root, state, taskId, review.gate, `Trusted host terminal: approve (${reviewHash})`);
|
|
56
|
+
saveState(root, state);
|
|
57
|
+
writeLocalGateReceipt(root, checkpoint, outcome.task, reviewHash);
|
|
58
|
+
console.log(`Approved ${review.gate}; next action: ${JSON.stringify(outcome.nextAction)}`);
|
|
59
|
+
} else {
|
|
60
|
+
const recordedAt = new Date().toISOString();
|
|
61
|
+
task.evidence.push({ kind: "diagnostic", result: "pass", source: `Trusted host terminal: feedback (${reviewHash})`, detail: `Cancelled gate wait at ${task.gate}: ${trimmed}`, recordedAt });
|
|
62
|
+
delete task.legacyG2Wait;
|
|
63
|
+
task.status = "active";
|
|
64
|
+
task.updatedAt = recordedAt;
|
|
65
|
+
saveState(root, state);
|
|
66
|
+
console.log(`Feedback recorded at ${review.gate}; the loop will revise the review and re-present the gate`);
|
|
67
|
+
}
|
|
56
68
|
} finally { release(); }
|
|
57
69
|
}
|
|
58
70
|
main().catch((error) => { console.error(`loop-gate: ${error.message}`); process.exitCode = 1; });
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem AI-DLC loop launcher (managed by aidlc-kit; upgrades overwrite local edits).
|
|
3
|
+
rem .agents\aidlc\scripts\loop.cmd [command] [options]
|
|
4
|
+
rem Edit .agents\config.json ("loop") and .agents\loop.env, then run "loop.cmd up".
|
|
5
|
+
rem The script only locates the aidlc-kit CLI and forwards to "aidlc-kit loop <command>";
|
|
6
|
+
rem it never installs or upgrades anything. Local workers need Docker Desktop; the loop is
|
|
7
|
+
rem verified on Linux, so treat native Windows as best effort.
|
|
8
|
+
setlocal
|
|
9
|
+
for %%I in ("%~dp0..\..\..") do set "ROOT=%%~fI"
|
|
10
|
+
|
|
11
|
+
where node >nul 2>nul
|
|
12
|
+
if errorlevel 1 (
|
|
13
|
+
echo loop.cmd: Node.js 20.17 or newer is required ^(https://nodejs.org^) 1>&2
|
|
14
|
+
exit /b 1
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
set "CMD=%~1"
|
|
18
|
+
rem Options without a command run the default command: "loop.cmd --open" = "loop.cmd up --open".
|
|
19
|
+
if "%CMD%"=="" goto default_cmd
|
|
20
|
+
if /i "%CMD%"=="-h" goto named_cmd
|
|
21
|
+
if /i "%CMD%"=="--help" goto named_cmd
|
|
22
|
+
if "%CMD:~0,1%"=="-" goto default_cmd
|
|
23
|
+
:named_cmd
|
|
24
|
+
shift
|
|
25
|
+
goto args
|
|
26
|
+
:default_cmd
|
|
27
|
+
set "CMD=up"
|
|
28
|
+
:args
|
|
29
|
+
set "ARGS="
|
|
30
|
+
:collect
|
|
31
|
+
if "%~1"=="" goto dispatch
|
|
32
|
+
set ARGS=%ARGS% %1
|
|
33
|
+
shift
|
|
34
|
+
goto collect
|
|
35
|
+
|
|
36
|
+
:dispatch
|
|
37
|
+
if /i "%CMD%"=="help" goto usage
|
|
38
|
+
if /i "%CMD%"=="-h" goto usage
|
|
39
|
+
if /i "%CMD%"=="--help" goto usage
|
|
40
|
+
if /i "%CMD%"=="check" set "CMD=env"
|
|
41
|
+
if /i "%CMD%"=="logs" goto logs
|
|
42
|
+
for %%C in (up down stop status env) do if /i "%CMD%"=="%%C" goto run
|
|
43
|
+
echo loop.cmd: unknown command: %CMD% 1>&2
|
|
44
|
+
call :usage 1>&2
|
|
45
|
+
exit /b 2
|
|
46
|
+
|
|
47
|
+
:run
|
|
48
|
+
call :cli loop %CMD% --root "%ROOT%" %ARGS%
|
|
49
|
+
exit /b %ERRORLEVEL%
|
|
50
|
+
|
|
51
|
+
:cli
|
|
52
|
+
if exist "%ROOT%\node_modules\.bin\aidlc-kit.cmd" (
|
|
53
|
+
call "%ROOT%\node_modules\.bin\aidlc-kit.cmd" %*
|
|
54
|
+
goto :eof
|
|
55
|
+
)
|
|
56
|
+
set "RECORDED_CLI="
|
|
57
|
+
for /f "usebackq delims=" %%P in (`node -e "try{const r=require(process.argv[1]);if(typeof r.cli==='string'&&require('fs').statSync(r.cli).isFile())process.stdout.write(r.cli)}catch{}" "%ROOT%\.agents\data\loop\cli.json" 2^>nul`) do set "RECORDED_CLI=%%P"
|
|
58
|
+
if defined RECORDED_CLI (
|
|
59
|
+
node "%RECORDED_CLI%" %*
|
|
60
|
+
goto :eof
|
|
61
|
+
)
|
|
62
|
+
where aidlc-kit >nul 2>nul
|
|
63
|
+
if not errorlevel 1 (
|
|
64
|
+
call aidlc-kit %*
|
|
65
|
+
goto :eof
|
|
66
|
+
)
|
|
67
|
+
set "VERSION=latest"
|
|
68
|
+
for /f "usebackq delims=" %%V in (`node -p "require(process.argv[1]).packageVersion" "%ROOT%\.agents\aidlc\manifest.json" 2^>nul`) do set "VERSION=%%V"
|
|
69
|
+
where npx >nul 2>nul
|
|
70
|
+
if not errorlevel 1 (
|
|
71
|
+
echo loop.cmd: aidlc-kit was not found in this project or recorded by init/upgrade; trying npx --no-install ^(if it fails: npm install --save-dev @felixdotgo/aidlc-kit@%VERSION%, or rerun init/upgrade from the package or .tgz you want^) 1>&2
|
|
72
|
+
call npx --no-install @felixdotgo/aidlc-kit %*
|
|
73
|
+
goto :eof
|
|
74
|
+
)
|
|
75
|
+
echo loop.cmd: the aidlc-kit CLI was not found: install it in this project ^(npm install --save-dev @felixdotgo/aidlc-kit@%VERSION%^), or rerun init/upgrade from the package or .tgz you want 1>&2
|
|
76
|
+
exit /b 1
|
|
77
|
+
|
|
78
|
+
:logs
|
|
79
|
+
set "LOG=%ROOT%\.agents\data\loop\daemon.log"
|
|
80
|
+
if exist "%LOG%" goto follow_log
|
|
81
|
+
echo No daemon log yet: "%LOG%"
|
|
82
|
+
exit /b 0
|
|
83
|
+
:follow_log
|
|
84
|
+
powershell -NoProfile -Command "Get-Content -LiteralPath $env:LOG -Tail 200 -Wait"
|
|
85
|
+
exit /b %ERRORLEVEL%
|
|
86
|
+
|
|
87
|
+
:usage
|
|
88
|
+
echo Usage: .agents\aidlc\scripts\loop.cmd [command] [options]
|
|
89
|
+
echo.
|
|
90
|
+
echo Commands:
|
|
91
|
+
echo up check the environment, start the loop daemon, and serve the dashboard (default)
|
|
92
|
+
echo options: --loop-env PATH --port 4173 --host 127.0.0.1 --open --no-ui
|
|
93
|
+
echo down stop the dashboard started by "up" and the loop daemon (--now cancels the running turn)
|
|
94
|
+
echo stop stop only the loop daemon (--now cancels the running turn)
|
|
95
|
+
echo status daemon, dashboard, and environment at a glance (--json)
|
|
96
|
+
echo check show providers, the env file, and every variable the loop needs (--json)
|
|
97
|
+
echo logs follow the daemon log
|
|
98
|
+
echo help show this help
|
|
99
|
+
echo.
|
|
100
|
+
echo Configure .agents\config.json ("loop" section) and .agents\loop.env (model and API keys).
|
|
101
|
+
exit /b 0
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { loopStatus, runLoop } from "./lib/loop-runtime.mjs";
|
|
4
4
|
import { LOOP_PROVIDERS, runAgentTurn } from "./lib/loop-drivers.mjs";
|
|
5
|
+
import { streamLog } from "./lib/loop-stream.mjs";
|
|
5
6
|
import { requireLocalAuthority } from "./lib/authority.mjs";
|
|
6
7
|
|
|
7
8
|
function parse(argv) {
|
|
@@ -27,7 +28,7 @@ function parse(argv) {
|
|
|
27
28
|
try {
|
|
28
29
|
requireLocalAuthority();
|
|
29
30
|
const options = parse(process.argv.slice(2));
|
|
30
|
-
const result = options.command === "status" ? loopStatus(options.root, options.taskId) : await runLoop({ root: options.root, taskId: options.taskId, agent: options.agent, mode: options.command, limits: options.limits, driver: runAgentTurn, preflight: true, onEvent: (event) => { if (event?.type === "diagnostic") console.error(event.message); if (event?.type === "resume" || event?.type === "context_changed") console.error(JSON.stringify(event)); } });
|
|
31
|
+
const result = options.command === "status" ? loopStatus(options.root, options.taskId) : await runLoop({ root: options.root, taskId: options.taskId, agent: options.agent, mode: options.command, limits: options.limits, driver: runAgentTurn, preflight: true, onEvent: (event) => { streamLog(event); if (event?.type === "diagnostic") console.error(event.message); if (event?.type === "resume" || event?.type === "context_changed") console.error(JSON.stringify(event)); } });
|
|
31
32
|
console.log(JSON.stringify({ ok: true, result }, null, 2));
|
|
32
33
|
if (result.status && !["complete", "gate"].includes(result.status)) process.exitCode = 2;
|
|
33
34
|
} catch (error) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# AI-DLC loop launcher (managed by aidlc-kit; upgrades overwrite local edits).
|
|
3
|
+
#
|
|
4
|
+
# .agents/aidlc/scripts/loop.sh [command] [options]
|
|
5
|
+
#
|
|
6
|
+
# Edit .agents/config.json ("loop") and .agents/loop.env, then run `loop.sh up`.
|
|
7
|
+
# The script only locates the aidlc-kit CLI and forwards to `aidlc-kit loop <command>`;
|
|
8
|
+
# it never installs or upgrades anything.
|
|
9
|
+
set -eu
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
12
|
+
ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)
|
|
13
|
+
|
|
14
|
+
usage() {
|
|
15
|
+
cat <<'EOF'
|
|
16
|
+
Usage: .agents/aidlc/scripts/loop.sh [command] [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
up check the environment, start the loop daemon, and serve the dashboard (default)
|
|
20
|
+
options: --loop-env PATH --port 4173 --host 127.0.0.1 --open --no-ui
|
|
21
|
+
down stop the dashboard started by `up` and the loop daemon (--now cancels the running turn)
|
|
22
|
+
stop stop only the loop daemon (--now cancels the running turn)
|
|
23
|
+
status daemon, dashboard, and environment at a glance (--json)
|
|
24
|
+
check show providers, the env file, and every variable the loop needs (--json)
|
|
25
|
+
logs follow the daemon log
|
|
26
|
+
help show this help
|
|
27
|
+
|
|
28
|
+
Configure:
|
|
29
|
+
.agents/config.json "loop": { "mode": "local", "agents": { "lead": "<provider>", "workers": { "worker-a": "<provider>" } } }
|
|
30
|
+
.agents/loop.env model and API keys (git-ignored); values your tools already store are detected
|
|
31
|
+
EOF
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
command -v node >/dev/null 2>&1 || { echo "loop.sh: Node.js 20.17 or newer is required (https://nodejs.org)" >&2; exit 1; }
|
|
35
|
+
|
|
36
|
+
# The installed workflow version, so hints name the matching package.
|
|
37
|
+
VERSION=$(node -p 'require(process.argv[1]).packageVersion' "$ROOT/.agents/aidlc/manifest.json" 2>/dev/null || echo latest)
|
|
38
|
+
INSTALL_HINT="install it in this project (npm install --save-dev @felixdotgo/aidlc-kit@$VERSION), or rerun init/upgrade from the package or .tgz you want"
|
|
39
|
+
# The CLI that installed this workflow (recorded by init/upgrade), if it still exists.
|
|
40
|
+
RECORDED_CLI=$(node -e 'try{const r=require(process.argv[1]);if(typeof r.cli==="string"&&require("fs").statSync(r.cli).isFile())process.stdout.write(r.cli)}catch{}' "$ROOT/.agents/data/loop/cli.json" 2>/dev/null || true)
|
|
41
|
+
|
|
42
|
+
run_cli() {
|
|
43
|
+
if [ -x "$ROOT/node_modules/.bin/aidlc-kit" ]; then exec "$ROOT/node_modules/.bin/aidlc-kit" "$@"; fi
|
|
44
|
+
if [ -n "$RECORDED_CLI" ]; then exec node "$RECORDED_CLI" "$@"; fi
|
|
45
|
+
if command -v aidlc-kit >/dev/null 2>&1; then exec aidlc-kit "$@"; fi
|
|
46
|
+
if command -v npx >/dev/null 2>&1; then
|
|
47
|
+
echo "loop.sh: aidlc-kit was not found in this project or recorded by init/upgrade; trying npx --no-install (if it fails: $INSTALL_HINT)" >&2
|
|
48
|
+
exec npx --no-install @felixdotgo/aidlc-kit "$@"
|
|
49
|
+
fi
|
|
50
|
+
echo "loop.sh: the aidlc-kit CLI was not found: $INSTALL_HINT" >&2
|
|
51
|
+
exit 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Options without a command run the default command: `loop.sh --open` = `loop.sh up --open`.
|
|
55
|
+
case "${1:-up}" in
|
|
56
|
+
-h|--help) cmd=help; shift ;;
|
|
57
|
+
-*) cmd=up ;;
|
|
58
|
+
*) cmd=${1:-up}; [ $# -gt 0 ] && shift ;;
|
|
59
|
+
esac
|
|
60
|
+
|
|
61
|
+
case "$cmd" in
|
|
62
|
+
up|down|stop|status) run_cli loop "$cmd" --root "$ROOT" "$@" ;;
|
|
63
|
+
check|env) run_cli loop env --root "$ROOT" "$@" ;;
|
|
64
|
+
logs)
|
|
65
|
+
log="$ROOT/.agents/data/loop/daemon.log"
|
|
66
|
+
if [ ! -f "$log" ]; then echo "No daemon log yet: $log"; exit 0; fi
|
|
67
|
+
exec tail -n 200 -f "$log"
|
|
68
|
+
;;
|
|
69
|
+
help|-h|--help) usage ;;
|
|
70
|
+
*) echo "loop.sh: unknown command: $cmd" >&2; usage >&2; exit 2 ;;
|
|
71
|
+
esac
|
|
@@ -5,7 +5,9 @@ import { readFile } from "node:fs/promises";
|
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { createRemoteReviewerClient, createRemoteStateClient, featureConfig, remoteConfig, reviewerConfig } from "./lib/remote-state-client.mjs";
|
|
7
7
|
import { compileContext } from "./lib/context-runtime.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { explainFleetConflict } from "./lib/loop-holder.mjs";
|
|
9
|
+
import { checkGate, closeTask, handoffTask, nextAction, readyTaskItems, recordLesson, recordNoLessons, recordTaskCommit, reopenTask, transitionTask, validateState } from "./lib/store.mjs";
|
|
10
|
+
import { headCommit } from "./lib/repo-head.mjs";
|
|
9
11
|
|
|
10
12
|
const parse = (argv) => {
|
|
11
13
|
const positionals = []; const options = new Map();
|
|
@@ -54,9 +56,9 @@ export const runRemoteState = async (argv, client, root, git = {}) => {
|
|
|
54
56
|
required(id, "task id");
|
|
55
57
|
const allowed = {
|
|
56
58
|
"task show": [], "task next": [], "task ready": [], "gate check": ["--gate"], "phase context": ["--phase", "--mode", "--item"],
|
|
57
|
-
"task create": ["--title", "--type", "--language", "--risk", "--area", "--feature"],
|
|
59
|
+
"task create": ["--title", "--type", "--language", "--risk", "--area", "--feature", "--base-commit"],
|
|
58
60
|
"task status": ["--status", "--mode", "--reason", "--source"], "task item": ["--status", "--label", "--depends-on", "--owns"],
|
|
59
|
-
"task transition": ["--to", "--mode", "--reason", "--source"],
|
|
61
|
+
"task transition": ["--to", "--mode", "--reason", "--source", "--final-commit"],
|
|
60
62
|
"task handoff": ["--kind", "--reason", "--source"], "task close": ["--reason", "--source"], "task reopen": ["--to", "--reason", "--source"],
|
|
61
63
|
"decision set": ["--status", "--label", "--resolution"],
|
|
62
64
|
"evidence add": ["--kind", "--result", "--gate", "--area", "--source", "--detail"],
|
|
@@ -118,6 +120,8 @@ export const runRemoteState = async (argv, client, root, git = {}) => {
|
|
|
118
120
|
areas: (option("--area") ?? "root").split(",").map((item) => item.trim()).filter(Boolean), branch: "—",
|
|
119
121
|
artifacts: { intent: `.agents/data/tasks/${id}/intent.md`, design: `.agents/data/tasks/${id}/design.md`, workplan: `.agents/data/tasks/${id}/workplan.md` },
|
|
120
122
|
decisions: [], tasks: [], evidence: [], createdAt: recordedAt, updatedAt: recordedAt };
|
|
123
|
+
const baseCommit = option("--base-commit") ?? headCommit(root);
|
|
124
|
+
if (baseCommit !== undefined) recordTaskCommit(task, "baseCommit", baseCommit);
|
|
121
125
|
} else {
|
|
122
126
|
if (!previous) throw new Error(`Unknown task: ${id}`);
|
|
123
127
|
task = structuredClone(previous);
|
|
@@ -161,6 +165,7 @@ export const runRemoteState = async (argv, client, root, git = {}) => {
|
|
|
161
165
|
if (option("--mode") !== "audited") throw new Error("Transition requires --mode audited");
|
|
162
166
|
const reason = required(option("--reason"), "--reason"); const source = required(option("--source"), "--source");
|
|
163
167
|
const target = required(option("--to"), "--to");
|
|
168
|
+
if (target !== "done" && option("--final-commit") !== undefined) throw new Error("--final-commit is only accepted with --to done");
|
|
164
169
|
if (target === "done" && task.featureId) {
|
|
165
170
|
const manifest = await client.featureGet(task.featureId);
|
|
166
171
|
const member = manifest?.feature?.tasks?.find((entry) => entry.id === id);
|
|
@@ -171,6 +176,7 @@ export const runRemoteState = async (argv, client, root, git = {}) => {
|
|
|
171
176
|
const clone = structuredClone(state); clone.tasks[id] = task;
|
|
172
177
|
transitionTask(clone, id, target);
|
|
173
178
|
task = clone.tasks[id];
|
|
179
|
+
if (target === "done") { const finalCommit = option("--final-commit") ?? headCommit(root); if (finalCommit !== undefined) recordTaskCommit(task, "finalCommit", finalCommit); }
|
|
174
180
|
task.evidence.push({ kind: "diagnostic", result: "pass", source, detail: `Audited transition ${previous.phase} → ${target}: ${reason}`, recordedAt: now() });
|
|
175
181
|
} else if (route === "decision set") {
|
|
176
182
|
const status = required(option("--status"), "--status");
|
|
@@ -307,5 +313,5 @@ if (isEntry) {
|
|
|
307
313
|
const result = isReview ? await runRemoteReview(argv, createRemoteReviewerClient(config), config) : argv[0] === "feature" ? await runRemoteFeature(argv, createRemoteStateClient(config)) : argv[0] === "run" ? await runRemoteLease(argv, createRemoteStateClient(config)) : await runRemoteState(argv, createRemoteStateClient(config), config.projectRoot, config);
|
|
308
314
|
console.log(JSON.stringify(result, null, 2));
|
|
309
315
|
if (!result.ok) process.exitCode = 1;
|
|
310
|
-
} catch (error) { console.error(JSON.stringify({ ok: false, error: error.message, code: error.code, currentRevision: error.current?.revision }, null, 2)); process.exitCode = 1; }
|
|
316
|
+
} catch (error) { console.error(JSON.stringify({ ok: false, error: error.code === "FLEET_CONFLICT" ? explainFleetConflict(error.message) : error.message, code: error.code, currentRevision: error.current?.revision }, null, 2)); process.exitCode = 1; }
|
|
311
317
|
}
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
import { acquireStateLock, approveAndAdvance, checkGate, closeTask, failEntry, handoffTask, listTaskSummaries, loadMemoryRegistry, loadState, loadTask, migrateState, nextAction, option, parseArguments, promoteAgenticMemory, rebuildLessonIndex, recordLesson, recordNoLessons, renderViews, reopenTask, retireAgenticMemory, rootOption, saveState, searchLessons, supersedeTask, transitionTask, validateArguments } from "./lib/runtime.mjs";
|
|
7
7
|
import { readLiveLease } from "./lib/loop-lease.mjs";
|
|
8
8
|
import { listFeatures, loadFeature, readyFeatureTasks, saveFeature } from "./lib/feature-store.mjs";
|
|
9
|
-
import { readyTaskItems } from "./lib/store.mjs";
|
|
9
|
+
import { readyTaskItems, recordTaskCommit } from "./lib/store.mjs";
|
|
10
|
+
import { headCommit } from "./lib/repo-head.mjs";
|
|
10
11
|
import { requireLocalAuthority } from "./lib/authority.mjs";
|
|
11
12
|
|
|
12
13
|
try {
|
|
13
14
|
requireLocalAuthority();
|
|
14
15
|
const raw = process.argv.slice(2);
|
|
15
|
-
const valueOptions = ["--root", "--status", "--limit", "--cursor", "--query", "--title", "--type", "--language", "--risk", "--area", "--mode", "--reason", "--source", "--to", "--kind", "--successor", "--label", "--resolution", "--result", "--gate", "--detail", "--summary", "--prevention", "--example", "--promotion", "--guidance", "--phase", "--priority", "--source-task", "--source-lesson", "--approved-by", "--switch-from", "--feature", "--depends-on", "--branch", "--owns"];
|
|
16
|
+
const valueOptions = ["--root", "--status", "--limit", "--cursor", "--query", "--title", "--type", "--language", "--risk", "--area", "--mode", "--reason", "--source", "--to", "--kind", "--successor", "--label", "--resolution", "--result", "--gate", "--detail", "--summary", "--prevention", "--example", "--promotion", "--guidance", "--phase", "--priority", "--source-task", "--source-lesson", "--approved-by", "--switch-from", "--feature", "--depends-on", "--branch", "--owns", "--base-commit", "--final-commit"];
|
|
16
17
|
const parsed = parseArguments(raw, { valueOptions, booleanOptions: ["--include-archive"] });
|
|
17
18
|
const args = parsed.positionals;
|
|
18
19
|
const [group, action, rawId] = args;
|
|
@@ -24,9 +25,9 @@ const schemas = {
|
|
|
24
25
|
"task find": { minPositionals: 2, valueOptions: ["--query", "--status", "--limit", "--cursor"], booleanOptions: ["--include-archive"], requiredOptions: ["--query"] },
|
|
25
26
|
"task next": { minPositionals: 3 },
|
|
26
27
|
"task ready": { minPositionals: 3 },
|
|
27
|
-
"task create": { minPositionals: 3, valueOptions: ["--title", "--type", "--language", "--risk", "--area", "--switch-from", "--feature", "--depends-on"], requiredOptions: ["--title"] },
|
|
28
|
-
"task transition": { minPositionals: 3, valueOptions: ["--mode", "--reason", "--source", "--to"], requiredOptions: ["--mode", "--reason", "--source", "--to"], usage: "task transition requires --mode audited, --reason, --source, and --to" },
|
|
29
|
-
"task archive": { minPositionals: 3, valueOptions: ["--reason", "--source"], requiredOptions: ["--reason", "--source"] },
|
|
28
|
+
"task create": { minPositionals: 3, valueOptions: ["--title", "--type", "--language", "--risk", "--area", "--switch-from", "--feature", "--depends-on", "--base-commit"], requiredOptions: ["--title"] },
|
|
29
|
+
"task transition": { minPositionals: 3, valueOptions: ["--mode", "--reason", "--source", "--to", "--final-commit"], requiredOptions: ["--mode", "--reason", "--source", "--to"], usage: "task transition requires --mode audited, --reason, --source, and --to" },
|
|
30
|
+
"task archive": { minPositionals: 3, valueOptions: ["--reason", "--source", "--final-commit"], requiredOptions: ["--reason", "--source"] },
|
|
30
31
|
"task handoff": { minPositionals: 3, valueOptions: ["--kind", "--reason", "--source"], requiredOptions: ["--kind", "--reason", "--source"] },
|
|
31
32
|
"task close": { minPositionals: 3, valueOptions: ["--reason", "--source"], requiredOptions: ["--reason", "--source"] },
|
|
32
33
|
"task supersede": { minPositionals: 3, valueOptions: ["--successor", "--reason", "--source"], requiredOptions: ["--successor", "--reason", "--source"] },
|
|
@@ -75,6 +76,7 @@ const summaryOptions = (query) => ({
|
|
|
75
76
|
cursor: numberOption("--cursor", 0)
|
|
76
77
|
});
|
|
77
78
|
const warnings = [];
|
|
79
|
+
const recordFinalCommit = (task) => { const sha = option(parsed, "--final-commit") ?? headCommit(root); if (sha !== undefined) recordTaskCommit(task, "finalCommit", sha); };
|
|
78
80
|
const derivedStep = (code, hint, step) => { try { step(); } catch (error) { warnings.push({ code, message: error instanceof Error ? error.message : String(error), hint }); } };
|
|
79
81
|
const persist = (tasks, rebuildLessons = false) => {
|
|
80
82
|
const archived = saveState(root, state);
|
|
@@ -158,6 +160,8 @@ if (group === "state" && action === "migrate") {
|
|
|
158
160
|
artifacts: { intent: `.agents/data/tasks/${id}/intent.md`, design: `.agents/data/tasks/${id}/design.md`, workplan: `.agents/data/tasks/${id}/workplan.md` },
|
|
159
161
|
decisions: [], tasks: [], evidence: [], createdAt: recordedAt, updatedAt: recordedAt
|
|
160
162
|
};
|
|
163
|
+
const baseCommit = option(parsed, "--base-commit") ?? headCommit(root);
|
|
164
|
+
if (baseCommit !== undefined) recordTaskCommit(task, "baseCommit", baseCommit);
|
|
161
165
|
const acknowledgedTasks = acknowledged.map((ack) => state.tasks[ack]);
|
|
162
166
|
for (const prior of acknowledgedTasks) {
|
|
163
167
|
prior.evidence.push({ kind: "diagnostic", result: "pass", source: `task create ${id}`, detail: `Switch acknowledged: created ${id} while ${prior.id} was actionable; ${prior.id} keeps its state — resume with task next ${prior.id}`, recordedAt });
|
|
@@ -172,13 +176,16 @@ if (group === "state" && action === "migrate") {
|
|
|
172
176
|
} else if (group === "task" && action === "transition") {
|
|
173
177
|
const mode = option(parsed, "--mode"); const reason = option(parsed, "--reason"); const source = option(parsed, "--source");
|
|
174
178
|
if (mode !== "audited" || !reason?.trim() || !source?.trim()) throw new Error("task transition requires --mode audited, --reason, and --source");
|
|
175
|
-
const from = state.tasks[id]?.phase; const target = option(parsed, "--to"); if (target === "done") requireFeaturePrerequisites(state.tasks[id]);
|
|
179
|
+
const from = state.tasks[id]?.phase; const target = option(parsed, "--to"); if (target === "done") requireFeaturePrerequisites(state.tasks[id]);
|
|
180
|
+
if (target !== "done" && option(parsed, "--final-commit") !== undefined) throw new Error("--final-commit is only accepted with --to done");
|
|
181
|
+
const task = transitionTask(state, id, target);
|
|
182
|
+
if (target === "done") recordFinalCommit(task);
|
|
176
183
|
task.evidence.push({ kind: "diagnostic", result: "pass", source, detail: `Audited transition ${from} → ${target}: ${reason}`, recordedAt: now() });
|
|
177
184
|
persist([task]); respond(task);
|
|
178
185
|
} else if (group === "task" && action === "archive") {
|
|
179
186
|
const reason = option(parsed, "--reason"); const source = option(parsed, "--source");
|
|
180
187
|
if (!reason?.trim() || !source?.trim()) throw new Error("task archive requires --reason and --source");
|
|
181
|
-
requireFeaturePrerequisites(state.tasks[id]); const task = transitionTask(state, id, "done");
|
|
188
|
+
requireFeaturePrerequisites(state.tasks[id]); const task = transitionTask(state, id, "done"); recordFinalCommit(task);
|
|
182
189
|
task.evidence.push({ kind: "diagnostic", result: "pass", source, detail: `Archive to done: ${reason}`, recordedAt: now() });
|
|
183
190
|
persist([task], true); respond(task);
|
|
184
191
|
} else if (group === "task" && action === "handoff") {
|
|
@@ -270,7 +277,8 @@ if (group === "state" && action === "migrate") {
|
|
|
270
277
|
} else if (group === "gate" && action === "approve") {
|
|
271
278
|
const gate = option(parsed, "--gate"); const source = option(parsed, "--source");
|
|
272
279
|
if (!id || !gate || !source) throw new Error("gate approve requires active task id, --gate, and explicit --source");
|
|
273
|
-
|
|
280
|
+
const runner = readLiveLease(root, id);
|
|
281
|
+
if (runner) throw new Error(`Gate approval is unavailable while loop runner owns ${id}; wait for the runner to stop at the human gate (task lease held by pid ${runner.pid}${runner.host ? ` on ${runner.host}` : ""}${runner.activity ? `, activity ${runner.activity}` : ""}${runner.expiresAt ? `, expires ${runner.expiresAt}` : ""}; inspect: node .agents/aidlc/scripts/loop-entry.mjs leases ${id})`);
|
|
274
282
|
const outcome = approveAndAdvance(root, state, id, gate, source); persist([outcome.task]); emit({ task: outcome.task, idempotent: outcome.idempotent }, outcome.task);
|
|
275
283
|
} else {
|
|
276
284
|
throw new Error(usage);
|
|
@@ -11,5 +11,14 @@
|
|
|
11
11
|
## 🔗 Cross-service contracts
|
|
12
12
|
- <contract or `none`>
|
|
13
13
|
|
|
14
|
+
## 🔀 Parallel work partition
|
|
15
|
+
| item | owned paths | depends on | verification |
|
|
16
|
+
|------|-------------|------------|--------------|
|
|
17
|
+
| <item-id> | <paths it alone writes> | <item-ids or `none`> | <narrow command> |
|
|
18
|
+
|
|
19
|
+
- One owner per path among items that can run at the same time; overlapping items must be ordered with `dependsOn`.
|
|
20
|
+
- Shared surfaces (barrel/index files, shared config, cross-cutting docs, a single entry script) get exactly one owning item; others depend on it.
|
|
21
|
+
- Prefer file/module-level ownership over broad parent directories.
|
|
22
|
+
|
|
14
23
|
## ⚠️ Risks / edge cases
|
|
15
24
|
- <risk and mitigation>
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
## 🗺️ Affected areas
|
|
7
7
|
- `<area>` — <why> — spec: <indexed source or `none indexed`>
|
|
8
8
|
|
|
9
|
+
## 🔀 Work boundaries
|
|
10
|
+
- `<work stream>` — <code areas/paths it touches> — <independent of / depends on>
|
|
11
|
+
- or `single stream` — <why the work cannot be split>
|
|
12
|
+
|
|
9
13
|
## 💭 Assumptions
|
|
10
14
|
- <assumption>
|
|
11
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: aidlc-plan
|
|
3
|
-
description: "AI-DLC phase 2 (Approve plan): after intent is confirmed, run bounded discovery, then produce a detailed design, canonical decisions, and a per-submodule workplan. Triggers when the AI-DLC Orchestrator routes a task after requirements confirmation to planning. Ends at the executable Approve plan review for explicit user approval."
|
|
3
|
+
description: "AI-DLC phase 2 (Approve plan): after intent is confirmed, run bounded discovery, then produce a detailed design, canonical decisions, and a per-submodule workplan whose items own non-overlapping paths so parallel workers do not collide. Triggers when the AI-DLC Orchestrator routes a task after requirements confirmation to planning. Ends at the executable Approve plan review for explicit user approval."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AI-DLC · Plan (Approve plan) — adapter
|