@staff0rd/assist 0.486.0 → 0.487.0
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/README.md +1 -1
- package/dist/commands/sessions/web/bundle.js +1 -1
- package/dist/index.js +254 -213
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.487.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -6829,7 +6829,6 @@ function consumePause(itemId2) {
|
|
|
6829
6829
|
}
|
|
6830
6830
|
|
|
6831
6831
|
// src/commands/backlog/executePhase.ts
|
|
6832
|
-
import { randomUUID } from "crypto";
|
|
6833
6832
|
import chalk44 from "chalk";
|
|
6834
6833
|
|
|
6835
6834
|
// src/shared/awaitClaude.ts
|
|
@@ -7000,6 +6999,13 @@ function logHookFired(status3, sessionId, source) {
|
|
|
7000
6999
|
);
|
|
7001
7000
|
}
|
|
7002
7001
|
|
|
7002
|
+
// src/commands/backlog/assertCodexResumeSupported.ts
|
|
7003
|
+
function assertCodexResumeSupported(options2) {
|
|
7004
|
+
if (options2?.resumeSessionId && options2.harness === "codex") {
|
|
7005
|
+
throw new Error("Codex backlog sessions cannot be resumed yet");
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
|
|
7003
7009
|
// src/commands/backlog/buildCommentLines.ts
|
|
7004
7010
|
function buildCommentLines(comments3) {
|
|
7005
7011
|
if (!comments3?.length) return [];
|
|
@@ -7142,6 +7148,101 @@ function buildPhasePrompt(item, phaseNumber, phase) {
|
|
|
7142
7148
|
});
|
|
7143
7149
|
}
|
|
7144
7150
|
|
|
7151
|
+
// src/shared/harnesses.ts
|
|
7152
|
+
import * as os from "os";
|
|
7153
|
+
import * as path22 from "path";
|
|
7154
|
+
|
|
7155
|
+
// src/shared/checkCliAvailable.ts
|
|
7156
|
+
import { execSync as execSync30 } from "child_process";
|
|
7157
|
+
function checkCliAvailable(cli) {
|
|
7158
|
+
const binary = cli.split(/\s+/)[0];
|
|
7159
|
+
const opts = {
|
|
7160
|
+
encoding: "utf8",
|
|
7161
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7162
|
+
};
|
|
7163
|
+
try {
|
|
7164
|
+
execSync30(`command -v ${binary}`, opts);
|
|
7165
|
+
return true;
|
|
7166
|
+
} catch {
|
|
7167
|
+
try {
|
|
7168
|
+
execSync30(`where ${binary}`, opts);
|
|
7169
|
+
return true;
|
|
7170
|
+
} catch {
|
|
7171
|
+
return false;
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
// src/shared/harnesses.ts
|
|
7177
|
+
var harnesses = {
|
|
7178
|
+
claude: {
|
|
7179
|
+
kind: "claude",
|
|
7180
|
+
command: "claude",
|
|
7181
|
+
homeDir: path22.join(os.homedir(), ".claude"),
|
|
7182
|
+
sync: {
|
|
7183
|
+
agentsFile: "CLAUDE.md",
|
|
7184
|
+
commandDest: (name) => path22.join("commands", `${name}.md`)
|
|
7185
|
+
}
|
|
7186
|
+
},
|
|
7187
|
+
codex: {
|
|
7188
|
+
kind: "codex",
|
|
7189
|
+
command: "codex",
|
|
7190
|
+
homeDir: path22.join(os.homedir(), ".codex"),
|
|
7191
|
+
sync: {
|
|
7192
|
+
agentsFile: "AGENTS.md",
|
|
7193
|
+
commandDest: (name) => path22.join("skills", name, "SKILL.md")
|
|
7194
|
+
}
|
|
7195
|
+
},
|
|
7196
|
+
pi: {
|
|
7197
|
+
kind: "pi",
|
|
7198
|
+
command: "pi",
|
|
7199
|
+
homeDir: path22.join(os.homedir(), ".pi", "agent"),
|
|
7200
|
+
sync: {
|
|
7201
|
+
agentsFile: "AGENTS.md",
|
|
7202
|
+
commandDest: (name) => path22.join("prompts", `${name}.md`)
|
|
7203
|
+
}
|
|
7204
|
+
}
|
|
7205
|
+
};
|
|
7206
|
+
function isHarnessAvailable(kind) {
|
|
7207
|
+
return checkCliAvailable(harnesses[kind].command);
|
|
7208
|
+
}
|
|
7209
|
+
|
|
7210
|
+
// src/shared/spawnCodex.ts
|
|
7211
|
+
function spawnCodex(prompt, options2 = {}) {
|
|
7212
|
+
const cwd = options2.cwd ?? process.cwd();
|
|
7213
|
+
return spawnInherit(harnesses.codex.command, [
|
|
7214
|
+
"-C",
|
|
7215
|
+
cwd,
|
|
7216
|
+
"--sandbox",
|
|
7217
|
+
options2.sandbox ?? "workspace-write",
|
|
7218
|
+
prompt
|
|
7219
|
+
]);
|
|
7220
|
+
}
|
|
7221
|
+
|
|
7222
|
+
// src/shared/spawnPi.ts
|
|
7223
|
+
function spawnPi(prompt, options2 = {}) {
|
|
7224
|
+
const cwd = options2.cwd ?? process.cwd();
|
|
7225
|
+
return spawnInherit(harnesses.pi.command, [prompt], { cwd });
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
// src/shared/spawnHarness.ts
|
|
7229
|
+
function spawnHarness(harness, prompt, options2 = {}) {
|
|
7230
|
+
if (harness === "codex") {
|
|
7231
|
+
return spawnCodex(prompt, {
|
|
7232
|
+
cwd: options2.cwd,
|
|
7233
|
+
sandbox: options2.allowEdits === false ? "read-only" : "workspace-write"
|
|
7234
|
+
});
|
|
7235
|
+
}
|
|
7236
|
+
if (harness === "pi") {
|
|
7237
|
+
return spawnPi(prompt, { cwd: options2.cwd });
|
|
7238
|
+
}
|
|
7239
|
+
return spawnClaude(prompt, {
|
|
7240
|
+
allowEdits: options2.allowEdits ?? true,
|
|
7241
|
+
sessionId: options2.sessionId,
|
|
7242
|
+
resumeSessionId: options2.resumeSessionId
|
|
7243
|
+
});
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7145
7246
|
// src/commands/backlog/watchForMarker.ts
|
|
7146
7247
|
import { existsSync as existsSync24, unwatchFile, watchFile } from "fs";
|
|
7147
7248
|
|
|
@@ -7150,8 +7251,8 @@ import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
|
|
|
7150
7251
|
|
|
7151
7252
|
// src/commands/backlog/writeSignal.ts
|
|
7152
7253
|
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync18 } from "fs";
|
|
7153
|
-
import { homedir as
|
|
7154
|
-
import { dirname as dirname16, join as
|
|
7254
|
+
import { homedir as homedir9 } from "os";
|
|
7255
|
+
import { dirname as dirname16, join as join21 } from "path";
|
|
7155
7256
|
import chalk41 from "chalk";
|
|
7156
7257
|
|
|
7157
7258
|
// src/commands/backlog/recordSignalOwner.ts
|
|
@@ -7162,10 +7263,10 @@ import {
|
|
|
7162
7263
|
rmSync,
|
|
7163
7264
|
writeFileSync as writeFileSync17
|
|
7164
7265
|
} from "fs";
|
|
7165
|
-
import { homedir as
|
|
7166
|
-
import { dirname as dirname15, join as
|
|
7266
|
+
import { homedir as homedir8 } from "os";
|
|
7267
|
+
import { dirname as dirname15, join as join20 } from "path";
|
|
7167
7268
|
function getOwnerPath(itemId2) {
|
|
7168
|
-
return
|
|
7269
|
+
return join20(homedir8(), ".assist", "signals", `owner-${itemId2}.json`);
|
|
7169
7270
|
}
|
|
7170
7271
|
function recordSignalOwner(itemId2) {
|
|
7171
7272
|
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
@@ -7195,7 +7296,7 @@ function clearSignalOwner(itemId2) {
|
|
|
7195
7296
|
// src/commands/backlog/writeSignal.ts
|
|
7196
7297
|
function getSignalPath(sessionId = process.env.ASSIST_SESSION_ID) {
|
|
7197
7298
|
if (!sessionId) return void 0;
|
|
7198
|
-
return
|
|
7299
|
+
return join21(homedir9(), ".assist", "signals", `signal-${sessionId}.json`);
|
|
7199
7300
|
}
|
|
7200
7301
|
function resolveSignalTarget(event, data) {
|
|
7201
7302
|
const caller = process.env.ASSIST_SESSION_ID;
|
|
@@ -7264,8 +7365,11 @@ function stopWatching() {
|
|
|
7264
7365
|
}
|
|
7265
7366
|
|
|
7266
7367
|
// src/commands/backlog/launchPhaseClaude.ts
|
|
7267
|
-
async function launchPhaseClaude(prompt, spawnOptions, context) {
|
|
7268
|
-
const { child, done: done2 } =
|
|
7368
|
+
async function launchPhaseClaude(prompt, spawnOptions, context, harness = "claude") {
|
|
7369
|
+
const { child, done: done2 } = spawnHarness(harness, prompt, {
|
|
7370
|
+
...spawnOptions,
|
|
7371
|
+
cwd: process.cwd()
|
|
7372
|
+
});
|
|
7269
7373
|
watchForMarker(child);
|
|
7270
7374
|
const exitCode = await awaitClaude(done2, context);
|
|
7271
7375
|
stopWatching();
|
|
@@ -7284,16 +7388,77 @@ function resumeNudge() {
|
|
|
7284
7388
|
|
|
7285
7389
|
// src/commands/backlog/launchPhaseSession.ts
|
|
7286
7390
|
function launchPhaseSession(item, phaseNumber, phase, phaseLabel2, claudeSessionId, spawnOptions) {
|
|
7287
|
-
const
|
|
7391
|
+
const harness = spawnOptions?.harness;
|
|
7392
|
+
const { harness: _harness, ...phaseOptions } = spawnOptions ?? {};
|
|
7393
|
+
const resumeSessionId = phaseOptions.resumeSessionId;
|
|
7288
7394
|
return launchPhaseClaude(
|
|
7289
7395
|
resumeSessionId ? resumeNudge() : buildPhasePrompt(item, phaseNumber, phase),
|
|
7290
|
-
resumeSessionId ?
|
|
7291
|
-
phaseLabel2
|
|
7396
|
+
resumeSessionId ? phaseOptions : { ...phaseOptions, sessionId: claudeSessionId },
|
|
7397
|
+
phaseLabel2,
|
|
7398
|
+
harness
|
|
7292
7399
|
);
|
|
7293
7400
|
}
|
|
7294
7401
|
|
|
7402
|
+
// src/commands/backlog/resolvePhaseResult.ts
|
|
7403
|
+
import { existsSync as existsSync25, unlinkSync as unlinkSync4 } from "fs";
|
|
7404
|
+
import chalk42 from "chalk";
|
|
7405
|
+
|
|
7406
|
+
// src/commands/backlog/handleIncompletePhase.ts
|
|
7407
|
+
import enquirer4 from "enquirer";
|
|
7408
|
+
async function handleIncompletePhase() {
|
|
7409
|
+
const { action } = await exitOnCancel(
|
|
7410
|
+
enquirer4.prompt({
|
|
7411
|
+
type: "select",
|
|
7412
|
+
name: "action",
|
|
7413
|
+
message: "Phase was not marked complete. What would you like to do?",
|
|
7414
|
+
choices: ["Retry this phase", "Skip to next phase", "Abort"]
|
|
7415
|
+
})
|
|
7416
|
+
);
|
|
7417
|
+
if (action === "Retry this phase") return "retry";
|
|
7418
|
+
if (action === "Skip to next phase") return "skip";
|
|
7419
|
+
return "abort";
|
|
7420
|
+
}
|
|
7421
|
+
|
|
7422
|
+
// src/commands/backlog/resolvePhaseResult.ts
|
|
7423
|
+
function cleanupSignal() {
|
|
7424
|
+
const statusPath = getSignalPath();
|
|
7425
|
+
if (statusPath && existsSync25(statusPath)) {
|
|
7426
|
+
unlinkSync4(statusPath);
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
async function isTerminalStatus(itemId2) {
|
|
7430
|
+
const { orm } = await getReady();
|
|
7431
|
+
const item = await loadItem(orm, itemId2);
|
|
7432
|
+
return item?.status === "done" || item?.status === "wontdo";
|
|
7433
|
+
}
|
|
7434
|
+
async function resolvePhaseResult(phaseIndex, itemId2) {
|
|
7435
|
+
const signalPath = getSignalPath();
|
|
7436
|
+
if (!signalPath || !existsSync25(signalPath)) {
|
|
7437
|
+
if (await isTerminalStatus(itemId2)) return { kind: "abort" };
|
|
7438
|
+
const action = await handleIncompletePhase();
|
|
7439
|
+
if (action === "abort") return { kind: "abort" };
|
|
7440
|
+
return action === "skip" ? { kind: "skip" } : { kind: "retry" };
|
|
7441
|
+
}
|
|
7442
|
+
const signal = readSignal();
|
|
7443
|
+
cleanupSignal();
|
|
7444
|
+
if (signal?.event === "rewind") {
|
|
7445
|
+
const targetPhase = signal.targetPhase;
|
|
7446
|
+
const targetPhaseNumber = targetPhase + 1;
|
|
7447
|
+
console.log(chalk42.yellow(`
|
|
7448
|
+
Rewinding to phase ${targetPhaseNumber}.`));
|
|
7449
|
+
return { kind: "rewind", targetPhase };
|
|
7450
|
+
}
|
|
7451
|
+
const phaseNumber = phaseIndex + 1;
|
|
7452
|
+
console.log(chalk42.green(`
|
|
7453
|
+
Phase ${phaseNumber} completed.`));
|
|
7454
|
+
return { kind: "advance" };
|
|
7455
|
+
}
|
|
7456
|
+
|
|
7457
|
+
// src/commands/backlog/preparePhaseSession.ts
|
|
7458
|
+
import { randomUUID } from "crypto";
|
|
7459
|
+
|
|
7295
7460
|
// src/commands/backlog/persistPhaseSession.ts
|
|
7296
|
-
import
|
|
7461
|
+
import os2 from "os";
|
|
7297
7462
|
|
|
7298
7463
|
// src/shared/db/recordPhaseSession.ts
|
|
7299
7464
|
import { sql as sql12 } from "drizzle-orm";
|
|
@@ -7318,8 +7483,8 @@ async function persistPhaseSession(itemId2, phaseIdx, claudeSessionId) {
|
|
|
7318
7483
|
itemId2,
|
|
7319
7484
|
phaseIdx,
|
|
7320
7485
|
claudeSessionId,
|
|
7321
|
-
|
|
7322
|
-
|
|
7486
|
+
os2.hostname(),
|
|
7487
|
+
os2.userInfo().username
|
|
7323
7488
|
);
|
|
7324
7489
|
} catch (error) {
|
|
7325
7490
|
console.error(
|
|
@@ -7348,8 +7513,8 @@ async function persistPhaseSessionId(itemId2, phaseIdx, claudeSessionId) {
|
|
|
7348
7513
|
|
|
7349
7514
|
// src/shared/emitActivity.ts
|
|
7350
7515
|
import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync19 } from "fs";
|
|
7351
|
-
import { homedir as
|
|
7352
|
-
import { dirname as dirname17, join as
|
|
7516
|
+
import { homedir as homedir10 } from "os";
|
|
7517
|
+
import { dirname as dirname17, join as join22 } from "path";
|
|
7353
7518
|
import { z as z4 } from "zod";
|
|
7354
7519
|
var activitySchema = z4.object({
|
|
7355
7520
|
kind: z4.enum(["command", "backlog"]),
|
|
@@ -7364,7 +7529,7 @@ var activitySchema = z4.object({
|
|
|
7364
7529
|
startedAt: z4.number()
|
|
7365
7530
|
});
|
|
7366
7531
|
function activityPath(sessionId) {
|
|
7367
|
-
return
|
|
7532
|
+
return join22(homedir10(), ".assist", "activity", `activity-${sessionId}.json`);
|
|
7368
7533
|
}
|
|
7369
7534
|
function emitActivity(activity2) {
|
|
7370
7535
|
const sessionId = process.env.ASSIST_ACTIVITY_ID;
|
|
@@ -7410,75 +7575,20 @@ function reportPhaseActivity(item, phaseNumber, totalPhases, phase, claudeSessio
|
|
|
7410
7575
|
});
|
|
7411
7576
|
}
|
|
7412
7577
|
|
|
7413
|
-
// src/commands/backlog/resolvePhaseResult.ts
|
|
7414
|
-
import { existsSync as existsSync25, unlinkSync as unlinkSync4 } from "fs";
|
|
7415
|
-
import chalk42 from "chalk";
|
|
7416
|
-
|
|
7417
|
-
// src/commands/backlog/handleIncompletePhase.ts
|
|
7418
|
-
import enquirer4 from "enquirer";
|
|
7419
|
-
async function handleIncompletePhase() {
|
|
7420
|
-
const { action } = await exitOnCancel(
|
|
7421
|
-
enquirer4.prompt({
|
|
7422
|
-
type: "select",
|
|
7423
|
-
name: "action",
|
|
7424
|
-
message: "Phase was not marked complete. What would you like to do?",
|
|
7425
|
-
choices: ["Retry this phase", "Skip to next phase", "Abort"]
|
|
7426
|
-
})
|
|
7427
|
-
);
|
|
7428
|
-
if (action === "Retry this phase") return "retry";
|
|
7429
|
-
if (action === "Skip to next phase") return "skip";
|
|
7430
|
-
return "abort";
|
|
7431
|
-
}
|
|
7432
|
-
|
|
7433
|
-
// src/commands/backlog/resolvePhaseResult.ts
|
|
7434
|
-
function cleanupSignal() {
|
|
7435
|
-
const statusPath = getSignalPath();
|
|
7436
|
-
if (statusPath && existsSync25(statusPath)) {
|
|
7437
|
-
unlinkSync4(statusPath);
|
|
7438
|
-
}
|
|
7439
|
-
}
|
|
7440
|
-
async function isTerminalStatus(itemId2) {
|
|
7441
|
-
const { orm } = await getReady();
|
|
7442
|
-
const item = await loadItem(orm, itemId2);
|
|
7443
|
-
return item?.status === "done" || item?.status === "wontdo";
|
|
7444
|
-
}
|
|
7445
|
-
async function resolvePhaseResult(phaseIndex, itemId2) {
|
|
7446
|
-
const signalPath = getSignalPath();
|
|
7447
|
-
if (!signalPath || !existsSync25(signalPath)) {
|
|
7448
|
-
if (await isTerminalStatus(itemId2)) return { kind: "abort" };
|
|
7449
|
-
const action = await handleIncompletePhase();
|
|
7450
|
-
if (action === "abort") return { kind: "abort" };
|
|
7451
|
-
return action === "skip" ? { kind: "skip" } : { kind: "retry" };
|
|
7452
|
-
}
|
|
7453
|
-
const signal = readSignal();
|
|
7454
|
-
cleanupSignal();
|
|
7455
|
-
if (signal?.event === "rewind") {
|
|
7456
|
-
const targetPhase = signal.targetPhase;
|
|
7457
|
-
const targetPhaseNumber = targetPhase + 1;
|
|
7458
|
-
console.log(chalk42.yellow(`
|
|
7459
|
-
Rewinding to phase ${targetPhaseNumber}.`));
|
|
7460
|
-
return { kind: "rewind", targetPhase };
|
|
7461
|
-
}
|
|
7462
|
-
const phaseNumber = phaseIndex + 1;
|
|
7463
|
-
console.log(chalk42.green(`
|
|
7464
|
-
Phase ${phaseNumber} completed.`));
|
|
7465
|
-
return { kind: "advance" };
|
|
7466
|
-
}
|
|
7467
|
-
|
|
7468
7578
|
// src/commands/backlog/verifyResumeConversation.ts
|
|
7469
7579
|
import chalk43 from "chalk";
|
|
7470
7580
|
|
|
7471
7581
|
// src/commands/sessions/shared/findSessionJsonlPath.ts
|
|
7472
|
-
import * as
|
|
7582
|
+
import * as path25 from "path";
|
|
7473
7583
|
|
|
7474
7584
|
// src/commands/sessions/shared/discoverSessions.ts
|
|
7475
7585
|
import * as fs18 from "fs";
|
|
7476
|
-
import * as
|
|
7477
|
-
import * as
|
|
7586
|
+
import * as os3 from "os";
|
|
7587
|
+
import * as path24 from "path";
|
|
7478
7588
|
|
|
7479
7589
|
// src/commands/sessions/shared/parseSessionFile.ts
|
|
7480
7590
|
import * as fs17 from "fs";
|
|
7481
|
-
import * as
|
|
7591
|
+
import * as path23 from "path";
|
|
7482
7592
|
|
|
7483
7593
|
// src/commands/sessions/shared/deriveHistoryFields.ts
|
|
7484
7594
|
var KNOWN = [
|
|
@@ -7602,10 +7712,10 @@ async function readHeadLines(handle) {
|
|
|
7602
7712
|
}
|
|
7603
7713
|
function deriveProject(cwd, filePath, origin) {
|
|
7604
7714
|
if (!cwd) return dirNameToProject(filePath);
|
|
7605
|
-
return origin === "windows" ?
|
|
7715
|
+
return origin === "windows" ? path23.win32.basename(cwd) : path23.basename(cwd);
|
|
7606
7716
|
}
|
|
7607
7717
|
function dirNameToProject(filePath) {
|
|
7608
|
-
const dirName =
|
|
7718
|
+
const dirName = path23.basename(path23.dirname(filePath));
|
|
7609
7719
|
const parts = dirName.split("--");
|
|
7610
7720
|
return parts[parts.length - 1].replace(/-/g, "/");
|
|
7611
7721
|
}
|
|
@@ -7613,7 +7723,7 @@ function dirNameToProject(filePath) {
|
|
|
7613
7723
|
// src/commands/sessions/shared/discoverSessions.ts
|
|
7614
7724
|
function sessionRoots() {
|
|
7615
7725
|
const roots = [
|
|
7616
|
-
{ dir:
|
|
7726
|
+
{ dir: path24.join(os3.homedir(), ".claude", "projects"), origin: "wsl" }
|
|
7617
7727
|
];
|
|
7618
7728
|
const windowsRoot = loadConfig().sessions?.windowsProjectsRoot;
|
|
7619
7729
|
if (windowsRoot) roots.push({ dir: windowsRoot, origin: "windows" });
|
|
@@ -7631,7 +7741,7 @@ async function discoverSessionJsonlPaths() {
|
|
|
7631
7741
|
}
|
|
7632
7742
|
await Promise.all(
|
|
7633
7743
|
projectDirs.map(async (dirName) => {
|
|
7634
|
-
const dirPath =
|
|
7744
|
+
const dirPath = path24.join(dir, dirName);
|
|
7635
7745
|
let entries;
|
|
7636
7746
|
try {
|
|
7637
7747
|
entries = await fs18.promises.readdir(dirPath);
|
|
@@ -7640,7 +7750,7 @@ async function discoverSessionJsonlPaths() {
|
|
|
7640
7750
|
}
|
|
7641
7751
|
for (const file of entries) {
|
|
7642
7752
|
if (file.endsWith(".jsonl"))
|
|
7643
|
-
results.push({ path:
|
|
7753
|
+
results.push({ path: path24.join(dirPath, file), origin });
|
|
7644
7754
|
}
|
|
7645
7755
|
})
|
|
7646
7756
|
);
|
|
@@ -7670,7 +7780,7 @@ async function discoverSessions() {
|
|
|
7670
7780
|
async function findSessionJsonlPath(sessionId) {
|
|
7671
7781
|
const paths = await discoverSessionJsonlPaths();
|
|
7672
7782
|
const direct = paths.find(
|
|
7673
|
-
(p) =>
|
|
7783
|
+
(p) => path25.basename(p.path, ".jsonl") === sessionId
|
|
7674
7784
|
);
|
|
7675
7785
|
if (direct) return direct.path;
|
|
7676
7786
|
for (const p of paths) {
|
|
@@ -7703,6 +7813,21 @@ async function verifyPhaseResume(itemId2, resumeSessionId, phaseLabel2) {
|
|
|
7703
7813
|
return true;
|
|
7704
7814
|
}
|
|
7705
7815
|
|
|
7816
|
+
// src/commands/backlog/preparePhaseSession.ts
|
|
7817
|
+
async function preparePhaseSession(item, phase, phaseIndex, phaseNumber, totalPhases, phaseLabel2, spawnOptions) {
|
|
7818
|
+
const resumeSessionId = spawnOptions?.resumeSessionId;
|
|
7819
|
+
const claudeSessionId = resumeSessionId ?? randomUUID();
|
|
7820
|
+
if (resumeSessionId && !await verifyPhaseResume(item.id, resumeSessionId, phaseLabel2)) {
|
|
7821
|
+
return void 0;
|
|
7822
|
+
}
|
|
7823
|
+
reportPhaseActivity(item, phaseNumber, totalPhases, phase, claudeSessionId);
|
|
7824
|
+
if (!resumeSessionId) {
|
|
7825
|
+
await persistPhaseSessionId(item.id, phaseIndex, claudeSessionId);
|
|
7826
|
+
}
|
|
7827
|
+
await persistPhaseSession(item.id, phaseIndex, claudeSessionId);
|
|
7828
|
+
return { claudeSessionId };
|
|
7829
|
+
}
|
|
7830
|
+
|
|
7706
7831
|
// src/commands/backlog/executePhase.ts
|
|
7707
7832
|
async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases = phases.length) {
|
|
7708
7833
|
const phase = phases[phaseIndex];
|
|
@@ -7714,27 +7839,27 @@ async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases
|
|
|
7714
7839
|
`
|
|
7715
7840
|
)
|
|
7716
7841
|
);
|
|
7717
|
-
|
|
7718
|
-
const claudeSessionId = resumeSessionId ?? randomUUID();
|
|
7842
|
+
assertCodexResumeSupported(spawnOptions);
|
|
7719
7843
|
process.env.ASSIST_SESSION_ID ??= String(process.pid);
|
|
7720
7844
|
process.env.ASSIST_BACKLOG_ITEM_ID = String(item.id);
|
|
7721
7845
|
recordSignalOwner(item.id);
|
|
7722
7846
|
const phaseLabel2 = `phase ${phaseNumber}/${totalPhases}`;
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7847
|
+
const phaseSession = await preparePhaseSession(
|
|
7848
|
+
item,
|
|
7849
|
+
phase,
|
|
7850
|
+
phaseIndex,
|
|
7851
|
+
phaseNumber,
|
|
7852
|
+
totalPhases,
|
|
7853
|
+
phaseLabel2,
|
|
7854
|
+
spawnOptions
|
|
7855
|
+
);
|
|
7856
|
+
if (!phaseSession) return { kind: "abort" };
|
|
7732
7857
|
const exitCode = await launchPhaseSession(
|
|
7733
7858
|
item,
|
|
7734
7859
|
phaseNumber,
|
|
7735
7860
|
phase,
|
|
7736
7861
|
phaseLabel2,
|
|
7737
|
-
claudeSessionId,
|
|
7862
|
+
phaseSession.claudeSessionId,
|
|
7738
7863
|
spawnOptions
|
|
7739
7864
|
);
|
|
7740
7865
|
if (exitCode === CLAUDE_SPAWN_FAILED) return { kind: "abort" };
|
|
@@ -8300,7 +8425,7 @@ import chalk61 from "chalk";
|
|
|
8300
8425
|
import { WebSocketServer } from "ws";
|
|
8301
8426
|
|
|
8302
8427
|
// src/shared/getInstallDir.ts
|
|
8303
|
-
import { execSync as
|
|
8428
|
+
import { execSync as execSync31 } from "child_process";
|
|
8304
8429
|
import { dirname as dirname19, resolve as resolve8 } from "path";
|
|
8305
8430
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8306
8431
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
@@ -8310,7 +8435,7 @@ function getInstallDir() {
|
|
|
8310
8435
|
}
|
|
8311
8436
|
function isGitRepo(dir) {
|
|
8312
8437
|
try {
|
|
8313
|
-
const result =
|
|
8438
|
+
const result = execSync31("git rev-parse --show-toplevel", {
|
|
8314
8439
|
cwd: dir,
|
|
8315
8440
|
stdio: "pipe"
|
|
8316
8441
|
}).toString().trim();
|
|
@@ -8327,11 +8452,11 @@ import {
|
|
|
8327
8452
|
import chalk57 from "chalk";
|
|
8328
8453
|
|
|
8329
8454
|
// src/lib/openBrowser.ts
|
|
8330
|
-
import { execSync as
|
|
8455
|
+
import { execSync as execSync32 } from "child_process";
|
|
8331
8456
|
function tryExec(commands) {
|
|
8332
8457
|
for (const cmd of commands) {
|
|
8333
8458
|
try {
|
|
8334
|
-
|
|
8459
|
+
execSync32(cmd, { stdio: "ignore" });
|
|
8335
8460
|
return true;
|
|
8336
8461
|
} catch {
|
|
8337
8462
|
}
|
|
@@ -8632,13 +8757,13 @@ function gitCommonDir(cwd) {
|
|
|
8632
8757
|
|
|
8633
8758
|
// src/shared/loadJson.ts
|
|
8634
8759
|
import { existsSync as existsSync26, mkdirSync as mkdirSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync20 } from "fs";
|
|
8635
|
-
import { homedir as
|
|
8636
|
-
import { join as
|
|
8760
|
+
import { homedir as homedir12 } from "os";
|
|
8761
|
+
import { join as join24 } from "path";
|
|
8637
8762
|
function getStoreDir() {
|
|
8638
|
-
return
|
|
8763
|
+
return join24(homedir12(), ".assist");
|
|
8639
8764
|
}
|
|
8640
8765
|
function getStorePath(filename) {
|
|
8641
|
-
return
|
|
8766
|
+
return join24(getStoreDir(), filename);
|
|
8642
8767
|
}
|
|
8643
8768
|
function loadJson(filename) {
|
|
8644
8769
|
const path71 = getStorePath(filename);
|
|
@@ -8709,10 +8834,10 @@ function hostedGroup(cwd, origin, clone) {
|
|
|
8709
8834
|
// src/shared/createBundleHandler.ts
|
|
8710
8835
|
import { createHash } from "crypto";
|
|
8711
8836
|
import { readFileSync as readFileSync20, statSync as statSync3 } from "fs";
|
|
8712
|
-
import { dirname as dirname20, join as
|
|
8837
|
+
import { dirname as dirname20, join as join25 } from "path";
|
|
8713
8838
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8714
8839
|
function createBundleHandler(importMetaUrl, bundlePath, contentType = "application/javascript") {
|
|
8715
|
-
const file =
|
|
8840
|
+
const file = join25(dirname20(fileURLToPath4(importMetaUrl)), bundlePath);
|
|
8716
8841
|
let cache4;
|
|
8717
8842
|
return (req, res) => {
|
|
8718
8843
|
const mtimeMs = statSync3(file).mtimeMs;
|
|
@@ -8849,12 +8974,12 @@ async function loadItemSummaries(orm, origin) {
|
|
|
8849
8974
|
import { existsSync as existsSync27 } from "fs";
|
|
8850
8975
|
|
|
8851
8976
|
// src/commands/backlog/cloneTargetDir.ts
|
|
8852
|
-
import { join as
|
|
8977
|
+
import { join as join26, resolve as resolve9 } from "path";
|
|
8853
8978
|
function cloneTargetDir(origin, baseDir) {
|
|
8854
8979
|
if (origin.startsWith("local:")) return null;
|
|
8855
8980
|
const repoName = origin.split("/").filter(Boolean).pop();
|
|
8856
8981
|
if (!repoName) return null;
|
|
8857
|
-
return resolve9(
|
|
8982
|
+
return resolve9(join26(baseDir, repoName));
|
|
8858
8983
|
}
|
|
8859
8984
|
|
|
8860
8985
|
// src/commands/backlog/resolveRepoLocation.ts
|
|
@@ -9841,19 +9966,19 @@ function handleServerRuns(req, res) {
|
|
|
9841
9966
|
// src/commands/sessions/web/getReviewSynthesis.ts
|
|
9842
9967
|
import { execFile as execFile4 } from "child_process";
|
|
9843
9968
|
import { readFileSync as readFileSync21 } from "fs";
|
|
9844
|
-
import { homedir as
|
|
9845
|
-
import { basename as basename8, join as
|
|
9969
|
+
import { homedir as homedir13 } from "os";
|
|
9970
|
+
import { basename as basename8, join as join28 } from "path";
|
|
9846
9971
|
import { promisify as promisify3 } from "util";
|
|
9847
9972
|
|
|
9848
9973
|
// src/commands/sessions/web/findSynthesisForBranch.ts
|
|
9849
9974
|
import { existsSync as existsSync28, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
9850
|
-
import { basename as basename7, dirname as dirname21, join as
|
|
9975
|
+
import { basename as basename7, dirname as dirname21, join as join27 } from "path";
|
|
9851
9976
|
function findSynthesisForBranch(repoReviewsDir, branch2) {
|
|
9852
|
-
const branchKeyPath =
|
|
9977
|
+
const branchKeyPath = join27(repoReviewsDir, `${branch2}-`);
|
|
9853
9978
|
const parent = dirname21(branchKeyPath);
|
|
9854
9979
|
const branchPrefix = basename7(branchKeyPath);
|
|
9855
9980
|
if (!existsSync28(parent)) return null;
|
|
9856
|
-
const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) =>
|
|
9981
|
+
const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join27(parent, name, "synthesis.md")).filter((path71) => existsSync28(path71)).map((path71) => ({ path: path71, mtime: statSync4(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
9857
9982
|
return synthesisFiles[0]?.path ?? null;
|
|
9858
9983
|
}
|
|
9859
9984
|
|
|
@@ -9870,8 +9995,8 @@ async function resolveSynthesisPath(cwd) {
|
|
|
9870
9995
|
runGit(cwd, ["rev-parse", "--show-toplevel"]),
|
|
9871
9996
|
runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])
|
|
9872
9997
|
]);
|
|
9873
|
-
const repoReviewsDir =
|
|
9874
|
-
|
|
9998
|
+
const repoReviewsDir = join28(
|
|
9999
|
+
homedir13(),
|
|
9875
10000
|
".assist",
|
|
9876
10001
|
"reviews",
|
|
9877
10002
|
basename8(repoRoot)
|
|
@@ -10013,65 +10138,6 @@ async function gitStatus(req, res) {
|
|
|
10013
10138
|
}
|
|
10014
10139
|
}
|
|
10015
10140
|
|
|
10016
|
-
// src/shared/harnesses.ts
|
|
10017
|
-
import * as os3 from "os";
|
|
10018
|
-
import * as path25 from "path";
|
|
10019
|
-
|
|
10020
|
-
// src/shared/checkCliAvailable.ts
|
|
10021
|
-
import { execSync as execSync32 } from "child_process";
|
|
10022
|
-
function checkCliAvailable(cli) {
|
|
10023
|
-
const binary = cli.split(/\s+/)[0];
|
|
10024
|
-
const opts = {
|
|
10025
|
-
encoding: "utf8",
|
|
10026
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
10027
|
-
};
|
|
10028
|
-
try {
|
|
10029
|
-
execSync32(`command -v ${binary}`, opts);
|
|
10030
|
-
return true;
|
|
10031
|
-
} catch {
|
|
10032
|
-
try {
|
|
10033
|
-
execSync32(`where ${binary}`, opts);
|
|
10034
|
-
return true;
|
|
10035
|
-
} catch {
|
|
10036
|
-
return false;
|
|
10037
|
-
}
|
|
10038
|
-
}
|
|
10039
|
-
}
|
|
10040
|
-
|
|
10041
|
-
// src/shared/harnesses.ts
|
|
10042
|
-
var harnesses = {
|
|
10043
|
-
claude: {
|
|
10044
|
-
kind: "claude",
|
|
10045
|
-
command: "claude",
|
|
10046
|
-
homeDir: path25.join(os3.homedir(), ".claude"),
|
|
10047
|
-
sync: {
|
|
10048
|
-
agentsFile: "CLAUDE.md",
|
|
10049
|
-
commandDest: (name) => path25.join("commands", `${name}.md`)
|
|
10050
|
-
}
|
|
10051
|
-
},
|
|
10052
|
-
codex: {
|
|
10053
|
-
kind: "codex",
|
|
10054
|
-
command: "codex",
|
|
10055
|
-
homeDir: path25.join(os3.homedir(), ".codex"),
|
|
10056
|
-
sync: {
|
|
10057
|
-
agentsFile: "AGENTS.md",
|
|
10058
|
-
commandDest: (name) => path25.join("skills", name, "SKILL.md")
|
|
10059
|
-
}
|
|
10060
|
-
},
|
|
10061
|
-
pi: {
|
|
10062
|
-
kind: "pi",
|
|
10063
|
-
command: "pi",
|
|
10064
|
-
homeDir: path25.join(os3.homedir(), ".pi", "agent"),
|
|
10065
|
-
sync: {
|
|
10066
|
-
agentsFile: "AGENTS.md",
|
|
10067
|
-
commandDest: (name) => path25.join("prompts", `${name}.md`)
|
|
10068
|
-
}
|
|
10069
|
-
}
|
|
10070
|
-
};
|
|
10071
|
-
function isHarnessAvailable(kind) {
|
|
10072
|
-
return checkCliAvailable(harnesses[kind].command);
|
|
10073
|
-
}
|
|
10074
|
-
|
|
10075
10141
|
// src/commands/sessions/web/harnessCapabilities.ts
|
|
10076
10142
|
function harnessCapabilities(_req, res) {
|
|
10077
10143
|
const config = loadConfig().harness;
|
|
@@ -12989,33 +13055,6 @@ import enquirer7 from "enquirer";
|
|
|
12989
13055
|
// src/commands/backlog/launchMode.ts
|
|
12990
13056
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12991
13057
|
|
|
12992
|
-
// src/shared/spawnCodex.ts
|
|
12993
|
-
function spawnCodex(prompt, options2 = {}) {
|
|
12994
|
-
const cwd = options2.cwd ?? process.cwd();
|
|
12995
|
-
return spawnInherit(harnesses.codex.command, ["-C", cwd, prompt]);
|
|
12996
|
-
}
|
|
12997
|
-
|
|
12998
|
-
// src/shared/spawnPi.ts
|
|
12999
|
-
function spawnPi(prompt, options2 = {}) {
|
|
13000
|
-
const cwd = options2.cwd ?? process.cwd();
|
|
13001
|
-
return spawnInherit(harnesses.pi.command, [prompt], { cwd });
|
|
13002
|
-
}
|
|
13003
|
-
|
|
13004
|
-
// src/shared/spawnHarness.ts
|
|
13005
|
-
function spawnHarness(harness, prompt, options2 = {}) {
|
|
13006
|
-
if (harness === "codex") {
|
|
13007
|
-
return spawnCodex(prompt, { cwd: options2.cwd });
|
|
13008
|
-
}
|
|
13009
|
-
if (harness === "pi") {
|
|
13010
|
-
return spawnPi(prompt, { cwd: options2.cwd });
|
|
13011
|
-
}
|
|
13012
|
-
return spawnClaude(prompt, {
|
|
13013
|
-
allowEdits: true,
|
|
13014
|
-
sessionId: options2.sessionId,
|
|
13015
|
-
resumeSessionId: options2.resumeSessionId
|
|
13016
|
-
});
|
|
13017
|
-
}
|
|
13018
|
-
|
|
13019
13058
|
// src/commands/backlog/handleLaunchSignal.ts
|
|
13020
13059
|
import chalk86 from "chalk";
|
|
13021
13060
|
|
|
@@ -13217,7 +13256,10 @@ function registerRewindCommand(cmd) {
|
|
|
13217
13256
|
|
|
13218
13257
|
// src/commands/backlog/registerRunCommand.ts
|
|
13219
13258
|
function registerRunCommand(cmd) {
|
|
13220
|
-
cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase
|
|
13259
|
+
cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase").option("-w, --write", "Run the harness with write access (default)").option("--no-write", "Run the harness without write access").option(
|
|
13260
|
+
"--harness <harness>",
|
|
13261
|
+
"Coding harness to launch (claude|codex|pi); defaults to the configured harness.engine"
|
|
13262
|
+
).option(
|
|
13221
13263
|
"--resume-session <id>",
|
|
13222
13264
|
"Resume an interrupted Claude session for the current phase (used by the sessions daemon on restart)"
|
|
13223
13265
|
).action(
|
|
@@ -13225,6 +13267,7 @@ function registerRunCommand(cmd) {
|
|
|
13225
13267
|
if (!opts.resumeSession) pullIfConfigured();
|
|
13226
13268
|
await run2(id, {
|
|
13227
13269
|
allowEdits: opts.write !== false,
|
|
13270
|
+
harness: resolveHarness(opts.harness),
|
|
13228
13271
|
resumeSessionId: opts.resumeSession
|
|
13229
13272
|
});
|
|
13230
13273
|
}
|
|
@@ -15740,16 +15783,14 @@ function parseInput(raw) {
|
|
|
15740
15783
|
}
|
|
15741
15784
|
}
|
|
15742
15785
|
function preToolUseOutput(decision) {
|
|
15743
|
-
if (decision.permissionDecision === "allow")
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
}
|
|
15752
|
-
return { decision: "block", reason: decision.permissionDecisionReason };
|
|
15786
|
+
if (decision.permissionDecision === "allow") return void 0;
|
|
15787
|
+
return {
|
|
15788
|
+
hookSpecificOutput: {
|
|
15789
|
+
hookEventName: "PreToolUse",
|
|
15790
|
+
permissionDecision: "deny",
|
|
15791
|
+
permissionDecisionReason: decision.permissionDecisionReason
|
|
15792
|
+
}
|
|
15793
|
+
};
|
|
15753
15794
|
}
|
|
15754
15795
|
function permissionRequestOutput(decision) {
|
|
15755
15796
|
return {
|
|
@@ -15768,7 +15809,7 @@ async function codexHook() {
|
|
|
15768
15809
|
const decision = decideCommand(input.toolName, input.command);
|
|
15769
15810
|
if (!decision) return;
|
|
15770
15811
|
const output = input.event === "PermissionRequest" ? permissionRequestOutput(decision) : preToolUseOutput(decision);
|
|
15771
|
-
console.log(JSON.stringify(output));
|
|
15812
|
+
if (output) console.log(JSON.stringify(output));
|
|
15772
15813
|
}
|
|
15773
15814
|
|
|
15774
15815
|
// src/commands/registerCodexHook.ts
|