@forwardimpact/pathway 0.25.15 → 0.25.21
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 +62 -54
- package/package.json +1 -3
- package/src/commands/agent-io.js +120 -0
- package/src/commands/agent.js +266 -349
- package/src/commands/init.js +2 -2
- package/src/commands/job.js +237 -183
- package/src/components/comparison-radar.js +118 -103
- package/src/components/progression-table.js +244 -208
- package/src/formatters/index.js +0 -19
- package/src/formatters/interview/markdown.js +100 -88
- package/src/formatters/job/description.js +76 -75
- package/src/formatters/job/dom.js +113 -97
- package/src/formatters/level/dom.js +87 -102
- package/src/formatters/questions/markdown.js +37 -33
- package/src/formatters/questions/shared.js +142 -75
- package/src/formatters/skill/dom.js +102 -93
- package/src/lib/comparison-radar-chart.js +256 -0
- package/src/lib/radar-utils.js +199 -0
- package/src/lib/radar.js +25 -662
- package/src/pages/agent-builder-download.js +170 -0
- package/src/pages/agent-builder-preview.js +344 -0
- package/src/pages/agent-builder.js +6 -550
- package/src/pages/progress-comparison.js +110 -0
- package/src/pages/progress.js +11 -111
- package/src/pages/self-assessment-steps.js +494 -0
- package/src/pages/self-assessment.js +54 -504
- package/src/formatters/behaviour/microdata.js +0 -106
- package/src/formatters/discipline/microdata.js +0 -117
- package/src/formatters/driver/microdata.js +0 -91
- package/src/formatters/level/microdata.js +0 -141
- package/src/formatters/microdata-shared.js +0 -184
- package/src/formatters/skill/microdata.js +0 -151
- package/src/formatters/stage/microdata.js +0 -116
- package/src/formatters/track/microdata.js +0 -111
package/bin/fit-pathway.js
CHANGED
|
@@ -235,6 +235,62 @@ Options:
|
|
|
235
235
|
--format=FORMAT Output format: table, yaml, json
|
|
236
236
|
`;
|
|
237
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",
|
|
276
|
+
};
|
|
277
|
+
|
|
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
|
+
|
|
238
294
|
/**
|
|
239
295
|
* Parse command line arguments
|
|
240
296
|
* @param {string[]} args
|
|
@@ -273,62 +329,14 @@ function parseArgs(args) {
|
|
|
273
329
|
};
|
|
274
330
|
|
|
275
331
|
for (const arg of args) {
|
|
276
|
-
if (arg
|
|
277
|
-
result
|
|
278
|
-
} else if (arg
|
|
279
|
-
result
|
|
280
|
-
} else if (arg === "--list" || arg === "-l") {
|
|
281
|
-
result.list = true;
|
|
282
|
-
} else if (arg === "--json") {
|
|
283
|
-
result.json = true;
|
|
284
|
-
} else if (arg.startsWith("--type=")) {
|
|
285
|
-
result.type = arg.slice(7);
|
|
286
|
-
} else if (arg.startsWith("--compare=")) {
|
|
287
|
-
result.compare = arg.slice(10);
|
|
288
|
-
} else if (arg.startsWith("--data=")) {
|
|
289
|
-
result.data = arg.slice(7);
|
|
290
|
-
} else if (arg.startsWith("--track=")) {
|
|
291
|
-
result.track = arg.slice(8);
|
|
292
|
-
} else if (arg.startsWith("--output=")) {
|
|
293
|
-
result.output = arg.slice(9);
|
|
294
|
-
} else if (arg.startsWith("--level=")) {
|
|
295
|
-
result.level = arg.slice(8);
|
|
296
|
-
} else if (arg.startsWith("--maturity=")) {
|
|
297
|
-
result.maturity = arg.slice(11);
|
|
298
|
-
} else if (arg.startsWith("--skill=")) {
|
|
299
|
-
result.skill = arg.slice(8);
|
|
300
|
-
} else if (arg.startsWith("--behaviour=")) {
|
|
301
|
-
result.behaviour = arg.slice(12);
|
|
302
|
-
} else if (arg.startsWith("--capability=")) {
|
|
303
|
-
result.capability = arg.slice(14);
|
|
304
|
-
} else if (arg.startsWith("--format=")) {
|
|
305
|
-
result.format = arg.slice(9);
|
|
306
|
-
} else if (arg === "--stats") {
|
|
307
|
-
result.stats = true;
|
|
308
|
-
} else if (arg.startsWith("--role=")) {
|
|
309
|
-
result.role = arg.slice(7);
|
|
310
|
-
} else if (arg === "--all-roles") {
|
|
311
|
-
result["all-roles"] = true;
|
|
312
|
-
} else if (arg.startsWith("--stage=")) {
|
|
313
|
-
result.stage = arg.slice(8);
|
|
314
|
-
} else if (arg === "--all-stages") {
|
|
315
|
-
result["all-stages"] = true;
|
|
316
|
-
} else if (arg === "--agent") {
|
|
317
|
-
result.agent = true;
|
|
318
|
-
} else if (arg.startsWith("--checklist=")) {
|
|
319
|
-
result.checklist = arg.slice(12);
|
|
320
|
-
} else if (arg === "--skills") {
|
|
321
|
-
result.skills = true;
|
|
322
|
-
} else if (arg === "--tools") {
|
|
323
|
-
result.tools = true;
|
|
332
|
+
if (BOOLEAN_FLAGS[arg]) {
|
|
333
|
+
result[BOOLEAN_FLAGS[arg]] = true;
|
|
334
|
+
} else if (NEGATION_FLAGS[arg]) {
|
|
335
|
+
result[NEGATION_FLAGS[arg]] = false;
|
|
324
336
|
} else if (arg.startsWith("--port=")) {
|
|
325
337
|
result.port = parseInt(arg.slice(7), 10);
|
|
326
|
-
} else if (arg
|
|
327
|
-
|
|
328
|
-
} else if (arg === "--no-clean") {
|
|
329
|
-
result.clean = false;
|
|
330
|
-
} else if (arg.startsWith("--url=")) {
|
|
331
|
-
result.url = arg.slice(6);
|
|
338
|
+
} else if (parseValueFlag(arg, result)) {
|
|
339
|
+
// handled
|
|
332
340
|
} else if (!arg.startsWith("-")) {
|
|
333
341
|
if (!result.command) {
|
|
334
342
|
result.command = arg;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.21",
|
|
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,7 +20,6 @@
|
|
|
20
20
|
"engineering"
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
|
-
"main": "src/index.js",
|
|
24
23
|
"bin": {
|
|
25
24
|
"fit-pathway": "./bin/fit-pathway.js"
|
|
26
25
|
},
|
|
@@ -36,7 +35,6 @@
|
|
|
36
35
|
"templates/"
|
|
37
36
|
],
|
|
38
37
|
"exports": {
|
|
39
|
-
".": "./src/index.js",
|
|
40
38
|
"./formatters": "./src/formatters/index.js",
|
|
41
39
|
"./commands": "./src/commands/index.js"
|
|
42
40
|
},
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent file I/O helpers
|
|
3
|
+
*
|
|
4
|
+
* Handles writing agent profiles, skills, settings, and team instructions
|
|
5
|
+
* to disk. Extracted from agent.js to keep command logic focused.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { writeFile, mkdir, readFile } from "fs/promises";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
12
|
+
import {
|
|
13
|
+
formatAgentSkill,
|
|
14
|
+
formatInstallScript,
|
|
15
|
+
formatReference,
|
|
16
|
+
} from "../formatters/agent/skill.js";
|
|
17
|
+
import { formatSuccess } from "../lib/cli-output.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Ensure directory exists for a file path
|
|
21
|
+
* @param {string} filePath - Full file path
|
|
22
|
+
*/
|
|
23
|
+
async function ensureDir(filePath) {
|
|
24
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate Claude Code settings file
|
|
29
|
+
* Merges with existing settings if file exists
|
|
30
|
+
* @param {string} baseDir - Base output directory
|
|
31
|
+
* @param {Object} claudeCodeSettings - Settings loaded from data
|
|
32
|
+
*/
|
|
33
|
+
export async function generateClaudeCodeSettings(baseDir, claudeCodeSettings) {
|
|
34
|
+
const settingsPath = join(baseDir, ".claude", "settings.json");
|
|
35
|
+
|
|
36
|
+
let settings = {};
|
|
37
|
+
if (existsSync(settingsPath)) {
|
|
38
|
+
const content = await readFile(settingsPath, "utf-8");
|
|
39
|
+
settings = JSON.parse(content);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const merged = { ...settings, ...claudeCodeSettings };
|
|
43
|
+
|
|
44
|
+
await ensureDir(settingsPath);
|
|
45
|
+
await writeFile(
|
|
46
|
+
settingsPath,
|
|
47
|
+
JSON.stringify(merged, null, 2) + "\n",
|
|
48
|
+
"utf-8",
|
|
49
|
+
);
|
|
50
|
+
console.log(formatSuccess(`Updated: ${settingsPath}`));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Write agent profile to file
|
|
55
|
+
* @param {Object} profile - Generated profile
|
|
56
|
+
* @param {string} baseDir - Base output directory
|
|
57
|
+
* @param {string} template - Mustache template for agent profile
|
|
58
|
+
*/
|
|
59
|
+
export async function writeProfile(profile, baseDir, template) {
|
|
60
|
+
const profilePath = join(baseDir, ".claude", "agents", profile.filename);
|
|
61
|
+
const profileContent = formatAgentProfile(profile, template);
|
|
62
|
+
await ensureDir(profilePath);
|
|
63
|
+
await writeFile(profilePath, profileContent, "utf-8");
|
|
64
|
+
console.log(formatSuccess(`Created: ${profilePath}`));
|
|
65
|
+
return profilePath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Write team instructions to CLAUDE.md
|
|
70
|
+
* @param {string|null} teamInstructions - Interpolated team instructions content
|
|
71
|
+
* @param {string} baseDir - Base output directory
|
|
72
|
+
* @returns {string|null} Path written, or null if skipped
|
|
73
|
+
*/
|
|
74
|
+
export async function writeTeamInstructions(teamInstructions, baseDir) {
|
|
75
|
+
if (!teamInstructions) return null;
|
|
76
|
+
const filePath = join(baseDir, ".claude", "CLAUDE.md");
|
|
77
|
+
await ensureDir(filePath);
|
|
78
|
+
await writeFile(filePath, teamInstructions.trim() + "\n", "utf-8");
|
|
79
|
+
console.log(formatSuccess(`Created: ${filePath}`));
|
|
80
|
+
return filePath;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Write skill files (SKILL.md, scripts/install.sh, references/REFERENCE.md)
|
|
85
|
+
* @param {Array} skills - Generated skills
|
|
86
|
+
* @param {string} baseDir - Base output directory
|
|
87
|
+
* @param {Object} templates - Templates object with skill, install, reference
|
|
88
|
+
*/
|
|
89
|
+
export async function writeSkills(skills, baseDir, templates) {
|
|
90
|
+
let fileCount = 0;
|
|
91
|
+
for (const skill of skills) {
|
|
92
|
+
const skillDir = join(baseDir, ".claude", "skills", skill.dirname);
|
|
93
|
+
|
|
94
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
95
|
+
const skillContent = formatAgentSkill(skill, templates.skill);
|
|
96
|
+
await ensureDir(skillPath);
|
|
97
|
+
await writeFile(skillPath, skillContent, "utf-8");
|
|
98
|
+
console.log(formatSuccess(`Created: ${skillPath}`));
|
|
99
|
+
fileCount++;
|
|
100
|
+
|
|
101
|
+
if (skill.installScript) {
|
|
102
|
+
const installPath = join(skillDir, "scripts", "install.sh");
|
|
103
|
+
const installContent = formatInstallScript(skill, templates.install);
|
|
104
|
+
await ensureDir(installPath);
|
|
105
|
+
await writeFile(installPath, installContent, { mode: 0o755 });
|
|
106
|
+
console.log(formatSuccess(`Created: ${installPath}`));
|
|
107
|
+
fileCount++;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (skill.implementationReference) {
|
|
111
|
+
const refPath = join(skillDir, "references", "REFERENCE.md");
|
|
112
|
+
const refContent = formatReference(skill, templates.reference);
|
|
113
|
+
await ensureDir(refPath);
|
|
114
|
+
await writeFile(refPath, refContent, "utf-8");
|
|
115
|
+
console.log(formatSuccess(`Created: ${refPath}`));
|
|
116
|
+
fileCount++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return fileCount;
|
|
120
|
+
}
|