@skill-map/cli 0.60.4 → 0.61.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.
Files changed (87) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/tutorial/sm-tutorial/SKILL.md +161 -266
  3. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/en/agents-hub.md +2 -0
  4. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/es/agents-hub.md +2 -0
  5. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/content-editor-style/en/content-editor-style.md +1 -0
  6. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/content-editor-style/es/content-editor-style.md +1 -0
  7. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/en/todo-bullet-agent.md +1 -0
  8. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/en/todo-bullet-command.md +1 -0
  9. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/en/todo-bullet-guideline.md +1 -0
  10. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/en/todo-bullet-guideline2.md +1 -0
  11. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/en/todo-bullet-skill.md +1 -0
  12. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/es/todo-bullet-agent.md +1 -0
  13. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/es/todo-bullet-command.md +1 -0
  14. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/es/todo-bullet-guideline.md +1 -0
  15. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/es/todo-bullet-guideline2.md +1 -0
  16. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/es/todo-bullet-skill.md +1 -0
  17. package/dist/cli/tutorial/sm-tutorial/fixtures-data/manifest.json +85 -0
  18. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/cli-external/en/link-validation/hijoA/note-with-external-link.md +10 -0
  19. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/cli-external/en/link-validation/hijoB/spec.md +11 -0
  20. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/cli-external/es/link-validation/hijoA/note-with-external-link.md +10 -0
  21. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/cli-external/es/link-validation/hijoB/spec.md +11 -0
  22. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/en/__PROVIDER__/commands/publish.md +15 -0
  23. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/en/__PROVIDER__/skills/check-links/SKILL.md +16 -0
  24. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/es/__PROVIDER__/commands/publish.md +16 -0
  25. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/es/__PROVIDER__/skills/check-links/SKILL.md +17 -0
  26. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/en/__PROVIDER__/agents/master-agent.md +14 -0
  27. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/en/__PROVIDER__/skills/master-skill/SKILL.md +18 -0
  28. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/en/notes/ideas.md +11 -0
  29. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/es/__PROVIDER__/agents/master-agent.md +15 -0
  30. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/es/__PROVIDER__/skills/master-skill/SKILL.md +18 -0
  31. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/es/notes/ideas.md +11 -0
  32. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/AGENTS.md +6 -0
  33. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/__PROVIDER__/agents/content-editor.md +21 -0
  34. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/docs/DEPLOY.md +11 -0
  35. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/docs/STYLE.md +20 -0
  36. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/public/index.html +5 -0
  37. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/AGENTS.md +7 -0
  38. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/__PROVIDER__/agents/content-editor.md +21 -0
  39. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/docs/DEPLOY.md +12 -0
  40. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/docs/STYLE.md +21 -0
  41. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/public/index.html +5 -0
  42. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/shared/CLAUDE.md +1 -0
  43. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/shared/package.json +6 -0
  44. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/shared/server.js +11 -0
  45. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/__PROVIDER__/agents/demo-agent.md +16 -0
  46. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/__PROVIDER__/commands/demo-command.md +11 -0
  47. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/__PROVIDER__/skills/demo-skill/SKILL.md +16 -0
  48. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/notes/demo-guideline.md +16 -0
  49. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/notes/demo-guideline2.md +12 -0
  50. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/notes/private-credentials.md +11 -0
  51. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/en/notes/todo.md +9 -0
  52. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/__PROVIDER__/agents/demo-agent.md +16 -0
  53. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/__PROVIDER__/commands/demo-command.md +11 -0
  54. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/__PROVIDER__/skills/demo-skill/SKILL.md +16 -0
  55. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/notes/demo-guideline.md +16 -0
  56. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/notes/demo-guideline2.md +13 -0
  57. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/notes/private-credentials.md +11 -0
  58. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/es/notes/todo.md +9 -0
  59. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/universal/en/findings.md +10 -0
  60. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/universal/es/findings.md +10 -0
  61. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/universal/shared/.skillmapignore +28 -0
  62. package/dist/cli/tutorial/sm-tutorial/references/_core.md +60 -40
  63. package/dist/cli/tutorial/sm-tutorial/references/_manifest.json +304 -0
  64. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +4 -5
  65. package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +93 -271
  66. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +1 -2
  67. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +7 -30
  68. package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +21 -40
  69. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +107 -194
  70. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +58 -143
  71. package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +2 -3
  72. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +22 -69
  73. package/dist/cli/tutorial/sm-tutorial/scripts/fixtures.js +238 -0
  74. package/dist/cli/tutorial/sm-tutorial/scripts/lib/args.js +29 -0
  75. package/dist/cli/tutorial/sm-tutorial/scripts/lib/fixtures-manifest.js +32 -0
  76. package/dist/cli/tutorial/sm-tutorial/scripts/lib/io.js +37 -0
  77. package/dist/cli/tutorial/sm-tutorial/scripts/lib/manifest.js +24 -0
  78. package/dist/cli/tutorial/sm-tutorial/scripts/lib/paths.js +68 -0
  79. package/dist/cli/tutorial/sm-tutorial/scripts/state.js +262 -0
  80. package/dist/cli.js +7 -8
  81. package/dist/index.js +6 -4
  82. package/dist/kernel/index.js +6 -4
  83. package/dist/ui/chunk-DG3EAGXB.js +3 -0
  84. package/dist/ui/index.html +1 -1
  85. package/dist/ui/{main-VJJVD754.js → main-32BO6UKY.js} +1 -1
  86. package/package.json +5 -3
  87. package/dist/ui/chunk-DDDCORVR.js +0 -3
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fixtures.js, the sm-tutorial fixture engine.
4
+ *
5
+ * Lays / edits / seeds / clears the demo, portfolio, harness, master,
6
+ * and cli-external fixtures from `fixtures-data/` instead of having the
7
+ * agent reproduce file content verbatim. Zero-dep, Node 24+, ESM.
8
+ * Backstage machinery (same class as `Write`), NOT a teaching `sm` verb.
9
+ *
10
+ * Content lives under `fixtures-data/sets/<set>/{shared,<lang>}/` with
11
+ * the provider dir as a literal `__PROVIDER__` path segment; only the
12
+ * PATH is rewritten per provider (content is verbatim, relative links
13
+ * are depth-correct in the claude layout). Kind is derived from the
14
+ * path (paths.js#kindForPath); files whose kind the provider does not
15
+ * claim are skipped.
16
+ *
17
+ * Verbs (all but `cat` print one JSON line to stdout):
18
+ * lay <set> [--provider p] [--lang l]
19
+ * edit <id> [--provider p] [--lang l]
20
+ * seed <snap> [--provider p] [--lang l]
21
+ * clear <footprint> [--provider p]
22
+ * cat <set> --file <relpath> [--provider p] [--lang l] (raw content to stdout)
23
+ *
24
+ * `clear` removes a named footprint (part-entry resets); the full
25
+ * start-over wipe is `state.js wipe` (cwd-guarded + confirmation-gated).
26
+ */
27
+
28
+ import { join, dirname, relative } from 'node:path';
29
+ import {
30
+ readFileSync, writeFileSync, mkdirSync, rmSync, rmdirSync, readdirSync, statSync, existsSync,
31
+ } from 'node:fs';
32
+
33
+ import { parseArgs } from './lib/args.js';
34
+ import { emit, succeed, die } from './lib/io.js';
35
+ import { loadFixturesManifest, fixturesDir, resolveFootprint } from './lib/fixtures-manifest.js';
36
+ import {
37
+ providerDir, kindsFor, resolveTargetPath, kindForPath, PROVIDER_TOKEN,
38
+ } from './lib/paths.js';
39
+
40
+ function opts(args) {
41
+ return { provider: args.flags.provider ?? 'claude', lang: args.flags.lang ?? 'en' };
42
+ }
43
+
44
+ function pdirAndKinds(o) {
45
+ return { pdir: providerDir(o.provider), kinds: kindsFor(o.provider) };
46
+ }
47
+
48
+ /** Recursively list files under `root`, relative to `root`, sorted. */
49
+ function walk(root) {
50
+ const out = [];
51
+ if (!existsSync(root)) return out;
52
+ const stack = [root];
53
+ while (stack.length) {
54
+ const cur = stack.pop();
55
+ for (const entry of readdirSync(cur)) {
56
+ const full = join(cur, entry);
57
+ if (statSync(full).isDirectory()) stack.push(full);
58
+ else out.push(relative(root, full));
59
+ }
60
+ }
61
+ return out.sort();
62
+ }
63
+
64
+ function writeFileEnsuring(abs, content) {
65
+ mkdirSync(dirname(abs), { recursive: true });
66
+ writeFileSync(abs, content);
67
+ }
68
+
69
+ /**
70
+ * Lay one set's files for the given lang + provider. `only` (a Set of
71
+ * token-form relpaths) restricts to those files, for the prologue's
72
+ * progressive reveal where each chapter lands its own nodes.
73
+ */
74
+ function laySet(manifest, set, o, only = null) {
75
+ const { kinds } = pdirAndKinds(o);
76
+ if (!manifest.sets.includes(set)) die('unknown-set', `set '${set}' is not in the manifest.`);
77
+ const base = join(fixturesDir(), 'sets', set);
78
+ const langDir = existsSync(join(base, o.lang)) ? o.lang : (manifest.defaultLang ?? 'en');
79
+ const laid = [];
80
+ const skipped = [];
81
+ // Lang-invariant `shared/` tier first, then the language tier.
82
+ for (const tier of ['shared', langDir]) {
83
+ const tierRoot = join(base, tier);
84
+ for (const rel of walk(tierRoot)) {
85
+ // `rel` is the token-form target path (e.g. __PROVIDER__/agents/x.md).
86
+ if (only && !only.has(rel)) continue;
87
+ const kind = kindForPath(rel);
88
+ if (!kinds.has(kind)) { skipped.push({ path: rel, kind }); continue; }
89
+ const target = resolveTargetPath(rel, o.provider);
90
+ writeFileEnsuring(join(process.cwd(), target), readFileSync(join(tierRoot, rel)));
91
+ laid.push(target);
92
+ }
93
+ }
94
+ return { laid, skipped };
95
+ }
96
+
97
+ const nodeCount = (paths) => paths.filter((p) => p.endsWith('.md')).length;
98
+
99
+ /** Resolve a fragment file path, falling back to the default language. */
100
+ function fragmentPath(manifest, id, lang, file) {
101
+ const langTry = join(fixturesDir(), 'edits', id, lang, file);
102
+ return existsSync(langTry) ? langTry : join(fixturesDir(), 'edits', id, manifest.defaultLang ?? 'en', file);
103
+ }
104
+
105
+ /** Apply one manifest edit (append fragments) honoring requiresKind. */
106
+ function applyEdit(manifest, id, o) {
107
+ const { kinds } = pdirAndKinds(o);
108
+ const def = manifest.edits?.[id];
109
+ if (!def) die('unknown-edit', `edit '${id}' is not in the manifest.`);
110
+ const target = resolveTargetPath(def.target, o.provider);
111
+ // Skip the whole edit if the target's own kind is unsupported (e.g. a
112
+ // content-editor agent does not exist on agent-skills).
113
+ if (!kinds.has(kindForPath(def.target))) return { target, appended: [], skipped: true };
114
+ const targetAbs = join(process.cwd(), target);
115
+ if (!existsSync(targetAbs)) die('edit-target-missing', `edit '${id}' target not found: ${target}`);
116
+
117
+ const fragments = (def.fragments ?? []).filter((f) => !f.requiresKind || kinds.has(f.requiresKind));
118
+ if (fragments.length === 0) return { target, appended: [] };
119
+
120
+ let content = readFileSync(targetAbs, 'utf8');
121
+ if (!content.endsWith('\n')) content += '\n';
122
+ if (def.prefix) content += def.prefix;
123
+ const appended = [];
124
+ for (const frag of fragments) {
125
+ content += readFileSync(fragmentPath(manifest, id, o.lang, frag.file), 'utf8');
126
+ appended.push(frag.file);
127
+ }
128
+ writeFileSync(targetAbs, content);
129
+ return { target, appended };
130
+ }
131
+
132
+ const VERBS = {
133
+ lay(args) {
134
+ const set = args.positional[0];
135
+ if (!set) die('bad-args', 'usage: lay <set> [--only a,b] [--provider p] [--lang l]');
136
+ const o = opts(args);
137
+ const only = args.flags.only ? new Set(String(args.flags.only).split(',')) : null;
138
+ const manifest = loadFixturesManifest();
139
+ const { laid, skipped } = laySet(manifest, set, o, only);
140
+ succeed({ laid, skipped, nodeCount: nodeCount(laid), needsProvision: !existsSync(join(process.cwd(), '.skill-map')) });
141
+ },
142
+
143
+ edit(args) {
144
+ const id = args.positional[0];
145
+ if (!id) die('bad-args', 'usage: edit <edit-id> [--provider p] [--lang l]');
146
+ const manifest = loadFixturesManifest();
147
+ succeed(applyEdit(manifest, id, opts(args)));
148
+ },
149
+
150
+ seed(args) {
151
+ const snap = args.positional[0];
152
+ if (!snap) die('bad-args', 'usage: seed <snapshot> [--provider p] [--lang l]');
153
+ const o = opts(args);
154
+ const manifest = loadFixturesManifest();
155
+ const def = manifest.seeds?.[snap];
156
+ if (!def) die('unknown-seed', `seed '${snap}' is not in the manifest.`);
157
+ const laid = [];
158
+ const skipped = [];
159
+ for (const set of def.lay ?? []) {
160
+ const r = laySet(manifest, set, o);
161
+ laid.push(...r.laid);
162
+ skipped.push(...r.skipped);
163
+ }
164
+ const edits = [];
165
+ for (const id of def.edits ?? []) edits.push(applyEdit(manifest, id, o));
166
+ const dropped = [];
167
+ for (const rel of def.drop ?? []) {
168
+ const resolved = resolveTargetPath(rel, o.provider);
169
+ const abs = join(process.cwd(), resolved);
170
+ if (existsSync(abs)) { rmSync(abs, { recursive: true, force: true }); dropped.push(resolved); }
171
+ }
172
+ const present = laid.filter((p) => !dropped.includes(p));
173
+ succeed({
174
+ laid, skipped, edits, dropped,
175
+ nodeCount: nodeCount(present),
176
+ needsProvision: !existsSync(join(process.cwd(), '.skill-map')),
177
+ });
178
+ },
179
+
180
+ clear(args) {
181
+ const name = args.positional[0];
182
+ if (!name) die('bad-args', 'usage: clear <footprint> [--provider p]');
183
+ const manifest = loadFixturesManifest();
184
+ if (!manifest.footprints?.[name]) die('unknown-footprint', `footprint '${name}' is not in the manifest.`);
185
+ const o = opts(args);
186
+ const { pdir } = pdirAndKinds(o);
187
+ const deleted = [];
188
+ for (const rel of resolveFootprint(manifest, name, o.provider)) {
189
+ const abs = join(process.cwd(), rel);
190
+ if (existsSync(abs)) { rmSync(abs, { recursive: true, force: true }); deleted.push(rel); }
191
+ }
192
+ rmdirEmptyParents(pdir);
193
+ succeed({ deleted });
194
+ },
195
+
196
+ cat(args) {
197
+ const set = args.positional[0];
198
+ const file = args.flags.file;
199
+ if (!set || typeof file !== 'string') {
200
+ emit({ ok: false, code: 'bad-args', error: 'usage: cat <set> --file <relpath> [--provider p] [--lang l]' });
201
+ process.exit(1);
202
+ }
203
+ const o = opts(args);
204
+ const { pdir } = pdirAndKinds(o);
205
+ const manifest = loadFixturesManifest();
206
+ const base = join(fixturesDir(), 'sets', set);
207
+ const langDir = existsSync(join(base, o.lang)) ? o.lang : (manifest.defaultLang ?? 'en');
208
+ // Accept a resolved path (`.claude/...`) or token form; normalise to token.
209
+ const tokenForm = file.startsWith(`${pdir}/`) ? PROVIDER_TOKEN + file.slice(pdir.length) : file;
210
+ const found = [join(base, langDir, tokenForm), join(base, 'shared', tokenForm)].find((c) => existsSync(c));
211
+ if (!found) {
212
+ emit({ ok: false, code: 'not-found', error: `file '${file}' not found in set '${set}'.` });
213
+ process.exit(1);
214
+ }
215
+ process.stdout.write(readFileSync(found, 'utf8'));
216
+ process.exit(0);
217
+ },
218
+ };
219
+
220
+ function rmdirEmptyParents(pdir) {
221
+ const candidates = ['notes', 'docs', 'public', `${pdir}/agents`, `${pdir}/skills`, `${pdir}/commands`, pdir];
222
+ for (const rel of candidates) {
223
+ try { rmdirSync(join(process.cwd(), rel)); } catch { /* not empty or missing */ }
224
+ }
225
+ }
226
+
227
+ function main() {
228
+ const [verb, ...rest] = process.argv.slice(2);
229
+ const handler = VERBS[verb];
230
+ if (!handler) {
231
+ die('unknown-verb', `unknown verb '${verb ?? ''}'; expected one of ${Object.keys(VERBS).join(', ')}`);
232
+ }
233
+ handler(parseArgs(rest));
234
+ }
235
+
236
+ main();
237
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7deeaba5-0c50-5d6e-93cc-ad31c6b5a37c")}catch(e){}}();
238
+ //# debugId=7deeaba5-0c50-5d6e-93cc-ad31c6b5a37c
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Minimal zero-dep argv parser shared by the tutorial scripts. Splits
3
+ * `--key value` pairs and bare `--flag` booleans from positionals.
4
+ * No external CLI framework (the script ships to a tester cwd with no
5
+ * node_modules).
6
+ */
7
+
8
+ export function parseArgs(argv) {
9
+ const positional = [];
10
+ const flags = {};
11
+ for (let i = 0; i < argv.length; i++) {
12
+ const a = argv[i];
13
+ if (a.startsWith('--')) {
14
+ const key = a.slice(2);
15
+ const next = argv[i + 1];
16
+ if (next === undefined || next.startsWith('--')) {
17
+ flags[key] = true;
18
+ } else {
19
+ flags[key] = next;
20
+ i++;
21
+ }
22
+ } else {
23
+ positional.push(a);
24
+ }
25
+ }
26
+ return { positional, flags };
27
+ }
28
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="11379635-8a15-5ac2-a59e-af05707a526b")}catch(e){}}();
29
+ //# debugId=11379635-8a15-5ac2-a59e-af05707a526b
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Reads `fixtures-data/manifest.json`, the index for the fixture
3
+ * engine (sets, footprints, edits, seeds). Shared by `fixtures.js`
4
+ * (lay / edit / seed / clear) and `state.js` (wipe reads footprints,
5
+ * so the per-fixture on-disk reach lives in ONE place).
6
+ */
7
+
8
+ import { dirname, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { readJson } from './io.js';
11
+ import { resolveTargetPath } from './paths.js';
12
+
13
+ const LIB_DIR = dirname(fileURLToPath(import.meta.url));
14
+ // lib/ -> scripts/ -> sm-tutorial/ -> fixtures-data/
15
+ const FIXTURES_DIR = resolve(LIB_DIR, '..', '..', 'fixtures-data');
16
+ const MANIFEST = resolve(FIXTURES_DIR, 'manifest.json');
17
+
18
+ export function fixturesDir() {
19
+ return FIXTURES_DIR;
20
+ }
21
+
22
+ export function loadFixturesManifest() {
23
+ return readJson(MANIFEST);
24
+ }
25
+
26
+ /** Footprint paths for a named fixture, resolved for the provider. */
27
+ export function resolveFootprint(manifest, name, provider) {
28
+ const fp = manifest.footprints?.[name] ?? [];
29
+ return fp.map((p) => resolveTargetPath(p, provider));
30
+ }
31
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="22500f53-280a-5e92-b22b-2608486d3d0c")}catch(e){}}();
32
+ //# debugId=22500f53-280a-5e92-b22b-2608486d3d0c
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Zero-dep IO + JSON-envelope helpers shared by the tutorial scripts.
3
+ * Every verb prints ONE JSON line to stdout: `{ ok: true, ... }` with
4
+ * exit 0, or `{ ok: false, code, error }` with a non-zero exit. The
5
+ * orchestrating agent parses stdout; it never hand-edits state.
6
+ */
7
+
8
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
9
+
10
+ export function exists(p) {
11
+ return existsSync(p);
12
+ }
13
+
14
+ export function readJson(p) {
15
+ return JSON.parse(readFileSync(p, 'utf8'));
16
+ }
17
+
18
+ /** Write pretty JSON with a trailing LF newline (project line-ending rule). */
19
+ export function writeJson(p, obj) {
20
+ writeFileSync(p, JSON.stringify(obj, null, 2) + '\n');
21
+ }
22
+
23
+ export function emit(obj) {
24
+ process.stdout.write(JSON.stringify(obj) + '\n');
25
+ }
26
+
27
+ export function succeed(obj) {
28
+ emit({ ok: true, ...obj });
29
+ process.exit(0);
30
+ }
31
+
32
+ export function die(code, error) {
33
+ emit({ ok: false, code, error });
34
+ process.exit(1);
35
+ }
36
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6f5406ec-7563-5466-a639-eee006c56913")}catch(e){}}();
37
+ //# debugId=6f5406ec-7563-5466-a639-eee006c56913
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Reads the generated book ToC sidecar (`references/_manifest.json`)
3
+ * that the repo codegen emits from `_manifest.yml`. Zero-dep: plain
4
+ * `JSON.parse`. The `.yml` is never parsed at runtime (its bespoke
5
+ * chapter shorthand is not standard YAML).
6
+ */
7
+
8
+ import { dirname, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { readJson } from './io.js';
11
+
12
+ const LIB_DIR = dirname(fileURLToPath(import.meta.url));
13
+ // lib/ -> scripts/ -> sm-tutorial/ -> references/_manifest.json
14
+ const MANIFEST = resolve(LIB_DIR, '..', '..', 'references', '_manifest.json');
15
+
16
+ export function loadManifest() {
17
+ return readJson(MANIFEST);
18
+ }
19
+
20
+ export function findPart(manifest, id) {
21
+ return manifest.parts.find((p) => p.id === id) ?? null;
22
+ }
23
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b51b0ac6-99f1-5d52-8cac-40f122545c4a")}catch(e){}}();
24
+ //# debugId=b51b0ac6-99f1-5d52-8cac-40f122545c4a
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Provider resolution shared by the tutorial scripts. The `__PROVIDER__`
3
+ * path token resolves to the on-disk base dir, and each provider claims
4
+ * a closed set of node kinds (see `_core.md` §Provider detection).
5
+ */
6
+
7
+ export const PROVIDER_TOKEN = '__PROVIDER__';
8
+
9
+ export function providerDir(provider) {
10
+ // agent-skills and Antigravity share the open `.agents/skills/` layout.
11
+ return provider === 'agent-skills' || provider === 'antigravity'
12
+ ? '.agents/skills'
13
+ : '.claude';
14
+ }
15
+
16
+ export const PROVIDER_KINDS = {
17
+ claude: new Set(['agent', 'command', 'skill', 'markdown']),
18
+ 'agent-skills': new Set(['skill', 'markdown']),
19
+ antigravity: new Set(['skill', 'markdown']),
20
+ };
21
+
22
+ export function kindsFor(provider) {
23
+ return PROVIDER_KINDS[provider] ?? PROVIDER_KINDS.claude;
24
+ }
25
+
26
+ /**
27
+ * Per-provider kind directories. The token path is always written in
28
+ * the claude shape (`__PROVIDER__/skills/<name>/...`); resolving it is
29
+ * NOT a flat string swap, because agent-skills puts skills directly
30
+ * under `.agents/skills/<name>/` (no intermediate `skills/` segment).
31
+ */
32
+ const KIND_DIRS = {
33
+ claude: { agents: '.claude/agents', commands: '.claude/commands', skills: '.claude/skills' },
34
+ 'agent-skills': { skills: '.agents/skills' },
35
+ antigravity: { skills: '.agents/skills' },
36
+ };
37
+
38
+ /**
39
+ * Resolve a token-form relative path (`__PROVIDER__/skills/x/SKILL.md`,
40
+ * `notes/todo.md`) to its real on-disk path for the provider. Paths
41
+ * without the token are returned unchanged. Unsupported kinds fall back
42
+ * to a flat join, callers skip them before laying, so the fallback only
43
+ * matters for footprint deletes (a no-op on a path never written).
44
+ */
45
+ export function resolveTargetPath(tokenRel, provider) {
46
+ if (!tokenRel.startsWith(`${PROVIDER_TOKEN}/`)) return tokenRel;
47
+ const rest = tokenRel.slice(PROVIDER_TOKEN.length + 1);
48
+ const slash = rest.indexOf('/');
49
+ const kindSeg = slash >= 0 ? rest.slice(0, slash) : rest;
50
+ const tail = slash >= 0 ? rest.slice(slash + 1) : '';
51
+ const base = (KIND_DIRS[provider] ?? KIND_DIRS.claude)[kindSeg];
52
+ if (!base) return `${providerDir(provider)}/${rest}`;
53
+ return tail ? `${base}/${tail}` : base;
54
+ }
55
+
56
+ /**
57
+ * Derive a node kind from a token-form relative path. Matches how
58
+ * skill-map's providers classify by directory: agents / commands /
59
+ * skills folders under the provider dir, everything else markdown.
60
+ */
61
+ export function kindForPath(tokenRelPath) {
62
+ if (tokenRelPath.startsWith(`${PROVIDER_TOKEN}/agents/`)) return 'agent';
63
+ if (tokenRelPath.startsWith(`${PROVIDER_TOKEN}/commands/`)) return 'command';
64
+ if (tokenRelPath.startsWith(`${PROVIDER_TOKEN}/skills/`)) return 'skill';
65
+ return 'markdown';
66
+ }
67
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="af99c74c-ee9a-5bf3-8daf-f0b9320a3e61")}catch(e){}}();
68
+ //# debugId=af99c74c-ee9a-5bf3-8daf-f0b9320a3e61