@staff0rd/assist 0.147.4 → 0.149.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 -0
- package/claude/commands/test-cover.md +4 -2
- package/claude/settings.json +1 -0
- package/dist/index.js +381 -346
- 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.149.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -483,6 +483,21 @@ function configList() {
|
|
|
483
483
|
console.log(stringifyYaml2(config, { lineWidth: 0 }).trimEnd());
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
// src/commands/coverage.ts
|
|
487
|
+
import { execSync as execSync2 } from "child_process";
|
|
488
|
+
function coverage() {
|
|
489
|
+
const output = execSync2(
|
|
490
|
+
"npx vitest run --coverage --coverage.include='src/**/*.ts' --coverage.all --coverage.reporter=text 2>&1",
|
|
491
|
+
{ encoding: "utf-8", timeout: 12e4 }
|
|
492
|
+
);
|
|
493
|
+
const match = output.match(/All files\s*\|\s*([\d.]+)/);
|
|
494
|
+
if (!match) {
|
|
495
|
+
console.error("Could not determine coverage from vitest output.");
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
498
|
+
console.log(`${match[1]}%`);
|
|
499
|
+
}
|
|
500
|
+
|
|
486
501
|
// src/commands/verify/init/index.ts
|
|
487
502
|
import chalk17 from "chalk";
|
|
488
503
|
|
|
@@ -555,7 +570,7 @@ function findPackageJsonWithVerifyScripts(startDir) {
|
|
|
555
570
|
}
|
|
556
571
|
|
|
557
572
|
// src/commands/verify/installPackage.ts
|
|
558
|
-
import { execSync as
|
|
573
|
+
import { execSync as execSync3 } from "child_process";
|
|
559
574
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
560
575
|
import chalk5 from "chalk";
|
|
561
576
|
function writePackageJson(filePath, pkg) {
|
|
@@ -574,7 +589,7 @@ function addScript(pkg, name, command) {
|
|
|
574
589
|
function installPackage(name, cwd) {
|
|
575
590
|
console.log(chalk5.dim(`Installing ${name}...`));
|
|
576
591
|
try {
|
|
577
|
-
|
|
592
|
+
execSync3(`npm install -D ${name}`, { stdio: "inherit", cwd });
|
|
578
593
|
return true;
|
|
579
594
|
} catch {
|
|
580
595
|
console.error(chalk5.red(`Failed to install ${name}`));
|
|
@@ -720,7 +735,7 @@ import * as path5 from "path";
|
|
|
720
735
|
import chalk13 from "chalk";
|
|
721
736
|
|
|
722
737
|
// src/commands/lint/init.ts
|
|
723
|
-
import { execSync as
|
|
738
|
+
import { execSync as execSync5 } from "child_process";
|
|
724
739
|
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
725
740
|
import { dirname as dirname6, join as join4 } from "path";
|
|
726
741
|
import { fileURLToPath } from "url";
|
|
@@ -744,7 +759,7 @@ async function promptConfirm(message, initial = true) {
|
|
|
744
759
|
}
|
|
745
760
|
|
|
746
761
|
// src/shared/removeEslint/index.ts
|
|
747
|
-
import { execSync as
|
|
762
|
+
import { execSync as execSync4 } from "child_process";
|
|
748
763
|
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
749
764
|
|
|
750
765
|
// src/shared/removeEslint/removeEslintConfigFiles.ts
|
|
@@ -779,7 +794,7 @@ function removeEslint(options2 = {}) {
|
|
|
779
794
|
const removedConfigFiles = removeEslintConfigFiles();
|
|
780
795
|
if (removedFromPackageJson || removedConfigFiles) {
|
|
781
796
|
console.log("Running npm install...");
|
|
782
|
-
|
|
797
|
+
execSync4("npm install", { stdio: "inherit" });
|
|
783
798
|
return true;
|
|
784
799
|
}
|
|
785
800
|
return false;
|
|
@@ -861,7 +876,7 @@ async function init() {
|
|
|
861
876
|
const biomeConfigPath = "biome.json";
|
|
862
877
|
if (!existsSync7(biomeConfigPath)) {
|
|
863
878
|
console.log("Initializing Biome...");
|
|
864
|
-
|
|
879
|
+
execSync5("npx @biomejs/biome init", { stdio: "inherit" });
|
|
865
880
|
}
|
|
866
881
|
if (!existsSync7(biomeConfigPath)) {
|
|
867
882
|
console.log("No biome.json found, skipping linter config");
|
|
@@ -1615,17 +1630,17 @@ function lint(options2 = {}) {
|
|
|
1615
1630
|
}
|
|
1616
1631
|
|
|
1617
1632
|
// src/commands/new/registerNew/newCli/index.ts
|
|
1618
|
-
import { execSync as
|
|
1633
|
+
import { execSync as execSync11 } from "child_process";
|
|
1619
1634
|
import { basename as basename2, resolve } from "path";
|
|
1620
1635
|
|
|
1621
1636
|
// src/commands/verify/hardcodedColors.ts
|
|
1622
|
-
import { execSync as
|
|
1637
|
+
import { execSync as execSync6 } from "child_process";
|
|
1623
1638
|
import { minimatch } from "minimatch";
|
|
1624
1639
|
var pattern = "0x[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,6}";
|
|
1625
1640
|
function hardcodedColors() {
|
|
1626
1641
|
const ignoreGlobs = loadConfig().hardcodedColors?.ignore ?? [];
|
|
1627
1642
|
try {
|
|
1628
|
-
const output =
|
|
1643
|
+
const output = execSync6(`grep -rEnH '${pattern}' src/`, {
|
|
1629
1644
|
encoding: "utf-8"
|
|
1630
1645
|
});
|
|
1631
1646
|
const lines = output.trim().split("\n").filter((line) => {
|
|
@@ -1704,10 +1719,10 @@ function list() {
|
|
|
1704
1719
|
}
|
|
1705
1720
|
|
|
1706
1721
|
// src/commands/verify/noVenv.ts
|
|
1707
|
-
import { execSync as
|
|
1722
|
+
import { execSync as execSync7 } from "child_process";
|
|
1708
1723
|
function noVenv() {
|
|
1709
1724
|
try {
|
|
1710
|
-
const output =
|
|
1725
|
+
const output = execSync7(
|
|
1711
1726
|
"find . -type d -name venv -not -path '*/node_modules/*'",
|
|
1712
1727
|
{
|
|
1713
1728
|
encoding: "utf-8"
|
|
@@ -1739,9 +1754,9 @@ Total: ${folders.length} venv folder(s)`);
|
|
|
1739
1754
|
import { minimatch as minimatch2 } from "minimatch";
|
|
1740
1755
|
|
|
1741
1756
|
// src/commands/verify/run/getChangedFiles.ts
|
|
1742
|
-
import { execSync as
|
|
1757
|
+
import { execSync as execSync8 } from "child_process";
|
|
1743
1758
|
function getChangedFiles() {
|
|
1744
|
-
const output =
|
|
1759
|
+
const output = execSync8("git diff --name-only HEAD", {
|
|
1745
1760
|
encoding: "utf-8"
|
|
1746
1761
|
}).trim();
|
|
1747
1762
|
if (output === "") return [];
|
|
@@ -1903,25 +1918,25 @@ async function run(options2 = {}) {
|
|
|
1903
1918
|
}
|
|
1904
1919
|
|
|
1905
1920
|
// src/commands/new/registerNew/initGit.ts
|
|
1906
|
-
import { execSync as
|
|
1921
|
+
import { execSync as execSync9 } from "child_process";
|
|
1907
1922
|
import { writeFileSync as writeFileSync7 } from "fs";
|
|
1908
1923
|
function initGit() {
|
|
1909
1924
|
console.log("Initializing git repository...");
|
|
1910
|
-
|
|
1925
|
+
execSync9("git init", { stdio: "inherit" });
|
|
1911
1926
|
writeFileSync7(".gitignore", "dist\nnode_modules\n");
|
|
1912
1927
|
}
|
|
1913
1928
|
|
|
1914
1929
|
// src/commands/new/registerNew/newCli/initPackageJson.ts
|
|
1915
|
-
import { execSync as
|
|
1930
|
+
import { execSync as execSync10 } from "child_process";
|
|
1916
1931
|
function initPackageJson(name) {
|
|
1917
1932
|
console.log("Initializing package.json...");
|
|
1918
|
-
|
|
1933
|
+
execSync10("npm init -y", { stdio: "inherit" });
|
|
1919
1934
|
console.log("Configuring package.json...");
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1935
|
+
execSync10("npm pkg delete main", { stdio: "inherit" });
|
|
1936
|
+
execSync10("npm pkg set type=module", { stdio: "inherit" });
|
|
1937
|
+
execSync10(`npm pkg set bin.${name}=./dist/index.js`, { stdio: "inherit" });
|
|
1938
|
+
execSync10("npm pkg set scripts.build=tsup", { stdio: "inherit" });
|
|
1939
|
+
execSync10('npm pkg set scripts.start="node dist/index.js"', {
|
|
1925
1940
|
stdio: "inherit"
|
|
1926
1941
|
});
|
|
1927
1942
|
}
|
|
@@ -1986,8 +2001,8 @@ async function newCli() {
|
|
|
1986
2001
|
initGit();
|
|
1987
2002
|
initPackageJson(name);
|
|
1988
2003
|
console.log("Installing dependencies...");
|
|
1989
|
-
|
|
1990
|
-
|
|
2004
|
+
execSync11("npm install commander", { stdio: "inherit" });
|
|
2005
|
+
execSync11("npm install -D tsup typescript @types/node", {
|
|
1991
2006
|
stdio: "inherit"
|
|
1992
2007
|
});
|
|
1993
2008
|
writeCliTemplate(name);
|
|
@@ -1996,11 +2011,11 @@ async function newCli() {
|
|
|
1996
2011
|
}
|
|
1997
2012
|
|
|
1998
2013
|
// src/commands/new/registerNew/newProject.ts
|
|
1999
|
-
import { execSync as
|
|
2014
|
+
import { execSync as execSync13 } from "child_process";
|
|
2000
2015
|
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
2001
2016
|
|
|
2002
2017
|
// src/commands/deploy/init/index.ts
|
|
2003
|
-
import { execSync as
|
|
2018
|
+
import { execSync as execSync12 } from "child_process";
|
|
2004
2019
|
import chalk24 from "chalk";
|
|
2005
2020
|
import enquirer3 from "enquirer";
|
|
2006
2021
|
|
|
@@ -2053,7 +2068,7 @@ Created ${WORKFLOW_PATH}`));
|
|
|
2053
2068
|
// src/commands/deploy/init/index.ts
|
|
2054
2069
|
async function ensureNetlifyCli() {
|
|
2055
2070
|
try {
|
|
2056
|
-
|
|
2071
|
+
execSync12("netlify sites:create --disable-linking", { stdio: "inherit" });
|
|
2057
2072
|
} catch (error) {
|
|
2058
2073
|
if (!(error instanceof Error) || !error.message.includes("command not found"))
|
|
2059
2074
|
throw error;
|
|
@@ -2068,9 +2083,9 @@ async function ensureNetlifyCli() {
|
|
|
2068
2083
|
process.exit(1);
|
|
2069
2084
|
}
|
|
2070
2085
|
console.log(chalk24.dim("\nInstalling netlify-cli...\n"));
|
|
2071
|
-
|
|
2086
|
+
execSync12("npm install -g netlify-cli", { stdio: "inherit" });
|
|
2072
2087
|
console.log();
|
|
2073
|
-
|
|
2088
|
+
execSync12("netlify sites:create --disable-linking", { stdio: "inherit" });
|
|
2074
2089
|
}
|
|
2075
2090
|
}
|
|
2076
2091
|
function printSetupInstructions() {
|
|
@@ -2113,7 +2128,7 @@ async function init5() {
|
|
|
2113
2128
|
// src/commands/new/registerNew/newProject.ts
|
|
2114
2129
|
async function newProject() {
|
|
2115
2130
|
console.log("Initializing Vite with react-ts template...");
|
|
2116
|
-
|
|
2131
|
+
execSync13("npm create vite@latest . -- --template react-ts", {
|
|
2117
2132
|
stdio: "inherit"
|
|
2118
2133
|
});
|
|
2119
2134
|
initGit();
|
|
@@ -2271,8 +2286,7 @@ async function notify() {
|
|
|
2271
2286
|
console.log(`Notification sent: ${notification_type} for ${projectName}`);
|
|
2272
2287
|
}
|
|
2273
2288
|
|
|
2274
|
-
// src/commands/backlog/
|
|
2275
|
-
import { existsSync as existsSync13 } from "fs";
|
|
2289
|
+
// src/commands/backlog/delete/index.ts
|
|
2276
2290
|
import chalk26 from "chalk";
|
|
2277
2291
|
|
|
2278
2292
|
// src/commands/backlog/shared.ts
|
|
@@ -2379,196 +2393,59 @@ function readStdin2() {
|
|
|
2379
2393
|
});
|
|
2380
2394
|
}
|
|
2381
2395
|
|
|
2382
|
-
// src/commands/backlog/add/shared.ts
|
|
2383
|
-
import { spawnSync } from "child_process";
|
|
2384
|
-
import { mkdtempSync, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
2385
|
-
import { tmpdir } from "os";
|
|
2386
|
-
import { join as join9 } from "path";
|
|
2387
|
-
import enquirer4 from "enquirer";
|
|
2388
|
-
async function promptType() {
|
|
2389
|
-
const { type } = await enquirer4.prompt({
|
|
2390
|
-
type: "select",
|
|
2391
|
-
name: "type",
|
|
2392
|
-
message: "Type:",
|
|
2393
|
-
choices: ["story", "bug"],
|
|
2394
|
-
initial: 0
|
|
2395
|
-
});
|
|
2396
|
-
return type;
|
|
2397
|
-
}
|
|
2398
|
-
async function promptName() {
|
|
2399
|
-
const { name } = await enquirer4.prompt({
|
|
2400
|
-
type: "input",
|
|
2401
|
-
name: "name",
|
|
2402
|
-
message: "Name:",
|
|
2403
|
-
validate: (value) => value.trim().length > 0 || "Name is required"
|
|
2404
|
-
});
|
|
2405
|
-
return name.trim();
|
|
2406
|
-
}
|
|
2407
|
-
async function promptDescription() {
|
|
2408
|
-
const { useEditor } = await enquirer4.prompt({
|
|
2409
|
-
type: "confirm",
|
|
2410
|
-
name: "useEditor",
|
|
2411
|
-
message: "Open editor for description?",
|
|
2412
|
-
initial: false
|
|
2413
|
-
});
|
|
2414
|
-
if (!useEditor) {
|
|
2415
|
-
const { description } = await enquirer4.prompt({
|
|
2416
|
-
type: "input",
|
|
2417
|
-
name: "description",
|
|
2418
|
-
message: "Description (optional):"
|
|
2419
|
-
});
|
|
2420
|
-
return description.trim() || void 0;
|
|
2421
|
-
}
|
|
2422
|
-
return openEditor();
|
|
2423
|
-
}
|
|
2424
|
-
function openEditor() {
|
|
2425
|
-
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
2426
|
-
const dir = mkdtempSync(join9(tmpdir(), "assist-"));
|
|
2427
|
-
const filePath = join9(dir, "description.md");
|
|
2428
|
-
writeFileSync12(filePath, "");
|
|
2429
|
-
const result = spawnSync(editor, [filePath], { stdio: "inherit" });
|
|
2430
|
-
if (result.status !== 0) {
|
|
2431
|
-
unlinkSync2(filePath);
|
|
2432
|
-
return void 0;
|
|
2433
|
-
}
|
|
2434
|
-
const content = readFileSync11(filePath, "utf-8").trim();
|
|
2435
|
-
unlinkSync2(filePath);
|
|
2436
|
-
return content || void 0;
|
|
2437
|
-
}
|
|
2438
|
-
async function promptAcceptanceCriteria() {
|
|
2439
|
-
const criteria = [];
|
|
2440
|
-
for (; ; ) {
|
|
2441
|
-
const { criterion } = await enquirer4.prompt({
|
|
2442
|
-
type: "input",
|
|
2443
|
-
name: "criterion",
|
|
2444
|
-
message: "Acceptance criterion (empty to finish):"
|
|
2445
|
-
});
|
|
2446
|
-
if (criterion.trim() === "") break;
|
|
2447
|
-
criteria.push(criterion.trim());
|
|
2448
|
-
}
|
|
2449
|
-
return criteria;
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
// src/commands/backlog/add/index.ts
|
|
2453
|
-
var addItemSchema = backlogItemSchema.omit({ id: true, status: true });
|
|
2454
|
-
async function addFromJson() {
|
|
2455
|
-
if (process.stdin.isTTY) {
|
|
2456
|
-
console.log(chalk26.red("--json requires piped input on stdin."));
|
|
2457
|
-
return;
|
|
2458
|
-
}
|
|
2459
|
-
const input = await readStdin2();
|
|
2460
|
-
const sanitised = input.replace(
|
|
2461
|
-
/"(?:[^"\\]|\\.)*"/g,
|
|
2462
|
-
(match) => match.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")
|
|
2463
|
-
);
|
|
2464
|
-
const data = addItemSchema.parse(JSON.parse(sanitised));
|
|
2465
|
-
const items = loadBacklog();
|
|
2466
|
-
const id = getNextId(items);
|
|
2467
|
-
items.push({ ...data, id, status: "todo" });
|
|
2468
|
-
saveBacklog(items);
|
|
2469
|
-
console.log(chalk26.green(`Added item #${id}: ${data.name}`));
|
|
2470
|
-
}
|
|
2471
|
-
async function addInteractive() {
|
|
2472
|
-
const type = await promptType();
|
|
2473
|
-
const name = await promptName();
|
|
2474
|
-
const description = await promptDescription();
|
|
2475
|
-
const acceptanceCriteria2 = await promptAcceptanceCriteria();
|
|
2476
|
-
const items = loadBacklog();
|
|
2477
|
-
const id = getNextId(items);
|
|
2478
|
-
items.push({
|
|
2479
|
-
id,
|
|
2480
|
-
type,
|
|
2481
|
-
name,
|
|
2482
|
-
description,
|
|
2483
|
-
acceptanceCriteria: acceptanceCriteria2,
|
|
2484
|
-
status: "todo"
|
|
2485
|
-
});
|
|
2486
|
-
saveBacklog(items);
|
|
2487
|
-
console.log(chalk26.green(`Added item #${id}: ${name}`));
|
|
2488
|
-
}
|
|
2489
|
-
async function add(options2) {
|
|
2490
|
-
if (!existsSync13(getBacklogPath())) {
|
|
2491
|
-
console.log(
|
|
2492
|
-
chalk26.yellow(
|
|
2493
|
-
"No backlog found. Run 'assist backlog init' to create one."
|
|
2494
|
-
)
|
|
2495
|
-
);
|
|
2496
|
-
return;
|
|
2497
|
-
}
|
|
2498
|
-
if (options2.json) {
|
|
2499
|
-
await addFromJson();
|
|
2500
|
-
} else {
|
|
2501
|
-
await addInteractive();
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
2396
|
// src/commands/backlog/delete/index.ts
|
|
2506
|
-
import chalk27 from "chalk";
|
|
2507
2397
|
async function del(id) {
|
|
2508
2398
|
const name = removeItem(id);
|
|
2509
2399
|
if (name) {
|
|
2510
|
-
console.log(
|
|
2400
|
+
console.log(chalk26.green(`Deleted item #${id}: ${name}`));
|
|
2511
2401
|
}
|
|
2512
2402
|
}
|
|
2513
2403
|
|
|
2514
2404
|
// src/commands/backlog/done/index.ts
|
|
2515
|
-
import
|
|
2405
|
+
import chalk27 from "chalk";
|
|
2516
2406
|
async function done(id) {
|
|
2517
2407
|
const name = setStatus(id, "done");
|
|
2518
2408
|
if (name) {
|
|
2519
|
-
console.log(
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
|
|
2523
|
-
// src/commands/backlog/init/index.ts
|
|
2524
|
-
import { existsSync as existsSync14 } from "fs";
|
|
2525
|
-
import chalk29 from "chalk";
|
|
2526
|
-
async function init6() {
|
|
2527
|
-
const backlogPath = getBacklogPath();
|
|
2528
|
-
if (existsSync14(backlogPath)) {
|
|
2529
|
-
console.log(chalk29.yellow("assist.backlog.yml already exists."));
|
|
2530
|
-
return;
|
|
2409
|
+
console.log(chalk27.green(`Completed item #${id}: ${name}`));
|
|
2531
2410
|
}
|
|
2532
|
-
saveBacklog([]);
|
|
2533
|
-
console.log(chalk29.green("Created assist.backlog.yml"));
|
|
2534
2411
|
}
|
|
2535
2412
|
|
|
2536
|
-
// src/commands/backlog/
|
|
2537
|
-
import
|
|
2538
|
-
import
|
|
2413
|
+
// src/commands/backlog/next.ts
|
|
2414
|
+
import chalk33 from "chalk";
|
|
2415
|
+
import enquirer5 from "enquirer";
|
|
2539
2416
|
|
|
2540
2417
|
// src/commands/backlog/list/shared.ts
|
|
2541
|
-
import
|
|
2418
|
+
import chalk28 from "chalk";
|
|
2542
2419
|
function statusIcon(status2) {
|
|
2543
2420
|
switch (status2) {
|
|
2544
2421
|
case "todo":
|
|
2545
|
-
return
|
|
2422
|
+
return chalk28.dim("[ ]");
|
|
2546
2423
|
case "in-progress":
|
|
2547
|
-
return
|
|
2424
|
+
return chalk28.yellow("[~]");
|
|
2548
2425
|
case "done":
|
|
2549
|
-
return
|
|
2426
|
+
return chalk28.green("[x]");
|
|
2550
2427
|
}
|
|
2551
2428
|
}
|
|
2552
2429
|
function typeLabel(type) {
|
|
2553
2430
|
switch (type) {
|
|
2554
2431
|
case "bug":
|
|
2555
|
-
return
|
|
2432
|
+
return chalk28.magenta("Bug");
|
|
2556
2433
|
case "story":
|
|
2557
|
-
return
|
|
2434
|
+
return chalk28.cyan("Story");
|
|
2558
2435
|
}
|
|
2559
2436
|
}
|
|
2560
2437
|
function phaseLabel(item) {
|
|
2561
2438
|
if (!item.plan) return "";
|
|
2562
|
-
return
|
|
2439
|
+
return chalk28.dim(
|
|
2563
2440
|
` (phase ${(item.currentPhase ?? 0) + 1}/${item.plan.length})`
|
|
2564
2441
|
);
|
|
2565
2442
|
}
|
|
2566
2443
|
function printVerboseDetails(item) {
|
|
2567
2444
|
if (item.description) {
|
|
2568
|
-
console.log(` ${
|
|
2445
|
+
console.log(` ${chalk28.dim("Description:")} ${item.description}`);
|
|
2569
2446
|
}
|
|
2570
2447
|
if (item.acceptanceCriteria.length > 0) {
|
|
2571
|
-
console.log(` ${
|
|
2448
|
+
console.log(` ${chalk28.dim("Acceptance criteria:")}`);
|
|
2572
2449
|
for (const [i, criterion] of item.acceptanceCriteria.entries()) {
|
|
2573
2450
|
console.log(` ${i + 1}. ${criterion}`);
|
|
2574
2451
|
}
|
|
@@ -2576,42 +2453,8 @@ function printVerboseDetails(item) {
|
|
|
2576
2453
|
console.log();
|
|
2577
2454
|
}
|
|
2578
2455
|
|
|
2579
|
-
// src/commands/backlog/list/index.ts
|
|
2580
|
-
function filterItems(items, options2) {
|
|
2581
|
-
if (options2.status) return items.filter((i) => i.status === options2.status);
|
|
2582
|
-
if (!options2.all) return items.filter((i) => i.status !== "done");
|
|
2583
|
-
return items;
|
|
2584
|
-
}
|
|
2585
|
-
async function list2(options2) {
|
|
2586
|
-
if (!existsSync15(getBacklogPath())) {
|
|
2587
|
-
console.log(
|
|
2588
|
-
chalk31.yellow(
|
|
2589
|
-
"No backlog found. Run 'assist backlog init' to create one."
|
|
2590
|
-
)
|
|
2591
|
-
);
|
|
2592
|
-
return;
|
|
2593
|
-
}
|
|
2594
|
-
const items = filterItems(loadBacklog(), options2);
|
|
2595
|
-
if (items.length === 0) {
|
|
2596
|
-
console.log(chalk31.dim("Backlog is empty."));
|
|
2597
|
-
return;
|
|
2598
|
-
}
|
|
2599
|
-
for (const item of items) {
|
|
2600
|
-
console.log(
|
|
2601
|
-
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk31.dim(`#${item.id}`)} ${item.name}${phaseLabel(item)}`
|
|
2602
|
-
);
|
|
2603
|
-
if (options2.verbose) {
|
|
2604
|
-
printVerboseDetails(item);
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
|
|
2609
|
-
// src/commands/backlog/next.ts
|
|
2610
|
-
import chalk36 from "chalk";
|
|
2611
|
-
import enquirer6 from "enquirer";
|
|
2612
|
-
|
|
2613
2456
|
// src/commands/backlog/run.ts
|
|
2614
|
-
import
|
|
2457
|
+
import chalk32 from "chalk";
|
|
2615
2458
|
|
|
2616
2459
|
// src/commands/backlog/buildAuthoredPhasePrompt.ts
|
|
2617
2460
|
function buildAuthoredPhasePrompt(item, phaseIndex, phase) {
|
|
@@ -2711,16 +2554,16 @@ function buildReviewPhase() {
|
|
|
2711
2554
|
}
|
|
2712
2555
|
|
|
2713
2556
|
// src/commands/backlog/executePhase.ts
|
|
2714
|
-
import
|
|
2557
|
+
import chalk31 from "chalk";
|
|
2715
2558
|
|
|
2716
2559
|
// src/commands/backlog/resolvePhaseResult.ts
|
|
2717
|
-
import { existsSync as
|
|
2718
|
-
import
|
|
2560
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync2 } from "fs";
|
|
2561
|
+
import chalk30 from "chalk";
|
|
2719
2562
|
|
|
2720
2563
|
// src/commands/backlog/handleIncompletePhase.ts
|
|
2721
|
-
import
|
|
2564
|
+
import enquirer4 from "enquirer";
|
|
2722
2565
|
async function handleIncompletePhase() {
|
|
2723
|
-
const { action } = await
|
|
2566
|
+
const { action } = await enquirer4.prompt({
|
|
2724
2567
|
type: "select",
|
|
2725
2568
|
name: "action",
|
|
2726
2569
|
message: "Phase was not marked complete. What would you like to do?",
|
|
@@ -2732,17 +2575,17 @@ async function handleIncompletePhase() {
|
|
|
2732
2575
|
}
|
|
2733
2576
|
|
|
2734
2577
|
// src/commands/backlog/phaseDone.ts
|
|
2735
|
-
import { writeFileSync as
|
|
2736
|
-
import { join as
|
|
2737
|
-
import
|
|
2578
|
+
import { writeFileSync as writeFileSync12 } from "fs";
|
|
2579
|
+
import { join as join9 } from "path";
|
|
2580
|
+
import chalk29 from "chalk";
|
|
2738
2581
|
var PHASE_STATUS_FILE = ".assist-phase-status.json";
|
|
2739
2582
|
function getPhaseStatusPath() {
|
|
2740
|
-
return
|
|
2583
|
+
return join9(process.cwd(), PHASE_STATUS_FILE);
|
|
2741
2584
|
}
|
|
2742
2585
|
function phaseDone(id, phase) {
|
|
2743
2586
|
const phaseIndex = Number.parseInt(phase, 10);
|
|
2744
2587
|
const statusPath = getPhaseStatusPath();
|
|
2745
|
-
|
|
2588
|
+
writeFileSync12(
|
|
2746
2589
|
statusPath,
|
|
2747
2590
|
JSON.stringify({
|
|
2748
2591
|
itemId: Number.parseInt(id, 10),
|
|
@@ -2751,32 +2594,36 @@ function phaseDone(id, phase) {
|
|
|
2751
2594
|
})
|
|
2752
2595
|
);
|
|
2753
2596
|
setCurrentPhase(id, phaseIndex + 1);
|
|
2754
|
-
console.log(
|
|
2597
|
+
console.log(chalk29.green(`Phase ${phase} of item #${id} marked as complete.`));
|
|
2755
2598
|
}
|
|
2756
2599
|
|
|
2757
2600
|
// src/commands/backlog/resolvePhaseResult.ts
|
|
2758
2601
|
function cleanupMarker() {
|
|
2759
2602
|
const statusPath = getPhaseStatusPath();
|
|
2760
|
-
if (
|
|
2761
|
-
|
|
2603
|
+
if (existsSync13(statusPath)) {
|
|
2604
|
+
unlinkSync2(statusPath);
|
|
2762
2605
|
}
|
|
2763
2606
|
}
|
|
2764
2607
|
async function resolvePhaseResult(phaseIndex) {
|
|
2765
|
-
if (!
|
|
2608
|
+
if (!existsSync13(getPhaseStatusPath())) {
|
|
2766
2609
|
const action = await handleIncompletePhase();
|
|
2767
2610
|
if (action === "abort") return -1;
|
|
2768
2611
|
return action === "skip" ? 1 : 0;
|
|
2769
2612
|
}
|
|
2770
2613
|
cleanupMarker();
|
|
2771
|
-
console.log(
|
|
2614
|
+
console.log(chalk30.green(`
|
|
2772
2615
|
Phase ${phaseIndex + 1} completed.`));
|
|
2773
2616
|
return 1;
|
|
2774
2617
|
}
|
|
2775
2618
|
|
|
2776
2619
|
// src/commands/backlog/spawnClaude.ts
|
|
2777
2620
|
import { spawn as spawn3 } from "child_process";
|
|
2778
|
-
function spawnClaude(prompt) {
|
|
2779
|
-
const
|
|
2621
|
+
function spawnClaude(prompt, options2 = {}) {
|
|
2622
|
+
const args = [prompt];
|
|
2623
|
+
if (options2.allowEdits) {
|
|
2624
|
+
args.push("--permission-mode", "acceptEdits");
|
|
2625
|
+
}
|
|
2626
|
+
const child = spawn3("claude", args, {
|
|
2780
2627
|
stdio: "inherit"
|
|
2781
2628
|
});
|
|
2782
2629
|
const done2 = new Promise((resolve7, reject) => {
|
|
@@ -2787,11 +2634,11 @@ function spawnClaude(prompt) {
|
|
|
2787
2634
|
}
|
|
2788
2635
|
|
|
2789
2636
|
// src/commands/backlog/watchForMarker.ts
|
|
2790
|
-
import { existsSync as
|
|
2637
|
+
import { existsSync as existsSync14, unwatchFile, watchFile } from "fs";
|
|
2791
2638
|
function watchForMarker(child) {
|
|
2792
2639
|
const statusPath = getPhaseStatusPath();
|
|
2793
2640
|
watchFile(statusPath, { interval: 1e3 }, () => {
|
|
2794
|
-
if (
|
|
2641
|
+
if (existsSync14(statusPath)) {
|
|
2795
2642
|
unwatchFile(statusPath);
|
|
2796
2643
|
child.kill("SIGTERM");
|
|
2797
2644
|
}
|
|
@@ -2802,17 +2649,18 @@ function stopWatching() {
|
|
|
2802
2649
|
}
|
|
2803
2650
|
|
|
2804
2651
|
// src/commands/backlog/executePhase.ts
|
|
2805
|
-
async function executePhase(item, phaseIndex, phases) {
|
|
2652
|
+
async function executePhase(item, phaseIndex, phases, spawnOptions) {
|
|
2806
2653
|
const phase = phases[phaseIndex];
|
|
2807
2654
|
console.log(
|
|
2808
|
-
|
|
2655
|
+
chalk31.bold(
|
|
2809
2656
|
`
|
|
2810
2657
|
--- Phase ${phaseIndex + 1}/${phases.length}: ${phase.name} ---
|
|
2811
2658
|
`
|
|
2812
2659
|
)
|
|
2813
2660
|
);
|
|
2814
2661
|
const { child, done: done2 } = spawnClaude(
|
|
2815
|
-
buildPhasePrompt(item, phaseIndex, phase)
|
|
2662
|
+
buildPhasePrompt(item, phaseIndex, phase),
|
|
2663
|
+
spawnOptions
|
|
2816
2664
|
);
|
|
2817
2665
|
watchForMarker(child);
|
|
2818
2666
|
await done2;
|
|
@@ -2835,7 +2683,7 @@ function resolvePlan(item) {
|
|
|
2835
2683
|
}
|
|
2836
2684
|
|
|
2837
2685
|
// src/commands/backlog/run.ts
|
|
2838
|
-
async function run2(id) {
|
|
2686
|
+
async function run2(id, spawnOptions) {
|
|
2839
2687
|
const result = loadAndFindItem(id);
|
|
2840
2688
|
if (!result) return;
|
|
2841
2689
|
const { item } = result;
|
|
@@ -2844,93 +2692,98 @@ async function run2(id) {
|
|
|
2844
2692
|
if (startPhase > plan2.length) {
|
|
2845
2693
|
if (item.status !== "done") setStatus(id, "done");
|
|
2846
2694
|
console.log(
|
|
2847
|
-
|
|
2695
|
+
chalk32.green(`All phases already complete for #${id}: ${item.name}`)
|
|
2848
2696
|
);
|
|
2849
2697
|
return;
|
|
2850
2698
|
}
|
|
2851
2699
|
setStatus(id, "in-progress");
|
|
2852
|
-
console.log(
|
|
2700
|
+
console.log(chalk32.bold(`Running plan for #${id}: ${item.name}`));
|
|
2853
2701
|
if (startPhase > 0) {
|
|
2854
2702
|
console.log(
|
|
2855
|
-
|
|
2703
|
+
chalk32.dim(`Resuming from phase ${startPhase + 1}/${plan2.length}
|
|
2856
2704
|
`)
|
|
2857
2705
|
);
|
|
2858
2706
|
} else {
|
|
2859
|
-
console.log(
|
|
2707
|
+
console.log(chalk32.dim(`${plan2.length} phase(s)
|
|
2860
2708
|
`));
|
|
2861
2709
|
}
|
|
2862
2710
|
let phaseIndex = startPhase;
|
|
2863
2711
|
while (phaseIndex < plan2.length) {
|
|
2864
|
-
phaseIndex = await executePhase(item, phaseIndex, plan2);
|
|
2712
|
+
phaseIndex = await executePhase(item, phaseIndex, plan2, spawnOptions);
|
|
2865
2713
|
if (phaseIndex < 0) return;
|
|
2866
2714
|
}
|
|
2867
2715
|
const reviewPhase = buildReviewPhase();
|
|
2868
2716
|
const allPhases = [...plan2, reviewPhase];
|
|
2869
2717
|
const reviewIndex = plan2.length;
|
|
2870
|
-
const reviewResult = await executePhase(
|
|
2718
|
+
const reviewResult = await executePhase(
|
|
2719
|
+
item,
|
|
2720
|
+
reviewIndex,
|
|
2721
|
+
allPhases,
|
|
2722
|
+
spawnOptions
|
|
2723
|
+
);
|
|
2871
2724
|
if (reviewResult < 0) return;
|
|
2872
2725
|
if (item.status !== "done") setStatus(id, "done");
|
|
2873
|
-
console.log(
|
|
2726
|
+
console.log(chalk32.green(`
|
|
2874
2727
|
All phases complete for #${id}: ${item.name}`));
|
|
2875
2728
|
}
|
|
2876
2729
|
|
|
2877
2730
|
// src/commands/backlog/next.ts
|
|
2878
|
-
async function next() {
|
|
2731
|
+
async function next(options2) {
|
|
2879
2732
|
const items = loadBacklog();
|
|
2880
2733
|
const inProgress = items.find((i) => i.status === "in-progress" && i.plan);
|
|
2881
2734
|
if (inProgress) {
|
|
2882
2735
|
console.log(
|
|
2883
|
-
|
|
2736
|
+
chalk33.bold(
|
|
2884
2737
|
`Resuming in-progress item #${inProgress.id}: ${inProgress.name}`
|
|
2885
2738
|
)
|
|
2886
2739
|
);
|
|
2887
|
-
await run2(String(inProgress.id));
|
|
2740
|
+
await run2(String(inProgress.id), options2);
|
|
2888
2741
|
return;
|
|
2889
2742
|
}
|
|
2890
2743
|
const todo = items.filter((i) => i.status === "todo");
|
|
2891
2744
|
if (todo.length === 0) {
|
|
2892
|
-
console.log(
|
|
2893
|
-
await spawnClaude("/draft");
|
|
2745
|
+
console.log(chalk33.dim("No incomplete backlog items. Opening /draft..."));
|
|
2746
|
+
await spawnClaude("/draft", options2);
|
|
2894
2747
|
return;
|
|
2895
2748
|
}
|
|
2896
2749
|
if (todo.length === 1) {
|
|
2897
2750
|
const only = todo[0];
|
|
2898
|
-
console.log(
|
|
2899
|
-
await run2(String(only.id));
|
|
2751
|
+
console.log(chalk33.bold(`Starting #${only.id}: ${only.name}`));
|
|
2752
|
+
await run2(String(only.id), options2);
|
|
2900
2753
|
return;
|
|
2901
2754
|
}
|
|
2902
2755
|
const choices = todo.map((i) => ({
|
|
2903
2756
|
name: `${typeLabel(i.type)} #${i.id}: ${i.name}`,
|
|
2904
2757
|
value: String(i.id)
|
|
2905
2758
|
}));
|
|
2906
|
-
const { selected } = await
|
|
2759
|
+
const { selected } = await enquirer5.prompt({
|
|
2907
2760
|
type: "select",
|
|
2908
2761
|
name: "selected",
|
|
2909
2762
|
message: "Choose a backlog item to start:",
|
|
2910
2763
|
choices: choices.map((c) => c.name)
|
|
2911
2764
|
});
|
|
2912
2765
|
const id = selected.match(/#(\d+)/)?.[1] ?? "";
|
|
2913
|
-
await run2(id);
|
|
2766
|
+
await run2(id, options2);
|
|
2914
2767
|
}
|
|
2915
2768
|
|
|
2916
2769
|
// src/commands/backlog/plan.ts
|
|
2917
|
-
import
|
|
2770
|
+
import chalk34 from "chalk";
|
|
2918
2771
|
function plan(id) {
|
|
2919
2772
|
const result = loadAndFindItem(id);
|
|
2920
2773
|
if (!result) return;
|
|
2921
2774
|
const { item } = result;
|
|
2922
2775
|
if (!item.plan || item.plan.length === 0) {
|
|
2923
|
-
console.log(
|
|
2776
|
+
console.log(chalk34.dim("No plan defined for this item."));
|
|
2924
2777
|
return;
|
|
2925
2778
|
}
|
|
2926
|
-
console.log(
|
|
2779
|
+
console.log(chalk34.bold(item.name));
|
|
2927
2780
|
console.log();
|
|
2928
2781
|
for (const [i, phase] of item.plan.entries()) {
|
|
2929
|
-
console.log(`${
|
|
2782
|
+
console.log(`${chalk34.bold(`Phase ${i + 1}:`)} ${phase.name}`);
|
|
2930
2783
|
for (const task of phase.tasks) {
|
|
2931
2784
|
console.log(` - ${task.task}`);
|
|
2932
2785
|
if (task.verify) {
|
|
2933
|
-
console.log(` ${
|
|
2786
|
+
console.log(` ${chalk34.dim(`verify: ${task.verify}`)}`);
|
|
2934
2787
|
}
|
|
2935
2788
|
}
|
|
2936
2789
|
console.log();
|
|
@@ -2938,23 +2791,23 @@ function plan(id) {
|
|
|
2938
2791
|
}
|
|
2939
2792
|
|
|
2940
2793
|
// src/commands/backlog/start/index.ts
|
|
2941
|
-
import
|
|
2794
|
+
import chalk35 from "chalk";
|
|
2942
2795
|
async function start(id) {
|
|
2943
2796
|
const name = setStatus(id, "in-progress");
|
|
2944
2797
|
if (name) {
|
|
2945
|
-
console.log(
|
|
2798
|
+
console.log(chalk35.green(`Started item #${id}: ${name}`));
|
|
2946
2799
|
}
|
|
2947
2800
|
}
|
|
2948
2801
|
|
|
2949
2802
|
// src/shared/web.ts
|
|
2950
2803
|
import { exec } from "child_process";
|
|
2951
|
-
import { readFileSync as
|
|
2804
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
2952
2805
|
import {
|
|
2953
2806
|
createServer
|
|
2954
2807
|
} from "http";
|
|
2955
|
-
import { dirname as dirname13, join as
|
|
2808
|
+
import { dirname as dirname13, join as join10 } from "path";
|
|
2956
2809
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2957
|
-
import
|
|
2810
|
+
import chalk36 from "chalk";
|
|
2958
2811
|
function respondJson(res, status2, data) {
|
|
2959
2812
|
res.writeHead(status2, { "Content-Type": "application/json" });
|
|
2960
2813
|
res.end(JSON.stringify(data));
|
|
@@ -2964,7 +2817,7 @@ function createBundleHandler(importMetaUrl, bundlePath) {
|
|
|
2964
2817
|
let cache;
|
|
2965
2818
|
return (_req, res) => {
|
|
2966
2819
|
if (!cache) {
|
|
2967
|
-
cache =
|
|
2820
|
+
cache = readFileSync11(join10(dir, bundlePath), "utf-8");
|
|
2968
2821
|
}
|
|
2969
2822
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
2970
2823
|
res.end(cache);
|
|
@@ -2998,8 +2851,8 @@ function startWebServer(label2, port, handler) {
|
|
|
2998
2851
|
handler(req, res, port);
|
|
2999
2852
|
});
|
|
3000
2853
|
server.listen(port, () => {
|
|
3001
|
-
console.log(
|
|
3002
|
-
console.log(
|
|
2854
|
+
console.log(chalk36.green(`${label2}: ${url}`));
|
|
2855
|
+
console.log(chalk36.dim("Press Ctrl+C to stop"));
|
|
3003
2856
|
exec(`open ${url}`);
|
|
3004
2857
|
});
|
|
3005
2858
|
}
|
|
@@ -3146,29 +2999,210 @@ async function web(options2) {
|
|
|
3146
2999
|
);
|
|
3147
3000
|
}
|
|
3148
3001
|
|
|
3149
|
-
// src/commands/
|
|
3002
|
+
// src/commands/backlog/add/index.ts
|
|
3003
|
+
import { existsSync as existsSync15 } from "fs";
|
|
3004
|
+
import chalk37 from "chalk";
|
|
3005
|
+
|
|
3006
|
+
// src/commands/backlog/add/shared.ts
|
|
3007
|
+
import { spawnSync } from "child_process";
|
|
3008
|
+
import { mkdtempSync, readFileSync as readFileSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "fs";
|
|
3009
|
+
import { tmpdir } from "os";
|
|
3010
|
+
import { join as join11 } from "path";
|
|
3011
|
+
import enquirer6 from "enquirer";
|
|
3012
|
+
async function promptType() {
|
|
3013
|
+
const { type } = await enquirer6.prompt({
|
|
3014
|
+
type: "select",
|
|
3015
|
+
name: "type",
|
|
3016
|
+
message: "Type:",
|
|
3017
|
+
choices: ["story", "bug"],
|
|
3018
|
+
initial: 0
|
|
3019
|
+
});
|
|
3020
|
+
return type;
|
|
3021
|
+
}
|
|
3022
|
+
async function promptName() {
|
|
3023
|
+
const { name } = await enquirer6.prompt({
|
|
3024
|
+
type: "input",
|
|
3025
|
+
name: "name",
|
|
3026
|
+
message: "Name:",
|
|
3027
|
+
validate: (value) => value.trim().length > 0 || "Name is required"
|
|
3028
|
+
});
|
|
3029
|
+
return name.trim();
|
|
3030
|
+
}
|
|
3031
|
+
async function promptDescription() {
|
|
3032
|
+
const { useEditor } = await enquirer6.prompt({
|
|
3033
|
+
type: "confirm",
|
|
3034
|
+
name: "useEditor",
|
|
3035
|
+
message: "Open editor for description?",
|
|
3036
|
+
initial: false
|
|
3037
|
+
});
|
|
3038
|
+
if (!useEditor) {
|
|
3039
|
+
const { description } = await enquirer6.prompt({
|
|
3040
|
+
type: "input",
|
|
3041
|
+
name: "description",
|
|
3042
|
+
message: "Description (optional):"
|
|
3043
|
+
});
|
|
3044
|
+
return description.trim() || void 0;
|
|
3045
|
+
}
|
|
3046
|
+
return openEditor();
|
|
3047
|
+
}
|
|
3048
|
+
function openEditor() {
|
|
3049
|
+
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
3050
|
+
const dir = mkdtempSync(join11(tmpdir(), "assist-"));
|
|
3051
|
+
const filePath = join11(dir, "description.md");
|
|
3052
|
+
writeFileSync13(filePath, "");
|
|
3053
|
+
const result = spawnSync(editor, [filePath], { stdio: "inherit" });
|
|
3054
|
+
if (result.status !== 0) {
|
|
3055
|
+
unlinkSync3(filePath);
|
|
3056
|
+
return void 0;
|
|
3057
|
+
}
|
|
3058
|
+
const content = readFileSync12(filePath, "utf-8").trim();
|
|
3059
|
+
unlinkSync3(filePath);
|
|
3060
|
+
return content || void 0;
|
|
3061
|
+
}
|
|
3062
|
+
async function promptAcceptanceCriteria() {
|
|
3063
|
+
const criteria = [];
|
|
3064
|
+
for (; ; ) {
|
|
3065
|
+
const { criterion } = await enquirer6.prompt({
|
|
3066
|
+
type: "input",
|
|
3067
|
+
name: "criterion",
|
|
3068
|
+
message: "Acceptance criterion (empty to finish):"
|
|
3069
|
+
});
|
|
3070
|
+
if (criterion.trim() === "") break;
|
|
3071
|
+
criteria.push(criterion.trim());
|
|
3072
|
+
}
|
|
3073
|
+
return criteria;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
// src/commands/backlog/add/index.ts
|
|
3077
|
+
var addItemSchema = backlogItemSchema.omit({ id: true, status: true });
|
|
3078
|
+
async function addFromJson() {
|
|
3079
|
+
if (process.stdin.isTTY) {
|
|
3080
|
+
console.log(chalk37.red("--json requires piped input on stdin."));
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
const input = await readStdin2();
|
|
3084
|
+
const sanitised = input.replace(
|
|
3085
|
+
/"(?:[^"\\]|\\.)*"/g,
|
|
3086
|
+
(match) => match.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")
|
|
3087
|
+
);
|
|
3088
|
+
const data = addItemSchema.parse(JSON.parse(sanitised));
|
|
3089
|
+
const items = loadBacklog();
|
|
3090
|
+
const id = getNextId(items);
|
|
3091
|
+
items.push({ ...data, id, status: "todo" });
|
|
3092
|
+
saveBacklog(items);
|
|
3093
|
+
console.log(chalk37.green(`Added item #${id}: ${data.name}`));
|
|
3094
|
+
}
|
|
3095
|
+
async function addInteractive() {
|
|
3096
|
+
const type = await promptType();
|
|
3097
|
+
const name = await promptName();
|
|
3098
|
+
const description = await promptDescription();
|
|
3099
|
+
const acceptanceCriteria2 = await promptAcceptanceCriteria();
|
|
3100
|
+
const items = loadBacklog();
|
|
3101
|
+
const id = getNextId(items);
|
|
3102
|
+
items.push({
|
|
3103
|
+
id,
|
|
3104
|
+
type,
|
|
3105
|
+
name,
|
|
3106
|
+
description,
|
|
3107
|
+
acceptanceCriteria: acceptanceCriteria2,
|
|
3108
|
+
status: "todo"
|
|
3109
|
+
});
|
|
3110
|
+
saveBacklog(items);
|
|
3111
|
+
console.log(chalk37.green(`Added item #${id}: ${name}`));
|
|
3112
|
+
}
|
|
3113
|
+
async function add(options2) {
|
|
3114
|
+
if (!existsSync15(getBacklogPath())) {
|
|
3115
|
+
console.log(
|
|
3116
|
+
chalk37.yellow(
|
|
3117
|
+
"No backlog found. Run 'assist backlog init' to create one."
|
|
3118
|
+
)
|
|
3119
|
+
);
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
if (options2.json) {
|
|
3123
|
+
await addFromJson();
|
|
3124
|
+
} else {
|
|
3125
|
+
await addInteractive();
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
// src/commands/backlog/init/index.ts
|
|
3130
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3131
|
+
import chalk38 from "chalk";
|
|
3132
|
+
async function init6() {
|
|
3133
|
+
const backlogPath = getBacklogPath();
|
|
3134
|
+
if (existsSync16(backlogPath)) {
|
|
3135
|
+
console.log(chalk38.yellow("assist.backlog.yml already exists."));
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
saveBacklog([]);
|
|
3139
|
+
console.log(chalk38.green("Created assist.backlog.yml"));
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// src/commands/backlog/list/index.ts
|
|
3143
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3144
|
+
import chalk39 from "chalk";
|
|
3145
|
+
function filterItems(items, options2) {
|
|
3146
|
+
if (options2.status) return items.filter((i) => i.status === options2.status);
|
|
3147
|
+
if (!options2.all) return items.filter((i) => i.status !== "done");
|
|
3148
|
+
return items;
|
|
3149
|
+
}
|
|
3150
|
+
async function list2(options2) {
|
|
3151
|
+
if (!existsSync17(getBacklogPath())) {
|
|
3152
|
+
console.log(
|
|
3153
|
+
chalk39.yellow(
|
|
3154
|
+
"No backlog found. Run 'assist backlog init' to create one."
|
|
3155
|
+
)
|
|
3156
|
+
);
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
const items = filterItems(loadBacklog(), options2);
|
|
3160
|
+
if (items.length === 0) {
|
|
3161
|
+
console.log(chalk39.dim("Backlog is empty."));
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
for (const item of items) {
|
|
3165
|
+
console.log(
|
|
3166
|
+
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk39.dim(`#${item.id}`)} ${item.name}${phaseLabel(item)}`
|
|
3167
|
+
);
|
|
3168
|
+
if (options2.verbose) {
|
|
3169
|
+
printVerboseDetails(item);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
// src/commands/backlog/registerItemCommands.ts
|
|
3150
3175
|
function registerItemCommands(cmd) {
|
|
3151
3176
|
cmd.command("init").description("Create an empty assist.backlog.yml").action(init6);
|
|
3152
3177
|
cmd.command("list").description("List all backlog items").option("--status <type>", "Filter by status (todo, in-progress, done)").option("-a, --all", "Include done items").option("-v, --verbose", "Show all item details").action(list2);
|
|
3153
3178
|
cmd.command("add").description("Add a new backlog item").option("--json", "Read item as JSON from stdin").action(add);
|
|
3154
3179
|
}
|
|
3180
|
+
|
|
3181
|
+
// src/commands/registerBacklog.ts
|
|
3155
3182
|
function registerStatusCommands(cmd) {
|
|
3156
3183
|
cmd.command("start <id>").description("Set a backlog item to in-progress").action(start);
|
|
3157
3184
|
cmd.command("done <id>").description("Set a backlog item to done").action(done);
|
|
3158
3185
|
cmd.command("delete <id>").alias("remove").description("Delete a backlog item").action(del);
|
|
3159
3186
|
cmd.command("web").description("Start a web view of the backlog").option("-p, --port <number>", "Port to listen on", "3000").action(web);
|
|
3160
3187
|
}
|
|
3161
|
-
function
|
|
3162
|
-
cmd.command("next").description("Pick and run the next backlog item, or open /draft if none").action(next);
|
|
3188
|
+
function registerPlanCommands(cmd) {
|
|
3163
3189
|
cmd.command("plan <id>").description("Display the plan for a backlog item").action(plan);
|
|
3164
3190
|
cmd.command("phase-done <id> <phase>").description("Signal that a plan phase is complete").action(phaseDone);
|
|
3165
|
-
|
|
3191
|
+
}
|
|
3192
|
+
function registerRunCommands(cmd) {
|
|
3193
|
+
cmd.command("next").description("Pick and run the next backlog item, or open /draft if none").option("--allow-edits", "Run Claude with acceptEdits permission mode").action(
|
|
3194
|
+
(opts) => next({ allowEdits: opts.allowEdits })
|
|
3195
|
+
);
|
|
3196
|
+
cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase with Claude").option("--allow-edits", "Run Claude with acceptEdits permission mode").action(
|
|
3197
|
+
(id, opts) => run2(id, { allowEdits: opts.allowEdits })
|
|
3198
|
+
);
|
|
3166
3199
|
}
|
|
3167
3200
|
function registerBacklog(program2) {
|
|
3168
3201
|
const cmd = program2.command("backlog").description("Manage a backlog of work items").action(() => web({ port: "3000" }));
|
|
3169
3202
|
registerItemCommands(cmd);
|
|
3170
3203
|
registerStatusCommands(cmd);
|
|
3171
|
-
|
|
3204
|
+
registerPlanCommands(cmd);
|
|
3205
|
+
registerRunCommands(cmd);
|
|
3172
3206
|
}
|
|
3173
3207
|
|
|
3174
3208
|
// src/shared/isApprovedRead.ts
|
|
@@ -3477,7 +3511,7 @@ import { homedir as homedir4 } from "os";
|
|
|
3477
3511
|
import { join as join13 } from "path";
|
|
3478
3512
|
|
|
3479
3513
|
// src/shared/getInstallDir.ts
|
|
3480
|
-
import { execSync as
|
|
3514
|
+
import { execSync as execSync14 } from "child_process";
|
|
3481
3515
|
import { dirname as dirname15, resolve as resolve4 } from "path";
|
|
3482
3516
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
3483
3517
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
@@ -3487,7 +3521,7 @@ function getInstallDir() {
|
|
|
3487
3521
|
}
|
|
3488
3522
|
function isGitRepo(dir) {
|
|
3489
3523
|
try {
|
|
3490
|
-
const result =
|
|
3524
|
+
const result = execSync14("git rev-parse --show-toplevel", {
|
|
3491
3525
|
cwd: dir,
|
|
3492
3526
|
stdio: "pipe"
|
|
3493
3527
|
}).toString().trim();
|
|
@@ -3498,7 +3532,7 @@ function isGitRepo(dir) {
|
|
|
3498
3532
|
}
|
|
3499
3533
|
|
|
3500
3534
|
// src/commands/permitCliReads/assertCliExists.ts
|
|
3501
|
-
import { execSync as
|
|
3535
|
+
import { execSync as execSync15 } from "child_process";
|
|
3502
3536
|
function assertCliExists(cli) {
|
|
3503
3537
|
const binary = cli.split(/\s+/)[0];
|
|
3504
3538
|
const opts = {
|
|
@@ -3506,10 +3540,10 @@ function assertCliExists(cli) {
|
|
|
3506
3540
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3507
3541
|
};
|
|
3508
3542
|
try {
|
|
3509
|
-
|
|
3543
|
+
execSync15(`command -v ${binary}`, opts);
|
|
3510
3544
|
} catch {
|
|
3511
3545
|
try {
|
|
3512
|
-
|
|
3546
|
+
execSync15(`where ${binary}`, opts);
|
|
3513
3547
|
} catch {
|
|
3514
3548
|
console.error(`CLI "${cli}" not found in PATH`);
|
|
3515
3549
|
process.exit(1);
|
|
@@ -4365,7 +4399,7 @@ function registerDeploy(program2) {
|
|
|
4365
4399
|
}
|
|
4366
4400
|
|
|
4367
4401
|
// src/commands/devlog/list/index.ts
|
|
4368
|
-
import { execSync as
|
|
4402
|
+
import { execSync as execSync17 } from "child_process";
|
|
4369
4403
|
import { basename as basename3 } from "path";
|
|
4370
4404
|
|
|
4371
4405
|
// src/commands/devlog/loadBlogSkipDays.ts
|
|
@@ -4380,7 +4414,7 @@ function loadBlogSkipDays(repoName) {
|
|
|
4380
4414
|
}
|
|
4381
4415
|
|
|
4382
4416
|
// src/commands/devlog/shared.ts
|
|
4383
|
-
import { execSync as
|
|
4417
|
+
import { execSync as execSync16 } from "child_process";
|
|
4384
4418
|
import chalk48 from "chalk";
|
|
4385
4419
|
|
|
4386
4420
|
// src/commands/devlog/loadDevlogEntries.ts
|
|
@@ -4450,7 +4484,7 @@ function loadAllDevlogLatestDates() {
|
|
|
4450
4484
|
// src/commands/devlog/shared.ts
|
|
4451
4485
|
function getCommitFiles(hash) {
|
|
4452
4486
|
try {
|
|
4453
|
-
const output =
|
|
4487
|
+
const output = execSync16(`git show --name-only --format="" ${hash}`, {
|
|
4454
4488
|
encoding: "utf-8"
|
|
4455
4489
|
});
|
|
4456
4490
|
return output.trim().split("\n").filter(Boolean);
|
|
@@ -4521,7 +4555,7 @@ function list3(options2) {
|
|
|
4521
4555
|
const devlogEntries = loadDevlogEntries(repoName);
|
|
4522
4556
|
const reverseFlag = options2.reverse ? "--reverse " : "";
|
|
4523
4557
|
const limitFlag = options2.reverse ? "" : "-n 500 ";
|
|
4524
|
-
const output =
|
|
4558
|
+
const output = execSync17(
|
|
4525
4559
|
`git log ${reverseFlag}${limitFlag}--pretty=format:'%ad|%h|%s' --date=short`,
|
|
4526
4560
|
{ encoding: "utf-8" }
|
|
4527
4561
|
);
|
|
@@ -4547,11 +4581,11 @@ function list3(options2) {
|
|
|
4547
4581
|
}
|
|
4548
4582
|
|
|
4549
4583
|
// src/commands/devlog/getLastVersionInfo.ts
|
|
4550
|
-
import { execSync as
|
|
4584
|
+
import { execSync as execSync18 } from "child_process";
|
|
4551
4585
|
import semver from "semver";
|
|
4552
4586
|
function getVersionAtCommit(hash) {
|
|
4553
4587
|
try {
|
|
4554
|
-
const content =
|
|
4588
|
+
const content = execSync18(`git show ${hash}:package.json`, {
|
|
4555
4589
|
encoding: "utf-8"
|
|
4556
4590
|
});
|
|
4557
4591
|
const pkg = JSON.parse(content);
|
|
@@ -4566,7 +4600,7 @@ function stripToMinor(version2) {
|
|
|
4566
4600
|
}
|
|
4567
4601
|
function getLastVersionInfoFromGit() {
|
|
4568
4602
|
try {
|
|
4569
|
-
const output =
|
|
4603
|
+
const output = execSync18(
|
|
4570
4604
|
"git log -1 --pretty=format:'%ad|%h' --date=short",
|
|
4571
4605
|
{
|
|
4572
4606
|
encoding: "utf-8"
|
|
@@ -4609,7 +4643,7 @@ function bumpVersion(version2, type) {
|
|
|
4609
4643
|
}
|
|
4610
4644
|
|
|
4611
4645
|
// src/commands/devlog/next/displayNextEntry/index.ts
|
|
4612
|
-
import { execSync as
|
|
4646
|
+
import { execSync as execSync19 } from "child_process";
|
|
4613
4647
|
import chalk51 from "chalk";
|
|
4614
4648
|
|
|
4615
4649
|
// src/commands/devlog/next/displayNextEntry/displayVersion.ts
|
|
@@ -4643,7 +4677,7 @@ function findTargetDate(commitsByDate, skipDays) {
|
|
|
4643
4677
|
return Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort()[0];
|
|
4644
4678
|
}
|
|
4645
4679
|
function fetchCommitsByDate(ignore2, lastDate) {
|
|
4646
|
-
const output =
|
|
4680
|
+
const output = execSync19(
|
|
4647
4681
|
"git log --pretty=format:'%ad|%h|%s' --date=short -n 500",
|
|
4648
4682
|
{ encoding: "utf-8" }
|
|
4649
4683
|
);
|
|
@@ -4721,7 +4755,7 @@ function next2(options2) {
|
|
|
4721
4755
|
}
|
|
4722
4756
|
|
|
4723
4757
|
// src/commands/devlog/repos/index.ts
|
|
4724
|
-
import { execSync as
|
|
4758
|
+
import { execSync as execSync20 } from "child_process";
|
|
4725
4759
|
|
|
4726
4760
|
// src/commands/devlog/repos/printReposTable.ts
|
|
4727
4761
|
import chalk52 from "chalk";
|
|
@@ -4756,7 +4790,7 @@ function getStatus(lastPush, lastDevlog) {
|
|
|
4756
4790
|
return lastDevlog < lastPush ? "outdated" : "ok";
|
|
4757
4791
|
}
|
|
4758
4792
|
function fetchRepos(days, all) {
|
|
4759
|
-
const json =
|
|
4793
|
+
const json = execSync20(
|
|
4760
4794
|
"gh repo list staff0rd --json name,pushedAt,isArchived --limit 200",
|
|
4761
4795
|
{ encoding: "utf-8" }
|
|
4762
4796
|
);
|
|
@@ -5116,7 +5150,7 @@ async function deps(csprojPath, options2) {
|
|
|
5116
5150
|
}
|
|
5117
5151
|
|
|
5118
5152
|
// src/commands/dotnet/getChangedCsFiles.ts
|
|
5119
|
-
import { execSync as
|
|
5153
|
+
import { execSync as execSync21 } from "child_process";
|
|
5120
5154
|
var SCOPE_ALL = "all";
|
|
5121
5155
|
var SCOPE_BASE = "base:";
|
|
5122
5156
|
var SCOPE_COMMIT = "commit:";
|
|
@@ -5140,7 +5174,7 @@ function getChangedCsFiles(scope) {
|
|
|
5140
5174
|
} else {
|
|
5141
5175
|
cmd = "git diff --name-only HEAD";
|
|
5142
5176
|
}
|
|
5143
|
-
const output =
|
|
5177
|
+
const output = execSync21(cmd, { encoding: "utf-8" }).trim();
|
|
5144
5178
|
if (output === "") return [];
|
|
5145
5179
|
return output.split("\n").filter((f) => f.toLowerCase().endsWith(".cs"));
|
|
5146
5180
|
}
|
|
@@ -5338,14 +5372,14 @@ function parseInspectReport(json) {
|
|
|
5338
5372
|
}
|
|
5339
5373
|
|
|
5340
5374
|
// src/commands/dotnet/runInspectCode.ts
|
|
5341
|
-
import { execSync as
|
|
5375
|
+
import { execSync as execSync22 } from "child_process";
|
|
5342
5376
|
import { existsSync as existsSync25, readFileSync as readFileSync20, unlinkSync as unlinkSync4 } from "fs";
|
|
5343
5377
|
import { tmpdir as tmpdir2 } from "os";
|
|
5344
5378
|
import path26 from "path";
|
|
5345
5379
|
import chalk62 from "chalk";
|
|
5346
5380
|
function assertJbInstalled() {
|
|
5347
5381
|
try {
|
|
5348
|
-
|
|
5382
|
+
execSync22("jb inspectcode --version", { stdio: "pipe" });
|
|
5349
5383
|
} catch {
|
|
5350
5384
|
console.error(chalk62.red("jb is not installed. Install with:"));
|
|
5351
5385
|
console.error(
|
|
@@ -5359,7 +5393,7 @@ function runInspectCode(slnPath, include, swea) {
|
|
|
5359
5393
|
const includeFlag = include ? ` --include="${include}"` : "";
|
|
5360
5394
|
const sweaFlag = swea ? " --swea" : "";
|
|
5361
5395
|
try {
|
|
5362
|
-
|
|
5396
|
+
execSync22(
|
|
5363
5397
|
`jb inspectcode "${slnPath}" -o="${reportPath}"${includeFlag}${sweaFlag} --verbosity=OFF`,
|
|
5364
5398
|
{ stdio: "pipe" }
|
|
5365
5399
|
);
|
|
@@ -5380,7 +5414,7 @@ function runInspectCode(slnPath, include, swea) {
|
|
|
5380
5414
|
}
|
|
5381
5415
|
|
|
5382
5416
|
// src/commands/dotnet/runRoslynInspect.ts
|
|
5383
|
-
import { execSync as
|
|
5417
|
+
import { execSync as execSync23 } from "child_process";
|
|
5384
5418
|
import chalk63 from "chalk";
|
|
5385
5419
|
function resolveMsbuildPath() {
|
|
5386
5420
|
const config = loadConfig();
|
|
@@ -5390,7 +5424,7 @@ function resolveMsbuildPath() {
|
|
|
5390
5424
|
function assertMsbuildInstalled() {
|
|
5391
5425
|
const msbuild = resolveMsbuildPath();
|
|
5392
5426
|
try {
|
|
5393
|
-
|
|
5427
|
+
execSync23(`"${msbuild}" -version`, { stdio: "pipe" });
|
|
5394
5428
|
} catch {
|
|
5395
5429
|
console.error(chalk63.red(`msbuild not found at: ${msbuild}`));
|
|
5396
5430
|
console.error(
|
|
@@ -5416,7 +5450,7 @@ function runRoslynInspect(slnPath) {
|
|
|
5416
5450
|
const msbuild = resolveMsbuildPath();
|
|
5417
5451
|
let output;
|
|
5418
5452
|
try {
|
|
5419
|
-
output =
|
|
5453
|
+
output = execSync23(
|
|
5420
5454
|
`"${msbuild}" "${slnPath}" -t:Build -v:minimal -maxcpucount -p:EnforceCodeStyleInBuild=true -p:RunAnalyzersDuringBuild=true 2>&1`,
|
|
5421
5455
|
{ encoding: "utf-8", stdio: "pipe", maxBuffer: 50 * 1024 * 1024 }
|
|
5422
5456
|
);
|
|
@@ -5548,12 +5582,12 @@ function adfToText(doc) {
|
|
|
5548
5582
|
}
|
|
5549
5583
|
|
|
5550
5584
|
// src/commands/jira/fetchIssue.ts
|
|
5551
|
-
import { execSync as
|
|
5585
|
+
import { execSync as execSync24 } from "child_process";
|
|
5552
5586
|
import chalk65 from "chalk";
|
|
5553
5587
|
function fetchIssue(issueKey, fields) {
|
|
5554
5588
|
let result;
|
|
5555
5589
|
try {
|
|
5556
|
-
result =
|
|
5590
|
+
result = execSync24(
|
|
5557
5591
|
`acli jira workitem view ${issueKey} -f ${fields} --json`,
|
|
5558
5592
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
5559
5593
|
);
|
|
@@ -5599,7 +5633,7 @@ function acceptanceCriteria(issueKey) {
|
|
|
5599
5633
|
}
|
|
5600
5634
|
|
|
5601
5635
|
// src/commands/jira/jiraAuth.ts
|
|
5602
|
-
import { execSync as
|
|
5636
|
+
import { execSync as execSync25 } from "child_process";
|
|
5603
5637
|
|
|
5604
5638
|
// src/shared/loadJson.ts
|
|
5605
5639
|
import { existsSync as existsSync26, mkdirSync as mkdirSync5, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "fs";
|
|
@@ -5663,7 +5697,7 @@ async function jiraAuth() {
|
|
|
5663
5697
|
console.error("All fields are required.");
|
|
5664
5698
|
process.exit(1);
|
|
5665
5699
|
}
|
|
5666
|
-
|
|
5700
|
+
execSync25(`acli jira auth login --site ${site} --email "${email}" --token`, {
|
|
5667
5701
|
encoding: "utf-8",
|
|
5668
5702
|
input: token,
|
|
5669
5703
|
stdio: ["pipe", "inherit", "inherit"]
|
|
@@ -5944,7 +5978,7 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
5944
5978
|
import { join as join20 } from "path";
|
|
5945
5979
|
|
|
5946
5980
|
// src/commands/prs/shared.ts
|
|
5947
|
-
import { execSync as
|
|
5981
|
+
import { execSync as execSync26 } from "child_process";
|
|
5948
5982
|
function isGhNotInstalled(error) {
|
|
5949
5983
|
if (error instanceof Error) {
|
|
5950
5984
|
const msg = error.message.toLowerCase();
|
|
@@ -5960,14 +5994,14 @@ function isNotFound(error) {
|
|
|
5960
5994
|
}
|
|
5961
5995
|
function getRepoInfo() {
|
|
5962
5996
|
const repoInfo = JSON.parse(
|
|
5963
|
-
|
|
5997
|
+
execSync26("gh repo view --json owner,name", { encoding: "utf-8" })
|
|
5964
5998
|
);
|
|
5965
5999
|
return { org: repoInfo.owner.login, repo: repoInfo.name };
|
|
5966
6000
|
}
|
|
5967
6001
|
function getCurrentPrNumber() {
|
|
5968
6002
|
try {
|
|
5969
6003
|
const prInfo = JSON.parse(
|
|
5970
|
-
|
|
6004
|
+
execSync26("gh pr view --json number", { encoding: "utf-8" })
|
|
5971
6005
|
);
|
|
5972
6006
|
return prInfo.number;
|
|
5973
6007
|
} catch (error) {
|
|
@@ -5981,7 +6015,7 @@ function getCurrentPrNumber() {
|
|
|
5981
6015
|
function getCurrentPrNodeId() {
|
|
5982
6016
|
try {
|
|
5983
6017
|
const prInfo = JSON.parse(
|
|
5984
|
-
|
|
6018
|
+
execSync26("gh pr view --json id", { encoding: "utf-8" })
|
|
5985
6019
|
);
|
|
5986
6020
|
return prInfo.id;
|
|
5987
6021
|
} catch (error) {
|
|
@@ -6052,10 +6086,10 @@ function comment(path49, line, body) {
|
|
|
6052
6086
|
}
|
|
6053
6087
|
|
|
6054
6088
|
// src/commands/prs/fixed.ts
|
|
6055
|
-
import { execSync as
|
|
6089
|
+
import { execSync as execSync28 } from "child_process";
|
|
6056
6090
|
|
|
6057
6091
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
6058
|
-
import { execSync as
|
|
6092
|
+
import { execSync as execSync27 } from "child_process";
|
|
6059
6093
|
import { unlinkSync as unlinkSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
6060
6094
|
import { tmpdir as tmpdir4 } from "os";
|
|
6061
6095
|
import { join as join22 } from "path";
|
|
@@ -6085,7 +6119,7 @@ function deleteCommentsCache(prNumber) {
|
|
|
6085
6119
|
|
|
6086
6120
|
// src/commands/prs/resolveCommentWithReply.ts
|
|
6087
6121
|
function replyToComment(org, repo, prNumber, commentId, message) {
|
|
6088
|
-
|
|
6122
|
+
execSync27(
|
|
6089
6123
|
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
|
|
6090
6124
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
6091
6125
|
);
|
|
@@ -6095,7 +6129,7 @@ function resolveThread(threadId) {
|
|
|
6095
6129
|
const queryFile = join22(tmpdir4(), `gh-mutation-${Date.now()}.graphql`);
|
|
6096
6130
|
writeFileSync20(queryFile, mutation);
|
|
6097
6131
|
try {
|
|
6098
|
-
|
|
6132
|
+
execSync27(
|
|
6099
6133
|
`gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
|
|
6100
6134
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
6101
6135
|
);
|
|
@@ -6147,7 +6181,7 @@ function resolveCommentWithReply(commentId, message) {
|
|
|
6147
6181
|
// src/commands/prs/fixed.ts
|
|
6148
6182
|
function verifySha(sha) {
|
|
6149
6183
|
try {
|
|
6150
|
-
return
|
|
6184
|
+
return execSync28(`git rev-parse --verify ${sha}`, {
|
|
6151
6185
|
encoding: "utf-8"
|
|
6152
6186
|
}).trim();
|
|
6153
6187
|
} catch {
|
|
@@ -6161,7 +6195,7 @@ function fixed(commentId, sha) {
|
|
|
6161
6195
|
const { org, repo } = getRepoInfo();
|
|
6162
6196
|
const repoUrl = `https://github.com/${org}/${repo}`;
|
|
6163
6197
|
const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
|
|
6164
|
-
|
|
6198
|
+
execSync28("git push", { stdio: "inherit" });
|
|
6165
6199
|
resolveCommentWithReply(commentId, message);
|
|
6166
6200
|
} catch (error) {
|
|
6167
6201
|
if (isGhNotInstalled(error)) {
|
|
@@ -6179,7 +6213,7 @@ import { join as join24 } from "path";
|
|
|
6179
6213
|
import { stringify } from "yaml";
|
|
6180
6214
|
|
|
6181
6215
|
// src/commands/prs/fetchThreadIds.ts
|
|
6182
|
-
import { execSync as
|
|
6216
|
+
import { execSync as execSync29 } from "child_process";
|
|
6183
6217
|
import { unlinkSync as unlinkSync8, writeFileSync as writeFileSync21 } from "fs";
|
|
6184
6218
|
import { tmpdir as tmpdir5 } from "os";
|
|
6185
6219
|
import { join as join23 } from "path";
|
|
@@ -6188,7 +6222,7 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
6188
6222
|
const queryFile = join23(tmpdir5(), `gh-query-${Date.now()}.graphql`);
|
|
6189
6223
|
writeFileSync21(queryFile, THREAD_QUERY);
|
|
6190
6224
|
try {
|
|
6191
|
-
const result =
|
|
6225
|
+
const result = execSync29(
|
|
6192
6226
|
`gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
|
|
6193
6227
|
{ encoding: "utf-8" }
|
|
6194
6228
|
);
|
|
@@ -6210,9 +6244,9 @@ function fetchThreadIds(org, repo, prNumber) {
|
|
|
6210
6244
|
}
|
|
6211
6245
|
|
|
6212
6246
|
// src/commands/prs/listComments/fetchReviewComments.ts
|
|
6213
|
-
import { execSync as
|
|
6247
|
+
import { execSync as execSync30 } from "child_process";
|
|
6214
6248
|
function fetchJson(endpoint) {
|
|
6215
|
-
const result =
|
|
6249
|
+
const result = execSync30(`gh api --paginate ${endpoint}`, {
|
|
6216
6250
|
encoding: "utf-8"
|
|
6217
6251
|
});
|
|
6218
6252
|
if (!result.trim()) return [];
|
|
@@ -6351,7 +6385,7 @@ async function listComments() {
|
|
|
6351
6385
|
}
|
|
6352
6386
|
|
|
6353
6387
|
// src/commands/prs/prs/index.ts
|
|
6354
|
-
import { execSync as
|
|
6388
|
+
import { execSync as execSync31 } from "child_process";
|
|
6355
6389
|
|
|
6356
6390
|
// src/commands/prs/prs/displayPaginated/index.ts
|
|
6357
6391
|
import enquirer8 from "enquirer";
|
|
@@ -6457,7 +6491,7 @@ async function displayPaginated(pullRequests) {
|
|
|
6457
6491
|
async function prs(options2) {
|
|
6458
6492
|
const state = options2.open ? "open" : options2.closed ? "closed" : "all";
|
|
6459
6493
|
try {
|
|
6460
|
-
const result =
|
|
6494
|
+
const result = execSync31(
|
|
6461
6495
|
`gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100`,
|
|
6462
6496
|
{ encoding: "utf-8" }
|
|
6463
6497
|
);
|
|
@@ -6480,7 +6514,7 @@ async function prs(options2) {
|
|
|
6480
6514
|
}
|
|
6481
6515
|
|
|
6482
6516
|
// src/commands/prs/wontfix.ts
|
|
6483
|
-
import { execSync as
|
|
6517
|
+
import { execSync as execSync32 } from "child_process";
|
|
6484
6518
|
function validateReason(reason) {
|
|
6485
6519
|
const lowerReason = reason.toLowerCase();
|
|
6486
6520
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -6497,7 +6531,7 @@ function validateShaReferences(reason) {
|
|
|
6497
6531
|
const invalidShas = [];
|
|
6498
6532
|
for (const sha of shas) {
|
|
6499
6533
|
try {
|
|
6500
|
-
|
|
6534
|
+
execSync32(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
6501
6535
|
} catch {
|
|
6502
6536
|
invalidShas.push(sha);
|
|
6503
6537
|
}
|
|
@@ -6611,10 +6645,10 @@ import chalk74 from "chalk";
|
|
|
6611
6645
|
import Enquirer2 from "enquirer";
|
|
6612
6646
|
|
|
6613
6647
|
// src/commands/ravendb/searchItems.ts
|
|
6614
|
-
import { execSync as
|
|
6648
|
+
import { execSync as execSync33 } from "child_process";
|
|
6615
6649
|
import chalk73 from "chalk";
|
|
6616
6650
|
function opExec(args) {
|
|
6617
|
-
return
|
|
6651
|
+
return execSync33(`op ${args}`, {
|
|
6618
6652
|
encoding: "utf-8",
|
|
6619
6653
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6620
6654
|
}).trim();
|
|
@@ -6766,7 +6800,7 @@ ${errorText}`
|
|
|
6766
6800
|
}
|
|
6767
6801
|
|
|
6768
6802
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
6769
|
-
import { execSync as
|
|
6803
|
+
import { execSync as execSync34 } from "child_process";
|
|
6770
6804
|
import chalk78 from "chalk";
|
|
6771
6805
|
function resolveOpSecret(reference) {
|
|
6772
6806
|
if (!reference.startsWith("op://")) {
|
|
@@ -6774,7 +6808,7 @@ function resolveOpSecret(reference) {
|
|
|
6774
6808
|
process.exit(1);
|
|
6775
6809
|
}
|
|
6776
6810
|
try {
|
|
6777
|
-
return
|
|
6811
|
+
return execSync34(`op read "${reference}"`, {
|
|
6778
6812
|
encoding: "utf-8",
|
|
6779
6813
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6780
6814
|
}).trim();
|
|
@@ -7023,7 +7057,7 @@ Refactor check failed:
|
|
|
7023
7057
|
}
|
|
7024
7058
|
|
|
7025
7059
|
// src/commands/refactor/check/getViolations/index.ts
|
|
7026
|
-
import { execSync as
|
|
7060
|
+
import { execSync as execSync35 } from "child_process";
|
|
7027
7061
|
import fs16 from "fs";
|
|
7028
7062
|
import { minimatch as minimatch4 } from "minimatch";
|
|
7029
7063
|
|
|
@@ -7073,7 +7107,7 @@ function getGitFiles(options2) {
|
|
|
7073
7107
|
}
|
|
7074
7108
|
const files = /* @__PURE__ */ new Set();
|
|
7075
7109
|
if (options2.staged || options2.modified) {
|
|
7076
|
-
const staged =
|
|
7110
|
+
const staged = execSync35("git diff --cached --name-only", {
|
|
7077
7111
|
encoding: "utf-8"
|
|
7078
7112
|
});
|
|
7079
7113
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -7081,7 +7115,7 @@ function getGitFiles(options2) {
|
|
|
7081
7115
|
}
|
|
7082
7116
|
}
|
|
7083
7117
|
if (options2.unstaged || options2.modified) {
|
|
7084
|
-
const unstaged =
|
|
7118
|
+
const unstaged = execSync35("git diff --name-only", { encoding: "utf-8" });
|
|
7085
7119
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
7086
7120
|
files.add(file);
|
|
7087
7121
|
}
|
|
@@ -9376,7 +9410,7 @@ import { mkdirSync as mkdirSync10 } from "fs";
|
|
|
9376
9410
|
import { join as join34 } from "path";
|
|
9377
9411
|
|
|
9378
9412
|
// src/commands/voice/checkLockFile.ts
|
|
9379
|
-
import { execSync as
|
|
9413
|
+
import { execSync as execSync36 } from "child_process";
|
|
9380
9414
|
import { existsSync as existsSync35, mkdirSync as mkdirSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync24 } from "fs";
|
|
9381
9415
|
import { join as join33 } from "path";
|
|
9382
9416
|
function isProcessAlive(pid) {
|
|
@@ -9405,7 +9439,7 @@ function bootstrapVenv() {
|
|
|
9405
9439
|
if (existsSync35(getVenvPython())) return;
|
|
9406
9440
|
console.log("Setting up Python environment...");
|
|
9407
9441
|
const pythonDir = getPythonDir();
|
|
9408
|
-
|
|
9442
|
+
execSync36(
|
|
9409
9443
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
9410
9444
|
{
|
|
9411
9445
|
stdio: "inherit",
|
|
@@ -9572,11 +9606,11 @@ import { randomBytes } from "crypto";
|
|
|
9572
9606
|
import chalk101 from "chalk";
|
|
9573
9607
|
|
|
9574
9608
|
// src/lib/openBrowser.ts
|
|
9575
|
-
import { execSync as
|
|
9609
|
+
import { execSync as execSync37 } from "child_process";
|
|
9576
9610
|
function tryExec(commands) {
|
|
9577
9611
|
for (const cmd of commands) {
|
|
9578
9612
|
try {
|
|
9579
|
-
|
|
9613
|
+
execSync37(cmd);
|
|
9580
9614
|
return true;
|
|
9581
9615
|
} catch {
|
|
9582
9616
|
}
|
|
@@ -9802,7 +9836,7 @@ function registerRoam(program2) {
|
|
|
9802
9836
|
}
|
|
9803
9837
|
|
|
9804
9838
|
// src/commands/run/index.ts
|
|
9805
|
-
import { execSync as
|
|
9839
|
+
import { execSync as execSync38 } from "child_process";
|
|
9806
9840
|
|
|
9807
9841
|
// src/commands/run/resolveParams.ts
|
|
9808
9842
|
function resolveParams(params, cliArgs) {
|
|
@@ -9965,7 +9999,7 @@ function listRunConfigs() {
|
|
|
9965
9999
|
function runPreCommands(pre) {
|
|
9966
10000
|
for (const cmd of pre) {
|
|
9967
10001
|
try {
|
|
9968
|
-
|
|
10002
|
+
execSync38(cmd, { stdio: "inherit" });
|
|
9969
10003
|
} catch (err) {
|
|
9970
10004
|
const code = err && typeof err === "object" && "status" in err ? err.status : 1;
|
|
9971
10005
|
process.exit(code);
|
|
@@ -9983,7 +10017,7 @@ function run3(name, args) {
|
|
|
9983
10017
|
}
|
|
9984
10018
|
|
|
9985
10019
|
// src/commands/screenshot/index.ts
|
|
9986
|
-
import { execSync as
|
|
10020
|
+
import { execSync as execSync39 } from "child_process";
|
|
9987
10021
|
import { existsSync as existsSync38, mkdirSync as mkdirSync13, unlinkSync as unlinkSync10, writeFileSync as writeFileSync27 } from "fs";
|
|
9988
10022
|
import { tmpdir as tmpdir6 } from "os";
|
|
9989
10023
|
import { join as join38, resolve as resolve5 } from "path";
|
|
@@ -10126,7 +10160,7 @@ function runPowerShellScript(processName, outputPath) {
|
|
|
10126
10160
|
const scriptPath = join38(tmpdir6(), `assist-screenshot-${Date.now()}.ps1`);
|
|
10127
10161
|
writeFileSync27(scriptPath, captureWindowPs1, "utf-8");
|
|
10128
10162
|
try {
|
|
10129
|
-
|
|
10163
|
+
execSync39(
|
|
10130
10164
|
`powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
|
|
10131
10165
|
{ stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }
|
|
10132
10166
|
);
|
|
@@ -10325,7 +10359,7 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
10325
10359
|
}
|
|
10326
10360
|
|
|
10327
10361
|
// src/commands/update.ts
|
|
10328
|
-
import { execSync as
|
|
10362
|
+
import { execSync as execSync40 } from "child_process";
|
|
10329
10363
|
import * as path48 from "path";
|
|
10330
10364
|
function isGlobalNpmInstall(dir) {
|
|
10331
10365
|
try {
|
|
@@ -10333,7 +10367,7 @@ function isGlobalNpmInstall(dir) {
|
|
|
10333
10367
|
if (resolved.split(path48.sep).includes("node_modules")) {
|
|
10334
10368
|
return true;
|
|
10335
10369
|
}
|
|
10336
|
-
const globalPrefix =
|
|
10370
|
+
const globalPrefix = execSync40("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
10337
10371
|
return resolved.toLowerCase().startsWith(path48.resolve(globalPrefix).toLowerCase());
|
|
10338
10372
|
} catch {
|
|
10339
10373
|
return false;
|
|
@@ -10344,18 +10378,18 @@ async function update() {
|
|
|
10344
10378
|
console.log(`Assist is installed at: ${installDir}`);
|
|
10345
10379
|
if (isGitRepo(installDir)) {
|
|
10346
10380
|
console.log("Detected git repo installation, pulling latest...");
|
|
10347
|
-
|
|
10381
|
+
execSync40("git pull", { cwd: installDir, stdio: "inherit" });
|
|
10348
10382
|
console.log("Installing dependencies...");
|
|
10349
|
-
|
|
10383
|
+
execSync40("npm i", { cwd: installDir, stdio: "inherit" });
|
|
10350
10384
|
console.log("Building...");
|
|
10351
|
-
|
|
10385
|
+
execSync40("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
10352
10386
|
console.log("Syncing commands...");
|
|
10353
|
-
|
|
10387
|
+
execSync40("assist sync", { stdio: "inherit" });
|
|
10354
10388
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
10355
10389
|
console.log("Detected global npm installation, updating...");
|
|
10356
|
-
|
|
10390
|
+
execSync40("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
10357
10391
|
console.log("Syncing commands...");
|
|
10358
|
-
|
|
10392
|
+
execSync40("assist sync", { stdio: "inherit" });
|
|
10359
10393
|
} else {
|
|
10360
10394
|
console.error(
|
|
10361
10395
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -10392,6 +10426,7 @@ program.command("notify").description(
|
|
|
10392
10426
|
"Show notification from Claude Code hook (reads JSON from stdin)"
|
|
10393
10427
|
).action(notify);
|
|
10394
10428
|
program.command("update").description("Update assist to the latest version and sync commands").action(update);
|
|
10429
|
+
program.command("coverage").description("Print global statement coverage percentage").action(coverage);
|
|
10395
10430
|
program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
|
|
10396
10431
|
registerCliHook(program);
|
|
10397
10432
|
registerJira(program);
|