@skill-map/cli 0.67.0 → 0.68.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 (105) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +30 -23
  2. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/agent-skills/en/agents-hub.md +2 -0
  3. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/agent-skills/es/agents-hub.md +2 -0
  4. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/content-editor-style/providers/agent-skills/en/content-editor-style.md +1 -0
  5. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/content-editor-style/providers/agent-skills/es/content-editor-style.md +1 -0
  6. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/en/todo-bullet-guideline.md +1 -0
  7. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/en/todo-bullet-guideline2.md +1 -0
  8. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/en/todo-bullet-skill.md +1 -0
  9. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/es/todo-bullet-guideline.md +1 -0
  10. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/es/todo-bullet-guideline2.md +1 -0
  11. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/agent-skills/es/todo-bullet-skill.md +1 -0
  12. package/dist/cli/tutorial/sm-tutorial/fixtures-data/manifest.json +9 -4
  13. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/agent-skills/en/__PROVIDER__/skills/publish/SKILL.md +15 -0
  14. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/agent-skills/es/__PROVIDER__/skills/publish/SKILL.md +16 -0
  15. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/en/__PROVIDER__/skills/publish/SKILL.md +15 -0
  16. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/es/__PROVIDER__/skills/publish/SKILL.md +16 -0
  17. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/agent-skills/en/__PROVIDER__/skills/master-agent/SKILL.md +13 -0
  18. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/agent-skills/es/__PROVIDER__/skills/master-agent/SKILL.md +14 -0
  19. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/en/.codex/agents/master-agent.toml +10 -0
  20. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/es/.codex/agents/master-agent.toml +10 -0
  21. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/agent-skills/en/AGENTS.md +7 -0
  22. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/agent-skills/en/__PROVIDER__/skills/content-editor/SKILL.md +20 -0
  23. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/agent-skills/es/AGENTS.md +7 -0
  24. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/agent-skills/es/__PROVIDER__/skills/content-editor/SKILL.md +20 -0
  25. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/agent-skills/shared/CLAUDE.md +1 -0
  26. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/en/.codex/agents/content-editor.toml +19 -0
  27. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/es/.codex/agents/content-editor.toml +19 -0
  28. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/en/.codex/agents/demo-agent.toml +13 -0
  29. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/en/__PROVIDER__/skills/demo-command/SKILL.md +11 -0
  30. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/es/.codex/agents/demo-agent.toml +13 -0
  31. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/es/__PROVIDER__/skills/demo-command/SKILL.md +12 -0
  32. package/dist/cli/tutorial/sm-tutorial/references/_core.md +102 -49
  33. package/dist/cli/tutorial/sm-tutorial/references/_manifest.json +168 -20
  34. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +85 -19
  35. package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +6 -7
  36. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +2 -2
  37. package/dist/cli/tutorial/sm-tutorial/references/part-basic-daily.md +241 -0
  38. package/dist/cli/tutorial/sm-tutorial/references/part-basic-fundamentals.md +351 -0
  39. package/dist/cli/tutorial/sm-tutorial/references/part-basic-kickoff.md +285 -0
  40. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +1 -1
  41. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +62 -99
  42. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +35 -34
  43. package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +3 -6
  44. package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +1 -1
  45. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +198 -26
  46. package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +19 -15
  47. package/dist/cli/tutorial/sm-tutorial/scripts/fixtures.js +85 -22
  48. package/dist/cli/tutorial/sm-tutorial/scripts/lib/paths.js +74 -4
  49. package/dist/cli/tutorial/sm-tutorial/scripts/state.js +22 -6
  50. package/dist/cli.js +409 -168
  51. package/dist/conformance/index.js +42 -2
  52. package/dist/index.js +43 -30
  53. package/dist/kernel/index.d.ts +28 -5
  54. package/dist/kernel/index.js +43 -30
  55. package/dist/ui/chunk-22EQLC23.js +1845 -0
  56. package/dist/ui/chunk-3ANNEMV4.js +499 -0
  57. package/dist/ui/{chunk-5BJGO7GH.js → chunk-3U4QZKU2.js} +4 -4
  58. package/dist/ui/chunk-3ZAHOYQ7.js +1 -0
  59. package/dist/ui/{chunk-56CBK7LB.js → chunk-6FGV5O5J.js} +1 -1
  60. package/dist/ui/chunk-7WMT2LX4.js +1 -0
  61. package/dist/ui/{chunk-276RLZR4.js → chunk-BSIR3ADF.js} +14 -14
  62. package/dist/ui/{chunk-FC22ZJQZ.js → chunk-CG25RHMO.js} +1 -1
  63. package/dist/ui/chunk-EFSC6SOL.js +3 -0
  64. package/dist/ui/chunk-EJVWTBMV.js +4 -0
  65. package/dist/ui/chunk-EZI3BXQN.js +1 -0
  66. package/dist/ui/{chunk-JZ2YF7EL.js → chunk-GUPPOK7U.js} +8 -8
  67. package/dist/ui/{chunk-CJURGJTN.js → chunk-HLALESGR.js} +1 -1
  68. package/dist/ui/chunk-I3I4KHR5.js +2 -0
  69. package/dist/ui/{chunk-BOVJVOLH.js → chunk-I6ED2OW7.js} +1 -1
  70. package/dist/ui/chunk-JKPG5PO7.js +375 -0
  71. package/dist/ui/chunk-K3ZRQNN5.js +2 -0
  72. package/dist/ui/chunk-KHDWXSGR.js +1 -0
  73. package/dist/ui/{chunk-HEK4PH5A.js → chunk-KMHXNOFZ.js} +1 -1
  74. package/dist/ui/chunk-KWT7E2RJ.js +16 -0
  75. package/dist/ui/{chunk-WHZVGOS3.js → chunk-MQSU6EFZ.js} +1 -1
  76. package/dist/ui/{chunk-43S72FTV.js → chunk-OGEE252A.js} +1 -1
  77. package/dist/ui/{chunk-J4J42HJ4.js → chunk-PU5OP5RN.js} +1 -1
  78. package/dist/ui/{chunk-UTRZTB6V.js → chunk-QVG7J2MP.js} +1 -1
  79. package/dist/ui/chunk-TLMV4LOQ.js +3 -0
  80. package/dist/ui/chunk-TQBXK5JN.js +1 -0
  81. package/dist/ui/chunk-Z7SOKILO.js +2 -0
  82. package/dist/ui/{chunk-WCABR6TI.js → chunk-ZRJ5ZCFR.js} +1 -1
  83. package/dist/ui/index.html +2 -2
  84. package/dist/ui/main-R7BIU4HU.js +4 -0
  85. package/dist/ui/styles-VEGETYWD.css +1 -0
  86. package/package.json +17 -18
  87. package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +0 -173
  88. package/dist/ui/chunk-34ZZDYNQ.js +0 -1
  89. package/dist/ui/chunk-444BFYGR.js +0 -3
  90. package/dist/ui/chunk-44VNNUSQ.js +0 -2
  91. package/dist/ui/chunk-4SG4352Z.js +0 -7
  92. package/dist/ui/chunk-5ITZXW3A.js +0 -1
  93. package/dist/ui/chunk-7ANZW2OI.js +0 -499
  94. package/dist/ui/chunk-BJ6X6WBO.js +0 -4
  95. package/dist/ui/chunk-CZSLV6YD.js +0 -1
  96. package/dist/ui/chunk-DLYJHLJX.js +0 -2
  97. package/dist/ui/chunk-LGFABCIA.js +0 -16
  98. package/dist/ui/chunk-LPDD2DHE.js +0 -369
  99. package/dist/ui/chunk-P3SNMV4X.js +0 -2
  100. package/dist/ui/chunk-S4S5ZMXJ.js +0 -3
  101. package/dist/ui/chunk-VHEFRMK3.js +0 -1
  102. package/dist/ui/chunk-X6TRIDBI.js +0 -1845
  103. package/dist/ui/main-V77F2KZX.js +0 -4
  104. package/dist/ui/styles-I4ULXD3V.css +0 -1
  105. /package/dist/ui/{chunk-Y2Z26SRI.js → chunk-5RNLC6V4.js} +0 -0
