@tea-agent/loop-agent 0.35.0-beta.2 → 0.35.1-beta.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/AGENTS.md +108 -108
- package/CHANGELOG.md +55 -4
- package/README.md +165 -165
- package/bin/agent-worker.js +0 -0
- package/bin/loop-agent.js +21 -21
- package/dist/application/task-lifecycle/advance.js +1 -0
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/init-upgrade.js +351 -19
- package/dist/commands/init.js +14 -67
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/commands/run-dag-progress.js +14 -0
- package/dist/commands/task-advance.js +33 -3
- package/dist/shared/operator/capabilities.js +38 -1
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/worker/console/chat/pi-runtime.js +41 -25
- package/dist/worker/console/chat/routes.js +27 -4
- package/dist/worker/console/operation-runner.js +24 -0
- package/dist/worker/console/operation-wait.js +241 -0
- package/dist/worker/console/operator-actions.js +58 -0
- package/dist/worker/console/static/assets/{index-hJqCPs_g.css → index-Dups4sSM.css} +1 -1
- package/dist/worker/console/static/assets/index-SjjjZnV3.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +141 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -2
- package/dist/worker/console/static-src/operator-chat/useComposer.js +30 -7
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-layout.d.ts +36 -36
- package/dist/worker/observe/static/dom.js +220 -220
- package/dist/worker/observe/static/relations.js +133 -133
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/views/batch.js +227 -227
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -492
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/frontend-prewrite-gate.js +9 -1
- package/dist/workflows/dag/init-hybrid.js +2 -0
- package/docs/architecture/evolution.md +73 -73
- package/docs/architecture/system-overview.md +100 -100
- package/docs/architecture/worker-and-feature.md +122 -122
- package/docs/skills/README.md +7 -7
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/backend-test-result.schema.json +99 -99
- package/docs/templates/evaluation/agents-map-slim-v1.md +87 -87
- package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -153
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -42
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
- package/docs/templates/frontend-eval/metrics.md +138 -138
- package/docs/templates/frontend-eval/smoke-targets.md +53 -53
- package/docs/templates/frontend-task-constraints.md +35 -35
- package/docs/templates/frontend-task-requirement.md +70 -70
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/init-managed-agents.md +156 -154
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
- package/docs/templates/knowledge-sync-dag.json +178 -178
- package/docs/templates/knowledge-sync-draft.schema.json +71 -71
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/harness.json +5 -2
- package/package.json +1 -1
- package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
- package/scripts/kb-graph-incremental-prepare.mjs +0 -0
- package/scripts/kb-graph-materialize.mjs +105 -105
- package/scripts/kb-graph-promote.mjs +164 -164
- package/scripts/kb-query.mjs +554 -554
- package/skills/agent-worker/SKILL.md +48 -48
- package/skills/agent-worker/references/agent-worker-operator.md +159 -159
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +0 -0
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +0 -0
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +0 -0
- package/skills/browser-tools/browser-content.js +103 -103
- package/skills/browser-tools/browser-cookies.js +35 -35
- package/skills/browser-tools/browser-eval.js +53 -53
- package/skills/browser-tools/browser-hn-scraper.js +108 -108
- package/skills/browser-tools/browser-nav.js +44 -44
- package/skills/browser-tools/browser-pick.js +162 -162
- package/skills/browser-tools/browser-screenshot.js +34 -34
- package/skills/browser-tools/browser-start.js +86 -86
- package/skills/browser-tools/package-lock.json +2556 -2556
- package/skills/browser-tools/package.json +19 -19
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/local-jacoco-coverage/scripts/run-coverage-analysis.sh +0 -0
- package/skills/local-jacoco-coverage/scripts/start-jacoco-agent.sh +0 -0
- package/skills/loop-agent/SKILL.md +1 -0
- package/skills/loop-agent/references/command-reference.md +641 -639
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/playwright-cli/references/element-attributes.md +23 -23
- package/skills/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/playwright-cli/references/request-mocking.md +87 -87
- package/skills/playwright-cli/references/running-code.md +241 -241
- package/skills/playwright-cli/references/session-management.md +225 -225
- package/skills/playwright-cli/references/storage-state.md +275 -275
- package/skills/playwright-cli/references/test-generation.md +433 -433
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
- package/dist/worker/console/static/assets/index-fsjzREob.js +0 -56
|
@@ -46,6 +46,60 @@ function isHomeFingerprint(value) {
|
|
|
46
46
|
keys[1] === "sha256" &&
|
|
47
47
|
isSha256(value.sha256));
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Shape guard for the controller-owned gitignore migration assessment.
|
|
51
|
+
* `assessGitignoreMigration` is the single schema fact source; status/report
|
|
52
|
+
* reads fail closed on any shape that the writer could not have produced
|
|
53
|
+
* (AC-005). `queryError` may be absent and arrays may be empty; extra keys are
|
|
54
|
+
* tolerated for forward compatibility.
|
|
55
|
+
*/
|
|
56
|
+
function isGitignoreMigrationAssessment(value) {
|
|
57
|
+
if (!isRecord(value))
|
|
58
|
+
return false;
|
|
59
|
+
const assessment = value;
|
|
60
|
+
if (assessment.schemaVersion !== 1)
|
|
61
|
+
return false;
|
|
62
|
+
if (typeof assessment.managedBlockRefreshed !== "boolean")
|
|
63
|
+
return false;
|
|
64
|
+
if (typeof assessment.needsAgentsReview !== "boolean")
|
|
65
|
+
return false;
|
|
66
|
+
if (!isRecord(assessment.git))
|
|
67
|
+
return false;
|
|
68
|
+
const git = assessment.git;
|
|
69
|
+
if (typeof git.isRepo !== "boolean")
|
|
70
|
+
return false;
|
|
71
|
+
if (git.queryError !== undefined && typeof git.queryError !== "string") {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(assessment.harnessTracked) ||
|
|
75
|
+
!assessment.harnessTracked.every((entry) => typeof entry === "string")) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (!Array.isArray(assessment.agentsTracked) ||
|
|
79
|
+
!assessment.agentsTracked.every((entry) => typeof entry === "string")) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(assessment.stagedRisk) ||
|
|
83
|
+
!assessment.stagedRisk.every((entry) => typeof entry === "string")) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(assessment.followUp) ||
|
|
87
|
+
!assessment.followUp.every((entry) => typeof entry === "string")) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (!Array.isArray(assessment.recommendedCommands))
|
|
91
|
+
return false;
|
|
92
|
+
for (const entry of assessment.recommendedCommands) {
|
|
93
|
+
if (!isRecord(entry))
|
|
94
|
+
return false;
|
|
95
|
+
if (typeof entry.command !== "string" || typeof entry.note !== "string") {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return (assessment.status === "clean" ||
|
|
100
|
+
assessment.status === "recommended" ||
|
|
101
|
+
assessment.status === "blocked");
|
|
102
|
+
}
|
|
49
103
|
function cloneJson(value) {
|
|
50
104
|
return JSON.parse(JSON.stringify(value));
|
|
51
105
|
}
|
|
@@ -171,6 +225,143 @@ function assertTerminalCoherence(state) {
|
|
|
171
225
|
throw new Error("invalid init upgrade status/phase coherence");
|
|
172
226
|
}
|
|
173
227
|
}
|
|
228
|
+
/** Read-only git query; never mutates the user index (no status refresh, no write). */
|
|
229
|
+
function runGitQuery(cwd, args) {
|
|
230
|
+
return new Promise((resolve) => {
|
|
231
|
+
const child = spawn("git", ["-C", cwd, ...args], {
|
|
232
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
233
|
+
});
|
|
234
|
+
const stdoutChunks = [];
|
|
235
|
+
const stderrChunks = [];
|
|
236
|
+
child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
237
|
+
child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
238
|
+
child.on("error", (error) => {
|
|
239
|
+
resolve({ ok: false, stdout: "", stderr: error.message });
|
|
240
|
+
});
|
|
241
|
+
child.on("close", (code) => {
|
|
242
|
+
resolve({
|
|
243
|
+
ok: code === 0,
|
|
244
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
245
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function splitNulPaths(output) {
|
|
251
|
+
return output.split("\0").filter((entry) => entry.length > 0);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Gitignore migration assessment (PRD 2026-08-13): inventories tracked
|
|
255
|
+
* `.harness/**` / `.agents/**` with read-only git queries after the managed
|
|
256
|
+
* block has converged, and recommends index-only untrack commands. The
|
|
257
|
+
* controller never mutates the user Git index and never deletes local files.
|
|
258
|
+
*/
|
|
259
|
+
export async function assessGitignoreMigration(repoRoot, input) {
|
|
260
|
+
const base = {
|
|
261
|
+
schemaVersion: 1,
|
|
262
|
+
managedBlockRefreshed: input.managedBlockRefreshed,
|
|
263
|
+
harnessTracked: [],
|
|
264
|
+
agentsTracked: [],
|
|
265
|
+
stagedRisk: [],
|
|
266
|
+
needsAgentsReview: false,
|
|
267
|
+
followUp: [
|
|
268
|
+
"run `git status --short` and confirm only the intended untrack changes",
|
|
269
|
+
"confirm the .harness/ and .agents/ directories still exist locally after untracking",
|
|
270
|
+
"run `loop-agent init doctor` to re-check the gitignore loop-agent block",
|
|
271
|
+
],
|
|
272
|
+
};
|
|
273
|
+
const inside = await runGitQuery(repoRoot, ["rev-parse", "--is-inside-work-tree"]);
|
|
274
|
+
if (!inside.ok) {
|
|
275
|
+
return {
|
|
276
|
+
...base,
|
|
277
|
+
git: {
|
|
278
|
+
isRepo: false,
|
|
279
|
+
queryError: inside.stderr.trim() || "not inside a Git work tree",
|
|
280
|
+
},
|
|
281
|
+
recommendedCommands: [],
|
|
282
|
+
status: "blocked",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const tracked = await runGitQuery(repoRoot, [
|
|
286
|
+
"ls-files",
|
|
287
|
+
"-z",
|
|
288
|
+
"--cached",
|
|
289
|
+
"--",
|
|
290
|
+
".harness",
|
|
291
|
+
".agents",
|
|
292
|
+
]);
|
|
293
|
+
if (!tracked.ok) {
|
|
294
|
+
return {
|
|
295
|
+
...base,
|
|
296
|
+
git: {
|
|
297
|
+
isRepo: true,
|
|
298
|
+
queryError: tracked.stderr.trim() || "git ls-files query failed",
|
|
299
|
+
},
|
|
300
|
+
recommendedCommands: [],
|
|
301
|
+
status: "blocked",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const staged = await runGitQuery(repoRoot, [
|
|
305
|
+
"diff",
|
|
306
|
+
"--cached",
|
|
307
|
+
"--name-only",
|
|
308
|
+
"-z",
|
|
309
|
+
]);
|
|
310
|
+
if (!staged.ok) {
|
|
311
|
+
return {
|
|
312
|
+
...base,
|
|
313
|
+
git: {
|
|
314
|
+
isRepo: true,
|
|
315
|
+
queryError: staged.stderr.trim() || "git diff --cached query failed",
|
|
316
|
+
},
|
|
317
|
+
recommendedCommands: [],
|
|
318
|
+
status: "blocked",
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const trackedPaths = splitNulPaths(tracked.stdout);
|
|
322
|
+
const harnessTracked = trackedPaths.filter((entry) => entry.startsWith(".harness/"));
|
|
323
|
+
const agentsTracked = trackedPaths.filter((entry) => entry.startsWith(".agents/"));
|
|
324
|
+
const stagedRisk = splitNulPaths(staged.stdout);
|
|
325
|
+
const needsAgentsReview = agentsTracked.length > 0;
|
|
326
|
+
if (stagedRisk.length > 0) {
|
|
327
|
+
// Fail closed: staged entries make any untrack advice ambiguous.
|
|
328
|
+
return {
|
|
329
|
+
...base,
|
|
330
|
+
git: { isRepo: true },
|
|
331
|
+
harnessTracked,
|
|
332
|
+
agentsTracked,
|
|
333
|
+
stagedRisk,
|
|
334
|
+
needsAgentsReview,
|
|
335
|
+
recommendedCommands: [],
|
|
336
|
+
status: "blocked",
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
const recommendedCommands = [];
|
|
340
|
+
if (harnessTracked.length > 0) {
|
|
341
|
+
recommendedCommands.push({
|
|
342
|
+
command: "git rm -r --cached --ignore-unmatch .harness",
|
|
343
|
+
note: "untracks tracked .harness runtime facts; index-only, working-tree files are kept",
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
if (agentsTracked.length > 0) {
|
|
347
|
+
recommendedCommands.push({
|
|
348
|
+
command: "git rm -r --cached --ignore-unmatch .agents",
|
|
349
|
+
note: "untracks tracked .agents local skills; index-only, working-tree files are kept; review team-shareable customizations first",
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
...base,
|
|
354
|
+
git: { isRepo: true },
|
|
355
|
+
harnessTracked,
|
|
356
|
+
agentsTracked,
|
|
357
|
+
stagedRisk,
|
|
358
|
+
needsAgentsReview,
|
|
359
|
+
recommendedCommands,
|
|
360
|
+
status: harnessTracked.length > 0 || agentsTracked.length > 0
|
|
361
|
+
? "recommended"
|
|
362
|
+
: "clean",
|
|
363
|
+
};
|
|
364
|
+
}
|
|
174
365
|
function runRoot(repoRoot) {
|
|
175
366
|
return path.join(repoRoot, RUN_ROOT);
|
|
176
367
|
}
|
|
@@ -240,6 +431,7 @@ async function writeControllerAnchor(input) {
|
|
|
240
431
|
throw new Error("init upgrade controller authority anchor path escapes Git directory");
|
|
241
432
|
}
|
|
242
433
|
let authoritativeHomeFingerprint;
|
|
434
|
+
let authoritativeGitignoreMigrationSha256;
|
|
243
435
|
if (input.initialize) {
|
|
244
436
|
if (await exists(location.file)) {
|
|
245
437
|
throw new Error("init upgrade controller authority anchor already exists");
|
|
@@ -252,6 +444,14 @@ async function writeControllerAnchor(input) {
|
|
|
252
444
|
else {
|
|
253
445
|
const current = await readControllerAnchor(location.repoRoot, input.state);
|
|
254
446
|
authoritativeHomeFingerprint = current.homeFingerprint;
|
|
447
|
+
// Preserve the gitignore migration artifact binding across later
|
|
448
|
+
// mergeAuthority/completion writes; fail closed on a corrupt or
|
|
449
|
+
// non-SHA-256 binding instead of silently dropping or propagating it.
|
|
450
|
+
const digest = input.gitignoreMigrationSha256 ?? current.gitignoreMigrationSha256;
|
|
451
|
+
if (digest !== undefined && !isSha256(digest)) {
|
|
452
|
+
throw new Error("init upgrade controller authority gitignore migration digest binding is invalid");
|
|
453
|
+
}
|
|
454
|
+
authoritativeGitignoreMigrationSha256 = digest;
|
|
255
455
|
}
|
|
256
456
|
await writeJsonAtomic(location.file, {
|
|
257
457
|
schemaVersion: 1,
|
|
@@ -259,6 +459,9 @@ async function writeControllerAnchor(input) {
|
|
|
259
459
|
runId: input.state.runId,
|
|
260
460
|
controllerIdentity: input.state.controllerIdentity,
|
|
261
461
|
homeFingerprint: authoritativeHomeFingerprint,
|
|
462
|
+
...(authoritativeGitignoreMigrationSha256 !== undefined
|
|
463
|
+
? { gitignoreMigrationSha256: authoritativeGitignoreMigrationSha256 }
|
|
464
|
+
: {}),
|
|
262
465
|
...(input.mergeAuthority ? { mergeAuthority: input.mergeAuthority } : {}),
|
|
263
466
|
...(input.completion ? { completion: input.completion } : {}),
|
|
264
467
|
});
|
|
@@ -451,7 +654,7 @@ async function saveState(directory, state) {
|
|
|
451
654
|
state.updatedAt = new Date().toISOString();
|
|
452
655
|
await writeJsonAtomic(path.join(directory, "state.json"), state);
|
|
453
656
|
}
|
|
454
|
-
function toResult(state, verification) {
|
|
657
|
+
function toResult(state, verification, gitignoreMigration) {
|
|
455
658
|
return {
|
|
456
659
|
runId: state.runId,
|
|
457
660
|
status: state.status,
|
|
@@ -462,9 +665,10 @@ function toResult(state, verification) {
|
|
|
462
665
|
humanDecisions: state.humanDecisions,
|
|
463
666
|
nextAction: state.nextAction,
|
|
464
667
|
...(verification ? { verification } : {}),
|
|
668
|
+
...(gitignoreMigration ? { gitignoreMigration } : {}),
|
|
465
669
|
};
|
|
466
670
|
}
|
|
467
|
-
function renderReport(state, verification) {
|
|
671
|
+
function renderReport(state, verification, gitignoreMigration) {
|
|
468
672
|
const lines = [
|
|
469
673
|
"# loop-agent init upgrade report",
|
|
470
674
|
"",
|
|
@@ -480,6 +684,47 @@ function renderReport(state, verification) {
|
|
|
480
684
|
`- retired path actions: ${state.actionReceipts.filter((action) => action.type === "migrate-owned-file" || action.type === "remove-owned-file" || action.type === "remove-empty-directory").length}`,
|
|
481
685
|
`- remaining model merge tasks: ${state.mergeTasks.length}`,
|
|
482
686
|
`- remaining human decisions: ${state.humanDecisions.length}`,
|
|
687
|
+
`- gitignore migration assessment: \`.harness/init-upgrades/${state.runId}/gitignore-migration.json\` (index-only untrack guidance; the controller never modifies the Git index)`,
|
|
688
|
+
...(gitignoreMigration
|
|
689
|
+
? [
|
|
690
|
+
"",
|
|
691
|
+
"## Gitignore migration assessment",
|
|
692
|
+
"",
|
|
693
|
+
`- status: \`${gitignoreMigration.status}\``,
|
|
694
|
+
`- managed block refreshed: \`${gitignoreMigration.managedBlockRefreshed}\``,
|
|
695
|
+
`- tracked \`.harness\` inventory: ${gitignoreMigration.harnessTracked.length > 0
|
|
696
|
+
? gitignoreMigration.harnessTracked
|
|
697
|
+
.map((entry) => `\`${entry}\``)
|
|
698
|
+
.join(", ")
|
|
699
|
+
: "(none)"}`,
|
|
700
|
+
`- tracked \`.agents\` inventory: ${gitignoreMigration.agentsTracked.length > 0
|
|
701
|
+
? gitignoreMigration.agentsTracked
|
|
702
|
+
.map((entry) => `\`${entry}\``)
|
|
703
|
+
.join(", ")
|
|
704
|
+
: "(none)"}`,
|
|
705
|
+
...(gitignoreMigration.stagedRisk.length > 0
|
|
706
|
+
? [
|
|
707
|
+
`- staged risk: ${gitignoreMigration.stagedRisk
|
|
708
|
+
.map((entry) => `\`${entry}\``)
|
|
709
|
+
.join(", ")}`,
|
|
710
|
+
]
|
|
711
|
+
: []),
|
|
712
|
+
...(gitignoreMigration.git.queryError
|
|
713
|
+
? [
|
|
714
|
+
`- git query blocker: \`${gitignoreMigration.git.queryError}\``,
|
|
715
|
+
]
|
|
716
|
+
: []),
|
|
717
|
+
`- \`.agents\` review required: \`${gitignoreMigration.needsAgentsReview}\``,
|
|
718
|
+
"- recommended commands:",
|
|
719
|
+
...(gitignoreMigration.recommendedCommands.length > 0
|
|
720
|
+
? gitignoreMigration.recommendedCommands.map((entry) => ` - \`${entry.command}\` — ${entry.note}`)
|
|
721
|
+
: [" - (none)"]),
|
|
722
|
+
"- follow-up:",
|
|
723
|
+
...(gitignoreMigration.followUp.length > 0
|
|
724
|
+
? gitignoreMigration.followUp.map((entry) => ` - ${entry}`)
|
|
725
|
+
: [" - (none)"]),
|
|
726
|
+
]
|
|
727
|
+
: []),
|
|
483
728
|
"",
|
|
484
729
|
"## Next action",
|
|
485
730
|
"",
|
|
@@ -501,8 +746,8 @@ function renderReport(state, verification) {
|
|
|
501
746
|
];
|
|
502
747
|
return `${lines.join("\n")}\n`;
|
|
503
748
|
}
|
|
504
|
-
async function writeReport(directory, state, verification) {
|
|
505
|
-
await writeFile(path.join(directory, "final-report.md"), renderReport(state, verification), "utf-8");
|
|
749
|
+
async function writeReport(directory, state, verification, gitignoreMigration) {
|
|
750
|
+
await writeFile(path.join(directory, "final-report.md"), renderReport(state, verification, gitignoreMigration), "utf-8");
|
|
506
751
|
}
|
|
507
752
|
async function assertInactive(input) {
|
|
508
753
|
let activity;
|
|
@@ -2303,14 +2548,62 @@ async function readValidatedStatusState(repoRoot, runId) {
|
|
|
2303
2548
|
throw new Error("Controller identity drift detected for persisted init upgrade state");
|
|
2304
2549
|
}
|
|
2305
2550
|
}
|
|
2306
|
-
return { state, directory };
|
|
2551
|
+
return { state, directory, anchor };
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Read-only projection of the controller-owned gitignore migration assessment
|
|
2555
|
+
* for status/report reads. The artifact is written by executeUpgrade after the
|
|
2556
|
+
* managed block has converged; a missing artifact is tolerated only while the
|
|
2557
|
+
* run has not yet reached that write point (DISCOVER/VERSION_GATE). Every
|
|
2558
|
+
* other missing, malformed, or shape-invalid artifact fails closed so
|
|
2559
|
+
* status/report never trust mutable or incomplete content (AC-005).
|
|
2560
|
+
*/
|
|
2561
|
+
async function readGitignoreMigrationProjection(directory, state, gitignoreMigrationSha256) {
|
|
2562
|
+
const file = path.join(directory, "gitignore-migration.json");
|
|
2563
|
+
let rawBytes;
|
|
2564
|
+
let raw;
|
|
2565
|
+
try {
|
|
2566
|
+
rawBytes = await readFile(file);
|
|
2567
|
+
raw = rawBytes.toString("utf-8");
|
|
2568
|
+
}
|
|
2569
|
+
catch (error) {
|
|
2570
|
+
const cause = error;
|
|
2571
|
+
if (cause.code === "ENOENT") {
|
|
2572
|
+
if (state.phase === "DISCOVER" || state.phase === "VERSION_GATE") {
|
|
2573
|
+
return undefined;
|
|
2574
|
+
}
|
|
2575
|
+
throw new Error(`init upgrade integrity error: gitignore migration artifact is missing for run ${state.runId} (phase ${state.phase}); refusing to trust an incomplete run`);
|
|
2576
|
+
}
|
|
2577
|
+
throw error;
|
|
2578
|
+
}
|
|
2579
|
+
let parsed;
|
|
2580
|
+
try {
|
|
2581
|
+
parsed = JSON.parse(raw);
|
|
2582
|
+
}
|
|
2583
|
+
catch {
|
|
2584
|
+
throw new Error(`init upgrade integrity error: gitignore migration artifact is malformed JSON for run ${state.runId} (${file})`);
|
|
2585
|
+
}
|
|
2586
|
+
if (!isGitignoreMigrationAssessment(parsed)) {
|
|
2587
|
+
throw new Error(`init upgrade integrity error: gitignore migration artifact has an invalid shape for run ${state.runId} (${file})`);
|
|
2588
|
+
}
|
|
2589
|
+
// The artifact exists, so this run has generated an assessment; the
|
|
2590
|
+
// controller authority binding must exist, be a legal SHA-256, and match
|
|
2591
|
+
// the raw artifact bytes on disk (fail closed otherwise).
|
|
2592
|
+
if (gitignoreMigrationSha256 === undefined || !isSha256(gitignoreMigrationSha256)) {
|
|
2593
|
+
throw new Error(`init upgrade integrity error: gitignore migration artifact digest binding is missing or invalid for run ${state.runId}`);
|
|
2594
|
+
}
|
|
2595
|
+
if (sha256(rawBytes) !== gitignoreMigrationSha256) {
|
|
2596
|
+
throw new Error(`init upgrade integrity error: gitignore migration artifact digest does not match the controller authority binding for run ${state.runId}`);
|
|
2597
|
+
}
|
|
2598
|
+
return parsed;
|
|
2307
2599
|
}
|
|
2308
2600
|
async function readStatusResult(repoRoot, runId) {
|
|
2309
|
-
const { state } = await readValidatedStatusState(repoRoot, runId);
|
|
2310
|
-
if (state.status !== "completed")
|
|
2311
|
-
return toResult(state);
|
|
2601
|
+
const { state, directory, anchor } = await readValidatedStatusState(repoRoot, runId);
|
|
2602
|
+
if (state.status !== "completed") {
|
|
2603
|
+
return toResult(state, undefined, await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256));
|
|
2604
|
+
}
|
|
2312
2605
|
const authenticated = await readFreshCompletedVerification(repoRoot, state);
|
|
2313
|
-
return toResult(state, authenticated.verification);
|
|
2606
|
+
return toResult(state, authenticated.verification, await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256));
|
|
2314
2607
|
}
|
|
2315
2608
|
async function executeUpgrade(directory, state, input) {
|
|
2316
2609
|
const identity = await readIdentity();
|
|
@@ -2445,17 +2738,50 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2445
2738
|
}
|
|
2446
2739
|
}
|
|
2447
2740
|
state.humanDecisions = post.humanDecisions;
|
|
2741
|
+
// Gitignore migration assessment: after the managed block has converged,
|
|
2742
|
+
// inventory tracked .harness/.agents paths with read-only git queries and
|
|
2743
|
+
// recommend index-only untrack commands. The controller never mutates the
|
|
2744
|
+
// user Git index; suggested commands only change the index and keep the
|
|
2745
|
+
// working tree intact.
|
|
2746
|
+
const gitignoreMigration = await assessGitignoreMigration(state.repoRoot, {
|
|
2747
|
+
managedBlockRefreshed: !post.deterministicActions.some((action) => action.type === "refresh-managed-block" &&
|
|
2748
|
+
action.path === ".gitignore"),
|
|
2749
|
+
});
|
|
2750
|
+
await writeJsonAtomic(path.join(directory, "gitignore-migration.json"), gitignoreMigration);
|
|
2751
|
+
// Bind the raw on-disk artifact bytes to the Git-dir controller authority
|
|
2752
|
+
// before any needs-human-decision/model-merge/completed return; later
|
|
2753
|
+
// mergeAuthority/completion anchor writes preserve this binding.
|
|
2754
|
+
await writeControllerAnchor({
|
|
2755
|
+
state,
|
|
2756
|
+
gitignoreMigrationSha256: sha256(await readFile(path.join(directory, "gitignore-migration.json"))),
|
|
2757
|
+
});
|
|
2758
|
+
if (gitignoreMigration.status === "blocked" ||
|
|
2759
|
+
gitignoreMigration.needsAgentsReview) {
|
|
2760
|
+
// Route through the existing needs-human-decision pause: `.agents`
|
|
2761
|
+
// tracked content must be reviewed (PRD §7) and staged blockers must be
|
|
2762
|
+
// resolved before safe untrack guidance can be given (PRD §8).
|
|
2763
|
+
post.humanDecisions.push(gitignoreMigration.status === "blocked"
|
|
2764
|
+
? {
|
|
2765
|
+
path: ".harness/** (gitignore migration)",
|
|
2766
|
+
reason: "staged changes or a failed git query block safe untrack guidance; resolve the blocker, then rerun the migration assessment",
|
|
2767
|
+
}
|
|
2768
|
+
: {
|
|
2769
|
+
path: ".agents/** (gitignore migration)",
|
|
2770
|
+
reason: "tracked .agents content may include team-shareable customizations; review before running the suggested untrack commands",
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
state.humanDecisions = post.humanDecisions;
|
|
2448
2774
|
if (post.humanDecisions.length > 0) {
|
|
2449
2775
|
state.mergeTasks = [];
|
|
2450
2776
|
state.mergeGuard = undefined;
|
|
2451
2777
|
state.status = "needs-human-decision";
|
|
2452
2778
|
state.phase = "MODEL_MERGE";
|
|
2453
2779
|
state.nextAction =
|
|
2454
|
-
"Resolve the recorded destructive, credential, or ambiguous decisions; the controller will not overwrite uncertain user intent.";
|
|
2780
|
+
"Resolve the recorded destructive, credential, or ambiguous decisions; the controller will not overwrite uncertain user intent. For gitignore migration: review tracked .agents customizations or resolve the staged blocker first, then rerun the assessment so index-only untrack commands can be recommended.";
|
|
2455
2781
|
await writeJsonAtomic(path.join(directory, "merge-tasks.json"), state.mergeTasks);
|
|
2456
2782
|
await saveState(directory, state);
|
|
2457
|
-
await writeReport(directory, state);
|
|
2458
|
-
return toResult(state);
|
|
2783
|
+
await writeReport(directory, state, undefined, gitignoreMigration);
|
|
2784
|
+
return toResult(state, undefined, gitignoreMigration);
|
|
2459
2785
|
}
|
|
2460
2786
|
if (post.modelMergeTasks.length > 0) {
|
|
2461
2787
|
const activeTask = post.modelMergeTasks[0];
|
|
@@ -2487,8 +2813,8 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2487
2813
|
state.nextAction = `Complete only task ${activeTask.taskId} within allowedPaths=[${activeTask.allowedPaths.join(", ")}], then call init upgrade --continue. The controller will reject every other workspace change and all symlink/realpath escapes.`;
|
|
2488
2814
|
await writeJsonAtomic(path.join(directory, "merge-tasks.json"), state.mergeTasks);
|
|
2489
2815
|
await saveState(directory, state);
|
|
2490
|
-
await writeReport(directory, state);
|
|
2491
|
-
return toResult(state);
|
|
2816
|
+
await writeReport(directory, state, undefined, gitignoreMigration);
|
|
2817
|
+
return toResult(state, undefined, gitignoreMigration);
|
|
2492
2818
|
}
|
|
2493
2819
|
state.mergeTasks = [];
|
|
2494
2820
|
state.mergeGuard = undefined;
|
|
@@ -2504,7 +2830,7 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2504
2830
|
state.nextAction =
|
|
2505
2831
|
"Init upgrade completed; reload the host session so updated AGENTS, skills, and recovery extensions are discovered.";
|
|
2506
2832
|
// The report is controller-produced before its exact bytes are anchored.
|
|
2507
|
-
await writeReport(directory, state, verification);
|
|
2833
|
+
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2508
2834
|
const finalReportSha256 = sha256(await readFile(path.join(directory, "final-report.md")));
|
|
2509
2835
|
await writeControllerAnchor({
|
|
2510
2836
|
state,
|
|
@@ -2529,8 +2855,8 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2529
2855
|
: "Verification failed. Inspect verification.json; repair only the controller-returned concrete merge path or address the reported external blocker, then use --continue.";
|
|
2530
2856
|
}
|
|
2531
2857
|
await saveState(directory, state);
|
|
2532
|
-
await writeReport(directory, state, verification);
|
|
2533
|
-
return toResult(state, verification);
|
|
2858
|
+
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2859
|
+
return toResult(state, verification, gitignoreMigration);
|
|
2534
2860
|
}
|
|
2535
2861
|
export async function runInitUpgrade(input) {
|
|
2536
2862
|
const repoRoot = path.resolve(input.repoRoot);
|
|
@@ -2541,14 +2867,20 @@ export async function runInitUpgrade(input) {
|
|
|
2541
2867
|
if (mode === "status")
|
|
2542
2868
|
return await readStatusResult(repoRoot, input.runId);
|
|
2543
2869
|
if (mode === "report") {
|
|
2544
|
-
const { state } = await readValidatedStatusState(repoRoot, input.runId);
|
|
2870
|
+
const { state, directory, anchor } = await readValidatedStatusState(repoRoot, input.runId);
|
|
2545
2871
|
if (state.status === "completed") {
|
|
2546
2872
|
const authenticated = await readFreshCompletedVerification(repoRoot, state);
|
|
2873
|
+
// Completed report bytes are anchored by finalReportSha256; still
|
|
2874
|
+
// verify the gitignore migration artifact binding so shape-valid
|
|
2875
|
+
// tampering fails closed here as well.
|
|
2876
|
+
await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256);
|
|
2547
2877
|
return { markdown: authenticated.reportMarkdown };
|
|
2548
2878
|
}
|
|
2549
2879
|
// Non-completed reports are projections of the validated state, never an
|
|
2550
2880
|
// unauthenticated read of a mutable run-local report file.
|
|
2551
|
-
return {
|
|
2881
|
+
return {
|
|
2882
|
+
markdown: renderReport(state, undefined, await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256)),
|
|
2883
|
+
};
|
|
2552
2884
|
}
|
|
2553
2885
|
const runId = mode === "continue"
|
|
2554
2886
|
? input.runId
|
package/dist/commands/init.js
CHANGED
|
@@ -846,55 +846,19 @@ function mergeGitignoreManagedBlock(existing, block) {
|
|
|
846
846
|
return `${block}\n`;
|
|
847
847
|
return `${existing.trimEnd()}\n\n${block}\n`;
|
|
848
848
|
}
|
|
849
|
-
/**
|
|
849
|
+
/**
|
|
850
|
+
* Shared ignore rules for loop-agent runtime facts. The four directories are
|
|
851
|
+
* local, rebuildable runtime surface: every developer runs loop-agent init
|
|
852
|
+
* after cloning, so nothing inside them is team-shareable by default.
|
|
853
|
+
* `scripts/` and `ai_workspace/loop-agent/` stay on the shared commit surface.
|
|
854
|
+
*/
|
|
850
855
|
export function buildManagedGitignoreBlock() {
|
|
851
856
|
return [
|
|
852
857
|
GITIGNORE_BLOCK_START,
|
|
853
|
-
"# loop-agent runtime:
|
|
854
|
-
"",
|
|
855
|
-
"
|
|
856
|
-
".harness/tasks/*",
|
|
857
|
-
"!.harness/tasks/.gitkeep",
|
|
858
|
-
"",
|
|
859
|
-
"# DAG runs",
|
|
860
|
-
".harness/dag-runs/active/*",
|
|
861
|
-
"!.harness/dag-runs/active/.gitkeep",
|
|
862
|
-
".harness/dag-runs/completed/*",
|
|
863
|
-
"!.harness/dag-runs/completed/.gitkeep",
|
|
864
|
-
".harness/dag-runs/paused/*",
|
|
865
|
-
"!.harness/dag-runs/paused/.gitkeep",
|
|
866
|
-
"",
|
|
867
|
-
"# one-shot executor runs",
|
|
868
|
-
".harness/runs/*",
|
|
869
|
-
"!.harness/runs/.gitkeep",
|
|
870
|
-
"!.harness/runs/active/",
|
|
871
|
-
"!.harness/runs/active/.gitkeep",
|
|
872
|
-
"!.harness/runs/completed/",
|
|
873
|
-
"!.harness/runs/completed/.gitkeep",
|
|
874
|
-
"!.harness/runs/failed/",
|
|
875
|
-
"!.harness/runs/failed/.gitkeep",
|
|
876
|
-
"",
|
|
877
|
-
"# session / cache / logs / recomputable surface state",
|
|
878
|
-
".harness/live/",
|
|
879
|
-
".harness/cache/",
|
|
880
|
-
".harness/*.log",
|
|
881
|
-
".harness/init-surface.json",
|
|
882
|
-
".harness/init-upgrades/",
|
|
883
|
-
"",
|
|
884
|
-
"# Eval Lab runtime (candidates, campaigns, scorecards, dogfood scratch)",
|
|
885
|
-
".harness/evaluation/",
|
|
886
|
-
"",
|
|
887
|
-
"# fullstack / worker dogfood evidence (local campaign scratch)",
|
|
888
|
-
".harness/dogfood-evidence/",
|
|
889
|
-
"",
|
|
890
|
-
"# worker task pool",
|
|
891
|
-
".harness/task-pool/*",
|
|
858
|
+
"# loop-agent runtime: local, rebuildable facts; do not ignore scripts/ or ai_workspace/loop-agent/",
|
|
859
|
+
".harness/",
|
|
860
|
+
".agents/",
|
|
892
861
|
".task-pool/",
|
|
893
|
-
"",
|
|
894
|
-
"# repo-local skill dependencies installed inside projected skills",
|
|
895
|
-
".agents/skills/*/node_modules/",
|
|
896
|
-
"",
|
|
897
|
-
"# multi-worktree parallel mode",
|
|
898
862
|
".worktrees/",
|
|
899
863
|
GITIGNORE_BLOCK_END,
|
|
900
864
|
].join("\n");
|
|
@@ -1087,26 +1051,8 @@ async function ensureHarnessDirs(repoRoot, written) {
|
|
|
1087
1051
|
await mkdir(path.join(repoRoot, dir), { recursive: true });
|
|
1088
1052
|
written.push(dir);
|
|
1089
1053
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
".harness/dag-runs/active/.gitkeep",
|
|
1093
|
-
".harness/dag-runs/completed/.gitkeep",
|
|
1094
|
-
".harness/dag-runs/paused/.gitkeep",
|
|
1095
|
-
".harness/runs/.gitkeep",
|
|
1096
|
-
".harness/runs/active/.gitkeep",
|
|
1097
|
-
".harness/runs/completed/.gitkeep",
|
|
1098
|
-
".harness/runs/failed/.gitkeep",
|
|
1099
|
-
];
|
|
1100
|
-
for (const relativePath of placeholders) {
|
|
1101
|
-
const target = path.join(repoRoot, relativePath);
|
|
1102
|
-
try {
|
|
1103
|
-
await access(target);
|
|
1104
|
-
}
|
|
1105
|
-
catch {
|
|
1106
|
-
await writeFile(target, "", "utf8");
|
|
1107
|
-
written.push(relativePath);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1054
|
+
// No .gitkeep placeholders: .harness/ is fully ignored by the managed
|
|
1055
|
+
// gitignore block, and runtime dirs are recreated on demand by init/doctor.
|
|
1110
1056
|
}
|
|
1111
1057
|
async function writeCompatPrompts(input) {
|
|
1112
1058
|
for (const [name, content] of Object.entries(COMPAT_PROMPTS)) {
|
|
@@ -2490,6 +2436,7 @@ export function buildInitInstructions(input) {
|
|
|
2490
2436
|
"When the user says `loop-agent初始化更新`, `loop-agent 初始化更新`, `更新 loop-agent 初始化内容`, or an equivalent write request, run `loop-agent init upgrade --repo-root . --json` as the single controller-owned entry. Complete returned single-file allowedPaths merge tasks and call `--continue` until a stable terminal result; do not stop at check-update, needs-safe-update, needs-model-merge, or verification-pending.",
|
|
2491
2437
|
"Explicit `检查初始化更新`, `初始化更新校验`, or `只检查,不要修改` stays strictly read-only: run only `loop-agent init check-update --repo-root . --markdown` and do not create an upgrade run.",
|
|
2492
2438
|
"Upgrade manages project `.opencode/plugins/`, `.pi/extensions/`, and `.pi/settings.json` by nested merge. Pi must trust the project before loading these settings; do not read or write `~/.pi/agent/settings.json` by default.",
|
|
2439
|
+
"The upgrade also writes a read-only gitignore migration assessment (`.harness/init-upgrades/<run-id>/gitignore-migration.json`): tracked `.harness/**` and `.agents/**` get index-only `git rm -r --cached --ignore-unmatch` guidance (working-tree files are kept). Pause for human review when `.agents` content is tracked or when staged changes / git query failures block safe guidance; never run git rm yourself.",
|
|
2493
2440
|
"",
|
|
2494
2441
|
"## Apply Defaults",
|
|
2495
2442
|
"",
|
|
@@ -2505,7 +2452,7 @@ export function buildInitInstructions(input) {
|
|
|
2505
2452
|
"- Enrich the root `README.md`: keep the deterministic project title and the loop-agent managed block intact, and fill the human-authored sections (项目概览, 技术栈与目录结构, 开发与验证) from the target project's actual files. The root README must serve both as a human-first project entry and as an agent work entry; replace the initialization-model supplement comments when the project files provide the information.",
|
|
2506
2453
|
`- Populate \`${governanceRoot}/verification-matrix.md\` with the target project's actual quick, standard, and full verification commands derived from its real language and toolchain, keeping the governance rows intact.`,
|
|
2507
2454
|
"- Project repo-local skills live in `.agents/skills/`. Do not create a root `skills/` directory in the target project; the package's bundled `skills/` remains the built-in fallback.",
|
|
2508
|
-
"- Merge a loop-agent managed block into `.gitignore` that ignores
|
|
2455
|
+
"- Merge a loop-agent managed block into `.gitignore` that ignores exactly the four local, rebuildable runtime directories (`.harness/`, `.agents/`, `.task-pool/`, `.worktrees/`) while keeping `scripts/` and `ai_workspace/loop-agent/` on the shared commit surface; keep all user rules outside the block untouched.",
|
|
2509
2456
|
"- Do not copy examples by default; examples stay bundled in the tool and are available through `loop-agent examples`.",
|
|
2510
2457
|
"- Add or update a loop-agent managed block in AGENTS.md.",
|
|
2511
2458
|
"- The generated AGENTS.md must include documentation convergence and structured DAG write-boundary rules so target projects keep the same working discipline as this repository.",
|
|
@@ -3382,7 +3329,7 @@ export async function runInitDoctor(input) {
|
|
|
3382
3329
|
? await readFile(gitignorePath, "utf-8")
|
|
3383
3330
|
: "";
|
|
3384
3331
|
add("gitignore loop-agent block", gitignoreContent.includes(GITIGNORE_BLOCK_START) &&
|
|
3385
|
-
gitignoreContent.includes(".harness/
|
|
3332
|
+
gitignoreContent.includes(".harness/"), ".gitignore managed runtime ignores");
|
|
3386
3333
|
const requiredScripts = Object.keys(INIT_SCRIPT_FILES);
|
|
3387
3334
|
const missingScripts = [];
|
|
3388
3335
|
for (const script of requiredScripts) {
|
|
@@ -37,17 +37,17 @@ export function parseLoopBenchmarkArgs(args) {
|
|
|
37
37
|
return { json, markdown, outputPath };
|
|
38
38
|
}
|
|
39
39
|
export function printLoopBenchmarkUsage() {
|
|
40
|
-
console.log(`usage: loop-benchmark [options]
|
|
41
|
-
|
|
42
|
-
Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
|
|
43
|
-
|
|
44
|
-
Options:
|
|
45
|
-
--json Emit JSON (default when no format flag is set)
|
|
46
|
-
--markdown Emit Markdown report
|
|
47
|
-
--output <path> Write Markdown report to a repo-relative or absolute path
|
|
48
|
-
-h, --help Show this help
|
|
49
|
-
|
|
50
|
-
Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
|
|
40
|
+
console.log(`usage: loop-benchmark [options]
|
|
41
|
+
|
|
42
|
+
Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--json Emit JSON (default when no format flag is set)
|
|
46
|
+
--markdown Emit Markdown report
|
|
47
|
+
--output <path> Write Markdown report to a repo-relative or absolute path
|
|
48
|
+
-h, --help Show this help
|
|
49
|
+
|
|
50
|
+
Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
|
|
51
51
|
Recommendation never changes convergence.enabled default.`);
|
|
52
52
|
}
|
|
53
53
|
export async function runLoopBenchmark(repoRoot, rawArgs) {
|