@mmerterden/multi-agent-pipeline 18.0.0 → 19.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +287 -0
- package/README.md +36 -20
- package/README.tr.md +14 -16
- package/docs/adr/0002-instruction-driven-flag.md +1 -0
- package/docs/adr/0005-lazy-phase-docs.md +11 -1
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +1 -0
- package/docs/adr/0010-own-code-graph.md +1 -0
- package/docs/adr/0014-six-phase-consolidation.md +134 -0
- package/docs/adr/README.md +2 -1
- package/docs/architecture.md +37 -38
- package/docs/best-practices.md +1 -1
- package/docs/ecosystem.md +46 -27
- package/docs/engineering.md +1 -1
- package/docs/facts.json +61 -0
- package/docs/features.md +55 -54
- package/docs/performance.md +5 -5
- package/docs/recovery-guide.md +17 -17
- package/docs/token-budget-history.md +3 -1
- package/index.js +2 -2
- package/install/_codex-agents.mjs +1 -1
- package/install/templates/claude-hooks.json +1 -1
- package/install/templates/codex-instructions.md +1 -1
- package/install/templates/copilot-instructions.md +28 -28
- package/manifest.json +234 -216
- package/package.json +2 -2
- package/pipeline/agents/dev-critic.md +7 -7
- package/pipeline/commands/figma-to-swiftui.md +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/analysis/SKILL.md +15 -15
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +7 -7
- package/pipeline/commands/multi-agent/channels/SKILL.md +15 -15
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/graph/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +62 -62
- package/pipeline/commands/multi-agent/language/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/local/SKILL.md +11 -11
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +13 -13
- package/pipeline/commands/multi-agent/log/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/model/SKILL.md +69 -0
- package/pipeline/commands/multi-agent/refactor/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/resume/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/resume-local/SKILL.md +19 -17
- package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/route-off/SKILL.md +36 -0
- package/pipeline/commands/multi-agent/route-on/SKILL.md +74 -0
- package/pipeline/commands/multi-agent/route-status/SKILL.md +56 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/status/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/steer/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/sync/SKILL.md +12 -13
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/lib/credential-inventory.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/model-dispatch.sh +140 -0
- package/pipeline/lib/model-rung.sh +142 -0
- package/pipeline/lib/outbound-gate.mjs +14 -0
- package/pipeline/lib/phase-schema.mjs +88 -0
- package/pipeline/lib/plan-todos.sh +5 -5
- package/pipeline/lib/route-state.sh +161 -0
- package/pipeline/lib/run-paths.sh +2 -2
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +6 -6
- package/pipeline/multi-agent-refs/_input-parser.md +1 -1
- package/pipeline/multi-agent-refs/analysis/evidence.md +2 -11
- package/pipeline/multi-agent-refs/analysis/intake.md +7 -7
- package/pipeline/multi-agent-refs/analysis/locked.md +48 -22
- package/pipeline/multi-agent-refs/analysis/redesign.md +1 -1
- package/pipeline/multi-agent-refs/analysis/render.md +10 -10
- package/pipeline/multi-agent-refs/analysis/resolve.md +1 -1
- package/pipeline/multi-agent-refs/analysis/review.md +2 -2
- package/pipeline/multi-agent-refs/analysis/synthesis.md +13 -7
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +9 -9
- package/pipeline/multi-agent-refs/analysis-template.md +19 -19
- package/pipeline/multi-agent-refs/android-guide.md +1 -1
- package/pipeline/multi-agent-refs/audit-guide.md +13 -13
- package/pipeline/multi-agent-refs/channels/issue-comment.md +2 -2
- package/pipeline/multi-agent-refs/channels/jira.md +3 -3
- package/pipeline/multi-agent-refs/channels/pr.md +4 -4
- package/pipeline/multi-agent-refs/channels/wiki.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +8 -8
- package/pipeline/multi-agent-refs/conventions-defaults.md +2 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +31 -6
- package/pipeline/multi-agent-refs/features/analysis-jira.md +1 -1
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +4 -4
- package/pipeline/multi-agent-refs/features/code-graph.md +5 -5
- package/pipeline/multi-agent-refs/features/design-conformance.md +1 -1
- package/pipeline/multi-agent-refs/features/dev-critic.md +3 -3
- package/pipeline/multi-agent-refs/features/doctor.md +3 -3
- package/pipeline/multi-agent-refs/features/external-context-injection.md +3 -3
- package/pipeline/multi-agent-refs/features/maturity-followup.md +3 -3
- package/pipeline/multi-agent-refs/features/model-fallback.md +41 -5
- package/pipeline/multi-agent-refs/features/plan-todos.md +1 -1
- package/pipeline/multi-agent-refs/features/repo-map.md +1 -1
- package/pipeline/multi-agent-refs/features/review-delta.md +3 -3
- package/pipeline/multi-agent-refs/features/review-multi-repo.md +2 -2
- package/pipeline/multi-agent-refs/features/scope-check.md +4 -4
- package/pipeline/multi-agent-refs/features/skill-conformance.md +2 -2
- package/pipeline/multi-agent-refs/features/stack-skill-routing.md +1 -1
- package/pipeline/multi-agent-refs/features/url-enrichment.md +1 -1
- package/pipeline/multi-agent-refs/features/verify-by-test.md +4 -4
- package/pipeline/multi-agent-refs/features/visual-evidence.md +19 -19
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +6 -6
- package/pipeline/multi-agent-refs/issue-jira-triad.md +10 -10
- package/pipeline/multi-agent-refs/knowledge.md +11 -11
- package/pipeline/multi-agent-refs/multi-repo-integration-build.md +13 -13
- package/pipeline/multi-agent-refs/payload-contracts.md +8 -8
- package/pipeline/multi-agent-refs/phases/log-format.md +10 -10
- package/pipeline/multi-agent-refs/phases/modes.md +30 -30
- package/pipeline/multi-agent-refs/phases/operations.md +8 -8
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +24 -24
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +599 -0
- package/pipeline/multi-agent-refs/phases/{phase-3-dev.md → phase-2-dev.md} +129 -49
- package/pipeline/multi-agent-refs/phases/{phase-4-review.md → phase-3-review.md} +225 -107
- package/pipeline/multi-agent-refs/phases/{phase-6-commit.md → phase-4-commit.md} +23 -23
- package/pipeline/multi-agent-refs/phases/{phase-7-report.md → phase-5-report.md} +29 -29
- package/pipeline/multi-agent-refs/phases.md +44 -48
- package/pipeline/multi-agent-refs/picker-contract.md +1 -1
- package/pipeline/multi-agent-refs/progress-contract.md +6 -6
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/rules.md +7 -7
- package/pipeline/multi-agent-refs/swiftui-guide.md +2 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +31 -32
- package/pipeline/multi-agent-refs/wiki-capture.md +14 -14
- package/pipeline/preferences-template.json +9 -1
- package/pipeline/rules/figma-pipeline.md +8 -8
- package/pipeline/rules/outside-the-pipeline.md +1 -1
- package/pipeline/schemas/agent-state.schema.json +50 -50
- package/pipeline/schemas/analysis-output.schema.json +3 -3
- package/pipeline/schemas/analysis-spec.schema.json +2 -2
- package/pipeline/schemas/autopilot-config.schema.json +1 -1
- package/pipeline/schemas/code-graph.schema.json +1 -1
- package/pipeline/schemas/criteria-manifest.schema.json +1 -1
- package/pipeline/schemas/dev-critic-output.schema.json +1 -1
- package/pipeline/schemas/diff-risk.schema.json +1 -1
- package/pipeline/schemas/figma-project-config.schema.json +1 -1
- package/pipeline/schemas/migrations/prefs-2.4.0-to-2.5.0.mjs +2 -2
- package/pipeline/schemas/migrations/prefs-2.6.0-to-2.7.0.mjs +31 -0
- package/pipeline/schemas/migrations/state-2.1.0-to-2.2.0.mjs +129 -0
- package/pipeline/schemas/phases.json +105 -0
- package/pipeline/schemas/plan-todos.schema.json +5 -5
- package/pipeline/schemas/planning-output.schema.json +1 -1
- package/pipeline/schemas/prefs.schema.json +102 -58
- package/pipeline/schemas/reviewer-output.schema.json +3 -3
- package/pipeline/schemas/route-config.schema.json +74 -0
- package/pipeline/schemas/scope-check.schema.json +1 -1
- package/pipeline/schemas/secret-patterns.json +124 -0
- package/pipeline/schemas/test-gap.schema.json +1 -1
- package/pipeline/schemas/token-budget.json +12 -18
- package/pipeline/schemas/triage-output.schema.json +6 -6
- package/pipeline/scripts/README.md +3 -3
- package/pipeline/scripts/_code-graph.mjs +2 -2
- package/pipeline/scripts/_run-paths.mjs +2 -2
- package/pipeline/scripts/_smoke-root.sh +1 -1
- package/pipeline/scripts/aggregate-metrics.mjs +1 -1
- package/pipeline/scripts/build-references.mjs +2 -2
- package/pipeline/scripts/bulk-read.sh +10 -1
- package/pipeline/scripts/capture-flush.sh +8 -8
- package/pipeline/scripts/capture-resume.sh +3 -3
- package/pipeline/scripts/classify-plan-safety.mjs +1 -1
- package/pipeline/scripts/cost-table.json +8 -1
- package/pipeline/scripts/diff-explain.mjs +1 -1
- package/pipeline/scripts/doctor.mjs +3 -3
- package/pipeline/scripts/gc-abandoned.sh +3 -3
- package/pipeline/scripts/gc-tmp.sh +1 -1
- package/pipeline/scripts/gc-worktrees.sh +1 -1
- package/pipeline/scripts/gen-facts.mjs +280 -0
- package/pipeline/scripts/gen-mode-dispatch.mjs +32 -37
- package/pipeline/scripts/gen-ref-toc.mjs +1 -1
- package/pipeline/scripts/graph-report.mjs +1 -1
- package/pipeline/scripts/jira-attach.sh +1 -1
- package/pipeline/scripts/learn-from-transcripts.mjs +1 -1
- package/pipeline/scripts/learning-curve.mjs +2 -2
- package/pipeline/scripts/log-metric.sh +17 -4
- package/pipeline/scripts/memory-save.sh +1 -1
- package/pipeline/scripts/migrate-prefs.mjs +22 -5
- package/pipeline/scripts/phase-banner.sh +20 -20
- package/pipeline/scripts/phase-tracker.sh +12 -12
- package/pipeline/scripts/plan-coverage-gate.mjs +2 -2
- package/pipeline/scripts/pre-commit-check.sh +30 -1
- package/pipeline/scripts/render-agent-log-cost.sh +1 -1
- package/pipeline/scripts/render-work-summary.sh +3 -3
- package/pipeline/scripts/review-file-filter.mjs +1 -1
- package/pipeline/scripts/run-aggregator.mjs +13 -6
- package/pipeline/scripts/run-metrics.mjs +1 -1
- package/pipeline/scripts/runs-index.mjs +11 -1
- package/pipeline/scripts/scan-skills.sh +26 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +6 -6
- package/pipeline/scripts/smoke-schema-validation.sh +26 -7
- package/pipeline/scripts/token-budget-report.mjs +13 -2
- package/pipeline/scripts/triage-memory.mjs +2 -2
- package/pipeline/scripts/validate-analysis-doc.mjs +274 -43
- package/pipeline/scripts/validate-planning.mjs +1 -1
- package/pipeline/scripts/validate-reviewer.mjs +1 -1
- package/pipeline/scripts/validate-state.mjs +45 -5
- package/pipeline/scripts/validate-triage.mjs +3 -3
- package/pipeline/scripts/verify-citations.mjs +1 -1
- package/pipeline/scripts/worktree-finalize.sh +5 -5
- package/pipeline/scripts/write-state.mjs +32 -0
- package/pipeline/skills/.skill-manifest.json +38 -22
- package/pipeline/skills/.skills-index.json +49 -5
- package/pipeline/skills/shared/README.md +10 -6
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -8
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +81 -82
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +14 -14
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +25 -23
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +6 -6
- package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +71 -0
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-resume-local/SKILL.md +7 -7
- package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +39 -0
- package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +76 -0
- package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +59 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +5 -5
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +6 -5
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +1 -1
- package/pipeline/skills/shared/external/signal-community/SKILL.md +8 -1
- package/pipeline/skills/skills-index.md +8 -4
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +0 -263
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +0 -344
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +0 -182
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# Three things keep this from being a foot-gun, and each is a rule rather than a
|
|
12
12
|
# heuristic:
|
|
13
13
|
#
|
|
14
|
-
# 1. A run WAITING FOR YOU is never reaped. An open PR, Phase
|
|
14
|
+
# 1. A run WAITING FOR YOU is never reaped. An open PR, Phase 4 or 7, or
|
|
15
15
|
# `status: awaiting_input` means the work landed and the pipeline is
|
|
16
16
|
# holding for an answer by design. That is finished work, not residue.
|
|
17
17
|
# 2. A path that is not strictly inside `<repo>/.worktrees/` is never removed.
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
#
|
|
50
50
|
# State is moved to `<run-dir>/artifacts/` before the worktree goes, so the log,
|
|
51
51
|
# the findings and the phase history survive the sweep. The same recovery
|
|
52
|
-
# contract Phase
|
|
52
|
+
# contract Phase 4 uses when it removes a finished worktree.
|
|
53
53
|
#
|
|
54
54
|
# SAFE BY DEFAULT: dry-run. Removes nothing until you pass --yes.
|
|
55
55
|
#
|
|
@@ -271,7 +271,7 @@ if [ "$STATE_ONLY" -eq 0 ] && [ -d "$REPOS" ]; then
|
|
|
271
271
|
ph=$(printf '%s' "$row" | cut -f5)
|
|
272
272
|
|
|
273
273
|
# Rule 1 again: landed work is not residue, whichever pass finds it. Order
|
|
274
|
-
# matters here - the phase-
|
|
274
|
+
# matters here - the phase-4/5 test is a proxy for "still waiting", and a
|
|
275
275
|
# run whose status already says it FINISHED is not waiting for anyone. A
|
|
276
276
|
# complete run sitting at phase 7 was read as waiting and never reaped,
|
|
277
277
|
# which is how three finished worktrees held 5.1 GB indefinitely.
|
|
@@ -107,7 +107,7 @@ NAME_ARGS=(
|
|
|
107
107
|
# `complaint-analysis-*` needs its own entry: find matches the basename against
|
|
108
108
|
# the whole glob, so `analysis-*` never matched it. Same for the other three,
|
|
109
109
|
# which were writing into $ROOT with nothing sweeping them (phase-0-init.md,
|
|
110
|
-
# phase-
|
|
110
|
+
# phase-4-commit.md, generate-issue.md).
|
|
111
111
|
#
|
|
112
112
|
# `*-wiki` is the one loose pattern here, and a suffix glob over $ROOT could
|
|
113
113
|
# name something a person created. It is narrowed twice: `-type d` above, and
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# `.pipeline/` left behind by a finished run stops reading as residue.
|
|
22
22
|
#
|
|
23
23
|
# Registered, healthy worktrees are NEVER touched. Finishing a task removes its
|
|
24
|
-
# own worktree in Phase
|
|
24
|
+
# own worktree in Phase 4 (worktree-finalize, v14.1.0+); killing one is
|
|
25
25
|
# /multi-agent:kill. This sweep only reaps orphans neither of those left behind.
|
|
26
26
|
#
|
|
27
27
|
# SAFE BY DEFAULT: dry-run. Deletes nothing until you pass --yes.
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file gen-facts.mjs - the numbers the website is allowed to state, derived.
|
|
4
|
+
*
|
|
5
|
+
* A consumer that keeps its own copy of these numbers has nothing guarding it:
|
|
6
|
+
* a count typed into a page or a README is right on the day it is typed and
|
|
7
|
+
* silent afterwards. `smoke-phase-contract.sh` enforces the same shape inside
|
|
8
|
+
* the pipeline - one producer, everything else reads it.
|
|
9
|
+
*
|
|
10
|
+
* Source of truth per field:
|
|
11
|
+
* phases pipeline/schemas/phases.json - the phase contract itself
|
|
12
|
+
* commandCount a count of pipeline/commands/multi-agent/<name>/SKILL.md
|
|
13
|
+
* skillCount a count of pipeline/skills/shared/external/<name>/SKILL.md
|
|
14
|
+
* skillCountAll every SKILL.md under pipeline/skills, at any depth
|
|
15
|
+
* agentCount a count of pipeline/agents/*.md
|
|
16
|
+
* hosts the CLIs install/ has an adapter for
|
|
17
|
+
* toolCount the toolkit's own tools/list response, when reachable
|
|
18
|
+
* toolCategories that same response, grouped by the prefix in each tool name
|
|
19
|
+
* pluginSkillCount every SKILL.md in the plugin marketplace, when checked out
|
|
20
|
+
* version package.json
|
|
21
|
+
*
|
|
22
|
+
* Counts come from the filesystem at the moment of writing, never from a README
|
|
23
|
+
* or a previous version of this file.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* node gen-facts.mjs write docs/facts.json
|
|
27
|
+
* node gen-facts.mjs --check exit 1 if the file on disk is stale
|
|
28
|
+
* node gen-facts.mjs --stdout print, write nothing
|
|
29
|
+
*
|
|
30
|
+
* Exit codes:
|
|
31
|
+
* 0 - written, or up to date
|
|
32
|
+
* 1 - --check found drift
|
|
33
|
+
* 2 - a source could not be read (nothing is written)
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync } from "node:fs";
|
|
37
|
+
import { join, dirname } from "node:path";
|
|
38
|
+
import { fileURLToPath } from "node:url";
|
|
39
|
+
import { spawnSync } from "node:child_process";
|
|
40
|
+
|
|
41
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
const ROOT = join(HERE, "..", "..");
|
|
43
|
+
const OUT = join(ROOT, "docs", "facts.json");
|
|
44
|
+
|
|
45
|
+
function die(msg) {
|
|
46
|
+
console.error(`gen-facts: ${msg}`);
|
|
47
|
+
process.exit(2);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Every SKILL.md under a tree, at any depth. `countSkillDirs` only sees the
|
|
51
|
+
// immediate children of one directory, which is the right shape for the
|
|
52
|
+
// external set and the wrong one for the whole tree: the shared/core and
|
|
53
|
+
// per-host mirrors sit a level deeper and were invisible to it.
|
|
54
|
+
function countSkillsDeep(rel) {
|
|
55
|
+
const dir = join(ROOT, rel);
|
|
56
|
+
if (!existsSync(dir)) die(`missing directory: ${rel}`);
|
|
57
|
+
let n = 0;
|
|
58
|
+
const walk = (d) => {
|
|
59
|
+
for (const e of readdirSync(d, { withFileTypes: true })) {
|
|
60
|
+
if (e.isDirectory()) walk(join(d, e.name));
|
|
61
|
+
else if (e.name === "SKILL.md") n += 1;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
walk(dir);
|
|
65
|
+
return n;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function countSkillDirs(rel) {
|
|
69
|
+
const dir = join(ROOT, rel);
|
|
70
|
+
if (!existsSync(dir)) die(`missing directory: ${rel}`);
|
|
71
|
+
return readdirSync(dir, { withFileTypes: true }).filter(
|
|
72
|
+
(e) => e.isDirectory() && existsSync(join(dir, e.name, "SKILL.md")),
|
|
73
|
+
).length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let contract;
|
|
77
|
+
try {
|
|
78
|
+
contract = JSON.parse(readFileSync(join(ROOT, "pipeline", "schemas", "phases.json"), "utf8"));
|
|
79
|
+
} catch (err) {
|
|
80
|
+
die(`cannot read the phase contract - ${err.message}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let pkg;
|
|
84
|
+
try {
|
|
85
|
+
pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
die(`cannot read package.json - ${err.message}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The toolkit is a separate repo and may not be checked out beside this one.
|
|
91
|
+
// Absent is reported as null rather than as a guess: a site that prints a stale
|
|
92
|
+
// tool count is the problem this file exists to remove, and a made-up one is
|
|
93
|
+
// worse than none.
|
|
94
|
+
let toolCount = null;
|
|
95
|
+
let toolCategories = null;
|
|
96
|
+
let toolkitVersion = null;
|
|
97
|
+
const toolkitDir = join(ROOT, "..", "multi-agent-toolkit-mcp");
|
|
98
|
+
if (existsSync(join(toolkitDir, "package.json"))) {
|
|
99
|
+
try {
|
|
100
|
+
toolkitVersion =
|
|
101
|
+
JSON.parse(readFileSync(join(toolkitDir, "package.json"), "utf8")).version ?? null;
|
|
102
|
+
} catch {
|
|
103
|
+
toolkitVersion = null;
|
|
104
|
+
}
|
|
105
|
+
// Asked of the server, not counted out of its source. Three tool families
|
|
106
|
+
// are defined in modules the main file only spreads in, so a regex over
|
|
107
|
+
// index.js returns a number that looks plausible and is wrong - the first
|
|
108
|
+
// version of this script said 2 when the answer was 99. tools/list is what
|
|
109
|
+
// a client actually receives, which is the number the site should print.
|
|
110
|
+
try {
|
|
111
|
+
const req =
|
|
112
|
+
[
|
|
113
|
+
JSON.stringify({
|
|
114
|
+
jsonrpc: "2.0",
|
|
115
|
+
id: 1,
|
|
116
|
+
method: "initialize",
|
|
117
|
+
params: {
|
|
118
|
+
protocolVersion: "2024-11-05",
|
|
119
|
+
capabilities: {},
|
|
120
|
+
clientInfo: { name: "gen-facts", version: "1" },
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }),
|
|
124
|
+
JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }),
|
|
125
|
+
].join("\n") + "\n";
|
|
126
|
+
const res = spawnSync(process.execPath, [join(toolkitDir, "index.js")], {
|
|
127
|
+
input: req,
|
|
128
|
+
encoding: "utf8",
|
|
129
|
+
timeout: 30000,
|
|
130
|
+
});
|
|
131
|
+
for (const line of (res.stdout || "").split("\n")) {
|
|
132
|
+
if (!line.trim()) continue;
|
|
133
|
+
let msg;
|
|
134
|
+
try {
|
|
135
|
+
msg = JSON.parse(line);
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (msg.id === 2 && Array.isArray(msg.result?.tools)) {
|
|
140
|
+
toolCount = msg.result.tools.length;
|
|
141
|
+
// Grouped by the prefix every tool name carries, from the same
|
|
142
|
+
// response the count comes from. Counting families out of the source
|
|
143
|
+
// gives a different and wrong answer for the reason above, and the
|
|
144
|
+
// site shows the per-family breakdown next to the total, so the two
|
|
145
|
+
// have to come from one place or they will disagree.
|
|
146
|
+
const byFamily = {};
|
|
147
|
+
for (const t of msg.result.tools) {
|
|
148
|
+
const family = String(t.name || "").split("_")[0];
|
|
149
|
+
if (family) byFamily[family] = (byFamily[family] || 0) + 1;
|
|
150
|
+
}
|
|
151
|
+
toolCategories = Object.fromEntries(
|
|
152
|
+
Object.entries(byFamily).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
toolCount = null;
|
|
158
|
+
toolCategories = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// The plugin marketplace is a third repo, probed the same way and reported as
|
|
163
|
+
// null when it is not checked out beside this one. The site states this number
|
|
164
|
+
// next to the pipeline's own skill counts, and three different skill
|
|
165
|
+
// populations on one page is exactly the shape that drifts.
|
|
166
|
+
let pluginSkillCount = null;
|
|
167
|
+
const pluginsDir = join(ROOT, "..", "multi-agent-plugins", "plugins");
|
|
168
|
+
if (existsSync(pluginsDir)) {
|
|
169
|
+
let n = 0;
|
|
170
|
+
const walk = (d) => {
|
|
171
|
+
for (const e of readdirSync(d, { withFileTypes: true })) {
|
|
172
|
+
if (e.isDirectory()) walk(join(d, e.name));
|
|
173
|
+
else if (e.name === "SKILL.md") n += 1;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
try {
|
|
177
|
+
walk(pluginsDir);
|
|
178
|
+
pluginSkillCount = n;
|
|
179
|
+
} catch {
|
|
180
|
+
pluginSkillCount = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const facts = {
|
|
185
|
+
$comment:
|
|
186
|
+
"Generated by pipeline/scripts/gen-facts.mjs. Do not hand-edit: the site reads this, and a number edited here instead of at its source is the drift this file removes.",
|
|
187
|
+
generatedAt: new Date().toISOString().slice(0, 10),
|
|
188
|
+
version: pkg.version,
|
|
189
|
+
phaseSchema: contract.phaseSchema,
|
|
190
|
+
phases: contract.phases.map((p) => ({ id: p.id, name: p.name })),
|
|
191
|
+
phaseCount: contract.phases.length,
|
|
192
|
+
modes: Object.fromEntries(Object.entries(contract.modes).map(([k, v]) => [k, v.phases])),
|
|
193
|
+
commandCount: countSkillDirs("pipeline/commands/multi-agent"),
|
|
194
|
+
// Two populations, and conflating them is how the site came to show a stale
|
|
195
|
+
// "212 Skills" chip next to a correct "216 skills" sentence. `skillCount` is
|
|
196
|
+
// the external set a user installs; `skillCountAll` is every SKILL.md the
|
|
197
|
+
// repo ships, mirrors included. Both are stated on the site, so both are
|
|
198
|
+
// generated here rather than counted by hand at the other end.
|
|
199
|
+
skillCount: countSkillDirs("pipeline/skills/shared/external"),
|
|
200
|
+
skillCountAll: countSkillsDeep("pipeline/skills"),
|
|
201
|
+
// The site shows both of these as headline figures and had typed both by
|
|
202
|
+
// hand: a "9 Sub-agents" that happened to be right and a host list that had
|
|
203
|
+
// gone stale, naming Claude Code and Copilot CLI while install/codex.mjs had
|
|
204
|
+
// been shipping a third target for releases.
|
|
205
|
+
agentCount: readdirSync(join(ROOT, "pipeline", "agents")).filter((f) => f.endsWith(".md")).length,
|
|
206
|
+
hosts: readdirSync(join(ROOT, "install"))
|
|
207
|
+
.filter((f) => f.endsWith(".mjs") && !f.startsWith("_") && f !== "index.mjs")
|
|
208
|
+
.map(
|
|
209
|
+
(f) =>
|
|
210
|
+
({ claude: "Claude Code", copilot: "Copilot CLI", codex: "Codex CLI" })[
|
|
211
|
+
f.replace(".mjs", "")
|
|
212
|
+
],
|
|
213
|
+
)
|
|
214
|
+
.filter(Boolean)
|
|
215
|
+
.sort(),
|
|
216
|
+
toolCount,
|
|
217
|
+
toolCategories,
|
|
218
|
+
pluginSkillCount,
|
|
219
|
+
toolkitVersion,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// JSON.stringify and prettier disagree about short arrays: stringify puts each
|
|
223
|
+
// phase id on its own line, prettier collapses a run that fits. That left this
|
|
224
|
+
// file failing `format:check` after every regeneration, held together only by
|
|
225
|
+
// someone remembering to run prettier by hand - and a generated file that needs
|
|
226
|
+
// a manual step to pass the gate is a generated file that will fail the gate.
|
|
227
|
+
//
|
|
228
|
+
// Importing prettier here is not the fix: this script ships in the published
|
|
229
|
+
// package, where a devDependency is not installed (`n/no-unpublished-import`
|
|
230
|
+
// says so). Collapsing the number runs directly costs nothing and leaves the
|
|
231
|
+
// script dependency-free.
|
|
232
|
+
const collapseShortArrays = (json) =>
|
|
233
|
+
json.replace(
|
|
234
|
+
/\[\n\s+((?:-?\d+|"[^"\n]*")(?:,\n\s+(?:-?\d+|"[^"\n]*"))*)\n\s+\]/g,
|
|
235
|
+
(whole, inner) => {
|
|
236
|
+
const one = `[${inner.split(/,\n\s*/).join(", ")}]`;
|
|
237
|
+
// Prettier only collapses what fits the print width; leave the rest alone
|
|
238
|
+
// rather than guess, so the two can never disagree in the other direction.
|
|
239
|
+
return one.length <= 72 ? one : whole;
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const body = collapseShortArrays(`${JSON.stringify(facts, null, 2)}\n`);
|
|
244
|
+
|
|
245
|
+
if (process.argv.includes("--stdout")) {
|
|
246
|
+
process.stdout.write(body);
|
|
247
|
+
process.exit(0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (process.argv.includes("--check")) {
|
|
251
|
+
if (!existsSync(OUT)) {
|
|
252
|
+
console.error("gen-facts: docs/facts.json does not exist - run without --check");
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
const onDisk = JSON.parse(readFileSync(OUT, "utf8"));
|
|
256
|
+
const fresh = JSON.parse(body);
|
|
257
|
+
// generatedAt is a timestamp, not a fact: comparing it would fail every day.
|
|
258
|
+
delete onDisk.generatedAt;
|
|
259
|
+
delete fresh.generatedAt;
|
|
260
|
+
if (JSON.stringify(onDisk) !== JSON.stringify(fresh)) {
|
|
261
|
+
console.error(
|
|
262
|
+
"gen-facts: docs/facts.json is stale - run: node pipeline/scripts/gen-facts.mjs",
|
|
263
|
+
);
|
|
264
|
+
for (const k of Object.keys(fresh)) {
|
|
265
|
+
if (JSON.stringify(onDisk[k]) !== JSON.stringify(fresh[k])) {
|
|
266
|
+
console.error(
|
|
267
|
+
` ${k}: on disk ${JSON.stringify(onDisk[k])}, derived ${JSON.stringify(fresh[k])}`,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
console.log("gen-facts: docs/facts.json is current");
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
writeFileSync(OUT, body);
|
|
278
|
+
console.log(
|
|
279
|
+
`gen-facts: wrote docs/facts.json (${facts.phaseCount} phases, ${facts.commandCount} commands, ${facts.skillCount} skills, ${facts.toolCount ?? "?"} tools)`,
|
|
280
|
+
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
/**
|
|
3
4
|
* gen-mode-dispatch.mjs - emit the canonical Phase Tracker Contract block
|
|
4
5
|
* for a given mode dispatch file (full / local / autopilot / analysis).
|
|
@@ -10,10 +11,10 @@
|
|
|
10
11
|
* call, anti-pattern warning, and ref link are identical.
|
|
11
12
|
*
|
|
12
13
|
* Usage:
|
|
13
|
-
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=full # 0..
|
|
14
|
-
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=local # 0
|
|
15
|
-
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=autopilot # 0
|
|
16
|
-
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=analysis # 0/1/
|
|
14
|
+
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=full # 0..5
|
|
15
|
+
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=local # 0..5 + local-mode caveat
|
|
16
|
+
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=autopilot # 0..5
|
|
17
|
+
* node pipeline/scripts/gen-mode-dispatch.mjs --mode=analysis # 0/1/3/4/5 (no Dev)
|
|
17
18
|
*
|
|
18
19
|
* v16.0.0 removed the four dev-* modes. Depth is no longer a command name: the
|
|
19
20
|
* Phase 0 Step 7.5 picker asks Full or Short and sets `state.onlyDevelop`. That
|
|
@@ -35,22 +36,20 @@ const args = Object.fromEntries(
|
|
|
35
36
|
|
|
36
37
|
const MODE = args.mode;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// v16.0.0 leaves exactly one command: `/multi-agent` itself.
|
|
53
|
-
const PHASE_NAMES_NO_TEST = PHASE_NAMES.filter((p) => p !== "5:Test");
|
|
39
|
+
// The phase list has one owner: schemas/phases.json. This file used to restate
|
|
40
|
+
// it, which is how eight independent copies came to exist. smoke-phase-contract.sh
|
|
41
|
+
// holds every remaining copy to the same file.
|
|
42
|
+
const CONTRACT = JSON.parse(
|
|
43
|
+
readFileSync(new URL("../schemas/phases.json", import.meta.url), "utf8"),
|
|
44
|
+
);
|
|
45
|
+
const PHASES = CONTRACT.phases;
|
|
46
|
+
const modePhases = (name) =>
|
|
47
|
+
CONTRACT.modes[name].phases.map((id) => {
|
|
48
|
+
const p = PHASES.find((x) => x.id === id);
|
|
49
|
+
return `${p.id}:${p.name}`;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const PHASE_NAMES = PHASES.map((p) => `${p.id}:${p.name}`);
|
|
54
53
|
|
|
55
54
|
/**
|
|
56
55
|
* Mode → phases + flag descriptor. The descriptor controls the autopilot /
|
|
@@ -59,21 +58,17 @@ const PHASE_NAMES_NO_TEST = PHASE_NAMES.filter((p) => p !== "5:Test");
|
|
|
59
58
|
* `smoke-mode-dispatch-drift.sh` can byte-equal-diff each on-disk section
|
|
60
59
|
* against its generator output.
|
|
61
60
|
*/
|
|
62
|
-
const MODES =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
local: false,
|
|
74
|
-
autopilot: false,
|
|
75
|
-
},
|
|
76
|
-
};
|
|
61
|
+
const MODES = Object.fromEntries(
|
|
62
|
+
Object.entries(CONTRACT.modes).map(([name, m]) => [
|
|
63
|
+
name,
|
|
64
|
+
{
|
|
65
|
+
phases: modePhases(name),
|
|
66
|
+
local: m.local,
|
|
67
|
+
autopilot: m.autopilot,
|
|
68
|
+
...(m.depth ? { depth: true } : {}),
|
|
69
|
+
},
|
|
70
|
+
]),
|
|
71
|
+
);
|
|
77
72
|
|
|
78
73
|
const spec = MODE ? MODES[MODE] : null;
|
|
79
74
|
if (!spec) {
|
|
@@ -101,7 +96,7 @@ const modeLabel = MODE_LABELS[MODE] ?? `\`${MODE}\``;
|
|
|
101
96
|
const skipNote =
|
|
102
97
|
skippedIds.length > 0
|
|
103
98
|
? `${modeLabel} mode does NOT TaskCreate phases ${skippedIds.join("/")} - those are not part of the ${modeLabel} phase set (\`${spec.phases.join(" ")}\`). Only register tiles for the active set.`
|
|
104
|
-
: `${modeLabel} mode TaskCreates all
|
|
99
|
+
: `${modeLabel} mode TaskCreates all ${PHASES.length} phases (no phase is skipped).`;
|
|
105
100
|
|
|
106
101
|
const orderingNote = `**All TaskCreate calls in a batch fire in strict phase-number order BEFORE any TaskUpdate is applied.** For ${modeLabel} that means: ${spec.depth ? `Phase 0 at Step -1, then the rest in ascending order at Step 7.5 (${phaseSequence} minus whatever the depth answer drops)` : phaseSequence}. The native widget renders by creation order, not by phase number - out-of-order calls produce visually scrambled tile stacks. Full ordering contract in \`$HOME/.claude/multi-agent-refs/tracker-contract.md\` section "TaskCreate ordering (strict)".`;
|
|
107
102
|
|
|
@@ -119,7 +114,7 @@ const banner = spec.autopilot
|
|
|
119
114
|
// tiles beside the question that decides whether two of them run is the failure this
|
|
120
115
|
// split exists to remove.
|
|
121
116
|
const fullSet = spec.phases.filter((p) => p !== "0:Init");
|
|
122
|
-
const shortSet = fullSet.filter((p) => Number(p.split(":")[0]) >=
|
|
117
|
+
const shortSet = fullSet.filter((p) => Number(p.split(":")[0]) >= 2);
|
|
123
118
|
const loopFor = (list) =>
|
|
124
119
|
`for p in ${list.map((x) => `"${x}"`).join(" ")}; do\n bash $HOME/.claude/scripts/phase-tracker.sh add "\${p%%:*}" "\${p#*:}"\ndone`;
|
|
125
120
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Section level is DERIVED, not assumed. These files are not consistent: some
|
|
15
15
|
// open at `##` and section at `###` (keychain.md), others open at `###` and
|
|
16
|
-
// section at `##` (phases/phase-
|
|
16
|
+
// section at `##` (phases/phase-2-dev.md). The rule is the shallowest heading
|
|
17
17
|
// depth that occurs more than once - a depth used exactly once is a title, not
|
|
18
18
|
// a section level.
|
|
19
19
|
//
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file graph-report.mjs - GRAPH_REPORT.md, the human-and-model readable summary.
|
|
5
5
|
*
|
|
6
|
-
* Phase 1 reads this before deciding how wide to explore, and Phase
|
|
6
|
+
* Phase 1 reads this before deciding how wide to explore, and Phase 5 ships it
|
|
7
7
|
* next to the knowledge base. Sections are ordered by how much they narrow a
|
|
8
8
|
* search: hubs first (the types everything touches), then the module map, then
|
|
9
9
|
* the long tail that is usually safe to ignore.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Two callers need this and they used to carry their own curl: the issue
|
|
7
7
|
# generator (`generate-issue.md` step 12) and visual evidence (Phase 3 / Phase 5
|
|
8
|
-
# captures rendered into the Phase
|
|
8
|
+
# captures rendered into the Phase 5 Jira comment). Two copies of an upload is
|
|
9
9
|
# two places for the `X-Atlassian-Token: no-check` header to go missing, which
|
|
10
10
|
# fails as a 403 that reads like an auth problem.
|
|
11
11
|
#
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS EXISTS
|
|
6
6
|
*
|
|
7
|
-
* Phase
|
|
7
|
+
* Phase 5 asks a subagent to read the run log and propose a few memory entries.
|
|
8
8
|
* That works for what the model noticed, and it is structurally blind to what it
|
|
9
9
|
* did not: the same wrong path tried in three sessions, the command that fails
|
|
10
10
|
* every time until its `--flag` variant is used, the directory that never has
|
|
@@ -87,9 +87,9 @@ for (const e of events) {
|
|
|
87
87
|
}
|
|
88
88
|
if (e.ts && (!t.firstTs || e.ts < t.firstTs)) t.firstTs = e.ts;
|
|
89
89
|
const d = e.details || {};
|
|
90
|
-
// phase-
|
|
90
|
+
// phase-3-review.md's actual review.completed emission (raw_count=/
|
|
91
91
|
// accepted=/deferred=/rejected=/approved=/duration_ms=) carries no
|
|
92
|
-
// review_cycles field - that name only appears on Phase
|
|
92
|
+
// review_cycles field - that name only appears on Phase 5's task.completed
|
|
93
93
|
// summary event, which this never matched. Phase 4 re-runs (and logs its
|
|
94
94
|
// own review.completed) once per rework cycle, so the cycle count for a
|
|
95
95
|
// task is how many review.completed events share its task_id - the same
|
|
@@ -6,11 +6,19 @@
|
|
|
6
6
|
# {
|
|
7
7
|
# "ts": "<ISO 8601 timestamp>",
|
|
8
8
|
# "task_id": "<jira id, github issue, or run id>",
|
|
9
|
-
# "phase": "<0..
|
|
9
|
+
# "phase": "<0..5>",
|
|
10
10
|
# "event": "<event name>",
|
|
11
|
-
# "details": { ...arbitrary key/value pairs... }
|
|
11
|
+
# "details": { ...arbitrary key/value pairs... },
|
|
12
|
+
# "phaseSchema": 2
|
|
12
13
|
# }
|
|
13
14
|
#
|
|
15
|
+
# phaseSchema names the phase vocabulary the line was written under, because
|
|
16
|
+
# this file is append-only and v19.0.0 renumbered the phases. A line with
|
|
17
|
+
# phase 3 means Dev under schema 1 and Review under schema 2. Lines with no
|
|
18
|
+
# phaseSchema field predate v19.0.0 and are schema 1. Aggregators pick their
|
|
19
|
+
# name table from this field; without it the two vocabularies are
|
|
20
|
+
# indistinguishable and every historical roll-up is quietly wrong.
|
|
21
|
+
#
|
|
14
22
|
# Usage:
|
|
15
23
|
# log-metric.sh <task_id> <phase> <event> [key=value ...]
|
|
16
24
|
#
|
|
@@ -18,7 +26,7 @@
|
|
|
18
26
|
# log-metric.sh PROJ-1234 4 review.completed raw_count=8 accepted=3 deferred=2 rejected=3 approved=true duration_ms=42000
|
|
19
27
|
# log-metric.sh PROJ-1234 4 triage.edge_case case=high-rejection-rate
|
|
20
28
|
# log-metric.sh PROJ-1234 3 rework.started iteration=2 accepted_blocking=2
|
|
21
|
-
# log-metric.sh #316
|
|
29
|
+
# log-metric.sh #316 5 task.completed phases=6 review_cycles=2 lang=tr
|
|
22
30
|
#
|
|
23
31
|
# Atomic append: each call uses `>> "$file"` which the kernel guarantees
|
|
24
32
|
# atomic for writes ≤ PIPE_BUF (4 KB on macOS/Linux). One JSONL line is well
|
|
@@ -64,8 +72,13 @@ for kv in "$@"; do
|
|
|
64
72
|
done
|
|
65
73
|
DETAILS="${DETAILS}}"
|
|
66
74
|
|
|
75
|
+
# Read from the phase contract rather than hardcoding: the next renumbering
|
|
76
|
+
# bumps one JSON field instead of hunting for a literal in a shell script.
|
|
77
|
+
PHASE_SCHEMA="$(node -e 'const c=require("'"$(cd "$(dirname "$0")" && pwd)"'/../schemas/phases.json"); process.stdout.write(String(c.phaseSchema))' 2>/dev/null)"
|
|
78
|
+
[ -n "$PHASE_SCHEMA" ] || PHASE_SCHEMA=2
|
|
79
|
+
|
|
67
80
|
TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
68
|
-
LINE="{\"ts\":\"${TS}\",\"task_id\":\"${TASK_ID}\",\"phase\":\"${PHASE}\",\"event\":\"${EVENT}\",\"details\":${DETAILS}}"
|
|
81
|
+
LINE="{\"ts\":\"${TS}\",\"task_id\":\"${TASK_ID}\",\"phase\":\"${PHASE}\",\"event\":\"${EVENT}\",\"details\":${DETAILS},\"phaseSchema\":${PHASE_SCHEMA}}"
|
|
69
82
|
|
|
70
83
|
# Atomic append (one line, well under PIPE_BUF).
|
|
71
84
|
echo "$LINE" >> "$METRICS_FILE" 2>/dev/null || {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# memory-save.sh - v6.2.E
|
|
3
3
|
#
|
|
4
4
|
# Writes a single per-repo memory file and updates the MEMORY.md index.
|
|
5
|
-
# Called by Phase
|
|
5
|
+
# Called by Phase 5's synthesis step (or any user skill that wants to
|
|
6
6
|
# persist a finding). Opt-in via prefs.global.perRepoMemory.
|
|
7
7
|
#
|
|
8
8
|
# Usage:
|
|
@@ -18,9 +18,26 @@ import { runMain } from "../lib/fatal.mjs";
|
|
|
18
18
|
|
|
19
19
|
const DEFAULT_FILE = path.join(os.homedir(), ".claude", "multi-agent-preferences.json");
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
21
|
+
// The migration target, read from the schema rather than written here. It was
|
|
22
|
+
// a literal, and a literal is how it came to say 2.6.0 while the schema had
|
|
23
|
+
// moved to 2.7.0 - the runner then reported every current file as needing a
|
|
24
|
+
// migration it had already had. Referenced by the migrate() bump chain AND
|
|
25
|
+
// every user-facing message, so they cannot drift from each other either.
|
|
26
|
+
const TARGET_VERSION = (() => {
|
|
27
|
+
const schemaPath = path.join(
|
|
28
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
29
|
+
"..",
|
|
30
|
+
"schemas",
|
|
31
|
+
"prefs.schema.json",
|
|
32
|
+
);
|
|
33
|
+
const sv = JSON.parse(fs.readFileSync(schemaPath, "utf-8"))?.properties?.schemaVersion;
|
|
34
|
+
const v = sv?.const ?? sv?.enum?.at(-1);
|
|
35
|
+
if (!v) {
|
|
36
|
+
console.error("migrate-prefs: prefs.schema.json declares no schemaVersion");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
return v;
|
|
40
|
+
})();
|
|
24
41
|
|
|
25
42
|
const USAGE = "Usage: migrate-prefs.mjs [--dry-run] [--file <path>]";
|
|
26
43
|
|
|
@@ -327,7 +344,7 @@ function migrate(prefs) {
|
|
|
327
344
|
out.global.routines = [];
|
|
328
345
|
changes.push("added empty routines (v2.4.0 user-defined /multi-agent:save routines)");
|
|
329
346
|
}
|
|
330
|
-
// v2.5.0 / v14.0.0: Phase
|
|
347
|
+
// v2.5.0 / v14.0.0: Phase 3 Review entered the --dev phase sets and Step 1.78
|
|
331
348
|
// (criteria resolution) landed. Default false because only Swift plus the two
|
|
332
349
|
// store-compliance catalogs ship a scoped registry today, so blocking on a
|
|
333
350
|
// coverage gap would stop every Kotlin/Python/Node/ObjC run from day one. The
|
|
@@ -488,7 +505,7 @@ function migrateReportChannels(out, changes) {
|
|
|
488
505
|
if (typeof g.autopilotReportTimeoutSeconds !== "number") {
|
|
489
506
|
g.autopilotReportTimeoutSeconds = 1800;
|
|
490
507
|
changes.push(
|
|
491
|
-
"added autopilotReportTimeoutSeconds: 1800 (30 min) - Phase
|
|
508
|
+
"added autopilotReportTimeoutSeconds: 1800 (30 min) - Phase 5 always-pause timeout",
|
|
492
509
|
);
|
|
493
510
|
}
|
|
494
511
|
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
# phase-banner.sh label <phase> (print canonical name)
|
|
13
13
|
#
|
|
14
14
|
# Examples:
|
|
15
|
-
# phase-banner.sh start
|
|
16
|
-
# phase-banner.sh start
|
|
17
|
-
# PHASE_LANG=tr phase-banner.sh start
|
|
15
|
+
# phase-banner.sh start 3 "Review" "parallel + triage + user test"
|
|
16
|
+
# phase-banner.sh start 3 auto # resolves to "Review" (en) or "İnceleme" (tr)
|
|
17
|
+
# PHASE_LANG=tr phase-banner.sh start 3 auto # "Faz 3: İnceleme"
|
|
18
18
|
#
|
|
19
19
|
# Status values for `end`: done | failed | skipped
|
|
20
20
|
#
|
|
@@ -32,28 +32,28 @@ set -uo pipefail
|
|
|
32
32
|
|
|
33
33
|
PHASE_LANG="${PHASE_LANG:-en}"
|
|
34
34
|
|
|
35
|
-
# Canonical phase label table.
|
|
36
|
-
# pipeline/
|
|
37
|
-
#
|
|
35
|
+
# Canonical phase label table. The English half is checked against
|
|
36
|
+
# pipeline/schemas/phases.json by smoke-phase-contract.sh, so it cannot drift
|
|
37
|
+
# from the contract in silence the way it did through the eight-phase era. It
|
|
38
|
+
# stays a case statement rather than a jq read because this runs on every
|
|
39
|
+
# banner and the pipeline's zero-dependency rule (ADR-0004) applies to the hot
|
|
40
|
+
# path. The Turkish half has no contract entry - labels are translations, not
|
|
41
|
+
# identifiers - and is kept beside it.
|
|
38
42
|
phase_label() {
|
|
39
43
|
local phase="$1" lang="${2:-en}"
|
|
40
44
|
case "$lang:$phase" in
|
|
41
45
|
en:0) echo "Init" ;;
|
|
42
|
-
en:1) echo "
|
|
43
|
-
en:2) echo "
|
|
44
|
-
en:3) echo "
|
|
45
|
-
en:4) echo "
|
|
46
|
-
en:5) echo "
|
|
47
|
-
en:6) echo "Commit & PR" ;;
|
|
48
|
-
en:7) echo "Report" ;;
|
|
46
|
+
en:1) echo "Plan" ;;
|
|
47
|
+
en:2) echo "Dev" ;;
|
|
48
|
+
en:3) echo "Review" ;;
|
|
49
|
+
en:4) echo "Commit & PR" ;;
|
|
50
|
+
en:5) echo "Report" ;;
|
|
49
51
|
tr:0) echo "Başlangıç" ;;
|
|
50
|
-
tr:1) echo "
|
|
51
|
-
tr:2) echo "
|
|
52
|
-
tr:3) echo "
|
|
53
|
-
tr:4) echo "
|
|
54
|
-
tr:5) echo "
|
|
55
|
-
tr:6) echo "Commit & PR" ;;
|
|
56
|
-
tr:7) echo "Rapor" ;;
|
|
52
|
+
tr:1) echo "Plan" ;;
|
|
53
|
+
tr:2) echo "Geliştirme" ;;
|
|
54
|
+
tr:3) echo "İnceleme" ;;
|
|
55
|
+
tr:4) echo "Commit & PR" ;;
|
|
56
|
+
tr:5) echo "Rapor" ;;
|
|
57
57
|
*) echo "" ;;
|
|
58
58
|
esac
|
|
59
59
|
}
|