@staff0rd/assist 0.148.0 → 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.
Files changed (2) hide show
  1. package/dist/index.js +266 -247
  2. 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.148.0",
9
+ version: "0.149.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -2286,8 +2286,7 @@ async function notify() {
2286
2286
  console.log(`Notification sent: ${notification_type} for ${projectName}`);
2287
2287
  }
2288
2288
 
2289
- // src/commands/backlog/add/index.ts
2290
- import { existsSync as existsSync13 } from "fs";
2289
+ // src/commands/backlog/delete/index.ts
2291
2290
  import chalk26 from "chalk";
2292
2291
 
2293
2292
  // src/commands/backlog/shared.ts
@@ -2394,196 +2393,59 @@ function readStdin2() {
2394
2393
  });
2395
2394
  }
2396
2395
 
2397
- // src/commands/backlog/add/shared.ts
2398
- import { spawnSync } from "child_process";
2399
- import { mkdtempSync, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
2400
- import { tmpdir } from "os";
2401
- import { join as join9 } from "path";
2402
- import enquirer4 from "enquirer";
2403
- async function promptType() {
2404
- const { type } = await enquirer4.prompt({
2405
- type: "select",
2406
- name: "type",
2407
- message: "Type:",
2408
- choices: ["story", "bug"],
2409
- initial: 0
2410
- });
2411
- return type;
2412
- }
2413
- async function promptName() {
2414
- const { name } = await enquirer4.prompt({
2415
- type: "input",
2416
- name: "name",
2417
- message: "Name:",
2418
- validate: (value) => value.trim().length > 0 || "Name is required"
2419
- });
2420
- return name.trim();
2421
- }
2422
- async function promptDescription() {
2423
- const { useEditor } = await enquirer4.prompt({
2424
- type: "confirm",
2425
- name: "useEditor",
2426
- message: "Open editor for description?",
2427
- initial: false
2428
- });
2429
- if (!useEditor) {
2430
- const { description } = await enquirer4.prompt({
2431
- type: "input",
2432
- name: "description",
2433
- message: "Description (optional):"
2434
- });
2435
- return description.trim() || void 0;
2436
- }
2437
- return openEditor();
2438
- }
2439
- function openEditor() {
2440
- const editor = process.env.EDITOR || process.env.VISUAL || "vi";
2441
- const dir = mkdtempSync(join9(tmpdir(), "assist-"));
2442
- const filePath = join9(dir, "description.md");
2443
- writeFileSync12(filePath, "");
2444
- const result = spawnSync(editor, [filePath], { stdio: "inherit" });
2445
- if (result.status !== 0) {
2446
- unlinkSync2(filePath);
2447
- return void 0;
2448
- }
2449
- const content = readFileSync11(filePath, "utf-8").trim();
2450
- unlinkSync2(filePath);
2451
- return content || void 0;
2452
- }
2453
- async function promptAcceptanceCriteria() {
2454
- const criteria = [];
2455
- for (; ; ) {
2456
- const { criterion } = await enquirer4.prompt({
2457
- type: "input",
2458
- name: "criterion",
2459
- message: "Acceptance criterion (empty to finish):"
2460
- });
2461
- if (criterion.trim() === "") break;
2462
- criteria.push(criterion.trim());
2463
- }
2464
- return criteria;
2465
- }
2466
-
2467
- // src/commands/backlog/add/index.ts
2468
- var addItemSchema = backlogItemSchema.omit({ id: true, status: true });
2469
- async function addFromJson() {
2470
- if (process.stdin.isTTY) {
2471
- console.log(chalk26.red("--json requires piped input on stdin."));
2472
- return;
2473
- }
2474
- const input = await readStdin2();
2475
- const sanitised = input.replace(
2476
- /"(?:[^"\\]|\\.)*"/g,
2477
- (match) => match.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")
2478
- );
2479
- const data = addItemSchema.parse(JSON.parse(sanitised));
2480
- const items = loadBacklog();
2481
- const id = getNextId(items);
2482
- items.push({ ...data, id, status: "todo" });
2483
- saveBacklog(items);
2484
- console.log(chalk26.green(`Added item #${id}: ${data.name}`));
2485
- }
2486
- async function addInteractive() {
2487
- const type = await promptType();
2488
- const name = await promptName();
2489
- const description = await promptDescription();
2490
- const acceptanceCriteria2 = await promptAcceptanceCriteria();
2491
- const items = loadBacklog();
2492
- const id = getNextId(items);
2493
- items.push({
2494
- id,
2495
- type,
2496
- name,
2497
- description,
2498
- acceptanceCriteria: acceptanceCriteria2,
2499
- status: "todo"
2500
- });
2501
- saveBacklog(items);
2502
- console.log(chalk26.green(`Added item #${id}: ${name}`));
2503
- }
2504
- async function add(options2) {
2505
- if (!existsSync13(getBacklogPath())) {
2506
- console.log(
2507
- chalk26.yellow(
2508
- "No backlog found. Run 'assist backlog init' to create one."
2509
- )
2510
- );
2511
- return;
2512
- }
2513
- if (options2.json) {
2514
- await addFromJson();
2515
- } else {
2516
- await addInteractive();
2517
- }
2518
- }
2519
-
2520
2396
  // src/commands/backlog/delete/index.ts
