@forwardimpact/pathway 0.25.26 ā 0.25.27
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 +2 -1
- package/src/commands/agent-io.js +9 -6
- package/src/commands/agent-list.js +3 -5
- package/src/commands/agent.js +3 -2
- package/src/commands/behaviour.js +1 -1
- package/src/commands/build-bundle.js +8 -5
- package/src/commands/build-packs.js +9 -6
- package/src/commands/build.js +23 -20
- package/src/commands/command-factory.js +6 -4
- package/src/commands/dev.js +5 -2
- package/src/commands/discipline.js +3 -1
- package/src/commands/index.js +0 -2
- package/src/commands/job.js +7 -7
- package/src/commands/level.js +1 -1
- package/src/commands/questions.js +2 -2
- package/src/commands/skill.js +2 -2
- package/src/commands/tool.js +3 -3
- package/src/commands/track.js +3 -2
- package/src/commands/update.js +13 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.27",
|
|
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": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"./commands": "./src/commands/index.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@forwardimpact/libtelemetry": "^0.1.33",
|
|
43
44
|
"@forwardimpact/map": "^0.15.5",
|
|
44
45
|
"@forwardimpact/libcli": "^0.1.0",
|
|
45
46
|
"@forwardimpact/libskill": "^4.0.0",
|
package/src/commands/agent-io.js
CHANGED
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
formatReference,
|
|
16
16
|
} from "../formatters/agent/skill.js";
|
|
17
17
|
import { formatSuccess } from "@forwardimpact/libcli";
|
|
18
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
19
|
+
|
|
20
|
+
const logger = createLogger("pathway");
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* Ensure directory exists for a file path
|
|
@@ -47,7 +50,7 @@ export async function generateClaudeCodeSettings(baseDir, claudeCodeSettings) {
|
|
|
47
50
|
JSON.stringify(merged, null, 2) + "\n",
|
|
48
51
|
"utf-8",
|
|
49
52
|
);
|
|
50
|
-
|
|
53
|
+
logger.info(formatSuccess(`Updated: ${settingsPath}`));
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
/**
|
|
@@ -61,7 +64,7 @@ export async function writeProfile(profile, baseDir, template) {
|
|
|
61
64
|
const profileContent = formatAgentProfile(profile, template);
|
|
62
65
|
await ensureDir(profilePath);
|
|
63
66
|
await writeFile(profilePath, profileContent, "utf-8");
|
|
64
|
-
|
|
67
|
+
logger.info(formatSuccess(`Created: ${profilePath}`));
|
|
65
68
|
return profilePath;
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -76,7 +79,7 @@ export async function writeTeamInstructions(teamInstructions, baseDir) {
|
|
|
76
79
|
const filePath = join(baseDir, ".claude", "CLAUDE.md");
|
|
77
80
|
await ensureDir(filePath);
|
|
78
81
|
await writeFile(filePath, teamInstructions.trim() + "\n", "utf-8");
|
|
79
|
-
|
|
82
|
+
logger.info(formatSuccess(`Created: ${filePath}`));
|
|
80
83
|
return filePath;
|
|
81
84
|
}
|
|
82
85
|
|
|
@@ -95,7 +98,7 @@ export async function writeSkills(skills, baseDir, templates) {
|
|
|
95
98
|
const skillContent = formatAgentSkill(skill, templates.skill);
|
|
96
99
|
await ensureDir(skillPath);
|
|
97
100
|
await writeFile(skillPath, skillContent, "utf-8");
|
|
98
|
-
|
|
101
|
+
logger.info(formatSuccess(`Created: ${skillPath}`));
|
|
99
102
|
fileCount++;
|
|
100
103
|
|
|
101
104
|
if (skill.installScript) {
|
|
@@ -103,7 +106,7 @@ export async function writeSkills(skills, baseDir, templates) {
|
|
|
103
106
|
const installContent = formatInstallScript(skill, templates.install);
|
|
104
107
|
await ensureDir(installPath);
|
|
105
108
|
await writeFile(installPath, installContent, { mode: 0o755 });
|
|
106
|
-
|
|
109
|
+
logger.info(formatSuccess(`Created: ${installPath}`));
|
|
107
110
|
fileCount++;
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -112,7 +115,7 @@ export async function writeSkills(skills, baseDir, templates) {
|
|
|
112
115
|
const refContent = formatReference(skill, templates.reference);
|
|
113
116
|
await ensureDir(refPath);
|
|
114
117
|
await writeFile(refPath, refContent, "utf-8");
|
|
115
|
-
|
|
118
|
+
logger.info(formatSuccess(`Created: ${refPath}`));
|
|
116
119
|
fileCount++;
|
|
117
120
|
}
|
|
118
121
|
}
|
|
@@ -16,8 +16,6 @@ import {
|
|
|
16
16
|
SummaryRenderer,
|
|
17
17
|
} from "@forwardimpact/libcli";
|
|
18
18
|
|
|
19
|
-
const summary = new SummaryRenderer({ process });
|
|
20
|
-
|
|
21
19
|
/**
|
|
22
20
|
* Find valid agent combination pairs
|
|
23
21
|
* @param {Object} data - Pathway data
|
|
@@ -47,6 +45,7 @@ export function findValidCombinations(data, agentData) {
|
|
|
47
45
|
* @param {Array} skillsWithAgent - Skills with agent sections
|
|
48
46
|
*/
|
|
49
47
|
export function showAgentSummary(data, agentData, skillsWithAgent) {
|
|
48
|
+
const summary = new SummaryRenderer({ process });
|
|
50
49
|
const validCombinations = findValidCombinations(data, agentData).length;
|
|
51
50
|
const skillsWithAgentCount = skillsWithAgent.filter((s) => s.agent).length;
|
|
52
51
|
|
|
@@ -101,9 +100,8 @@ function listAgentCombinationsCompact(data, agentData) {
|
|
|
101
100
|
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
102
101
|
const agentName = `${abbrev}-${toKebabCase(track.id)}`;
|
|
103
102
|
const specName = humanDiscipline.specialization || humanDiscipline.id;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
|
|
103
|
+
process.stdout.write(
|
|
104
|
+
`${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})\n`,
|
|
107
105
|
);
|
|
108
106
|
}
|
|
109
107
|
}
|
package/src/commands/agent.js
CHANGED
|
@@ -347,7 +347,8 @@ export async function runAgentCommand({
|
|
|
347
347
|
level,
|
|
348
348
|
skills: skillsWithAgent,
|
|
349
349
|
});
|
|
350
|
-
for (const skill of derivedSkills)
|
|
350
|
+
for (const skill of derivedSkills)
|
|
351
|
+
process.stdout.write(skill.skillId + "\n");
|
|
351
352
|
return;
|
|
352
353
|
}
|
|
353
354
|
|
|
@@ -362,7 +363,7 @@ export async function runAgentCommand({
|
|
|
362
363
|
skillMatrix: derivedSkills,
|
|
363
364
|
skills: skillsWithAgent,
|
|
364
365
|
});
|
|
365
|
-
|
|
366
|
+
process.stdout.write(toolkitToPlainList(toolkit) + "\n");
|
|
366
367
|
return;
|
|
367
368
|
}
|
|
368
369
|
|
|
@@ -65,7 +65,7 @@ function formatSummary(behaviours, data) {
|
|
|
65
65
|
*/
|
|
66
66
|
function formatDetail(viewAndContext) {
|
|
67
67
|
const { behaviour, drivers } = viewAndContext;
|
|
68
|
-
|
|
68
|
+
process.stdout.write(behaviourToMarkdown(behaviour, { drivers }) + "\n");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export const runBehaviourCommand = createEntityCommand({
|
|
@@ -11,6 +11,9 @@ import { cp, mkdir, rm, readFile, writeFile } from "fs/promises";
|
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
import { execFileSync } from "child_process";
|
|
13
13
|
import Mustache from "mustache";
|
|
14
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
15
|
+
|
|
16
|
+
const logger = createLogger("pathway");
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Generate distribution bundle (bundle.tar.gz + install.sh)
|
|
@@ -30,7 +33,7 @@ export async function generateBundle({
|
|
|
30
33
|
version,
|
|
31
34
|
templatesDir,
|
|
32
35
|
}) {
|
|
33
|
-
|
|
36
|
+
logger.info("š¦ Generating distribution bundle...");
|
|
34
37
|
|
|
35
38
|
const frameworkTitle = framework.title || "Engineering Pathway";
|
|
36
39
|
|
|
@@ -51,14 +54,14 @@ export async function generateBundle({
|
|
|
51
54
|
join(bundleDir, "package.json"),
|
|
52
55
|
JSON.stringify(bundlePkg, null, 2) + "\n",
|
|
53
56
|
);
|
|
54
|
-
|
|
57
|
+
logger.info(` ā package.json (pathway ^${version})`);
|
|
55
58
|
|
|
56
59
|
// 3. Copy data files into bundle
|
|
57
60
|
await cp(dataDir, join(bundleDir, "data"), {
|
|
58
61
|
recursive: true,
|
|
59
62
|
dereference: true,
|
|
60
63
|
});
|
|
61
|
-
|
|
64
|
+
logger.info(" ā data/");
|
|
62
65
|
|
|
63
66
|
// 4. Create tar.gz from the bundle directory
|
|
64
67
|
execFileSync("tar", [
|
|
@@ -68,7 +71,7 @@ export async function generateBundle({
|
|
|
68
71
|
outputDir,
|
|
69
72
|
"_bundle",
|
|
70
73
|
]);
|
|
71
|
-
|
|
74
|
+
logger.info(" ā bundle.tar.gz");
|
|
72
75
|
|
|
73
76
|
// 5. Clean up temporary bundle directory
|
|
74
77
|
await rm(bundleDir, { recursive: true });
|
|
@@ -84,5 +87,5 @@ export async function generateBundle({
|
|
|
84
87
|
await writeFile(join(outputDir, "install.sh"), installScript, {
|
|
85
88
|
mode: 0o755,
|
|
86
89
|
});
|
|
87
|
-
|
|
90
|
+
logger.info(" ā install.sh");
|
|
88
91
|
}
|
|
@@ -18,7 +18,10 @@ import { join } from "path";
|
|
|
18
18
|
import { execFileSync } from "child_process";
|
|
19
19
|
import { createHash } from "crypto";
|
|
20
20
|
|
|
21
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
21
22
|
import { createDataLoader } from "@forwardimpact/map/loader";
|
|
23
|
+
|
|
24
|
+
const logger = createLogger("pathway");
|
|
22
25
|
import { createTemplateLoader } from "@forwardimpact/libtemplate";
|
|
23
26
|
import {
|
|
24
27
|
generateStageAgentProfile,
|
|
@@ -467,7 +470,7 @@ export async function generatePacks({
|
|
|
467
470
|
version,
|
|
468
471
|
templatesDir,
|
|
469
472
|
}) {
|
|
470
|
-
|
|
473
|
+
logger.info("š¦ Generating agent/skill packs...");
|
|
471
474
|
|
|
472
475
|
const normalizedSiteUrl = siteUrl.replace(/\/$/, "");
|
|
473
476
|
const frameworkTitle = framework.title || "Engineering Pathway";
|
|
@@ -495,7 +498,7 @@ export async function generatePacks({
|
|
|
495
498
|
|
|
496
499
|
const combinations = findValidCombinations(data, agentData);
|
|
497
500
|
if (combinations.length === 0) {
|
|
498
|
-
|
|
501
|
+
logger.info(" (no valid discipline/track combinations ā skipping)");
|
|
499
502
|
await rm(stagingDir, { recursive: true, force: true });
|
|
500
503
|
return;
|
|
501
504
|
}
|
|
@@ -538,7 +541,7 @@ export async function generatePacks({
|
|
|
538
541
|
digest,
|
|
539
542
|
});
|
|
540
543
|
|
|
541
|
-
|
|
544
|
+
logger.info(` ā packs/${agentName}.tar.gz`);
|
|
542
545
|
}
|
|
543
546
|
|
|
544
547
|
// Write per-pack skill repositories (one per discipline/track combination)
|
|
@@ -550,17 +553,17 @@ export async function generatePacks({
|
|
|
550
553
|
pack.name,
|
|
551
554
|
);
|
|
552
555
|
allPackEntries.push({ packName: pack.name, entries });
|
|
553
|
-
|
|
556
|
+
logger.info(
|
|
554
557
|
` ā packs/${pack.name}/.well-known/skills/ (${entries.length} skills)`,
|
|
555
558
|
);
|
|
556
559
|
}
|
|
557
560
|
|
|
558
561
|
// Write aggregate repository at packs/ level
|
|
559
562
|
await writeAggregateRepository(packsDir, allPackEntries);
|
|
560
|
-
|
|
563
|
+
logger.info(" ā packs/.well-known/skills/index.json (aggregate)");
|
|
561
564
|
|
|
562
565
|
await rm(stagingDir, { recursive: true, force: true });
|
|
563
566
|
|
|
564
567
|
await writeApmManifest(outputDir, packs, version, frameworkTitle);
|
|
565
|
-
|
|
568
|
+
logger.info(" ā apm.yml");
|
|
566
569
|
}
|
package/src/commands/build.js
CHANGED
|
@@ -13,8 +13,11 @@ import { cp, mkdir, rm, access, realpath, writeFile } from "fs/promises";
|
|
|
13
13
|
import { readFileSync } from "fs";
|
|
14
14
|
import { join, dirname, relative, resolve } from "path";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
16
17
|
import { createIndexGenerator } from "@forwardimpact/map/index-generator";
|
|
17
18
|
import { createDataLoader } from "@forwardimpact/map/loader";
|
|
19
|
+
|
|
20
|
+
const logger = createLogger("pathway");
|
|
18
21
|
import { generateBundle } from "./build-bundle.js";
|
|
19
22
|
import { generatePacks } from "./build-packs.js";
|
|
20
23
|
|
|
@@ -85,7 +88,7 @@ export async function runBuildCommand({ dataDir, options }) {
|
|
|
85
88
|
framework = { emojiIcon: "š", title: "Engineering Pathway" };
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
logger.info(`
|
|
89
92
|
${framework.emojiIcon} Generating ${framework.title} static site...
|
|
90
93
|
`);
|
|
91
94
|
|
|
@@ -93,7 +96,7 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
93
96
|
if (clean) {
|
|
94
97
|
try {
|
|
95
98
|
await access(outputDir);
|
|
96
|
-
|
|
99
|
+
logger.info(`šļø Cleaning ${outputDir}...`);
|
|
97
100
|
await rm(outputDir, { recursive: true });
|
|
98
101
|
} catch {
|
|
99
102
|
// Directory doesn't exist, nothing to clean
|
|
@@ -104,12 +107,12 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
104
107
|
await mkdir(outputDir, { recursive: true });
|
|
105
108
|
|
|
106
109
|
// Generate index files in data directory
|
|
107
|
-
|
|
110
|
+
logger.info("š Generating index files...");
|
|
108
111
|
const indexGenerator = createIndexGenerator();
|
|
109
112
|
await indexGenerator.generateAllIndexes(dataDir);
|
|
110
113
|
|
|
111
114
|
// Copy app assets
|
|
112
|
-
|
|
115
|
+
logger.info("š¦ Copying application files...");
|
|
113
116
|
for (const asset of PUBLIC_ASSETS) {
|
|
114
117
|
const src = join(appDir, asset);
|
|
115
118
|
const dest = join(outputDir, asset);
|
|
@@ -117,9 +120,9 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
117
120
|
try {
|
|
118
121
|
await access(src);
|
|
119
122
|
await cp(src, dest, { recursive: true });
|
|
120
|
-
|
|
123
|
+
logger.info(` ā ${asset}`);
|
|
121
124
|
} catch (err) {
|
|
122
|
-
|
|
125
|
+
logger.info(` ā ļø Skipped ${asset}: ${err.message}`);
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -132,19 +135,19 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
132
135
|
try {
|
|
133
136
|
await access(src);
|
|
134
137
|
await cp(src, dest, { recursive: true });
|
|
135
|
-
|
|
138
|
+
logger.info(` ā ${asset}`);
|
|
136
139
|
} catch (err) {
|
|
137
|
-
|
|
140
|
+
logger.info(` ā ļø Skipped ${asset}: ${err.message}`);
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
// Copy @forwardimpact/map and @forwardimpact/libskill packages
|
|
142
145
|
// These are needed by the browser's import map
|
|
143
|
-
|
|
146
|
+
logger.info("š Copying package dependencies...");
|
|
144
147
|
await cp(mapLibDir, join(outputDir, "map/lib"), { recursive: true });
|
|
145
|
-
|
|
148
|
+
logger.info(` ā map/lib`);
|
|
146
149
|
await cp(modelLibDir, join(outputDir, "model/lib"), { recursive: true });
|
|
147
|
-
|
|
150
|
+
logger.info(` ā model/lib`);
|
|
148
151
|
// Copy libui JS (src/) and CSS (src/css/)
|
|
149
152
|
await cp(uiLibDir, join(outputDir, "ui/lib"), { recursive: true });
|
|
150
153
|
// CSS is within uiLibDir/css/ so it's already copied as ui/lib/css/
|
|
@@ -152,10 +155,10 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
152
155
|
await cp(join(uiLibDir, "css"), join(outputDir, "ui/css"), {
|
|
153
156
|
recursive: true,
|
|
154
157
|
});
|
|
155
|
-
|
|
158
|
+
logger.info(` ā ui/lib + ui/css`);
|
|
156
159
|
|
|
157
160
|
// Copy vendor dependencies for offline usage
|
|
158
|
-
|
|
161
|
+
logger.info("š¦ Copying vendor dependencies...");
|
|
159
162
|
const vendorDir = join(outputDir, "vendor");
|
|
160
163
|
await mkdir(vendorDir, { recursive: true });
|
|
161
164
|
|
|
@@ -163,17 +166,17 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
163
166
|
const mustacheSrc = fileURLToPath(import.meta.resolve("mustache"));
|
|
164
167
|
const mustacheMjs = join(dirname(mustacheSrc), "mustache.mjs");
|
|
165
168
|
await cp(mustacheMjs, join(vendorDir, "mustache.mjs"));
|
|
166
|
-
|
|
169
|
+
logger.info(" ā vendor/mustache.mjs");
|
|
167
170
|
|
|
168
171
|
// yaml (browser ESM build ā not in package exports, resolve via filesystem)
|
|
169
172
|
// import.meta.resolve("yaml") ā .../yaml/dist/index.js, go up two levels
|
|
170
173
|
const yamlPkg = dirname(dirname(fileURLToPath(import.meta.resolve("yaml"))));
|
|
171
174
|
const yamlBrowserDist = join(yamlPkg, "browser", "dist");
|
|
172
175
|
await cp(yamlBrowserDist, join(vendorDir, "yaml"), { recursive: true });
|
|
173
|
-
|
|
176
|
+
logger.info(" ā vendor/yaml/");
|
|
174
177
|
|
|
175
178
|
// Copy data directory (dereference symlinks to copy actual content)
|
|
176
|
-
|
|
179
|
+
logger.info("š Copying data files...");
|
|
177
180
|
const dataOutputDir = join(outputDir, "data");
|
|
178
181
|
|
|
179
182
|
// Check if source and destination are the same (e.g., when --output=.)
|
|
@@ -181,10 +184,10 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
181
184
|
const resolvedDataOutputDir = resolve(dataOutputDir);
|
|
182
185
|
|
|
183
186
|
if (resolvedDataDir === resolvedDataOutputDir) {
|
|
184
|
-
|
|
187
|
+
logger.info(` ā data/ (already in place)`);
|
|
185
188
|
} else {
|
|
186
189
|
await cp(dataDir, dataOutputDir, { recursive: true, dereference: true });
|
|
187
|
-
|
|
190
|
+
logger.info(` ā data/ (from ${relative(process.cwd(), dataDir)})`);
|
|
188
191
|
}
|
|
189
192
|
|
|
190
193
|
// Write version.json for the web app footer
|
|
@@ -193,7 +196,7 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
193
196
|
join(outputDir, "version.json"),
|
|
194
197
|
JSON.stringify({ version }) + "\n",
|
|
195
198
|
);
|
|
196
|
-
|
|
199
|
+
logger.info(` ā version.json (${version})`);
|
|
197
200
|
|
|
198
201
|
// Generate distribution surfaces if siteUrl is configured
|
|
199
202
|
const siteUrl = options.url || framework.distribution?.siteUrl;
|
|
@@ -218,7 +221,7 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
218
221
|
}
|
|
219
222
|
|
|
220
223
|
// Show summary
|
|
221
|
-
|
|
224
|
+
logger.info(`
|
|
222
225
|
ā
Site generated successfully!
|
|
223
226
|
|
|
224
227
|
Output: ${outputDir}
|
|
@@ -59,7 +59,9 @@ export function createEntityCommand({
|
|
|
59
59
|
// --list: Output descriptive comma-separated lines for piping and AI agent discovery
|
|
60
60
|
if (options.list) {
|
|
61
61
|
for (const item of items) {
|
|
62
|
-
|
|
62
|
+
process.stdout.write(
|
|
63
|
+
(formatListItem ? formatListItem(item) : item.id) + "\n",
|
|
64
|
+
);
|
|
63
65
|
}
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
@@ -67,7 +69,7 @@ export function createEntityCommand({
|
|
|
67
69
|
// No args: Show summary
|
|
68
70
|
if (!id) {
|
|
69
71
|
if (options.json) {
|
|
70
|
-
|
|
72
|
+
process.stdout.write(JSON.stringify(items, null, 2) + "\n");
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
73
75
|
formatSummary(items, data);
|
|
@@ -168,7 +170,7 @@ function handleDetail({
|
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
if (options.json) {
|
|
171
|
-
|
|
173
|
+
process.stdout.write(JSON.stringify(view, null, 2) + "\n");
|
|
172
174
|
return;
|
|
173
175
|
}
|
|
174
176
|
|
|
@@ -226,7 +228,7 @@ export function createCompositeCommand({
|
|
|
226
228
|
}
|
|
227
229
|
|
|
228
230
|
if (options.json) {
|
|
229
|
-
|
|
231
|
+
process.stdout.write(JSON.stringify(view, null, 2) + "\n");
|
|
230
232
|
return;
|
|
231
233
|
}
|
|
232
234
|
|
package/src/commands/dev.js
CHANGED
|
@@ -10,9 +10,12 @@ import { readFile, stat } from "fs/promises";
|
|
|
10
10
|
import { readFileSync } from "fs";
|
|
11
11
|
import { join, extname, dirname } from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
13
14
|
import { createIndexGenerator } from "@forwardimpact/map/index-generator";
|
|
14
15
|
import { createDataLoader } from "@forwardimpact/map/loader";
|
|
15
16
|
|
|
17
|
+
const logger = createLogger("pathway");
|
|
18
|
+
|
|
16
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
20
|
const __dirname = dirname(__filename);
|
|
18
21
|
const publicDir = join(__dirname, "..");
|
|
@@ -131,7 +134,7 @@ export async function runDevCommand({ dataDir, options }) {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
// Generate _index.yaml files before serving
|
|
134
|
-
|
|
137
|
+
logger.info("Generating index files...");
|
|
135
138
|
const indexGenerator = createIndexGenerator();
|
|
136
139
|
await indexGenerator.generateAllIndexes(dataDir);
|
|
137
140
|
|
|
@@ -191,7 +194,7 @@ export async function runDevCommand({ dataDir, options }) {
|
|
|
191
194
|
});
|
|
192
195
|
|
|
193
196
|
server.listen(port, () => {
|
|
194
|
-
|
|
197
|
+
logger.info(`
|
|
195
198
|
${framework.emojiIcon} ${framework.title} running at http://localhost:${port}
|
|
196
199
|
š Data directory: ${dataDir}
|
|
197
200
|
|
|
@@ -67,7 +67,9 @@ function formatSummary(disciplines) {
|
|
|
67
67
|
*/
|
|
68
68
|
function formatDetail(viewAndContext) {
|
|
69
69
|
const { discipline, skills, behaviours, tracks } = viewAndContext;
|
|
70
|
-
|
|
70
|
+
process.stdout.write(
|
|
71
|
+
disciplineToMarkdown(discipline, { skills, behaviours, tracks }) + "\n",
|
|
72
|
+
);
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
export const runDisciplineCommand = createEntityCommand({
|
package/src/commands/index.js
CHANGED
|
@@ -16,6 +16,4 @@ export { runJobCommand } from "./job.js";
|
|
|
16
16
|
export { runInterviewCommand } from "./interview.js";
|
|
17
17
|
export { runProgressCommand } from "./progress.js";
|
|
18
18
|
export { runQuestionsCommand } from "./questions.js";
|
|
19
|
-
export { runServeCommand } from "./serve.js";
|
|
20
|
-
export { runSiteCommand } from "./site.js";
|
|
21
19
|
export { runUpdateCommand } from "./update.js";
|
package/src/commands/job.js
CHANGED
|
@@ -41,7 +41,7 @@ import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
|
41
41
|
* @param {string} jobTemplate - Mustache template for job description
|
|
42
42
|
*/
|
|
43
43
|
function formatJob(view, _options, entities, jobTemplate) {
|
|
44
|
-
|
|
44
|
+
process.stdout.write(jobToMarkdown(view, entities, jobTemplate) + "\n");
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -56,11 +56,11 @@ function printJobList(filteredJobs) {
|
|
|
56
56
|
track: job.track,
|
|
57
57
|
});
|
|
58
58
|
if (job.track) {
|
|
59
|
-
|
|
60
|
-
`${job.discipline.id} ${job.level.id} ${job.track.id}, ${title}`,
|
|
59
|
+
process.stdout.write(
|
|
60
|
+
`${job.discipline.id} ${job.level.id} ${job.track.id}, ${title}\n`,
|
|
61
61
|
);
|
|
62
62
|
} else {
|
|
63
|
-
|
|
63
|
+
process.stdout.write(`${job.discipline.id} ${job.level.id}, ${title}\n`);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -401,18 +401,18 @@ export async function runJobCommand({
|
|
|
401
401
|
|
|
402
402
|
if (options.skills) {
|
|
403
403
|
for (const skill of view.skillMatrix) {
|
|
404
|
-
|
|
404
|
+
process.stdout.write(skill.skillId + "\n");
|
|
405
405
|
}
|
|
406
406
|
return;
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
if (options.tools) {
|
|
410
|
-
|
|
410
|
+
process.stdout.write(toolkitToPlainList(view.toolkit) + "\n");
|
|
411
411
|
return;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
if (options.json) {
|
|
415
|
-
|
|
415
|
+
process.stdout.write(JSON.stringify(view, null, 2) + "\n");
|
|
416
416
|
return;
|
|
417
417
|
}
|
|
418
418
|
|
package/src/commands/level.js
CHANGED
|
@@ -79,7 +79,7 @@ function formatSummary(levels, data) {
|
|
|
79
79
|
* @param {Object} framework - Framework config
|
|
80
80
|
*/
|
|
81
81
|
function formatDetail(level, framework) {
|
|
82
|
-
|
|
82
|
+
process.stdout.write(levelToMarkdown(level, framework) + "\n");
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export const runLevelCommand = createEntityCommand({
|
|
@@ -162,7 +162,7 @@ export async function runQuestionsCommand({
|
|
|
162
162
|
filter,
|
|
163
163
|
});
|
|
164
164
|
for (const q of view.questions) {
|
|
165
|
-
|
|
165
|
+
process.stdout.write(q.id + "\n");
|
|
166
166
|
}
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
@@ -196,5 +196,5 @@ export async function runQuestionsCommand({
|
|
|
196
196
|
break;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
process.stdout.write(output + "\n");
|
|
200
200
|
}
|
package/src/commands/skill.js
CHANGED
|
@@ -65,14 +65,14 @@ function formatSummary(skills, data) {
|
|
|
65
65
|
*/
|
|
66
66
|
function formatDetail(viewAndContext, framework) {
|
|
67
67
|
const { skill, disciplines, tracks, drivers, capabilities } = viewAndContext;
|
|
68
|
-
|
|
68
|
+
process.stdout.write(
|
|
69
69
|
skillToMarkdown(skill, {
|
|
70
70
|
disciplines,
|
|
71
71
|
tracks,
|
|
72
72
|
drivers,
|
|
73
73
|
capabilities,
|
|
74
74
|
framework,
|
|
75
|
-
}),
|
|
75
|
+
}) + "\n",
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
package/src/commands/tool.js
CHANGED
|
@@ -33,7 +33,7 @@ export async function runToolCommand({ data, args, options }) {
|
|
|
33
33
|
// --list: Output descriptive comma-separated tool lines for piping
|
|
34
34
|
if (options.list) {
|
|
35
35
|
for (const tool of tools) {
|
|
36
|
-
|
|
36
|
+
process.stdout.write(`${tool.name}, ${truncate(tool.description, 60)}\n`);
|
|
37
37
|
}
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
@@ -41,7 +41,7 @@ export async function runToolCommand({ data, args, options }) {
|
|
|
41
41
|
// No args: Show summary
|
|
42
42
|
if (!name) {
|
|
43
43
|
if (options.json) {
|
|
44
|
-
|
|
44
|
+
process.stdout.write(JSON.stringify(tools, null, 2) + "\n");
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
formatSummary(tools, totalCount);
|
|
@@ -58,7 +58,7 @@ export async function runToolCommand({ data, args, options }) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (options.json) {
|
|
61
|
-
|
|
61
|
+
process.stdout.write(JSON.stringify(tool, null, 2) + "\n");
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
|
package/src/commands/track.js
CHANGED
|
@@ -73,8 +73,9 @@ function formatSummary(tracks, data) {
|
|
|
73
73
|
*/
|
|
74
74
|
function formatDetail(viewAndContext, framework) {
|
|
75
75
|
const { track, skills, behaviours, disciplines } = viewAndContext;
|
|
76
|
-
|
|
77
|
-
trackToMarkdown(track, { skills, behaviours, disciplines, framework })
|
|
76
|
+
process.stdout.write(
|
|
77
|
+
trackToMarkdown(track, { skills, behaviours, disciplines, framework }) +
|
|
78
|
+
"\n",
|
|
78
79
|
);
|
|
79
80
|
}
|
|
80
81
|
|
package/src/commands/update.js
CHANGED
|
@@ -11,8 +11,11 @@ import { join } from "path";
|
|
|
11
11
|
import { homedir } from "os";
|
|
12
12
|
import { execFileSync } from "child_process";
|
|
13
13
|
import { tmpdir } from "os";
|
|
14
|
+
import { createLogger } from "@forwardimpact/libtelemetry";
|
|
14
15
|
import { createDataLoader } from "@forwardimpact/map/loader";
|
|
15
16
|
|
|
17
|
+
const logger = createLogger("pathway");
|
|
18
|
+
|
|
16
19
|
const BASE_DIR = join(homedir(), ".fit", "data");
|
|
17
20
|
const INSTALL_DIR = join(BASE_DIR, "pathway");
|
|
18
21
|
|
|
@@ -53,7 +56,7 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
|
|
|
53
56
|
const baseUrl = siteUrl.replace(/\/$/, "");
|
|
54
57
|
const bundleName = "bundle.tar.gz";
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
logger.info(`\nš Updating from ${baseUrl}...\n`);
|
|
57
60
|
|
|
58
61
|
// 1. Download bundle to temp location
|
|
59
62
|
const tmpDir = join(tmpdir(), "fit-pathway-update");
|
|
@@ -62,17 +65,17 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
|
|
|
62
65
|
const tmpBundle = join(tmpDir, bundleName);
|
|
63
66
|
|
|
64
67
|
try {
|
|
65
|
-
|
|
68
|
+
logger.info(" Downloading bundle...");
|
|
66
69
|
execFileSync("curl", [
|
|
67
70
|
"-fsSL",
|
|
68
71
|
`${baseUrl}/${bundleName}`,
|
|
69
72
|
"-o",
|
|
70
73
|
tmpBundle,
|
|
71
74
|
]);
|
|
72
|
-
|
|
75
|
+
logger.info(" ā Downloaded");
|
|
73
76
|
|
|
74
77
|
// 2. Extract bundle
|
|
75
|
-
|
|
78
|
+
logger.info(" Extracting...");
|
|
76
79
|
const extractDir = join(tmpDir, "extracted");
|
|
77
80
|
await mkdir(extractDir, { recursive: true });
|
|
78
81
|
execFileSync("tar", [
|
|
@@ -82,7 +85,7 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
|
|
|
82
85
|
extractDir,
|
|
83
86
|
"--strip-components=1",
|
|
84
87
|
]);
|
|
85
|
-
|
|
88
|
+
logger.info(" ā Extracted");
|
|
86
89
|
|
|
87
90
|
// 3. Compare versions from bundle's package.json (version manifest)
|
|
88
91
|
const newPkgPath = join(extractDir, "package.json");
|
|
@@ -101,17 +104,17 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
|
|
|
101
104
|
newPkg.dependencies?.["@forwardimpact/pathway"] || "unknown";
|
|
102
105
|
|
|
103
106
|
// 4. Replace data
|
|
104
|
-
|
|
107
|
+
logger.info(" Updating data files...");
|
|
105
108
|
await rm(INSTALL_DIR, { recursive: true });
|
|
106
109
|
await cp(join(extractDir, "data"), INSTALL_DIR, { recursive: true });
|
|
107
|
-
|
|
110
|
+
logger.info(" ā Data updated");
|
|
108
111
|
|
|
109
112
|
// 5. Update version manifest
|
|
110
113
|
await writeFile(oldPkgPath, JSON.stringify(newPkg, null, 2) + "\n");
|
|
111
114
|
|
|
112
115
|
// 6. Update global pathway package if version changed
|
|
113
116
|
if (oldVersion !== newVersion) {
|
|
114
|
-
|
|
117
|
+
logger.info(` Updating pathway ${oldVersion} ā ${newVersion}...`);
|
|
115
118
|
execFileSync(
|
|
116
119
|
"npm",
|
|
117
120
|
["install", "-g", `@forwardimpact/pathway@${newVersion}`],
|
|
@@ -119,11 +122,11 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
|
|
|
119
122
|
stdio: "ignore",
|
|
120
123
|
},
|
|
121
124
|
);
|
|
122
|
-
|
|
125
|
+
logger.info(" ā Global package updated");
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
// 7. Report
|
|
126
|
-
|
|
129
|
+
logger.info(`
|
|
127
130
|
ā
Update complete!
|
|
128
131
|
|
|
129
132
|
Pathway: ${oldVersion === newVersion ? newVersion + " (unchanged)" : oldVersion + " ā " + newVersion}
|