@@ -34,7 +34,8 @@ import { parseArgs } from './lib/args.js';
34
34
  import { emit, succeed, die } from './lib/io.js';
35
35
  import { loadFixturesManifest, fixturesDir, resolveFootprint } from './lib/fixtures-manifest.js';
36
36
  import {
37
- providerDir, kindsFor, resolveTargetPath, kindForPath, PROVIDER_TOKEN,
37
+ providerDir, kindsFor, resolveTargetPath, kindForPath, nodeIdForTokenPath, overlayKey,
38
+ fragmentKindsFor, PROVIDER_TOKEN,
38
39
  } from './lib/paths.js';
39
40
 
40
41
  function opts(args) {
@@ -68,38 +69,76 @@ function writeFileEnsuring(abs, content) {
68
69
 
69
70
  /**
70
71
  * 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.
72
+ * token-form relpaths, claude-shaped and lens-agnostic) restricts to those
73
+ * nodes, for the prologue's progressive reveal where each chapter lands its
74
+ * own nodes; matching is by logical node id so a claude-shaped `--only`
75
+ * entry still selects the agent-skills skill overlay of the same node.
76
+ *
77
+ * Tiers, in lay order (later wins): lang-invariant `shared/`, the language
78
+ * tier, then the per-provider overlay (`providers/<provider>/{shared,<lang>}/`).
79
+ * The overlay carries the skill-shaped variants of nodes a lens renders
80
+ * differently (e.g. the `content-editor` agent becomes a skill on
81
+ * agent-skills); claude declares no overlay because the base IS its shape.
73
82
  */
74
83
  function laySet(manifest, set, o, only = null) {
75
84
  const { kinds } = pdirAndKinds(o);
76
85
  if (!manifest.sets.includes(set)) die('unknown-set', `set '${set}' is not in the manifest.`);
77
86
  const base = join(fixturesDir(), 'sets', set);
78
87
  const langDir = existsSync(join(base, o.lang)) ? o.lang : (manifest.defaultLang ?? 'en');
88
+ const onlyIds = only ? new Set([...only].map(nodeIdForTokenPath)) : null;
89
+ const oKey = overlayKey(o.provider);
79
90
  const laid = [];
91
+ const laidIds = new Set();
80
92
  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);
93
+ const tiers = [
94
+ join(base, 'shared'),
95
+ join(base, langDir),
96
+ join(base, 'providers', oKey, 'shared'),
97
+ join(base, 'providers', oKey, langDir),
98
+ ];
99
+ for (const tierRoot of tiers) {
84
100
  for (const rel of walk(tierRoot)) {
85
101
  // `rel` is the token-form target path (e.g. __PROVIDER__/agents/x.md).
86
- if (only && !only.has(rel)) continue;
102
+ if (onlyIds && !onlyIds.has(nodeIdForTokenPath(rel))) continue;
87
103
  const kind = kindForPath(rel);
88
104
  if (!kinds.has(kind)) { skipped.push({ path: rel, kind }); continue; }
89
105
  const target = resolveTargetPath(rel, o.provider);
90
106
  writeFileEnsuring(join(process.cwd(), target), readFileSync(join(tierRoot, rel)));
91
107
  laid.push(target);
108
+ laidIds.add(nodeIdForTokenPath(rel));
92
109
  }
93
110
  }
94
- return { laid, skipped };
111
+ // Drop from `skipped` any node that the overlay laid under another kind
112
+ // (the claude-shaped agent file is skipped, but its skill overlay landed),
113
+ // so the report only flags nodes genuinely absent on this lens.
114
+ const netSkipped = skipped.filter((s) => !laidIds.has(nodeIdForTokenPath(s.path)));
115
+ return { laid, skipped: netSkipped };
95
116
  }
96
117
 
97
- const nodeCount = (paths) => paths.filter((p) => p.endsWith('.md')).length;
118
+ // Count UNIQUE `.md` targets: a per-provider overlay can overwrite a base
119
+ // file (e.g. the open-standard `AGENTS.md` handbook), so `laid` may list the
120
+ // same target twice; the node count is the on-disk reality, not the write count.
121
+ const nodeCount = (paths) => new Set(paths.filter((p) => p.endsWith('.md'))).size;
98
122
 
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);
123
+ /**
124
+ * Resolve a fragment file path. A per-provider overlay
125
+ * (`edits/<id>/providers/<provider>/<lang>/`) wins when present, mirroring
126
+ * the set overlay, a fragment with a relative link to a provider-dir file
127
+ * is depth-sensitive (the content-editor lives one level deeper as a skill
128
+ * under agent-skills), so its link text differs per lens. Falls back to the
129
+ * shared fragment, then to the default language for either tier.
130
+ */
131
+ function fragmentPath(manifest, id, o, file) {
132
+ const def = manifest.defaultLang ?? 'en';
133
+ const dir = join(fixturesDir(), 'edits', id);
134
+ const oKey = overlayKey(o.provider);
135
+ const candidates = [
136
+ join(dir, 'providers', oKey, o.lang, file),
137
+ join(dir, 'providers', oKey, def, file),
138
+ join(dir, o.lang, file),
139
+ join(dir, def, file),
140
+ ];
141
+ return candidates.find((c) => existsSync(c)) ?? candidates[candidates.length - 1];
103
142
  }
