@dtlabs/ai-toolkit 0.11.0 → 0.13.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/CHANGELOG.md +65 -0
- package/bin/cli.js +757 -68
- package/lib/agent-registry.js +1018 -0
- package/lib/execution-ledger.js +609 -0
- package/package.json +1 -1
- package/src/claude/agents/{concurrency-safety-assessment.md → gaia-concurrency-safety-assessment.md} +1 -1
- package/src/claude/agents/{define-feature.md → gaia-define-feature.md} +37 -61
- package/src/claude/agents/{dependency-injection-refactoring.md → gaia-dependency-injection-refactoring.md} +1 -1
- package/src/claude/agents/{dependency-supply-chain-security.md → gaia-dependency-supply-chain-security.md} +1 -1
- package/src/claude/agents/{developer-backend.md → gaia-developer-backend.md} +1 -1
- package/src/claude/agents/{developer-frontend.md → gaia-developer-frontend.md} +1 -1
- package/src/claude/agents/{developer-testing.md → gaia-developer-testing.md} +1 -1
- package/src/claude/agents/{domain-model-refactoring.md → gaia-domain-model-refactoring.md} +1 -1
- package/src/claude/agents/{generate-requirements.md → gaia-generate-requirements.md} +1 -1
- package/src/claude/agents/{generate-tech-spec.md → gaia-generate-tech-spec.md} +1 -1
- package/src/claude/agents/{generate-work-breakdown.md → gaia-generate-work-breakdown.md} +5 -5
- package/src/claude/agents/{generic-software-assessment.md → gaia-generic-software-assessment.md} +1 -1
- package/src/claude/agents/{god-class-decomposition.md → gaia-god-class-decomposition.md} +1 -1
- package/src/claude/agents/{init-agents-md.md → gaia-init-agents-md.md} +1 -1
- package/src/claude/agents/{install-toolkit.md → gaia-install-toolkit.md} +1 -1
- package/src/claude/agents/{intervention-documentation-standard.md → gaia-intervention-documentation-standard.md} +1 -1
- package/src/claude/agents/{layered-architecture-assessment.md → gaia-layered-architecture-assessment.md} +1 -1
- package/src/claude/agents/{review-solution.md → gaia-review-solution.md} +1 -1
- package/src/claude/agents/{security-hardening.md → gaia-security-hardening.md} +1 -1
- package/src/claude/agents/{validate-feature-docs.md → gaia-validate-feature-docs.md} +2 -2
- package/src/claude/agents/{validate-work-breakdown-semantic.md → gaia-validate-work-breakdown-semantic.md} +1 -1
- package/src/claude/commands/next-task.md +4 -4
- package/src/claude/scripts/wb-render.js +101 -2
- package/src/claude/scripts/wb-validate.js +32 -2
- package/src/claude/skills/assess-codebase/SKILL.md +1 -1
- package/src/claude/skills/define-feature/SKILL.md +2 -2
- package/src/claude/skills/hi-gaia/SKILL.md +1 -1
- package/src/claude/skills/implement-feature/SKILL.md +190 -10
- package/src/claude/skills/init-agents/SKILL.md +2 -2
- package/src/claude/skills/install-toolkit/SKILL.md +2 -2
- package/src/claude/workflows/pm-phase1.js +94 -73
- package/src/claude/workflows/pm-phase2.js +118 -108
- package/src/claude/workflows/pm-phase3.js +219 -75
|
@@ -7,42 +7,6 @@ export const meta = {
|
|
|
7
7
|
],
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
// ── Ledger helper functions ───────────────────────────────────────────────────
|
|
11
|
-
// Route all ledger I/O through agent() — fs is not available in the workflow runtime.
|
|
12
|
-
// Mirrors appendLedgerEntry / updateLedgerEntry from bin/cli.js (same contract).
|
|
13
|
-
|
|
14
|
-
async function appendLedgerEntry(featureDir, prefix, entry) {
|
|
15
|
-
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
16
|
-
// Defaults come first so the caller can override started_at/completed_at.
|
|
17
|
-
// Skipped entries pass completed_at: '__TS__' to get a real timestamp; running entries
|
|
18
|
-
// pass completed_at: null to keep it null. Both are replaced/kept by the agent below.
|
|
19
|
-
const entryWithoutTs = JSON.stringify({ started_at: '__TS__', completed_at: null, ...entry })
|
|
20
|
-
await agent(
|
|
21
|
-
`Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
|
|
22
|
-
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
23
|
-
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
|
|
24
|
-
`3. Push this object onto the array, replacing every "__TS__" string value in the object with NOW: ${entryWithoutTs}\n` +
|
|
25
|
-
`4. Write the full array back (JSON, 2-space indent). Return no output.`,
|
|
26
|
-
{ label: 'append-ledger', phase: 'Work Breakdown', model: 'haiku' }
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
|
|
31
|
-
const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
|
|
32
|
-
// completed_at is generated via Bash inside the agent to get a real UTC timestamp with time
|
|
33
|
-
const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
|
|
34
|
-
await agent(
|
|
35
|
-
`Update an entry in the ledger array at: ${ledgerPath}\n\n` +
|
|
36
|
-
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
37
|
-
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
|
|
38
|
-
`3. Search from the end for the last entry where agent === "${agentKey}".\n` +
|
|
39
|
-
`4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
|
|
40
|
-
`5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
|
|
41
|
-
`6. If not found, do nothing.`,
|
|
42
|
-
{ label: 'update-ledger', phase: 'Work Breakdown', model: 'haiku' }
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
10
|
// Output schemas for wb-validate and wb-render agent invocations (used by US-05)
|
|
47
11
|
const WB_WRAPPER_SCHEMA = {
|
|
48
12
|
type: 'object',
|
|
@@ -85,43 +49,105 @@ function normalizeError(err) {
|
|
|
85
49
|
try { return JSON.stringify(err) } catch (_) { return '<unknown error>' }
|
|
86
50
|
}
|
|
87
51
|
|
|
52
|
+
// ── Tier 2 resolution guard ───────────────────────────────────────────────────
|
|
53
|
+
// All worker agents must be resolved via the CLI before dispatch.
|
|
54
|
+
// No direct require('lib/agent-registry') — resolution goes through the CLI facade.
|
|
55
|
+
const RESOLVE_SCHEMA = {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
nativeName: { type: 'string' },
|
|
59
|
+
status: { type: 'string' },
|
|
60
|
+
agentId: { type: 'string' },
|
|
61
|
+
exitNonZero: { type: 'boolean' },
|
|
62
|
+
error: { type: 'string' },
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function resolveAgentTier2(agentId, label, phaseName) {
|
|
67
|
+
const result = await agent(
|
|
68
|
+
`Run this command via Bash:\n\nai-toolkit agents resolve --project . --id ${agentId} --require-verified\n\nIf the command exits 0: parse the JSON from stdout and return it with exitNonZero: false.\nIf the command exits non-zero: return {"exitNonZero": true, "error": "<stderr text>", "agentId": "${agentId}"}.`,
|
|
69
|
+
{ label, phase: phaseName, model: 'haiku', schema: RESOLVE_SCHEMA }
|
|
70
|
+
)
|
|
71
|
+
if (!result || result.exitNonZero || !result.nativeName) {
|
|
72
|
+
throw new Error(`HARD STOP — Tier 2 resolution failed for ${agentId}: ${result && result.error ? result.error : 'no nativeName returned'}`)
|
|
73
|
+
}
|
|
74
|
+
return result.nativeName
|
|
75
|
+
}
|
|
76
|
+
|
|
88
77
|
// ── Parse args ────────────────────────────────────────────────────────────────
|
|
89
78
|
|
|
90
79
|
// args: "<path-to-feature.md>"
|
|
91
80
|
const featurePath = (typeof args === 'string' ? args : '').trim().split(/\s+/)[0]
|
|
92
81
|
|
|
82
|
+
// Feature directory = parent of the feature.md path. Separator-agnostic: the workflow
|
|
83
|
+
// runtime provides no `path` module, so we cannot use path.dirname. Handle BOTH POSIX
|
|
84
|
+
// "/" and Windows "\": strip a trailing "<sep>feature.md" tail when present; if the
|
|
85
|
+
// input is already a directory (no feature.md tail), strip only a trailing separator.
|
|
86
|
+
// This replaces the previous POSIX-only regex (/\/[^/]+$/), which left "\feature.md"
|
|
87
|
+
// attached on Windows paths and produced malformed "…\feature.md/…json" paths that only
|
|
88
|
+
// "worked" because an LLM wrapper agent silently repaired them.
|
|
89
|
+
function deriveFeatureDir(featurePath) {
|
|
90
|
+
return /[/\\]feature\.md$/i.test(featurePath)
|
|
91
|
+
? featurePath.replace(/[/\\]feature\.md$/i, '')
|
|
92
|
+
: featurePath.replace(/[/\\]+$/, '')
|
|
93
|
+
}
|
|
94
|
+
|
|
93
95
|
// Derive feature directory and prefix early (needed for ledger writes before metrics agent runs)
|
|
94
|
-
const featureDir = featurePath
|
|
96
|
+
const featureDir = deriveFeatureDir(featurePath)
|
|
95
97
|
const prefixMatch = featureDir.match(/([A-Z]+-\d+)/)
|
|
96
98
|
const prefix = prefixMatch ? prefixMatch[1] : 'FTR-000'
|
|
97
99
|
|
|
100
|
+
// ── Tier 3 — self-ledger helper ───────────────────────────────────────────────
|
|
101
|
+
const SELF_LEDGER_SCHEMA = {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
exitCode: { type: 'number' },
|
|
105
|
+
stdout: { type: 'string' },
|
|
106
|
+
stderr: { type: 'string' },
|
|
107
|
+
},
|
|
108
|
+
required: ['exitCode'],
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function selfLedgerOp(cmd, label, phaseName) {
|
|
112
|
+
const result = await agent(
|
|
113
|
+
`Run this shell command via Bash and return the exit code as structured output.\n\nCommand: ${cmd}\n\nCapture: exitCode (integer), stdout (string), stderr (string). Return all three.`,
|
|
114
|
+
{ label, phase: phaseName, model: 'haiku', schema: SELF_LEDGER_SCHEMA }
|
|
115
|
+
)
|
|
116
|
+
const status = (result && typeof result.exitCode === 'number') ? result.exitCode : 1
|
|
117
|
+
if (status !== 0) {
|
|
118
|
+
throw new Error(`HARD STOP — self-ledger operation failed. Exit code: ${status}. Command: ${cmd}`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Tier 3 self-registration (open BEFORE any main logic, fail-closed) ────────
|
|
123
|
+
await selfLedgerOp(
|
|
124
|
+
`ai-toolkit ledger open --prefix ${prefix} --agent pm-phase2:self --phase phase2 --dir "${featureDir}" --attempt 1`,
|
|
125
|
+
'ledger-open-pm-phase2-self', 'Work Breakdown'
|
|
126
|
+
)
|
|
127
|
+
|
|
98
128
|
// ── generate-work-breakdown ───────────────────────────────────────────────────
|
|
99
129
|
phase('Work Breakdown')
|
|
100
130
|
|
|
101
131
|
const tokenLedger = []
|
|
102
132
|
|
|
103
133
|
log(`Running generate-work-breakdown for ${featurePath}`)
|
|
104
|
-
await
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
model
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
started_at: '__TS__',
|
|
111
|
-
completed_at: null,
|
|
112
|
-
})
|
|
134
|
+
const wbNativeName = await resolveAgentTier2('gaia.agent.planner.work-breakdown', 'resolve-generate-work-breakdown', 'Work Breakdown')
|
|
135
|
+
const wbKey = 'generate-work-breakdown:phase2'
|
|
136
|
+
await agent(
|
|
137
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${featureDir} --prefix ${prefix} --agent ${wbKey} --phase phase2 --model haiku --attempt 1\n\nReturn no output.`,
|
|
138
|
+
{ label: 'ledger-open-generate-work-breakdown', phase: 'Work Breakdown', model: 'haiku' }
|
|
139
|
+
)
|
|
113
140
|
const beforeWB = budget.spent()
|
|
114
141
|
await agent(featurePath, {
|
|
115
|
-
agentType:
|
|
142
|
+
agentType: wbNativeName,
|
|
116
143
|
label: 'generate-work-breakdown',
|
|
117
144
|
phase: 'Work Breakdown',
|
|
118
145
|
})
|
|
119
146
|
const wbTokens = budget.spent() - beforeWB
|
|
120
|
-
await
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
})
|
|
147
|
+
await agent(
|
|
148
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${featureDir} --prefix ${prefix} --agent ${wbKey} --tokens ${wbTokens} --attempt 1\n\nReturn no output.`,
|
|
149
|
+
{ label: 'ledger-close-generate-work-breakdown', phase: 'Work Breakdown', model: 'haiku' }
|
|
150
|
+
)
|
|
125
151
|
tokenLedger.push({ agent: 'generate-work-breakdown', model: 'haiku', phase_delta_tokens: wbTokens })
|
|
126
152
|
log(`generate-work-breakdown done — phase delta: ${wbTokens} tokens`)
|
|
127
153
|
|
|
@@ -129,11 +155,11 @@ log(`generate-work-breakdown done — phase delta: ${wbTokens} tokens`)
|
|
|
129
155
|
let wbValidatorReport = null
|
|
130
156
|
let validateFailed = false
|
|
131
157
|
let validateExitCode = null
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
phase
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
const wbValidateKey = 'wb-validate:phase2'
|
|
159
|
+
await agent(
|
|
160
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${featureDir} --prefix ${prefix} --agent ${wbValidateKey} --phase phase2 --model haiku --attempt 1\n\nReturn no output.`,
|
|
161
|
+
{ label: 'ledger-open-wb-validate', phase: 'Work Breakdown', model: 'haiku' }
|
|
162
|
+
)
|
|
137
163
|
const beforeValidate = budget.spent()
|
|
138
164
|
try {
|
|
139
165
|
const validateWrapper = await agent(
|
|
@@ -173,20 +199,14 @@ try {
|
|
|
173
199
|
if (validateWrapper.exitCode === 1 && wbValidatorReport.valid) {
|
|
174
200
|
throw new Error('Inconsistent wb-validate result: exit 1 with valid=true')
|
|
175
201
|
}
|
|
176
|
-
await updateLedgerEntry(featureDir, prefix, 'wb-validate:phase2', {
|
|
177
|
-
status: 'done', completed_at: '__TS__',
|
|
178
|
-
phase_delta_tokens: budget.spent() - beforeValidate,
|
|
179
|
-
})
|
|
180
202
|
} catch (err) {
|
|
181
203
|
validateFailed = true
|
|
182
|
-
await updateLedgerEntry(featureDir, prefix, 'wb-validate:phase2', {
|
|
183
|
-
status: 'failed', completed_at: '__TS__',
|
|
184
|
-
phase_delta_tokens: budget.spent() - beforeValidate,
|
|
185
|
-
error_summary: normalizeError(err),
|
|
186
|
-
exit_code: err._exitCode ?? validateExitCode ?? null,
|
|
187
|
-
})
|
|
188
204
|
}
|
|
189
205
|
const validateTokens = budget.spent() - beforeValidate
|
|
206
|
+
await agent(
|
|
207
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${featureDir} --prefix ${prefix} --agent ${wbValidateKey} --tokens ${validateTokens} --attempt 1\n\nReturn no output.`,
|
|
208
|
+
{ label: 'ledger-close-wb-validate', phase: 'Work Breakdown', model: 'haiku' }
|
|
209
|
+
)
|
|
190
210
|
const wbValidatorPassed = !validateFailed && wbValidatorReport !== null
|
|
191
211
|
&& wbValidatorReport.valid === true && wbValidatorReport.errors.length === 0
|
|
192
212
|
log(`wb-validate: ${wbValidatorPassed ? 'passed' : 'failed/errors'} — ${validateTokens} tokens`)
|
|
@@ -195,18 +215,15 @@ log(`wb-validate: ${wbValidatorPassed ? 'passed' : 'failed/errors'} — ${valida
|
|
|
195
215
|
let semanticResult = null
|
|
196
216
|
let semanticFailed = false
|
|
197
217
|
let semanticTokens = 0
|
|
218
|
+
const semanticKey = 'validate-work-breakdown-semantic:phase2'
|
|
198
219
|
if (!wbValidatorPassed) {
|
|
199
|
-
|
|
200
|
-
agent: 'validate-work-breakdown-semantic:phase2',
|
|
201
|
-
phase: 'phase2', model: 'sonnet', status: 'skipped',
|
|
202
|
-
phase_delta_tokens: 0, started_at: '__TS__', completed_at: '__TS__',
|
|
203
|
-
})
|
|
220
|
+
log('validate-work-breakdown-semantic: skipped (wb-validate did not pass)')
|
|
204
221
|
} else {
|
|
205
|
-
await
|
|
206
|
-
|
|
207
|
-
phase
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
const semanticNativeName = await resolveAgentTier2('gaia.agent.planner.validate-work-breakdown', 'resolve-validate-work-breakdown-semantic', 'Work Breakdown')
|
|
223
|
+
await agent(
|
|
224
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${featureDir} --prefix ${prefix} --agent ${semanticKey} --phase phase2 --model sonnet --attempt 1\n\nReturn no output.`,
|
|
225
|
+
{ label: 'ledger-open-semantic-validator', phase: 'Work Breakdown', model: 'haiku' }
|
|
226
|
+
)
|
|
210
227
|
const beforeSemantic = budget.spent()
|
|
211
228
|
try {
|
|
212
229
|
semanticResult = await agent(
|
|
@@ -214,22 +231,18 @@ if (!wbValidatorPassed) {
|
|
|
214
231
|
{
|
|
215
232
|
label: 'validate-work-breakdown-semantic',
|
|
216
233
|
phase: 'Work Breakdown',
|
|
217
|
-
agentType:
|
|
234
|
+
agentType: semanticNativeName,
|
|
218
235
|
schema: WB_SEMANTIC_SCHEMA,
|
|
219
236
|
}
|
|
220
237
|
)
|
|
221
|
-
semanticTokens = budget.spent() - beforeSemantic
|
|
222
|
-
await updateLedgerEntry(featureDir, prefix, 'validate-work-breakdown-semantic:phase2', {
|
|
223
|
-
status: 'done', completed_at: '__TS__', phase_delta_tokens: semanticTokens,
|
|
224
|
-
})
|
|
225
238
|
} catch (err) {
|
|
226
239
|
semanticFailed = true
|
|
227
|
-
semanticTokens = budget.spent() - beforeSemantic
|
|
228
|
-
await updateLedgerEntry(featureDir, prefix, 'validate-work-breakdown-semantic:phase2', {
|
|
229
|
-
status: 'failed', completed_at: '__TS__', phase_delta_tokens: semanticTokens,
|
|
230
|
-
error_summary: normalizeError(err), exit_code: null,
|
|
231
|
-
})
|
|
232
240
|
}
|
|
241
|
+
semanticTokens = budget.spent() - beforeSemantic
|
|
242
|
+
await agent(
|
|
243
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${featureDir} --prefix ${prefix} --agent ${semanticKey} --tokens ${semanticTokens} --attempt 1\n\nReturn no output.`,
|
|
244
|
+
{ label: 'ledger-close-semantic-validator', phase: 'Work Breakdown', model: 'haiku' }
|
|
245
|
+
)
|
|
233
246
|
}
|
|
234
247
|
log(`semantic validator: ${semanticFailed ? 'failed' : semanticResult ? 'done' : 'skipped'}`)
|
|
235
248
|
|
|
@@ -237,18 +250,14 @@ log(`semantic validator: ${semanticFailed ? 'failed' : semanticResult ? 'done' :
|
|
|
237
250
|
const canRender = wbValidatorPassed && !semanticFailed
|
|
238
251
|
let renderFailed = false
|
|
239
252
|
let renderResult = null
|
|
253
|
+
const wbRenderKey = 'wb-render:phase2'
|
|
240
254
|
if (!canRender) {
|
|
241
|
-
|
|
242
|
-
agent: 'wb-render:phase2',
|
|
243
|
-
phase: 'phase2', model: 'haiku', status: 'skipped',
|
|
244
|
-
phase_delta_tokens: 0, started_at: '__TS__', completed_at: '__TS__',
|
|
245
|
-
})
|
|
255
|
+
log('wb-render: skipped (wb-validate or semantic validator did not pass)')
|
|
246
256
|
} else {
|
|
247
|
-
await
|
|
248
|
-
agent
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
})
|
|
257
|
+
await agent(
|
|
258
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${featureDir} --prefix ${prefix} --agent ${wbRenderKey} --phase phase2 --model haiku --attempt 1\n\nReturn no output.`,
|
|
259
|
+
{ label: 'ledger-open-wb-render', phase: 'Work Breakdown', model: 'haiku' }
|
|
260
|
+
)
|
|
252
261
|
const beforeRender = budget.spent()
|
|
253
262
|
try {
|
|
254
263
|
renderResult = await agent(
|
|
@@ -291,20 +300,15 @@ if (!canRender) {
|
|
|
291
300
|
{ _exitCode: renderResult.exitCode }
|
|
292
301
|
)
|
|
293
302
|
}
|
|
294
|
-
await updateLedgerEntry(featureDir, prefix, 'wb-render:phase2', {
|
|
295
|
-
status: 'done', completed_at: '__TS__',
|
|
296
|
-
phase_delta_tokens: budget.spent() - beforeRender,
|
|
297
|
-
})
|
|
298
303
|
} catch (err) {
|
|
299
304
|
renderFailed = true
|
|
300
|
-
await updateLedgerEntry(featureDir, prefix, 'wb-render:phase2', {
|
|
301
|
-
status: 'failed', completed_at: '__TS__',
|
|
302
|
-
phase_delta_tokens: budget.spent() - beforeRender,
|
|
303
|
-
error_summary: normalizeError(err),
|
|
304
|
-
exit_code: err._exitCode ?? renderResult?.exitCode ?? null,
|
|
305
|
-
})
|
|
306
305
|
}
|
|
307
|
-
|
|
306
|
+
const renderTokens = budget.spent() - beforeRender
|
|
307
|
+
await agent(
|
|
308
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${featureDir} --prefix ${prefix} --agent ${wbRenderKey} --tokens ${renderTokens} --attempt 1\n\nReturn no output.`,
|
|
309
|
+
{ label: 'ledger-close-wb-render', phase: 'Work Breakdown', model: 'haiku' }
|
|
310
|
+
)
|
|
311
|
+
tokenLedger.push({ agent: 'wb-render', model: 'haiku', phase_delta_tokens: renderTokens })
|
|
308
312
|
}
|
|
309
313
|
|
|
310
314
|
tokenLedger.push({ agent: 'wb-validate', model: 'haiku', phase_delta_tokens: validateTokens })
|
|
@@ -546,6 +550,12 @@ ${phase2Events}`,
|
|
|
546
550
|
{ label: 'append-process-log', phase: 'Effort Estimate' }
|
|
547
551
|
)
|
|
548
552
|
|
|
553
|
+
// ── Tier 3 self-registration — close on successful completion ─────────────────
|
|
554
|
+
await selfLedgerOp(
|
|
555
|
+
`ai-toolkit ledger close --prefix ${prefix} --agent pm-phase2:self --dir "${featureDir}" --attempt 1`,
|
|
556
|
+
'ledger-close-pm-phase2-self', 'Effort Estimate'
|
|
557
|
+
)
|
|
558
|
+
|
|
549
559
|
return {
|
|
550
560
|
prefix: metrics.prefix || prefix,
|
|
551
561
|
feature_path: featurePath,
|