@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
package/bin/dev.js CHANGED
@@ -3,59 +3,11 @@
3
3
  // @ts-check
4
4
 
5
5
  import { execute, settings } from '@oclif/core';
6
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
7
- import { dirname, join, resolve } from "node:path";
6
+ import { dirname, resolve } from "node:path";
8
7
  import { fileURLToPath } from "node:url";
9
- import { Plugin } from "@oclif/core";
10
8
 
11
9
  settings.performanceEnabled = false;
12
10
 
13
11
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
14
12
 
15
- async function ensureManifestIsFresh() {
16
- const manifestPath = join(root, "oclif.manifest.json");
17
- const panesSnapshotPath = join(root, "dist", "cli", "commands", "panes", "snapshot.js");
18
- const panesBrokerPath = join(root, "dist", "cli", "commands", "panes", "broker.js");
19
- const panesPipeSinkPath = join(root, "dist", "cli", "commands", "panes", "pipe-sink.js");
20
-
21
- if (!existsSync(panesSnapshotPath)) return;
22
-
23
- try {
24
- if (existsSync(manifestPath)) {
25
- const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
26
- const commands = parsed?.commands ?? {};
27
- const hasSnapshot = Boolean(commands["panes:snapshot"]);
28
- const hasBroker = Boolean(commands["panes:broker"]);
29
- const hasPipeSink = Boolean(commands["panes:pipe-sink"]);
30
-
31
- const distHasBroker = existsSync(panesBrokerPath);
32
- const distHasPipeSink = existsSync(panesPipeSinkPath);
33
-
34
- if (hasSnapshot && (!distHasBroker || hasBroker) && (!distHasPipeSink || hasPipeSink)) return;
35
- }
36
- } catch {
37
- // fall through -> regenerate
38
- }
39
-
40
- try {
41
- const pjson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
42
- const plugin = new Plugin({ root, pjson, isRoot: true, ignoreManifest: true });
43
- await plugin.load();
44
- writeFileSync(manifestPath, `${JSON.stringify(plugin.manifest, null, 2)}\n`);
45
- } catch {
46
- // Best-effort. If manifest regen fails, oclif will surface errors as usual.
47
- }
48
- }
49
-
50
- await ensureManifestIsFresh();
51
-
52
- // Default command: if no args (or only flags), run "start"
53
- const userArgs = process.argv.slice(2);
54
- const firstArg = userArgs[0];
55
- const isSubcommand = firstArg && !firstArg.startsWith("-");
56
-
57
- if (!isSubcommand) {
58
- process.argv.splice(2, 0, "start");
59
- }
60
-
61
13
  await execute({ development: true, dir: import.meta.url });
package/bin/run.js CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  // @ts-check
4
4
 
5
- import { execute, Plugin, settings } from "@oclif/core";
6
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { execute, settings } from "@oclif/core";
7
6
  import { dirname, join, resolve } from "node:path";
8
7
  import { fileURLToPath } from "node:url";
9
8
 
@@ -11,56 +10,50 @@ settings.performanceEnabled = false;
11
10
 
12
11
  const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
13
12
 
14
- async function ensureManifestIsFresh() {
15
- const manifestPath = join(root, "oclif.manifest.json");
16
- const panesSnapshotPath = join(root, "dist", "cli", "commands", "panes", "snapshot.js");
17
- const panesBrokerPath = join(root, "dist", "cli", "commands", "panes", "broker.js");
18
- const panesPipeSinkPath = join(root, "dist", "cli", "commands", "panes", "pipe-sink.js");
13
+ // No manifest fixup needed — panes commands removed
19
14
 
20
- // If the panes snapshot command isn't built, don't force manifest regeneration.
21
- if (!existsSync(panesSnapshotPath)) return;
22
-
23
- try {
24
- if (existsSync(manifestPath)) {
25
- const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
26
- const commands = parsed?.commands ?? {};
27
- const hasSnapshot = Boolean(commands["panes:snapshot"]);
28
- const hasBroker = Boolean(commands["panes:broker"]);
29
- const hasPipeSink = Boolean(commands["panes:pipe-sink"]);
30
-
31
- // If dist contains the command but manifest doesn't, regenerate.
32
- const distHasBroker = existsSync(panesBrokerPath);
33
- const distHasPipeSink = existsSync(panesPipeSinkPath);
34
-
35
- if (hasSnapshot && (!distHasBroker || hasBroker) && (!distHasPipeSink || hasPipeSink)) return;
36
- }
37
- } catch {
38
- // fall through -> regenerate
39
- }
40
-
41
- try {
42
- const pjson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
43
- const plugin = new Plugin({ root, pjson, isRoot: true, ignoreManifest: true });
44
- await plugin.load();
45
- writeFileSync(manifestPath, `${JSON.stringify(plugin.manifest, null, 2)}\n`);
46
- } catch {
47
- // Best-effort. If manifest regen fails, oclif will surface errors as usual.
48
- }
49
- }
50
-
51
- await ensureManifestIsFresh();
52
-
53
- // Default command: if no args (or only flags like --status/--setup), run "start"
15
+ // Default behavior: if no args, launch TUI (like `claude` launches Claude Code).
16
+ // If subcommand provided, dispatch to oclif.
54
17
  const userArgs = process.argv.slice(2);
