@azure-id/orc 1.9.1 → 2.0.2
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 +390 -0
- package/README-id.md +20 -36
- package/README.md +24 -34
- package/bin/build-agents.js +206 -109
- package/bin/cli.js +760 -62
- package/bin/gotcha-import.js +1081 -0
- package/bin/gotcha.js +1286 -0
- package/bin/graph-query.js +1 -1
- package/bin/graph.js +717 -717
- package/bin/habit.js +1453 -0
- package/bin/mockrun-catalog.js +281 -276
- package/bin/onboarding-content.js +4 -4
- package/bin/pricing.json +207 -200
- package/bin/run-undo.js +398 -0
- package/bin/trace-write.js +657 -0
- package/bin/verify-contracts.js +527 -81
- package/bin/verify-package.js +58 -11
- package/bin/webui/api.js +26 -0
- package/bin/webui/app.html +239 -232
- package/bin/webui/css/00-tokens.css +110 -92
- package/bin/webui/css/04-motion.css +249 -152
- package/bin/webui/css/06-responsive.css +203 -178
- package/bin/webui/css/panels/behaviour.css +205 -0
- package/bin/webui/css/panels/knowledge.css +563 -116
- package/bin/webui/fixtures/behaviour.js +532 -0
- package/bin/webui/fixtures/extra.js +2036 -2036
- package/bin/webui/fixtures/hookui.js +5 -5
- package/bin/webui/fixtures/index.js +34 -0
- package/bin/webui/fixtures/knowledge.js +7 -1
- package/bin/webui/fixtures/settings.js +305 -305
- package/bin/webui/fixtures/stats.js +16 -0
- package/bin/webui/i18n/en/behaviour.json +143 -0
- package/bin/webui/i18n/en/knowledge.json +117 -2
- package/bin/webui/i18n/en/nav.json +25 -24
- package/bin/webui/i18n/en/tour.json +37 -35
- package/bin/webui/i18n/id/behaviour.json +143 -0
- package/bin/webui/i18n/id/knowledge.json +117 -2
- package/bin/webui/i18n/id/nav.json +25 -24
- package/bin/webui/i18n/id/tour.json +37 -35
- package/bin/webui/js/01-i18n.js +155 -154
- package/bin/webui/js/90-tour.js +498 -494
- package/bin/webui/js/91-shortcuts.js +126 -126
- package/bin/webui/js/99-boot.js +121 -118
- package/bin/webui/js/panels/behaviour.js +1022 -0
- package/bin/webui/js/panels/knowledge.js +552 -7
- package/bin/webui/js/panels/overview.js +13 -4
- package/mock-run/INDEX.md +109 -107
- package/mock-run/context-combiner.md +100 -100
- package/mock-run/gotcha-import.md +118 -0
- package/mock-run/habits.md +129 -0
- package/mock-run/orc-budget.md +534 -534
- package/mock-run/orc-challenge-council.md +262 -262
- package/mock-run/orc-quick.md +6 -1
- package/mock-run/orc-ultra.md +103 -103
- package/package.json +1 -1
- package/templates/agents/MODEL-MAPPING.md +49 -49
- package/templates/agents/orc-advisor-opus-5-xhigh.md +50 -56
- package/templates/agents/orc-analyze-mini-opus-5-med.md +55 -60
- package/templates/agents/orc-analyze-mini-sonnet-5-high.md +55 -58
- package/templates/agents/orc-challenge-advisor-opus-5-med.md +75 -75
- package/templates/agents/orc-challenge-contrarian-opus-5-high.md +110 -110
- package/templates/agents/orc-challenge-executor-opus-5-med.md +114 -114
- package/templates/agents/orc-challenge-expansionist-opus-5-med.md +112 -112
- package/templates/agents/orc-challenge-judge-opus-5-high.md +132 -132
- package/templates/agents/orc-challenge-outsider-opus-5-low.md +109 -109
- package/templates/agents/orc-challenge-principles-opus-5-high.md +109 -109
- package/templates/agents/orc-challenge-reader-opus-5-low.md +90 -90
- package/templates/agents/orc-claude-writer-opus-4-8-high.md +48 -53
- package/templates/agents/orc-claude-writer-opus-5-med.md +48 -55
- package/templates/agents/orc-context-combiner-opus-5-high.md +78 -88
- package/templates/agents/orc-doc-checker-opus-5-low.md +108 -108
- package/templates/agents/orc-doc-writer-opus-5-med.md +134 -134
- package/templates/agents/orc-executor-haiku-4-5.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-high.md +14 -6
- package/templates/agents/orc-executor-opus-4-7-med.md +14 -6
- package/templates/agents/orc-executor-opus-4-8-high.md +14 -6
- package/templates/agents/orc-executor-opus-5-high.md +15 -7
- package/templates/agents/orc-executor-opus-5-low.md +15 -7
- package/templates/agents/orc-executor-opus-5-med.md +15 -7
- package/templates/agents/orc-executor-sonnet-4-6-high.md +14 -6
- package/templates/agents/orc-executor-sonnet-4-6-med.md +14 -6
- package/templates/agents/orc-executor-sonnet-5-high.md +14 -6
- package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +1 -10
- package/templates/agents/orc-judge-opus-5-xhigh.md +81 -85
- package/templates/agents/orc-learn-writer-opus-5-low.md +67 -73
- package/templates/agents/orc-pattern-codifier-opus-5-med.md +58 -65
- package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +58 -63
- package/templates/agents/orc-planner-mini-opus-5-med.md +4 -7
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +2 -3
- package/templates/agents/orc-planner-opus-5-med.md +157 -160
- package/templates/agents/orc-recon-opus-5-low.md +3 -10
- package/templates/agents/orc-recon-sonnet-4-6-med.md +1 -8
- package/templates/agents/orc-retro-opus-5-med.md +8 -10
- package/templates/agents/orc-retro-sonnet-5-high.md +6 -7
- package/templates/agents/orc-reviewer-opus-5-med.md +96 -60
- package/templates/agents/orc-scout-opus-5-low.md +35 -40
- package/templates/agents/orc-scout-sonnet-4-6-high.md +35 -39
- package/templates/agents/orc-system-analyst-opus-5-high.md +115 -120
- package/templates/agents/orc-test-author-opus-5-med.md +70 -71
- package/templates/agents/orc-test-designer-opus-5-high.md +158 -158
- package/templates/agents/orc-test-interpreter-opus-5-low.md +130 -130
- package/templates/agents/orc-trace-writer-haiku-4-5.md +3 -7
- package/templates/agents/orc-verifier-opus-5-med.md +77 -69
- package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +74 -79
- package/templates/agents/orc-wiki-scanner-opus-5-med.md +74 -81
- package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +97 -106
- package/templates/commands/orc-analyze.md +13 -21
- package/templates/commands/orc-fast.md +10 -15
- package/templates/commands/orc-poly.md +12 -21
- package/templates/commands/orc-pr-driver.md +11 -30
- package/templates/commands/orc-pr-setup.md +10 -31
- package/templates/commands/orc-route.md +11 -41
- package/templates/commands/orc-test.md +5 -60
- package/templates/commands/orc-ultra.md +17 -17
- package/templates/commands/orc-verify.md +11 -11
- package/templates/hooks/README.md +36 -2
- package/templates/hooks/orc-effort-guard.js +178 -178
- package/templates/hooks/orc-session-hook.js +264 -0
- package/templates/hooks/orc-statusline.js +9 -7
- package/templates/skills/_shared/README.md +9 -0
- package/templates/skills/_shared/code-graph.md +48 -56
- package/templates/skills/_shared/config-precedence.md +200 -198
- package/templates/skills/_shared/extra-dispatch.md +1331 -1346
- package/templates/skills/_shared/gotchas.md +228 -177
- package/templates/skills/_shared/habits.md +101 -0
- package/templates/skills/_shared/lane-contract.md +84 -0
- package/templates/skills/_shared/opus5-only.md +8 -8
- package/templates/skills/_shared/phases/README.md +142 -83
- package/templates/skills/_shared/phases/analyst-gates.md +125 -136
- package/templates/skills/_shared/phases/execution.md +8 -14
- package/templates/skills/_shared/phases/house-rules.md +27 -32
- package/templates/skills/_shared/phases/intake.md +127 -133
- package/templates/skills/_shared/phases/mock-example.md +46 -56
- package/templates/skills/_shared/phases/plan-handoff.md +91 -97
- package/templates/skills/_shared/phases/planning.md +7 -17
- package/templates/skills/_shared/phases/preflight.md +19 -42
- package/templates/skills/_shared/phases/review.md +23 -27
- package/templates/skills/_shared/phases/rules.md +18 -42
- package/templates/skills/_shared/phases/scoring.md +55 -65
- package/templates/skills/_shared/phases/security-checklist.md +46 -50
- package/templates/skills/_shared/phases/security.md +45 -55
- package/templates/skills/_shared/phases/ship.md +6 -15
- package/templates/skills/_shared/phases/stop-resume.md +2 -5
- package/templates/skills/_shared/phases/summary.md +73 -48
- package/templates/skills/_shared/phases/testgen.md +41 -51
- package/templates/skills/_shared/phases/trace-verbs.md +433 -0
- package/templates/skills/_shared/phases/trace.md +136 -367
- package/templates/skills/_shared/phases/verify.md +62 -70
- package/templates/skills/_shared/phases/wave-grouping.md +128 -133
- package/templates/skills/_shared/phases/wiki-consult.md +10 -6
- package/templates/skills/_shared/read-ladder.md +2 -55
- package/templates/skills/_shared/return-validation.md +17 -70
- package/templates/skills/_shared/review-slice.md +79 -0
- package/templates/skills/_shared/smoke-gate.md +46 -28
- package/templates/skills/context-combiner/SKILL.md +16 -45
- package/templates/skills/context-combiner/schemas/combined-report.md +78 -78
- package/templates/skills/orc/README.md +2 -2
- package/templates/skills/orc/SKILL.md +41 -69
- package/templates/skills/orc/config.md +9 -9
- package/templates/skills/orc/examples/full-run-mock.md +73 -73
- package/templates/skills/orc/references/effort-and-mode.md +222 -222
- package/templates/skills/orc/references/pattern-gate.md +89 -89
- package/templates/skills/orc/references/phases/intake.md +41 -47
- package/templates/skills/orc/references/phases/integration.md +13 -19
- package/templates/skills/orc/references/preflight-report.md +10 -11
- package/templates/skills/orc/references/ultra-mode.md +11 -9
- package/templates/skills/orc/subskills/orc-execution/SKILL.md +27 -73
- package/templates/skills/orc/subskills/orc-execution/core.md +12 -99
- package/templates/skills/orc/subskills/orc-execution/subagent.md +14 -13
- package/templates/skills/orc/subskills/orc-planner/SKILL.md +2 -2
- package/templates/skills/orc/subskills/orc-planner-mini/SKILL.md +121 -121
- package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +35 -76
- package/templates/skills/orc/subskills/orc-review-verify/core.md +52 -135
- package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -7
- package/templates/skills/orc/subskills/orc-testgen/SKILL.md +34 -45
- package/templates/skills/orc/subskills/orc-testgen/core.md +20 -59
- package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -7
- package/templates/skills/orc-advisor/SKILL.md +56 -60
- package/templates/skills/orc-analyze/SKILL.md +32 -68
- package/templates/skills/orc-analyze/examples/analyze-mock.md +42 -42
- package/templates/skills/orc-analyze/schemas/report-audit.md +84 -83
- package/templates/skills/orc-analyze/schemas/report-prose.md +64 -63
- package/templates/skills/orc-analyze/schemas/report-requirement.md +78 -78
- package/templates/skills/orc-analyze-mini/SKILL.md +35 -70
- package/templates/skills/orc-challenge/README.md +142 -142
- package/templates/skills/orc-challenge/examples/council-full-roster.md +273 -273
- package/templates/skills/orc-challenge/references/council.md +315 -315
- package/templates/skills/orc-challenge/references/intake.md +171 -171
- package/templates/skills/orc-claude/SKILL.md +14 -14
- package/templates/skills/orc-diy/README.md +174 -143
- package/templates/skills/orc-diy/SKILL.md +23 -74
- package/templates/skills/orc-diy/references/blocks/pattern.md +18 -18
- package/templates/skills/orc-diy/references/flow-schema.md +1 -1
- package/templates/skills/orc-doc/README.md +229 -229
- package/templates/skills/orc-doc/examples/orc-doc-prd-run.md +325 -325
- package/templates/skills/orc-doc/references/chunking.md +527 -527
- package/templates/skills/orc-fast/SKILL.md +45 -73
- package/templates/skills/orc-judge/SKILL.md +77 -82
- package/templates/skills/orc-learn/SKILL.md +14 -14
- package/templates/skills/orc-learn/examples/learn-run-mock.md +61 -61
- package/templates/skills/orc-mini/SKILL.md +65 -113
- package/templates/skills/orc-pattern/SKILL.md +27 -48
- package/templates/skills/orc-poly/SKILL.md +32 -61
- package/templates/skills/orc-pr-driver/SKILL.md +25 -51
- package/templates/skills/orc-pr-driver/references/green-gate.md +113 -105
- package/templates/skills/orc-pr-setup/SKILL.md +20 -45
- package/templates/skills/orc-quick/README.md +43 -2
- package/templates/skills/orc-quick/SKILL.md +76 -107
- package/templates/skills/orc-quick/references/dispatch-gate.md +16 -5
- package/templates/skills/orc-quick/references/gh-mode.md +48 -1
- package/templates/skills/orc-quick/references/look.md +3 -1
- package/templates/skills/orc-retro/SKILL.md +19 -18
- package/templates/skills/orc-retro/examples/retro-mock.md +1 -1
- package/templates/skills/orc-route/SKILL.md +25 -45
- package/templates/skills/orc-test/SKILL.md +16 -37
- package/templates/skills/orc-verify/SKILL.md +21 -39
- package/templates/skills/orc-verify/examples/verify-mock.md +33 -33
- package/templates/skills/orc-wait/SKILL.md +156 -163
- package/templates/skills/orc-wiki/references/extra.md +1 -1
- package/templates/skills/orc-wiki/references/phases/phase-0.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-1.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-2.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3.md +1 -6
- package/templates/skills/orc-wiki/references/phases/phase-3c.md +1 -6
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
What ORC knows about this repo: the wiki, its coverage, the code patterns,
|
|
4
4
|
the repair memory, and what it knows from next door.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
SIX TABS since v1.9.2 (the code graph got its own), FIVE since v0.49.1 —
|
|
7
|
+
the Crosslink two-tab precedent. This was one
|
|
7
8
|
scrolling column of six cards, and the release that made the CLI stop
|
|
8
9
|
discarding what it computes roughly tripled the content — which is not
|
|
9
10
|
survivable as one scroll.
|
|
@@ -89,6 +90,7 @@ async function renderKnowledge(body) {
|
|
|
89
90
|
patterns: () => knPatternsTab(d),
|
|
90
91
|
memory: () => knMemoryTab(d, body),
|
|
91
92
|
peers: () => knPeersTab(d),
|
|
93
|
+
graph: () => knGraphTab(d, body),
|
|
92
94
|
};
|
|
93
95
|
const select = (which) => {
|
|
94
96
|
KN_TAB = which;
|
|
@@ -103,6 +105,8 @@ async function renderKnowledge(body) {
|
|
|
103
105
|
["patterns", t("knowledge.tab.patterns")],
|
|
104
106
|
["memory", t("knowledge.tab.memory")],
|
|
105
107
|
["peers", t("knowledge.tab.peers")],
|
|
108
|
+
// v1.9.2 — the code graph moved off the Wiki tab onto its own.
|
|
109
|
+
["graph", t("knowledge.tab.graph")],
|
|
106
110
|
]) {
|
|
107
111
|
const b = el("button", null, label);
|
|
108
112
|
b.type = "button";
|
|
@@ -149,8 +153,10 @@ function knowledgeHeaderStrip(d) {
|
|
|
149
153
|
the state word is never replaced by a friendlier synonym. An update is FREE
|
|
150
154
|
(parser only, no model), so it is a button; an OFF graph shows the one config
|
|
151
155
|
command that turns it on, because turning a feature on is the user's call. */
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
// `exact` (v1.9.2): on the Code graph tab the state ladder above already carries
|
|
157
|
+
// the chip and the update button, so this card is the exact numbers only.
|
|
158
|
+
function graphCard(g, body, gain, exact) {
|
|
159
|
+
const c = card(exact ? t("knowledge.cg.exact.title") : t("knowledge.graph.title"));
|
|
154
160
|
if (!g || !g.state) {
|
|
155
161
|
c.append(empty(t("knowledge.graph.unknown"), t("knowledge.graph.unknownHint")));
|
|
156
162
|
return c;
|
|
@@ -160,6 +166,7 @@ function graphCard(g, body, gain) {
|
|
|
160
166
|
c.append(el("pre", "cmd", "orc config set code_graph on"));
|
|
161
167
|
return c;
|
|
162
168
|
}
|
|
169
|
+
if (exact) c.append(el("div", "note", t("knowledge.cg.exact.lead")));
|
|
163
170
|
const head = el("div", "row-actions");
|
|
164
171
|
head.append(chip(String(g.state).toUpperCase(), g.state === "fresh" ? "ok" : g.state === "drifted" ? "warn" : "idle", g.state === "drifted"));
|
|
165
172
|
const upd = el("button", "btn btn-sm", "orc graph update");
|
|
@@ -170,7 +177,7 @@ function graphCard(g, body, gain) {
|
|
|
170
177
|
renderKnowledge(body);
|
|
171
178
|
});
|
|
172
179
|
head.append(upd);
|
|
173
|
-
c.append(head);
|
|
180
|
+
if (!exact) c.append(head);
|
|
174
181
|
if (g.state === "none") {
|
|
175
182
|
c.append(el("div", "note", t("knowledge.graph.none")));
|
|
176
183
|
return c;
|
|
@@ -205,8 +212,10 @@ function graphCard(g, body, gain) {
|
|
|
205
212
|
// EW3/EW4 — two things keep the map fresh without a lane step. A panel that
|
|
206
213
|
// shows a DRIFTED map without saying that is a panel that invites a needless
|
|
207
214
|
// click.
|
|
208
|
-
|
|
209
|
-
|
|
215
|
+
if (!exact) {
|
|
216
|
+
c.append(el("div", "note", t("knowledge.graph.selfheal")));
|
|
217
|
+
c.append(el("div", "note", t("knowledge.graph.locator")));
|
|
218
|
+
}
|
|
210
219
|
// K5 (v1.8.2 W4b) — the gain strip. It renders exactly the CLI's JSON and
|
|
211
220
|
// invents nothing: the word "estimate" is part of the string, never a
|
|
212
221
|
// tooltip, and `avoided` is a RANGE because one number for a counterfactual
|
|
@@ -283,7 +292,7 @@ function knWikiTab(d, body) {
|
|
|
283
292
|
const out = frag();
|
|
284
293
|
out.append(wikiPlanCard(d.plan, d.debt, body));
|
|
285
294
|
out.append(wikiOneDocCard(d.refs));
|
|
286
|
-
|
|
295
|
+
// v1.9.2 — the code graph card moved to its own tab (knGraphTab).
|
|
287
296
|
if (d.usage && d.usage.rows) out.append(wikiUsageCard(d.usage, body));
|
|
288
297
|
|
|
289
298
|
const w = d.wiki;
|
|
@@ -1147,3 +1156,539 @@ function wikiActions(body, w) {
|
|
|
1147
1156
|
wrap.append(sync);
|
|
1148
1157
|
return wrap;
|
|
1149
1158
|
}
|
|
1159
|
+
|
|
1160
|
+
/* ── TAB 6 — CODE GRAPH (v1.9.2) ──────────────────────────────────────────────
|
|
1161
|
+
The graph used to be one card on the Wiki tab: a list of exact numbers and no
|
|
1162
|
+
picture of what any of them meant. It has its own tab now, in reading order:
|
|
1163
|
+
WHERE THIS GRAPH STANDS (the state ladder and the one action it needs), WHAT
|
|
1164
|
+
IT IS (the pipeline), WHAT A GRAPH LOOKS LIKE (an example, labelled as one),
|
|
1165
|
+
THE NUMBERS (drawn to scale), WHAT IT COST AND SAVED (two bars on one scale),
|
|
1166
|
+
every exact number, and what each word means.
|
|
1167
|
+
|
|
1168
|
+
THE PICTURES DERIVE NOTHING. Every number is `orc graph status --json` or
|
|
1169
|
+
`orc graph gain --json`, drawn to scale and never re-computed into a new
|
|
1170
|
+
claim. The example graph is static prose, and it says so on its face. */
|
|
1171
|
+
function knGraphTab(d, body) {
|
|
1172
|
+
const out = frag();
|
|
1173
|
+
const g = d.graph;
|
|
1174
|
+
const live = !!(g && (g.state === "fresh" || g.state === "drifted"));
|
|
1175
|
+
out.append(cgStateCard(g, body));
|
|
1176
|
+
out.append(cgFlowCard(g, live));
|
|
1177
|
+
out.append(cgAnatomyCard());
|
|
1178
|
+
if (live) out.append(cgNumbersCard(g));
|
|
1179
|
+
if (g && g.state && g.state !== "off") out.append(cgGainCard(d.graphGain));
|
|
1180
|
+
if (live) out.append(graphCard(g, body, d.graphGain, true));
|
|
1181
|
+
out.append(cgGlossaryCard());
|
|
1182
|
+
return out;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Reduced motion means the final number at once — never a count that moves.
|
|
1186
|
+
const cgStill = () => !!(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
1187
|
+
|
|
1188
|
+
// Counts a tile up to the CLI's number, then writes the CLI's own string, so the
|
|
1189
|
+
// resting value is exactly what `--json` sent.
|
|
1190
|
+
function cgCount(node, value, delay) {
|
|
1191
|
+
const final = value === undefined || value === null ? "—" : String(value);
|
|
1192
|
+
const n = Number(value);
|
|
1193
|
+
if (value === undefined || value === null || !Number.isFinite(n) || cgStill()) {
|
|
1194
|
+
node.textContent = final;
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
const dec = Number.isInteger(n) ? 0 : 1;
|
|
1198
|
+
node.textContent = (0).toFixed(dec);
|
|
1199
|
+
const dur = 1100;
|
|
1200
|
+
let start = null;
|
|
1201
|
+
const step = (ts) => {
|
|
1202
|
+
if (start === null) start = ts;
|
|
1203
|
+
const p = Math.min(1, (ts - start) / dur);
|
|
1204
|
+
const eased = 1 - Math.pow(1 - p, 3);
|
|
1205
|
+
node.textContent = p < 1 ? (n * eased).toFixed(dec) : final;
|
|
1206
|
+
if (p < 1) requestAnimationFrame(step);
|
|
1207
|
+
};
|
|
1208
|
+
setTimeout(() => requestAnimationFrame(step), delay || 0);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// A bar segment that grows to its share. The width is set through CSSOM, never a
|
|
1212
|
+
// `style` attribute (the panel serves under `style-src 'self'`).
|
|
1213
|
+
function cgSeg(cls, share, label) {
|
|
1214
|
+
const s = el("div", "cg-seg " + cls);
|
|
1215
|
+
s.style.setProperty("--w", Math.max(0, Math.min(100, share * 100)).toFixed(2) + "%");
|
|
1216
|
+
if (label) s.title = label;
|
|
1217
|
+
return s;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* ── 1. the state ladder ─────────────────────────────────────────────────────
|
|
1221
|
+
Four states, in the order a graph lives through them. The word is the CLI's;
|
|
1222
|
+
the sentence under it says what it means, and the connector says what moves
|
|
1223
|
+
the graph to the next one. */
|
|
1224
|
+
function cgStateCard(g, body) {
|
|
1225
|
+
const c = card(t("knowledge.cg.state.title"));
|
|
1226
|
+
if (!g || !g.state) {
|
|
1227
|
+
c.append(empty(t("knowledge.graph.unknown"), t("knowledge.graph.unknownHint")));
|
|
1228
|
+
return c;
|
|
1229
|
+
}
|
|
1230
|
+
c.append(el("div", "note", t("knowledge.cg.state.lead")));
|
|
1231
|
+
const ladder = el("div", "cg-ladder");
|
|
1232
|
+
const steps = [
|
|
1233
|
+
["OFF", t("knowledge.cg.state.off"), t("knowledge.cg.state.toNone")],
|
|
1234
|
+
["NONE", t("knowledge.cg.state.none"), t("knowledge.cg.state.toFresh")],
|
|
1235
|
+
["FRESH", t("knowledge.cg.state.fresh"), t("knowledge.cg.state.toDrifted")],
|
|
1236
|
+
["DRIFTED", t("knowledge.cg.state.drifted"), null],
|
|
1237
|
+
];
|
|
1238
|
+
// The CLI's state word, upper-cased the way the header strip shows it.
|
|
1239
|
+
const nowWord = String(g.state).toUpperCase();
|
|
1240
|
+
steps.forEach(([word, what, next], i) => {
|
|
1241
|
+
const here = nowWord === word;
|
|
1242
|
+
const s = el("div", "cg-step cg-step-" + word.toLowerCase() + (here ? " cg-step-now" : ""));
|
|
1243
|
+
s.style.setProperty("--i", String(i));
|
|
1244
|
+
const dot = el("span", "cg-step-dot");
|
|
1245
|
+
if (here) dot.append(el("span", "cg-beacon"));
|
|
1246
|
+
s.append(dot);
|
|
1247
|
+
s.append(el("span", "cg-step-word", word));
|
|
1248
|
+
s.append(el("span", "cg-step-what", what));
|
|
1249
|
+
if (here) s.append(el("span", "cg-step-here", t("knowledge.cg.state.here")));
|
|
1250
|
+
ladder.append(s);
|
|
1251
|
+
if (next) {
|
|
1252
|
+
const link = el("div", "cg-step-link");
|
|
1253
|
+
link.style.setProperty("--i", String(i));
|
|
1254
|
+
link.append(el("span", "cg-step-line"));
|
|
1255
|
+
link.append(el("span", "cg-step-next", next));
|
|
1256
|
+
ladder.append(link);
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
// The loop back: a DRIFTED graph becomes FRESH again with one free update.
|
|
1260
|
+
c.append(ladder, el("div", "cg-loop", t("knowledge.cg.state.loop")));
|
|
1261
|
+
|
|
1262
|
+
// What to do now — the ONE action this state needs, or none.
|
|
1263
|
+
const now = el("div", "cg-now cg-now-" + g.state);
|
|
1264
|
+
if (g.state === "off") {
|
|
1265
|
+
now.append(el("div", null, t("knowledge.cg.now.off")));
|
|
1266
|
+
now.append(el("pre", "cmd", "orc config set code_graph on"));
|
|
1267
|
+
} else if (g.state === "none" || g.state === "drifted") {
|
|
1268
|
+
now.append(el("div", null, g.state === "none" ? t("knowledge.cg.now.none") : t("knowledge.cg.now.drifted")));
|
|
1269
|
+
const upd = el("button", "btn btn-sm", "orc graph update");
|
|
1270
|
+
upd.type = "button";
|
|
1271
|
+
upd.addEventListener("click", async () => {
|
|
1272
|
+
upd.disabled = true;
|
|
1273
|
+
const r = await post("/api/graph/update", {});
|
|
1274
|
+
toast(r.command, r.ok ? "ok" : "bad", r.output);
|
|
1275
|
+
renderKnowledge(body);
|
|
1276
|
+
});
|
|
1277
|
+
now.append(upd);
|
|
1278
|
+
} else {
|
|
1279
|
+
now.append(el("div", null, t("knowledge.cg.now.fresh")));
|
|
1280
|
+
}
|
|
1281
|
+
c.append(now);
|
|
1282
|
+
// The line a lane prints into the chat, verbatim. It is the CLI's sentence.
|
|
1283
|
+
if (g.line) {
|
|
1284
|
+
c.append(el("div", "note", t("knowledge.cg.state.lineNote")));
|
|
1285
|
+
c.append(el("pre", "cmd cg-line", g.line));
|
|
1286
|
+
}
|
|
1287
|
+
return c;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/* ── 2. the pipeline ─────────────────────────────────────────────────────────
|
|
1291
|
+
Five stations, left to right, with dots running between them. The first
|
|
1292
|
+
three are free (a parser, no model); the card at the end is what a lane
|
|
1293
|
+
pays for in context tokens. */
|
|
1294
|
+
function cgFlowCard(g, live) {
|
|
1295
|
+
const c = card(t("knowledge.cg.flow.title"));
|
|
1296
|
+
c.append(el("div", "note", t("knowledge.cg.flow.lead")));
|
|
1297
|
+
const rail = el("div", "cg-flow");
|
|
1298
|
+
const stations = [
|
|
1299
|
+
["</>", t("knowledge.cg.flow.code"), t("knowledge.cg.flow.codeWhat"), live ? t("knowledge.cg.flow.files", { n: g.files }) : null, "free"],
|
|
1300
|
+
["f(x)", t("knowledge.cg.flow.parse"), t("knowledge.cg.flow.parseWhat"), t("knowledge.cg.flow.parseCost"), "free"],
|
|
1301
|
+
["◉─◉", t("knowledge.cg.flow.store"), t("knowledge.cg.flow.storeWhat"), live ? t("knowledge.cg.flow.symbols", { n: g.symbols }) : null, "free"],
|
|
1302
|
+
["?", t("knowledge.cg.flow.ask"), t("knowledge.cg.flow.askWhat"), "ctx · impact · map", null],
|
|
1303
|
+
["▤", t("knowledge.cg.flow.card"), t("knowledge.cg.flow.cardWhat"), t("knowledge.cg.flow.cardCost"), "paid"],
|
|
1304
|
+
];
|
|
1305
|
+
stations.forEach(([glyph, name, what, value, cost], i) => {
|
|
1306
|
+
if (i) {
|
|
1307
|
+
const link = el("div", "cg-link");
|
|
1308
|
+
link.style.setProperty("--i", String(i));
|
|
1309
|
+
for (let k = 0; k < 3; k++) {
|
|
1310
|
+
const dot = el("span", "cg-link-dot");
|
|
1311
|
+
dot.style.setProperty("--k", String(k));
|
|
1312
|
+
link.append(dot);
|
|
1313
|
+
}
|
|
1314
|
+
rail.append(link);
|
|
1315
|
+
}
|
|
1316
|
+
const s = el("div", "cg-station" + (cost ? " cg-cost-" + cost : ""));
|
|
1317
|
+
s.style.setProperty("--i", String(i));
|
|
1318
|
+
s.append(el("span", "cg-station-num", String(i + 1)));
|
|
1319
|
+
s.append(el("span", "cg-station-glyph", glyph));
|
|
1320
|
+
s.append(el("span", "cg-station-name", name));
|
|
1321
|
+
s.append(el("span", "cg-station-what", what));
|
|
1322
|
+
if (value) s.append(el("span", "cg-station-value", value));
|
|
1323
|
+
rail.append(s);
|
|
1324
|
+
});
|
|
1325
|
+
c.append(rail);
|
|
1326
|
+
// The second layer — the only one that can cost a model.
|
|
1327
|
+
const notes = el("div", "cg-notes-layer");
|
|
1328
|
+
notes.append(el("span", "cg-notes-tag", t("knowledge.cg.flow.notesTag")));
|
|
1329
|
+
notes.append(el("span", null, t("knowledge.cg.flow.notes", { mode: g && g.notes ? g.notes : "—" })));
|
|
1330
|
+
c.append(notes);
|
|
1331
|
+
c.append(el("div", "note", t("knowledge.graph.locator")));
|
|
1332
|
+
return c;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/* ── 3. the anatomy — an EXAMPLE graph ───────────────────────────────────────
|
|
1336
|
+
Four small files from a made-up shop, drawn the way the graph sees them.
|
|
1337
|
+
It is never this repo's code, and the card says so in its title. Point at a
|
|
1338
|
+
dot and the caption says what `orc graph ctx` would answer for it. */
|
|
1339
|
+
const CG_SAMPLE = {
|
|
1340
|
+
files: [
|
|
1341
|
+
{ name: "routes/orders.js", x: 16, y: 34, w: 212, h: 236 },
|
|
1342
|
+
{ name: "middleware/auth.js", x: 262, y: 20, w: 184, h: 110 },
|
|
1343
|
+
{ name: "services/orders.js", x: 262, y: 162, w: 184, h: 122 },
|
|
1344
|
+
{ name: "db/index.js", x: 480, y: 96, w: 184, h: 116 },
|
|
1345
|
+
],
|
|
1346
|
+
syms: [
|
|
1347
|
+
{ id: "route", label: "GET /orders/:id", x: 122, y: 110 },
|
|
1348
|
+
{ id: "list", label: "listOrders", x: 122, y: 206 },
|
|
1349
|
+
{ id: "auth", label: "requireAuth", x: 354, y: 78 },
|
|
1350
|
+
{ id: "find", label: "findOrder", x: 354, y: 226 },
|
|
1351
|
+
{ id: "query", label: "query", x: 572, y: 158 },
|
|
1352
|
+
],
|
|
1353
|
+
// kind: call (solid, a dot runs along it) · ref (dashed) · import (dotted, file to file)
|
|
1354
|
+
edges: [
|
|
1355
|
+
{ from: "route", to: "auth", kind: "ref" },
|
|
1356
|
+
{ from: "route", to: "find", kind: "call" },
|
|
1357
|
+
{ from: "list", to: "find", kind: "call" },
|
|
1358
|
+
{ from: "find", to: "query", kind: "call" },
|
|
1359
|
+
{ from: "f1", to: "f2", kind: "import", d: "M228 60 L262 60" },
|
|
1360
|
+
{ from: "f1", to: "f3", kind: "import", d: "M228 256 L262 256" },
|
|
1361
|
+
{ from: "f3", to: "f4", kind: "import", d: "M446 196 L480 184" },
|
|
1362
|
+
],
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
// The captions, one per example dot. Keys are written out in full.
|
|
1366
|
+
const CG_CAPTION = {
|
|
1367
|
+
route: () => t("knowledge.cg.anatomy.route"),
|
|
1368
|
+
list: () => t("knowledge.cg.anatomy.list"),
|
|
1369
|
+
auth: () => t("knowledge.cg.anatomy.auth"),
|
|
1370
|
+
find: () => t("knowledge.cg.anatomy.find"),
|
|
1371
|
+
query: () => t("knowledge.cg.anatomy.query"),
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
function cgSvg(tag, attrs) {
|
|
1375
|
+
const node = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
1376
|
+
for (const [k, v] of Object.entries(attrs || {})) node.setAttribute(k, String(v));
|
|
1377
|
+
return node;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
function cgAnatomyCard() {
|
|
1381
|
+
const c = card(t("knowledge.cg.anatomy.title"));
|
|
1382
|
+
c.append(el("div", "note", t("knowledge.cg.anatomy.lead")));
|
|
1383
|
+
const wrap = el("div", "cg-anatomy");
|
|
1384
|
+
const scroll = el("div", "scroll-x cg-anatomy-scroll");
|
|
1385
|
+
const svg = cgSvg("svg", { class: "cg-svg", viewBox: "0 0 680 300", role: "img", "aria-label": t("knowledge.cg.anatomy.title") });
|
|
1386
|
+
const defs = cgSvg("defs");
|
|
1387
|
+
for (const [id, cls] of [["cg-arrow-call", "cg-head-call"], ["cg-arrow-ref", "cg-head-ref"]]) {
|
|
1388
|
+
const m = cgSvg("marker", { id, viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse" });
|
|
1389
|
+
m.append(cgSvg("path", { d: "M0 0 L10 5 L0 10 z", class: cls }));
|
|
1390
|
+
defs.append(m);
|
|
1391
|
+
}
|
|
1392
|
+
svg.append(defs);
|
|
1393
|
+
|
|
1394
|
+
CG_SAMPLE.files.forEach((f, i) => {
|
|
1395
|
+
const gF = cgSvg("g", { class: "cg-file" });
|
|
1396
|
+
gF.style.setProperty("--d", i * 90 + "ms");
|
|
1397
|
+
gF.append(cgSvg("rect", { x: f.x, y: f.y, width: f.w, height: f.h, rx: 10 }));
|
|
1398
|
+
const label = cgSvg("text", { x: f.x + 12, y: f.y + 20, class: "cg-file-name" });
|
|
1399
|
+
label.textContent = f.name;
|
|
1400
|
+
gF.append(label);
|
|
1401
|
+
svg.append(gF);
|
|
1402
|
+
});
|
|
1403
|
+
const pos = {};
|
|
1404
|
+
for (const s of CG_SAMPLE.syms) pos[s.id] = s;
|
|
1405
|
+
|
|
1406
|
+
// Edges first, so the dots sit on top of them.
|
|
1407
|
+
const edgeEls = [];
|
|
1408
|
+
CG_SAMPLE.edges.forEach((e, i) => {
|
|
1409
|
+
let d = e.d;
|
|
1410
|
+
if (!d) {
|
|
1411
|
+
const a = pos[e.from];
|
|
1412
|
+
const b = pos[e.to];
|
|
1413
|
+
const mx = (a.x + b.x) / 2;
|
|
1414
|
+
d = `M${a.x + 10} ${a.y} C${mx} ${a.y} ${mx} ${b.y} ${b.x - 11} ${b.y}`;
|
|
1415
|
+
}
|
|
1416
|
+
const gE = cgSvg("g", { class: "cg-edge cg-edge-" + e.kind });
|
|
1417
|
+
gE.dataset.from = e.from;
|
|
1418
|
+
gE.dataset.to = e.to;
|
|
1419
|
+
gE.style.setProperty("--d", 380 + i * 110 + "ms");
|
|
1420
|
+
const attrs = { d, pathLength: 1, class: "cg-edge-line" };
|
|
1421
|
+
if (e.kind === "call") attrs["marker-end"] = "url(#cg-arrow-call)";
|
|
1422
|
+
if (e.kind === "ref") attrs["marker-end"] = "url(#cg-arrow-ref)";
|
|
1423
|
+
gE.append(cgSvg("path", attrs));
|
|
1424
|
+
if (e.kind === "call") {
|
|
1425
|
+
const p = cgSvg("path", { d, pathLength: 1, class: "cg-pulse" });
|
|
1426
|
+
p.style.setProperty("--d", 1200 + i * 450 + "ms");
|
|
1427
|
+
gE.append(p);
|
|
1428
|
+
}
|
|
1429
|
+
svg.append(gE);
|
|
1430
|
+
edgeEls.push(gE);
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
const caption = el("div", "cg-caption");
|
|
1434
|
+
const setCaption = (id) => {
|
|
1435
|
+
caption.replaceChildren();
|
|
1436
|
+
if (!id) {
|
|
1437
|
+
caption.append(el("span", "cg-caption-hint", t("knowledge.cg.anatomy.hint")));
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const s = pos[id];
|
|
1441
|
+
caption.append(el("span", "cg-caption-sym mono", s.label));
|
|
1442
|
+
caption.append(el("span", "cg-caption-text", CG_CAPTION[id]()));
|
|
1443
|
+
caption.append(el("span", "cg-caption-cmd mono", "orc graph ctx " + (s.label.includes(" ") ? `"${s.label}"` : s.label)));
|
|
1444
|
+
};
|
|
1445
|
+
const focus = (id) => {
|
|
1446
|
+
wrap.classList.toggle("cg-focus", !!id);
|
|
1447
|
+
for (const gE of edgeEls) gE.classList.toggle("cg-edge-hi", !!id && (gE.dataset.from === id || gE.dataset.to === id));
|
|
1448
|
+
for (const n of svg.querySelectorAll(".cg-sym")) n.classList.toggle("cg-sym-hi", n.dataset.id === id);
|
|
1449
|
+
setCaption(id);
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
CG_SAMPLE.syms.forEach((s, i) => {
|
|
1453
|
+
const gS = cgSvg("g", { class: "cg-sym", tabindex: 0, role: "button", "aria-label": s.label });
|
|
1454
|
+
gS.dataset.id = s.id;
|
|
1455
|
+
gS.style.setProperty("--d", 200 + i * 80 + "ms");
|
|
1456
|
+
gS.append(cgSvg("circle", { cx: s.x, cy: s.y, r: 16, class: "cg-sym-halo" }));
|
|
1457
|
+
gS.append(cgSvg("circle", { cx: s.x, cy: s.y, r: 8, class: "cg-sym-dot" }));
|
|
1458
|
+
const tx = cgSvg("text", { x: s.x, y: s.y - 20, class: "cg-sym-name", "text-anchor": "middle" });
|
|
1459
|
+
tx.textContent = s.label;
|
|
1460
|
+
gS.append(tx);
|
|
1461
|
+
gS.addEventListener("mouseenter", () => focus(s.id));
|
|
1462
|
+
gS.addEventListener("focus", () => focus(s.id));
|
|
1463
|
+
gS.addEventListener("mouseleave", () => focus(null));
|
|
1464
|
+
gS.addEventListener("blur", () => focus(null));
|
|
1465
|
+
svg.append(gS);
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
scroll.append(svg);
|
|
1469
|
+
wrap.append(scroll);
|
|
1470
|
+
setCaption(null);
|
|
1471
|
+
wrap.append(caption);
|
|
1472
|
+
|
|
1473
|
+
// The legend: every mark on the picture, and what it means.
|
|
1474
|
+
const legend = el("div", "cg-legend");
|
|
1475
|
+
for (const [cls, name, what] of [
|
|
1476
|
+
["cg-lg-file", t("knowledge.cg.legend.file"), t("knowledge.cg.legend.fileWhat")],
|
|
1477
|
+
["cg-lg-sym", t("knowledge.cg.legend.symbol"), t("knowledge.cg.legend.symbolWhat")],
|
|
1478
|
+
["cg-lg-call", t("knowledge.cg.legend.call"), t("knowledge.cg.legend.callWhat")],
|
|
1479
|
+
["cg-lg-ref", t("knowledge.cg.legend.ref"), t("knowledge.cg.legend.refWhat")],
|
|
1480
|
+
["cg-lg-import", t("knowledge.cg.legend.import"), t("knowledge.cg.legend.importWhat")],
|
|
1481
|
+
]) {
|
|
1482
|
+
const row = el("div", "cg-legend-row");
|
|
1483
|
+
row.append(el("span", "cg-lg " + cls));
|
|
1484
|
+
const txt = el("div", "cg-legend-text");
|
|
1485
|
+
txt.append(el("strong", null, name));
|
|
1486
|
+
txt.append(el("span", "note", what));
|
|
1487
|
+
row.append(txt);
|
|
1488
|
+
legend.append(row);
|
|
1489
|
+
}
|
|
1490
|
+
wrap.append(legend);
|
|
1491
|
+
c.append(wrap);
|
|
1492
|
+
return c;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/* ── 4. the numbers, drawn ───────────────────────────────────────────────────
|
|
1496
|
+
Tiles that count up to the CLI's own number, the three change counts, and
|
|
1497
|
+
the density as a bar — per language when the CLI sent the split. */
|
|
1498
|
+
function cgNumbersCard(g) {
|
|
1499
|
+
const c = card(t("knowledge.cg.num.title"));
|
|
1500
|
+
const tiles = el("div", "cg-tiles");
|
|
1501
|
+
const tile = (label, value, what, i) => {
|
|
1502
|
+
const box = el("div", "cg-tile");
|
|
1503
|
+
box.style.setProperty("--i", String(i));
|
|
1504
|
+
const v = el("span", "cg-tile-value");
|
|
1505
|
+
cgCount(v, value, i * 120);
|
|
1506
|
+
box.append(v, el("span", "cg-tile-label", label), el("span", "cg-tile-what", what));
|
|
1507
|
+
tiles.append(box);
|
|
1508
|
+
};
|
|
1509
|
+
tile(t("knowledge.graph.files"), g.files, t("knowledge.cg.num.filesWhat"), 0);
|
|
1510
|
+
tile(t("knowledge.graph.symbols"), g.symbols, t("knowledge.cg.num.symbolsWhat"), 1);
|
|
1511
|
+
tile(t("knowledge.cg.num.perFile"), g.density ? g.density.symbols_per_file : undefined, t("knowledge.cg.num.perFileWhat"), 2);
|
|
1512
|
+
tile(t("knowledge.graph.generation"), g.generation, t("knowledge.cg.num.generationWhat"), 3);
|
|
1513
|
+
c.append(tiles);
|
|
1514
|
+
|
|
1515
|
+
// What changed since the map was last built. Three counts, the CLI's.
|
|
1516
|
+
if (g.behind) {
|
|
1517
|
+
const b = g.behind;
|
|
1518
|
+
const row = el("div", "cg-behind");
|
|
1519
|
+
row.append(el("span", "cg-behind-label", t("knowledge.cg.num.behind")));
|
|
1520
|
+
for (const [cls, sign, n, label] of [
|
|
1521
|
+
["cg-b-add", "+", b.added || 0, t("knowledge.cg.num.added")],
|
|
1522
|
+
["cg-b-chg", "~", b.changed || 0, t("knowledge.cg.num.changed")],
|
|
1523
|
+
["cg-b-del", "-", b.deleted || 0, t("knowledge.cg.num.deleted")],
|
|
1524
|
+
]) {
|
|
1525
|
+
const chipEl = el("span", "cg-bchip " + cls + (n ? "" : " cg-b-zero"));
|
|
1526
|
+
chipEl.append(el("strong", null, sign + n), el("span", null, label));
|
|
1527
|
+
row.append(chipEl);
|
|
1528
|
+
}
|
|
1529
|
+
c.append(row);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// The density. `zero_share` is the CLI's; the bar only draws it.
|
|
1533
|
+
if (g.density) {
|
|
1534
|
+
const den = g.density;
|
|
1535
|
+
const pctEmpty = Math.round(den.zero_share * 100);
|
|
1536
|
+
c.append(el("div", "cg-sub", t("knowledge.cg.num.densityTitle")));
|
|
1537
|
+
const bar = el("div", "cg-bar");
|
|
1538
|
+
bar.append(cgSeg("cg-seg-ok", 1 - den.zero_share), cgSeg("cg-seg-empty", den.zero_share));
|
|
1539
|
+
c.append(bar);
|
|
1540
|
+
const key = el("div", "cg-bar-key");
|
|
1541
|
+
key.append(el("span", "cg-key cg-key-ok", t("knowledge.cg.num.hasSymbols", { pct: 100 - pctEmpty })));
|
|
1542
|
+
key.append(el("span", "cg-key cg-key-empty", t("knowledge.cg.num.empty", { pct: pctEmpty, n: den.zero_files })));
|
|
1543
|
+
c.append(key);
|
|
1544
|
+
if (den.by_lang && Object.keys(den.by_lang).length) {
|
|
1545
|
+
const langs = el("div", "cg-langs");
|
|
1546
|
+
Object.entries(den.by_lang).forEach(([lang, v], i) => {
|
|
1547
|
+
const r = el("div", "cg-lang");
|
|
1548
|
+
r.style.setProperty("--i", String(i));
|
|
1549
|
+
r.append(el("span", "cg-lang-name mono", lang));
|
|
1550
|
+
const lb = el("div", "cg-bar cg-bar-sm");
|
|
1551
|
+
const share = v.files ? v.zero / v.files : 0;
|
|
1552
|
+
lb.append(cgSeg("cg-seg-ok", 1 - share), cgSeg("cg-seg-empty", share));
|
|
1553
|
+
r.append(lb);
|
|
1554
|
+
r.append(el("span", "cg-lang-nums", t("knowledge.cg.num.lang", { files: v.files, symbols: v.symbols, zero: v.zero })));
|
|
1555
|
+
langs.append(r);
|
|
1556
|
+
});
|
|
1557
|
+
c.append(langs);
|
|
1558
|
+
}
|
|
1559
|
+
c.append(el("div", "note", t("knowledge.cg.num.densityNote")));
|
|
1560
|
+
if (g.thin) c.append(el("div", "note warn", t("knowledge.graph.thin")));
|
|
1561
|
+
}
|
|
1562
|
+
return c;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
/* ── 5. what it cost, and what it probably saved ─────────────────────────────
|
|
1566
|
+
Two bars on ONE scale. `paid` is exact and solid; `avoided` is an ESTIMATE
|
|
1567
|
+
and a RANGE, so it is drawn solid to its low end and striped to its high
|
|
1568
|
+
end — a single-number bar for a counterfactual is the claim this meter
|
|
1569
|
+
refuses to make. */
|
|
1570
|
+
const CG_READS = {
|
|
1571
|
+
ctx: () => t("knowledge.cg.read.ctx"),
|
|
1572
|
+
"for-slice": () => t("knowledge.cg.read.forSlice"),
|
|
1573
|
+
impact: () => t("knowledge.cg.read.impact"),
|
|
1574
|
+
map: () => t("knowledge.cg.read.map"),
|
|
1575
|
+
changes: () => t("knowledge.cg.read.changes"),
|
|
1576
|
+
cochange: () => t("knowledge.cg.read.cochange"),
|
|
1577
|
+
coverage: () => t("knowledge.cg.read.coverage"),
|
|
1578
|
+
path: () => t("knowledge.cg.read.path"),
|
|
1579
|
+
};
|
|
1580
|
+
|
|
1581
|
+
function cgGainCard(gain) {
|
|
1582
|
+
const c = card(t("knowledge.cg.gain.title"));
|
|
1583
|
+
if (!gain || gain.state !== "rows") {
|
|
1584
|
+
c.append(empty(t("knowledge.gain.none"), t("knowledge.cg.gain.noneHint")));
|
|
1585
|
+
return c;
|
|
1586
|
+
}
|
|
1587
|
+
c.append(el("div", "note", t("knowledge.cg.gain.lead")));
|
|
1588
|
+
const p = gain.paid || {};
|
|
1589
|
+
const a = gain.avoided || {};
|
|
1590
|
+
const scale = Math.max(1, p.total || 0, a.high || 0);
|
|
1591
|
+
|
|
1592
|
+
const rows = el("div", "cg-gain");
|
|
1593
|
+
const paidRow = el("div", "cg-gain-row");
|
|
1594
|
+
paidRow.append(el("span", "cg-gain-name", t("knowledge.cg.gain.paid")));
|
|
1595
|
+
const paidBar = el("div", "cg-bar cg-bar-lg");
|
|
1596
|
+
for (const [cls, n, label] of [
|
|
1597
|
+
["cg-seg-card", p.card || 0, t("knowledge.cg.gain.card")],
|
|
1598
|
+
["cg-seg-source", p.source || 0, t("knowledge.cg.gain.source")],
|
|
1599
|
+
["cg-seg-hints", p.hints || 0, t("knowledge.cg.gain.hintsPart")],
|
|
1600
|
+
["cg-seg-env", p.envelope || 0, t("knowledge.cg.gain.envelope")],
|
|
1601
|
+
])
|
|
1602
|
+
paidBar.append(cgSeg(cls, n / scale, `${label} ${kTokUi(n)}`));
|
|
1603
|
+
paidRow.append(paidBar, el("span", "cg-gain-val", kTokUi(p.total || 0)));
|
|
1604
|
+
rows.append(paidRow);
|
|
1605
|
+
const avRow = el("div", "cg-gain-row");
|
|
1606
|
+
avRow.append(el("span", "cg-gain-name", t("knowledge.cg.gain.avoided")));
|
|
1607
|
+
const avBar = el("div", "cg-bar cg-bar-lg");
|
|
1608
|
+
avBar.append(cgSeg("cg-seg-low", (a.low || 0) / scale), cgSeg("cg-seg-range", ((a.high || 0) - (a.low || 0)) / scale));
|
|
1609
|
+
avRow.append(avBar, el("span", "cg-gain-val", `~${kTokUi(a.low || 0)} – ${kTokUi(a.high || 0)}`));
|
|
1610
|
+
rows.append(avRow);
|
|
1611
|
+
c.append(rows);
|
|
1612
|
+
|
|
1613
|
+
const key = el("div", "cg-bar-key");
|
|
1614
|
+
for (const [cls, label, n] of [
|
|
1615
|
+
["cg-key-card", t("knowledge.cg.gain.card"), p.card],
|
|
1616
|
+
["cg-key-source", t("knowledge.cg.gain.source"), p.source],
|
|
1617
|
+
["cg-key-hints", t("knowledge.cg.gain.hintsPart"), p.hints],
|
|
1618
|
+
["cg-key-env", t("knowledge.cg.gain.envelope"), p.envelope],
|
|
1619
|
+
["cg-key-range", t("knowledge.cg.gain.range"), null],
|
|
1620
|
+
])
|
|
1621
|
+
key.append(el("span", "cg-key " + cls, n === null ? label : `${label} ${kTokUi(n || 0)}`));
|
|
1622
|
+
c.append(key);
|
|
1623
|
+
c.append(el("div", "note", t("knowledge.gain.estimate")));
|
|
1624
|
+
|
|
1625
|
+
// Which reads were asked, in the CLI's own order. A read never asked keeps its
|
|
1626
|
+
// slot, muted — "never asked" and "not a read" are different facts.
|
|
1627
|
+
const names = gain.read_set && gain.read_set.length ? gain.read_set : Object.keys(gain.by_command || {});
|
|
1628
|
+
if (names.length) {
|
|
1629
|
+
c.append(el("div", "cg-sub", t("knowledge.cg.gain.readsTitle", { n: gain.calls_recorded, runs: gain.runs === undefined ? "—" : gain.runs })));
|
|
1630
|
+
const counts = names.map((k) => (gain.by_command && gain.by_command[k]) || 0);
|
|
1631
|
+
const most = Math.max(1, ...counts);
|
|
1632
|
+
const list = el("div", "cg-reads");
|
|
1633
|
+
names.forEach((k, i) => {
|
|
1634
|
+
const n = counts[i];
|
|
1635
|
+
const r = el("div", "cg-read" + (n ? "" : " cg-read-never"));
|
|
1636
|
+
r.style.setProperty("--i", String(i));
|
|
1637
|
+
r.append(el("span", "cg-read-cmd mono", k));
|
|
1638
|
+
const lb = el("div", "cg-bar cg-bar-sm");
|
|
1639
|
+
lb.append(cgSeg("cg-seg-read", n / most));
|
|
1640
|
+
r.append(lb);
|
|
1641
|
+
r.append(el("span", "cg-read-n", n ? String(n) : t("knowledge.cg.gain.never")));
|
|
1642
|
+
r.append(el("span", "cg-read-what", CG_READS[k] ? CG_READS[k]() : ""));
|
|
1643
|
+
list.append(r);
|
|
1644
|
+
});
|
|
1645
|
+
c.append(list);
|
|
1646
|
+
}
|
|
1647
|
+
return c;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/* ── 6. what each word means ─────────────────────────────────────────────────
|
|
1651
|
+
Every word this tab and the exact card use, in four groups. */
|
|
1652
|
+
function cgGlossaryCard() {
|
|
1653
|
+
const c = card(t("knowledge.cg.gloss.title"));
|
|
1654
|
+
c.append(el("div", "note", t("knowledge.cg.gloss.lead")));
|
|
1655
|
+
const groups = [
|
|
1656
|
+
[t("knowledge.cg.gloss.gStructure"), [
|
|
1657
|
+
[t("knowledge.cg.legend.file"), t("knowledge.cg.gloss.file")],
|
|
1658
|
+
[t("knowledge.cg.legend.symbol"), t("knowledge.cg.gloss.symbol")],
|
|
1659
|
+
[t("knowledge.cg.legend.call"), t("knowledge.cg.gloss.call")],
|
|
1660
|
+
[t("knowledge.graph.generation"), t("knowledge.cg.gloss.generation")],
|
|
1661
|
+
]],
|
|
1662
|
+
[t("knowledge.cg.gloss.gFresh"), [
|
|
1663
|
+
["FRESH · DRIFTED", t("knowledge.cg.gloss.freshDrifted")],
|
|
1664
|
+
["NONE · OFF", t("knowledge.cg.gloss.noneOff")],
|
|
1665
|
+
[t("knowledge.cg.num.behind"), t("knowledge.cg.gloss.behind")],
|
|
1666
|
+
[t("knowledge.cg.gloss.selfHealTerm"), t("knowledge.graph.selfheal")],
|
|
1667
|
+
]],
|
|
1668
|
+
[t("knowledge.cg.gloss.gQuality"), [
|
|
1669
|
+
[t("knowledge.cg.num.perFile"), t("knowledge.cg.gloss.density")],
|
|
1670
|
+
["THIN", t("knowledge.cg.gloss.thin")],
|
|
1671
|
+
[t("knowledge.graph.notes"), t("knowledge.cg.gloss.notes")],
|
|
1672
|
+
]],
|
|
1673
|
+
[t("knowledge.cg.gloss.gCost"), [
|
|
1674
|
+
[t("knowledge.cg.gain.paid"), t("knowledge.cg.gloss.paid")],
|
|
1675
|
+
[t("knowledge.cg.gain.envelope"), t("knowledge.cg.gloss.envelope")],
|
|
1676
|
+
[t("knowledge.cg.gain.avoided"), t("knowledge.cg.gloss.avoided")],
|
|
1677
|
+
[t("knowledge.cg.gloss.netTerm"), t("knowledge.cg.gloss.net")],
|
|
1678
|
+
[t("knowledge.cg.gloss.hintsTerm"), t("knowledge.cg.gloss.hints")],
|
|
1679
|
+
[t("knowledge.gain.measure"), t("knowledge.cg.gloss.measured")],
|
|
1680
|
+
]],
|
|
1681
|
+
];
|
|
1682
|
+
const grid = el("div", "cg-gloss");
|
|
1683
|
+
groups.forEach(([title, terms], gi) => {
|
|
1684
|
+
const box = el("div", "cg-gloss-group");
|
|
1685
|
+
box.style.setProperty("--i", String(gi));
|
|
1686
|
+
box.append(el("div", "cg-gloss-head", title));
|
|
1687
|
+
const dl = el("dl", "cg-gloss-list");
|
|
1688
|
+
for (const [term, meaning] of terms) dl.append(el("dt", null, term), el("dd", null, meaning));
|
|
1689
|
+
box.append(dl);
|
|
1690
|
+
grid.append(box);
|
|
1691
|
+
});
|
|
1692
|
+
c.append(grid);
|
|
1693
|
+
return c;
|
|
1694
|
+
}
|
|
@@ -66,8 +66,9 @@ const FINDING_ROUTE = {
|
|
|
66
66
|
"lane-keys-drifted": { panel: "maintenance", cta: "overview.item.laneKeysDrifted.cta" },
|
|
67
67
|
// v1.8.0. A DRIFTED code graph is cleared by the free `orc graph update`, which
|
|
68
68
|
// is a button on the Knowledge panel's code graph card — the panel that can
|
|
69
|
-
// CLEAR it, not the default.
|
|
70
|
-
|
|
69
|
+
// CLEAR it, not the default. Since v1.9.2 it opens the Code graph TAB, where
|
|
70
|
+
// the state ladder carries that button.
|
|
71
|
+
"graph-drifted": { panel: "knowledge", tab: "graph", cta: "overview.item.graphDrifted.cta" },
|
|
71
72
|
// v1.3.0. The rule again, and cleanly: every one of these is cleared on the
|
|
72
73
|
// CLI Hook Interface panel — the board is where an orphaned component is
|
|
73
74
|
// removed, the caution strip is where an invalid layout is named, and the
|
|
@@ -97,6 +98,13 @@ const FINDING_ROUTE = {
|
|
|
97
98
|
const DEFAULT_FINDING_ROUTE = { panel: "maintenance", cta: "overview.item.doctor.cta" };
|
|
98
99
|
const findingRoute = (id) => FINDING_ROUTE[id] || DEFAULT_FINDING_ROUTE;
|
|
99
100
|
|
|
101
|
+
// Go to a panel, and — when the route names one — to its tab. Only Knowledge
|
|
102
|
+
// has a tab a finding can name (KN_TAB, which survives a re-render).
|
|
103
|
+
function goRoute(panel, tab) {
|
|
104
|
+
if (panel === "knowledge" && tab && typeof KN_TAB !== "undefined") KN_TAB = tab;
|
|
105
|
+
location.hash = "#/" + panel;
|
|
106
|
+
}
|
|
107
|
+
|
|
100
108
|
PANELS.overview = function (host) {
|
|
101
109
|
head(host, t("overview.title"), t("overview.sub"));
|
|
102
110
|
section(
|
|
@@ -317,7 +325,7 @@ PANELS.overview = function (host) {
|
|
|
317
325
|
if (r.panel) {
|
|
318
326
|
const go = el("button", "btn btn-ghost btn-sm", t(r.cta));
|
|
319
327
|
go.type = "button";
|
|
320
|
-
go.addEventListener("click", () => (
|
|
328
|
+
go.addEventListener("click", () => goRoute(r.panel, r.tab));
|
|
321
329
|
detail.append(go);
|
|
322
330
|
} else {
|
|
323
331
|
detail.append(el("div", "note", t("overview.attention.nothingToDo")));
|
|
@@ -387,6 +395,7 @@ function attentionCard(d, findings) {
|
|
|
387
395
|
// as the title beats paraphrasing it into something less exact.
|
|
388
396
|
title: f.message,
|
|
389
397
|
panel: r.panel,
|
|
398
|
+
tab: r.tab,
|
|
390
399
|
cta: t(r.cta),
|
|
391
400
|
});
|
|
392
401
|
}
|
|
@@ -458,7 +467,7 @@ function attentionCard(d, findings) {
|
|
|
458
467
|
// The CLI's own words for WHY, kept verbatim under our explanation.
|
|
459
468
|
if (it.evidence) mid.append(el("div", "todo-evidence", it.evidence));
|
|
460
469
|
row.append(mid, el("span", "todo-cta", it.cta));
|
|
461
|
-
row.addEventListener("click", () => (
|
|
470
|
+
row.addEventListener("click", () => goRoute(it.panel, it.tab));
|
|
462
471
|
list.append(row);
|
|
463
472
|
}
|
|
464
473
|
if (items.length) c.append(list);
|