@forwardimpact/pathway 0.16.1 → 0.17.1
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 +3 -3
- package/src/commands/agent.js +47 -15
- package/src/components/code-display.js +108 -48
- package/src/components/file-card.js +106 -0
- package/src/components/skill-file-viewer.js +61 -0
- package/src/css/bundles/app.css +2 -0
- package/src/css/components/file-card.css +109 -0
- package/src/css/components/forms.css +47 -16
- package/src/css/components/skill-file-viewer.css +18 -0
- package/src/css/pages/agent-builder.css +4 -143
- package/src/formatters/agent/dom.js +7 -37
- package/src/formatters/agent/profile.js +0 -29
- package/src/formatters/agent/skill.js +69 -11
- package/src/formatters/job/dom.js +6 -4
- package/src/formatters/skill/dom.js +47 -9
- package/src/formatters/skill/shared.js +2 -0
- package/src/lib/template-loader.js +18 -0
- package/src/lib/yaml-loader.js +5 -0
- package/src/pages/agent-builder.js +127 -91
- package/src/pages/skill.js +27 -1
- package/templates/agent.template.md +15 -45
- package/templates/skill-install.template.sh +4 -0
- package/templates/skill-reference.template.md +3 -0
- package/templates/skill.template.md +37 -34
- package/src/components/markdown-textarea.js +0 -153
|
@@ -103,31 +103,62 @@
|
|
|
103
103
|
color: var(--color-primary);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
/* Code display -
|
|
107
|
-
.code-display-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
/* Code display - collapsible component for code/markdown with copy buttons */
|
|
107
|
+
.code-display-pane {
|
|
108
|
+
border: 1px solid var(--color-border);
|
|
109
|
+
border-radius: var(--radius-md);
|
|
110
|
+
background: var(--color-bg);
|
|
111
|
+
overflow: hidden;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
.code-display-
|
|
114
|
+
.code-display-summary {
|
|
114
115
|
display: flex;
|
|
115
|
-
justify-content: space-between;
|
|
116
116
|
align-items: center;
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
gap: var(--space-sm);
|
|
118
|
+
padding: var(--space-sm) var(--space-md);
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
list-style: none;
|
|
121
|
+
background: var(--color-surface);
|
|
122
|
+
border-bottom: 1px solid transparent;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
.code-display-
|
|
122
|
-
display:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
.code-display-summary::-webkit-details-marker {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.code-display-summary::before {
|
|
130
|
+
content: "▸";
|
|
131
|
+
font-size: var(--font-size-xs);
|
|
132
|
+
color: var(--color-text-muted);
|
|
133
|
+
flex-shrink: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.code-display-pane[open] > .code-display-summary {
|
|
137
|
+
border-bottom-color: var(--color-border);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.code-display-pane[open] > .code-display-summary::before {
|
|
141
|
+
content: "▾";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.code-display-summary .code-display-filename {
|
|
125
145
|
flex: 1;
|
|
126
|
-
min-width:
|
|
146
|
+
min-width: 0;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
text-overflow: ellipsis;
|
|
149
|
+
white-space: nowrap;
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
.code-display-
|
|
130
|
-
|
|
152
|
+
.code-display-summary .button-group {
|
|
153
|
+
flex-shrink: 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.code-display-body {
|
|
157
|
+
padding: var(--space-sm) var(--space-md) var(--space-md);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.code-display-body .text-muted {
|
|
161
|
+
margin: 0 0 var(--space-sm);
|
|
131
162
|
}
|
|
132
163
|
|
|
133
164
|
.code-display-filename {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill File Viewer
|
|
3
|
+
*
|
|
4
|
+
* Vertically stacked collapsible panes for viewing skill files
|
|
5
|
+
* (SKILL.md, install.sh, REFERENCE.md).
|
|
6
|
+
* Uses code-display-pane component with sfv-pane modifier.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.sfv {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.sfv-pane + .sfv-pane {
|
|
16
|
+
margin-top: var(--space-sm);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Builder Page Styles
|
|
3
3
|
*
|
|
4
|
-
* Agent builder form, preview, and card
|
|
4
|
+
* Agent builder form, preview, and card grid layout.
|
|
5
|
+
* Card surfaces are in components/file-card.css.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
@layer pages {
|
|
@@ -32,11 +33,9 @@
|
|
|
32
33
|
gap: var(--space-xl);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/* Agent section — spacing container, no surface */
|
|
35
37
|
.agent-section {
|
|
36
|
-
|
|
37
|
-
border-radius: var(--radius-lg);
|
|
38
|
-
padding: var(--space-lg);
|
|
39
|
-
box-shadow: var(--shadow-md);
|
|
38
|
+
margin-bottom: var(--space-lg);
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
.agent-section .section-header {
|
|
@@ -50,13 +49,6 @@
|
|
|
50
49
|
margin: 0;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
.agent-section .filename {
|
|
54
|
-
font-family: var(--font-family-mono);
|
|
55
|
-
font-size: var(--font-size-xs);
|
|
56
|
-
color: var(--color-text-muted);
|
|
57
|
-
margin-bottom: var(--space-sm);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
52
|
/* Agent cards grid */
|
|
61
53
|
.agent-cards-grid {
|
|
62
54
|
display: grid;
|
|
@@ -69,51 +61,6 @@
|
|
|
69
61
|
max-width: 800px;
|
|
70
62
|
}
|
|
71
63
|
|
|
72
|
-
.agent-card {
|
|
73
|
-
border: 1px solid var(--color-border);
|
|
74
|
-
border-radius: var(--radius-lg);
|
|
75
|
-
background: var(--color-bg);
|
|
76
|
-
overflow: hidden;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.agent-card-header {
|
|
80
|
-
display: flex;
|
|
81
|
-
justify-content: space-between;
|
|
82
|
-
align-items: flex-start;
|
|
83
|
-
padding: var(--space-md);
|
|
84
|
-
background: var(--color-surface);
|
|
85
|
-
border-bottom: 1px solid var(--color-border);
|
|
86
|
-
gap: var(--space-sm);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.agent-card-title {
|
|
90
|
-
display: flex;
|
|
91
|
-
align-items: center;
|
|
92
|
-
gap: var(--space-sm);
|
|
93
|
-
flex-wrap: wrap;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.agent-card-title h3 {
|
|
97
|
-
margin: 0;
|
|
98
|
-
font-size: var(--font-size-md);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.agent-card-emoji {
|
|
102
|
-
font-size: 1.5rem;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.agent-card-preview {
|
|
106
|
-
padding: var(--space-md);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.agent-card-preview .code-display-container {
|
|
110
|
-
margin: 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.agent-card-preview .code-display {
|
|
114
|
-
max-height: 400px;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
64
|
/* Skill cards grid */
|
|
118
65
|
.skill-cards-grid {
|
|
119
66
|
display: grid;
|
|
@@ -121,39 +68,6 @@
|
|
|
121
68
|
gap: var(--space-md);
|
|
122
69
|
}
|
|
123
70
|
|
|
124
|
-
.skill-card {
|
|
125
|
-
border: 1px solid var(--color-border);
|
|
126
|
-
border-radius: var(--radius-md);
|
|
127
|
-
background: var(--color-bg);
|
|
128
|
-
overflow: hidden;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.skill-card-header {
|
|
132
|
-
display: flex;
|
|
133
|
-
justify-content: space-between;
|
|
134
|
-
align-items: center;
|
|
135
|
-
padding: var(--space-sm) var(--space-md);
|
|
136
|
-
background: var(--color-surface);
|
|
137
|
-
border-bottom: 1px solid var(--color-border);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.skill-card-name {
|
|
141
|
-
font-weight: 600;
|
|
142
|
-
font-size: var(--font-size-sm);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.skill-card-preview {
|
|
146
|
-
padding: var(--space-sm) var(--space-md) var(--space-md);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.skill-card-preview .code-display-container {
|
|
150
|
-
margin: 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.skill-card-preview .code-display {
|
|
154
|
-
max-height: 300px;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
71
|
.skills-list {
|
|
158
72
|
display: flex;
|
|
159
73
|
flex-direction: column;
|
|
@@ -167,59 +81,6 @@
|
|
|
167
81
|
gap: var(--space-md);
|
|
168
82
|
}
|
|
169
83
|
|
|
170
|
-
.role-agent-card {
|
|
171
|
-
border: 1px solid var(--color-border);
|
|
172
|
-
border-radius: var(--radius-md);
|
|
173
|
-
overflow: hidden;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.role-agent-card > summary {
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
padding: var(--space-md);
|
|
179
|
-
background: var(--color-bg);
|
|
180
|
-
border-bottom: 1px solid transparent;
|
|
181
|
-
list-style: none;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.role-agent-card > summary::-webkit-details-marker {
|
|
185
|
-
display: none;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.role-agent-card[open] > summary {
|
|
189
|
-
border-bottom-color: var(--color-border);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.role-agent-header {
|
|
193
|
-
display: flex;
|
|
194
|
-
justify-content: space-between;
|
|
195
|
-
align-items: center;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.role-name {
|
|
199
|
-
font-weight: var(--font-weight-semibold);
|
|
200
|
-
color: var(--color-text);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.role-filename {
|
|
204
|
-
font-family: var(--font-family-mono);
|
|
205
|
-
font-size: var(--font-size-xs);
|
|
206
|
-
color: var(--color-text-muted);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.role-agent-content {
|
|
210
|
-
padding: var(--space-md);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.role-description {
|
|
214
|
-
margin-bottom: var(--space-md);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.role-agent-actions {
|
|
218
|
-
display: flex;
|
|
219
|
-
gap: var(--space-sm);
|
|
220
|
-
margin-bottom: var(--space-sm);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
84
|
/* Stage agent preview */
|
|
224
85
|
.stage-agent-preview .stage-header {
|
|
225
86
|
display: flex;
|
|
@@ -5,17 +5,7 @@
|
|
|
5
5
|
* Includes copy and download functionality.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
div,
|
|
10
|
-
h2,
|
|
11
|
-
h3,
|
|
12
|
-
p,
|
|
13
|
-
span,
|
|
14
|
-
button,
|
|
15
|
-
section,
|
|
16
|
-
details,
|
|
17
|
-
summary,
|
|
18
|
-
} from "../../lib/render.js";
|
|
8
|
+
import { div, h2, h3, p, span, button, section } from "../../lib/render.js";
|
|
19
9
|
import { createCodeDisplay } from "../../components/code-display.js";
|
|
20
10
|
import { formatAgentProfile } from "./profile.js";
|
|
21
11
|
import { formatAgentSkill } from "./skill.js";
|
|
@@ -163,33 +153,13 @@ function createSkillCard(skill) {
|
|
|
163
153
|
*/
|
|
164
154
|
function createRoleAgentCard(agent) {
|
|
165
155
|
const content = formatAgentProfile(agent);
|
|
166
|
-
const roleName = agent.frontmatter.name.split("-").pop(); // Extract role suffix (plan, review)
|
|
167
156
|
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
span(
|
|
175
|
-
{ className: "role-name" },
|
|
176
|
-
`${roleName.charAt(0).toUpperCase() + roleName.slice(1)} Agent`,
|
|
177
|
-
),
|
|
178
|
-
span({ className: "role-filename" }, agent.filename),
|
|
179
|
-
),
|
|
180
|
-
),
|
|
181
|
-
div(
|
|
182
|
-
{ className: "role-agent-content" },
|
|
183
|
-
p(
|
|
184
|
-
{ className: "text-muted role-description" },
|
|
185
|
-
agent.frontmatter.description,
|
|
186
|
-
),
|
|
187
|
-
createCodeDisplay({
|
|
188
|
-
content,
|
|
189
|
-
maxHeight: 400,
|
|
190
|
-
}),
|
|
191
|
-
),
|
|
192
|
-
);
|
|
157
|
+
return createCodeDisplay({
|
|
158
|
+
content,
|
|
159
|
+
filename: agent.filename,
|
|
160
|
+
description: agent.frontmatter.description,
|
|
161
|
+
maxHeight: 400,
|
|
162
|
+
});
|
|
193
163
|
}
|
|
194
164
|
|
|
195
165
|
/**
|
|
@@ -22,13 +22,6 @@ import {
|
|
|
22
22
|
* @property {string} content - Working style content (markdown)
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {Object} ChecklistEntry
|
|
27
|
-
* @property {{id: string, name: string}} skill - Skill info
|
|
28
|
-
* @property {{id: string, name: string, emojiIcon: string}} capability - Capability info
|
|
29
|
-
* @property {string[]} items - Checklist items
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
25
|
/**
|
|
33
26
|
* Prepare agent profile data for template rendering
|
|
34
27
|
* Normalizes string values by trimming trailing newlines for consistent template output.
|
|
@@ -49,8 +42,6 @@ import {
|
|
|
49
42
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} params.bodyData.skillIndex - Skill index entries
|
|
50
43
|
* @param {string} params.bodyData.roleContext - Role context text
|
|
51
44
|
* @param {WorkingStyleEntry[]} params.bodyData.workingStyles - Working style entries
|
|
52
|
-
* @param {ChecklistEntry[]} [params.bodyData.readChecklist] - Read-Then-Do Checklist entries
|
|
53
|
-
* @param {ChecklistEntry[]} [params.bodyData.confirmChecklist] - Do-Then-Confirm Checklist entries
|
|
54
45
|
* @param {string[]} params.bodyData.constraints - List of constraints
|
|
55
46
|
* @param {Array<{id: string, name: string, description: string}>} [params.bodyData.agentIndex] - List of all available agents
|
|
56
47
|
* @param {boolean} [params.bodyData.hasAgentIndex] - Whether agent index is available
|
|
@@ -81,20 +72,6 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
81
72
|
content: "required",
|
|
82
73
|
});
|
|
83
74
|
|
|
84
|
-
// Process readChecklist: trim items in each entry
|
|
85
|
-
const readChecklist = (bodyData.readChecklist || []).map((entry) => ({
|
|
86
|
-
skill: entry.skill,
|
|
87
|
-
capability: entry.capability,
|
|
88
|
-
items: (entry.items || []).map((item) => trimRequired(item)),
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
// Process confirmChecklist: trim items in each entry
|
|
92
|
-
const confirmChecklist = (bodyData.confirmChecklist || []).map((entry) => ({
|
|
93
|
-
skill: entry.skill,
|
|
94
|
-
capability: entry.capability,
|
|
95
|
-
items: (entry.items || []).map((item) => trimRequired(item)),
|
|
96
|
-
}));
|
|
97
|
-
|
|
98
75
|
return {
|
|
99
76
|
// Frontmatter - flatten description for single-line front matter
|
|
100
77
|
name: frontmatter.name,
|
|
@@ -115,10 +92,6 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
115
92
|
roleContext: trimValue(bodyData.roleContext),
|
|
116
93
|
workingStyles,
|
|
117
94
|
hasWorkingStyles: workingStyles.length > 0,
|
|
118
|
-
readChecklist,
|
|
119
|
-
hasReadChecklist: readChecklist.length > 0,
|
|
120
|
-
confirmChecklist,
|
|
121
|
-
hasConfirmChecklist: confirmChecklist.length > 0,
|
|
122
95
|
constraints,
|
|
123
96
|
hasConstraints: constraints.length > 0,
|
|
124
97
|
agentIndex,
|
|
@@ -143,8 +116,6 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
143
116
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} profile.bodyData.skillIndex - Skill index entries
|
|
144
117
|
* @param {string} profile.bodyData.roleContext - Role context text
|
|
145
118
|
* @param {WorkingStyleEntry[]} profile.bodyData.workingStyles - Working style entries
|
|
146
|
-
* @param {ChecklistEntry[]} [profile.bodyData.readChecklist] - Read-Then-Do Checklist entries (optional)
|
|
147
|
-
* @param {ChecklistEntry[]} [profile.bodyData.confirmChecklist] - Do-Then-Confirm Checklist entries (optional)
|
|
148
119
|
* @param {string[]} profile.bodyData.constraints - List of constraints
|
|
149
120
|
* @param {string} template - Mustache template string
|
|
150
121
|
* @returns {string} Complete .agent.md file content
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Skill Formatter
|
|
3
3
|
*
|
|
4
|
-
* Formats agent skill data into SKILL.md
|
|
5
|
-
* following the Agent Skills Standard
|
|
4
|
+
* Formats agent skill data into SKILL.md, scripts/install.sh, and
|
|
5
|
+
* references/REFERENCE.md file content following the Agent Skills Standard
|
|
6
|
+
* specification with progressive disclosure.
|
|
6
7
|
*
|
|
7
8
|
* Uses Mustache templates for flexible output formatting.
|
|
8
9
|
* Templates are loaded from data/ directory with fallback to templates/ directory.
|
|
@@ -13,6 +14,13 @@ import Mustache from "mustache";
|
|
|
13
14
|
import { trimValue, splitLines, trimFields } from "../shared.js";
|
|
14
15
|
import { flattenToLine } from "../template-preprocess.js";
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Lowercase the first character of a string
|
|
19
|
+
* @param {string} s
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
const lcFirst = (s) => (s ? s[0].toLowerCase() + s.slice(1) : s);
|
|
23
|
+
|
|
16
24
|
/**
|
|
17
25
|
* Prepare agent skill data for template rendering
|
|
18
26
|
* Normalizes string values by trimming trailing newlines for consistent template output.
|
|
@@ -23,7 +31,9 @@ import { flattenToLine } from "../template-preprocess.js";
|
|
|
23
31
|
* @param {string} [params.frontmatter.useWhen] - When to use this skill
|
|
24
32
|
* @param {string} params.title - Human-readable skill title for heading
|
|
25
33
|
* @param {Array} params.stages - Array of stage objects with stageName, focus, readChecklist, confirmChecklist
|
|
26
|
-
* @param {string} params.
|
|
34
|
+
* @param {string} params.instructions - Workflow guidance content (markdown)
|
|
35
|
+
* @param {string} params.installScript - Shell commands for install script
|
|
36
|
+
* @param {string} params.implementationReference - Reference content (markdown)
|
|
27
37
|
* @param {Array} [params.toolReferences] - Array of tool reference objects
|
|
28
38
|
* @returns {Object} Data object ready for Mustache template
|
|
29
39
|
*/
|
|
@@ -31,7 +41,9 @@ function prepareAgentSkillData({
|
|
|
31
41
|
frontmatter,
|
|
32
42
|
title,
|
|
33
43
|
stages,
|
|
34
|
-
|
|
44
|
+
instructions,
|
|
45
|
+
installScript,
|
|
46
|
+
implementationReference,
|
|
35
47
|
toolReferences,
|
|
36
48
|
}) {
|
|
37
49
|
// Process stages - trim focus and array values
|
|
@@ -43,12 +55,14 @@ function prepareAgentSkillData({
|
|
|
43
55
|
|
|
44
56
|
// Flatten multi-line strings to single line for front matter compatibility
|
|
45
57
|
const description = flattenToLine(frontmatter.description);
|
|
46
|
-
const useWhen = flattenToLine(frontmatter.useWhen);
|
|
58
|
+
const useWhen = lcFirst(flattenToLine(frontmatter.useWhen));
|
|
47
59
|
|
|
48
60
|
// Keep line arrays for body rendering
|
|
49
61
|
const descriptionLines = splitLines(frontmatter.description);
|
|
50
62
|
|
|
51
|
-
const
|
|
63
|
+
const trimmedInstructions = trimValue(instructions) || "";
|
|
64
|
+
const trimmedInstallScript = trimValue(installScript) || "";
|
|
65
|
+
const trimmedReference = trimValue(implementationReference) || "";
|
|
52
66
|
const tools = toolReferences || [];
|
|
53
67
|
|
|
54
68
|
return {
|
|
@@ -63,7 +77,11 @@ function prepareAgentSkillData({
|
|
|
63
77
|
title,
|
|
64
78
|
stages: processedStages,
|
|
65
79
|
hasStages: processedStages.length > 0,
|
|
66
|
-
|
|
80
|
+
instructions: trimmedInstructions,
|
|
81
|
+
hasInstructions: !!trimmedInstructions,
|
|
82
|
+
installScript: trimmedInstallScript,
|
|
83
|
+
hasInstallScript: !!trimmedInstallScript,
|
|
84
|
+
implementationReference: trimmedReference,
|
|
67
85
|
hasReference: !!trimmedReference,
|
|
68
86
|
toolReferences: tools,
|
|
69
87
|
hasToolReferences: tools.length > 0,
|
|
@@ -72,27 +90,67 @@ function prepareAgentSkillData({
|
|
|
72
90
|
|
|
73
91
|
/**
|
|
74
92
|
* Format agent skill as SKILL.md file content using Mustache template
|
|
75
|
-
* @param {Object} skill - Skill with frontmatter, title, stages,
|
|
93
|
+
* @param {Object} skill - Skill with frontmatter, title, stages, instructions, installScript, implementationReference
|
|
76
94
|
* @param {Object} skill.frontmatter - YAML frontmatter data
|
|
77
95
|
* @param {string} skill.frontmatter.name - Skill name (required)
|
|
78
96
|
* @param {string} skill.frontmatter.description - Skill description (required)
|
|
79
97
|
* @param {string} skill.title - Human-readable skill title for heading
|
|
80
98
|
* @param {Array} skill.stages - Array of stage objects with stageName, focus, readChecklist, confirmChecklist
|
|
81
|
-
* @param {string} skill.
|
|
99
|
+
* @param {string} skill.instructions - Workflow guidance (markdown)
|
|
100
|
+
* @param {string} skill.installScript - Shell commands for install script
|
|
101
|
+
* @param {string} skill.implementationReference - Reference content (markdown)
|
|
82
102
|
* @param {Array} [skill.toolReferences] - Array of tool reference objects
|
|
83
103
|
* @param {string} template - Mustache template string
|
|
84
104
|
* @returns {string} Complete SKILL.md file content
|
|
85
105
|
*/
|
|
86
106
|
export function formatAgentSkill(
|
|
87
|
-
{
|
|
107
|
+
{
|
|
108
|
+
frontmatter,
|
|
109
|
+
title,
|
|
110
|
+
stages,
|
|
111
|
+
instructions,
|
|
112
|
+
installScript,
|
|
113
|
+
implementationReference,
|
|
114
|
+
toolReferences,
|
|
115
|
+
},
|
|
88
116
|
template,
|
|
89
117
|
) {
|
|
90
118
|
const data = prepareAgentSkillData({
|
|
91
119
|
frontmatter,
|
|
92
120
|
title,
|
|
93
121
|
stages,
|
|
94
|
-
|
|
122
|
+
instructions,
|
|
123
|
+
installScript,
|
|
124
|
+
implementationReference,
|
|
95
125
|
toolReferences,
|
|
96
126
|
});
|
|
97
127
|
return Mustache.render(template, data);
|
|
98
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Format install script file content using Mustache template
|
|
132
|
+
* @param {Object} skill - Skill data with installScript and frontmatter
|
|
133
|
+
* @param {string} template - Mustache template string for install script
|
|
134
|
+
* @returns {string} Complete install.sh file content
|
|
135
|
+
*/
|
|
136
|
+
export function formatInstallScript(skill, template) {
|
|
137
|
+
const data = {
|
|
138
|
+
name: skill.frontmatter.name,
|
|
139
|
+
installScript: trimValue(skill.installScript) || "",
|
|
140
|
+
};
|
|
141
|
+
return Mustache.render(template, data);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Format reference file content using Mustache template
|
|
146
|
+
* @param {Object} skill - Skill data with implementationReference and title
|
|
147
|
+
* @param {string} template - Mustache template string for reference
|
|
148
|
+
* @returns {string} Complete REFERENCE.md file content
|
|
149
|
+
*/
|
|
150
|
+
export function formatReference(skill, template) {
|
|
151
|
+
const data = {
|
|
152
|
+
title: skill.title,
|
|
153
|
+
implementationReference: trimValue(skill.implementationReference) || "",
|
|
154
|
+
};
|
|
155
|
+
return Mustache.render(template, data);
|
|
156
|
+
}
|
|
@@ -233,16 +233,18 @@ export function createJobDescriptionSection({
|
|
|
233
233
|
template,
|
|
234
234
|
);
|
|
235
235
|
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
return section(
|
|
237
|
+
{ className: "section section-detail" },
|
|
238
|
+
h2({ className: "section-title" }, "Job Description"),
|
|
239
|
+
createCodeDisplay({
|
|
239
240
|
content: markdown,
|
|
241
|
+
filename: "job-description.md",
|
|
240
242
|
description:
|
|
241
243
|
"Copy this markdown-formatted job description for use in job postings, documentation, or sharing.",
|
|
242
244
|
toHtml: markdownToHtml,
|
|
243
245
|
minHeight: 450,
|
|
244
246
|
}),
|
|
245
|
-
|
|
247
|
+
);
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
/**
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "../../lib/render.js";
|
|
19
19
|
import { createBackLink } from "../../components/nav.js";
|
|
20
20
|
import { createLevelCell } from "../../components/detail.js";
|
|
21
|
-
import {
|
|
21
|
+
import { createSkillFileViewer } from "../../components/skill-file-viewer.js";
|
|
22
22
|
import { createToolIcon } from "../../lib/card-mappers.js";
|
|
23
23
|
import { SKILL_LEVEL_ORDER } from "@forwardimpact/schema/levels";
|
|
24
24
|
import { prepareSkillDetail } from "./shared.js";
|
|
@@ -34,6 +34,7 @@ import { createJsonLdScript, skillToJsonLd } from "../json-ld.js";
|
|
|
34
34
|
* @param {Array} context.capabilities - Capability entities
|
|
35
35
|
* @param {boolean} [context.showBackLink=true] - Whether to show back navigation link
|
|
36
36
|
* @param {boolean} [context.showToolsAndPatterns=true] - Whether to show required tools and implementation patterns
|
|
37
|
+
* @param {string} [context.agentSkillContent] - Pre-generated SKILL.md content for agent file viewer
|
|
37
38
|
* @returns {HTMLElement}
|
|
38
39
|
*/
|
|
39
40
|
export function skillToDOM(
|
|
@@ -45,6 +46,7 @@ export function skillToDOM(
|
|
|
45
46
|
capabilities,
|
|
46
47
|
showBackLink = true,
|
|
47
48
|
showToolsAndPatterns = true,
|
|
49
|
+
agentSkillContent,
|
|
48
50
|
} = {},
|
|
49
51
|
) {
|
|
50
52
|
const view = prepareSkillDetail(skill, {
|
|
@@ -222,18 +224,54 @@ export function skillToDOM(
|
|
|
222
224
|
)
|
|
223
225
|
: null,
|
|
224
226
|
|
|
225
|
-
//
|
|
226
|
-
showToolsAndPatterns &&
|
|
227
|
+
// Agent Skill Files
|
|
228
|
+
showToolsAndPatterns &&
|
|
229
|
+
(agentSkillContent || view.implementationReference || view.installScript)
|
|
227
230
|
? div(
|
|
228
231
|
{ className: "detail-section" },
|
|
229
|
-
heading2({ className: "section-title" }, "
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"Project-specific implementation guidance for this skill.",
|
|
234
|
-
minHeight: 450,
|
|
232
|
+
heading2({ className: "section-title" }, "Agent Skill Files"),
|
|
233
|
+
createSkillFileViewer({
|
|
234
|
+
files: buildSkillFiles(view, agentSkillContent),
|
|
235
|
+
maxHeight: 450,
|
|
235
236
|
}),
|
|
236
237
|
)
|
|
237
238
|
: null,
|
|
238
239
|
);
|
|
239
240
|
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Build file descriptors for the skill file viewer
|
|
244
|
+
* @param {import('./shared.js').SkillDetailView} view
|
|
245
|
+
* @param {string} [agentSkillContent] - Pre-generated SKILL.md content
|
|
246
|
+
* @returns {import('../../components/skill-file-viewer.js').SkillFile[]}
|
|
247
|
+
*/
|
|
248
|
+
function buildSkillFiles(view, agentSkillContent) {
|
|
249
|
+
/** @type {import('../../components/skill-file-viewer.js').SkillFile[]} */
|
|
250
|
+
const files = [];
|
|
251
|
+
|
|
252
|
+
if (agentSkillContent) {
|
|
253
|
+
files.push({
|
|
254
|
+
filename: "SKILL.md",
|
|
255
|
+
content: agentSkillContent,
|
|
256
|
+
language: "markdown",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (view.installScript) {
|
|
261
|
+
files.push({
|
|
262
|
+
filename: "scripts/install.sh",
|
|
263
|
+
content: view.installScript,
|
|
264
|
+
language: "bash",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (view.implementationReference) {
|
|
269
|
+
files.push({
|
|
270
|
+
filename: "references/REFERENCE.md",
|
|
271
|
+
content: view.implementationReference,
|
|
272
|
+
language: "markdown",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return files;
|
|
277
|
+
}
|
|
@@ -74,6 +74,7 @@ export function prepareSkillsList(
|
|
|
74
74
|
* @property {Array<{id: string, name: string, modifier: number}>} relatedTracks
|
|
75
75
|
* @property {Array<{id: string, name: string}>} relatedDrivers
|
|
76
76
|
* @property {Array<{name: string, url?: string, description: string, useWhen: string}>} toolReferences
|
|
77
|
+
* @property {string|null} installScript
|
|
77
78
|
* @property {string|null} implementationReference
|
|
78
79
|
*/
|
|
79
80
|
|
|
@@ -130,6 +131,7 @@ export function prepareSkillDetail(
|
|
|
130
131
|
toolReferences: (skill.toolReferences || [])
|
|
131
132
|
.slice()
|
|
132
133
|
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
134
|
+
installScript: skill.installScript || null,
|
|
133
135
|
implementationReference: skill.implementationReference || null,
|
|
134
136
|
};
|
|
135
137
|
}
|