@bastani/atomic 0.5.4-0 → 0.5.5-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.
Files changed (173) hide show
  1. package/README.md +44 -1
  2. package/dist/lib/path-root-guard.d.ts +4 -0
  3. package/dist/lib/path-root-guard.d.ts.map +1 -0
  4. package/dist/sdk/components/color-utils.d.ts +1 -0
  5. package/dist/sdk/components/color-utils.d.ts.map +1 -0
  6. package/dist/sdk/components/connectors.d.ts +3 -2
  7. package/dist/sdk/components/connectors.d.ts.map +1 -0
  8. package/dist/sdk/components/connectors.test.d.ts +1 -0
  9. package/dist/sdk/components/connectors.test.d.ts.map +1 -0
  10. package/dist/sdk/components/edge.d.ts +2 -1
  11. package/dist/sdk/components/edge.d.ts.map +1 -0
  12. package/dist/sdk/components/error-boundary.d.ts +1 -0
  13. package/dist/sdk/components/error-boundary.d.ts.map +1 -0
  14. package/dist/sdk/components/graph-theme.d.ts +2 -1
  15. package/dist/sdk/components/graph-theme.d.ts.map +1 -0
  16. package/dist/sdk/components/header.d.ts +1 -0
  17. package/dist/sdk/components/header.d.ts.map +1 -0
  18. package/dist/sdk/components/hooks.d.ts +15 -0
  19. package/dist/sdk/components/hooks.d.ts.map +1 -0
  20. package/dist/sdk/components/layout.d.ts +2 -1
  21. package/dist/sdk/components/layout.d.ts.map +1 -0
  22. package/dist/sdk/components/layout.test.d.ts +1 -0
  23. package/dist/sdk/components/layout.test.d.ts.map +1 -0
  24. package/dist/sdk/components/node-card.d.ts +5 -3
  25. package/dist/sdk/components/node-card.d.ts.map +1 -0
  26. package/dist/sdk/components/orchestrator-panel-contexts.d.ts +3 -2
  27. package/dist/sdk/components/orchestrator-panel-contexts.d.ts.map +1 -0
  28. package/dist/sdk/components/orchestrator-panel-store.d.ts +2 -1
  29. package/dist/sdk/components/orchestrator-panel-store.d.ts.map +1 -0
  30. package/dist/sdk/components/orchestrator-panel-store.test.d.ts +1 -0
  31. package/dist/sdk/components/orchestrator-panel-store.test.d.ts.map +1 -0
  32. package/dist/sdk/components/orchestrator-panel-types.d.ts +1 -0
  33. package/dist/sdk/components/orchestrator-panel-types.d.ts.map +1 -0
  34. package/dist/sdk/components/orchestrator-panel.d.ts +2 -1
  35. package/dist/sdk/components/orchestrator-panel.d.ts.map +1 -0
  36. package/dist/sdk/components/session-graph-panel.d.ts +1 -0
  37. package/dist/sdk/components/session-graph-panel.d.ts.map +1 -0
  38. package/dist/sdk/components/status-helpers.d.ts +2 -1
  39. package/dist/sdk/components/status-helpers.d.ts.map +1 -0
  40. package/dist/sdk/components/statusline.d.ts +2 -1
  41. package/dist/sdk/components/statusline.d.ts.map +1 -0
  42. package/dist/sdk/components/workflow-picker-panel.d.ts +11 -8
  43. package/dist/sdk/components/workflow-picker-panel.d.ts.map +1 -0
  44. package/dist/sdk/define-workflow.d.ts +2 -1
  45. package/dist/sdk/define-workflow.d.ts.map +1 -0
  46. package/dist/sdk/define-workflow.test.d.ts +1 -0
  47. package/dist/sdk/define-workflow.test.d.ts.map +1 -0
  48. package/dist/sdk/errors.d.ts +3 -0
  49. package/dist/sdk/errors.d.ts.map +1 -0
  50. package/dist/sdk/errors.test.d.ts +2 -0
  51. package/dist/sdk/errors.test.d.ts.map +1 -0
  52. package/dist/sdk/index.d.ts +7 -6
  53. package/dist/sdk/index.d.ts.map +1 -0
  54. package/dist/sdk/providers/claude.d.ts +17 -6
  55. package/dist/sdk/providers/claude.d.ts.map +1 -0
  56. package/dist/sdk/providers/copilot.d.ts +2 -5
  57. package/dist/sdk/providers/copilot.d.ts.map +1 -0
  58. package/dist/sdk/providers/opencode.d.ts +2 -5
  59. package/dist/sdk/providers/opencode.d.ts.map +1 -0
  60. package/dist/sdk/runtime/discovery.d.ts +2 -1
  61. package/dist/sdk/runtime/discovery.d.ts.map +1 -0
  62. package/dist/sdk/runtime/executor-entry.d.ts +1 -0
  63. package/dist/sdk/runtime/executor-entry.d.ts.map +1 -0
  64. package/dist/sdk/runtime/executor.d.ts +3 -6
  65. package/dist/sdk/runtime/executor.d.ts.map +1 -0
  66. package/dist/sdk/runtime/executor.test.d.ts +1 -0
  67. package/dist/sdk/runtime/executor.test.d.ts.map +1 -0
  68. package/dist/sdk/runtime/graph-inference.d.ts +1 -0
  69. package/dist/sdk/runtime/graph-inference.d.ts.map +1 -0
  70. package/dist/sdk/runtime/loader.d.ts +5 -7
  71. package/dist/sdk/runtime/loader.d.ts.map +1 -0
  72. package/dist/sdk/runtime/panel.d.ts +3 -2
  73. package/dist/sdk/runtime/panel.d.ts.map +1 -0
  74. package/dist/sdk/runtime/theme.d.ts +1 -0
  75. package/dist/sdk/runtime/theme.d.ts.map +1 -0
  76. package/dist/sdk/runtime/tmux.d.ts +26 -8
  77. package/dist/sdk/runtime/tmux.d.ts.map +1 -0
  78. package/dist/sdk/types.d.ts +23 -1
  79. package/dist/sdk/types.d.ts.map +1 -0
  80. package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts +1 -0
  81. package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts.map +1 -0
  82. package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts +1 -0
  83. package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts.map +1 -0
  84. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts +1 -0
  85. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts.map +1 -0
  86. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts +2 -1
  87. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts.map +1 -0
  88. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts +1 -0
  89. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts.map +1 -0
  90. package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts +1 -0
  91. package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts.map +1 -0
  92. package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts +1 -0
  93. package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts.map +1 -0
  94. package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts +1 -0
  95. package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts.map +1 -0
  96. package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts +1 -0
  97. package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts.map +1 -0
  98. package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +1 -0
  99. package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts.map +1 -0
  100. package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts +2 -1
  101. package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts.map +1 -0
  102. package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts +1 -0
  103. package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts.map +1 -0
  104. package/dist/sdk/workflows/index.d.ts +14 -14
  105. package/dist/sdk/workflows/index.d.ts.map +1 -0
  106. package/dist/services/config/definitions.d.ts +85 -0
  107. package/dist/services/config/definitions.d.ts.map +1 -0
  108. package/dist/services/system/copy.d.ts +77 -0
  109. package/dist/services/system/copy.d.ts.map +1 -0
  110. package/dist/services/system/detect.d.ts +75 -0
  111. package/dist/services/system/detect.d.ts.map +1 -0
  112. package/package.json +13 -34
  113. package/src/cli.ts +11 -10
  114. package/src/commands/cli/chat/index.ts +11 -11
  115. package/src/commands/cli/chat.ts +1 -1
  116. package/src/commands/cli/config.ts +10 -9
  117. package/src/commands/cli/init/index.ts +11 -11
  118. package/src/commands/cli/init/onboarding.ts +4 -4
  119. package/src/commands/cli/init/scm.ts +5 -5
  120. package/src/commands/cli/init.ts +1 -1
  121. package/src/commands/cli/workflow-command.test.ts +19 -11
  122. package/src/commands/cli/workflow.test.ts +2 -2
  123. package/src/commands/cli/workflow.ts +6 -6
  124. package/src/lib/merge.ts +17 -31
  125. package/src/lib/path-root-guard.ts +2 -2
  126. package/src/lib/spawn.ts +13 -7
  127. package/src/scripts/bump-version.ts +1 -1
  128. package/src/scripts/constants.ts +2 -2
  129. package/src/sdk/components/header.tsx +21 -23
  130. package/src/sdk/components/hooks.ts +21 -0
  131. package/src/sdk/components/node-card.tsx +3 -2
  132. package/src/sdk/components/session-graph-panel.tsx +14 -18
  133. package/src/sdk/components/workflow-picker-panel.tsx +201 -216
  134. package/src/sdk/errors.test.ts +56 -0
  135. package/src/sdk/errors.ts +5 -0
  136. package/src/sdk/providers/claude.ts +279 -70
  137. package/src/sdk/providers/copilot.ts +17 -27
  138. package/src/sdk/providers/opencode.ts +17 -27
  139. package/src/sdk/runtime/discovery.ts +18 -18
  140. package/src/sdk/runtime/executor.test.ts +15 -48
  141. package/src/sdk/runtime/executor.ts +152 -121
  142. package/src/sdk/runtime/loader.ts +16 -21
  143. package/src/sdk/runtime/tmux.ts +95 -32
  144. package/src/sdk/types.ts +45 -0
  145. package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +27 -0
  146. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.ts +25 -16
  147. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scout.ts +25 -24
  148. package/src/sdk/workflows/builtin/ralph/claude/index.ts +5 -0
  149. package/src/sdk/workflows/index.ts +3 -3
  150. package/src/services/config/atomic-config.ts +7 -8
  151. package/src/services/config/atomic-global-config.ts +9 -9
  152. package/src/services/config/config-path.ts +1 -1
  153. package/src/services/config/definitions.ts +3 -4
  154. package/src/services/config/index.ts +1 -1
  155. package/src/services/config/settings.ts +30 -36
  156. package/src/services/system/agents.ts +3 -3
  157. package/src/services/system/auto-sync.ts +9 -9
  158. package/src/services/system/copy.ts +9 -9
  159. package/src/services/system/file-lock.ts +2 -2
  160. package/src/services/system/install-ui.ts +2 -2
  161. package/src/services/system/skills.ts +1 -1
  162. package/src/theme/colors.ts +1 -1
  163. package/src/theme/logo.ts +1 -1
  164. package/tsconfig.json +3 -4
  165. package/dist/chunk-1gb5qxz9.js +0 -1
  166. package/dist/chunk-fdk7tact.js +0 -417
  167. package/dist/chunk-xkxndz5g.js +0 -1041
  168. package/dist/sdk/index.js +0 -52
  169. package/dist/sdk/workflows/builtin/ralph/claude/index.js +0 -96
  170. package/dist/sdk/workflows/builtin/ralph/copilot/index.js +0 -119
  171. package/dist/sdk/workflows/builtin/ralph/opencode/index.js +0 -148
  172. package/dist/sdk/workflows/index.js +0 -100
  173. package/src/commands/cli/chat/client.ts +0 -18
