@hasna/todos 0.13.10 → 0.13.11

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.
@@ -8,6 +8,13 @@
8
8
  *
9
9
  * Refusal exits the process via `handleError` (typed `never`), so a caller
10
10
  * cannot accidentally fall through to a silent self-assign.
11
+ *
12
+ * `buildSeatHint` is REQUIRED, not optional, and deliberately has no default:
13
+ * every call site takes the assignee on a different real shape (a
14
+ * `--assign <agent>` flag on three verbs, a bare positional on the fourth),
15
+ * so there is no invocation-shaped default that is correct everywhere. See
16
+ * {@link AssigneeContext.seatHint} for why a shared default was exactly the
17
+ * bug (todos 75296282).
11
18
  */
12
- export declare function resolveValidatedAssignee(value: string, allowSeat: boolean): Promise<string>;
19
+ export declare function resolveValidatedAssignee(value: string, allowSeat: boolean, buildSeatHint: (value: string) => string): Promise<string>;
13
20
  //# sourceMappingURL=assignee-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assignee-guard.d.ts","sourceRoot":"","sources":["../../src/cli/assignee-guard.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAejG"}
1
+ {"version":3,"file":"assignee-guard.d.ts","sourceRoot":"","sources":["../../src/cli/assignee-guard.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GACvC,OAAO,CAAC,MAAM,CAAC,CAejB"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/query-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgPzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QAkqGrD"}
1
+ {"version":3,"file":"query-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/query-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgPzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QA6qGrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0DzC,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAkPD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAshDpD"}
1
+ {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0DzC,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAkPD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAqiDpD"}
package/dist/cli/index.js CHANGED
@@ -2123,7 +2123,7 @@ var package_default;
2123
2123
  var init_package = __esm(() => {
2124
2124
  package_default = {
2125
2125
  name: "@hasna/todos",
2126
- version: "0.13.10",
2126
+ version: "0.13.11",
2127
2127
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
2128
2128
  type: "module",
2129
2129
  main: "dist/index.js",
@@ -19090,10 +19090,11 @@ function validateAssignee(input, ctx) {
19090
19090
  if (ctx.allowSeat) {
19091
19091
  return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
19092
19092
  }
19093
+ const hint = ctx.seatHint?.(raw);
19093
19094
  return {
19094
19095
  ok: false,
19095
19096
  reason: "seat",
19096
- message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or pass --assign-seat if filing at the seat is what you mean.`
19097
+ message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or repeat the command with --assign-seat added to confirm the seat is deliberate` + (hint ? `: ${hint}` : " (see --help for this command's exact form).")
19097
19098
  };
19098
19099
  }
19099
19100
  if (byId) {
@@ -19821,10 +19822,10 @@ var init_agents = __esm(() => {
19821
19822
 
19822
19823
  // src/cli/assignee-guard.ts
19823
19824
  import chalk2 from "chalk";
19824
- async function resolveValidatedAssignee(value, allowSeat) {
19825
+ async function resolveValidatedAssignee(value, allowSeat, buildSeatHint) {
19825
19826
  const cloud = getTodosCloudClient();
19826
19827
  const ctx = await loadAssigneeContext(() => cloud ? cloudListAgents(cloud) : listAgents(), allowSeat);
19827
- const verdict = validateAssignee(value, ctx);
19828
+ const verdict = validateAssignee(value, { ...ctx, seatHint: buildSeatHint });
19828
19829
  if (!verdict.ok) {
19829
19830
  handleError(new Error(`Cannot assign to '${value}'. ${verdict.message}`));
19830
19831
  }
@@ -20344,7 +20345,7 @@ function registerTaskCommands(program2) {
20344
20345
  opts.list = opts.list || opts.taskList;
20345
20346
  const creator = resolveCreatorIdentity(opts.createdBy || globalOpts.agent);
20346
20347
  const router = resolveWritableIdentity(opts.createdBy || globalOpts.agent);
20347
- const requestedAssign = opts.assign ? await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat)) : undefined;
20348
+ const requestedAssign = opts.assign ? await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`) : undefined;
20348
20349
  const assignee = requestedAssign || (opts.unassigned ? undefined : router.agent_id || undefined);
20349
20350
  if (!assignee && !opts.unassigned) {
20350
20351
  console.error(chalk3.yellow("Warning: task is ownerless and unattributable \u2014 export TODOS_AGENT_ID=<name> for this session, or pass --agent/--assign <agent> or --unassigned."));
@@ -20447,7 +20448,7 @@ function registerTaskCommands(program2) {
20447
20448
  opts.tags = opts.tags || opts.tag;
20448
20449
  opts.list = opts.list || opts.taskList;
20449
20450
  if (opts.assign) {
20450
- opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat));
20451
+ opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`);
20451
20452
  }
20452
20453
  const explicitProject = opts.project || globalOpts.project;
20453
20454
  const cloud = getTodosCloudClient();
@@ -21108,7 +21109,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
21108
21109
  handleError(new Error("Use either --working-dir or --clear-working-dir, not both."));
21109
21110
  }
21110
21111
  if (opts.assign) {
21111
- opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat));
21112
+ opts.assign = await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`);
21112
21113
  }
21113
21114
  const cloud = getTodosCloudClient();
21114
21115
  if (cloud) {
@@ -68355,7 +68356,7 @@ No task claimed (nothing available).`));
68355
68356
  });
68356
68357
  program2.command("assign <id> <agent>").description("Assign a task to an agent").option("-j, --json", "Output as JSON").option("--assign-seat", "Allow <agent> to name a durable seat (a seat queue has no session watching it)").action(async (id, agentInput, opts) => {
68357
68358
  const globalOpts = program2.opts();
68358
- const agent = await resolveValidatedAssignee(agentInput, Boolean(opts.assignSeat));
68359
+ const agent = await resolveValidatedAssignee(agentInput, Boolean(opts.assignSeat), (v) => `${id} ${v} --assign-seat`);
68359
68360
  const cloud = getTodosCloudClient();
68360
68361
  if (cloud) {
68361
68362
  try {
package/dist/contracts.js CHANGED
@@ -12009,7 +12009,7 @@ var init_tasks = __esm(() => {
12009
12009
  // package.json
12010
12010
  var package_default = {
12011
12011
  name: "@hasna/todos",
12012
- version: "0.13.10",
12012
+ version: "0.13.11",
12013
12013
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12014
12014
  type: "module",
12015
12015
  main: "dist/index.js",
package/dist/index.js CHANGED
@@ -12122,7 +12122,7 @@ var init_dispatches = __esm(() => {
12122
12122
  // package.json
12123
12123
  var package_default = {
12124
12124
  name: "@hasna/todos",
12125
- version: "0.13.10",
12125
+ version: "0.13.11",
12126
12126
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12127
12127
  type: "module",
12128
12128
  main: "dist/index.js",
@@ -10,6 +10,28 @@ export interface AssigneeContext {
10
10
  seats: Set<string>;
11
11
  /** Caller passed `--assign-seat`: filing at a seat is deliberate. */
12
12
  allowSeat?: boolean;
13
+ /**
14
+ * Builds the exact runnable suffix that pairs `--assign-seat` with the
15
+ * refused value, FOR THE VERB THE CALLER IS ACTUALLY ON.
16
+ *
17
+ * This function is shared by four call sites — `add`, `update`,
18
+ * `task upsert`, and the standalone `assign <id> <agent>` — and three of
19
+ * them take the assignee via a `--assign <agent>` FLAG while the fourth
20
+ * takes it POSITIONALLY with no `--assign` flag at all. A single hardcoded
21
+ * `--assign <value> --assign-seat` snippet is therefore correct for three
22
+ * verbs and `error: unknown option '--assign'` on the fourth (todos
23
+ * 75296282, PR hasna/todos#162 review by pr162-reviewer, cycle 1): the
24
+ * seat-refusal message recommended an invocation the verb it was printed
25
+ * from does not accept — the exact class of defect the message exists to
26
+ * remove, relocated rather than fixed.
27
+ *
28
+ * Each call site supplies its own builder naming its own real shape, so the
29
+ * message can never again claim a flag a given verb does not have.
30
+ * Undefined falls back to naming `--assign-seat` in prose with no runnable
31
+ * snippet, which is always safe because it recommends no invocation shape
32
+ * at all.
33
+ */
34
+ seatHint?: (value: string) => string;
13
35
  }
14
36
  /**
15
37
  * Three outcomes, and the split between REFUSE and WARN is evidence-driven
@@ -1 +1 @@
1
- {"version":3,"file":"assignee-validation.d.ts","sourceRoot":"","sources":["../../src/lib/assignee-validation.ts"],"names":[],"mappings":"AA2CA,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,gEAAgE;IAChE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACnF;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC;AAEN;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAgC,GAAG,GAAG,CAAC,MAAM,CAAC,CAajF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,eAAe,CAoErF"}
1
+ {"version":3,"file":"assignee-validation.d.ts","sourceRoot":"","sources":["../../src/lib/assignee-validation.ts"],"names":[],"mappings":"AA2CA,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,gEAAgE;IAChE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACnF;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAC;AAEN;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAgC,GAAG,GAAG,CAAC,MAAM,CAAC,CAajF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,GAAG,eAAe,CAsErF"}
package/dist/mcp/index.js CHANGED
@@ -18012,10 +18012,11 @@ function validateAssignee(input, ctx) {
18012
18012
  if (ctx.allowSeat) {
18013
18013
  return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
18014
18014
  }
18015
+ const hint = ctx.seatHint?.(raw);
18015
18016
  return {
18016
18017
  ok: false,
18017
18018
  reason: "seat",
18018
- message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or pass --assign-seat if filing at the seat is what you mean.`
18019
+ message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or repeat the command with --assign-seat added to confirm the seat is deliberate` + (hint ? `: ${hint}` : " (see --help for this command's exact form).")
18019
18020
  };
18020
18021
  }
18021
18022
  if (byId) {
@@ -34842,7 +34843,7 @@ var package_default;
34842
34843
  var init_package = __esm(() => {
34843
34844
  package_default = {
34844
34845
  name: "@hasna/todos",
34845
- version: "0.13.10",
34846
+ version: "0.13.11",
34846
34847
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
34847
34848
  type: "module",
34848
34849
  main: "dist/index.js",
package/dist/mcp.js CHANGED
@@ -41,7 +41,7 @@ var __require = import.meta.require;
41
41
  // package.json
42
42
  var package_default = {
43
43
  name: "@hasna/todos",
44
- version: "0.13.10",
44
+ version: "0.13.11",
45
45
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
46
46
  type: "module",
47
47
  main: "dist/index.js",
package/dist/registry.js CHANGED
@@ -12009,7 +12009,7 @@ var init_tasks = __esm(() => {
12009
12009
  // package.json
12010
12010
  var package_default = {
12011
12011
  name: "@hasna/todos",
12012
- version: "0.13.10",
12012
+ version: "0.13.11",
12013
12013
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12014
12014
  type: "module",
12015
12015
  main: "dist/index.js",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.13.10",
3
+ "packageVersion": "0.13.11",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "caef21b58b4e8d4cd7996e57bc922504c98ddee9",
6
- "gitTree": "574134d0b7552948e8f31013b4b247fd8ec6d187",
7
- "sourceTreeSha256": "eca04b82f06e0d20a8f744e7efc9091afa1f0606b4899d473ee1c44e0966769c",
8
- "generatedAt": "2026-08-02T04:51:15.000Z"
5
+ "gitCommit": "af551bd57da4ef1835d763232c1e3b3b8cee4f9f",
6
+ "gitTree": "23029d1f9a1afeecf519674e13746e615431ae8b",
7
+ "sourceTreeSha256": "b14e57ef9ec0cbd0e0142681b993aa34c941d59a18649ddd499f3ee1a62cfef9",
8
+ "generatedAt": "2026-08-02T06:21:53.000Z"
9
9
  }
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.13.10",
73
+ version: "0.13.11",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -44798,10 +44798,11 @@ function validateAssignee(input, ctx) {
44798
44798
  if (ctx.allowSeat) {
44799
44799
  return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
44800
44800
  }
44801
+ const hint = ctx.seatHint?.(raw);
44801
44802
  return {
44802
44803
  ok: false,
44803
44804
  reason: "seat",
44804
- message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or pass --assign-seat if filing at the seat is what you mean.`
44805
+ message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or repeat the command with --assign-seat added to confirm the seat is deliberate` + (hint ? `: ${hint}` : " (see --help for this command's exact form).")
44805
44806
  };
44806
44807
  }
44807
44808
  if (byId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.13.10",
3
+ "version": "0.13.11",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",