@forwardimpact/pathway 0.23.1 → 0.24.0

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/README.md CHANGED
@@ -7,15 +7,15 @@ teams.
7
7
 
8
8
  Pathway is the primary interface for interacting with engineering competency
9
9
  data. It provides tools for browsing career paths, generating job descriptions,
10
- creating agent team profiles, and preparing interviews—all from a unified web
11
- experience and command line.
10
+ generating agent teams and skills, and preparing interviews—all from a unified
11
+ web experience and command line.
12
12
 
13
13
  ## What It Does
14
14
 
15
15
  - **Web application** — Interactive browser for jobs, skills, and career paths
16
16
  - **CLI tools** — Command-line access to all functionality
17
- - **Agent generation** — Create VS Code Custom Agent profiles (`.agent.md`)
18
- - **Skill generation** — Generate Agent Skills files (`SKILL.md`)
17
+ - **Agent teams** — Generate VS Code Custom Agent teams (`.agent.md`) and skills
18
+ (`SKILL.md`)
19
19
  - **Interview prep** — Build interview question sets by role
20
20
  - **Static site** — Export everything as a static site
21
21
 
@@ -29,24 +29,24 @@ npx fit-pathway serve
29
29
  npx fit-pathway skill --list
30
30
  npx fit-pathway job software_engineering senior --track=platform
31
31
 
32
- # Generate agent profiles
32
+ # Generate agent teams and skills
33
33
  npx fit-pathway agent software_engineering --track=platform --output=./.github/agents
34
34
  ```
35
35
 
36
36
  ## CLI Commands
37
37
 
38
- | Command | Description |
39
- | ----------- | ---------------------------- |
40
- | `serve` | Start web server |
41
- | `site` | Generate static site |
42
- | `init` | Create data directory |
43
- | `skill` | Browse skills |
44
- | `behaviour` | Browse behaviours |
45
- | `job` | Generate job definitions |
46
- | `agent` | Generate agent profiles |
47
- | `interview` | Generate interview questions |
48
- | `progress` | Analyze career progression |
49
- | `questions` | Browse interview questions |
38
+ | Command | Description |
39
+ | ----------- | ------------------------------- |
40
+ | `serve` | Start web server |
41
+ | `site` | Generate static site |
42
+ | `init` | Create data directory |
43
+ | `skill` | Browse skills |
44
+ | `behaviour` | Browse behaviours |
45
+ | `job` | Generate job definitions |
46
+ | `agent` | Generate agent teams and skills |
47
+ | `interview` | Generate interview questions |
48
+ | `progress` | Analyze career progression |
49
+ | `questions` | Browse interview questions |
50
50
 
51
51
  Use `--help` with any command for full options.
52
52
 
@@ -56,7 +56,7 @@ Use `--help` with any command for full options.
56
56
  - **Skill Browser** — View all skills with proficiency descriptions
57
57
  - **Career Progression** — Compare levels and identify growth areas
58
58
  - **Interview Prep** — Generate role-specific question sets
59
- - **Agent Preview** — Preview generated agent profiles
59
+ - **Agent Preview** — Preview generated agent teams and skills
60
60
 
61
61
  ## Package Exports
62
62
 
@@ -65,4 +65,4 @@ import { formatSkillForMarkdown } from "@forwardimpact/pathway/formatters";
65
65
  import { runCommand } from "@forwardimpact/pathway/commands";
66
66
  ```
67
67
 
68
- See the [documentation](../../docs/pathway/index.md) for usage details.
68
+ See the [documentation](../../website/docs/pathway/index.md) for usage details.
@@ -29,11 +29,14 @@
29
29
  * --help Show help
30
30
  */
31
31
 
32
- import { join, resolve } from "path";
32
+ import { join, resolve, dirname } from "path";
33
33
  import { existsSync } from "fs";
34
34
  import { homedir } from "os";
35
- import { loadAllData } from "@forwardimpact/map/loader";
35
+ import { fileURLToPath } from "url";
36
+ import { createDataLoader } from "@forwardimpact/map/loader";
37
+ import { validateAllData } from "@forwardimpact/map/validation";
36
38
  import { formatError } from "../src/lib/cli-output.js";
39
+ import { createTemplateLoader } from "@forwardimpact/libtemplate";
37
40
 
