@creativeintelligence/abbie 0.1.6 → 0.1.8
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/bin/dev.js +1 -49
- package/bin/run.js +42 -49
- package/dist/cli/commands/project/add.d.ts +0 -1
- package/dist/cli/commands/project/add.js +16 -52
- package/dist/cli/commands/project/list.js +13 -93
- package/dist/cli/commands/project/remove.d.ts +0 -2
- package/dist/cli/commands/project/remove.js +11 -28
- package/dist/cli/commands/session/list.js +3 -12
- package/dist/cli/commands/session/mark-done.js +1 -7
- package/dist/cli/commands/session/start.d.ts +0 -1
- package/dist/cli/commands/session/start.js +5 -7
- package/dist/lib/active-sessions.d.ts +0 -12
- package/dist/lib/active-sessions.js +6 -175
- package/dist/lib/project-path.d.ts +6 -0
- package/dist/lib/project-path.js +21 -0
- package/dist/lib.d.ts +1 -2
- package/dist/lib.js +2 -4
- package/oclif.manifest.json +2569 -6368
- package/package.json +21 -10
- package/dist/cli/commands/backlog/add.d.ts +0 -22
- package/dist/cli/commands/backlog/add.js +0 -65
- package/dist/cli/commands/backlog/claim.d.ts +0 -19
- package/dist/cli/commands/backlog/claim.js +0 -45
- package/dist/cli/commands/backlog/complete.d.ts +0 -18
- package/dist/cli/commands/backlog/complete.js +0 -42
- package/dist/cli/commands/backlog/list.d.ts +0 -20
- package/dist/cli/commands/backlog/list.js +0 -91
- package/dist/cli/commands/backlog/pick.d.ts +0 -18
- package/dist/cli/commands/backlog/pick.js +0 -42
- package/dist/cli/commands/backlog/sync.d.ts +0 -24
- package/dist/cli/commands/backlog/sync.js +0 -109
- package/dist/cli/commands/daemon.d.ts +0 -56
- package/dist/cli/commands/daemon.js +0 -1465
- package/dist/cli/commands/docs/lint.d.ts +0 -18
- package/dist/cli/commands/docs/lint.js +0 -82
- package/dist/cli/commands/docs/sync.d.ts +0 -19
- package/dist/cli/commands/docs/sync.js +0 -76
- package/dist/cli/commands/gc.d.ts +0 -29
- package/dist/cli/commands/gc.js +0 -211
- package/dist/cli/commands/index.d.ts +0 -36
- package/dist/cli/commands/index.js +0 -228
- package/dist/cli/commands/panes/broker.d.ts +0 -17
- package/dist/cli/commands/panes/broker.js +0 -57
- package/dist/cli/commands/panes/pipe-sink.d.ts +0 -17
- package/dist/cli/commands/panes/pipe-sink.js +0 -90
- package/dist/cli/commands/panes/snapshot.d.ts +0 -20
- package/dist/cli/commands/panes/snapshot.js +0 -125
- package/dist/cli/commands/preview/init.d.ts +0 -25
- package/dist/cli/commands/preview/init.js +0 -159
- package/dist/cli/commands/preview/sync.d.ts +0 -23
- package/dist/cli/commands/preview/sync.js +0 -144
- package/dist/cli/commands/preview/watch.d.ts +0 -24
- package/dist/cli/commands/preview/watch.js +0 -153
- package/dist/cli/commands/resource/acquire.d.ts +0 -21
- package/dist/cli/commands/resource/acquire.js +0 -90
- package/dist/cli/commands/resource/list.d.ts +0 -15
- package/dist/cli/commands/resource/list.js +0 -61
- package/dist/cli/commands/resource/release.d.ts +0 -18
- package/dist/cli/commands/resource/release.js +0 -50
- package/dist/cli/commands/resource/wait.d.ts +0 -21
- package/dist/cli/commands/resource/wait.js +0 -73
- package/dist/cli/commands/session/view.d.ts +0 -24
- package/dist/cli/commands/session/view.js +0 -145
- package/dist/cli/commands/start.d.ts +0 -37
- package/dist/cli/commands/start.js +0 -234
- package/dist/cli/commands/triage/claim.d.ts +0 -23
- package/dist/cli/commands/triage/claim.js +0 -186
- package/dist/cli/commands/triage/list.d.ts +0 -22
- package/dist/cli/commands/triage/list.js +0 -112
- package/dist/cli/commands/triage/next.d.ts +0 -18
- package/dist/cli/commands/triage/next.js +0 -63
- package/dist/cli/commands/triage/pull.d.ts +0 -19
- package/dist/cli/commands/triage/pull.js +0 -82
- package/dist/cli/commands/triage/stats.d.ts +0 -16
- package/dist/cli/commands/triage/stats.js +0 -69
- package/dist/cli/commands/tunnel/list.d.ts +0 -16
- package/dist/cli/commands/tunnel/list.js +0 -98
- package/dist/cli/commands/tunnel/start.d.ts +0 -24
- package/dist/cli/commands/tunnel/start.js +0 -107
- package/dist/cli/commands/tunnel/stop.d.ts +0 -20
- package/dist/cli/commands/tunnel/stop.js +0 -90
- package/dist/cli/commands/tunnel/url.d.ts +0 -21
- package/dist/cli/commands/tunnel/url.js +0 -70
- package/dist/cli/commands/windows/context.d.ts +0 -18
- package/dist/cli/commands/windows/context.js +0 -326
- package/dist/cli/commands/windows/focus.d.ts +0 -17
- package/dist/cli/commands/windows/focus.js +0 -103
- package/dist/cli/commands/windows/list.d.ts +0 -21
- package/dist/cli/commands/windows/list.js +0 -172
- package/dist/cli/commands/windows/map.d.ts +0 -17
- package/dist/cli/commands/windows/map.js +0 -168
- package/dist/cli/commands/windows/read.d.ts +0 -21
- package/dist/cli/commands/windows/read.js +0 -241
- package/dist/cli/commands/windows/search.d.ts +0 -24
- package/dist/cli/commands/windows/search.js +0 -171
- package/dist/cli/commands/windows/show.d.ts +0 -19
- package/dist/cli/commands/windows/show.js +0 -165
- package/dist/cli/commands/windows/watch.d.ts +0 -19
- package/dist/cli/commands/windows/watch.js +0 -241
- package/dist/lib/managed-session.d.ts +0 -27
- package/dist/lib/managed-session.js +0 -105
- package/dist/lib/panes/broker.d.ts +0 -130
- package/dist/lib/panes/broker.js +0 -97
- package/dist/lib/panes/index.d.ts +0 -2
- package/dist/lib/panes/index.js +0 -1
- package/dist/lib/panes/server.d.ts +0 -17
- package/dist/lib/panes/server.js +0 -308
- package/dist/lib/preview/manager.d.ts +0 -77
- package/dist/lib/preview/manager.js +0 -369
- package/dist/lib/preview/schema.d.ts +0 -2
- package/dist/lib/preview/schema.js +0 -32
- package/dist/lib/preview/sprite.d.ts +0 -85
- package/dist/lib/preview/sprite.js +0 -321
- package/dist/lib/preview/watcher.d.ts +0 -63
- package/dist/lib/preview/watcher.js +0 -185
- package/dist/lib/project-identity.d.ts +0 -16
- package/dist/lib/project-identity.js +0 -75
- package/dist/lib/tmux/bridge.d.ts +0 -133
- package/dist/lib/tmux/bridge.js +0 -315
- package/dist/lib/tmux/context.d.ts +0 -82
- package/dist/lib/tmux/context.js +0 -239
- package/dist/lib/tmux/index.d.ts +0 -8
- package/dist/lib/tmux/index.js +0 -11
- package/dist/lib/tmux/map.d.ts +0 -57
- package/dist/lib/tmux/map.js +0 -198
- package/dist/lib/tmux/panes.d.ts +0 -27
- package/dist/lib/tmux/panes.js +0 -151
- package/dist/lib/tmux/redaction.d.ts +0 -57
- package/dist/lib/tmux/redaction.js +0 -152
- package/dist/lib/web/analytics.d.ts +0 -63
- package/dist/lib/web/analytics.js +0 -168
- package/dist/lib/web/server.d.ts +0 -26
- package/dist/lib/web/server.js +0 -697
- package/dist/lib/web/tmux-bridge.d.ts +0 -7
- package/dist/lib/web/tmux-bridge.js +0 -7
- package/dist/lib/windows/index.d.ts +0 -3
- package/dist/lib/windows/index.js +0 -2
- package/dist/lib/windows/inventory.d.ts +0 -21
- package/dist/lib/windows/inventory.js +0 -263
- package/dist/lib/windows/types.d.ts +0 -46
- package/dist/lib/windows/types.js +0 -1
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import { getCurrentBufferInfo } from "../../../lib/nvim/remote.js";
|
|
3
|
-
import { isRunning } from "../../../lib/tmux/index.js";
|
|
4
|
-
import * as windows from "../../../lib/windows/index.js";
|
|
5
|
-
import { BaseCommand } from "../../base-command.js";
|
|
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
|
-
function paneRef(window) {
|
|
17
|
-
return `${window.session}:${window.window}.${window.pane}`;
|
|
18
|
-
}
|
|
19
|
-
function formatPathForDisplay(path, cwd) {
|
|
20
|
-
if (!path)
|
|
21
|
-
return path;
|
|
22
|
-
const normalizedCwd = cwd.endsWith("/") ? cwd : `${cwd}/`;
|
|
23
|
-
if (path.startsWith(normalizedCwd)) {
|
|
24
|
-
return path.slice(normalizedCwd.length);
|
|
25
|
-
}
|
|
26
|
-
return abbreviateHome(path);
|
|
27
|
-
}
|
|
28
|
-
function toBufferInfo(buf) {
|
|
29
|
-
if (!buf)
|
|
30
|
-
return null;
|
|
31
|
-
return {
|
|
32
|
-
name: buf.name,
|
|
33
|
-
path: buf.name,
|
|
34
|
-
modified: buf.modified,
|
|
35
|
-
filetype: buf.filetype,
|
|
36
|
-
lineCount: 0,
|
|
37
|
-
cursor: buf.cursor,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
export default class WindowsShowCommand extends BaseCommand {
|
|
41
|
-
static summary = "Show detailed window state";
|
|
42
|
-
static hidden = false;
|
|
43
|
-
static examples = [
|
|
44
|
-
"$ abbie windows show",
|
|
45
|
-
"$ abbie windows show %3",
|
|
46
|
-
"$ abbie windows show work:0.0",
|
|
47
|
-
"$ abbie windows show --with-buffers",
|
|
48
|
-
"$ abbie windows show --json",
|
|
49
|
-
];
|
|
50
|
-
static args = {
|
|
51
|
-
ref: Args.string({
|
|
52
|
-
description: "Pane ID (e.g. %3) or session:window.pane (e.g. work:0.0)",
|
|
53
|
-
required: false,
|
|
54
|
-
}),
|
|
55
|
-
};
|
|
56
|
-
static flags = {
|
|
57
|
-
...BaseCommand.baseFlags,
|
|
58
|
-
"with-buffers": Flags.boolean({
|
|
59
|
-
description: "Include all buffers (expensive)",
|
|
60
|
-
default: false,
|
|
61
|
-
}),
|
|
62
|
-
};
|
|
63
|
-
async execute() {
|
|
64
|
-
const { args, flags } = await this.parse(WindowsShowCommand);
|
|
65
|
-
this.parsedFlags = flags;
|
|
66
|
-
const tmuxRunning = await isRunning();
|
|
67
|
-
if (!tmuxRunning) {
|
|
68
|
-
const result = { tmuxRunning: false, window: null };
|
|
69
|
-
if (!this.jsonEnabled?.()) {
|
|
70
|
-
this.log("tmux is not running");
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
const state = args.ref
|
|
75
|
-
? await windows.getWindowState(args.ref, {
|
|
76
|
-
includeNvim: true,
|
|
77
|
-
includeAgent: true,
|
|
78
|
-
includeBuffers: flags["with-buffers"],
|
|
79
|
-
})
|
|
80
|
-
: await windows.getFocusedWindow({
|
|
81
|
-
includeNvim: true,
|
|
82
|
-
includeAgent: true,
|
|
83
|
-
includeBuffers: flags["with-buffers"],
|
|
84
|
-
});
|
|
85
|
-
let enriched = state;
|
|
86
|
-
if (enriched?.nvim?.available && enriched.nvim.serverAddr && !enriched.nvim.currentBuffer) {
|
|
87
|
-
const buf = await getCurrentBufferInfo(enriched.nvim.serverAddr);
|
|
88
|
-
const converted = toBufferInfo(buf);
|
|
89
|
-
if (converted) {
|
|
90
|
-
enriched = {
|
|
91
|
-
...enriched,
|
|
92
|
-
nvim: {
|
|
93
|
-
...enriched.nvim,
|
|
94
|
-
currentBuffer: converted,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const result = { tmuxRunning: true, window: enriched };
|
|
100
|
-
if (!this.jsonEnabled?.()) {
|
|
101
|
-
this.printText(result, flags);
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
printText(result, flags) {
|
|
106
|
-
const state = result.window;
|
|
107
|
-
if (!state) {
|
|
108
|
-
this.log("Window not found");
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const id = state.window.paneId || paneRef(state.window);
|
|
112
|
-
const ref = paneRef(state.window);
|
|
113
|
-
this.log(`WINDOW ${id} (${ref})`);
|
|
114
|
-
this.log(`Session: ${state.window.session}`);
|
|
115
|
-
this.log(`Window: ${state.window.window} (${state.window.windowName})`);
|
|
116
|
-
this.log(`Pane: ${state.window.pane} (${state.window.active ? "active" : "inactive"})`);
|
|
117
|
-
this.log(`CWD: ${abbreviateHome(state.window.cwd)}`);
|
|
118
|
-
this.log(`CMD: ${state.window.title}`);
|
|
119
|
-
this.log(`Size: ${state.window.size.cols}x${state.window.size.rows}`);
|
|
120
|
-
this.log("");
|
|
121
|
-
this.log("NVIM");
|
|
122
|
-
if (state.nvim?.available) {
|
|
123
|
-
this.log(`PID: ${state.nvim.pid ?? "-"}`);
|
|
124
|
-
this.log(`Server: ${state.nvim.serverAddr ?? "-"}${state.nvim.discoveryMethod ? ` (${state.nvim.discoveryMethod})` : ""}`);
|
|
125
|
-
const buf = state.nvim.currentBuffer;
|
|
126
|
-
if (buf) {
|
|
127
|
-
const cursor = buf.cursor ? `${buf.cursor.line}:${buf.cursor.col}` : "-";
|
|
128
|
-
const displayPath = formatPathForDisplay(buf.path, state.window.cwd);
|
|
129
|
-
const mod = buf.modified ? " [modified]" : "";
|
|
130
|
-
this.log(`Buffer: ${displayPath} (${buf.filetype || "unknown"})${mod}`);
|
|
131
|
-
this.log(`Cursor: ${cursor}`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
this.log("Buffer: -");
|
|
135
|
-
}
|
|
136
|
-
if (flags["with-buffers"] && state.buffers.length > 0) {
|
|
137
|
-
this.log("");
|
|
138
|
-
this.log("BUFFERS");
|
|
139
|
-
for (const b of state.buffers) {
|
|
140
|
-
const displayPath = formatPathForDisplay(b.path, state.window.cwd);
|
|
141
|
-
const mod = b.modified ? " [modified]" : "";
|
|
142
|
-
this.log(`- ${displayPath} (${b.filetype || "unknown"})${mod}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
this.log("Not available");
|
|
148
|
-
}
|
|
149
|
-
this.log("");
|
|
150
|
-
this.log("AGENT");
|
|
151
|
-
if (state.agent) {
|
|
152
|
-
this.log(`Type: ${state.agent.type}`);
|
|
153
|
-
this.log(`PID: ${state.agent.pid}`);
|
|
154
|
-
if (state.agent.sessionId) {
|
|
155
|
-
this.log(`Session: ${state.agent.sessionId}`);
|
|
156
|
-
}
|
|
157
|
-
if (state.agent.issue) {
|
|
158
|
-
this.log(`Issue: ${state.agent.issue}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
this.log("None");
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { BaseCommand } from "../../base-command.js";
|
|
2
|
-
export default class WindowsWatchCommand extends BaseCommand {
|
|
3
|
-
static summary: string;
|
|
4
|
-
static hidden: boolean;
|
|
5
|
-
static examples: string[];
|
|
6
|
-
static flags: {
|
|
7
|
-
session: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
active: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
-
"with-nvim": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
"with-agent": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
"debounce-ms": import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
-
"json-errors": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
-
ndjson: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
};
|
|
17
|
-
execute(): Promise<unknown>;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { Flags } from "@oclif/core";
|
|
4
|
-
import { isRunning } from "../../../lib/tmux/index.js";
|
|
5
|
-
import * as windows from "../../../lib/windows/index.js";
|
|
6
|
-
import { BaseCommand } from "../../base-command.js";
|
|
7
|
-
function paneRef(window) {
|
|
8
|
-
return `${window.session}:${window.window}.${window.pane}`;
|
|
9
|
-
}
|
|
10
|
-
function stableWindowId(state) {
|
|
11
|
-
return state.window.paneId || paneRef(state.window);
|
|
12
|
-
}
|
|
13
|
-
function sortWindows(states) {
|
|
14
|
-
return [...states].sort((a, b) => {
|
|
15
|
-
if (a.window.session !== b.window.session)
|
|
16
|
-
return a.window.session.localeCompare(b.window.session);
|
|
17
|
-
if (a.window.window !== b.window.window)
|
|
18
|
-
return a.window.window - b.window.window;
|
|
19
|
-
return a.window.pane - b.window.pane;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function hashWindows(states) {
|
|
23
|
-
const stable = states.map((s) => ({
|
|
24
|
-
id: stableWindowId(s),
|
|
25
|
-
session: s.window.session,
|
|
26
|
-
window: s.window.window,
|
|
27
|
-
windowName: s.window.windowName,
|
|
28
|
-
pane: s.window.pane,
|
|
29
|
-
cwd: s.window.cwd,
|
|
30
|
-
title: s.window.title,
|
|
31
|
-
active: s.window.active,
|
|
32
|
-
cols: s.window.size.cols,
|
|
33
|
-
rows: s.window.size.rows,
|
|
34
|
-
hasNvim: s.nvim?.available ?? false,
|
|
35
|
-
nvimPid: s.nvim?.pid ?? null,
|
|
36
|
-
agentType: s.agent?.type ?? null,
|
|
37
|
-
agentPid: s.agent?.pid ?? null,
|
|
38
|
-
}));
|
|
39
|
-
return createHash("sha1").update(JSON.stringify(stable)).digest("hex");
|
|
40
|
-
}
|
|
41
|
-
function isControlModeNotification(line) {
|
|
42
|
-
if (!line.startsWith("%"))
|
|
43
|
-
return false;
|
|
44
|
-
if (line.startsWith("%begin"))
|
|
45
|
-
return false;
|
|
46
|
-
if (line.startsWith("%end"))
|
|
47
|
-
return false;
|
|
48
|
-
if (line.startsWith("%error"))
|
|
49
|
-
return false;
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
export default class WindowsWatchCommand extends BaseCommand {
|
|
53
|
-
static summary = "Watch workspace windows (tmux panes) and stream updates";
|
|
54
|
-
static hidden = false;
|
|
55
|
-
static examples = [
|
|
56
|
-
"$ abbie windows watch --ndjson --quiet",
|
|
57
|
-
"$ abbie windows watch --ndjson --quiet --debounce-ms 100",
|
|
58
|
-
"$ abbie windows watch --ndjson --quiet --no-with-nvim",
|
|
59
|
-
];
|
|
60
|
-
static flags = {
|
|
61
|
-
...BaseCommand.baseFlags,
|
|
62
|
-
session: Flags.string({
|
|
63
|
-
char: "s",
|
|
64
|
-
description: "Filter to specific tmux session",
|
|
65
|
-
}),
|
|
66
|
-
active: Flags.boolean({
|
|
67
|
-
description: "Only show active panes",
|
|
68
|
-
default: false,
|
|
69
|
-
}),
|
|
70
|
-
"with-nvim": Flags.boolean({
|
|
71
|
-
description: "Include nvim state",
|
|
72
|
-
default: true,
|
|
73
|
-
allowNo: true,
|
|
74
|
-
}),
|
|
75
|
-
"with-agent": Flags.boolean({
|
|
76
|
-
description: "Include agent detection",
|
|
77
|
-
default: true,
|
|
78
|
-
allowNo: true,
|
|
79
|
-
}),
|
|
80
|
-
"debounce-ms": Flags.integer({
|
|
81
|
-
description: "Debounce inventory refresh triggered by tmux notifications (ms)",
|
|
82
|
-
default: 75,
|
|
83
|
-
}),
|
|
84
|
-
};
|
|
85
|
-
async execute() {
|
|
86
|
-
const { flags } = await this.parse(WindowsWatchCommand);
|
|
87
|
-
this.parsedFlags = flags;
|
|
88
|
-
if (!this.ndjsonEnabled()) {
|
|
89
|
-
this.error("windows watch is a streaming command; run with --ndjson (and usually --quiet)");
|
|
90
|
-
}
|
|
91
|
-
const tmuxRunning = await isRunning();
|
|
92
|
-
if (!tmuxRunning) {
|
|
93
|
-
const evt = {
|
|
94
|
-
type: "windows.status",
|
|
95
|
-
at: new Date().toISOString(),
|
|
96
|
-
reason: "initial",
|
|
97
|
-
tmuxRunning: false,
|
|
98
|
-
message: "tmux is not running",
|
|
99
|
-
};
|
|
100
|
-
this.outputNdjson([evt]);
|
|
101
|
-
return evt;
|
|
102
|
-
}
|
|
103
|
-
const includeNvim = flags["with-nvim"];
|
|
104
|
-
const includeAgent = flags["with-agent"];
|
|
105
|
-
const onlyActive = flags.active;
|
|
106
|
-
const debounceMs = Math.max(0, flags["debounce-ms"]);
|
|
107
|
-
let lastHash = null;
|
|
108
|
-
let pendingTimer = null;
|
|
109
|
-
let lastReason = "tmux";
|
|
110
|
-
let stopped = false;
|
|
111
|
-
const emitInventory = async (reason) => {
|
|
112
|
-
if (stopped)
|
|
113
|
-
return;
|
|
114
|
-
const states = await windows.inventory({
|
|
115
|
-
session: flags.session,
|
|
116
|
-
includeNvim,
|
|
117
|
-
includeAgent,
|
|
118
|
-
includeBuffers: false,
|
|
119
|
-
});
|
|
120
|
-
const filtered = onlyActive ? states.filter((s) => s.window.active) : states;
|
|
121
|
-
const sorted = sortWindows(filtered);
|
|
122
|
-
const hash = hashWindows(sorted);
|
|
123
|
-
if (reason !== "initial" && lastHash === hash)
|
|
124
|
-
return;
|
|
125
|
-
lastHash = hash;
|
|
126
|
-
const evt = {
|
|
127
|
-
type: "windows.update",
|
|
128
|
-
at: new Date().toISOString(),
|
|
129
|
-
reason,
|
|
130
|
-
tmuxRunning: true,
|
|
131
|
-
windows: sorted,
|
|
132
|
-
hash,
|
|
133
|
-
};
|
|
134
|
-
this.outputNdjson([evt]);
|
|
135
|
-
};
|
|
136
|
-
const scheduleInventory = (reason) => {
|
|
137
|
-
lastReason = reason;
|
|
138
|
-
if (pendingTimer)
|
|
139
|
-
return;
|
|
140
|
-
pendingTimer = setTimeout(async () => {
|
|
141
|
-
pendingTimer = null;
|
|
142
|
-
try {
|
|
143
|
-
await emitInventory(lastReason);
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
// Streaming command: emit a status event rather than crashing the process.
|
|
147
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
148
|
-
const evt = {
|
|
149
|
-
type: "windows.status",
|
|
150
|
-
at: new Date().toISOString(),
|
|
151
|
-
reason: "tmux",
|
|
152
|
-
tmuxRunning: true,
|
|
153
|
-
message,
|
|
154
|
-
};
|
|
155
|
-
this.outputNdjson([evt]);
|
|
156
|
-
}
|
|
157
|
-
}, debounceMs);
|
|
158
|
-
};
|
|
159
|
-
// Initial inventory
|
|
160
|
-
await emitInventory("initial");
|
|
161
|
-
// tmux control-mode client: uses notifications to trigger refresh without polling.
|
|
162
|
-
const tmux = spawn("tmux", ["-C"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
163
|
-
// Kick control-mode to ensure it's alive; output is ignored by our notification filter.
|
|
164
|
-
try {
|
|
165
|
-
tmux.stdin.write("refresh-client -S\n");
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
// ignore
|
|
169
|
-
}
|
|
170
|
-
const stop = (reason) => {
|
|
171
|
-
if (stopped)
|
|
172
|
-
return;
|
|
173
|
-
stopped = true;
|
|
174
|
-
if (pendingTimer) {
|
|
175
|
-
clearTimeout(pendingTimer);
|
|
176
|
-
pendingTimer = null;
|
|
177
|
-
}
|
|
178
|
-
try {
|
|
179
|
-
tmux.kill("SIGTERM");
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
// ignore
|
|
183
|
-
}
|
|
184
|
-
const evt = {
|
|
185
|
-
type: "windows.status",
|
|
186
|
-
at: new Date().toISOString(),
|
|
187
|
-
reason,
|
|
188
|
-
tmuxRunning: false,
|
|
189
|
-
message: "stopped",
|
|
190
|
-
};
|
|
191
|
-
this.outputNdjson([evt]);
|
|
192
|
-
};
|
|
193
|
-
const onSigint = () => stop("tmux-exit");
|
|
194
|
-
const onSigterm = () => stop("tmux-exit");
|
|
195
|
-
process.on("SIGINT", onSigint);
|
|
196
|
-
process.on("SIGTERM", onSigterm);
|
|
197
|
-
return await new Promise((resolve) => {
|
|
198
|
-
let buffer = "";
|
|
199
|
-
tmux.stdout.on("data", (data) => {
|
|
200
|
-
buffer += data.toString();
|
|
201
|
-
for (;;) {
|
|
202
|
-
const idx = buffer.indexOf("\n");
|
|
203
|
-
if (idx === -1)
|
|
204
|
-
break;
|
|
205
|
-
const line = buffer.slice(0, idx).trimEnd();
|
|
206
|
-
buffer = buffer.slice(idx + 1);
|
|
207
|
-
if (!line)
|
|
208
|
-
continue;
|
|
209
|
-
if (isControlModeNotification(line)) {
|
|
210
|
-
scheduleInventory("tmux");
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
tmux.stderr.on("data", (data) => {
|
|
215
|
-
const message = data.toString().trim();
|
|
216
|
-
if (!message)
|
|
217
|
-
return;
|
|
218
|
-
const evt = {
|
|
219
|
-
type: "windows.status",
|
|
220
|
-
at: new Date().toISOString(),
|
|
221
|
-
reason: "tmux",
|
|
222
|
-
tmuxRunning: true,
|
|
223
|
-
message,
|
|
224
|
-
};
|
|
225
|
-
this.outputNdjson([evt]);
|
|
226
|
-
});
|
|
227
|
-
tmux.on("exit", () => {
|
|
228
|
-
stop("tmux-exit");
|
|
229
|
-
process.off("SIGINT", onSigint);
|
|
230
|
-
process.off("SIGTERM", onSigterm);
|
|
231
|
-
resolve({
|
|
232
|
-
type: "windows.status",
|
|
233
|
-
at: new Date().toISOString(),
|
|
234
|
-
reason: "tmux-exit",
|
|
235
|
-
tmuxRunning: false,
|
|
236
|
-
message: "tmux exited",
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Managed abbie tmux session utilities.
|
|
3
|
-
*
|
|
4
|
-
* Abbie owns a dedicated tmux session where it manages project windows.
|
|
5
|
-
* This module provides the shared primitives for creating, querying,
|
|
6
|
-
* and managing that session.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Get the managed session name (from env or default).
|
|
10
|
-
*/
|
|
11
|
-
export declare function getManagedSessionName(): string;
|
|
12
|
-
/**
|
|
13
|
-
* Ensure the managed abbie tmux session exists.
|
|
14
|
-
* Creates it (detached) if it doesn't exist yet.
|
|
15
|
-
* Returns the session name.
|
|
16
|
-
*/
|
|
17
|
-
export declare function ensureManagedSession(): Promise<string>;
|
|
18
|
-
/**
|
|
19
|
-
* List windows in the managed session.
|
|
20
|
-
* Returns empty array if session doesn't exist.
|
|
21
|
-
*/
|
|
22
|
-
export declare function listManagedWindows(): Promise<import("./tmux/bridge.js").TmuxWindow[]>;
|
|
23
|
-
/**
|
|
24
|
-
* Check if a project window already exists in the managed session.
|
|
25
|
-
*/
|
|
26
|
-
export declare function hasProjectWindow(name: string): Promise<boolean>;
|
|
27
|
-
//# sourceMappingURL=managed-session.d.ts.map
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Managed abbie tmux session utilities.
|
|
3
|
-
*
|
|
4
|
-
* Abbie owns a dedicated tmux session where it manages project windows.
|
|
5
|
-
* This module provides the shared primitives for creating, querying,
|
|
6
|
-
* and managing that session.
|
|
7
|
-
*/
|
|
8
|
-
import { spawn as spawnCmd } from "node:child_process";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
const DEFAULT_SESSION_NAME = "abbie";
|
|
11
|
-
const INTERNAL_WINDOW_NAME = "_abbie";
|
|
12
|
-
const LEGACY_SHELL_WINDOWS = new Set(["zsh", "bash", "fish", "sh"]);
|
|
13
|
-
/**
|
|
14
|
-
* Get the managed session name (from env or default).
|
|
15
|
-
*/
|
|
16
|
-
export function getManagedSessionName() {
|
|
17
|
-
return process.env.ABBIE_TMUX_SESSION ?? DEFAULT_SESSION_NAME;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Ensure the managed abbie tmux session exists.
|
|
21
|
-
* Creates it (detached) if it doesn't exist yet.
|
|
22
|
-
* Returns the session name.
|
|
23
|
-
*/
|
|
24
|
-
export async function ensureManagedSession() {
|
|
25
|
-
const { isRunning, listSessions, listWindows } = await import("./tmux/bridge.js");
|
|
26
|
-
const session = getManagedSessionName();
|
|
27
|
-
const running = await isRunning();
|
|
28
|
-
if (!running) {
|
|
29
|
-
// No tmux server at all — create session (this also starts the server)
|
|
30
|
-
await createDetachedSession(session);
|
|
31
|
-
return session;
|
|
32
|
-
}
|
|
33
|
-
const sessions = await listSessions();
|
|
34
|
-
const exists = sessions.some((s) => s.name === session);
|
|
35
|
-
if (!exists) {
|
|
36
|
-
await createDetachedSession(session);
|
|
37
|
-
return session;
|
|
38
|
-
}
|
|
39
|
-
// Normalize legacy bootstrap windows created by older versions (e.g. `zsh` window at index 0)
|
|
40
|
-
// so users don’t see a confusing "zsh" project when attaching to the managed session.
|
|
41
|
-
try {
|
|
42
|
-
const windows = await listWindows(session);
|
|
43
|
-
const hasInternal = windows.some((w) => w.name === INTERNAL_WINDOW_NAME);
|
|
44
|
-
const legacyBootstrap = windows.find((w) => w.index === 0 && LEGACY_SHELL_WINDOWS.has(w.name));
|
|
45
|
-
if (!hasInternal && legacyBootstrap) {
|
|
46
|
-
await renameWindow(session, legacyBootstrap.index, INTERNAL_WINDOW_NAME);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
// Best-effort only
|
|
51
|
-
}
|
|
52
|
-
return session;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* List windows in the managed session.
|
|
56
|
-
* Returns empty array if session doesn't exist.
|
|
57
|
-
*/
|
|
58
|
-
export async function listManagedWindows() {
|
|
59
|
-
const { isRunning, listSessions, listWindows } = await import("./tmux/bridge.js");
|
|
60
|
-
const session = getManagedSessionName();
|
|
61
|
-
if (!(await isRunning()))
|
|
62
|
-
return [];
|
|
63
|
-
const sessions = await listSessions();
|
|
64
|
-
if (!sessions.some((s) => s.name === session))
|
|
65
|
-
return [];
|
|
66
|
-
return listWindows(session);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Check if a project window already exists in the managed session.
|
|
70
|
-
*/
|
|
71
|
-
export async function hasProjectWindow(name) {
|
|
72
|
-
const windows = await listManagedWindows();
|
|
73
|
-
return windows.some((w) => w.name === name);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Create a detached tmux session.
|
|
77
|
-
*/
|
|
78
|
-
async function createDetachedSession(name) {
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
80
|
-
const proc = spawnCmd("tmux", ["new-session", "-d", "-s", name, "-n", INTERNAL_WINDOW_NAME, "-c", homedir()], {
|
|
81
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
82
|
-
});
|
|
83
|
-
proc.on("close", (code) => {
|
|
84
|
-
if (code === 0)
|
|
85
|
-
resolve();
|
|
86
|
-
else
|
|
87
|
-
reject(new Error(`Failed to create tmux session: ${name}`));
|
|
88
|
-
});
|
|
89
|
-
proc.on("error", reject);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
async function renameWindow(session, index, name) {
|
|
93
|
-
await new Promise((resolve, reject) => {
|
|
94
|
-
const proc = spawnCmd("tmux", ["rename-window", "-t", `${session}:${index}`, name], {
|
|
95
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
96
|
-
});
|
|
97
|
-
proc.on("close", (code) => {
|
|
98
|
-
if (code === 0)
|
|
99
|
-
resolve();
|
|
100
|
-
else
|
|
101
|
-
reject(new Error(`Failed to rename window ${session}:${index} → ${name}`));
|
|
102
|
-
});
|
|
103
|
-
proc.on("error", reject);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { type Socket } from "node:net";
|
|
2
|
-
/**
|
|
3
|
-
* Panes broker protocol (local fanout).
|
|
4
|
-
*
|
|
5
|
-
* Transport: Unix domain socket (newline-delimited JSON).
|
|
6
|
-
* - Client → server: control messages (subscribe/unsubscribe, ping)
|
|
7
|
-
* - Server → client: pane snapshots + deltas + status
|
|
8
|
-
*
|
|
9
|
-
* Goal: enable multiple clients (TUI + tools) to subscribe without each
|
|
10
|
-
* attaching to tmux / mutating pane state.
|
|
11
|
-
*
|
|
12
|
-
* This file defines the protocol + a minimal client helper. The daemon/broker
|
|
13
|
-
* implementation lives elsewhere and can be integrated later.
|
|
14
|
-
*/
|
|
15
|
-
export type PanesBrokerRedactionMode = "raw" | "default";
|
|
16
|
-
export type PanesBrokerContentEncoding = "text" | "ansi";
|
|
17
|
-
export type PanesBrokerPaneTarget = {
|
|
18
|
-
paneId: string;
|
|
19
|
-
ref?: string;
|
|
20
|
-
} | {
|
|
21
|
-
ref: string;
|
|
22
|
-
paneId?: string;
|
|
23
|
-
};
|
|
24
|
-
export type PanesBrokerSubscribeOptions = {
|
|
25
|
-
requestId?: string;
|
|
26
|
-
target: PanesBrokerPaneTarget;
|
|
27
|
-
redaction?: PanesBrokerRedactionMode;
|
|
28
|
-
encoding?: PanesBrokerContentEncoding;
|
|
29
|
-
history?: {
|
|
30
|
-
lines: number;
|
|
31
|
-
};
|
|
32
|
-
follow?: boolean;
|
|
33
|
-
};
|
|
34
|
-
export type PanesBrokerClientHello = {
|
|
35
|
-
type: "client.hello";
|
|
36
|
-
at: string;
|
|
37
|
-
clientId: string;
|
|
38
|
-
version: 1;
|
|
39
|
-
pid: number;
|
|
40
|
-
};
|
|
41
|
-
export type PanesBrokerSubscribe = {
|
|
42
|
-
type: "panes.subscribe";
|
|
43
|
-
at: string;
|
|
44
|
-
requestId: string;
|
|
45
|
-
target: PanesBrokerPaneTarget;
|
|
46
|
-
redaction: PanesBrokerRedactionMode;
|
|
47
|
-
encoding: PanesBrokerContentEncoding;
|
|
48
|
-
history?: {
|
|
49
|
-
lines: number;
|
|
50
|
-
};
|
|
51
|
-
follow: boolean;
|
|
52
|
-
};
|
|
53
|
-
export type PanesBrokerUnsubscribe = {
|
|
54
|
-
type: "panes.unsubscribe";
|
|
55
|
-
at: string;
|
|
56
|
-
requestId: string;
|
|
57
|
-
};
|
|
58
|
-
export type PanesBrokerPing = {
|
|
59
|
-
type: "client.ping";
|
|
60
|
-
at: string;
|
|
61
|
-
nonce: string;
|
|
62
|
-
};
|
|
63
|
-
export type PanesBrokerClientMessage = PanesBrokerClientHello | PanesBrokerSubscribe | PanesBrokerUnsubscribe | PanesBrokerPing;
|
|
64
|
-
export type PanesBrokerServerHello = {
|
|
65
|
-
type: "broker.hello";
|
|
66
|
-
at: string;
|
|
67
|
-
version: 1;
|
|
68
|
-
pid: number;
|
|
69
|
-
};
|
|
70
|
-
export type PanesBrokerPong = {
|
|
71
|
-
type: "broker.pong";
|
|
72
|
-
at: string;
|
|
73
|
-
nonce: string;
|
|
74
|
-
};
|
|
75
|
-
export type PanesBrokerPanesSnapshot = {
|
|
76
|
-
type: "panes.snapshot";
|
|
77
|
-
at: string;
|
|
78
|
-
requestId: string;
|
|
79
|
-
paneId: string;
|
|
80
|
-
ref?: string;
|
|
81
|
-
encoding: PanesBrokerContentEncoding;
|
|
82
|
-
redaction: PanesBrokerRedactionMode;
|
|
83
|
-
cols?: number;
|
|
84
|
-
rows?: number;
|
|
85
|
-
truncated?: boolean;
|
|
86
|
-
content: string;
|
|
87
|
-
};
|
|
88
|
-
export type PanesBrokerPanesDelta = {
|
|
89
|
-
type: "panes.delta";
|
|
90
|
-
at: string;
|
|
91
|
-
requestId: string;
|
|
92
|
-
paneId: string;
|
|
93
|
-
ref?: string;
|
|
94
|
-
encoding: PanesBrokerContentEncoding;
|
|
95
|
-
redaction: PanesBrokerRedactionMode;
|
|
96
|
-
chunk: string;
|
|
97
|
-
};
|
|
98
|
-
export type PanesBrokerPanesStatus = {
|
|
99
|
-
type: "panes.status";
|
|
100
|
-
at: string;
|
|
101
|
-
requestId?: string;
|
|
102
|
-
paneId?: string;
|
|
103
|
-
level: "info" | "warn" | "error";
|
|
104
|
-
message: string;
|
|
105
|
-
};
|
|
106
|
-
export type PanesBrokerServerMessage = PanesBrokerServerHello | PanesBrokerPong | PanesBrokerPanesSnapshot | PanesBrokerPanesDelta | PanesBrokerPanesStatus;
|
|
107
|
-
export type PanesBrokerConnectOptions = {
|
|
108
|
-
socketPath?: string;
|
|
109
|
-
};
|
|
110
|
-
export declare function getDefaultPanesBrokerSocketPath(): string;
|
|
111
|
-
export type LineParser<T> = {
|
|
112
|
-
pushChunk: (chunk: Buffer | string) => void;
|
|
113
|
-
onMessage: (fn: (msg: T) => void) => void;
|
|
114
|
-
onError: (fn: (err: Error) => void) => void;
|
|
115
|
-
};
|
|
116
|
-
export declare function createNdjsonLineParser<T>(): LineParser<T>;
|
|
117
|
-
export type PanesBrokerClient = {
|
|
118
|
-
socket: Socket;
|
|
119
|
-
clientId: string;
|
|
120
|
-
send: (msg: PanesBrokerClientMessage) => void;
|
|
121
|
-
subscribe: (opts: PanesBrokerSubscribeOptions) => string;
|
|
122
|
-
unsubscribe: (requestId: string) => void;
|
|
123
|
-
ping: () => string;
|
|
124
|
-
};
|
|
125
|
-
export declare function connectPanesBroker(opts?: PanesBrokerConnectOptions, handlers?: {
|
|
126
|
-
onMessage?: (msg: PanesBrokerServerMessage) => void;
|
|
127
|
-
onError?: (err: Error) => void;
|
|
128
|
-
onClose?: () => void;
|
|
129
|
-
}): PanesBrokerClient;
|
|
130
|
-
//# sourceMappingURL=broker.d.ts.map
|