@tea-agent/loop-agent 0.21.0 → 0.23.1
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/AGENTS.md +42 -108
- package/CHANGELOG.md +114 -0
- package/README.md +7 -4
- package/bin/agent-worker.js +0 -0
- package/dist/adapters/loop-agent.js +52 -0
- package/dist/application/context-usage/skill-resolution-stats.js +263 -0
- package/dist/application/dag/generate-task-dag.js +17 -3
- package/dist/cli/command-definitions.js +8 -7
- package/dist/cli/program.js +17 -15
- package/dist/commands/doctor.js +269 -18
- package/dist/commands/init.js +198 -86
- package/dist/commands/stats.js +40 -11
- package/dist/executors/dag-pi-executor.js +2 -0
- package/dist/executors/shell-executor.js +162 -19
- package/dist/shared/openspec-spec.js +49 -0
- package/dist/shared/operator/capabilities.js +11 -1
- package/dist/worker/console/app-data.js +4 -0
- package/dist/worker/console/chat/instruction-skills.js +217 -0
- package/dist/worker/console/chat/model-resolver.js +106 -0
- package/dist/worker/console/chat/pi-runtime.js +605 -0
- package/dist/worker/console/chat/resource-loader.js +66 -0
- package/dist/worker/console/chat/routes.js +357 -0
- package/dist/worker/console/chat/session-store.js +238 -0
- package/dist/worker/console/chat/tool-adapter.js +238 -0
- package/dist/worker/console/chat/tools.js +171 -0
- package/dist/worker/console/server.js +55 -0
- package/dist/worker/console/static/assets/index-DVl7Jxt5.js +25 -0
- package/dist/worker/console/static/assets/index-lVcIr9Ju.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/feature/profile-schema.js +1 -1
- package/dist/worker/observability/read-model.js +21 -1
- package/dist/worker/observe/spec-evidence.js +12 -15
- package/dist/worker/observe/static/dag-helpers.js +22 -0
- package/dist/worker/observe/static/views/dag.js +5 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +37 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +141 -32
- package/dist/workflows/dag/frontend-lint-baseline.js +471 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +79 -16
- package/dist/workflows/dag/frontend-project-capability.js +11 -8
- package/dist/workflows/dag/frontend-repair.js +6 -4
- package/dist/workflows/dag/frontend-review-context.js +67 -0
- package/dist/workflows/dag/frontend-test-case-quality.js +105 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +71 -66
- package/dist/workflows/dag/frontend-verification-trace.js +31 -1
- package/dist/workflows/dag/frontend-worktree-diff.js +81 -6
- package/dist/workflows/dag/init-hybrid.js +412 -84
- package/dist/workflows/dag/node-execution.js +38 -1
- package/dist/workflows/dag/output-protocol.js +89 -0
- package/dist/workflows/dag/prompt.js +35 -1
- package/dist/workflows/dag/recovery-recommendation.js +45 -0
- package/dist/workflows/dag/report.js +28 -1
- package/dist/workflows/dag/rerun-task.js +1 -1
- package/dist/workflows/dag/scheduler.js +9 -0
- package/dist/workflows/dag/types.js +74 -1
- package/dist/workflows/dag/validate.js +55 -0
- package/docs/README.md +73 -156
- package/docs/architecture/README.md +3 -2
- package/docs/architecture/dag-execution.md +2 -2
- package/docs/architecture/evolution.md +14 -12
- package/docs/architecture/system-overview.md +1 -1
- package/docs/architecture/worker-and-feature.md +3 -3
- package/docs/governance/README.md +15 -0
- package/docs/{harness-methodology-debugging.md → governance/harness-methodology-debugging.md} +27 -3
- package/docs/init-surface.manifest.json +22 -4
- package/docs/operations/README.md +12 -0
- package/docs/{local-development-environment.md → operations/local-development-environment.md} +1 -1
- package/docs/skills/vetted-skill-registry.md +23 -3
- package/docs/templates/README.md +55 -0
- package/docs/templates/agent-dag.schema.json +15 -5
- package/docs/templates/backend-test-dag.json +1 -1
- package/docs/templates/evaluation/agents-map-slim-v1.candidate.json +9 -0
- package/docs/templates/evaluation/agents-map-slim-v1.md +87 -0
- package/docs/templates/evaluation/agents-map-verbose-v0.candidate.json +9 -0
- package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -0
- package/docs/templates/frontend-implementation-contract.schema.json +4 -3
- package/docs/templates/frontend-test-case-checklist.md +6 -2
- package/docs/templates/frontend-test-dag.json +2 -2
- package/docs/templates/hybrid-dag.json +1 -1
- package/docs/templates/progress-log.md +9 -2
- package/harness.json +5 -5
- package/package.json +5 -5
- package/scripts/kb-bootstrap-init-skeleton.sh +2 -2
- package/skills/agent-worker/SKILL.md +1 -1
- package/skills/frontend-design-review/SKILL.md +12 -10
- package/skills/frontend-design-review/references/review-checklist.md +4 -4
- package/skills/frontend-implementation/SKILL.md +2 -2
- package/skills/frontend-implementation/references/code-standards.md +4 -3
- package/skills/frontend-implementation/references/design-spec.md +19 -14
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/frontend-review/SKILL.md +15 -28
- package/skills/frontend-review/references/review-findings.md +16 -18
- package/skills/frontend-verification/SKILL.md +16 -13
- package/skills/frontend-verification/references/verification-checklist.md +18 -30
- package/skills/grill-with-docs/SKILL.md +44 -52
- package/skills/grill-with-docs/adr-format.md +37 -26
- package/skills/grill-with-docs/context-format.md +18 -26
- package/skills/loop-agent/SKILL.md +28 -112
- package/skills/loop-agent/references/command-reference.md +9 -3
- package/skills/loop-agent/references/harness-policy.md +3 -3
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/skills/loop-agent/references/orchestrator-and-interventions.md +1 -1
- package/skills/loop-agent/references/task-workflow.md +2 -0
- package/skills/systematic-debugging/SKILL.md +20 -4
- package/skills/test-driven-development/SKILL.md +10 -3
- package/dist/worker/console/static/assets/index-CUDke82y.js +0 -18
- package/dist/worker/console/static/assets/index-wSEksVSO.css +0 -1
- /package/docs/{harness-methodology-tdd.md → governance/harness-methodology-tdd.md} +0 -0
- /package/docs/{harness-methodology-verification.md → governance/harness-methodology-verification.md} +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only skill-resolution / context-usage aggregation over recent DAG runs.
|
|
3
|
+
*
|
|
4
|
+
* Terminology: this reports resolution / truncation / unresolved-reference
|
|
5
|
+
* distribution and correlated node outcomes. It does NOT claim skill "execution"
|
|
6
|
+
* or model compliance rates — snapshots prove parse+inject only.
|
|
7
|
+
*/
|
|
8
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { isDagSkillSnapshotIntegrityError, readSkillSnapshot, } from "../../workflows/dag/skill-snapshot.js";
|
|
11
|
+
const LIFECYCLES = ["active", "paused", "completed"];
|
|
12
|
+
function emptySkillCounts() {
|
|
13
|
+
return {
|
|
14
|
+
declared: 0,
|
|
15
|
+
resolved: 0,
|
|
16
|
+
missing: 0,
|
|
17
|
+
error: 0,
|
|
18
|
+
entryTruncated: 0,
|
|
19
|
+
referenceTruncated: 0,
|
|
20
|
+
unresolvedByReason: {},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function emptyNodeOutcomes() {
|
|
24
|
+
return {
|
|
25
|
+
FINISHED: 0,
|
|
26
|
+
ERROR: 0,
|
|
27
|
+
SKIPPED: 0,
|
|
28
|
+
PENDING: 0,
|
|
29
|
+
RUNNING: 0,
|
|
30
|
+
other: 0,
|
|
31
|
+
failureCategories: {},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function bumpReason(map, reason) {
|
|
35
|
+
map[reason] = (map[reason] ?? 0) + 1;
|
|
36
|
+
}
|
|
37
|
+
function ensureSkill(skills, name) {
|
|
38
|
+
if (!skills[name])
|
|
39
|
+
skills[name] = emptySkillCounts();
|
|
40
|
+
return skills[name];
|
|
41
|
+
}
|
|
42
|
+
export function parseStatsContextArgs(args) {
|
|
43
|
+
let last = 50;
|
|
44
|
+
let json = false;
|
|
45
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
46
|
+
const token = args[i];
|
|
47
|
+
if (token === "--json" || token === "--json=true") {
|
|
48
|
+
json = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (token.startsWith("--json=")) {
|
|
52
|
+
json = token.slice("--json=".length) === "true";
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (token === "--last") {
|
|
56
|
+
const raw = args[i + 1];
|
|
57
|
+
if (!raw || raw.startsWith("-")) {
|
|
58
|
+
throw new Error("usage: stats context [--last <n>] [--json]");
|
|
59
|
+
}
|
|
60
|
+
const parsed = Number.parseInt(raw, 10);
|
|
61
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
62
|
+
throw new Error(`--last must be a positive integer, got: ${raw}`);
|
|
63
|
+
}
|
|
64
|
+
last = parsed;
|
|
65
|
+
i += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (token.startsWith("--last=")) {
|
|
69
|
+
const raw = token.slice("--last=".length);
|
|
70
|
+
const parsed = Number.parseInt(raw, 10);
|
|
71
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
72
|
+
throw new Error(`--last must be a positive integer, got: ${raw}`);
|
|
73
|
+
}
|
|
74
|
+
last = parsed;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`unknown argument: ${token}\nusage: stats context [--last <n>] [--json]`);
|
|
78
|
+
}
|
|
79
|
+
return { last, json };
|
|
80
|
+
}
|
|
81
|
+
async function listRecentDagRuns(repoRoot, last) {
|
|
82
|
+
const matches = [];
|
|
83
|
+
for (const lifecycle of LIFECYCLES) {
|
|
84
|
+
const lifecycleDir = path.join(repoRoot, ".harness", "dag-runs", lifecycle);
|
|
85
|
+
let entries = [];
|
|
86
|
+
try {
|
|
87
|
+
entries = await readdir(lifecycleDir);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
for (const name of entries) {
|
|
93
|
+
if (!name || name === ".gitkeep")
|
|
94
|
+
continue;
|
|
95
|
+
const runDir = path.join(lifecycleDir, name);
|
|
96
|
+
let st;
|
|
97
|
+
try {
|
|
98
|
+
st = await stat(runDir);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (!st.isDirectory())
|
|
104
|
+
continue;
|
|
105
|
+
matches.push({
|
|
106
|
+
runId: name,
|
|
107
|
+
lifecycle,
|
|
108
|
+
runDir,
|
|
109
|
+
mtimeMs: st.mtimeMs,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
114
|
+
return matches.slice(0, last);
|
|
115
|
+
}
|
|
116
|
+
async function readOptionalJson(filePath) {
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(await readFile(filePath, "utf8"));
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function ingestSnapshot(skills, snapshot) {
|
|
125
|
+
for (const profile of snapshot.profiles) {
|
|
126
|
+
for (const skillName of profile.skills) {
|
|
127
|
+
ensureSkill(skills, skillName).declared += 1;
|
|
128
|
+
}
|
|
129
|
+
for (const instruction of profile.resolvedInstructions) {
|
|
130
|
+
const row = ensureSkill(skills, instruction.name);
|
|
131
|
+
if (instruction.resolution === "resolved")
|
|
132
|
+
row.resolved += 1;
|
|
133
|
+
else if (instruction.resolution === "missing")
|
|
134
|
+
row.missing += 1;
|
|
135
|
+
else if (instruction.resolution === "error")
|
|
136
|
+
row.error += 1;
|
|
137
|
+
if (instruction.truncated)
|
|
138
|
+
row.entryTruncated += 1;
|
|
139
|
+
for (const ref of instruction.references ?? []) {
|
|
140
|
+
if (ref.truncated)
|
|
141
|
+
row.referenceTruncated += 1;
|
|
142
|
+
}
|
|
143
|
+
for (const unresolved of instruction.unresolvedReferences ?? []) {
|
|
144
|
+
bumpReason(row.unresolvedByReason, unresolved.reason);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function ingestNodeOutcomes(outcomes, state) {
|
|
150
|
+
for (const node of Object.values(state.nodes ?? {})) {
|
|
151
|
+
const status = node.status;
|
|
152
|
+
if (status === "FINISHED")
|
|
153
|
+
outcomes.FINISHED += 1;
|
|
154
|
+
else if (status === "ERROR")
|
|
155
|
+
outcomes.ERROR += 1;
|
|
156
|
+
else if (status === "SKIPPED")
|
|
157
|
+
outcomes.SKIPPED += 1;
|
|
158
|
+
else if (status === "PENDING")
|
|
159
|
+
outcomes.PENDING += 1;
|
|
160
|
+
else if (status === "RUNNING")
|
|
161
|
+
outcomes.RUNNING += 1;
|
|
162
|
+
else
|
|
163
|
+
outcomes.other += 1;
|
|
164
|
+
if (node.failureCategory) {
|
|
165
|
+
bumpReason(outcomes.failureCategories, node.failureCategory);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Pure-ish builder: filesystem reads only; never writes `.harness/**`.
|
|
171
|
+
*/
|
|
172
|
+
export async function buildContextUsageReport(repoRoot, options = {}) {
|
|
173
|
+
const last = options.last ?? 50;
|
|
174
|
+
const candidates = await listRecentDagRuns(repoRoot, last);
|
|
175
|
+
const skills = {};
|
|
176
|
+
const nodeOutcomes = emptyNodeOutcomes();
|
|
177
|
+
const byLifecycle = {
|
|
178
|
+
active: 0,
|
|
179
|
+
paused: 0,
|
|
180
|
+
completed: 0,
|
|
181
|
+
};
|
|
182
|
+
const findings = [];
|
|
183
|
+
const notes = [
|
|
184
|
+
"skill-resolution telemetry only; does not claim model compliance or skill execution rates",
|
|
185
|
+
"corrupted snapshots are reported as findings and are not silently skipped",
|
|
186
|
+
];
|
|
187
|
+
for (const candidate of candidates) {
|
|
188
|
+
byLifecycle[candidate.lifecycle] += 1;
|
|
189
|
+
const state = await readOptionalJson(path.join(candidate.runDir, "state.json"));
|
|
190
|
+
const runJson = await readOptionalJson(path.join(candidate.runDir, "run.json"));
|
|
191
|
+
const finding = {
|
|
192
|
+
runId: candidate.runId,
|
|
193
|
+
lifecycle: candidate.lifecycle,
|
|
194
|
+
mtimeMs: candidate.mtimeMs,
|
|
195
|
+
runStatus: state?.status,
|
|
196
|
+
contextPolicyId: runJson?.defaults?.contextPolicyId,
|
|
197
|
+
snapshot: "missing",
|
|
198
|
+
};
|
|
199
|
+
if (state)
|
|
200
|
+
ingestNodeOutcomes(nodeOutcomes, state);
|
|
201
|
+
const ref = state?.skillSnapshotRef;
|
|
202
|
+
if (!ref) {
|
|
203
|
+
findings.push(finding);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const snapshot = await readSkillSnapshot(candidate.runDir, ref);
|
|
208
|
+
finding.snapshot = "ok";
|
|
209
|
+
ingestSnapshot(skills, snapshot);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
finding.snapshot = "corrupted";
|
|
213
|
+
finding.snapshotError = isDagSkillSnapshotIntegrityError(error)
|
|
214
|
+
? error.message
|
|
215
|
+
: error instanceof Error
|
|
216
|
+
? error.message
|
|
217
|
+
: String(error);
|
|
218
|
+
}
|
|
219
|
+
findings.push(finding);
|
|
220
|
+
}
|
|
221
|
+
const corrupted = findings.filter((f) => f.snapshot === "corrupted").length;
|
|
222
|
+
return {
|
|
223
|
+
schemaVersion: 1,
|
|
224
|
+
command: "stats.context",
|
|
225
|
+
ok: corrupted === 0,
|
|
226
|
+
scannedRuns: candidates.length,
|
|
227
|
+
requestedLast: last,
|
|
228
|
+
byLifecycle,
|
|
229
|
+
skills,
|
|
230
|
+
nodeOutcomes,
|
|
231
|
+
findings,
|
|
232
|
+
notes,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export function formatContextUsageReportHuman(report) {
|
|
236
|
+
const lines = [];
|
|
237
|
+
lines.push(`stats context (last ${report.requestedLast}, scanned ${report.scannedRuns})`);
|
|
238
|
+
lines.push(`lifecycle: active=${report.byLifecycle.active} paused=${report.byLifecycle.paused} completed=${report.byLifecycle.completed}`);
|
|
239
|
+
lines.push(`nodes: FINISHED=${report.nodeOutcomes.FINISHED} ERROR=${report.nodeOutcomes.ERROR} SKIPPED=${report.nodeOutcomes.SKIPPED} PENDING=${report.nodeOutcomes.PENDING} RUNNING=${report.nodeOutcomes.RUNNING}`);
|
|
240
|
+
const skillNames = Object.keys(report.skills).sort();
|
|
241
|
+
if (skillNames.length === 0) {
|
|
242
|
+
lines.push("skills: (none resolved in scanned snapshots)");
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
lines.push("skills:");
|
|
246
|
+
for (const name of skillNames) {
|
|
247
|
+
const s = report.skills[name];
|
|
248
|
+
const budget = s.unresolvedByReason.budget ?? 0;
|
|
249
|
+
lines.push(` ${name}: declared=${s.declared} resolved=${s.resolved} missing=${s.missing} error=${s.error} entryTrunc=${s.entryTruncated} refTrunc=${s.referenceTruncated} budgetSkip=${budget}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const corrupted = report.findings.filter((f) => f.snapshot === "corrupted");
|
|
253
|
+
if (corrupted.length > 0) {
|
|
254
|
+
lines.push(`corrupted snapshots: ${corrupted.length}`);
|
|
255
|
+
for (const finding of corrupted.slice(0, 5)) {
|
|
256
|
+
lines.push(` ${finding.lifecycle}/${finding.runId}: ${finding.snapshotError ?? "unknown"}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const note of report.notes) {
|
|
260
|
+
lines.push(`note: ${note}`);
|
|
261
|
+
}
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
}
|
|
@@ -68,6 +68,15 @@ function buildNextSteps(taskId, outputPath, cwd) {
|
|
|
68
68
|
`loop-agent dag run-task ${taskId} --execute --cwd ${cwd}`,
|
|
69
69
|
];
|
|
70
70
|
}
|
|
71
|
+
function buildBasicValidationLevel(outputPath) {
|
|
72
|
+
return {
|
|
73
|
+
level: "basic",
|
|
74
|
+
strictGovernance: false,
|
|
75
|
+
strictGovernanceValidationRequiredSeparately: true,
|
|
76
|
+
standaloneStrictGovernanceCommand: `loop-agent dag validate --dag ${outputPath} --strict-models --strict-governance`,
|
|
77
|
+
note: "Internal generation validation is basic only; standalone strict governance validation must be confirmed separately.",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
71
80
|
function isBroadWriteSetEntryForPacket(entry) {
|
|
72
81
|
const normalized = entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
73
82
|
if (!normalized || normalized === "." || normalized === "./")
|
|
@@ -168,6 +177,7 @@ async function buildReviewPacket(input) {
|
|
|
168
177
|
? (task.decisionGate.mode ?? "record-only")
|
|
169
178
|
: "disabled",
|
|
170
179
|
})),
|
|
180
|
+
validation: buildBasicValidationLevel(input.dagPath),
|
|
171
181
|
expectedVerification: shellVerification.flatMap((entry) => entry.commands),
|
|
172
182
|
};
|
|
173
183
|
}
|
|
@@ -281,10 +291,12 @@ export async function generateTaskDagUseCase(input) {
|
|
|
281
291
|
profileRouting,
|
|
282
292
|
governanceProfile,
|
|
283
293
|
});
|
|
294
|
+
const validation = buildBasicValidationLevel(outputPath);
|
|
295
|
+
const message = "DAG draft created with basic validation; standalone strict governance validation still requires separate confirmation.";
|
|
284
296
|
if (!shouldRunExecution(parsed)) {
|
|
285
297
|
return {
|
|
286
|
-
mode: "generate+validate",
|
|
287
|
-
message
|
|
298
|
+
mode: "generate+basic-validate",
|
|
299
|
+
message,
|
|
288
300
|
ok: true,
|
|
289
301
|
taskId: initResult.taskId,
|
|
290
302
|
outputPath,
|
|
@@ -296,6 +308,7 @@ export async function generateTaskDagUseCase(input) {
|
|
|
296
308
|
governanceProfile,
|
|
297
309
|
profileRouting,
|
|
298
310
|
reviewPacket,
|
|
311
|
+
validation,
|
|
299
312
|
warnings: validateSummary.warnings,
|
|
300
313
|
next: buildNextSteps(parsed.taskId, outputPath, parsed.cwd),
|
|
301
314
|
};
|
|
@@ -327,7 +340,7 @@ export async function generateTaskDagUseCase(input) {
|
|
|
327
340
|
});
|
|
328
341
|
return {
|
|
329
342
|
mode: resolveExecutionMode(parsed),
|
|
330
|
-
message
|
|
343
|
+
message,
|
|
331
344
|
ok: true,
|
|
332
345
|
taskId: initResult.taskId,
|
|
333
346
|
outputPath,
|
|
@@ -338,6 +351,7 @@ export async function generateTaskDagUseCase(input) {
|
|
|
338
351
|
governanceProfile,
|
|
339
352
|
profileRouting,
|
|
340
353
|
reviewPacket,
|
|
354
|
+
validation,
|
|
341
355
|
warnings: validateSummary.warnings,
|
|
342
356
|
run: runSummary,
|
|
343
357
|
};
|
|
@@ -184,9 +184,9 @@ export const COMMAND_DEFINITIONS = [
|
|
|
184
184
|
adapter: "required",
|
|
185
185
|
tier: "operator",
|
|
186
186
|
intent: "Check local loop-agent runtime health.",
|
|
187
|
-
usage: "doctor",
|
|
188
|
-
handler: async ({ adapter, repoRoot }) => {
|
|
189
|
-
await runDoctor(adapter, repoRoot);
|
|
187
|
+
usage: "doctor [--json] [--context]",
|
|
188
|
+
handler: async ({ adapter, repoRoot, rest }) => {
|
|
189
|
+
await runDoctor(adapter, repoRoot, rest.filter((arg) => Boolean(arg)));
|
|
190
190
|
},
|
|
191
191
|
},
|
|
192
192
|
{
|
|
@@ -345,10 +345,11 @@ export const COMMAND_DEFINITIONS = [
|
|
|
345
345
|
name: "stats",
|
|
346
346
|
adapter: "required",
|
|
347
347
|
tier: "operator",
|
|
348
|
-
intent: "Inspect historical step duration statistics.",
|
|
349
|
-
usage: "stats",
|
|
350
|
-
|
|
351
|
-
|
|
348
|
+
intent: "Inspect historical step duration statistics, or skill-resolution context usage.",
|
|
349
|
+
usage: "stats | stats context [--last <n>] [--json]",
|
|
350
|
+
subcommands: ["context"],
|
|
351
|
+
handler: async ({ repoRoot, subcommand, rest }) => {
|
|
352
|
+
await runStats(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
|
|
352
353
|
},
|
|
353
354
|
},
|
|
354
355
|
{
|
package/dist/cli/program.js
CHANGED
|
@@ -92,25 +92,27 @@ function commandChain(command) {
|
|
|
92
92
|
}
|
|
93
93
|
return names;
|
|
94
94
|
}
|
|
95
|
-
async function
|
|
96
|
-
const
|
|
97
|
-
const
|
|
95
|
+
async function dispatchRegisteredCommand(commandObject, defaultRepoRoot, rawArgs) {
|
|
96
|
+
const chain = commandChain(commandObject);
|
|
97
|
+
const command = chain[0] ?? "";
|
|
98
|
+
const [subcommand, ...rest] = chain.length <= 1 ? rawArgs : [chain[1], ...chain.slice(2), ...rawArgs];
|
|
99
|
+
const options = collectGlobalOptions(commandObject);
|
|
100
|
+
const quiet = (command === "doctor" &&
|
|
101
|
+
[...rest, ...rawArgs].some((token) => token === "--json")) ||
|
|
102
|
+
(command === "stats" &&
|
|
103
|
+
subcommand === "context" &&
|
|
104
|
+
[...rest, ...rawArgs].some((token) => token === "--json"));
|
|
105
|
+
const repoRoot = resolveRepoRootOnly(commandObject, defaultRepoRoot, {
|
|
106
|
+
quiet,
|
|
107
|
+
});
|
|
98
108
|
const adapter = await resolveAdapter(repoRoot, options.adapter);
|
|
99
|
-
if (options.adapter)
|
|
109
|
+
if (options.adapter && !quiet)
|
|
100
110
|
console.log(`[loop-agent] adapter: ${adapter.name}`);
|
|
101
|
-
|
|
102
|
-
console.log(`[loop-agent] repo root: ${repoRoot}`);
|
|
103
|
-
return {
|
|
111
|
+
const ctx = {
|
|
104
112
|
adapter,
|
|
105
113
|
repoRoot,
|
|
106
114
|
explicitWorktree: options.worktree,
|
|
107
115
|
};
|
|
108
|
-
}
|
|
109
|
-
async function dispatchRegisteredCommand(commandObject, defaultRepoRoot, rawArgs) {
|
|
110
|
-
const chain = commandChain(commandObject);
|
|
111
|
-
const command = chain[0] ?? "";
|
|
112
|
-
const [subcommand, ...rest] = chain.length <= 1 ? rawArgs : [chain[1], ...chain.slice(2), ...rawArgs];
|
|
113
|
-
const ctx = await resolveDispatchContext(commandObject, defaultRepoRoot);
|
|
114
116
|
await runCommanderAction(ctx, command, subcommand, rest);
|
|
115
117
|
}
|
|
116
118
|
async function runCommanderAction(ctx, command, subcommand, rest) {
|
|
@@ -119,7 +121,7 @@ async function runCommanderAction(ctx, command, subcommand, rest) {
|
|
|
119
121
|
await runInspect(ctx.adapter, ctx.repoRoot);
|
|
120
122
|
return;
|
|
121
123
|
case "doctor":
|
|
122
|
-
await runDoctor(ctx.adapter, ctx.repoRoot);
|
|
124
|
+
await runDoctor(ctx.adapter, ctx.repoRoot, compactArgs([subcommand, ...rest]));
|
|
123
125
|
return;
|
|
124
126
|
case "docs":
|
|
125
127
|
if (subcommand === "audit") {
|
|
@@ -184,7 +186,7 @@ async function runCommanderAction(ctx, command, subcommand, rest) {
|
|
|
184
186
|
await runCloseout(ctx.repoRoot, compactArgs([subcommand, ...rest]));
|
|
185
187
|
return;
|
|
186
188
|
case "stats":
|
|
187
|
-
await runStats(ctx.repoRoot);
|
|
189
|
+
await runStats(ctx.repoRoot, compactArgs([subcommand, ...rest]));
|
|
188
190
|
return;
|
|
189
191
|
case "plan":
|
|
190
192
|
if (subcommand === "list" || subcommand === undefined) {
|