@creativeintelligence/abbie 0.1.5 → 0.1.7

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 (142) hide show
  1. package/bin/dev.js +1 -49
  2. package/bin/run.js +42 -49
  3. package/dist/cli/commands/login.js +26 -0
  4. package/dist/cli/commands/project/add.d.ts +0 -1
  5. package/dist/cli/commands/project/add.js +16 -52
  6. package/dist/cli/commands/project/list.js +13 -93
  7. package/dist/cli/commands/project/remove.d.ts +0 -2
  8. package/dist/cli/commands/project/remove.js +11 -28
  9. package/dist/cli/commands/session/list.js +3 -12
  10. package/dist/cli/commands/session/mark-done.js +1 -7
  11. package/dist/cli/commands/session/start.d.ts +0 -1
  12. package/dist/cli/commands/session/start.js +5 -7
  13. package/dist/lib/active-sessions.d.ts +0 -12
  14. package/dist/lib/active-sessions.js +6 -175
  15. package/dist/lib/project-path.d.ts +6 -0
  16. package/dist/lib/project-path.js +21 -0
  17. package/dist/lib.d.ts +1 -2
  18. package/dist/lib.js +2 -4
  19. package/oclif.manifest.json +2569 -6368
  20. package/package.json +1 -1
  21. package/dist/cli/commands/backlog/add.d.ts +0 -22
  22. package/dist/cli/commands/backlog/add.js +0 -65
  23. package/dist/cli/commands/backlog/claim.d.ts +0 -19
  24. package/dist/cli/commands/backlog/claim.js +0 -45
  25. package/dist/cli/commands/backlog/complete.d.ts +0 -18
  26. package/dist/cli/commands/backlog/complete.js +0 -42
  27. package/dist/cli/commands/backlog/list.d.ts +0 -20
  28. package/dist/cli/commands/backlog/list.js +0 -91
  29. package/dist/cli/commands/backlog/pick.d.ts +0 -18
  30. package/dist/cli/commands/backlog/pick.js +0 -42
  31. package/dist/cli/commands/backlog/sync.d.ts +0 -24
  32. package/dist/cli/commands/backlog/sync.js +0 -109
  33. package/dist/cli/commands/daemon.d.ts +0 -56
  34. package/dist/cli/commands/daemon.js +0 -1465
  35. package/dist/cli/commands/docs/lint.d.ts +0 -18
  36. package/dist/cli/commands/docs/lint.js +0 -82
  37. package/dist/cli/commands/docs/sync.d.ts +0 -19
  38. package/dist/cli/commands/docs/sync.js +0 -76
  39. package/dist/cli/commands/gc.d.ts +0 -29
  40. package/dist/cli/commands/gc.js +0 -211
  41. package/dist/cli/commands/index.d.ts +0 -36
  42. package/dist/cli/commands/index.js +0 -228
  43. package/dist/cli/commands/panes/broker.d.ts +0 -17
  44. package/dist/cli/commands/panes/broker.js +0 -57
  45. package/dist/cli/commands/panes/pipe-sink.d.ts +0 -17
  46. package/dist/cli/commands/panes/pipe-sink.js +0 -90
  47. package/dist/cli/commands/panes/snapshot.d.ts +0 -20
  48. package/dist/cli/commands/panes/snapshot.js +0 -125
  49. package/dist/cli/commands/preview/init.d.ts +0 -25
  50. package/dist/cli/commands/preview/init.js +0 -159
  51. package/dist/cli/commands/preview/sync.d.ts +0 -23
  52. package/dist/cli/commands/preview/sync.js +0 -144
  53. package/dist/cli/commands/preview/watch.d.ts +0 -24
  54. package/dist/cli/commands/preview/watch.js +0 -153
  55. package/dist/cli/commands/resource/acquire.d.ts +0 -21
  56. package/dist/cli/commands/resource/acquire.js +0 -90
  57. package/dist/cli/commands/resource/list.d.ts +0 -15
  58. package/dist/cli/commands/resource/list.js +0 -61
  59. package/dist/cli/commands/resource/release.d.ts +0 -18
  60. package/dist/cli/commands/resource/release.js +0 -50
  61. package/dist/cli/commands/resource/wait.d.ts +0 -21
  62. package/dist/cli/commands/resource/wait.js +0 -73
  63. package/dist/cli/commands/session/view.d.ts +0 -24
  64. package/dist/cli/commands/session/view.js +0 -145
  65. package/dist/cli/commands/start.d.ts +0 -37
  66. package/dist/cli/commands/start.js +0 -234
  67. package/dist/cli/commands/triage/claim.d.ts +0 -23
  68. package/dist/cli/commands/triage/claim.js +0 -186
  69. package/dist/cli/commands/triage/list.d.ts +0 -22
  70. package/dist/cli/commands/triage/list.js +0 -112
  71. package/dist/cli/commands/triage/next.d.ts +0 -18
  72. package/dist/cli/commands/triage/next.js +0 -63
  73. package/dist/cli/commands/triage/pull.d.ts +0 -19
  74. package/dist/cli/commands/triage/pull.js +0 -82
  75. package/dist/cli/commands/triage/stats.d.ts +0 -16
  76. package/dist/cli/commands/triage/stats.js +0 -69
  77. package/dist/cli/commands/tunnel/list.d.ts +0 -16
  78. package/dist/cli/commands/tunnel/list.js +0 -98
  79. package/dist/cli/commands/tunnel/start.d.ts +0 -24
  80. package/dist/cli/commands/tunnel/start.js +0 -107
  81. package/dist/cli/commands/tunnel/stop.d.ts +0 -20
  82. package/dist/cli/commands/tunnel/stop.js +0 -90
  83. package/dist/cli/commands/tunnel/url.d.ts +0 -21
  84. package/dist/cli/commands/tunnel/url.js +0 -70
  85. package/dist/cli/commands/windows/context.d.ts +0 -18
  86. package/dist/cli/commands/windows/context.js +0 -326
  87. package/dist/cli/commands/windows/focus.d.ts +0 -17
  88. package/dist/cli/commands/windows/focus.js +0 -103
  89. package/dist/cli/commands/windows/list.d.ts +0 -21
  90. package/dist/cli/commands/windows/list.js +0 -172
  91. package/dist/cli/commands/windows/map.d.ts +0 -17
  92. package/dist/cli/commands/windows/map.js +0 -168
  93. package/dist/cli/commands/windows/read.d.ts +0 -21
  94. package/dist/cli/commands/windows/read.js +0 -241
  95. package/dist/cli/commands/windows/search.d.ts +0 -24
  96. package/dist/cli/commands/windows/search.js +0 -171
  97. package/dist/cli/commands/windows/show.d.ts +0 -19
  98. package/dist/cli/commands/windows/show.js +0 -165
  99. package/dist/cli/commands/windows/watch.d.ts +0 -19
  100. package/dist/cli/commands/windows/watch.js +0 -241
  101. package/dist/lib/managed-session.d.ts +0 -27
  102. package/dist/lib/managed-session.js +0 -105
  103. package/dist/lib/panes/broker.d.ts +0 -130
  104. package/dist/lib/panes/broker.js +0 -97
  105. package/dist/lib/panes/index.d.ts +0 -2
  106. package/dist/lib/panes/index.js +0 -1
  107. package/dist/lib/panes/server.d.ts +0 -17
  108. package/dist/lib/panes/server.js +0 -308
  109. package/dist/lib/preview/manager.d.ts +0 -77
  110. package/dist/lib/preview/manager.js +0 -369
  111. package/dist/lib/preview/schema.d.ts +0 -2
  112. package/dist/lib/preview/schema.js +0 -32
  113. package/dist/lib/preview/sprite.d.ts +0 -85
  114. package/dist/lib/preview/sprite.js +0 -321
  115. package/dist/lib/preview/watcher.d.ts +0 -63
  116. package/dist/lib/preview/watcher.js +0 -185
  117. package/dist/lib/project-identity.d.ts +0 -16
  118. package/dist/lib/project-identity.js +0 -75
  119. package/dist/lib/tmux/bridge.d.ts +0 -133
  120. package/dist/lib/tmux/bridge.js +0 -315
  121. package/dist/lib/tmux/context.d.ts +0 -82
  122. package/dist/lib/tmux/context.js +0 -239
  123. package/dist/lib/tmux/index.d.ts +0 -8
  124. package/dist/lib/tmux/index.js +0 -11
  125. package/dist/lib/tmux/map.d.ts +0 -57
  126. package/dist/lib/tmux/map.js +0 -198
  127. package/dist/lib/tmux/panes.d.ts +0 -27
  128. package/dist/lib/tmux/panes.js +0 -151
  129. package/dist/lib/tmux/redaction.d.ts +0 -57
  130. package/dist/lib/tmux/redaction.js +0 -152
  131. package/dist/lib/web/analytics.d.ts +0 -63
  132. package/dist/lib/web/analytics.js +0 -168
  133. package/dist/lib/web/server.d.ts +0 -26
  134. package/dist/lib/web/server.js +0 -697
  135. package/dist/lib/web/tmux-bridge.d.ts +0 -7
  136. package/dist/lib/web/tmux-bridge.js +0 -7
  137. package/dist/lib/windows/index.d.ts +0 -3
  138. package/dist/lib/windows/index.js +0 -2
  139. package/dist/lib/windows/inventory.d.ts +0 -21
  140. package/dist/lib/windows/inventory.js +0 -263
  141. package/dist/lib/windows/types.d.ts +0 -46
  142. package/dist/lib/windows/types.js +0 -1
