@bastani/atomic 0.6.0-0 → 0.6.1-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 +1 -0
- package/dist/lib/spawn.d.ts +102 -0
- package/dist/lib/spawn.d.ts.map +1 -0
- package/dist/sdk/providers/claude.d.ts.map +1 -1
- package/dist/sdk/runtime/attached-footer.d.ts +14 -0
- package/dist/sdk/runtime/attached-footer.d.ts.map +1 -1
- package/dist/sdk/runtime/tmux.d.ts +22 -11
- package/dist/sdk/runtime/tmux.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/cli/chat/index.test.ts +60 -0
- package/src/commands/cli/chat/index.ts +11 -33
- package/src/commands/cli/footer.tsx +170 -54
- package/src/lib/spawn.test.ts +109 -0
- package/src/lib/spawn.ts +371 -33
- package/src/sdk/providers/claude.ts +17 -0
- package/src/sdk/runtime/attached-footer.ts +96 -7
- package/src/sdk/runtime/tmux.ts +102 -52
- package/src/services/system/auto-sync.ts +14 -8
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ bun install -g @bastani/atomic
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
This skips the Bun install step but doesn't set up shell completions — run `atomic completions <shell>` separately if you want them (see [Commands Reference](#atomic-completions--shell-completions)).
|
|
90
|
+
If your shell cannot find `atomic` after the install, add the directory from `bun pm bin -g` to your PATH or use the bootstrap installer above to do it automatically.
|
|
90
91
|
|
|
91
92
|
**Prerelease builds:** `bun install -g @bastani/atomic@next` (may contain breaking changes).
|
|
92
93
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared spawn utilities for postinstall and lifecycle scripts.
|
|
3
|
+
*
|
|
4
|
+
* Provides a thin async wrapper around Bun.spawn and a PATH-prepend helper,
|
|
5
|
+
* eliminating duplication across postinstall-playwright, postinstall-liteparse, etc.
|
|
6
|
+
*/
|
|
7
|
+
export interface SpawnResult {
|
|
8
|
+
success: boolean;
|
|
9
|
+
details: string;
|
|
10
|
+
stdout?: string;
|
|
11
|
+
stderr?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RunCommandOptions {
|
|
14
|
+
/** When true, stdout/stderr are inherited so the user sees live output. */
|
|
15
|
+
inherit?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run a command asynchronously and collect its output.
|
|
19
|
+
* Returns a result object instead of throwing on failure.
|
|
20
|
+
*
|
|
21
|
+
* When `inherit` is true, output streams directly to the terminal so the
|
|
22
|
+
* user can follow installation progress in real time.
|
|
23
|
+
*/
|
|
24
|
+
export declare function runCommand(cmd: string[], options?: RunCommandOptions): Promise<SpawnResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Prepend a directory to the PATH environment variable (if not already present).
|
|
27
|
+
*/
|
|
28
|
+
export declare function prependPath(directory: string): void;
|
|
29
|
+
export declare function resolveCommandFromCurrentPath(cmd: string): string | null;
|
|
30
|
+
export type MuxBinaryName = "tmux" | "psmux" | "pmux";
|
|
31
|
+
export declare function requiredMuxBinaryCandidatesForPlatform(platform?: NodeJS.Platform): MuxBinaryName[];
|
|
32
|
+
export declare function isMuxBinaryRequiredForPlatform(binary: MuxBinaryName, platform?: NodeJS.Platform): boolean;
|
|
33
|
+
export declare function hasRequiredMuxBinary(): boolean;
|
|
34
|
+
export declare function psmuxReleaseAssetSuffix(arch?: NodeJS.Architecture): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get the user's home directory.
|
|
37
|
+
* Uses Node.js os.homedir() which handles cross-platform resolution
|
|
38
|
+
* (HOME on Unix, USERPROFILE on Windows, and fallback to passwd on Linux).
|
|
39
|
+
*/
|
|
40
|
+
export declare function getHomeDir(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Options for the user-facing ensure* installers.
|
|
43
|
+
*
|
|
44
|
+
* `quiet: true` captures subprocess output instead of streaming it to the
|
|
45
|
+
* terminal, so a higher-level spinner UI (see auto-sync's `runSteps`) can
|
|
46
|
+
* own the display. Failures collected in the captured buffer are thrown
|
|
47
|
+
* out of the ensure* function so the spinner can mark the step red and
|
|
48
|
+
* surface the captured tail in its summary.
|
|
49
|
+
*
|
|
50
|
+
* Default (`quiet: false`) preserves the historical inherit-stdout
|
|
51
|
+
* behavior used by the ad-hoc fallbacks in chat.ts / workflow.ts.
|
|
52
|
+
*/
|
|
53
|
+
export interface EnsureOptions {
|
|
54
|
+
quiet?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Install one or more global packages via a single `bun install -g` call.
|
|
58
|
+
* Uses `--trust` to allow postinstall lifecycle scripts (required by
|
|
59
|
+
* packages like @playwright/cli).
|
|
60
|
+
*
|
|
61
|
+
* Combining multiple packages into one invocation is important: Bun's
|
|
62
|
+
* global linker is not safe to run concurrently — two parallel
|
|
63
|
+
* `bun install -g` processes race to create the same symlinks in the
|
|
64
|
+
* shared global store, causing EEXIST errors for transitive deps that
|
|
65
|
+
* both packages (or the already-installed @bastani/atomic) share.
|
|
66
|
+
*/
|
|
67
|
+
export declare function upgradeGlobalPackages(pkgs: string[]): Promise<void>;
|
|
68
|
+
/** Upgrade @playwright/cli and @llamaindex/liteparse globally in one pass. */
|
|
69
|
+
export declare function upgradeGlobalToolPackages(): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Ensure a terminal multiplexer (tmux on Unix, psmux on Windows) is installed.
|
|
72
|
+
* No-op when already present on PATH.
|
|
73
|
+
*
|
|
74
|
+
* When `quiet: true`, subprocess output is captured instead of inherited
|
|
75
|
+
* so an outer spinner UI owns the display. On failure the captured tail
|
|
76
|
+
* is re-thrown as the error message.
|
|
77
|
+
*/
|
|
78
|
+
export declare function ensureTmuxInstalled(options?: EnsureOptions): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Ensure bun is installed and available on PATH.
|
|
81
|
+
* No-op when already present.
|
|
82
|
+
*/
|
|
83
|
+
export declare function ensureBunInstalled(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Ensure tmux/psmux is installed. Used as a ToolingStep in the update pipeline.
|
|
86
|
+
* Does not attempt version upgrades — just ensures the tool exists.
|
|
87
|
+
*/
|
|
88
|
+
export declare function upgradeTmux(): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Upgrade bun to the latest version, or install if missing.
|
|
91
|
+
*/
|
|
92
|
+
export declare function upgradeBun(): Promise<void>;
|
|
93
|
+
export declare class ToolingSetupError extends Error {
|
|
94
|
+
readonly failures: string[];
|
|
95
|
+
constructor(failures: string[]);
|
|
96
|
+
}
|
|
97
|
+
export interface ToolingStep {
|
|
98
|
+
label: string;
|
|
99
|
+
fn: () => Promise<unknown>;
|
|
100
|
+
}
|
|
101
|
+
export declare function collectFailures(steps: ToolingStep[], results: PromiseSettledResult<unknown>[]): string[];
|
|
102
|
+
//# sourceMappingURL=spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/lib/spawn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuCjG;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAUnD;AAMD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAExE;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,wBAAgB,sCAAsC,CACpD,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,aAAa,EAAE,CAEjB;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,aAAa,EACrB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAET;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAI9C;AAkHD,wBAAgB,uBAAuB,CACrC,IAAI,GAAE,MAAM,CAAC,YAA2B,GACvC,MAAM,GAAG,IAAI,CAWf;AA0HD;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzE;AAED,8EAA8E;AAC9E,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6GpF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAoExD;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAUhD;AAMD,qBAAa,iBAAkB,SAAQ,KAAK;aACd,QAAQ,EAAE,MAAM,EAAE;gBAAlB,QAAQ,EAAE,MAAM,EAAE;CAQ/C;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,GACvC,MAAM,EAAE,CAaV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,OAAO,IAAI,UAAU,EAC3B,MAAM,gCAAgC,CAAC;AA8BxC;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,OAAO,IAAI,UAAU,EAC3B,MAAM,gCAAgC,CAAC;AA8BxC;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AA6KD,MAAM,WAAW,oBAAoB;IACnC,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,sIAAsI;IACtI,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBxF;AA8HD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAUnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EACjC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAMD;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAE3D;AAmFD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjF;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;;GAEG;AACH,wBAAsB,WAAW,CAC/B,eAAe,EAAE,MAAM,EACvB,qBAAqB,EAAE,MAAM,GAC5B,OAAO,CAAC,cAAc,EAAE,CAAC,CAiG3B;AAMD,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EACvD,UAAU,EAAE,MAAM,GACjB,MAAM,CAoBR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA8FxF;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,EAC9B,MAAM,EAAE,MAAM,EAAE,GACf,MAAM,EAAE,CAMV;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;gBAG9C,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO;IAMrC;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAO9B,yEAAyE;IACnE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAC5B;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2C;gBAG/D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;IAOpC;;;;;;;;OAQG;IACG,KAAK,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAC7B,OAAO,CAAC,cAAc,EAAE,CAAC;IAQ5B;;;;;OAKG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAElC;IAED,gEAAgE;IAC1D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAClC;AAMD;;;GAGG;AACH,qBAAa,2BAA2B;IACtC;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAGxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAajD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,4BAA4B;IACvC,QAAQ,CAAC,MAAM,MAAM;IACrB;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAc;IACpC;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB,CAAsB;IAEnD,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,oBAAoB,IAAI,OAAO,CAElC;IAEK,KAAK,CACT,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,cAAc,CAAC,EAC9C,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAC5B,OAAO,CAAC,cAAc,EAAE,CAAC;IA2CtB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAClC;AAQD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,+DAejC,CAAC"}
|
|
@@ -13,5 +13,19 @@
|
|
|
13
13
|
* so it can't be used here.
|
|
14
14
|
*/
|
|
15
15
|
import type { AgentType } from "../types.ts";
|
|
16
|
+
export declare function resolveAttachedFooterCliPath(runtimeDir?: string, platform?: NodeJS.Platform): string;
|
|
17
|
+
export declare function buildAttachedFooterCommand({ runtime, cliPath, windowName, agentType, platform, }: {
|
|
18
|
+
runtime: string;
|
|
19
|
+
cliPath: string;
|
|
20
|
+
windowName: string;
|
|
21
|
+
agentType?: AgentType;
|
|
22
|
+
platform?: NodeJS.Platform;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function buildAttachedFooterCloseHooks(agentPaneId: string, footerPaneId: string, options?: {
|
|
25
|
+
guardAgentPane?: boolean;
|
|
26
|
+
}): Array<{
|
|
27
|
+
event: string;
|
|
28
|
+
command: string;
|
|
29
|
+
}>;
|
|
16
30
|
export declare function spawnAttachedFooter(windowName: string, paneId: string, agentType?: AgentType): void;
|
|
17
31
|
//# sourceMappingURL=attached-footer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attached-footer.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/attached-footer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"attached-footer.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/attached-footer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA6B7C,wBAAgB,4BAA4B,CAC1C,UAAU,SAAkB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,CAIR;AAED,wBAAgB,0BAA0B,CAAC,EACzC,OAAO,EACP,OAAO,EACP,UAAU,EACV,SAAS,EACT,QAA2B,GAC5B,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,GAAG,MAAM,CAoBT;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAU3C;AAOD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,GACpB,IAAI,CAyCN"}
|
|
@@ -19,7 +19,9 @@ export type TmuxResult = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Resolve the terminal multiplexer binary for the current platform.
|
|
21
21
|
*
|
|
22
|
-
* On Windows, tries psmux → pmux
|
|
22
|
+
* On Windows, tries psmux → pmux. Do not accept arbitrary `tmux.exe` because
|
|
23
|
+
* that can be a non-native shim and would prevent the psmux installer from
|
|
24
|
+
* running.
|
|
23
25
|
* On Unix/macOS, uses tmux directly.
|
|
24
26
|
*
|
|
25
27
|
* Returns the binary name (not the full path) or null if none is found.
|
|
@@ -65,17 +67,24 @@ export declare function tmuxRun(args: string[]): TmuxResult;
|
|
|
65
67
|
* @returns The pane ID of the initial pane (e.g., "%0")
|
|
66
68
|
*/
|
|
67
69
|
export declare function createSession(sessionName: string, initialCommand: string, windowName?: string, cwd?: string, envVars?: Record<string, string>): string;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
export declare function buildKillSessionOnPaneExitHooks(sessionName: string, paneId: string, options?: {
|
|
71
|
+
guardPaneExited?: boolean;
|
|
72
|
+
}): Array<{
|
|
73
|
+
event: string;
|
|
74
|
+
command: string;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Install hooks that kill the entire session when the agent pane goes away.
|
|
78
|
+
* Used by chat sessions so the session is torn down when the agent CLI exits
|
|
79
|
+
* — whether via `/exit`, a deliberate double Ctrl+C, a crash, or a direct
|
|
80
|
+
* pane close — without leaving the footer pane keeping the session alive.
|
|
74
81
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
82
|
+
* tmux fires `pane-exited` when a pane process exits; psmux also supports
|
|
83
|
+
* that event, but does not currently populate tmux's `#{hook_pane}` format,
|
|
84
|
+
* so the psmux hook is session-scoped. A direct pane close/kill fires
|
|
85
|
+
* `after-kill-pane` instead. These session-scoped hooks are safe for chat
|
|
86
|
+
* sessions: they only have the agent pane plus its footer, and closing either
|
|
87
|
+
* should close the entire chat window.
|
|
79
88
|
*/
|
|
80
89
|
export declare function killSessionOnPaneExit(sessionName: string, paneId: string): void;
|
|
81
90
|
/**
|
|
@@ -165,6 +174,7 @@ export declare function sessionExists(sessionName: string): boolean;
|
|
|
165
174
|
* the individual session, not the global server environment.
|
|
166
175
|
*/
|
|
167
176
|
export declare function setSessionEnv(sessionName: string, key: string, value: string): void;
|
|
177
|
+
export declare function parseSessionEnvValue(stdout: string, key: string): string | null;
|
|
168
178
|
/**
|
|
169
179
|
* Read a session-level environment variable.
|
|
170
180
|
* Returns `null` when the session doesn't exist or the variable isn't set.
|
|
@@ -201,6 +211,7 @@ export interface TmuxSession {
|
|
|
201
211
|
/** Agent backend that owns this session (e.g. "claude", "copilot", "opencode") */
|
|
202
212
|
agent?: string;
|
|
203
213
|
}
|
|
214
|
+
export declare function parseListSessionsOutput(stdout: string, getEnv?: (sessionName: string, key: string) => string | null): TmuxSession[];
|
|
204
215
|
/**
|
|
205
216
|
* List all sessions on the atomic tmux socket.
|
|
206
217
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/tmux.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/tmux.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { buildLauncherScript } from "./index.ts";
|
|
3
|
+
|
|
4
|
+
function withMockPlatform<T>(platform: NodeJS.Platform, fn: () => T): T {
|
|
5
|
+
const originalPlatform = process.platform;
|
|
6
|
+
Object.defineProperty(process, "platform", {
|
|
7
|
+
configurable: true,
|
|
8
|
+
value: platform,
|
|
9
|
+
});
|
|
10
|
+
try {
|
|
11
|
+
return fn();
|
|
12
|
+
} finally {
|
|
13
|
+
Object.defineProperty(process, "platform", {
|
|
14
|
+
configurable: true,
|
|
15
|
+
value: originalPlatform,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("buildLauncherScript", () => {
|
|
21
|
+
test("builds a PowerShell launcher with cwd, env, args, and exit code", () => {
|
|
22
|
+
const { script, ext } = withMockPlatform("win32", () =>
|
|
23
|
+
buildLauncherScript(
|
|
24
|
+
"copilot",
|
|
25
|
+
["--debug"],
|
|
26
|
+
"C:\\repo",
|
|
27
|
+
{ ATOMIC_AGENT: "copilot" },
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(ext).toBe("ps1");
|
|
32
|
+
expect(script).toContain('Set-Location "C:\\repo"');
|
|
33
|
+
expect(script).toContain('$env:ATOMIC_AGENT = "copilot"');
|
|
34
|
+
expect(script).toContain('& "copilot" @("--debug")');
|
|
35
|
+
expect(script).toContain('if ($LASTEXITCODE -is [int]) { $atomicExitCode = $LASTEXITCODE }');
|
|
36
|
+
expect(script).toContain("exit $atomicExitCode");
|
|
37
|
+
expect(script).not.toContain("Invoke-AtomicSessionCleanup");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("builds a bash launcher without tmux input suppression", () => {
|
|
41
|
+
const { script, ext } = withMockPlatform("linux", () =>
|
|
42
|
+
buildLauncherScript(
|
|
43
|
+
"claude",
|
|
44
|
+
["--dangerously-skip-permissions"],
|
|
45
|
+
"/repo",
|
|
46
|
+
{ ATOMIC_AGENT: "claude" },
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(ext).toBe("sh");
|
|
51
|
+
expect(script).toContain('cd "/repo"');
|
|
52
|
+
expect(script).toContain('export ATOMIC_AGENT="claude"');
|
|
53
|
+
expect(script).toContain('"claude" "--dangerously-skip-permissions"');
|
|
54
|
+
expect(script).toContain("atomic_exit_code=$?");
|
|
55
|
+
expect(script).not.toContain("exec ");
|
|
56
|
+
expect(script).not.toContain("stty -echo -icanon");
|
|
57
|
+
expect(script).not.toContain("atomic_original_tty_state");
|
|
58
|
+
expect(script).not.toContain("trap atomic_cleanup");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Chat CLI command for atomic
|
|
4
4
|
*
|
|
5
|
-
* Spawns the native agent CLI
|
|
6
|
-
* When already inside a tmux/psmux session, the agent spawns inline
|
|
7
|
-
* in the current pane. When outside tmux, it creates a new tmux
|
|
8
|
-
* session and attaches to it.
|
|
5
|
+
* Spawns the native agent CLI in tmux/psmux with an OpenTUI footer pane.
|
|
9
6
|
*
|
|
10
7
|
* All extra arguments after `-a <agent>` are forwarded to the native CLI.
|
|
11
8
|
*
|
|
@@ -36,9 +33,8 @@ import {
|
|
|
36
33
|
import {
|
|
37
34
|
createSession,
|
|
38
35
|
detachAndAttachAtomic,
|
|
39
|
-
killSession,
|
|
40
36
|
killSessionOnPaneExit,
|
|
41
|
-
|
|
37
|
+
killSession,
|
|
42
38
|
spawnMuxAttach,
|
|
43
39
|
switchClient,
|
|
44
40
|
} from "../../../sdk/workflows/index.ts";
|
|
@@ -113,7 +109,7 @@ function escPwsh(s: string): string {
|
|
|
113
109
|
* Build a launcher script that preserves cwd and properly quotes args.
|
|
114
110
|
* This avoids shell-injection risks from passthrough args.
|
|
115
111
|
*/
|
|
116
|
-
function buildLauncherScript(
|
|
112
|
+
export function buildLauncherScript(
|
|
117
113
|
cmd: string,
|
|
118
114
|
args: string[],
|
|
119
115
|
projectRoot: string,
|
|
@@ -134,6 +130,9 @@ function buildLauncherScript(
|
|
|
134
130
|
argList.length > 0
|
|
135
131
|
? `& "${escPwsh(cmd)}" @(${argList})`
|
|
136
132
|
: `& "${escPwsh(cmd)}"`,
|
|
133
|
+
"$atomicExitCode = 0",
|
|
134
|
+
"if ($LASTEXITCODE -is [int]) { $atomicExitCode = $LASTEXITCODE }",
|
|
135
|
+
"exit $atomicExitCode",
|
|
137
136
|
].join("\n");
|
|
138
137
|
return { script, ext: "ps1" };
|
|
139
138
|
}
|
|
@@ -145,21 +144,13 @@ function buildLauncherScript(
|
|
|
145
144
|
const envLines = envEntries.map(
|
|
146
145
|
([key, value]) => `export ${key}="${escBash(value)}"`,
|
|
147
146
|
);
|
|
148
|
-
// Pre-silence the tty before exec'ing the agent. copilot and opencode
|
|
149
|
-
// write terminal-capability probes (CSI ?Pn $p, OSC 4;0;?, CSI c) to
|
|
150
|
-
// stdout at startup; under tmux the DECRPM / OSC 4 / DA1 replies arrive
|
|
151
|
-
// on stdin a few ms later. If the agent hasn't enabled raw mode yet,
|
|
152
|
-
// the tty driver echoes those replies back to the screen as garbage
|
|
153
|
-
// (e.g. `^[[?1016;2$y^[[?2004;1$y^[]4;0;rgb:...`). Disabling echo and
|
|
154
|
-
// canonical mode here means the driver drops them silently; the agent's
|
|
155
|
-
// own termios setup then takes over once it's ready. Redirected stdin
|
|
156
|
-
// (no tty) harmlessly no-ops the stty call.
|
|
157
147
|
const script = [
|
|
158
148
|
"#!/bin/bash",
|
|
159
149
|
`cd "${escBash(projectRoot)}"`,
|
|
160
150
|
...envLines,
|
|
161
|
-
"
|
|
162
|
-
|
|
151
|
+
`"${escBash(cmd)}" ${quotedArgs}`,
|
|
152
|
+
"atomic_exit_code=$?",
|
|
153
|
+
'exit "$atomic_exit_code"',
|
|
163
154
|
].join("\n");
|
|
164
155
|
return { script, ext: "sh" };
|
|
165
156
|
}
|
|
@@ -221,9 +212,7 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
|
|
|
221
212
|
const cmd = [config.cmd, ...args];
|
|
222
213
|
const overrides = await getProviderOverrides(agentType, projectRoot);
|
|
223
214
|
// ATOMIC_AGENT must be baked into the launcher env so the agent CLI
|
|
224
|
-
//
|
|
225
|
-
// affects processes spawned *after* the initial command, so it cannot
|
|
226
|
-
// populate the env of the agent CLI that `new-session` kicks off.
|
|
215
|
+
// and anything it spawns can read it from process start.
|
|
227
216
|
const envVars = {
|
|
228
217
|
...config.env_vars,
|
|
229
218
|
...overrides.envVars,
|
|
@@ -272,19 +261,8 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
|
|
|
272
261
|
// ── Create session on the atomic socket and attach ──
|
|
273
262
|
try {
|
|
274
263
|
const paneId = createSession(windowName, shellCmd, undefined, projectRoot);
|
|
275
|
-
setSessionEnv(windowName, "ATOMIC_AGENT", agentType);
|
|
276
|
-
|
|
277
|
-
// When the agent CLI exits (via `/exit`, double Ctrl+C, or crash),
|
|
278
|
-
// tear down the whole session so the footer pane doesn't keep it
|
|
279
|
-
// alive in the background. Pane-scoped so the footer's own exit
|
|
280
|
-
// (which cascades from the kill-session) doesn't re-trigger.
|
|
281
|
-
killSessionOnPaneExit(windowName, paneId);
|
|
282
|
-
|
|
283
|
-
// Split the pane so the agent CLI runs on top and the React footer
|
|
284
|
-
// (provider pill + window name) runs in a 5% bottom pane. Matches
|
|
285
|
-
// the workflow-window layout, minus the keyboard hints (chat sessions
|
|
286
|
-
// only host a single agent, so ctrl+g / ctrl+\ navigation is moot).
|
|
287
264
|
spawnAttachedFooter(windowName, paneId, agentType);
|
|
265
|
+
killSessionOnPaneExit(windowName, paneId);
|
|
288
266
|
|
|
289
267
|
if (isInsideAtomicSocket()) {
|
|
290
268
|
// Already on the atomic server — just switch to the new session.
|