2521
- import chalk27 from "chalk";
2522
2397
  async function del(id) {
2523
2398
  const name = removeItem(id);
2524
2399
  if (name) {
2525
- console.log(chalk27.green(`Deleted item #${id}: ${name}`));
2400
+ console.log(chalk26.green(`Deleted item #${id}: ${name}`));
2526
2401
  }
2527
2402
  }
2528
2403
 
2529
2404
  // src/commands/backlog/done/index.ts
2530
- import chalk28 from "chalk";
2405
+ import chalk27 from "chalk";
2531
2406
  async function done(id) {
2532
2407
  const name = setStatus(id, "done");
2533
2408
  if (name) {
2534
- console.log(chalk28.green(`Completed item #${id}: ${name}`));
2535
- }
2536
- }
2537
-
2538
- // src/commands/backlog/init/index.ts
2539
- import { existsSync as existsSync14 } from "fs";
2540
- import chalk29 from "chalk";
2541
- async function init6() {
2542
- const backlogPath = getBacklogPath();
2543
- if (existsSync14(backlogPath)) {
2544
- console.log(chalk29.yellow("assist.backlog.yml already exists."));
2545
- return;
2409
+ console.log(chalk27.green(`Completed item #${id}: ${name}`));
2546
2410
  }
2547
- saveBacklog([]);
2548
- console.log(chalk29.green("Created assist.backlog.yml"));
2549
2411
  }
2550
2412
 
2551
- // src/commands/backlog/list/index.ts
2552
- import { existsSync as existsSync15 } from "fs";
2553
- import chalk31 from "chalk";
2413
+ // src/commands/backlog/next.ts
2414
+ import chalk33 from "chalk";
2415
+ import enquirer5 from "enquirer";
2554
2416
 
2555
2417
  // src/commands/backlog/list/shared.ts
2556
- import chalk30 from "chalk";
2418
+ import chalk28 from "chalk";
2557
2419
  function statusIcon(status2) {
2558
2420
  switch (status2) {
2559
2421
  case "todo":
2560
- return chalk30.dim("[ ]");
2422
+ return chalk28.dim("[ ]");
2561
2423
  case "in-progress":
2562
- return chalk30.yellow("[~]");
2424
+ return chalk28.yellow("[~]");
2563
2425
  case "done":
2564
- return chalk30.green("[x]");
2426
+ return chalk28.green("[x]");
2565
2427
  }
2566
2428
  }
2567
2429
  function typeLabel(type) {
2568
2430
  switch (type) {
2569
2431
  case "bug":
2570
- return chalk30.magenta("Bug");
2432
+ return chalk28.magenta("Bug");
2571
2433
  case "story":
2572
- return chalk30.cyan("Story");
2434
+ return chalk28.cyan("Story");
2573
2435
  }
2574
2436
  }
