@forwardimpact/pathway 0.25.24 → 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.
@@ -17,7 +17,12 @@ 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 "@forwardimpact/libcli";
20
+ import {
21
+ formatTable,
22
+ formatHeader,
23
+ formatSubheader,
24
+ formatBullet,
25
+ } from "@forwardimpact/libcli";
21
26
 
22
27
  /**
23
28
  * Parse questions command options
@@ -46,7 +51,7 @@ function showQuestionsSummary(data) {
46
51
  const { skills, behaviours } = data;
47
52
  const questions = data.questions;
48
53
 
49
- console.log(`\nā“ Questions\n`);
54
+ process.stdout.write("\n" + formatHeader("\u2753 Questions") + "\n\n");
50
55
 
51
56
  // Skill questions by level
52
57
  const skillProficiencies = [
@@ -70,8 +75,8 @@ function showQuestionsSummary(data) {
70
75
  return [level, count];
71
76
  });
72
77
 
73
- console.log("Skill Questions:");
74
- console.log(formatTable(["Level", "Count"], skillRows));
78
+ process.stdout.write(formatSubheader("Skill Questions") + "\n");
79
+ process.stdout.write(formatTable(["Level", "Count"], skillRows) + "\n");
75
80
 
76
81
  // Behaviour questions by maturity
77
82
  const maturities = [
@@ -94,13 +99,24 @@ function showQuestionsSummary(data) {
94
99
  return [maturity.replace(/_/g, " "), count];
95
100
  });
96
101
 
97
- console.log("\nBehaviour Questions:");
98
- console.log(formatTable(["Maturity", "Count"], behaviourRows));
102
+ process.stdout.write("\n" + formatSubheader("Behaviour Questions") + "\n");
103
+ process.stdout.write(
104
+ formatTable(["Maturity", "Count"], behaviourRows) + "\n",
105
+ );
99
106
 
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`,
107
+ process.stdout.write("\n");
108
+ process.stdout.write(
109
+ formatBullet("Run 'npx fit-pathway questions --list' for question IDs") +
110
+ "\n",
111
+ );
112
+ process.stdout.write(
113
+ formatBullet("Run 'npx fit-pathway questions --stats' for detailed stats") +
114
+ "\n",
115
+ );
116
+ process.stdout.write(
117
+ formatBullet(
118
+ "Run 'npx fit-pathway questions --level=practitioner' to filter",
119
+ ) + "\n\n",
104
120
  );
105
121
  }
106
122
 
@@ -15,7 +15,13 @@ 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 "@forwardimpact/libcli";
18
+ import {
19
+ formatTable,
20
+ formatError,
21
+ formatHeader,
22
+ formatSubheader,
23
+ formatBullet,
24
+ } from "@forwardimpact/libcli";
19
25
  import { generateSkillMarkdown } from "@forwardimpact/libskill/agent";
20
26
  import { formatAgentSkill } from "../formatters/agent/skill.js";
21
27
 
@@ -29,7 +35,7 @@ function formatSummary(skills, data) {
29
35
  const { groups, groupOrder } = prepareSkillsList(skills, capabilities);
30
36
  const emoji = framework ? getConceptEmoji(framework, "skill") : "šŸ“š";
31
37
 
32
- console.log(`\n${emoji} Skills\n`);
38
+ process.stdout.write("\n" + formatHeader(`${emoji} Skills`) + "\n\n");
33
39
 
34
40
  // Summary table by capability
35
41
  const rows = groupOrder.map((capability) => {
@@ -38,10 +44,18 @@ function formatSummary(skills, data) {
38
44
  return [capability, count, withAgent];
39
45
  });
40
46
 
41
- console.log(formatTable(["Capability", "Count", "Agent"], rows));
42
- console.log(`\nTotal: ${skills.length} skills`);
43
- console.log(`\nRun 'npx fit-pathway skill --list' for IDs`);
44
- console.log(`Run 'npx fit-pathway skill <id>' for details\n`);
47
+ process.stdout.write(
48
+ formatTable(["Capability", "Count", "Agent"], rows) + "\n",
49
+ );
50
+ process.stdout.write(
51
+ "\n" + formatSubheader(`Total: ${skills.length} skills`) + "\n\n",
52
+ );
53
+ process.stdout.write(
54
+ formatBullet("Run 'npx fit-pathway skill --list' for IDs") + "\n",
55
+ );
56
+ process.stdout.write(
57
+ formatBullet("Run 'npx fit-pathway skill <id>' for details") + "\n\n",
58
+ );
45
59
  }
46
60
 
47
61
  /**
@@ -70,10 +84,12 @@ function formatDetail(viewAndContext, framework) {
70
84
  */
