@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.
- package/bin/fit-pathway.js +117 -325
- package/package.json +2 -2
- package/src/commands/agent-io.js +1 -1
- package/src/commands/agent-list.js +164 -0
- package/src/commands/agent.js +83 -184
- package/src/commands/behaviour.js +22 -10
- package/src/commands/build-packs.js +208 -34
- package/src/commands/build.js +2 -2
- package/src/commands/command-factory.js +39 -14
- package/src/commands/discipline.js +24 -10
- package/src/commands/driver.js +28 -19
- package/src/commands/index.js +0 -1
- package/src/commands/interview.js +15 -10
- package/src/commands/job.js +110 -62
- package/src/commands/level.js +23 -11
- package/src/commands/progress.js +12 -7
- package/src/commands/questions.js +32 -14
- package/src/commands/skill.js +36 -18
- package/src/commands/stage.js +37 -27
- package/src/commands/tool.js +29 -19
- package/src/commands/track.js +23 -10
- package/src/formatters/questions/yaml.js +1 -1
- package/src/index.html +1 -1
- package/src/lib/cli-command.js +33 -33
- package/src/lib/cli-output.js +9 -189
- package/src/pages/agent-builder-install.js +6 -5
- package/src/commands/init.js +0 -64
- package/starter/behaviours/systems_thinking.yaml +0 -32
- package/starter/capabilities/delivery.yaml +0 -105
- package/starter/capabilities/reliability.yaml +0 -72
- package/starter/disciplines/software_engineering.yaml +0 -46
- package/starter/drivers.yaml +0 -10
- package/starter/framework.yaml +0 -49
- package/starter/levels.yaml +0 -39
- package/starter/questions/behaviours/.gitkeep +0 -0
- package/starter/questions/capabilities/.gitkeep +0 -0
- package/starter/questions/skills/.gitkeep +0 -0
- package/starter/stages.yaml +0 -21
- package/starter/tracks/forward_deployed.yaml +0 -33
- package/starter/tracks/platform.yaml +0 -33
package/src/commands/job.js
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* Generates and displays job definitions in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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,13 @@ import {
|
|
|
20
20
|
generateJobTitle,
|
|
21
21
|
generateAllJobs,
|
|
22
22
|
} from "@forwardimpact/libskill/derivation";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
formatTable,
|
|
25
|
+
formatHeader,
|
|
26
|
+
formatSubheader,
|
|
27
|
+
formatBullet,
|
|
28
|
+
formatError,
|
|
29
|
+
} from "@forwardimpact/libcli";
|
|
24
30
|
import {
|
|
25
31
|
deriveChecklist,
|
|
26
32
|
formatChecklistMarkdown,
|
|
@@ -66,7 +72,9 @@ function printJobList(filteredJobs) {
|
|
|
66
72
|
*/
|
|
67
73
|
function printJobSummary(filteredJobs, options) {
|
|
68
74
|
const trackLabel = options.track ? ` ā ${options.track}` : "";
|
|
69
|
-
|
|
75
|
+
process.stdout.write(
|
|
76
|
+
"\n" + formatHeader(`\u{1F4BC} Jobs${trackLabel}`) + "\n\n",
|
|
77
|
+
);
|
|
70
78
|
|
|
71
79
|
const byDiscipline = {};
|
|
72
80
|
for (const job of filteredJobs) {
|
|
@@ -91,15 +99,24 @@ function printJobSummary(filteredJobs, options) {
|
|
|
91
99
|
info.count,
|
|
92
100
|
info.tracks.size > 0 ? [...info.tracks].join(", ") : "ā",
|
|
93
101
|
]);
|
|
94
|
-
|
|
95
|
-
formatTable(["ID", "Specialization", "Type", "Jobs", "Tracks"], rows)
|
|
102
|
+
process.stdout.write(
|
|
103
|
+
formatTable(["ID", "Specialization", "Type", "Jobs", "Tracks"], rows) +
|
|
104
|
+
"\n",
|
|
96
105
|
);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
process.stdout.write(
|
|
107
|
+
"\n" +
|
|
108
|
+
formatSubheader(`Total: ${filteredJobs.length} valid job combinations`) +
|
|
109
|
+
"\n\n",
|
|
100
110
|
);
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
process.stdout.write(
|
|
112
|
+
formatBullet(
|
|
113
|
+
"Run 'npx fit-pathway job --list' for all combinations with titles",
|
|
114
|
+
) + "\n",
|
|
115
|
+
);
|
|
116
|
+
process.stdout.write(
|
|
117
|
+
formatBullet(
|
|
118
|
+
"Run 'npx fit-pathway job <discipline> <level> [--track=<track>]' for details",
|
|
119
|
+
) + "\n\n",
|
|
103
120
|
);
|
|
104
121
|
}
|
|
105
122
|
|
|
@@ -112,22 +129,28 @@ function handleSingleArg(arg, data) {
|
|
|
112
129
|
const isLevel = data.levels.some((g) => g.id === arg);
|
|
113
130
|
const isTrack = data.tracks.some((t) => t.id === arg);
|
|
114
131
|
if (isLevel) {
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
process.stderr.write(
|
|
133
|
+
formatError(
|
|
134
|
+
`Missing discipline. Usage: npx fit-pathway job <discipline> ${arg} [--track=<track>]`,
|
|
135
|
+
) + "\n",
|
|
117
136
|
);
|
|
118
|
-
|
|
119
|
-
`Disciplines: ${data.disciplines.map((d) => d.id).join(", ")}`,
|
|
137
|
+
process.stderr.write(
|
|
138
|
+
`Disciplines: ${data.disciplines.map((d) => d.id).join(", ")}\n`,
|
|
120
139
|
);
|
|
121
140
|
} else if (isTrack) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
141
|
+
process.stderr.write(
|
|
142
|
+
formatError(`Track must be passed as a flag: --track=${arg}`) + "\n",
|
|
143
|
+
);
|
|
144
|
+
process.stderr.write(
|
|
145
|
+
`Usage: npx fit-pathway job <discipline> <level> --track=${arg}\n`,
|
|
125
146
|
);
|
|
126
147
|
} else {
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
process.stderr.write(
|
|
149
|
+
formatError(
|
|
150
|
+
"Usage: npx fit-pathway job <discipline> <level> [--track=<track>]",
|
|
151
|
+
) + "\n",
|
|
129
152
|
);
|
|
130
|
-
|
|
153
|
+
process.stderr.write(" npx fit-pathway job --list\n");
|
|
131
154
|
}
|
|
132
155
|
process.exit(1);
|
|
133
156
|
}
|
|
@@ -150,14 +173,18 @@ function resolveJobEntities(data, args, options) {
|
|
|
150
173
|
const maybeLevel = data.levels.find((g) => g.id === args[0]);
|
|
151
174
|
const maybeDiscipline = data.disciplines.find((d) => d.id === args[1]);
|
|
152
175
|
if (maybeLevel && maybeDiscipline) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
process.stderr.write(
|
|
177
|
+
formatError("Arguments are in the wrong order. Try:") + "\n",
|
|
178
|
+
);
|
|
179
|
+
process.stderr.write(
|
|
180
|
+
` npx fit-pathway job ${args[1]} ${args[0]}${options.track ? ` --track=${options.track}` : ""}\n`,
|
|
156
181
|
);
|
|
157
182
|
} else {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
183
|
+
process.stderr.write(
|
|
184
|
+
formatError(`Discipline not found: ${args[0]}`) + "\n",
|
|
185
|
+
);
|
|
186
|
+
process.stderr.write(
|
|
187
|
+
`Available: ${data.disciplines.map((d) => d.id).join(", ")}\n`,
|
|
161
188
|
);
|
|
162
189
|
}
|
|
163
190
|
process.exit(1);
|
|
@@ -166,21 +193,33 @@ function resolveJobEntities(data, args, options) {
|
|
|
166
193
|
if (!level) {
|
|
167
194
|
const isTrack = data.tracks.some((t) => t.id === args[1]);
|
|
168
195
|
if (isTrack) {
|
|
169
|
-
|
|
170
|
-
|
|
196
|
+
process.stderr.write(
|
|
197
|
+
formatError(
|
|
198
|
+
"Track must be passed as a flag, not a positional argument:",
|
|
199
|
+
) + "\n",
|
|
200
|
+
);
|
|
201
|
+
process.stderr.write(
|
|
202
|
+
` npx fit-pathway job ${args[0]} <level> --track=${args[1]}\n`,
|
|
203
|
+
);
|
|
204
|
+
process.stderr.write(
|
|
205
|
+
`Levels: ${data.levels.map((g) => g.id).join(", ")}\n`,
|
|
171
206
|
);
|
|
172
|
-
console.error(` bunx pathway job ${args[0]} <level> --track=${args[1]}`);
|
|
173
|
-
console.error(`Levels: ${data.levels.map((g) => g.id).join(", ")}`);
|
|
174
207
|
} else {
|
|
175
|
-
|
|
176
|
-
|
|
208
|
+
process.stderr.write(formatError(`Level not found: ${args[1]}`) + "\n");
|
|
209
|
+
process.stderr.write(
|
|
210
|
+
`Available: ${data.levels.map((g) => g.id).join(", ")}\n`,
|
|
211
|
+
);
|
|
177
212
|
}
|
|
178
213
|
process.exit(1);
|
|
179
214
|
}
|
|
180
215
|
|
|
181
216
|
if (options.track && !track) {
|
|
182
|
-
|
|
183
|
-
|
|
217
|
+
process.stderr.write(
|
|
218
|
+
formatError(`Track not found: ${options.track}`) + "\n",
|
|
219
|
+
);
|
|
220
|
+
process.stderr.write(
|
|
221
|
+
`Available: ${data.tracks.map((t) => t.id).join(", ")}\n`,
|
|
222
|
+
);
|
|
184
223
|
process.exit(1);
|
|
185
224
|
}
|
|
186
225
|
|
|
@@ -196,8 +235,8 @@ function resolveJobEntities(data, args, options) {
|
|
|
196
235
|
function handleChecklist(view, data, stageId) {
|
|
197
236
|
const validStages = data.stages.map((s) => s.id);
|
|
198
237
|
if (!validStages.includes(stageId)) {
|
|
199
|
-
|
|
200
|
-
|
|
238
|
+
process.stderr.write(formatError(`Invalid stage: ${stageId}`) + "\n");
|
|
239
|
+
process.stderr.write(`Available: ${validStages.join(", ")}\n`);
|
|
201
240
|
process.exit(1);
|
|
202
241
|
}
|
|
203
242
|
|
|
@@ -209,21 +248,24 @@ function handleChecklist(view, data, stageId) {
|
|
|
209
248
|
});
|
|
210
249
|
|
|
211
250
|
if (readChecklist.length === 0 && confirmChecklist.length === 0) {
|
|
212
|
-
|
|
251
|
+
process.stdout.write(
|
|
252
|
+
"\n" +
|
|
253
|
+
formatSubheader(`No checklist items for ${stageId} stage`) +
|
|
254
|
+
"\n\n",
|
|
255
|
+
);
|
|
213
256
|
return;
|
|
214
257
|
}
|
|
215
258
|
|
|
259
|
+
// Markdown output (#/##) is intentional ā this is consumed as markdown.
|
|
216
260
|
const stageLabel = stageId.charAt(0).toUpperCase() + stageId.slice(1);
|
|
217
|
-
|
|
261
|
+
process.stdout.write(`\n# ${view.title} ā ${stageLabel} Stage Checklist\n\n`);
|
|
218
262
|
if (readChecklist.length > 0) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
console.log("");
|
|
263
|
+
process.stdout.write("## Read-Then-Do\n\n");
|
|
264
|
+
process.stdout.write(formatChecklistMarkdown(readChecklist) + "\n\n");
|
|
222
265
|
}
|
|
223
266
|
if (confirmChecklist.length > 0) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
console.log("");
|
|
267
|
+
process.stdout.write("## Do-Then-Confirm\n\n");
|
|
268
|
+
process.stdout.write(formatChecklistMarkdown(confirmChecklist) + "\n\n");
|
|
227
269
|
}
|
|
228
270
|
}
|
|
229
271
|
|
|
@@ -245,16 +287,22 @@ function validateTrackFilter(filteredJobs, data, options) {
|
|
|
245
287
|
if (!options.track || filteredJobs.length > 0) return;
|
|
246
288
|
const trackExists = data.tracks.some((t) => t.id === options.track);
|
|
247
289
|
if (!trackExists) {
|
|
248
|
-
|
|
249
|
-
|
|
290
|
+
process.stderr.write(
|
|
291
|
+
formatError(`Track not found: ${options.track}`) + "\n",
|
|
292
|
+
);
|
|
293
|
+
process.stderr.write(
|
|
294
|
+
`Available: ${data.tracks.map((t) => t.id).join(", ")}\n`,
|
|
295
|
+
);
|
|
250
296
|
} else {
|
|
251
|
-
|
|
297
|
+
process.stderr.write(
|
|
298
|
+
formatError(`No jobs found for track: ${options.track}`) + "\n",
|
|
299
|
+
);
|
|
252
300
|
const trackDisciplines = data.disciplines
|
|
253
301
|
.filter((d) => d.validTracks && d.validTracks.includes(options.track))
|
|
254
302
|
.map((d) => d.id);
|
|
255
303
|
if (trackDisciplines.length > 0) {
|
|
256
|
-
|
|
257
|
-
`Disciplines with this track: ${trackDisciplines.join(", ")}`,
|
|
304
|
+
process.stderr.write(
|
|
305
|
+
`Disciplines with this track: ${trackDisciplines.join(", ")}\n`,
|
|
258
306
|
);
|
|
259
307
|
}
|
|
260
308
|
}
|
|
@@ -272,23 +320,23 @@ function reportInvalidCombination(discipline, level, track, data) {
|
|
|
272
320
|
const combo = track
|
|
273
321
|
? `${discipline.id} Ć ${level.id} Ć ${track.id}`
|
|
274
322
|
: `${discipline.id} Ć ${level.id}`;
|
|
275
|
-
|
|
323
|
+
process.stderr.write(formatError(`Invalid combination: ${combo}`) + "\n");
|
|
276
324
|
if (track) {
|
|
277
325
|
const validTracks = discipline.validTracks?.filter((t) => t !== null) || [];
|
|
278
326
|
if (validTracks.length > 0) {
|
|
279
|
-
|
|
280
|
-
`Valid tracks for ${discipline.id}: ${validTracks.join(", ")}`,
|
|
327
|
+
process.stderr.write(
|
|
328
|
+
`Valid tracks for ${discipline.id}: ${validTracks.join(", ")}\n`,
|
|
281
329
|
);
|
|
282
330
|
} else {
|
|
283
|
-
|
|
331
|
+
process.stderr.write(`${discipline.id} does not support tracks\n`);
|
|
284
332
|
}
|
|
285
333
|
}
|
|
286
334
|
if (discipline.minLevel) {
|
|
287
335
|
const levelIndex = data.levels.findIndex((g) => g.id === level.id);
|
|
288
336
|
const minIndex = data.levels.findIndex((g) => g.id === discipline.minLevel);
|
|
289
337
|
if (levelIndex >= 0 && minIndex >= 0 && levelIndex < minIndex) {
|
|
290
|
-
|
|
291
|
-
`${discipline.id} requires minimum level: ${discipline.minLevel}`,
|
|
338
|
+
process.stderr.write(
|
|
339
|
+
`${discipline.id} requires minimum level: ${discipline.minLevel}\n`,
|
|
292
340
|
);
|
|
293
341
|
}
|
|
294
342
|
}
|
package/src/commands/level.js
CHANGED
|
@@ -4,15 +4,20 @@
|
|
|
4
4
|
* Handles level summary, listing, and detail display in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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 {
|
|
15
|
+
import {
|
|
16
|
+
formatTable,
|
|
17
|
+
formatHeader,
|
|
18
|
+
formatSubheader,
|
|
19
|
+
formatBullet,
|
|
20
|
+
} from "@forwardimpact/libcli";
|
|
16
21
|
import { getConceptEmoji } from "@forwardimpact/map/levels";
|
|
17
22
|
import { capitalize } from "../formatters/shared.js";
|
|
18
23
|
|
|
@@ -34,7 +39,7 @@ function formatSummary(levels, data) {
|
|
|
34
39
|
const { framework } = data;
|
|
35
40
|
const emoji = framework ? getConceptEmoji(framework, "level") : "š";
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
process.stdout.write("\n" + formatHeader(`${emoji} Levels`) + "\n\n");
|
|
38
43
|
|
|
39
44
|
const rows = levels.map((g) => [
|
|
40
45
|
g.id,
|
|
@@ -44,7 +49,7 @@ function formatSummary(levels, data) {
|
|
|
44
49
|
capitalize(g.baseSkillProficiencies?.primary || "-"),
|
|
45
50
|
]);
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
process.stdout.write(
|
|
48
53
|
formatTable(
|
|
49
54
|
[
|
|
50
55
|
"ID",
|
|
@@ -54,11 +59,18 @@ function formatSummary(levels, data) {
|
|
|
54
59
|
"Primary Level",
|
|
55
60
|
],
|
|
56
61
|
rows,
|
|
57
|
-
),
|
|
62
|
+
) + "\n",
|
|
63
|
+
);
|
|
64
|
+
process.stdout.write(
|
|
65
|
+
"\n" + formatSubheader(`Total: ${levels.length} levels`) + "\n\n",
|
|
66
|
+
);
|
|
67
|
+
process.stdout.write(
|
|
68
|
+
formatBullet("Run 'npx fit-pathway level --list' for IDs and titles") +
|
|
69
|
+
"\n",
|
|
70
|
+
);
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
formatBullet("Run 'npx fit-pathway level <id>' for details") + "\n\n",
|
|
58
73
|
);
|
|
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`);
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
/**
|
package/src/commands/progress.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Shows career progression analysis in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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";
|
|
@@ -15,13 +15,14 @@ import {
|
|
|
15
15
|
getDefaultTargetLevel,
|
|
16
16
|
} from "../formatters/progress/shared.js";
|
|
17
17
|
import { progressToMarkdown } from "../formatters/progress/markdown.js";
|
|
18
|
+
import { formatError } from "@forwardimpact/libcli";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Format progress output
|
|
21
22
|
* @param {Object} view - Presenter view
|
|
22
23
|
*/
|
|
23
24
|
function formatProgress(view) {
|
|
24
|
-
|
|
25
|
+
process.stdout.write(progressToMarkdown(view) + "\n");
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export const runProgressCommand = createCompositeCommand({
|
|
@@ -38,13 +39,17 @@ export const runProgressCommand = createCompositeCommand({
|
|
|
38
39
|
if (options.compare) {
|
|
39
40
|
targetLevel = data.levels.find((g) => g.id === options.compare);
|
|
40
41
|
if (!targetLevel) {
|
|
41
|
-
|
|
42
|
+
process.stderr.write(
|
|
43
|
+
formatError(`Target level not found: ${options.compare}`) + "\n",
|
|
44
|
+
);
|
|
42
45
|
process.exit(1);
|
|
43
46
|
}
|
|
44
47
|
} else {
|
|
45
48
|
targetLevel = getDefaultTargetLevel(level, data.levels);
|
|
46
49
|
if (!targetLevel) {
|
|
47
|
-
|
|
50
|
+
process.stderr.write(
|
|
51
|
+
formatError("No next level available for progression.") + "\n",
|
|
52
|
+
);
|
|
48
53
|
process.exit(1);
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -80,5 +85,5 @@ export const runProgressCommand = createCompositeCommand({
|
|
|
80
85
|
}),
|
|
81
86
|
formatter: formatProgress,
|
|
82
87
|
usageExample:
|
|
83
|
-
"
|
|
88
|
+
"npx fit-pathway progress software_engineering L3 --track=platform --compare=L4",
|
|
84
89
|
});
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Browse and compare interview questions across skills and behaviours.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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,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,12 +99,25 @@ function showQuestionsSummary(data) {
|
|
|
94
99
|
return [maturity.replace(/_/g, " "), count];
|
|
95
100
|
});
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
process.stdout.write("\n" + formatSubheader("Behaviour Questions") + "\n");
|
|
103
|
+
process.stdout.write(
|
|
104
|
+
formatTable(["Maturity", "Count"], behaviourRows) + "\n",
|
|
105
|
+
);
|
|
106
|
+
|
|
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",
|
|
120
|
+
);
|
|
103
121
|
}
|
|
104
122
|
|
|
105
123
|
/**
|
package/src/commands/skill.js
CHANGED
|
@@ -4,18 +4,24 @@
|
|
|
4
4
|
* Handles skill summary, listing, and detail display in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 {
|
|
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
|
|