@bastani/atomic 0.5.34-0 → 0.6.0-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/README.md +329 -50
- package/dist/commands/cli/session.d.ts +67 -0
- package/dist/commands/cli/session.d.ts.map +1 -0
- package/dist/commands/cli/workflow-status.d.ts +63 -0
- package/dist/commands/cli/workflow-status.d.ts.map +1 -0
- package/dist/sdk/commander.d.ts +74 -0
- package/dist/sdk/commander.d.ts.map +1 -0
- package/dist/sdk/components/workflow-picker-panel.d.ts +14 -17
- package/dist/sdk/components/workflow-picker-panel.d.ts.map +1 -1
- package/dist/sdk/define-workflow.d.ts +18 -9
- package/dist/sdk/define-workflow.d.ts.map +1 -1
- package/dist/sdk/index.d.ts +4 -3
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/management-commands.d.ts +42 -0
- package/dist/sdk/management-commands.d.ts.map +1 -0
- package/dist/sdk/registry.d.ts +27 -0
- package/dist/sdk/registry.d.ts.map +1 -0
- package/dist/sdk/runtime/attached-footer.d.ts +1 -1
- package/dist/sdk/runtime/executor-env.d.ts +20 -0
- package/dist/sdk/runtime/executor-env.d.ts.map +1 -0
- package/dist/sdk/runtime/executor.d.ts +61 -10
- package/dist/sdk/runtime/executor.d.ts.map +1 -1
- package/dist/sdk/types.d.ts +147 -4
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk/worker-shared.d.ts +42 -0
- package/dist/sdk/worker-shared.d.ts.map +1 -0
- package/dist/sdk/workflow-cli.d.ts +103 -0
- package/dist/sdk/workflow-cli.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin-registry.d.ts +113 -0
- package/dist/sdk/workflows/builtin-registry.d.ts.map +1 -0
- package/dist/sdk/workflows/index.d.ts +5 -5
- package/dist/sdk/workflows/index.d.ts.map +1 -1
- package/package.json +12 -8
- package/src/cli.ts +85 -144
- package/src/commands/cli/chat/index.ts +10 -0
- package/src/commands/cli/workflow-command.test.ts +279 -938
- package/src/commands/cli/workflow-inputs.test.ts +41 -11
- package/src/commands/cli/workflow-inputs.ts +47 -12
- package/src/commands/cli/workflow-list.test.ts +234 -0
- package/src/commands/cli/workflow-list.ts +0 -0
- package/src/commands/cli/workflow.ts +11 -798
- package/src/scripts/constants.ts +2 -1
- package/src/sdk/commander.ts +161 -0
- package/src/sdk/components/workflow-picker-panel.tsx +78 -258
- package/src/sdk/define-workflow.test.ts +104 -11
- package/src/sdk/define-workflow.ts +47 -11
- package/src/sdk/errors.test.ts +16 -0
- package/src/sdk/index.ts +8 -8
- package/src/sdk/management-commands.ts +151 -0
- package/src/sdk/registry.ts +132 -0
- package/src/sdk/runtime/attached-footer.ts +1 -1
- package/src/sdk/runtime/executor-env.ts +45 -0
- package/src/sdk/runtime/executor.test.ts +37 -0
- package/src/sdk/runtime/executor.ts +147 -68
- package/src/sdk/types.ts +169 -4
- package/src/sdk/worker-shared.test.ts +163 -0
- package/src/sdk/worker-shared.ts +155 -0
- package/src/sdk/workflow-cli.ts +409 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +1 -1
- package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +1 -1
- package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +1 -1
- package/src/sdk/workflows/builtin/open-claude-design/claude/index.ts +1 -1
- package/src/sdk/workflows/builtin/open-claude-design/copilot/index.ts +1 -1
- package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +1 -1
- package/src/sdk/workflows/builtin/ralph/claude/index.ts +1 -1
- package/src/sdk/workflows/builtin/ralph/copilot/index.ts +1 -1
- package/src/sdk/workflows/builtin/ralph/opencode/index.ts +1 -1
- package/src/sdk/workflows/builtin-registry.ts +23 -0
- package/src/sdk/workflows/index.ts +10 -20
- package/src/services/system/auth.test.ts +63 -1
- package/.agents/skills/workflow-creator/SKILL.md +0 -334
- package/.agents/skills/workflow-creator/references/agent-sessions.md +0 -888
- package/.agents/skills/workflow-creator/references/computation-and-validation.md +0 -201
- package/.agents/skills/workflow-creator/references/control-flow.md +0 -470
- package/.agents/skills/workflow-creator/references/discovery-and-verification.md +0 -232
- package/.agents/skills/workflow-creator/references/failure-modes.md +0 -903
- package/.agents/skills/workflow-creator/references/getting-started.md +0 -275
- package/.agents/skills/workflow-creator/references/running-workflows.md +0 -235
- package/.agents/skills/workflow-creator/references/session-config.md +0 -384
- package/.agents/skills/workflow-creator/references/state-and-data-flow.md +0 -357
- package/.agents/skills/workflow-creator/references/user-input.md +0 -234
- package/.agents/skills/workflow-creator/references/workflow-inputs.md +0 -272
- package/dist/sdk/runtime/discovery.d.ts +0 -132
- package/dist/sdk/runtime/discovery.d.ts.map +0 -1
- package/dist/sdk/runtime/executor-entry.d.ts +0 -11
- package/dist/sdk/runtime/executor-entry.d.ts.map +0 -1
- package/dist/sdk/runtime/loader.d.ts +0 -70
- package/dist/sdk/runtime/loader.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/src/commands/cli/workflow.test.ts +0 -317
- package/src/sdk/runtime/discovery.ts +0 -368
- package/src/sdk/runtime/executor-entry.ts +0 -18
- package/src/sdk/runtime/loader.ts +0 -267
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Lifecycle:
|
|
18
18
|
*
|
|
19
|
-
* const panel = await WorkflowPickerPanel.create({ agent,
|
|
19
|
+
* const panel = await WorkflowPickerPanel.create({ agent, registry });
|
|
20
20
|
* const result = await panel.waitForSelection();
|
|
21
21
|
* panel.destroy();
|
|
22
22
|
* if (result) await executeWorkflow({ ... });
|
|
@@ -41,17 +41,13 @@ import {
|
|
|
41
41
|
import { useState, useEffect, useMemo, useRef, useCallback, useContext, createContext, memo } from "react";
|
|
42
42
|
import { useLatest } from "./hooks.ts";
|
|
43
43
|
import { resolveTheme, type TerminalTheme } from "../runtime/theme.ts";
|
|
44
|
-
import type { AgentType, WorkflowInput } from "../types.ts";
|
|
45
|
-
import type {
|
|
46
|
-
WorkflowWithMetadata,
|
|
47
|
-
WorkflowMetadataStatus,
|
|
48
|
-
} from "../runtime/discovery.ts";
|
|
44
|
+
import type { AgentType, WorkflowInput, WorkflowDefinition, Registry } from "../types.ts";
|
|
49
45
|
import { ErrorBoundary } from "./error-boundary.tsx";
|
|
50
46
|
|
|
51
47
|
// ─── Theme ──────────────────────────────────────
|
|
52
48
|
// The picker uses a slightly extended palette vs. the base terminal theme:
|
|
53
49
|
// an `info` (sky) hue for built-in workflows and a `mauve` hue for global
|
|
54
|
-
// ones — the same distinctions `atomic workflow
|
|
50
|
+
// ones — the same distinctions `atomic workflow list` already draws. The
|
|
55
51
|
// rest is sourced from {@link resolveTheme} so light/dark mode tracks the
|
|
56
52
|
// orchestrator panel.
|
|
57
53
|
export interface PickerTheme {
|
|
@@ -110,42 +106,23 @@ function usePickerTheme(): PickerTheme {
|
|
|
110
106
|
|
|
111
107
|
// ─── Types ──────────────────────────────────────
|
|
112
108
|
|
|
113
|
-
type Source = "local" | "global" | "builtin";
|
|
114
109
|
type Phase = "pick" | "prompt";
|
|
115
110
|
|
|
116
111
|
/** The payload the picker resolves with on successful submission. */
|
|
117
112
|
export interface WorkflowPickerResult {
|
|
118
113
|
/** The workflow the user committed to running. */
|
|
119
|
-
workflow:
|
|
114
|
+
workflow: WorkflowDefinition;
|
|
120
115
|
/** Populated form values, one per declared input (or { prompt } for free-form). */
|
|
121
116
|
inputs: Record<string, string>;
|
|
122
117
|
}
|
|
123
118
|
|
|
124
119
|
// ─── Helpers ────────────────────────────────────
|
|
125
120
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const SOURCE_DIR: Record<Source, string> = {
|
|
133
|
-
local: ".atomic/workflows",
|
|
134
|
-
global: "~/.atomic/workflows",
|
|
135
|
-
builtin: "built-in",
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const SOURCE_COLOR: Record<Source, keyof PickerTheme> = {
|
|
139
|
-
local: "success",
|
|
140
|
-
global: "mauve",
|
|
141
|
-
builtin: "info",
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/** Higher number wins when two workflows share a name. */
|
|
145
|
-
const SOURCE_PRECEDENCE: Record<Source, number> = {
|
|
146
|
-
global: 0,
|
|
147
|
-
local: 1,
|
|
148
|
-
builtin: 2,
|
|
121
|
+
/** Per-agent display color in the picker list / section headers. */
|
|
122
|
+
const AGENT_COLOR: Record<AgentType, keyof PickerTheme> = {
|
|
123
|
+
claude: "warning",
|
|
124
|
+
copilot: "success",
|
|
125
|
+
opencode: "mauve",
|
|
149
126
|
};
|
|
150
127
|
|
|
151
128
|
/**
|
|
@@ -180,39 +157,22 @@ export function fuzzyMatch(query: string, target: string): number | null {
|
|
|
180
157
|
// ─── List Building ──────────────────────────────
|
|
181
158
|
|
|
182
159
|
interface ListEntry {
|
|
183
|
-
workflow:
|
|
184
|
-
section
|
|
160
|
+
workflow: WorkflowDefinition;
|
|
161
|
+
/** Agent the workflow belongs to — used for section grouping. */
|
|
162
|
+
section: AgentType;
|
|
185
163
|
}
|
|
186
164
|
|
|
187
165
|
type ListRow =
|
|
188
|
-
| { kind: "section";
|
|
166
|
+
| { kind: "section"; agent: AgentType }
|
|
189
167
|
| { kind: "entry"; entry: ListEntry };
|
|
190
168
|
|
|
191
|
-
/**
|
|
192
|
-
* Deduplicate workflows by name using builtin > local > global precedence.
|
|
193
|
-
* When two workflows share a name, only the higher-precedence entry is kept.
|
|
194
|
-
*/
|
|
195
|
-
export function deduplicateByName(
|
|
196
|
-
workflows: WorkflowWithMetadata[],
|
|
197
|
-
): WorkflowWithMetadata[] {
|
|
198
|
-
const byName = new Map<string, WorkflowWithMetadata>();
|
|
199
|
-
for (const wf of workflows) {
|
|
200
|
-
const existing = byName.get(wf.name);
|
|
201
|
-
if (!existing || SOURCE_PRECEDENCE[wf.source] > SOURCE_PRECEDENCE[existing.source]) {
|
|
202
|
-
byName.set(wf.name, wf);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return Array.from(byName.values());
|
|
206
|
-
}
|
|
207
|
-
|
|
208
169
|
export function buildEntries(
|
|
209
170
|
query: string,
|
|
210
|
-
workflows:
|
|
171
|
+
workflows: WorkflowDefinition[],
|
|
211
172
|
): ListEntry[] {
|
|
212
|
-
|
|
213
|
-
type Scored = { wf: WorkflowWithMetadata; score: number };
|
|
173
|
+
type Scored = { wf: WorkflowDefinition; score: number };
|
|
214
174
|
const scored: Scored[] = [];
|
|
215
|
-
for (const wf of
|
|
175
|
+
for (const wf of workflows) {
|
|
216
176
|
const nameScore = fuzzyMatch(query, wf.name);
|
|
217
177
|
const descScore = fuzzyMatch(query, wf.description);
|
|
218
178
|
const best =
|
|
@@ -228,11 +188,11 @@ export function buildEntries(
|
|
|
228
188
|
|
|
229
189
|
if (query === "") {
|
|
230
190
|
const rest: ListEntry[] = [];
|
|
231
|
-
for (const
|
|
191
|
+
for (const agent of ["claude", "copilot", "opencode"] as AgentType[]) {
|
|
232
192
|
const group = scored
|
|
233
|
-
.filter((s) => s.wf.
|
|
193
|
+
.filter((s) => s.wf.agent === agent)
|
|
234
194
|
.sort((a, b) => a.wf.name.localeCompare(b.wf.name));
|
|
235
|
-
for (const s of group) rest.push({ workflow: s.wf, section:
|
|
195
|
+
for (const s of group) rest.push({ workflow: s.wf, section: agent });
|
|
236
196
|
}
|
|
237
197
|
return rest;
|
|
238
198
|
}
|
|
@@ -240,7 +200,7 @@ export function buildEntries(
|
|
|
240
200
|
scored.sort((a, b) => a.score - b.score);
|
|
241
201
|
return scored.map<ListEntry>((s) => ({
|
|
242
202
|
workflow: s.wf,
|
|
243
|
-
section: s.wf.
|
|
203
|
+
section: s.wf.agent,
|
|
244
204
|
}));
|
|
245
205
|
}
|
|
246
206
|
|
|
@@ -250,7 +210,7 @@ export function buildRows(entries: ListEntry[], query: string): ListRow[] {
|
|
|
250
210
|
let lastSection: string | null = null;
|
|
251
211
|
for (const e of entries) {
|
|
252
212
|
if (e.section !== lastSection) {
|
|
253
|
-
rows.push({ kind: "section",
|
|
213
|
+
rows.push({ kind: "section", agent: e.section });
|
|
254
214
|
lastSection = e.section;
|
|
255
215
|
}
|
|
256
216
|
rows.push({ kind: "entry", entry: e });
|
|
@@ -261,38 +221,6 @@ export function buildRows(entries: ListEntry[], query: string): ListRow[] {
|
|
|
261
221
|
return rows;
|
|
262
222
|
}
|
|
263
223
|
|
|
264
|
-
// ─── Status helpers ─────────────────────────────
|
|
265
|
-
// Non-ok entries stay visible in the picker so the user sees that a
|
|
266
|
-
// workflow from an older SDK release (or one with a load error) still
|
|
267
|
-
// exists on disk — the previous behaviour silently dropped them, which
|
|
268
|
-
// made user/global workflows appear to vanish after an atomic upgrade.
|
|
269
|
-
|
|
270
|
-
/** Unicode glyph that prefixes a non-ok entry and heads its preview. */
|
|
271
|
-
const STATUS_ICON: Record<WorkflowMetadataStatus["kind"], string> = {
|
|
272
|
-
ok: " ",
|
|
273
|
-
incompatible: "⚠",
|
|
274
|
-
error: "✗",
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
/** Compact single-word label used in list rows and the bottom hint. */
|
|
278
|
-
const STATUS_LABEL: Record<WorkflowMetadataStatus["kind"], string> = {
|
|
279
|
-
ok: "",
|
|
280
|
-
incompatible: "update",
|
|
281
|
-
error: "broken",
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
/** Map each status kind to its semantic palette slot. */
|
|
285
|
-
const STATUS_COLOR: Record<WorkflowMetadataStatus["kind"], keyof PickerTheme> = {
|
|
286
|
-
ok: "success",
|
|
287
|
-
incompatible: "warning",
|
|
288
|
-
error: "error",
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
/** Non-ok rows are inert — Enter / run commands must not transition the picker. */
|
|
292
|
-
function isRunnable(wf: WorkflowWithMetadata): boolean {
|
|
293
|
-
return wf.status.kind === "ok";
|
|
294
|
-
}
|
|
295
|
-
|
|
296
224
|
// ─── Validation ─────────────────────────────────
|
|
297
225
|
|
|
298
226
|
export function isFieldValid(field: WorkflowInput, value: string): boolean {
|
|
@@ -408,20 +336,17 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
408
336
|
<box flexDirection="column">
|
|
409
337
|
{rows.map((row, i) => {
|
|
410
338
|
if (row.kind === "section") {
|
|
411
|
-
const
|
|
339
|
+
const ag = row.agent;
|
|
412
340
|
return (
|
|
413
341
|
<box
|
|
414
|
-
key={`section-${
|
|
342
|
+
key={`section-${ag}`}
|
|
415
343
|
height={2}
|
|
416
344
|
paddingTop={1}
|
|
417
345
|
paddingLeft={2}
|
|
418
346
|
>
|
|
419
347
|
<text>
|
|
420
|
-
<span fg={theme[
|
|
421
|
-
{
|
|
422
|
-
</span>
|
|
423
|
-
<span fg={theme.textDim}>
|
|
424
|
-
{" (" + SOURCE_DIR[src] + ")"}
|
|
348
|
+
<span fg={theme[AGENT_COLOR[ag]]}>
|
|
349
|
+
{ag}
|
|
425
350
|
</span>
|
|
426
351
|
</text>
|
|
427
352
|
</box>
|
|
@@ -430,28 +355,11 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
430
355
|
const entryIdx = entryIndexByRow.get(i) ?? -1;
|
|
431
356
|
const isFocused = entryIdx === focusedEntryIdx;
|
|
432
357
|
const wf = row.entry.workflow;
|
|
433
|
-
const
|
|
434
|
-
const runnable = statusKind === "ok";
|
|
435
|
-
// Status indicator sits between the focus marker and the name so
|
|
436
|
-
// every row shares a 4-character gutter — ok rows render a blank
|
|
437
|
-
// gutter, so the list stays visually flush while non-ok rows
|
|
438
|
-
// always occupy a fixed slot (no layout jitter when filtering).
|
|
439
|
-
const statusIcon = STATUS_ICON[statusKind];
|
|
440
|
-
const statusCol = theme[STATUS_COLOR[statusKind]];
|
|
441
|
-
// Non-ok rows fade the name so the "diagnostic, not selectable"
|
|
442
|
-
// read is immediate even when the user hasn't reached the row
|
|
443
|
-
// yet — the eye catches the warning glyph + dim text together.
|
|
444
|
-
const nameCol = runnable
|
|
445
|
-
? isFocused
|
|
446
|
-
? theme.text
|
|
447
|
-
: theme.textMuted
|
|
448
|
-
: isFocused
|
|
449
|
-
? theme.textMuted
|
|
450
|
-
: theme.textDim;
|
|
358
|
+
const nameCol = isFocused ? theme.text : theme.textMuted;
|
|
451
359
|
|
|
452
360
|
return (
|
|
453
361
|
<box
|
|
454
|
-
key={`wf-${wf.name}`}
|
|
362
|
+
key={`wf-${wf.agent}-${wf.name}`}
|
|
455
363
|
height={1}
|
|
456
364
|
flexDirection="row"
|
|
457
365
|
backgroundColor={isFocused ? theme.border : "transparent"}
|
|
@@ -462,9 +370,6 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
462
370
|
<span fg={isFocused ? theme.primary : theme.textDim}>
|
|
463
371
|
{isFocused ? "▸ " : " "}
|
|
464
372
|
</span>
|
|
465
|
-
<span fg={statusCol}>
|
|
466
|
-
{statusIcon + " "}
|
|
467
|
-
</span>
|
|
468
373
|
<span fg={nameCol}>
|
|
469
374
|
{wf.name}
|
|
470
375
|
</span>
|
|
@@ -525,67 +430,13 @@ const ArgumentRow = memo(function ArgumentRow({
|
|
|
525
430
|
);
|
|
526
431
|
});
|
|
527
432
|
|
|
528
|
-
/**
|
|
529
|
-
* Diagnostic block for non-ok workflows. Replaces the description +
|
|
530
|
-
* arguments sections in the preview pane so the user sees *why* a row
|
|
531
|
-
* is inert and what to do about it, instead of an empty-looking panel.
|
|
532
|
-
*/
|
|
533
|
-
const StatusDiagnostic = memo(function StatusDiagnostic({
|
|
534
|
-
status,
|
|
535
|
-
}: {
|
|
536
|
-
status: Exclude<WorkflowMetadataStatus, { kind: "ok" }>;
|
|
537
|
-
}) {
|
|
538
|
-
const theme = usePickerTheme();
|
|
539
|
-
const color = theme[STATUS_COLOR[status.kind]];
|
|
540
|
-
const icon = STATUS_ICON[status.kind];
|
|
541
|
-
|
|
542
|
-
// Headline + sub-copy split: the headline is terse (three words) so
|
|
543
|
-
// it reads at a glance even on a narrow right pane; the sub-copy
|
|
544
|
-
// explains *why* and the third paragraph tells the user what to do.
|
|
545
|
-
const headline =
|
|
546
|
-
status.kind === "incompatible"
|
|
547
|
-
? "update required"
|
|
548
|
-
: "failed to load";
|
|
549
|
-
const detail =
|
|
550
|
-
status.kind === "incompatible"
|
|
551
|
-
? `Needs Atomic v${status.requiredVersion}. Installed: v${status.currentVersion}.`
|
|
552
|
-
: status.message;
|
|
553
|
-
const remediation =
|
|
554
|
-
status.kind === "incompatible"
|
|
555
|
-
? "Update Atomic, or re-save the workflow against the current SDK."
|
|
556
|
-
: "Open the workflow file and fix the error above.";
|
|
557
|
-
|
|
558
|
-
return (
|
|
559
|
-
<box flexDirection="column">
|
|
560
|
-
<text>
|
|
561
|
-
<span fg={color}>
|
|
562
|
-
<strong>{icon + " " + headline}</strong>
|
|
563
|
-
</span>
|
|
564
|
-
</text>
|
|
565
|
-
|
|
566
|
-
<box height={1} />
|
|
567
|
-
|
|
568
|
-
<text>
|
|
569
|
-
<span fg={theme.textMuted}>{detail}</span>
|
|
570
|
-
</text>
|
|
571
|
-
|
|
572
|
-
<box height={1} />
|
|
573
|
-
|
|
574
|
-
<text>
|
|
575
|
-
<span fg={theme.textDim}>{remediation}</span>
|
|
576
|
-
</text>
|
|
577
|
-
</box>
|
|
578
|
-
);
|
|
579
|
-
});
|
|
580
|
-
|
|
581
433
|
const Preview = memo(function Preview({
|
|
582
434
|
wf,
|
|
583
435
|
}: {
|
|
584
|
-
wf:
|
|
436
|
+
wf: WorkflowDefinition;
|
|
585
437
|
}) {
|
|
586
438
|
const theme = usePickerTheme();
|
|
587
439
|
const args = wf.inputs;
|
|
588
|
-
const status = wf.status;
|
|
589
440
|
|
|
590
441
|
return (
|
|
591
442
|
<box
|
|
@@ -603,37 +454,28 @@ const Preview = memo(function Preview({
|
|
|
603
454
|
<box height={1} />
|
|
604
455
|
|
|
605
456
|
<text>
|
|
606
|
-
<span fg={theme[
|
|
607
|
-
{
|
|
608
|
-
</span>
|
|
609
|
-
<span fg={theme.textDim}>
|
|
610
|
-
{" (" + SOURCE_DIR[wf.source] + ")"}
|
|
457
|
+
<span fg={theme[AGENT_COLOR[wf.agent]]}>
|
|
458
|
+
{wf.agent}
|
|
611
459
|
</span>
|
|
612
460
|
</text>
|
|
613
461
|
|
|
614
462
|
<box height={2} />
|
|
615
463
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
</span>
|
|
622
|
-
</text>
|
|
464
|
+
<text>
|
|
465
|
+
<span fg={theme.textMuted}>
|
|
466
|
+
{wf.description || "(no description)"}
|
|
467
|
+
</span>
|
|
468
|
+
</text>
|
|
623
469
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
</>
|
|
633
|
-
)}
|
|
470
|
+
{args.length > 0 && (
|
|
471
|
+
<>
|
|
472
|
+
<box height={2} />
|
|
473
|
+
<SectionLabel label="ARGUMENTS" />
|
|
474
|
+
<box height={1} />
|
|
475
|
+
{args.map((f) => (
|
|
476
|
+
<ArgumentRow key={f.name} field={f} />
|
|
477
|
+
))}
|
|
634
478
|
</>
|
|
635
|
-
) : (
|
|
636
|
-
<StatusDiagnostic status={status} />
|
|
637
479
|
)}
|
|
638
480
|
</box>
|
|
639
481
|
);
|
|
@@ -664,16 +506,25 @@ function EmptyPreview({
|
|
|
664
506
|
</text>
|
|
665
507
|
<box height={2} />
|
|
666
508
|
<text>
|
|
667
|
-
<span fg={theme.textDim}>
|
|
509
|
+
<span fg={theme.textDim}>
|
|
510
|
+
define one with{" "}
|
|
511
|
+
</span>
|
|
512
|
+
<span fg={theme.primary}>defineWorkflow(...).for<"agent">(...)</span>
|
|
513
|
+
<span fg={theme.textDim}>,</span>
|
|
668
514
|
</text>
|
|
669
515
|
<box height={1} />
|
|
670
|
-
<
|
|
671
|
-
<
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
516
|
+
<text>
|
|
517
|
+
<span fg={theme.textDim}>
|
|
518
|
+
then register and start it via{" "}
|
|
519
|
+
</span>
|
|
520
|
+
<span fg={theme.primary}>createRegistry().register(wf)</span>
|
|
521
|
+
</text>
|
|
522
|
+
<box height={1} />
|
|
523
|
+
<text>
|
|
524
|
+
<span fg={theme.textDim}>{"and "}</span>
|
|
525
|
+
<span fg={theme.primary}>createWorkflowCli(registry).run()</span>
|
|
526
|
+
<span fg={theme.textDim}>{" in your entrypoint"}</span>
|
|
527
|
+
</text>
|
|
677
528
|
</box>
|
|
678
529
|
);
|
|
679
530
|
}
|
|
@@ -912,7 +763,7 @@ function InputPhase({
|
|
|
912
763
|
focusedFieldIdx,
|
|
913
764
|
onFieldInput,
|
|
914
765
|
}: {
|
|
915
|
-
workflow:
|
|
766
|
+
workflow: WorkflowDefinition;
|
|
916
767
|
agent: AgentType;
|
|
917
768
|
fields: readonly WorkflowInput[];
|
|
918
769
|
values: Record<string, string>;
|
|
@@ -998,13 +849,6 @@ function InputPhase({
|
|
|
998
849
|
</span>
|
|
999
850
|
<span fg={theme.textDim}>{" · "}</span>
|
|
1000
851
|
<span fg={theme.mauve}>{agent}</span>
|
|
1001
|
-
<span fg={theme.textDim}>{" · "}</span>
|
|
1002
|
-
<span fg={theme[SOURCE_COLOR[workflow.source]]}>
|
|
1003
|
-
{SOURCE_DISPLAY[workflow.source]}
|
|
1004
|
-
</span>
|
|
1005
|
-
<span fg={theme.textDim}>
|
|
1006
|
-
{" (" + SOURCE_DIR[workflow.source] + ")"}
|
|
1007
|
-
</span>
|
|
1008
852
|
</text>
|
|
1009
853
|
<box height={1} />
|
|
1010
854
|
<text>
|
|
@@ -1076,7 +920,7 @@ function ConfirmModal({
|
|
|
1076
920
|
workflow,
|
|
1077
921
|
agent,
|
|
1078
922
|
}: {
|
|
1079
|
-
workflow:
|
|
923
|
+
workflow: WorkflowDefinition;
|
|
1080
924
|
agent: AgentType;
|
|
1081
925
|
}) {
|
|
1082
926
|
const theme = usePickerTheme();
|
|
@@ -1148,14 +992,6 @@ const PICK_HINTS: Hint[] = [
|
|
|
1148
992
|
{ key: "↵", label: "select" },
|
|
1149
993
|
{ key: "esc", label: "quit" },
|
|
1150
994
|
];
|
|
1151
|
-
// Shown instead of PICK_HINTS when the focused row is non-ok — the dim
|
|
1152
|
-
// `↵ unavailable` reads immediately as "this row is navigable but not
|
|
1153
|
-
// runnable", which matches the muted row colour and preview diagnostic.
|
|
1154
|
-
const PICK_HINTS_UNAVAILABLE: Hint[] = [
|
|
1155
|
-
{ key: "↑↓", label: "navigate" },
|
|
1156
|
-
{ key: "↵", label: "unavailable", dim: true },
|
|
1157
|
-
{ key: "esc", label: "quit" },
|
|
1158
|
-
];
|
|
1159
995
|
const CONFIRM_HINTS: Hint[] = [
|
|
1160
996
|
{ key: "y", label: "submit" },
|
|
1161
997
|
{ key: "n", label: "cancel" },
|
|
@@ -1238,7 +1074,7 @@ const Statusline = memo(function Statusline({
|
|
|
1238
1074
|
phase: Phase;
|
|
1239
1075
|
confirmOpen: boolean;
|
|
1240
1076
|
hints: { key: string; label: string; dim?: boolean }[];
|
|
1241
|
-
focusedWf:
|
|
1077
|
+
focusedWf: WorkflowDefinition | undefined;
|
|
1242
1078
|
}) {
|
|
1243
1079
|
const theme = usePickerTheme();
|
|
1244
1080
|
const modeLabel = confirmOpen
|
|
@@ -1268,26 +1104,9 @@ const Statusline = memo(function Statusline({
|
|
|
1268
1104
|
{focusedWf ? (
|
|
1269
1105
|
<box paddingLeft={1} paddingRight={1} alignItems="center">
|
|
1270
1106
|
<text>
|
|
1271
|
-
{
|
|
1272
|
-
<span fg={theme[STATUS_COLOR[focusedWf.status.kind]]}>
|
|
1273
|
-
{STATUS_ICON[focusedWf.status.kind] + " "}
|
|
1274
|
-
</span>
|
|
1275
|
-
) : null}
|
|
1276
|
-
<span
|
|
1277
|
-
fg={
|
|
1278
|
-
focusedWf.status.kind === "ok" ? theme.text : theme.textMuted
|
|
1279
|
-
}
|
|
1280
|
-
>
|
|
1107
|
+
<span fg={theme.text}>
|
|
1281
1108
|
{focusedWf.name}
|
|
1282
1109
|
</span>
|
|
1283
|
-
{focusedWf.status.kind !== "ok" ? (
|
|
1284
|
-
<>
|
|
1285
|
-
<span fg={theme.textDim}>{" · "}</span>
|
|
1286
|
-
<span fg={theme[STATUS_COLOR[focusedWf.status.kind]]}>
|
|
1287
|
-
{STATUS_LABEL[focusedWf.status.kind]}
|
|
1288
|
-
</span>
|
|
1289
|
-
</>
|
|
1290
|
-
) : null}
|
|
1291
1110
|
</text>
|
|
1292
1111
|
</box>
|
|
1293
1112
|
) : null}
|
|
@@ -1321,7 +1140,7 @@ interface PickerKeyboardState {
|
|
|
1321
1140
|
entries: ListEntry[];
|
|
1322
1141
|
clampedEntryIdx: number;
|
|
1323
1142
|
savedEntryIdx: number;
|
|
1324
|
-
focusedWf:
|
|
1143
|
+
focusedWf: WorkflowDefinition | undefined;
|
|
1325
1144
|
fieldValues: Record<string, string>;
|
|
1326
1145
|
isFormValid: boolean;
|
|
1327
1146
|
invalidFieldIndices: number[];
|
|
@@ -1400,10 +1219,7 @@ function usePickerKeyboard(state: PickerKeyboardState): void {
|
|
|
1400
1219
|
if (key.name === "return") {
|
|
1401
1220
|
key.stopPropagation();
|
|
1402
1221
|
const wf = focusedWfRef.current;
|
|
1403
|
-
|
|
1404
|
-
// preview pane already explains the failure; advancing into the
|
|
1405
|
-
// prompt phase would be misleading since the workflow can't run.
|
|
1406
|
-
if (wf && isRunnable(wf)) {
|
|
1222
|
+
if (wf) {
|
|
1407
1223
|
const initial: Record<string, string> = {};
|
|
1408
1224
|
for (const f of wf.inputs) {
|
|
1409
1225
|
initial[f.name] =
|
|
@@ -1494,7 +1310,7 @@ function usePickerKeyboard(state: PickerKeyboardState): void {
|
|
|
1494
1310
|
interface PickerAppProps {
|
|
1495
1311
|
theme: PickerTheme;
|
|
1496
1312
|
agent: AgentType;
|
|
1497
|
-
workflows:
|
|
1313
|
+
workflows: WorkflowDefinition[];
|
|
1498
1314
|
onSubmit: (result: WorkflowPickerResult) => void;
|
|
1499
1315
|
onCancel: () => void;
|
|
1500
1316
|
}
|
|
@@ -1570,13 +1386,10 @@ export function WorkflowPicker({
|
|
|
1570
1386
|
setConfirmOpen,
|
|
1571
1387
|
});
|
|
1572
1388
|
|
|
1573
|
-
const focusedIsRunnable = focusedWf ? isRunnable(focusedWf) : true;
|
|
1574
1389
|
const hints = confirmOpen
|
|
1575
1390
|
? CONFIRM_HINTS
|
|
1576
1391
|
: phase === "pick"
|
|
1577
|
-
?
|
|
1578
|
-
? PICK_HINTS
|
|
1579
|
-
: PICK_HINTS_UNAVAILABLE
|
|
1392
|
+
? PICK_HINTS
|
|
1580
1393
|
: isFormValid
|
|
1581
1394
|
? PROMPT_HINTS_VALID
|
|
1582
1395
|
: PROMPT_HINTS_INVALID;
|
|
@@ -1652,8 +1465,11 @@ export function WorkflowPicker({
|
|
|
1652
1465
|
|
|
1653
1466
|
export interface WorkflowPickerPanelOptions {
|
|
1654
1467
|
agent: AgentType;
|
|
1655
|
-
/**
|
|
1656
|
-
|
|
1468
|
+
/**
|
|
1469
|
+
* Registry of compiled workflow definitions. The panel calls
|
|
1470
|
+
* `registry.list()` and filters to the selected `agent`.
|
|
1471
|
+
*/
|
|
1472
|
+
registry: Registry<Record<string, WorkflowDefinition>>;
|
|
1657
1473
|
}
|
|
1658
1474
|
|
|
1659
1475
|
/**
|
|
@@ -1680,6 +1496,10 @@ export class WorkflowPickerPanel {
|
|
|
1680
1496
|
|
|
1681
1497
|
const isDark = renderer.themeMode !== "light";
|
|
1682
1498
|
const theme = buildPickerTheme(resolveTheme(renderer.themeMode), isDark);
|
|
1499
|
+
// Filter registry to only the workflows for the selected agent.
|
|
1500
|
+
const workflows = options.registry
|
|
1501
|
+
.list()
|
|
1502
|
+
.filter((wf) => wf.agent === options.agent);
|
|
1683
1503
|
this.root = createRoot(renderer);
|
|
1684
1504
|
this.root.render(
|
|
1685
1505
|
<ErrorBoundary
|
|
@@ -1702,7 +1522,7 @@ export class WorkflowPickerPanel {
|
|
|
1702
1522
|
<WorkflowPicker
|
|
1703
1523
|
theme={theme}
|
|
1704
1524
|
agent={options.agent}
|
|
1705
|
-
workflows={
|
|
1525
|
+
workflows={workflows}
|
|
1706
1526
|
onSubmit={(result) => this.handleSubmit(result)}
|
|
1707
1527
|
onCancel={() => this.handleCancel()}
|
|
1708
1528
|
/>
|