104
143
 
105
144
  /** Apply one manifest edit (append fragments) honoring requiresKind. */
@@ -107,14 +146,30 @@ function applyEdit(manifest, id, o) {
107
146
  const { kinds } = pdirAndKinds(o);
108
147
  const def = manifest.edits?.[id];
109
148
  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 };
149
+ // Resolve the target to the shape this provider actually laid. When the
150
+ // declared (claude-shaped) target's kind is unclaimed, fall back to the
151
+ // skill-overlay path for the same node id, agent-skills renders the
152
+ // content-editor agent as a skill, so the style fragment must append to
153
+ // that skill body. Only genuinely-absent nodes skip the edit.
154
+ let targetTok = def.target;
155
+ if (!kinds.has(kindForPath(targetTok))) {
156
+ const skillTok = `${PROVIDER_TOKEN}/skills/${nodeIdForTokenPath(def.target)}/SKILL.md`;
157
+ const skillAbs = join(process.cwd(), resolveTargetPath(skillTok, o.provider));
158
+ if (kinds.has('skill') && existsSync(skillAbs)) {
159
+ targetTok = skillTok;
160
+ } else {
161
+ return { target: resolveTargetPath(def.target, o.provider), appended: [], skipped: true };
162
+ }
163
+ }
164
+ const target = resolveTargetPath(targetTok, o.provider);
114
165
  const targetAbs = join(process.cwd(), target);
