@bastani/atomic 0.5.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +956 -0
- package/assets/settings.schema.json +52 -0
- package/package.json +68 -0
- package/src/cli.ts +197 -0
- package/src/commands/cli/chat/client.ts +18 -0
- package/src/commands/cli/chat/index.ts +247 -0
- package/src/commands/cli/chat.ts +8 -0
- package/src/commands/cli/config.ts +55 -0
- package/src/commands/cli/init/index.ts +452 -0
- package/src/commands/cli/init/onboarding.ts +45 -0
- package/src/commands/cli/init/scm.ts +190 -0
- package/src/commands/cli/init.ts +8 -0
- package/src/commands/cli/update.ts +46 -0
- package/src/commands/cli/workflow.ts +164 -0
- package/src/lib/merge.ts +65 -0
- package/src/lib/path-root-guard.ts +38 -0
- package/src/lib/spawn.ts +467 -0
- package/src/scripts/bump-version.ts +94 -0
- package/src/scripts/constants-base.ts +14 -0
- package/src/scripts/constants.ts +34 -0
- package/src/sdk/components/color-utils.ts +20 -0
- package/src/sdk/components/connectors.test.ts +661 -0
- package/src/sdk/components/connectors.ts +156 -0
- package/src/sdk/components/edge.tsx +11 -0
- package/src/sdk/components/error-boundary.tsx +38 -0
- package/src/sdk/components/graph-theme.ts +36 -0
- package/src/sdk/components/header.tsx +60 -0
- package/src/sdk/components/layout.test.ts +924 -0
- package/src/sdk/components/layout.ts +186 -0
- package/src/sdk/components/node-card.tsx +68 -0
- package/src/sdk/components/orchestrator-panel-contexts.ts +26 -0
- package/src/sdk/components/orchestrator-panel-store.test.ts +561 -0
- package/src/sdk/components/orchestrator-panel-store.ts +118 -0
- package/src/sdk/components/orchestrator-panel-types.ts +21 -0
- package/src/sdk/components/orchestrator-panel.tsx +143 -0
- package/src/sdk/components/session-graph-panel.tsx +364 -0
- package/src/sdk/components/status-helpers.ts +32 -0
- package/src/sdk/components/statusline.tsx +63 -0
- package/src/sdk/define-workflow.ts +98 -0
- package/src/sdk/errors.ts +39 -0
- package/src/sdk/index.ts +38 -0
- package/src/sdk/providers/claude.ts +316 -0
- package/src/sdk/providers/copilot.ts +43 -0
- package/src/sdk/providers/opencode.ts +43 -0
- package/src/sdk/runtime/discovery.ts +172 -0
- package/src/sdk/runtime/executor.test.ts +415 -0
- package/src/sdk/runtime/executor.ts +695 -0
- package/src/sdk/runtime/loader.ts +372 -0
- package/src/sdk/runtime/panel.tsx +9 -0
- package/src/sdk/runtime/theme.ts +76 -0
- package/src/sdk/runtime/tmux.ts +542 -0
- package/src/sdk/types.ts +114 -0
- package/src/sdk/workflows.ts +85 -0
- package/src/services/config/atomic-config.ts +124 -0
- package/src/services/config/atomic-global-config.ts +361 -0
- package/src/services/config/config-path.ts +19 -0
- package/src/services/config/definitions.ts +176 -0
- package/src/services/config/index.ts +7 -0
- package/src/services/config/settings-schema.ts +2 -0
- package/src/services/config/settings.ts +149 -0
- package/src/services/system/copy.ts +381 -0
- package/src/services/system/detect.ts +161 -0
- package/src/services/system/download.ts +325 -0
- package/src/services/system/file-lock.ts +289 -0
- package/src/services/system/skills.ts +67 -0
- package/src/theme/colors.ts +25 -0
- package/src/version.ts +7 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Loader — multi-stage pipeline for resolving and loading workflows.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline: Discover → Resolve → Validate → Load
|
|
5
|
+
*
|
|
6
|
+
* Each stage returns a typed discriminated result so callers get structured
|
|
7
|
+
* error information without try/catch guesswork.
|
|
8
|
+
*
|
|
9
|
+
* Discovery (finding workflow files on disk) remains in `discovery.ts`.
|
|
10
|
+
* This module handles everything after a workflow is discovered.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { join, dirname } from "path";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
import type { WorkflowDefinition, AgentType } from "../types.ts";
|
|
16
|
+
import type { DiscoveredWorkflow } from "./discovery.ts";
|
|
17
|
+
import { validateCopilotWorkflow } from "../providers/copilot.ts";
|
|
18
|
+
import { validateOpenCodeWorkflow } from "../providers/opencode.ts";
|
|
19
|
+
import { validateClaudeWorkflow } from "../providers/claude.ts";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// SDK module resolver
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
// Absolute path to the currently-running atomic CLI's own SDK source tree
|
|
26
|
+
// (i.e. `<install_root>/src/sdk`). Computed from this file's URL so it always
|
|
27
|
+
// points at the actual installed atomic, regardless of how it was launched
|
|
28
|
+
// (dev checkout, global `bun install -g atomic`, `bunx atomic`, etc).
|
|
29
|
+
const ATOMIC_SDK_DIR = Bun.fileURLToPath(new URL("..", import.meta.url));
|
|
30
|
+
|
|
31
|
+
// Directory of this loader file. Used as the parent for `Bun.resolveSync`
|
|
32
|
+
// when delegating non-`atomic/*` bare specifiers, so workflows resolve them
|
|
33
|
+
// against atomic's own `node_modules` tree.
|
|
34
|
+
const LOADER_DIR = Bun.fileURLToPath(new URL(".", import.meta.url));
|
|
35
|
+
|
|
36
|
+
// Common TypeScript/JavaScript extensions tried for `atomic/<subpath>`
|
|
37
|
+
// imports when the specifier doesn't already include one.
|
|
38
|
+
const ATOMIC_SUBPATH_EXTS = [".ts", ".tsx", ".js", ".jsx"];
|
|
39
|
+
|
|
40
|
+
// Workflow roots for which a Bun.plugin onLoad hook has already been
|
|
41
|
+
// registered. Deduped so repeated `load()` calls don't stack plugins.
|
|
42
|
+
const registeredRoots = new Set<string>();
|
|
43
|
+
|
|
44
|
+
function escapeRegex(s: string): string {
|
|
45
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve `atomic/<subpath>` (or bare `atomic`) to an absolute file path in
|
|
50
|
+
* the running CLI's `src/sdk/` tree. Returns `null` if no matching file
|
|
51
|
+
* exists so the caller can fall back to the original specifier.
|
|
52
|
+
*/
|
|
53
|
+
function resolveAtomicSubpath(subpath: string): string | null {
|
|
54
|
+
const direct = join(ATOMIC_SDK_DIR, subpath);
|
|
55
|
+
if (existsSync(direct)) return direct;
|
|
56
|
+
for (const ext of ATOMIC_SUBPATH_EXTS) {
|
|
57
|
+
const candidate = join(ATOMIC_SDK_DIR, subpath + ext);
|
|
58
|
+
if (existsSync(candidate)) return candidate;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a bare import specifier to an absolute file path. `atomic/<x>`
|
|
65
|
+
* maps onto the SDK source tree; everything else delegates to atomic's own
|
|
66
|
+
* module resolution context (so `@github/copilot-sdk`, `zod`, etc. resolve
|
|
67
|
+
* from atomic's installed `node_modules`).
|
|
68
|
+
*/
|
|
69
|
+
const ATOMIC_PKG = "@bastani/atomic";
|
|
70
|
+
const ATOMIC_PKG_PREFIX = `${ATOMIC_PKG}/`;
|
|
71
|
+
|
|
72
|
+
function resolveBareSpecifier(spec: string): string | null {
|
|
73
|
+
if (spec === ATOMIC_PKG) return resolveAtomicSubpath("index");
|
|
74
|
+
if (spec.startsWith(ATOMIC_PKG_PREFIX)) {
|
|
75
|
+
return resolveAtomicSubpath(spec.slice(ATOMIC_PKG_PREFIX.length));
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return Bun.resolveSync(spec, LOADER_DIR);
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Rewrite bare `import`/`from` specifiers in workflow source to absolute
|
|
86
|
+
* file paths resolved from atomic's own context. Relative and absolute
|
|
87
|
+
* paths are left untouched. Unresolvable specifiers are also left alone so
|
|
88
|
+
* Bun produces its standard "module not found" error instead of a silent
|
|
89
|
+
* miss.
|
|
90
|
+
*/
|
|
91
|
+
function rewriteBareImports(source: string): string {
|
|
92
|
+
// Matches: from "spec" | from 'spec' | import "spec" | import 'spec'
|
|
93
|
+
// The backreference (\2) ensures matched quotes are balanced.
|
|
94
|
+
const importRe = /(\bfrom\s*|\bimport\s*)(["'])([^"']+)\2/g;
|
|
95
|
+
return source.replace(importRe, (match, kw: string, quote: string, spec: string) => {
|
|
96
|
+
if (spec.startsWith(".") || spec.startsWith("/")) return match;
|
|
97
|
+
const resolved = resolveBareSpecifier(spec);
|
|
98
|
+
return resolved ? `${kw}${quote}${resolved}${quote}` : match;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Register a Bun `onLoad` plugin that rewrites bare imports inside any TS
|
|
104
|
+
* file under the given workflow root. This lets workflow authors write
|
|
105
|
+
* `import { defineWorkflow } from "@bastani/atomic/workflows"` (and import
|
|
106
|
+
* atomic's transitive deps like `@github/copilot-sdk`) without maintaining
|
|
107
|
+
* their own `package.json` / `node_modules`.
|
|
108
|
+
*
|
|
109
|
+
* Why source rewriting via `onLoad` instead of `onResolve`?
|
|
110
|
+
* Bun's runtime plugin API honors `onLoad` but silently ignores `onResolve`
|
|
111
|
+
* hooks for dynamic `await import()` calls — `onResolve` only fires during
|
|
112
|
+
* `Bun.build`. Source rewriting is the only mechanism that actually changes
|
|
113
|
+
* how the runtime loader resolves imports inside workflow files today.
|
|
114
|
+
*
|
|
115
|
+
* Each distinct workflow root installs its own plugin (deduped via
|
|
116
|
+
* `registeredRoots`). The plugin's filter is a path-prefix regex so we
|
|
117
|
+
* don't incur the cost of reading every `.ts` file in the process — only
|
|
118
|
+
* files under active workflow roots are intercepted.
|
|
119
|
+
*/
|
|
120
|
+
function installAtomicLoader(workflowRoot: string): void {
|
|
121
|
+
if (registeredRoots.has(workflowRoot)) return;
|
|
122
|
+
registeredRoots.add(workflowRoot);
|
|
123
|
+
|
|
124
|
+
// Match `<workflowRoot>/<...>/*.ts(x)`. Uses a character class for the
|
|
125
|
+
// separator so the same filter works on both POSIX and Windows paths.
|
|
126
|
+
const filter = new RegExp(
|
|
127
|
+
"^" + escapeRegex(workflowRoot) + "[/\\\\].*\\.tsx?$",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
Bun.plugin({
|
|
131
|
+
name: `atomic-sdk-rewriter:${workflowRoot}`,
|
|
132
|
+
setup(build) {
|
|
133
|
+
build.onLoad({ filter }, async (args) => {
|
|
134
|
+
const source = await Bun.file(args.path).text();
|
|
135
|
+
const contents = rewriteBareImports(source);
|
|
136
|
+
const loader = args.path.endsWith("x") ? "tsx" : "ts";
|
|
137
|
+
return { contents, loader };
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export namespace WorkflowLoader {
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Result types
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
/** Successful pipeline result. */
|
|
149
|
+
export type Ok<T> = { ok: true; value: T };
|
|
150
|
+
|
|
151
|
+
/** Failed pipeline result with stage and error context. */
|
|
152
|
+
export type StageError<S extends string> = {
|
|
153
|
+
ok: false;
|
|
154
|
+
stage: S;
|
|
155
|
+
error: unknown;
|
|
156
|
+
message: string;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export type StageResult<T, S extends string> = Ok<T> | StageError<S>;
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Stage data types
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
/** Input to the pipeline — a discovered workflow from disk. */
|
|
166
|
+
export type Plan = DiscoveredWorkflow;
|
|
167
|
+
|
|
168
|
+
/** Output of the resolve stage. */
|
|
169
|
+
export type Resolved = Plan;
|
|
170
|
+
|
|
171
|
+
/** A source validation warning (agent-specific). */
|
|
172
|
+
export interface ValidationWarning {
|
|
173
|
+
rule: string;
|
|
174
|
+
message: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Output of the validate stage. */
|
|
178
|
+
export type Validated = Resolved & {
|
|
179
|
+
warnings: ValidationWarning[];
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/** Output of the load stage — the final result. */
|
|
183
|
+
export type Loaded = Validated & {
|
|
184
|
+
definition: WorkflowDefinition;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Report callbacks — callers provide these for logging/UI
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
export interface Report {
|
|
192
|
+
/** Called when a stage begins. */
|
|
193
|
+
start?: (stage: "resolve" | "validate" | "load") => void;
|
|
194
|
+
/** Called when source validation produces warnings. */
|
|
195
|
+
warn?: (warnings: ValidationWarning[]) => void;
|
|
196
|
+
/** Called when a stage fails. */
|
|
197
|
+
error?: (stage: "resolve" | "validate" | "load", error: unknown, message: string) => void;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Stage 1: Resolve
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
/** Verify the workflow file exists. */
|
|
205
|
+
export async function resolve(
|
|
206
|
+
plan: Plan,
|
|
207
|
+
): Promise<StageResult<Resolved, "resolve">> {
|
|
208
|
+
try {
|
|
209
|
+
const file = Bun.file(plan.path);
|
|
210
|
+
if (!(await file.exists())) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
stage: "resolve",
|
|
214
|
+
error: new Error(`Workflow file not found: ${plan.path}`),
|
|
215
|
+
message: `Workflow file not found: ${plan.path}`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return { ok: true, value: plan };
|
|
219
|
+
} catch (error) {
|
|
220
|
+
return {
|
|
221
|
+
ok: false,
|
|
222
|
+
stage: "resolve",
|
|
223
|
+
error,
|
|
224
|
+
message: error instanceof Error ? error.message : String(error),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Stage 2: Validate
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
/** Run agent-specific source validation. */
|
|
234
|
+
function validateSource(source: string, agent: AgentType): ValidationWarning[] {
|
|
235
|
+
switch (agent) {
|
|
236
|
+
case "copilot":
|
|
237
|
+
return validateCopilotWorkflow(source);
|
|
238
|
+
case "opencode":
|
|
239
|
+
return validateOpenCodeWorkflow(source);
|
|
240
|
+
case "claude":
|
|
241
|
+
return validateClaudeWorkflow(source);
|
|
242
|
+
default:
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Read the workflow source and run agent-specific validation checks.
|
|
249
|
+
* Validation warnings are non-fatal — the pipeline continues.
|
|
250
|
+
*/
|
|
251
|
+
export async function validate(
|
|
252
|
+
resolved: Resolved,
|
|
253
|
+
): Promise<StageResult<Validated, "validate">> {
|
|
254
|
+
try {
|
|
255
|
+
const source = await Bun.file(resolved.path).text();
|
|
256
|
+
const warnings = validateSource(source, resolved.agent);
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
ok: true,
|
|
260
|
+
value: { ...resolved, warnings },
|
|
261
|
+
};
|
|
262
|
+
} catch (error) {
|
|
263
|
+
return {
|
|
264
|
+
ok: false,
|
|
265
|
+
stage: "validate",
|
|
266
|
+
error,
|
|
267
|
+
message: error instanceof Error ? error.message : String(error),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// Stage 3: Load
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Import the workflow module and extract the WorkflowDefinition.
|
|
278
|
+
* Checks for common authoring mistakes (missing `.compile()`, wrong export).
|
|
279
|
+
*/
|
|
280
|
+
export async function load(
|
|
281
|
+
validated: Validated,
|
|
282
|
+
): Promise<StageResult<Loaded, "load">> {
|
|
283
|
+
try {
|
|
284
|
+
// The workflow root is two levels up from <root>/<agent>/index.ts,
|
|
285
|
+
// which covers agent dirs and any sibling `helpers/` folders.
|
|
286
|
+
installAtomicLoader(dirname(dirname(validated.path)));
|
|
287
|
+
const mod = await import(validated.path);
|
|
288
|
+
const definition = mod.default ?? mod;
|
|
289
|
+
|
|
290
|
+
if (!definition || definition.__brand !== "WorkflowDefinition") {
|
|
291
|
+
if (definition && definition.__brand === "WorkflowBuilder") {
|
|
292
|
+
return {
|
|
293
|
+
ok: false,
|
|
294
|
+
stage: "load",
|
|
295
|
+
error: new Error("Workflow not compiled"),
|
|
296
|
+
message:
|
|
297
|
+
`Workflow at ${validated.path} was defined but not compiled.\n` +
|
|
298
|
+
` Add .compile() at the end of your defineWorkflow() chain:\n\n` +
|
|
299
|
+
` export default defineWorkflow({ ... })\n` +
|
|
300
|
+
` .session({ ... })\n` +
|
|
301
|
+
` .compile();`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
stage: "load",
|
|
308
|
+
error: new Error("Invalid workflow export"),
|
|
309
|
+
message:
|
|
310
|
+
`${validated.path} does not export a valid WorkflowDefinition.\n` +
|
|
311
|
+
` Make sure it exports defineWorkflow(...).compile() as the default export.`,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
ok: true,
|
|
317
|
+
value: { ...validated, definition: definition as WorkflowDefinition },
|
|
318
|
+
};
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return {
|
|
321
|
+
ok: false,
|
|
322
|
+
stage: "load",
|
|
323
|
+
error,
|
|
324
|
+
message: error instanceof Error ? error.message : String(error),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// Full pipeline
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Run the full pipeline: resolve → validate → load.
|
|
335
|
+
*
|
|
336
|
+
* Returns a structured result with the loaded WorkflowDefinition on success,
|
|
337
|
+
* or a stage-specific error on failure.
|
|
338
|
+
*/
|
|
339
|
+
export async function loadWorkflow(
|
|
340
|
+
plan: Plan,
|
|
341
|
+
report?: Report,
|
|
342
|
+
): Promise<StageResult<Loaded, "resolve" | "validate" | "load">> {
|
|
343
|
+
// Stage 1: Resolve
|
|
344
|
+
report?.start?.("resolve");
|
|
345
|
+
const resolved = await resolve(plan);
|
|
346
|
+
if (!resolved.ok) {
|
|
347
|
+
report?.error?.("resolve", resolved.error, resolved.message);
|
|
348
|
+
return resolved;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Stage 2: Validate
|
|
352
|
+
report?.start?.("validate");
|
|
353
|
+
const validated = await validate(resolved.value);
|
|
354
|
+
if (!validated.ok) {
|
|
355
|
+
report?.error?.("validate", validated.error, validated.message);
|
|
356
|
+
return validated;
|
|
357
|
+
}
|
|
358
|
+
if (validated.value.warnings.length > 0) {
|
|
359
|
+
report?.warn?.(validated.value.warnings);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Stage 3: Load
|
|
363
|
+
report?.start?.("load");
|
|
364
|
+
const loaded = await load(validated.value);
|
|
365
|
+
if (!loaded.ok) {
|
|
366
|
+
report?.error?.("load", loaded.error, loaded.message);
|
|
367
|
+
return loaded;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return loaded;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTUI React-based orchestrator panel with session graph view.
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports the public API from the components/ directory.
|
|
5
|
+
* Internal implementation is modularized under ../components/.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { OrchestratorPanel } from "../components/orchestrator-panel.tsx";
|
|
9
|
+
export type { PanelSession, PanelOptions } from "../components/orchestrator-panel-types.ts";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal color theme using Catppuccin palettes.
|
|
3
|
+
*
|
|
4
|
+
* Uses OpenTUI's built-in dark/light mode detection (via the renderer's
|
|
5
|
+
* themeMode property) to select the appropriate palette:
|
|
6
|
+
* - Mocha for dark terminals (and as fallback)
|
|
7
|
+
* - Latte for light terminals
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ThemeMode } from "@opentui/core";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Theme type
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface TerminalTheme {
|
|
17
|
+
bg: string;
|
|
18
|
+
surface: string;
|
|
19
|
+
selection: string;
|
|
20
|
+
border: string;
|
|
21
|
+
borderDim: string;
|
|
22
|
+
accent: string;
|
|
23
|
+
text: string;
|
|
24
|
+
dim: string;
|
|
25
|
+
success: string;
|
|
26
|
+
error: string;
|
|
27
|
+
warning: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Catppuccin Mocha (dark)
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
const CATPPUCCIN_MOCHA: TerminalTheme = {
|
|
35
|
+
bg: "#1e1e2e", // Base
|
|
36
|
+
surface: "#313244", // Surface0
|
|
37
|
+
selection: "#45475a", // Surface1
|
|
38
|
+
border: "#6c7086", // Overlay0
|
|
39
|
+
borderDim: "#585b70", // Surface2
|
|
40
|
+
accent: "#89b4fa", // Blue
|
|
41
|
+
text: "#cdd6f4", // Text
|
|
42
|
+
dim: "#7f849c", // Overlay1
|
|
43
|
+
success: "#a6e3a1", // Green
|
|
44
|
+
error: "#f38ba8", // Red
|
|
45
|
+
warning: "#f9e2af", // Yellow
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Catppuccin Latte (light)
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
const CATPPUCCIN_LATTE: TerminalTheme = {
|
|
53
|
+
bg: "#eff1f5", // Base
|
|
54
|
+
surface: "#ccd0da", // Surface0
|
|
55
|
+
selection: "#bcc0cc", // Surface1
|
|
56
|
+
border: "#9ca0b0", // Overlay0
|
|
57
|
+
borderDim: "#acb0be", // Surface2
|
|
58
|
+
accent: "#1e66f5", // Blue
|
|
59
|
+
text: "#4c4f69", // Text
|
|
60
|
+
dim: "#8c8fa1", // Overlay1
|
|
61
|
+
success: "#40a02b", // Green
|
|
62
|
+
error: "#d20f39", // Red
|
|
63
|
+
warning: "#df8e1d", // Yellow
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Public API
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the terminal theme from the renderer's detected theme mode.
|
|
72
|
+
* Returns Catppuccin Latte for light terminals, Mocha for dark or unknown.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveTheme(mode: ThemeMode | null): TerminalTheme {
|
|
75
|
+
return mode === "light" ? CATPPUCCIN_LATTE : CATPPUCCIN_MOCHA;
|
|
76
|
+
}
|