@forwardimpact/pathway 0.25.22 → 0.25.25

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 (40) 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-list.js +164 -0
  5. package/src/commands/agent.js +83 -184
  6. package/src/commands/behaviour.js +22 -10
  7. package/src/commands/build-packs.js +208 -34
  8. package/src/commands/build.js +2 -2
  9. package/src/commands/command-factory.js +39 -14
  10. package/src/commands/discipline.js +24 -10
  11. package/src/commands/driver.js +28 -19
  12. package/src/commands/index.js +0 -1
  13. package/src/commands/interview.js +15 -10
  14. package/src/commands/job.js +110 -62
  15. package/src/commands/level.js +23 -11
  16. package/src/commands/progress.js +12 -7
  17. package/src/commands/questions.js +32 -14
  18. package/src/commands/skill.js +36 -18
  19. package/src/commands/stage.js +37 -27
  20. package/src/commands/tool.js +29 -19
  21. package/src/commands/track.js +23 -10
  22. package/src/formatters/questions/yaml.js +1 -1
  23. package/src/index.html +1 -1
  24. package/src/lib/cli-command.js +33 -33
  25. package/src/lib/cli-output.js +9 -189
  26. package/src/pages/agent-builder-install.js +6 -5
  27. package/src/commands/init.js +0 -64
  28. package/starter/behaviours/systems_thinking.yaml +0 -32
  29. package/starter/capabilities/delivery.yaml +0 -105
  30. package/starter/capabilities/reliability.yaml +0 -72
  31. package/starter/disciplines/software_engineering.yaml +0 -46
  32. package/starter/drivers.yaml +0 -10
  33. package/starter/framework.yaml +0 -49
  34. package/starter/levels.yaml +0 -39
  35. package/starter/questions/behaviours/.gitkeep +0 -0
  36. package/starter/questions/capabilities/.gitkeep +0 -0
  37. package/starter/questions/skills/.gitkeep +0 -0
  38. package/starter/stages.yaml +0 -21
  39. package/starter/tracks/forward_deployed.yaml +0 -33
  40. package/starter/tracks/platform.yaml +0 -33
@@ -6,27 +6,7 @@
6
6
  * interview questions, career progression analysis, and AI agent configurations.
7
7
  *
8
8
  * Usage:
9
- * bunx fit-pathway <command> [options]
10
- *
11
- * Commands:
12
- * discipline [<id>] Show disciplines
13
- * level [<id>] Show levels
14
- * track [<id>] Show tracks
15
- * behaviour [<id>] Show behaviours
16
- * skill [<id>] Show skills (summary, --list, or detail)
17
- * driver [<id>] Show drivers
18
- * stage [<id>] Show stages
19
- * tool [<name>] Show tools
20
- * job [<discipline> <level>] [--track=TRACK] Generate job definition
21
- * interview <discipline> <level> [--track=TRACK] [--type=mission|decomposition|stakeholder] Generate interview
22
- * progress <discipline> <level> [--track=TRACK] [--compare=LEVEL] Career progression
23
- * questions [options] Browse interview questions
24
- * agent [<discipline> <track>] [--output=PATH] Generate AI agent
25
- *
26
- * Global Options:
27
- * --list Output IDs only (for piping)
28
- * --json Output as JSON
29
- * --help Show help
9
+ * npx fit-pathway <command> [options]
30
10
  */
31
11
 
32
12
  import { join, resolve, dirname } from "path";
@@ -38,7 +18,7 @@ import { createDataLoader } from "@forwardimpact/map/loader";
38
18
  import { validateAllData } from "@forwardimpact/map/validation";
39
19
  import { Finder } from "@forwardimpact/libutil";
40
20
  import { createLogger } from "@forwardimpact/libtelemetry";
41
- import { formatError } from "../src/lib/cli-output.js";
21
+ import { createCli } from "@forwardimpact/libcli";
42
22
  import { createTemplateLoader } from "@forwardimpact/libtemplate";
