@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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* systemd integration for pit daemon auto-start.
|
|
3
|
+
*
|
|
4
|
+
* Provides install/uninstall commands that create and manage a systemd user
|
|
5
|
+
* service for automatically starting the pit daemon on login.
|
|
6
|
+
*
|
|
7
|
+
* These functions have zero coupling to daemon runtime state — they only use
|
|
8
|
+
* execFileAsync, fs, path, and os.
|
|
9
|
+
*/
|
|
10
|
+
import { execFile } from "node:child_process";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import * as os from "node:os";
|
|
15
|
+
const execFileAsync = promisify(execFile);
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Service template
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
export const SERVICE_TEMPLATE = `[Unit]
|
|
20
|
+
Description=pit daemon for %i
|
|
21
|
+
After=default.target
|
|
22
|
+
|
|
23
|
+
[Service]
|
|
24
|
+
Type=simple
|
|
25
|
+
ExecStart=/bin/sh -c 'pit daemon start --project-root "$(echo %i | base64 -d)"'
|
|
26
|
+
ExecStop=/bin/sh -c 'pit daemon stop --project-root "$(echo %i | base64 -d)"'
|
|
27
|
+
Restart=on-failure
|
|
28
|
+
RestartSec=5
|
|
29
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
30
|
+
|
|
31
|
+
[Install]
|
|
32
|
+
WantedBy=default.target
|
|
33
|
+
`;
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Helpers
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the git repository root, used as the default project root when
|
|
39
|
+
* --project-root is not specified.
|
|
40
|
+
*
|
|
41
|
+
* Note: This is a local helper distinct from the resolveProjectRoot() in
|
|
42
|
+
* commands/shared.ts — it is intentionally kept here to avoid coupling the
|
|
43
|
+
* systemd module to other command internals.
|
|
44
|
+
*/
|
|
45
|
+
async function resolveProjectRoot() {
|
|
46
|
+
try {
|
|
47
|
+
const { stdout: gitRoot } = await execFileAsync("git", ["rev-parse", "--show-toplevel"]);
|
|
48
|
+
return gitRoot.trim();
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const error = new Error(`Failed to resolve git root: ${err.message}`);
|
|
52
|
+
error.cause = err;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Commands
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
export async function daemonInstallCommand(options) {
|
|
60
|
+
if (process.platform !== "linux") {
|
|
61
|
+
console.log(JSON.stringify({
|
|
62
|
+
error: "systemd services only supported on Linux",
|
|
63
|
+
suggestion: "Run daemon manually: pit daemon start",
|
|
64
|
+
}));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const projectRoot = options.projectRoot ?? (await resolveProjectRoot());
|
|
68
|
+
// Encode project path for systemd template
|
|
69
|
+
const encodedPath = Buffer.from(projectRoot).toString("base64");
|
|
70
|
+
// Write service template
|
|
71
|
+
const serviceDir = path.join(os.homedir(), ".config/systemd/user");
|
|
72
|
+
await fs.promises.mkdir(serviceDir, { recursive: true });
|
|
73
|
+
const servicePath = path.join(serviceDir, "pit@.service");
|
|
74
|
+
// Only write template if not exists (don't overwrite user customizations)
|
|
75
|
+
const templateExists = await fs.promises
|
|
76
|
+
.access(servicePath)
|
|
77
|
+
.then(() => true)
|
|
78
|
+
.catch(() => false);
|
|
79
|
+
if (!templateExists) {
|
|
80
|
+
await fs.promises.writeFile(servicePath, SERVICE_TEMPLATE);
|
|
81
|
+
}
|
|
82
|
+
// Reload systemd
|
|
83
|
+
await execFileAsync("systemctl", ["--user", "daemon-reload"]);
|
|
84
|
+
// Enable (but don't start) the service for this project
|
|
85
|
+
const serviceName = `pit@${encodedPath}.service`;
|
|
86
|
+
await execFileAsync("systemctl", ["--user", "enable", serviceName]);
|
|
87
|
+
console.log(JSON.stringify({
|
|
88
|
+
status: "installed",
|
|
89
|
+
serviceName,
|
|
90
|
+
projectRoot,
|
|
91
|
+
instructions: [
|
|
92
|
+
`Service installed and enabled for: ${projectRoot}`,
|
|
93
|
+
`To start now: systemctl --user start ${serviceName}`,
|
|
94
|
+
`To check status: systemctl --user status ${serviceName}`,
|
|
95
|
+
`Will auto-start on login`,
|
|
96
|
+
],
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
export async function daemonUninstallCommand(options) {
|
|
100
|
+
if (process.platform !== "linux") {
|
|
101
|
+
console.log(JSON.stringify({
|
|
102
|
+
error: "systemd services only supported on Linux",
|
|
103
|
+
suggestion: "Run daemon manually: pit daemon start",
|
|
104
|
+
}));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
const projectRoot = options.projectRoot ?? (await resolveProjectRoot());
|
|
108
|
+
const encodedPath = Buffer.from(projectRoot).toString("base64");
|
|
109
|
+
const serviceName = `pit@${encodedPath}.service`;
|
|
110
|
+
// Stop service if running
|
|
111
|
+
try {
|
|
112
|
+
await execFileAsync("systemctl", ["--user", "stop", serviceName]);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Ignore errors if service not running
|
|
116
|
+
}
|
|
117
|
+
// Disable service
|
|
118
|
+
try {
|
|
119
|
+
await execFileAsync("systemctl", ["--user", "disable", serviceName]);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Ignore errors if service not enabled
|
|
123
|
+
}
|
|
124
|
+
console.log(JSON.stringify({
|
|
125
|
+
status: "uninstalled",
|
|
126
|
+
serviceName,
|
|
127
|
+
projectRoot,
|
|
128
|
+
}));
|
|
129
|
+
// Note: We don't remove the template file - other projects may use it
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=systemd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemd.js","sourceRoot":"","sources":["../../src/daemon/systemd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code hook script — event bridge for pit orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Receives JSON on stdin from Claude Code hooks (Stop, PermissionRequest),
|
|
5
|
+
* sends a JSON-RPC message to the pit daemon via Unix socket.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Never write to stdout — Claude Code parses stdout for JSON decisions.
|
|
8
|
+
* All logging goes to stderr. On any error, exit 0 (don't block Claude Code).
|
|
9
|
+
*
|
|
10
|
+
* Core logic is exported as named functions for testability.
|
|
11
|
+
* main() is the entry point and only runs when executed directly.
|
|
12
|
+
*/
|
|
13
|
+
export interface StopHookInput {
|
|
14
|
+
hook_event_name: "Stop";
|
|
15
|
+
session_id: string;
|
|
16
|
+
stop_hook_active: boolean;
|
|
17
|
+
last_assistant_message: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PermissionHookInput {
|
|
20
|
+
hook_event_name: "PermissionRequest";
|
|
21
|
+
session_id: string;
|
|
22
|
+
tool_name: string;
|
|
23
|
+
tool_input: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
export interface PermissionEventResult {
|
|
26
|
+
tool: string;
|
|
27
|
+
input: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function handleStopEvent(input: StopHookInput): void;
|
|
30
|
+
export declare function handlePermissionEvent(input: PermissionHookInput): PermissionEventResult;
|
|
31
|
+
export declare function sendToDaemon(socketPath: string, method: string, params: Record<string, unknown>): Promise<void>;
|
|
32
|
+
//# sourceMappingURL=claude-code-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/claude-code-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAI1D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,mBAAmB,GAAG,qBAAqB,CAKvF;AAMD,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code hook script — event bridge for pit orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Receives JSON on stdin from Claude Code hooks (Stop, PermissionRequest),
|
|
5
|
+
* sends a JSON-RPC message to the pit daemon via Unix socket.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Never write to stdout — Claude Code parses stdout for JSON decisions.
|
|
8
|
+
* All logging goes to stderr. On any error, exit 0 (don't block Claude Code).
|
|
9
|
+
*
|
|
10
|
+
* Core logic is exported as named functions for testability.
|
|
11
|
+
* main() is the entry point and only runs when executed directly.
|
|
12
|
+
*/
|
|
13
|
+
import * as net from "node:net";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Handlers (exported for testing)
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
export function handleStopEvent(input) {
|
|
19
|
+
if (input.stop_hook_active) {
|
|
20
|
+
console.error("[pit hook] stop_hook_active=true, notifying daemon anyway");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function handlePermissionEvent(input) {
|
|
24
|
+
return {
|
|
25
|
+
tool: input.tool_name ?? "unknown",
|
|
26
|
+
input: JSON.stringify(input.tool_input ?? {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Unix socket RPC helper
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
export async function sendToDaemon(socketPath, method, params) {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const socket = net.createConnection(socketPath);
|
|
35
|
+
const request = {
|
|
36
|
+
id: randomUUID(),
|
|
37
|
+
method,
|
|
38
|
+
params,
|
|
39
|
+
};
|
|
40
|
+
socket.on("connect", () => {
|
|
41
|
+
socket.write(JSON.stringify(request) + "\n", () => {
|
|
42
|
+
socket.destroy();
|
|
43
|
+
resolve();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
socket.on("error", (err) => {
|
|
47
|
+
console.error(`[pit hook] Socket error (${method}):`, err.message);
|
|
48
|
+
socket.destroy();
|
|
49
|
+
resolve(); // Swallow error, don't block Claude Code
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Entry point
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
async function main() {
|
|
57
|
+
const socketPath = process.env.PIT_SOCKET_PATH;
|
|
58
|
+
const epicId = process.env.PIT_EPIC;
|
|
59
|
+
if (!socketPath) {
|
|
60
|
+
console.error("[pit hook] PIT_SOCKET_PATH not set, exiting");
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
if (!epicId) {
|
|
64
|
+
console.error("[pit hook] PIT_EPIC not set, exiting");
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
// Read stdin
|
|
68
|
+
const chunks = [];
|
|
69
|
+
for await (const chunk of process.stdin) {
|
|
70
|
+
chunks.push(chunk);
|
|
71
|
+
}
|
|
72
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
73
|
+
// Parse JSON
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(raw);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error("[pit hook] Failed to parse stdin as JSON:", err);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
if (!parsed || typeof parsed !== "object") {
|
|
83
|
+
console.error("[pit hook] Parsed input is not an object, exiting");
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
const input = parsed;
|
|
87
|
+
if (input.hook_event_name === "Stop") {
|
|
88
|
+
handleStopEvent(input);
|
|
89
|
+
await sendToDaemon(socketPath, "agent-idle", { epicId });
|
|
90
|
+
}
|
|
91
|
+
else if (input.hook_event_name === "PermissionRequest") {
|
|
92
|
+
const { tool, input: toolInput } = handlePermissionEvent(input);
|
|
93
|
+
await sendToDaemon(socketPath, "agent-permission", {
|
|
94
|
+
epicId,
|
|
95
|
+
tool,
|
|
96
|
+
input: toolInput,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Unknown hook event — exit silently
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
// Only run main when executed directly (not imported in tests)
|
|
106
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
107
|
+
main().catch((err) => {
|
|
108
|
+
console.error("[pit hook] Unexpected error:", err);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=claude-code-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-hook.js","sourceRoot":"","sources":["../../src/hooks/claude-code-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pit agent instructions template.
|
|
3
|
+
*
|
|
4
|
+
* Rendered with renderInstructions() to substitute {{EPIC_ID}} placeholders
|
|
5
|
+
* before being written into the worktree via the adapter's writeInstructions().
|
|
6
|
+
*/
|
|
7
|
+
export declare const INSTRUCTIONS_TEMPLATE = "## Pit \u2014 Agent Instructions\n\nYou are an autonomous coding agent working on epic: {{EPIC_ID}}.\n\n### CRITICAL: Single-Task Workflow Rules\n\n**Work on ONE task at a time. No exceptions.**\n\n1. **Pick ONE task only** - Never claim multiple tasks upfront\n2. **Complete ALL steps** for a task before claiming the next one\n3. **Never batch multiple tasks** into one commit\n4. **Switch epics only when**:\n - Current epic is complete, OR\n - You're explicitly blocked and can't proceed\n5. **Before starting a new epic**:\n - **MUST ask user for confirmation** before claiming any task from a different epic\n - Wait for explicit approval before proceeding\n\n### Finding Work\n\n- Run `bd ready --parent {{EPIC_ID}}` to find your next available ticket.\n- If the command returns no tickets, all work for this epic is complete.\n\n### Per-Task Workflow\n\n**For EACH task, complete ALL steps before moving to the next:**\n\n1. **Claim task**: `bd update <id> --status in_progress`\n2. **Read task details**: `bd show <id>` - understand requirements fully\n3. **Implement**: Write code, tests, documentation as specified\n4. **Test locally**: Run affected tests to verify your changes work\n5. **Review your code**: Check for issues, ensure quality\n6. **Commit immediately**: Create ONE commit for this ONE task\n\n ```bash\n git add <files>\n git commit -m \"Task summary\n\n Detailed description of what changed.\n\n Closes: <task-id>\n Co-Authored-By: Claude <noreply@anthropic.com>\"\n ```\n\n7. **Push immediately**: Sync work to remote after each task\n ```bash\n git pull --rebase\n bd sync\n git push\n ```\n8. **Close task**: `bd close <id>`\n9. **Report completion and wait**: Report the task is complete, committed, and pushed. WAIT for the user to tell you which task to pick next. DO NOT automatically claim the next task.\n\n### Session Completion Protocol (Landing the Plane)\n\n**When ending a work session**, you MUST complete ALL steps below:\n\n1. **File issues for remaining work** - Create issues for anything that needs follow-up\n2. **Run quality gates** (if code changed) - Tests, linters, builds\n ```bash\n npm test # Unit tests - must pass\n npm run test:e2e # E2E tests - must pass (if available)\n npm run lint # Linting - must pass\n ```\n **STOP if any test fails** - Fix before continuing\n3. **Update issue status** - Close finished work, update in-progress items\n4. **PUSH TO REMOTE** - This is MANDATORY:\n ```bash\n git pull --rebase\n bd sync\n git push\n git status # MUST show \"up to date with origin\"\n ```\n5. **Verify** - All changes committed AND pushed\n6. **Hand off** - Provide context for next session\n\n**CRITICAL: Work is NOT complete until `git push` succeeds.**\n\n### Communication Rules\n\n1. **Report progress clearly** at each step\n2. **Ask for clarification** when task requirements are unclear\n3. **Never assume or guess** implementation details\n4. **Wait for explicit direction** before picking next task\n5. **Never automatically pick the next task** after finishing one - always report completion and wait\n\n### What NOT To Do\n\n- **NEVER** claim 5+ tasks at once\n- **NEVER** jump between epics without asking permission first\n- **NEVER** work on \"just one more quick task\" without committing the previous one\n- **NEVER** rationalize batching tasks as \"efficient\" - it makes code review impossible\n- **NEVER** silently switch epics - always get user confirmation\n- **NEVER** automatically pick the next task after finishing one\n- **NEVER** commit without pushing immediately after - keeps work stranded locally\n\n### Completion Protocol\n\nWhen you finish working on a ticket:\n1. Ensure all changes are committed and pushed\n2. Close the ticket with `bd close <id>`\n3. Stop working \u2014 the orchestrator will detect the closure and send you the next task\n\nWhen there are no more tickets available (`bd ready --parent {{EPIC_ID}}` returns nothing):\n- Close the last ticket and stop \u2014 the orchestrator will detect that no work remains\n\n### Requesting Human Input\n\nIf you are stuck, unsure, or need a human decision:\n1. Clearly explain what you need and why.\n2. Wait for the orchestrator to send you a response.\n";
|
|
8
|
+
export declare function renderInstructions(epicId: string, customTemplate?: string): string;
|
|
9
|
+
//# sourceMappingURL=instructions-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions-template.d.ts","sourceRoot":"","sources":["../src/instructions-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,qBAAqB,ssIA+GjC,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pit agent instructions template.
|
|
3
|
+
*
|
|
4
|
+
* Rendered with renderInstructions() to substitute {{EPIC_ID}} placeholders
|
|
5
|
+
* before being written into the worktree via the adapter's writeInstructions().
|
|
6
|
+
*/
|
|
7
|
+
export const INSTRUCTIONS_TEMPLATE = `## Pit — Agent Instructions
|
|
8
|
+
|
|
9
|
+
You are an autonomous coding agent working on epic: {{EPIC_ID}}.
|
|
10
|
+
|
|
11
|
+
### CRITICAL: Single-Task Workflow Rules
|
|
12
|
+
|
|
13
|
+
**Work on ONE task at a time. No exceptions.**
|
|
14
|
+
|
|
15
|
+
1. **Pick ONE task only** - Never claim multiple tasks upfront
|
|
16
|
+
2. **Complete ALL steps** for a task before claiming the next one
|
|
17
|
+
3. **Never batch multiple tasks** into one commit
|
|
18
|
+
4. **Switch epics only when**:
|
|
19
|
+
- Current epic is complete, OR
|
|
20
|
+
- You're explicitly blocked and can't proceed
|
|
21
|
+
5. **Before starting a new epic**:
|
|
22
|
+
- **MUST ask user for confirmation** before claiming any task from a different epic
|
|
23
|
+
- Wait for explicit approval before proceeding
|
|
24
|
+
|
|
25
|
+
### Finding Work
|
|
26
|
+
|
|
27
|
+
- Run \`bd ready --parent {{EPIC_ID}}\` to find your next available ticket.
|
|
28
|
+
- If the command returns no tickets, all work for this epic is complete.
|
|
29
|
+
|
|
30
|
+
### Per-Task Workflow
|
|
31
|
+
|
|
32
|
+
**For EACH task, complete ALL steps before moving to the next:**
|
|
33
|
+
|
|
34
|
+
1. **Claim task**: \`bd update <id> --status in_progress\`
|
|
35
|
+
2. **Read task details**: \`bd show <id>\` - understand requirements fully
|
|
36
|
+
3. **Implement**: Write code, tests, documentation as specified
|
|
37
|
+
4. **Test locally**: Run affected tests to verify your changes work
|
|
38
|
+
5. **Review your code**: Check for issues, ensure quality
|
|
39
|
+
6. **Commit immediately**: Create ONE commit for this ONE task
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
git add <files>
|
|
43
|
+
git commit -m "Task summary
|
|
44
|
+
|
|
45
|
+
Detailed description of what changed.
|
|
46
|
+
|
|
47
|
+
Closes: <task-id>
|
|
48
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
7. **Push immediately**: Sync work to remote after each task
|
|
52
|
+
\`\`\`bash
|
|
53
|
+
git pull --rebase
|
|
54
|
+
bd sync
|
|
55
|
+
git push
|
|
56
|
+
\`\`\`
|
|
57
|
+
8. **Close task**: \`bd close <id>\`
|
|
58
|
+
9. **Report completion and wait**: Report the task is complete, committed, and pushed. WAIT for the user to tell you which task to pick next. DO NOT automatically claim the next task.
|
|
59
|
+
|
|
60
|
+
### Session Completion Protocol (Landing the Plane)
|
|
61
|
+
|
|
62
|
+
**When ending a work session**, you MUST complete ALL steps below:
|
|
63
|
+
|
|
64
|
+
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
65
|
+
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
npm test # Unit tests - must pass
|
|
68
|
+
npm run test:e2e # E2E tests - must pass (if available)
|
|
69
|
+
npm run lint # Linting - must pass
|
|
70
|
+
\`\`\`
|
|
71
|
+
**STOP if any test fails** - Fix before continuing
|
|
72
|
+
3. **Update issue status** - Close finished work, update in-progress items
|
|
73
|
+
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
git pull --rebase
|
|
76
|
+
bd sync
|
|
77
|
+
git push
|
|
78
|
+
git status # MUST show "up to date with origin"
|
|
79
|
+
\`\`\`
|
|
80
|
+
5. **Verify** - All changes committed AND pushed
|
|
81
|
+
6. **Hand off** - Provide context for next session
|
|
82
|
+
|
|
83
|
+
**CRITICAL: Work is NOT complete until \`git push\` succeeds.**
|
|
84
|
+
|
|
85
|
+
### Communication Rules
|
|
86
|
+
|
|
87
|
+
1. **Report progress clearly** at each step
|
|
88
|
+
2. **Ask for clarification** when task requirements are unclear
|
|
89
|
+
3. **Never assume or guess** implementation details
|
|
90
|
+
4. **Wait for explicit direction** before picking next task
|
|
91
|
+
5. **Never automatically pick the next task** after finishing one - always report completion and wait
|
|
92
|
+
|
|
93
|
+
### What NOT To Do
|
|
94
|
+
|
|
95
|
+
- **NEVER** claim 5+ tasks at once
|
|
96
|
+
- **NEVER** jump between epics without asking permission first
|
|
97
|
+
- **NEVER** work on "just one more quick task" without committing the previous one
|
|
98
|
+
- **NEVER** rationalize batching tasks as "efficient" - it makes code review impossible
|
|
99
|
+
- **NEVER** silently switch epics - always get user confirmation
|
|
100
|
+
- **NEVER** automatically pick the next task after finishing one
|
|
101
|
+
- **NEVER** commit without pushing immediately after - keeps work stranded locally
|
|
102
|
+
|
|
103
|
+
### Completion Protocol
|
|
104
|
+
|
|
105
|
+
When you finish working on a ticket:
|
|
106
|
+
1. Ensure all changes are committed and pushed
|
|
107
|
+
2. Close the ticket with \`bd close <id>\`
|
|
108
|
+
3. Stop working — the orchestrator will detect the closure and send you the next task
|
|
109
|
+
|
|
110
|
+
When there are no more tickets available (\`bd ready --parent {{EPIC_ID}}\` returns nothing):
|
|
111
|
+
- Close the last ticket and stop — the orchestrator will detect that no work remains
|
|
112
|
+
|
|
113
|
+
### Requesting Human Input
|
|
114
|
+
|
|
115
|
+
If you are stuck, unsure, or need a human decision:
|
|
116
|
+
1. Clearly explain what you need and why.
|
|
117
|
+
2. Wait for the orchestrator to send you a response.
|
|
118
|
+
`;
|
|
119
|
+
export function renderInstructions(epicId, customTemplate) {
|
|
120
|
+
const template = customTemplate ?? INSTRUCTIONS_TEMPLATE;
|
|
121
|
+
return template.replaceAll("{{EPIC_ID}}", epicId).replaceAll("{{EPIC_NAME}}", epicId);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=instructions-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions-template.js","sourceRoot":"","sources":["../src/instructions-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+GpC,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,cAAuB;IACxE,MAAM,QAAQ,GAAG,cAAc,IAAI,qBAAqB,CAAC;IACzD,OAAO,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger for pit.
|
|
3
|
+
*
|
|
4
|
+
* Format: 2026-02-20T12:34:56Z [level] message { context }
|
|
5
|
+
*
|
|
6
|
+
* Output target is configurable (default: process.stderr) so tests can inject
|
|
7
|
+
* a PassThrough stream and inspect output without touching stderr.
|
|
8
|
+
*
|
|
9
|
+
* Debug output is gated behind DEBUG=pit or PIT_DEBUG=1.
|
|
10
|
+
*/
|
|
11
|
+
import type { Writable } from "node:stream";
|
|
12
|
+
export type LogLevel = "info" | "warn" | "error" | "debug";
|
|
13
|
+
export type LogContext = Record<string, unknown>;
|
|
14
|
+
export interface Logger {
|
|
15
|
+
info(msg: string, context?: LogContext): void;
|
|
16
|
+
warn(msg: string, context?: LogContext): void;
|
|
17
|
+
error(msg: string, context?: LogContext): void;
|
|
18
|
+
debug(msg: string, context?: LogContext): void;
|
|
19
|
+
}
|
|
20
|
+
export interface LoggerOptions {
|
|
21
|
+
/** Output stream. Defaults to process.stderr. */
|
|
22
|
+
stream?: Writable;
|
|
23
|
+
}
|
|
24
|
+
export declare function createLogger(options?: LoggerOptions): Logger;
|
|
25
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAeD,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAuB5D"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger for pit.
|
|
3
|
+
*
|
|
4
|
+
* Format: 2026-02-20T12:34:56Z [level] message { context }
|
|
5
|
+
*
|
|
6
|
+
* Output target is configurable (default: process.stderr) so tests can inject
|
|
7
|
+
* a PassThrough stream and inspect output without touching stderr.
|
|
8
|
+
*
|
|
9
|
+
* Debug output is gated behind DEBUG=pit or PIT_DEBUG=1.
|
|
10
|
+
*/
|
|
11
|
+
function isDebugEnabled() {
|
|
12
|
+
return process.env["PIT_DEBUG"] === "1" || process.env["DEBUG"] === "pit";
|
|
13
|
+
}
|
|
14
|
+
function formatLine(level, msg, context) {
|
|
15
|
+
const ts = new Date().toISOString();
|
|
16
|
+
const base = `${ts} [${level}] ${msg}`;
|
|
17
|
+
if (context !== undefined && Object.keys(context).length > 0) {
|
|
18
|
+
return `${base} ${JSON.stringify(context)}`;
|
|
19
|
+
}
|
|
20
|
+
return base;
|
|
21
|
+
}
|
|
22
|
+
export function createLogger(options) {
|
|
23
|
+
const out = options?.stream ?? process.stderr;
|
|
24
|
+
function write(level, msg, context) {
|
|
25
|
+
out.write(formatLine(level, msg, context) + "\n");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
info(msg, context) {
|
|
29
|
+
write("info", msg, context);
|
|
30
|
+
},
|
|
31
|
+
warn(msg, context) {
|
|
32
|
+
write("warn", msg, context);
|
|
33
|
+
},
|
|
34
|
+
error(msg, context) {
|
|
35
|
+
write("error", msg, context);
|
|
36
|
+
},
|
|
37
|
+
debug(msg, context) {
|
|
38
|
+
if (isDebugEnabled()) {
|
|
39
|
+
write("debug", msg, context);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmBH,SAAS,cAAc;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC;AAC5E,CAAC;AAED,SAAS,UAAU,CAAC,KAAe,EAAE,GAAW,EAAE,OAAoB;IACpE,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,GAAG,EAAE,KAAK,KAAK,KAAK,GAAG,EAAE,CAAC;IACvC,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7D,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAuB;IAClD,MAAM,GAAG,GAAa,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAExD,SAAS,KAAK,CAAC,KAAe,EAAE,GAAW,EAAE,OAAoB;QAC/D,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,OAAO;QACL,IAAI,CAAC,GAAG,EAAE,OAAO;YACf,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,OAAO;YACf,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,KAAK,CAAC,GAAG,EAAE,OAAO;YAChB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,KAAK,CAAC,GAAG,EAAE,OAAO;YAChB,IAAI,cAAc,EAAE,EAAE,CAAC;gBACrB,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/loop.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop runner for pit.
|
|
3
|
+
*
|
|
4
|
+
* startLoop() starts the autonomous ticket loop for a single epic:
|
|
5
|
+
* - Creates the state machine and immediately advances it to RUNNING
|
|
6
|
+
* - On state changes: updates the tmux status bar
|
|
7
|
+
* - Writes all transitions to the session log
|
|
8
|
+
*
|
|
9
|
+
* Returns a LoopHandle that lets the caller stop the loop and query its state.
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentAdapter } from "./adapters/types.js";
|
|
12
|
+
import type { Logger } from "./logger.js";
|
|
13
|
+
import { EpicStateMachine, type MachineState, type StateTransition } from "./state-machine.js";
|
|
14
|
+
export interface LoopOptions {
|
|
15
|
+
/** The epic ID (e.g. "auth") */
|
|
16
|
+
epic: string;
|
|
17
|
+
/** tmux session name */
|
|
18
|
+
tmuxSession: string;
|
|
19
|
+
/** tmux window name for the epic (e.g. "epic-auth") */
|
|
20
|
+
windowName: string;
|
|
21
|
+
/** Adapter to use for clearCommand and waitForReady */
|
|
22
|
+
adapter: AgentAdapter;
|
|
23
|
+
/** Session logger — writes to the session log file */
|
|
24
|
+
logger: Logger;
|
|
25
|
+
/** Initial state for recovery (optional) */
|
|
26
|
+
initialState?: MachineState;
|
|
27
|
+
/** Initial pause reason for recovery (optional) */
|
|
28
|
+
initialPauseReason?: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Optional callback fired on every state transition.
|
|
31
|
+
* Used by the daemon to update the tmux status bar with fresh progress data.
|
|
32
|
+
* May return a Promise — fire-and-forget, errors are swallowed.
|
|
33
|
+
*/
|
|
34
|
+
onStatusBarUpdate?: (epic: string, transition: StateTransition) => void | Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export interface LoopHandle {
|
|
37
|
+
/** Current machine state */
|
|
38
|
+
readonly state: MachineState;
|
|
39
|
+
/** Current pause reason (null if not paused) */
|
|
40
|
+
readonly pauseReason: string | null;
|
|
41
|
+
/** The underlying state machine (for external inspection / resume / pause) */
|
|
42
|
+
readonly machine: EpicStateMachine;
|
|
43
|
+
/** Stop the loop immediately. Idempotent. */
|
|
44
|
+
stop(): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start the autonomous ticket loop for one epic.
|
|
48
|
+
*
|
|
49
|
+
* Precondition: the agent TUI is running and the initial prompt has already
|
|
50
|
+
* been sent by setupEpic(). The state machine starts in SETUP and this
|
|
51
|
+
* function immediately advances it to RUNNING.
|
|
52
|
+
*
|
|
53
|
+
* @returns A LoopHandle for stopping the loop and querying state.
|
|
54
|
+
*/
|
|
55
|
+
export declare function startLoop(options: LoopOptions): LoopHandle;
|
|
56
|
+
/**
|
|
57
|
+
* Rich per-epic data for status bar rendering.
|
|
58
|
+
*/
|
|
59
|
+
export interface StatusBarEpic {
|
|
60
|
+
state: MachineState;
|
|
61
|
+
/** Progress from beads. null means beads is unavailable — omit count. */
|
|
62
|
+
progress: {
|
|
63
|
+
done: number;
|
|
64
|
+
total: number;
|
|
65
|
+
} | null;
|
|
66
|
+
/** The pause reason string (null when not paused). */
|
|
67
|
+
pauseReason: string | null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Map a raw pauseReason string (or null) to a short category keyword.
|
|
71
|
+
*/
|
|
72
|
+
export declare function pauseReasonToCategory(reason: string | null): string;
|
|
73
|
+
/**
|
|
74
|
+
* Build a tmux status bar string from a map of epic → StatusBarEpic.
|
|
75
|
+
*
|
|
76
|
+
* Format:
|
|
77
|
+
* RUNNING/CLEARING: "[auth: running 5/12]" or "[auth: running]" (no progress)
|
|
78
|
+
* PAUSED: "[pay: PAUSED! timeout]"
|
|
79
|
+
* DONE: "[ui: done 8/8]"
|
|
80
|
+
* SETUP: "[auth: setup]"
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatStatusBar(epics: Map<string, StatusBarEpic>): string;
|
|
83
|
+
/**
|
|
84
|
+
* Update the tmux status bar with the current epic states.
|
|
85
|
+
* Fire-and-forget — errors are swallowed.
|
|
86
|
+
*/
|
|
87
|
+
export declare function updateStatusBar(tmuxSession: string, epics: Map<string, StatusBarEpic>): Promise<void>;
|
|
88
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,gBAAgB,EAEhB,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAO5B,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IAEb,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IAEpB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IAEnB,uDAAuD;IACvD,OAAO,EAAE,YAAY,CAAC;IAEtB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IAEf,4CAA4C;IAC5C,YAAY,CAAC,EAAE,YAAY,CAAC;IAE5B,mDAAmD;IACnD,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAE7B,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IAEnC,6CAA6C;IAC7C,IAAI,IAAI,IAAI,CAAC;CACd;AAMD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAmF1D;AAUD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,yEAAyE;IACzE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,sDAAsD;IACtD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAUnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,CAuBzE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAGf"}
|