38
41
  // Import command handlers
39
42
  import { runDisciplineCommand } from "../src/commands/discipline.js";
@@ -54,6 +57,9 @@ import { runInitCommand } from "../src/commands/init.js";
54
57
  import { runBuildCommand } from "../src/commands/build.js";
55
58
  import { runUpdateCommand } from "../src/commands/update.js";
56
59
 
60
+ const __dirname = dirname(fileURLToPath(import.meta.url));
61
+ const TEMPLATE_DIR = join(__dirname, "..", "templates");
62
+
57
63
  const COMMANDS = {
58
64
  discipline: runDisciplineCommand,
59
65
  level: runLevelCommand,
@@ -235,9 +241,7 @@ function parseArgs(args) {
235
241
  type: "full",
236
242
  compare: null,
237
243
  data: null,
238
- // Shared command options
239
244
  track: null,
240
- // Questions command options
241
245
  level: null,
242
246
  maturity: null,
243
247
  skill: null,
@@ -245,20 +249,15 @@ function parseArgs(args) {
245
249
  capability: null,
246
250
  format: null,
247
251
  stats: false,
248
- // Job command options
249
252
  checklist: null,
250
253
  skills: false,
251
254
  tools: false,
252
- // Agent command options
253
255
  output: null,
254
256
  stage: null,
255
257
  "all-stages": false,
256
258
  agent: false,
257
- // Serve command options
258
259
  port: null,
259
- // Init command options
260
260
  path: null,
261
- // Site command options
262
261
  clean: true,
263
262
  url: null,
264
263
  };
@@ -339,54 +338,45 @@ function printHelp() {
339
338
 
340
339
  /**
341
340
  * Resolve the data directory path.
342
- * Resolution order:
343
- * 1. --data=<path> flag (explicit override)
344
- * 2. PATHWAY_DATA environment variable
345
- * 3. ~/.fit/pathway/data/ (home directory install)
346
- * 4. ./data/ relative to current working directory
347
- * 5. ./examples/ relative to current working directory
348
- * 6. products/map/examples/ for monorepo development
349
- *
350
341
  * @param {Object} options - Parsed command options
351
342
  * @returns {string} Resolved absolute path to data directory
352
343
  */
353
344
  function resolveDataPath(options) {
354
- // 1. Explicit flag
355
345
  if (options.data) {
356
346
  return resolve(options.data);
357
347
  }
358
348
 
359
- // 2. Environment variable
360
349
  if (process.env.PATHWAY_DATA) {
361
350
  return resolve(process.env.PATHWAY_DATA);
362
351
  }
363
352
 
364
- // 3. Home directory install (~/.fit/pathway/data/)
365
353
  const homeData = join(homedir(), ".fit", "pathway", "data");
366
354
  if (existsSync(homeData)) {
367
355
  return homeData;
368
356
  }
369
357
 
370
- // 4. Current working directory ./data/
358
+ const cwdDataPathway = join(process.cwd(), "data/pathway");
359
+ if (existsSync(cwdDataPathway)) {
360
+ return cwdDataPathway;
361
+ }
362
+
363
+ const cwdExamplesPathway = join(process.cwd(), "examples/pathway");
364
+ if (existsSync(cwdExamplesPathway)) {
365
+ return cwdExamplesPathway;
366
+ }
367
+
371
368
  const cwdData = join(process.cwd(), "data");
372
369
  if (existsSync(cwdData)) {
373
370
  return cwdData;
374
371
  }
375
372
 
376
- // 5. Current working directory ./examples/
377
373
  const cwdExamples = join(process.cwd(), "examples");
378
374
  if (existsSync(cwdExamples)) {
379
375
  return cwdExamples;
380
376
  }
381
377
 
382
- // 6. Monorepo: products/map/examples/
383
- const mapExamples = join(process.cwd(), "products/map/examples");
384
- if (existsSync(mapExamples)) {
385
- return mapExamples;
386
- }
387
-
388
378
  throw new Error(
389
- "No data directory found. Create ./data/ or use --data=<path>",
379
+ "No data directory found. Create ./data/pathway/ or use --data=<path>",
390
380
  );
391
381
  }
392
382
 
@@ -402,7 +392,6 @@ async function main() {
402
392
  process.exit(0);
403
393
  }
404
394
 
405
- // No command: show help
406
395
  if (!options.command) {
407
396
  printHelp();
408
397
  process.exit(0);
@@ -410,7 +399,6 @@ async function main() {
410
399
 
411
400
  const command = options.command;
412
401
 
413
- // Handle init command (doesn't need data directory to exist)
414
402
  if (command === "init") {
415
403
  await runInitCommand({ options });
416
404
  process.exit(0);
@@ -418,20 +406,16 @@ async function main() {
418
406
 
419
407
  const dataDir = resolveDataPath(options);
420
408
 
421
- // Handle dev command (needs data directory)
422
409
  if (command === "dev") {
423
410
  await runDevCommand({ dataDir, options });
424
- // dev doesn't exit, keeps running
425
411
  return;
426
412
  }
427
413
 
428
- // Handle build command (generates static site)
429
414
  if (command === "build") {
430
415
  await runBuildCommand({ dataDir, options });
431
416
  process.exit(0);
432
417
  }
433
418
 
434
- // Handle update command (re-downloads bundle for local install)
435
419
  if (command === "update") {
436
420
  await runUpdateCommand({ dataDir, options });
437
421
  process.exit(0);
@@ -446,12 +430,20 @@ async function main() {
446
430
  }
447
431
 
448
432
  try {
449
- const data = await loadAllData(dataDir, {
450
- validate: true,
451
- throwOnError: true,
433
+ const loader = createDataLoader();
434
+ const templateLoader = createTemplateLoader(TEMPLATE_DIR);
435
+
436
+ const data = await loader.loadAllData(dataDir);
437
+ validateAllData(data);
438
+
439
+ await handler({
440
+ data,
441
+ args: options.args,
442
+ options,
443
+ dataDir,
444
+ templateLoader,
445
+ loader,
452
446
  });
453
-
454
- await handler({ data, args: options.args, options, dataDir });
455
447
  } catch (error) {
456
448
  console.error(formatError(error.message));
457
449
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
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,8 +40,9 @@
40
40
  "./commands": "./src/commands/index.js"
41
41
  },
42
42
  "dependencies": {
43
- "@forwardimpact/map": "^0.11.0",
43
+ "@forwardimpact/map": "^0.14.0",
44
44
  "@forwardimpact/libskill": "^3.0.0",
45
+ "@forwardimpact/libtemplate": "^0.2.0",
45
46
  "@forwardimpact/libui": "^1.0.0",
46
47
  "mustache": "^4.2.0",
47
48
  "simple-icons": "^16.7.0",
@@ -27,10 +27,7 @@ import { writeFile, mkdir, readFile } from "fs/promises";
27
27
  import { join, dirname } from "path";
28
28
  import { existsSync } from "fs";
29
29
  import { stringify as stringifyYaml } from "yaml";
30
- import {
31
- loadAgentData,
32
- loadSkillsWithAgentData,
33
- } from "@forwardimpact/map/loader";
30
+ import { createDataLoader } from "@forwardimpact/map/loader";
34
31
  import {
35
32
  generateStageAgentProfile,
36
33
  validateAgentProfile,
@@ -50,12 +47,6 @@ import {
50
47
  formatReference,
51
48
  } from "../formatters/agent/skill.js";
52
49
  import { formatError, formatSuccess } from "../lib/cli-output.js";
53
- import {
54
- loadAgentTemplate,
55
- loadSkillTemplate,
56
- loadSkillInstallTemplate,
57
- loadSkillReferenceTemplate,
58
- } from "../lib/template-loader.js";
59
50
  import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
60
51
 
61
52
  /**
@@ -325,10 +316,18 @@ async function writeSkills(skills, baseDir, templates) {
325
316
  * @param {Object} params.options - Command options
326
317
  * @param {string} params.dataDir - Path to data directory
327
318
  */
328
- export async function runAgentCommand({ data, args, options, dataDir }) {
319
+ export async function runAgentCommand({
320
+ data,
321
+ args,
322
+ options,
323
+ dataDir,
324
+ templateLoader,
325
+ loader,
326
+ }) {
329
327
  // Load agent-specific data
330
- const agentData = await loadAgentData(dataDir);
331
- const skillsWithAgent = await loadSkillsWithAgentData(dataDir);
328
+ const dataLoader = loader || createDataLoader();
329
+ const agentData = await dataLoader.loadAgentData(dataDir);
330
+ const skillsWithAgent = await dataLoader.loadSkillsWithAgentData(dataDir);
332
331
 
333
332
  // --list: Output clean lines for piping
334
333
  if (options.list) {
@@ -500,7 +499,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
500
499
  }
501
500
 
502
501
  // Load template
503
- const agentTemplate = await loadAgentTemplate(dataDir);
502
+ const agentTemplate = templateLoader.load("agent.template.md", dataDir);
504
503
 
505
504
  // Output to console (default) or write to files (with --output)
506
505
  if (!options.output) {
@@ -575,10 +574,16 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
575
574
  }
576
575
 
577
576
  // Load templates
578
- const agentTemplate = await loadAgentTemplate(dataDir);
579
- const skillTemplate = await loadSkillTemplate(dataDir);
580
- const installTemplate = await loadSkillInstallTemplate(dataDir);
581
- const referenceTemplate = await loadSkillReferenceTemplate(dataDir);
577
+ const agentTemplate = templateLoader.load("agent.template.md", dataDir);
578
+ const skillTemplate = templateLoader.load("skill.template.md", dataDir);
579
+ const installTemplate = templateLoader.load(
580
+ "skill-install.template.sh",
581
+ dataDir,
582
+ );
583
+ const referenceTemplate = templateLoader.load(
584
+ "skill-reference.template.md",
585
+ dataDir,
586
+ );
582
587
  const skillTemplates = {
583
588
  skill: skillTemplate,
584
589
  install: installTemplate,
@@ -21,8 +21,8 @@ import { join, dirname, relative, resolve } from "path";
21
21
  import { fileURLToPath } from "url";
22
22
  import { execFileSync } from "child_process";
23
23
  import Mustache from "mustache";
24
- import { generateAllIndexes } from "@forwardimpact/map/index-generator";
25
- import { loadFrameworkConfig } from "@forwardimpact/map/loader";
24
+ import { createIndexGenerator } from "@forwardimpact/map/index-generator";
25
+ import { createDataLoader } from "@forwardimpact/map/loader";
26
26
 
27
27
  const __filename = fileURLToPath(import.meta.url);
28
28
  const __dirname = dirname(__filename);
@@ -85,7 +85,8 @@ export async function runBuildCommand({ dataDir, options }) {
85
85
  // Load framework config for display
86
86
  let framework;
87
87
  try {
88
- framework = await loadFrameworkConfig(dataDir);
88
+ const loader = createDataLoader();
89
+ framework = await loader.loadFrameworkConfig(dataDir);
89
90
  } catch {
90
91
  framework = { emojiIcon: "🚀", title: "Engineering Pathway" };
91
92
  }
@@ -110,7 +111,8 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
110
111
 
111
112
  // Generate index files in data directory
112
113
  console.log("📇 Generating index files...");
113
- await generateAllIndexes(dataDir);
114
+ const indexGenerator = createIndexGenerator();
115
+ await indexGenerator.generateAllIndexes(dataDir);
114
116
 
115
117
  // Copy app assets
116
118
  console.log("📦 Copying application files...");
@@ -9,8 +9,8 @@ import { createServer } from "http";
9
9
  import { readFile, stat } from "fs/promises";
10
10
  import { join, extname, dirname } from "path";
11
11
  import { fileURLToPath } from "url";
12
- import { generateAllIndexes } from "@forwardimpact/map/index-generator";
13
- import { loadFrameworkConfig } from "@forwardimpact/map/loader";
12
+ import { createIndexGenerator } from "@forwardimpact/map/index-generator";
13
+ import { createDataLoader } from "@forwardimpact/map/loader";
14
14
 
15
15
  const __filename = fileURLToPath(import.meta.url);
16
16
  const __dirname = dirname(__filename);
@@ -108,7 +108,8 @@ export async function runDevCommand({ dataDir, options }) {
108
108
  // Load framework config for display
109
109
  let framework;
110
110
  try {
111
- framework = await loadFrameworkConfig(dataDir);
111
+ const loader = createDataLoader();
112
+ framework = await loader.loadFrameworkConfig(dataDir);
112
113
  } catch {
113
114
  // Fallback if framework config fails
114
115
  framework = { emojiIcon: "🚀", title: "Engineering Pathway" };
@@ -116,7 +117,8 @@ export async function runDevCommand({ dataDir, options }) {
116
117
 
117
118
  // Generate _index.yaml files before serving
118
119
  console.log("Generating index files...");
119
- await generateAllIndexes(dataDir);
120
+ const indexGenerator = createIndexGenerator();
121
+ await indexGenerator.generateAllIndexes(dataDir);
120
122
 
121
123
  const server = createServer(async (req, res) => {
122
124
  const url = new URL(req.url, `http://localhost:${port}`);
@@ -10,7 +10,17 @@ import { fileURLToPath } from "url";
10
10
 
11
11
  const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = dirname(__filename);
13
- const examplesDir = join(__dirname, "..", "..", "examples");
13
+ // Prefer monorepo root examples/framework/, fall back to legacy co-located examples/
14
+ const monorepoExamplesDir = join(
15
+ __dirname,
16
+ "..",
17
+ "..",
18
+ "..",
19
+ "..",
20
+ "examples",
21
+ "framework",
22
+ );
23
+ const legacyExamplesDir = join(__dirname, "..", "..", "examples");
14
24
 
15
25
  /**
16
26
  * Run the init command
@@ -31,13 +41,20 @@ export async function runInitCommand({ options }) {
31
41
  // Directory doesn't exist, proceed
32
42
  }
33
43
 
34
- // Check if examples directory exists
44
+ // Find examples directory — monorepo root first, then legacy
45
+ let examplesDir;
35
46
  try {
36
- await access(examplesDir);
47
+ await access(monorepoExamplesDir);
48
+ examplesDir = monorepoExamplesDir;
37
49
  } catch {
38
- console.error("Error: Examples directory not found in package.");
39
- console.error("This may indicate a corrupted package installation.");
40
- process.exit(1);
50
+ try {
51
+ await access(legacyExamplesDir);
52
+ examplesDir = legacyExamplesDir;
53
+ } catch {
54
+ console.error("Error: Examples directory not found in package.");
55
+ console.error("This may indicate a corrupted package installation.");
56
+ process.exit(1);
57
+ }
41
58
  }
42
59
 
43
60
  // Copy example data
@@ -25,7 +25,6 @@ import {
25
25
  deriveChecklist,
26
26
  formatChecklistMarkdown,
27
27
  } from "@forwardimpact/libskill/checklist";
28
- import { loadJobTemplate } from "../lib/template-loader.js";
29
28
  import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
30
29
 
31
30
  /**
@@ -47,7 +46,13 @@ function formatJob(view, _options, entities, jobTemplate) {
47
46
  * @param {Object} params.options - Command options
48
47
  * @param {string} params.dataDir - Path to data directory
49
48
  */
50
- export async function runJobCommand({ data, args, options, dataDir }) {
49
+ export async function runJobCommand({
50
+ data,
51
+ args,
52
+ options,
53
+ dataDir,
54
+ templateLoader,
55
+ }) {
51
56
  const jobs = generateAllJobs({
52
57
  disciplines: data.disciplines,
53
58
  levels: data.levels,
@@ -310,6 +315,6 @@ export async function runJobCommand({ data, args, options, dataDir }) {
310
315
  }
311
316
 
312
317
  // Load job template for description formatting
313
- const jobTemplate = await loadJobTemplate(dataDir);
318
+ const jobTemplate = templateLoader.load("job.template.md", dataDir);
314
319
  formatJob(view, options, { discipline, level, track }, jobTemplate);
315
320
  }
@@ -18,7 +18,6 @@ import { getConceptEmoji } from "@forwardimpact/map/levels";
18
18
  import { formatTable, formatError } from "../lib/cli-output.js";
19
19
  import { generateSkillMarkdown } from "@forwardimpact/libskill/agent";
20
20
  import { formatAgentSkill } from "../formatters/agent/skill.js";
21
- import { loadSkillTemplate } from "../lib/template-loader.js";
22
21
 
23
22
  /**
24
23
  * Format skill summary output
@@ -69,7 +68,7 @@ function formatDetail(viewAndContext, framework) {
69
68
  * @param {Array} stages - All stage entities
70
69
  * @param {string} dataDir - Path to data directory for template loading
71
70
  */
72
- async function formatAgentDetail(skill, stages, dataDir) {
71
+ async function formatAgentDetail(skill, stages, templateLoader, dataDir) {
73
72
  if (!skill.agent) {
74
73
  console.error(formatError(`Skill '${skill.id}' has no agent section`));
75
74
  console.error(`\nSkills with agent support:`);
@@ -79,7 +78,7 @@ async function formatAgentDetail(skill, stages, dataDir) {
79
78
  process.exit(1);
80
79
  }
81
80
 
82
- const template = await loadSkillTemplate(dataDir);
81
+ const template = templateLoader.load("skill.template.md", dataDir);
83
82
  const skillMd = generateSkillMarkdown(skill, stages);
84
83
  const output = formatAgentSkill(skillMd, template);
85
84
  console.log(output);
@@ -119,7 +118,13 @@ const baseSkillCommand = createEntityCommand({
119
118
  * @param {Object} params.options - Command options
120
119
  * @param {string} params.dataDir - Path to data directory
121
120
  */
122
- export async function runSkillCommand({ data, args, options, dataDir }) {
121
+ export async function runSkillCommand({
122
+ data,
123
+ args,
124
+ options,
125
+ dataDir,
126
+ templateLoader,
127
+ }) {
123
128
  // Handle --agent flag for detail view
124
129
  if (options.agent && args.length > 0) {
125
130
  const [id] = args;
@@ -131,7 +136,7 @@ export async function runSkillCommand({ data, args, options, dataDir }) {
131
136
  process.exit(1);
132
137
  }
133
138
 
134
- await formatAgentDetail(skill, data.stages, dataDir);
139
+ await formatAgentDetail(skill, data.stages, templateLoader, dataDir);
135
140
  return;
136
141
  }
137
142
 
@@ -10,7 +10,7 @@ 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, execSync } from "child_process";
13
- import { loadFrameworkConfig } from "@forwardimpact/map/loader";
13
+ import { createDataLoader } from "@forwardimpact/map/loader";
14
14
 
15
15
  const INSTALL_DIR = join(homedir(), ".fit", "pathway");
16
16
 
@@ -38,7 +38,8 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
38
38
  }
39
39
 
40
40
  // Load framework config to get siteUrl
41
- const framework = await loadFrameworkConfig(installDataDir);
41
+ const loader = createDataLoader();
42
+ const framework = await loader.loadFrameworkConfig(installDataDir);
42
43
  const siteUrl = options.url || framework.distribution?.siteUrl;
43
44
 
44
45
  if (!siteUrl) {
@@ -23,14 +23,9 @@ import { trimValue, trimFields } from "../shared.js";
23
23
  * @returns {Object} Data object ready for Mustache template
24
24
  */
25
25
  function prepareJobDescriptionData({ job, discipline, level, track }) {
26
- // Build role summary from discipline - use manager version if applicable
27
- const isManagement = discipline.isManagement === true;
28
- let roleSummary =
29
- isManagement && discipline.managementRoleSummary
30
- ? discipline.managementRoleSummary
31
- : discipline.professionalRoleSummary || discipline.description;
32
- // Replace placeholders
26
+ // Build role summary from discipline
33
27
  const { roleTitle, specialization } = discipline;
28
+ let roleSummary = discipline.roleSummary || discipline.description;
34
29
  roleSummary = roleSummary.replace(/\{roleTitle\}/g, roleTitle);
35
30
  roleSummary = roleSummary.replace(/\{specialization\}/g, specialization);
36
31
 
@@ -298,9 +298,15 @@ export async function loadAgentDataBrowser(dataDir = "./data") {
298
298
  loadDisciplinesFromDir(`${dataDir}/disciplines`),
299
299
  loadTracksFromDir(`${dataDir}/tracks`),
300
300
  loadBehavioursFromDir(`${dataDir}/behaviours`),
301
- tryLoadYamlFile(`${dataDir}/vscode-settings.yaml`),
302
- tryLoadYamlFile(`${dataDir}/devcontainer.yaml`),
303
- tryLoadYamlFile(`${dataDir}/copilot-setup-steps.yaml`),
301
+ tryLoadYamlFile(`${dataDir}/repository/vscode-settings.yaml`).then(
302
+ (r) => r ?? tryLoadYamlFile(`${dataDir}/vscode-settings.yaml`),
303
+ ),
304
+ tryLoadYamlFile(`${dataDir}/repository/devcontainer.yaml`).then(
305
+ (r) => r ?? tryLoadYamlFile(`${dataDir}/devcontainer.yaml`),
306
+ ),
307
+ tryLoadYamlFile(`${dataDir}/repository/copilot-setup-steps.yaml`).then(
308
+ (r) => r ?? tryLoadYamlFile(`${dataDir}/copilot-setup-steps.yaml`),
309
+ ),
304
310
  ]);
305
311
 
306
312
  return {
package/src/main.js CHANGED
@@ -50,6 +50,7 @@ async function init() {
50
50
  // Load data
51
51
  try {
52
52
  const data = await loadAllData("./data");
53
+ data.disciplines = data.disciplines.filter((d) => !d.hidden);
53
54
  setData(data);
54
55
 
55
56
  // Populate branding from framework data
@@ -1,93 +0,0 @@
1
- /**
2
- * Template Loader
3
- *
4
- * Loads Mustache templates from the data directory with fallback to the
5
- * top-level templates directory. This allows users to customize agent
6
- * and skill templates by placing them in their data directory.
7
- *
8
- * Resolution order:
9
- * 1. {dataDir}/templates/{name} (user customization)
10
- * 2. {codebaseDir}/templates/{name} (fallback)
11
- */
12
-
13
- import { readFile } from "fs/promises";
14
- import { join, dirname } from "path";
15
- import { fileURLToPath } from "url";
16
- import { existsSync } from "fs";
17
-
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
- const CODEBASE_TEMPLATES_DIR = join(__dirname, "..", "..", "templates");
20
-
21
- /**
22
- * Load a template file with fallback to codebase templates
23
- * @param {string} templateName - Template filename (e.g., 'agent.template.md')
24
- * @param {string} dataDir - Path to data directory
25
- * @returns {Promise<string>} Template content
26
- * @throws {Error} If template not found in either location
27
- */
28
- export async function loadTemplate(templateName, dataDir) {
29
- // Build list of paths to try
30
- const paths = [];
31
- if (dataDir) {
32
- paths.push(join(dataDir, "templates", templateName));
33
- }
34
- paths.push(join(CODEBASE_TEMPLATES_DIR, templateName));
35
-
36
- // Try each path in order
37
- for (const path of paths) {
38
- if (existsSync(path)) {
39
- return await readFile(path, "utf-8");
40
- }
41
- }
42
-
43
- // Not found
44
- throw new Error(
45
- `Template '${templateName}' not found. Checked:\n` +
46
- paths.map((p) => ` - ${p}`).join("\n"),
47
- );
48
- }
49
-
50
- /**
51
- * Load agent profile template
52
- * @param {string} dataDir - Path to data directory
53
- * @returns {Promise<string>} Agent template content
54
- */
55
- export async function loadAgentTemplate(dataDir) {
56
- return loadTemplate("agent.template.md", dataDir);
57
- }
58
-
59
- /**
60
- * Load agent skill template
61
- * @param {string} dataDir - Path to data directory
62
- * @returns {Promise<string>} Skill template content
63
- */
64
- export async function loadSkillTemplate(dataDir) {
65
- return loadTemplate("skill.template.md", dataDir);
66
- }
67
-
68
- /**
69
- * Load skill install script template
70
- * @param {string} dataDir - Path to data directory
71
- * @returns {Promise<string>} Install script template content
72
- */
73
- export async function loadSkillInstallTemplate(dataDir) {
74
- return loadTemplate("skill-install.template.sh", dataDir);
75
- }
76
-
77
- /**
78
- * Load skill reference template
79
- * @param {string} dataDir - Path to data directory
80
- * @returns {Promise<string>} Reference template content
81
- */
82
- export async function loadSkillReferenceTemplate(dataDir) {
83
- return loadTemplate("skill-reference.template.md", dataDir);
84
- }
85
-
86
- /**
87
- * Load job description template
88
- * @param {string} dataDir - Path to data directory
89
- * @returns {Promise<string>} Job template content
90
- */
91
- export async function loadJobTemplate(dataDir) {
92
- return loadTemplate("job.template.md", dataDir);
93
- }