71
85
  async function formatAgentDetail(skill, stages, templateLoader, dataDir) {
72
86
  if (!skill.agent) {
73
- console.error(formatError(`Skill '${skill.id}' has no agent section`));
74
- console.error(`\nSkills with agent support:`);
75
- console.error(
76
- ` npx fit-pathway skill --list | xargs -I{} sh -c 'npx fit-pathway skill {} --json | jq -e .skill.agent > /dev/null && echo {}'`,
87
+ process.stderr.write(
88
+ formatError(`Skill '${skill.id}' has no agent section`) + "\n",
89
+ );
90
+ process.stderr.write("\nSkills with agent support:\n");
91
+ process.stderr.write(
92
+ ` npx fit-pathway skill --list | xargs -I{} sh -c 'npx fit-pathway skill {} --json | jq -e .skill.agent > /dev/null && echo {}'\n`,
77
93
  );
78
94
  process.exit(1);
79
95
  }
@@ -81,7 +97,7 @@ async function formatAgentDetail(skill, stages, templateLoader, dataDir) {
81
97
  const template = templateLoader.load("skill.template.md", dataDir);
82
98
  const skillMd = generateSkillMarkdown({ skillData: skill, stages });
83
99
  const output = formatAgentSkill(skillMd, template);
84
- console.log(output);
100
+ process.stdout.write(output + "\n");
85
101
  }
86
102
 
87
103
  /**
@@ -131,8 +147,10 @@ export async function runSkillCommand({
131
147
  const skill = data.skills.find((s) => s.id === id);
132
148
 
133
149
  if (!skill) {
134
- console.error(formatError(`Skill not found: ${id}`));
135
- console.error(`Available: ${data.skills.map((s) => s.id).join(", ")}`);
150
+ process.stderr.write(formatError(`Skill not found: ${id}`) + "\n");
151
+ process.stderr.write(
152
+ `Available: ${data.skills.map((s) => s.id).join(", ")}\n`,
153
+ );
136
154
  process.exit(1);
137
155
  }
138
156
 
@@ -19,6 +19,7 @@ import {
19
19
  formatHeader,
20
20
  formatSubheader,
21
21
  formatBullet,
22
+ formatWarning,
22
23
  } from "@forwardimpact/libcli";
23
24
 
24
25
  /**
@@ -36,13 +37,13 @@ function formatListItem(stage) {
36
37
  * @param {Object} _data - Full data context (unused)
37
38
  */
38
39
  function formatSummary(stages, _data) {
39
- console.log("\nšŸ”„ Stages\n");
40
+ process.stdout.write("\n" + formatHeader("\u{1F504} Stages") + "\n\n");
40
41
 
41
42
  // Show lifecycle flow
42
43
  const flow = stages
43
44
  .map((s) => `${getStageEmoji(stages, s.id)} ${s.name}`)
44
45
  .join(" → ");
45
- console.log(`Lifecycle: ${flow}\n`);
46
+ process.stdout.write(formatSubheader(`Lifecycle: ${flow}`) + "\n\n");
46
47
 
47
48
  const rows = stages.map((s) => {
48
49
  const toolCount = s.tools?.length || 0;
@@ -50,10 +51,18 @@ function formatSummary(stages, _data) {
50
51
  return [s.id, s.name, s.mode, toolCount, handoffCount];
51
52
  });
52
53
 
53
- console.log(formatTable(["ID", "Name", "Mode", "Tools", "Handoffs"], rows));
54
- console.log(`\nTotal: ${stages.length} stages`);
55
- console.log(`\nRun 'npx fit-pathway stage --list' for IDs and names`);
56
- console.log(`Run 'npx fit-pathway stage <id>' for details\n`);
54
+ process.stdout.write(
55
+ formatTable(["ID", "Name", "Mode", "Tools", "Handoffs"], rows) + "\n",
56
+ );
57
+ process.stdout.write(
58
+ "\n" + formatSubheader(`Total: ${stages.length} stages`) + "\n\n",
59
+ );
60
+ process.stdout.write(
61
+ formatBullet("Run 'npx fit-pathway stage --list' for IDs and names") + "\n",
62
+ );
63
+ process.stdout.write(
64
+ formatBullet("Run 'npx fit-pathway stage <id>' for details") + "\n\n",
65
+ );
57
66
  }
58
67
 
59
68
  /**
@@ -66,51 +75,52 @@ function formatDetail(viewAndContext, _framework) {
66
75
  const view = prepareStageDetail(stage);
67
76
  const emoji = getStageEmoji(stages, stage.id);
68
77
 
69
- console.log(formatHeader(`\n${emoji} ${view.name}\n`));
70
- console.log(`${view.description}\n`);
78
+ process.stdout.write("\n" + formatHeader(`${emoji} ${view.name}`) + "\n\n");
79
+ process.stdout.write(view.description + "\n\n");
71
80
 
72
81
  // Read checklist
73
82
  if (view.readChecklist.length > 0) {
74
- console.log(formatSubheader("Read-Then-Do Checklist\n"));
83
+ process.stdout.write(formatSubheader("Read-Then-Do Checklist") + "\n\n");
75
84
  for (const item of view.readChecklist) {
76
- console.log(formatBullet(item, 1));
85
+ process.stdout.write(formatBullet(item, 1) + "\n");
77
86
  }
78
- console.log();
87
+ process.stdout.write("\n");
79
88
  }
80
89
 
81
90
  // Confirm checklist
82
91
  if (view.confirmChecklist.length > 0) {
83
- console.log(formatSubheader("Do-Then-Confirm Checklist\n"));
92
+ process.stdout.write(formatSubheader("Do-Then-Confirm Checklist") + "\n\n");
84
93
  for (const item of view.confirmChecklist) {
85
- console.log(formatBullet(item, 1));
94
+ process.stdout.write(formatBullet(item, 1) + "\n");
86
95
  }
87
- console.log();
96
+ process.stdout.write("\n");
88
97
  }
89
98
 
90
99
  // Constraints
91
100
  if (view.constraints.length > 0) {
92
- console.log(formatSubheader("Constraints\n"));
101
+ process.stdout.write(formatSubheader("Constraints") + "\n\n");
93
102
  for (const item of view.constraints) {
94
- console.log(formatBullet(`āš ļø ${item}`, 1));
103
+ process.stdout.write(" " + formatWarning(item) + "\n");
95
104
  }
96
- console.log();
105
+ process.stdout.write("\n");
97
106
  }
98
107
 
99
108
  // Handoffs
100
109
  if (view.handoffs.length > 0) {
101
- console.log(formatSubheader("Handoffs\n"));
110
+ process.stdout.write(formatSubheader("Handoffs") + "\n\n");
102
111
  for (const handoff of view.handoffs) {
103
112
  const targetStage = stages.find((s) => s.id === handoff.target);
104
113
  const targetEmoji = getStageEmoji(stages, handoff.target);
105
114
  const targetName = targetStage?.name || handoff.target;
106
- console.log(
107
- formatBullet(`${targetEmoji} ${handoff.label} → ${targetName}`, 1),
115
+ process.stdout.write(
116
+ formatBullet(`${targetEmoji} ${handoff.label} → ${targetName}`, 1) +
117
+ "\n",
108
118
  );
109
119
  if (handoff.prompt) {
110
- console.log(` "${handoff.prompt}"`);
120
+ process.stdout.write(` "${handoff.prompt}"\n`);
111
121
  }
112
122
  }
113
- console.log();
123
+ process.stdout.write("\n");
114
124
  }
115
125
  }
116
126
 
@@ -15,6 +15,8 @@ import {
15
15
  formatTable,
16
16
  formatHeader,
17
17
  formatSubheader,
18
+ formatBullet,
19
+ formatError,
18
20
  } from "@forwardimpact/libcli";
19
21
 
20
22
  /**
@@ -50,8 +52,8 @@ export async function runToolCommand({ data, args, options }) {
50
52
  const tool = tools.find((t) => t.name.toLowerCase() === name.toLowerCase());
51
53
 
52
54
  if (!tool) {
53
- console.error(`Tool not found: ${name}`);
54
- console.error(`Available: ${tools.map((t) => t.name).join(", ")}`);
55
+ process.stderr.write(formatError(`Tool not found: ${name}`) + "\n");
56
+ process.stderr.write(`Available: ${tools.map((t) => t.name).join(", ")}\n`);
55
57
  process.exit(1);
56
58
  }
57
59
 
@@ -69,7 +71,7 @@ export async function runToolCommand({ data, args, options }) {
69
71
  * @param {number} totalCount - Total tool count
70
72
  */
71
73
  function formatSummary(tools, totalCount) {
72
- console.log(`\nšŸ”§ Tools\n`);
74
+ process.stdout.write("\n" + formatHeader("\u{1F527} Tools") + "\n\n");
73
75
 
74
76
  // Show tools sorted by usage count
75
77
  const sorted = [...tools].sort((a, b) => b.usages.length - a.usages.length);
@@ -83,15 +85,24 @@ function formatSummary(tools, totalCount) {
83
85
  : t.description,
84
86
  ]);
85
87
 
86
- console.log(formatTable(["Tool", "Skills", "Description"], rows));
87
- console.log(`\nTotal: ${totalCount} tools`);
88
+ process.stdout.write(
89
+ formatTable(["Tool", "Skills", "Description"], rows) + "\n",
90
+ );
91
+ process.stdout.write(
92
+ "\n" + formatSubheader(`Total: ${totalCount} tools`) + "\n",
93
+ );
88
94
  if (sorted.length > 15) {
89
- console.log(`(showing top 15 by usage)`);
95
+ process.stdout.write(formatBullet("(showing top 15 by usage)") + "\n");
90
96
  }
91
- console.log(
92
- `\nRun 'npx fit-pathway tool --list' for all tool names and descriptions`,
97
+ process.stdout.write("\n");
98
+ process.stdout.write(
99
+ formatBullet(
100
+ "Run 'npx fit-pathway tool --list' for all tool names and descriptions",
101
+ ) + "\n",
102
+ );
103
+ process.stdout.write(
104
+ formatBullet("Run 'npx fit-pathway tool <name>' for details") + "\n\n",
93
105
  );
94
- console.log(`Run 'npx fit-pathway tool <name>' for details\n`);
95
106
  }
96
107
 
97
108
  /**
@@ -99,17 +110,16 @@ function formatSummary(tools, totalCount) {
99
110
  * @param {Object} tool - Aggregated tool with usages
100
111
  */
101
112
  function formatDetail(tool) {
102
- console.log(formatHeader(`\nšŸ”§ ${tool.name}\n`));
103
- console.log(`${tool.description}\n`);
113
+ process.stdout.write("\n" + formatHeader(`\u{1F527} ${tool.name}`) + "\n\n");
114
+ process.stdout.write(tool.description + "\n\n");
104
115
 
105
116
  if (tool.url) {
106
- console.log(`Documentation: ${tool.url}\n`);
117
+ process.stdout.write(`Documentation: ${tool.url}\n\n`);
107
118
  }
108
119
 
109
120
  if (tool.usages.length > 0) {
110
- console.log(formatSubheader("Used in Skills\n"));
121
+ process.stdout.write(formatSubheader("Used in Skills") + "\n\n");
111
122
  const rows = tool.usages.map((u) => [u.skillName, u.useWhen]);
112
- console.log(formatTable(["Skill", "Use When"], rows));
113
- console.log();
123
+ process.stdout.write(formatTable(["Skill", "Use When"], rows) + "\n\n");
114
124
  }
115
125
  }
@@ -13,7 +13,12 @@
13
13
  import { createEntityCommand } from "./command-factory.js";
14
14
  import { trackToMarkdown } from "../formatters/track/markdown.js";
15
15
  import { sortTracksByName } from "../formatters/track/shared.js";
16
- import { formatTable } from "@forwardimpact/libcli";
16
+ import {
17
+ formatTable,
18
+ formatHeader,
19
+ formatSubheader,
20
+ formatBullet,
21
+ } from "@forwardimpact/libcli";
17
22
  import { getConceptEmoji } from "@forwardimpact/map/levels";
18
23
 
19
24
  /**
@@ -34,7 +39,7 @@ function formatSummary(tracks, data) {
34
39
  const { framework, disciplines } = data;
35
40
  const emoji = framework ? getConceptEmoji(framework, "track") : "šŸ›¤ļø";
36
41
 
37
- console.log(`\n${emoji} Tracks\n`);
42
+ process.stdout.write("\n" + formatHeader(`${emoji} Tracks`) + "\n\n");
38
43
 
39
44
  const rows = tracks.map((t) => {
40
45
  const modCount = Object.keys(t.skillModifiers || {}).length;
@@ -47,10 +52,18 @@ function formatSummary(tracks, data) {
47
52
  return [t.id, t.name, modCount, disciplineNames || "—"];
48
53
  });
49
54
 
50
- console.log(formatTable(["ID", "Name", "Modifiers", "Disciplines"], rows));
51
- console.log(`\nTotal: ${tracks.length} tracks`);
52
- console.log(`\nRun 'npx fit-pathway track --list' for IDs and names`);
53
- console.log(`Run 'npx fit-pathway track <id>' for details\n`);
55
+ process.stdout.write(
56
+ formatTable(["ID", "Name", "Modifiers", "Disciplines"], rows) + "\n",
57
+ );
58
+ process.stdout.write(
59
+ "\n" + formatSubheader(`Total: ${tracks.length} tracks`) + "\n\n",
60
+ );
61
+ process.stdout.write(
62
+ formatBullet("Run 'npx fit-pathway track --list' for IDs and names") + "\n",
63
+ );
64
+ process.stdout.write(
65
+ formatBullet("Run 'npx fit-pathway track <id>' for details") + "\n\n",
66
+ );
54
67
  }
55
68
 
56
69
  /**