115
166
  if (!existsSync(targetAbs)) die('edit-target-missing', `edit '${id}' target not found: ${target}`);
116
167
 
117
- const fragments = (def.fragments ?? []).filter((f) => !f.requiresKind || kinds.has(f.requiresKind));
168
+ // Fragment gating is by TRACK, not the base-tier `kinds`: a rich provider
169
+ // (codex) links to agent / command roles it renders via overlay (TOML agent,
170
+ // command-as-skill), so those bullets apply even though its base kinds omit them.
171
+ const fragKinds = fragmentKindsFor(o.provider);
172
+ const fragments = (def.fragments ?? []).filter((f) => !f.requiresKind || fragKinds.has(f.requiresKind));
118
173
  if (fragments.length === 0) return { target, appended: [] };
119
174
 
120
175
  let content = readFileSync(targetAbs, 'utf8');
@@ -122,7 +177,7 @@ function applyEdit(manifest, id, o) {
122
177
  if (def.prefix) content += def.prefix;
123
178
  const appended = [];
124
179
  for (const frag of fragments) {
125
- content += readFileSync(fragmentPath(manifest, id, o.lang, frag.file), 'utf8');
180
+ content += readFileSync(fragmentPath(manifest, id, o, frag.file), 'utf8');
126
181
  appended.push(frag.file);
127
182
  }
128
183
  writeFileSync(targetAbs, content);
@@ -207,7 +262,15 @@ const VERBS = {
207
262
  const langDir = existsSync(join(base, o.lang)) ? o.lang : (manifest.defaultLang ?? 'en');
208
263
  // Accept a resolved path (`.claude/...`) or token form; normalise to token.
209
264
  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));
