@alavida/agentpack 0.1.2 → 0.1.4

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.
Files changed (46) hide show
  1. package/README.md +35 -1
  2. package/bin/intent.js +20 -0
  3. package/package.json +15 -5
  4. package/skills/agentpack-cli/SKILL.md +9 -1
  5. package/skills/authoring-skillgraphs-from-knowledge/SKILL.md +148 -0
  6. package/skills/authoring-skillgraphs-from-knowledge/references/authored-metadata.md +6 -0
  7. package/skills/developing-and-testing-skills/SKILL.md +127 -0
  8. package/skills/developing-and-testing-skills/references/local-workbench.md +7 -0
  9. package/skills/getting-started-skillgraphs/SKILL.md +115 -0
  10. package/skills/getting-started-skillgraphs/references/command-routing.md +7 -0
  11. package/skills/identifying-skill-opportunities/SKILL.md +119 -0
  12. package/skills/identifying-skill-opportunities/references/capability-boundaries.md +6 -0
  13. package/skills/maintaining-skillgraph-freshness/SKILL.md +110 -0
  14. package/skills/repairing-broken-skill-or-plugin-state/SKILL.md +116 -0
  15. package/skills/repairing-broken-skill-or-plugin-state/references/diagnostic-flows.md +6 -0
  16. package/skills/shipping-production-plugins-and-packages/SKILL.md +123 -0
  17. package/skills/shipping-production-plugins-and-packages/references/plugin-delivery.md +6 -0
  18. package/skills/sync-state.json +83 -0
  19. package/src/application/skills/build-skill-workbench-model.js +194 -0
  20. package/src/application/skills/run-skill-workbench-action.js +23 -0
  21. package/src/application/skills/start-skill-dev-workbench.js +192 -0
  22. package/src/cli.js +1 -1
  23. package/src/commands/skills.js +34 -10
  24. package/src/dashboard/App.jsx +343 -0
  25. package/src/dashboard/components/Breadcrumbs.jsx +45 -0
  26. package/src/dashboard/components/ControlStrip.jsx +153 -0
  27. package/src/dashboard/components/InspectorPanel.jsx +203 -0
  28. package/src/dashboard/components/SkillGraph.jsx +567 -0
  29. package/src/dashboard/components/Tooltip.jsx +111 -0
  30. package/src/dashboard/dist/dashboard.js +26692 -0
  31. package/src/dashboard/index.html +81 -0
  32. package/src/dashboard/lib/api.js +19 -0
  33. package/src/dashboard/lib/router.js +15 -0
  34. package/src/dashboard/main.jsx +4 -0
  35. package/src/domain/plugins/load-plugin-definition.js +163 -0
  36. package/src/domain/plugins/plugin-diagnostic-error.js +18 -0
  37. package/src/domain/plugins/plugin-requirements.js +15 -0
  38. package/src/domain/skills/skill-graph.js +1 -0
  39. package/src/infrastructure/fs/dev-session-repository.js +25 -0
  40. package/src/infrastructure/runtime/materialize-skills.js +18 -1
  41. package/src/infrastructure/runtime/open-browser.js +20 -0
  42. package/src/infrastructure/runtime/skill-dev-workbench-server.js +96 -0
  43. package/src/infrastructure/runtime/watch-skill-workbench.js +68 -0
  44. package/src/lib/plugins.js +19 -28
  45. package/src/lib/skills.js +245 -16
  46. package/src/utils/errors.js +33 -1
