@forwardimpact/pathway 0.25.8 → 0.25.10

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.
@@ -104,7 +104,7 @@ GETTING STARTED
104
104
  init Create ./data/ with example data
105
105
  dev [--port=PORT] Run live development server
106
106
  build [--output=PATH] [--url=URL] Generate static site + distribution bundle
107
- update [--url=URL] Update local ~/.fit/pathway/ installation
107
+ update [--url=URL] Update local ~/.fit/data/pathway/ installation
108
108
 
109
109
  ────────────────────────────────────────────────────────────────────────────────
110
110
  ENTITY COMMANDS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.25.8",
3
+ "version": "0.25.10",
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": {
@@ -38,6 +38,7 @@ import {
38
38
  deriveToolkit,
39
39
  getDisciplineAbbreviation,
40
40
  toKebabCase,
41
+ interpolateTeamInstructions,
41
42
  } from "@forwardimpact/libskill";
42
43
  import { formatAgentProfile } from "../formatters/agent/profile.js";
43
44
  import {
@@ -207,6 +208,21 @@ async function writeProfile(profile, baseDir, template) {
207
208
  return profilePath;
208
209
  }
209
210
 
211
+ /**
212
+ * Write team instructions to CLAUDE.md
213
+ * @param {string|null} teamInstructions - Interpolated team instructions content
214
+ * @param {string} baseDir - Base output directory
215
+ * @returns {string|null} Path written, or null if skipped
216
+ */
217
+ async function writeTeamInstructions(teamInstructions, baseDir) {
218
+ if (!teamInstructions) return null;
219
+ const filePath = join(baseDir, ".claude", "CLAUDE.md");
220
+ await ensureDir(filePath);
221
+ await writeFile(filePath, teamInstructions.trim() + "\n", "utf-8");
222
+ console.log(formatSuccess(`Created: ${filePath}`));
223
+ return filePath;
224
+ }
225
+
210
226
  /**
211
227
  * Write skill files (SKILL.md, scripts/install.sh, references/REFERENCE.md)
212
228
  * @param {Array} skills - Generated skills
@@ -434,10 +450,18 @@ export async function runAgentCommand({
434
450
 
435
451
  // Output to console (default) or write to files (with --output)
436
452
  if (!options.output) {
453
+ const teamInstructions = interpolateTeamInstructions(agentTrack, humanDiscipline);
454
+ if (teamInstructions) {
455
+ console.log("# Team Instructions (CLAUDE.md)\n");
456
+ console.log(teamInstructions.trim());
457
+ console.log("\n---\n");
458
+ }
437
459
  console.log(formatAgentProfile(profile, agentTemplate));
438
460
  return;
439
461
  }
440
462
 
463
+ const teamInstructions = interpolateTeamInstructions(agentTrack, humanDiscipline);
464
+ await writeTeamInstructions(teamInstructions, baseDir);
441
465
  await writeProfile(profile, baseDir, agentTemplate);
442
466
  await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
443
467
  console.log("");
@@ -516,6 +540,12 @@ export async function runAgentCommand({
516
540
 
517
541
  // Output to console (default) or write to files (with --output)
518
542
  if (!options.output) {
543
+ const teamInstructions = interpolateTeamInstructions(agentTrack, humanDiscipline);
544
+ if (teamInstructions) {
545
+ console.log("# Team Instructions (CLAUDE.md)\n");
546
+ console.log(teamInstructions.trim());
547
+ console.log("\n---\n");
548
+ }
519
549
  for (const profile of profiles) {
520
550
  console.log(formatAgentProfile(profile, agentTemplate));
521
551
  console.log("\n---\n");
@@ -523,6 +553,8 @@ export async function runAgentCommand({
523
553
  return;
524
554
  }
525
555
 
556
+ const teamInstructions = interpolateTeamInstructions(agentTrack, humanDiscipline);
557
+ await writeTeamInstructions(teamInstructions, baseDir);
526
558
  for (const profile of profiles) {
527
559
  await writeProfile(profile, baseDir, agentTemplate);
528
560
  }
@@ -2,7 +2,7 @@
2
2
  * Update Command
3
3
  *
4
4
  * Re-downloads the distribution bundle from the published site URL
5
- * and updates the local ~/.fit/pathway/ installation.
5
+ * and updates the local ~/.fit/data/pathway/ installation.
6
6
  * Updates the global @forwardimpact/pathway package if the version changed.
7
7
  */
8
8
 
@@ -10,9 +10,11 @@ import { cp, mkdir, rm, readFile, writeFile, access } from "fs/promises";
10
10
  import { join } from "path";
11
11
  import { homedir } from "os";
12
12
  import { execFileSync } from "child_process";
13
+ import { tmpdir } from "os";
13
14
  import { createDataLoader } from "@forwardimpact/map/loader";
14
15
 
15
- const INSTALL_DIR = join(homedir(), ".fit", "pathway");
16
+ const BASE_DIR = join(homedir(), ".fit", "data");
17
+ const INSTALL_DIR = join(BASE_DIR, "pathway");
16
18
 
17
19
  /**
18
20
  * Run the update command.
@@ -24,13 +26,11 @@ const INSTALL_DIR = join(homedir(), ".fit", "pathway");
24
26
  * @param {Object} params.options - Command options
25
27
  */
26
28
  export async function runUpdateCommand({ dataDir: _dataDir, options }) {
27
- const installDataDir = join(INSTALL_DIR, "data");
28
-
29
29
  // Verify we have a home-directory installation
30
30
  try {
31
- await access(installDataDir);
31
+ await access(INSTALL_DIR);
32
32
  } catch {
33
- console.error("Error: No local installation found at ~/.fit/pathway/");
33
+ console.error("Error: No local installation found at ~/.fit/data/pathway/");
34
34
  console.error(
35
35
  "Install first using the install.sh script from your organization's pathway site.",
36
36
  );
@@ -39,12 +39,12 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
39
39
 
40
40
  // Load framework config to get siteUrl
41
41
  const loader = createDataLoader();
42
- const framework = await loader.loadFrameworkConfig(installDataDir);
42
+ const framework = await loader.loadFrameworkConfig(INSTALL_DIR);
43
43
  const siteUrl = options.url || framework.distribution?.siteUrl;
44
44
 
45
45
  if (!siteUrl) {
46
46
  console.error(
47
- "Error: No siteUrl found in ~/.fit/pathway/data/framework.yaml (distribution.siteUrl)",
47
+ "Error: No siteUrl found in ~/.fit/data/pathway/framework.yaml (distribution.siteUrl)",
48
48
  );
49
49
  console.error("Provide one with --url=<URL> or add it to framework.yaml.");
50
50
  process.exit(1);
@@ -56,7 +56,7 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
56
56
  console.log(`\n🔄 Updating from ${baseUrl}...\n`);
57
57
 
58
58
  // 1. Download bundle to temp location
59
- const tmpDir = join(INSTALL_DIR, "_update_tmp");
59
+ const tmpDir = join(tmpdir(), "fit-pathway-update");
60
60
  await mkdir(tmpDir, { recursive: true });
61
61
 
62
62
  const tmpBundle = join(tmpDir, bundleName);
@@ -86,7 +86,7 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
86
86
 
87
87
  // 3. Compare versions from bundle's package.json (version manifest)
88
88
  const newPkgPath = join(extractDir, "package.json");
89
- const oldPkgPath = join(INSTALL_DIR, "package.json");
89
+ const oldPkgPath = join(BASE_DIR, "package.json");
90
90
  const newPkg = JSON.parse(await readFile(newPkgPath, "utf8"));
91
91
  let oldPkg;
92
92
  try {
@@ -102,8 +102,8 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
102
102
 
103
103
  // 4. Replace data
104
104
  console.log(" Updating data files...");
105
- await rm(installDataDir, { recursive: true });
106
- await cp(join(extractDir, "data"), installDataDir, { recursive: true });
105
+ await rm(INSTALL_DIR, { recursive: true });
106
+ await cp(join(extractDir, "data"), INSTALL_DIR, { recursive: true });
107
107
  console.log(" ✓ Data updated");
108
108
 
109
109
  // 5. Update version manifest
@@ -18,6 +18,7 @@ import { getStageEmoji } from "../stage/shared.js";
18
18
  * @param {Array} deployment.skills - Agent skills
19
19
  * @param {Array} [deployment.roleAgents] - Role variant agents (plan, review)
20
20
  * @param {Object} [deployment.claudeCodeSettings] - Claude Code settings to include in download
21
+ * @param {string|null} [deployment.teamInstructions] - Team instructions content for CLAUDE.md
21
22
  * @returns {HTMLElement}
22
23
  */
23
24
  export function agentDeploymentToDOM({
@@ -25,6 +26,7 @@ export function agentDeploymentToDOM({
25
26
  skills,
26
27
  roleAgents = [],
27
28
  claudeCodeSettings = {},
29
+ teamInstructions = null,
28
30
  }) {
29
31
  const profileContent = formatAgentProfile(profile);
30
32
  const agentName = profile.frontmatter.name;
@@ -39,6 +41,7 @@ export function agentDeploymentToDOM({
39
41
  roleAgents,
40
42
  claudeCodeSettings,
41
43
  agentName,
44
+ teamInstructions,
42
45
  ),
43
46
 
44
47
  // Profile section
@@ -92,6 +95,7 @@ export function agentDeploymentToDOM({
92
95
  * @param {Array} roleAgents - Role variant agents
93
96
  * @param {Object} claudeCodeSettings - Claude Code settings to include
94
97
  * @param {string} agentName - Agent name for zip filename
98
+ * @param {string|null} teamInstructions - Team instructions content for CLAUDE.md
95
99
  * @returns {HTMLElement}
96
100
  */
97
101
  function createDownloadButton(
@@ -100,6 +104,7 @@ function createDownloadButton(
100
104
  roleAgents,
101
105
  claudeCodeSettings,
102
106
  agentName,
107
+ teamInstructions,
103
108
  ) {
104
109
  const btn = button(
105
110
  { className: "btn btn-primary download-all-btn" },
@@ -117,6 +122,7 @@ function createDownloadButton(
117
122
  roleAgents,
118
123
  claudeCodeSettings,
119
124
  agentName,
125
+ teamInstructions,
120
126
  );
121
127
  } finally {
122
128
  btn.disabled = false;
@@ -169,6 +175,7 @@ function createRoleAgentCard(agent) {
169
175
  * @param {Array} roleAgents - Role variant agents
170
176
  * @param {Object} claudeCodeSettings - Claude Code settings to include
171
177
  * @param {string} agentName - Agent name for zip filename
178
+ * @param {string|null} teamInstructions - Team instructions content for CLAUDE.md
172
179
  */
173
180
  async function downloadAllAsZip(
174
181
  profile,
@@ -176,6 +183,7 @@ async function downloadAllAsZip(
176
183
  roleAgents,
177
184
  claudeCodeSettings,
178
185
  agentName,
186
+ teamInstructions,
179
187
  ) {
180
188
  // Dynamically import JSZip
181
189
  const JSZip = await importJSZip();
@@ -185,6 +193,11 @@ async function downloadAllAsZip(
185
193
  const profileContent = formatAgentProfile(profile);
186
194
  zip.file(`.claude/agents/${profile.filename}`, profileContent);
187
195
 
196
+ // Add team instructions to .claude/CLAUDE.md
197
+ if (teamInstructions) {
198
+ zip.file(".claude/CLAUDE.md", teamInstructions.trim() + "\n");
199
+ }
200
+
188
201
  // Add role agent profiles to .claude/agents/ folder
189
202
  for (const roleAgent of roleAgents) {
190
203
  const roleContent = formatAgentProfile(roleAgent);
@@ -3,7 +3,7 @@
3
3
  # Generated by @forwardimpact/pathway v{{{version}}}
4
4
  #
5
5
  # Installs fit-pathway globally via bun and downloads organization data
6
- # to ~/.fit/pathway/data/.
6
+ # to ~/.fit/data/pathway/.
7
7
  #
8
8
  # Usage:
9
9
  # curl -fsSL {{{siteUrl}}}/install.sh | bash
@@ -11,7 +11,7 @@
11
11
  set -euo pipefail
12
12
 
13
13
  SITE_URL="{{{siteUrl}}}"
14
- INSTALL_DIR="${HOME}/.fit/pathway"
14
+ INSTALL_DIR="${HOME}/.fit/data/pathway"
15
15
 
16
16
  command -v bun >/dev/null 2>&1 || { echo "Error: Bun 1.2+ is required. https://bun.sh"; exit 1; }
17
17
 
@@ -19,14 +19,16 @@ command -v bun >/dev/null 2>&1 || { echo "Error: Bun 1.2+ is required. https://b
19
19
  echo "Installing @forwardimpact/pathway globally..."
20
20
  bun install -g @forwardimpact/pathway@{{{version}}}
21
21
 
22
- # Download organization data to ~/.fit/pathway/data/
23
- echo "Downloading organization data to ${INSTALL_DIR}/data/..."
22
+ # Download organization data to ~/.fit/data/pathway/
23
+ echo "Downloading organization data to ${INSTALL_DIR}..."
24
24
  mkdir -p "${INSTALL_DIR}"
25
25
 
26
- TMPFILE=$(mktemp)
27
- trap 'rm -f "$TMPFILE"' EXIT
28
- curl -fsSL "${SITE_URL}/bundle.tar.gz" -o "$TMPFILE"
29
- tar -xzf "$TMPFILE" -C "${INSTALL_DIR}" --strip-components=1
26
+ TMPDIR_INSTALL=$(mktemp -d)
27
+ trap 'rm -rf "$TMPDIR_INSTALL"' EXIT
28
+ curl -fsSL "${SITE_URL}/bundle.tar.gz" -o "${TMPDIR_INSTALL}/bundle.tar.gz"
29
+ tar -xzf "${TMPDIR_INSTALL}/bundle.tar.gz" -C "${TMPDIR_INSTALL}" --strip-components=1
30
+ cp -R "${TMPDIR_INSTALL}/data/." "${INSTALL_DIR}/"
31
+ cp "${TMPDIR_INSTALL}/package.json" "${HOME}/.fit/data/package.json" 2>/dev/null || true
30
32
 
31
33
  echo ""
32
34
  echo "Done. Usage:"