@azure-id/orc 1.8.1 → 1.9.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 +386 -0
- package/README-id.md +110 -73
- package/README.md +96 -33
- package/bin/cli.js +45520 -44867
- package/bin/graph-extract.js +2409 -120
- package/bin/graph-gain.js +404 -0
- package/bin/graph-map.js +232 -0
- package/bin/graph-notes.js +49 -8
- package/bin/graph-query.js +1770 -808
- package/bin/graph-resolve.js +93 -16
- package/bin/graph-shard.js +325 -0
- package/bin/graph.js +658 -605
- package/bin/verify-contracts.js +297 -56
- package/bin/verify-package.js +29 -1
- package/bin/webui/api.js +6 -0
- package/bin/webui/fixtures/index.js +6 -1
- package/bin/webui/fixtures/knowledge.js +41 -1
- package/bin/webui/fixtures/stats.js +107 -104
- package/bin/webui/i18n/en/knowledge.json +16 -1
- package/bin/webui/i18n/id/knowledge.json +16 -1
- package/bin/webui/js/panels/knowledge.js +68 -3
- package/mock-run/orc-quick.md +141 -113
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +15 -5
- package/templates/agents/orc-executor-haiku-4-5.md +25 -13
- package/templates/agents/orc-executor-opus-4-7-high.md +25 -13
- package/templates/agents/orc-executor-opus-4-7-med.md +25 -13
- package/templates/agents/orc-executor-opus-4-8-high.md +25 -13
- package/templates/agents/orc-executor-opus-5-high.md +25 -13
- package/templates/agents/orc-executor-opus-5-low.md +25 -13
- package/templates/agents/orc-executor-opus-5-med.md +25 -13
- package/templates/agents/orc-executor-sonnet-4-6-high.md +25 -13
- package/templates/agents/orc-executor-sonnet-4-6-med.md +25 -13
- package/templates/agents/orc-executor-sonnet-5-high.md +25 -13
- package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +15 -12
- package/templates/agents/orc-planner-mini-opus-5-med.md +75 -69
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +73 -67
- package/templates/agents/orc-recon-opus-5-low.md +99 -0
- package/templates/agents/orc-recon-sonnet-4-6-med.md +99 -0
- package/templates/commands/orc-mini.md +10 -12
- package/templates/commands/orc-quick.md +20 -33
- package/templates/hooks/README.md +13 -3
- package/templates/hooks/orc-graph-hook.js +148 -13
- package/templates/hooks/orc-trace.js +476 -471
- package/templates/skills/_shared/code-graph.md +148 -20
- package/templates/skills/_shared/phases/execution.md +13 -11
- package/templates/skills/_shared/phases/planning.md +8 -1
- package/templates/skills/_shared/phases/rules.md +172 -159
- package/templates/skills/_shared/phases/ship.md +5 -1
- package/templates/skills/_shared/phases/trace.md +4 -1
- package/templates/skills/_shared/phases/wiki-consult.md +10 -6
- package/templates/skills/_shared/read-ladder.md +10 -2
- package/templates/skills/_shared/return-validation.md +22 -0
- package/templates/skills/context-combiner/SKILL.md +13 -13
- package/templates/skills/orc/SKILL.md +1 -1
- package/templates/skills/orc/subskills/orc-execution/core.md +171 -159
- package/templates/skills/orc-analyze/SKILL.md +13 -13
- package/templates/skills/orc-diy/references/flow-schema.md +1 -1
- package/templates/skills/orc-mini/SKILL.md +148 -136
- package/templates/skills/orc-mini/examples/mini-run-mock.md +64 -50
- package/templates/skills/orc-mini/references/complexity.md +105 -0
- package/templates/skills/orc-quick/README.md +495 -423
- package/templates/skills/orc-quick/SKILL.md +157 -211
- package/templates/skills/orc-quick/references/context-doc.md +145 -114
- package/templates/skills/orc-quick/references/defect.md +101 -0
- package/templates/skills/orc-quick/references/dispatch-gate.md +55 -24
- package/templates/skills/orc-quick/references/gh-mode.md +148 -127
- package/templates/skills/orc-quick/references/look.md +107 -0
- package/templates/skills/orc-wiki/references/staleness.md +1 -1
|
@@ -15,10 +15,21 @@
|
|
|
15
15
|
* that forgot its own update step cannot leave the map behind.
|
|
16
16
|
* SubagentStart (E4a) one line telling a fresh subagent the graph is there.
|
|
17
17
|
* PreToolUse (E4b) a Grep or Glob whose pattern contains a symbol name →
|
|
18
|
-
* up to five rows saying where that name is defined.
|
|
18
|
+
* up to five rows saying where that name is defined. v1.8.2 W3
|
|
19
|
+
* (D3) adds a Bash SEARCH (grep, rg, git grep, findstr,
|
|
20
|
+
* Select-String, ag, ack) to the same hint: a shell search is
|
|
21
|
+
* the same question asked through a different tool.
|
|
22
|
+
* PreToolUse (D3, v1.8.2 W3) a whole-file Read of a WIDE file → one line
|
|
23
|
+
* naming its six most important symbols and their ranges, so
|
|
24
|
+
* the NEXT read can be a range read. The read still runs. OFF
|
|
25
|
+
* by default: it is armed with `code_graph_hooks: on,read`.
|
|
19
26
|
* PostToolUse (E4c) a Read of a file the extractor did NOT fully see →
|
|
20
27
|
* one line naming the lines it missed.
|
|
21
28
|
*
|
|
29
|
+
* NO DOUBLE PAYMENT. Every hint dedupes against the run's own counters file,
|
|
30
|
+
* and `orc graph ctx --for-slice` (D2) writes the names it delivered into that
|
|
31
|
+
* same file — so a name the slice already carried is never injected again.
|
|
32
|
+
*
|
|
22
33
|
* EVERY ONE OF THESE IS SILENT UNLESS ALL OF THIS IS TRUE:
|
|
23
34
|
* · an ORC run is open (the trace pointer exists and is fresh)
|
|
24
35
|
* · `code_graph` is on AND `code_graph_hooks` is on
|
|
@@ -44,6 +55,7 @@
|
|
|
44
55
|
* hooks.SubagentStart[] { hooks:[{command:"node <..>/orc-graph-hook.js"}] }
|
|
45
56
|
* hooks.SubagentStop[] { hooks:[{command:"node <..>/orc-graph-hook.js"}] }
|
|
46
57
|
* hooks.PreToolUse[] { matcher:"Grep|Glob", hooks:[{…}] }
|
|
58
|
+
* hooks.PreToolUse[] { matcher:"Bash|Read", hooks:[{…}] }
|
|
47
59
|
* hooks.PostToolUse[] { matcher:"Read", hooks:[{…}] }
|
|
48
60
|
*
|
|
49
61
|
* Wired even though it does nothing until `code_graph` is on — arming the
|
|
@@ -70,6 +82,18 @@ const MAX_TEXT = 1200;
|
|
|
70
82
|
const EXECUTOR = /^orc-executor-/i;
|
|
71
83
|
const DELIVER_TO = /^orc-(executor|planner|reviewer|verifier|system-analyst|analyze)/i;
|
|
72
84
|
|
|
85
|
+
// D3 — a SHELL search is the same question as a Grep. Only these seven, only at
|
|
86
|
+
// the head of the command, and only the first QUOTED argument is read: an
|
|
87
|
+
// unquoted pattern cannot be told apart from a flag or a path, and guessing
|
|
88
|
+
// there would hint about the wrong word.
|
|
89
|
+
const BASH_SEARCH = /^\s*(?:grep|rg|ag|ack|findstr|git\s+grep|select-string|sls)\b/i;
|
|
90
|
+
const FIRST_QUOTED = /(?:'([^']*)'|"([^"]*)")/;
|
|
91
|
+
const WIDE_ROWS = 6;
|
|
92
|
+
// One 80-line range read, at the 40-characters-a-line the ladder assumes. The
|
|
93
|
+
// CLI prices a range from the file's own average; the hook must not open a file
|
|
94
|
+
// to price a hint, so it uses the ladder's own figure.
|
|
95
|
+
const HINT_RANGE_TOKENS = Math.ceil((80 * 40) / 4);
|
|
96
|
+
|
|
73
97
|
// ── config, the same tolerant scalar read every ORC hook uses ───────────────
|
|
74
98
|
function cfg(key) {
|
|
75
99
|
try {
|
|
@@ -121,11 +145,17 @@ function readJson(file) {
|
|
|
121
145
|
// beside the trace and the lane copies them in at each phase close as ONE
|
|
122
146
|
// GRAPH-HINT line — never one line per hint, which is the noise rule the read
|
|
123
147
|
// gate already pays for.
|
|
148
|
+
// The pointer names the trace file WITH its extension, so drop it: the
|
|
149
|
+
// counters sit beside the trace, not inside its name. `orc graph ctx
|
|
150
|
+
// --for-slice` writes to this same file (D2).
|
|
151
|
+
function seenFile(run) {
|
|
152
|
+
return path.join(logDir(), run.replace(/\.txt$/, "") + ".graph-hook.json");
|
|
153
|
+
}
|
|
154
|
+
|
|
124
155
|
function bump(run, field, token) {
|
|
125
|
-
|
|
126
|
-
// counters sit beside the trace, not inside its name.
|
|
127
|
-
const file = path.join(logDir(), run.replace(/\.txt$/, "") + ".graph-hook.json");
|
|
156
|
+
const file = seenFile(run);
|
|
128
157
|
const seen = readJson(file) || { injected: 0, subagent_start: 0, read_notes: 0, updates: 0, tokens: [] };
|
|
158
|
+
if (!Array.isArray(seen.tokens)) seen.tokens = [];
|
|
129
159
|
if (token) {
|
|
130
160
|
if (seen.tokens.includes(token)) return false; // already said, to this run
|
|
131
161
|
seen.tokens.push(token);
|
|
@@ -176,6 +206,43 @@ function emit(event, text) {
|
|
|
176
206
|
try {
|
|
177
207
|
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: event, additionalContext: `${PREFIX} ${body}` } }));
|
|
178
208
|
} catch (_) {}
|
|
209
|
+
return body;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// K1 (v1.8.2 W4b) — the gain ledger. It is appended AFTER the hint is already
|
|
213
|
+
// out, inside the fail-quiet wrapper, so a full disk can slow a Grep and can
|
|
214
|
+
// never block one. A coverage note records PAID ONLY: it adds, it saves
|
|
215
|
+
// nothing (K6).
|
|
216
|
+
function ledger(runName, cmd, body, avoided) {
|
|
217
|
+
try {
|
|
218
|
+
fs.appendFileSync(
|
|
219
|
+
path.join(GRAPH_DIR, "gain.jsonl"),
|
|
220
|
+
JSON.stringify({
|
|
221
|
+
at: stampNow(),
|
|
222
|
+
run: runName,
|
|
223
|
+
agent: null,
|
|
224
|
+
cmd,
|
|
225
|
+
targets: [],
|
|
226
|
+
gen: null,
|
|
227
|
+
ms: 0,
|
|
228
|
+
paid: { card: 0, source: 0, hints: Math.ceil((PREFIX.length + 1 + String(body || "").length) / 4) },
|
|
229
|
+
avoided: avoided || { low: 0, high: 0, calls_low: 0, calls_high: 0 },
|
|
230
|
+
basis: { files: [], grep_hits: 0 },
|
|
231
|
+
}) + "\n"
|
|
232
|
+
);
|
|
233
|
+
} catch (_) {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// The LEDGER stamp, which is ORC's own `DD-MM-YYYY HH:MM:SS` — the same one
|
|
237
|
+
// the CLI writes. (The TRACE stamp above is a different format on purpose: it
|
|
238
|
+
// is the trace's, and it is not this.)
|
|
239
|
+
function stampNow() {
|
|
240
|
+
const d = new Date();
|
|
241
|
+
const q = (n) => String(n).padStart(2, "0");
|
|
242
|
+
return (
|
|
243
|
+
q(d.getDate()) + "-" + q(d.getMonth() + 1) + "-" + d.getFullYear() + " " +
|
|
244
|
+
q(d.getHours()) + ":" + q(d.getMinutes()) + ":" + q(d.getSeconds())
|
|
245
|
+
);
|
|
179
246
|
}
|
|
180
247
|
|
|
181
248
|
// ── main ───────────────────────────────────────────────────────────────────
|
|
@@ -201,7 +268,13 @@ function run() {
|
|
|
201
268
|
if (!event) return;
|
|
202
269
|
|
|
203
270
|
if (String(cfg("code_graph") || "off").toLowerCase() !== "on") return;
|
|
204
|
-
|
|
271
|
+
// `code_graph_hooks` is `off`, `on`, or `on,read` (D3/DE-J). `on` is every
|
|
272
|
+
// hint that was on in 1.8.1 PLUS the Bash search (DE-C: a shell grep is a
|
|
273
|
+
// Grep). The whole-file READ hint is the one that changes how an agent reads,
|
|
274
|
+
// so it is armed on purpose and never by an upgrade.
|
|
275
|
+
const mode = String(cfg("code_graph_hooks") || "on").toLowerCase().split(",").map((s) => s.trim()).filter(Boolean);
|
|
276
|
+
if (!mode.length || mode[0] !== "on") return;
|
|
277
|
+
const readHint = mode.includes("read");
|
|
205
278
|
const runName = currentRun();
|
|
206
279
|
if (!runName) return;
|
|
207
280
|
|
|
@@ -217,7 +290,11 @@ function run() {
|
|
|
217
290
|
if (data.agent_id == null) return;
|
|
218
291
|
|
|
219
292
|
if (event === "SubagentStart") return onSubagentStart(data, runName, meta);
|
|
220
|
-
if (event === "PreToolUse")
|
|
293
|
+
if (event === "PreToolUse") {
|
|
294
|
+
const tool = String(data.tool_name || "");
|
|
295
|
+
if (tool === "Read") return readHint ? onWideRead(data, runName, meta) : undefined;
|
|
296
|
+
return onSearch(data, runName, meta);
|
|
297
|
+
}
|
|
221
298
|
if (event === "PostToolUse") return onRead(data, runName, meta);
|
|
222
299
|
}
|
|
223
300
|
|
|
@@ -262,11 +339,18 @@ function onSubagentStart(data, runName, meta) {
|
|
|
262
339
|
const agent = String(data.agent_type || data.agentType || "");
|
|
263
340
|
if (agent && !DELIVER_TO.test(agent)) return;
|
|
264
341
|
if (!bump(runName, "subagent_start", "start:" + (data.agent_id || agent))) return;
|
|
342
|
+
// D3 — the "run ctx before a Grep" sentence is dropped when this run already
|
|
343
|
+
// delivered a `--for-slice` block: the agent has the outside view in its
|
|
344
|
+
// slice, and saying it again is the double payment this wave removes.
|
|
345
|
+
const counters = readJson(seenFile(runName)) || {};
|
|
346
|
+
const advice = counters.for_slice
|
|
347
|
+
? ""
|
|
348
|
+
: ` Before a wide Grep for a symbol, run: orc graph ctx <name|file> --json .`;
|
|
265
349
|
emit(
|
|
266
350
|
"SubagentStart",
|
|
267
|
-
`a code graph of this repository is indexed (generation ${meta.generation}, ${meta.files} files)
|
|
268
|
-
|
|
269
|
-
`A card is a LOCATOR — read the range it names before you rely on behaviour.`
|
|
351
|
+
`a code graph of this repository is indexed (generation ${meta.generation}, ${meta.files} files).` +
|
|
352
|
+
advice +
|
|
353
|
+
` A card is a LOCATOR — read the range it names before you rely on behaviour.`
|
|
270
354
|
);
|
|
271
355
|
}
|
|
272
356
|
|
|
@@ -275,9 +359,18 @@ function onSubagentStart(data, runName, meta) {
|
|
|
275
359
|
// is what keeps the hook off the critical path of every Grep.
|
|
276
360
|
function onSearch(data, runName, meta) {
|
|
277
361
|
const tool = String(data.tool_name || "");
|
|
278
|
-
if (tool !== "Grep" && tool !== "Glob") return;
|
|
279
362
|
const input = data.tool_input || {};
|
|
280
|
-
|
|
363
|
+
let pattern = "";
|
|
364
|
+
if (tool === "Grep" || tool === "Glob") pattern = String(input.pattern || "");
|
|
365
|
+
else if (tool === "Bash") {
|
|
366
|
+
// DE-C — a shell search is a Grep. The command must START with one of the
|
|
367
|
+
// seven search programs, and the word comes from its first QUOTED argument.
|
|
368
|
+
const cmd = String(input.command || "");
|
|
369
|
+
if (!BASH_SEARCH.test(cmd)) return;
|
|
370
|
+
const q = FIRST_QUOTED.exec(cmd);
|
|
371
|
+
if (!q) return;
|
|
372
|
+
pattern = q[1] !== undefined ? q[1] : q[2];
|
|
373
|
+
} else return;
|
|
281
374
|
if (!pattern) return;
|
|
282
375
|
|
|
283
376
|
// The longest plain identifier in the pattern. Regex metacharacters are
|
|
@@ -300,11 +393,50 @@ function onSearch(data, runName, meta) {
|
|
|
300
393
|
if (!bump(runName, "injected", "name:" + hit.word)) return;
|
|
301
394
|
|
|
302
395
|
const rows = hit.rows.slice(0, MAX_ROWS).map((r) => `${clean(r[0], 80)} (${clean(r[1], 20)}) ${clean(r[2], 160)}:${Number(r[3]) || 0}-${Number(r[4]) || 0} fan-in ${Number(r[5]) || 0}`);
|
|
303
|
-
emit(
|
|
396
|
+
const body = emit(
|
|
304
397
|
"PreToolUse",
|
|
305
398
|
`"${clean(hit.word, 80)}" is defined at ${rows.join(" · ")}. ` +
|
|
306
399
|
`Read those ranges, or run orc graph ctx ${clean(hit.word, 80)} --json for callers and callees. The search below still runs.`
|
|
307
400
|
);
|
|
401
|
+
// The search STILL RUNS, so a careful ladder saved nothing here; a careless
|
|
402
|
+
// one saved the range read the anchor pointed at. Low 0, high one range.
|
|
403
|
+
ledger(runName, "hint", body, { low: 0, high: HINT_RANGE_TOKENS, calls_low: 0, calls_high: 1 });
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ── D3 (v1.8.2 W3) — a whole-file Read of a WIDE file ──────────────────────
|
|
407
|
+
// OFF by default (`code_graph_hooks: on,read` arms it). The read STILL RUNS —
|
|
408
|
+
// this hook never blocks and never rewrites an input. `updatedInput` exists and
|
|
409
|
+
// this file deliberately never uses it: turning an agent's Read into a range
|
|
410
|
+
// read is the corruption the read ladder's first exception forbids, and the
|
|
411
|
+
// read gate stays the only layer allowed to touch a read.
|
|
412
|
+
//
|
|
413
|
+
// The point is the NEXT read. One line naming the file's six most important
|
|
414
|
+
// symbols and their ranges is what an agent needs to ask for a range instead of
|
|
415
|
+
// 2,000 lines the turn after.
|
|
416
|
+
function onWideRead(data, runName, meta) {
|
|
417
|
+
const input = data.tool_input || {};
|
|
418
|
+
if (input.offset !== undefined || input.limit !== undefined) return; // already a range read
|
|
419
|
+
const abs = String(input.file_path || "");
|
|
420
|
+
if (!abs) return;
|
|
421
|
+
let rel;
|
|
422
|
+
try {
|
|
423
|
+
rel = path.relative(PROJECT_ROOT, abs).split(path.sep).join("/");
|
|
424
|
+
} catch (_) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!rel || rel.startsWith("..")) return;
|
|
428
|
+
const wide = readJson(path.join(GRAPH_DIR, "wide.json"));
|
|
429
|
+
if (!wide || !wide.files || wide.generation !== meta.generation) return;
|
|
430
|
+
const rows = Object.prototype.hasOwnProperty.call(wide.files, rel) ? wide.files[rel] : null;
|
|
431
|
+
if (!Array.isArray(rows) || !rows.length) return;
|
|
432
|
+
if (!bump(runName, "injected", "wide:" + rel)) return;
|
|
433
|
+
const body = emit(
|
|
434
|
+
"PreToolUse",
|
|
435
|
+
`${clean(rel, 160)} holds many symbols; the most reached are ` +
|
|
436
|
+
rows.slice(0, WIDE_ROWS).map((r) => `${clean(r[0], 80)} ${Number(r[1]) || 0}-${Number(r[2]) || 0}`).join(" · ") +
|
|
437
|
+
`. The read below still runs; a later read of this file can name a range.`
|
|
438
|
+
);
|
|
439
|
+
ledger(runName, "hint", body, { low: 0, high: HINT_RANGE_TOKENS, calls_low: 0, calls_high: 1 });
|
|
308
440
|
}
|
|
309
441
|
|
|
310
442
|
// ── E4c — a Read of a file the extractor did not fully see ─────────────────
|
|
@@ -332,5 +464,8 @@ function onRead(data, runName, meta) {
|
|
|
332
464
|
}
|
|
333
465
|
if (!note) return;
|
|
334
466
|
if (!bump(runName, "read_notes", "read:" + rel)) return;
|
|
335
|
-
emit("PostToolUse", `${note}. The source you just read is ground truth; no recorded gap is not proof of completeness.`);
|
|
467
|
+
const body = emit("PostToolUse", `${note}. The source you just read is ground truth; no recorded gap is not proof of completeness.`);
|
|
468
|
+
// PAID ONLY. A coverage note tells you what the card cannot show; it replaces
|
|
469
|
+
// no read and it is never counted as a saving (K6).
|
|
470
|
+
ledger(runName, "read-note", body, { low: 0, high: 0, calls_low: 0, calls_high: 0 });
|
|
336
471
|
}
|