@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 +3 -1
- package/src/commands/agent-list.js +164 -0
- package/src/commands/agent.js +74 -175
- package/src/commands/behaviour.js +18 -6
- package/src/commands/command-factory.js +39 -14
- package/src/commands/discipline.js +20 -6
- package/src/commands/driver.js +22 -13
- package/src/commands/interview.js +11 -6
- package/src/commands/job.js +101 -55
- package/src/commands/level.js +19 -7
- package/src/commands/progress.js +8 -3
- package/src/commands/questions.js +26 -10
- package/src/commands/skill.js +31 -13
- package/src/commands/stage.js +32 -22
- package/src/commands/tool.js +25 -15
- package/src/commands/track.js +19 -6
- package/src/index.js +7 -0
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
98
|
-
|
|
102
|
+
process.stdout.write("\n" + formatSubheader("Behaviour Questions") + "\n");
|
|
103
|
+
process.stdout.write(
|
|
104
|
+
formatTable(["Maturity", "Count"], behaviourRows) + "\n",
|
|
105
|
+
);
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
package/src/commands/skill.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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
|
|
package/src/commands/stage.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
83
|
+
process.stdout.write(formatSubheader("Read-Then-Do Checklist") + "\n\n");
|
|
75
84
|
for (const item of view.readChecklist) {
|
|
76
|
-
|
|
85
|
+
process.stdout.write(formatBullet(item, 1) + "\n");
|
|
77
86
|
}
|
|
78
|
-
|
|
87
|
+
process.stdout.write("\n");
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
// Confirm checklist
|
|
82
91
|
if (view.confirmChecklist.length > 0) {
|
|
83
|
-
|
|
92
|
+
process.stdout.write(formatSubheader("Do-Then-Confirm Checklist") + "\n\n");
|
|
84
93
|
for (const item of view.confirmChecklist) {
|
|
85
|
-
|
|
94
|
+
process.stdout.write(formatBullet(item, 1) + "\n");
|
|
86
95
|
}
|
|
87
|
-
|
|
96
|
+
process.stdout.write("\n");
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
// Constraints
|
|
91
100
|
if (view.constraints.length > 0) {
|
|
92
|
-
|
|
101
|
+
process.stdout.write(formatSubheader("Constraints") + "\n\n");
|
|
93
102
|
for (const item of view.constraints) {
|
|
94
|
-
|
|
103
|
+
process.stdout.write(" " + formatWarning(item) + "\n");
|
|
95
104
|
}
|
|
96
|
-
|
|
105
|
+
process.stdout.write("\n");
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
// Handoffs
|
|
100
109
|
if (view.handoffs.length > 0) {
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
+
process.stdout.write(` "${handoff.prompt}"\n`);
|
|
111
121
|
}
|
|
112
122
|
}
|
|
113
|
-
|
|
123
|
+
process.stdout.write("\n");
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
|
package/src/commands/tool.js
CHANGED
|
@@ -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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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
|
-
|
|
95
|
+
process.stdout.write(formatBullet("(showing top 15 by usage)") + "\n");
|
|
90
96
|
}
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
117
|
+
process.stdout.write(`Documentation: ${tool.url}\n\n`);
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
if (tool.usages.length > 0) {
|
|
110
|
-
|
|
121
|
+
process.stdout.write(formatSubheader("Used in Skills") + "\n\n");
|
|
111
122
|
const rows = tool.usages.map((u) => [u.skillName, u.useWhen]);
|
|
112
|
-
|
|
113
|
-
console.log();
|
|
123
|
+
process.stdout.write(formatTable(["Skill", "Use When"], rows) + "\n\n");
|
|
114
124
|
}
|
|
115
125
|
}
|
package/src/commands/track.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
/**
|
package/src/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Public entry point for @forwardimpact/pathway.
|
|
2
|
+
// The primary consumption mode is the CLI (fit-pathway) ā this
|
|
3
|
+
// re-export exists so the package conforms to the repo-wide layout
|
|
4
|
+
// contract (spec 390) and so consumers who import
|
|
5
|
+
// @forwardimpact/pathway directly receive the shared type
|
|
6
|
+
// definitions.
|
|
7
|
+
export * from "./types.js";
|