265
+ // Prefer the per-provider overlay (so codex / agent-skills get their own
266
+ // shape, e.g. the publish skill or the TOML agent) before the base.
267
+ const oKey = overlayKey(o.provider);
268
+ const found = [
269
+ join(base, 'providers', oKey, langDir, tokenForm),
270
+ join(base, 'providers', oKey, 'shared', tokenForm),
271
+ join(base, langDir, tokenForm),
272
+ join(base, 'shared', tokenForm),
273
+ ].find((c) => existsSync(c));
211
274
  if (!found) {
212
275
  emit({ ok: false, code: 'not-found', error: `file '${file}' not found in set '${set}'.` });
213
276
  process.exit(1);
@@ -234,5 +297,5 @@ function main() {
234
297
  }
235
298
 
236
299
  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
300
+ !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]="9e366749-efd9-5bc5-bba4-41147eabf625")}catch(e){}}();
301
+ //# debugId=9e366749-efd9-5bc5-bba4-41147eabf625
@@ -7,14 +7,22 @@
7
7
  export const PROVIDER_TOKEN = '__PROVIDER__';
8
8
 
9
9
  export function providerDir(provider) {
10
- // agent-skills and Antigravity share the open `.agents/skills/` layout.
11
- return provider === 'agent-skills' || provider === 'antigravity'
10
+ // agent-skills, Antigravity and Codex all keep their SKILLS under the
11
+ // open `.agents/skills/` layout. (Codex additionally has TOML agents under
12
+ // `.codex/agents/`, supplied by the codex overlay's literal paths, not this
13
+ // single base dir.)
14
+ return provider === 'agent-skills' || provider === 'antigravity' || provider === 'codex'
12
15
  ? '.agents/skills'
13
16
  : '.claude';
14
17
  }
15
18
 
16
19
  export const PROVIDER_KINDS = {
17
20
  claude: new Set(['agent', 'command', 'skill', 'markdown']),
21
+ // Codex authors its agents as TOML under `.codex/agents/` (a different shape
22
+ // than the base `__PROVIDER__/agents/*.md`), so the base tier lays only its
23
+ // shared skill + markdown nodes; the codex overlay supplies the TOML agents
24
+ // and the command-as-skill nodes (Codex has no `command` kind).
25
+ codex: new Set(['skill', 'markdown']),
18
26
  'agent-skills': new Set(['skill', 'markdown']),
19
27
  antigravity: new Set(['skill', 'markdown']),
20
28
  };
@@ -23,6 +31,44 @@ export function kindsFor(provider) {
23
31
  return PROVIDER_KINDS[provider] ?? PROVIDER_KINDS.claude;
24
32
  }
25
33
 