43
23
 
44
24
  // Import command handlers
@@ -56,7 +36,6 @@ import { runProgressCommand } from "../src/commands/progress.js";
56
36
  import { runQuestionsCommand } from "../src/commands/questions.js";
57
37
  import { runAgentCommand } from "../src/commands/agent.js";
58
38
  import { runDevCommand } from "../src/commands/dev.js";
59
- import { runInitCommand } from "../src/commands/init.js";
60
39
  import { runBuildCommand } from "../src/commands/build.js";
61
40
  import { runUpdateCommand } from "../src/commands/update.js";
62
41
 
@@ -84,343 +63,156 @@ const COMMANDS = {
84
63
  agent: runAgentCommand,
85
64
  };
86
65
 
87
- const HELP_TEXT = `
88
- Engineering Pathway CLI
89
-
90
- Usage:
91
- bunx fit-pathway <command> [options]
92
-
93
- Global Options:
94
- --list Output IDs only (for piping to other commands)
95
- --json Output as JSON
96
- --data=PATH Path to data directory (default: ./data)
97
- --version Show version number
98
- --help Show this help message
99
-
100
- ────────────────────────────────────────────────────────────────────────────────
101
- GETTING STARTED
102
- ────────────────────────────────────────────────────────────────────────────────
103
-
104
- init Create ./data/ with example data
105
- dev [--port=PORT] Run live development server
106
- build [--output=PATH] [--url=URL] Generate static site + distribution bundle
107
- update [--url=URL] Update local ~/.fit/data/pathway/ installation
108
-
109
- ────────────────────────────────────────────────────────────────────────────────
110
- ENTITY COMMANDS
111
- ────────────────────────────────────────────────────────────────────────────────
112
-
113
- All entity commands support: summary (default), --list (IDs for piping), <id> (detail)
114
-
115
- discipline [<id>] Browse engineering disciplines
116
- level [<id>] Browse career levels
117
- track [<id>] Browse track specializations
118
- behaviour [<id>] Browse professional behaviours
119
- driver [<id>] Browse outcome drivers
120
- stage [<id>] Browse lifecycle stages
121
-
122
- skill [<id>] Browse skills
123
- --agent Output as agent SKILL.md format
124
-
125
- tool [<name>] Browse required tools (aggregated from skills)
126
-
127
- ────────────────────────────────────────────────────────────────────────────────
128
- JOB COMMAND
129
- ────────────────────────────────────────────────────────────────────────────────
130
-
131
- Generate job definitions from discipline × level × track combinations.
132
-
133
- Usage:
134
- bunx fit-pathway job Summary with stats
135
- bunx fit-pathway job --track=<track> Summary filtered by track
136
- bunx fit-pathway job --list All valid combinations
137
- bunx fit-pathway job --list --track=<track> Combinations for a track
138
- bunx fit-pathway job <discipline> <level> Detail view (trackless)
139
- bunx fit-pathway job <d> <l> --track=<track> Detail view (with track)
140
- bunx fit-pathway job <d> <l> --skills Plain list of skill IDs
141
- bunx fit-pathway job <d> <l> --tools Plain list of tool names
142
- bunx fit-pathway job <d> <l> --checklist=<stage> Show handoff checklist
143
-
144
- Options:
145
- --track=TRACK Track specialization (e.g., platform, forward_deployed)
146
- Also filters --list and summary modes
147
- --skills Output plain list of skill IDs (for piping)
148
- --tools Output plain list of tool names (for piping)
149
- --checklist=STAGE Show checklist for stage handoff (plan, code)
150
-
151
- Examples:
152
- bunx fit-pathway job # overview of all jobs
153
- bunx fit-pathway job --track=forward_deployed # jobs on a specific track
154
- bunx fit-pathway job --list --track=forward_deployed # list for piping
155
- bunx fit-pathway job software_engineering J060 # trackless job detail
156
- bunx fit-pathway job software_engineering J060 --track=platform # with track
157
-
158
- ────────────────────────────────────────────────────────────────────────────────
159
- AGENT COMMAND
160
- ────────────────────────────────────────────────────────────────────────────────
161
-
162
- Generate AI coding agent configurations from discipline × track × stage.
163
-
164
- Usage:
165
- bunx fit-pathway agent Summary with stats
166
- bunx fit-pathway agent --list All valid combinations
167
- bunx fit-pathway agent <discipline> --track=<track> Generate all stage agents
168
- bunx fit-pathway agent <d> --track=<t> --stage=<s> Generate single stage agent
169
- bunx fit-pathway agent <d> --track=<t> --skills Plain list of skill IDs
170
- bunx fit-pathway agent <d> --track=<t> --tools Plain list of tool names
171
-
172
- Options:
173
- --track=TRACK Track for the agent (required for generation)
174
- --stage=STAGE Generate specific stage agent (plan, code, review)
175
- --output=PATH Write files to directory (without this, outputs to console)
176
- --skills Output plain list of skill IDs (for piping)
177
- --tools Output plain list of tool names (for piping)
178
-
179
- Examples:
180
- bunx fit-pathway agent software_engineering --track=platform
181
- bunx fit-pathway agent software_engineering --track=platform --stage=plan
182
- bunx fit-pathway agent software_engineering --track=platform --output=./agents
183
- bunx fit-pathway agent software_engineering --track=platform --skills
184
-
185
- ────────────────────────────────────────────────────────────────────────────────
186
- INTERVIEW COMMAND
187
- ────────────────────────────────────────────────────────────────────────────────
188
-
189
- Generate interview question sets based on job requirements.
190
-
191
- Usage:
192
- bunx fit-pathway interview <discipline> <level> All types
193
- bunx fit-pathway interview <d> <l> --track=<track> With track
194
- bunx fit-pathway interview <d> <l> --track=<t> --type=<type> Single type
195
-
196
- Options:
197
- --track=TRACK Track specialization
198
- --type=TYPE Interview type: mission, decomposition, stakeholder
199
- (omit for all types)
200
-
201
- ────────────────────────────────────────────────────────────────────────────────
202
- PROGRESS COMMAND
203
- ────────────────────────────────────────────────────────────────────────────────
204
-
205
- Analyze career progression between levels.
206
-
207
- Usage:
208
- bunx fit-pathway progress <discipline> <level>
209
- bunx fit-pathway progress <d> <l> --track=<track>
210
- bunx fit-pathway progress <d> <l> --compare=<to_level>
211
-
212
- Options:
213
- --track=TRACK Track specialization
214
- --compare=LEVEL Compare to specific level
215
-
216
- ────────────────────────────────────────────────────────────────────────────────
217
- QUESTIONS COMMAND
218
- ────────────────────────────────────────────────────────────────────────────────
219
-
220
- Browse and filter interview questions.
221
-
222
- Usage:
223
- bunx fit-pathway questions
224
- bunx fit-pathway questions --level=practitioner
225
- bunx fit-pathway questions --skill=architecture_design
226
- bunx fit-pathway questions --stats
227
-
228
- Options:
229
- --level=LEVEL Filter by skill proficiency
230
- --maturity=MATURITY Filter by behaviour maturity
231
- --skill=ID Filter to specific skill
232
- --behaviour=ID Filter to specific behaviour
233
- --capability=CAP Filter by capability
234
- --stats Show detailed statistics
235
- --format=FORMAT Output format: table, yaml, json
236
- `;
237
-
238
- /** Boolean flags: exact match sets the field to true */
239
- const BOOLEAN_FLAGS = {
240
- "--version": "version",
241
- "-v": "version",
242
- "--help": "help",
243
- "-h": "help",
244
- "--list": "list",
245
- "-l": "list",
246
- "--json": "json",
247
- "--stats": "stats",
248
- "--all-roles": "all-roles",
249
- "--all-stages": "all-stages",
250
- "--agent": "agent",
251
- "--skills": "skills",
252
- "--tools": "tools",
253
- };
254
-
255
- /** Negation flags: exact match sets the field to false */
256
- const NEGATION_FLAGS = { "--no-clean": "clean" };
257
-
258
- /** Value flags: --key=val sets result[field] = val */
259
- const VALUE_FLAGS = {
260
- "--type": "type",
261
- "--compare": "compare",
262
- "--data": "data",
263
- "--track": "track",
264
- "--output": "output",
265
- "--level": "level",
266
- "--maturity": "maturity",
267
- "--skill": "skill",
268
- "--behaviour": "behaviour",
269
- "--capability": "capability",
270
- "--format": "format",
271
- "--role": "role",
272
- "--stage": "stage",
273
- "--checklist": "checklist",
274
- "--path": "path",
275
- "--url": "url",
66
+ const definition = {
67
+ name: "fit-pathway",
68
+ version: VERSION,
69
+ description: "Career progression for engineering frameworks",
70
+ commands: [
71
+ { name: "discipline", args: "[<id>]", description: "Show disciplines" },
72
+ { name: "level", args: "[<id>]", description: "Show levels" },
73
+ { name: "track", args: "[<id>]", description: "Show tracks" },
74
+ { name: "behaviour", args: "[<id>]", description: "Show behaviours" },
75
+ { name: "skill", args: "[<id>]", description: "Show skills" },
76
+ { name: "driver", args: "[<id>]", description: "Show drivers" },
77
+ { name: "stage", args: "[<id>]", description: "Show stages" },
78
+ { name: "tool", args: "[<name>]", description: "Show tools" },
79
+ {
80
+ name: "job",
81
+ args: "[<discipline> <level>]",
82
+ description: "Generate job definition",
83
+ },
84
+ {
85
+ name: "interview",
86
+ args: "<discipline> <level>",
87
+ description: "Generate interview questions",
88
+ },
89
+ {
90
+ name: "progress",
91
+ args: "<discipline> <level>",
92
+ description: "Career progression analysis",
93
+ },
94
+ {
95
+ name: "questions",
96
+ args: "[options]",
97
+ description: "Browse interview questions",
98
+ },
99
+ {
100
+ name: "agent",
101
+ args: "[<discipline>]",
102
+ description: "Generate AI agent profile",
103
+ },
104
+ {
105
+ name: "dev",
106
+ args: "[--port=PORT]",
107
+ description: "Run live development server",
108
+ },
109
+ {
110
+ name: "build",
111
+ args: "[--output=PATH]",
112
+ description: "Generate static site",
113
+ },
114
+ {
115
+ name: "update",
116
+ args: "[--url=URL]",
117
+ description: "Update local installation",
118
+ },
119
+ ],
120
+ options: {
121
+ list: {
122
+ type: "boolean",
123
+ short: "l",
124
+ description: "Output IDs only (for piping)",
125
+ },
126
+ json: { type: "boolean", description: "Output as JSON" },
127
+ data: { type: "string", description: "Path to data directory" },
128
+ track: { type: "string", description: "Track specialization" },
129
+ level: { type: "string", description: "Target level" },
130
+ type: { type: "string", description: "Interview type", default: "full" },
131
+ compare: { type: "string", description: "Compare to level" },
132
+ format: { type: "string", description: "Output format" },
133
+ output: { type: "string", description: "Output path" },
134
+ stage: { type: "string", description: "Lifecycle stage" },
135
+ checklist: { type: "string", description: "Handoff checklist stage" },
136
+ maturity: {
137
+ type: "string",
138
+ description: "Filter by behaviour maturity",
139
+ },
140
+ skill: { type: "string", description: "Filter by skill ID" },
141
+ behaviour: { type: "string", description: "Filter by behaviour ID" },
142
+ capability: { type: "string", description: "Filter by capability" },
143
+ port: { type: "string", description: "Dev server port" },
144
+ path: { type: "string", description: "File path" },
145
+ url: { type: "string", description: "URL for update" },
146
+ role: { type: "string", description: "Role filter" },
147
+ stats: { type: "boolean", description: "Show detailed statistics" },
148
+ "all-stages": { type: "boolean", description: "Show all stages" },
149
+ agent: { type: "boolean", description: "Output as agent format" },
150
+ skills: { type: "boolean", description: "Output skill IDs" },
151
+ tools: { type: "boolean", description: "Output tool names" },
152
+ clean: { type: "boolean", default: true, description: "Clean build" },
153
+ help: { type: "boolean", short: "h", description: "Show this help" },
154
+ version: { type: "boolean", short: "v", description: "Show version" },
155
+ },
156
+ examples: [
157
+ "fit-pathway discipline backend",
158
+ "fit-pathway job software_engineering J060 --track=platform",
159
+ "fit-pathway interview software_engineering J060 --json",
160
+ "fit-pathway agent software_engineering --track=platform",
161
+ ],
276
162
  };
277
163
 
278
- /**
279
- * Try to parse a --key=value argument using the VALUE_FLAGS table
280
- * @param {string} arg
281
- * @param {Object} result
282
- * @returns {boolean} true if the arg was handled
283
- */
284
- function parseValueFlag(arg, result) {
285
- const eqIndex = arg.indexOf("=");
286
- if (eqIndex === -1) return false;
287
- const key = arg.slice(0, eqIndex);
288
- const field = VALUE_FLAGS[key];
289
- if (!field) return false;
290
- result[field] = arg.slice(eqIndex + 1);
291
- return true;
292
- }
293
-
294
- /**
295
- * Parse command line arguments
296
- * @param {string[]} args
297
- * @returns {Object}
298
- */
299
- function parseArgs(args) {
300
- const result = {
301
- command: null,
302
- args: [],
303
- list: false,
304
- json: false,
305
- help: false,
306
- version: false,
307
- type: "full",
308
- compare: null,
309
- data: null,
310
- track: null,
311
- level: null,
312
- maturity: null,
313
- skill: null,
314
- behaviour: null,
315
- capability: null,
316
- format: null,
317
- stats: false,
318
- checklist: null,
319
- skills: false,
320
- tools: false,
321
- output: null,
322
- stage: null,
323
- "all-stages": false,
324
- agent: false,
325
- port: null,
326
- path: null,
327
- clean: true,
328
- url: null,
329
- };
330
-
331
- for (const arg of args) {
332
- if (BOOLEAN_FLAGS[arg]) {
333
- result[BOOLEAN_FLAGS[arg]] = true;
334
- } else if (NEGATION_FLAGS[arg]) {
335
- result[NEGATION_FLAGS[arg]] = false;
336
- } else if (arg.startsWith("--port=")) {
337
- result.port = parseInt(arg.slice(7), 10);
338
- } else if (parseValueFlag(arg, result)) {
339
- // handled
340
- } else if (!arg.startsWith("-")) {
341
- if (!result.command) {
342
- result.command = arg;
343
- } else {
344
- result.args.push(arg);
345
- }
346
- }
347
- }
348
-
349
- return result;
350
- }
351
-
352
- /**
353
- * Print help text
354
- */
355
- function printHelp() {
356
- console.log(HELP_TEXT);
357
- }
358
-
359
164
  /**
360
165
  * Main CLI entry point
361
166
  */
362
167
  async function main() {
363
- const args = process.argv.slice(2);
364
- const options = parseArgs(args);
168
+ const cli = createCli(definition);
169
+ const parsed = cli.parse(process.argv.slice(2));
170
+ if (!parsed) process.exit(0);
365
171
 
366
- if (options.version) {
367
- console.log(VERSION);
368
- process.exit(0);
369
- }
172
+ const { values, positionals } = parsed;
173
+ const [command, ...args] = positionals;
370
174
 
371
- if (options.help) {
372
- printHelp();
373
- process.exit(0);
374
- }
375
-
376
- if (!options.command) {
377
- printHelp();
378
- process.exit(0);
379
- }
380
-
381
- const command = options.command;
382
-
383
- if (command === "init") {
384
- await runInitCommand({ options });
175
+ if (!command) {
176
+ cli.showHelp();
385
177
  process.exit(0);
386
178
  }
387
179
 
388
180
  let dataDir;
389
- if (options.data) {
390
- dataDir = resolve(options.data);
181
+ if (values.data) {
182
+ dataDir = resolve(values.data);
391
183
  } else {
392
184
  const logger = createLogger("pathway");
393
185
  const finder = new Finder(fs, logger, process);
394
186
  try {
395
187
  dataDir = join(finder.findData("data", homedir()), "pathway");
396
188
  } catch {
397
- throw new Error(
189
+ cli.error(
398
190
  "No data directory found. Use --data=<path> to specify location.",
399
191
  );
192
+ process.exit(1);
400
193
  }
401
194
  }
402
195
 
403
196
  if (command === "dev") {
404
- await runDevCommand({ dataDir, options });
197
+ await runDevCommand({ dataDir, options: values });
405
198
  return;
406
199
  }
407
200
 
408
201
  if (command === "build") {
409
- await runBuildCommand({ dataDir, options });
202
+ await runBuildCommand({ dataDir, options: values });
410
203
  process.exit(0);
411
204
  }
412
205
 
413
206
  if (command === "update") {
414
- await runUpdateCommand({ dataDir, options });
207
+ await runUpdateCommand({ dataDir, options: values });
415
208
  process.exit(0);
416
209
  }
417
210
 
418
211
  const handler = COMMANDS[command];
419
212
 
420
213
  if (!handler) {
421
- console.error(formatError(`Unknown command: ${command}`));
422
- console.error(`Run 'bunx fit-pathway --help' for usage.`);
423
- process.exit(1);
214
+ cli.usageError(`unknown command "${command}"`);
215
+ process.exit(2);
424
216
  }
425
217
 
426
218
  try {
@@ -432,14 +224,14 @@ async function main() {
432
224
 
433
225
  await handler({
434
226
  data,
435
- args: options.args,
436
- options,
227
+ args,
228
+ options: values,
437
229
  dataDir,
438
230
  templateLoader,
439
231
  loader,
440
232
  });
441
233
  } catch (error) {
442
- console.error(formatError(error.message));
234
+ cli.error(error.message);
443
235
  process.exit(1);
444
236
  }
445
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.25.22",
3
+ "version": "0.25.25",
4
4
  "description": "Career progression web app and CLI for exploring roles and generating agent teams",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -31,7 +31,6 @@
31
31
  "files": [
32
32
  "bin/",
33
33
  "src/",
34
- "starter/",
35
34
  "templates/"
36
35
  ],
37
36
  "exports": {
@@ -40,6 +39,7 @@
40
39
  },
41
40
  "dependencies": {
42
41
  "@forwardimpact/map": "^0.15.5",
42
+ "@forwardimpact/libcli": "^0.1.0",
43
43
  "@forwardimpact/libskill": "^4.0.0",
44
44
  "@forwardimpact/libtemplate": "^0.2.0",
45
45
  "@forwardimpact/libui": "^1.0.0",
@@ -14,7 +14,7 @@ import {
14
14
  formatInstallScript,
15
15
  formatReference,
16
16
  } from "../formatters/agent/skill.js";
17
- import { formatSuccess } from "../lib/cli-output.js";
17
+ import { formatSuccess } from "@forwardimpact/libcli";
18
18
 
19
19
  /**
20
20
  * Ensure directory exists for a file path