@tea-agent/loop-agent 0.9.0 → 0.10.0-alpha.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 +8 -0
- package/CHANGELOG.md +92 -11
- package/README.md +69 -5
- package/dist/application/dag/args.js +4 -14
- package/dist/application/dag/generate-task-dag.js +32 -2
- package/dist/application/dag/run-dag.js +1 -27
- package/dist/application/dag/validate-dag.js +2 -2
- package/dist/application/loop/run-action.js +0 -4
- package/dist/cli/command-definitions.js +7 -11
- package/dist/cli/program.js +9 -21
- package/dist/commands/cursor-prompt.js +42 -82
- package/dist/commands/dag-approve.js +36 -0
- package/dist/commands/dag-reconcile-run.js +118 -0
- package/dist/commands/delegate.js +75 -77
- package/dist/commands/doctor.js +0 -18
- package/dist/commands/init.js +60 -40
- package/dist/commands/instructions.js +7 -10
- package/dist/commands/loop.js +4 -20
- package/dist/executors/config-core.js +0 -51
- package/dist/executors/dag-pi-executor.js +1 -1
- package/dist/executors/dag.js +0 -1
- package/dist/executors/index.js +0 -2
- package/dist/executors/model-routing.js +9 -9
- package/dist/executors/shell-executor.js +1 -1
- package/dist/governance/checks.js +6 -3
- package/dist/governance/manifest-types.js +33 -2
- package/dist/infrastructure/harness/loop-action-store.js +0 -3
- package/dist/records/harvest.js +2 -23
- package/dist/records/one-shot-runs.js +1 -1
- package/dist/shared/artifacts-core.js +24 -5
- package/dist/shared/output-truncation.js +37 -0
- package/dist/shared/package-metadata.js +353 -0
- package/dist/{executors/cursor-executor.js → sidecars/cursor-prompt/executor.js} +2 -42
- package/dist/sidecars/cursor-prompt/index.js +3 -0
- package/dist/sidecars/cursor-prompt/stream.js +121 -0
- package/dist/task/config-types.js +29 -12
- package/dist/task/delegate.js +9 -21
- package/dist/task/runtime.js +1 -2
- package/dist/worker/cli.js +243 -0
- package/dist/worker/closeout/apply.js +73 -0
- package/dist/worker/closeout/preview.js +30 -0
- package/dist/worker/delivery/final-verification.js +194 -0
- package/dist/worker/delivery/git-transaction.js +354 -0
- package/dist/worker/delivery/package.js +502 -0
- package/dist/worker/feature/decision-loader.js +68 -0
- package/dist/worker/feature/discover.js +14 -0
- package/dist/worker/feature/next-action.js +74 -0
- package/dist/worker/feature/reducer.js +133 -0
- package/dist/worker/feature/review.js +502 -0
- package/dist/worker/feature/run.js +365 -0
- package/dist/worker/feature/types.js +1 -0
- package/dist/worker/follow-up/approve.js +270 -0
- package/dist/worker/follow-up/factory.js +234 -0
- package/dist/worker/follow-up/paths.js +25 -0
- package/dist/worker/follow-up/policy.js +26 -0
- package/dist/worker/follow-up/schema.js +93 -0
- package/dist/worker/follow-up/store.js +96 -0
- package/dist/worker/loop-agent/loop-agent-client.js +329 -126
- package/dist/worker/metrics/projector.js +139 -0
- package/dist/worker/observability/read-model.js +282 -15
- package/dist/worker/observe/paths.js +17 -5
- package/dist/worker/observe/static/app.js +443 -61
- package/dist/worker/observe/static/index.html +3 -1
- package/dist/worker/observe/static/styles.css +85 -18
- package/dist/worker/pool/run-store.js +14 -2
- package/dist/worker/pool/validation.js +59 -0
- package/dist/worker/preflight.js +49 -1
- package/dist/worker/report/morning-report.js +41 -6
- package/dist/worker/run-task/run-task.js +23 -13
- package/dist/worker/runner/run-ready.js +89 -11
- package/dist/worker/task-spec/schema.js +0 -1
- package/dist/workflows/dag/convergence/controller.js +1 -1
- package/dist/workflows/dag/executor-registry.js +0 -2
- package/dist/workflows/dag/governance-profile.js +10 -0
- package/dist/workflows/dag/init-hybrid.js +601 -26
- package/dist/workflows/dag/lifecycle.js +146 -0
- package/dist/workflows/dag/node-execution.js +64 -7
- package/dist/workflows/dag/prompt.js +16 -0
- package/dist/workflows/dag/report.js +2 -0
- package/dist/workflows/dag/runner.js +176 -119
- package/dist/workflows/dag/scheduler.js +7 -2
- package/dist/workflows/dag/skill-snapshot.js +527 -0
- package/dist/workflows/dag/types.js +45 -9
- package/dist/workflows/dag/validate.js +5 -8
- package/dist/workflows/loop/actions/dag-action.js +0 -2
- package/dist/workflows/loop/actions/shared.js +1 -1
- package/dist/workflows/loop/actions.js +14 -31
- package/dist/workflows/loop/benchmark.js +1 -1
- package/dist/workflows/loop/index.js +1 -1
- package/dist/workflows/loop/policy/auto-policy.js +22 -14
- package/dist/workflows/loop/policy/path-patterns.js +13 -0
- package/docs/README.md +35 -16
- package/docs/agent-dag-recovery-playbook.md +1 -1
- package/docs/architecture/README.md +26 -0
- package/docs/architecture/dag-execution.md +134 -0
- package/docs/architecture/evolution.md +52 -0
- package/docs/architecture/facts-and-state.md +58 -0
- package/docs/architecture/runtime-boundaries.md +41 -15
- package/docs/architecture/system-overview.md +93 -0
- package/docs/architecture/worker-and-feature.md +81 -0
- package/docs/cursor-prompt-sidecar.md +36 -0
- package/docs/decisions/README.md +13 -1
- package/docs/design/README.md +39 -13
- package/docs/development-principles.md +1 -1
- package/docs/exec-plans/active/README.md +2 -2
- package/docs/exec-plans/completed/README.md +14 -1
- package/docs/feature-workflow.md +44 -4
- package/docs/init-surface.manifest.json +63 -1
- package/docs/loop-agent-harness.md +65 -3
- package/docs/progress/README.md +27 -0
- package/docs/reports/README.md +74 -8
- package/docs/skills/README.md +2 -1
- package/docs/skills/vetted-skill-registry.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +4 -2
- package/docs/templates/agent-dag.base.json +0 -5
- package/docs/templates/agent-dag.final-verification.json +0 -5
- package/docs/templates/agent-dag.schema.json +1 -2
- package/docs/templates/agent-dag.supervised-implementation.json +1 -6
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +131 -0
- package/docs/templates/backend-test-dag.json +213 -0
- package/docs/templates/backend-test-dag.retrospect.prompt.md +128 -0
- package/docs/templates/backend-test-dag.review-cases.prompt.md +85 -0
- package/docs/templates/frontend-design-contract.md +33 -0
- package/docs/templates/frontend-task-constraints.md +25 -0
- package/docs/templates/frontend-task-requirement.md +61 -0
- package/docs/templates/harness.schema.json +8 -5
- package/docs/templates/hybrid-dag.json +1 -6
- package/docs/templates/init-evolution-review.md +4 -2
- package/docs/templates/interactive-ui-round2-experiment.md +1 -1
- package/docs/templates/product-line/task.yaml +0 -1
- package/docs/templates/worker-dogfood-evidence.md +28 -0
- package/docs/templates/worker-dogfood-setup.md +20 -0
- package/docs/verification-matrix.md +17 -0
- package/examples/decision-gate-agent-dag.json +87 -33
- package/examples/example-dag.json +0 -5
- package/examples/hybrid-loop-agent-dag.json +0 -5
- package/harness.json +6 -11
- package/package.json +22 -44
- package/scripts/check-product-line-docs.sh +10 -3
- package/skills/agent-worker/SKILL.md +37 -0
- package/skills/agent-worker/references/agent-worker-operator.md +43 -0
- package/skills/frontend-design-review/SKILL.md +59 -0
- package/skills/frontend-design-review/references/review-checklist.md +37 -0
- package/skills/frontend-implementation/SKILL.md +48 -0
- package/skills/frontend-implementation/references/code-standards.md +34 -0
- package/skills/frontend-implementation/references/design-spec.md +46 -0
- package/skills/frontend-implementation/references/node-contracts.md +32 -0
- package/skills/frontend-review/SKILL.md +53 -0
- package/skills/frontend-review/references/review-findings.md +42 -0
- package/skills/frontend-verification/SKILL.md +40 -0
- package/skills/frontend-verification/references/verification-checklist.md +56 -0
- package/skills/grill-me/SKILL.md +10 -0
- package/skills/grill-with-docs/SKILL.md +88 -0
- package/skills/grill-with-docs/adr-format.md +47 -0
- package/skills/grill-with-docs/context-format.md +60 -0
- package/skills/init-capability-evolution/SKILL.md +1 -0
- package/skills/loop-agent/SKILL.md +11 -9
- package/skills/loop-agent/references/command-reference.md +28 -15
- package/skills/loop-agent/references/docs-converge.md +126 -0
- package/skills/loop-agent/references/harness-policy.md +7 -7
- package/skills/loop-agent/references/hybrid-dag.md +13 -15
- package/skills/loop-agent/references/long-running-loop.md +4 -6
- package/skills/loop-agent/references/multi-worktree.md +6 -6
- package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +14 -11
- package/skills/using-git-worktrees/SKILL.md +215 -0
- package/dist/commands/cursor-worker.js +0 -43
- package/dist/cursor-worker-entry.js +0 -8
- package/dist/executors/cursor-artifacts.js +0 -33
- package/dist/executors/cursor-execution-log.js +0 -81
- package/dist/executors/cursor-executor-artifacts.js +0 -134
- package/dist/executors/cursor-run.js +0 -115
- package/dist/executors/cursor-tool.js +0 -94
- package/dist/executors/cursor-worker-client.js +0 -223
- package/dist/executors/cursor-worker-protocol.js +0 -18
- package/dist/executors/cursor-worker-server.js +0 -54
- package/dist/executors/cursor-worker.js +0 -3
- package/dist/executors/cursor.js +0 -6
- package/dist/executors/dag-cursor-executor.js +0 -87
- package/dist/workflows/loop/actions/cursor-fix.js +0 -191
- package/dist/workflows/loop/policy/cursor-fix-policy.js +0 -31
- package/docs/cursor-executor-usage.md +0 -25
- package/docs/dynamic-workflow-dag-engine-roadmap.md +0 -1749
package/dist/commands/init.js
CHANGED
|
@@ -367,41 +367,54 @@ set -euo pipefail
|
|
|
367
367
|
ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
|
|
368
368
|
cd "\${ROOT_DIR}"
|
|
369
369
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
missing=()
|
|
384
|
-
for ref in "\${required_refs[@]}"; do
|
|
385
|
-
[[ -f "skills/loop-agent/\${ref}" ]] || missing+=("\${ref}")
|
|
386
|
-
done
|
|
370
|
+
check_skill() {
|
|
371
|
+
local skill_name="$1"
|
|
372
|
+
shift
|
|
373
|
+
local skill_dir="skills/\${skill_name}"
|
|
374
|
+
local skill_md="\${skill_dir}/SKILL.md"
|
|
375
|
+
local missing=()
|
|
376
|
+
|
|
377
|
+
if [[ ! -f "\${skill_md}" ]]; then
|
|
378
|
+
echo "skill entry 检查失败:缺少 \${skill_md}" >&2
|
|
379
|
+
exit 1
|
|
380
|
+
fi
|
|
381
|
+
grep -Eq "^name:[[:space:]]*\${skill_name}[[:space:]]*$" "\${skill_md}" || missing+=("frontmatter name")
|
|
382
|
+
grep -Eq '^description:[[:space:]]*[^[:space:]]' "\${skill_md}" || missing+=("frontmatter description")
|
|
387
383
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
384
|
+
local ref
|
|
385
|
+
for ref in "$@"; do
|
|
386
|
+
[[ -f "\${skill_dir}/\${ref}" ]] || missing+=("\${ref}")
|
|
387
|
+
grep -Fq "\${ref}" "\${skill_md}" || missing+=("SKILL.md -> \${ref}")
|
|
388
|
+
done
|
|
389
|
+
if (( \${#missing[@]} > 0 )); then
|
|
390
|
+
echo "skill entry 检查失败(\${skill_name}):" >&2
|
|
391
|
+
printf ' - %s\\n' "\${missing[@]}" >&2
|
|
392
|
+
exit 1
|
|
393
|
+
fi
|
|
392
394
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
395
|
+
local line_count
|
|
396
|
+
line_count="$(wc -l < "\${skill_md}" | tr -d ' ')"
|
|
397
|
+
if [[ "\${line_count}" =~ ^[0-9]+$ && "\${line_count}" -gt 180 ]]; then
|
|
398
|
+
echo "skill entry 检查失败:\${skill_md} 行数为 \${line_count},入口应保持精简" >&2
|
|
399
|
+
exit 1
|
|
400
|
+
fi
|
|
401
|
+
echo "skill entry 检查通过:\${skill_name} references ok, lines=\${line_count}"
|
|
402
|
+
}
|
|
398
403
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
404
|
+
check_skill "loop-agent" \\
|
|
405
|
+
"references/harness-policy.md" \\
|
|
406
|
+
"references/hybrid-dag.md" \\
|
|
407
|
+
"references/verification-and-failure-handling.md" \\
|
|
408
|
+
"references/command-reference.md"
|
|
409
|
+
check_skill "agent-worker" "references/agent-worker-operator.md"
|
|
403
410
|
|
|
404
|
-
|
|
411
|
+
description_line="$(grep -E '^description:' skills/agent-worker/SKILL.md | head -n 1 | tr '[:upper:]' '[:lower:]')"
|
|
412
|
+
for term in "agent-worker" "feature packet" "taskspec" "task pool" "self-host" "candidate" "loop-agent"; do
|
|
413
|
+
if [[ "\${description_line}" != *"\${term}"* ]]; then
|
|
414
|
+
echo "skill entry 检查失败(agent-worker):description 缺少触发/路由词 \${term}" >&2
|
|
415
|
+
exit 1
|
|
416
|
+
fi
|
|
417
|
+
done
|
|
405
418
|
`;
|
|
406
419
|
const INIT_CHECK_REPO_SH = `#!/usr/bin/env bash
|
|
407
420
|
set -euo pipefail
|
|
@@ -443,11 +456,20 @@ fi
|
|
|
443
456
|
feature_dirs=()
|
|
444
457
|
for root in features product/features dogfood/features; do
|
|
445
458
|
[[ -d "\${root}" ]] || continue
|
|
446
|
-
while IFS= read -r
|
|
447
|
-
|
|
448
|
-
|
|
459
|
+
while IFS= read -r feature_dir; do
|
|
460
|
+
if [[ ! -f "\${feature_dir}/acceptance.yaml" ]]; then
|
|
461
|
+
echo "product-line docs check failed: incomplete feature packet missing acceptance.yaml: \${feature_dir}" >&2
|
|
462
|
+
exit 1
|
|
463
|
+
fi
|
|
464
|
+
feature_dirs+=("\${feature_dir}")
|
|
465
|
+
done < <(find "\${root}" -mindepth 1 -maxdepth 1 -type d | sort)
|
|
449
466
|
done
|
|
450
467
|
|
|
468
|
+
if [[ \${#feature_dirs[@]} -eq 0 ]]; then
|
|
469
|
+
echo "product-line docs check skipped: no feature packets found"
|
|
470
|
+
exit 0
|
|
471
|
+
fi
|
|
472
|
+
|
|
451
473
|
for feature_dir in "\${feature_dirs[@]}"; do
|
|
452
474
|
echo "==> validate product-line feature: \${feature_dir}"
|
|
453
475
|
agent-worker task validate-feature "\${feature_dir}"
|
|
@@ -811,11 +833,9 @@ function buildHarness(input) {
|
|
|
811
833
|
const existingExecutors = isRecord(input.existing.executors)
|
|
812
834
|
? input.existing.executors
|
|
813
835
|
: {};
|
|
814
|
-
// Cursor executor
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
delete templateExecutors.cursor;
|
|
818
|
-
}
|
|
836
|
+
// Cursor executor must never be projected; Pi-only governed runtime.
|
|
837
|
+
delete templateExecutors.cursor;
|
|
838
|
+
delete existingExecutors.cursor;
|
|
819
839
|
const mergedExecutors = mergeRecord(templateExecutors, existingExecutors);
|
|
820
840
|
// requiresApiKey is Cursor-facing only; never project it on pi.
|
|
821
841
|
if (isRecord(mergedExecutors.pi) && "requiresApiKey" in mergedExecutors.pi) {
|
|
@@ -1937,7 +1957,7 @@ export async function checkInitUpdate(input) {
|
|
|
1937
1957
|
deterministicActions.push({
|
|
1938
1958
|
type: "strip-pi-requires-api-key",
|
|
1939
1959
|
path: "harness.json",
|
|
1940
|
-
reason: "executors.pi.requiresApiKey is unused; strip it while
|
|
1960
|
+
reason: "executors.pi.requiresApiKey is unused; strip it while preserving Pi model configuration",
|
|
1941
1961
|
});
|
|
1942
1962
|
}
|
|
1943
1963
|
}
|
|
@@ -153,23 +153,20 @@ export async function buildInstructions(repoRoot, stage, taskId) {
|
|
|
153
153
|
],
|
|
154
154
|
}));
|
|
155
155
|
}
|
|
156
|
-
const artifactSteps = [
|
|
157
|
-
"analyze",
|
|
158
|
-
"plan",
|
|
159
|
-
"implement",
|
|
160
|
-
"verify",
|
|
161
|
-
"retrospective",
|
|
162
|
-
];
|
|
163
156
|
return refreshInstructionBlockers(withMissingDependencies({
|
|
164
157
|
...base,
|
|
165
158
|
outputPath: path.join(taskDir, "artifacts"),
|
|
166
159
|
dependencies: [paths.taskConfigPath, paths.statePath],
|
|
167
160
|
template: {
|
|
168
|
-
requiredHeadings:
|
|
161
|
+
requiredHeadings: [
|
|
162
|
+
getArtifactRelativePath("implement"),
|
|
163
|
+
getArtifactRelativePath("verify"),
|
|
164
|
+
],
|
|
169
165
|
},
|
|
170
166
|
completionCriteria: [
|
|
171
|
-
"
|
|
172
|
-
"
|
|
167
|
+
"修改记录.md and 验证结果.md are promote-run bridge artifacts filled from completed DAG/one-shot evidence",
|
|
168
|
+
"do not hand-write L1 分析报告/实现计划/复盘报告; those are not seeded or required",
|
|
169
|
+
"verification artifact summarizes available completed-run evidence; inspect the run facts for exact shell commands and full output",
|
|
173
170
|
],
|
|
174
171
|
}));
|
|
175
172
|
}
|
package/dist/commands/loop.js
CHANGED
|
@@ -7,10 +7,9 @@ function usage() {
|
|
|
7
7
|
" loop status <task-id> [--json]",
|
|
8
8
|
" loop run <task-id> --action shell-verify [--command <cmd>]... [--timeout-ms N] [--cwd <repo-relative-dir>]",
|
|
9
9
|
" loop run <task-id> --action pi-review [--provider <id>] [--model <id>] [--timeout-ms N] [--cwd <repo-relative-dir>]",
|
|
10
|
-
" loop run <task-id> --action
|
|
11
|
-
" loop run <task-id> --action dag [--execute] [--dag <path>] [--run-id <id>] [--max-concurrent N] [--cwd <repo-relative-dir>] [--no-cursor]",
|
|
10
|
+
" loop run <task-id> --action dag [--execute] [--dag <path>] [--run-id <id>] [--max-concurrent N] [--cwd <repo-relative-dir>]",
|
|
12
11
|
" loop run <task-id> --action workflow [--profile pr-review|--workflow <path>] [--execute] [--run-id <id>] [--changed-file <path>]... [--cwd <repo-relative-dir>]",
|
|
13
|
-
" loop run <task-id> --auto [--max-rounds N] [--
|
|
12
|
+
" loop run <task-id> --auto [--max-rounds N] [--approval-granted]",
|
|
14
13
|
" loop record-round <task-id> --action <name> --result <summary> --lesson <summary> --next <summary> --decision <continue|pause|blocked|complete> [--verification <summary>] [--failure-category <category>] [--ref <path>]...",
|
|
15
14
|
" loop add-signal <task-id> --type <human_followup|approval|scope_changed|review_feedback> --message <text> [--urgent] [--ref <path>]...",
|
|
16
15
|
" loop closeout <task-id>",
|
|
@@ -144,7 +143,7 @@ export async function runLoop(repoRoot, args) {
|
|
|
144
143
|
}
|
|
145
144
|
const record = await runLoopAuto(repoRoot, taskId, {
|
|
146
145
|
maxRounds,
|
|
147
|
-
|
|
146
|
+
approvalGranted: rest.includes("--approval-granted"),
|
|
148
147
|
});
|
|
149
148
|
console.log(JSON.stringify(record, null, 2));
|
|
150
149
|
return;
|
|
@@ -174,20 +173,6 @@ export async function runLoop(repoRoot, args) {
|
|
|
174
173
|
console.log(JSON.stringify(record, null, 2));
|
|
175
174
|
return;
|
|
176
175
|
}
|
|
177
|
-
if (action === "cursor-fix") {
|
|
178
|
-
const record = await runLoopAction({
|
|
179
|
-
action,
|
|
180
|
-
repoRoot,
|
|
181
|
-
taskId,
|
|
182
|
-
options: {
|
|
183
|
-
timeoutMs,
|
|
184
|
-
cwd: readFlag(rest, "--cwd"),
|
|
185
|
-
model: readFlag(rest, "--model"),
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
console.log(JSON.stringify(record, null, 2));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
176
|
if (action === "dag") {
|
|
192
177
|
const maxConcurrentRaw = readFlag(rest, "--max-concurrent");
|
|
193
178
|
let maxConcurrent;
|
|
@@ -208,7 +193,6 @@ export async function runLoop(repoRoot, args) {
|
|
|
208
193
|
dagPath: readFlag(rest, "--dag"),
|
|
209
194
|
runId: readFlag(rest, "--run-id"),
|
|
210
195
|
maxConcurrent,
|
|
211
|
-
noCursor: rest.includes("--no-cursor"),
|
|
212
196
|
},
|
|
213
197
|
});
|
|
214
198
|
console.log(JSON.stringify(record, null, 2));
|
|
@@ -248,7 +232,7 @@ export async function runLoop(repoRoot, args) {
|
|
|
248
232
|
return;
|
|
249
233
|
}
|
|
250
234
|
if (action !== "shell-verify") {
|
|
251
|
-
throw new Error("loop run currently supports --action shell-verify|pi-review|
|
|
235
|
+
throw new Error("loop run currently supports --action shell-verify|pi-review|dag|workflow");
|
|
252
236
|
}
|
|
253
237
|
const record = await runLoopAction({
|
|
254
238
|
action: "shell-verify",
|
|
@@ -1,56 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_CURSOR_MODEL } from './cursor-executor.js';
|
|
2
|
-
import { resolveExecutorModelMatrix } from './model-routing.js';
|
|
3
1
|
export const TASK_COMPLEXITY_TO_DAG = {
|
|
4
2
|
small: 'LOW',
|
|
5
3
|
medium: 'MED',
|
|
6
4
|
large: 'HIGH',
|
|
7
5
|
};
|
|
8
|
-
export function resolveCursorModel(overrides = {}, cursorConfig) {
|
|
9
|
-
return overrides.taskModel ?? cursorConfig?.defaultModel ?? DEFAULT_CURSOR_MODEL;
|
|
10
|
-
}
|
|
11
|
-
export function resolveCursorModelForTaskConfig(taskConfig, cursorConfig) {
|
|
12
|
-
if (taskConfig.cursorModel?.trim()) {
|
|
13
|
-
return taskConfig.cursorModel.trim();
|
|
14
|
-
}
|
|
15
|
-
const dagLevel = TASK_COMPLEXITY_TO_DAG[taskConfig.complexity ?? 'medium'];
|
|
16
|
-
const matrix = resolveExecutorModelMatrix('cursor', cursorConfig);
|
|
17
|
-
return matrix[dagLevel] ?? resolveCursorModel({}, cursorConfig);
|
|
18
|
-
}
|
|
19
|
-
export function resolveTaskExecutor(taskConfig, override) {
|
|
20
|
-
if (override)
|
|
21
|
-
return override;
|
|
22
|
-
return taskConfig.executor ?? 'pi';
|
|
23
|
-
}
|
|
24
|
-
export function resolveCursorExecutorConfig(manifest) {
|
|
25
|
-
const config = manifest.executors?.cursor;
|
|
26
|
-
if (config?.enabled === false)
|
|
27
|
-
return undefined;
|
|
28
|
-
return config;
|
|
29
|
-
}
|
|
30
|
-
export function assertCursorExecutorAvailable(manifest) {
|
|
31
|
-
const config = resolveCursorExecutorConfig(manifest);
|
|
32
|
-
if (!config) {
|
|
33
|
-
throw new Error('cursor executor is not registered in harness.json executors.cursor');
|
|
34
|
-
}
|
|
35
|
-
return config;
|
|
36
|
-
}
|
|
37
|
-
export function validateCursorTaskPreflight(taskConfig) {
|
|
38
|
-
if (taskConfig.executor !== 'cursor')
|
|
39
|
-
return;
|
|
40
|
-
const allowed = taskConfig.allowedPaths ?? [];
|
|
41
|
-
if (allowed.length === 0) {
|
|
42
|
-
// Read-only analysis tasks may omit allowedPaths; write tasks must declare scope explicitly.
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export function assertCursorWriteTaskScope(taskConfig, changedPaths) {
|
|
47
|
-
if (taskConfig.executor !== 'cursor')
|
|
48
|
-
return;
|
|
49
|
-
if ((taskConfig.allowedPaths ?? []).length > 0)
|
|
50
|
-
return;
|
|
51
|
-
const codeChanges = changedPaths.filter((filePath) => !filePath.startsWith('.harness/tasks/'));
|
|
52
|
-
if (codeChanges.length > 0) {
|
|
53
|
-
throw new Error('cursor write task refused: allowedPaths is empty but git diff contains code changes. ' +
|
|
54
|
-
'Declare allowedPaths explicitly or keep the task read-only.');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { writeTextArtifactFile } from "../infrastructure/harness/artifact-store.js";
|
|
3
3
|
import { executePiStep, } from "./pi-executor.js";
|
|
4
|
-
import { redactPromptForLog, truncateOutput } from "
|
|
4
|
+
import { redactPromptForLog, truncateOutput, } from "../shared/output-truncation.js";
|
|
5
5
|
import { readGitStatusPorcelain, runPostRunWriteGuard, } from "./shell-write-guard.js";
|
|
6
6
|
export const DAG_PI_READONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
7
7
|
export const DAG_PI_WRITE_TOOLS = [
|
package/dist/executors/dag.js
CHANGED
package/dist/executors/index.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { DEFAULT_DAG_EXECUTOR_MODELS, } from '../workflows/dag/types.js';
|
|
2
|
-
export const DEFAULT_DAG_CURSOR_MODEL = "composer-2.5";
|
|
3
2
|
export const DEFAULT_DAG_MODELS = {
|
|
4
3
|
HIGH: "gpt-5.5",
|
|
5
|
-
MED: "
|
|
6
|
-
LOW: "
|
|
4
|
+
MED: "glm-5.2",
|
|
5
|
+
LOW: "gpt-5.3-codex-spark",
|
|
7
6
|
};
|
|
8
7
|
/**
|
|
9
8
|
* DAG executor model tier keys that may carry a per-complexity override.
|
|
10
9
|
*/
|
|
11
10
|
const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
|
|
12
11
|
/**
|
|
13
|
-
* Resolve the DAG executor model matrix for
|
|
14
|
-
* `executors.<name>` config.
|
|
12
|
+
* Resolve the DAG executor model matrix for Pi from harness `executors.pi`.
|
|
15
13
|
*
|
|
16
14
|
* Priority per tier (LOW/MED/HIGH):
|
|
17
15
|
* 1. execConfig[tier] (truthy and !== "default" sentinel)
|
|
18
16
|
* 2. execConfig.defaultModel (truthy and !== "default" sentinel)
|
|
19
|
-
* 3. DEFAULT_DAG_EXECUTOR_MODELS[
|
|
17
|
+
* 3. DEFAULT_DAG_EXECUTOR_MODELS.pi[tier]
|
|
20
18
|
*
|
|
21
19
|
* The "default" literal (injected by the schema `.default("default")`) and
|
|
22
20
|
* absent/undefined both mean "no override, fall through".
|
|
@@ -38,15 +36,17 @@ export function resolveExecutorModelMatrix(executor, execConfig) {
|
|
|
38
36
|
};
|
|
39
37
|
}
|
|
40
38
|
/**
|
|
41
|
-
* Resolve
|
|
42
|
-
* Single entry point for DAG generation and --strict-models baseline resolution.
|
|
39
|
+
* Resolve the Pi DAG executor model matrix from a harness manifest.
|
|
43
40
|
*/
|
|
44
41
|
export function resolveExecutorModelMatrices(manifest) {
|
|
45
42
|
return {
|
|
46
43
|
pi: resolveExecutorModelMatrix("pi", manifest.executors?.pi),
|
|
47
|
-
cursor: resolveExecutorModelMatrix("cursor", manifest.executors?.cursor),
|
|
48
44
|
};
|
|
49
45
|
}
|
|
46
|
+
/** @deprecated Prefer resolveExecutorModelMatrices; alias kept for call-site clarity. */
|
|
47
|
+
export function resolvePiModelMatrix(manifest) {
|
|
48
|
+
return resolveExecutorModelMatrices(manifest).pi;
|
|
49
|
+
}
|
|
50
50
|
export function resolveModelSelection(manifest, taskConfig, step, options) {
|
|
51
51
|
const retryAttempt = options?.retryAttempt ?? 0;
|
|
52
52
|
if (Object.keys(manifest.modelProfiles ?? {}).length === 0) {
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { writeDagNodeTextArtifact } from "../infrastructure/harness/artifact-store.js";
|
|
5
|
-
import { truncateOutput } from "
|
|
5
|
+
import { truncateOutput } from "../shared/output-truncation.js";
|
|
6
6
|
import { expandShellPreset, buildVerdictGateShellCommand } from "./shell-presets.js";
|
|
7
7
|
import { pathsChangedDuringRun, snapshotGitStatusPorcelain, validateShellWriteGuard, } from "./shell-write-guard.js";
|
|
8
8
|
import { buildShellProcessEnv } from "./shell-verification.js";
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { repoRelativePath } from '../shared/path-refs.js';
|
|
4
4
|
import { loadHarnessManifest } from './harness.js';
|
|
5
5
|
import { auditLoopAgentSkillSafety } from './skill-safety.js';
|
|
6
|
-
import { getTaskPaths, getTaskStatus, loadTaskConfig
|
|
6
|
+
import { getTaskPaths, getTaskStatus, loadTaskConfig } from '../task/runtime.js';
|
|
7
7
|
import { buildTaskReadModel } from '../task/read-model.js';
|
|
8
8
|
const SKIP_DIRS = new Set([
|
|
9
9
|
'.git',
|
|
@@ -244,12 +244,15 @@ export async function checkTaskHandoff(repoRoot, taskId) {
|
|
|
244
244
|
message: '任务已 completed,但验证工件未标记完成',
|
|
245
245
|
});
|
|
246
246
|
}
|
|
247
|
-
|
|
247
|
+
// Standard DAG tasks do not require a retrospective artifact. Preserve an
|
|
248
|
+
// explicit legacy task contract, however, so historical tasks that opted
|
|
249
|
+
// into requireRetrospective do not silently lose their handoff warning.
|
|
250
|
+
if (state.status === 'completed' && taskConfig.requireRetrospective && !state.artifacts.retrospective) {
|
|
248
251
|
issues.push({
|
|
249
252
|
type: 'missing-retrospective-artifact',
|
|
250
253
|
severity: 'warning',
|
|
251
254
|
file: toRepoRelative(repoRoot, path.join(taskPaths.taskDir, 'artifacts', '复盘报告.md')),
|
|
252
|
-
message: '
|
|
255
|
+
message: '任务显式要求 retrospective,但复盘工件未完成',
|
|
253
256
|
});
|
|
254
257
|
}
|
|
255
258
|
if (readModel.promotion.required && !readModel.promotion.ready) {
|
|
@@ -16,7 +16,8 @@ export const complexityRouteSchema = z.object({
|
|
|
16
16
|
export const worktreeManifestConfigSchema = z.object({
|
|
17
17
|
rootRelativePath: z.string().optional().default(".worktrees"),
|
|
18
18
|
});
|
|
19
|
-
export const taskExecutorSchema = z.enum(["pi"
|
|
19
|
+
export const taskExecutorSchema = z.enum(["pi"]);
|
|
20
|
+
export const CURSOR_TASK_EXECUTOR_REMOVED_ERROR = 'task executor "cursor" is no longer supported; governed runtime is Pi-only';
|
|
20
21
|
export const executorManifestSchema = z.object({
|
|
21
22
|
description: z.string().optional(),
|
|
22
23
|
enabled: z.boolean().optional(),
|
|
@@ -36,12 +37,16 @@ export const workflowPolicyProfileNameSchema = z.enum([
|
|
|
36
37
|
"standard",
|
|
37
38
|
"reviewed",
|
|
38
39
|
"supervised",
|
|
40
|
+
"backend-test",
|
|
39
41
|
]);
|
|
40
42
|
export const workflowPolicyDagTemplateSchema = z.enum([
|
|
41
43
|
"standard-dag",
|
|
42
44
|
"review-gated-dag",
|
|
43
45
|
"supervised-implementation",
|
|
46
|
+
"frontend-implementation",
|
|
47
|
+
"backend-test-dag",
|
|
44
48
|
]);
|
|
49
|
+
export const workflowPolicyOutputLanguageSchema = z.enum(["zh-CN", "en"]);
|
|
45
50
|
export const workflowPolicySchema = z
|
|
46
51
|
.object({
|
|
47
52
|
/** Preferred implementation workflow for recoverable loop-agent work. Declarative policy; callers may still require explicit CLI flags. */
|
|
@@ -51,6 +56,9 @@ export const workflowPolicySchema = z
|
|
|
51
56
|
.default("agent-dag"),
|
|
52
57
|
dag: z
|
|
53
58
|
.object({
|
|
59
|
+
outputLanguage: workflowPolicyOutputLanguageSchema
|
|
60
|
+
.optional()
|
|
61
|
+
.default("zh-CN"),
|
|
54
62
|
defaultEntry: z
|
|
55
63
|
.enum(["dag run-task", "run-dag"])
|
|
56
64
|
.optional()
|
|
@@ -69,6 +77,9 @@ export const workflowPolicySchema = z
|
|
|
69
77
|
supervised: workflowPolicyDagTemplateSchema
|
|
70
78
|
.optional()
|
|
71
79
|
.default("supervised-implementation"),
|
|
80
|
+
"backend-test": workflowPolicyDagTemplateSchema
|
|
81
|
+
.optional()
|
|
82
|
+
.default("backend-test-dag"),
|
|
72
83
|
})
|
|
73
84
|
.optional()
|
|
74
85
|
.default({}),
|
|
@@ -88,7 +99,9 @@ export const workflowPolicySchema = z
|
|
|
88
99
|
})
|
|
89
100
|
.optional()
|
|
90
101
|
.default({});
|
|
91
|
-
export const
|
|
102
|
+
export const CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR = 'harness executors.cursor is no longer supported; remove it and use executors.pi only (Cursor is available only via cursor-prompt sidecar)';
|
|
103
|
+
export const harnessManifestSchema = z
|
|
104
|
+
.object({
|
|
92
105
|
version: z.number(),
|
|
93
106
|
project: z.string(),
|
|
94
107
|
adapter: z.string().optional(),
|
|
@@ -134,4 +147,22 @@ export const harnessManifestSchema = z.object({
|
|
|
134
147
|
.record(z.string(), executorManifestSchema)
|
|
135
148
|
.optional()
|
|
136
149
|
.default({}),
|
|
150
|
+
})
|
|
151
|
+
.superRefine((manifest, ctx) => {
|
|
152
|
+
const executors = manifest.executors ?? {};
|
|
153
|
+
if ("cursor" in executors) {
|
|
154
|
+
ctx.addIssue({
|
|
155
|
+
code: z.ZodIssueCode.custom,
|
|
156
|
+
message: CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR,
|
|
157
|
+
path: ["executors", "cursor"],
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const entrypoints = manifest.entrypoints ?? {};
|
|
161
|
+
if ("cursorExecutorUsage" in entrypoints) {
|
|
162
|
+
ctx.addIssue({
|
|
163
|
+
code: z.ZodIssueCode.custom,
|
|
164
|
+
message: 'entrypoints.cursorExecutorUsage is no longer supported; remove it (Cursor is cursor-prompt sidecar only)',
|
|
165
|
+
path: ["entrypoints", "cursorExecutorUsage"],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
137
168
|
});
|
|
@@ -9,9 +9,6 @@ function loopActionWriteOptions(repoRoot) {
|
|
|
9
9
|
export async function writeLoopShellVerifyRecord(repoRoot, taskId, round, record) {
|
|
10
10
|
await writeJsonAtomic(loopActionFile(repoRoot, taskId, "verification", `round-${round}.json`), record, loopActionWriteOptions(repoRoot));
|
|
11
11
|
}
|
|
12
|
-
export async function writeLoopCursorFixRecord(repoRoot, taskId, round, record) {
|
|
13
|
-
await writeJsonAtomic(loopActionFile(repoRoot, taskId, "reviews", `round-${round}-cursor-fix.json`), record, loopActionWriteOptions(repoRoot));
|
|
14
|
-
}
|
|
15
12
|
export async function writeLoopDagRecord(repoRoot, taskId, round, record) {
|
|
16
13
|
await writeJsonAtomic(loopActionFile(repoRoot, taskId, "dag", `round-${round}.json`), record, loopActionWriteOptions(repoRoot));
|
|
17
14
|
}
|
package/dist/records/harvest.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { loadHarnessManifest } from '../governance/harness.js';
|
|
5
5
|
import { resolveWorktreePath } from '../task/worktree.js';
|
|
6
6
|
import { getTaskPaths, getTaskStatus, loadTaskConfig } from '../task/runtime.js';
|
|
7
7
|
import { validateDiffPathGuard, listDiffPaths } from '../governance/path-guard.js';
|
|
8
|
-
import { validateCursorArtifacts } from '../executors/cursor-artifacts.js';
|
|
9
|
-
import { checkTaskHandoff } from '../governance/checks.js';
|
|
10
8
|
import { archiveTaskDirFromWorktree } from '../task/worktree-cleanup.js';
|
|
11
9
|
export { copyDir } from '../shared/copy-dir.js';
|
|
12
10
|
/**
|
|
@@ -154,8 +152,7 @@ async function runHarvestGates(repoRoot, taskId, taskRoot, baseBranch, branch) {
|
|
|
154
152
|
catch {
|
|
155
153
|
return;
|
|
156
154
|
}
|
|
157
|
-
const shouldRunPathGuard = taskConfig.
|
|
158
|
-
|| (taskConfig.allowedPaths ?? []).length > 0
|
|
155
|
+
const shouldRunPathGuard = (taskConfig.allowedPaths ?? []).length > 0
|
|
159
156
|
|| (taskConfig.forbiddenPaths ?? []).length > 0;
|
|
160
157
|
if (shouldRunPathGuard) {
|
|
161
158
|
const changedPaths = await listDiffPaths(repoRoot, baseBranch, branch);
|
|
@@ -165,24 +162,6 @@ async function runHarvestGates(repoRoot, taskId, taskRoot, baseBranch, branch) {
|
|
|
165
162
|
'Worktree preserved for manual review.');
|
|
166
163
|
}
|
|
167
164
|
}
|
|
168
|
-
if (taskConfig.executor !== 'cursor') {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const paths = getTaskPaths(taskRoot, taskId);
|
|
172
|
-
const modifyLog = await readFile(path.join(paths.taskDir, 'artifacts', '修改记录.md'), 'utf-8');
|
|
173
|
-
const verifyResult = await readFile(path.join(paths.taskDir, 'artifacts', '验证结果.md'), 'utf-8');
|
|
174
|
-
const artifactIssues = validateCursorArtifacts(modifyLog, verifyResult);
|
|
175
|
-
if (artifactIssues.length > 0) {
|
|
176
|
-
throw new Error(`harvest artifact validation failed for cursor task "${taskId}": ${artifactIssues.join('; ')}. ` +
|
|
177
|
-
'Worktree preserved for manual review.');
|
|
178
|
-
}
|
|
179
|
-
const handoff = await checkTaskHandoff(taskRoot, taskId);
|
|
180
|
-
const taskHandoff = handoff.tasks.find((task) => task.taskId === taskId);
|
|
181
|
-
const blockingIssues = (taskHandoff?.issues ?? []).filter((issue) => issue.severity === 'error');
|
|
182
|
-
if (blockingIssues.length > 0) {
|
|
183
|
-
throw new Error(`harvest handoff check failed for task "${taskId}": ${blockingIssues.map((issue) => issue.message).join('; ')}. ` +
|
|
184
|
-
'Worktree preserved for manual review.');
|
|
185
|
-
}
|
|
186
165
|
}
|
|
187
166
|
// ---------------------------------------------------------------------------
|
|
188
167
|
// Helpers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { access, readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { truncateOutput } from '../
|
|
3
|
+
import { truncateOutput } from '../shared/output-truncation.js';
|
|
4
4
|
import { createOneShotActiveRunDir, ensureOneShotRunDirs, finalizeLegacyRunLogToCompleted, transferOneShotRunToCompleted, transferOneShotRunToFailed, writeOneShotActiveLogAtPath, writeOneShotRunMarkdown, writeOneShotRunMeta, } from '../infrastructure/harness/one-shot-run-store.js';
|
|
5
5
|
/** Maximum chars for result summary in the run log. */
|
|
6
6
|
export const RUN_LOG_RESULT_MAX = 4_000;
|
|
@@ -2,13 +2,25 @@ import path from 'node:path';
|
|
|
2
2
|
import { resolveTaskContextFromDir, writeTaskArtifactFile, } from '../infrastructure/harness/task-store.js';
|
|
3
3
|
export const ARTIFACTS_DIR = 'artifacts';
|
|
4
4
|
const SPEC_ARTIFACT_STEPS = ['analyze', 'plan'];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
'实现计划.md': '# 实现计划\n\n## 1. 修改目标\n\n## 2. 修改范围\n\n## 3. 允许修改文件 / 目录\n\n## 4. 明确不做的内容\n\n## 5. 关键决策\n\n## 6. 不变式\n\n## 7. 风险与回滚点\n\n## 8. 测试与验证方案\n\n## 9. 不在本次范围内的事项\n\n## 10. 待确认事项\n',
|
|
5
|
+
/** Promote-run / closeout bridge templates seeded by new-task. */
|
|
6
|
+
const SEEDED_ARTIFACTS = {
|
|
8
7
|
'修改记录.md': '# 修改记录\n\n## 1. 修改文件清单\n\n## 2. 关键改动说明\n',
|
|
9
8
|
'验证结果.md': '# 验证结果\n\n## 1. 执行的命令\n\n## 2. 通过项\n\n## 3. 失败项\n',
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Legacy L1 analyze/plan/retrospective templates.
|
|
12
|
+
* Kept only so validateArtifactContent can recognize unchanged historical files.
|
|
13
|
+
* new-task must not seed these.
|
|
14
|
+
*/
|
|
15
|
+
const LEGACY_ARTIFACT_TEMPLATES = {
|
|
16
|
+
'分析报告.md': '# 分析报告\n\n## 1. 任务目标\n\n## 2. 当前现状\n\n## 3. 受影响模块与文件\n\n## 4. 关键约束与不变式\n\n## 5. 硬约束摘要\n\n## 6. 允许修改范围 / 禁止修改范围\n\n## 7. 风险点\n\n## 8. 待确认问题\n',
|
|
17
|
+
'实现计划.md': '# 实现计划\n\n## 1. 修改目标\n\n## 2. 修改范围\n\n## 3. 允许修改文件 / 目录\n\n## 4. 明确不做的内容\n\n## 5. 关键决策\n\n## 6. 不变式\n\n## 7. 风险与回滚点\n\n## 8. 测试与验证方案\n\n## 9. 不在本次范围内的事项\n\n## 10. 待确认事项\n',
|
|
10
18
|
'复盘报告.md': '# 复盘报告\n\n## 1. 质量复盘\n\n## 2. 流程问题\n\n## 3. 下次建议\n',
|
|
11
19
|
};
|
|
20
|
+
const ARTIFACT_TEMPLATES = {
|
|
21
|
+
...LEGACY_ARTIFACT_TEMPLATES,
|
|
22
|
+
...SEEDED_ARTIFACTS,
|
|
23
|
+
};
|
|
12
24
|
const ARTIFACT_NAME_BY_STEP = {
|
|
13
25
|
analyze: '分析报告.md',
|
|
14
26
|
plan: '实现计划.md',
|
|
@@ -16,6 +28,8 @@ const ARTIFACT_NAME_BY_STEP = {
|
|
|
16
28
|
verify: '验证结果.md',
|
|
17
29
|
retrospective: '复盘报告.md',
|
|
18
30
|
};
|
|
31
|
+
/** Steps whose artifact files are seeded by new-task (promote/closeout bridge). */
|
|
32
|
+
export const SEEDED_ARTIFACT_STEPS = ['implement', 'verify'];
|
|
19
33
|
export function getArtifactFileName(step) {
|
|
20
34
|
return ARTIFACT_NAME_BY_STEP[step];
|
|
21
35
|
}
|
|
@@ -35,6 +49,9 @@ export function formatSpecArtifactRelativePaths() {
|
|
|
35
49
|
export function formatSpecArtifactDisplayNames() {
|
|
36
50
|
return SPEC_ARTIFACT_STEPS.map((step) => getArtifactDisplayName(step)).join(' + ');
|
|
37
51
|
}
|
|
52
|
+
export function listSeededArtifactFileNames() {
|
|
53
|
+
return Object.keys(SEEDED_ARTIFACTS);
|
|
54
|
+
}
|
|
38
55
|
const REQUIRED_HEADINGS = {
|
|
39
56
|
analyze: ['# 分析报告', '## 1. 任务目标'],
|
|
40
57
|
plan: ['# 实现计划', '## 1. 修改目标'],
|
|
@@ -43,7 +60,7 @@ const REQUIRED_HEADINGS = {
|
|
|
43
60
|
};
|
|
44
61
|
export async function initializeArtifacts(taskDir) {
|
|
45
62
|
const { repoRoot, taskId } = resolveTaskContextFromDir(taskDir);
|
|
46
|
-
await Promise.all(Object.entries(
|
|
63
|
+
await Promise.all(Object.entries(SEEDED_ARTIFACTS).map(([name, content]) => writeTaskArtifactFile(repoRoot, taskId, name, content)));
|
|
47
64
|
}
|
|
48
65
|
export function getArtifactPath(taskDir, step) {
|
|
49
66
|
return path.join(taskDir, ARTIFACTS_DIR, getArtifactFileName(step));
|
|
@@ -57,7 +74,9 @@ function normalizeArtifactContent(content) {
|
|
|
57
74
|
}
|
|
58
75
|
function isUnchangedNewTaskTemplate(step, content) {
|
|
59
76
|
const artifactName = ARTIFACT_NAME_BY_STEP[step];
|
|
60
|
-
const template =
|
|
77
|
+
const template = ARTIFACT_TEMPLATES[artifactName];
|
|
78
|
+
if (!template)
|
|
79
|
+
return false;
|
|
61
80
|
return normalizeArtifactContent(content) === normalizeArtifactContent(template);
|
|
62
81
|
}
|
|
63
82
|
function firstRequiredSectionIsEmpty(step, content) {
|