34
+ /**
35
+ * Tutorial track for a provider, by the "does this lens have an `agent`
36
+ * kind?" axis (see `_core.md` §Provider detection):
37
+ * - `rich` (agent + skill + slash + `@`): `claude`, `codex`.
38
+ * - `basic` (skill + markdown, connected by markdown references): the
39
+ * open-standard family `agent-skills`, `antigravity`.
40
+ * The book renders the track's parts; the same lens always resolves to
41
+ * the same track, so a resumed session never re-derives it.
42
+ */
43
+ export function trackFor(provider) {
44
+ return provider === 'claude' || provider === 'codex' ? 'rich' : 'basic';
45
+ }
46
+
47
+ /**
48
+ * The provider whose fixture overlays a given provider reuses. The
49
+ * open-standard family (`agent-skills`, `antigravity`) shares one on-disk
50
+ * shape (`.agents/skills/`, skill + markdown, connected by markdown
51
+ * references), so `antigravity` reuses the canonical `agent-skills`
52
+ * overlays rather than duplicating them. Every other provider keys its own.
53
+ */
54
+ export function overlayKey(provider) {
55
+ return provider === 'antigravity' ? 'agent-skills' : provider;
56
+ }
57
+
58
+ /**
59
+ * Kinds whose edit fragments (the todo-connectors hub bullets, etc.) apply for
60
+ * a provider, keyed by TRACK, not by the base-tier kinds. A rich provider links
61
+ * to every node role even when it renders some differently, Codex's agent is a
62
+ * TOML overlay and its command-node is a skill, but an `@agent` mention and a
63
+ * `/command` invocation still resolve, so every bullet applies. A basic
64
+ * provider only has skill + markdown, so the agent / command bullets fold away.
65
+ */
66
+ export function fragmentKindsFor(provider) {
67
+ return trackFor(provider) === 'rich'
68
+ ? new Set(['agent', 'command', 'skill', 'markdown'])
69
+ : new Set(['skill', 'markdown']);
70
+ }
71
+
26
72
  /**
27
73
  * Per-provider kind directories. The token path is always written in
28
74
  * the claude shape (`__PROVIDER__/skills/<name>/...`); resolving it is
@@ -31,6 +77,7 @@ export function kindsFor(provider) {
31
77
  */
32
78
  const KIND_DIRS = {
33
79
  claude: { agents: '.claude/agents', commands: '.claude/commands', skills: '.claude/skills' },
80
+ codex: { skills: '.agents/skills' },
34
81
  'agent-skills': { skills: '.agents/skills' },
35
82
  antigravity: { skills: '.agents/skills' },
36
83
  };
@@ -64,5 +111,28 @@ export function kindForPath(tokenRelPath) {
64
111
  if (tokenRelPath.startsWith(`${PROVIDER_TOKEN}/skills/`)) return 'skill';
65
112
  return 'markdown';
66
113
  }
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
114
+
115
+ /**
116
+ * Logical, lens-agnostic node id for a token-form path. The SAME
117
+ * conceptual node renders in a different kind per lens (a `content-editor`
118
+ * is an `agent` on claude but a `skill` on agent-skills), so a `--only`
119
+ * filter or an edit target written in the claude shape must still match
120
+ * the agent-skills overlay. Agents / commands use the file stem; skills
121
+ * use the skill directory name; everything else (markdown, notes, docs)
122
+ * keeps its relpath verbatim. So both `__PROVIDER__/agents/content-editor.md`
123
+ * and `__PROVIDER__/skills/content-editor/SKILL.md` resolve to `content-editor`.
124
+ */
125
+ export function nodeIdForTokenPath(tokenRelPath) {
126
+ const flat = tokenRelPath.match(/^__PROVIDER__\/(?:agents|commands)\/(.+)\.md$/);
127
+ if (flat) return flat[1];
128
+ const skill = tokenRelPath.match(/^__PROVIDER__\/skills\/([^/]+)\//);
129
+ if (skill) return skill[1];
130
+ // Codex renders an agent as a literal `.codex/agents/<name>.toml`; map it
131
+ // to the same id as the claude-shaped `__PROVIDER__/agents/<name>.md` so a
132
+ // `--only` filter (or the skipped-node dedup) matches across the two shapes.
133
+ const codexAgent = tokenRelPath.match(/^\.codex\/agents\/(.+)\.toml$/);
134
+ if (codexAgent) return codexAgent[1];
135
+ return tokenRelPath;
136
+ }
137
+ !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]="9a867c5a-0c61-500e-a583-7d7dd6c5ac92")}catch(e){}}();
138
+ //# debugId=9a867c5a-0c61-500e-a583-7d7dd6c5ac92
@@ -31,7 +31,7 @@ import { rmSync, rmdirSync, readdirSync } from 'node:fs';
31
31
  import { parseArgs } from './lib/args.js';
