@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
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent listing and summary helpers
|
|
3
|
+
*
|
|
4
|
+
* Splits the listing / summary output out of agent.js so the command file
|
|
5
|
+
* stays focused on orchestration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
getDisciplineAbbreviation,
|
|
10
|
+
toKebabCase,
|
|
11
|
+
} from "@forwardimpact/libskill/agent";
|
|
12
|
+
import {
|
|
13
|
+
formatHeader,
|
|
14
|
+
formatSubheader,
|
|
15
|
+
formatBullet,
|
|
16
|
+
SummaryRenderer,
|
|
17
|
+
} from "@forwardimpact/libcli";
|
|
18
|
+
|
|
19
|
+
const summary = new SummaryRenderer({ process });
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Find valid agent combination pairs
|
|
23
|
+
* @param {Object} data - Pathway data
|
|
24
|
+
* @param {Object} agentData - Agent-specific data
|
|
25
|
+
* @returns {Array<{discipline: Object, track: Object, humanDiscipline: Object, humanTrack: Object}>}
|
|
26
|
+
*/
|
|
27
|
+
export function findValidCombinations(data, agentData) {
|
|
28
|
+
const pairs = [];
|
|
29
|
+
for (const discipline of agentData.disciplines) {
|
|
30
|
+
for (const track of agentData.tracks) {
|
|
31
|
+
const humanDiscipline = data.disciplines.find(
|
|
32
|
+
(d) => d.id === discipline.id,
|
|
33
|
+
);
|
|
34
|
+
const humanTrack = data.tracks.find((t) => t.id === track.id);
|
|
35
|
+
if (humanDiscipline && humanTrack) {
|
|
36
|
+
pairs.push({ discipline, track, humanDiscipline, humanTrack });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return pairs;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Show agent summary with stats
|
|
45
|
+
* @param {Object} data - Pathway data
|
|
46
|
+
* @param {Object} agentData - Agent-specific data
|
|
47
|
+
* @param {Array} skillsWithAgent - Skills with agent sections
|
|
48
|
+
*/
|
|
49
|
+
export function showAgentSummary(data, agentData, skillsWithAgent) {
|
|
50
|
+
const validCombinations = findValidCombinations(data, agentData).length;
|
|
51
|
+
const skillsWithAgentCount = skillsWithAgent.filter((s) => s.agent).length;
|
|
52
|
+
|
|
53
|
+
process.stdout.write("\n" + formatHeader("\u{1F916} Agent") + "\n\n");
|
|
54
|
+
summary.render({
|
|
55
|
+
title: formatSubheader("Coverage"),
|
|
56
|
+
items: [
|
|
57
|
+
{
|
|
58
|
+
label: "Disciplines",
|
|
59
|
+
description: `${agentData.disciplines.length}/${data.disciplines.length} with agent definitions`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: "Tracks",
|
|
63
|
+
description: `${agentData.tracks.length}/${data.tracks.length} with agent definitions`,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: "Skills",
|
|
67
|
+
description: `${skillsWithAgentCount}/${skillsWithAgent.length} with agent sections`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: "Stages",
|
|
71
|
+
description: `${data.stages.length} available`,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
process.stdout.write(
|
|
76
|
+
"\n" + formatSubheader(`Valid combinations: ${validCombinations}`) + "\n\n",
|
|
77
|
+
);
|
|
78
|
+
process.stdout.write(
|
|
79
|
+
formatBullet("Run 'npx fit-pathway agent --list' for all combinations") +
|
|
80
|
+
"\n",
|
|
81
|
+
);
|
|
82
|
+
process.stdout.write(
|
|
83
|
+
formatBullet(
|
|
84
|
+
"Run 'npx fit-pathway agent <discipline> <track>' to generate files",
|
|
85
|
+
) + "\n\n",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* List available agent combinations — compact output for piping
|
|
91
|
+
* @param {Object} data - Pathway data
|
|
92
|
+
* @param {Object} agentData - Agent-specific data
|
|
93
|
+
*/
|
|
94
|
+
function listAgentCombinationsCompact(data, agentData) {
|
|
95
|
+
for (const {
|
|
96
|
+
discipline,
|
|
97
|
+
track,
|
|
98
|
+
humanDiscipline,
|
|
99
|
+
humanTrack,
|
|
100
|
+
} of findValidCombinations(data, agentData)) {
|
|
101
|
+
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
102
|
+
const agentName = `${abbrev}-${toKebabCase(track.id)}`;
|
|
103
|
+
const specName = humanDiscipline.specialization || humanDiscipline.id;
|
|
104
|
+
// Piped output — keep as plain console.log for stable format
|
|
105
|
+
console.log(
|
|
106
|
+
`${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* List available agent combinations — verbose output (markdown)
|
|
113
|
+
* @param {Object} data - Pathway data
|
|
114
|
+
* @param {Object} agentData - Agent-specific data
|
|
115
|
+
*/
|
|
116
|
+
function listAgentCombinationsVerbose(data, agentData) {
|
|
117
|
+
// Markdown headings stay literal so downstream tools can parse them.
|
|
118
|
+
process.stdout.write("# 🤖 Available Agent Combinations\n\n");
|
|
119
|
+
|
|
120
|
+
const agentDisciplineIds = new Set(agentData.disciplines.map((d) => d.id));
|
|
121
|
+
const agentTrackIds = new Set(agentData.tracks.map((t) => t.id));
|
|
122
|
+
|
|
123
|
+
process.stdout.write("## Disciplines with agent definitions:\n\n");
|
|
124
|
+
for (const discipline of data.disciplines) {
|
|
125
|
+
const status = agentDisciplineIds.has(discipline.id) ? "✅" : "⬜";
|
|
126
|
+
process.stdout.write(
|
|
127
|
+
` ${status} ${discipline.id} - ${discipline.specialization || discipline.name}\n`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
process.stdout.write("\n## Tracks with agent definitions:\n\n");
|
|
132
|
+
for (const track of data.tracks) {
|
|
133
|
+
const status = agentTrackIds.has(track.id) ? "✅" : "⬜";
|
|
134
|
+
process.stdout.write(` ${status} ${track.id} - ${track.name}\n`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
process.stdout.write("\n## Valid combinations:\n\n");
|
|
138
|
+
for (const { discipline, track } of findValidCombinations(data, agentData)) {
|
|
139
|
+
process.stdout.write(
|
|
140
|
+
` npx fit-pathway agent ${discipline.id} ${track.id}\n`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
process.stdout.write("\n## Available stages:\n\n");
|
|
145
|
+
for (const stage of data.stages) {
|
|
146
|
+
process.stdout.write(
|
|
147
|
+
` --stage=${stage.id}: ${stage.description.split(" - ")[0]}\n`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* List available agent combinations
|
|
154
|
+
* @param {Object} data - Pathway data
|
|
155
|
+
* @param {Object} agentData - Agent-specific data
|
|
156
|
+
* @param {boolean} verbose - Show verbose output
|
|
157
|
+
*/
|
|
158
|
+
export function listAgentCombinations(data, agentData, verbose = false) {
|
|
159
|
+
if (verbose) {
|
|
160
|
+
listAgentCombinationsVerbose(data, agentData);
|
|
161
|
+
} else {
|
|
162
|
+
listAgentCombinationsCompact(data, agentData);
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/commands/agent.js
CHANGED
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
* By default, outputs to console. Use --output to write files.
|
|
12
12
|
*
|
|
13
13
|
* Usage:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* npx fit-pathway agent <discipline> [--track=<track>]
|
|
15
|
+
* npx fit-pathway agent <discipline> --track=<track> --stage=plan
|
|
16
|
+
* npx fit-pathway agent <discipline> --track=<track> --output=./agents
|
|
17
|
+
* npx fit-pathway agent <discipline> [--track=<track>] --skills # Plain list of skill IDs
|
|
18
|
+
* npx fit-pathway agent <discipline> [--track=<track>] --tools # Plain list of tool names
|
|
19
|
+
* npx fit-pathway agent --list
|
|
20
20
|
*
|
|
21
21
|
* Examples:
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* npx fit-pathway agent software_engineering --track=platform
|
|
23
|
+
* npx fit-pathway agent software_engineering --track=platform --stage=plan
|
|
24
|
+
* npx fit-pathway agent software_engineering --track=platform --output=./agents
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { createDataLoader } from "@forwardimpact/map/loader";
|
|
@@ -32,14 +32,18 @@ import {
|
|
|
32
32
|
deriveReferenceLevel,
|
|
33
33
|
deriveAgentSkills,
|
|
34
34
|
generateSkillMarkdown,
|
|
35
|
-
getDisciplineAbbreviation,
|
|
36
|
-
toKebabCase,
|
|
37
35
|
interpolateTeamInstructions,
|
|
38
36
|
} from "@forwardimpact/libskill/agent";
|
|
39
37
|
import { deriveToolkit } from "@forwardimpact/libskill/toolkit";
|
|
40
38
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
41
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
formatError,
|
|
41
|
+
formatSuccess,
|
|
42
|
+
formatSubheader,
|
|
43
|
+
formatBullet,
|
|
44
|
+
} from "@forwardimpact/libcli";
|
|
42
45
|
import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
46
|
+
import { showAgentSummary, listAgentCombinations } from "./agent-list.js";
|
|
43
47
|
import {
|
|
44
48
|
generateClaudeCodeSettings,
|
|
45
49
|
writeProfile,
|
|
@@ -47,139 +51,9 @@ import {
|
|
|
47
51
|
writeSkills,
|
|
48
52
|
} from "./agent-io.js";
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* @param {Object} agentData - Agent-specific data
|
|
54
|
-
* @param {Array} skillsWithAgent - Skills with agent sections
|
|
55
|
-
*/
|
|
56
|
-
function showAgentSummary(data, agentData, skillsWithAgent) {
|
|
57
|
-
// Count valid combinations
|
|
58
|
-
let validCombinations = 0;
|
|
59
|
-
for (const discipline of agentData.disciplines) {
|
|
60
|
-
for (const track of agentData.tracks) {
|
|
61
|
-
const humanDiscipline = data.disciplines.find(
|
|
62
|
-
(d) => d.id === discipline.id,
|
|
63
|
-
);
|
|
64
|
-
const humanTrack = data.tracks.find((t) => t.id === track.id);
|
|
65
|
-
if (humanDiscipline && humanTrack) {
|
|
66
|
-
validCombinations++;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const skillsWithAgentCount = skillsWithAgent.filter((s) => s.agent).length;
|
|
72
|
-
|
|
73
|
-
console.log(`\n🤖 Agent\n`);
|
|
74
|
-
console.log(
|
|
75
|
-
`Disciplines: ${agentData.disciplines.length}/${data.disciplines.length} with agent definitions`,
|
|
76
|
-
);
|
|
77
|
-
console.log(
|
|
78
|
-
`Tracks: ${agentData.tracks.length}/${data.tracks.length} with agent definitions`,
|
|
79
|
-
);
|
|
80
|
-
console.log(
|
|
81
|
-
`Skills: ${skillsWithAgentCount}/${skillsWithAgent.length} with agent sections`,
|
|
82
|
-
);
|
|
83
|
-
console.log(`Stages: ${data.stages.length} available`);
|
|
84
|
-
console.log(`\nValid combinations: ${validCombinations}`);
|
|
85
|
-
console.log(`\nRun 'bunx pathway agent --list' for all combinations`);
|
|
86
|
-
console.log(
|
|
87
|
-
`Run 'bunx pathway agent <discipline> <track>' to generate files\n`,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Find valid agent combination pairs
|
|
93
|
-
* @param {Object} data - Pathway data
|
|
94
|
-
* @param {Object} agentData - Agent-specific data
|
|
95
|
-
* @returns {Array<{discipline: Object, track: Object, humanDiscipline: Object, humanTrack: Object}>}
|
|
96
|
-
*/
|
|
97
|
-
export function findValidCombinations(data, agentData) {
|
|
98
|
-
const pairs = [];
|
|
99
|
-
for (const discipline of agentData.disciplines) {
|
|
100
|
-
for (const track of agentData.tracks) {
|
|
101
|
-
const humanDiscipline = data.disciplines.find(
|
|
102
|
-
(d) => d.id === discipline.id,
|
|
103
|
-
);
|
|
104
|
-
const humanTrack = data.tracks.find((t) => t.id === track.id);
|
|
105
|
-
if (humanDiscipline && humanTrack) {
|
|
106
|
-
pairs.push({ discipline, track, humanDiscipline, humanTrack });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return pairs;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* List available agent combinations — compact output for piping
|
|
115
|
-
* @param {Object} data - Pathway data
|
|
116
|
-
* @param {Object} agentData - Agent-specific data
|
|
117
|
-
*/
|
|
118
|
-
function listAgentCombinationsCompact(data, agentData) {
|
|
119
|
-
for (const {
|
|
120
|
-
discipline,
|
|
121
|
-
track,
|
|
122
|
-
humanDiscipline,
|
|
123
|
-
humanTrack,
|
|
124
|
-
} of findValidCombinations(data, agentData)) {
|
|
125
|
-
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
126
|
-
const agentName = `${abbrev}-${toKebabCase(track.id)}`;
|
|
127
|
-
const specName = humanDiscipline.specialization || humanDiscipline.id;
|
|
128
|
-
console.log(
|
|
129
|
-
`${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* List available agent combinations — verbose output
|
|
136
|
-
* @param {Object} data - Pathway data
|
|
137
|
-
* @param {Object} agentData - Agent-specific data
|
|
138
|
-
*/
|
|
139
|
-
function listAgentCombinationsVerbose(data, agentData) {
|
|
140
|
-
console.log("# 🤖 Available Agent Combinations\n");
|
|
141
|
-
|
|
142
|
-
const agentDisciplineIds = new Set(agentData.disciplines.map((d) => d.id));
|
|
143
|
-
const agentTrackIds = new Set(agentData.tracks.map((t) => t.id));
|
|
144
|
-
|
|
145
|
-
console.log("## Disciplines with agent definitions:\n");
|
|
146
|
-
for (const discipline of data.disciplines) {
|
|
147
|
-
const status = agentDisciplineIds.has(discipline.id) ? "✅" : "⬜";
|
|
148
|
-
console.log(
|
|
149
|
-
` ${status} ${discipline.id} - ${discipline.specialization || discipline.name}`,
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
console.log("\n## Tracks with agent definitions:\n");
|
|
154
|
-
for (const track of data.tracks) {
|
|
155
|
-
const status = agentTrackIds.has(track.id) ? "✅" : "⬜";
|
|
156
|
-
console.log(` ${status} ${track.id} - ${track.name}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
console.log("\n## Valid combinations:\n");
|
|
160
|
-
for (const { discipline, track } of findValidCombinations(data, agentData)) {
|
|
161
|
-
console.log(` bunx pathway agent ${discipline.id} ${track.id}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
console.log("\n## Available stages:\n");
|
|
165
|
-
for (const stage of data.stages) {
|
|
166
|
-
console.log(` --stage=${stage.id}: ${stage.description.split(" - ")[0]}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* List available agent combinations (clean output for piping)
|
|
172
|
-
* @param {Object} data - Pathway data
|
|
173
|
-
* @param {Object} agentData - Agent-specific data
|
|
174
|
-
* @param {boolean} verbose - Show verbose output
|
|
175
|
-
*/
|
|
176
|
-
function listAgentCombinations(data, agentData, verbose = false) {
|
|
177
|
-
if (verbose) {
|
|
178
|
-
listAgentCombinationsVerbose(data, agentData);
|
|
179
|
-
} else {
|
|
180
|
-
listAgentCombinationsCompact(data, agentData);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
54
|
+
// Re-export so downstream consumers (build-packs, tests) can import
|
|
55
|
+
// findValidCombinations from './commands/agent.js' as before.
|
|
56
|
+
export { findValidCombinations } from "./agent-list.js";
|
|
183
57
|
|
|
184
58
|
/**
|
|
185
59
|
* Resolve and validate human + agent entities for a discipline/track pair
|
|
@@ -194,15 +68,21 @@ function resolveAgentEntities(data, agentData, disciplineId, trackId) {
|
|
|
194
68
|
const humanTrack = trackId ? data.tracks.find((t) => t.id === trackId) : null;
|
|
195
69
|
|
|
196
70
|
if (!humanDiscipline) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
formatError(`Unknown discipline: ${disciplineId}`) + "\n",
|
|
73
|
+
);
|
|
74
|
+
process.stderr.write("\nAvailable disciplines:\n");
|
|
75
|
+
for (const d of data.disciplines) {
|
|
76
|
+
process.stderr.write(formatBullet(d.id, 1) + "\n");
|
|
77
|
+
}
|
|
200
78
|
process.exit(1);
|
|
201
79
|
}
|
|
202
80
|
if (trackId && !humanTrack) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
for (const t of data.tracks)
|
|
81
|
+
process.stderr.write(formatError(`Unknown track: ${trackId}`) + "\n");
|
|
82
|
+
process.stderr.write("\nAvailable tracks:\n");
|
|
83
|
+
for (const t of data.tracks) {
|
|
84
|
+
process.stderr.write(formatBullet(t.id, 1) + "\n");
|
|
85
|
+
}
|
|
206
86
|
process.exit(1);
|
|
207
87
|
}
|
|
208
88
|
|
|
@@ -214,17 +94,23 @@ function resolveAgentEntities(data, agentData, disciplineId, trackId) {
|
|
|
214
94
|
: null;
|
|
215
95
|
|
|
216
96
|
if (!agentDiscipline) {
|
|
217
|
-
|
|
218
|
-
formatError(`No agent definition for discipline: ${disciplineId}`),
|
|
97
|
+
process.stderr.write(
|
|
98
|
+
formatError(`No agent definition for discipline: ${disciplineId}`) + "\n",
|
|
219
99
|
);
|
|
220
|
-
|
|
221
|
-
for (const d of agentData.disciplines)
|
|
100
|
+
process.stderr.write("\nAgent definitions exist for:\n");
|
|
101
|
+
for (const d of agentData.disciplines) {
|
|
102
|
+
process.stderr.write(formatBullet(d.id, 1) + "\n");
|
|
103
|
+
}
|
|
222
104
|
process.exit(1);
|
|
223
105
|
}
|
|
224
106
|
if (trackId && !agentTrack) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
formatError(`No agent definition for track: ${trackId}`) + "\n",
|
|
109
|
+
);
|
|
110
|
+
process.stderr.write("\nAgent definitions exist for:\n");
|
|
111
|
+
for (const t of agentData.tracks) {
|
|
112
|
+
process.stderr.write(formatBullet(t.id, 1) + "\n");
|
|
113
|
+
}
|
|
228
114
|
process.exit(1);
|
|
229
115
|
}
|
|
230
116
|
|
|
@@ -242,9 +128,10 @@ function printTeamInstructions(agentTrack, humanDiscipline) {
|
|
|
242
128
|
humanDiscipline,
|
|
243
129
|
});
|
|
244
130
|
if (teamInstructions) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
131
|
+
// Markdown output — headings stay literal so downstream tools parse them
|
|
132
|
+
process.stdout.write("# Team Instructions (CLAUDE.md)\n\n");
|
|
133
|
+
process.stdout.write(teamInstructions.trim() + "\n");
|
|
134
|
+
process.stdout.write("\n---\n\n");
|
|
248
135
|
}
|
|
249
136
|
}
|
|
250
137
|
|
|
@@ -264,17 +151,21 @@ async function handleSingleStage({
|
|
|
264
151
|
}) {
|
|
265
152
|
const stage = data.stages.find((s) => s.id === options.stage);
|
|
266
153
|
if (!stage) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
for (const s of data.stages)
|
|
154
|
+
process.stderr.write(formatError(`Unknown stage: ${options.stage}`) + "\n");
|
|
155
|
+
process.stderr.write("\nAvailable stages:\n");
|
|
156
|
+
for (const s of data.stages) {
|
|
157
|
+
process.stderr.write(formatBullet(s.id, 1) + "\n");
|
|
158
|
+
}
|
|
270
159
|
process.exit(1);
|
|
271
160
|
}
|
|
272
161
|
|
|
273
162
|
const profile = generateStageAgentProfile({ ...stageParams, stage });
|
|
274
163
|
const errors = validateAgentProfile(profile);
|
|
275
164
|
if (errors.length > 0) {
|
|
276
|
-
|
|
277
|
-
for (const err of errors)
|
|
165
|
+
process.stderr.write(formatError("Profile validation failed:") + "\n");
|
|
166
|
+
for (const err of errors) {
|
|
167
|
+
process.stderr.write(formatBullet(err, 1) + "\n");
|
|
168
|
+
}
|
|
278
169
|
process.exit(1);
|
|
279
170
|
}
|
|
280
171
|
|
|
@@ -283,7 +174,7 @@ async function handleSingleStage({
|
|
|
283
174
|
|
|
284
175
|
if (!options.output) {
|
|
285
176
|
printTeamInstructions(agentTrack, humanDiscipline);
|
|
286
|
-
|
|
177
|
+
process.stdout.write(formatAgentProfile(profile, agentTemplate) + "\n");
|
|
287
178
|
return;
|
|
288
179
|
}
|
|
289
180
|
|
|
@@ -294,9 +185,9 @@ async function handleSingleStage({
|
|
|
294
185
|
await writeTeamInstructions(teamInstructions, baseDir);
|
|
295
186
|
await writeProfile(profile, baseDir, agentTemplate);
|
|
296
187
|
await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`),
|
|
188
|
+
process.stdout.write("\n");
|
|
189
|
+
process.stdout.write(
|
|
190
|
+
formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`) + "\n",
|
|
300
191
|
);
|
|
301
192
|
}
|
|
302
193
|
|
|
@@ -338,10 +229,13 @@ async function handleAllStages({
|
|
|
338
229
|
for (const profile of profiles) {
|
|
339
230
|
const errors = validateAgentProfile(profile);
|
|
340
231
|
if (errors.length > 0) {
|
|
341
|
-
|
|
342
|
-
formatError(`Profile ${profile.frontmatter.name} validation failed:`)
|
|
232
|
+
process.stderr.write(
|
|
233
|
+
formatError(`Profile ${profile.frontmatter.name} validation failed:`) +
|
|
234
|
+
"\n",
|
|
343
235
|
);
|
|
344
|
-
for (const err of errors)
|
|
236
|
+
for (const err of errors) {
|
|
237
|
+
process.stderr.write(formatBullet(err, 1) + "\n");
|
|
238
|
+
}
|
|
345
239
|
process.exit(1);
|
|
346
240
|
}
|
|
347
241
|
}
|
|
@@ -349,10 +243,13 @@ async function handleAllStages({
|
|
|
349
243
|
for (const skill of skillFiles) {
|
|
350
244
|
const errors = validateAgentSkill(skill);
|
|
351
245
|
if (errors.length > 0) {
|
|
352
|
-
|
|
353
|
-
formatError(`Skill ${skill.frontmatter.name} validation failed:`)
|
|
246
|
+
process.stderr.write(
|
|
247
|
+
formatError(`Skill ${skill.frontmatter.name} validation failed:`) +
|
|
248
|
+
"\n",
|
|
354
249
|
);
|
|
355
|
-
for (const err of errors)
|
|
250
|
+
for (const err of errors) {
|
|
251
|
+
process.stderr.write(formatBullet(err, 1) + "\n");
|
|
252
|
+
}
|
|
356
253
|
process.exit(1);
|
|
357
254
|
}
|
|
358
255
|
}
|
|
@@ -369,8 +266,8 @@ async function handleAllStages({
|
|
|
369
266
|
if (!options.output) {
|
|
370
267
|
printTeamInstructions(agentTrack, humanDiscipline);
|
|
371
268
|
for (const profile of profiles) {
|
|
372
|
-
|
|
373
|
-
|
|
269
|
+
process.stdout.write(formatAgentProfile(profile, agentTemplate) + "\n");
|
|
270
|
+
process.stdout.write("\n---\n\n");
|
|
374
271
|
}
|
|
375
272
|
return;
|
|
376
273
|
}
|
|
@@ -386,12 +283,14 @@ async function handleAllStages({
|
|
|
386
283
|
const fileCount = await writeSkills(skillFiles, baseDir, skillTemplates);
|
|
387
284
|
await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
|
|
388
285
|
|
|
389
|
-
|
|
390
|
-
|
|
286
|
+
process.stdout.write("\n");
|
|
287
|
+
process.stdout.write(
|
|
288
|
+
formatSuccess(`Generated ${profiles.length} agents:`) + "\n",
|
|
289
|
+
);
|
|
391
290
|
for (const profile of profiles) {
|
|
392
|
-
|
|
291
|
+
process.stdout.write(formatBullet(profile.frontmatter.name, 1) + "\n");
|
|
393
292
|
}
|
|
394
|
-
|
|
293
|
+
process.stdout.write(formatSubheader(`Skills: ${fileCount} files`) + "\n");
|
|
395
294
|
}
|
|
396
295
|
|
|
397
296
|
/**
|
|
@@ -428,10 +327,10 @@ export async function runAgentCommand({
|
|
|
428
327
|
const trackId = options.track;
|
|
429
328
|
|
|
430
329
|
if (args.length > 1) {
|
|
431
|
-
|
|
330
|
+
process.stderr.write(
|
|
432
331
|
formatError(
|
|
433
332
|
`Unexpected argument: ${args[1]}. Did you mean --track=${args[1]}?`,
|
|
434
|
-
),
|
|
333
|
+
) + "\n",
|
|
435
334
|
);
|
|
436
335
|
process.exit(1);
|
|
437
336
|
}
|
|
@@ -4,15 +4,20 @@
|
|
|
4
4
|
* Handles behaviour summary, listing, and detail display in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* npx fit-pathway behaviour # Summary with stats
|
|
8
|
+
* npx fit-pathway behaviour --list # IDs only (for piping)
|
|
9
|
+
* npx fit-pathway behaviour <id> # Detail view
|
|
10
|
+
* npx fit-pathway behaviour --validate # Validation checks
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { createEntityCommand } from "./command-factory.js";
|
|
14
14
|
import { behaviourToMarkdown } from "../formatters/behaviour/markdown.js";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
formatTable,
|
|
17
|
+
formatHeader,
|
|
18
|
+
formatSubheader,
|
|
19
|
+
formatBullet,
|
|
20
|
+
} from "@forwardimpact/libcli";
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* Format behaviour list item for --list output
|
|
@@ -31,7 +36,7 @@ function formatListItem(behaviour) {
|
|
|
31
36
|
function formatSummary(behaviours, data) {
|
|
32
37
|
const { drivers } = data;
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
process.stdout.write("\n" + formatHeader("\u{1F9E0} Behaviours") + "\n\n");
|
|
35
40
|
|
|
36
41
|
// Summary table
|
|
37
42
|
const rows = behaviours.map((b) => {
|
|
@@ -41,10 +46,17 @@ function formatSummary(behaviours, data) {
|
|
|
41
46
|
return [b.id, b.name, linkedDrivers];
|
|
42
47
|
});
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
process.stdout.write(formatTable(["ID", "Name", "Drivers"], rows) + "\n");
|
|
50
|
+
process.stdout.write(
|
|
51
|
+
"\n" + formatSubheader(`Total: ${behaviours.length} behaviours`) + "\n\n",
|
|
52
|
+
);
|
|
53
|
+
process.stdout.write(
|
|
54
|
+
formatBullet("Run 'npx fit-pathway behaviour --list' for IDs and names") +
|
|
55
|
+
"\n",
|
|
56
|
+
);
|
|
57
|
+
process.stdout.write(
|
|
58
|
+
formatBullet("Run 'npx fit-pathway behaviour <id>' for details") + "\n\n",
|
|
59
|
+
);
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
/**
|