@drisp/cli 0.4.1 → 0.4.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.
- package/dist/{WorkflowInstallWizard-5JWVHIVJ.js → WorkflowInstallWizard-2MC5A7W4.js} +5 -3
- package/dist/athena-gateway.js +928 -974
- package/dist/chunk-2HR7FV3M.js +502 -0
- package/dist/{chunk-3FVULBV4.js → chunk-4CRZXLIP.js} +53 -117
- package/dist/{chunk-LPG5WBPV.js → chunk-5VK2ZMVV.js} +106 -629
- package/dist/chunk-6TJHAUNB.js +161 -0
- package/dist/{chunk-PSD3WBN4.js → chunk-BTKQ67RE.js} +1 -1
- package/dist/chunk-HXBCZAP7.js +1 -0
- package/dist/chunk-JAPBSL7D.js +12898 -0
- package/dist/cli.js +7000 -17642
- package/dist/dashboard-daemon.js +255 -0
- package/dist/hook-forwarder.js +2 -1
- package/package.json +5 -2
|
@@ -1,312 +1,4 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
var darkTheme = {
|
|
3
|
-
name: "dark",
|
|
4
|
-
// ── Text hierarchy ──────────────────────────────────────
|
|
5
|
-
text: "#c9d1d9",
|
|
6
|
-
// Primary text. Warm gray, not pure white.
|
|
7
|
-
textMuted: "#848d97",
|
|
8
|
-
// Dim text. Bumped from #6e7681 for AA compliance (~4.8:1).
|
|
9
|
-
textInverse: "#0d1117",
|
|
10
|
-
// Text on colored backgrounds.
|
|
11
|
-
// ── Accent ──────────────────────────────────────────────
|
|
12
|
-
border: "#2d333b",
|
|
13
|
-
// Bumped from #1e2a38 for visibility (~2.2:1 vs bg).
|
|
14
|
-
accent: "#58a6ff",
|
|
15
|
-
// Blue. Focus bar, branding, selection, links.
|
|
16
|
-
accentSecondary: "#bc8cff",
|
|
17
|
-
// Soft purple. Permission events.
|
|
18
|
-
// ── Status ──────────────────────────────────────────────
|
|
19
|
-
status: {
|
|
20
|
-
success: "#3fb950",
|
|
21
|
-
// Green. Completion, done glyphs.
|
|
22
|
-
error: "#f85149",
|
|
23
|
-
// Red. Failures, blocks, Tool Fail.
|
|
24
|
-
warning: "#d29922",
|
|
25
|
-
// Amber. Active stage, zero-result tint, caution.
|
|
26
|
-
info: "#58a6ff",
|
|
27
|
-
// Blue. Agent messages, Run OK.
|
|
28
|
-
working: "#d29922",
|
|
29
|
-
// Amber. Spinner state (same as warning).
|
|
30
|
-
neutral: "#9da5ae"
|
|
31
|
-
// Mid gray. Bumped from #8b949e for AA (~4.8:1).
|
|
32
|
-
},
|
|
33
|
-
// ── Context bar ─────────────────────────────────────────
|
|
34
|
-
contextBar: {
|
|
35
|
-
track: "#1e2a38",
|
|
36
|
-
// Empty segment track.
|
|
37
|
-
low: "#3fb950",
|
|
38
|
-
// Green. 0–50% budget used.
|
|
39
|
-
medium: "#d29922",
|
|
40
|
-
// Amber. 50–80% budget used.
|
|
41
|
-
high: "#f85149"
|
|
42
|
-
// Red. 80–100% budget used.
|
|
43
|
-
},
|
|
44
|
-
// ── Dialog borders ──────────────────────────────────────
|
|
45
|
-
dialog: {
|
|
46
|
-
borderPermission: "#d29922",
|
|
47
|
-
// Amber border for permission prompts.
|
|
48
|
-
borderQuestion: "#58a6ff"
|
|
49
|
-
// Blue border for question prompts.
|
|
50
|
-
},
|
|
51
|
-
// ── Input ───────────────────────────────────────────────
|
|
52
|
-
inputPrompt: "#388bfd",
|
|
53
|
-
// Blue "input" keyword in prompt.
|
|
54
|
-
inputChevron: "#484f58",
|
|
55
|
-
// Bumped from #30363d for visibility (~2.8:1).
|
|
56
|
-
inputBackground: "#141a21",
|
|
57
|
-
// Slight lift so the input row reads as its own surface.
|
|
58
|
-
// ── Feed ────────────────────────────────────────────────
|
|
59
|
-
feed: {
|
|
60
|
-
headerLabel: "#848d97",
|
|
61
|
-
// Matches textMuted.
|
|
62
|
-
stripeBackground: "#161b22",
|
|
63
|
-
// Bumped from #0d1521 for visible alternation.
|
|
64
|
-
focusBackground: "#1b2a3f"
|
|
65
|
-
// Blue tint for focused row.
|
|
66
|
-
},
|
|
67
|
-
// ── User messages ───────────────────────────────────────
|
|
68
|
-
userMessage: {
|
|
69
|
-
text: "#6e7681",
|
|
70
|
-
// Dimmed text for user messages in the message panel.
|
|
71
|
-
background: "#151d2b",
|
|
72
|
-
// Blue-tinted lift — differentiates user turns from agent.
|
|
73
|
-
border: "#30363d",
|
|
74
|
-
// Subtle border.
|
|
75
|
-
agentBorder: "#2a4a6a",
|
|
76
|
-
// Steel blue left border for agent messages.
|
|
77
|
-
focusBorder: "#58a6ff"
|
|
78
|
-
// Accent blue for focused message indicator.
|
|
79
|
-
},
|
|
80
|
-
// ── Badges ──────────────────────────────────────────────
|
|
81
|
-
badge: {
|
|
82
|
-
error: { bg: "#4b1014", fg: "#ff7b72" },
|
|
83
|
-
running: { bg: "#4a3a0c", fg: "#fbbf24" },
|
|
84
|
-
idle: { bg: "#10321d", fg: "#3fb950" },
|
|
85
|
-
search: { bg: "#1b2a3f" },
|
|
86
|
-
command: { bg: "#2a1b3f" }
|
|
87
|
-
},
|
|
88
|
-
// ── Detail view ─────────────────────────────────────────
|
|
89
|
-
detail: {
|
|
90
|
-
title: "#c9d1d9",
|
|
91
|
-
subject: "#58a6ff"
|
|
92
|
-
},
|
|
93
|
-
// ── Tool pills ──────────────────────────────────────────
|
|
94
|
-
toolPill: {
|
|
95
|
-
safe: { bg: "#0e2233", fg: "#5ba3cc" },
|
|
96
|
-
mutating: { bg: "#2a1d0a", fg: "#d4a44a" },
|
|
97
|
-
browser: { bg: "#0b2625", fg: "#5cc4ba" },
|
|
98
|
-
neutral: { bg: "#141a22", fg: "#7d8590" },
|
|
99
|
-
skill: { bg: "#2a0f24", fg: "#c98ab8" },
|
|
100
|
-
"subagent.spawn": { bg: "#0a2e22", fg: "#5cc4a0" },
|
|
101
|
-
"subagent.return": { bg: "#0a2e22", fg: "#56b492" }
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
var lightTheme = {
|
|
105
|
-
name: "light",
|
|
106
|
-
// ── Text hierarchy ──────────────────────────────────────
|
|
107
|
-
text: "#1f2328",
|
|
108
|
-
// Near-black. Strong contrast.
|
|
109
|
-
textMuted: "#656d76",
|
|
110
|
-
// Medium gray. Same role as dark textMuted.
|
|
111
|
-
textInverse: "#ffffff",
|
|
112
|
-
// White on colored backgrounds.
|
|
113
|
-
// ── Accent ──────────────────────────────────────────────
|
|
114
|
-
border: "#8c959f",
|
|
115
|
-
// Darker neutral border so frame lines stay visible on white.
|
|
116
|
-
accent: "#0969da",
|
|
117
|
-
// Darker blue for light backgrounds.
|
|
118
|
-
accentSecondary: "#8250df",
|
|
119
|
-
// Purple, darkened for readability.
|
|
120
|
-
// ── Status ──────────────────────────────────────────────
|
|
121
|
-
status: {
|
|
122
|
-
success: "#1a7f37",
|
|
123
|
-
// Dark green. Readable on white.
|
|
124
|
-
error: "#cf222e",
|
|
125
|
-
// Dark red.
|
|
126
|
-
warning: "#9a6700",
|
|
127
|
-
// Dark amber.
|
|
128
|
-
info: "#0969da",
|
|
129
|
-
// Dark blue. Matches accent.
|
|
130
|
-
working: "#9a6700",
|
|
131
|
-
// Dark amber.
|
|
132
|
-
neutral: "#656d76"
|
|
133
|
-
// Mid gray.
|
|
134
|
-
},
|
|
135
|
-
// ── Context bar ─────────────────────────────────────────
|
|
136
|
-
contextBar: {
|
|
137
|
-
track: "#d0d7de",
|
|
138
|
-
// Light gray track.
|
|
139
|
-
low: "#1a7f37",
|
|
140
|
-
medium: "#9a6700",
|
|
141
|
-
high: "#cf222e"
|
|
142
|
-
},
|
|
143
|
-
// ── Dialog borders ──────────────────────────────────────
|
|
144
|
-
dialog: {
|
|
145
|
-
borderPermission: "#9a6700",
|
|
146
|
-
borderQuestion: "#0969da"
|
|
147
|
-
},
|
|
148
|
-
// ── Input ───────────────────────────────────────────────
|
|
149
|
-
inputPrompt: "#0969da",
|
|
150
|
-
inputChevron: "#656d76",
|
|
151
|
-
inputBackground: "#f3f6f9",
|
|
152
|
-
// ── Feed ────────────────────────────────────────────────
|
|
153
|
-
feed: {
|
|
154
|
-
headerLabel: "#656d76",
|
|
155
|
-
stripeBackground: "#f6f8fa",
|
|
156
|
-
focusBackground: "#ddf4ff"
|
|
157
|
-
// Light blue tint for focused row.
|
|
158
|
-
},
|
|
159
|
-
// ── User messages ───────────────────────────────────────
|
|
160
|
-
userMessage: {
|
|
161
|
-
text: "#8b949e",
|
|
162
|
-
// Dimmed text for user messages in the message panel.
|
|
163
|
-
background: "#edf2fb",
|
|
164
|
-
// Blue-tinted lift — differentiates user turns from agent.
|
|
165
|
-
border: "#8c959f",
|
|
166
|
-
agentBorder: "#6cb0e0",
|
|
167
|
-
// Medium blue on white bg.
|
|
168
|
-
focusBorder: "#0969da"
|
|
169
|
-
// Accent blue for focused message indicator.
|
|
170
|
-
},
|
|
171
|
-
// ── Badges ──────────────────────────────────────────────
|
|
172
|
-
badge: {
|
|
173
|
-
error: { bg: "#ffebe9", fg: "#cf222e" },
|
|
174
|
-
running: { bg: "#fff8c5", fg: "#9a6700" },
|
|
175
|
-
idle: { bg: "#dafbe1", fg: "#1a7f37" },
|
|
176
|
-
search: { bg: "#ddf4ff" },
|
|
177
|
-
command: { bg: "#fbefff" }
|
|
178
|
-
},
|
|
179
|
-
// ── Detail view ─────────────────────────────────────────
|
|
180
|
-
detail: {
|
|
181
|
-
title: "#1f2328",
|
|
182
|
-
subject: "#0969da"
|
|
183
|
-
},
|
|
184
|
-
// ── Tool pills ──────────────────────────────────────────
|
|
185
|
-
toolPill: {
|
|
186
|
-
safe: { bg: "#ddf4ff", fg: "#0550ae" },
|
|
187
|
-
mutating: { bg: "#fff8c5", fg: "#7c5200" },
|
|
188
|
-
browser: { bg: "#dafbe1", fg: "#116329" },
|
|
189
|
-
neutral: { bg: "#f6f8fa", fg: "#424a53" },
|
|
190
|
-
skill: { bg: "#fbefff", fg: "#6639ba" },
|
|
191
|
-
"subagent.spawn": { bg: "#dafbe1", fg: "#116329" },
|
|
192
|
-
"subagent.return": { bg: "#dafbe1", fg: "#1a7f37" }
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
var highContrastTheme = {
|
|
196
|
-
name: "high-contrast",
|
|
197
|
-
// ── Text hierarchy ──────────────────────────────────────
|
|
198
|
-
text: "#f0f6fc",
|
|
199
|
-
// Near-white. Maximum brightness.
|
|
200
|
-
textMuted: "#9ea7b3",
|
|
201
|
-
// Bumped from #7d8590 for better contrast (~5.2:1).
|
|
202
|
-
textInverse: "#010409",
|
|
203
|
-
// Near-black.
|
|
204
|
-
// ── Accent ──────────────────────────────────────────────
|
|
205
|
-
border: "#71b7ff",
|
|
206
|
-
accent: "#71b7ff",
|
|
207
|
-
// Brighter blue. Punches through.
|
|
208
|
-
accentSecondary: "#d2a8ff",
|
|
209
|
-
// Bright purple.
|
|
210
|
-
// ── Status ──────────────────────────────────────────────
|
|
211
|
-
status: {
|
|
212
|
-
success: "#56d364",
|
|
213
|
-
// Bright green. Higher saturation.
|
|
214
|
-
error: "#ff7b72",
|
|
215
|
-
// Bright red. Softened for readability.
|
|
216
|
-
warning: "#e3b341",
|
|
217
|
-
// Bright amber.
|
|
218
|
-
info: "#71b7ff",
|
|
219
|
-
// Bright blue.
|
|
220
|
-
working: "#e3b341",
|
|
221
|
-
// Bright amber.
|
|
222
|
-
neutral: "#b1bac4"
|
|
223
|
-
// Bumped from #9ea7b3 for HC (~6.0:1).
|
|
224
|
-
},
|
|
225
|
-
// ── Context bar ─────────────────────────────────────────
|
|
226
|
-
contextBar: {
|
|
227
|
-
track: "#3d444d",
|
|
228
|
-
// HC track — brighter than dark.
|
|
229
|
-
low: "#56d364",
|
|
230
|
-
medium: "#e3b341",
|
|
231
|
-
high: "#ff7b72"
|
|
232
|
-
},
|
|
233
|
-
// ── Dialog borders ──────────────────────────────────────
|
|
234
|
-
dialog: {
|
|
235
|
-
borderPermission: "#e3b341",
|
|
236
|
-
borderQuestion: "#71b7ff"
|
|
237
|
-
},
|
|
238
|
-
// ── Input ───────────────────────────────────────────────
|
|
239
|
-
inputPrompt: "#71b7ff",
|
|
240
|
-
inputChevron: "#9ea7b3",
|
|
241
|
-
// Bumped from #7d8590 for visibility.
|
|
242
|
-
inputBackground: "#16202b",
|
|
243
|
-
// ── Feed ────────────────────────────────────────────────
|
|
244
|
-
feed: {
|
|
245
|
-
headerLabel: "#9ea7b3",
|
|
246
|
-
// Matches textMuted.
|
|
247
|
-
stripeBackground: "#0d1521",
|
|
248
|
-
// Bumped from #0b141f for visible alternation.
|
|
249
|
-
focusBackground: "#1a3350"
|
|
250
|
-
// Brighter blue tint for HC.
|
|
251
|
-
},
|
|
252
|
-
// ── User messages ───────────────────────────────────────
|
|
253
|
-
userMessage: {
|
|
254
|
-
text: "#7d8590",
|
|
255
|
-
// Dimmed text for user messages in the message panel.
|
|
256
|
-
background: "#1a2436",
|
|
257
|
-
// Blue-tinted lift — differentiates user turns from agent.
|
|
258
|
-
border: "#444c56",
|
|
259
|
-
// Bumped from #3d444d for HC visibility.
|
|
260
|
-
agentBorder: "#5599cc",
|
|
261
|
-
// Brighter blue for HC.
|
|
262
|
-
focusBorder: "#71b7ff"
|
|
263
|
-
// Accent blue for focused message indicator.
|
|
264
|
-
},
|
|
265
|
-
// ── Badges ──────────────────────────────────────────────
|
|
266
|
-
badge: {
|
|
267
|
-
error: { bg: "#6e1b16", fg: "#ff7b72" },
|
|
268
|
-
running: { bg: "#5c4813", fg: "#e3b341" },
|
|
269
|
-
idle: { bg: "#154228", fg: "#56d364" },
|
|
270
|
-
search: { bg: "#1a3350" },
|
|
271
|
-
command: { bg: "#301e50" }
|
|
272
|
-
},
|
|
273
|
-
// ── Detail view ─────────────────────────────────────────
|
|
274
|
-
detail: {
|
|
275
|
-
title: "#f0f6fc",
|
|
276
|
-
subject: "#71b7ff"
|
|
277
|
-
},
|
|
278
|
-
// ── Tool pills ──────────────────────────────────────────
|
|
279
|
-
toolPill: {
|
|
280
|
-
safe: { bg: "#122d42", fg: "#a2d2ff" },
|
|
281
|
-
mutating: { bg: "#3a2e10", fg: "#f0d070" },
|
|
282
|
-
browser: { bg: "#0f3520", fg: "#7ee28b" },
|
|
283
|
-
neutral: { bg: "#1c2128", fg: "#b1bac4" },
|
|
284
|
-
skill: { bg: "#2b1a40", fg: "#e0c0ff" },
|
|
285
|
-
"subagent.spawn": { bg: "#0f3520", fg: "#7ee28b" },
|
|
286
|
-
"subagent.return": { bg: "#0f3520", fg: "#6bcc79" }
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
var THEMES = {
|
|
290
|
-
dark: darkTheme,
|
|
291
|
-
light: lightTheme,
|
|
292
|
-
"high-contrast": highContrastTheme
|
|
293
|
-
};
|
|
294
|
-
function resolveTheme(name) {
|
|
295
|
-
if (name && name in THEMES) {
|
|
296
|
-
return THEMES[name];
|
|
297
|
-
}
|
|
298
|
-
return darkTheme;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// src/ui/theme/ThemeContext.tsx
|
|
302
|
-
import { createContext, useContext } from "react";
|
|
303
|
-
var ThemeContext = createContext(darkTheme);
|
|
304
|
-
var ThemeProvider = ThemeContext.Provider;
|
|
305
|
-
function useTheme() {
|
|
306
|
-
return useContext(ThemeContext);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// src/infra/plugins/workflowSourceResolution.ts
|
|
1
|
+
// src/infra/plugins/workflowResolver.ts
|
|
310
2
|
import fs2 from "fs";
|
|
311
3
|
import path2 from "path";
|
|
312
4
|
|
|
@@ -336,15 +28,6 @@ function resolvePluginManifestPath(repoDir) {
|
|
|
336
28
|
function resolveLegacyPluginManifestPath(repoDir) {
|
|
337
29
|
return path.join(repoDir, ".claude-plugin", "marketplace.json");
|
|
338
30
|
}
|
|
339
|
-
function resolveWorkflowManifestPath(repoDir) {
|
|
340
|
-
const preferredManifestPath = path.join(
|
|
341
|
-
repoDir,
|
|
342
|
-
".athena-workflow",
|
|
343
|
-
"marketplace.json"
|
|
344
|
-
);
|
|
345
|
-
const legacyManifestPath = resolveLegacyPluginManifestPath(repoDir);
|
|
346
|
-
return fs.existsSync(preferredManifestPath) ? preferredManifestPath : legacyManifestPath;
|
|
347
|
-
}
|
|
348
31
|
function readManifest(manifestPath) {
|
|
349
32
|
if (!fs.existsSync(manifestPath)) {
|
|
350
33
|
throw new Error(`Marketplace manifest not found: ${manifestPath}`);
|
|
@@ -413,67 +96,6 @@ function resolvePluginDirFromManifest(pluginName, repoDir, manifestPath) {
|
|
|
413
96
|
}
|
|
414
97
|
return pluginDir;
|
|
415
98
|
}
|
|
416
|
-
function resolveWorkflowEntryPath(entry, manifest, repoDir) {
|
|
417
|
-
if (typeof entry.source !== "string") {
|
|
418
|
-
throw new Error(
|
|
419
|
-
`Workflow "${entry.name}" uses a remote source type which is not supported.`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
let sourcePath = entry.source;
|
|
423
|
-
const { workflowRoot } = manifest.metadata ?? {};
|
|
424
|
-
if (workflowRoot && !path.isAbsolute(sourcePath) && !sourcePath.startsWith("./") && !sourcePath.startsWith("../")) {
|
|
425
|
-
sourcePath = path.join(workflowRoot, sourcePath);
|
|
426
|
-
}
|
|
427
|
-
const workflowPath = path.resolve(repoDir, sourcePath);
|
|
428
|
-
if (!workflowPath.startsWith(repoDir + path.sep) && workflowPath !== repoDir) {
|
|
429
|
-
throw new Error(
|
|
430
|
-
`Workflow "${entry.name}" source resolves outside the marketplace repo: ${workflowPath}`
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
const resolvedWorkflowPath = preferCanonicalWorkflowPath(
|
|
434
|
-
repoDir,
|
|
435
|
-
workflowPath
|
|
436
|
-
);
|
|
437
|
-
if (!fs.existsSync(resolvedWorkflowPath)) {
|
|
438
|
-
throw new Error(`Workflow source not found: ${resolvedWorkflowPath}`);
|
|
439
|
-
}
|
|
440
|
-
return resolvedWorkflowPath;
|
|
441
|
-
}
|
|
442
|
-
function resolveWorkflowPathFromManifest(workflowName, repoDir, manifestPath) {
|
|
443
|
-
const manifest = readManifest(manifestPath);
|
|
444
|
-
const workflows = manifest.workflows ?? [];
|
|
445
|
-
const entry = workflows.find((w) => w.name === workflowName);
|
|
446
|
-
if (!entry) {
|
|
447
|
-
const available = workflows.map((w) => w.name).join(", ") || "(none)";
|
|
448
|
-
throw new Error(
|
|
449
|
-
`Workflow "${workflowName}" not found in marketplace manifest ${manifestPath}. Available workflows: ${available}`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
return resolveWorkflowEntryPath(entry, manifest, repoDir);
|
|
453
|
-
}
|
|
454
|
-
function listWorkflowEntriesFromManifest(repoDir, manifestPath, source) {
|
|
455
|
-
const manifest = readManifest(manifestPath);
|
|
456
|
-
const workflows = manifest.workflows ?? [];
|
|
457
|
-
return workflows.filter(
|
|
458
|
-
(entry) => typeof entry.source === "string"
|
|
459
|
-
).map((entry) => ({
|
|
460
|
-
name: entry.name,
|
|
461
|
-
description: entry.description,
|
|
462
|
-
version: entry.version,
|
|
463
|
-
workflowPath: resolveWorkflowEntryPath(entry, manifest, repoDir),
|
|
464
|
-
ref: source.kind === "remote" ? `${entry.name}@${source.owner}/${source.repo}` : void 0,
|
|
465
|
-
source
|
|
466
|
-
}));
|
|
467
|
-
}
|
|
468
|
-
function preferCanonicalWorkflowPath(repoDir, workflowPath) {
|
|
469
|
-
const relativePath = path.relative(repoDir, workflowPath);
|
|
470
|
-
const segments = relativePath.split(path.sep);
|
|
471
|
-
if (segments[0] !== ".workflows") {
|
|
472
|
-
return workflowPath;
|
|
473
|
-
}
|
|
474
|
-
const canonicalPath = path.join(repoDir, "workflows", ...segments.slice(1));
|
|
475
|
-
return fs.existsSync(canonicalPath) ? canonicalPath : workflowPath;
|
|
476
|
-
}
|
|
477
99
|
function isMarketplaceRef(entry) {
|
|
478
100
|
return MARKETPLACE_REF_RE.test(entry);
|
|
479
101
|
}
|
|
@@ -634,8 +256,86 @@ var WorkflowNotFoundError = class extends Error {
|
|
|
634
256
|
this.searchedSources = searchedSources;
|
|
635
257
|
}
|
|
636
258
|
};
|
|
259
|
+
var WorkflowVersionNotFoundError = class extends Error {
|
|
260
|
+
workflowName;
|
|
261
|
+
requestedVersion;
|
|
262
|
+
availableVersion;
|
|
263
|
+
sourceLabel;
|
|
264
|
+
constructor(workflowName, requestedVersion, availableVersion, sourceLabel) {
|
|
265
|
+
const availableText = availableVersion ? `found version ${availableVersion}` : "marketplace entry does not declare a version";
|
|
266
|
+
super(
|
|
267
|
+
`Workflow "${workflowName}" version ${requestedVersion} not found in ${sourceLabel} (${availableText}).`
|
|
268
|
+
);
|
|
269
|
+
this.name = "WorkflowVersionNotFoundError";
|
|
270
|
+
this.workflowName = workflowName;
|
|
271
|
+
this.requestedVersion = requestedVersion;
|
|
272
|
+
this.availableVersion = availableVersion;
|
|
273
|
+
this.sourceLabel = sourceLabel;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
637
276
|
|
|
638
|
-
// src/infra/plugins/
|
|
277
|
+
// src/infra/plugins/workflowResolver.ts
|
|
278
|
+
function resolveWorkflowManifestPath(repoDir) {
|
|
279
|
+
const preferred = path2.join(repoDir, ".athena-workflow", "marketplace.json");
|
|
280
|
+
const legacy = path2.join(repoDir, ".claude-plugin", "marketplace.json");
|
|
281
|
+
return fs2.existsSync(preferred) ? preferred : legacy;
|
|
282
|
+
}
|
|
283
|
+
function preferCanonicalWorkflowPath(repoDir, workflowPath) {
|
|
284
|
+
const relativePath = path2.relative(repoDir, workflowPath);
|
|
285
|
+
const segments = relativePath.split(path2.sep);
|
|
286
|
+
if (segments[0] !== ".workflows") return workflowPath;
|
|
287
|
+
const canonical = path2.join(repoDir, "workflows", ...segments.slice(1));
|
|
288
|
+
return fs2.existsSync(canonical) ? canonical : workflowPath;
|
|
289
|
+
}
|
|
290
|
+
function resolveWorkflowEntryPath(entry, manifest, repoDir) {
|
|
291
|
+
if (typeof entry.source !== "string") {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Workflow "${entry.name}" uses a remote source type which is not supported.`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
let sourcePath = entry.source;
|
|
297
|
+
const { workflowRoot } = manifest.metadata ?? {};
|
|
298
|
+
if (workflowRoot && !path2.isAbsolute(sourcePath) && !sourcePath.startsWith("./") && !sourcePath.startsWith("../")) {
|
|
299
|
+
sourcePath = path2.join(workflowRoot, sourcePath);
|
|
300
|
+
}
|
|
301
|
+
const workflowPath = path2.resolve(repoDir, sourcePath);
|
|
302
|
+
if (!workflowPath.startsWith(repoDir + path2.sep) && workflowPath !== repoDir) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Workflow "${entry.name}" source resolves outside the marketplace repo: ${workflowPath}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
const resolved = preferCanonicalWorkflowPath(repoDir, workflowPath);
|
|
308
|
+
if (!fs2.existsSync(resolved)) {
|
|
309
|
+
throw new Error(`Workflow source not found: ${resolved}`);
|
|
310
|
+
}
|
|
311
|
+
return resolved;
|
|
312
|
+
}
|
|
313
|
+
function resolveWorkflowPathFromManifest(workflowName, repoDir, manifestPath) {
|
|
314
|
+
const manifest = readManifest(manifestPath);
|
|
315
|
+
const workflows = manifest.workflows ?? [];
|
|
316
|
+
const entry = workflows.find((w) => w.name === workflowName);
|
|
317
|
+
if (!entry) {
|
|
318
|
+
const available = workflows.map((w) => w.name).join(", ") || "(none)";
|
|
319
|
+
throw new Error(
|
|
320
|
+
`Workflow "${workflowName}" not found in marketplace manifest ${manifestPath}. Available workflows: ${available}`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
return resolveWorkflowEntryPath(entry, manifest, repoDir);
|
|
324
|
+
}
|
|
325
|
+
function listWorkflowEntriesFromManifest(repoDir, manifestPath, source) {
|
|
326
|
+
const manifest = readManifest(manifestPath);
|
|
327
|
+
const workflows = manifest.workflows ?? [];
|
|
328
|
+
return workflows.filter(
|
|
329
|
+
(entry) => typeof entry.source === "string"
|
|
330
|
+
).map((entry) => ({
|
|
331
|
+
name: entry.name,
|
|
332
|
+
description: entry.description,
|
|
333
|
+
version: entry.version,
|
|
334
|
+
workflowPath: resolveWorkflowEntryPath(entry, manifest, repoDir),
|
|
335
|
+
ref: source.kind === "remote" ? `${entry.name}@${source.owner}/${source.repo}` : void 0,
|
|
336
|
+
source
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
639
339
|
function findMarketplaceRepoDir(startPath) {
|
|
640
340
|
let currentDir = path2.resolve(startPath);
|
|
641
341
|
for (; ; ) {
|
|
@@ -643,9 +343,7 @@ function findMarketplaceRepoDir(startPath) {
|
|
|
643
343
|
return currentDir;
|
|
644
344
|
}
|
|
645
345
|
const parentDir = path2.dirname(currentDir);
|
|
646
|
-
if (parentDir === currentDir)
|
|
647
|
-
return void 0;
|
|
648
|
-
}
|
|
346
|
+
if (parentDir === currentDir) return void 0;
|
|
649
347
|
currentDir = parentDir;
|
|
650
348
|
}
|
|
651
349
|
}
|
|
@@ -667,11 +365,7 @@ function resolveWorkflowMarketplaceSource(source) {
|
|
|
667
365
|
`Local marketplace not found from source: ${trimmed}. Expected a marketplace repo root or a path inside one.`
|
|
668
366
|
);
|
|
669
367
|
}
|
|
670
|
-
return {
|
|
671
|
-
kind: "local",
|
|
672
|
-
path: resolvedPath,
|
|
673
|
-
repoDir
|
|
674
|
-
};
|
|
368
|
+
return { kind: "local", path: resolvedPath, repoDir };
|
|
675
369
|
}
|
|
676
370
|
function listMarketplaceWorkflows(owner, repo) {
|
|
677
371
|
requireGitForMarketplace("workflows");
|
|
@@ -699,47 +393,11 @@ function resolveMarketplaceWorkflow(ref) {
|
|
|
699
393
|
resolveWorkflowManifestPath(repoDir)
|
|
700
394
|
);
|
|
701
395
|
}
|
|
702
|
-
var WorkflowVersionNotFoundError = class extends Error {
|
|
703
|
-
workflowName;
|
|
704
|
-
requestedVersion;
|
|
705
|
-
availableVersion;
|
|
706
|
-
sourceLabel;
|
|
707
|
-
constructor(workflowName, requestedVersion, availableVersion, sourceLabel) {
|
|
708
|
-
const availableText = availableVersion ? `found version ${availableVersion}` : "marketplace entry does not declare a version";
|
|
709
|
-
super(
|
|
710
|
-
`Workflow "${workflowName}" version ${requestedVersion} not found in ${sourceLabel} (${availableText}).`
|
|
711
|
-
);
|
|
712
|
-
this.name = "WorkflowVersionNotFoundError";
|
|
713
|
-
this.workflowName = workflowName;
|
|
714
|
-
this.requestedVersion = requestedVersion;
|
|
715
|
-
this.availableVersion = availableVersion;
|
|
716
|
-
this.sourceLabel = sourceLabel;
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
function parseBareWorkflowName(source) {
|
|
720
|
-
const atIdx = source.indexOf("@");
|
|
721
|
-
if (atIdx <= 0 || atIdx === source.length - 1) {
|
|
722
|
-
return { bareName: source, pinnedVersion: void 0 };
|
|
723
|
-
}
|
|
724
|
-
const suffix = source.slice(atIdx + 1);
|
|
725
|
-
if (suffix.includes("/")) {
|
|
726
|
-
return { bareName: source, pinnedVersion: void 0 };
|
|
727
|
-
}
|
|
728
|
-
return {
|
|
729
|
-
bareName: source.slice(0, atIdx),
|
|
730
|
-
pinnedVersion: suffix
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
396
|
function gatherMarketplaceWorkflowSources(source) {
|
|
734
397
|
const trimmed = source.trim();
|
|
735
398
|
const resolvedPath = path2.resolve(trimmed);
|
|
736
399
|
if (fs2.existsSync(resolvedPath) && fs2.statSync(resolvedPath).isFile()) {
|
|
737
|
-
return [
|
|
738
|
-
{
|
|
739
|
-
kind: "filesystem",
|
|
740
|
-
workflowPath: fs2.realpathSync(resolvedPath)
|
|
741
|
-
}
|
|
742
|
-
];
|
|
400
|
+
return [{ kind: "filesystem", workflowPath: fs2.realpathSync(resolvedPath) }];
|
|
743
401
|
}
|
|
744
402
|
if (!fs2.existsSync(resolvedPath) && isMarketplaceSlug(trimmed)) {
|
|
745
403
|
const slashIdx = trimmed.indexOf("/");
|
|
@@ -785,6 +443,17 @@ function gatherMarketplaceWorkflowSources(source) {
|
|
|
785
443
|
workflowPath: entry.workflowPath
|
|
786
444
|
}));
|
|
787
445
|
}
|
|
446
|
+
function parseBareWorkflowName(source) {
|
|
447
|
+
const atIdx = source.indexOf("@");
|
|
448
|
+
if (atIdx <= 0 || atIdx === source.length - 1) {
|
|
449
|
+
return { bareName: source, pinnedVersion: void 0 };
|
|
450
|
+
}
|
|
451
|
+
const suffix = source.slice(atIdx + 1);
|
|
452
|
+
if (suffix.includes("/")) {
|
|
453
|
+
return { bareName: source, pinnedVersion: void 0 };
|
|
454
|
+
}
|
|
455
|
+
return { bareName: source.slice(0, atIdx), pinnedVersion: suffix };
|
|
456
|
+
}
|
|
788
457
|
function resolvedSourceLabel(s) {
|
|
789
458
|
if (s.kind === "marketplace-remote") return `marketplace ${s.slug}`;
|
|
790
459
|
if (s.kind === "marketplace-local") return `local marketplace ${s.repoDir}`;
|
|
@@ -1265,190 +934,6 @@ function hasProjectWorkflow(projectDir) {
|
|
|
1265
934
|
return hasActiveWorkflow(projectConfigPath(projectDir));
|
|
1266
935
|
}
|
|
1267
936
|
|
|
1268
|
-
// src/setup/components/StepStatus.tsx
|
|
1269
|
-
import { Text, Box } from "ink";
|
|
1270
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1271
|
-
function StepStatus({ status, message }) {
|
|
1272
|
-
const theme = useTheme();
|
|
1273
|
-
const icon = status === "success" ? "\u2713" : status === "error" ? "\u2717" : "\u280B";
|
|
1274
|
-
const color = status === "success" ? theme.status.success : status === "error" ? theme.status.error : theme.status.working;
|
|
1275
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginTop: 1, children: [
|
|
1276
|
-
/* @__PURE__ */ jsxs(Text, { color, bold: true, children: [
|
|
1277
|
-
icon,
|
|
1278
|
-
" "
|
|
1279
|
-
] }),
|
|
1280
|
-
/* @__PURE__ */ jsx(Text, { color, children: message })
|
|
1281
|
-
] });
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
// src/setup/steps/McpOptionsStep.tsx
|
|
1285
|
-
import { useState as useState2, useCallback, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1286
|
-
import { Box as Box3, Text as Text3 } from "ink";
|
|
1287
|
-
|
|
1288
|
-
// src/setup/components/StepSelector.tsx
|
|
1289
|
-
import { useEffect, useRef, useState } from "react";
|
|
1290
|
-
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
1291
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1292
|
-
function getInitialCursor(options, initialValue) {
|
|
1293
|
-
if (initialValue) {
|
|
1294
|
-
const initialIndex = options.findIndex(
|
|
1295
|
-
(option) => option.value === initialValue && !option.disabled
|
|
1296
|
-
);
|
|
1297
|
-
if (initialIndex >= 0) {
|
|
1298
|
-
return initialIndex;
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
const firstEnabled = options.findIndex((option) => !option.disabled);
|
|
1302
|
-
return firstEnabled >= 0 ? firstEnabled : 0;
|
|
1303
|
-
}
|
|
1304
|
-
function StepSelector({
|
|
1305
|
-
options,
|
|
1306
|
-
onSelect,
|
|
1307
|
-
isActive = true,
|
|
1308
|
-
initialValue,
|
|
1309
|
-
onHighlight,
|
|
1310
|
-
gap = 0
|
|
1311
|
-
}) {
|
|
1312
|
-
const theme = useTheme();
|
|
1313
|
-
const [cursor, setCursor] = useState(
|
|
1314
|
-
() => getInitialCursor(options, initialValue)
|
|
1315
|
-
);
|
|
1316
|
-
const highlightedRef = useRef(void 0);
|
|
1317
|
-
const moveCursor = (direction) => {
|
|
1318
|
-
setCursor((prev) => {
|
|
1319
|
-
if (options.length <= 1) {
|
|
1320
|
-
return prev;
|
|
1321
|
-
}
|
|
1322
|
-
let next = prev;
|
|
1323
|
-
for (let i = 0; i < options.length; i += 1) {
|
|
1324
|
-
const candidate = Math.max(
|
|
1325
|
-
0,
|
|
1326
|
-
Math.min(next + direction, options.length - 1)
|
|
1327
|
-
);
|
|
1328
|
-
if (candidate === next) {
|
|
1329
|
-
return prev;
|
|
1330
|
-
}
|
|
1331
|
-
next = candidate;
|
|
1332
|
-
if (!options[next]?.disabled) {
|
|
1333
|
-
return next;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
return prev;
|
|
1337
|
-
});
|
|
1338
|
-
};
|
|
1339
|
-
useInput(
|
|
1340
|
-
(_input, key) => {
|
|
1341
|
-
if (key.downArrow) {
|
|
1342
|
-
moveCursor(1);
|
|
1343
|
-
} else if (key.upArrow) {
|
|
1344
|
-
moveCursor(-1);
|
|
1345
|
-
} else if (key.return) {
|
|
1346
|
-
const opt = options[cursor];
|
|
1347
|
-
if (!opt.disabled) {
|
|
1348
|
-
onSelect(opt.value);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
},
|
|
1352
|
-
{ isActive }
|
|
1353
|
-
);
|
|
1354
|
-
useEffect(() => {
|
|
1355
|
-
if (!onHighlight) {
|
|
1356
|
-
return;
|
|
1357
|
-
}
|
|
1358
|
-
const option = options[cursor];
|
|
1359
|
-
if (option.disabled) {
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
if (highlightedRef.current === option.value) {
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
|
-
highlightedRef.current = option.value;
|
|
1366
|
-
onHighlight(option.value);
|
|
1367
|
-
}, [cursor, options, onHighlight]);
|
|
1368
|
-
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", gap, children: options.map((opt, i) => {
|
|
1369
|
-
const isCursor = i === cursor;
|
|
1370
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
1371
|
-
/* @__PURE__ */ jsxs2(
|
|
1372
|
-
Text2,
|
|
1373
|
-
{
|
|
1374
|
-
color: opt.disabled ? theme.textMuted : isCursor ? theme.accent : theme.text,
|
|
1375
|
-
bold: isCursor && !opt.disabled,
|
|
1376
|
-
inverse: isCursor && !opt.disabled,
|
|
1377
|
-
dimColor: opt.disabled,
|
|
1378
|
-
children: [
|
|
1379
|
-
isCursor ? " > " : " ",
|
|
1380
|
-
opt.label,
|
|
1381
|
-
isCursor ? " " : ""
|
|
1382
|
-
]
|
|
1383
|
-
}
|
|
1384
|
-
),
|
|
1385
|
-
opt.description && !opt.disabled ? /* @__PURE__ */ jsx2(Box2, { paddingLeft: 5, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, italic: true, children: [
|
|
1386
|
-
"\u21B3 ",
|
|
1387
|
-
opt.description
|
|
1388
|
-
] }) }) : null
|
|
1389
|
-
] }, opt.value);
|
|
1390
|
-
}) });
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
// src/setup/steps/McpOptionsStep.tsx
|
|
1394
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1395
|
-
function McpOptionsStep({ servers, onComplete }) {
|
|
1396
|
-
const theme = useTheme();
|
|
1397
|
-
const [serverIndex, setServerIndex] = useState2(0);
|
|
1398
|
-
const choicesRef = useRef2({});
|
|
1399
|
-
const autoSkippedRef = useRef2(false);
|
|
1400
|
-
useEffect2(() => {
|
|
1401
|
-
if (servers.length === 0 && !autoSkippedRef.current) {
|
|
1402
|
-
autoSkippedRef.current = true;
|
|
1403
|
-
onComplete({});
|
|
1404
|
-
}
|
|
1405
|
-
}, [servers.length, onComplete]);
|
|
1406
|
-
const handleSelect = useCallback(
|
|
1407
|
-
(value) => {
|
|
1408
|
-
const server = servers[serverIndex];
|
|
1409
|
-
const selectedOption = server.options[Number(value)];
|
|
1410
|
-
choicesRef.current = {
|
|
1411
|
-
...choicesRef.current,
|
|
1412
|
-
[server.serverName]: selectedOption.env
|
|
1413
|
-
};
|
|
1414
|
-
if (serverIndex + 1 < servers.length) {
|
|
1415
|
-
setServerIndex((prev) => prev + 1);
|
|
1416
|
-
} else {
|
|
1417
|
-
onComplete(choicesRef.current);
|
|
1418
|
-
}
|
|
1419
|
-
},
|
|
1420
|
-
[serverIndex, servers, onComplete]
|
|
1421
|
-
);
|
|
1422
|
-
if (servers.length === 0) {
|
|
1423
|
-
return null;
|
|
1424
|
-
}
|
|
1425
|
-
const currentServer = servers[serverIndex];
|
|
1426
|
-
const selectorOptions = currentServer.options.map((opt, i) => ({
|
|
1427
|
-
label: i === 0 ? `${opt.label} (default)` : opt.label,
|
|
1428
|
-
value: String(i)
|
|
1429
|
-
}));
|
|
1430
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1431
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accent, children: "Configure MCP servers" }),
|
|
1432
|
-
/* @__PURE__ */ jsxs3(Text3, { color: theme.textMuted, children: [
|
|
1433
|
-
"Server ",
|
|
1434
|
-
serverIndex + 1,
|
|
1435
|
-
" of ",
|
|
1436
|
-
servers.length,
|
|
1437
|
-
":",
|
|
1438
|
-
" ",
|
|
1439
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: currentServer.serverName })
|
|
1440
|
-
] }),
|
|
1441
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(
|
|
1442
|
-
StepSelector,
|
|
1443
|
-
{
|
|
1444
|
-
options: selectorOptions,
|
|
1445
|
-
onSelect: handleSelect
|
|
1446
|
-
},
|
|
1447
|
-
currentServer.serverName
|
|
1448
|
-
) })
|
|
1449
|
-
] });
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
937
|
// src/core/workflows/types.ts
|
|
1453
938
|
function pluginSpecRef(spec) {
|
|
1454
939
|
return typeof spec === "string" ? spec : spec.ref;
|
|
@@ -2062,7 +1547,7 @@ import fs10 from "fs";
|
|
|
2062
1547
|
import path8 from "path";
|
|
2063
1548
|
|
|
2064
1549
|
// src/core/workflows/stateMachine.md
|
|
2065
|
-
var stateMachine_default = "# Stateless Session Protocol\n\nYou operate in stateless sessions managed by a workflow runner. Each session is a fresh process with no memory of prior sessions. The **tracker file** is your only continuity \u2014 it's how you talk to your future self.\n\n## Execution Model\n\nThe runner spawns `claude -p` sessions in a loop:\n\n- **Session 1**: You receive the user's original request.\n- **Sessions 2+**: You receive a continuation prompt directing you to read the tracker.\n- **Between sessions**: The runner inspects the tracker for terminal markers. If found, or if the max iteration cap is reached, the loop ends. The tracker is preserved for resume, audit, and debugging.\n\n### Terminal Markers\n\nBy default, workflows use these tracker markers:\n\n- `<!-- WORKFLOW_COMPLETE -->`\n- `<!-- WORKFLOW_BLOCKED -->`\n- `<!-- WORKFLOW_BLOCKED: reason -->`\n\nWorkflows may override the default marker strings via configuration. Use the markers configured for the current workflow.\n\nRules:\n\n- Only the last non-empty line of the tracker is treated as authoritative\n- Marker-like text earlier in the tracker, including notes, examples, or quoted instructions, is ignored\n- Write `WORKFLOW_COMPLETE` only when the workflow's completion criteria have been fully verified\n- Write `WORKFLOW_BLOCKED` only when progress cannot continue in the current workflow without external intervention or a workflow-defined stop condition has been reached\n- Include a concrete reason after the colon whenever possible, but `<!-- WORKFLOW_BLOCKED -->` without a reason is still valid\n\n### Tracker Path\n\nBy default, the tracker file lives at `.athena/<session_id>/tracker.md` in the project root, where `<session_id>` is the current Athena session ID. This session-scoped path allows multiple workflows to run concurrently and makes resume reliable. The runner provides the session ID \u2014 do not generate one yourself.\n\nWorkflows may override the default tracker path via configuration. Read and write the tracker at the configured path for the current workflow.\n\n**Assume interruption.** Your context window can reset at any moment \u2014 the runner may kill a session that's taking too long, or you may hit token limits mid-task. Any progress not written to the tracker is gone. This isn't a theoretical risk; it's the normal operating mode.\n\n## Session Protocol\n\nEvery session follows four phases: **Read**, **Orient**, **Execute**, **End**.\n\n### Phase 1 \u2014 Read the Tracker\n\nRead the tracker file at the configured tracker path for the current workflow. By default this is `.athena/<session_id>/tracker.md`.\n\n- **Contains `<!-- TRACKER_SKELETON -->`**: This is session 1. The runner created a skeleton tracker with the goal and session metadata. Proceed to Phase 2 (Orient) \u2014 replace the skeleton with a real tracker. **You must do this even if the entire request can be satisfied in a single turn.** Write a minimal tracker (what was asked, what was done, the outcome) and then append `<!-- WORKFLOW_COMPLETE -->`. Leaving the skeleton in place causes the runner to classify the session as a failure.\n- **Otherwise**: This is a continuation session. The tracker contains everything prior sessions learned and decided. Skip to Phase 3 (Execute) using the tracker's context.\n\nWhy read first: without the tracker, you'll duplicate work already done or contradict decisions made in prior sessions. The tracker is the single source of truth across sessions.\n\n### Phase 2 \u2014 Orient (Session 1 Only)\n\n#### 2a. Create the tracker immediately\n\nWrite a skeleton tracker as your first write operation, before doing any domain work. Even a minimal tracker with just the goal and \"orientation in progress\" provides continuity if the session is interrupted during setup.\n\nThe tracker must always answer four questions for any future session:\n\n1. What are we trying to accomplish?\n2. What has been completed so far?\n3. What work is left?\n4. What should the next session do first?\n\nThese answers are the contract between sessions. The exact section headings may vary by workflow, but the tracker must make all four answers explicit and easy to find. A future session reading this tracker has no other context \u2014 if something isn't here, it doesn't exist.\n\n#### 2b. Workflow-specific orientation\n\nExecute the orientation steps defined by the workflow. These vary by domain \u2014 a test-writing workflow explores the product in a browser; a migration workflow audits the database schema. The workflow defines what orientation means.\n\n#### 2c. Create a task plan\n\nRefine the skeleton tracker into granular, verifiable checkpoints based on what orientation revealed. Each task should be a concrete unit of progress, not a vague phase. Include verification steps (running checks, reviewing output), not just implementation. Vague tasks like \"write tests\" can't be meaningfully resumed by a future session that has no idea what \"write tests\" means in this context.\n\n#### 2d. Update the tracker\n\nAfter orientation, ensure the tracker captures: the goal, what was discovered, what's planned, and what the next session should do first. Record concrete observations \u2014 what you actually saw, not what you assumed. Assumptions that turn out wrong waste entire future sessions on rework.\n\n### Phase 3 \u2014 Execute\n\nWork through tasks, advancing the plan step by step.\n\n#### Load skills before acting\n\nIf the workflow defines a skill table, load the relevant skill before each activity. Skills carry implementation details \u2014 scaffolding steps, authentication strategies, locator rules, anti-patterns, code templates \u2014 that would otherwise be lost between sessions. This prompt defines the protocol; skills define how to execute each step.\n\n#### Follow the workflow's sequence\n\nExecute in the order the workflow prescribes. Not every session covers all steps \u2014 pick up where the tracker says rather than restarting the flow.\n\n#### Delegate heavy work\n\nUse subagents via the Task tool to offload heavy exploration or generation, preserving your main context for orchestration. Pass file paths, conventions, and concrete output expectations. Instruct subagents to load the appropriate skill.\n\nRespect the workflow's **delegation constraints** \u2014 some operations must run in the main agent because their output serves as proof or because the main agent needs to interpret results in context.\n\n#### Execute quality gates\n\nIf the workflow defines quality gates, execute them in order. Do not skip gates \u2014 they exist because prior experience showed that skipping them leads to cascading rework. If a gate returns a failing verdict, address the issues and re-run the gate before proceeding.\n\nRespect the workflow's **retry limits** for failing steps. Repeated failures usually signal a deeper issue that another retry won't fix.\n\n#### Update the tracker as you work\n\nTreat tracker updates as defensive checkpoints against three failure modes: the runner killing your session, your context collapsing under tool-output load, and you simply forgetting an hour from now what you just learned. The right cadence sits between \"every tool call\" (noisy, wastes tokens, turns the tracker into a log) and \"at the end of the session\" (everything is lost if you die mid-task).\n\nUpdate the tracker whenever any of the following happens \u2014 these are the checkpoints, not \"felt like a good moment\":\n\n- **You finished a discrete unit of work.** A file written, a fix applied, a test run, a quality gate passed. The tracker should reflect the new reality before you start the next unit, not after several units have piled up.\n- **You learned something a future session can't cheaply rederive.** An API quirk, a config field that turned out to matter, a dead end you've now ruled out, a decision between two approaches. Insights are tracker-worthy even when no code changed \u2014 losing them costs the next session a full re-exploration. The tracker is your knowledge ledger, not just a task log.\n- **You're about to do something risky or long-running.** Dispatching a subagent, kicking off a long build, calling a flaky external service, starting a large refactor. If that operation kills your session, only what's already in the tracker survives. Write first, then act.\n- **Your plan changed.** A task got resequenced, a new task surfaced, a planned task turned out to be unnecessary. Stale plans poison continuation sessions \u2014 the tracker must reflect what you'll actually do next, not what you thought five steps ago.\n- **You've been working a while without writing.** If you can't remember when you last touched the tracker, you've gone too long. A short defensive update (\"currently doing X, last completed Y, next is Z\") beats nothing.\n\nWhat an update contains depends on the trigger, but always cover: what changed (work or knowledge), what's now next, and any caveat the next session needs to know. Avoid transcribing tool calls \u2014 the tracker is a contract with your future self, not a replay log.\n\nThe cost of one extra tracker update is a few tokens. The cost of dying without one is a whole wasted session rediscovering what you already knew. Bias toward writing.\n\n#### Task visibility\n\nThe tracker contains the authoritative task plan \u2014 it persists across sessions. Your harness's task UI is only a live projection of that plan, visible to the user in their CLI widget. It is session-scoped and does not survive process exit.\n\n**The relationship:** tracker is the source of truth, task tools are the display.\n\n{{TASK_TOOL_INSTRUCTIONS}}\n\n- **Session 1 (Orient):** After creating the task plan in the tracker, project each task into the task management tools so the user can see progress in real time.\n- **Session 2+ (Resume):** After reading the tracker, recreate the task projection from the tracker's plan. Set statuses to match what the tracker says is done, remaining, and next. The user sees consistent progress across sessions.\n- **During work:** Update both \u2014 the task tools for immediate UI feedback, the tracker for persistence. When a task completes, mark it done in the task tools and record it in the tracker in the same working phase.\n\nThis gives the user a consistent view of progress in their CLI regardless of which session they're in, while the tracker remains the durable contract between sessions.\n\n### Phase 4 \u2014 End of Session\n\n1. Ensure the tracker reflects all progress, discoveries, and blockers.\n2. Write clear instructions for what the next session should do first.\n3. If all work is complete and verified: write `<!-- WORKFLOW_COMPLETE -->` at the end of the tracker.\n4. If an unrecoverable blocker prevents progress: write `<!-- WORKFLOW_BLOCKED -->` or `<!-- WORKFLOW_BLOCKED: reason -->` at the end of the tracker.\n\nDo not write terminal markers prematurely. The runner trusts markers unconditionally \u2014 a premature marker kills the loop before work is done, and there's no automatic recovery.\n\n## Session Bounding\n\nEach fresh session starts with a clean context window and a compact tracker \u2014 effectively a self-compaction. As you work, your context fills with tool outputs, exploration results, and intermediate state. The longer you run, the more attention is spread across tokens that are no longer relevant, degrading your precision on the work that matters now.\n\nWork on a bounded chunk per session. Ending early and letting the next session pick up from a clean tracker is almost always better than pushing through with a heavy context.\n\nHeuristics for when to checkpoint and end:\n\n- After completing a quality gate \u2014 natural boundary\n- After crossing multiple phases (e.g., explored + planned + wrote specs) \u2014 stop before pushing into the next\n- When you notice your context is heavy with tool outputs from earlier work\n\n## Guardrails\n\nQuick-reference checklist \u2014 each of these is explained in detail above:\n\n- Read the tracker before doing anything else\n- Replace the skeleton immediately \u2014 even for simple requests. Write minimal tracker content (what was asked, what was done, outcome) and then the terminal marker before the session ends.\n- Update the tracker on concrete triggers (unit of work done, insight learned, risky operation pending, plan changed) \u2014 not on a vague sense of \"meaningful progress\"\n- Project the tracker's task plan into task management tools at session start\n- Update both task tools and tracker as milestones complete\n- Load the relevant skill before each activity\n- Do not write the completion marker until all work is verified\n- Respect the workflow's delegation constraints and retry limits\n";
|
|
1550
|
+
var stateMachine_default = "# Stateless Session Protocol\n\nYou run in a stateless loop. Each session is a fresh process with no memory of prior sessions. **The tracker file is your only continuity** \u2014 read it, work, write it. Assume interruption: the runner may kill a long session, your context may collapse under tool output, you may hit token limits mid-task. Anything not in the tracker is gone.\n\n## First action, every session\n\n1. Read the tracker at the configured path (default: `.athena/<session_id>/tracker.md`). The runner provides the session ID \u2014 do not invent one.\n2. If the tracker contains `<!-- TRACKER_SKELETON -->` \u2192 this is session 1, run [**Orient**](#orient-session-1).\n3. Otherwise \u2192 this is a continuation, run [**Execute**](#execute-session-2) from where the tracker says, not from the start of the flow.\n\nReading first prevents two failure modes that waste whole sessions: redoing work already done, or contradicting decisions a prior session made.\n\n## Tracker contract\n\nThe tracker must always answer four questions:\n\n1. What are we trying to accomplish?\n2. What has been done?\n3. What's left?\n4. What should the next session do first?\n\nA future session has no other context. If something isn't here, it doesn't exist. Section headings may vary by workflow, but these four answers must be explicit and easy to find.\n\n### Terminal markers\n\nDefault markers (workflows may override \u2014 use the markers configured for the active workflow):\n\n- `<!-- WORKFLOW_COMPLETE -->` \u2014 all work done and verified\n- `<!-- WORKFLOW_BLOCKED -->` or `<!-- WORKFLOW_BLOCKED: reason -->` \u2014 cannot proceed without external intervention\n\nRules:\n\n- Only the last non-empty line of the tracker is authoritative. Marker-like text in notes, examples, or quoted instructions earlier in the file is ignored.\n- The runner trusts markers unconditionally. A premature marker ends the loop with no automatic recovery \u2014 write one only when its criteria are fully met.\n- Include a concrete reason after `WORKFLOW_BLOCKED:` whenever possible; the bare form is still valid.\n\n## Phases\n\n### Orient (session 1)\n\n1. **Replace the skeleton immediately**, before any domain work. Even a three-line tracker (goal + \"orienting\") protects you if the session dies during setup.\n2. Run the workflow's orientation steps. These vary by domain \u2014 a test-writing workflow explores the product in a browser; a migration workflow audits the schema. The workflow defines what orientation means.\n3. Refine the tracker into a granular plan. Each task a concrete, verifiable unit of work, including verification steps (running checks, reviewing output) \u2014 not just implementation. Vague tasks (\"write tests\") cannot be meaningfully resumed by a future session that has no idea what they mean here.\n4. Record concrete observations \u2014 what you actually saw, not what you assumed. Wrong assumptions burn entire future sessions on rework.\n5. **Single-turn requests still go through this phase.** If the entire request is satisfied in one turn, write a minimal tracker (what was asked, what was done, the outcome) and append `<!-- WORKFLOW_COMPLETE -->`. Leaving the skeleton in place causes the runner to classify the session as a failure.\n\n### Execute (session 2+)\n\n- Work from where the tracker says, in the workflow's prescribed sequence. Not every session covers every step.\n- If the workflow defines a skill table, **load the relevant skill before each activity**. Skills carry the implementation detail (scaffolding steps, locator rules, anti-patterns, code templates) that this protocol intentionally doesn't repeat.\n- Delegate heavy exploration or generation to subagents via the Task tool. Pass file paths, conventions, and concrete output expectations; tell them which skill to load. Respect the workflow's **delegation constraints** \u2014 some operations must run in the main agent because their output is proof, or because the main agent needs to interpret results in context.\n- Run quality gates in order. Do not skip \u2014 they exist because skipping cascades into rework. On a failing verdict, address the issues and re-run before proceeding. Respect the workflow's **retry limits**: repeated failure usually signals a deeper issue another retry won't fix.\n\n### End\n\n1. Tracker reflects all progress, discoveries, and blockers.\n2. Tracker says clearly what the next session should do first.\n3. If all work is verified: append the completion marker.\n4. If an unrecoverable blocker prevents progress: append the blocked marker, with a reason if you have one.\n\n## When to write the tracker\n\nWrite on **concrete triggers**, not on a vague sense of \"meaningful progress.\" The right cadence sits between every-tool-call (noisy log, wastes tokens) and end-of-session (everything lost if you die mid-task).\n\n- **Discrete unit done** \u2014 file written, fix applied, test run, gate passed. Reflect the new reality before starting the next unit.\n- **Insight learned** \u2014 API quirk, config field that turned out to matter, dead end ruled out, decision between two approaches. Insights are tracker-worthy even when no code changed; rediscovering them costs the next session a full re-exploration. The tracker is a knowledge ledger, not just a task log.\n- **About to do something risky or long-running** \u2014 subagent dispatch, long build, flaky external call, large refactor. Write _first_, then act. If the operation kills your session, only what's on disk survives.\n- **Plan changed** \u2014 task resequenced, new task surfaced, planned task no longer needed. Stale plans poison continuation sessions.\n- **You haven't written in a while** \u2014 if you can't remember the last update, you've gone too long. A short defensive update (\"doing X, last completed Y, next is Z\") beats nothing.\n\nEach update covers: what changed (work or knowledge), what's now next, and any caveat the next session needs. Don't transcribe tool calls \u2014 the tracker is a contract with your future self, not a replay log.\n\nThe cost of one extra tracker update is a few tokens. The cost of dying without one is a whole wasted session. Bias toward writing.\n\n## Task UI projection\n\nThe tracker is the durable source of truth. Your harness's task tools are a session-scoped UI projection of the same plan, shown to the user in their CLI widget. They do not survive process exit.\n\n{{TASK_TOOL_INSTRUCTIONS}}\n\n- **Session 1, after orientation:** project the tracker's task plan into the task tools.\n- **Session 2+, after reading the tracker:** recreate the projection from the tracker; do not assume task IDs from prior sessions still exist.\n- **During work:** update both \u2014 the task tools for immediate UI feedback, the tracker for persistence \u2014 in the same working phase.\n\n## Session bounding\n\nEach fresh session starts with a clean context window and a compact tracker \u2014 effectively self-compaction. As you work, context fills with tool outputs and intermediate state. The longer you run, the more attention is spread across tokens that are no longer relevant, degrading precision on the work that matters now.\n\nWork a bounded chunk per session. Ending early and letting the next session pick up from a clean tracker is almost always better than pushing through with a heavy context. Natural checkpoints:\n\n- After a quality gate\n- After crossing multiple phases (explored \u2192 planned \u2192 wrote specs) \u2014 stop before pushing into the next\n- When your context is visibly heavy with tool output from earlier work\n\n## Quick reference\n\n- [ ] Read the tracker before doing anything else\n- [ ] Replace the skeleton immediately, even for single-turn requests\n- [ ] Update on concrete triggers \u2014 unit done, insight learned, risky op pending, plan changed\n- [ ] Project the tracker plan into task tools at session start; keep both in sync as work lands\n- [ ] Load the workflow's skill before each activity\n- [ ] Run quality gates in order; respect delegation constraints and retry limits\n- [ ] Write the completion marker only when all work is verified\n- [ ] Checkpoint and end before context goes stale\n";
|
|
2066
1551
|
|
|
2067
1552
|
// src/core/workflows/stateMachine.ts
|
|
2068
1553
|
function buildTaskToolInstructions(harness) {
|
|
@@ -2236,7 +1721,7 @@ function cleanupWorkflowRun(state) {
|
|
|
2236
1721
|
}
|
|
2237
1722
|
|
|
2238
1723
|
// src/core/workflows/useWorkflowSessionController.ts
|
|
2239
|
-
import { useCallback
|
|
1724
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2240
1725
|
|
|
2241
1726
|
// src/core/workflows/workflowRunner.ts
|
|
2242
1727
|
import crypto from "crypto";
|
|
@@ -2413,8 +1898,7 @@ function createWorkflowRunner(input) {
|
|
|
2413
1898
|
} else if (loopStop.reason === "max_iterations") {
|
|
2414
1899
|
status = "exhausted";
|
|
2415
1900
|
} else if (loopStop.reason === "skeleton_not_replaced") {
|
|
2416
|
-
status = "
|
|
2417
|
-
stopReason = "Agent did not replace the tracker skeleton. The session completed without engaging with the workflow tracker.";
|
|
1901
|
+
status = "completed";
|
|
2418
1902
|
} else {
|
|
2419
1903
|
status = "failed";
|
|
2420
1904
|
stopReason = `Loop stopped: ${loopStop.reason}`;
|
|
@@ -2456,10 +1940,10 @@ function createWorkflowRunner(input) {
|
|
|
2456
1940
|
|
|
2457
1941
|
// src/core/workflows/useWorkflowSessionController.ts
|
|
2458
1942
|
function useWorkflowSessionController(base, input) {
|
|
2459
|
-
const [isRunning, setIsRunning] =
|
|
2460
|
-
const runnerRef =
|
|
2461
|
-
const activeRunIdRef =
|
|
2462
|
-
const cancelCurrentRun =
|
|
1943
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
1944
|
+
const runnerRef = useRef(null);
|
|
1945
|
+
const activeRunIdRef = useRef(null);
|
|
1946
|
+
const cancelCurrentRun = useCallback(async () => {
|
|
2463
1947
|
const runner = runnerRef.current;
|
|
2464
1948
|
if (runner) {
|
|
2465
1949
|
runner.kill();
|
|
@@ -2469,7 +1953,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2469
1953
|
activeRunIdRef.current = null;
|
|
2470
1954
|
}
|
|
2471
1955
|
}, []);
|
|
2472
|
-
const interrupt =
|
|
1956
|
+
const interrupt = useCallback(() => {
|
|
2473
1957
|
const runner = runnerRef.current;
|
|
2474
1958
|
if (runner) {
|
|
2475
1959
|
runner.kill();
|
|
@@ -2481,7 +1965,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2481
1965
|
}
|
|
2482
1966
|
setIsRunning(false);
|
|
2483
1967
|
}, [base]);
|
|
2484
|
-
const kill =
|
|
1968
|
+
const kill = useCallback(async () => {
|
|
2485
1969
|
if (runnerRef.current) {
|
|
2486
1970
|
await cancelCurrentRun();
|
|
2487
1971
|
} else {
|
|
@@ -2489,7 +1973,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2489
1973
|
}
|
|
2490
1974
|
setIsRunning(false);
|
|
2491
1975
|
}, [base, cancelCurrentRun]);
|
|
2492
|
-
const spawn =
|
|
1976
|
+
const spawn = useCallback(
|
|
2493
1977
|
async (prompt, continuation, _configOverride) => {
|
|
2494
1978
|
await cancelCurrentRun();
|
|
2495
1979
|
setIsRunning(true);
|
|
@@ -2538,7 +2022,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2538
2022
|
input.persistRunState
|
|
2539
2023
|
]
|
|
2540
2024
|
);
|
|
2541
|
-
|
|
2025
|
+
useEffect(() => {
|
|
2542
2026
|
return () => {
|
|
2543
2027
|
runnerRef.current?.kill();
|
|
2544
2028
|
runnerRef.current = null;
|
|
@@ -2583,10 +2067,6 @@ function collectMcpServersWithOptions(pluginDirs) {
|
|
|
2583
2067
|
}
|
|
2584
2068
|
|
|
2585
2069
|
export {
|
|
2586
|
-
darkTheme,
|
|
2587
|
-
resolveTheme,
|
|
2588
|
-
ThemeProvider,
|
|
2589
|
-
useTheme,
|
|
2590
2070
|
createWorkflowRunner,
|
|
2591
2071
|
useWorkflowSessionController,
|
|
2592
2072
|
isMarketplaceRef,
|
|
@@ -2604,9 +2084,6 @@ export {
|
|
|
2604
2084
|
writeGlobalConfig,
|
|
2605
2085
|
writeProjectConfig,
|
|
2606
2086
|
hasProjectWorkflow,
|
|
2607
|
-
StepSelector,
|
|
2608
|
-
StepStatus,
|
|
2609
|
-
McpOptionsStep,
|
|
2610
2087
|
installWorkflowPlugins,
|
|
2611
2088
|
resolveWorkflowPlugins,
|
|
2612
2089
|
listBuiltinWorkflows,
|
|
@@ -2618,4 +2095,4 @@ export {
|
|
|
2618
2095
|
compileWorkflowPlan,
|
|
2619
2096
|
collectMcpServersWithOptions
|
|
2620
2097
|
};
|
|
2621
|
-
//# sourceMappingURL=chunk-
|
|
2098
|
+
//# sourceMappingURL=chunk-5VK2ZMVV.js.map
|