@kitsy/coop 2.2.0 → 2.2.1

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 +682 -194
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -237,8 +237,8 @@ function findTaskFileById(root, id) {
237
237
  function todayIsoDate() {
238
238
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
239
239
  }
240
- function normalizeIdPart(input3, fallback, maxLength = 12) {
241
- const normalized = input3.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").replace(/-/g, "");
240
+ function normalizeIdPart(input2, fallback, maxLength = 12) {
241
+ const normalized = input2.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").replace(/-/g, "");
242
242
  if (!normalized) return fallback;
243
243
  return normalized.slice(0, maxLength);
244
244
  }
@@ -274,24 +274,24 @@ function shortDateToken(now = /* @__PURE__ */ new Date()) {
274
274
  function randomToken() {
275
275
  return crypto.randomBytes(4).toString("hex").toUpperCase();
276
276
  }
277
- function sanitizeTemplateValue(input3, fallback = "X") {
278
- const normalized = input3.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
277
+ function sanitizeTemplateValue(input2, fallback = "X") {
278
+ const normalized = input2.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
279
279
  return normalized || fallback;
280
280
  }
281
- function sanitizeSemanticWord(input3) {
282
- return input3.toUpperCase().replace(/[^A-Z0-9]+/g, "").trim();
281
+ function sanitizeSemanticWord(input2) {
282
+ return input2.toUpperCase().replace(/[^A-Z0-9]+/g, "").trim();
283
283
  }
284
- function semanticWords(input3) {
285
- const rawWords = input3.split(/[^a-zA-Z0-9]+/g).map(sanitizeSemanticWord).filter(Boolean);
284
+ function semanticWords(input2) {
285
+ const rawWords = input2.split(/[^a-zA-Z0-9]+/g).map(sanitizeSemanticWord).filter(Boolean);
286
286
  if (rawWords.length <= 1) {
287
287
  return rawWords;
288
288
  }
289
289
  const filtered = rawWords.filter((word) => !SEMANTIC_STOP_WORDS.has(word));
290
290
  return filtered.length > 0 ? filtered : rawWords;
291
291
  }
292
- function semanticTitleToken(input3, maxLength = DEFAULT_TITLE_TOKEN_LENGTH) {
292
+ function semanticTitleToken(input2, maxLength = DEFAULT_TITLE_TOKEN_LENGTH) {
293
293
  const safeMaxLength = Number.isFinite(maxLength) ? Math.max(4, Math.floor(maxLength)) : DEFAULT_TITLE_TOKEN_LENGTH;
294
- const words = semanticWords(input3);
294
+ const words = semanticWords(input2);
295
295
  if (words.length === 0) {
296
296
  return "UNTITLED";
297
297
  }
@@ -452,27 +452,27 @@ function generateConfiguredId(root, existingIds, context) {
452
452
 
453
453
  // src/utils/aliases.ts
454
454
  var ALIAS_PATTERN = /^[A-Z0-9]+(?:[.-][A-Z0-9]+)*$/;
455
- function toPosixPath(input3) {
456
- return input3.replace(/\\/g, "/");
455
+ function toPosixPath(input2) {
456
+ return input2.replace(/\\/g, "/");
457
457
  }
458
458
  function indexFilePath(root) {
459
459
  const { indexDataFormat } = readCoopConfig(root);
460
460
  const extension = indexDataFormat === "json" ? "json" : "yml";
461
461
  return path2.join(ensureCoopInitialized(root), ".index", `aliases.${extension}`);
462
462
  }
463
- function normalizeAliasValue(input3) {
464
- return input3.trim().toUpperCase().replace(/_/g, ".").replace(/\.+/g, ".");
463
+ function normalizeAliasValue(input2) {
464
+ return input2.trim().toUpperCase().replace(/_/g, ".").replace(/\.+/g, ".");
465
465
  }
466
- function normalizePatternValue(input3) {
467
- return input3.trim().toUpperCase().replace(/_/g, ".");
466
+ function normalizePatternValue(input2) {
467
+ return input2.trim().toUpperCase().replace(/_/g, ".");
468
468
  }
469
- function normalizeAlias(input3) {
470
- const normalized = normalizeAliasValue(input3);
469
+ function normalizeAlias(input2) {
470
+ const normalized = normalizeAliasValue(input2);
471
471
  if (!normalized) {
472
472
  throw new Error("Alias cannot be empty.");
473
473
  }
474
474
  if (!ALIAS_PATTERN.test(normalized)) {
475
- throw new Error(`Invalid alias '${input3}'. Use letters/numbers with '.' and '-'.`);
475
+ throw new Error(`Invalid alias '${input2}'. Use letters/numbers with '.' and '-'.`);
476
476
  }
477
477
  return normalized;
478
478
  }
@@ -659,8 +659,8 @@ function updateTaskAliases(filePath, aliases) {
659
659
  function updateIdeaAliases(filePath, aliases) {
660
660
  const parsed = parseIdeaFile(filePath);
661
661
  const nextRaw = { ...parsed.raw, aliases };
662
- const output3 = stringifyFrontmatter(nextRaw, parsed.body);
663
- fs2.writeFileSync(filePath, output3, "utf8");
662
+ const output2 = stringifyFrontmatter(nextRaw, parsed.body);
663
+ fs2.writeFileSync(filePath, output2, "utf8");
664
664
  }
665
665
  function resolveFilePath(root, target) {
666
666
  return path2.join(root, ...target.file.split("/"));
@@ -832,8 +832,8 @@ import { Octokit } from "octokit";
832
832
  function isObject(value) {
833
833
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
834
834
  }
835
- function sanitizeBranchPart(input3, fallback) {
836
- const normalized = input3.trim().toLowerCase().replace(/[^a-z0-9/_-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
835
+ function sanitizeBranchPart(input2, fallback) {
836
+ const normalized = input2.trim().toLowerCase().replace(/[^a-z0-9/_-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
837
837
  return normalized || fallback;
838
838
  }
839
839
  function readGitRemote(root) {
@@ -993,15 +993,15 @@ function toGitHubClient(config) {
993
993
  baseUrl: config.apiBaseUrl
994
994
  });
995
995
  return {
996
- async createPullRequest(input3) {
996
+ async createPullRequest(input2) {
997
997
  const response = await octokit.rest.pulls.create({
998
- owner: input3.owner,
999
- repo: input3.repo,
1000
- title: input3.title,
1001
- body: input3.body,
1002
- head: input3.head,
1003
- base: input3.base,
1004
- draft: input3.draft
998
+ owner: input2.owner,
999
+ repo: input2.repo,
1000
+ title: input2.title,
1001
+ body: input2.body,
1002
+ head: input2.head,
1003
+ base: input2.base,
1004
+ draft: input2.draft
1005
1005
  });
1006
1006
  return {
1007
1007
  number: response.data.number,
@@ -1011,14 +1011,14 @@ function toGitHubClient(config) {
1011
1011
  draft: response.data.draft
1012
1012
  };
1013
1013
  },
1014
- async updatePullRequest(input3) {
1014
+ async updatePullRequest(input2) {
1015
1015
  const response = await octokit.rest.pulls.update({
1016
- owner: input3.owner,
1017
- repo: input3.repo,
1018
- pull_number: input3.pull_number,
1019
- title: input3.title,
1020
- body: input3.body,
1021
- base: input3.base
1016
+ owner: input2.owner,
1017
+ repo: input2.repo,
1018
+ pull_number: input2.pull_number,
1019
+ title: input2.title,
1020
+ body: input2.body,
1021
+ base: input2.base
1022
1022
  });
1023
1023
  return {
1024
1024
  number: response.data.number,
@@ -1028,11 +1028,11 @@ function toGitHubClient(config) {
1028
1028
  draft: response.data.draft
1029
1029
  };
1030
1030
  },
1031
- async getPullRequest(input3) {
1031
+ async getPullRequest(input2) {
1032
1032
  const response = await octokit.rest.pulls.get({
1033
- owner: input3.owner,
1034
- repo: input3.repo,
1035
- pull_number: input3.pull_number
1033
+ owner: input2.owner,
1034
+ repo: input2.repo,
1035
+ pull_number: input2.pull_number
1036
1036
  });
1037
1037
  return {
1038
1038
  number: response.data.number,
@@ -1042,12 +1042,12 @@ function toGitHubClient(config) {
1042
1042
  draft: response.data.draft
1043
1043
  };
1044
1044
  },
1045
- async mergePullRequest(input3) {
1045
+ async mergePullRequest(input2) {
1046
1046
  const response = await octokit.rest.pulls.merge({
1047
- owner: input3.owner,
1048
- repo: input3.repo,
1049
- pull_number: input3.pull_number,
1050
- merge_method: input3.merge_method
1047
+ owner: input2.owner,
1048
+ repo: input2.repo,
1049
+ pull_number: input2.pull_number,
1050
+ merge_method: input2.merge_method
1051
1051
  });
1052
1052
  return {
1053
1053
  merged: response.data.merged,
@@ -1416,8 +1416,8 @@ function loadIdeaEntry(root, idOrAlias) {
1416
1416
  return { filePath, parsed: parseIdeaFile2(filePath) };
1417
1417
  }
1418
1418
  function writeIdeaFile(filePath, parsed, idea, body = parsed.body) {
1419
- const output3 = stringifyFrontmatter2({ ...parsed.raw, ...idea }, body);
1420
- fs3.writeFileSync(filePath, output3, "utf8");
1419
+ const output2 = stringifyFrontmatter2({ ...parsed.raw, ...idea }, body);
1420
+ fs3.writeFileSync(filePath, output2, "utf8");
1421
1421
  }
1422
1422
  function validateTaskForWrite(task, filePath) {
1423
1423
  const structuralIssues = validateStructural2(task, { filePath });
@@ -1574,12 +1574,12 @@ function clearWorkingContext(root, coopHome, scope) {
1574
1574
  delete next[scope];
1575
1575
  return writeWorkingContext(root, coopHome, next);
1576
1576
  }
1577
- function resolveContextValueWithSource(explicit, contextValue, sharedDefault2) {
1577
+ function resolveContextValueWithSource(explicit, contextValue2, sharedDefault2) {
1578
1578
  if (explicit?.trim()) {
1579
1579
  return { value: explicit.trim(), source: "arg" };
1580
1580
  }
1581
- if (contextValue?.trim()) {
1582
- return { value: contextValue.trim(), source: "use" };
1581
+ if (contextValue2?.trim()) {
1582
+ return { value: contextValue2.trim(), source: "use" };
1583
1583
  }
1584
1584
  if (sharedDefault2?.trim()) {
1585
1585
  return { value: sharedDefault2.trim(), source: "config" };
@@ -2143,10 +2143,11 @@ import {
2143
2143
  import { create_provider_idea_decomposer, decompose_idea_to_tasks } from "@kitsy/coop-ai";
2144
2144
 
2145
2145
  // src/utils/prompt.ts
2146
- import readline from "readline/promises";
2146
+ import readline from "readline";
2147
+ import readlinePromises from "readline/promises";
2147
2148
  import process2 from "process";
2148
2149
  async function ask(question, defaultValue = "") {
2149
- const rl = readline.createInterface({
2150
+ const rl = readlinePromises.createInterface({
2150
2151
  input: process2.stdin,
2151
2152
  output: process2.stdout
2152
2153
  });
@@ -2155,6 +2156,71 @@ async function ask(question, defaultValue = "") {
2155
2156
  rl.close();
2156
2157
  return answer || defaultValue;
2157
2158
  }
2159
+ function renderSelect(question, choices, selected) {
2160
+ process2.stdout.write(`${question}
2161
+ `);
2162
+ for (let index = 0; index < choices.length; index += 1) {
2163
+ const choice = choices[index];
2164
+ const prefix = index === selected ? ">" : " ";
2165
+ const hint = choice.hint ? ` - ${choice.hint}` : "";
2166
+ process2.stdout.write(` ${prefix} ${choice.label}${hint}
2167
+ `);
2168
+ }
2169
+ process2.stdout.write("\nUse \u2191/\u2193 to choose, Enter to confirm.\n");
2170
+ }
2171
+ function moveCursorUp(lines) {
2172
+ if (lines <= 0) return;
2173
+ readline.moveCursor(process2.stdout, 0, -lines);
2174
+ readline.clearScreenDown(process2.stdout);
2175
+ }
2176
+ async function select(question, choices, defaultIndex = 0) {
2177
+ if (choices.length === 0) {
2178
+ throw new Error(`No choices available for '${question}'.`);
2179
+ }
2180
+ if (!process2.stdin.isTTY || !process2.stdout.isTTY) {
2181
+ return choices[Math.min(Math.max(defaultIndex, 0), choices.length - 1)].value;
2182
+ }
2183
+ readline.emitKeypressEvents(process2.stdin);
2184
+ const previousRawMode = process2.stdin.isRaw;
2185
+ process2.stdin.setRawMode(true);
2186
+ let selected = Math.min(Math.max(defaultIndex, 0), choices.length - 1);
2187
+ const renderedLines = choices.length + 2;
2188
+ renderSelect(question, choices, selected);
2189
+ return await new Promise((resolve, reject) => {
2190
+ const cleanup = () => {
2191
+ process2.stdin.off("keypress", onKeypress);
2192
+ process2.stdin.setRawMode(previousRawMode ?? false);
2193
+ process2.stdout.write("\n");
2194
+ };
2195
+ const rerender = () => {
2196
+ moveCursorUp(renderedLines + 1);
2197
+ renderSelect(question, choices, selected);
2198
+ };
2199
+ const onKeypress = (_input, key) => {
2200
+ if (key.name === "up") {
2201
+ selected = selected === 0 ? choices.length - 1 : selected - 1;
2202
+ rerender();
2203
+ return;
2204
+ }
2205
+ if (key.name === "down") {
2206
+ selected = selected === choices.length - 1 ? 0 : selected + 1;
2207
+ rerender();
2208
+ return;
2209
+ }
2210
+ if (key.name === "return") {
2211
+ const value = choices[selected].value;
2212
+ cleanup();
2213
+ resolve(value);
2214
+ return;
2215
+ }
2216
+ if (key.ctrl && key.name === "c") {
2217
+ cleanup();
2218
+ reject(new Error("Prompt cancelled."));
2219
+ }
2220
+ };
2221
+ process2.stdin.on("keypress", onKeypress);
2222
+ });
2223
+ }
2158
2224
 
2159
2225
  // src/utils/idea-drafts.ts
2160
2226
  import fs5 from "fs";
@@ -2580,28 +2646,28 @@ function updateIdeaLinkedTasks(filePath, idea, raw, body, linked) {
2580
2646
  };
2581
2647
  fs7.writeFileSync(filePath, stringifyFrontmatter4(nextRaw, body), "utf8");
2582
2648
  }
2583
- function makeTaskDraft(input3) {
2649
+ function makeTaskDraft(input2) {
2584
2650
  return {
2585
- title: input3.title,
2586
- type: input3.type,
2587
- status: input3.status,
2588
- track: input3.track,
2589
- priority: input3.priority,
2590
- body: input3.body,
2591
- acceptance: unique(input3.acceptance ?? []),
2592
- testsRequired: unique(input3.testsRequired ?? []),
2593
- authorityRefs: unique(input3.authorityRefs ?? []),
2594
- derivedRefs: unique(input3.derivedRefs ?? [])
2651
+ title: input2.title,
2652
+ type: input2.type,
2653
+ status: input2.status,
2654
+ track: input2.track,
2655
+ priority: input2.priority,
2656
+ body: input2.body,
2657
+ acceptance: unique(input2.acceptance ?? []),
2658
+ testsRequired: unique(input2.testsRequired ?? []),
2659
+ authorityRefs: unique(input2.authorityRefs ?? []),
2660
+ derivedRefs: unique(input2.derivedRefs ?? [])
2595
2661
  };
2596
2662
  }
2597
2663
  function registerCreateCommand(program) {
2598
2664
  const create = program.command("create").description("Create COOP entities");
2599
- create.command("task").description("Create a task").argument("[title]", "Task title").option("--id <id>", "Task id").option("--from <idea>", "Create task(s) from an idea id/alias").option("--ai", "Use AI-assisted decomposition for --from").option("--title <title>", "Task title").option("--type <type>", `Task type (${Object.values(TaskType2).join(", ")})`).option("--status <status>", `Task status (${Object.values(TaskStatus3).join(", ")})`).option("--track <track>", "Track id").option("--priority <priority>", "Task priority").option("--body <body>", "Markdown body").option("--acceptance <items>", "Comma-separated acceptance criteria", collectMultiValue, []).option("--tests-required <items>", "Comma-separated required tests", collectMultiValue, []).option("--authority-ref <ref>", "Authority document reference", collectMultiValue, []).option("--derived-ref <ref>", "Derived planning document reference", collectMultiValue, []).option("--from-file <path>", "Create task(s) from task draft/refinement draft file").option("--stdin", "Read task draft/refinement draft from stdin").option("--interactive", "Prompt for optional fields").action(async (titleArg, options) => {
2665
+ create.command("task").description("Create a task").argument("[title]", "Task title").option("--id <id>", "Task id").option("--from <idea>", "Create task(s) from an idea id/alias").option("--ai", "Use AI-assisted decomposition for --from").option("--title <title>", "Task title").option("--type <type>", `Task type (${Object.values(TaskType2).join(", ")})`).option("--status <status>", `Task status (${Object.values(TaskStatus3).join(", ")})`).option("--track <track>", "Home/origin track id").option("--delivery <delivery>", "Primary delivery id").option("--priority <priority>", "Task priority").option("--body <body>", "Markdown body").option("--acceptance <items>", "Comma-separated acceptance criteria", collectMultiValue, []).option("--tests-required <items>", "Comma-separated required tests", collectMultiValue, []).option("--authority-ref <ref>", "Authority document reference", collectMultiValue, []).option("--derived-ref <ref>", "Derived planning document reference", collectMultiValue, []).option("--from-file <path>", "Create task(s) from task draft/refinement draft file").option("--stdin", "Read task draft/refinement draft from stdin").option("--interactive", "Prompt for optional fields").action(async (titleArg, options) => {
2600
2666
  const root = resolveRepoRoot();
2601
2667
  const coop = ensureCoopInitialized(root);
2602
2668
  const interactive = Boolean(options.interactive);
2603
2669
  if (options.fromFile?.trim() || options.stdin) {
2604
- if (options.id || options.from || options.ai || options.title || titleArg || options.type || options.status || options.track || options.priority || options.body || (options.acceptance?.length ?? 0) > 0 || (options.testsRequired?.length ?? 0) > 0 || (options.authorityRef?.length ?? 0) > 0 || (options.derivedRef?.length ?? 0) > 0) {
2670
+ if (options.id || options.from || options.ai || options.title || titleArg || options.type || options.status || options.track || options.delivery || options.priority || options.body || (options.acceptance?.length ?? 0) > 0 || (options.testsRequired?.length ?? 0) > 0 || (options.authorityRef?.length ?? 0) > 0 || (options.derivedRef?.length ?? 0) > 0) {
2605
2671
  throw new Error("Cannot combine --from-file/--stdin with direct task field flags. Use one input mode.");
2606
2672
  }
2607
2673
  const draftInput = await readDraftContent(root, {
@@ -2623,6 +2689,7 @@ function registerCreateCommand(program) {
2623
2689
  const statusInput = (options.status?.trim() || (interactive ? await ask("Task status", "todo") : "todo")).toLowerCase();
2624
2690
  const track = options.track?.trim() || (interactive ? await ask("Track", "unassigned") : "unassigned");
2625
2691
  const priority = options.priority?.trim() || (interactive ? await ask("Priority", "p2") : "p2");
2692
+ const delivery = options.delivery?.trim() || (interactive ? await ask("Delivery (optional)", "") : "");
2626
2693
  const body = options.body ?? (interactive ? await ask("Task body (optional)", "") : "");
2627
2694
  const acceptance = options.acceptance && options.acceptance.length > 0 ? unique(options.acceptance) : interactive ? parseCsv(await ask("Acceptance criteria (comma-separated, optional)", "")) : [];
2628
2695
  const testsRequired = options.testsRequired && options.testsRequired.length > 0 ? unique(options.testsRequired) : interactive ? parseCsv(await ask("Tests required (comma-separated, optional)", "")) : [];
@@ -2733,6 +2800,7 @@ function registerCreateCommand(program) {
2733
2800
  aliases: [],
2734
2801
  track: draft.track,
2735
2802
  priority: draft.priority,
2803
+ delivery: delivery || void 0,
2736
2804
  acceptance: draft.acceptance,
2737
2805
  tests_required: draft.testsRequired,
2738
2806
  origin: draft.authorityRefs.length > 0 || draft.derivedRefs.length > 0 || options.from?.trim() ? {
@@ -2983,6 +3051,27 @@ ${message}`);
2983
3051
  }
2984
3052
 
2985
3053
  // src/commands/current.ts
3054
+ function contextValue(value) {
3055
+ return value?.trim() || "unset";
3056
+ }
3057
+ function selectionScope(context) {
3058
+ if (context.delivery?.trim()) {
3059
+ return `delivery '${context.delivery.trim()}'`;
3060
+ }
3061
+ if (context.track?.trim()) {
3062
+ return `track '${context.track.trim()}'`;
3063
+ }
3064
+ return "workspace-wide (no working track or delivery set)";
3065
+ }
3066
+ function selectionReason(context, score) {
3067
+ if (context.delivery?.trim()) {
3068
+ return `top ready task for delivery '${context.delivery.trim()}' with score ${score.toFixed(1)}`;
3069
+ }
3070
+ if (context.track?.trim()) {
3071
+ return `top ready task for track '${context.track.trim()}' with score ${score.toFixed(1)}`;
3072
+ }
3073
+ return `top ready task across the workspace with score ${score.toFixed(1)}`;
3074
+ }
2986
3075
  function registerCurrentCommand(program) {
2987
3076
  program.command("current").description("Show active project, working context, my in-progress tasks, and the next ready task").action(() => {
2988
3077
  const root = resolveRepoRoot();
@@ -2994,9 +3083,14 @@ function registerCurrentCommand(program) {
2994
3083
  );
2995
3084
  console.log(`Project: ${identity.name} (${identity.id})`);
2996
3085
  console.log(`Actor: ${actor}`);
2997
- console.log(`Track: ${context.track ?? "-"}`);
2998
- console.log(`Delivery: ${context.delivery ?? "-"}`);
2999
- console.log(`Version: ${context.version ?? "-"}`);
3086
+ console.log("");
3087
+ console.log("Working Context:");
3088
+ console.log(`- Track: ${contextValue(context.track)}`);
3089
+ console.log(`- Delivery: ${contextValue(context.delivery)}`);
3090
+ console.log(`- Version: ${contextValue(context.version)}`);
3091
+ if (!context.track?.trim() && !context.delivery?.trim()) {
3092
+ console.log("- Hint: use `coop use track <id>` and/or `coop use delivery <id>` to set your working context.");
3093
+ }
3000
3094
  console.log("");
3001
3095
  console.log("My Active Tasks:");
3002
3096
  if (inProgress.length === 0) {
@@ -3014,6 +3108,11 @@ function registerCurrentCommand(program) {
3014
3108
  delivery: context.delivery,
3015
3109
  version: context.version
3016
3110
  });
3111
+ console.log(`Selection scope: ${selectionScope(context)}`);
3112
+ console.log(`Why selected: ${selectionReason(context, selected.entry.score)}`);
3113
+ if ((selected.entry.task.track ?? "").trim().toLowerCase() === "unassigned") {
3114
+ console.log("Warning: selected task has no assigned track.");
3115
+ }
3017
3116
  console.log(formatSelectedTask(selected.entry, selected.selection));
3018
3117
  } catch (error) {
3019
3118
  console.log(error instanceof Error ? error.message : String(error));
@@ -3024,7 +3123,7 @@ function registerCurrentCommand(program) {
3024
3123
  // src/commands/deps.ts
3025
3124
  import { load_graph as load_graph3 } from "@kitsy/coop-core";
3026
3125
  function registerDepsCommand(program) {
3027
- program.command("deps").description("Show dependencies and reverse dependencies for a task").argument("<id>", "Task id or alias").action((id) => {
3126
+ program.command("deps").description("Show dependencies and reverse dependencies for a task, including status and title").argument("<id>", "Task id or alias").action((id) => {
3028
3127
  const root = resolveRepoRoot();
3029
3128
  const graph = load_graph3(coopDir(root));
3030
3129
  const reference = resolveReference(root, id, "task");
@@ -3033,9 +3132,25 @@ function registerDepsCommand(program) {
3033
3132
  throw new Error(`Task '${reference.id}' not found.`);
3034
3133
  }
3035
3134
  const reverse = Array.from(graph.reverse.get(task.id) ?? []).sort((a, b) => a.localeCompare(b));
3036
- console.log(`Task: ${task.id}`);
3037
- console.log(`Depends On: ${task.depends_on && task.depends_on.length > 0 ? task.depends_on.join(", ") : "-"}`);
3038
- console.log(`Required By: ${reverse.length > 0 ? reverse.join(", ") : "-"}`);
3135
+ console.log(`Task: ${task.id} [${task.status}] ${task.title}`);
3136
+ console.log("Depends On:");
3137
+ if (!task.depends_on || task.depends_on.length === 0) {
3138
+ console.log("- none");
3139
+ } else {
3140
+ for (const depId of task.depends_on) {
3141
+ const dep = graph.nodes.get(depId);
3142
+ console.log(`- ${depId}${dep ? ` [${dep.status}] ${dep.title}` : " [missing]"}`);
3143
+ }
3144
+ }
3145
+ console.log("Required By:");
3146
+ if (reverse.length === 0) {
3147
+ console.log("- none");
3148
+ } else {
3149
+ for (const dependentId of reverse) {
3150
+ const dependent = graph.nodes.get(dependentId);
3151
+ console.log(`- ${dependentId}${dependent ? ` [${dependent.status}] ${dependent.title}` : " [missing]"}`);
3152
+ }
3153
+ }
3039
3154
  });
3040
3155
  }
3041
3156
 
@@ -3055,10 +3170,10 @@ import {
3055
3170
  function normalize(value) {
3056
3171
  return value.trim().toLowerCase();
3057
3172
  }
3058
- function resolveDelivery(graph, input3) {
3059
- const direct = graph.deliveries.get(input3);
3173
+ function resolveDelivery(graph, input2) {
3174
+ const direct = graph.deliveries.get(input2);
3060
3175
  if (direct) return direct;
3061
- const target = normalize(input3);
3176
+ const target = normalize(input2);
3062
3177
  const byId = Array.from(graph.deliveries.values()).find((delivery) => normalize(delivery.id) === target);
3063
3178
  if (byId) return byId;
3064
3179
  const byName = Array.from(graph.deliveries.values()).filter((delivery) => normalize(delivery.name) === target);
@@ -3066,9 +3181,9 @@ function resolveDelivery(graph, input3) {
3066
3181
  return byName[0];
3067
3182
  }
3068
3183
  if (byName.length > 1) {
3069
- throw new Error(`Multiple deliveries match '${input3}'. Use delivery id instead.`);
3184
+ throw new Error(`Multiple deliveries match '${input2}'. Use delivery id instead.`);
3070
3185
  }
3071
- throw new Error(`Delivery '${input3}' not found.`);
3186
+ throw new Error(`Delivery '${input2}' not found.`);
3072
3187
  }
3073
3188
 
3074
3189
  // src/commands/graph.ts
@@ -3223,6 +3338,7 @@ var catalog = {
3223
3338
  "Use `coop use show` to inspect the current user-local working defaults for track, delivery, and version.",
3224
3339
  "Use `coop graph next --delivery <delivery>` or `coop next task` to choose work. Do not reprioritize outside COOP unless the user explicitly overrides it.",
3225
3340
  "Commands resolve selection scope from: explicit CLI arg, then `coop use` working context, then shared project defaults.",
3341
+ "Use `--track` for the workstream lens (home track or delivery_tracks). Use `--delivery` for release/scope membership.",
3226
3342
  "Use `coop show <id>` or `coop show task <id>` before implementation to read acceptance, tests_required, dependencies, origin refs, and task metadata.",
3227
3343
  "Use `coop refine idea` or `coop refine task` when the task lacks planning detail. COOP owns canonical writes; agents should not edit `.coop` files directly."
3228
3344
  ],
@@ -3290,6 +3406,7 @@ var catalog = {
3290
3406
  { usage: "coop create idea --from-file idea-draft.yml", purpose: "Ingest a structured idea draft file." },
3291
3407
  { usage: "cat idea.md | coop create idea --stdin", purpose: "Ingest an idea draft from stdin." },
3292
3408
  { usage: 'coop create task "Implement webhook pipeline"', purpose: "Create a task with defaults." },
3409
+ { usage: 'coop create task --title "Lock auth contract" --track MVP --delivery MVP', purpose: "Create a task directly inside a track and delivery scope." },
3293
3410
  {
3294
3411
  usage: 'coop create task --title "Lock auth contract" --acceptance "Contract approved,Client mapping documented" --tests-required "Contract fixture test" --authority-ref docs/webapp-mvp-plan.md#auth',
3295
3412
  purpose: "Create a planning-grade task with acceptance, tests, and origin refs."
@@ -3333,15 +3450,18 @@ var catalog = {
3333
3450
  description: "Read backlog state, task details, and planning output.",
3334
3451
  commands: [
3335
3452
  { usage: "coop list tasks --status todo", purpose: "List tasks with filters." },
3336
- { usage: "coop list tasks --track MVP --delivery MVP --ready", purpose: "List ready tasks with track and delivery filtering." },
3453
+ { usage: "coop list tasks --track MVP --delivery MVP --ready --columns id,title,p,assignee,score", purpose: "List ready tasks with lean columns and score visible." },
3337
3454
  { usage: "coop list tasks --mine", purpose: "List tasks assigned to the current default COOP author." },
3338
3455
  { usage: 'coop search "auth and login form"', purpose: "Run deterministic non-AI search across tasks, ideas, and deliveries." },
3339
3456
  { usage: 'coop search "auth" --open', purpose: "Require a single match and print the resolved summary row." },
3340
3457
  { usage: "coop show PM-101", purpose: "Resolve a task, idea, or delivery by reference without an extra entity noun." },
3458
+ { usage: "coop show PM-101 --compact", purpose: "Show a smaller summary view for a large task." },
3341
3459
  { usage: "coop show task PM-101", purpose: "Show a task with acceptance, tests_required, refs, and runbook sections." },
3342
3460
  { usage: "coop show idea IDEA-101", purpose: "Show an idea." },
3343
- { usage: "coop deps PM-101", purpose: "Show task dependencies and reverse dependencies." },
3461
+ { usage: "coop deps PM-101", purpose: "Show task dependencies and reverse dependencies with status and title." },
3344
3462
  { usage: "coop prompt PM-101 --format markdown", purpose: "Generate a manual handoff prompt from a task and current working context." },
3463
+ { usage: "coop update PM-101 --track MVP --delivery MVP", purpose: "Update a task's home track or primary delivery without editing `.coop` files directly." },
3464
+ { usage: "coop update PM-101 --add-delivery-track MVP --priority-in MVP:p0", purpose: "Add a contributing track lens and scoped priority override." },
3345
3465
  { usage: "coop update PM-101 --priority p1 --add-fix-version v2", purpose: "Update task metadata without editing `.coop` files directly." },
3346
3466
  { usage: 'coop comment PM-101 --message "Needs API review"', purpose: "Append a comment to a task." },
3347
3467
  { usage: 'coop log-time PM-101 --hours 2 --kind worked --note "pairing"', purpose: "Append a planned or worked time log to a task." },
@@ -3595,6 +3715,9 @@ function renderAiHelp(format) {
3595
3715
  const heading = format === "markdown" ? "# COOP AI Help" : "COOP AI Help";
3596
3716
  lines.push(heading, "");
3597
3717
  lines.push(catalog.purpose, "");
3718
+ lines.push("Fast agent handoff:");
3719
+ lines.push("- `coop help-ai --initial-prompt --strict --repo C:/path/to/repo --delivery MVP --command coop.cmd`");
3720
+ lines.push("");
3598
3721
  const bullet = (value) => format === "markdown" ? `- ${value}` : `- ${value}`;
3599
3722
  lines.push(format === "markdown" ? "## Selection Rules" : "Selection Rules");
3600
3723
  for (const rule of catalog.selection_rules) {
@@ -3680,7 +3803,7 @@ function registerHelpAiCommand(program) {
3680
3803
  } catch {
3681
3804
  artifactsDir = "docs";
3682
3805
  }
3683
- const output3 = options.initialPrompt ? renderAiInitialPrompt({
3806
+ const output2 = options.initialPrompt ? renderAiInitialPrompt({
3684
3807
  repoPath: options.repo,
3685
3808
  delivery: options.delivery,
3686
3809
  track: options.track,
@@ -3688,7 +3811,7 @@ function registerHelpAiCommand(program) {
3688
3811
  rigour,
3689
3812
  artifactsDir
3690
3813
  }) : topic ? renderAiHelpTopic(format, topic) : renderAiHelp(format);
3691
- console.log(output3.trimEnd());
3814
+ console.log(output2.trimEnd());
3692
3815
  });
3693
3816
  }
3694
3817
  function resolveHelpTopic(options) {
@@ -3775,8 +3898,6 @@ function registerIndexCommand(program) {
3775
3898
  import fs10 from "fs";
3776
3899
  import path13 from "path";
3777
3900
  import { spawnSync as spawnSync4 } from "child_process";
3778
- import { createInterface } from "readline/promises";
3779
- import { stdin as input, stdout as output } from "process";
3780
3901
  import { CURRENT_SCHEMA_VERSION, write_schema_version } from "@kitsy/coop-core";
3781
3902
 
3782
3903
  // src/hooks/pre-commit.ts
@@ -4101,6 +4222,28 @@ function installPostMergeHook(repoRoot) {
4101
4222
  }
4102
4223
 
4103
4224
  // src/commands/init.ts
4225
+ var NAMING_TEMPLATE_PRESETS = [
4226
+ {
4227
+ label: "<TYPE>-<TITLE16>-<SEQ>",
4228
+ value: "<TYPE>-<TITLE16>-<SEQ>",
4229
+ hint: "Balanced semantic default"
4230
+ },
4231
+ {
4232
+ label: "<TYPE>-<TRACK>-<TITLE16>-<SEQ>",
4233
+ value: "<TYPE>-<TRACK>-<TITLE16>-<SEQ>",
4234
+ hint: "Include track in ids"
4235
+ },
4236
+ {
4237
+ label: "<TYPE>-<USER>-<YYMMDD>-<RAND>",
4238
+ value: "<TYPE>-<USER>-<YYMMDD>-<RAND>",
4239
+ hint: "Actor/date/random oriented"
4240
+ },
4241
+ {
4242
+ label: "Custom template",
4243
+ value: "__custom__",
4244
+ hint: "Enter a custom naming template manually"
4245
+ }
4246
+ ];
4104
4247
  function normalizeProjectId(value) {
4105
4248
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
4106
4249
  }
@@ -4321,36 +4464,27 @@ function installMergeDrivers(root) {
4321
4464
  return "Updated .gitattributes but could not register merge drivers in git config.";
4322
4465
  }
4323
4466
  async function promptInitIdentity(root, options) {
4324
- const rl = createInterface({ input, output });
4325
- const ask2 = async (question, fallback) => {
4326
- try {
4327
- const answer = await rl.question(`${question} [${fallback}]: `);
4328
- return answer.trim() || fallback;
4329
- } catch {
4330
- return fallback;
4331
- }
4332
- };
4333
4467
  const defaultName = repoDisplayName(root);
4334
- try {
4335
- const initialName = options.name?.trim() || defaultName;
4336
- const projectName = options.name?.trim() || await ask2("Project name", initialName);
4337
- const defaultId = options.id?.trim() || normalizeProjectId(projectName) || repoIdentityId(root);
4338
- const projectIdRaw = options.id?.trim() || await ask2("Project id", defaultId);
4339
- const projectId = normalizeProjectId(projectIdRaw);
4340
- if (!projectId) {
4341
- throw new Error("Invalid project id. Use letters, numbers, and hyphens.");
4342
- }
4343
- const aliasesInput = options.aliases !== void 0 ? options.aliases : await ask2("Aliases (csv, optional)", "");
4344
- const namingTemplate = options.naming?.trim() || await ask2("ID naming template", DEFAULT_ID_NAMING_TEMPLATE);
4345
- return {
4346
- projectName,
4347
- projectId,
4348
- projectAliases: parseAliases2(aliasesInput),
4349
- namingTemplate
4350
- };
4351
- } finally {
4352
- rl.close();
4468
+ const initialName = options.name?.trim() || defaultName;
4469
+ const projectName = options.name?.trim() || await ask("Project name", initialName);
4470
+ const defaultId = options.id?.trim() || normalizeProjectId(projectName) || repoIdentityId(root);
4471
+ const projectIdRaw = options.id?.trim() || await ask("Project id", defaultId);
4472
+ const projectId = normalizeProjectId(projectIdRaw);
4473
+ if (!projectId) {
4474
+ throw new Error("Invalid project id. Use letters, numbers, and hyphens.");
4475
+ }
4476
+ const aliasesInput = options.aliases !== void 0 ? options.aliases : await ask("Aliases (csv, optional)", "");
4477
+ let namingTemplate = options.naming?.trim();
4478
+ if (!namingTemplate) {
4479
+ const selected = await select("ID naming template", [...NAMING_TEMPLATE_PRESETS], 0);
4480
+ namingTemplate = selected === "__custom__" ? await ask("Custom ID naming template", DEFAULT_ID_NAMING_TEMPLATE) : selected;
4353
4481
  }
4482
+ return {
4483
+ projectName,
4484
+ projectId,
4485
+ projectAliases: parseAliases2(aliasesInput),
4486
+ namingTemplate
4487
+ };
4354
4488
  }
4355
4489
  async function resolveInitIdentity(root, options) {
4356
4490
  const defaultName = repoDisplayName(root);
@@ -4358,7 +4492,7 @@ async function resolveInitIdentity(root, options) {
4358
4492
  const fallbackId = normalizeProjectId(options.id?.trim() || fallbackName) || repoIdentityId(root);
4359
4493
  const fallbackAliases = parseAliases2(options.aliases);
4360
4494
  const fallbackNaming = options.naming?.trim() || DEFAULT_ID_NAMING_TEMPLATE;
4361
- const interactive = Boolean(options.interactive || !options.yes && input.isTTY && output.isTTY);
4495
+ const interactive = Boolean(options.interactive || !options.yes && process.stdin.isTTY && process.stdout.isTTY);
4362
4496
  if (interactive) {
4363
4497
  return promptInitIdentity(root, options);
4364
4498
  }
@@ -4435,6 +4569,8 @@ function registerInitCommand(program) {
4435
4569
  console.log(' 1. coop create idea "Describe the idea"');
4436
4570
  console.log(' 2. coop create task "Describe the task"');
4437
4571
  console.log(" 3. coop graph validate");
4572
+ console.log(` 4. coop help-ai --initial-prompt --strict --repo ${root.replace(/\\/g, "/")} --command coop.cmd`);
4573
+ console.log(" 5. after you create a delivery, add --delivery <id> to that help-ai prompt for agent handoff");
4438
4574
  });
4439
4575
  }
4440
4576
 
@@ -4501,7 +4637,14 @@ function registerLifecycleCommands(program) {
4501
4637
 
4502
4638
  // src/commands/list.ts
4503
4639
  import path15 from "path";
4504
- import { load_graph as load_graph6, parseDeliveryFile as parseDeliveryFile2, parseIdeaFile as parseIdeaFile4, parseTaskFile as parseTaskFile10, schedule_next as schedule_next3 } from "@kitsy/coop-core";
4640
+ import {
4641
+ effective_priority as effective_priority3,
4642
+ load_graph as load_graph6,
4643
+ parseDeliveryFile as parseDeliveryFile2,
4644
+ parseIdeaFile as parseIdeaFile4,
4645
+ parseTaskFile as parseTaskFile10,
4646
+ schedule_next as schedule_next3
4647
+ } from "@kitsy/coop-core";
4505
4648
  import chalk2 from "chalk";
4506
4649
  function statusColor(status) {
4507
4650
  switch (status) {
@@ -4519,8 +4662,210 @@ function statusColor(status) {
4519
4662
  return status;
4520
4663
  }
4521
4664
  }
4522
- function sortByIdAsc(items) {
4523
- return [...items].sort((a, b) => a.id.localeCompare(b.id));
4665
+ function parseColumns(input2) {
4666
+ return (input2 ?? "").split(",").map((value) => value.trim().toLowerCase()).filter(Boolean);
4667
+ }
4668
+ function normalizeTaskColumns(value, ready) {
4669
+ if (!value?.trim()) {
4670
+ return ready ? ["id", "title", "priority", "status", "assignee", "score"] : ["id", "title", "priority", "status", "assignee"];
4671
+ }
4672
+ const raw = parseColumns(value);
4673
+ if (raw.length === 1 && raw[0] === "all") {
4674
+ return ["id", "title", "priority", "status", "assignee", "track", "delivery", "score", "file"];
4675
+ }
4676
+ const normalized = raw.map((column) => {
4677
+ if (column === "p") return "priority";
4678
+ return column;
4679
+ });
4680
+ const valid = /* @__PURE__ */ new Set(["id", "title", "status", "priority", "assignee", "track", "delivery", "score", "file"]);
4681
+ for (const column of normalized) {
4682
+ if (!valid.has(column)) {
4683
+ throw new Error(`Invalid task column '${column}'. Expected id|title|status|priority|assignee|track|delivery|score|file|all.`);
4684
+ }
4685
+ }
4686
+ return normalized;
4687
+ }
4688
+ function normalizeIdeaColumns(value) {
4689
+ if (!value?.trim()) {
4690
+ return ["id", "title", "status"];
4691
+ }
4692
+ const raw = parseColumns(value);
4693
+ if (raw.length === 1 && raw[0] === "all") {
4694
+ return ["id", "title", "status", "file"];
4695
+ }
4696
+ const valid = /* @__PURE__ */ new Set(["id", "title", "status", "file"]);
4697
+ for (const column of raw) {
4698
+ if (!valid.has(column)) {
4699
+ throw new Error(`Invalid idea column '${column}'. Expected id|title|status|file|all.`);
4700
+ }
4701
+ }
4702
+ return raw;
4703
+ }
4704
+ function normalizeTaskSort(value, fallback) {
4705
+ switch ((value ?? "").trim().toLowerCase()) {
4706
+ case "":
4707
+ return fallback;
4708
+ case "id":
4709
+ case "priority":
4710
+ case "status":
4711
+ case "title":
4712
+ case "updated":
4713
+ case "created":
4714
+ case "score":
4715
+ return value.trim().toLowerCase();
4716
+ default:
4717
+ throw new Error(`Invalid sort '${value}'. Expected id|priority|status|title|updated|created|score.`);
4718
+ }
4719
+ }
4720
+ function normalizeIdeaSort(value, fallback) {
4721
+ switch ((value ?? "").trim().toLowerCase()) {
4722
+ case "":
4723
+ return fallback;
4724
+ case "id":
4725
+ case "status":
4726
+ case "title":
4727
+ case "updated":
4728
+ case "created":
4729
+ return value.trim().toLowerCase();
4730
+ default:
4731
+ throw new Error(`Invalid sort '${value}'. Expected id|status|title|updated|created.`);
4732
+ }
4733
+ }
4734
+ function priorityRank(value) {
4735
+ switch (value) {
4736
+ case "p0":
4737
+ return 0;
4738
+ case "p1":
4739
+ return 1;
4740
+ case "p2":
4741
+ return 2;
4742
+ case "p3":
4743
+ return 3;
4744
+ default:
4745
+ return 4;
4746
+ }
4747
+ }
4748
+ function taskStatusRank(value) {
4749
+ switch (value) {
4750
+ case "in_progress":
4751
+ return 0;
4752
+ case "in_review":
4753
+ return 1;
4754
+ case "todo":
4755
+ return 2;
4756
+ case "blocked":
4757
+ return 3;
4758
+ case "done":
4759
+ return 4;
4760
+ case "canceled":
4761
+ return 5;
4762
+ default:
4763
+ return 6;
4764
+ }
4765
+ }
4766
+ function ideaStatusRank(value) {
4767
+ switch (value) {
4768
+ case "active":
4769
+ return 0;
4770
+ case "captured":
4771
+ return 1;
4772
+ case "validated":
4773
+ return 2;
4774
+ case "rejected":
4775
+ return 3;
4776
+ case "promoted":
4777
+ return 4;
4778
+ default:
4779
+ return 5;
4780
+ }
4781
+ }
4782
+ function compareDatesDesc(a, b) {
4783
+ return (b ?? "").localeCompare(a ?? "");
4784
+ }
4785
+ function compareTaskScoreLike(a, b, readyOrder, track) {
4786
+ const aReady = readyOrder.get(a.id);
4787
+ const bReady = readyOrder.get(b.id);
4788
+ if (aReady !== void 0 || bReady !== void 0) {
4789
+ if (aReady === void 0) return 1;
4790
+ if (bReady === void 0) return -1;
4791
+ return aReady - bReady;
4792
+ }
4793
+ const status = taskStatusRank(a.status) - taskStatusRank(b.status);
4794
+ if (status !== 0) return status;
4795
+ const priority = priorityRank(effective_priority3(b.task, track)) - priorityRank(effective_priority3(a.task, track));
4796
+ if (priority !== 0) return priority;
4797
+ const updated = compareDatesDesc(a.task.updated, b.task.updated);
4798
+ if (updated !== 0) return updated;
4799
+ return a.id.localeCompare(b.id);
4800
+ }
4801
+ function sortTaskRows(rows, sortMode, readyOrder, track) {
4802
+ return [...rows].sort((a, b) => {
4803
+ switch (sortMode) {
4804
+ case "score":
4805
+ return compareTaskScoreLike(a, b, readyOrder, track);
4806
+ case "priority": {
4807
+ const priority = priorityRank(a.priority) - priorityRank(b.priority);
4808
+ if (priority !== 0) return priority;
4809
+ return a.id.localeCompare(b.id);
4810
+ }
4811
+ case "status": {
4812
+ const status = taskStatusRank(a.status) - taskStatusRank(b.status);
4813
+ if (status !== 0) return status;
4814
+ return a.id.localeCompare(b.id);
4815
+ }
4816
+ case "title":
4817
+ return a.title.localeCompare(b.title);
4818
+ case "updated": {
4819
+ const updated = compareDatesDesc(a.task.updated, b.task.updated);
4820
+ if (updated !== 0) return updated;
4821
+ return a.id.localeCompare(b.id);
4822
+ }
4823
+ case "created": {
4824
+ const created = compareDatesDesc(a.task.created, b.task.created);
4825
+ if (created !== 0) return created;
4826
+ return a.id.localeCompare(b.id);
4827
+ }
4828
+ case "id":
4829
+ default:
4830
+ return a.id.localeCompare(b.id);
4831
+ }
4832
+ });
4833
+ }
4834
+ function taskColumnHeader(column) {
4835
+ switch (column) {
4836
+ case "priority":
4837
+ return "P";
4838
+ case "assignee":
4839
+ return "Assignee";
4840
+ case "track":
4841
+ return "Track";
4842
+ case "delivery":
4843
+ return "Delivery";
4844
+ case "score":
4845
+ return "Score";
4846
+ case "file":
4847
+ return "File";
4848
+ case "status":
4849
+ return "Status";
4850
+ case "title":
4851
+ return "Title";
4852
+ case "id":
4853
+ default:
4854
+ return "ID";
4855
+ }
4856
+ }
4857
+ function ideaColumnHeader(column) {
4858
+ switch (column) {
4859
+ case "file":
4860
+ return "File";
4861
+ case "status":
4862
+ return "Status";
4863
+ case "title":
4864
+ return "Title";
4865
+ case "id":
4866
+ default:
4867
+ return "ID";
4868
+ }
4524
4869
  }
4525
4870
  function loadTasks2(root) {
4526
4871
  return listTaskFiles(root).map((filePath) => ({
@@ -4539,24 +4884,30 @@ function listTasks(options) {
4539
4884
  ensureCoopInitialized(root);
4540
4885
  const context = readWorkingContext(root, resolveCoopHome());
4541
4886
  const graph = load_graph6(coopDir(root));
4542
- const resolvedTrack = resolveContextValueWithSource(options.track, context.track, sharedDefault(root, "track"));
4543
- const resolvedDelivery = resolveContextValueWithSource(options.delivery, context.delivery, sharedDefault(root, "delivery"));
4887
+ const resolvedTrack = resolveContextValueWithSource(options.track, context.track);
4888
+ const resolvedDelivery = resolveContextValueWithSource(options.delivery, context.delivery);
4889
+ const resolvedVersion = resolveContextValueWithSource(options.version, context.version);
4544
4890
  const assignee = options.mine ? defaultCoopAuthor(root) : options.assignee?.trim();
4545
4891
  const deliveryScope = resolvedDelivery.value ? new Set(graph.deliveries.get(resolvedDelivery.value)?.scope.include ?? []) : null;
4892
+ const defaultSort = options.ready || resolvedTrack.value || resolvedDelivery.value ? "score" : "id";
4893
+ const sortMode = normalizeTaskSort(options.sort, defaultSort);
4894
+ const columns = normalizeTaskColumns(options.columns, Boolean(options.ready));
4546
4895
  if (isVerboseRequested()) {
4547
4896
  for (const line of formatResolvedContextMessage({
4548
4897
  track: resolvedTrack,
4549
- delivery: resolvedDelivery
4898
+ delivery: resolvedDelivery,
4899
+ version: resolvedVersion
4550
4900
  })) {
4551
4901
  console.log(line);
4552
4902
  }
4553
4903
  }
4554
- const readyEntries = options.ready ? schedule_next3(load_graph6(ensureCoopInitialized(root)), {
4904
+ const scoredEntries = sortMode === "score" || options.ready ? schedule_next3(graph, {
4555
4905
  track: resolvedTrack.value,
4556
4906
  delivery: resolvedDelivery.value
4557
- }) : null;
4558
- const readyIds = readyEntries ? new Set(readyEntries.map((entry) => entry.task.id)) : null;
4559
- const readyOrder = readyEntries ? new Map(readyEntries.map((entry, index) => [entry.task.id, index])) : null;
4907
+ }) : [];
4908
+ const readyIds = options.ready ? new Set(scoredEntries.map((entry) => entry.task.id)) : null;
4909
+ const readyOrder = new Map(scoredEntries.map((entry, index) => [entry.task.id, index]));
4910
+ const scoreMap = new Map(scoredEntries.map((entry) => [entry.task.id, entry.score]));
4560
4911
  const rows = loadTasks2(root).filter(({ task }) => {
4561
4912
  if (readyIds && !readyIds.has(task.id)) return false;
4562
4913
  if (options.status && task.status !== options.status) return false;
@@ -4566,11 +4917,12 @@ function listTasks(options) {
4566
4917
  if (resolvedDelivery.value && task.delivery !== resolvedDelivery.value && !deliveryScope?.has(task.id)) return false;
4567
4918
  if (options.priority && taskEffectivePriority(task, resolvedTrack.value) !== options.priority) return false;
4568
4919
  if (assignee && (task.assignee ?? "") !== assignee) return false;
4569
- if (options.version && !(task.fix_versions ?? []).includes(options.version) && !(task.released_in ?? []).includes(options.version)) {
4920
+ if (resolvedVersion.value && !(task.fix_versions ?? []).includes(resolvedVersion.value) && !(task.released_in ?? []).includes(resolvedVersion.value)) {
4570
4921
  return false;
4571
4922
  }
4572
4923
  return true;
4573
4924
  }).map(({ task, filePath }) => ({
4925
+ task,
4574
4926
  id: task.id,
4575
4927
  title: task.title,
4576
4928
  status: task.status,
@@ -4580,24 +4932,39 @@ function listTasks(options) {
4580
4932
  delivery: task.delivery ?? "-",
4581
4933
  filePath
4582
4934
  }));
4583
- const sorted = readyOrder ? [...rows].sort((a, b) => (readyOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (readyOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER)) : sortByIdAsc(rows);
4935
+ const sorted = sortTaskRows(rows, sortMode, readyOrder, resolvedTrack.value);
4584
4936
  if (sorted.length === 0) {
4585
4937
  console.log("No tasks found.");
4586
4938
  return;
4587
4939
  }
4588
4940
  console.log(
4589
4941
  formatTable(
4590
- ["ID", "Title", "Status", "Priority", "Track", "Assignee", "Delivery", "File"],
4591
- sorted.map((entry) => [
4592
- entry.id,
4593
- entry.title,
4594
- statusColor(entry.status),
4595
- entry.priority,
4596
- entry.track,
4597
- entry.assignee,
4598
- entry.delivery,
4599
- path15.relative(root, entry.filePath)
4600
- ])
4942
+ columns.map(taskColumnHeader),
4943
+ sorted.map(
4944
+ (entry) => columns.map((column) => {
4945
+ switch (column) {
4946
+ case "title":
4947
+ return entry.title;
4948
+ case "status":
4949
+ return statusColor(entry.status);
4950
+ case "priority":
4951
+ return entry.priority;
4952
+ case "assignee":
4953
+ return entry.assignee;
4954
+ case "track":
4955
+ return entry.track;
4956
+ case "delivery":
4957
+ return entry.delivery;
4958
+ case "score":
4959
+ return scoreMap.has(entry.id) ? scoreMap.get(entry.id).toFixed(1) : "-";
4960
+ case "file":
4961
+ return path15.relative(root, entry.filePath);
4962
+ case "id":
4963
+ default:
4964
+ return entry.id;
4965
+ }
4966
+ })
4967
+ )
4601
4968
  )
4602
4969
  );
4603
4970
  console.log(`
@@ -4606,6 +4973,8 @@ Total tasks: ${sorted.length}`);
4606
4973
  function listIdeas(options) {
4607
4974
  const root = resolveRepoRoot();
4608
4975
  ensureCoopInitialized(root);
4976
+ const sortMode = normalizeIdeaSort(options.sort, "id");
4977
+ const columns = normalizeIdeaColumns(options.columns);
4609
4978
  const rows = loadIdeas(root).filter(({ idea }) => {
4610
4979
  if (options.status && idea.status !== options.status) return false;
4611
4980
  return true;
@@ -4617,22 +4986,45 @@ function listIdeas(options) {
4617
4986
  track: "-",
4618
4987
  filePath
4619
4988
  }));
4620
- const sorted = sortByIdAsc(rows);
4989
+ const sorted = [...rows].sort((a, b) => {
4990
+ switch (sortMode) {
4991
+ case "status": {
4992
+ const status = ideaStatusRank(a.status) - ideaStatusRank(b.status);
4993
+ if (status !== 0) return status;
4994
+ return a.id.localeCompare(b.id);
4995
+ }
4996
+ case "title":
4997
+ return a.title.localeCompare(b.title);
4998
+ case "updated":
4999
+ case "created":
5000
+ return a.id.localeCompare(b.id);
5001
+ case "id":
5002
+ default:
5003
+ return a.id.localeCompare(b.id);
5004
+ }
5005
+ });
4621
5006
  if (sorted.length === 0) {
4622
5007
  console.log("No ideas found.");
4623
5008
  return;
4624
5009
  }
4625
5010
  console.log(
4626
5011
  formatTable(
4627
- ["ID", "Title", "Status", "Priority", "Track", "File"],
4628
- sorted.map((entry) => [
4629
- entry.id,
4630
- entry.title,
4631
- statusColor(entry.status),
4632
- entry.priority,
4633
- entry.track,
4634
- path15.relative(root, entry.filePath)
4635
- ])
5012
+ columns.map(ideaColumnHeader),
5013
+ sorted.map(
5014
+ (entry) => columns.map((column) => {
5015
+ switch (column) {
5016
+ case "title":
5017
+ return entry.title;
5018
+ case "status":
5019
+ return statusColor(entry.status);
5020
+ case "file":
5021
+ return path15.relative(root, entry.filePath);
5022
+ case "id":
5023
+ default:
5024
+ return entry.id;
5025
+ }
5026
+ })
5027
+ )
4636
5028
  )
4637
5029
  );
4638
5030
  console.log(`
@@ -4655,10 +5047,10 @@ function listDeliveries() {
4655
5047
  }
4656
5048
  function registerListCommand(program) {
4657
5049
  const list = program.command("list").description("List COOP entities");
4658
- list.command("tasks").description("List tasks").option("--status <status>", "Filter by status").option("--track <track>", "Filter by track, using `coop use track` if omitted").option("--delivery <delivery>", "Filter by delivery, using `coop use delivery` if omitted").option("--priority <priority>", "Filter by effective priority").option("--assignee <assignee>", "Filter by assignee").option("--version <version>", "Filter by fix/released version, using `coop use version` if omitted").option("--mine", "Filter to the current default COOP author").option("--ready", "Only list ready tasks in scored order").action((options) => {
5050
+ list.command("tasks").description("List tasks").option("--status <status>", "Filter by status").option("--track <track>", "Filter by home/contributing track lens, using `coop use track` if omitted").option("--delivery <delivery>", "Filter by delivery membership, using `coop use delivery` if omitted").option("--priority <priority>", "Filter by effective priority").option("--assignee <assignee>", "Filter by assignee").option("--version <version>", "Filter by fix/released version, using `coop use version` if omitted").option("--mine", "Filter to the current default COOP author").option("--ready", "Only list ready tasks in scored order").option("--sort <sort>", "Sort by id|priority|status|title|updated|created|score").option("--columns <columns>", "Columns: id,title,status,priority,assignee,track,delivery,score,file or all").action((options) => {
4659
5051
  listTasks(options);
4660
5052
  });
4661
- list.command("ideas").description("List ideas").option("--status <status>", "Filter by status").action((options) => {
5053
+ list.command("ideas").description("List ideas").option("--status <status>", "Filter by status").option("--sort <sort>", "Sort by id|status|title|updated|created").option("--columns <columns>", "Columns: id,title,status,file or all").action((options) => {
4662
5054
  listIdeas(options);
4663
5055
  });
4664
5056
  list.command("alias").description("List aliases").argument("[pattern]", "Wildcard pattern, e.g. PAY*").action((pattern) => {
@@ -4672,17 +5064,30 @@ function registerListCommand(program) {
4672
5064
  // src/utils/logger.ts
4673
5065
  import fs11 from "fs";
4674
5066
  import path16 from "path";
4675
- function resolveRepoSafe(start = process.cwd()) {
4676
- try {
4677
- return resolveRepoRoot(start);
4678
- } catch {
4679
- return path16.resolve(start);
5067
+ function resolveWorkspaceRoot(start = process.cwd()) {
5068
+ let current = path16.resolve(start);
5069
+ while (true) {
5070
+ const gitDir = path16.join(current, ".git");
5071
+ const coopDir2 = coopWorkspaceDir(current);
5072
+ const workspaceConfig = path16.join(coopDir2, "config.yml");
5073
+ const projectsDir = path16.join(coopDir2, "projects");
5074
+ if (fs11.existsSync(gitDir)) {
5075
+ return current;
5076
+ }
5077
+ if (fs11.existsSync(workspaceConfig) || fs11.existsSync(projectsDir)) {
5078
+ return current;
5079
+ }
5080
+ const parent = path16.dirname(current);
5081
+ if (parent === current) {
5082
+ return null;
5083
+ }
5084
+ current = parent;
4680
5085
  }
4681
5086
  }
4682
5087
  function resolveCliLogFile(start = process.cwd()) {
4683
- const root = resolveRepoSafe(start);
4684
- const workspace = coopWorkspaceDir(root);
4685
- if (fs11.existsSync(workspace)) {
5088
+ const root = resolveWorkspaceRoot(start);
5089
+ if (root) {
5090
+ const workspace = coopWorkspaceDir(root);
4686
5091
  return path16.join(workspace, "logs", "cli.log");
4687
5092
  }
4688
5093
  return path16.join(resolveCoopHome(), "logs", "cli.log");
@@ -4794,8 +5199,8 @@ function registerLogTimeCommand(program) {
4794
5199
  // src/commands/migrate.ts
4795
5200
  import fs12 from "fs";
4796
5201
  import path17 from "path";
4797
- import { createInterface as createInterface2 } from "readline/promises";
4798
- import { stdin as input2, stdout as output2 } from "process";
5202
+ import { createInterface } from "readline/promises";
5203
+ import { stdin as input, stdout as output } from "process";
4799
5204
  import { CURRENT_SCHEMA_VERSION as CURRENT_SCHEMA_VERSION2, IndexManager as IndexManager3, migrate_repository, parseYamlFile as parseYamlFile2, writeYamlFile as writeYamlFile4 } from "@kitsy/coop-core";
4800
5205
  var COOP_IGNORE_TEMPLATE2 = `.index/
4801
5206
  logs/
@@ -4868,7 +5273,7 @@ function parseAliases3(value) {
4868
5273
  );
4869
5274
  }
4870
5275
  async function promptProjectIdentity(defaults, options) {
4871
- const rl = createInterface2({ input: input2, output: output2 });
5276
+ const rl = createInterface({ input, output });
4872
5277
  const ask2 = async (question, fallback) => {
4873
5278
  try {
4874
5279
  const answer = await rl.question(`${question} [${fallback}]: `);
@@ -4901,7 +5306,7 @@ async function resolveMigrationIdentity(root, options) {
4901
5306
  projectId: normalizeProjectId2(existing.projectId || repoIdentityId(root)) || repoIdentityId(root),
4902
5307
  projectAliases: options.aliases !== void 0 ? parseAliases3(options.aliases) : existing.projectAliases
4903
5308
  };
4904
- const interactive = Boolean(options.interactive || !options.yes && input2.isTTY && output2.isTTY);
5309
+ const interactive = Boolean(options.interactive || !options.yes && input.isTTY && output.isTTY);
4905
5310
  if (interactive) {
4906
5311
  return promptProjectIdentity(defaults, options);
4907
5312
  }
@@ -5638,15 +6043,15 @@ function registerPromptCommand(program) {
5638
6043
  const root = resolveRepoRoot();
5639
6044
  const payload = buildPayload(root, id);
5640
6045
  const format = options.format ?? "text";
5641
- let output3 = "";
6046
+ let output2 = "";
5642
6047
  if (format === "json") {
5643
- output3 = `${JSON.stringify(payload, null, 2)}
6048
+ output2 = `${JSON.stringify(payload, null, 2)}
5644
6049
  `;
5645
6050
  } else {
5646
- output3 = renderMarkdown(payload);
6051
+ output2 = renderMarkdown(payload);
5647
6052
  }
5648
6053
  if (options.save) {
5649
- fs14.writeFileSync(options.save, output3, "utf8");
6054
+ fs14.writeFileSync(options.save, output2, "utf8");
5650
6055
  }
5651
6056
  if (isVerboseRequested()) {
5652
6057
  for (const line of formatResolvedContextMessage({
@@ -5657,7 +6062,7 @@ function registerPromptCommand(program) {
5657
6062
  console.log(line);
5658
6063
  }
5659
6064
  }
5660
- console.log(output3.trimEnd());
6065
+ console.log(output2.trimEnd());
5661
6066
  });
5662
6067
  }
5663
6068
 
@@ -6244,13 +6649,31 @@ function formatTimeSummary(task) {
6244
6649
  const plannedLogged = (task.time?.logs ?? []).filter((entry) => entry.kind === "planned").reduce((sum, entry) => sum + entry.hours, 0);
6245
6650
  return `planned_hours=${planned ?? "-"} | planned_logged=${plannedLogged || 0} | worked=${worked || 0}`;
6246
6651
  }
6247
- function showTask(taskId) {
6652
+ function showTask(taskId, options = {}) {
6248
6653
  const root = resolveRepoRoot();
6249
6654
  const context = readWorkingContext(root, resolveCoopHome());
6250
6655
  const { filePath, parsed } = loadTaskEntry(root, taskId);
6251
6656
  const task = parsed.task;
6252
6657
  const body = parsed.body.trim();
6253
6658
  const computed = loadComputedFromIndex(root, task.id);
6659
+ if (options.compact) {
6660
+ const compactLines = [
6661
+ `Task: ${task.id}`,
6662
+ `Title: ${task.title}`,
6663
+ `Status: ${task.status}`,
6664
+ `Priority: ${task.priority ?? "-"}`,
6665
+ `Effective Priority: ${taskEffectivePriority(task, context.track)}`,
6666
+ `Track: ${task.track ?? "-"}`,
6667
+ `Delivery: ${task.delivery ?? "-"}`,
6668
+ `Assignee: ${task.assignee ?? "-"}`,
6669
+ `Depends On: ${stringify(task.depends_on)}`,
6670
+ `Acceptance: ${task.acceptance && task.acceptance.length > 0 ? task.acceptance.join(" | ") : "-"}`,
6671
+ `Tests Required: ${task.tests_required && task.tests_required.length > 0 ? task.tests_required.join(", ") : "-"}`,
6672
+ `File: ${path22.relative(root, filePath)}`
6673
+ ];
6674
+ console.log(compactLines.join("\n"));
6675
+ return;
6676
+ }
6254
6677
  const lines = [
6255
6678
  `Task: ${task.id}`,
6256
6679
  `Title: ${task.title}`,
@@ -6315,13 +6738,26 @@ function showTask(taskId) {
6315
6738
  }
6316
6739
  console.log(lines.join("\n"));
6317
6740
  }
6318
- function showIdea(ideaId) {
6741
+ function showIdea(ideaId, options = {}) {
6319
6742
  const root = resolveRepoRoot();
6320
6743
  const target = resolveReference(root, ideaId, "idea");
6321
6744
  const ideaFile = path22.join(root, ...target.file.split("/"));
6322
6745
  const parsed = parseIdeaFile7(ideaFile);
6323
6746
  const idea = parsed.idea;
6324
6747
  const body = parsed.body.trim();
6748
+ if (options.compact) {
6749
+ console.log(
6750
+ [
6751
+ `Idea: ${idea.id}`,
6752
+ `Title: ${idea.title}`,
6753
+ `Status: ${idea.status}`,
6754
+ `Tags: ${stringify(idea.tags)}`,
6755
+ `Linked Tasks: ${stringify(idea.linked_tasks)}`,
6756
+ `File: ${path22.relative(root, ideaFile)}`
6757
+ ].join("\n")
6758
+ );
6759
+ return;
6760
+ }
6325
6761
  const lines = [
6326
6762
  `Idea: ${idea.id}`,
6327
6763
  `Title: ${idea.title}`,
@@ -6339,9 +6775,22 @@ function showIdea(ideaId) {
6339
6775
  ];
6340
6776
  console.log(lines.join("\n"));
6341
6777
  }
6342
- function showDelivery(ref) {
6778
+ function showDelivery(ref, options = {}) {
6343
6779
  const root = resolveRepoRoot();
6344
6780
  const { filePath, delivery, body } = resolveDeliveryEntry(root, ref);
6781
+ if (options.compact) {
6782
+ console.log(
6783
+ [
6784
+ `Delivery: ${delivery.id}`,
6785
+ `Name: ${delivery.name}`,
6786
+ `Status: ${delivery.status}`,
6787
+ `Target Date: ${delivery.target_date ?? "-"}`,
6788
+ `Scope Include: ${delivery.scope.include.length > 0 ? delivery.scope.include.join(", ") : "-"}`,
6789
+ `File: ${path22.relative(root, filePath)}`
6790
+ ].join("\n")
6791
+ );
6792
+ return;
6793
+ }
6345
6794
  const lines = [
6346
6795
  `Delivery: ${delivery.id}`,
6347
6796
  `Name: ${delivery.name}`,
@@ -6359,35 +6808,35 @@ function showDelivery(ref) {
6359
6808
  ];
6360
6809
  console.log(lines.join("\n"));
6361
6810
  }
6362
- function showByReference(ref) {
6811
+ function showByReference(ref, options = {}) {
6363
6812
  const root = resolveRepoRoot();
6364
6813
  try {
6365
6814
  const resolved = resolveReference(root, ref);
6366
6815
  if (resolved.type === "task") {
6367
- showTask(ref);
6816
+ showTask(ref, options);
6368
6817
  return;
6369
6818
  }
6370
- showIdea(ref);
6819
+ showIdea(ref, options);
6371
6820
  return;
6372
6821
  } catch {
6373
- showDelivery(ref);
6822
+ showDelivery(ref, options);
6374
6823
  }
6375
6824
  }
6376
6825
  function registerShowCommand(program) {
6377
- const show = program.command("show").description("Show detailed COOP entities").argument("[ref]", "Task, idea, or delivery reference").action((ref) => {
6826
+ const show = program.command("show").description("Show detailed COOP entities").argument("[ref]", "Task, idea, or delivery reference").option("--compact", "Show a smaller summary view").action((ref, options) => {
6378
6827
  if (!ref?.trim()) {
6379
6828
  throw new Error("Provide a task, idea, or delivery reference.");
6380
6829
  }
6381
- showByReference(ref);
6830
+ showByReference(ref, options);
6382
6831
  });
6383
- show.command("task").description("Show task details").argument("<id>", "Task ID").action((id) => {
6384
- showTask(id);
6832
+ show.command("task").description("Show task details").argument("<id>", "Task ID").option("--compact", "Show a smaller summary view").action((id, options) => {
6833
+ showTask(id, options);
6385
6834
  });
6386
- show.command("idea").description("Show idea details").argument("<id>", "Idea ID").action((id) => {
6387
- showIdea(id);
6835
+ show.command("idea").description("Show idea details").argument("<id>", "Idea ID").option("--compact", "Show a smaller summary view").action((id, options) => {
6836
+ showIdea(id, options);
6388
6837
  });
6389
- show.command("delivery").description("Show delivery details").argument("<id>", "Delivery id or name").action((id) => {
6390
- showDelivery(id);
6838
+ show.command("delivery").description("Show delivery details").argument("<id>", "Delivery id or name").option("--compact", "Show a smaller summary view").action((id, options) => {
6839
+ showDelivery(id, options);
6391
6840
  });
6392
6841
  }
6393
6842
 
@@ -6581,7 +7030,7 @@ function printResolvedSelectionContext(root, options) {
6581
7030
  }
6582
7031
  function registerTaskFlowCommands(program) {
6583
7032
  const next = program.command("next").description("Select the next COOP work item");
6584
- next.command("task").description("Show the top ready task").option("--track <track>", "Filter by track, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid)").option("--today <date>", "Evaluation date (YYYY-MM-DD)").action((options) => {
7033
+ next.command("task").description("Show the top ready task").option("--track <track>", "Filter by the home/contributing track lens, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery membership").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid)").option("--today <date>", "Evaluation date (YYYY-MM-DD)").action((options) => {
6585
7034
  const root = resolveRepoRoot();
6586
7035
  printResolvedSelectionContext(root, options);
6587
7036
  const selected = selectTopReadyTask(root, {
@@ -6593,7 +7042,7 @@ function registerTaskFlowCommands(program) {
6593
7042
  console.log(formatSelectedTask(selected.entry, selected.selection));
6594
7043
  });
6595
7044
  const pick = program.command("pick").description("Pick the next COOP work item");
6596
- pick.command("task").description("Select the top ready task and move it into active work").argument("[id]", "Task ID or alias").option("--track <track>", "Filter by track, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid)").option("--today <date>", "Evaluation date (YYYY-MM-DD)").option("--promote", "Promote the task in the current track/version context before starting it").option("--to <assignee>", "Assign the selected task before starting it").option("--claim", "Assign the selected task to the actor/user/default author before starting it").option("--actor <actor>", "Actor performing assignment/transition").option("--user <user>", "Current user for advisory authorization checks").option("--force", "Override advisory authorization checks").action(async (id, options) => {
7045
+ pick.command("task").description("Select the top ready task and move it into active work").argument("[id]", "Task ID or alias").option("--track <track>", "Filter by the home/contributing track lens, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery membership").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid)").option("--today <date>", "Evaluation date (YYYY-MM-DD)").option("--promote", "Promote the task in the current track/version context before starting it").option("--to <assignee>", "Assign the selected task before starting it").option("--claim", "Assign the selected task to the actor/user/default author before starting it").option("--actor <actor>", "Actor performing assignment/transition").option("--user <user>", "Current user for advisory authorization checks").option("--force", "Override advisory authorization checks").action(async (id, options) => {
6597
7046
  const root = resolveRepoRoot();
6598
7047
  printResolvedSelectionContext(root, options);
6599
7048
  const selected = id?.trim() ? {
@@ -6623,7 +7072,7 @@ function registerTaskFlowCommands(program) {
6623
7072
  await claimAndStart(root, selected.entry.task.id, options);
6624
7073
  });
6625
7074
  const start = program.command("start").description("Start COOP work on a task");
6626
- start.command("task").description("Start a specific task, or the top ready task if no id is provided").argument("[id]", "Task ID or alias").option("--track <track>", "Filter by track when no id is provided, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery when no id is provided").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid) when no id is provided").option("--today <date>", "Evaluation date (YYYY-MM-DD) when no id is provided").option("--promote", "Promote the task in the current track/version context before starting it").option("--to <assignee>", "Assign the task before starting it").option("--claim", "Assign the task to the actor/user/default author before starting it").option("--actor <actor>", "Actor performing assignment/transition").option("--user <user>", "Current user for advisory authorization checks").option("--force", "Override advisory authorization checks").action(async (id, options) => {
7075
+ start.command("task").description("Start a specific task, or the top ready task if no id is provided").argument("[id]", "Task ID or alias").option("--track <track>", "Filter by the home/contributing track lens when no id is provided, else config.defaults.track if set").option("--delivery <delivery>", "Filter by delivery membership when no id is provided").option("--executor <executor>", "Filter by executor (human|ai|ci|hybrid) when no id is provided").option("--today <date>", "Evaluation date (YYYY-MM-DD) when no id is provided").option("--promote", "Promote the task in the current track/version context before starting it").option("--to <assignee>", "Assign the task before starting it").option("--claim", "Assign the task to the actor/user/default author before starting it").option("--actor <actor>", "Actor performing assignment/transition").option("--user <user>", "Current user for advisory authorization checks").option("--force", "Override advisory authorization checks").action(async (id, options) => {
6627
7076
  const root = resolveRepoRoot();
6628
7077
  printResolvedSelectionContext(root, options);
6629
7078
  const taskId = id?.trim() || selectTopReadyTask(root, {
@@ -6899,7 +7348,7 @@ function updateIdea(id, options) {
6899
7348
  console.log(`Updated ${next.id}`);
6900
7349
  }
6901
7350
  function registerUpdateCommand(program) {
6902
- program.command("update").description("Update an existing COOP task or idea").argument("<id-or-type>", "Task or idea id/alias, or an explicit entity type").argument("[id]", "Entity id when an explicit type is provided").option("--title <title>").option("--priority <priority>").option("--status <status>").option("--assign <user>").option("--track <id>").option("--delivery <id>").option("--story-points <n>").option("--planned-hours <n>").option("--add-delivery-track <id>", "", collect, []).option("--remove-delivery-track <id>", "", collect, []).option("--priority-in <track:priority>", "", collect, []).option("--clear-priority-in <track>", "", collect, []).option("--add-dep <id>", "", collect, []).option("--remove-dep <id>", "", collect, []).option("--add-tag <tag>", "", collect, []).option("--remove-tag <tag>", "", collect, []).option("--add-fix-version <v>", "", collect, []).option("--remove-fix-version <v>", "", collect, []).option("--add-released-in <v>", "", collect, []).option("--remove-released-in <v>", "", collect, []).option("--acceptance-add <text>", "", collect, []).option("--acceptance-remove <text>", "", collect, []).option("--tests-add <text>", "", collect, []).option("--tests-remove <text>", "", collect, []).option("--add-linked-task <id>", "", collect, []).option("--remove-linked-task <id>", "", collect, []).option("--body-file <path>").option("--body-stdin").option("--dry-run").action((first, second, options) => {
7351
+ program.command("update").description("Update an existing COOP task or idea").argument("<id-or-type>", "Task or idea id/alias, or an explicit entity type").argument("[id]", "Entity id when an explicit type is provided").option("--title <title>").option("--priority <priority>").option("--status <status>").option("--assign <user>").option("--track <id>", "Set the task home/origin track").option("--delivery <id>", "Set the task primary delivery id").option("--story-points <n>").option("--planned-hours <n>").option("--add-delivery-track <id>", "", collect, []).option("--remove-delivery-track <id>", "", collect, []).option("--priority-in <track:priority>", "", collect, []).option("--clear-priority-in <track>", "", collect, []).option("--add-dep <id>", "", collect, []).option("--remove-dep <id>", "", collect, []).option("--add-tag <tag>", "", collect, []).option("--remove-tag <tag>", "", collect, []).option("--add-fix-version <v>", "", collect, []).option("--remove-fix-version <v>", "", collect, []).option("--add-released-in <v>", "", collect, []).option("--remove-released-in <v>", "", collect, []).option("--acceptance-add <text>", "", collect, []).option("--acceptance-remove <text>", "", collect, []).option("--tests-add <text>", "", collect, []).option("--tests-remove <text>", "", collect, []).option("--add-linked-task <id>", "", collect, []).option("--remove-linked-task <id>", "", collect, []).option("--body-file <path>").option("--body-stdin").option("--dry-run").action((first, second, options) => {
6903
7352
  let resolved = resolveOptionalEntityArg(first, second, ["task", "idea"], "task");
6904
7353
  if (!second && resolved.entity === "task") {
6905
7354
  try {
@@ -6923,9 +7372,17 @@ function registerUpdateCommand(program) {
6923
7372
 
6924
7373
  // src/commands/use.ts
6925
7374
  function printContext(values) {
6926
- console.log(`Track: ${values.track ?? "-"}`);
6927
- console.log(`Delivery: ${values.delivery ?? "-"}`);
6928
- console.log(`Version: ${values.version ?? "-"}`);
7375
+ const track = values.track?.trim() || "unset";
7376
+ const delivery = values.delivery?.trim() || "unset";
7377
+ const version = values.version?.trim() || "unset";
7378
+ console.log("Working Context:");
7379
+ console.log(`- Track: ${track}`);
7380
+ console.log(`- Delivery: ${delivery}`);
7381
+ console.log(`- Version: ${version}`);
7382
+ if (track === "unset" && delivery === "unset" && version === "unset") {
7383
+ console.log("");
7384
+ console.log("Hint: use `coop use track <id>`, `coop use delivery <id>`, or `coop use version <id>`.");
7385
+ }
6929
7386
  }
6930
7387
  function registerUseCommand(program) {
6931
7388
  const use = program.command("use").description("Manage user-local working defaults for the current COOP project");
@@ -7352,16 +7809,16 @@ function chooseFieldValue(key, baseValue, oursValue, theirsValue, oursUpdated, t
7352
7809
  }
7353
7810
  function mergeTaskFrontmatter(base, ours, theirs) {
7354
7811
  const keys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)]);
7355
- const output3 = {};
7812
+ const output2 = {};
7356
7813
  const oursUpdated = asTimestamp(ours.updated);
7357
7814
  const theirsUpdated = asTimestamp(theirs.updated);
7358
7815
  for (const key of keys) {
7359
7816
  const merged = chooseFieldValue(key, base[key], ours[key], theirs[key], oursUpdated, theirsUpdated);
7360
7817
  if (merged !== void 0) {
7361
- output3[key] = merged;
7818
+ output2[key] = merged;
7362
7819
  }
7363
7820
  }
7364
- return output3;
7821
+ return output2;
7365
7822
  }
7366
7823
  function mergeTextWithGit(ancestor, ours, theirs) {
7367
7824
  const result = spawnSync5("git", ["merge-file", "-p", ours, ancestor, theirs], {
@@ -7392,8 +7849,8 @@ function mergeTaskFile(ancestorPath, oursPath, theirsPath) {
7392
7849
  fs21.writeFileSync(oursBody, ours.body, "utf8");
7393
7850
  fs21.writeFileSync(theirsBody, theirs.body, "utf8");
7394
7851
  const mergedBody = mergeTextWithGit(ancestorBody, oursBody, theirsBody);
7395
- const output3 = stringifyFrontmatter6(mergedFrontmatter, mergedBody.output);
7396
- fs21.writeFileSync(oursPath, output3, "utf8");
7852
+ const output2 = stringifyFrontmatter6(mergedFrontmatter, mergedBody.output);
7853
+ fs21.writeFileSync(oursPath, output2, "utf8");
7397
7854
  return mergedBody.ok ? 0 : 1;
7398
7855
  } finally {
7399
7856
  fs21.rmSync(tempDir, { recursive: true, force: true });
@@ -7422,6 +7879,27 @@ function runMergeDriver(kind, ancestorPath, oursPath, theirsPath) {
7422
7879
  return mergeTaskFile(ancestorPath, oursPath, theirsPath);
7423
7880
  }
7424
7881
 
7882
+ // src/utils/basic-help.ts
7883
+ function renderBasicHelp() {
7884
+ return [
7885
+ "COOP Basics",
7886
+ "",
7887
+ "Day-to-day commands:",
7888
+ "- `coop current`: show working context, active work, and the next ready task",
7889
+ "- `coop use track <id>` / `coop use delivery <id>`: set working scope defaults",
7890
+ "- `coop next task` or `coop pick task`: choose work from COOP",
7891
+ "- `coop show <id>`: inspect a task, idea, or delivery",
7892
+ "- `coop list tasks --track <id>`: browse scoped work",
7893
+ "- `coop update <id> --track <id> --delivery <id>`: update task metadata",
7894
+ '- `coop comment <id> --message "..."`: append a task comment',
7895
+ "- `coop log-time <id> --hours 2 --kind worked`: append time spent",
7896
+ "- `coop review task <id>` / `coop complete task <id>`: move work through lifecycle",
7897
+ "- `coop help-ai --initial-prompt --strict --repo C:/path/to/repo --delivery MVP --command coop.cmd`: hand off COOP context to an agent",
7898
+ "",
7899
+ "Use `coop <command> --help` for detailed flags."
7900
+ ].join("\n");
7901
+ }
7902
+
7425
7903
  // src/utils/not-implemented.ts
7426
7904
  function printNotImplemented(command, phase) {
7427
7905
  console.log(`${command}: Not yet implemented - coming in Phase ${phase}.`);
@@ -7447,9 +7925,16 @@ function createProgram() {
7447
7925
  const program = new Command();
7448
7926
  program.name("coop");
7449
7927
  program.version(readVersion());
7450
- program.description("COOP CLI");
7928
+ program.description("COOP CLI for Git-native planning, backlog management, task execution, and delivery workflows.");
7451
7929
  program.option("--verbose", "Print stack traces for command errors");
7452
7930
  program.option("-p, --project <id>", "Select the active COOP project");
7931
+ program.addHelpText("after", `
7932
+ Common day-to-day commands:
7933
+ coop basics
7934
+ coop current
7935
+ coop next task
7936
+ coop show <id>
7937
+ `);
7453
7938
  registerInitCommand(program);
7454
7939
  registerCreateCommand(program);
7455
7940
  registerCurrentCommand(program);
@@ -7485,6 +7970,9 @@ function createProgram() {
7485
7970
  registerViewCommand(program);
7486
7971
  registerWebhookCommand(program);
7487
7972
  registerPhasePlaceholder(program, "ext", 3, "Plugin extension commands");
7973
+ program.command("basics").alias("help-basic").description("Show the small set of COOP commands that cover most day-to-day work").action(() => {
7974
+ console.log(renderBasicHelp());
7975
+ });
7488
7976
  const hooks = program.command("hook");
7489
7977
  hooks.command("pre-commit").option("--repo <path>", "Repository root", process.cwd()).action((options) => {
7490
7978
  const repoRoot = options.repo ?? process.cwd();