@forwardimpact/pathway 0.16.0 → 0.17.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/package.json +3 -3
- package/src/commands/agent.js +47 -15
- package/src/components/code-display.js +108 -48
- package/src/components/skill-file-viewer.js +61 -0
- package/src/css/bundles/app.css +1 -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 +13 -55
- 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 +94 -31
- package/src/pages/skill.js +27 -1
- package/templates/agent.template.md +18 -48
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.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,8 @@
|
|
|
40
40
|
"./commands": "./src/commands/index.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@forwardimpact/schema": "^0.
|
|
44
|
-
"@forwardimpact/model": "^1.
|
|
43
|
+
"@forwardimpact/schema": "^0.9.0",
|
|
44
|
+
"@forwardimpact/model": "^1.1.0",
|
|
45
45
|
"mustache": "^4.2.0",
|
|
46
46
|
"simple-icons": "^16.7.0",
|
|
47
47
|
"yaml": "^2.3.4"
|
package/src/commands/agent.js
CHANGED
|
@@ -42,11 +42,17 @@ import {
|
|
|
42
42
|
buildAgentIndex,
|
|
43
43
|
} from "@forwardimpact/model";
|
|
44
44
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
formatAgentSkill,
|
|
47
|
+
formatInstallScript,
|
|
48
|
+
formatReference,
|
|
49
|
+
} from "../formatters/agent/skill.js";
|
|
46
50
|
import { formatError, formatSuccess } from "../lib/cli-output.js";
|
|
47
51
|
import {
|
|
48
52
|
loadAgentTemplate,
|
|
49
53
|
loadSkillTemplate,
|
|
54
|
+
loadSkillInstallTemplate,
|
|
55
|
+
loadSkillReferenceTemplate,
|
|
50
56
|
} from "../lib/template-loader.js";
|
|
51
57
|
import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
52
58
|
|
|
@@ -263,25 +269,45 @@ async function writeProfile(profile, baseDir, template) {
|
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
/**
|
|
266
|
-
* Write skill files
|
|
272
|
+
* Write skill files (SKILL.md, scripts/install.sh, references/REFERENCE.md)
|
|
267
273
|
* @param {Array} skills - Generated skills
|
|
268
274
|
* @param {string} baseDir - Base output directory
|
|
269
|
-
* @param {
|
|
275
|
+
* @param {Object} templates - Templates object with skill, install, reference
|
|
270
276
|
*/
|
|
271
|
-
async function writeSkills(skills, baseDir,
|
|
277
|
+
async function writeSkills(skills, baseDir, templates) {
|
|
278
|
+
let fileCount = 0;
|
|
272
279
|
for (const skill of skills) {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
"SKILL.md",
|
|
279
|
-
);
|
|
280
|
-
const skillContent = formatAgentSkill(skill, template);
|
|
280
|
+
const skillDir = join(baseDir, ".claude", "skills", skill.dirname);
|
|
281
|
+
|
|
282
|
+
// Write SKILL.md (always)
|
|
283
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
284
|
+
const skillContent = formatAgentSkill(skill, templates.skill);
|
|
281
285
|
await ensureDir(skillPath);
|
|
282
286
|
await writeFile(skillPath, skillContent, "utf-8");
|
|
283
287
|
console.log(formatSuccess(`Created: ${skillPath}`));
|
|
288
|
+
fileCount++;
|
|
289
|
+
|
|
290
|
+
// Write scripts/install.sh (only when installScript exists)
|
|
291
|
+
if (skill.installScript) {
|
|
292
|
+
const installPath = join(skillDir, "scripts", "install.sh");
|
|
293
|
+
const installContent = formatInstallScript(skill, templates.install);
|
|
294
|
+
await ensureDir(installPath);
|
|
295
|
+
await writeFile(installPath, installContent, { mode: 0o755 });
|
|
296
|
+
console.log(formatSuccess(`Created: ${installPath}`));
|
|
297
|
+
fileCount++;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Write references/REFERENCE.md (only when implementationReference exists)
|
|
301
|
+
if (skill.implementationReference) {
|
|
302
|
+
const refPath = join(skillDir, "references", "REFERENCE.md");
|
|
303
|
+
const refContent = formatReference(skill, templates.reference);
|
|
304
|
+
await ensureDir(refPath);
|
|
305
|
+
await writeFile(refPath, refContent, "utf-8");
|
|
306
|
+
console.log(formatSuccess(`Created: ${refPath}`));
|
|
307
|
+
fileCount++;
|
|
308
|
+
}
|
|
284
309
|
}
|
|
310
|
+
return fileCount;
|
|
285
311
|
}
|
|
286
312
|
|
|
287
313
|
/**
|
|
@@ -428,7 +454,6 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
428
454
|
agentBehaviours: agentData.behaviours,
|
|
429
455
|
agentDiscipline,
|
|
430
456
|
agentTrack,
|
|
431
|
-
capabilities: data.capabilities,
|
|
432
457
|
stages: data.stages,
|
|
433
458
|
agentIndex,
|
|
434
459
|
};
|
|
@@ -535,6 +560,13 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
535
560
|
// Load templates
|
|
536
561
|
const agentTemplate = await loadAgentTemplate(dataDir);
|
|
537
562
|
const skillTemplate = await loadSkillTemplate(dataDir);
|
|
563
|
+
const installTemplate = await loadSkillInstallTemplate(dataDir);
|
|
564
|
+
const referenceTemplate = await loadSkillReferenceTemplate(dataDir);
|
|
565
|
+
const skillTemplates = {
|
|
566
|
+
skill: skillTemplate,
|
|
567
|
+
install: installTemplate,
|
|
568
|
+
reference: referenceTemplate,
|
|
569
|
+
};
|
|
538
570
|
|
|
539
571
|
// Output to console (default) or write to files (with --output)
|
|
540
572
|
if (!options.output) {
|
|
@@ -548,7 +580,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
548
580
|
for (const profile of profiles) {
|
|
549
581
|
await writeProfile(profile, baseDir, agentTemplate);
|
|
550
582
|
}
|
|
551
|
-
await writeSkills(skillFiles, baseDir,
|
|
583
|
+
const fileCount = await writeSkills(skillFiles, baseDir, skillTemplates);
|
|
552
584
|
await generateVSCodeSettings(baseDir, agentData.vscodeSettings);
|
|
553
585
|
await generateDevcontainer(
|
|
554
586
|
baseDir,
|
|
@@ -562,5 +594,5 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
562
594
|
for (const profile of profiles) {
|
|
563
595
|
console.log(` - ${profile.frontmatter.name}`);
|
|
564
596
|
}
|
|
565
|
-
console.log(` Skills: ${
|
|
597
|
+
console.log(` Skills: ${fileCount} files`);
|
|
566
598
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Code Display Component
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Collapsible read-only code block with copy buttons and syntax highlighting.
|
|
5
|
+
* Wrapped in a <details>/<summary> element with the filename and copy buttons
|
|
6
|
+
* always visible in the summary. Content is lazy-rendered on first open.
|
|
7
|
+
*
|
|
5
8
|
* Used for markdown content, agent profiles, skills, and code snippets.
|
|
6
9
|
*/
|
|
7
10
|
|
|
@@ -20,7 +23,9 @@ export function createCopyButton(content) {
|
|
|
20
23
|
const btn = button(
|
|
21
24
|
{
|
|
22
25
|
className: "btn btn-sm copy-btn",
|
|
23
|
-
onClick: async () => {
|
|
26
|
+
onClick: async (e) => {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
e.stopPropagation();
|
|
24
29
|
try {
|
|
25
30
|
await navigator.clipboard.writeText(content);
|
|
26
31
|
btn.textContent = "✓ Copied!";
|
|
@@ -52,7 +57,9 @@ function createCopyHtmlButton(html) {
|
|
|
52
57
|
const btn = button(
|
|
53
58
|
{
|
|
54
59
|
className: "btn btn-sm btn-secondary copy-btn",
|
|
55
|
-
onClick: async () => {
|
|
60
|
+
onClick: async (e) => {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
e.stopPropagation();
|
|
56
63
|
try {
|
|
57
64
|
const blob = new Blob([html], { type: "text/html" });
|
|
58
65
|
const clipboardItem = new ClipboardItem({ "text/html": blob });
|
|
@@ -78,27 +85,15 @@ function createCopyHtmlButton(html) {
|
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
/**
|
|
81
|
-
* Create
|
|
88
|
+
* Create the code <pre> element with syntax highlighting
|
|
82
89
|
* @param {Object} options
|
|
83
|
-
* @param {string} options.content -
|
|
84
|
-
* @param {string}
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {Function} [options.toHtml] - Function to convert content to HTML (enables "Copy as HTML" button)
|
|
88
|
-
* @param {number} [options.minHeight] - Optional minimum height in pixels
|
|
89
|
-
* @param {number} [options.maxHeight] - Optional maximum height in pixels
|
|
90
|
+
* @param {string} options.content - Code content
|
|
91
|
+
* @param {string} options.language - Language for highlighting
|
|
92
|
+
* @param {number} [options.minHeight] - Min height in pixels
|
|
93
|
+
* @param {number} [options.maxHeight] - Max height in pixels
|
|
90
94
|
* @returns {HTMLElement}
|
|
91
95
|
*/
|
|
92
|
-
|
|
93
|
-
content,
|
|
94
|
-
language = "markdown",
|
|
95
|
-
filename,
|
|
96
|
-
description,
|
|
97
|
-
toHtml,
|
|
98
|
-
minHeight,
|
|
99
|
-
maxHeight,
|
|
100
|
-
}) {
|
|
101
|
-
// Create highlighted code block
|
|
96
|
+
function createCodeBlock({ content, language, minHeight, maxHeight }) {
|
|
102
97
|
const pre = document.createElement("pre");
|
|
103
98
|
pre.className = "code-display";
|
|
104
99
|
if (minHeight) pre.style.minHeight = `${minHeight}px`;
|
|
@@ -108,46 +103,111 @@ export function createCodeDisplay({
|
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
const code = document.createElement("code");
|
|
111
|
-
if (language) {
|
|
112
|
-
code.className = `language-${language}`;
|
|
113
|
-
}
|
|
106
|
+
if (language) code.className = `language-${language}`;
|
|
114
107
|
code.textContent = content;
|
|
115
108
|
pre.appendChild(code);
|
|
116
109
|
|
|
117
|
-
// Apply Prism highlighting if available and language specified
|
|
118
110
|
if (language && typeof Prism !== "undefined") {
|
|
119
111
|
Prism.highlightElement(code);
|
|
120
112
|
}
|
|
121
113
|
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
return pre;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a collapsible code display component with syntax highlighting and copy buttons.
|
|
119
|
+
*
|
|
120
|
+
* Always rendered as a <details>/<summary> element. The filename and copy buttons
|
|
121
|
+
* appear in the summary (always visible). The code block is in the collapsible body
|
|
122
|
+
* and is lazy-rendered on first open.
|
|
123
|
+
*
|
|
124
|
+
* @param {Object} options
|
|
125
|
+
* @param {string} options.content - The code content to display
|
|
126
|
+
* @param {string} [options.language="markdown"] - Language for syntax highlighting
|
|
127
|
+
* @param {string} [options.filename] - Filename to display in summary
|
|
128
|
+
* @param {string} [options.description] - Optional description text shown in body
|
|
129
|
+
* @param {Function} [options.toHtml] - Function to convert content to HTML (enables "Copy as HTML" button)
|
|
130
|
+
* @param {number} [options.minHeight] - Optional minimum height in pixels
|
|
131
|
+
* @param {number} [options.maxHeight] - Optional maximum height in pixels
|
|
132
|
+
* @param {boolean} [options.open=false] - Whether the details element starts open
|
|
133
|
+
* @returns {HTMLDetailsElement}
|
|
134
|
+
*/
|
|
135
|
+
export function createCodeDisplay({
|
|
136
|
+
content,
|
|
137
|
+
language = "markdown",
|
|
138
|
+
filename,
|
|
139
|
+
description,
|
|
140
|
+
toHtml,
|
|
141
|
+
minHeight,
|
|
142
|
+
maxHeight,
|
|
143
|
+
open = false,
|
|
144
|
+
}) {
|
|
145
|
+
const detailsEl = document.createElement("details");
|
|
146
|
+
detailsEl.className = "code-display-pane";
|
|
147
|
+
if (open) detailsEl.open = true;
|
|
148
|
+
|
|
149
|
+
// Build summary: filename (left) + copy buttons (right)
|
|
150
|
+
const summaryEl = document.createElement("summary");
|
|
151
|
+
summaryEl.className = "code-display-summary";
|
|
152
|
+
|
|
124
153
|
if (filename) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
headerLeft.push(p({ className: "text-muted" }, description));
|
|
154
|
+
summaryEl.appendChild(
|
|
155
|
+
span({ className: "code-display-filename" }, filename),
|
|
156
|
+
);
|
|
129
157
|
}
|
|
130
158
|
|
|
131
|
-
// Build buttons
|
|
132
159
|
const buttons = [createCopyButton(content)];
|
|
133
160
|
if (toHtml) {
|
|
134
161
|
buttons.push(createCopyHtmlButton(toHtml(content)));
|
|
135
162
|
}
|
|
163
|
+
summaryEl.appendChild(div({ className: "button-group" }, ...buttons));
|
|
136
164
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
detailsEl.appendChild(summaryEl);
|
|
166
|
+
|
|
167
|
+
// Lazy-render body on first open
|
|
168
|
+
let rendered = false;
|
|
169
|
+
const renderBody = () => {
|
|
170
|
+
if (rendered) return;
|
|
171
|
+
rendered = true;
|
|
172
|
+
|
|
173
|
+
const body = div({ className: "code-display-body" });
|
|
174
|
+
|
|
175
|
+
if (description) {
|
|
176
|
+
body.appendChild(p({ className: "text-muted" }, description));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
body.appendChild(
|
|
180
|
+
createCodeBlock({ content, language, minHeight, maxHeight }),
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
detailsEl.appendChild(body);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (open) {
|
|
187
|
+
renderBody();
|
|
188
|
+
} else {
|
|
189
|
+
detailsEl.addEventListener("toggle", () => {
|
|
190
|
+
if (detailsEl.open) renderBody();
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return detailsEl;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Wire accordion behaviour on an array of <details> elements.
|
|
199
|
+
* Opening one pane closes all others.
|
|
200
|
+
*
|
|
201
|
+
* @param {HTMLDetailsElement[]} panes - Details elements to accordion
|
|
202
|
+
*/
|
|
203
|
+
export function accordionize(panes) {
|
|
204
|
+
for (const pane of panes) {
|
|
205
|
+
pane.addEventListener("toggle", () => {
|
|
206
|
+
if (pane.open) {
|
|
207
|
+
for (const other of panes) {
|
|
208
|
+
if (other !== pane) other.open = false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
153
213
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill File Viewer Component
|
|
3
|
+
*
|
|
4
|
+
* Vertically stacked collapsible panes for skill files: SKILL.md,
|
|
5
|
+
* scripts/install.sh, and references/REFERENCE.md.
|
|
6
|
+
* Reused across agent builder and skill detail pages.
|
|
7
|
+
*
|
|
8
|
+
* Each file is rendered as a collapsible code-display pane with
|
|
9
|
+
* accordion behaviour (only one open at a time).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { div } from "../lib/render.js";
|
|
13
|
+
import { createCodeDisplay, accordionize } from "./code-display.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} SkillFile
|
|
17
|
+
* @property {string} filename - File path for code display header
|
|
18
|
+
* @property {string} content - File content
|
|
19
|
+
* @property {string} [language="markdown"] - Syntax highlighting language
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a stacked skill file viewer component
|
|
24
|
+
*
|
|
25
|
+
* Shows files as vertically stacked collapsible code-display panes.
|
|
26
|
+
* Only one pane is open at a time (accordion).
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} options
|
|
29
|
+
* @param {SkillFile[]} options.files - Array of files to display
|
|
30
|
+
* @param {number} [options.maxHeight=300] - Maximum height for code displays
|
|
31
|
+
* @param {number} [options.openIndex=0] - Initially open pane index (-1 for all closed)
|
|
32
|
+
* @returns {HTMLElement}
|
|
33
|
+
*/
|
|
34
|
+
export function createSkillFileViewer({
|
|
35
|
+
files,
|
|
36
|
+
maxHeight = 300,
|
|
37
|
+
openIndex = 0,
|
|
38
|
+
}) {
|
|
39
|
+
if (files.length === 0) return div();
|
|
40
|
+
|
|
41
|
+
const container = div({ className: "sfv" });
|
|
42
|
+
/** @type {HTMLDetailsElement[]} */
|
|
43
|
+
const panes = [];
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < files.length; i++) {
|
|
46
|
+
const pane = createCodeDisplay({
|
|
47
|
+
content: files[i].content,
|
|
48
|
+
filename: files[i].filename,
|
|
49
|
+
language: files[i].language || "markdown",
|
|
50
|
+
maxHeight,
|
|
51
|
+
open: i === openIndex,
|
|
52
|
+
});
|
|
53
|
+
pane.classList.add("sfv-pane");
|
|
54
|
+
panes.push(pane);
|
|
55
|
+
container.appendChild(pane);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
accordionize(panes);
|
|
59
|
+
|
|
60
|
+
return container;
|
|
61
|
+
}
|
package/src/css/bundles/app.css
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
@import "../components/nav.css" layer(components);
|
|
27
27
|
@import "../components/top-bar.css" layer(components);
|
|
28
28
|
@import "../components/command-prompt.css" layer(components);
|
|
29
|
+
@import "../components/skill-file-viewer.css" layer(components);
|
|
29
30
|
|
|
30
31
|
/* Utilities */
|
|
31
32
|
@import "../components/utilities.css" layer(utilities);
|
|
@@ -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
|
+
}
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
padding: var(--space-md);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
.agent-card-preview .code-display-
|
|
109
|
+
.agent-card-preview .code-display-pane {
|
|
110
110
|
margin: 0;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
padding: var(--space-sm) var(--space-md) var(--space-md);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
.skill-card-preview .code-display-
|
|
149
|
+
.skill-card-preview .code-display-pane {
|
|
150
150
|
margin: 0;
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -154,6 +154,17 @@
|
|
|
154
154
|
max-height: 300px;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/* Skill card file count badge */
|
|
158
|
+
.skill-card-badge {
|
|
159
|
+
font-size: var(--font-size-xs);
|
|
160
|
+
color: var(--color-text-muted);
|
|
161
|
+
background: var(--color-surface);
|
|
162
|
+
border: 1px solid var(--color-border);
|
|
163
|
+
border-radius: var(--radius-sm);
|
|
164
|
+
padding: 0.1em 0.5em;
|
|
165
|
+
white-space: nowrap;
|
|
166
|
+
}
|
|
167
|
+
|
|
157
168
|
.skills-list {
|
|
158
169
|
display: flex;
|
|
159
170
|
flex-direction: column;
|
|
@@ -167,59 +178,6 @@
|
|
|
167
178
|
gap: var(--space-md);
|
|
168
179
|
}
|
|
169
180
|
|
|
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
181
|
/* Stage agent preview */
|
|
224
182
|
.stage-agent-preview .stage-header {
|
|
225
183
|
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
|
/**
|