@staff0rd/assist 0.224.1 → 0.226.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 +3 -1
- package/claude/commands/refine.md +4 -4
- package/dist/index.js +208 -10
- package/package.json +1 -1
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.
|
|
9
|
+
version: "0.226.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -5597,6 +5597,78 @@ async function removePhase(id, phase) {
|
|
|
5597
5597
|
// src/commands/backlog/update/index.ts
|
|
5598
5598
|
import chalk67 from "chalk";
|
|
5599
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
|
+
|
|
5600
5672
|
// src/commands/backlog/update/buildUpdateSql.ts
|
|
5601
5673
|
import chalk66 from "chalk";
|
|
5602
5674
|
function buildUpdateSql(options2) {
|
|
@@ -5641,7 +5713,24 @@ function buildUpdateSql(options2) {
|
|
|
5641
5713
|
async function update(id, options2) {
|
|
5642
5714
|
const result = await loadAndFindItem(id);
|
|
5643
5715
|
if (!result) return;
|
|
5644
|
-
|
|
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 });
|
|
5645
5734
|
if (!built) return;
|
|
5646
5735
|
const db = await getBacklogDb();
|
|
5647
5736
|
const itemId = result.item.id;
|
|
@@ -5685,17 +5774,81 @@ async function applyPhaseUpdate(db, itemId, phaseIdx, fields) {
|
|
|
5685
5774
|
});
|
|
5686
5775
|
}
|
|
5687
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
|
+
|
|
5688
5840
|
// src/commands/backlog/updatePhase.ts
|
|
5689
5841
|
async function updatePhase(id, phase, options2) {
|
|
5690
|
-
const
|
|
5691
|
-
if (!
|
|
5692
|
-
|
|
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));
|
|
5693
5848
|
process.exitCode = 1;
|
|
5694
5849
|
return;
|
|
5695
5850
|
}
|
|
5696
|
-
const
|
|
5697
|
-
if (!found) return;
|
|
5698
|
-
const { db, itemId, phaseIdx } = found;
|
|
5851
|
+
const { name, task, manualCheck } = resolved.fields;
|
|
5699
5852
|
await applyPhaseUpdate(db, itemId, phaseIdx, { name, task, manualCheck });
|
|
5700
5853
|
const fields = [
|
|
5701
5854
|
name && "name",
|
|
@@ -5710,9 +5863,28 @@ async function updatePhase(id, phase, options2) {
|
|
|
5710
5863
|
}
|
|
5711
5864
|
|
|
5712
5865
|
// src/commands/backlog/registerUpdateCommands.ts
|
|
5866
|
+
function collect(value, previous) {
|
|
5867
|
+
return [...previous, value];
|
|
5868
|
+
}
|
|
5713
5869
|
function registerUpdateCommands(cmd) {
|
|
5714
|
-
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)").
|
|
5715
|
-
|
|
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);
|
|
5716
5888
|
cmd.command("remove-phase <id> <phase>").description("Remove a plan phase from a backlog item").action(removePhase);
|
|
5717
5889
|
}
|
|
5718
5890
|
|
|
@@ -6194,6 +6366,30 @@ function matchesConfigDeny(command) {
|
|
|
6194
6366
|
}
|
|
6195
6367
|
|
|
6196
6368
|
// src/commands/cliHook/resolvePermission.ts
|
|
6369
|
+
var SUBCOMMAND_READS = [
|
|
6370
|
+
{
|
|
6371
|
+
prefix: "assist complexity",
|
|
6372
|
+
subcommands: ["maintainability", "cyclomatic", "halstead"]
|
|
6373
|
+
}
|
|
6374
|
+
];
|
|
6375
|
+
function matchesSubcommandRead(part) {
|
|
6376
|
+
return SUBCOMMAND_READS.find(
|
|
6377
|
+
(rule) => part === rule.prefix || part.startsWith(`${rule.prefix} `)
|
|
6378
|
+
);
|
|
6379
|
+
}
|
|
6380
|
+
function findSubcommandAdvice(parts) {
|
|
6381
|
+
if (parts.length <= 1) return void 0;
|
|
6382
|
+
for (const part of parts) {
|
|
6383
|
+
const rule = matchesSubcommandRead(part);
|
|
6384
|
+
if (rule) {
|
|
6385
|
+
return {
|
|
6386
|
+
permissionDecision: "deny",
|
|
6387
|
+
permissionDecisionReason: `Do not pipe or chain '${rule.prefix}'. Run a focused sub-command directly for targeted output: ${rule.subcommands.map((s) => `${rule.prefix} ${s} <file>`).join(", ")}.`
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
return void 0;
|
|
6392
|
+
}
|
|
6197
6393
|
function findDeny(toolName, parts) {
|
|
6198
6394
|
for (const part of parts) {
|
|
6199
6395
|
const configDeny = matchesConfigDeny(part);
|
|
@@ -6204,6 +6400,8 @@ function findDeny(toolName, parts) {
|
|
|
6204
6400
|
};
|
|
6205
6401
|
}
|
|
6206
6402
|
}
|
|
6403
|
+
const subcommandAdvice = findSubcommandAdvice(parts);
|
|
6404
|
+
if (subcommandAdvice) return subcommandAdvice;
|
|
6207
6405
|
for (const part of parts) {
|
|
6208
6406
|
const denied = matchesDeny(toolName, part);
|
|
6209
6407
|
if (denied) {
|