@@ -1,61 +0,0 @@
1
- import { createResourceManager } from "../../../lib/resources.js";
2
- import { BaseCommand } from "../../base-command.js";
3
- export default class ResourceList extends BaseCommand {
4
- static description = "List resource availability and active leases";
5
- static hidden = false;
6
- static examples = [
7
- "<%= config.bin %> resource list",
8
- "<%= config.bin %> resource list --json",
9
- ];
10
- static flags = {
11
- ...BaseCommand.baseFlags,
12
- };
13
- async execute() {
14
- const { flags } = await this.parse(ResourceList);
15
- this.parsedFlags = flags;
16
- const resources = createResourceManager();
17
- const list = resources.listResources();
18
- if (flags.json || flags.format === "json") {
19
- return list;
20
- }
21
- // NDJSON: one resource per line
22
- if (this.ndjsonEnabled()) {
23
- this.outputNdjson(list);
24
- return list;
25
- }
26
- if (list.length === 0) {
27
- this.logInfo("No active resource leases");
28
- return [];
29
- }
30
- this.logInfo("Resource availability:\n");
31
- for (const { resource, availability } of list) {
32
- const { available, total, leases } = availability;
33
- const indicator = available > 0 ? "🟢" : "🔴";
34
- const capacityStr = total === -1 ? "∞" : `${available}/${total}`;
35
- this.log(`${indicator} ${resource}: ${capacityStr} available`);
36
- if (leases.length > 0) {
37
- for (const lease of leases) {
38
- const expiresIn = this.getExpiresIn(lease.expires_at);
39
- this.log(` └─ ${lease.session_id} (${lease.agent}) - expires ${expiresIn}`);
40
- }
41
- }
42
- this.log("");
43
- }
44
- return list;
45
- }
46
- getExpiresIn(expiresAt) {
47
- const ms = new Date(expiresAt).getTime() - Date.now();
48
- if (ms < 0)
49
- return "expired";
50
- const seconds = Math.floor(ms / 1000);
51
- const minutes = Math.floor(seconds / 60);
52
- const hours = Math.floor(minutes / 60);
53
- if (hours > 0) {
54
- return `in ${hours}h ${minutes % 60}m`;
55
- }
56
- if (minutes > 0) {
57
- return `in ${minutes}m`;
58
- }
59
- return `in ${seconds}s`;
60
- }
61
- }
@@ -1,18 +0,0 @@
1
- import { BaseCommand } from "../../base-command.js";
2
- export default class ResourceRelease extends BaseCommand {
3
- static description: string;
4
- static hidden: boolean;
5
- static examples: string[];
6
- static args: {
7
- resource: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
- session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
9
- };
10
- static flags: {
11
- format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
- ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
- };
16
- protected execute(): Promise<unknown>;
17
- }
18
- //# sourceMappingURL=release.d.ts.map
@@ -1,50 +0,0 @@
1
- import { Args } from "@oclif/core";
2
- import { createEvent, EventStore } from "../../../lib/events.js";
3
- import { createResourceManager, DEFAULT_RESOURCES } from "../../../lib/resources.js";
4
- import { BaseCommand } from "../../base-command.js";
5
- export default class ResourceRelease extends BaseCommand {
6
- static description = "Release a resource lease";
7
- static hidden = false;
8
- static examples = [
9
- "<%= config.bin %> resource release codex_api SESSION_ID",
10
- "<%= config.bin %> resource release ngrok_tunnel SESSION_ID --json",
11
- ];
12
- static args = {
13
- resource: Args.string({
14
- description: "Resource name",
15
- required: true,
16
- options: Object.keys(DEFAULT_RESOURCES),
17
- }),
18
- session_id: Args.string({
19
- description: "Session ID holding the resource",
20
- required: true,
21
- }),
22
- };
23
- static flags = {
24
- ...BaseCommand.baseFlags,
25
- };
26
- async execute() {
27
- const { args, flags } = await this.parse(ResourceRelease);
28
- this.parsedFlags = flags;
29
- const resources = createResourceManager();
30
- const events = new EventStore();
31
- const released = resources.release(args.resource, args.session_id);
32
- if (!released) {
33
- if (flags.json || flags.format === "json") {
34
- return { success: false, reason: "Lease not found" };
35
- }
36
- this.logInfo(`No active lease found for ${args.session_id} on ${args.resource}`);
37
- return { success: false };
38
- }
39
- // Emit resource released event
40
- events.append(createEvent.resourceReleased(args.session_id, "claude", {
41
- resource: args.resource,
42
- lease_id: `${args.session_id}-${args.resource}`,
43
- }));
44
- if (flags.json || flags.format === "json") {
45
- return { success: true };
46
- }
47
- this.logInfo(`Released ${args.resource} for ${args.session_id}`);
48
- return { success: true };
49
- }
50
- }
@@ -1,21 +0,0 @@
1
- import { BaseCommand } from "../../base-command.js";
2
- export default class ResourceWait extends BaseCommand {
3
- static description: string;
4
- static hidden: boolean;
5
- static examples: string[];
6
- static args: {
7
- resource: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
- session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
9
- };
10
- static flags: {
11
- agent: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
- "max-wait": import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
- ttl: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
- format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
- quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
- "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
- ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
- };
19
- protected execute(): Promise<unknown>;
20
- }
21
- //# sourceMappingURL=wait.d.ts.map
@@ -1,73 +0,0 @@
1
- import { Args, Flags } from "@oclif/core";
2
- import { createEvent, EventStore } from "../../../lib/events.js";
3
- import { createResourceManager, DEFAULT_RESOURCES } from "../../../lib/resources.js";
4
- import { AGENTS } from "../../../lib/types.js";
5
- import { BaseCommand } from "../../base-command.js";
6
- export default class ResourceWait extends BaseCommand {
7
- static description = "Wait for a resource to become available and acquire it";
8
- static hidden = false;
9
- static examples = [
10
- "<%= config.bin %> resource wait codex_api SESSION_ID --agent claude",
11
- "<%= config.bin %> resource wait codex_api SESSION_ID --agent claude --max-wait 300000",
12
- "<%= config.bin %> resource wait ngrok_tunnel SESSION_ID --agent codex --json",
13
- ];
14
- static args = {
15
- resource: Args.string({
16
- description: "Resource name",
17
- required: true,
18
- options: Object.keys(DEFAULT_RESOURCES),
19
- }),
20
- session_id: Args.string({
21
- description: "Session ID requesting the resource",
22
- required: true,
23
- }),
24
- };
25
- static flags = {
26
- ...BaseCommand.baseFlags,
27
- agent: Flags.string({
28
- char: "a",
29
- description: "Agent type",
30
- required: true,
31
- options: [...AGENTS],
32
- }),
33
- "max-wait": Flags.integer({
34
- description: "Maximum wait time in milliseconds",
35
- default: 300000, // 5 minutes
36
- }),
37
- ttl: Flags.integer({
38
- char: "t",
39
- description: "Time-to-live for acquired lease in milliseconds",
40
- }),
41
- };
42
- async execute() {
43
- const { args, flags } = await this.parse(ResourceWait);
44
- this.parsedFlags = flags;
45
- const resources = createResourceManager();
46
- const events = new EventStore();
47
- if (!(flags.json || flags.format === "json")) {
48
- this.logInfo(`Waiting for ${args.resource} (max ${flags["max-wait"]}ms)...`);
49
- }
50
- const lease = await resources.waitForResource(args.resource, args.session_id, flags.agent, {
51
- maxWaitMs: flags["max-wait"],
52
- ttlMs: flags.ttl,
53
- });
54
- if (!lease) {
55
- if (flags.json || flags.format === "json") {
56
- return { success: false, reason: "Timeout waiting for resource" };
57
- }
58
- this.logInfo(`Timeout waiting for ${args.resource}`);
59
- return { success: false };
60
- }
61
- // Emit resource acquired event
62
- events.append(createEvent.resourceAcquired(args.session_id, flags.agent, {
63
- resource: args.resource,
64
- lease_id: lease.lease_id,
65
- expires_at: lease.expires_at,
66
- }));
67
- if (flags.json || flags.format === "json") {
68
- return { success: true, lease };
69
- }
70
- this.log(lease.lease_id);
71
- return { success: true, lease };
72
- }
73
- }
@@ -1,24 +0,0 @@
1
- import { BaseCommand } from "../../base-command.js";
2
- export default class SessionView extends BaseCommand {
3
- static description: string;
4
- static hidden: boolean;
5
- static examples: string[];
6
- static args: {
7
- session_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
- };
9
- static flags: {
10
- "tmux-session": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
- format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
- ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
- };
16
- protected execute(): Promise<unknown>;
17
- private ensureFile;
18
- private shellEscape;
19
- private runTmux;
20
- private hasProjectWindow;
21
- private openInNvimIfAvailable;
22
- private openInTmuxPane;
23
- }
24
- //# sourceMappingURL=view.d.ts.map
@@ -1,145 +0,0 @@
1
- import { spawn, spawnSync } from "node:child_process";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
- import { dirname } from "node:path";
4
- import { Args, Flags } from "@oclif/core";
5
- import { getActiveSessionManager } from "../../../lib/active-sessions.js";
6
- import { ensureManagedSession } from "../../../lib/managed-session.js";
7
- import { getNvimInstanceForWindow } from "../../../lib/nvim/discovery.js";
8
- import { isServerReachable, remoteSend } from "../../../lib/nvim/remote.js";
9
- import { buildProcessTree, getProcessSnapshot } from "../../../lib/process/snapshot.js";
10
- import { getSessionLogPaths } from "../../../lib/session-result.js";
11
- import { getPaneId, listWindows } from "../../../lib/tmux/bridge.js";
12
- import { BaseCommand } from "../../base-command.js";
13
- export default class SessionView extends BaseCommand {
14
- static description = "Attach a viewer to a session's runner stdout log";
15
- static hidden = false;
16
- static examples = [
17
- "<%= config.bin %> session view hostagent-20241225-120000-abc1",
18
- "<%= config.bin %> session view hostagent-20241225-120000-abc1 --tmux-session abbie",
19
- ];
20
- static args = {
21
- session_id: Args.string({
22
- description: "Session ID to view",
23
- required: true,
24
- }),
25
- };
26
- static flags = {
27
- ...BaseCommand.baseFlags,
28
- "tmux-session": Flags.string({
29
- description: 'Tmux session name (default: managed "abbie" session)',
30
- }),
31
- };
32
- async execute() {
33
- const { args, flags } = await this.parse(SessionView);
34
- this.parsedFlags = flags;
35
- const manager = getActiveSessionManager();
36
- const session = await manager.getAsync(args.session_id);
37
- if (!session) {
38
- this.error(`Session not found: ${args.session_id}`);
39
- }
40
- const stdoutPath = session.stdout_path || getSessionLogPaths(args.session_id).stdoutPath;
41
- this.ensureFile(stdoutPath);
42
- const tmuxSession = flags["tmux-session"] || (await ensureManagedSession());
43
- const projectWindowExists = await this.hasProjectWindow(tmuxSession, session.project);
44
- const attachedInNvim = await this.openInNvimIfAvailable(projectWindowExists, session.project, tmuxSession, stdoutPath);
45
- if (attachedInNvim) {
46
- const payload = {
47
- session_id: args.session_id,
48
- project: session.project,
49
- method: "nvim-tab",
50
- stdout_path: stdoutPath,
51
- tmux_session: tmuxSession,
52
- };
53
- if (flags.json || flags.format === "json") {
54
- return payload;
55
- }
56
- this.log(`Attached via nvim tab: tail -F ${stdoutPath}`);
57
- return payload;
58
- }
59
- await this.openInTmuxPane(projectWindowExists, session.project, session.cwd, tmuxSession, stdoutPath, args.session_id);
60
- const payload = {
61
- session_id: args.session_id,
62
- project: session.project,
63
- method: "tmux-pane",
64
- stdout_path: stdoutPath,
65
- tmux_session: tmuxSession,
66
- };
67
- if (flags.json || flags.format === "json") {
68
- return payload;
69
- }
70
- this.log(`Attached via tmux pane: tail -F ${stdoutPath}`);
71
- return payload;
72
- }
73
- ensureFile(path) {
74
- if (existsSync(path))
75
- return;
76
- mkdirSync(dirname(path), { recursive: true });
77
- writeFileSync(path, "", { flag: "a" });
78
- }
79
- shellEscape(value) {
80
- return `'${value.replace(/'/g, `'\\''`)}'`;
81
- }
82
- async runTmux(args) {
83
- await new Promise((resolve, reject) => {
84
- const proc = spawn("tmux", args, { stdio: ["ignore", "ignore", "ignore"] });
85
- proc.on("close", (code) => {
86
- if (code === 0)
87
- resolve();
88
- else
89
- reject(new Error(`tmux ${args.join(" ")} failed with code ${code}`));
90
- });
91
- proc.on("error", reject);
92
- });
93
- }
94
- async hasProjectWindow(tmuxSession, project) {
95
- const windows = await listWindows(tmuxSession);
96
- return windows.some((window) => window.name === project);
97
- }
98
- async openInNvimIfAvailable(projectWindowExists, project, tmuxSession, stdoutPath) {
99
- if (!projectWindowExists)
100
- return false;
101
- const paneId = await getPaneId(project, 0, tmuxSession);
102
- if (!paneId)
103
- return false;
104
- const panePidRaw = spawnSync("tmux", ["display-message", "-p", "-t", paneId, "#{pane_pid}"], {
105
- encoding: "utf8",
106
- }).stdout.trim();
107
- const panePid = parseInt(panePidRaw, 10);
108
- if (!(panePid > 0))
109
- return false;
110
- const snapshot = await getProcessSnapshot();
111
- const tree = buildProcessTree(snapshot);
112
- const nvimInstance = await getNvimInstanceForWindow(panePid, snapshot, tree);
113
- if (!nvimInstance?.serverAddr)
114
- return false;
115
- const reachable = await isServerReachable(nvimInstance.serverAddr);
116
- if (!reachable)
117
- return false;
118
- const tailCommand = `tail -F ${this.shellEscape(stdoutPath)}`;
119
- return remoteSend(nvimInstance.serverAddr, `:tabnew | terminal ${tailCommand}\n`);
120
- }
121
- async openInTmuxPane(projectWindowExists, project, cwd, tmuxSession, stdoutPath, sessionId) {
122
- const tailCommand = `tail -F ${this.shellEscape(stdoutPath)}`;
123
- if (projectWindowExists) {
124
- await this.runTmux([
125
- "split-window",
126
- "-t",
127
- `${tmuxSession}:${project}`,
128
- "-c",
129
- cwd,
130
- tailCommand,
131
- ]);
132
- return;
133
- }
134
- await this.runTmux([
135
- "new-window",
136
- "-t",
137
- `${tmuxSession}:`,
138
- "-n",
139
- `${project}-view-${sessionId.slice(-4)}`,
140
- "-c",
141
- cwd,
142
- tailCommand,
143
- ]);
144
- }
145
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * abbie (default command) — the single entry point.
3
- *
4
- * First run:
5
- * 1. Check Pi → not found → auto-install
6
- * 2. Install Abbie Pi extension
7
- * 3. Open browser for Clerk auth
8
- * 4. Connect AI provider (OAuth — uses your subscription)
9
- * 5. Launch Pi (extension auto-starts bridge)
10
- *
11
- * Subsequent runs:
12
- * Launch Pi directly (extension handles bridge)
13
- */
14
- import { BaseCommand } from "../base-command.js";
15
- export default class DefaultCommand extends BaseCommand {
16
- static id: string;
17
- static summary: string;
18
- static description: string;
19
- static examples: string[];
20
- static flags: {
21
- setup: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
- status: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
- format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
- quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
- "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
26
- ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
27
- };
28
- static strict: boolean;
29
- execute(): Promise<unknown>;
30
- private runSetup;
31
- /**
32
- * Find the bundled extension source directory.
33
- * Looks in several locations depending on how the CLI is installed.
34
- */
35
- private findExtensionSource;
36
- }
37
- //# sourceMappingURL=start.d.ts.map
@@ -1,234 +0,0 @@
1
- /**
2
- * abbie (default command) — the single entry point.
3
- *
4
- * First run:
5
- * 1. Check Pi → not found → auto-install
6
- * 2. Install Abbie Pi extension
7
- * 3. Open browser for Clerk auth
8
- * 4. Connect AI provider (OAuth — uses your subscription)
9
- * 5. Launch Pi (extension auto-starts bridge)
10
- *
11
- * Subsequent runs:
12
- * Launch Pi directly (extension handles bridge)
13
- */
14
- import { existsSync, mkdirSync, cpSync, readFileSync } from "node:fs";
15
- import { homedir } from "node:os";
16
- import { join, dirname } from "node:path";
17
- import { fileURLToPath } from "node:url";
18
- import { spawnSync, spawn, execSync } from "node:child_process";
19
- import { Flags } from "@oclif/core";
20
- import { BaseCommand } from "../base-command.js";
21
- import { hasAnyProvider, ensureProviderConnected, getConnectedProviders } from "../../lib/provider-auth.js";
22
- const __filename = fileURLToPath(import.meta.url);
23
- const __dirname = dirname(__filename);
24
- const PI_PACKAGE = "@mariozechner/pi-coding-agent";
25
- const ABBIE_HOME = join(homedir(), ".abbie");
26
- const PI_AGENT_DIR = join(homedir(), ".pi", "agent");
27
- const PI_EXT_DIR = join(PI_AGENT_DIR, "extensions", "abbie");
28
- function piInstalled() {
29
- try {
30
- const result = spawnSync("pi", ["--version"], { encoding: "utf8", timeout: 5000 });
31
- if (result.status === 0 && result.stdout?.trim()) {
32
- return { installed: true, version: result.stdout.trim(), path: "pi" };
33
- }
34
- }
35
- catch { /* not found */ }
36
- return { installed: false };
37
- }
38
- function abbieConfigured() {
39
- const configPath = join(ABBIE_HOME, "config.json");
40
- if (!existsSync(configPath))
41
- return false;
42
- try {
43
- const cfg = JSON.parse(readFileSync(configPath, "utf8"));
44
- // Must have clerkId — convexUrl alone isn't enough (user may have logged out)
45
- return Boolean(cfg.clerkId);
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- function extensionInstalled() {
52
- return existsSync(join(PI_EXT_DIR, "index.ts")) || existsSync(join(PI_EXT_DIR, "index.js"));
53
- }
54
- export default class DefaultCommand extends BaseCommand {
55
- static id = "";
56
- static summary = "Launch Abbie";
57
- static description = `Start an agent session powered by Pi with cloud sync via Abbie.
58
-
59
- On first run, walks you through setup:
60
- • Installs Pi (the coding agent runtime) if needed
61
- • Installs the Abbie extension into Pi
62
- • Authenticates with your Abbie account
63
- • Launches Pi with the bridge auto-connected
64
-
65
- After setup, just run \`abbie\` to start working.`;
66
- static examples = [
67
- "$ abbie",
68
- "$ abbie --setup",
69
- "$ abbie --status",
70
- ];
71
- static flags = {
72
- ...BaseCommand.baseFlags,
73
- setup: Flags.boolean({
74
- description: "Force re-run the setup wizard",
75
- default: false,
76
- }),
77
- status: Flags.boolean({
78
- description: "Show setup status and exit",
79
- default: false,
80
- }),
81
- };
82
- // Pass-through: any extra args go to pi
83
- static strict = false;
84
- async execute() {
85
- const { flags, argv } = await this.parse(DefaultCommand);
86
- this.parsedFlags = flags;
87
- const pi = piInstalled();
88
- const configured = abbieConfigured();
89
- const extOk = extensionInstalled();
90
- // --status: show state and exit
91
- if (flags.status) {
92
- const providers = getConnectedProviders();
93
- this.log("");
94
- this.log(" abbie status");
95
- this.log("");
96
- this.log(` pi: ${pi.installed ? `✓ ${pi.version}` : "✗ not installed"}`);
97
- this.log(` extension: ${extOk ? "✓ installed" : "✗ not installed"}`);
98
- this.log(` auth: ${configured ? "✓ configured" : "✗ not configured"}`);
99
- this.log(` providers: ${providers.length > 0 ? `✓ ${providers.join(", ")}` : "✗ none connected"}`);
100
- this.log("");
101
- return { pi: pi.installed, extension: extOk, auth: configured, providers };
102
- }
103
- // Setup needed?
104
- const providerOk = hasAnyProvider();
105
- const needsSetup = flags.setup || !pi.installed || !configured || !extOk || !providerOk;
106
- if (needsSetup) {
107
- await this.runSetup(pi, configured, extOk);
108
- // Re-check after setup
109
- const piNow = piInstalled();
110
- if (!piNow.installed) {
111
- this.error("Pi installation failed. Install manually:\n\n npm install -g @mariozechner/pi-coding-agent\n");
112
- }
113
- }
114
- // Launch Pi with any pass-through args
115
- this.log("");
116
- this.log(" launching pi...");
117
- this.log("");
118
- const piArgs = argv.length > 0 ? argv : [];
119
- const child = spawn("pi", piArgs, {
120
- stdio: "inherit",
121
- env: process.env,
122
- cwd: process.cwd(),
123
- });
124
- process.on("SIGINT", () => child.kill("SIGINT"));
125
- process.on("SIGTERM", () => child.kill("SIGTERM"));
126
- return new Promise((resolve) => {
127
- child.on("exit", (code) => {
128
- resolve({ exitCode: code ?? 0 });
129
- });
130
- });
131
- }
132
- async runSetup(pi, configured, extOk) {
133
- this.log("");
134
- this.log(" abbie setup");
135
- this.log(" ──────────");
136
- this.log("");
137
- // Step 1: Install Pi
138
- if (!pi.installed) {
139
- this.log(" [1/4] installing pi...");
140
- this.log(` npm install -g ${PI_PACKAGE}`);
141
- this.log("");
142
- try {
143
- execSync(`npm install -g ${PI_PACKAGE}`, {
144
- stdio: ["pipe", "pipe", "pipe"],
145
- encoding: "utf8",
146
- timeout: 120_000,
147
- });
148
- const check = piInstalled();
149
- if (check.installed) {
150
- this.log(` ✓ pi ${check.version} installed`);
151
- }
152
- else {
153
- this.error("Pi install completed but binary not found. Check your PATH.");
154
- }
155
- }
156
- catch (err) {
157
- const msg = err instanceof Error ? err.message : String(err);
158
- this.logWarn(`auto-install failed: ${msg}`);
159
- this.log("");
160
- this.log(" install manually:");
161
- this.log(` npm install -g ${PI_PACKAGE}`);
162
- this.log("");
163
- this.error("Pi is required to continue.");
164
- }
165
- }
166
- else {
167
- this.log(` [1/4] pi ${pi.version} ✓`);
168
- }
169
- this.log("");
170
- // Step 2: Install Abbie extension
171
- if (!extOk) {
172
- this.log(" [2/4] installing abbie extension...");
173
- // Find the bundled extension
174
- const extSource = this.findExtensionSource();
175
- if (extSource) {
176
- mkdirSync(PI_EXT_DIR, { recursive: true });
177
- cpSync(extSource, PI_EXT_DIR, { recursive: true });
178
- this.log(" ✓ extension installed");
179
- }
180
- else {
181
- this.logWarn("extension source not found — the abbie tools won't be available in Pi");
182
- this.logWarn("this is non-fatal; you can install it manually later");
183
- }
184
- }
185
- else {
186
- this.log(" [2/4] extension ✓");
187
- }
188
- this.log("");
189
- // Step 3: Authenticate
190
- if (!configured) {
191
- this.log(" [3/4] authenticating...");
192
- this.log(" opening browser for login...");
193
- this.log("");
194
- // Delegate to the login command
195
- try {
196
- await this.config.runCommand("login", []);
197
- }
198
- catch {
199
- this.logWarn("login failed or was cancelled — you can run `abbie login` later");
200
- }
201
- }
202
- else {
203
- this.log(" [3/4] authenticated ✓");
204
- }
205
- this.log("");
206
- // Step 4: Connect AI provider
207
- await ensureProviderConnected((msg) => this.log(msg));
208
- this.log("");
209
- this.log(" setup complete ✓");
210
- }
211
- /**
212
- * Find the bundled extension source directory.
213
- * Looks in several locations depending on how the CLI is installed.
214
- */
215
- findExtensionSource() {
216
- const candidates = [
217
- // When installed globally via npm — extension bundled in package
218
- join(__dirname, "..", "..", "..", "extensions", "abbie"),
219
- // When running from the monorepo
220
- join(process.cwd(), "extensions", "abbie"),
221
- // The canonical location (might already exist from manual setup)
222
- join(homedir(), ".pi", "agent", "extensions", "abbie"),
223
- ];
224
- for (const candidate of candidates) {
225
- if (existsSync(join(candidate, "index.ts")) || existsSync(join(candidate, "index.js"))) {
226
- // Don't "find" the target as a source — that's circular
227
- if (candidate === PI_EXT_DIR)
228
- continue;
229
- return candidate;
230
- }
231
- }
232
- return null;
233
- }
234
- }