55
18
  const firstArg = userArgs[0];
56
19
  const isSubcommand = firstArg && !firstArg.startsWith("-");
57
20
 
58
- if (!isSubcommand) {
59
- // No subcommand providedinject "start" as the default
60
- process.argv.splice(2, 0, "start");
61
- }
21
+ if (!isSubcommand && !firstArg) {
22
+ // No args at all launch TUI
23
+ const { existsSync } = await import("node:fs");
24
+ const { join } = await import("node:path");
25
+ const { spawn } = await import("node:child_process");
26
+
27
+ // Look for TUI binary
28
+ const candidates = [
29
+ join(root, "..", "tui", "target", "release", "abbie-tui"),
30
+ join(root, "..", "..", "apps", "tui", "target", "release", "abbie-tui"),
31
+ ];
32
+
33
+ // Also check if installed globally
34
+ const { spawnSync } = await import("node:child_process");
35
+ const whichResult = spawnSync("which", ["abbie-tui"], { encoding: "utf8" });
36
+ if (whichResult.status === 0 && whichResult.stdout.trim()) {
37
+ candidates.unshift(whichResult.stdout.trim());
38
+ }
62
39
 
63
- await execute({
64
- development: false,
65
- loadOptions: { root },
66
- });
40
+ const tuiBin = candidates.find((c) => existsSync(c));
41
+
42
+ if (tuiBin) {
43
+ const tui = spawn(tuiBin, userArgs, { stdio: "inherit" });
44
+ tui.on("exit", (code) => { process.exitCode = code ?? 0; });
45
+ } else {
46
+ // TUI not built — show help
47
+ console.log("Abbie TUI not found. Run: cd apps/tui && cargo build --release");
48
+ console.log("");
49
+ console.log("Available commands:");
50
+ process.argv.splice(2, 0, "--help");
51
+ await execute({ development: false, loadOptions: { root } });
52
+ }
53
+ } else {
54
+ // Has subcommand or flags — dispatch to oclif
55
+ await execute({
56
+ development: false,
57
+ loadOptions: { root },
58
+ });
59
+ }
@@ -141,6 +141,32 @@ If already authenticated, shows current status. Use --force to re-authenticate.`
141
141
  }
142
142
  // Step 4: Save credentials
143
143
  saveClerkId(clerkId);
144
+ // Step 4b: Fetch bridge secret for auto-bridge startup
145
+ try {
146
+ const deployConfig = await client.query(api.functions.auth.tokens.getDeploymentConfig, {
147
+ code,
148
+ deviceId: device.deviceId,
149
+ });
150
+ if (deployConfig.bridgeSecret) {
151
+ const { existsSync, readFileSync, writeFileSync, mkdirSync } = await import("node:fs");
152
+ const { join } = await import("node:path");
153
+ const { homedir } = await import("node:os");
154
+ const configPath = join(homedir(), ".abbie", "config.json");
155
+ const dir = join(homedir(), ".abbie");
156
+ if (!existsSync(dir))
157
+ mkdirSync(dir, { recursive: true });
158
+ let cfg = {};
159
+ try {
160
+ cfg = JSON.parse(readFileSync(configPath, "utf8"));
161
+ }
162
+ catch { /* new file */ }
163
+ cfg.bridgeSecret = deployConfig.bridgeSecret;
164
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2));
165
+ }
166
+ }
167
+ catch {
168
+ // Non-fatal — bridge can still be started manually
169
+ }
144
170
  this.log("");
145
171
  this.log(" ✓ authenticated");
146
172
  this.log(` user: ${clerkId.slice(0, 16)}...`);
@@ -8,7 +8,6 @@ export default class ProjectAdd extends BaseCommand {
8
8
  path: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
9
9
  };
10
10
  static flags: {
11
- bootstrap: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
11
  format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
12
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
13
  "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,16 +1,15 @@
1
1
  import { existsSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { Args, Flags } from "@oclif/core";
2
+ import { resolve } from "node:path";
3
+ import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
4
+ import { Args } from "@oclif/core";
4
5
  import { findProjectPath } from "../../../lib/config-loader.js";
5
- import { ensureManagedSession, hasProjectWindow } from "../../../lib/managed-session.js";
6
6
  import { BaseCommand } from "../../base-command.js";
7
7
  export default class ProjectAdd extends BaseCommand {
8
- static description = "Add a project to the managed abbie workspace";
8
+ static description = "Add a project to Abbie";
9
9
  static hidden = false;
10
10
  static examples = [
11
11
  "<%= config.bin %> project add arbor",
12
12
  "<%= config.bin %> project add arbor ~/Developer/apps/arbor/arbor-xyz",
13
- "<%= config.bin %> project add arbor --bootstrap",
14
13
  ];
15
14
  static args = {
16
15
  name: Args.string({ required: true, description: "Project name" }),
@@ -18,22 +17,11 @@ export default class ProjectAdd extends BaseCommand {
18
17
  };
19
18
  static flags = {
20
19
  ...BaseCommand.baseFlags,
21
- bootstrap: Flags.boolean({
22
- description: "Run bootstrap if .abbie/config.json is missing",
23
- default: false,
24
- }),
25
20
  };
26
21
  async execute() {
27
22
  const { args, flags } = await this.parse(ProjectAdd);
28
23
  this.parsedFlags = flags;
29
24
  const name = args.name;
30
- // Ensure the managed abbie tmux session exists
31
- const session = await ensureManagedSession();
32
- // Check if project window already exists
33
- if (await hasProjectWindow(name)) {
34
- this.error(`Project "${name}" already exists in the ${session} session`);
35
- }
36
- // Resolve project path
37
25
  let projectPath = args.path;
38
26
  if (!projectPath) {
39
27
  projectPath = findProjectPath(name);
@@ -41,49 +29,25 @@ export default class ProjectAdd extends BaseCommand {
41
29
  this.error(`Could not find project "${name}". Provide an explicit path: abbie project add ${name} /path/to/project`);
42
30
  }
43
31
  }
32
+ projectPath = resolve(projectPath);
44
33
  if (!existsSync(projectPath)) {
45
34
  this.error(`Path does not exist: ${projectPath}`);
46
35
  }
47
- // Check for config
48
- const configPath = join(projectPath, ".abbie", "config.json");
49
- const hasConfig = existsSync(configPath);
50
- // Create tmux window with nvim
51
- const { newWindow } = await import("../../../lib/tmux/bridge.js");
52
- await newWindow({
53
- name,
54
- cwd: projectPath,
55
- session,
56
- command: "nvim",
57
- });
36
+ try {
37
+ const client = getHttpClient();
38
+ await client.mutation(api.projects.create, {
39
+ name,
40
+ path: projectPath,
41
+ });
42
+ }
43
+ catch (error) {
44
+ this.logWarn(`Failed to sync to Convex: ${error.message}`);
45
+ }
58
46
  const isJson = flags.json || flags.format === "json";
47
+ const result = { name, path: projectPath, status: "added" };
59
48
  if (!isJson) {
60
49
  this.log(`Added project: ${name}`);
61
50
  this.log(`Path: ${projectPath}`);
62
- this.log(`Session: ${session}`);
63
- if (!hasConfig) {
64
- this.log(`Note: No .abbie/config.json found. Run: abbie bootstrap --path ${projectPath}`);
65
- }
66
- }
67
- // Run bootstrap if requested and config missing
68
- if (flags.bootstrap && !hasConfig) {
69
- this.logInfo(`Bootstrapping .abbie for ${name}...`);
70
- const { spawn } = await import("node:child_process");
71
- await new Promise((resolve) => {
72
- const proc = spawn("abbie", ["bootstrap", "--path", projectPath], {
73
- stdio: "inherit",
74
- });
75
- proc.on("close", () => resolve());
76
- });
77
- }
78
- const result = {
79
- name,
80
- path: projectPath,
81
- session,
82
- hasConfig,
83
- status: "added",
84
- };
85
- if (isJson) {
86
- return result;
87
51
  }
88
52
  return result;
89
53
  }
@@ -1,24 +1,11 @@
1
- import { findProject, loadConfigFromPath } from "../../../lib/config-loader.js";
2
- import { getManagedSessionName, listManagedWindows } from "../../../lib/managed-session.js";
1
+ import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
3
2
  import { BaseCommand } from "../../base-command.js";
4
- const INTERNAL_WINDOW_NAMES = new Set(["_abbie"]);
5
- const INTERNAL_SHELL_COMMANDS = new Set(["zsh", "bash", "fish", "sh"]);
6
- function abbreviateHome(path) {
7
- const home = process.env.HOME;
8
- if (!home)
9
- return path;
10
- if (path === home)
11
- return "~";
12
- if (path.startsWith(`${home}/`))
13
- return `~/${path.slice(home.length + 1)}`;
14
- return path;
15
- }
16
3
  export default class ProjectList extends BaseCommand {
17
- static description = "List projects in the managed abbie workspace";
4
+ static description = "List projects";
18
5
  static hidden = false;
19
6
  static examples = [
20
7
  "<%= config.bin %> project list",
21
- "<%= config.bin %> project list --json --quiet",
8
+ "<%= config.bin %> project list --json",
22
9
  ];
23
10
  static flags = {
24
11
  ...BaseCommand.baseFlags,
@@ -26,88 +13,21 @@ export default class ProjectList extends BaseCommand {
26
13
  async execute() {
27
14
  const { flags } = await this.parse(ProjectList);
28
15
  this.parsedFlags = flags;
29
- const session = getManagedSessionName();
30
- const windows = await listManagedWindows();
31
- // Enrich each window with project config metadata
32
- const projects = [];
33
- for (const win of windows) {
34
- // Exclude internal/session bootstrap window from project list
35
- if (INTERNAL_WINDOW_NAMES.has(win.name))
36
- continue;
37
- // Get pane cwd for config lookup
38
- let path = "";
39
- let currentCommand = "";
40
- let hasConfig = false;
41
- let emoji;
42
- let team;
43
- try {
44
- const { listPanes } = await import("../../../lib/tmux/bridge.js");
45
- const panes = await listPanes(win.index, session);
46
- if (panes.length > 0) {
47
- path = panes[0].currentPath;
48
- currentCommand = panes[0].currentCommand;
49
- }
50
- }
51
- catch {
52
- // Fall back to discovery
53
- }
54
- // Best-effort heuristic to hide legacy bootstrap windows created before `_abbie`
55
- if (win.index === 0 &&
56
- INTERNAL_SHELL_COMMANDS.has(currentCommand) &&
57
- win.name === currentCommand) {
58
- continue;
59
- }
60
- // Try to load config from pane path
61
- if (path) {
62
- try {
63
- const loaded = loadConfigFromPath(path);
64
- if (loaded) {
65
- hasConfig = true;
66
- emoji = loaded.config.emoji;
67
- team = loaded.config.team;
68
- }
69
- }
70
- catch {
71
- // No config — fine
72
- }
73
- }
74
- // Fall back to project discovery
75
- if (!hasConfig) {
76
- const discovered = findProject(win.name);
77
- if (discovered) {
78
- if (!path)
79
- path = discovered.path;
80
- hasConfig = discovered.hasConfig;
81
- }
82
- }
83
- projects.push({
84
- name: win.name,
85
- path,
86
- windowIndex: win.index,
87
- hasConfig,
88
- emoji,
89
- team,
90
- });
91
- }
16
+ const client = getHttpClient();
17
+ const projects = await client.query(api.projects.list, {});
92
18
  const isJson = flags.json || flags.format === "json";
93
19
  if (isJson) {
94
- return { session, projects };
20
+ return projects;
95
21
  }
96
22
  if (projects.length === 0) {
97
- this.log(`No projects in ${session} session`);
98
- this.log("");
99
- this.log("Add one with: abbie project add <name> [path]");
100
- return { session, projects };
23
+ this.log("No projects found. Add one with: abbie project add <name>");
24
+ return [];
101
25
  }
102
- // Table output
103
- this.log(`Projects in ${session} session:\n`);
104
- for (const p of projects) {
105
- const icon = p.emoji ?? "";
106
- const configMark = p.hasConfig ? "✓" : "-";
107
- const teamLabel = p.team ? ` [${p.team}]` : "";
108
- this.log(` ${icon} ${p.name}${teamLabel} ${abbreviateHome(p.path)} config:${configMark}`);
26
+ for (const project of projects) {
27
+ const emoji = project.emoji ?? "📁";
28
+ const path = project.path ?? "";
29
+ this.log(`${emoji} ${project.name}${path ? ` ${path}` : ""}`);
109
30
  }
110
- this.log(`\n${projects.length} project(s)`);
111
- return { session, projects };
31
+ return projects;
112
32
  }
113
33
  }
@@ -2,12 +2,10 @@ import { BaseCommand } from "../../base-command.js";
2
2
  export default class ProjectRemove extends BaseCommand {
3
3
  static description: string;
4
4
  static hidden: boolean;
5
- static examples: string[];
6
5
  static args: {
7
6
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
7
  };
9
8
  static flags: {
10
- force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
9
  format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
10
  quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
11
  "json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,46 +1,29 @@
1
- import { Args, Flags } from "@oclif/core";
2
- import { getManagedSessionName, listManagedWindows } from "../../../lib/managed-session.js";
1
+ import { api, getHttpClient } from "@creativeintelligence/sdk/convex";
2
+ import { Args } from "@oclif/core";
3
3
  import { BaseCommand } from "../../base-command.js";
4
4
  export default class ProjectRemove extends BaseCommand {
5
- static description = "Remove a project from the managed abbie workspace";
5
+ static description = "Remove a project";
6
6
  static hidden = false;
7
- static examples = [
8
- "<%= config.bin %> project remove arbor",
9
- "<%= config.bin %> project remove arbor --force",
10
- ];
11
7
  static args = {
12
8
  name: Args.string({ required: true, description: "Project name" }),
13
9
  };
14
10
  static flags = {
15
11
  ...BaseCommand.baseFlags,
16
- force: Flags.boolean({
17
- char: "f",
18
- description: "Skip confirmation",
19
- default: false,
20
- }),
21
12
  };
22
13
  async execute() {
23
14
  const { args, flags } = await this.parse(ProjectRemove);
24
15
  this.parsedFlags = flags;
25
- const name = args.name;
26
- const session = getManagedSessionName();
27
- // Find the window
28
- const windows = await listManagedWindows();
29
- const target = windows.find((w) => w.name === name);
30
- if (!target) {
31
- this.error(`Project "${name}" not found in the ${session} session`);
16
+ try {
17
+ const client = getHttpClient();
18
+ await client.mutation(api.projects.remove, { name: args.name });
19
+ }
20
+ catch (error) {
21
+ this.error(`Failed to remove project: ${error.message}`);
32
22
  }
33
- // Kill the window
34
- const { killWindow } = await import("../../../lib/tmux/bridge.js");
35
- await killWindow(target.index, session);
36
23
  const isJson = flags.json || flags.format === "json";
37
24
  if (!isJson) {
38
- this.log(`Removed project: ${name}`);
39
- }
40
- const result = { name, status: "removed" };
41
- if (isJson) {
42
- return result;
25
+ this.log(`Removed project: ${args.name}`);
43
26
  }
44
- return result;
27
+ return { name: args.name, status: "removed" };
45
28
  }
46
29
  }
@@ -1,4 +1,3 @@
1
- import { spawnSync } from "node:child_process";
2
1
  import { getHttpClient, api } from "@creativeintelligence/sdk/convex";
3
2
  import { Flags } from "@oclif/core";
4
3
  import { getActiveSessionManager } from "../../../lib/active-sessions.js";
@@ -123,7 +122,7 @@ export default class SessionList extends BaseCommand {
123
122
  catch { }
124
123
  continue;
125
124
  }
126
- // Process/pane liveness check
125
+ // Process liveness check
127
126
  let dead = false;
128
127
  if (session.pid > 0) {
129
128
  try {
@@ -133,16 +132,8 @@ export default class SessionList extends BaseCommand {
133
132
  dead = true;
134
133
  }
135
134
  }
136
- else if (localSession?.pane_id && localSession.spawn_mode === "terminal") {
137
- const result = spawnSync("tmux", ["list-panes", "-t", localSession.pane_id], {
138
- stdio: ["ignore", "ignore", "ignore"],
139
- });
140
- if (result.status !== 0) {
141
- dead = true;
142
- }
143
- }
144
- else if (localSession && localSession.pid <= 0 && !localSession.pane_id) {
145
- // No PID and no pane — cannot verify liveness
135
+ else if (localSession && localSession.pid <= 0) {
136
+ // No PID cannot verify liveness
146
137
  dead = true;
147
138
  }
148
139
  if (dead) {
@@ -1,4 +1,3 @@
1
- import { spawnSync } from "node:child_process";
2
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
2
  import { homedir } from "node:os";
4
3
  import { join } from "node:path";
@@ -101,12 +100,7 @@ export default class SessionMarkDone extends BaseCommand {
101
100
  catch {
102
101
  // Store update is best-effort — local JSON is the source of truth
103
102
  }
104
- // Clean up the tmux pane so zombie windows don't linger
105
- if (session.pane_id) {
106
- spawnSync("tmux", ["kill-pane", "-t", session.pane_id], {
107
- stdio: ["ignore", "ignore", "ignore"],
108
- });
109
- }
103
+ // Legacy tmux cleanup removed sessions are headless now
110
104
  const isJson = this.parsedFlags?.json || this.parsedFlags?.format === "json";
111
105
  if (!isJson) {
112
106
  this.log(`Session ${args.sessionId} marked as ${status} (exit code: ${args.exitCode})`);
@@ -20,7 +20,6 @@ export default class SessionStart extends BaseCommand {
20
20
  "trace-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
21
  await: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
22
  headless: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
- "tmux-session": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
23
  "auto-context": import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
24
  skills: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
25
  format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -8,7 +8,7 @@ import { loadConfigFromPath } from "../../../lib/config-loader.js";
8
8
  import { validateOutputContract } from "../../../lib/contracts.js";
9
9
  import { getDeviceId } from "../../../lib/device.js";
10
10
  import { readSessionResult } from "../../../lib/session-result.js";
11
- import { extractProjectFromPath } from "../../../lib/tmux/map.js";
11
+ import { extractProjectFromPath } from "../../../lib/project-path.js";
12
12
  import { AGENTS } from "../../../lib/types.js";
13
13
  import { BaseCommand } from "../../base-command.js";
14
14
  function hasExplicitSkillRouting(goal, skills) {
@@ -235,11 +235,9 @@ export default class SessionStart extends BaseCommand {
235
235
  default: false,
236
236
  }),
237
237
  headless: Flags.boolean({
238
- description: "Run as headless subprocess (no terminal window). Default is terminal mode.",
239
- default: false,
240
- }),
241
- "tmux-session": Flags.string({
242
- description: 'Tmux session name for terminal mode (default: "abbie"). Prevents hijacking your personal tmux.',
238
+ description: "Run as headless background subprocess (default).",
239
+ default: true,
240
+ allowNo: true,
243
241
  }),
244
242
  "auto-context": Flags.boolean({
245
243
  description: "Inject recent file_activity from prior sessions as context (default: true)",
@@ -342,7 +340,7 @@ export default class SessionStart extends BaseCommand {
342
340
  model: flags.model,
343
341
  awaitCompletion: flags.await, // Use runner foreground mode for --await
344
342
  headless: flags.headless,
345
- tmuxSession: flags["tmux-session"], // Defaults to "abbie" if not specified
343
+ tmuxSession: undefined,
346
344
  autoContext: flags["auto-context"],
347
345
  skills: flags.skills,
348
346
  });
@@ -185,7 +185,6 @@ export declare class ActiveSessionManager {
185
185
  * Kill processes running inside a tmux pane.
186
186
  * Used only as a legacy fallback for pre-runner terminal sessions.
187
187
  */
188
- private killPaneProcesses;
189
188
  /**
190
189
  * Get events for a session.
191
190
  */
@@ -194,11 +193,6 @@ export declare class ActiveSessionManager {
194
193
  * Clean up completed/failed sessions older than N days.
195
194
  */
196
195
  cleanup(daysOld?: number): number;
197
- private shellEscape;
198
- private buildRunnerLaunchScript;
199
- /**
200
- * Build prompt for agent.
201
- */
202
196
  /**
203
197
  * Build workspace awareness context from other active sessions in the same cwd.
204
198
  */
@@ -267,12 +261,6 @@ export declare class ActiveSessionManager {
267
261
  * Check if a process is running.
268
262
  */
269
263
  private isProcessRunning;
270
- /**
271
- * Check if a tmux pane is still alive.
272
- * Uses list-panes which returns exit 1 for nonexistent targets
273
- * (display-message returns 0 even for dead panes).
274
- */
275
- private isTmuxPaneAlive;
276
264
  }
277
265
  export declare function getActiveSessionManager(): ActiveSessionManager;
278
266
  //# sourceMappingURL=active-sessions.d.ts.map