@azure-id/orc 1.5.0 → 1.7.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 +185 -0
- package/README-id.md +206 -125
- package/README.md +270 -57
- package/bin/cli.js +1676 -2
- package/bin/mockrun-catalog.js +272 -267
- package/bin/verify-contracts.js +4485 -4449
- package/bin/verify-package.js +9 -0
- package/bin/webui/api.js +26 -0
- package/bin/webui/app.html +10 -0
- package/bin/webui/css/panels/knowledge.css +30 -0
- package/bin/webui/css/panels/rules.css +145 -0
- package/bin/webui/fixtures/index.js +27 -0
- package/bin/webui/fixtures/rules.js +1002 -0
- package/bin/webui/fixtures/wiki-one.js +198 -0
- package/bin/webui/i18n/en/knowledge.json +127 -117
- package/bin/webui/i18n/en/nav.json +1 -0
- package/bin/webui/i18n/en/overview.json +1 -0
- package/bin/webui/i18n/en/rules.json +55 -0
- package/bin/webui/i18n/id/knowledge.json +127 -117
- package/bin/webui/i18n/id/nav.json +1 -0
- package/bin/webui/i18n/id/overview.json +1 -0
- package/bin/webui/i18n/id/rules.json +55 -0
- package/bin/webui/js/01-i18n.js +1 -0
- package/bin/webui/js/panels/knowledge.js +117 -1
- package/bin/webui/js/panels/overview.js +8 -0
- package/bin/webui/js/panels/rules.js +351 -0
- package/mock-run/INDEX.md +107 -106
- package/mock-run/orc-rules.md +254 -0
- package/package.json +1 -1
- package/templates/commands/orc-rules.md +66 -0
- package/templates/commands/orc-wiki.md +23 -1
- package/templates/hooks/README.md +110 -1
- package/templates/hooks/orc-read-gate.js +279 -0
- package/templates/skills/_shared/README.md +61 -54
- package/templates/skills/_shared/phases/README.md +1 -0
- package/templates/skills/_shared/phases/preflight.md +20 -0
- package/templates/skills/_shared/phases/rules.md +154 -0
- package/templates/skills/_shared/return-validation.md +28 -0
- package/templates/skills/_shared/rules/CREDITS.md +141 -0
- package/templates/skills/_shared/rules/INDEX.md +107 -0
- package/templates/skills/_shared/rules/code.md +229 -0
- package/templates/skills/_shared/rules/delivery.md +114 -0
- package/templates/skills/_shared/rules/ui.md +139 -0
- package/templates/skills/_shared/rules/writing.md +223 -0
- package/templates/skills/context-combiner/SKILL.md +9 -1
- package/templates/skills/orc/SKILL.md +7 -0
- package/templates/skills/orc-aftermath/SKILL.md +7 -0
- package/templates/skills/orc-analyze/SKILL.md +7 -0
- package/templates/skills/orc-analyze-mini/SKILL.md +9 -1
- package/templates/skills/orc-boundary/SKILL.md +7 -0
- package/templates/skills/orc-brainstorm/SKILL.md +7 -0
- package/templates/skills/orc-budget/SKILL.md +7 -0
- package/templates/skills/orc-challenge/SKILL.md +7 -0
- package/templates/skills/orc-claude/SKILL.md +7 -0
- package/templates/skills/orc-diy/SKILL.md +7 -0
- package/templates/skills/orc-explain/SKILL.md +7 -0
- package/templates/skills/orc-export/SKILL.md +7 -0
- package/templates/skills/orc-fast/SKILL.md +7 -0
- package/templates/skills/orc-grill/SKILL.md +7 -0
- package/templates/skills/orc-handoff/SKILL.md +9 -1
- package/templates/skills/orc-learn/SKILL.md +7 -0
- package/templates/skills/orc-mini/SKILL.md +7 -0
- package/templates/skills/orc-pact/SKILL.md +7 -0
- package/templates/skills/orc-poly/SKILL.md +7 -0
- package/templates/skills/orc-pr-driver/SKILL.md +9 -1
- package/templates/skills/orc-pr-setup/SKILL.md +9 -1
- package/templates/skills/orc-quick/SKILL.md +7 -0
- package/templates/skills/orc-retro/SKILL.md +7 -0
- package/templates/skills/orc-route/SKILL.md +7 -0
- package/templates/skills/orc-test/SKILL.md +7 -0
- package/templates/skills/orc-verify/SKILL.md +7 -0
- package/templates/skills/orc-wiki/SKILL.md +15 -0
- package/templates/skills/orc-wiki/references/partial-refresh.md +68 -0
- package/templates/skills/orc-wiki/references/phases/phase-0.md +15 -1
package/bin/mockrun-catalog.js
CHANGED
|
@@ -1,271 +1,276 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* mockrun-catalog.js — the catalogue of MOCKED RUNS that ship with ORC.
|
|
4
|
-
*
|
|
5
|
-
* A mocked run is a written walkthrough of ONE lane: what you type, what ORC
|
|
6
|
-
* prints back, what lands on disk. Nothing was executed to produce them — they
|
|
7
|
-
* are documentation — and that is exactly why they exist: a new user needs to
|
|
8
|
-
* know what a lane LOOKS like before paying tokens to find out.
|
|
9
|
-
*
|
|
10
|
-
* Two sources, one catalogue:
|
|
11
|
-
* · `mock-run/*.md` the reader-facing walkthroughs
|
|
12
|
-
* · `templates/skills/<skill>/examples/*` the dense annotated examples that
|
|
13
|
-
* already shipped with each skill
|
|
14
|
-
*
|
|
15
|
-
* DERIVED, never hand-listed. A new file in either place appears by itself:
|
|
16
|
-
* the title comes from the file's own `# ` heading, the lane from whether
|
|
17
|
-
* `templates/commands/<name>.md` exists, and the one-line summary from the
|
|
18
|
-
* `> ` blockquote the walkthroughs open with. The only hand-written table here
|
|
19
|
-
* is the READING ORDER (`GROUPS` + `GROUP_OF`) — an order is editorial and
|
|
20
|
-
* cannot be derived from a filename. A file nobody placed still shows up, in
|
|
21
|
-
* the `other` group, because a mocked run that exists and is invisible is the
|
|
22
|
-
* failure mode this module is meant to make impossible.
|
|
23
|
-
*
|
|
24
|
-
* This is package content, not project state: it is the same on every machine
|
|
25
|
-
* and needs no `.claude/`. `orc mock-run` reads it, and `orc ui` serves it the
|
|
26
|
-
* same way it serves the onboarding walkthrough — straight from the module,
|
|
27
|
-
* because spawning a subprocess to read files that ship in this package would
|
|
28
|
-
* be ceremony with a cost. It is NOT `orc mock`, which lists the runnable
|
|
29
|
-
* `mock-examples/<slug>/` folders a green verify left in YOUR project.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
const fs = require("fs");
|
|
33
|
-
const path = require("path");
|
|
34
|
-
|
|
35
|
-
const ROOT = path.join(__dirname, "..");
|
|
36
|
-
const WALKTHROUGH_DIR = path.join(ROOT, "mock-run");
|
|
37
|
-
const SKILLS_DIR = path.join(ROOT, "templates", "skills");
|
|
38
|
-
const COMMANDS_DIR = path.join(ROOT, "templates", "commands");
|
|
39
|
-
|
|
40
|
-
// The reading order. Group titles are DATA (the panel prints them as written
|
|
41
|
-
// and never translates them, exactly like a config key or a doctor message).
|
|
42
|
-
const GROUPS = [
|
|
43
|
-
{ id: "start", title: "Start here" },
|
|
44
|
-
{ id: "build", title: "Build a change" },
|
|
45
|
-
{ id: "decide", title: "Decide what to build" },
|
|
46
|
-
{ id: "knowledge", title: "Teach ORC your project" },
|
|
47
|
-
{ id: "check", title: "Check what happened" },
|
|
48
|
-
{ id: "ship", title: "Ship and hand over" },
|
|
49
|
-
{ id: "tools", title: "Panel and terminal" },
|
|
50
|
-
{ id: "other", title: "More" },
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
// slug (or lane name) → group. Anything absent lands in `other`.
|
|
54
|
-
const GROUP_OF = {
|
|
55
|
-
"the-example-project": "start",
|
|
56
|
-
"a-normal-day": "start",
|
|
57
|
-
|
|
58
|
-
orc: "build",
|
|
59
|
-
"orc-ultra": "build",
|
|
60
|
-
"orc-mini": "build",
|
|
61
|
-
"orc-fast": "build",
|
|
62
|
-
"orc-quick": "build",
|
|
63
|
-
"orc-diy": "build",
|
|
64
|
-
|
|
65
|
-
// v0.48.0. It sits with the deciding lanes rather than the knowledge ones: a
|
|
66
|
-
// /orc-doc document is derived from what you DECIDED, not from the code —
|
|
67
|
-
// which is exactly what separates it from /orc-wiki and /orc-learn.
|
|
68
|
-
"orc-doc": "decide",
|
|
69
|
-
|
|
70
|
-
"orc-brainstorm": "decide",
|
|
71
|
-
"orc-grill": "decide",
|
|
72
|
-
"orc-plan": "decide",
|
|
73
|
-
"orc-route": "decide",
|
|
74
|
-
"orc-explain": "decide",
|
|
75
|
-
"orc-analyze": "decide",
|
|
76
|
-
"orc-analyze-mini": "decide",
|
|
77
|
-
"context-combiner": "decide",
|
|
78
|
-
"orc-poly": "decide",
|
|
79
|
-
|
|
80
|
-
// v0.50.0. It sits with the TOOLS rather than the build lanes: `orc extra` is
|
|
81
|
-
// not a lane you invoke — it is a setting that changes where the lanes you
|
|
82
|
-
// already run send their work.
|
|
83
|
-
"orc-extra": "tools",
|
|
84
|
-
// v0.54.0. It sits beside `orc-extra` for the same reason: recovering a
|
|
85
|
-
// half-finished foreign dispatch is a thing you do at the terminal and in the
|
|
86
|
-
// panel, not a lane you invoke.
|
|
87
|
-
"extra-recovery": "tools",
|
|
88
|
-
// v0.55.0. Same shelf again: the four lanes that pin an agent instead of
|
|
89
|
-
// scoring a task are a routing decision, and routing is where `orc-extra`
|
|
90
|
-
// already lives.
|
|
91
|
-
"extra-slots": "tools",
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"orc-
|
|
97
|
-
|
|
98
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* mockrun-catalog.js — the catalogue of MOCKED RUNS that ship with ORC.
|
|
4
|
+
*
|
|
5
|
+
* A mocked run is a written walkthrough of ONE lane: what you type, what ORC
|
|
6
|
+
* prints back, what lands on disk. Nothing was executed to produce them — they
|
|
7
|
+
* are documentation — and that is exactly why they exist: a new user needs to
|
|
8
|
+
* know what a lane LOOKS like before paying tokens to find out.
|
|
9
|
+
*
|
|
10
|
+
* Two sources, one catalogue:
|
|
11
|
+
* · `mock-run/*.md` the reader-facing walkthroughs
|
|
12
|
+
* · `templates/skills/<skill>/examples/*` the dense annotated examples that
|
|
13
|
+
* already shipped with each skill
|
|
14
|
+
*
|
|
15
|
+
* DERIVED, never hand-listed. A new file in either place appears by itself:
|
|
16
|
+
* the title comes from the file's own `# ` heading, the lane from whether
|
|
17
|
+
* `templates/commands/<name>.md` exists, and the one-line summary from the
|
|
18
|
+
* `> ` blockquote the walkthroughs open with. The only hand-written table here
|
|
19
|
+
* is the READING ORDER (`GROUPS` + `GROUP_OF`) — an order is editorial and
|
|
20
|
+
* cannot be derived from a filename. A file nobody placed still shows up, in
|
|
21
|
+
* the `other` group, because a mocked run that exists and is invisible is the
|
|
22
|
+
* failure mode this module is meant to make impossible.
|
|
23
|
+
*
|
|
24
|
+
* This is package content, not project state: it is the same on every machine
|
|
25
|
+
* and needs no `.claude/`. `orc mock-run` reads it, and `orc ui` serves it the
|
|
26
|
+
* same way it serves the onboarding walkthrough — straight from the module,
|
|
27
|
+
* because spawning a subprocess to read files that ship in this package would
|
|
28
|
+
* be ceremony with a cost. It is NOT `orc mock`, which lists the runnable
|
|
29
|
+
* `mock-examples/<slug>/` folders a green verify left in YOUR project.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const fs = require("fs");
|
|
33
|
+
const path = require("path");
|
|
34
|
+
|
|
35
|
+
const ROOT = path.join(__dirname, "..");
|
|
36
|
+
const WALKTHROUGH_DIR = path.join(ROOT, "mock-run");
|
|
37
|
+
const SKILLS_DIR = path.join(ROOT, "templates", "skills");
|
|
38
|
+
const COMMANDS_DIR = path.join(ROOT, "templates", "commands");
|
|
39
|
+
|
|
40
|
+
// The reading order. Group titles are DATA (the panel prints them as written
|
|
41
|
+
// and never translates them, exactly like a config key or a doctor message).
|
|
42
|
+
const GROUPS = [
|
|
43
|
+
{ id: "start", title: "Start here" },
|
|
44
|
+
{ id: "build", title: "Build a change" },
|
|
45
|
+
{ id: "decide", title: "Decide what to build" },
|
|
46
|
+
{ id: "knowledge", title: "Teach ORC your project" },
|
|
47
|
+
{ id: "check", title: "Check what happened" },
|
|
48
|
+
{ id: "ship", title: "Ship and hand over" },
|
|
49
|
+
{ id: "tools", title: "Panel and terminal" },
|
|
50
|
+
{ id: "other", title: "More" },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// slug (or lane name) → group. Anything absent lands in `other`.
|
|
54
|
+
const GROUP_OF = {
|
|
55
|
+
"the-example-project": "start",
|
|
56
|
+
"a-normal-day": "start",
|
|
57
|
+
|
|
58
|
+
orc: "build",
|
|
59
|
+
"orc-ultra": "build",
|
|
60
|
+
"orc-mini": "build",
|
|
61
|
+
"orc-fast": "build",
|
|
62
|
+
"orc-quick": "build",
|
|
63
|
+
"orc-diy": "build",
|
|
64
|
+
|
|
65
|
+
// v0.48.0. It sits with the deciding lanes rather than the knowledge ones: a
|
|
66
|
+
// /orc-doc document is derived from what you DECIDED, not from the code —
|
|
67
|
+
// which is exactly what separates it from /orc-wiki and /orc-learn.
|
|
68
|
+
"orc-doc": "decide",
|
|
69
|
+
|
|
70
|
+
"orc-brainstorm": "decide",
|
|
71
|
+
"orc-grill": "decide",
|
|
72
|
+
"orc-plan": "decide",
|
|
73
|
+
"orc-route": "decide",
|
|
74
|
+
"orc-explain": "decide",
|
|
75
|
+
"orc-analyze": "decide",
|
|
76
|
+
"orc-analyze-mini": "decide",
|
|
77
|
+
"context-combiner": "decide",
|
|
78
|
+
"orc-poly": "decide",
|
|
79
|
+
|
|
80
|
+
// v0.50.0. It sits with the TOOLS rather than the build lanes: `orc extra` is
|
|
81
|
+
// not a lane you invoke — it is a setting that changes where the lanes you
|
|
82
|
+
// already run send their work.
|
|
83
|
+
"orc-extra": "tools",
|
|
84
|
+
// v0.54.0. It sits beside `orc-extra` for the same reason: recovering a
|
|
85
|
+
// half-finished foreign dispatch is a thing you do at the terminal and in the
|
|
86
|
+
// panel, not a lane you invoke.
|
|
87
|
+
"extra-recovery": "tools",
|
|
88
|
+
// v0.55.0. Same shelf again: the four lanes that pin an agent instead of
|
|
89
|
+
// scoring a task are a routing decision, and routing is where `orc-extra`
|
|
90
|
+
// already lives.
|
|
91
|
+
"extra-slots": "tools",
|
|
92
|
+
|
|
93
|
+
// v1.7.0. It sits with the KNOWLEDGE lanes rather than the tools: a rule set
|
|
94
|
+
// is something you teach ORC about this project, the same shelf as the wiki
|
|
95
|
+
// and the code patterns — and like both of those it is read on every run.
|
|
96
|
+
"orc-rules": "knowledge",
|
|
97
|
+
|
|
98
|
+
"orc-wiki": "knowledge",
|
|
99
|
+
"orc-pattern": "knowledge",
|
|
100
|
+
"orc-learn": "knowledge",
|
|
101
|
+
"orc-claude": "knowledge",
|
|
102
|
+
"orc-export": "knowledge",
|
|
103
|
+
|
|
99
104
|
// v1.5.0. It sits with the CHECKING lanes rather than the build ones:
|
|
100
105
|
// /orc-test makes nothing. It runs the requests, writes down what came back,
|
|
101
106
|
// and hands the machine back — the same shape as /orc-verify beside it.
|
|
102
107
|
"orc-test": "check",
|
|
103
|
-
"orc-verify": "check",
|
|
104
|
-
"orc-challenge": "check",
|
|
105
|
-
// v0.49.1. It sits beside the lane it belongs to, and it is a SEPARATE
|
|
106
|
-
// walkthrough because the council is a decision the reader takes before the
|
|
107
|
-
// first review runs — not a detail of one.
|
|
108
|
-
"orc-challenge-council": "check",
|
|
109
|
-
"orc-pact": "check",
|
|
110
|
-
"orc-boundary": "check",
|
|
111
|
-
"orc-aftermath": "check",
|
|
112
|
-
"orc-budget": "check",
|
|
113
|
-
"orc-retro": "check",
|
|
114
|
-
|
|
115
|
-
"orc-pr-setup": "ship",
|
|
116
|
-
"orc-pr-driver": "ship",
|
|
117
|
-
"orc-handoff": "ship",
|
|
118
|
-
|
|
119
|
-
"orc-ui": "tools",
|
|
120
|
-
"orc-cli": "tools",
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// A few docs lead their group because they are prerequisites for reading the
|
|
124
|
-
// rest, which alphabetical order cannot know. Everything else sorts by title.
|
|
125
|
-
const PINNED_FIRST = ["the-example-project", "orc", "orc-brainstorm", "orc-wiki-example", "orc-verify-example"];
|
|
126
|
-
|
|
127
|
-
// Files in mock-run/ that are not themselves a mocked run.
|
|
128
|
-
const NOT_A_DOC = new Set(["INDEX.md", "README.md"]);
|
|
129
|
-
|
|
130
|
-
function readSafe(p) {
|
|
131
|
-
try {
|
|
132
|
-
return fs.readFileSync(p, "utf8");
|
|
133
|
-
} catch (_) {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function listSafe(dir) {
|
|
139
|
-
try {
|
|
140
|
-
return fs.readdirSync(dir, { withFileTypes: true });
|
|
141
|
-
} catch (_) {
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// `# Mock run — \`/orc-pact\`` → `Mock run — /orc-pact`. Backticks are markup
|
|
147
|
-
// for a reader, not part of the title.
|
|
148
|
-
function titleOf(md, fallback) {
|
|
149
|
-
const m = String(md || "").match(/^#\s+(.+?)\s*$/m);
|
|
150
|
-
return m ? m[1].replace(/`/g, "").trim() : fallback;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// The walkthroughs open with a `> one line` blockquote under the title. The
|
|
154
|
-
// older annotated examples do not, so the first real line stands in.
|
|
155
|
-
function summaryOf(md) {
|
|
156
|
-
const lines = String(md || "").split(/\r?\n/);
|
|
157
|
-
let seenTitle = false;
|
|
158
|
-
for (const raw of lines) {
|
|
159
|
-
const line = raw.trim();
|
|
160
|
-
if (!line) continue;
|
|
161
|
-
if (line.startsWith("# ")) {
|
|
162
|
-
seenTitle = true;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (!seenTitle) continue;
|
|
166
|
-
if (line.startsWith(">")) return clip(line.replace(/^>\s?/, ""));
|
|
167
|
-
if (line.startsWith("#") || line.startsWith("---")) continue;
|
|
168
|
-
return clip(line);
|
|
169
|
-
}
|
|
170
|
-
return "";
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// A summary is read as one line of prose, so the markup that made it readable
|
|
174
|
-
// in the file (`**bold**`, `` `code` ``) is noise here.
|
|
175
|
-
function clip(s, max = 200) {
|
|
176
|
-
const t = String(s).replace(/\*\*/g, "").replace(/`/g, "").trim();
|
|
177
|
-
return t.length > max ? t.slice(0, max - 1).trimEnd() + "…" : t;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// A name is a lane only when the payload really ships that slash command. This
|
|
181
|
-
// is what keeps `/orc-pact` a link and `a-normal-day` plain text, with no list
|
|
182
|
-
// to maintain in two places.
|
|
183
|
-
function laneOf(name) {
|
|
184
|
-
return fs.existsSync(path.join(COMMANDS_DIR, name + ".md")) ? "/" + name : null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function entry(slug, file, kind, name, md) {
|
|
188
|
-
const stat = (() => {
|
|
189
|
-
try {
|
|
190
|
-
return fs.statSync(file);
|
|
191
|
-
} catch (_) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
})();
|
|
195
|
-
const body = md === null ? "" : md;
|
|
196
|
-
return {
|
|
197
|
-
slug,
|
|
198
|
-
title: titleOf(body, slug),
|
|
199
|
-
lane: laneOf(name),
|
|
200
|
-
kind, // "walkthrough" = written for a reader · "annotated" = the dense one
|
|
201
|
-
group: GROUP_OF[name] || GROUP_OF[slug] || "other",
|
|
202
|
-
summary: summaryOf(body),
|
|
203
|
-
path: path.relative(ROOT, file).split(path.sep).join("/"),
|
|
204
|
-
bytes: stat ? stat.size : body.length,
|
|
205
|
-
lines: body ? body.split(/\r?\n/).length : 0,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Every mocked run that ships in this package, in reading order.
|
|
210
|
-
function list() {
|
|
211
|
-
const out = [];
|
|
212
|
-
|
|
213
|
-
for (const e of listSafe(WALKTHROUGH_DIR)) {
|
|
214
|
-
if (!e.isFile() || !e.name.endsWith(".md") || NOT_A_DOC.has(e.name)) continue;
|
|
215
|
-
const name = e.name.replace(/\.md$/, "");
|
|
216
|
-
const file = path.join(WALKTHROUGH_DIR, e.name);
|
|
217
|
-
out.push(entry(name, file, "walkthrough", name, readSafe(file)));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
for (const s of listSafe(SKILLS_DIR)) {
|
|
221
|
-
if (!s.isDirectory()) continue;
|
|
222
|
-
const exDir = path.join(SKILLS_DIR, s.name, "examples");
|
|
223
|
-
for (const f of listSafe(exDir)) {
|
|
224
|
-
if (!f.isFile() || !f.name.endsWith(".md")) continue;
|
|
225
|
-
const file = path.join(exDir, f.name);
|
|
226
|
-
// One example per skill is the shipped shape; a second one keeps its own
|
|
227
|
-
// file name so neither can hide the other.
|
|
228
|
-
const base = f.name.replace(/\.md$/, "");
|
|
229
|
-
const slug = `${s.name}-example`;
|
|
230
|
-
const taken = out.some((d) => d.slug === slug);
|
|
231
|
-
out.push(entry(taken ? `${s.name}-${base}` : slug, file, "annotated", s.name, readSafe(file)));
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const rank = new Map(GROUPS.map((g, i) => [g.id, i]));
|
|
236
|
-
out.sort((a, b) => {
|
|
237
|
-
const g = (rank.get(a.group) ?? 99) - (rank.get(b.group) ?? 99);
|
|
238
|
-
if (g) return g;
|
|
239
|
-
// Inside a group: pinned docs first, then the reader-facing walkthrough
|
|
240
|
-
// before the dense annotated example, then by title.
|
|
241
|
-
const p = (d) => {
|
|
242
|
-
const i = PINNED_FIRST.indexOf(d.slug);
|
|
243
|
-
return i === -1 ? PINNED_FIRST.length : i;
|
|
244
|
-
};
|
|
245
|
-
const pin = p(a) - p(b);
|
|
246
|
-
if (pin) return pin;
|
|
247
|
-
if (a.kind !== b.kind) return a.kind === "walkthrough" ? -1 : 1;
|
|
248
|
-
return a.title.localeCompare(b.title);
|
|
249
|
-
});
|
|
250
|
-
return out;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// The catalogue, grouped, with the counts a header can print without counting.
|
|
254
|
-
function catalogue() {
|
|
255
|
-
const docs = list();
|
|
256
|
-
const groups = GROUPS.map((g) => ({
|
|
257
|
-
...g,
|
|
258
|
-
docs: docs.filter((d) => d.group === g.id),
|
|
259
|
-
})).filter((g) => g.docs.length);
|
|
260
|
-
return { root: path.relative(ROOT, WALKTHROUGH_DIR) || "mock-run", total: docs.length, groups, docs };
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// One doc, with its body. Returns null when the slug is unknown — the caller
|
|
264
|
-
// decides what a miss means (exit 1 in the CLI, a 404 in the panel).
|
|
265
|
-
function get(slug) {
|
|
266
|
-
const hit = list().find((d) => d.slug === slug);
|
|
267
|
-
if (!hit) return null;
|
|
268
|
-
return { ...hit, body: readSafe(path.join(ROOT, hit.path)) || "" };
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
module.exports = { GROUPS, catalogue, list, get };
|
|
108
|
+
"orc-verify": "check",
|
|
109
|
+
"orc-challenge": "check",
|
|
110
|
+
// v0.49.1. It sits beside the lane it belongs to, and it is a SEPARATE
|
|
111
|
+
// walkthrough because the council is a decision the reader takes before the
|
|
112
|
+
// first review runs — not a detail of one.
|
|
113
|
+
"orc-challenge-council": "check",
|
|
114
|
+
"orc-pact": "check",
|
|
115
|
+
"orc-boundary": "check",
|
|
116
|
+
"orc-aftermath": "check",
|
|
117
|
+
"orc-budget": "check",
|
|
118
|
+
"orc-retro": "check",
|
|
119
|
+
|
|
120
|
+
"orc-pr-setup": "ship",
|
|
121
|
+
"orc-pr-driver": "ship",
|
|
122
|
+
"orc-handoff": "ship",
|
|
123
|
+
|
|
124
|
+
"orc-ui": "tools",
|
|
125
|
+
"orc-cli": "tools",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// A few docs lead their group because they are prerequisites for reading the
|
|
129
|
+
// rest, which alphabetical order cannot know. Everything else sorts by title.
|
|
130
|
+
const PINNED_FIRST = ["the-example-project", "orc", "orc-brainstorm", "orc-wiki-example", "orc-verify-example"];
|
|
131
|
+
|
|
132
|
+
// Files in mock-run/ that are not themselves a mocked run.
|
|
133
|
+
const NOT_A_DOC = new Set(["INDEX.md", "README.md"]);
|
|
134
|
+
|
|
135
|
+
function readSafe(p) {
|
|
136
|
+
try {
|
|
137
|
+
return fs.readFileSync(p, "utf8");
|
|
138
|
+
} catch (_) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function listSafe(dir) {
|
|
144
|
+
try {
|
|
145
|
+
return fs.readdirSync(dir, { withFileTypes: true });
|
|
146
|
+
} catch (_) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// `# Mock run — \`/orc-pact\`` → `Mock run — /orc-pact`. Backticks are markup
|
|
152
|
+
// for a reader, not part of the title.
|
|
153
|
+
function titleOf(md, fallback) {
|
|
154
|
+
const m = String(md || "").match(/^#\s+(.+?)\s*$/m);
|
|
155
|
+
return m ? m[1].replace(/`/g, "").trim() : fallback;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// The walkthroughs open with a `> one line` blockquote under the title. The
|
|
159
|
+
// older annotated examples do not, so the first real line stands in.
|
|
160
|
+
function summaryOf(md) {
|
|
161
|
+
const lines = String(md || "").split(/\r?\n/);
|
|
162
|
+
let seenTitle = false;
|
|
163
|
+
for (const raw of lines) {
|
|
164
|
+
const line = raw.trim();
|
|
165
|
+
if (!line) continue;
|
|
166
|
+
if (line.startsWith("# ")) {
|
|
167
|
+
seenTitle = true;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (!seenTitle) continue;
|
|
171
|
+
if (line.startsWith(">")) return clip(line.replace(/^>\s?/, ""));
|
|
172
|
+
if (line.startsWith("#") || line.startsWith("---")) continue;
|
|
173
|
+
return clip(line);
|
|
174
|
+
}
|
|
175
|
+
return "";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// A summary is read as one line of prose, so the markup that made it readable
|
|
179
|
+
// in the file (`**bold**`, `` `code` ``) is noise here.
|
|
180
|
+
function clip(s, max = 200) {
|
|
181
|
+
const t = String(s).replace(/\*\*/g, "").replace(/`/g, "").trim();
|
|
182
|
+
return t.length > max ? t.slice(0, max - 1).trimEnd() + "…" : t;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// A name is a lane only when the payload really ships that slash command. This
|
|
186
|
+
// is what keeps `/orc-pact` a link and `a-normal-day` plain text, with no list
|
|
187
|
+
// to maintain in two places.
|
|
188
|
+
function laneOf(name) {
|
|
189
|
+
return fs.existsSync(path.join(COMMANDS_DIR, name + ".md")) ? "/" + name : null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function entry(slug, file, kind, name, md) {
|
|
193
|
+
const stat = (() => {
|
|
194
|
+
try {
|
|
195
|
+
return fs.statSync(file);
|
|
196
|
+
} catch (_) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
})();
|
|
200
|
+
const body = md === null ? "" : md;
|
|
201
|
+
return {
|
|
202
|
+
slug,
|
|
203
|
+
title: titleOf(body, slug),
|
|
204
|
+
lane: laneOf(name),
|
|
205
|
+
kind, // "walkthrough" = written for a reader · "annotated" = the dense one
|
|
206
|
+
group: GROUP_OF[name] || GROUP_OF[slug] || "other",
|
|
207
|
+
summary: summaryOf(body),
|
|
208
|
+
path: path.relative(ROOT, file).split(path.sep).join("/"),
|
|
209
|
+
bytes: stat ? stat.size : body.length,
|
|
210
|
+
lines: body ? body.split(/\r?\n/).length : 0,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Every mocked run that ships in this package, in reading order.
|
|
215
|
+
function list() {
|
|
216
|
+
const out = [];
|
|
217
|
+
|
|
218
|
+
for (const e of listSafe(WALKTHROUGH_DIR)) {
|
|
219
|
+
if (!e.isFile() || !e.name.endsWith(".md") || NOT_A_DOC.has(e.name)) continue;
|
|
220
|
+
const name = e.name.replace(/\.md$/, "");
|
|
221
|
+
const file = path.join(WALKTHROUGH_DIR, e.name);
|
|
222
|
+
out.push(entry(name, file, "walkthrough", name, readSafe(file)));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const s of listSafe(SKILLS_DIR)) {
|
|
226
|
+
if (!s.isDirectory()) continue;
|
|
227
|
+
const exDir = path.join(SKILLS_DIR, s.name, "examples");
|
|
228
|
+
for (const f of listSafe(exDir)) {
|
|
229
|
+
if (!f.isFile() || !f.name.endsWith(".md")) continue;
|
|
230
|
+
const file = path.join(exDir, f.name);
|
|
231
|
+
// One example per skill is the shipped shape; a second one keeps its own
|
|
232
|
+
// file name so neither can hide the other.
|
|
233
|
+
const base = f.name.replace(/\.md$/, "");
|
|
234
|
+
const slug = `${s.name}-example`;
|
|
235
|
+
const taken = out.some((d) => d.slug === slug);
|
|
236
|
+
out.push(entry(taken ? `${s.name}-${base}` : slug, file, "annotated", s.name, readSafe(file)));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const rank = new Map(GROUPS.map((g, i) => [g.id, i]));
|
|
241
|
+
out.sort((a, b) => {
|
|
242
|
+
const g = (rank.get(a.group) ?? 99) - (rank.get(b.group) ?? 99);
|
|
243
|
+
if (g) return g;
|
|
244
|
+
// Inside a group: pinned docs first, then the reader-facing walkthrough
|
|
245
|
+
// before the dense annotated example, then by title.
|
|
246
|
+
const p = (d) => {
|
|
247
|
+
const i = PINNED_FIRST.indexOf(d.slug);
|
|
248
|
+
return i === -1 ? PINNED_FIRST.length : i;
|
|
249
|
+
};
|
|
250
|
+
const pin = p(a) - p(b);
|
|
251
|
+
if (pin) return pin;
|
|
252
|
+
if (a.kind !== b.kind) return a.kind === "walkthrough" ? -1 : 1;
|
|
253
|
+
return a.title.localeCompare(b.title);
|
|
254
|
+
});
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// The catalogue, grouped, with the counts a header can print without counting.
|
|
259
|
+
function catalogue() {
|
|
260
|
+
const docs = list();
|
|
261
|
+
const groups = GROUPS.map((g) => ({
|
|
262
|
+
...g,
|
|
263
|
+
docs: docs.filter((d) => d.group === g.id),
|
|
264
|
+
})).filter((g) => g.docs.length);
|
|
265
|
+
return { root: path.relative(ROOT, WALKTHROUGH_DIR) || "mock-run", total: docs.length, groups, docs };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// One doc, with its body. Returns null when the slug is unknown — the caller
|
|
269
|
+
// decides what a miss means (exit 1 in the CLI, a 404 in the panel).
|
|
270
|
+
function get(slug) {
|
|
271
|
+
const hit = list().find((d) => d.slug === slug);
|
|
272
|
+
if (!hit) return null;
|
|
273
|
+
return { ...hit, body: readSafe(path.join(ROOT, hit.path)) || "" };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = { GROUPS, catalogue, list, get };
|