@forwardimpact/pathway 0.25.24 → 0.25.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.25.24",
3
+ "version": "0.25.26",
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": {
@@ -20,6 +20,7 @@
20
20
  "engineering"
21
21
  ],
22
22
  "type": "module",
23
+ "main": "./src/index.js",
23
24
  "bin": {
24
25
  "fit-pathway": "./bin/fit-pathway.js"
25
26
  },
@@ -34,6 +35,7 @@
34
35
  "templates/"
35
36
  ],
36
37
  "exports": {
38
+ ".": "./src/index.js",
37
39
  "./formatters": "./src/formatters/index.js",
38
40
  "./commands": "./src/commands/index.js"
39
41
  },
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Agent listing and summary helpers
3
+ *
4
+ * Splits the listing / summary output out of agent.js so the command file
5
+ * stays focused on orchestration.
6
+ */
7
+
8
+ import {
9
+ getDisciplineAbbreviation,
10
+ toKebabCase,
11
+ } from "@forwardimpact/libskill/agent";
12
+ import {
13
+ formatHeader,
14
+ formatSubheader,
15
+ formatBullet,
16
+ SummaryRenderer,
17
+ } from "@forwardimpact/libcli";
18
+
19
+ const summary = new SummaryRenderer({ process });
20
+
21
+ /**
22
+ * Find valid agent combination pairs
23
+ * @param {Object} data - Pathway data
24
+ * @param {Object} agentData - Agent-specific data
25
+ * @returns {Array<{discipline: Object, track: Object, humanDiscipline: Object, humanTrack: Object}>}
26
+ */
27
+ export function findValidCombinations(data, agentData) {
28
+ const pairs = [];
29
+ for (const discipline of agentData.disciplines) {
30
+ for (const track of agentData.tracks) {
31
+ const humanDiscipline = data.disciplines.find(
32
+ (d) => d.id === discipline.id,
33
+ );
34
+ const humanTrack = data.tracks.find((t) => t.id === track.id);
35
+ if (humanDiscipline && humanTrack) {
36
+ pairs.push({ discipline, track, humanDiscipline, humanTrack });
37
+ }
38
+ }
39
+ }
40
+ return pairs;
41
+ }
42
+
43
+ /**
44
+ * Show agent summary with stats
45
+ * @param {Object} data - Pathway data
46
+ * @param {Object} agentData - Agent-specific data
47
+ * @param {Array} skillsWithAgent - Skills with agent sections
48
+ */
49
+ export function showAgentSummary(data, agentData, skillsWithAgent) {
50
+ const validCombinations = findValidCombinations(data, agentData).length;
51
+ const skillsWithAgentCount = skillsWithAgent.filter((s) => s.agent).length;
52
+
53
+ process.stdout.write("\n" + formatHeader("\u{1F916} Agent") + "\n\n");
54
+ summary.render({
55
+ title: formatSubheader("Coverage"),
56
+ items: [
57
+ {
58
+ label: "Disciplines",
59
+ description: `${agentData.disciplines.length}/${data.disciplines.length} with agent definitions`,
60
+ },
61
+ {
62
+ label: "Tracks",
63
+ description: `${agentData.tracks.length}/${data.tracks.length} with agent definitions`,
64
+ },
65
+ {
66
+ label: "Skills",
67
+ description: `${skillsWithAgentCount}/${skillsWithAgent.length} with agent sections`,
68
+ },
69
+ {
70
+ label: "Stages",
71
+ description: `${data.stages.length} available`,
72
+ },
73
+ ],
74
+ });
75
+ process.stdout.write(
76
+ "\n" + formatSubheader(`Valid combinations: ${validCombinations}`) + "\n\n",
77
+ );
78
+ process.stdout.write(
79
+ formatBullet("Run 'npx fit-pathway agent --list' for all combinations") +
80
+ "\n",
81
+ );
82
+ process.stdout.write(
83
+ formatBullet(
84
+ "Run 'npx fit-pathway agent <discipline> <track>' to generate files",
85
+ ) + "\n\n",
86
+ );
87
+ }
88
+
89
+ /**
90
+ * List available agent combinations — compact output for piping
91
+ * @param {Object} data - Pathway data
92
+ * @param {Object} agentData - Agent-specific data
93
+ */
94
+ function listAgentCombinationsCompact(data, agentData) {
95
+ for (const {
96
+ discipline,
97
+ track,
98
+ humanDiscipline,
99
+ humanTrack,
100
+ } of findValidCombinations(data, agentData)) {
101
+ const abbrev = getDisciplineAbbreviation(discipline.id);
102
+ const agentName = `${abbrev}-${toKebabCase(track.id)}`;
103
+ const specName = humanDiscipline.specialization || humanDiscipline.id;
104
+ // Piped output — keep as plain console.log for stable format
105
+ console.log(
106
+ `${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
107
+ );
108
+ }
109
+ }
110
+
111
+ /**
112
+ * List available agent combinations — verbose output (markdown)
113
+ * @param {Object} data - Pathway data
114
+ * @param {Object} agentData - Agent-specific data
115
+ */
116
+ function listAgentCombinationsVerbose(data, agentData) {
117
+ // Markdown headings stay literal so downstream tools can parse them.
118
+ process.stdout.write("# 🤖 Available Agent Combinations\n\n");
119
+
120
+ const agentDisciplineIds = new Set(agentData.disciplines.map((d) => d.id));
121
+ const agentTrackIds = new Set(agentData.tracks.map((t) => t.id));
122
+
123
+ process.stdout.write("## Disciplines with agent definitions:\n\n");
124
+ for (const discipline of data.disciplines) {
125
+ const status = agentDisciplineIds.has(discipline.id) ? "✅" : "⬜";
126
+ process.stdout.write(
127
+ ` ${status} ${discipline.id} - ${discipline.specialization || discipline.name}\n`,
128
+ );
129
+ }
130
+
131
+ process.stdout.write("\n## Tracks with agent definitions:\n\n");
132
+ for (const track of data.tracks) {
133
+ const status = agentTrackIds.has(track.id) ? "✅" : "⬜";
134
+ process.stdout.write(` ${status} ${track.id} - ${track.name}\n`);
135
+ }
136
+
137
+ process.stdout.write("\n## Valid combinations:\n\n");
138
+ for (const { discipline, track } of findValidCombinations(data, agentData)) {
139
+ process.stdout.write(
140
+ ` npx fit-pathway agent ${discipline.id} ${track.id}\n`,
141
+ );
142
+ }
143
+
144
+ process.stdout.write("\n## Available stages:\n\n");
145
+ for (const stage of data.stages) {
146
+ process.stdout.write(
147
+ ` --stage=${stage.id}: ${stage.description.split(" - ")[0]}\n`,
148
+ );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * List available agent combinations
154
+ * @param {Object} data - Pathway data
155
+ * @param {Object} agentData - Agent-specific data
156
+ * @param {boolean} verbose - Show verbose output
157
+ */
158
+ export function listAgentCombinations(data, agentData, verbose = false) {
159
+ if (verbose) {
160
+ listAgentCombinationsVerbose(data, agentData);
161
+ } else {
162
+ listAgentCombinationsCompact(data, agentData);
163
+ }
164
+ }
@@ -32,14 +32,18 @@ import {
32
32
  deriveReferenceLevel,
33
33
  deriveAgentSkills,
34
34
  generateSkillMarkdown,
35
- getDisciplineAbbreviation,
36
- toKebabCase,
37
35
  interpolateTeamInstructions,
38
36
  } from "@forwardimpact/libskill/agent";
39
37
  import { deriveToolkit } from "@forwardimpact/libskill/toolkit";
40
38
  import { formatAgentProfile } from "../formatters/agent/profile.js";
41
- import { formatError, formatSuccess } from "@forwardimpact/libcli";
39
+ import {
40
+ formatError,
41
+ formatSuccess,
42
+ formatSubheader,
43
+ formatBullet,
44
+ } from "@forwardimpact/libcli";
42
45
  import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
46
+ import { showAgentSummary, listAgentCombinations } from "./agent-list.js";
43
47
  import {
44
48
  generateClaudeCodeSettings,
45
49
  writeProfile,
@@ -47,139 +51,9 @@ import {
47
51
  writeSkills,
48
52
  } from "./agent-io.js";
49
53
 
50
- /**
51
- * Show agent summary with stats
52
- * @param {Object} data - Pathway data
53
- * @param {Object} agentData - Agent-specific data
54
- * @param {Array} skillsWithAgent - Skills with agent sections
55
- */
56
- function showAgentSummary(data, agentData, skillsWithAgent) {
57
- // Count valid combinations
58
- let validCombinations = 0;
59
- for (const discipline of agentData.disciplines) {
60
- for (const track of agentData.tracks) {
61
- const humanDiscipline = data.disciplines.find(
62
- (d) => d.id === discipline.id,
63
- );
64
- const humanTrack = data.tracks.find((t) => t.id === track.id);
65
- if (humanDiscipline && humanTrack) {
66
- validCombinations++;
67
- }
68
- }
69
- }
70
-
71
- const skillsWithAgentCount = skillsWithAgent.filter((s) => s.agent).length;
72
-
73
- console.log(`\n🤖 Agent\n`);
74
- console.log(
75
- `Disciplines: ${agentData.disciplines.length}/${data.disciplines.length} with agent definitions`,
76
- );
77
- console.log(
78
- `Tracks: ${agentData.tracks.length}/${data.tracks.length} with agent definitions`,
79
- );
80
- console.log(
81
- `Skills: ${skillsWithAgentCount}/${skillsWithAgent.length} with agent sections`,
82
- );
83
- console.log(`Stages: ${data.stages.length} available`);
84
- console.log(`\nValid combinations: ${validCombinations}`);
85
- console.log(`\nRun 'npx fit-pathway agent --list' for all combinations`);
86
- console.log(
87
- `Run 'npx fit-pathway agent <discipline> <track>' to generate files\n`,
88
- );
89
- }
90
-
91
- /**
92
- * Find valid agent combination pairs
93
- * @param {Object} data - Pathway data
94
- * @param {Object} agentData - Agent-specific data
95
- * @returns {Array<{discipline: Object, track: Object, humanDiscipline: Object, humanTrack: Object}>}
96
- */
97
- export function findValidCombinations(data, agentData) {
98
- const pairs = [];
99
- for (const discipline of agentData.disciplines) {
100
- for (const track of agentData.tracks) {
101
- const humanDiscipline = data.disciplines.find(
102
- (d) => d.id === discipline.id,
103
- );
104
- const humanTrack = data.tracks.find((t) => t.id === track.id);
105
- if (humanDiscipline && humanTrack) {
106
- pairs.push({ discipline, track, humanDiscipline, humanTrack });
107
- }
108
- }
109
- }
110
- return pairs;
111
- }
112
-
113
- /**
114
- * List available agent combinations — compact output for piping
115
- * @param {Object} data - Pathway data
116
- * @param {Object} agentData - Agent-specific data
117
- */
118
- function listAgentCombinationsCompact(data, agentData) {
119
- for (const {
120
- discipline,
121
- track,
122
- humanDiscipline,
123
- humanTrack,
124
- } of findValidCombinations(data, agentData)) {
125
- const abbrev = getDisciplineAbbreviation(discipline.id);
126
- const agentName = `${abbrev}-${toKebabCase(track.id)}`;
127
- const specName = humanDiscipline.specialization || humanDiscipline.id;
128
- console.log(
129
- `${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
130
- );
131
- }
132
- }
133
-
134
- /**
135
- * List available agent combinations — verbose output
136
- * @param {Object} data - Pathway data
137
- * @param {Object} agentData - Agent-specific data
138
- */
139
- function listAgentCombinationsVerbose(data, agentData) {
140
- console.log("# 🤖 Available Agent Combinations\n");
141
-
142
- const agentDisciplineIds = new Set(agentData.disciplines.map((d) => d.id));
143
- const agentTrackIds = new Set(agentData.tracks.map((t) => t.id));
144
-
145
- console.log("## Disciplines with agent definitions:\n");
146
- for (const discipline of data.disciplines) {
147
- const status = agentDisciplineIds.has(discipline.id) ? "✅" : "⬜";
148
- console.log(
149
- ` ${status} ${discipline.id} - ${discipline.specialization || discipline.name}`,
150
- );
151
- }
152
-
153
- console.log("\n## Tracks with agent definitions:\n");
154
- for (const track of data.tracks) {
155
- const status = agentTrackIds.has(track.id) ? "✅" : "⬜";
156
- console.log(` ${status} ${track.id} - ${track.name}`);
157
- }
158
-
159
- console.log("\n## Valid combinations:\n");
160
- for (const { discipline, track } of findValidCombinations(data, agentData)) {
161
- console.log(` npx fit-pathway agent ${discipline.id} ${track.id}`);
162
- }
163
-
164
- console.log("\n## Available stages:\n");
165
- for (const stage of data.stages) {
166
- console.log(` --stage=${stage.id}: ${stage.description.split(" - ")[0]}`);
167
- }
168
- }
169
-
170
- /**
171
- * List available agent combinations (clean output for piping)
172
- * @param {Object} data - Pathway data
173
- * @param {Object} agentData - Agent-specific data
174
- * @param {boolean} verbose - Show verbose output
175
- */
176
- function listAgentCombinations(data, agentData, verbose = false) {
177
- if (verbose) {
178
- listAgentCombinationsVerbose(data, agentData);
179
- } else {
180
- listAgentCombinationsCompact(data, agentData);
181
- }
182
- }
54
+ // Re-export so downstream consumers (build-packs, tests) can import
55
+ // findValidCombinations from './commands/agent.js' as before.
56
+ export { findValidCombinations } from "./agent-list.js";
183
57
 
184
58
  /**
185
59
  * Resolve and validate human + agent entities for a discipline/track pair
@@ -194,15 +68,21 @@ function resolveAgentEntities(data, agentData, disciplineId, trackId) {
194
68
  const humanTrack = trackId ? data.tracks.find((t) => t.id === trackId) : null;
195
69
 
196
70
  if (!humanDiscipline) {
197
- console.error(formatError(`Unknown discipline: ${disciplineId}`));
198
- console.error("\nAvailable disciplines:");
199
- for (const d of data.disciplines) console.error(` - ${d.id}`);
71
+ process.stderr.write(
72
+ formatError(`Unknown discipline: ${disciplineId}`) + "\n",
73
+ );
74
+ process.stderr.write("\nAvailable disciplines:\n");
75
+ for (const d of data.disciplines) {
76
+ process.stderr.write(formatBullet(d.id, 1) + "\n");
77
+ }
200
78
  process.exit(1);
201
79
  }
202
80
  if (trackId && !humanTrack) {
203
- console.error(formatError(`Unknown track: ${trackId}`));
204
- console.error("\nAvailable tracks:");
205
- for (const t of data.tracks) console.error(` - ${t.id}`);
81
+ process.stderr.write(formatError(`Unknown track: ${trackId}`) + "\n");
82
+ process.stderr.write("\nAvailable tracks:\n");
83
+ for (const t of data.tracks) {
84
+ process.stderr.write(formatBullet(t.id, 1) + "\n");
85
+ }
206
86
  process.exit(1);
207
87
  }
208
88
 
@@ -214,17 +94,23 @@ function resolveAgentEntities(data, agentData, disciplineId, trackId) {
214
94
  : null;
215
95
 
216
96
  if (!agentDiscipline) {
217
- console.error(
218
- formatError(`No agent definition for discipline: ${disciplineId}`),
97
+ process.stderr.write(
98
+ formatError(`No agent definition for discipline: ${disciplineId}`) + "\n",
219
99
  );
220
- console.error("\nAgent definitions exist for:");
221
- for (const d of agentData.disciplines) console.error(` - ${d.id}`);
100
+ process.stderr.write("\nAgent definitions exist for:\n");
101
+ for (const d of agentData.disciplines) {
102
+ process.stderr.write(formatBullet(d.id, 1) + "\n");
103
+ }
222
104
  process.exit(1);
223
105
  }
224
106
  if (trackId && !agentTrack) {
225
- console.error(formatError(`No agent definition for track: ${trackId}`));
226
- console.error("\nAgent definitions exist for:");
227
- for (const t of agentData.tracks) console.error(` - ${t.id}`);
107
+ process.stderr.write(
108
+ formatError(`No agent definition for track: ${trackId}`) + "\n",
109
+ );
110
+ process.stderr.write("\nAgent definitions exist for:\n");
111
+ for (const t of agentData.tracks) {
112
+ process.stderr.write(formatBullet(t.id, 1) + "\n");
113
+ }
228
114
  process.exit(1);
229
115
  }
230
116
 
@@ -242,9 +128,10 @@ function printTeamInstructions(agentTrack, humanDiscipline) {
242
128
  humanDiscipline,
243
129
  });
244
130
  if (teamInstructions) {
245
- console.log("# Team Instructions (CLAUDE.md)\n");
246
- console.log(teamInstructions.trim());
247
- console.log("\n---\n");
131
+ // Markdown output — headings stay literal so downstream tools parse them
132
+ process.stdout.write("# Team Instructions (CLAUDE.md)\n\n");
133
+ process.stdout.write(teamInstructions.trim() + "\n");
134
+ process.stdout.write("\n---\n\n");
248
135
  }
249
136
  }
250
137
 
@@ -264,17 +151,21 @@ async function handleSingleStage({
264
151
  }) {
265
152
  const stage = data.stages.find((s) => s.id === options.stage);
266
153
  if (!stage) {
267
- console.error(formatError(`Unknown stage: ${options.stage}`));
268
- console.error("\nAvailable stages:");
269
- for (const s of data.stages) console.error(` - ${s.id}`);
154
+ process.stderr.write(formatError(`Unknown stage: ${options.stage}`) + "\n");
155
+ process.stderr.write("\nAvailable stages:\n");
156
+ for (const s of data.stages) {
157
+ process.stderr.write(formatBullet(s.id, 1) + "\n");
158
+ }
270
159
  process.exit(1);
271
160
  }
272
161
 
273
162
  const profile = generateStageAgentProfile({ ...stageParams, stage });
274
163
  const errors = validateAgentProfile(profile);
275
164
  if (errors.length > 0) {
276
- console.error(formatError("Profile validation failed:"));
277
- for (const err of errors) console.error(` - ${err}`);
165
+ process.stderr.write(formatError("Profile validation failed:") + "\n");
166
+ for (const err of errors) {
167
+ process.stderr.write(formatBullet(err, 1) + "\n");
168
+ }
278
169
  process.exit(1);
279
170
  }
280
171
 
@@ -283,7 +174,7 @@ async function handleSingleStage({
283
174
 
284
175
  if (!options.output) {
285
176
  printTeamInstructions(agentTrack, humanDiscipline);
286
- console.log(formatAgentProfile(profile, agentTemplate));
177
+ process.stdout.write(formatAgentProfile(profile, agentTemplate) + "\n");
287
178
  return;
288
179
  }
289
180
 
@@ -294,9 +185,9 @@ async function handleSingleStage({
294
185
  await writeTeamInstructions(teamInstructions, baseDir);
295
186
  await writeProfile(profile, baseDir, agentTemplate);
296
187
  await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
297
- console.log("");
298
- console.log(
299
- formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`),
188
+ process.stdout.write("\n");
189
+ process.stdout.write(
190
+ formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`) + "\n",
300
191
  );
301
192
  }
302
193
 
@@ -338,10 +229,13 @@ async function handleAllStages({
338
229
  for (const profile of profiles) {
339
230
  const errors = validateAgentProfile(profile);
340
231
  if (errors.length > 0) {
341
- console.error(
342
- formatError(`Profile ${profile.frontmatter.name} validation failed:`),
232
+ process.stderr.write(
233
+ formatError(`Profile ${profile.frontmatter.name} validation failed:`) +
234
+ "\n",
343
235
  );
344
- for (const err of errors) console.error(` - ${err}`);
236
+ for (const err of errors) {
237
+ process.stderr.write(formatBullet(err, 1) + "\n");
238
+ }
345
239
  process.exit(1);
346
240
  }
347
241
  }
@@ -349,10 +243,13 @@ async function handleAllStages({
349
243
  for (const skill of skillFiles) {
350
244
  const errors = validateAgentSkill(skill);
351
245
  if (errors.length > 0) {
352
- console.error(
353
- formatError(`Skill ${skill.frontmatter.name} validation failed:`),
246
+ process.stderr.write(
247
+ formatError(`Skill ${skill.frontmatter.name} validation failed:`) +
248
+ "\n",
354
249
  );
355
- for (const err of errors) console.error(` - ${err}`);
250
+ for (const err of errors) {
251
+ process.stderr.write(formatBullet(err, 1) + "\n");
252
+ }
356
253
  process.exit(1);
357
254
  }
358
255
  }
@@ -369,8 +266,8 @@ async function handleAllStages({
369
266
  if (!options.output) {
370
267
  printTeamInstructions(agentTrack, humanDiscipline);
371
268
  for (const profile of profiles) {
372
- console.log(formatAgentProfile(profile, agentTemplate));
373
- console.log("\n---\n");
269
+ process.stdout.write(formatAgentProfile(profile, agentTemplate) + "\n");
270
+ process.stdout.write("\n---\n\n");
374
271
  }
375
272
  return;
376
273
  }
@@ -386,12 +283,14 @@ async function handleAllStages({
386
283
  const fileCount = await writeSkills(skillFiles, baseDir, skillTemplates);
387
284
  await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
388
285
 
389
- console.log("");
390
- console.log(formatSuccess(`Generated ${profiles.length} agents:`));
286
+ process.stdout.write("\n");
287
+ process.stdout.write(
288
+ formatSuccess(`Generated ${profiles.length} agents:`) + "\n",
289
+ );
391
290
  for (const profile of profiles) {
392
- console.log(` - ${profile.frontmatter.name}`);
291
+ process.stdout.write(formatBullet(profile.frontmatter.name, 1) + "\n");
393
292
  }
394
- console.log(` Skills: ${fileCount} files`);
293
+ process.stdout.write(formatSubheader(`Skills: ${fileCount} files`) + "\n");
395
294
  }
396
295
 
397
296
  /**
@@ -428,10 +327,10 @@ export async function runAgentCommand({
428
327
  const trackId = options.track;
429
328
 
430
329
  if (args.length > 1) {
431
- console.error(
330
+ process.stderr.write(
432
331
  formatError(
433
332
  `Unexpected argument: ${args[1]}. Did you mean --track=${args[1]}?`,
434
- ),
333
+ ) + "\n",
435
334
  );
436
335
  process.exit(1);
437
336
  }
@@ -12,7 +12,12 @@
12
12
 
13
13
  import { createEntityCommand } from "./command-factory.js";
14
14
  import { behaviourToMarkdown } from "../formatters/behaviour/markdown.js";
15
- import { formatTable } from "@forwardimpact/libcli";
15
+ import {
16
+ formatTable,
17
+ formatHeader,
18
+ formatSubheader,
19
+ formatBullet,
20
+ } from "@forwardimpact/libcli";
16
21
 
17
22
  /**
18
23
  * Format behaviour list item for --list output
@@ -31,7 +36,7 @@ function formatListItem(behaviour) {
31
36
  function formatSummary(behaviours, data) {
32
37
  const { drivers } = data;
33
38
 
34
- console.log(`\n🧠 Behaviours\n`);
39
+ process.stdout.write("\n" + formatHeader("\u{1F9E0} Behaviours") + "\n\n");
35
40
 
36
41
  // Summary table
37
42
  const rows = behaviours.map((b) => {
@@ -41,10 +46,17 @@ function formatSummary(behaviours, data) {
41
46
  return [b.id, b.name, linkedDrivers];
42
47
  });
43
48
 
44
- console.log(formatTable(["ID", "Name", "Drivers"], rows));
45
- console.log(`\nTotal: ${behaviours.length} behaviours`);
46
- console.log(`\nRun 'npx fit-pathway behaviour --list' for IDs and names`);
47
- console.log(`Run 'npx fit-pathway behaviour <id>' for details\n`);
49
+ process.stdout.write(formatTable(["ID", "Name", "Drivers"], rows) + "\n");
50
+ process.stdout.write(
51
+ "\n" + formatSubheader(`Total: ${behaviours.length} behaviours`) + "\n\n",
52
+ );
53
+ process.stdout.write(
54
+ formatBullet("Run 'npx fit-pathway behaviour --list' for IDs and names") +
55
+ "\n",
56
+ );
57
+ process.stdout.write(
58
+ formatBullet("Run 'npx fit-pathway behaviour <id>' for details") + "\n\n",
59
+ );
48
60
  }
49
61
 
50
62
  /**