@elhu/pit 0.1.1
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/LICENSE +21 -0
- package/README.md +380 -0
- package/dist/adapters/claude-code.d.ts +70 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +166 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/index.d.ts +16 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +49 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +53 -0
- package/dist/adapters/opencode.d.ts.map +1 -0
- package/dist/adapters/opencode.js +120 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/adapters/process-utils.d.ts +29 -0
- package/dist/adapters/process-utils.d.ts.map +1 -0
- package/dist/adapters/process-utils.js +96 -0
- package/dist/adapters/process-utils.js.map +1 -0
- package/dist/adapters/types.d.ts +41 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +6 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/assets.generated.d.ts +13 -0
- package/dist/assets.generated.d.ts.map +1 -0
- package/dist/assets.generated.js +162 -0
- package/dist/assets.generated.js.map +1 -0
- package/dist/beads.d.ts +85 -0
- package/dist/beads.d.ts.map +1 -0
- package/dist/beads.js +120 -0
- package/dist/beads.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +39 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +10 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +58 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/cleanup.d.ts +13 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +174 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/daemon.d.ts +3 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +162 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install-keybinding.d.ts +61 -0
- package/dist/commands/install-keybinding.d.ts.map +1 -0
- package/dist/commands/install-keybinding.js +138 -0
- package/dist/commands/install-keybinding.js.map +1 -0
- package/dist/commands/install-status.d.ts +35 -0
- package/dist/commands/install-status.d.ts.map +1 -0
- package/dist/commands/install-status.js +115 -0
- package/dist/commands/install-status.js.map +1 -0
- package/dist/commands/log.d.ts +7 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +60 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/pause.d.ts +12 -0
- package/dist/commands/pause.d.ts.map +1 -0
- package/dist/commands/pause.js +47 -0
- package/dist/commands/pause.js.map +1 -0
- package/dist/commands/resume.d.ts +12 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +59 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/shared.d.ts +7 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +56 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/start.d.ts +12 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +274 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +24 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +101 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +52 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/teardown.d.ts +15 -0
- package/dist/commands/teardown.d.ts.map +1 -0
- package/dist/commands/teardown.js +72 -0
- package/dist/commands/teardown.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +129 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/client.d.ts +38 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +254 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/context.d.ts +63 -0
- package/dist/daemon/context.d.ts.map +1 -0
- package/dist/daemon/context.js +14 -0
- package/dist/daemon/context.js.map +1 -0
- package/dist/daemon/handlers.d.ts +79 -0
- package/dist/daemon/handlers.d.ts.map +1 -0
- package/dist/daemon/handlers.js +1260 -0
- package/dist/daemon/handlers.js.map +1 -0
- package/dist/daemon/index.d.ts +6 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +7 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/lifecycle.d.ts +56 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +341 -0
- package/dist/daemon/lifecycle.js.map +1 -0
- package/dist/daemon/protocol.d.ts +174 -0
- package/dist/daemon/protocol.d.ts.map +1 -0
- package/dist/daemon/protocol.js +3 -0
- package/dist/daemon/protocol.js.map +1 -0
- package/dist/daemon/recovery.d.ts +37 -0
- package/dist/daemon/recovery.d.ts.map +1 -0
- package/dist/daemon/recovery.js +197 -0
- package/dist/daemon/recovery.js.map +1 -0
- package/dist/daemon/server.d.ts +31 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +294 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/socket.d.ts +18 -0
- package/dist/daemon/socket.d.ts.map +1 -0
- package/dist/daemon/socket.js +36 -0
- package/dist/daemon/socket.js.map +1 -0
- package/dist/daemon/state.d.ts +60 -0
- package/dist/daemon/state.d.ts.map +1 -0
- package/dist/daemon/state.js +156 -0
- package/dist/daemon/state.js.map +1 -0
- package/dist/daemon/systemd.d.ts +19 -0
- package/dist/daemon/systemd.d.ts.map +1 -0
- package/dist/daemon/systemd.js +131 -0
- package/dist/daemon/systemd.js.map +1 -0
- package/dist/hooks/claude-code-hook.d.ts +32 -0
- package/dist/hooks/claude-code-hook.d.ts.map +1 -0
- package/dist/hooks/claude-code-hook.js +112 -0
- package/dist/hooks/claude-code-hook.js.map +1 -0
- package/dist/instructions-template.d.ts +9 -0
- package/dist/instructions-template.d.ts.map +1 -0
- package/dist/instructions-template.js +123 -0
- package/dist/instructions-template.js.map +1 -0
- package/dist/logger.d.ts +25 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +44 -0
- package/dist/logger.js.map +1 -0
- package/dist/loop.d.ts +88 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +161 -0
- package/dist/loop.js.map +1 -0
- package/dist/orchestrator-instructions-template.d.ts +13 -0
- package/dist/orchestrator-instructions-template.d.ts.map +1 -0
- package/dist/orchestrator-instructions-template.js +147 -0
- package/dist/orchestrator-instructions-template.js.map +1 -0
- package/dist/output.d.ts +12 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +25 -0
- package/dist/output.js.map +1 -0
- package/dist/plugin/pit.js +57 -0
- package/dist/session.d.ts +55 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +135 -0
- package/dist/session.js.map +1 -0
- package/dist/setup.d.ts +92 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +382 -0
- package/dist/setup.js.map +1 -0
- package/dist/shell-quote.d.ts +16 -0
- package/dist/shell-quote.d.ts.map +1 -0
- package/dist/shell-quote.js +18 -0
- package/dist/shell-quote.js.map +1 -0
- package/dist/signals.d.ts +17 -0
- package/dist/signals.d.ts.map +1 -0
- package/dist/signals.js +26 -0
- package/dist/signals.js.map +1 -0
- package/dist/state-machine.d.ts +74 -0
- package/dist/state-machine.d.ts.map +1 -0
- package/dist/state-machine.js +153 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/tmux.d.ts +101 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +208 -0
- package/dist/tmux.js.map +1 -0
- package/dist/worktree.d.ts +33 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +116 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { loadPitConfig } from "../config.js";
|
|
3
|
+
import { recoverFromPersistedState } from "../daemon/recovery.js";
|
|
4
|
+
import { handleDaemonStart, handleDaemonRun, handleDaemonStop, handleDaemonStatus, handleDaemonRestart, } from "../daemon/lifecycle.js";
|
|
5
|
+
import { PitDaemonServer } from "../daemon/server.js";
|
|
6
|
+
import { createLogger } from "../logger.js";
|
|
7
|
+
import { registerAllHandlers, startHealthCheck, makeClearAndReprompt } from "../daemon/handlers.js";
|
|
8
|
+
import { saveDaemonState } from "../daemon/state.js";
|
|
9
|
+
import { daemonInstallCommand, daemonUninstallCommand } from "../daemon/systemd.js";
|
|
10
|
+
const logger = createLogger();
|
|
11
|
+
async function runDaemon(projectRoot, sessionId, tmuxSession) {
|
|
12
|
+
const server = new PitDaemonServer({ sessionId, projectRoot, tmuxSession });
|
|
13
|
+
const fileConfig = loadPitConfig(projectRoot);
|
|
14
|
+
const pidPath = `/tmp/pit/${sessionId}/daemon.pid`;
|
|
15
|
+
await fs.promises.writeFile(pidPath, process.pid.toString());
|
|
16
|
+
const ctx = {
|
|
17
|
+
daemonState: null,
|
|
18
|
+
debouncedSaver: null,
|
|
19
|
+
loopHandles: new Map(),
|
|
20
|
+
agentInstance: null,
|
|
21
|
+
daemonStartTime: Date.now(),
|
|
22
|
+
projectRoot,
|
|
23
|
+
sessionId,
|
|
24
|
+
tmuxSession,
|
|
25
|
+
fileConfig,
|
|
26
|
+
healthCheckInterval: null,
|
|
27
|
+
};
|
|
28
|
+
registerAllHandlers(server, ctx);
|
|
29
|
+
let isShuttingDown = false;
|
|
30
|
+
const shutdown = async () => {
|
|
31
|
+
if (isShuttingDown) {
|
|
32
|
+
// Force exit on second signal
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
isShuttingDown = true;
|
|
36
|
+
// 1. Stop health check interval
|
|
37
|
+
if (ctx.healthCheckInterval) {
|
|
38
|
+
clearInterval(ctx.healthCheckInterval);
|
|
39
|
+
ctx.healthCheckInterval = null;
|
|
40
|
+
}
|
|
41
|
+
// 2. Flush daemon state to disk synchronously (bypass debounce) so recovery works
|
|
42
|
+
if (ctx.daemonState) {
|
|
43
|
+
try {
|
|
44
|
+
await saveDaemonState(ctx.daemonState);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Best-effort — don't prevent shutdown
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// 3. Close the server socket
|
|
51
|
+
await server.shutdown("shutdown");
|
|
52
|
+
// 4. Remove PID file (signals the daemon is dead for liveness checks)
|
|
53
|
+
await fs.promises.unlink(pidPath).catch((_e) => undefined);
|
|
54
|
+
// NOTE: We intentionally do NOT rm -rf the session directory here.
|
|
55
|
+
// daemon-state.json must survive for recoverFromPersistedState() to work.
|
|
56
|
+
// Only 'pit teardown' should remove the session directory.
|
|
57
|
+
process.exit(0);
|
|
58
|
+
};
|
|
59
|
+
// Single shutdown path for both RPC and signal-triggered shutdowns
|
|
60
|
+
server.once("shutdown-requested", () => {
|
|
61
|
+
shutdown().catch(() => process.exit(1));
|
|
62
|
+
});
|
|
63
|
+
process.once("SIGTERM", () => {
|
|
64
|
+
shutdown().catch(() => process.exit(1));
|
|
65
|
+
});
|
|
66
|
+
process.once("SIGINT", () => {
|
|
67
|
+
shutdown().catch(() => process.exit(1));
|
|
68
|
+
});
|
|
69
|
+
const clearAndReprompt = makeClearAndReprompt(ctx);
|
|
70
|
+
await recoverFromPersistedState(ctx, clearAndReprompt);
|
|
71
|
+
try {
|
|
72
|
+
await server.start();
|
|
73
|
+
logger.info("Daemon started", { socketPath: server.getSocketPath() });
|
|
74
|
+
ctx.healthCheckInterval = startHealthCheck(ctx);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
logger.error("Failed to start daemon", { error: String(error) });
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function registerDaemon(program) {
|
|
82
|
+
const daemonCommand = program.command("daemon").description("Manage the pit daemon service");
|
|
83
|
+
daemonCommand
|
|
84
|
+
.command("start")
|
|
85
|
+
.description("Start the pit daemon")
|
|
86
|
+
.option("--project-root <path>", "Project root directory")
|
|
87
|
+
.action(async (opts, cmd) => {
|
|
88
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
89
|
+
const args = opts.projectRoot ? ["--project-root", opts.projectRoot] : [];
|
|
90
|
+
await handleDaemonStart(args, pretty);
|
|
91
|
+
});
|
|
92
|
+
daemonCommand
|
|
93
|
+
.command("__run")
|
|
94
|
+
.description("Internal command to run the daemon process")
|
|
95
|
+
.action(async () => {
|
|
96
|
+
await handleDaemonRun(runDaemon);
|
|
97
|
+
});
|
|
98
|
+
daemonCommand
|
|
99
|
+
.command("stop")
|
|
100
|
+
.description("Stop the pit daemon")
|
|
101
|
+
.option("--pause-epics", "Pause all running epics before stopping daemon")
|
|
102
|
+
.option("--project-root <path>", "Project root directory")
|
|
103
|
+
.action(async (opts, cmd) => {
|
|
104
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
105
|
+
await handleDaemonStop(pretty, opts.pauseEpics || false, opts.projectRoot);
|
|
106
|
+
});
|
|
107
|
+
daemonCommand
|
|
108
|
+
.command("status")
|
|
109
|
+
.description("Check pit daemon status")
|
|
110
|
+
.action(async (opts, cmd) => {
|
|
111
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
112
|
+
await handleDaemonStatus(pretty);
|
|
113
|
+
});
|
|
114
|
+
daemonCommand
|
|
115
|
+
.command("restart")
|
|
116
|
+
.description("Restart the pit daemon")
|
|
117
|
+
.action(async (opts, cmd) => {
|
|
118
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
119
|
+
await handleDaemonRestart([], pretty);
|
|
120
|
+
});
|
|
121
|
+
daemonCommand
|
|
122
|
+
.command("install")
|
|
123
|
+
.description("Install and enable systemd user service for auto-start")
|
|
124
|
+
.option("--project-root <path>", "Project root directory")
|
|
125
|
+
.action(async (opts, cmd) => {
|
|
126
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
127
|
+
try {
|
|
128
|
+
await daemonInstallCommand({ projectRoot: opts.projectRoot });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
132
|
+
if (pretty) {
|
|
133
|
+
console.error(`Error: ${errorMessage}`);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log(JSON.stringify({ error: errorMessage }));
|
|
137
|
+
}
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
daemonCommand
|
|
142
|
+
.command("uninstall")
|
|
143
|
+
.description("Disable and stop systemd user service")
|
|
144
|
+
.option("--project-root <path>", "Project root directory")
|
|
145
|
+
.action(async (opts, cmd) => {
|
|
146
|
+
const pretty = cmd.parent?.parent?.opts().pretty;
|
|
147
|
+
try {
|
|
148
|
+
await daemonUninstallCommand({ projectRoot: opts.projectRoot });
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
152
|
+
if (pretty) {
|
|
153
|
+
console.error(`Error: ${errorMessage}`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.log(JSON.stringify({ error: errorMessage }));
|
|
157
|
+
}
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
export interface InitOutput {
|
|
3
|
+
file: string;
|
|
4
|
+
action: "created" | "updated" | "added";
|
|
5
|
+
}
|
|
6
|
+
export declare class InitError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Inject orchestrator instructions into the target project's AGENTS.md.
|
|
11
|
+
*
|
|
12
|
+
* - If AGENTS.md does not exist, create it with the orchestrator section.
|
|
13
|
+
* - If it exists but has no pit section, append the section.
|
|
14
|
+
* - If it already has a pit section (detected by sentinel comments), replace it.
|
|
15
|
+
*
|
|
16
|
+
* Returns the action taken: "created", "added", or "updated".
|
|
17
|
+
*/
|
|
18
|
+
export declare function initProject(projectRoot: string): Promise<InitOutput>;
|
|
19
|
+
export declare function registerInit(program: Command): void;
|
|
20
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CACzC;AAMD,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAwD1E;AAMD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0CnD"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { printError, printOutput } from "../output.js";
|
|
6
|
+
import { ORCHESTRATOR_INSTRUCTIONS, ORCHESTRATOR_SECTION_START, ORCHESTRATOR_SECTION_END, } from "../orchestrator-instructions-template.js";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Core init logic (exported for testability)
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
export class InitError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "InitError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Inject orchestrator instructions into the target project's AGENTS.md.
|
|
19
|
+
*
|
|
20
|
+
* - If AGENTS.md does not exist, create it with the orchestrator section.
|
|
21
|
+
* - If it exists but has no pit section, append the section.
|
|
22
|
+
* - If it already has a pit section (detected by sentinel comments), replace it.
|
|
23
|
+
*
|
|
24
|
+
* Returns the action taken: "created", "added", or "updated".
|
|
25
|
+
*/
|
|
26
|
+
export async function initProject(projectRoot) {
|
|
27
|
+
const agentsPath = join(projectRoot, "AGENTS.md");
|
|
28
|
+
let existing;
|
|
29
|
+
try {
|
|
30
|
+
existing = await readFile(agentsPath, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err.code === "ENOENT") {
|
|
34
|
+
existing = null;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
let content;
|
|
41
|
+
let action;
|
|
42
|
+
if (existing === null) {
|
|
43
|
+
// No AGENTS.md — create one with just the pit section
|
|
44
|
+
content = ORCHESTRATOR_INSTRUCTIONS;
|
|
45
|
+
action = "created";
|
|
46
|
+
}
|
|
47
|
+
else if (existing.includes(ORCHESTRATOR_SECTION_START)) {
|
|
48
|
+
// Pit section exists — replace it
|
|
49
|
+
const startIdx = existing.indexOf(ORCHESTRATOR_SECTION_START);
|
|
50
|
+
const endIdx = existing.indexOf(ORCHESTRATOR_SECTION_END);
|
|
51
|
+
if (endIdx === -1) {
|
|
52
|
+
throw new InitError(`Found ${ORCHESTRATOR_SECTION_START} in AGENTS.md but no matching ${ORCHESTRATOR_SECTION_END} — file may be corrupted`);
|
|
53
|
+
}
|
|
54
|
+
const before = existing.slice(0, startIdx);
|
|
55
|
+
const after = existing.slice(endIdx + ORCHESTRATOR_SECTION_END.length);
|
|
56
|
+
// Trim trailing newlines from 'before' to avoid blank line accumulation,
|
|
57
|
+
// then ensure exactly one blank line before the section
|
|
58
|
+
const trimmedBefore = before.replace(/\n+$/, "");
|
|
59
|
+
const trimmedAfter = after.replace(/^\n+/, "");
|
|
60
|
+
const parts = [trimmedBefore, ORCHESTRATOR_INSTRUCTIONS];
|
|
61
|
+
if (trimmedAfter.length > 0) {
|
|
62
|
+
parts.push(trimmedAfter);
|
|
63
|
+
}
|
|
64
|
+
content = parts.filter((p) => p.length > 0).join("\n\n");
|
|
65
|
+
// Ensure file ends with a single newline
|
|
66
|
+
content = content.replace(/\n*$/, "\n");
|
|
67
|
+
action = "updated";
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// AGENTS.md exists but no pit section — append
|
|
71
|
+
const trimmed = existing.replace(/\n+$/, "");
|
|
72
|
+
content = trimmed + "\n\n" + ORCHESTRATOR_INSTRUCTIONS;
|
|
73
|
+
// Ensure file ends with a single newline
|
|
74
|
+
content = content.replace(/\n*$/, "\n");
|
|
75
|
+
action = "added";
|
|
76
|
+
}
|
|
77
|
+
await writeFile(agentsPath, content, "utf-8");
|
|
78
|
+
return { file: "AGENTS.md", action };
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Command registration
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
export function registerInit(program) {
|
|
84
|
+
program
|
|
85
|
+
.command("init")
|
|
86
|
+
.description("Inject pit orchestrator instructions into AGENTS.md")
|
|
87
|
+
.action(async (_opts, cmd) => {
|
|
88
|
+
const pretty = (cmd.parent?.opts()).pretty ?? false;
|
|
89
|
+
// Resolve project root
|
|
90
|
+
let projectRoot;
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"]);
|
|
93
|
+
projectRoot = stdout.trim();
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
printError({ error: `Failed to resolve project root: ${err.message}` }, pretty);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let result;
|
|
101
|
+
try {
|
|
102
|
+
result = await initProject(projectRoot);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (err instanceof InitError) {
|
|
106
|
+
printError({ error: err.message }, pretty);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
if (pretty) {
|
|
113
|
+
const verb = result.action === "created"
|
|
114
|
+
? "Created AGENTS.md with"
|
|
115
|
+
: result.action === "updated"
|
|
116
|
+
? "Updated existing"
|
|
117
|
+
: "Added";
|
|
118
|
+
process.stdout.write(`${verb} pit orchestrator instructions in ${result.file}\n`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
printOutput(result, false);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
export interface InstalledBinding {
|
|
3
|
+
key: string;
|
|
4
|
+
binding: string;
|
|
5
|
+
}
|
|
6
|
+
export interface InstallKeybindingOutput {
|
|
7
|
+
status: "installed";
|
|
8
|
+
bindings: InstalledBinding[];
|
|
9
|
+
note: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the tmux bind-key command string for `pit resume`.
|
|
13
|
+
*
|
|
14
|
+
* tmux windows are named 'epic-<name>', so we strip the 'epic-' prefix
|
|
15
|
+
* to derive the epic ID for `pit resume`.
|
|
16
|
+
*
|
|
17
|
+
* We use a shell command inside run-shell that invokes sed to strip the
|
|
18
|
+
* prefix for portability with tmux < 3.1.
|
|
19
|
+
*
|
|
20
|
+
* NOTE: tmux >= 3.1 supports a cleaner approach using format substitution:
|
|
21
|
+
* bind-key R run-shell 'pit resume #{s/epic-//:window_name}'
|
|
22
|
+
* When the minimum supported tmux version can be bumped to 3.1+, switch
|
|
23
|
+
* to that syntax instead.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildBindKeyCommand(key: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Build the run-shell argument portion of the resume bind-key command.
|
|
28
|
+
* This is the value that gets passed to tmux bind-key directly.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildRunShellArg(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Build the tmux bind-key command string for `pit pause`.
|
|
33
|
+
*
|
|
34
|
+
* Uses the same epic-name derivation as the resume binding.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildPauseBindKeyCommand(key: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Build the run-shell argument portion of the pause bind-key command.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildPauseRunShellArg(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Install the keybindings into the running tmux server.
|
|
43
|
+
*
|
|
44
|
+
* Installs two bindings:
|
|
45
|
+
* - resumeKey (default R): pit resume <epic>
|
|
46
|
+
* - pauseKey (default H): pit pause <epic> (H for Halt)
|
|
47
|
+
*
|
|
48
|
+
* Steps:
|
|
49
|
+
* 1. Check for existing bindings on both keys and warn if there are conflicts.
|
|
50
|
+
* 2. Apply both bindings via `tmux bind-key`.
|
|
51
|
+
*
|
|
52
|
+
* Note: bind-key is SERVER-GLOBAL, not session-scoped. The binding applies
|
|
53
|
+
* to all tmux sessions on this server. If the user presses a key in a
|
|
54
|
+
* non-pit window, the pit command will fail with EPIC_NOT_FOUND (graceful failure).
|
|
55
|
+
*/
|
|
56
|
+
export declare function installKeybinding(resumeKey: string, pauseKey: string): Promise<{
|
|
57
|
+
output: InstallKeybindingOutput;
|
|
58
|
+
warnings: string[];
|
|
59
|
+
}>;
|
|
60
|
+
export declare function registerInstallKeybinding(program: Command): void;
|
|
61
|
+
//# sourceMappingURL=install-keybinding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-keybinding.d.ts","sourceRoot":"","sources":["../../src/commands/install-keybinding.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IACT,MAAM,EAAE,uBAAuB,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC,CA+CD;AAMD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiChE"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { printOutput, printError } from "../output.js";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Core logic (exported for testability)
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* Build the tmux bind-key command string for `pit resume`.
|
|
10
|
+
*
|
|
11
|
+
* tmux windows are named 'epic-<name>', so we strip the 'epic-' prefix
|
|
12
|
+
* to derive the epic ID for `pit resume`.
|
|
13
|
+
*
|
|
14
|
+
* We use a shell command inside run-shell that invokes sed to strip the
|
|
15
|
+
* prefix for portability with tmux < 3.1.
|
|
16
|
+
*
|
|
17
|
+
* NOTE: tmux >= 3.1 supports a cleaner approach using format substitution:
|
|
18
|
+
* bind-key R run-shell 'pit resume #{s/epic-//:window_name}'
|
|
19
|
+
* When the minimum supported tmux version can be bumped to 3.1+, switch
|
|
20
|
+
* to that syntax instead.
|
|
21
|
+
*/
|
|
22
|
+
export function buildBindKeyCommand(key) {
|
|
23
|
+
return `bind-key ${key} run-shell "pit resume \\$(tmux display-message -p '#{window_name}' | sed 's/^epic-//')"`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the run-shell argument portion of the resume bind-key command.
|
|
27
|
+
* This is the value that gets passed to tmux bind-key directly.
|
|
28
|
+
*/
|
|
29
|
+
export function buildRunShellArg() {
|
|
30
|
+
return `pit resume $(tmux display-message -p '#{window_name}' | sed 's/^epic-//')`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the tmux bind-key command string for `pit pause`.
|
|
34
|
+
*
|
|
35
|
+
* Uses the same epic-name derivation as the resume binding.
|
|
36
|
+
*/
|
|
37
|
+
export function buildPauseBindKeyCommand(key) {
|
|
38
|
+
return `bind-key ${key} run-shell "pit pause \\$(tmux display-message -p '#{window_name}' | sed 's/^epic-//')"`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the run-shell argument portion of the pause bind-key command.
|
|
42
|
+
*/
|
|
43
|
+
export function buildPauseRunShellArg() {
|
|
44
|
+
return `pit pause $(tmux display-message -p '#{window_name}' | sed 's/^epic-//')`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Install the keybindings into the running tmux server.
|
|
48
|
+
*
|
|
49
|
+
* Installs two bindings:
|
|
50
|
+
* - resumeKey (default R): pit resume <epic>
|
|
51
|
+
* - pauseKey (default H): pit pause <epic> (H for Halt)
|
|
52
|
+
*
|
|
53
|
+
* Steps:
|
|
54
|
+
* 1. Check for existing bindings on both keys and warn if there are conflicts.
|
|
55
|
+
* 2. Apply both bindings via `tmux bind-key`.
|
|
56
|
+
*
|
|
57
|
+
* Note: bind-key is SERVER-GLOBAL, not session-scoped. The binding applies
|
|
58
|
+
* to all tmux sessions on this server. If the user presses a key in a
|
|
59
|
+
* non-pit window, the pit command will fail with EPIC_NOT_FOUND (graceful failure).
|
|
60
|
+
*/
|
|
61
|
+
export async function installKeybinding(resumeKey, pauseKey) {
|
|
62
|
+
const warnings = [];
|
|
63
|
+
// Check for existing bindings on both keys
|
|
64
|
+
try {
|
|
65
|
+
const { stdout } = await execFileAsync("tmux", ["list-keys"]);
|
|
66
|
+
const lines = stdout.split("\n");
|
|
67
|
+
for (const { key, pitCmd } of [
|
|
68
|
+
{ key: resumeKey, pitCmd: "pit resume" },
|
|
69
|
+
{ key: pauseKey, pitCmd: "pit pause" },
|
|
70
|
+
]) {
|
|
71
|
+
const existingBinding = lines.find((line) => {
|
|
72
|
+
const match = line.match(/bind-key\s+-T\s+prefix\s+(\S+)/);
|
|
73
|
+
return match && match[1] === key;
|
|
74
|
+
});
|
|
75
|
+
if (existingBinding && !existingBinding.includes(pitCmd)) {
|
|
76
|
+
warnings.push(`Key '${key}' already has a binding: ${existingBinding.trim()}. It will be overwritten.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// tmux not running or list-keys failed — proceed anyway,
|
|
82
|
+
// bind-key will fail with a clearer error if tmux isn't available
|
|
83
|
+
}
|
|
84
|
+
// Apply the resume binding
|
|
85
|
+
await execFileAsync("tmux", ["bind-key", resumeKey, "run-shell", buildRunShellArg()]);
|
|
86
|
+
// Apply the pause binding
|
|
87
|
+
await execFileAsync("tmux", ["bind-key", pauseKey, "run-shell", buildPauseRunShellArg()]);
|
|
88
|
+
const resumeBindKeyLine = buildBindKeyCommand(resumeKey);
|
|
89
|
+
const pauseBindKeyLine = buildPauseBindKeyCommand(pauseKey);
|
|
90
|
+
const persistLines = [resumeBindKeyLine, pauseBindKeyLine].join("\n ");
|
|
91
|
+
const output = {
|
|
92
|
+
status: "installed",
|
|
93
|
+
bindings: [
|
|
94
|
+
{ key: resumeKey, binding: resumeBindKeyLine },
|
|
95
|
+
{ key: pauseKey, binding: pauseBindKeyLine },
|
|
96
|
+
],
|
|
97
|
+
note: `Bindings are server-global (apply to all tmux sessions). To persist, add to your tmux.conf:\n ${persistLines}`,
|
|
98
|
+
};
|
|
99
|
+
return { output, warnings };
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Command registration
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
export function registerInstallKeybinding(program) {
|
|
105
|
+
program
|
|
106
|
+
.command("install-keybinding")
|
|
107
|
+
.description("Install tmux keybindings for quick epic resume (R) and pause/halt (H)")
|
|
108
|
+
.option("--tmux-session <name>", "tmux session name", "pit")
|
|
109
|
+
.action(async (_opts, cmd) => {
|
|
110
|
+
const pretty = (cmd.parent?.opts()).pretty ?? false;
|
|
111
|
+
const resumeKey = "R";
|
|
112
|
+
const pauseKey = "H";
|
|
113
|
+
try {
|
|
114
|
+
const { output, warnings } = await installKeybinding(resumeKey, pauseKey);
|
|
115
|
+
if (pretty) {
|
|
116
|
+
for (const w of warnings) {
|
|
117
|
+
process.stderr.write(`Warning: ${w}\n`);
|
|
118
|
+
}
|
|
119
|
+
process.stdout.write(`Installed tmux keybindings:\n`);
|
|
120
|
+
for (const { key, binding } of output.bindings) {
|
|
121
|
+
process.stdout.write(` prefix + ${key}: ${binding}\n`);
|
|
122
|
+
}
|
|
123
|
+
process.stdout.write(`\nNote: ${output.note}\n`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
for (const w of warnings) {
|
|
127
|
+
process.stderr.write(JSON.stringify({ warning: w }) + "\n");
|
|
128
|
+
}
|
|
129
|
+
printOutput(output, false);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
printError({ error: `Failed to install keybinding: ${err.message}` }, pretty);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=install-keybinding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-keybinding.js","sourceRoot":"","sources":["../../src/commands/install-keybinding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
export type InstallStatusResult = {
|
|
3
|
+
status: "installed";
|
|
4
|
+
segment: "#{@pit-status}";
|
|
5
|
+
previous: string;
|
|
6
|
+
current: string;
|
|
7
|
+
note: string;
|
|
8
|
+
} | {
|
|
9
|
+
status: "already_installed";
|
|
10
|
+
segment: "#{@pit-status}";
|
|
11
|
+
note: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Append #{@pit-status} to the tmux server's global status-right option.
|
|
15
|
+
*
|
|
16
|
+
* Steps:
|
|
17
|
+
* 1. Read the current status-right value via `tmux show-options -gv status-right`.
|
|
18
|
+
* 2. If #{@pit-status} is already present, return { status: "already_installed" }.
|
|
19
|
+
* 3. Append ` #{@pit-status}` (with a space separator, or no separator if empty).
|
|
20
|
+
* 4. Set the new value via `tmux set-option -g status-right "<new_value>"`.
|
|
21
|
+
* 5. Return { status: "installed", previous, current }.
|
|
22
|
+
*
|
|
23
|
+
* Corner cases:
|
|
24
|
+
* - tmux not running: show-options throws → propagate with friendly error message.
|
|
25
|
+
* - status-right is empty/unset: set to #{@pit-status} with no leading space.
|
|
26
|
+
* - status-right already contains #{@pit-status}: idempotent, return already_installed.
|
|
27
|
+
* - Special characters (quotes, #): use execFileAsync args array to avoid shell injection.
|
|
28
|
+
*
|
|
29
|
+
* Note: set-option -g is SERVER-GLOBAL (applies to all sessions on this server).
|
|
30
|
+
* #{@pit-status} evaluates to empty string when the @pit-status option is unset,
|
|
31
|
+
* so it is invisible when pit isn't running.
|
|
32
|
+
*/
|
|
33
|
+
export declare function installStatus(): Promise<InstallStatusResult>;
|
|
34
|
+
export declare function registerInstallStatus(program: Command): void;
|
|
35
|
+
//# sourceMappingURL=install-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-status.d.ts","sourceRoot":"","sources":["../../src/commands/install-status.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBzC,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAQN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CA4ClE;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiC5D"}
|