2575
2437
  function phaseLabel(item) {
2576
2438
  if (!item.plan) return "";
2577
- return chalk30.dim(
2439
+ return chalk28.dim(
2578
2440
  ` (phase ${(item.currentPhase ?? 0) + 1}/${item.plan.length})`
2579
2441
  );
2580
2442
  }
2581
2443
  function printVerboseDetails(item) {
2582
2444
  if (item.description) {
2583
- console.log(` ${chalk30.dim("Description:")} ${item.description}`);
2445
+ console.log(` ${chalk28.dim("Description:")} ${item.description}`);
2584
2446
  }
2585
2447
  if (item.acceptanceCriteria.length > 0) {
2586
- console.log(` ${chalk30.dim("Acceptance criteria:")}`);
2448
+ console.log(` ${chalk28.dim("Acceptance criteria:")}`);
2587
2449
  for (const [i, criterion] of item.acceptanceCriteria.entries()) {
2588
2450
  console.log(` ${i + 1}. ${criterion}`);
2589
2451
  }
@@ -2591,42 +2453,8 @@ function printVerboseDetails(item) {
2591
2453
  console.log();
2592
2454
  }
2593
2455
 
2594
- // src/commands/backlog/list/index.ts
2595
- function filterItems(items, options2) {
2596
- if (options2.status) return items.filter((i) => i.status === options2.status);
2597
- if (!options2.all) return items.filter((i) => i.status !== "done");
2598
- return items;
2599
- }
2600
- async function list2(options2) {
2601
- if (!existsSync15(getBacklogPath())) {
2602
- console.log(
2603
- chalk31.yellow(
2604
- "No backlog found. Run 'assist backlog init' to create one."
2605
- )
2606
- );
2607
- return;
2608
- }
2609
- const items = filterItems(loadBacklog(), options2);
2610
- if (items.length === 0) {
2611
- console.log(chalk31.dim("Backlog is empty."));
2612
- return;
2613
- }
2614
- for (const item of items) {
2615
- console.log(
2616
- `${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk31.dim(`#${item.id}`)} ${item.name}${phaseLabel(item)}`
2617
- );
2618
- if (options2.verbose) {
2619
- printVerboseDetails(item);
2620
- }
2621
- }
2622
- }
2623
-
2624
- // src/commands/backlog/next.ts
2625
- import chalk36 from "chalk";
2626
- import enquirer6 from "enquirer";
2627
-
2628
2456
  // src/commands/backlog/run.ts
2629
- import chalk35 from "chalk";
2457
+ import chalk32 from "chalk";
2630
2458
 
2631
2459
  // src/commands/backlog/buildAuthoredPhasePrompt.ts
2632
2460
  function buildAuthoredPhasePrompt(item, phaseIndex, phase) {
@@ -2726,16 +2554,16 @@ function buildReviewPhase() {
2726
2554
  }
2727
2555
 
2728
2556
  // src/commands/backlog/executePhase.ts
2729
- import chalk34 from "chalk";
2557
+ import chalk31 from "chalk";
2730
2558
 
2731
2559
  // src/commands/backlog/resolvePhaseResult.ts
2732
- import { existsSync as existsSync16, unlinkSync as unlinkSync3 } from "fs";
2733
- import chalk33 from "chalk";
2560
+ import { existsSync as existsSync13, unlinkSync as unlinkSync2 } from "fs";
2561
+ import chalk30 from "chalk";
2734
2562
 
2735
2563
  // src/commands/backlog/handleIncompletePhase.ts
2736
- import enquirer5 from "enquirer";
2564
+ import enquirer4 from "enquirer";
2737
2565
  async function handleIncompletePhase() {
2738
- const { action } = await enquirer5.prompt({
2566
+ const { action } = await enquirer4.prompt({
2739
2567
  type: "select",
2740
2568
  name: "action",
2741
2569
  message: "Phase was not marked complete. What would you like to do?",
@@ -2747,17 +2575,17 @@ async function handleIncompletePhase() {
2747
2575
  }
2748
2576
 
2749
2577
  // src/commands/backlog/phaseDone.ts
2750
- import { writeFileSync as writeFileSync13 } from "fs";
2751
- import { join as join10 } from "path";
2752
- import chalk32 from "chalk";
2578
+ import { writeFileSync as writeFileSync12 } from "fs";
2579
+ import { join as join9 } from "path";
2580
+ import chalk29 from "chalk";
2753
2581
  var PHASE_STATUS_FILE = ".assist-phase-status.json";
2754
2582
  function getPhaseStatusPath() {
2755
- return join10(process.cwd(), PHASE_STATUS_FILE);
2583
+ return join9(process.cwd(), PHASE_STATUS_FILE);
2756
2584
  }
2757
2585
  function phaseDone(id, phase) {
2758
2586
  const phaseIndex = Number.parseInt(phase, 10);
2759
2587
  const statusPath = getPhaseStatusPath();
2760
- writeFileSync13(
2588
+ writeFileSync12(
2761
2589
  statusPath,
2762
2590
  JSON.stringify({
2763
2591
  itemId: Number.parseInt(id, 10),
@@ -2766,32 +2594,36 @@ function phaseDone(id, phase) {
2766
2594
  })
2767
2595
  );
2768
2596
  setCurrentPhase(id, phaseIndex + 1);
2769
- console.log(chalk32.green(`Phase ${phase} of item #${id} marked as complete.`));
2597
+ console.log(chalk29.green(`Phase ${phase} of item #${id} marked as complete.`));
2770
2598
  }
2771
2599
 
2772
2600
  // src/commands/backlog/resolvePhaseResult.ts
2773
2601
  function cleanupMarker() {
2774
2602
  const statusPath = getPhaseStatusPath();
2775
- if (existsSync16(statusPath)) {
2776
- unlinkSync3(statusPath);
2603
+ if (existsSync13(statusPath)) {
2604
+ unlinkSync2(statusPath);
2777
2605
  }
2778
2606
  }
2779
2607
  async function resolvePhaseResult(phaseIndex) {
2780
- if (!existsSync16(getPhaseStatusPath())) {
2608
+ if (!existsSync13(getPhaseStatusPath())) {
2781
2609
  const action = await handleIncompletePhase();
2782
2610
  if (action === "abort") return -1;
2783
2611
  return action === "skip" ? 1 : 0;
2784
2612
  }
2785
2613
  cleanupMarker();
2786
- console.log(chalk33.green(`
2614
+ console.log(chalk30.green(`
2787
2615
  Phase ${phaseIndex + 1} completed.`));
2788
2616
  return 1;
2789
2617
  }
2790
2618
 
2791
2619
  // src/commands/backlog/spawnClaude.ts
2792
2620
  import { spawn as spawn3 } from "child_process";
2793
- function spawnClaude(prompt) {
2794
- const child = spawn3("claude", [prompt], {
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, {
2795
2627
  stdio: "inherit"
2796
2628
  });
2797
2629
  const done2 = new Promise((resolve7, reject) => {
@@ -2802,11 +2634,11 @@ function spawnClaude(prompt) {
2802
2634
  }
2803
2635
 
2804
2636
  // src/commands/backlog/watchForMarker.ts
2805
- import { existsSync as existsSync17, unwatchFile, watchFile } from "fs";
2637
+ import { existsSync as existsSync14, unwatchFile, watchFile } from "fs";
2806
2638
  function watchForMarker(child) {
2807
2639
  const statusPath = getPhaseStatusPath();
2808
2640
  watchFile(statusPath, { interval: 1e3 }, () => {
2809
- if (existsSync17(statusPath)) {
2641
+ if (existsSync14(statusPath)) {
2810
2642
  unwatchFile(statusPath);
2811
2643
  child.kill("SIGTERM");
2812
2644
  }
@@ -2817,17 +2649,18 @@ function stopWatching() {
2817
2649
  }
2818
2650
 
2819
2651
  // src/commands/backlog/executePhase.ts
2820
- async function executePhase(item, phaseIndex, phases) {
2652
+ async function executePhase(item, phaseIndex, phases, spawnOptions) {
2821
2653
  const phase = phases[phaseIndex];
2822
2654
  console.log(
2823
- chalk34.bold(
2655
+ chalk31.bold(
2824
2656
  `
2825
2657
  --- Phase ${phaseIndex + 1}/${phases.length}: ${phase.name} ---
2826
2658
  `
2827
2659
  )
2828
2660
  );
2829
2661
  const { child, done: done2 } = spawnClaude(
2830
- buildPhasePrompt(item, phaseIndex, phase)
2662
+ buildPhasePrompt(item, phaseIndex, phase),
2663
+ spawnOptions
2831
2664
  );
2832
2665
  watchForMarker(child);
2833
2666
  await done2;
@@ -2850,7 +2683,7 @@ function resolvePlan(item) {
2850
2683
  }
2851
2684
 
2852
2685
  // src/commands/backlog/run.ts
2853
- async function run2(id) {
2686
+ async function run2(id, spawnOptions) {
2854
2687
  const result = loadAndFindItem(id);
2855
2688
  if (!result) return;
2856
2689
  const { item } = result;
@@ -2859,93 +2692,98 @@ async function run2(id) {
2859
2692
  if (startPhase > plan2.length) {
2860
2693
  if (item.status !== "done") setStatus(id, "done");
2861
2694
  console.log(
2862
- chalk35.green(`All phases already complete for #${id}: ${item.name}`)
2695
+ chalk32.green(`All phases already complete for #${id}: ${item.name}`)
2863
2696
  );
2864
2697
  return;
2865
2698
  }
2866
2699
  setStatus(id, "in-progress");
2867
- console.log(chalk35.bold(`Running plan for #${id}: ${item.name}`));
2700
+ console.log(chalk32.bold(`Running plan for #${id}: ${item.name}`));
2868
2701
  if (startPhase > 0) {
2869
2702
  console.log(
2870
- chalk35.dim(`Resuming from phase ${startPhase + 1}/${plan2.length}
2703
+ chalk32.dim(`Resuming from phase ${startPhase + 1}/${plan2.length}
2871
2704
  `)
2872
2705
  );
2873
2706
  } else {
2874
- console.log(chalk35.dim(`${plan2.length} phase(s)
2707
+ console.log(chalk32.dim(`${plan2.length} phase(s)
2875
2708
  `));
2876
2709
  }
2877
2710
  let phaseIndex = startPhase;
2878
2711
  while (phaseIndex < plan2.length) {
2879
- phaseIndex = await executePhase(item, phaseIndex, plan2);
2712
+ phaseIndex = await executePhase(item, phaseIndex, plan2, spawnOptions);
2880
2713
  if (phaseIndex < 0) return;
2881
2714
  }
2882
2715
  const reviewPhase = buildReviewPhase();
2883
2716
  const allPhases = [...plan2, reviewPhase];
2884
2717
  const reviewIndex = plan2.length;
2885
- const reviewResult = await executePhase(item, reviewIndex, allPhases);
2718
+ const reviewResult = await executePhase(
2719
+ item,
2720
+ reviewIndex,
2721
+ allPhases,
2722
+ spawnOptions
2723
+ );
2886
2724
  if (reviewResult < 0) return;
2887
2725
  if (item.status !== "done") setStatus(id, "done");
2888
- console.log(chalk35.green(`
2726
+ console.log(chalk32.green(`
2889
2727
  All phases complete for #${id}: ${item.name}`));
2890
2728
  }
2891
2729
 
2892
2730
  // src/commands/backlog/next.ts
2893
- async function next() {
2731
+ async function next(options2) {
2894
2732
  const items = loadBacklog();
2895
2733
  const inProgress = items.find((i) => i.status === "in-progress" && i.plan);
2896
2734
  if (inProgress) {
2897
2735
  console.log(
2898
- chalk36.bold(
2736
+ chalk33.bold(
2899
2737
  `Resuming in-progress item #${inProgress.id}: ${inProgress.name}`
2900
2738
  )
2901
2739
  );
2902
- await run2(String(inProgress.id));
2740
+ await run2(String(inProgress.id), options2);
2903
2741
  return;
2904
2742
  }
2905
2743
  const todo = items.filter((i) => i.status === "todo");
2906
2744
  if (todo.length === 0) {
2907
- console.log(chalk36.dim("No incomplete backlog items. Opening /draft..."));
2908
- await spawnClaude("/draft");
2745
+ console.log(chalk33.dim("No incomplete backlog items. Opening /draft..."));
2746
+ await spawnClaude("/draft", options2);
2909
2747
  return;
2910
2748
  }
2911
2749
  if (todo.length === 1) {
2912
2750
  const only = todo[0];
2913
- console.log(chalk36.bold(`Starting #${only.id}: ${only.name}`));
2914
- await run2(String(only.id));
2751
+ console.log(chalk33.bold(`Starting #${only.id}: ${only.name}`));
2752
+ await run2(String(only.id), options2);
2915
2753
  return;
2916
2754
  }
2917
2755
  const choices = todo.map((i) => ({
2918
2756
  name: `${typeLabel(i.type)} #${i.id}: ${i.name}`,
2919
2757
  value: String(i.id)
2920
2758
  }));
2921
- const { selected } = await enquirer6.prompt({
2759
+ const { selected } = await enquirer5.prompt({
2922
2760
  type: "select",
2923
2761
  name: "selected",
2924
2762
  message: "Choose a backlog item to start:",
2925
2763
  choices: choices.map((c) => c.name)
2926
2764
  });
2927
2765
  const id = selected.match(/#(\d+)/)?.[1] ?? "";
2928
- await run2(id);
2766
+ await run2(id, options2);
2929
2767
  }
2930
2768
 
2931
2769
  // src/commands/backlog/plan.ts
2932
- import chalk37 from "chalk";
2770
+ import chalk34 from "chalk";
2933
2771
  function plan(id) {
2934
2772
  const result = loadAndFindItem(id);
2935
2773
  if (!result) return;
2936
2774
  const { item } = result;
2937
2775
  if (!item.plan || item.plan.length === 0) {
2938
- console.log(chalk37.dim("No plan defined for this item."));
2776
+ console.log(chalk34.dim("No plan defined for this item."));
2939
2777
  return;
2940
2778
  }
2941
- console.log(chalk37.bold(item.name));
2779
+ console.log(chalk34.bold(item.name));
2942
2780
  console.log();
2943
2781
  for (const [i, phase] of item.plan.entries()) {
2944
- console.log(`${chalk37.bold(`Phase ${i + 1}:`)} ${phase.name}`);
2782
+ console.log(`${chalk34.bold(`Phase ${i + 1}:`)} ${phase.name}`);
2945
2783
  for (const task of phase.tasks) {
2946
2784
  console.log(` - ${task.task}`);
2947
2785
  if (task.verify) {
2948
- console.log(` ${chalk37.dim(`verify: ${task.verify}`)}`);
2786
+ console.log(` ${chalk34.dim(`verify: ${task.verify}`)}`);
2949
2787
  }
2950
2788
  }
2951
2789
  console.log();
@@ -2953,23 +2791,23 @@ function plan(id) {
2953
2791
  }
2954
2792
 
2955
2793
  // src/commands/backlog/start/index.ts
2956
- import chalk38 from "chalk";
2794
+ import chalk35 from "chalk";
2957
2795
  async function start(id) {
2958
2796
  const name = setStatus(id, "in-progress");
2959
2797
  if (name) {
2960
- console.log(chalk38.green(`Started item #${id}: ${name}`));
2798
+ console.log(chalk35.green(`Started item #${id}: ${name}`));
2961
2799
  }
2962
2800
  }
2963
2801
 
2964
2802
  // src/shared/web.ts
2965
2803
  import { exec } from "child_process";
2966
- import { readFileSync as readFileSync12 } from "fs";
2804
+ import { readFileSync as readFileSync11 } from "fs";
2967
2805
  import {
2968
2806
  createServer
2969
2807
  } from "http";
2970
- import { dirname as dirname13, join as join11 } from "path";
2808
+ import { dirname as dirname13, join as join10 } from "path";
2971
2809
  import { fileURLToPath as fileURLToPath3 } from "url";
2972
- import chalk39 from "chalk";
2810
+ import chalk36 from "chalk";
2973
2811
  function respondJson(res, status2, data) {
2974
2812
  res.writeHead(status2, { "Content-Type": "application/json" });
2975
2813
  res.end(JSON.stringify(data));
@@ -2979,7 +2817,7 @@ function createBundleHandler(importMetaUrl, bundlePath) {
2979
2817
  let cache;
2980
2818
  return (_req, res) => {
2981
2819
  if (!cache) {
2982
- cache = readFileSync12(join11(dir, bundlePath), "utf-8");
2820
+ cache = readFileSync11(join10(dir, bundlePath), "utf-8");
2983
2821
  }
2984
2822
  res.writeHead(200, { "Content-Type": "application/javascript" });
2985
2823
  res.end(cache);
@@ -3013,8 +2851,8 @@ function startWebServer(label2, port, handler) {
3013
2851
  handler(req, res, port);
3014
2852
  });
3015
2853
  server.listen(port, () => {
3016
- console.log(chalk39.green(`${label2}: ${url}`));
3017
- console.log(chalk39.dim("Press Ctrl+C to stop"));
2854
+ console.log(chalk36.green(`${label2}: ${url}`));
2855
+ console.log(chalk36.dim("Press Ctrl+C to stop"));
3018
2856
  exec(`open ${url}`);
3019
2857
  });
3020
2858
  }
@@ -3161,29 +2999,210 @@ async function web(options2) {
3161
2999
  );
3162
3000
  }
3163
3001
 
3164
- // src/commands/registerBacklog.ts
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
3165
3175
  function registerItemCommands(cmd) {
3166
3176
  cmd.command("init").description("Create an empty assist.backlog.yml").action(init6);
3167
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);
3168
3178
  cmd.command("add").description("Add a new backlog item").option("--json", "Read item as JSON from stdin").action(add);
3169
3179
  }
3180
+
3181
+ // src/commands/registerBacklog.ts
3170
3182
  function registerStatusCommands(cmd) {
3171
3183
  cmd.command("start <id>").description("Set a backlog item to in-progress").action(start);
3172
3184
  cmd.command("done <id>").description("Set a backlog item to done").action(done);
3173
3185
  cmd.command("delete <id>").alias("remove").description("Delete a backlog item").action(del);
3174
3186
  cmd.command("web").description("Start a web view of the backlog").option("-p, --port <number>", "Port to listen on", "3000").action(web);
3175
3187
  }
3176
- function registerOrchestrationCommands(cmd) {
3177
- cmd.command("next").description("Pick and run the next backlog item, or open /draft if none").action(next);
3188
+ function registerPlanCommands(cmd) {
3178
3189
  cmd.command("plan <id>").description("Display the plan for a backlog item").action(plan);
3179
3190
  cmd.command("phase-done <id> <phase>").description("Signal that a plan phase is complete").action(phaseDone);
3180
- cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase with Claude").action(run2);
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
+ );
3181
3199
  }
3182
3200
  function registerBacklog(program2) {
3183
3201
  const cmd = program2.command("backlog").description("Manage a backlog of work items").action(() => web({ port: "3000" }));
3184
3202
  registerItemCommands(cmd);
3185
3203
  registerStatusCommands(cmd);
3186
- registerOrchestrationCommands(cmd);
3204
+ registerPlanCommands(cmd);
3205
+ registerRunCommands(cmd);
3187
3206
  }
3188
3207
 
3189
3208
  // src/shared/isApprovedRead.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.148.0",
3
+ "version": "0.149.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {