@forwardimpact/pathway 0.25.22 → 0.25.24

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 (39) hide show
  1. package/bin/fit-pathway.js +117 -325
  2. package/package.json +2 -2
  3. package/src/commands/agent-io.js +1 -1
  4. package/src/commands/agent.js +13 -13
  5. package/src/commands/behaviour.js +7 -7
  6. package/src/commands/build-packs.js +208 -34
  7. package/src/commands/build.js +2 -2
  8. package/src/commands/command-factory.js +2 -2
  9. package/src/commands/discipline.js +7 -7
  10. package/src/commands/driver.js +8 -8
  11. package/src/commands/index.js +0 -1
  12. package/src/commands/interview.js +4 -4
  13. package/src/commands/job.js +19 -17
  14. package/src/commands/level.js +7 -7
  15. package/src/commands/progress.js +4 -4
  16. package/src/commands/questions.js +10 -8
  17. package/src/commands/skill.js +9 -9
  18. package/src/commands/stage.js +7 -7
  19. package/src/commands/tool.js +6 -6
  20. package/src/commands/track.js +7 -7
  21. package/src/formatters/questions/yaml.js +1 -1
  22. package/src/index.html +1 -1
  23. package/src/lib/cli-command.js +33 -33
  24. package/src/lib/cli-output.js +9 -189
  25. package/src/pages/agent-builder-install.js +6 -5
  26. package/src/commands/init.js +0 -64
  27. package/starter/behaviours/systems_thinking.yaml +0 -32
  28. package/starter/capabilities/delivery.yaml +0 -105
  29. package/starter/capabilities/reliability.yaml +0 -72
  30. package/starter/disciplines/software_engineering.yaml +0 -46
  31. package/starter/drivers.yaml +0 -10
  32. package/starter/framework.yaml +0 -49
  33. package/starter/levels.yaml +0 -39
  34. package/starter/questions/behaviours/.gitkeep +0 -0
  35. package/starter/questions/capabilities/.gitkeep +0 -0
  36. package/starter/questions/skills/.gitkeep +0 -0
  37. package/starter/stages.yaml +0 -21
  38. package/starter/tracks/forward_deployed.yaml +0 -33
  39. package/starter/tracks/platform.yaml +0 -33
@@ -1,15 +1,19 @@
1
1
  /**
2
2
  * Pack generation for Pathway distribution.
3
3
  *
4
- * Emits one pre-built agent/skill pack per valid discipline/track combination
5
- * and two discovery manifests (`.well-known/agent-skills/index.json` for
6
- * `npx skills` and `apm.yml` for Microsoft APM). See
7
- * specs/320-pathway-ecosystem-distribution for context.
4
+ * Emits one pre-built agent/skill pack per valid discipline/track combination.
5
+ * Each pack becomes its own `npx skills`-compatible repository at
6
+ * `packs/{name}/.well-known/skills/`, with an aggregate repository at
7
+ * `packs/.well-known/skills/` listing every skill from every pack.
8
+ * An `apm.yml` for Microsoft APM is also written at the site root.
9
+ *
10
+ * See specs/320-pathway-ecosystem-distribution for context.
8
11
  *
9
12
  * Invoked from build.js after the distribution bundle has been generated.
10
13
  */
11
14
 
12
- import { mkdir, rm, readFile, writeFile } from "fs/promises";
15
+ import { mkdir, rm, readFile, writeFile, readdir, cp } from "fs/promises";
16
+ import { utimesSync } from "fs";
13
17
  import { join } from "path";
14
18
  import { execFileSync } from "child_process";
15
19
  import { createHash } from "crypto";