32
32
  import { exists, readJson, writeJson, succeed, die } from './lib/io.js';
33
33
  import { loadManifest, findPart } from './lib/manifest.js';
34
- import { providerDir } from './lib/paths.js';
34
+ import { providerDir, trackFor } from './lib/paths.js';
35
35
  import { loadFixturesManifest, resolveFootprint } from './lib/fixtures-manifest.js';
36
36
 
37
37
  const STATE_FILE = 'tutorial-state.json';
@@ -68,13 +68,18 @@ const VERBS = {
68
68
  if (exists(p) && !args.flags.force) {
69
69
  die('exists', `${STATE_FILE} already exists; pass --force to overwrite.`);
70
70
  }
71
+ const provider = args.flags.provider ?? 'claude';
71
72
  const state = {
72
73
  tutorial: {
73
74
  version: STATE_VERSION,
74
75
  started_at: now(),
75
76
  cwd: args.flags.cwd ?? process.cwd(),
76
77
  sm_version: args.flags['sm-version'] ?? null,
77
- provider: args.flags.provider ?? 'claude',
78
+ provider,
79
+ // Derived from the provider (`_core.md` §Provider detection): the
80
+ // book renders this track's parts. `rich` = claude/codex,
81
+ // `basic` = the open-standard family (agent-skills/antigravity).
82
+ track: trackFor(provider),
78
83
  lang: args.flags.lang ?? 'en',
79
84
  },
80
85
  tester: { level: 2 },
@@ -148,8 +153,13 @@ const VERBS = {
148
153
  status() {
149
154
  const state = loadState();
150
155
  const manifest = loadManifest();
156
+ // Show only the active track's parts (plus `both`). The rich and basic
157
+ // campaigns share titles and order, so a session sees exactly one book,
158
+ // the track resolved at pre-flight (see `_core.md` §Routing + menu).
159
+ const track = state.tutorial?.track ?? 'rich';
151
160
  const parts = manifest.parts
152
161
  .filter((p) => p.status === 'active' || state.parts[p.id])
162
+ .filter((p) => !p.track || p.track === 'both' || p.track === track)
153
163
  .map((p) => {
154
164
  const tracked = state.parts[p.id];
155
165
  return {
@@ -221,8 +231,14 @@ function computeWipePaths(state) {
221
231
  const paths = new Set(['tutorial-state.json', 'findings.md', '.skillmapignore', '.skill-map']);
222
232
  const addFootprint = (name) => resolveFootprint(manifest, name, provider).forEach((p) => paths.add(p));
223
233
 
224
- if (has('fundamentals')) addFootprint('prologue');
225
- if (has('project-kickoff') || has('connect-harness') || has('daily-loop')) addFootprint('portfolio');
234
+ // Each footprint covers both tracks: the rich and basic prologues /
235
+ // campaigns lay the same fixtures (the basic one under the open-standard
236
+ // provider), so either part's presence means that fixture is on disk.
237
+ if (has('fundamentals') || has('basic-fundamentals')) addFootprint('prologue');
238
+ if (
239
+ has('project-kickoff') || has('daily-loop')
240
+ || has('basic-kickoff') || has('basic-daily')
241
+ ) addFootprint('portfolio');
226
242
  if (has('extend')) addFootprint('master');
227
243
  // `cli` seeds the prologue demo fixture plus its external-ref demo.
228
244
  if (has('cli')) { addFootprint('prologue'); addFootprint('cli-external'); }
@@ -258,5 +274,5 @@ function main() {
258
274
  }
259
275
 
260
276
  main();
261
- !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]="547a8c8d-f962-571c-ae72-8edbb1fc756c")}catch(e){}}();
262
- //# debugId=547a8c8d-f962-571c-ae72-8edbb1fc756c
277
+ !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]="3ef90c0e-0700-5a7b-a25f-c47aa19fd567")}catch(e){}}();
278
+ //# debugId=3ef90c0e-0700-5a7b-a25f-c47aa19fd567