@staff0rd/assist 0.224.0 → 0.225.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 CHANGED
@@ -111,8 +111,10 @@ The first backlog command in a repository that still has a local `.assist/backlo
111
111
  - `assist backlog add` - Add a new backlog item interactively (prompts for type: story/bug)
112
112
  - `assist backlog add --name <n> --type <t> --desc <d> --ac <criterion...>` - Add a backlog item from CLI options (used by `/draft`)
113
113
  - `assist backlog add-phase <id> <name> --task <t...> [--manual-check <c...>] [--position <pos>]` - Add a phase (appends by default; `--position` inserts at a 1-indexed position)
114
- - `assist backlog update <id> [--name <n>] [--desc <d>] [--type <t>] [--ac <criterion...>]` - Update fields on a backlog item
114
+ - `assist backlog update-field <id> [--name <n>] [--desc <d>] [--type <t>] [--ac <criterion...>]` - Update fields on a backlog item
115
+ - `assist backlog update-field <id> [--add-ac <text>] [--edit-ac <n> <text>] [--remove-ac <n>]` - Granular acceptance-criteria edits using 1-based indices matching `backlog show`: `--add-ac` appends (repeatable), `--edit-ac` replaces criterion n in place, `--remove-ac` deletes criterion n and renumbers the rest (cannot be combined with the whole-list `--ac`)
115
116
  - `assist backlog update-phase <id> <phase> [--name <n>] [--task <t...>] [--manual-check <c...>]` - Modify a plan phase (name, tasks, or manual checks)
117
+ - `assist backlog update-phase <id> <phase> [--add-task <text>] [--edit-task <n> <text>] [--remove-task <n>] [--add-check <text>] [--edit-check <n> <text>] [--remove-check <n>]` - Granular task and manual-check edits using 1-based indices matching `backlog show`: `--add-*` appends (repeatable), `--edit-*` replaces entry n in place, `--remove-*` deletes entry n and renumbers the rest (task ops cannot be combined with `--task`; check ops cannot be combined with `--manual-check`)
116
118
  - `assist backlog remove-phase <id> <phase>` - Remove a plan phase from a backlog item
117
119
  - `assist backlog next` - Pick and run the next backlog item, or open `/draft` if none remain
118
120
  - `assist backlog start <id>` - Set a backlog item to in-progress
@@ -35,10 +35,10 @@ Based on the user's input, apply changes using the appropriate commands. Always
35
35
 
36
36
  **To update item fields:**
37
37
  ```