@@ -209,50 +213,205 @@ function derivePackContent({
209
213
  return { profiles, skillFiles, teamInstructions };
210
214
  }
211
215
 
216
+ /**
217
+ * Recursively collect all paths (files and directories) under `dir`,
218
+ * relative to `dir`, in sorted order.
219
+ */
220
+ async function collectPaths(dir, prefix = ".") {
221
+ const entries = await readdir(dir, { withFileTypes: true });
222
+ const result = [];
223
+ for (const entry of entries) {
224
+ const rel = prefix + "/" + entry.name;
225
+ const abs = join(dir, entry.name);
226
+ if (entry.isDirectory()) {
227
+ result.push(rel);
228
+ result.push(...(await collectPaths(abs, rel)));
229
+ } else {
230
+ result.push(rel);
231
+ }
232
+ }
233
+ return result;
234
+ }
235
+
236
+ /**
237
+ * Set mtime and atime to the Unix epoch for every entry under `dir`.
238
+ */
239
+ async function resetTimestamps(dir) {
240
+ const epoch = new Date(0);
241
+ const paths = await collectPaths(dir);
242
+ for (const rel of paths) {
243
+ utimesSync(join(dir, rel), epoch, epoch);
244
+ }
245
+ utimesSync(dir, epoch, epoch);
246
+ }
247
+
212
248
  /**
213
249
  * Archive a staged pack directory as a deterministic tar.gz and return its
214
250
  * sha256 digest.
251
+ *
252
+ * Determinism strategy (works on GNU tar and BSD tar):
253
+ * 1. Reset all file timestamps to epoch via Node's utimesSync.
254
+ * 2. Collect and sort the file list in JS — no reliance on --sort=name.
255
+ * 3. Create an uncompressed tar to stdout with the sorted list.
256
+ * 4. Pipe through `gzip -n` to suppress the gzip header timestamp.
257
+ *
215
258
  * @param {string} packDir - Staging directory containing the pack files
216
259
  * @param {string} archivePath - Destination path for the tar.gz
217
260
  * @returns {Promise<string>} sha256 digest string (e.g. "sha256:abc...")
218
261
  */
219
262
  async function archivePack(packDir, archivePath) {
220
- execFileSync("tar", [
221
- "-czf",
222
- archivePath,
223
- "--sort=name",
224
- "--mtime=1970-01-01",
225
- "--owner=0",
226
- "--group=0",
227
- "--numeric-owner",
228
- "-C",
229
- packDir,
230
- ".",
231
- ]);
263
+ await resetTimestamps(packDir);
264
+
265
+ const files = await collectPaths(packDir);
266
+ files.sort();
267
+
268
+ const tarBuf = execFileSync("tar", ["-cf", "-", "-C", packDir, ...files]);
269
+ const gzBuf = execFileSync("gzip", ["-n"], { input: tarBuf });
270
+ await writeFile(archivePath, gzBuf);
232
271
 
233
272
  const bytes = await readFile(archivePath);
234
273
  return "sha256:" + createHash("sha256").update(bytes).digest("hex");
235
274
  }
236
275
 
237
276
  /**
238
- * Write the `npx skills` discovery manifest.
239
- * @param {string} outputDir
240
- * @param {Array<{name: string, description: string, url: string, digest: string}>} packs
241
- * @param {string} version
277
+ * Collect all file paths under `dir`, relative to `dir`, for the manifest
278
+ * `files` array. Returns sorted paths with forward slashes.
279
+ * @param {string} dir
280
+ * @param {string} [prefix]
281
+ * @returns {Promise<string[]>}
282
+ */
283
+ async function collectFileList(dir, prefix = "") {
284
+ const entries = await readdir(dir, { withFileTypes: true });
285
+ const result = [];
286
+ for (const entry of entries) {
287
+ const rel = prefix ? prefix + "/" + entry.name : entry.name;
288
+ if (entry.isDirectory()) {
289
+ result.push(...(await collectFileList(join(dir, entry.name), rel)));
290
+ } else {
291
+ result.push(rel);
292
+ }
293
+ }
294
+ return result.sort();
295
+ }
296
+
297
+ /**
298
+ * Parse YAML frontmatter from a SKILL.md file. Returns an object with
299
+ * the key/value pairs found between the `---` fences.
300
+ * @param {string} content
301
+ * @returns {Record<string, string>}
302
+ */
303
+ function parseFrontmatter(content) {
304
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
305
+ if (!match) return {};
306
+ const result = {};
307
+ for (const line of match[1].split("\n")) {
308
+ const idx = line.indexOf(":");
309
+ if (idx > 0) result[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
310
+ }
311
+ return result;
312
+ }
313
+
314
+ /**
315
+ * Build a single skill index entry from a staged skill directory.
316
+ * @param {string} skillDir - Path containing SKILL.md and optional extras
317
+ * @param {string} name - Skill name for the manifest
318
+ * @returns {Promise<{name: string, description: string, files: string[]}>}
319
+ */
320
+ async function buildSkillEntry(skillDir, name) {
321
+ const skillMd = await readFile(join(skillDir, "SKILL.md"), "utf-8");
322
+ const fm = parseFrontmatter(skillMd);
323
+ const files = await collectFileList(skillDir);
324
+ return { description: fm.description || "", files, name };
325
+ }
326
+
327
+ /**
328
+ * Write a `npx skills`-compatible repository for a single pack.
329
+ *
330
+ * Each pack becomes its own skill repository at
331
+ * `packs/{name}/.well-known/skills/` so that individual skills
332
+ * within the pack can be discovered and installed independently:
333
+ *
334
+ * npx skills add domain.org/packs/se-platform --all
335
+ * npx skills add domain.org/packs/se-platform -s architecture-design
336
+ *
337
+ * @param {string} packsOutputDir - The `packs/` output directory
338
+ * @param {string} packStagingDir - Staging directory for this pack
339
+ * @param {string} packName - Pack name (e.g. "se-platform")
340
+ * @returns {Promise<Array<{name: string, description: string, files: string[]}>>}
341
+ * The skill entries written, for use in the aggregate manifest.
242
342
  */
243
- async function writeSkillsManifest(outputDir, packs, version) {
244
- const wellKnownDir = join(outputDir, ".well-known", "agent-skills");
343
+ async function writePackRepository(packsOutputDir, packStagingDir, packName) {
344
+ const wellKnownDir = join(packsOutputDir, packName, ".well-known", "skills");
245
345
  await mkdir(wellKnownDir, { recursive: true });
346
+
347
+ // Discover individual skills from the staged pack's .claude/skills/
348
+ const skillsSrcDir = join(packStagingDir, ".claude", "skills");
349
+ const skillDirs = (
350
+ await readdir(skillsSrcDir, { withFileTypes: true })
351
+ ).filter((e) => e.isDirectory());
352
+
353
+ const entries = [];
354
+ for (const dir of skillDirs) {
355
+ const src = join(skillsSrcDir, dir.name);
356
+ const dest = join(wellKnownDir, dir.name);
357
+ await cp(src, dest, { recursive: true });
358
+ entries.push(await buildSkillEntry(dest, dir.name));
359
+ }
360
+
246
361
  const manifest = {
247
362
  $schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
248
- skills: packs.map((pack) => ({
249
- description: pack.description,
250
- digest: pack.digest,
251
- name: pack.name,
252
- type: "archive",
253
- url: pack.url,
254
- version,
255
- })),
363
+ skills: entries,
364
+ };
365
+ await writeFile(
366
+ join(wellKnownDir, "index.json"),
367
+ stringifySorted(manifest),
368
+ "utf-8",
369
+ );
370
+
371
+ return entries;
372
+ }
373
+
374
+ /**
375
+ * Write an aggregate `npx skills` repository at `packs/` that lists every
376
+ * unique skill across all packs. Skills with the same name produce identical
377
+ * SKILL.md content regardless of discipline/track, so we deduplicate by name
378
+ * and write one copy.
379
+ *
380
+ * npx skills add domain.org/packs --list
381
+ *
382
+ * @param {string} packsOutputDir
383
+ * @param {Array<{packName: string, entries: Array}>} allPackEntries
384
+ */
385
+ async function writeAggregateRepository(packsOutputDir, allPackEntries) {
386
+ const wellKnownDir = join(packsOutputDir, ".well-known", "skills");
387
+ await mkdir(wellKnownDir, { recursive: true });
388
+
389
+ // Deduplicate: first occurrence of each skill name wins (content is identical)
390
+ const seen = new Map();
391
+ for (const { packName, entries } of allPackEntries) {
392
+ for (const entry of entries) {
393
+ if (seen.has(entry.name)) continue;
394
+ seen.set(entry.name, { packName, entry });
395
+ }
396
+ }
397
+
398
+ const skills = [];
399
+ for (const [, { packName, entry }] of seen) {
400
+ const dest = join(wellKnownDir, entry.name);
401
+ const src = join(
402
+ packsOutputDir,
403
+ packName,
404
+ ".well-known",
405
+ "skills",
406
+ entry.name,
407
+ );
408
+ await cp(src, dest, { recursive: true });
409
+ skills.push(entry);
410
+ }
411
+
412
+ const manifest = {
413
+ $schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
414
+ skills,
256
415
  };
257
416
  await writeFile(
258
417
  join(wellKnownDir, "index.json"),
@@ -382,10 +541,25 @@ export async function generatePacks({
382
541
  console.log(` ✓ packs/${agentName}.tar.gz`);
383
542
  }
384
543
 
385
- await rm(stagingDir, { recursive: true, force: true });
544
+ // Write per-pack skill repositories (one per discipline/track combination)
545
+ const allPackEntries = [];
546
+ for (const pack of packs) {
547
+ const entries = await writePackRepository(
548
+ packsDir,
549
+ join(stagingDir, pack.name),
550
+ pack.name,
551
+ );
552
+ allPackEntries.push({ packName: pack.name, entries });
553
+ console.log(
554
+ ` ✓ packs/${pack.name}/.well-known/skills/ (${entries.length} skills)`,
555
+ );
556
+ }
386
557
 
387
- await writeSkillsManifest(outputDir, packs, version);
388
- console.log(" ✓ .well-known/agent-skills/index.json");
558
+ // Write aggregate repository at packs/ level
559
+ await writeAggregateRepository(packsDir, allPackEntries);
560
+ console.log(" ✓ packs/.well-known/skills/index.json (aggregate)");
561
+
562
+ await rm(stagingDir, { recursive: true, force: true });
389
563
 
390
564
  await writeApmManifest(outputDir, packs, version, frameworkTitle);
391
565
  console.log(" ✓ apm.yml");
@@ -222,10 +222,10 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
222
222
  ✅ Site generated successfully!
223
223
 
224
224
  Output: ${outputDir}
225
- ${siteUrl ? `\nDistribution:\n ${outputDir}/bundle.tar.gz\n ${outputDir}/install.sh\n ${outputDir}/packs/ (agent/skill packs)\n ${outputDir}/.well-known/agent-skills/index.json\n ${outputDir}/apm.yml\n` : ""}
225
+ ${siteUrl ? `\nDistribution:\n ${outputDir}/bundle.tar.gz\n ${outputDir}/install.sh\n ${outputDir}/packs/ (agent/skill packs)\n ${outputDir}/packs/{name}/.well-known/skills/ (per-pack skill repositories)\n ${outputDir}/packs/.well-known/skills/ (aggregate skill repository)\n ${outputDir}/apm.yml\n` : ""}
226
226
  To serve locally:
227
227
  cd ${relative(process.cwd(), outputDir) || "."}
228
- bunx serve .
228
+ npx serve .
229
229
  `);
230
230
  }
231
231
 
@@ -89,7 +89,7 @@ export function createEntityCommand({
89
89
  function handleValidate({ data, _entityName, pluralName, validate }) {
90
90
  if (!validate) {
91
91
  console.log(`No specific validation for ${pluralName}.`);
92
- console.log(`Run 'bunx pathway --validate' for full data validation.`);
92
+ console.log(`Run 'npx fit-pathway --validate' for full data validation.`);
93
93
  return;
94
94
  }
95
95
 
@@ -178,7 +178,7 @@ export function createCompositeCommand({
178
178
  return async function runCommand({ data, args, options }) {
179
179
  if (args.length < requiredArgs.length) {
180
180
  const argsList = requiredArgs.map((arg) => `<${arg}>`).join(" ");
181
- console.error(`Usage: bunx pathway ${commandName} ${argsList}`);
181
+ console.error(`Usage: npx fit-pathway ${commandName} ${argsList}`);
182
182
  if (usageExample) {
183
183
  console.error(`Example: ${usageExample}`);
184
184
  }
@@ -4,15 +4,15 @@
4
4
  * Handles discipline summary, listing, and detail display in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway discipline # Summary with stats
8
- * bunx pathway discipline --list # IDs only (for piping)
9
- * bunx pathway discipline <id> # Detail view
10
- * bunx pathway discipline --validate # Validation checks
7
+ * npx fit-pathway discipline # Summary with stats
8
+ * npx fit-pathway discipline --list # IDs only (for piping)
9
+ * npx fit-pathway discipline <id> # Detail view
10
+ * npx fit-pathway discipline --validate # Validation checks
11
11
  */
12
12
 
13
13
  import { createEntityCommand } from "./command-factory.js";
14
14
  import { disciplineToMarkdown } from "../formatters/discipline/markdown.js";
15
- import { formatTable } from "../lib/cli-output.js";
15
+ import { formatTable } from "@forwardimpact/libcli";
16
16
 
17
17
  /**
18
18
  * Format discipline list item for --list output
@@ -43,8 +43,8 @@ function formatSummary(disciplines) {
43
43
 
44
44
  console.log(formatTable(["ID", "Specialization", "Type", "Tracks"], rows));
45
45
  console.log(`\nTotal: ${disciplines.length} disciplines`);
46
- console.log(`\nRun 'bunx pathway discipline --list' for IDs and names`);
47
- console.log(`Run 'bunx pathway discipline <id>' for details\n`);
46
+ console.log(`\nRun 'npx fit-pathway discipline --list' for IDs and names`);
47
+ console.log(`Run 'npx fit-pathway discipline <id>' for details\n`);
48
48
  }
49
49
 
50
50
  /**
@@ -4,20 +4,20 @@
4
4
  * Handles driver summary, listing, and detail display in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway driver # Summary with stats
8
- * bunx pathway driver --list # IDs only (for piping)
9
- * bunx pathway driver <id> # Detail view
10
- * bunx pathway driver --validate # Validation checks
7
+ * npx fit-pathway driver # Summary with stats
8
+ * npx fit-pathway driver --list # IDs only (for piping)
9
+ * npx fit-pathway driver <id> # Detail view
10
+ * npx fit-pathway driver --validate # Validation checks
11
11
  */
12
12
 
13
13
  import { createEntityCommand } from "./command-factory.js";
14
14
  import { prepareDriverDetail } from "../formatters/driver/shared.js";
15
- import { formatTable } from "../lib/cli-output.js";
16
15
  import {
16
+ formatTable,
17
17
  formatHeader,
18
18
  formatSubheader,
19
19
  formatBullet,
20
- } from "../lib/cli-output.js";
20
+ } from "@forwardimpact/libcli";
21
21
  import { getConceptEmoji } from "@forwardimpact/map/levels";
22
22
 
23
23
  /**
@@ -52,8 +52,8 @@ function formatSummary(drivers, data) {
52
52
 
53
53
  console.log(formatTable(["ID", "Name", "Skills", "Behaviours"], rows));
54
54
  console.log(`\nTotal: ${drivers.length} drivers`);
55
- console.log(`\nRun 'bunx pathway driver --list' for IDs and names`);
56
- console.log(`Run 'bunx pathway driver <id>' for details\n`);
55
+ console.log(`\nRun 'npx fit-pathway driver --list' for IDs and names`);
56
+ console.log(`Run 'npx fit-pathway driver <id>' for details\n`);
57
57
  }
58
58
 
59
59
  /**
@@ -17,6 +17,5 @@ export { runInterviewCommand } from "./interview.js";
17
17
  export { runProgressCommand } from "./progress.js";
18
18
  export { runQuestionsCommand } from "./questions.js";
19
19
  export { runServeCommand } from "./serve.js";
20
- export { runInitCommand } from "./init.js";
21
20
  export { runSiteCommand } from "./site.js";
22
21
  export { runUpdateCommand } from "./update.js";
@@ -4,9 +4,9 @@
4
4
  * Generates and displays interview questions in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx fit-pathway interview <discipline> <level> # All interview types
8
- * bunx fit-pathway interview <discipline> <level> --track=<track> # With track
9
- * bunx fit-pathway interview <discipline> <level> --track=<track> --type=mission # Single type
7
+ * npx fit-pathway interview <discipline> <level> # All interview types
8
+ * npx fit-pathway interview <discipline> <level> --track=<track> # With track
9
+ * npx fit-pathway interview <discipline> <level> --track=<track> --type=mission # Single type
10
10
  */
11
11
 
12
12
  import { createCompositeCommand } from "./command-factory.js";
@@ -109,5 +109,5 @@ export const runInterviewCommand = createCompositeCommand({
109
109
  }
110
110
  },
111
111
  usageExample:
112
- "bunx fit-pathway interview software_engineering J090 --track=platform --type=mission",
112
+ "npx fit-pathway interview software_engineering J090 --track=platform --type=mission",
113
113
  });
@@ -4,14 +4,14 @@
4
4
  * Generates and displays job definitions in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway job # Summary with stats
8
- * bunx pathway job --list # All valid combinations (for piping)
9
- * bunx pathway job <discipline> <level> # Detail view (trackless)
10
- * bunx pathway job <discipline> <level> --track=<track> # Detail view (with track)
11
- * bunx pathway job <d> <l> [--track=<t>] --skills # Plain list of skill IDs
12
- * bunx pathway job <d> <l> [--track=<t>] --tools # Plain list of tool names
13
- * bunx pathway job se L3 --track=platform --checklist=code # Show checklist for handoff
14
- * bunx pathway job --validate # Validation checks
7
+ * npx fit-pathway job # Summary with stats
8
+ * npx fit-pathway job --list # All valid combinations (for piping)
9
+ * npx fit-pathway job <discipline> <level> # Detail view (trackless)
10
+ * npx fit-pathway job <discipline> <level> --track=<track> # Detail view (with track)
11
+ * npx fit-pathway job <d> <l> [--track=<t>] --skills # Plain list of skill IDs
12
+ * npx fit-pathway job <d> <l> [--track=<t>] --tools # Plain list of tool names
13
+ * npx fit-pathway job se L3 --track=platform --checklist=code # Show checklist for handoff
14
+ * npx fit-pathway job --validate # Validation checks
15
15
  */
16
16
 
17
17
  import { prepareJobDetail } from "@forwardimpact/libskill/job";
@@ -20,7 +20,7 @@ import {
20
20
  generateJobTitle,
21
21
  generateAllJobs,
22
22
  } from "@forwardimpact/libskill/derivation";
23
- import { formatTable } from "../lib/cli-output.js";
23
+ import { formatTable } from "@forwardimpact/libcli";
24
24
  import {
25
25
  deriveChecklist,
26
26
  formatChecklistMarkdown,
@@ -96,10 +96,10 @@ function printJobSummary(filteredJobs, options) {
96
96
  );
97
97
  console.log(`\nTotal: ${filteredJobs.length} valid job combinations`);
98
98
  console.log(
99
- `\nRun 'bunx pathway job --list' for all combinations with titles`,
99
+ `\nRun 'npx fit-pathway job --list' for all combinations with titles`,
100
100
  );
101
101
  console.log(
102
- `Run 'bunx pathway job <discipline> <level> [--track=<track>]' for details\n`,
102
+ `Run 'npx fit-pathway job <discipline> <level> [--track=<track>]' for details\n`,
103
103
  );
104
104
  }
105
105
 
@@ -113,7 +113,7 @@ function handleSingleArg(arg, data) {
113
113
  const isTrack = data.tracks.some((t) => t.id === arg);
114
114
  if (isLevel) {
115
115
  console.error(
116
- `Missing discipline. Usage: bunx pathway job <discipline> ${arg} [--track=<track>]`,
116
+ `Missing discipline. Usage: npx fit-pathway job <discipline> ${arg} [--track=<track>]`,
117
117
  );
118
118
  console.error(
119
119
  `Disciplines: ${data.disciplines.map((d) => d.id).join(", ")}`,
@@ -121,13 +121,13 @@ function handleSingleArg(arg, data) {
121
121
  } else if (isTrack) {
122
122
  console.error(`Track must be passed as a flag: --track=${arg}`);
123
123
  console.error(
124
- `Usage: bunx pathway job <discipline> <level> --track=${arg}`,
124
+ `Usage: npx fit-pathway job <discipline> <level> --track=${arg}`,
125
125
  );
126
126
  } else {
127
127
  console.error(
128
- "Usage: bunx pathway job <discipline> <level> [--track=<track>]",
128
+ "Usage: npx fit-pathway job <discipline> <level> [--track=<track>]",
129
129
  );
130
- console.error(" bunx pathway job --list");
130
+ console.error(" npx fit-pathway job --list");
131
131
  }
132
132
  process.exit(1);
133
133
  }
@@ -152,7 +152,7 @@ function resolveJobEntities(data, args, options) {
152
152
  if (maybeLevel && maybeDiscipline) {
153
153
  console.error(`Arguments are in the wrong order. Try:`);
154
154
  console.error(
155
- ` bunx pathway job ${args[1]} ${args[0]}${options.track ? ` --track=${options.track}` : ""}`,
155
+ ` npx fit-pathway job ${args[1]} ${args[0]}${options.track ? ` --track=${options.track}` : ""}`,
156
156
  );
157
157
  } else {
158
158
  console.error(`Discipline not found: ${args[0]}`);
@@ -169,7 +169,9 @@ function resolveJobEntities(data, args, options) {
169
169
  console.error(
170
170
  `Track must be passed as a flag, not a positional argument:`,
171
171
  );
172
- console.error(` bunx pathway job ${args[0]} <level> --track=${args[1]}`);
172
+ console.error(
173
+ ` npx fit-pathway job ${args[0]} <level> --track=${args[1]}`,
174
+ );
173
175
  console.error(`Levels: ${data.levels.map((g) => g.id).join(", ")}`);
174
176
  } else {
175
177
  console.error(`Level not found: ${args[1]}`);
@@ -4,15 +4,15 @@
4
4
  * Handles level summary, listing, and detail display in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway level # Summary with stats
8
- * bunx pathway level --list # IDs only (for piping)
9
- * bunx pathway level <id> # Detail view
10
- * bunx pathway level --validate # Validation checks
7
+ * npx fit-pathway level # Summary with stats
8
+ * npx fit-pathway level --list # IDs only (for piping)
9
+ * npx fit-pathway level <id> # Detail view
10
+ * npx fit-pathway level --validate # Validation checks
11
11
  */
12
12
 
13
13
  import { createEntityCommand } from "./command-factory.js";
14
14
  import { levelToMarkdown } from "../formatters/level/markdown.js";
15
- import { formatTable } from "../lib/cli-output.js";
15
+ import { formatTable } from "@forwardimpact/libcli";
16
16
  import { getConceptEmoji } from "@forwardimpact/map/levels";
17
17
  import { capitalize } from "../formatters/shared.js";
18
18
 
@@ -57,8 +57,8 @@ function formatSummary(levels, data) {
57
57
  ),
58
58
  );
59
59
  console.log(`\nTotal: ${levels.length} levels`);
60
- console.log(`\nRun 'bunx pathway level --list' for IDs and titles`);
61
- console.log(`Run 'bunx pathway level <id>' for details\n`);
60
+ console.log(`\nRun 'npx fit-pathway level --list' for IDs and titles`);
61
+ console.log(`Run 'npx fit-pathway level <id>' for details\n`);
62
62
  }
63
63
 
64
64
  /**
@@ -4,9 +4,9 @@
4
4
  * Shows career progression analysis in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway progress <discipline> <level> # Progress for trackless job
8
- * bunx pathway progress <discipline> <level> --track=<track> # Progress with track
9
- * bunx pathway progress <discipline> <from_level> --compare=<to_level> # Compare levels
7
+ * npx fit-pathway progress <discipline> <level> # Progress for trackless job
8
+ * npx fit-pathway progress <discipline> <level> --track=<track> # Progress with track
9
+ * npx fit-pathway progress <discipline> <from_level> --compare=<to_level> # Compare levels
10
10
  */
11
11
 
12
12
  import { createCompositeCommand } from "./command-factory.js";
@@ -80,5 +80,5 @@ export const runProgressCommand = createCompositeCommand({
80
80
  }),
81
81
  formatter: formatProgress,
82
82
  usageExample:
83
- "bunx pathway progress software_engineering L3 --track=platform --compare=L4",
83
+ "npx fit-pathway progress software_engineering L3 --track=platform --compare=L4",
84
84
  });
@@ -4,10 +4,10 @@
4
4
  * Browse and compare interview questions across skills and behaviours.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway questions # Summary with stats
8
- * bunx pathway questions --list # Question IDs (for piping)
9
- * bunx pathway questions --level=practitioner # Filter by level
10
- * bunx pathway questions --stats # Detailed statistics
7
+ * npx fit-pathway questions # Summary with stats
8
+ * npx fit-pathway questions --list # Question IDs (for piping)
9
+ * npx fit-pathway questions --level=practitioner # Filter by level
10
+ * npx fit-pathway questions --stats # Detailed statistics
11
11
  */
12
12
 
13
13
  import {
@@ -17,7 +17,7 @@ import {
17
17
  import { questionsToMarkdown } from "../formatters/questions/markdown.js";
18
18
  import { questionsToYaml } from "../formatters/questions/yaml.js";
19
19
  import { questionsToJson } from "../formatters/questions/json.js";
20
- import { formatTable } from "../lib/cli-output.js";
20
+ import { formatTable } from "@forwardimpact/libcli";
21
21
 
22
22
  /**
23
23
  * Parse questions command options
@@ -97,9 +97,11 @@ function showQuestionsSummary(data) {
97
97
  console.log("\nBehaviour Questions:");
98
98
  console.log(formatTable(["Maturity", "Count"], behaviourRows));
99
99
 
100
- console.log(`\nRun 'bunx pathway questions --list' for question IDs`);
101
- console.log(`Run 'bunx pathway questions --stats' for detailed stats`);
102
- console.log(`Run 'bunx pathway questions --level=practitioner' to filter\n`);
100
+ console.log(`\nRun 'npx fit-pathway questions --list' for question IDs`);
101
+ console.log(`Run 'npx fit-pathway questions --stats' for detailed stats`);
102
+ console.log(
103
+ `Run 'npx fit-pathway questions --level=practitioner' to filter\n`,
104
+ );
103
105
  }
104
106
 
105
107
  /**
@@ -4,18 +4,18 @@
4
4
  * Handles skill summary, listing, and detail display in the terminal.
5
5
  *
6
6
  * Usage:
7
- * bunx pathway skill # Summary with stats
8
- * bunx pathway skill --list # IDs only (for piping)
9
- * bunx pathway skill <id> # Detail view
10
- * bunx pathway skill <id> --agent # Agent SKILL.md output
11
- * bunx pathway skill --validate # Validation checks
7
+ * npx fit-pathway skill # Summary with stats
8
+ * npx fit-pathway skill --list # IDs only (for piping)
9
+ * npx fit-pathway skill <id> # Detail view
10
+ * npx fit-pathway skill <id> --agent # Agent SKILL.md output
11
+ * npx fit-pathway skill --validate # Validation checks
12
12
  */
13
13
 
14
14
  import { createEntityCommand } from "./command-factory.js";
15
15
  import { skillToMarkdown } from "../formatters/skill/markdown.js";
16
16
  import { prepareSkillsList } from "../formatters/skill/shared.js";
17
17
  import { getConceptEmoji } from "@forwardimpact/map/levels";
18
- import { formatTable, formatError } from "../lib/cli-output.js";
18
+ import { formatTable, formatError } from "@forwardimpact/libcli";
19
19
  import { generateSkillMarkdown } from "@forwardimpact/libskill/agent";
20
20
  import { formatAgentSkill } from "../formatters/agent/skill.js";
21
21
 
@@ -40,8 +40,8 @@ function formatSummary(skills, data) {
40
40
 
41
41
  console.log(formatTable(["Capability", "Count", "Agent"], rows));
42
42
  console.log(`\nTotal: ${skills.length} skills`);
43
- console.log(`\nRun 'bunx pathway skill --list' for IDs`);
44
- console.log(`Run 'bunx pathway skill <id>' for details\n`);
43
+ console.log(`\nRun 'npx fit-pathway skill --list' for IDs`);
44
+ console.log(`Run 'npx fit-pathway skill <id>' for details\n`);
45
45
  }
46
46
 
47
47
  /**
@@ -73,7 +73,7 @@ async function formatAgentDetail(skill, stages, templateLoader, dataDir) {
73
73
  console.error(formatError(`Skill '${skill.id}' has no agent section`));
74
74
  console.error(`\nSkills with agent support:`);
75
75
  console.error(
76
- ` bunx pathway skill --list | xargs -I{} sh -c 'bunx pathway skill {} --json | jq -e .skill.agent > /dev/null && echo {}'`,
76
+ ` npx fit-pathway skill --list | xargs -I{} sh -c 'npx fit-pathway skill {} --json | jq -e .skill.agent > /dev/null && echo {}'`,
77
77
  );
78
78
  process.exit(1);
79
79
  }