@forwardimpact/pathway 0.6.0 → 0.8.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/LICENSE +201 -0
- package/README.md +67 -0
- package/bin/fit-pathway.js +126 -58
- package/package.json +6 -5
- package/src/commands/agent.js +46 -1
- package/src/commands/job.js +32 -1
- package/src/formatters/agent/profile.js +33 -25
- package/src/formatters/agent/skill.js +14 -4
- package/src/formatters/behaviour/microdata.js +1 -1
- package/src/formatters/discipline/microdata.js +1 -1
- package/src/formatters/driver/microdata.js +1 -1
- package/src/formatters/grade/microdata.js +1 -1
- package/src/formatters/job/description.js +22 -9
- package/src/formatters/job/dom.js +19 -10
- package/src/formatters/job/markdown.js +16 -8
- package/src/formatters/json-ld.js +2 -2
- package/src/formatters/microdata-shared.js +2 -2
- package/src/formatters/skill/microdata.js +1 -1
- package/src/formatters/stage/microdata.js +1 -1
- package/src/formatters/toolkit/dom.js +75 -0
- package/src/formatters/toolkit/markdown.js +38 -0
- package/src/formatters/track/microdata.js +1 -1
- package/src/pages/agent-builder.js +47 -1
- package/templates/agent.template.md +47 -27
- package/templates/job.template.md +24 -15
- package/templates/skill.template.md +27 -18
|
@@ -12,6 +12,12 @@ import Mustache from "mustache";
|
|
|
12
12
|
|
|
13
13
|
import { trimValue, trimRequired, trimFields } from "../shared.js";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} WorkingStyleEntry
|
|
17
|
+
* @property {string} title - Section title
|
|
18
|
+
* @property {string} content - Working style content (markdown)
|
|
19
|
+
*/
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Prepare agent profile data for template rendering
|
|
17
23
|
* Normalizes string values by trimming trailing newlines for consistent template output.
|
|
@@ -27,30 +33,32 @@ import { trimValue, trimRequired, trimFields } from "../shared.js";
|
|
|
27
33
|
* @param {string} params.bodyData.identity - Core identity text
|
|
28
34
|
* @param {string} [params.bodyData.priority] - Priority/philosophy statement
|
|
29
35
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} params.bodyData.skillIndex - Skill index entries
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {string} params.bodyData.operationalContext - Operational context text
|
|
33
|
-
* @param {string} params.bodyData.workingStyle - Working style markdown section
|
|
36
|
+
* @param {string} params.bodyData.roleContext - Role context text
|
|
37
|
+
* @param {WorkingStyleEntry[]} params.bodyData.workingStyles - Working style entries
|
|
34
38
|
* @param {string} [params.bodyData.beforeHandoff] - Before handoff checklist markdown
|
|
35
39
|
* @param {string[]} params.bodyData.constraints - List of constraints
|
|
40
|
+
* @param {Array<{id: string, name: string, description: string}>} [params.bodyData.agentIndex] - List of all available agents
|
|
41
|
+
* @param {boolean} [params.bodyData.hasAgentIndex] - Whether agent index is available
|
|
36
42
|
* @returns {Object} Data object ready for Mustache template
|
|
37
43
|
*/
|
|
38
44
|
function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
39
|
-
// Trim array fields using helpers
|
|
45
|
+
// Trim array fields using shared helpers
|
|
40
46
|
const handoffs = trimFields(frontmatter.handoffs, { prompt: "required" });
|
|
41
|
-
const beforeMakingChanges = trimFields(bodyData.beforeMakingChanges, {
|
|
42
|
-
text: "required",
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Trim simple string arrays
|
|
46
47
|
const constraints = (bodyData.constraints || []).map((c) => trimRequired(c));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
const skillIndex = trimFields(bodyData.skillIndex, {
|
|
49
|
+
name: "required",
|
|
50
|
+
dirname: "required",
|
|
51
|
+
useWhen: "required",
|
|
52
|
+
});
|
|
53
|
+
const agentIndex = trimFields(bodyData.agentIndex, {
|
|
54
|
+
id: "required",
|
|
55
|
+
name: "required",
|
|
56
|
+
description: "required",
|
|
57
|
+
});
|
|
58
|
+
const workingStyles = trimFields(bodyData.workingStyles, {
|
|
59
|
+
title: "required",
|
|
60
|
+
content: "required",
|
|
61
|
+
});
|
|
54
62
|
|
|
55
63
|
return {
|
|
56
64
|
// Frontmatter
|
|
@@ -66,12 +74,14 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
66
74
|
priority: trimValue(bodyData.priority),
|
|
67
75
|
skillIndex,
|
|
68
76
|
hasSkills: skillIndex.length > 0,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
workingStyle: trimValue(bodyData.workingStyle),
|
|
77
|
+
roleContext: trimValue(bodyData.roleContext),
|
|
78
|
+
workingStyles,
|
|
79
|
+
hasWorkingStyles: workingStyles.length > 0,
|
|
73
80
|
beforeHandoff: trimValue(bodyData.beforeHandoff),
|
|
74
81
|
constraints,
|
|
82
|
+
hasConstraints: constraints.length > 0,
|
|
83
|
+
agentIndex,
|
|
84
|
+
hasAgentIndex: agentIndex.length > 0,
|
|
75
85
|
};
|
|
76
86
|
}
|
|
77
87
|
|
|
@@ -90,10 +100,8 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
90
100
|
* @param {string} profile.bodyData.identity - Core identity text
|
|
91
101
|
* @param {string} [profile.bodyData.priority] - Priority/philosophy statement (optional)
|
|
92
102
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} profile.bodyData.skillIndex - Skill index entries
|
|
93
|
-
* @param {
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {string} profile.bodyData.operationalContext - Operational context text
|
|
96
|
-
* @param {string} profile.bodyData.workingStyle - Working style markdown section
|
|
103
|
+
* @param {string} profile.bodyData.roleContext - Role context text
|
|
104
|
+
* @param {WorkingStyleEntry[]} profile.bodyData.workingStyles - Working style entries
|
|
97
105
|
* @param {string} [profile.bodyData.beforeHandoff] - Before handoff checklist markdown (optional)
|
|
98
106
|
* @param {string[]} profile.bodyData.constraints - List of constraints
|
|
99
107
|
* @param {string} template - Mustache template string
|
|
@@ -40,14 +40,24 @@ function prepareAgentSkillData({
|
|
|
40
40
|
ready: "array",
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
const descriptionLines = splitLines(frontmatter.description);
|
|
44
|
+
const useWhenLines = splitLines(frontmatter.useWhen);
|
|
45
|
+
const trimmedReference = trimValue(reference) || "";
|
|
46
|
+
const tools = toolReferences || [];
|
|
47
|
+
|
|
43
48
|
return {
|
|
44
49
|
name: frontmatter.name,
|
|
45
|
-
descriptionLines
|
|
46
|
-
|
|
50
|
+
descriptionLines,
|
|
51
|
+
hasDescription: descriptionLines.length > 0,
|
|
52
|
+
useWhenLines,
|
|
53
|
+
hasUseWhen: useWhenLines.length > 0,
|
|
47
54
|
title,
|
|
48
55
|
stages: processedStages,
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
hasStages: processedStages.length > 0,
|
|
57
|
+
reference: trimmedReference,
|
|
58
|
+
hasReference: !!trimmedReference,
|
|
59
|
+
toolReferences: tools,
|
|
60
|
+
hasToolReferences: tools.length > 0,
|
|
51
61
|
};
|
|
52
62
|
}
|
|
53
63
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Behaviour formatting for microdata HTML output
|
|
3
3
|
*
|
|
4
4
|
* Generates clean, class-less HTML with microdata aligned with behaviour.schema.json
|
|
5
|
-
* RDF vocab: https://
|
|
5
|
+
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Discipline formatting for microdata HTML output
|
|
3
3
|
*
|
|
4
4
|
* Generates clean, class-less HTML with microdata aligned with discipline.schema.json
|
|
5
|
-
* RDF vocab: https://
|
|
5
|
+
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -129,25 +129,38 @@ function prepareJobDescriptionData({ job, discipline, grade, track }) {
|
|
|
129
129
|
grade.typicalExperienceRange || "",
|
|
130
130
|
) || null;
|
|
131
131
|
|
|
132
|
+
const responsibilities = trimFields(job.derivedResponsibilities, {
|
|
133
|
+
responsibility: "required",
|
|
134
|
+
});
|
|
135
|
+
const behaviours = trimFields(sortedBehaviours, {
|
|
136
|
+
maturityDescription: "optional",
|
|
137
|
+
});
|
|
138
|
+
const trimmedTrackRoleContext = trimValue(track?.roleContext);
|
|
139
|
+
const trimmedExpectationsParagraph = trimValue(expectationsParagraph);
|
|
140
|
+
const trimmedQualificationSummary = trimValue(qualificationSummary);
|
|
141
|
+
|
|
132
142
|
return {
|
|
133
143
|
title: job.title,
|
|
134
144
|
gradeId: grade.id,
|
|
135
145
|
typicalExperienceRange: grade.typicalExperienceRange,
|
|
136
146
|
trackName: track?.name || null,
|
|
147
|
+
hasTrack: !!track,
|
|
137
148
|
roleSummary: trimValue(roleSummary),
|
|
138
|
-
trackRoleContext:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
trackRoleContext: trimmedTrackRoleContext,
|
|
150
|
+
hasTrackRoleContext: !!trimmedTrackRoleContext,
|
|
151
|
+
expectationsParagraph: trimmedExpectationsParagraph,
|
|
152
|
+
hasExpectationsParagraph: !!trimmedExpectationsParagraph,
|
|
153
|
+
responsibilities,
|
|
154
|
+
hasResponsibilities: responsibilities.length > 0,
|
|
155
|
+
behaviours,
|
|
156
|
+
hasBehaviours: behaviours.length > 0,
|
|
146
157
|
skillLevels: skillLevels.map((level) => ({
|
|
147
158
|
...level,
|
|
148
159
|
skills: trimFields(level.skills, { levelDescription: "optional" }),
|
|
149
160
|
})),
|
|
150
|
-
|
|
161
|
+
hasSkillLevels: skillLevels.length > 0,
|
|
162
|
+
qualificationSummary: trimmedQualificationSummary,
|
|
163
|
+
hasQualificationSummary: !!trimmedQualificationSummary,
|
|
151
164
|
};
|
|
152
165
|
}
|
|
153
166
|
|
|
@@ -17,6 +17,7 @@ import { createBehaviourProfile } from "../../components/behaviour-profile.js";
|
|
|
17
17
|
import { createCodeDisplay } from "../../components/code-display.js";
|
|
18
18
|
import { markdownToHtml } from "../../lib/markdown.js";
|
|
19
19
|
import { formatJobDescription } from "./description.js";
|
|
20
|
+
import { createToolkitTable } from "../toolkit/dom.js";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Format job detail as DOM elements
|
|
@@ -77,14 +78,14 @@ export function jobToDOM(view, options = {}) {
|
|
|
77
78
|
// Radar charts
|
|
78
79
|
div(
|
|
79
80
|
{ className: "section auto-grid-lg" },
|
|
80
|
-
createSkillRadar(view.skillMatrix, {
|
|
81
|
-
title: "Skills Radar",
|
|
82
|
-
size: 420,
|
|
83
|
-
}),
|
|
84
81
|
createBehaviourRadar(view.behaviourProfile, {
|
|
85
82
|
title: "Behaviours Radar",
|
|
86
83
|
size: 420,
|
|
87
84
|
}),
|
|
85
|
+
createSkillRadar(view.skillMatrix, {
|
|
86
|
+
title: "Skills Radar",
|
|
87
|
+
size: 420,
|
|
88
|
+
}),
|
|
88
89
|
),
|
|
89
90
|
|
|
90
91
|
// Job Description HTML (for print view)
|
|
@@ -104,21 +105,29 @@ export function jobToDOM(view, options = {}) {
|
|
|
104
105
|
})
|
|
105
106
|
: null,
|
|
106
107
|
|
|
107
|
-
// Skill matrix,
|
|
108
|
+
// Behaviour profile, Skill matrix, Toolkit, Driver coverage tables
|
|
108
109
|
showTables
|
|
109
110
|
? div(
|
|
110
111
|
{ className: "job-tables-section" },
|
|
111
|
-
createDetailSection({
|
|
112
|
-
title: "Skill Matrix",
|
|
113
|
-
content: createSkillMatrix(view.skillMatrix),
|
|
114
|
-
}),
|
|
115
|
-
|
|
116
112
|
// Behaviour profile table
|
|
117
113
|
createDetailSection({
|
|
118
114
|
title: "Behaviour Profile",
|
|
119
115
|
content: createBehaviourProfile(view.behaviourProfile),
|
|
120
116
|
}),
|
|
121
117
|
|
|
118
|
+
createDetailSection({
|
|
119
|
+
title: "Skill Matrix",
|
|
120
|
+
content: createSkillMatrix(view.skillMatrix),
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
// Toolkit (after skill matrix)
|
|
124
|
+
view.toolkit && view.toolkit.length > 0
|
|
125
|
+
? createDetailSection({
|
|
126
|
+
title: "Tool Kit",
|
|
127
|
+
content: createToolkitTable(view.toolkit),
|
|
128
|
+
})
|
|
129
|
+
: null,
|
|
130
|
+
|
|
122
131
|
// Driver coverage
|
|
123
132
|
view.driverCoverage.length > 0
|
|
124
133
|
? createDetailSection({
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { formatLevel } from "../../lib/render.js";
|
|
11
11
|
import { formatJobDescription } from "./description.js";
|
|
12
12
|
import { SKILL_LEVEL_ORDER } from "@forwardimpact/schema/levels";
|
|
13
|
+
import { toolkitToMarkdown } from "../toolkit/markdown.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Format job detail as markdown
|
|
@@ -33,6 +34,15 @@ export function jobToMarkdown(view, entities = {}, jobTemplate) {
|
|
|
33
34
|
lines.push("");
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// Behaviour Profile
|
|
38
|
+
lines.push("## Behaviour Profile", "");
|
|
39
|
+
const behaviourRows = view.behaviourProfile.map((b) => [
|
|
40
|
+
b.behaviourName,
|
|
41
|
+
formatLevel(b.maturity),
|
|
42
|
+
]);
|
|
43
|
+
lines.push(tableToMarkdown(["Behaviour", "Maturity"], behaviourRows));
|
|
44
|
+
lines.push("");
|
|
45
|
+
|
|
36
46
|
// Skill Matrix - sorted by level descending
|
|
37
47
|
lines.push("## Skill Matrix", "");
|
|
38
48
|
const sortedSkills = [...view.skillMatrix].sort((a, b) => {
|
|
@@ -50,14 +60,12 @@ export function jobToMarkdown(view, entities = {}, jobTemplate) {
|
|
|
50
60
|
lines.push(tableToMarkdown(["Skill", "Level"], skillRows));
|
|
51
61
|
lines.push("");
|
|
52
62
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
lines.push(tableToMarkdown(["Behaviour", "Maturity"], behaviourRows));
|
|
60
|
-
lines.push("");
|
|
63
|
+
// Toolkit
|
|
64
|
+
if (view.toolkit && view.toolkit.length > 0) {
|
|
65
|
+
lines.push("## Tool Kit", "");
|
|
66
|
+
lines.push(toolkitToMarkdown(view.toolkit));
|
|
67
|
+
lines.push("");
|
|
68
|
+
}
|
|
61
69
|
|
|
62
70
|
// Driver Coverage
|
|
63
71
|
if (view.driverCoverage.length > 0) {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* JSON-LD structured data generation
|
|
3
3
|
*
|
|
4
4
|
* Generates JSON-LD for entity pages to enable machine-readable data.
|
|
5
|
-
* Aligns with the RDF schema at https://
|
|
5
|
+
* Aligns with the RDF schema at https://www.forwardimpact.team/schema/rdf/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const VOCAB_BASE = "https://
|
|
8
|
+
const VOCAB_BASE = "https://www.forwardimpact.team/schema/rdf/";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Create a JSON-LD script element
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Shared microdata HTML utilities
|
|
3
3
|
*
|
|
4
4
|
* Helper functions for generating clean, class-less HTML with microdata attributes
|
|
5
|
-
* aligned with the RDF schema at https://
|
|
5
|
+
* aligned with the RDF schema at https://www.forwardimpact.team/schema/rdf/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const VOCAB_BASE = "https://
|
|
8
|
+
const VOCAB_BASE = "https://www.forwardimpact.team/schema/rdf/";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Create an opening tag with microdata attributes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Skill formatting for microdata HTML output
|
|
3
3
|
*
|
|
4
4
|
* Generates clean, class-less HTML with microdata aligned with capability.schema.json
|
|
5
|
-
* RDF vocab: https://
|
|
5
|
+
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toolkit formatting for DOM/web output
|
|
3
|
+
*
|
|
4
|
+
* Displays a compact toolkit table showing tools with icons and descriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
div,
|
|
9
|
+
table,
|
|
10
|
+
thead,
|
|
11
|
+
tbody,
|
|
12
|
+
tr,
|
|
13
|
+
th,
|
|
14
|
+
td,
|
|
15
|
+
a,
|
|
16
|
+
span,
|
|
17
|
+
} from "../../lib/render.js";
|
|
18
|
+
import { createToolIcon } from "../../lib/card-mappers.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a toolkit table for display in job/agent detail pages
|
|
22
|
+
* @param {Array<{name: string, description: string, url?: string, simpleIcon?: string, skillIds: string[]}>} toolkit - Derived toolkit entries
|
|
23
|
+
* @returns {HTMLElement}
|
|
24
|
+
*/
|
|
25
|
+
export function createToolkitTable(toolkit) {
|
|
26
|
+
if (!toolkit || toolkit.length === 0) {
|
|
27
|
+
return div({ className: "empty-state" }, "No tools in toolkit");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const rows = toolkit.map((tool) => {
|
|
31
|
+
const iconCell = tool.simpleIcon
|
|
32
|
+
? td(
|
|
33
|
+
{ className: "tool-icon-cell" },
|
|
34
|
+
createToolIcon(tool.simpleIcon, tool.name),
|
|
35
|
+
)
|
|
36
|
+
: td({ className: "tool-icon-cell" });
|
|
37
|
+
|
|
38
|
+
const nameContent = tool.url
|
|
39
|
+
? a(
|
|
40
|
+
{
|
|
41
|
+
href: tool.url,
|
|
42
|
+
target: "_blank",
|
|
43
|
+
rel: "noopener noreferrer",
|
|
44
|
+
className: "tool-link",
|
|
45
|
+
},
|
|
46
|
+
tool.name,
|
|
47
|
+
span({ className: "external-icon" }, " ↗"),
|
|
48
|
+
)
|
|
49
|
+
: span({}, tool.name);
|
|
50
|
+
|
|
51
|
+
return tr(
|
|
52
|
+
{},
|
|
53
|
+
iconCell,
|
|
54
|
+
td({ className: "tool-name-cell" }, nameContent),
|
|
55
|
+
td({ className: "tool-description-cell" }, tool.description),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return div(
|
|
60
|
+
{ className: "table-container" },
|
|
61
|
+
table(
|
|
62
|
+
{ className: "table toolkit-table" },
|
|
63
|
+
thead(
|
|
64
|
+
{},
|
|
65
|
+
tr(
|
|
66
|
+
{},
|
|
67
|
+
th({ style: "width: 40px" }, ""),
|
|
68
|
+
th({}, "Tool"),
|
|
69
|
+
th({}, "Description"),
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
tbody({}, ...rows),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toolkit formatting for markdown/CLI output
|
|
3
|
+
*
|
|
4
|
+
* Displays toolkit as a markdown table with tools, icons, and descriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { tableToMarkdown } from "../shared.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format toolkit as markdown table
|
|
11
|
+
* @param {Array<{name: string, description: string, url?: string, simpleIcon?: string, skillIds: string[]}>} toolkit - Derived toolkit entries
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export function toolkitToMarkdown(toolkit) {
|
|
15
|
+
if (!toolkit || toolkit.length === 0) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const rows = toolkit.map((tool) => {
|
|
20
|
+
const name = tool.url ? `[${tool.name}](${tool.url})` : tool.name;
|
|
21
|
+
return [name, tool.description];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return tableToMarkdown(["Tool", "Description"], rows);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Format toolkit as a plain list of tool names (for --tools flag)
|
|
29
|
+
* @param {Array<{name: string}>} toolkit - Derived toolkit entries
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function toolkitToPlainList(toolkit) {
|
|
33
|
+
if (!toolkit || toolkit.length === 0) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return toolkit.map((tool) => tool.name).join("\n");
|
|
38
|
+
}
|
|
@@ -27,7 +27,9 @@ import {
|
|
|
27
27
|
generateSkillMd,
|
|
28
28
|
deriveAgentSkills,
|
|
29
29
|
deriveReferenceGrade,
|
|
30
|
-
|
|
30
|
+
deriveToolkit,
|
|
31
|
+
buildAgentIndex,
|
|
32
|
+
} from "@forwardimpact/model";
|
|
31
33
|
import {
|
|
32
34
|
createSelectWithValue,
|
|
33
35
|
createDisciplineSelect,
|
|
@@ -37,6 +39,8 @@ import { getStageEmoji } from "../formatters/stage/shared.js";
|
|
|
37
39
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
38
40
|
import { formatAgentSkill } from "../formatters/agent/skill.js";
|
|
39
41
|
import { createCodeDisplay } from "../components/code-display.js";
|
|
42
|
+
import { createToolkitTable } from "../formatters/toolkit/dom.js";
|
|
43
|
+
import { createDetailSection } from "../components/detail.js";
|
|
40
44
|
|
|
41
45
|
/** All stages option value */
|
|
42
46
|
const ALL_STAGES_VALUE = "all";
|
|
@@ -247,6 +251,15 @@ export async function renderAgentBuilder() {
|
|
|
247
251
|
// Get reference grade for derivation
|
|
248
252
|
const grade = deriveReferenceGrade(data.grades);
|
|
249
253
|
|
|
254
|
+
// Build agent index for all valid combinations
|
|
255
|
+
const agentIndex = buildAgentIndex({
|
|
256
|
+
disciplines: data.disciplines,
|
|
257
|
+
tracks: data.tracks,
|
|
258
|
+
stages,
|
|
259
|
+
agentDisciplines: agentData.disciplines,
|
|
260
|
+
agentTracks: agentData.tracks,
|
|
261
|
+
});
|
|
262
|
+
|
|
250
263
|
// Build context for generation
|
|
251
264
|
const context = {
|
|
252
265
|
humanDiscipline,
|
|
@@ -262,6 +275,7 @@ export async function renderAgentBuilder() {
|
|
|
262
275
|
vscodeSettings: agentData.vscodeSettings,
|
|
263
276
|
devcontainer: agentData.devcontainer,
|
|
264
277
|
templates,
|
|
278
|
+
agentIndex,
|
|
265
279
|
};
|
|
266
280
|
|
|
267
281
|
// Generate preview based on stage selection
|
|
@@ -439,6 +453,7 @@ function createAllStagesPreview(context) {
|
|
|
439
453
|
vscodeSettings,
|
|
440
454
|
devcontainer,
|
|
441
455
|
templates,
|
|
456
|
+
agentIndex,
|
|
442
457
|
} = context;
|
|
443
458
|
|
|
444
459
|
// Generate all stage agents
|
|
@@ -469,6 +484,7 @@ function createAllStagesPreview(context) {
|
|
|
469
484
|
agentTrack,
|
|
470
485
|
capabilities,
|
|
471
486
|
stages,
|
|
487
|
+
agentIndex,
|
|
472
488
|
});
|
|
473
489
|
|
|
474
490
|
return { stage, derived, profile };
|
|
@@ -488,6 +504,12 @@ function createAllStagesPreview(context) {
|
|
|
488
504
|
.filter((skill) => skill?.agent)
|
|
489
505
|
.map((skill) => generateSkillMd(skill, stages));
|
|
490
506
|
|
|
507
|
+
// Derive toolkit from agent skills
|
|
508
|
+
const toolkit = deriveToolkit({
|
|
509
|
+
skillMatrix: derivedSkills,
|
|
510
|
+
skills,
|
|
511
|
+
});
|
|
512
|
+
|
|
491
513
|
return div(
|
|
492
514
|
{ className: "agent-deployment" },
|
|
493
515
|
|
|
@@ -533,6 +555,14 @@ function createAllStagesPreview(context) {
|
|
|
533
555
|
),
|
|
534
556
|
),
|
|
535
557
|
|
|
558
|
+
// Tool Kit section
|
|
559
|
+
toolkit.length > 0
|
|
560
|
+
? createDetailSection({
|
|
561
|
+
title: `Tool Kit (${toolkit.length})`,
|
|
562
|
+
content: createToolkitTable(toolkit),
|
|
563
|
+
})
|
|
564
|
+
: null,
|
|
565
|
+
|
|
536
566
|
// CLI hint
|
|
537
567
|
createCliHint(humanDiscipline.id, humanTrack.id),
|
|
538
568
|
);
|
|
@@ -559,6 +589,7 @@ function createSingleStagePreview(context, stage) {
|
|
|
559
589
|
devcontainer,
|
|
560
590
|
stages,
|
|
561
591
|
templates,
|
|
592
|
+
agentIndex,
|
|
562
593
|
} = context;
|
|
563
594
|
|
|
564
595
|
// Derive stage agent
|
|
@@ -588,6 +619,7 @@ function createSingleStagePreview(context, stage) {
|
|
|
588
619
|
agentTrack,
|
|
589
620
|
capabilities,
|
|
590
621
|
stages,
|
|
622
|
+
agentIndex,
|
|
591
623
|
});
|
|
592
624
|
|
|
593
625
|
// Get skills for this stage (using full derived skills)
|
|
@@ -603,6 +635,12 @@ function createSingleStagePreview(context, stage) {
|
|
|
603
635
|
.filter((skill) => skill?.agent)
|
|
604
636
|
.map((skill) => generateSkillMd(skill, stages));
|
|
605
637
|
|
|
638
|
+
// Derive toolkit from agent skills
|
|
639
|
+
const toolkit = deriveToolkit({
|
|
640
|
+
skillMatrix: derivedSkills,
|
|
641
|
+
skills,
|
|
642
|
+
});
|
|
643
|
+
|
|
606
644
|
return div(
|
|
607
645
|
{ className: "agent-deployment" },
|
|
608
646
|
|
|
@@ -642,6 +680,14 @@ function createSingleStagePreview(context, stage) {
|
|
|
642
680
|
),
|
|
643
681
|
),
|
|
644
682
|
|
|
683
|
+
// Tool Kit section
|
|
684
|
+
toolkit.length > 0
|
|
685
|
+
? createDetailSection({
|
|
686
|
+
title: `Tool Kit (${toolkit.length})`,
|
|
687
|
+
content: createToolkitTable(toolkit),
|
|
688
|
+
})
|
|
689
|
+
: null,
|
|
690
|
+
|
|
645
691
|
// CLI hint
|
|
646
692
|
createCliHint(humanDiscipline.id, humanTrack.id, stage.id),
|
|
647
693
|
);
|