38
- assist backlog update <id> --name "New name"
39
- assist backlog update <id> --desc "New description"
40
- assist backlog update <id> --type story
41
- assist backlog update <id> --ac "criterion 1" --ac "criterion 2"
38
+ assist backlog update-field <id> --name "New name"
39
+ assist backlog update-field <id> --desc "New description"
40
+ assist backlog update-field <id> --type story
41
+ assist backlog update-field <id> --ac "criterion 1" --ac "criterion 2"
42
42
  ```
43
43
 
44
44
  Note: `--ac` replaces all acceptance criteria, so include the full list (both existing and new).
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.224.0",
9
+ version: "0.225.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -909,11 +909,13 @@ function getDatabaseUrl() {
909
909
  }
910
910
  var _db;
911
911
  var _connecting;
912
+ var _pool;
912
913
  function getBacklogDb() {
913
914
  if (_db) return Promise.resolve(_db);
914
915
  if (_connecting) return _connecting;
915
916
  _connecting = (async () => {
916
917
  const pool = new Pool({ connectionString: getDatabaseUrl() });
918
+ _pool = pool;
917
919
  const db = makeBacklogDb(
918
920
  { query: (sql2, params) => pool.query(sql2, params) },
919
921
  pool
@@ -924,6 +926,14 @@ function getBacklogDb() {
924
926
  })();
925
927
  return _connecting;
926
928
  }
929
+ async function closeBacklogDb() {
930
+ const pool = _pool;
931
+ if (!pool) return;
932
+ _pool = void 0;
933
+ _db = void 0;
934
+ _connecting = void 0;
935
+ await pool.end();
936
+ }
927
937
 
928
938
  // src/commands/backlog/getCurrentOrigin.ts
929
939
  import { execSync as execSync2 } from "child_process";
@@ -5587,6 +5597,78 @@ async function removePhase(id, phase) {
5587
5597
  // src/commands/backlog/update/index.ts
5588
5598
  import chalk67 from "chalk";
5589
5599
 
5600
+ // src/commands/backlog/update/parseListIndex.ts
5601
+ function parseListIndex(raw, length, label2) {
5602
+ if (!/^\d+$/.test(raw)) {
5603
+ return { ok: false, error: `${label2} must be a positive integer.` };
5604
+ }
5605
+ const index = Number.parseInt(raw, 10);
5606
+ if (index < 1 || index > length) {
5607
+ return {
5608
+ ok: false,
5609
+ error: `${label2} ${index} is out of range (1-${length}).`
5610
+ };
5611
+ }
5612
+ return { ok: true, index };
5613
+ }
5614
+
5615
+ // src/commands/backlog/update/applyListMutations.ts
5616
+ function hasListMutations(options2) {
5617
+ return Boolean(
5618
+ options2.add && options2.add.length > 0 || options2.edit || options2.remove
5619
+ );
5620
+ }
5621
+ function applyListMutations(current, options2, flags) {
5622
+ let items = [...current];
5623
+ if (options2.edit) {
5624
+ const [rawIndex, ...textParts] = options2.edit;
5625
+ if (rawIndex === void 0 || textParts.length === 0) {
5626
+ return {
5627
+ ok: false,
5628
+ error: `${flags.edit} requires an index and replacement text.`
5629
+ };
5630
+ }
5631
+ const parsed = parseListIndex(
5632
+ rawIndex,
5633
+ items.length,
5634
+ `${flags.edit} index`
5635
+ );
5636
+ if (!parsed.ok) return parsed;
5637
+ items[parsed.index - 1] = textParts.join(" ");
5638
+ }
5639
+ if (options2.remove) {
5640
+ const parsed = parseListIndex(
5641
+ options2.remove,
5642
+ items.length,
5643
+ `${flags.remove} index`
5644
+ );
5645
+ if (!parsed.ok) return parsed;
5646
+ items = items.filter((_, i) => i !== parsed.index - 1);
5647
+ }
5648
+ if (options2.add) {
5649
+ items.push(...options2.add);
5650
+ }
5651
+ return { ok: true, items };
5652
+ }
5653
+
5654
+ // src/commands/backlog/update/applyAcMutations.ts
5655
+ function hasAcMutations(options2) {
5656
+ return hasListMutations({
5657
+ add: options2.addAc,
5658
+ edit: options2.editAc,
5659
+ remove: options2.removeAc
5660
+ });
5661
+ }
5662
+ function applyAcMutations(current, options2) {
5663
+ const result = applyListMutations(
5664
+ current,
5665
+ { add: options2.addAc, edit: options2.editAc, remove: options2.removeAc },
5666
+ { add: "--add-ac", edit: "--edit-ac", remove: "--remove-ac" }
5667
+ );
5668
+ if (!result.ok) return result;
5669
+ return { ok: true, criteria: result.items };
5670
+ }
5671
+
5590
5672
  // src/commands/backlog/update/buildUpdateSql.ts
5591
5673
  import chalk66 from "chalk";
5592
5674
  function buildUpdateSql(options2) {
@@ -5631,7 +5713,24 @@ function buildUpdateSql(options2) {
5631
5713
  async function update(id, options2) {
5632
5714
  const result = await loadAndFindItem(id);
5633
5715
  if (!result) return;
5634
- const built = buildUpdateSql(options2);
5716
+ let ac = options2.ac;
5717
+ if (hasAcMutations(options2)) {
5718
+ if (options2.ac) {
5719
+ console.log(
5720
+ chalk67.red("Cannot combine --ac with --add-ac/--edit-ac/--remove-ac.")
5721
+ );
5722
+ process.exitCode = 1;
5723
+ return;
5724
+ }
5725
+ const mutation = applyAcMutations(result.item.acceptanceCriteria, options2);
5726
+ if (!mutation.ok) {
5727
+ console.log(chalk67.red(mutation.error));
5728
+ process.exitCode = 1;
5729
+ return;
5730
+ }
5731
+ ac = mutation.criteria;
5732
+ }
5733
+ const built = buildUpdateSql({ ...options2, ac });
5635
5734
  if (!built) return;
5636
5735
  const db = await getBacklogDb();
5637
5736
  const itemId = result.item.id;
@@ -5675,17 +5774,81 @@ async function applyPhaseUpdate(db, itemId, phaseIdx, fields) {
5675
5774
  });
5676
5775
  }
5677
5776
 
5777
+ // src/commands/backlog/update/resolveListUpdate.ts
5778
+ function resolveListUpdate(whole, current, mutations, wholeFlag, flags) {
5779
+ if (!hasListMutations(mutations)) return { ok: true, items: whole };
5780
+ if (whole) {
5781
+ return {
5782
+ ok: false,
5783
+ error: `Cannot combine ${wholeFlag} with ${flags.add}/${flags.edit}/${flags.remove}.`
5784
+ };
5785
+ }
5786
+ return applyListMutations(current, mutations, flags);
5787
+ }
5788
+
5789
+ // src/commands/backlog/resolvePhaseFields.ts
5790
+ var TASK_FLAGS = {
5791
+ add: "--add-task",
5792
+ edit: "--edit-task",
5793
+ remove: "--remove-task"
5794
+ };
5795
+ var CHECK_FLAGS = {
5796
+ add: "--add-check",
5797
+ edit: "--edit-check",
5798
+ remove: "--remove-check"
5799
+ };
5800
+ function resolveTasks(o, current) {
5801
+ return resolveListUpdate(
5802
+ o.task,
5803
+ (current?.tasks ?? []).map((t) => t.task),
5804
+ { add: o.addTask, edit: o.editTask, remove: o.removeTask },
5805
+ "--task",
5806
+ TASK_FLAGS
5807
+ );
5808
+ }
5809
+ function resolveChecks(o, current) {
5810
+ return resolveListUpdate(
5811
+ o.manualCheck,
5812
+ current?.manualChecks ?? [],
5813
+ { add: o.addCheck, edit: o.editCheck, remove: o.removeCheck },
5814
+ "--manual-check",
5815
+ CHECK_FLAGS
5816
+ );
5817
+ }
5818
+ function buildFields(name, tasks, checks) {
5819
+ const fields = {
5820
+ name,
5821
+ task: tasks.items,
5822
+ manualCheck: checks.items
5823
+ };
5824
+ if (!fields.name && !fields.task && !fields.manualCheck) {
5825
+ return {
5826
+ ok: false,
5827
+ error: "Nothing to update. Provide at least one flag."
5828
+ };
5829
+ }
5830
+ return { ok: true, fields };
5831
+ }
5832
+ function resolvePhaseFields(options2, current) {
5833
+ const tasks = resolveTasks(options2, current);
5834
+ if (!tasks.ok) return tasks;
5835
+ const checks = resolveChecks(options2, current);
5836
+ if (!checks.ok) return checks;
5837
+ return buildFields(options2.name, tasks, checks);
5838
+ }
5839
+
5678
5840
  // src/commands/backlog/updatePhase.ts
5679
5841
  async function updatePhase(id, phase, options2) {
5680
- const { name, task, manualCheck } = options2;
5681
- if (!name && !task && !manualCheck) {
5682
- console.log(chalk68.red("Nothing to update. Provide at least one flag."));
5842
+ const found = await findPhase(id, phase);
5843
+ if (!found) return;
5844
+ const { result, db, itemId, phaseIdx } = found;
5845
+ const resolved = resolvePhaseFields(options2, result.item.plan?.[phaseIdx]);
5846
+ if (!resolved.ok) {
5847
+ console.log(chalk68.red(resolved.error));
5683
5848
  process.exitCode = 1;
5684
5849
  return;
5685
5850
  }
5686
- const found = await findPhase(id, phase);
5687
- if (!found) return;
5688
- const { db, itemId, phaseIdx } = found;
5851
+ const { name, task, manualCheck } = resolved.fields;
5689
5852
  await applyPhaseUpdate(db, itemId, phaseIdx, { name, task, manualCheck });
5690
5853
  const fields = [
5691
5854
  name && "name",
@@ -5700,9 +5863,28 @@ async function updatePhase(id, phase, options2) {
5700
5863
  }
5701
5864
 
5702
5865
  // src/commands/backlog/registerUpdateCommands.ts
5866
+ function collect(value, previous) {
5867
+ return [...previous, value];
5868
+ }
5703
5869
  function registerUpdateCommands(cmd) {
5704
- cmd.command("update <id>").description("Update fields on a backlog item").option("--name <name>", "New item name").option("--desc <description>", "New description").option("--type <type>", "New type (story or bug)").option("--ac <criterion...>", "Replace acceptance criteria (repeatable)").action(update);
5705
- cmd.command("update-phase <id> <phase>").description("Modify a plan phase on a backlog item").option("--name <name>", "New phase name").option("--task <task...>", "Replace tasks (repeatable)").option("--manual-check <check...>", "Replace manual checks (repeatable)").action(updatePhase);
5870
+ cmd.command("update-field <id>").description("Update fields on a backlog item").option("--name <name>", "New item name").option("--desc <description>", "New description").option("--type <type>", "New type (story or bug)").option("--ac <criterion...>", "Replace acceptance criteria (repeatable)").option(
5871
+ "--add-ac <text>",
5872
+ "Append one acceptance criterion (repeatable)",
5873
+ collect,
5874
+ []
5875
+ ).option(
5876
+ "--edit-ac <n> <text...>",
5877
+ "Replace acceptance criterion n (1-based) in place"
5878
+ ).option("--remove-ac <n>", "Remove acceptance criterion n (1-based)").action(update);
5879
+ cmd.command("update-phase <id> <phase>").description("Modify a plan phase on a backlog item").option("--name <name>", "New phase name").option("--task <task...>", "Replace tasks (repeatable)").option("--manual-check <check...>", "Replace manual checks (repeatable)").option("--add-task <text>", "Append one task (repeatable)", collect, []).option("--edit-task <n> <text...>", "Replace task n (1-based) in place").option("--remove-task <n>", "Remove task n (1-based)").option(
5880
+ "--add-check <text>",
5881
+ "Append one manual check (repeatable)",
5882
+ collect,
5883
+ []
5884
+ ).option(
5885
+ "--edit-check <n> <text...>",
5886
+ "Replace manual check n (1-based) in place"
5887
+ ).option("--remove-check <n>", "Remove manual check n (1-based)").action(updatePhase);
5706
5888
  cmd.command("remove-phase <id> <phase>").description("Remove a plan phase from a backlog item").action(removePhase);
5707
5889
  }
5708
5890
 
@@ -16440,4 +16622,7 @@ program.command("draft").alias("feat").description("Launch Claude in /draft mode
16440
16622
  program.command("bug").description("Launch Claude in /bug mode, chain into next on /next signal").action(() => launchMode("bug"));
16441
16623
  program.command("refine").argument("[id]", "Backlog item ID").description("Launch Claude in /refine mode to refine a backlog item").action((id) => refine(id));
16442
16624
  registerSignal(program);
16443
- program.parse();
16625
+ program.parseAsync().catch((error) => {
16626
+ console.error(error);
16627
+ process.exitCode = 1;
16628
+ }).finally(() => closeBacklogDb());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.224.0",
3
+ "version": "0.225.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {