@mmerterden/multi-agent-pipeline 16.11.0 → 16.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +9 -7
- package/README.tr.md +9 -7
- package/docs/adr/0001-three-model-triage.md +5 -0
- package/docs/adr/0010-own-code-graph.md +129 -0
- package/docs/adr/README.md +1 -0
- package/docs/architecture.md +2 -2
- package/docs/ecosystem.md +5 -5
- package/docs/features.md +26 -2
- package/package.json +1 -1
- package/pipeline/claude-md-template.md +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/graph/SKILL.md +105 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +10 -10
- package/pipeline/commands/multi-agent/resume-local/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +12 -12
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +9 -7
- package/pipeline/lib/figma-screenshot.sh +107 -7
- package/pipeline/lib/md2confluence-v3.py +133 -25
- package/pipeline/multi-agent-refs/analysis/locked.md +4 -3
- package/pipeline/multi-agent-refs/analysis/render.md +44 -9
- package/pipeline/multi-agent-refs/analysis/review.md +17 -1
- package/pipeline/multi-agent-refs/analysis-template-corporate.md +14 -3
- package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -10
- package/pipeline/multi-agent-refs/features/code-graph.md +62 -0
- package/pipeline/multi-agent-refs/features/model-fallback.md +44 -2
- package/pipeline/multi-agent-refs/knowledge.md +7 -1
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +5 -0
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +9 -9
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +2 -0
- package/pipeline/preferences-template.json +2 -0
- package/pipeline/schemas/analysis-spec.schema.json +2 -0
- package/pipeline/schemas/code-graph.schema.json +91 -0
- package/pipeline/schemas/prefs.schema.json +45 -0
- package/pipeline/schemas/reviewer-output.schema.json +1 -1
- package/pipeline/schemas/token-budget.json +2 -2
- package/pipeline/schemas/triage-output.schema.json +1 -1
- package/pipeline/scripts/_code-graph.mjs +518 -0
- package/pipeline/scripts/_path-match.mjs +87 -0
- package/pipeline/scripts/anonymize-findings.mjs +1 -1
- package/pipeline/scripts/code-graph-rules/android.json +130 -0
- package/pipeline/scripts/code-graph-rules/ios.json +95 -0
- package/pipeline/scripts/code-graph-rules/node.json +151 -0
- package/pipeline/scripts/code-graph-rules/python.json +91 -0
- package/pipeline/scripts/graph-affected.mjs +161 -0
- package/pipeline/scripts/graph-build.mjs +157 -0
- package/pipeline/scripts/graph-query.mjs +191 -0
- package/pipeline/scripts/graph-report.mjs +237 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +13 -10
- package/pipeline/scripts/test-gap-rules/ios.json +38 -10
- package/pipeline/scripts/test-gap-scan.mjs +2 -21
- package/pipeline/scripts/uninstall.mjs +11 -2
- package/pipeline/scripts/validate-analysis-doc.mjs +85 -23
- package/pipeline/scripts/validate-code-graph.mjs +174 -0
- package/pipeline/skills/.skills-index.json +14 -3
- package/pipeline/skills/shared/README.md +6 -5
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +106 -0
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +11 -11
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +4 -4
- package/pipeline/skills/skills-index.md +4 -3
|
@@ -42,6 +42,7 @@ import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
|
42
42
|
import { execFileSync } from "node:child_process";
|
|
43
43
|
import { join, dirname, extname, basename } from "node:path";
|
|
44
44
|
import { fileURLToPath } from "node:url";
|
|
45
|
+
import { isExcludedBy } from "./_path-match.mjs";
|
|
45
46
|
|
|
46
47
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
47
48
|
|
|
@@ -118,27 +119,7 @@ function loadDiff() {
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
function isExcluded(path) {
|
|
121
|
-
|
|
122
|
-
for (const pat of rules.excludePathGlobs) {
|
|
123
|
-
if (pat.endsWith("/")) {
|
|
124
|
-
if (path.includes(pat) || path.startsWith(pat)) return true;
|
|
125
|
-
} else if (pat.includes("**")) {
|
|
126
|
-
// Tokenize ** before the single-* pass so the * rewrite cannot mangle
|
|
127
|
-
// the already-translated .* (which broke cross-directory matches).
|
|
128
|
-
const DOUBLE_STAR = "\u0000";
|
|
129
|
-
const re = new RegExp(
|
|
130
|
-
"^" +
|
|
131
|
-
pat
|
|
132
|
-
.replace(/\./g, "\\.")
|
|
133
|
-
.replace(/\*\*/g, DOUBLE_STAR)
|
|
134
|
-
.replace(/\*/g, "[^/]*")
|
|
135
|
-
.replace(new RegExp(DOUBLE_STAR, "g"), ".*") +
|
|
136
|
-
"$",
|
|
137
|
-
);
|
|
138
|
-
if (re.test(path)) return true;
|
|
139
|
-
} else if (path.endsWith(pat)) return true;
|
|
140
|
-
}
|
|
141
|
-
return false;
|
|
122
|
+
return isExcludedBy(path, rules.excludePathGlobs);
|
|
142
123
|
}
|
|
143
124
|
|
|
144
125
|
// git quotes a `diff --git` path (core.quotePath, on by default) whenever it
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* - $HOME/.claude/multi-agent-preferences.json (pipeline settings)
|
|
33
33
|
* - $HOME/.claude/logs/multi-agent/ (task logs, state, metrics, audit trail)
|
|
34
34
|
* - $HOME/.claude/memory/multi-agent/ (per-repo learnings ledger + triage corpus)
|
|
35
|
+
* - $HOME/.claude/knowledge/ (per-repo knowledge base + code graph)
|
|
35
36
|
*
|
|
36
37
|
* @module pipeline/scripts/runtime/uninstall
|
|
37
38
|
*/
|
|
@@ -726,6 +727,7 @@ export async function main() {
|
|
|
726
727
|
console.log(" - ~/.claude/multi-agent-preferences.json (pipeline settings)");
|
|
727
728
|
console.log(" - ~/.claude/logs/multi-agent/ (task logs, state, metrics, audit trail)");
|
|
728
729
|
console.log(" - ~/.claude/memory/multi-agent/ (per-repo learnings ledger + triage corpus)");
|
|
730
|
+
console.log(" - ~/.claude/knowledge/ (per-repo knowledge base + code graph)");
|
|
729
731
|
console.log("");
|
|
730
732
|
}
|
|
731
733
|
console.log(" PRESERVED (never touched):");
|
|
@@ -834,12 +836,19 @@ export async function main() {
|
|
|
834
836
|
// architectural facts and past review findings written in prose about the
|
|
835
837
|
// repos they were collected from. On a corporate checkout that is concrete
|
|
836
838
|
// information about the codebase, and it survived even --all-data.
|
|
839
|
+
//
|
|
840
|
+
// The knowledge root is the same class of data and was missing for the same
|
|
841
|
+
// reason: architecture.md, patterns.md, gotchas.md and decisions.md are prose
|
|
842
|
+
// about a private codebase, and code-graph.json is every symbol name in it.
|
|
843
|
+
// Leaving that behind after "remove everything" is the worst version of this
|
|
844
|
+
// bug, because the graph is the most complete of the three.
|
|
837
845
|
if (allData && HOME) {
|
|
838
846
|
console.log("");
|
|
839
|
-
console.log(" [Full cleanup] Removing pipeline settings, logs +
|
|
847
|
+
console.log(" [Full cleanup] Removing pipeline settings, logs, memory + knowledge...");
|
|
840
848
|
preservePrefsCredentialMapping(join(HOME, ".claude", "multi-agent-preferences.json"));
|
|
841
849
|
rmIfExists(join(HOME, ".claude", "logs", "multi-agent"));
|
|
842
850
|
rmIfExists(join(HOME, ".claude", "memory", "multi-agent"));
|
|
851
|
+
rmIfExists(join(HOME, ".claude", "knowledge"));
|
|
843
852
|
}
|
|
844
853
|
|
|
845
854
|
// Legacy adapter-file cleanup (adapters removed in v10.7.0). These blocks
|
|
@@ -917,7 +926,7 @@ export async function main() {
|
|
|
917
926
|
if (!allData) {
|
|
918
927
|
console.log("");
|
|
919
928
|
console.log(
|
|
920
|
-
" Kept your pipeline settings, logs
|
|
929
|
+
" Kept your pipeline settings, logs, per-repo memory + knowledge base. To remove those too, re-run with --all-data.",
|
|
921
930
|
);
|
|
922
931
|
} else {
|
|
923
932
|
console.log("");
|
|
@@ -215,11 +215,15 @@ function main() {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
// 2a. Corporate profile: every EKLENECEK owes an open question.
|
|
218
|
+
// 2a. Corporate profile: every EKLENECEK owes an open question, by id.
|
|
219
219
|
//
|
|
220
220
|
// A placeholder with nobody assigned to resolve it is how "to be filled in"
|
|
221
|
-
// reaches production.
|
|
222
|
-
//
|
|
221
|
+
// reaches production. This used to pair a gap with Section 20 by matching the
|
|
222
|
+
// section NUMBER inside the risks block, which is ambiguous in both directions:
|
|
223
|
+
// "9" is a substring of "19" and "5.9" of "5.9.5", so the regex grew guards for
|
|
224
|
+
// cases it still could not separate. Stable ids remove the ambiguity the same
|
|
225
|
+
// way IG / UC / FG already do: the body writes EKLENECEK (AS-07) and Section 20
|
|
226
|
+
// defines AS-07, and each side is checked against the other.
|
|
223
227
|
if (profile === "corporate") {
|
|
224
228
|
const lines = text.split("\n");
|
|
225
229
|
const risksStart = lines.findIndex((l) =>
|
|
@@ -232,29 +236,61 @@ function main() {
|
|
|
232
236
|
risksBlock += `${lines[i]}\n`;
|
|
233
237
|
}
|
|
234
238
|
}
|
|
235
|
-
|
|
236
|
-
const
|
|
239
|
+
const AS_ID = /\bAS-(\d{2,3})\b/g;
|
|
240
|
+
const definedIds = new Set(
|
|
241
|
+
[...risksBlock.matchAll(AS_ID)].map((m) => `AS-${m[1]}`),
|
|
242
|
+
);
|
|
243
|
+
const referencedIds = new Set();
|
|
237
244
|
for (let i = 0; i < lines.length; i++) {
|
|
238
|
-
const m = lines[i].match(/^#{2,4}\s+(\d+(?:\.\d+)*)\.?\s/);
|
|
239
|
-
if (m) currentSection = m[1];
|
|
240
|
-
if (!lines[i].includes("EKLENECEK")) continue;
|
|
241
245
|
if (risksStart !== -1 && i > risksStart) continue;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
);
|
|
252
|
-
if (!token.test(risksBlock)) unpaired.add(currentSection);
|
|
246
|
+
for (const m of lines[i].matchAll(AS_ID)) referencedIds.add(`AS-${m[1]}`);
|
|
247
|
+
if (!lines[i].includes("EKLENECEK")) continue;
|
|
248
|
+
// A gap that names no id cannot be traced to an owner, which is the whole
|
|
249
|
+
// point of admitting it.
|
|
250
|
+
if (!/\bAS-\d{2,3}\b/.test(lines[i])) {
|
|
251
|
+
errors.push(
|
|
252
|
+
`line ${i + 1}: EKLENECEK carries no AS-NN reference (Locked 33)`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
253
255
|
}
|
|
254
|
-
for (const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
for (const id of referencedIds) {
|
|
257
|
+
if (!definedIds.has(id)) {
|
|
258
|
+
errors.push(
|
|
259
|
+
`${id} is referenced in the body but not defined in Risks and Open Questions (Locked 33)`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (const id of definedIds) {
|
|
264
|
+
if (!referencedIds.has(id)) {
|
|
265
|
+
// An open question with no body reference is allowed only when the row
|
|
266
|
+
// says so; otherwise it is a question about nothing the document raises.
|
|
267
|
+
const row = risksBlock
|
|
268
|
+
.split("\n")
|
|
269
|
+
.find((l) => l.includes(id)) || "";
|
|
270
|
+
if (!/(no body reference|govde referansi yok)/i.test(row)) {
|
|
271
|
+
errors.push(
|
|
272
|
+
`${id} is defined in Risks and Open Questions but never referenced in the body (Locked 33)`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 2b. JSON does not fit in a table cell (Locked 37).
|
|
279
|
+
//
|
|
280
|
+
// One document rendered `Request` as inline JSON with a field note glued to
|
|
281
|
+
// the end, and `Response` as a proper fenced block below the table: two
|
|
282
|
+
// formats for the same thing, one table apart. The payload belongs under the
|
|
283
|
+
// table; the cell says which of the three states it is in.
|
|
284
|
+
for (let i = 0; i < lines.length; i++) {
|
|
285
|
+
const line = lines[i];
|
|
286
|
+
if (!/^\|\s*\*\*(Request|Response)\*\*\s*\|/.test(line)) continue;
|
|
287
|
+
const cell = line.split("|")[2] || "";
|
|
288
|
+
if (/[{}]/.test(cell)) {
|
|
289
|
+
errors.push(
|
|
290
|
+
`line ${i + 1}: ${/Request/.test(line) ? "Request" : "Response"} cell holds JSON; ` +
|
|
291
|
+
"put the payload in a fenced block under the table (Locked 37)",
|
|
292
|
+
);
|
|
293
|
+
}
|
|
258
294
|
}
|
|
259
295
|
|
|
260
296
|
// The traceability matrix is the most expensive thing in the document to get
|
|
@@ -295,6 +331,32 @@ function main() {
|
|
|
295
331
|
}
|
|
296
332
|
}
|
|
297
333
|
|
|
334
|
+
// 2c. Diagram labels keep their diacritics.
|
|
335
|
+
//
|
|
336
|
+
// The punctuation gate skips fenced blocks, so a mermaid diagram is the one
|
|
337
|
+
// place a Turkish document can quietly go ASCII: one run shipped `Hayir` and
|
|
338
|
+
// `Basarili` in its flow chart while every other line was correct. A warning,
|
|
339
|
+
// not an error - node ids are legitimately ASCII, only labels are checked.
|
|
340
|
+
if (parsed.fm.language === "tr") {
|
|
341
|
+
const mmLines = text.split("\n");
|
|
342
|
+
let inMermaid = false;
|
|
343
|
+
for (let i = 0; i < mmLines.length; i++) {
|
|
344
|
+
const t = mmLines[i].trim();
|
|
345
|
+
if (/^```mermaid\s*$/.test(t)) { inMermaid = true; continue; }
|
|
346
|
+
if (inMermaid && t === "```") { inMermaid = false; continue; }
|
|
347
|
+
if (!inMermaid) continue;
|
|
348
|
+
const labels = [...mmLines[i].matchAll(/[[({|]([^\]})|]{3,})[\]})|]/g)].map((m) => m[1]);
|
|
349
|
+
for (const lbl of labels) {
|
|
350
|
+
if (/^[\x20-\x7E]+$/.test(lbl) && /\b(Hayir|Basarili|Basarisiz|Odeme|Iptal|Gecerli|Gecersiz|Secim|Dogrulama|Uyari|Baslangic|Bitis|Onay|Aciklama)\b/.test(lbl)) {
|
|
351
|
+
errors.push(
|
|
352
|
+
`WARN line ${i + 1}: diagram label "${lbl}" looks like Turkish flattened to ASCII (Locked 7)`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
298
360
|
// 2b. Opt-in coverage sections must be present when the front-matter says so.
|
|
299
361
|
const uiTests = String(parsed?.fm?.ui_tests || "false").toLowerCase() === "true";
|
|
300
362
|
const a11yDepth = String(parsed?.fm?.a11y_depth || "basic").toLowerCase();
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// validate-code-graph.mjs - schema validator for graph-build.mjs output.
|
|
3
|
+
//
|
|
4
|
+
// Zero deps (ADR-0004). Reads JSON from path or STDIN, validates against
|
|
5
|
+
// pipeline/schemas/code-graph.schema.json.
|
|
6
|
+
//
|
|
7
|
+
// Edge endpoints are checked against the node set, not just for type: a graph
|
|
8
|
+
// whose edges point at ids no node declares still parses as valid JSON, but
|
|
9
|
+
// every traversal built on it silently drops those hops.
|
|
10
|
+
//
|
|
11
|
+
// Exit codes:
|
|
12
|
+
// 0 - valid
|
|
13
|
+
// 1 - invalid
|
|
14
|
+
// 64 - usage error
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
const ALLOWED_STACK = new Set(["ios", "android", "node", "python"]);
|
|
19
|
+
const ALLOWED_NODE_KIND = new Set(["file", "symbol", "module"]);
|
|
20
|
+
const ALLOWED_EDGE_KIND = new Set(["defines", "imports", "references"]);
|
|
21
|
+
const MAX_REPORTED_DANGLING = 5;
|
|
22
|
+
|
|
23
|
+
function readInput() {
|
|
24
|
+
const arg = process.argv[2];
|
|
25
|
+
if (!arg) {
|
|
26
|
+
console.error("error: provide a path or '-' for STDIN");
|
|
27
|
+
process.exit(64);
|
|
28
|
+
}
|
|
29
|
+
if (arg === "-") {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const chunks = [];
|
|
32
|
+
process.stdin.setEncoding("utf-8");
|
|
33
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
34
|
+
process.stdin.on("end", () => resolve(chunks.join("")));
|
|
35
|
+
process.stdin.on("error", reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return Promise.resolve(readFileSync(arg, "utf-8"));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateStats(obj, errors) {
|
|
42
|
+
if (typeof obj.stats !== "object" || obj.stats === null || Array.isArray(obj.stats)) {
|
|
43
|
+
errors.push("stats must be an object");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const key of ["files", "nodes", "edges"]) {
|
|
47
|
+
if (!Number.isInteger(obj.stats[key]) || obj.stats[key] < 0) {
|
|
48
|
+
errors.push(`stats.${key} must be a non-negative integer`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(obj.nodes) && obj.stats.nodes !== obj.nodes.length) {
|
|
52
|
+
errors.push(
|
|
53
|
+
`stats.nodes (${obj.stats.nodes}) does not match nodes.length (${obj.nodes.length})`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(obj.edges) && obj.stats.edges !== obj.edges.length) {
|
|
57
|
+
errors.push(
|
|
58
|
+
`stats.edges (${obj.stats.edges}) does not match edges.length (${obj.edges.length})`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function validateNodes(obj, errors) {
|
|
64
|
+
if (!Array.isArray(obj.nodes)) {
|
|
65
|
+
errors.push("nodes must be an array");
|
|
66
|
+
return new Set();
|
|
67
|
+
}
|
|
68
|
+
const ids = new Set();
|
|
69
|
+
obj.nodes.forEach((n, i) => {
|
|
70
|
+
const p = `nodes[${i}]`;
|
|
71
|
+
if (typeof n !== "object" || n === null) {
|
|
72
|
+
errors.push(`${p} must be an object`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (typeof n.id !== "string" || n.id.length === 0) errors.push(`${p}.id must be a string`);
|
|
76
|
+
else if (ids.has(n.id)) errors.push(`${p}.id is duplicated: ${n.id}`);
|
|
77
|
+
else ids.add(n.id);
|
|
78
|
+
if (!ALLOWED_NODE_KIND.has(n.kind))
|
|
79
|
+
errors.push(`${p}.kind must be one of ${[...ALLOWED_NODE_KIND].join(",")}`);
|
|
80
|
+
if (typeof n.name !== "string") errors.push(`${p}.name must be a string`);
|
|
81
|
+
if (!Number.isInteger(n.degree) || n.degree < 0)
|
|
82
|
+
errors.push(`${p}.degree must be a non-negative integer`);
|
|
83
|
+
});
|
|
84
|
+
return ids;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function validateEdges(obj, ids, errors) {
|
|
88
|
+
if (!Array.isArray(obj.edges)) {
|
|
89
|
+
errors.push("edges must be an array");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const dangling = [];
|
|
93
|
+
obj.edges.forEach((e, i) => {
|
|
94
|
+
const p = `edges[${i}]`;
|
|
95
|
+
if (typeof e !== "object" || e === null) {
|
|
96
|
+
errors.push(`${p} must be an object`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (typeof e.from !== "string") errors.push(`${p}.from must be a string`);
|
|
100
|
+
if (typeof e.to !== "string") errors.push(`${p}.to must be a string`);
|
|
101
|
+
if (!ALLOWED_EDGE_KIND.has(e.kind))
|
|
102
|
+
errors.push(`${p}.kind must be one of ${[...ALLOWED_EDGE_KIND].join(",")}`);
|
|
103
|
+
if (ids.size) {
|
|
104
|
+
if (typeof e.from === "string" && !ids.has(e.from)) dangling.push(`${p}.from -> ${e.from}`);
|
|
105
|
+
if (typeof e.to === "string" && !ids.has(e.to)) dangling.push(`${p}.to -> ${e.to}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (dangling.length) {
|
|
109
|
+
errors.push(
|
|
110
|
+
`${dangling.length} edge endpoint(s) reference no node: ` +
|
|
111
|
+
dangling.slice(0, MAX_REPORTED_DANGLING).join("; ") +
|
|
112
|
+
(dangling.length > MAX_REPORTED_DANGLING ? "; ..." : ""),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateManifest(obj, errors) {
|
|
118
|
+
if (obj.manifest === undefined) return;
|
|
119
|
+
if (typeof obj.manifest !== "object" || obj.manifest === null || Array.isArray(obj.manifest)) {
|
|
120
|
+
errors.push("manifest must be an object");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
for (const [path, entry] of Object.entries(obj.manifest)) {
|
|
124
|
+
if (typeof entry !== "object" || entry === null) {
|
|
125
|
+
errors.push(`manifest["${path}"] must be an object`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (!Number.isInteger(entry.size) || entry.size < 0)
|
|
129
|
+
errors.push(`manifest["${path}"].size must be a non-negative integer`);
|
|
130
|
+
if (!Number.isInteger(entry.mtimeMs) || entry.mtimeMs < 0)
|
|
131
|
+
errors.push(`manifest["${path}"].mtimeMs must be a non-negative integer`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function validate(obj) {
|
|
136
|
+
const errors = [];
|
|
137
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj))
|
|
138
|
+
return ["root must be an object"];
|
|
139
|
+
|
|
140
|
+
if (obj.schemaVersion !== "1.0.0") errors.push(`schemaVersion must be "1.0.0"`);
|
|
141
|
+
if (!ALLOWED_STACK.has(obj.stack))
|
|
142
|
+
errors.push(`stack must be one of ${[...ALLOWED_STACK].join(",")}`);
|
|
143
|
+
if (typeof obj.root !== "string" || obj.root.length === 0)
|
|
144
|
+
errors.push("root must be a non-empty string");
|
|
145
|
+
if (typeof obj.generatedAt !== "string" || Number.isNaN(Date.parse(obj.generatedAt)))
|
|
146
|
+
errors.push("generatedAt must be an ISO 8601 timestamp");
|
|
147
|
+
if (obj.baseCommit !== undefined && obj.baseCommit !== null && typeof obj.baseCommit !== "string")
|
|
148
|
+
errors.push("baseCommit must be a string or null");
|
|
149
|
+
|
|
150
|
+
validateStats(obj, errors);
|
|
151
|
+
const ids = validateNodes(obj, errors);
|
|
152
|
+
validateEdges(obj, ids, errors);
|
|
153
|
+
validateManifest(obj, errors);
|
|
154
|
+
|
|
155
|
+
return errors;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
(async () => {
|
|
159
|
+
const raw = await readInput();
|
|
160
|
+
let obj;
|
|
161
|
+
try {
|
|
162
|
+
obj = JSON.parse(raw);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.log(JSON.stringify({ ok: false, errors: [`JSON parse error: ${e.message}`] }));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
const errors = validate(obj);
|
|
168
|
+
if (errors.length === 0) {
|
|
169
|
+
console.log(JSON.stringify({ ok: true, errors: [] }));
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
console.log(JSON.stringify({ ok: false, errors }));
|
|
173
|
+
process.exit(1);
|
|
174
|
+
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"skillCount":
|
|
3
|
+
"skillCount": 211,
|
|
4
4
|
"entries": [
|
|
5
5
|
{
|
|
6
6
|
"name": "accessibility-compliance-accessibility-audit",
|
|
@@ -950,7 +950,7 @@
|
|
|
950
950
|
},
|
|
951
951
|
{
|
|
952
952
|
"name": "multi-agent",
|
|
953
|
-
"description": "Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable + Sonnet on Claude Code, GPT + Opus + Sonnet on Copilot CLI) → commit → log. Every step is written to agent-log.md. Use when given a Jira ID, a GitHub
|
|
953
|
+
"description": "Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable + Opus + Sonnet on Claude Code, GPT + Opus + Sonnet on Copilot CLI) → commit → log. Every step is written to agent-log.md. Use when given a Jira ID, a GitHub is",
|
|
954
954
|
"platform": null,
|
|
955
955
|
"group": "core",
|
|
956
956
|
"plugin": null,
|
|
@@ -1135,6 +1135,17 @@
|
|
|
1135
1135
|
"triggerPaths": [],
|
|
1136
1136
|
"relativePath": "shared/core/multi-agent-garbage-collect/SKILL.md"
|
|
1137
1137
|
},
|
|
1138
|
+
{
|
|
1139
|
+
"name": "multi-agent-graph",
|
|
1140
|
+
"description": "Build and query this repo's code graph: a deterministic, LLM-free map of symbols, imports and references used to narrow Phase 1's Explore scope and to keep the knowledge base current. Read-only on code, costs no API tokens. Use when a task needs to know where something lives or what depends on it be",
|
|
1141
|
+
"platform": null,
|
|
1142
|
+
"group": "core",
|
|
1143
|
+
"plugin": null,
|
|
1144
|
+
"invokeAs": "multi-agent-graph",
|
|
1145
|
+
"triggerKeywords": [],
|
|
1146
|
+
"triggerPaths": [],
|
|
1147
|
+
"relativePath": "shared/core/multi-agent-graph/SKILL.md"
|
|
1148
|
+
},
|
|
1138
1149
|
{
|
|
1139
1150
|
"name": "multi-agent-help",
|
|
1140
1151
|
"description": "Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatibility). Use when asked what the pipeline can do, which mode to pick, or what a command means.",
|
|
@@ -1313,7 +1324,7 @@
|
|
|
1313
1324
|
},
|
|
1314
1325
|
{
|
|
1315
1326
|
"name": "multi-agent-review",
|
|
1316
|
-
"description": "Run parallel review on a branch diff or a Pull Request:
|
|
1327
|
+
"description": "Run parallel review on a branch diff or a Pull Request: 3 models on Claude Code (Fable + Opus + Sonnet), 3 models on Copilot CLI (GPT + Opus + Sonnet). On PR input, posts per-finding inline comments + approve/needs-work. With no input (interactive), lists open GitHub + Bitbucket PRs to multi-select.",
|
|
1317
1328
|
"platform": null,
|
|
1318
1329
|
"group": "core",
|
|
1319
1330
|
"plugin": null,
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Single source of truth for skills delivered to both Claude Code (`~/.claude/skills/`) and Copilot CLI (`~/.copilot/skills/`) by the installer.
|
|
4
4
|
|
|
5
|
-
**Total:**
|
|
5
|
+
**Total:** 211 skills (57 core + 154 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
|
|
6
6
|
|
|
7
7
|
## Directory layout
|
|
8
8
|
|
|
9
|
-
- **`core/`** -
|
|
9
|
+
- **`core/`** - 57 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
|
|
10
10
|
- **`external/`** - 154 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
|
|
11
11
|
- Install destinations (ADR-0009): Claude Code gets NO local copy of `external/` - it loads those skills from the `multi-agent-plugins` marketplace, namespaced (`ai-<stack>-toolkit:<name>`); only the two compliance catalogs from `core/` land in `~/.claude/skills/`. Copilot CLI and Codex CLI receive a flat copy filtered to the enabled stacks. `external/` remains the single authoring source that `build-stack-plugins.mjs` publishes from.
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
14
14
|
|
|
15
15
|
## Categories
|
|
16
16
|
|
|
17
|
-
- [Pipeline Orchestration](#pipeline-orchestration) -
|
|
17
|
+
- [Pipeline Orchestration](#pipeline-orchestration) - 57
|
|
18
18
|
- [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 90
|
|
19
19
|
- [Android / Kotlin](#android-kotlin) - 13
|
|
20
20
|
- [Web](#web) - 10
|
|
@@ -44,6 +44,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
44
44
|
| [`multi-agent-feedback`](./core/multi-agent-feedback/) | `core` | Send one message to the maintainer: a bug, an idea or a question. Only the text you type is sent - no logs, no repo names, no paths. Shows t |
|
|
45
45
|
| [`multi-agent-forget`](./core/multi-agent-forget/) | `core` | Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one an |
|
|
46
46
|
| [`multi-agent-garbage-collect`](./core/multi-agent-garbage-collect/) | `core` | Sweep leftover /tmp scratch (picker state, review diffs, channel payloads, analysis drafts) from past runs. Dry-run first; confirms before d |
|
|
47
|
+
| [`multi-agent-graph`](./core/multi-agent-graph/) | `core` | Build and query this repo's code graph: a deterministic, LLM-free map of symbols, imports and references used to narrow Phase 1's Explore sc |
|
|
47
48
|
| [`multi-agent-help`](./core/multi-agent-help/) | `core` | Multi-agent pipeline usage guide - renders in EN or TR per prefs.global.outputLanguage (falls back to promptLanguage for backward compatib |
|
|
48
49
|
| [`multi-agent-ios-coding-standard`](./core/multi-agent-ios-coding-standard/) | `core` | Audit an iOS module against the shared coding-standard registry (99 stable-ID rules), produce a remediation plan, then hand off to dev/dev-l |
|
|
49
50
|
| [`multi-agent-issue`](./core/multi-agent-issue/) | `core` | List unassigned GitHub issues, pick one, auto-assign, and launch the multi-agent pipeline. Use when a GitHub issue should be picked up and s |
|
|
@@ -60,7 +61,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
60
61
|
| [`multi-agent-refactor`](./core/multi-agent-refactor/) | `core` | Analyse the project: extract adapted best-practices, hunt real bugs + improvement areas, check upstream drift of derived skills, research th |
|
|
61
62
|
| [`multi-agent-resume`](./core/multi-agent-resume/) | `core` | Resume a stopped or failed task from the phase where it left off. Use when a task stopped or failed and should carry on from where it left o |
|
|
62
63
|
| [`multi-agent-resume-local`](./core/multi-agent-resume-local/) | `core` | Continue already-done LOCAL work through the pipeline tail: Review → Build+Test → Commit/PR → Report (technical analysis + Jira test-scenari |
|
|
63
|
-
| [`multi-agent-review`](./core/multi-agent-review/) | `core` | Run parallel review on a branch diff or a Pull Request:
|
|
64
|
+
| [`multi-agent-review`](./core/multi-agent-review/) | `core` | Run parallel review on a branch diff or a Pull Request: 3 models on Claude Code (Fable + Opus + Sonnet), 3 models on Copilot CLI (GPT + Opus |
|
|
64
65
|
| [`multi-agent-review-analysis`](./core/multi-agent-review-analysis/) | `core` | Review a written analysis document instead of a diff: resolve it from a path, a Confluence page or a Jira issue, run the deterministic gates |
|
|
65
66
|
| [`multi-agent-review-issue`](./core/multi-agent-review-issue/) | `core` | Assess whether a GitHub issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / sta |
|
|
66
67
|
| [`multi-agent-review-jira`](./core/multi-agent-review-jira/) | `core` | Assess whether a Jira issue is ready for multi-agent development: fetch it, grade scope / acceptance criteria / repro / design / API / stack |
|
|
@@ -258,6 +259,6 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
|
|
|
258
259
|
| [`xcode-build-benchmark`](./external/xcode-build-benchmark/) | `external` | Benchmark Xcode clean and incremental builds with repeatable inputs, timing summaries, and timestamped `.build-benchmark/` artifacts. Use wh |
|
|
259
260
|
| [`xcode-build-fixer`](./external/xcode-build-fixer/) | `external` | Apply approved Xcode build optimization changes following best practices, then re-benchmark to verify improvement. Use when a developer has |
|
|
260
261
|
| [`xcode-build-orchestrator`](./external/xcode-build-orchestrator/) | `external` | Orchestrate Xcode build optimization by benchmarking first, running the specialist analysis skills, prioritizing findings, requesting explic |
|
|
261
|
-
| [`xcode-compilation-analyzer`](./external/xcode-compilation-analyzer/) | `external` | Analyze Swift and mixed-language compile hotspots using build timing summaries and Swift
|
|
262
|
+
| [`xcode-compilation-analyzer`](./external/xcode-compilation-analyzer/) | `external` | Analyze Swift and mixed-language compile hotspots using build timing summaries and Swift frontend diagnostics, then produce a recommend-firs |
|
|
262
263
|
| [`xcode-project-analyzer`](./external/xcode-project-analyzer/) | `external` | Audit Xcode project configuration, build settings, scheme behavior, and script phases to find build-time improvements with explicit approval |
|
|
263
264
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: multi-agent
|
|
3
3
|
language: en
|
|
4
|
-
description: "Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable + Sonnet on Claude Code, GPT + Opus + Sonnet on Copilot CLI) → commit → log. Every step is written to agent-log.md. Use when given a Jira ID, a GitHub issue or a free-text task and the whole pipeline should run."
|
|
4
|
+
description: "Task orchestrator: runs the full pipeline from a Jira ID or GitHub Issue URL - analysis → plan → TDD development → parallel review (Fable + Opus + Sonnet on Claude Code, GPT + Opus + Sonnet on Copilot CLI) → commit → log. Every step is written to agent-log.md. Use when given a Jira ID, a GitHub issue or a free-text task and the whole pipeline should run."
|
|
5
5
|
user-invocable: true
|
|
6
6
|
argument-hint: '"PROJ-12345" "feature/PROJ-12345-flight-filter" | "https://github.com/.../issues/316" | status | log #1 | resume #1 | kill #1 | clear-logs | purge | review'
|
|
7
7
|
---
|
|
@@ -455,7 +455,7 @@ For each todo (respecting dependency order):
|
|
|
455
455
|
### Phase 4: Review (parallel + triage)
|
|
456
456
|
0. **Diff Risk Scoring (advisory, v8.3+)** - before reviewer dispatch run `node $HOME/.claude/scripts/diff-risk-score.mjs --base "$BASE_BRANCH" --top 5` and inject the top-N risk-ranked files as a `${PRIORITY_FILES}` block into each reviewer's prompt. Heuristic, deterministic, sub-second, never gates the pipeline. Disabled when `prefs.global.diffRiskAdvisory = false`. Signals: security paths (×3), schema migrations (×4), public API surfaces (×2), no-test-change (×2.5), complexity delta (×1.5), UI-critical paths (×1.5), loc changed (×1).
|
|
457
457
|
1. Launch **code-reviewer** agents in parallel. Reviewer set depends on which CLI is hosting the pipeline:
|
|
458
|
-
- **Claude Code** (
|
|
458
|
+
- **Claude Code** (3 reviewers): `claude-fable-5` (deep security + architecture) + `claude-sonnet-5` (quality + correctness)
|
|
459
459
|
- **Copilot CLI** (3 reviewers): `gpt-5.4` (edge cases, different perspective) + `claude-opus-5` + `claude-sonnet-5` (Fable 5 is not offered on Copilot CLI)
|
|
460
460
|
- Triage: single top-tier pass over merged findings (`claude-fable-5` on Claude Code, `claude-opus-5` on Copilot CLI)
|
|
461
461
|
2. Collect findings, classify:
|
|
@@ -656,7 +656,7 @@ Phase 3: Dev
|
|
|
656
656
|
└─ TDD, token rules, code style - from global instructions
|
|
657
657
|
|
|
658
658
|
Phase 4: Review (parallel + triage)
|
|
659
|
-
Claude Code (
|
|
659
|
+
Claude Code (3 parallel):
|
|
660
660
|
├─ code-reviewer (claude-opus) → all 5 review skills
|
|
661
661
|
└─ code-reviewer (claude-sonnet) → all 5 review skills
|
|
662
662
|
Copilot CLI (3 parallel):
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: multi-agent-graph
|
|
3
|
+
language: en
|
|
4
|
+
description: "Build and query this repo's code graph: a deterministic, LLM-free map of symbols, imports and references used to narrow Phase 1's Explore scope and to keep the knowledge base current. Read-only on code, costs no API tokens. Use when a task needs to know where something lives or what depends on it before reading files."
|
|
5
|
+
user-invocable: true
|
|
6
|
+
argument-hint: "[build | refresh | ask \"<question>\" | affected \"<symbol>\" | report | status]"
|
|
7
|
+
---
|
|
8
|
+
# multi-agent-graph - code graph build and query
|
|
9
|
+
|
|
10
|
+
**Input**: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
A code graph is a map of what this repo declares and what refers to what, built by
|
|
13
|
+
regex over comment-stripped source. It exists so Phase 1 can narrow its Explore
|
|
14
|
+
fan-out and Phase 7 can refresh `~/.claude/knowledge/<project>/` without an LLM
|
|
15
|
+
pass. It answers "where does this live" and "what depends on this". It does not
|
|
16
|
+
answer "what calls this at runtime": call graphs and type resolution need a real
|
|
17
|
+
parser, which would be an npm runtime dependency, and ADR-0004 forbids one.
|
|
18
|
+
|
|
19
|
+
No worktree, no branch, no commit, no pipeline chaining.
|
|
20
|
+
|
|
21
|
+
## Sub-commands
|
|
22
|
+
|
|
23
|
+
| Input | Runs | Notes |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `build` | `graph-build.mjs --root <repo> --stack <stack>` | Writes `~/.claude/knowledge/<project>/code-graph.json` |
|
|
26
|
+
| `refresh` | same as `build` | A full rebuild takes seconds, so there is no separate incremental path |
|
|
27
|
+
| `ask "<question>"` | `graph-query.mjs "<question>" --budget N` | Token-budgeted traversal; default budget 2000 |
|
|
28
|
+
| `affected "<symbol>"` | `graph-affected.mjs "<symbol>" --depth N` | Reverse traversal: the blast radius of a change |
|
|
29
|
+
| `report` | `graph-report.mjs` | Writes `GRAPH_REPORT.md` beside the graph |
|
|
30
|
+
| `status` | `graph-report.mjs --status` | One line: stack, scale, build time and whether `baseCommit` still matches HEAD. Never read the graph file yourself - it is 22MB on a large repo |
|
|
31
|
+
|
|
32
|
+
With no argument, run `status`, then offer `build` when no graph exists and
|
|
33
|
+
`refresh` when `baseCommit` differs from the current HEAD.
|
|
34
|
+
|
|
35
|
+
## Steps
|
|
36
|
+
|
|
37
|
+
1. **Resolve the repo.** `PROJECT_ROOT` is the current repo root unless the user
|
|
38
|
+
named another. Derive the graph path once and pass it to every call below:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
GRAPH_PATH="$HOME/.claude/knowledge/$(basename "$PROJECT_ROOT")/code-graph.json"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The query, affected and report scripts default that path from the CWD's
|
|
45
|
+
basename, which is not the same thing when the shell sits in a worktree or a
|
|
46
|
+
sub-package, so a defaulted path can point at a graph that was never built.
|
|
47
|
+
|
|
48
|
+
2. **Resolve the stack.** Read `state.detectedStack` when a run is in flight;
|
|
49
|
+
otherwise detect from project markers the way Phase 1 Step 2 does. Only stacks
|
|
50
|
+
with a file in `$HOME/.claude/scripts/code-graph-rules/` can be built. A stack with
|
|
51
|
+
no rule file is reported as unsupported, never guessed at.
|
|
52
|
+
|
|
53
|
+
3. **Run the sub-command.** Every script is read-only on the repo and writes only
|
|
54
|
+
under `~/.claude/knowledge/`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
node $HOME/.claude/scripts/graph-build.mjs --root "$PROJECT_ROOT" --stack "$STACK" --out "$GRAPH_PATH"
|
|
58
|
+
node $HOME/.claude/scripts/graph-query.mjs "<question>" --graph "$GRAPH_PATH" --budget 2000
|
|
59
|
+
node $HOME/.claude/scripts/graph-affected.mjs "<symbol>" --graph "$GRAPH_PATH" --depth 2
|
|
60
|
+
node $HOME/.claude/scripts/graph-report.mjs --graph "$GRAPH_PATH"
|
|
61
|
+
node $HOME/.claude/scripts/graph-report.mjs --graph "$GRAPH_PATH" --status
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
4. **Validate after a build.** A graph that parses but whose edges point at
|
|
65
|
+
missing nodes yields silently truncated traversals, so the build is not
|
|
66
|
+
reported as successful until the validator agrees:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
node $HOME/.claude/scripts/validate-code-graph.mjs "$GRAPH_PATH"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A non-zero exit fails CLOSED: report the validator's `errors[]` verbatim and
|
|
73
|
+
do not record the graph as usable.
|
|
74
|
+
|
|
75
|
+
5. **Report.** One line for a build (`files / nodes / edges / elapsed`), the
|
|
76
|
+
traversal output as-is for `ask` and `affected`, the report path for `report`.
|
|
77
|
+
|
|
78
|
+
## What the output is for
|
|
79
|
+
|
|
80
|
+
`ask` returns ranked nodes plus their neighbourhood within a token budget. Feed
|
|
81
|
+
it to an Explore agent as the starting file set rather than pasting it into a
|
|
82
|
+
final answer: it is a search result, not an explanation.
|
|
83
|
+
|
|
84
|
+
`affected` returns dependents, which is what `analysis.touchedAreas[]` wants.
|
|
85
|
+
|
|
86
|
+
## Limits worth stating when reporting
|
|
87
|
+
|
|
88
|
+
- References resolve only when a name maps to exactly one declaration. A type
|
|
89
|
+
declared in two files is ambiguous and is deliberately dropped, so `affected`
|
|
90
|
+
under-reports for duplicated names rather than fanning out to every candidate.
|
|
91
|
+
- Only type-like symbols are reference targets. Functions appear in the graph
|
|
92
|
+
through their declaring file, not as targets, because a bare lowercase name
|
|
93
|
+
matched across files is almost never a call to that exact declaration.
|
|
94
|
+
- Comments and string literals are stripped before extraction, so a name that
|
|
95
|
+
appears only in prose or in a string produces no edge.
|
|
96
|
+
- A nested declaration is a node but never a reference target. A Kotlin sealed
|
|
97
|
+
case or a Python inner class named `Icon` or `Color` is declared exactly once,
|
|
98
|
+
so the ambiguity rule above does not catch it, and every file that merely
|
|
99
|
+
mentions the framework type of that name would otherwise gain an edge to it.
|
|
100
|
+
- On stacks whose exported unit is a function (Node most of all) the symbol
|
|
101
|
+
layer is thin by design and the import graph between files carries the value.
|
|
102
|
+
Ask `affected "<file>.mjs"` there, not `affected "<functionName>"`.
|
|
103
|
+
- `affected` at `--depth 1` returns direct symbol references only. An import
|
|
104
|
+
whose module name matches a declaring file's basename reaches the symbol
|
|
105
|
+
through that file, so its importers appear at depth 2. Keep the default
|
|
106
|
+
depth of 2 unless direct references are what you actually want.
|
|
@@ -63,7 +63,7 @@ Pipeline (after Phase 0):
|
|
|
63
63
|
Phase 2: Planning -> Task breakdown + architecture review + Plan Approval Gate
|
|
64
64
|
Phase 3: Dev -> TDD: test -> code -> build (Sonnet) + build queue
|
|
65
65
|
Phase 4: Review -> Deterministic gates + parallel AI review + Fable triage
|
|
66
|
-
(Claude Code: Fable + Sonnet · Copilot CLI: GPT-5.4 + Opus + Sonnet)
|
|
66
|
+
(Claude Code: Fable + Opus + Sonnet · Copilot CLI: GPT-5.4 + Opus + Sonnet)
|
|
67
67
|
Phase 5: Test -> Optional: switch to branch, test in Xcode
|
|
68
68
|
Phase 6: Commit -> Commit -> push -> PR + issue body update (never auto-closes)
|
|
69
69
|
Phase 7: Report -> Channels dispatcher (PR · Jira · Confluence · Wiki, multi-select)
|
|
@@ -240,7 +240,7 @@ Pipeline (Phase 0'dan sonra):
|
|
|
240
240
|
Phase 2: Planning -> Task kırılımı + mimari inceleme + Plan Onay Kapısı
|
|
241
241
|
Phase 3: Dev -> TDD: test -> kod -> build (Sonnet) + build queue
|
|
242
242
|
Phase 4: Review -> Deterministik kapılar + paralel AI review + Fable triage
|
|
243
|
-
(Claude Code: Fable + Sonnet · Copilot CLI: GPT-5.4 + Opus + Sonnet)
|
|
243
|
+
(Claude Code: Fable + Opus + Sonnet · Copilot CLI: GPT-5.4 + Opus + Sonnet)
|
|
244
244
|
Phase 5: Test -> Opsiyonel: branch'e geç, Xcode'da test
|
|
245
245
|
Phase 6: Commit -> Commit -> push -> PR + issue body güncelleme (hiç auto-close yok)
|
|
246
246
|
Phase 7: Report -> Channels dispatcher (PR · Jira · Confluence · Wiki, multi-select)
|