@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,156 @@
|
|
|
1
|
+
// ─── Connectors ───────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import type { GraphTheme } from "./graph-theme.ts";
|
|
4
|
+
import { NODE_W, NODE_H, type LayoutNode } from "./layout.ts";
|
|
5
|
+
|
|
6
|
+
export interface ConnectorResult {
|
|
7
|
+
text: string;
|
|
8
|
+
col: number;
|
|
9
|
+
row: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
color: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Fan-out connector: one parent branching down to one or more tree children. */
|
|
16
|
+
export function buildConnector(
|
|
17
|
+
parent: LayoutNode,
|
|
18
|
+
rowH: Record<number, number>,
|
|
19
|
+
theme: GraphTheme,
|
|
20
|
+
): ConnectorResult | null {
|
|
21
|
+
if (parent.children.length === 0) return null;
|
|
22
|
+
|
|
23
|
+
const pcx = parent.x + Math.floor(NODE_W / 2);
|
|
24
|
+
const parentBottom = parent.y + (rowH[parent.depth] ?? NODE_H);
|
|
25
|
+
const firstChildRow = Math.min(...parent.children.map((c: LayoutNode) => c.y));
|
|
26
|
+
const numRows = firstChildRow - parentBottom;
|
|
27
|
+
if (numRows < 1) return null;
|
|
28
|
+
|
|
29
|
+
const childCxs = parent.children.map((c: LayoutNode) => c.x + Math.floor(NODE_W / 2));
|
|
30
|
+
const isStraight = parent.children.length === 1 && childCxs[0] === pcx;
|
|
31
|
+
|
|
32
|
+
// Straight drop: single child directly below
|
|
33
|
+
if (isStraight) {
|
|
34
|
+
const text = Array(numRows).fill("│").join("\n");
|
|
35
|
+
return {
|
|
36
|
+
text,
|
|
37
|
+
col: pcx,
|
|
38
|
+
row: parentBottom,
|
|
39
|
+
width: 1,
|
|
40
|
+
height: numRows,
|
|
41
|
+
color: theme.borderActive,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Branching: horizontal bar connecting all children
|
|
46
|
+
const allCols = [pcx, ...childCxs];
|
|
47
|
+
const minCol = Math.min(...allCols);
|
|
48
|
+
const maxCol = Math.max(...allCols);
|
|
49
|
+
const width = maxCol - minCol + 1;
|
|
50
|
+
const toL = (c: number) => c - minCol;
|
|
51
|
+
|
|
52
|
+
const barRow = numRows - 1;
|
|
53
|
+
const grid: string[][] = Array.from({ length: numRows }, () => Array(width).fill(" "));
|
|
54
|
+
|
|
55
|
+
// Vertical stem from parent center down to bar
|
|
56
|
+
for (let r = 0; r < barRow; r++) grid[r]![toL(pcx)] = "│";
|
|
57
|
+
|
|
58
|
+
// Horizontal bar
|
|
59
|
+
for (let c = 0; c < width; c++) grid[barRow]![c] = "─";
|
|
60
|
+
|
|
61
|
+
// Parent junction on bar
|
|
62
|
+
const childAtParent = childCxs.includes(pcx);
|
|
63
|
+
const pl = toL(pcx);
|
|
64
|
+
if (pcx === minCol) {
|
|
65
|
+
grid[barRow]![pl] = childAtParent ? "├" : "╰";
|
|
66
|
+
} else if (pcx === maxCol) {
|
|
67
|
+
grid[barRow]![pl] = childAtParent ? "┤" : "╯";
|
|
68
|
+
} else {
|
|
69
|
+
grid[barRow]![pl] = childAtParent ? "┼" : "┴";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Child junctions on bar
|
|
73
|
+
for (const cx of childCxs) {
|
|
74
|
+
if (cx === pcx) continue;
|
|
75
|
+
const cl = toL(cx);
|
|
76
|
+
if (cx === minCol) grid[barRow]![cl] = "╭";
|
|
77
|
+
else if (cx === maxCol) grid[barRow]![cl] = "╮";
|
|
78
|
+
else grid[barRow]![cl] = "┬";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
text: grid.map((row) => row.join("")).join("\n"),
|
|
83
|
+
col: minCol,
|
|
84
|
+
row: parentBottom,
|
|
85
|
+
width,
|
|
86
|
+
height: numRows,
|
|
87
|
+
color: theme.borderActive,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Fan-in connector: multiple parents converging down to a single merge child. */
|
|
92
|
+
export function buildMergeConnector(
|
|
93
|
+
child: LayoutNode,
|
|
94
|
+
rowH: Record<number, number>,
|
|
95
|
+
allNodes: Record<string, LayoutNode>,
|
|
96
|
+
theme: GraphTheme,
|
|
97
|
+
): ConnectorResult | null {
|
|
98
|
+
if (child.parents.length < 2) return null;
|
|
99
|
+
|
|
100
|
+
const parentNodes = child.parents
|
|
101
|
+
.map((p) => allNodes[p])
|
|
102
|
+
.filter((n): n is LayoutNode => n != null);
|
|
103
|
+
if (parentNodes.length < 2) return null;
|
|
104
|
+
|
|
105
|
+
const parentCxs = parentNodes.map((p) => p.x + Math.floor(NODE_W / 2));
|
|
106
|
+
const childCx = child.x + Math.floor(NODE_W / 2);
|
|
107
|
+
|
|
108
|
+
const parentBottom = Math.max(
|
|
109
|
+
...parentNodes.map((p) => p.y + (rowH[p.depth] ?? NODE_H)),
|
|
110
|
+
);
|
|
111
|
+
const childTop = child.y;
|
|
112
|
+
const numRows = childTop - parentBottom;
|
|
113
|
+
if (numRows < 1) return null;
|
|
114
|
+
|
|
115
|
+
const allCols = [...parentCxs, childCx];
|
|
116
|
+
const minCol = Math.min(...allCols);
|
|
117
|
+
const maxCol = Math.max(...allCols);
|
|
118
|
+
const width = maxCol - minCol + 1;
|
|
119
|
+
const toL = (c: number) => c - minCol;
|
|
120
|
+
|
|
121
|
+
const grid: string[][] = Array.from({ length: numRows }, () => Array(width).fill(" "));
|
|
122
|
+
|
|
123
|
+
// Bar at the top row — parents converge here
|
|
124
|
+
const barRow = 0;
|
|
125
|
+
for (let c = 0; c < width; c++) grid[barRow]![c] = "─";
|
|
126
|
+
|
|
127
|
+
// Vertical stem from bar down to child
|
|
128
|
+
for (let r = barRow + 1; r < numRows; r++) grid[r]![toL(childCx)] = "│";
|
|
129
|
+
|
|
130
|
+
// Junction characters on the bar
|
|
131
|
+
const parentSet = new Set(parentCxs);
|
|
132
|
+
for (const cx of allCols) {
|
|
133
|
+
const cl = toL(cx);
|
|
134
|
+
const hasUp = parentSet.has(cx);
|
|
135
|
+
const hasDown = cx === childCx;
|
|
136
|
+
const isLeft = cx === minCol;
|
|
137
|
+
const isRight = cx === maxCol;
|
|
138
|
+
|
|
139
|
+
if (hasUp && hasDown) {
|
|
140
|
+
grid[barRow]![cl] = isLeft ? "├" : isRight ? "┤" : "┼";
|
|
141
|
+
} else if (hasUp) {
|
|
142
|
+
grid[barRow]![cl] = isLeft ? "╰" : isRight ? "╯" : "┴";
|
|
143
|
+
} else if (hasDown) {
|
|
144
|
+
grid[barRow]![cl] = isLeft ? "╭" : isRight ? "╮" : "┬";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
text: grid.map((row) => row.join("")).join("\n"),
|
|
150
|
+
col: minCol,
|
|
151
|
+
row: parentBottom,
|
|
152
|
+
width,
|
|
153
|
+
height: numRows,
|
|
154
|
+
color: theme.borderActive,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/react */
|
|
2
|
+
|
|
3
|
+
import type { ConnectorResult } from "./connectors.ts";
|
|
4
|
+
|
|
5
|
+
export function Edge({ text, col, row, width, height, color: edgeColor }: ConnectorResult) {
|
|
6
|
+
return (
|
|
7
|
+
<box position="absolute" left={col} top={row} width={width} height={height}>
|
|
8
|
+
<text fg={edgeColor}>{text}</text>
|
|
9
|
+
</box>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/react */
|
|
2
|
+
/**
|
|
3
|
+
* React Error Boundary for the orchestrator panel.
|
|
4
|
+
*
|
|
5
|
+
* Catches render-time errors in the component tree and displays a
|
|
6
|
+
* static fallback so the rest of the TUI doesn't crash.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Component, type ReactNode, type ErrorInfo } from "react";
|
|
10
|
+
|
|
11
|
+
interface ErrorBoundaryProps {
|
|
12
|
+
fallback: (error: Error) => ReactNode;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ErrorBoundaryState {
|
|
17
|
+
error: Error | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
21
|
+
override state: ErrorBoundaryState = { error: null };
|
|
22
|
+
|
|
23
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
24
|
+
return { error };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override componentDidCatch(error: Error, info: ErrorInfo): void {
|
|
28
|
+
// Log to stderr so it lands in the orchestrator log file
|
|
29
|
+
console.error("[ErrorBoundary]", error, info.componentStack);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override render(): ReactNode {
|
|
33
|
+
if (this.state.error) {
|
|
34
|
+
return this.props.fallback(this.state.error);
|
|
35
|
+
}
|
|
36
|
+
return this.props.children;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// ─── Graph Theme ──────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import type { TerminalTheme } from "../runtime/theme.ts";
|
|
4
|
+
import { lerpColor } from "./color-utils.ts";
|
|
5
|
+
|
|
6
|
+
export interface GraphTheme {
|
|
7
|
+
background: string;
|
|
8
|
+
backgroundElement: string;
|
|
9
|
+
text: string;
|
|
10
|
+
textMuted: string;
|
|
11
|
+
textDim: string;
|
|
12
|
+
primary: string;
|
|
13
|
+
success: string;
|
|
14
|
+
error: string;
|
|
15
|
+
warning: string;
|
|
16
|
+
info: string;
|
|
17
|
+
border: string;
|
|
18
|
+
borderActive: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function deriveGraphTheme(t: TerminalTheme): GraphTheme {
|
|
22
|
+
return {
|
|
23
|
+
background: t.bg,
|
|
24
|
+
backgroundElement: t.surface,
|
|
25
|
+
text: t.text,
|
|
26
|
+
textMuted: lerpColor(t.text, t.bg, 0.3),
|
|
27
|
+
textDim: t.dim,
|
|
28
|
+
primary: t.accent,
|
|
29
|
+
success: t.success,
|
|
30
|
+
error: t.error,
|
|
31
|
+
warning: t.warning,
|
|
32
|
+
info: t.accent,
|
|
33
|
+
border: t.borderDim,
|
|
34
|
+
borderActive: t.border,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/react */
|
|
2
|
+
|
|
3
|
+
import type { SessionStatus } from "./orchestrator-panel-types.ts";
|
|
4
|
+
import { useStore, useGraphTheme } from "./orchestrator-panel-contexts.ts";
|
|
5
|
+
|
|
6
|
+
export function Header() {
|
|
7
|
+
const store = useStore();
|
|
8
|
+
const theme = useGraphTheme();
|
|
9
|
+
|
|
10
|
+
const counts: Record<SessionStatus, number> = { complete: 0, running: 0, pending: 0, error: 0 };
|
|
11
|
+
for (const s of store.sessions) counts[s.status]++;
|
|
12
|
+
|
|
13
|
+
const isFailed = store.fatalError !== null;
|
|
14
|
+
const isDone = store.completionInfo !== null;
|
|
15
|
+
const badgeColor = isFailed ? theme.error : isDone ? theme.success : theme.info;
|
|
16
|
+
const badgeText = isFailed
|
|
17
|
+
? " \u2717 Failed "
|
|
18
|
+
: isDone
|
|
19
|
+
? ` \u2713 ${store.workflowName} `
|
|
20
|
+
: " Orchestrator ";
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<box
|
|
24
|
+
height={1}
|
|
25
|
+
backgroundColor={theme.backgroundElement}
|
|
26
|
+
flexDirection="row"
|
|
27
|
+
paddingRight={2}
|
|
28
|
+
alignItems="center"
|
|
29
|
+
>
|
|
30
|
+
<text>
|
|
31
|
+
<span fg={theme.backgroundElement} bg={badgeColor}>
|
|
32
|
+
<strong>{badgeText}</strong>
|
|
33
|
+
</span>
|
|
34
|
+
</text>
|
|
35
|
+
|
|
36
|
+
<box flexGrow={1} justifyContent="flex-end" flexDirection="row" gap={2}>
|
|
37
|
+
{counts.complete > 0 ? (
|
|
38
|
+
<text>
|
|
39
|
+
<span fg={theme.success}>{"\u2713"} {counts.complete}</span>
|
|
40
|
+
</text>
|
|
41
|
+
) : null}
|
|
42
|
+
{counts.running > 0 ? (
|
|
43
|
+
<text>
|
|
44
|
+
<span fg={theme.warning}>{"\u25CF"} {counts.running}</span>
|
|
45
|
+
</text>
|
|
46
|
+
) : null}
|
|
47
|
+
{counts.pending > 0 ? (
|
|
48
|
+
<text>
|
|
49
|
+
<span fg={theme.textDim}>{"\u25CB"} {counts.pending}</span>
|
|
50
|
+
</text>
|
|
51
|
+
) : null}
|
|
52
|
+
{counts.error > 0 ? (
|
|
53
|
+
<text>
|
|
54
|
+
<span fg={theme.error}>{"\u2717"} {counts.error}</span>
|
|
55
|
+
</text>
|
|
56
|
+
) : null}
|
|
57
|
+
</box>
|
|
58
|
+
</box>
|
|
59
|
+
);
|
|
60
|
+
}
|