package/src/lib/spawn.ts CHANGED
@@ -5,7 +5,8 @@
5
5
  * eliminating duplication across postinstall-playwright, postinstall-liteparse, etc.
6
6
  */
7
7
 
8
- import { join } from "path";
8
+ import { join } from "node:path";
9
+ import { homedir } from "node:os";
9
10
 
10
11
  export interface SpawnResult {
11
12
  success: boolean;
@@ -73,10 +74,12 @@ export function prependPath(directory: string): void {
73
74
  }
74
75
 
75
76
  /**
76
- * Get the user's home directory from environment variables.
77
+ * Get the user's home directory.
78
+ * Uses Node.js os.homedir() which handles cross-platform resolution
79
+ * (HOME on Unix, USERPROFILE on Windows, and fallback to passwd on Linux).
77
80
  */
78
- export function getHomeDir(): string | undefined {
79
- return process.env.HOME ?? process.env.USERPROFILE;
81
+ export function getHomeDir(): string {
82
+ return homedir();
80
83
  }
81
84
 
82
85
  /**
@@ -163,13 +166,16 @@ async function installNodeViaFnm(quiet: boolean): Promise<boolean> {
163
166
 
164
167
  // Activate the installed version by adding its bin dir to PATH.
165
168
  const envShell = process.platform === "win32" ? "cmd" : "bash";
166
- const envResult = Bun.spawnSync({
169
+ const envProc = Bun.spawn({
167
170
  cmd: [fnmPath, "env", "--shell", envShell],
168
171
  stdout: "pipe",
169
172
  stderr: "pipe",
170
173
  });
171
- if (envResult.success) {
172
- const envOutput = envResult.stdout.toString();
174
+ const [envOutput, envExitCode] = await Promise.all([
175
+ new Response(envProc.stdout).text(),
176
+ envProc.exited,
177
+ ]);
178
+ if (envExitCode === 0) {
173
179
  if (process.platform === "win32") {
174
180
  // cmd output: SET "PATH=C:\...\fnm_multishells\...;..."
175
181
  const pathMatch = envOutput.match(/SET "PATH=([^"]+?)"/i);
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { $ } from "bun";
21
- import { resolve } from "path";
21
+ import { resolve } from "node:path";
22
22
  import { VERSION_FILES } from "./constants-base.ts";
23
23
 
24
24
  const ROOT = resolve(import.meta.dir, "../..");
@@ -5,8 +5,8 @@
5
5
  * change propagates everywhere.
6
6
  */
7
7
 
8
- import { AGENTS } from "@/sdk/workflows/index.ts";
9
- import type { AgentType } from "@/sdk/workflows/index.ts";
8
+ import { AGENTS } from "../sdk/workflows/index.ts";
9
+ import type { AgentType } from "../sdk/workflows/index.ts";
10
10
 
11
11
  export {
12
12
  SDK_PACKAGE_NAME,
@@ -1,14 +1,28 @@
1
1
  /** @jsxImportSource @opentui/react */
2
2
 
3
+ import { useMemo } from "react";
3
4
  import type { SessionStatus } from "./orchestrator-panel-types.ts";
4
- import { useStore, useGraphTheme } from "./orchestrator-panel-contexts.ts";
5
+ import { useStore, useGraphTheme, useStoreVersion } from "./orchestrator-panel-contexts.ts";
6
+
7
+ function CountBadge({ color, icon, count }: { color: string; icon: string; count: number }) {
8
+ if (count <= 0) return null;
9
+ return (
10
+ <text>
11
+ <span fg={color}>{icon} {count}</span>
12
+ </text>
13
+ );
14
+ }
5
15
 
6
16
  export function Header() {
7
17
  const store = useStore();
8
18
  const theme = useGraphTheme();
19
+ const storeVersion = useStoreVersion(store);
9
20
 
10
- const counts: Record<SessionStatus, number> = { complete: 0, running: 0, pending: 0, error: 0 };
11
- for (const s of store.sessions) counts[s.status]++;
21
+ const counts = useMemo(() => {
22
+ const c: Record<SessionStatus, number> = { complete: 0, running: 0, pending: 0, error: 0 };
23
+ for (const s of store.sessions) c[s.status]++;
24
+ return c;
25
+ }, [storeVersion]);
12
26
 
13
27
  const isFailed = store.fatalError !== null;
14
28
  const isDone = store.completionInfo !== null;
@@ -34,26 +48,10 @@ export function Header() {
34
48
  </text>
35
49
 
36
50
  <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}
51
+ <CountBadge color={theme.success} icon={"\u2713"} count={counts.complete} />
52
+ <CountBadge color={theme.warning} icon={"\u25CF"} count={counts.running} />
53
+ <CountBadge color={theme.textDim} icon={"\u25CB"} count={counts.pending} />
54
+ <CountBadge color={theme.error} icon={"\u2717"} count={counts.error} />
57
55
  </box>
58
56
  </box>
59
57
  );
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared React hooks for OpenTUI components.
3
+ */
4
+
5
+ import { useRef } from "react";
6
+
7
+ /**
8
+ * Return a ref whose `.current` always holds the latest value.
9
+ *
10
+ * Useful for reading state inside event callbacks (e.g. `useKeyboard`)
11
+ * that capture the initial closure and would otherwise go stale.
12
+ *
13
+ * This is safe because OpenTUI's React reconciler is synchronous —
14
+ * the ref is assigned during render, which is guaranteed to complete
15
+ * before any event handler fires.
16
+ */
17
+ export function useLatest<T>(value: T): React.RefObject<T> {
18
+ const ref = useRef(value);
19
+ ref.current = value;
20
+ return ref;
21
+ }
@@ -1,11 +1,12 @@
1
1
  /** @jsxImportSource @opentui/react */
2
2
 
3
+ import React from "react";
3
4
  import { lerpColor } from "./color-utils.ts";
4
5
  import { useGraphTheme } from "./orchestrator-panel-contexts.ts";
5
6
  import { statusColor, fmtDuration } from "./status-helpers.ts";
6
7
  import { NODE_W, type LayoutNode } from "./layout.ts";
7
8
 
8
- export function NodeCard({
9
+ export const NodeCard = React.memo(function NodeCard({
9
10
  node,
10
11
  focused,
11
12
  pulsePhase,
@@ -65,4 +66,4 @@ export function NodeCard({
65
66
  </box>
66
67
  </box>
67
68
  );
68
- }
69
+ });
@@ -25,9 +25,7 @@ import {
25
25
  useStoreVersion,
26
26
  TmuxSessionContext,
27
27
  } from "./orchestrator-panel-contexts.ts";
28
- import { computeLayout } from "./layout.ts";
29
- import { NODE_W, NODE_H } from "./layout.ts";
30
- import type { LayoutNode } from "./layout.ts";
28
+ import { computeLayout, NODE_W, NODE_H, type LayoutNode } from "./layout.ts";
31
29
  import { buildConnector, buildMergeConnector } from "./connectors.ts";
32
30
  import type { ConnectorResult } from "./connectors.ts";
33
31
  import { NodeCard } from "./node-card.tsx";
@@ -85,23 +83,21 @@ export function SessionGraphPanel() {
85
83
  }, [storeVersion]);
86
84
 
87
85
  // Pulse animation for running nodes — paused when nothing is running
88
- const hasRunning = store.sessions.some((s) => s.status === "running");
86
+ const hasRunning = useMemo(
87
+ () => store.sessions.some((s) => s.status === "running"),
88
+ [storeVersion],
89
+ );
89
90
  const [pulsePhase, setPulsePhase] = useState(0);
91
+ // Pulse animation doubles as a live timer refresh — 60ms updates keep
92
+ // both the pulse animation and duration displays current, so a separate
93
+ // 1s tick interval is unnecessary.
90
94
  useEffect(() => {
91
95
  if (!hasRunning) return;
92
- const id = setInterval(
96
+ const pulseId = setInterval(
93
97
  () => setPulsePhase((p: number) => (p + 1) % PULSE_FRAME_COUNT),
94
98
  PULSE_INTERVAL_MS,
95
99
  );
96
- return () => clearInterval(id);
97
- }, [hasRunning]);
98
-
99
- // Live timer refresh — re-render every second while any session is running
100
- const [, setTick] = useState(0);
101
- useEffect(() => {
102
- if (!hasRunning) return;
103
- const id = setInterval(() => setTick((t) => t + 1), 1000);
104
- return () => clearInterval(id);
100
+ return () => clearInterval(pulseId);
105
101
  }, [hasRunning]);
106
102
 
107
103
  // Attach flash message
@@ -129,13 +125,13 @@ export function SessionGraphPanel() {
129
125
 
130
126
  tmuxRun(["switch-client", "-t", `${tmuxSession}:${n.name}`]);
131
127
  },
132
- [layout.map, tmuxSession, store.sessions],
128
+ [layout.map, tmuxSession],
133
129
  );
134
130
 
135
131
  // Spatial navigation
136
132
  const navigate = useCallback(
137
133
  (dir: "left" | "right" | "up" | "down") => {
138
- const cur = layout.map[focusedId];
134
+ const cur = layout.map[focusedIdRef.current];
139
135
  if (!cur) return;
140
136
  const cx = cur.x + NODE_W / 2;
141
137
  const cy = cur.y + NODE_H / 2;
@@ -143,7 +139,7 @@ export function SessionGraphPanel() {
143
139
  let bestDist = Infinity;
144
140
 
145
141
  for (const n of nodeList) {
146
- if (n.name === focusedId) continue;
142
+ if (n.name === focusedIdRef.current) continue;
147
143
  const nx = n.x + NODE_W / 2;
148
144
  const ny = n.y + NODE_H / 2;
149
145
  const dx = nx - cx;
@@ -169,7 +165,7 @@ export function SessionGraphPanel() {
169
165
 
170
166
  if (best) setFocusedId(best.name);
171
167
  },
172
- [focusedId, layout.map, nodeList],
168
+ [layout.map, nodeList],
173
169
  );
174
170
 
175
171
  // gg double-tap tracking