@azure-id/orc 1.7.1 → 1.8.1
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 +3649 -3381
- package/README-id.md +923 -844
- package/README.md +836 -788
- package/bin/build-agents.js +43 -27
- package/bin/cli.js +701 -3
- package/bin/graph-extract.js +927 -0
- package/bin/graph-notes.js +188 -0
- package/bin/graph-query.js +808 -0
- package/bin/graph-resolve.js +178 -0
- package/bin/graph-signals.js +277 -0
- package/bin/graph.js +605 -0
- package/bin/verify-contracts.js +4669 -4553
- package/bin/verify-package.js +626 -616
- package/bin/webui/api.js +1419 -1414
- package/bin/webui/fixtures/index.js +579 -576
- package/bin/webui/fixtures/knowledge.js +316 -291
- package/bin/webui/i18n/en/knowledge.json +167 -151
- package/bin/webui/i18n/en/overview.json +101 -100
- package/bin/webui/i18n/id/knowledge.json +167 -151
- package/bin/webui/i18n/id/overview.json +101 -100
- package/bin/webui/js/panels/knowledge.js +1065 -1006
- package/bin/webui/js/panels/overview.js +492 -488
- package/package.json +39 -39
- package/templates/agents/MODEL-MAPPING.md +163 -158
- package/templates/agents/orc-executor-haiku-4-5.md +133 -121
- package/templates/agents/orc-executor-opus-4-7-high.md +134 -122
- package/templates/agents/orc-executor-opus-4-7-med.md +134 -122
- package/templates/agents/orc-executor-opus-4-8-high.md +134 -122
- package/templates/agents/orc-executor-opus-5-high.md +134 -122
- package/templates/agents/orc-executor-opus-5-low.md +134 -122
- package/templates/agents/orc-executor-opus-5-med.md +134 -122
- package/templates/agents/orc-executor-sonnet-4-6-high.md +134 -122
- package/templates/agents/orc-executor-sonnet-4-6-med.md +134 -122
- package/templates/agents/orc-executor-sonnet-5-high.md +134 -122
- package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +86 -0
- package/templates/hooks/README.md +444 -396
- package/templates/hooks/orc-graph-hook.js +336 -0
- package/templates/hooks/orc-statusline-render.js +922 -921
- package/templates/hooks/orc-statusline.js +1596 -1545
- package/templates/skills/_shared/README.md +4 -0
- package/templates/skills/_shared/code-graph.md +220 -0
- package/templates/skills/_shared/opus5-only.md +4 -0
- package/templates/skills/_shared/phases/execution.md +166 -147
- package/templates/skills/_shared/phases/planning.md +142 -135
- package/templates/skills/_shared/phases/preflight.md +132 -118
- package/templates/skills/_shared/phases/review.md +63 -53
- package/templates/skills/_shared/phases/ship.md +96 -88
- package/templates/skills/_shared/phases/trace.md +6 -0
- package/templates/skills/_shared/phases/wiki-consult.md +194 -189
- package/templates/skills/_shared/read-ladder.md +124 -102
- package/templates/skills/_shared/return-validation.md +259 -250
- package/templates/skills/orc/SKILL.md +255 -254
- package/templates/skills/orc-diy/references/flow-schema.md +101 -100
- package/templates/skills/orc-fast/SKILL.md +236 -229
- package/templates/skills/orc-mini/SKILL.md +267 -259
- package/templates/skills/orc-quick/SKILL.md +378 -361
- package/templates/skills/orc-quick/references/dispatch-gate.md +6 -0
- package/templates/skills/orc-wiki/references/staleness.md +294 -288
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ── orc graph — NOTES (v1.8.0 W4) ───────────────────────────────────────────
|
|
3
|
+
//
|
|
4
|
+
// Layer 2: one sentence per function or method, written by
|
|
5
|
+
// `orc-graph-noter-sonnet-4-6-med`, stored by THIS module and nothing else.
|
|
6
|
+
//
|
|
7
|
+
// THE RULES THIS FILE HOLDS
|
|
8
|
+
//
|
|
9
|
+
// 1. A note is keyed by `(symbol, body_hash)`. It is shown as current ONLY
|
|
10
|
+
// while the symbol's body still hashes the same. A note about code that
|
|
11
|
+
// has since changed is labelled stale and never presented as a fact — the
|
|
12
|
+
// RepoMirage finding: agents trust provided context over the code.
|
|
13
|
+
// 2. The CLI is the only writer (S1). The agent pipes JSON to
|
|
14
|
+
// `orc graph notes apply -`; every row is validated against the CURRENT
|
|
15
|
+
// index, and a rejected row is named with its reason. The valid rows
|
|
16
|
+
// still apply.
|
|
17
|
+
// 3. Notes are for touched code only. `pending` needs `--files` — there is no
|
|
18
|
+
// repo-wide "note everything" mode, because that is the cost this layer
|
|
19
|
+
// must never become.
|
|
20
|
+
// 4. A batch below `--min` is not dispatched. The fixed start-up cost of a
|
|
21
|
+
// subagent (~5.7K tokens) makes a one-symbol batch the most expensive note
|
|
22
|
+
// there is. Nothing is lost by waiting: pending is recomputed from hashes.
|
|
23
|
+
// 5. `notes.jsonl` is append-only between compactions. `orc graph gc`
|
|
24
|
+
// compacts it to the latest note per symbol whose hash is still current.
|
|
25
|
+
|
|
26
|
+
const fs = require("fs");
|
|
27
|
+
|
|
28
|
+
const NOTE_MAX = 240;
|
|
29
|
+
// A two-line getter gains nothing from a sentence about it.
|
|
30
|
+
const MIN_LINES = 3;
|
|
31
|
+
// Classes are excluded: a class body hash moves whenever ANY of its methods
|
|
32
|
+
// moves, so a class note would go stale on every edit to the class.
|
|
33
|
+
// A route handler is a function with no name — it is noted like one.
|
|
34
|
+
const NOTE_KINDS = new Set(["function", "method", "route"]);
|
|
35
|
+
|
|
36
|
+
const G = () => require("./graph.js");
|
|
37
|
+
|
|
38
|
+
function clampInt(v, lo, hi, def) {
|
|
39
|
+
const n = Number(v);
|
|
40
|
+
if (!Number.isFinite(n) || v === true || v === undefined || v === null || v === "") return def;
|
|
41
|
+
return Math.max(lo, Math.min(hi, Math.round(n)));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readIndex(claudeDir) {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(fs.readFileSync(G().graphPaths(claudeDir).index, "utf8"));
|
|
47
|
+
} catch (_) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readNotes(file) {
|
|
53
|
+
const rows = [];
|
|
54
|
+
let text;
|
|
55
|
+
try {
|
|
56
|
+
text = fs.readFileSync(file, "utf8");
|
|
57
|
+
} catch (_) {
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
for (const line of text.split("\n")) {
|
|
61
|
+
if (!line.trim()) continue;
|
|
62
|
+
try {
|
|
63
|
+
const r = JSON.parse(line);
|
|
64
|
+
if (r && typeof r.sym === "string" && typeof r.body_hash === "string" && typeof r.note === "string") rows.push(r);
|
|
65
|
+
} catch (_) {
|
|
66
|
+
// A torn last line from a crash mid-append is skipped, never fatal.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return rows;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The latest note per (sym, body_hash), and the latest note per sym whatever
|
|
73
|
+
// its hash (used only to say "stale").
|
|
74
|
+
function noteIndex(claudeDir) {
|
|
75
|
+
const rows = readNotes(G().graphPaths(claudeDir).notes);
|
|
76
|
+
const byKey = new Map();
|
|
77
|
+
const bySym = new Map();
|
|
78
|
+
for (const r of rows) {
|
|
79
|
+
byKey.set(`${r.sym}\0${r.body_hash}`, r);
|
|
80
|
+
bySym.set(r.sym, r);
|
|
81
|
+
}
|
|
82
|
+
return { byKey, bySym, rows: rows.length };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function noteFor(idx, sym) {
|
|
86
|
+
if (!idx || !sym) return null;
|
|
87
|
+
const cur = idx.byKey.get(`${sym.id}\0${sym.body_hash}`);
|
|
88
|
+
if (cur) return { text: cur.note, model: cur.model, at: cur.at, current: true };
|
|
89
|
+
const any = idx.bySym.get(sym.id);
|
|
90
|
+
if (any) return { text: null, model: any.model, at: any.at, current: false };
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const normPath = (f) => String(f).replace(/\\/g, "/").replace(/^\.\//, "");
|
|
95
|
+
|
|
96
|
+
// exit 0 = rows to note · 1 = no index / no --files · 5 = none, or fewer than --min
|
|
97
|
+
function notesPending(claudeDir, opts) {
|
|
98
|
+
const index = readIndex(claudeDir);
|
|
99
|
+
if (!index || !index.by_file) return { ok: false, state: "none", reason: "no-index", exit: 1 };
|
|
100
|
+
const files = [...new Set((opts.files || []).map(normPath).filter(Boolean))];
|
|
101
|
+
if (!files.length) return { ok: false, state: "usage", reason: "missing-files", exit: 1 };
|
|
102
|
+
const cap = clampInt(opts.cap, 1, 200, 40);
|
|
103
|
+
const min = clampInt(opts.min, 1, 200, 5);
|
|
104
|
+
const idx = noteIndex(claudeDir);
|
|
105
|
+
const missing = [];
|
|
106
|
+
const rows = [];
|
|
107
|
+
for (const rel of files) {
|
|
108
|
+
const f = index.by_file[rel];
|
|
109
|
+
if (!f) {
|
|
110
|
+
missing.push(rel);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
for (const s of f.symbols || []) {
|
|
114
|
+
if (!NOTE_KINDS.has(s.kind)) continue;
|
|
115
|
+
if (s.lines[1] - s.lines[0] + 1 < MIN_LINES) continue;
|
|
116
|
+
if (idx.byKey.has(`${s.id}\0${s.body_hash}`)) continue;
|
|
117
|
+
rows.push({ sym: s.id, file: rel, lines: s.lines, body_hash: s.body_hash, restale: idx.bySym.has(s.id) });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const base = { ok: true, files, missing, total: rows.length, cap, min };
|
|
121
|
+
if (!rows.length) return { ...base, state: "none", rows: [], more: 0, exit: 5 };
|
|
122
|
+
if (rows.length < min) return { ...base, state: "below-min", rows: [], waiting: rows.map((r) => r.sym), more: 0, exit: 5 };
|
|
123
|
+
const out = rows.slice(0, cap);
|
|
124
|
+
return { ...base, state: "pending", rows: out, more: rows.length - out.length, exit: 0 };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// exit 0 = every row applied · 1 = no index / locked · 6 = one or more rows rejected
|
|
128
|
+
function notesApply(claudeDir, payload) {
|
|
129
|
+
const index = readIndex(claudeDir);
|
|
130
|
+
if (!index || !index.by_file) return { ok: false, state: "none", reason: "no-index", exit: 1 };
|
|
131
|
+
let data = payload;
|
|
132
|
+
if (typeof payload === "string") {
|
|
133
|
+
try {
|
|
134
|
+
data = JSON.parse(payload);
|
|
135
|
+
} catch (_) {
|
|
136
|
+
return { ok: false, state: "invalid", reason: "not-json", applied: 0, rejected: [], exit: 6 };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const list = Array.isArray(data) ? data : data && Array.isArray(data.notes) ? data.notes : null;
|
|
140
|
+
if (!list) return { ok: false, state: "invalid", reason: "no-notes-array", applied: 0, rejected: [], exit: 6 };
|
|
141
|
+
const model = data && !Array.isArray(data) && typeof data.model === "string" && data.model.trim() ? data.model.trim().slice(0, 60) : "unknown";
|
|
142
|
+
|
|
143
|
+
const syms = new Map();
|
|
144
|
+
for (const f of Object.values(index.by_file)) for (const s of f.symbols || []) syms.set(s.id, s);
|
|
145
|
+
const applied = [];
|
|
146
|
+
const rejected = [];
|
|
147
|
+
const at = G().stamp(new Date());
|
|
148
|
+
list.forEach((n, i) => {
|
|
149
|
+
const reject = (reason) => rejected.push({ index: i, sym: (n && n.sym) || null, reason });
|
|
150
|
+
if (!n || typeof n !== "object") return reject("not-an-object");
|
|
151
|
+
const s = syms.get(n.sym);
|
|
152
|
+
if (!s) return reject("unknown-symbol");
|
|
153
|
+
if (n.body_hash !== s.body_hash) return reject("stale-body-hash");
|
|
154
|
+
if (typeof n.note !== "string" || !n.note.trim()) return reject("empty-note");
|
|
155
|
+
const text = n.note.trim();
|
|
156
|
+
if (/[\r\n]/.test(text)) return reject("multi-line");
|
|
157
|
+
if (text.length > NOTE_MAX) return reject("too-long");
|
|
158
|
+
applied.push({ sym: s.id, body_hash: s.body_hash, note: text, model, at });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (applied.length) {
|
|
162
|
+
const g = G();
|
|
163
|
+
const p = g.graphPaths(claudeDir);
|
|
164
|
+
const lock = g.acquireLock(p);
|
|
165
|
+
if (!lock.ok) return { ok: false, state: "unavailable", reason: "locked", holder: lock.holder, applied: 0, rejected, exit: 1 };
|
|
166
|
+
try {
|
|
167
|
+
fs.appendFileSync(p.notes, applied.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
168
|
+
} finally {
|
|
169
|
+
g.releaseLock(p);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { ok: true, state: rejected.length ? "partial" : "applied", applied: applied.length, rejected, model, exit: rejected.length ? 6 : 0 };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Called by `graphGc` while it already holds the lock.
|
|
176
|
+
function compactNotes(p, index, atomicWrite) {
|
|
177
|
+
const rows = readNotes(p.notes);
|
|
178
|
+
if (!rows.length) return { kept: 0, removed: 0 };
|
|
179
|
+
const current = new Map();
|
|
180
|
+
for (const f of Object.values((index && index.by_file) || {})) for (const s of f.symbols || []) current.set(s.id, s.body_hash);
|
|
181
|
+
const latest = new Map();
|
|
182
|
+
for (const r of rows) if (current.get(r.sym) === r.body_hash) latest.set(r.sym, r);
|
|
183
|
+
const keep = [...latest.values()];
|
|
184
|
+
if (keep.length !== rows.length) atomicWrite(p.notes, keep.map((r) => JSON.stringify(r)).join("\n") + (keep.length ? "\n" : ""));
|
|
185
|
+
return { kept: keep.length, removed: rows.length - keep.length };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = { NOTE_MAX, notesPending, notesApply, noteIndex, noteFor, compactNotes };
|