@@ -0,0 +1,343 @@
1
+ import { useEffect, useLayoutEffect, useState, useCallback, useRef } from 'react';
2
+ import { fetchWorkbenchModel, runWorkbenchAction } from './lib/api.js';
3
+ import { getSkillFromHash, setSkillHash, onHashChange } from './lib/router.js';
4
+ import { SkillGraph } from './components/SkillGraph.jsx';
5
+ import { InspectorPanel } from './components/InspectorPanel.jsx';
6
+ import { Breadcrumbs } from './components/Breadcrumbs.jsx';
7
+ import { ControlStrip } from './components/ControlStrip.jsx';
8
+ import { Tooltip } from './components/Tooltip.jsx';
9
+
10
+ export function App() {
11
+ const [model, setModel] = useState(null);
12
+ const [selectedId, setSelectedId] = useState(null);
13
+ const [error, setError] = useState(null);
14
+ const [loading, setLoading] = useState(true);
15
+ const [busyAction, setBusyAction] = useState(null);
16
+ const [labelsVisible, setLabelsVisible] = useState(true);
17
+ const [knowledgeVisible, setKnowledgeVisible] = useState(true);
18
+ const [resetZoomSignal, setResetZoomSignal] = useState(0);
19
+ const [lightMode, setLightMode] = useState(false);
20
+ const [trail, setTrail] = useState([]);
21
+ const [tooltipNode, setTooltipNode] = useState(null);
22
+ const [tooltipPos, setTooltipPos] = useState(null);
23
+ const inspectedNode = model?.nodes.find((n) => n.id === selectedId) || null;
24
+
25
+ useLayoutEffect(() => {
26
+ document.documentElement.setAttribute('data-theme', lightMode ? 'light' : 'dark');
27
+ }, [lightMode]);
28
+
29
+ const loadModel = useCallback(async (skillPackageName) => {
30
+ try {
31
+ setLoading(true);
32
+ setError(null);
33
+ const nextModel = await fetchWorkbenchModel(skillPackageName);
34
+ setModel(nextModel);
35
+ setSelectedId(null);
36
+ return nextModel;
37
+ } catch (err) {
38
+ setError(err.message);
39
+ return null;
40
+ } finally {
41
+ setLoading(false);
42
+ }
43
+ }, []);
44
+
45
+ // Initial load
46
+ useEffect(() => {
47
+ const hashSkill = getSkillFromHash();
48
+ loadModel(hashSkill).then((m) => {
49
+ if (m) {
50
+ const entry = { packageName: m.selected.id, name: m.selected.name };
51
+ if (hashSkill) {
52
+ setTrail([entry]);
53
+ } else {
54
+ setTrail([entry]);
55
+ setSkillHash(m.selected.id);
56
+ }
57
+ }
58
+ });
59
+ }, [loadModel]);
60
+
61
+ // Hash change listener
62
+ useEffect(() => {
63
+ onHashChange((skillPackageName) => {
64
+ if (skillPackageName) {
65
+ loadModel(skillPackageName).then((m) => {
66
+ if (m) {
67
+ setTrail((prev) => {
68
+ const existingIndex = prev.findIndex((e) => e.packageName === skillPackageName);
69
+ if (existingIndex >= 0) {
70
+ return prev.slice(0, existingIndex + 1);
71
+ }
72
+ return [...prev, { packageName: m.selected.id, name: m.selected.name }];
73
+ });
74
+ }
75
+ });
76
+ }
77
+ });
78
+ }, [loadModel]);
79
+
80
+ function navigateToSkill(packageName) {
81
+ setSkillHash(packageName);
82
+ }
83
+
84
+ function handleBreadcrumbNavigate(packageName, index) {
85
+ setTrail((prev) => prev.slice(0, index + 1));
86
+ setSkillHash(packageName);
87
+ }
88
+
89
+ async function handleAction(action) {
90
+ if (action === 'reset-zoom') {
91
+ setResetZoomSignal((s) => s + 1);
92
+ return;
93
+ }
94
+
95
+ if (action === 'refresh') {
96
+ const hashSkill = getSkillFromHash();
97
+ setBusyAction('refresh');
98
+ await loadModel(hashSkill);
99
+ setBusyAction(null);
100
+ return;
101
+ }
102
+
103
+ try {
104
+ setBusyAction(action);
105
+ await runWorkbenchAction(action);
106
+ } catch (err) {
107
+ setError(err.message);
108
+ } finally {
109
+ setBusyAction(null);
110
+ }
111
+ }
112
+
113
+ const handleHover = useCallback((node, pos) => {
114
+ setTooltipNode(node);
115
+ setTooltipPos(pos);
116
+ }, []);
117
+
118
+ const handleHoverEnd = useCallback(() => {
119
+ setTooltipNode(null);
120
+ setTooltipPos(null);
121
+ }, []);
122
+
123
+ const handleGraphClick = useCallback((nodeId) => {
124
+ setSelectedId((prev) => prev === nodeId ? null : nodeId);
125
+ }, []);
126
+
127
+ return (
128
+ <>
129
+ {/* Header */}
130
+ <header style={{
131
+ padding: '20px 40px 0',
132
+ display: 'flex',
133
+ justifyContent: 'space-between',
134
+ alignItems: 'flex-start',
135
+ flexShrink: 0,
136
+ }}>
137
+ <div>
138
+ <div style={{
139
+ fontFamily: 'var(--font-mono)',
140
+ fontVariant: 'small-caps',
141
+ textTransform: 'uppercase',
142
+ letterSpacing: 3,
143
+ fontSize: 9,
144
+ color: 'var(--text-dim)',
145
+ marginBottom: 4,
146
+ }}>
147
+ Agentpack
148
+ </div>
149
+ <div style={{
150
+ fontFamily: 'var(--font-body)',
151
+ fontSize: 28,
152
+ fontWeight: 400,
153
+ fontStyle: 'italic',
154
+ color: 'var(--text)',
155
+ }}>
156
+ Skill Graph
157
+ </div>
158
+ <hr style={{
159
+ width: 36,
160
+ height: 1,
161
+ background: 'var(--status-current)',
162
+ border: 'none',
163
+ marginTop: 10,
164
+ }} />
165
+ </div>
166
+ <div style={{
167
+ display: 'flex',
168
+ gap: 20,
169
+ alignItems: 'center',
170
+ }}>
171
+ <LegendItem color="var(--edge-provenance)" label="Source" shape="diamond" />
172
+ <LegendItem color="#c45454" label="Changed" shape="diamond" />
173
+ <LegendItem color="var(--status-current)" label="Current" shape="dot" />
174
+ <LegendItem color="var(--status-stale)" label="Stale" shape="dot" />
175
+ <LegendItem color="var(--status-affected)" label="Affected" shape="ring" />
176
+ <LegendItem color="var(--edge-requires)" label="Requires" shape="line" />
177
+ <LegendItem color="var(--edge-provenance)" label="Provenance" shape="dashed" />
178
+ </div>
179
+ </header>
180
+
181
+ {/* Breadcrumbs */}
182
+ <Breadcrumbs trail={trail} onNavigate={handleBreadcrumbNavigate} />
183
+
184
+ {/* Main area */}
185
+ <div style={{ flex: 1, position: 'relative', minHeight: 0 }}>
186
+ {loading && !model && (
187
+ <div style={{
188
+ display: 'flex',
189
+ alignItems: 'center',
190
+ justifyContent: 'center',
191
+ height: '100%',
192
+ fontFamily: 'var(--font-mono)',
193
+ fontSize: 12,
194
+ color: 'var(--text-dim)',
195
+ }}>
196
+ Loading...
197
+ </div>
198
+ )}
199
+
200
+ {error && (
201
+ <div style={{
202
+ display: 'flex',
203
+ flexDirection: 'column',
204
+ alignItems: 'center',
205
+ justifyContent: 'center',
206
+ height: '100%',
207
+ gap: 16,
208
+ }}>
209
+ <div style={{
210
+ fontFamily: 'var(--font-body)',
211
+ fontSize: 18,
212
+ fontStyle: 'italic',
213
+ color: 'var(--status-stale)',
214
+ }}>
215
+ {error}
216
+ </div>
217
+ <button
218
+ type="button"
219
+ onClick={() => handleAction('refresh')}
220
+ style={{
221
+ fontFamily: 'var(--font-mono)',
222
+ fontSize: 11,
223
+ background: 'var(--surface)',
224
+ border: '1px solid var(--border-bright)',
225
+ color: 'var(--text-dim)',
226
+ padding: '8px 16px',
227
+ cursor: 'pointer',
228
+ letterSpacing: '0.04em',
229
+ }}
230
+ >
231
+ Retry
232
+ </button>
233
+ </div>
234
+ )}
235
+
236
+ {model && !error && (
237
+ <SkillGraph
238
+ model={model}
239
+ selectedId={selectedId}
240
+ onSelect={handleGraphClick}
241
+ onHover={handleHover}
242
+ onHoverEnd={handleHoverEnd}
243
+ labelsVisible={labelsVisible}
244
+ knowledgeVisible={knowledgeVisible}
245
+ resetZoomSignal={resetZoomSignal}
246
+ />
247
+ )}
248
+
249
+ {model && model.nodes.length === 1 && model.nodes[0].type === 'skill' && (
250
+ <div style={{
251
+ position: 'absolute',
252
+ bottom: 80,
253
+ left: '50%',
254
+ transform: 'translateX(-50%)',
255
+ fontFamily: 'var(--font-body)',
256
+ fontSize: 14,
257
+ fontStyle: 'italic',
258
+ color: 'var(--text-faint)',
259
+ }}>
260
+ No dependencies or sources found.
261
+ </div>
262
+ )}
263
+ </div>
264
+
265
+ {/* Tooltip */}
266
+ <Tooltip node={tooltipNode} position={tooltipPos} />
267
+
268
+ {/* Inspector Panel */}
269
+ <InspectorPanel
270
+ node={inspectedNode}
271
+ onClose={() => setSelectedId(null)}
272
+ onNavigate={navigateToSkill}
273
+ />
274
+
275
+ {/* Control Strip */}
276
+ <ControlStrip
277
+ onAction={handleAction}
278
+ busyAction={busyAction}
279
+ labelsVisible={labelsVisible}
280
+ onToggleLabels={() => setLabelsVisible((v) => !v)}
281
+ knowledgeVisible={knowledgeVisible}
282
+ onToggleKnowledge={() => setKnowledgeVisible((v) => !v)}
283
+ lightMode={lightMode}
284
+ onToggleTheme={() => setLightMode((v) => !v)}
285
+ />
286
+ </>
287
+ );
288
+ }
289
+
290
+ function LegendItem({ color, label, shape }) {
291
+ return (
292
+ <div style={{
293
+ display: 'flex',
294
+ alignItems: 'center',
295
+ gap: 6,
296
+ fontFamily: 'var(--font-mono)',
297
+ fontSize: 10,
298
+ color: 'var(--text-dim)',
299
+ letterSpacing: '0.04em',
300
+ }}>
301
+ {shape === 'dot' && (
302
+ <div style={{
303
+ width: 10,
304
+ height: 10,
305
+ borderRadius: '50%',
306
+ background: color,
307
+ }} />
308
+ )}
309
+ {shape === 'ring' && (
310
+ <div style={{
311
+ width: 10,
312
+ height: 10,
313
+ borderRadius: '50%',
314
+ background: 'transparent',
315
+ border: `1.5px solid ${color}`,
316
+ }} />
317
+ )}
318
+ {shape === 'diamond' && (
319
+ <svg width="12" height="12" viewBox="-6 -6 12 12" style={{ display: 'block' }}>
320
+ <path d="M0,-5 L5,0 L0,5 L-5,0 Z" fill={color} opacity={0.6} />
321
+ </svg>
322
+ )}
323
+ {shape === 'line' && (
324
+ <div style={{
325
+ width: 20,
326
+ height: 2,
327
+ borderRadius: 1,
328
+ background: color,
329
+ opacity: 0.6,
330
+ }} />
331
+ )}
332
+ {shape === 'dashed' && (
333
+ <div style={{
334
+ width: 20,
335
+ height: 0,
336
+ borderTop: `2px dashed ${color}`,
337
+ opacity: 0.5,
338
+ }} />
339
+ )}
340
+ {label}
341
+ </div>
342
+ );
343
+ }
@@ -0,0 +1,45 @@
1
+ export function Breadcrumbs({ trail, onNavigate }) {
2
+ if (!trail || trail.length === 0) return null;
3
+
4
+ return (
5
+ <nav style={{
6
+ display: 'flex',
7
+ alignItems: 'center',
8
+ gap: 6,
9
+ fontFamily: 'var(--font-mono)',
10
+ fontSize: 11,
11
+ letterSpacing: '0.02em',
12
+ color: 'var(--text-dim)',
13
+ padding: '0 40px',
14
+ height: 32,
15
+ borderBottom: '1px solid var(--border)',
16
+ flexShrink: 0,
17
+ }}>
18
+ {trail.map((entry, index) => {
19
+ const isLast = index === trail.length - 1;
20
+ return (
21
+ <span key={entry.packageName} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
22
+ {index > 0 && <span style={{ color: 'var(--text-faint)' }}>›</span>}
23
+ {isLast ? (
24
+ <span style={{ color: 'var(--text)' }}>
25
+ {entry.name || entry.packageName}
26
+ </span>
27
+ ) : (
28
+ <span
29
+ onClick={() => onNavigate(entry.packageName, index)}
30
+ style={{
31
+ cursor: 'pointer',
32
+ transition: 'color 200ms ease',
33
+ }}
34
+ onMouseEnter={(e) => { e.target.style.color = 'var(--text)'; }}
35
+ onMouseLeave={(e) => { e.target.style.color = 'var(--text-dim)'; }}
36
+ >
37
+ {entry.name || entry.packageName}
38
+ </span>
39
+ )}
40
+ </span>
41
+ );
42
+ })}
43
+ </nav>
44
+ );
45
+ }
@@ -0,0 +1,153 @@
1
+ const BUTTON_STYLE = {
2
+ fontFamily: 'var(--font-mono)',
3
+ fontSize: 11,
4
+ background: 'var(--surface)',
5
+ border: '1px solid var(--border-bright)',
6
+ color: 'var(--text-dim)',
7
+ padding: '8px 16px',
8
+ cursor: 'pointer',
9
+ transition: 'all 200ms ease',
10
+ letterSpacing: '0.04em',
11
+ };
12
+
13
+ export function ControlStrip({ onAction, busyAction, labelsVisible, onToggleLabels, knowledgeVisible, onToggleKnowledge, lightMode, onToggleTheme }) {
14
+ return (
15
+ <div style={{
16
+ position: 'fixed',
17
+ bottom: 28,
18
+ left: 40,
19
+ display: 'flex',
20
+ gap: 8,
21
+ zIndex: 10,
22
+ }}>
23
+ <button
24
+ type="button"
25
+ style={BUTTON_STYLE}
26
+ onClick={() => onAction('reset-zoom')}
27
+ onMouseEnter={(e) => {
28
+ e.target.style.color = 'var(--text)';
29
+ e.target.style.borderColor = 'var(--status-current)';
30
+ }}
31
+ onMouseLeave={(e) => {
32
+ e.target.style.color = 'var(--text-dim)';
33
+ e.target.style.borderColor = 'var(--border-bright)';
34
+ }}
35
+ >
36
+ Reset
37
+ </button>
38
+ <button
39
+ type="button"
40
+ style={BUTTON_STYLE}
41
+ disabled={Boolean(busyAction)}
42
+ onClick={() => onAction('validate-skill')}
43
+ onMouseEnter={(e) => {
44
+ e.target.style.color = 'var(--text)';
45
+ e.target.style.borderColor = 'var(--status-current)';
46
+ }}
47
+ onMouseLeave={(e) => {
48
+ e.target.style.color = 'var(--text-dim)';
49
+ e.target.style.borderColor = 'var(--border-bright)';
50
+ }}
51
+ >
52
+ {busyAction === 'validate-skill' ? 'Validating...' : 'Validate'}
53
+ </button>
54
+ <button
55
+ type="button"
56
+ style={BUTTON_STYLE}
57
+ disabled={Boolean(busyAction)}
58
+ onClick={() => onAction('refresh')}
59
+ onMouseEnter={(e) => {
60
+ e.target.style.color = 'var(--text)';
61
+ e.target.style.borderColor = 'var(--status-current)';
62
+ }}
63
+ onMouseLeave={(e) => {
64
+ e.target.style.color = 'var(--text-dim)';
65
+ e.target.style.borderColor = 'var(--border-bright)';
66
+ }}
67
+ >
68
+ {busyAction === 'refresh' ? 'Refreshing...' : 'Refresh'}
69
+ </button>
70
+ <button
71
+ type="button"
72
+ style={{
73
+ ...BUTTON_STYLE,
74
+ ...(labelsVisible ? {} : {
75
+ color: 'var(--status-current)',
76
+ borderColor: 'var(--status-current)',
77
+ background: 'rgba(143, 166, 126, 0.08)',
78
+ }),
79
+ }}
80
+ onClick={onToggleLabels}
81
+ onMouseEnter={(e) => {
82
+ e.target.style.color = 'var(--text)';
83
+ e.target.style.borderColor = 'var(--status-current)';
84
+ }}
85
+ onMouseLeave={(e) => {
86
+ if (labelsVisible) {
87
+ e.target.style.color = 'var(--text-dim)';
88
+ e.target.style.borderColor = 'var(--border-bright)';
89
+ } else {
90
+ e.target.style.color = 'var(--status-current)';
91
+ e.target.style.borderColor = 'var(--status-current)';
92
+ }
93
+ }}
94
+ >
95
+ {labelsVisible ? 'Hide labels' : 'Show labels'}
96
+ </button>
97
+ <button
98
+ type="button"
99
+ style={{
100
+ ...BUTTON_STYLE,
101
+ ...(knowledgeVisible ? {
102
+ color: 'var(--edge-provenance)',
103
+ borderColor: 'var(--edge-provenance)',
104
+ background: 'rgba(122, 154, 187, 0.08)',
105
+ } : {}),
106
+ }}
107
+ onClick={onToggleKnowledge}
108
+ onMouseEnter={(e) => {
109
+ e.target.style.color = 'var(--text)';
110
+ e.target.style.borderColor = 'var(--edge-provenance)';
111
+ }}
112
+ onMouseLeave={(e) => {
113
+ if (knowledgeVisible) {
114
+ e.target.style.color = 'var(--edge-provenance)';
115
+ e.target.style.borderColor = 'var(--edge-provenance)';
116
+ } else {
117
+ e.target.style.color = 'var(--text-dim)';
118
+ e.target.style.borderColor = 'var(--border-bright)';
119
+ }
120
+ }}
121
+ >
122
+ Knowledge
123
+ </button>
124
+ <button
125
+ type="button"
126
+ style={{
127
+ ...BUTTON_STYLE,
128
+ ...(lightMode ? {
129
+ color: 'var(--accent)',
130
+ borderColor: 'var(--accent)',
131
+ background: 'rgba(196, 138, 32, 0.08)',
132
+ } : {}),
133
+ }}
134
+ onClick={onToggleTheme}
135
+ onMouseEnter={(e) => {
136
+ e.target.style.color = 'var(--text)';
137
+ e.target.style.borderColor = 'var(--accent)';
138
+ }}
139
+ onMouseLeave={(e) => {
140
+ if (lightMode) {
141
+ e.target.style.color = 'var(--accent)';
142
+ e.target.style.borderColor = 'var(--accent)';
143
+ } else {
144
+ e.target.style.color = 'var(--text-dim)';
145
+ e.target.style.borderColor = 'var(--border-bright)';
146
+ }
147
+ }}
148
+ >
149
+ {lightMode ? 'Dark' : 'Light'}
150
+ </button>
151
+ </div>
152
+ );
153
+ }