@cleocode/cleo 2026.4.20 → 2026.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -10836,8 +10836,9 @@ function getProjectRoot(cwd) {
10836
10836
  if (scope !== void 0) {
10837
10837
  return scope.worktreeRoot;
10838
10838
  }
10839
- if (process.env["CLEO_ROOT"]) {
10840
- return process.env["CLEO_ROOT"];
10839
+ const envRoot = process.env["CLEO_ROOT"] ?? process.env["CLEO_PROJECT_ROOT"];
10840
+ if (envRoot) {
10841
+ return envRoot;
10841
10842
  }
10842
10843
  const cleoDirEnv = process.env["CLEO_DIR"];
10843
10844
  if (cleoDirEnv && isAbsolutePath(cleoDirEnv)) {
@@ -34311,6 +34312,31 @@ function validatePhaseFormat(phase) {
34311
34312
  );
34312
34313
  }
34313
34314
  }
34315
+ function throwCombinedValidationError(issues, options) {
34316
+ const summary = issues.length === 1 ? issues[0].message : `${issues.length} validation issues found:
34317
+ ${issues.map((i, n) => ` ${n + 1}. ${i.message}`).join("\n")}`;
34318
+ const fixes = issues.map((i) => i.fix).filter(Boolean);
34319
+ const fixSummary = fixes.length === 0 ? void 0 : fixes.length === 1 ? fixes[0] : fixes.map((f2, n) => `${n + 1}. ${f2}`).join("\n");
34320
+ const submittedParams = { title: options.title };
34321
+ if (options.parentId) submittedParams.parent = options.parentId;
34322
+ if (options.type) submittedParams.type = options.type;
34323
+ if (options.priority) submittedParams.priority = options.priority;
34324
+ if (options.size) submittedParams.size = options.size;
34325
+ if (options.description) submittedParams.description = "(provided)";
34326
+ if (options.acceptance?.length)
34327
+ submittedParams.acceptance = `${options.acceptance.length} criteria`;
34328
+ if (options.depends?.length) submittedParams.depends = options.depends;
34329
+ if (options.phase) submittedParams.phase = options.phase;
34330
+ if (options.labels?.length) submittedParams.labels = options.labels;
34331
+ throw new CleoError(6 /* VALIDATION_ERROR */, summary, {
34332
+ fix: fixSummary,
34333
+ details: {
34334
+ field: issues.length === 1 ? issues[0].field : "multiple",
34335
+ issues,
34336
+ submittedParams
34337
+ }
34338
+ });
34339
+ }
34314
34340
  function validateDepends(depends, tasks2) {
34315
34341
  const existingIds = new Set(tasks2.map((t) => t.id));
34316
34342
  for (const depId of depends) {
@@ -34438,46 +34464,69 @@ function findRecentDuplicate(title, phase, tasks2, windowSeconds = 60) {
34438
34464
  }
34439
34465
  async function addTask(options, cwd, accessor) {
34440
34466
  validateTitle(options.title);
34441
- if (options.description && options.title.trim().toLowerCase() === options.description.trim().toLowerCase()) {
34442
- throw new CleoError(
34443
- 6 /* VALIDATION_ERROR */,
34444
- "Title and description must be different (anti-hallucination rule)",
34445
- {
34446
- fix: "Provide --desc with a description different from the title",
34447
- details: { field: "description" }
34448
- }
34449
- );
34450
- }
34451
34467
  if (!options.dryRun) {
34452
34468
  await requireActiveSession("tasks.add", cwd);
34453
34469
  }
34470
+ const issues = [];
34471
+ const warnings = [];
34472
+ if (options.description && options.title.trim().toLowerCase() === options.description.trim().toLowerCase()) {
34473
+ issues.push({
34474
+ field: "description",
34475
+ message: "Title and description must be different (anti-hallucination rule)",
34476
+ fix: "Provide --desc with a description different from the title"
34477
+ });
34478
+ }
34454
34479
  const parentId = options.parentId ?? null;
34455
34480
  if (!options.dryRun && !parentId && options.type !== "epic") {
34456
34481
  const lifecycleMode = await getLifecycleMode(cwd);
34457
34482
  if (lifecycleMode === "strict") {
34458
- throw new CleoError(
34459
- 6 /* VALIDATION_ERROR */,
34460
- 'Tasks must have a parent (epic or task) in strict mode. Use --parent <epicId>, --type epic for a root-level epic, or set lifecycle.mode to "advisory".',
34461
- {
34462
- fix: 'cleo add "Task title" --parent T### --acceptance "AC1|AC2|AC3"',
34463
- alternatives: [
34464
- {
34465
- action: "Create as epic",
34466
- command: 'cleo add "Epic title" --type epic --priority high'
34467
- }
34468
- ]
34469
- }
34483
+ issues.push({
34484
+ field: "parentId",
34485
+ message: 'Tasks must have a parent (epic or task) in strict mode. Use --parent <epicId>, --type epic for a root-level epic, or set lifecycle.mode to "advisory".',
34486
+ fix: 'cleo add "Task title" --parent T### --acceptance "AC1|AC2|AC3"'
34487
+ });
34488
+ } else {
34489
+ warnings.push(
34490
+ "Task created without a parent. Use --parent <epicId> to assign to an epic hierarchy."
34470
34491
  );
34471
34492
  }
34472
34493
  }
34473
34494
  const dataAccessor = accessor ?? await (await Promise.resolve().then(() => (init_data_accessor(), data_accessor_exports))).getAccessor(cwd);
34474
34495
  const status = options.status ?? "pending";
34475
- const priority = normalizePriority(options.priority ?? "medium");
34496
+ let priority;
34497
+ try {
34498
+ priority = normalizePriority(options.priority ?? "medium");
34499
+ } catch (err) {
34500
+ if (err instanceof CleoError) {
34501
+ issues.push({ field: "priority", message: err.message, fix: err.fix });
34502
+ }
34503
+ priority = "medium";
34504
+ }
34476
34505
  const size = options.size ?? "medium";
34477
34506
  let taskType = options.type;
34478
- validateStatus(status);
34479
- validateSize(size);
34480
- if (options.labels?.length) validateLabels(options.labels);
34507
+ try {
34508
+ validateStatus(status);
34509
+ } catch (err) {
34510
+ if (err instanceof CleoError) {
34511
+ issues.push({ field: "status", message: err.message, fix: err.fix });
34512
+ }
34513
+ }
34514
+ try {
34515
+ validateSize(size);
34516
+ } catch (err) {
34517
+ if (err instanceof CleoError) {
34518
+ issues.push({ field: "size", message: err.message, fix: err.fix });
34519
+ }
34520
+ }
34521
+ if (options.labels?.length) {
34522
+ try {
34523
+ validateLabels(options.labels);
34524
+ } catch (err) {
34525
+ if (err instanceof CleoError) {
34526
+ issues.push({ field: "labels", message: err.message, fix: err.fix });
34527
+ }
34528
+ }
34529
+ }
34481
34530
  if (!options.dryRun) {
34482
34531
  const enforcement = await createAcceptanceEnforcement(cwd);
34483
34532
  const acValidation = enforcement.validateCreation({
@@ -34485,17 +34534,28 @@ async function addTask(options, cwd, accessor) {
34485
34534
  priority
34486
34535
  });
34487
34536
  if (!acValidation.valid) {
34488
- throw new CleoError(acValidation.exitCode ?? 6 /* VALIDATION_ERROR */, acValidation.error, {
34537
+ issues.push({
34538
+ field: "acceptance",
34539
+ message: acValidation.error,
34489
34540
  fix: acValidation.fix
34490
34541
  });
34491
34542
  }
34492
34543
  if (options.type === "epic") {
34493
- await validateEpicCreation(
34494
- { acceptance: options.acceptance, description: options.description },
34495
- cwd
34496
- );
34544
+ try {
34545
+ await validateEpicCreation(
34546
+ { acceptance: options.acceptance, description: options.description },
34547
+ cwd
34548
+ );
34549
+ } catch (err) {
34550
+ if (err instanceof CleoError) {
34551
+ issues.push({ field: "epic", message: err.message, fix: err.fix });
34552
+ }
34553
+ }
34497
34554
  }
34498
34555
  }
34556
+ if (issues.length > 0) {
34557
+ throwCombinedValidationError(issues, options);
34558
+ }
34499
34559
  if (options.depends?.length) {
34500
34560
  for (const depId of options.depends) {
34501
34561
  const trimmed = depId.trim();
@@ -34794,7 +34854,7 @@ async function addTask(options, cwd, accessor) {
34794
34854
  after: { title: options.title, status, priority }
34795
34855
  });
34796
34856
  });
34797
- return { task };
34857
+ return { task, ...warnings.length > 0 && { warnings } };
34798
34858
  }
34799
34859
  var NUMERIC_PRIORITY_MAP, VALID_PRIORITIES;
34800
34860
  var init_add = __esm({
@@ -38729,10 +38789,10 @@ async function readProjectMeta(projectPath) {
38729
38789
  }
38730
38790
  async function readProjectId(projectPath) {
38731
38791
  try {
38732
- const { readFileSync: readFileSync101, existsSync: existsSync131 } = await import("node:fs");
38792
+ const { readFileSync: readFileSync102, existsSync: existsSync131 } = await import("node:fs");
38733
38793
  const infoPath = join64(projectPath, ".cleo", "project-info.json");
38734
38794
  if (!existsSync131(infoPath)) return "";
38735
- const data = JSON.parse(readFileSync101(infoPath, "utf-8"));
38795
+ const data = JSON.parse(readFileSync102(infoPath, "utf-8"));
38736
38796
  return typeof data.projectId === "string" ? data.projectId : "";
38737
38797
  } catch {
38738
38798
  return "";
@@ -54949,7 +55009,7 @@ function fuzzyScore(query, text3) {
54949
55009
  return 0;
54950
55010
  }
54951
55011
  async function findTasks(options, cwd, accessor) {
54952
- if (!options.query && !options.id) {
55012
+ if (options.query == null && !options.id) {
54953
55013
  throw new CleoError(2 /* INVALID_INPUT */, "Search query or --id is required", {
54954
55014
  fix: 'cleo find "<query>"',
54955
55015
  details: { field: "query" }
@@ -54986,6 +55046,8 @@ async function findTasks(options, cwd, accessor) {
54986
55046
  priority: t.priority,
54987
55047
  type: t.type,
54988
55048
  parentId: t.parentId,
55049
+ depends: t.depends ?? [],
55050
+ size: t.size ?? void 0,
54989
55051
  score: t.id.toUpperCase() === idQuery ? 100 : t.id.toUpperCase().startsWith(idQuery) ? 80 : 50
54990
55052
  }));
54991
55053
  } else if (options.exact) {
@@ -54998,6 +55060,8 @@ async function findTasks(options, cwd, accessor) {
54998
55060
  priority: t.priority,
54999
55061
  type: t.type,
55000
55062
  parentId: t.parentId,
55063
+ depends: t.depends ?? [],
55064
+ size: t.size ?? void 0,
55001
55065
  score: 100
55002
55066
  }));
55003
55067
  } else {
@@ -55016,6 +55080,8 @@ async function findTasks(options, cwd, accessor) {
55016
55080
  priority: t.priority,
55017
55081
  type: t.type,
55018
55082
  parentId: t.parentId,
55083
+ depends: t.depends ?? [],
55084
+ size: t.size ?? void 0,
55019
55085
  score: Math.round(score)
55020
55086
  });
55021
55087
  }
@@ -107789,14 +107855,55 @@ async function taskFind(projectRoot, query, limit, options) {
107789
107855
  projectRoot,
107790
107856
  accessor
107791
107857
  );
107858
+ if (options?.verbose) {
107859
+ const fullResults = [];
107860
+ for (const r of findResult.results) {
107861
+ const task = await accessor.loadSingleTask(r.id);
107862
+ if (task) fullResults.push(taskToRecord(task));
107863
+ }
107864
+ return { success: true, data: { results: fullResults, total: findResult.total } };
107865
+ }
107866
+ if (options?.fields) {
107867
+ const extraFields = options.fields.split(",").map((f2) => f2.trim());
107868
+ const extendedResults = [];
107869
+ for (const r of findResult.results) {
107870
+ const base = {
107871
+ id: r.id,
107872
+ title: r.title,
107873
+ status: r.status,
107874
+ priority: r.priority,
107875
+ parentId: r.parentId,
107876
+ depends: r.depends,
107877
+ type: r.type,
107878
+ size: r.size
107879
+ };
107880
+ const task = await accessor.loadSingleTask(r.id);
107881
+ if (task) {
107882
+ const record2 = taskToRecord(task);
107883
+ for (const field of extraFields) {
107884
+ if (field in record2) {
107885
+ base[field] = record2[field];
107886
+ }
107887
+ }
107888
+ }
107889
+ extendedResults.push(base);
107890
+ }
107891
+ return {
107892
+ success: true,
107893
+ data: { results: extendedResults, total: findResult.total }
107894
+ };
107895
+ }
107792
107896
  const results = findResult.results.map((r) => ({
107793
107897
  id: r.id,
107794
107898
  title: r.title,
107795
107899
  status: r.status,
107796
107900
  priority: r.priority,
107797
- parentId: r.parentId
107901
+ parentId: r.parentId,
107902
+ depends: r.depends,
107903
+ type: r.type,
107904
+ size: r.size
107798
107905
  }));
107799
- return { success: true, data: { results, total: results.length } };
107906
+ return { success: true, data: { results, total: findResult.total } };
107800
107907
  } catch (err) {
107801
107908
  return cleoErrorToEngineError(err, "E_NOT_INITIALIZED", "Task database not initialized");
107802
107909
  }
@@ -107804,11 +107911,37 @@ async function taskFind(projectRoot, query, limit, options) {
107804
107911
  async function taskCreate(projectRoot, params) {
107805
107912
  try {
107806
107913
  const accessor = await getAccessor(projectRoot);
107914
+ let resolvedParent = params.parent || null;
107915
+ if (!resolvedParent && params.parentSearch) {
107916
+ const searchResult = await findTasks(
107917
+ { query: params.parentSearch, limit: 1 },
107918
+ projectRoot,
107919
+ accessor
107920
+ );
107921
+ if (searchResult.results.length > 0) {
107922
+ resolvedParent = searchResult.results[0].id;
107923
+ } else {
107924
+ return cleoErrorToEngineError(
107925
+ new Error(`No task found matching --parent-search "${params.parentSearch}"`),
107926
+ "E_NOT_FOUND",
107927
+ `No task found matching "${params.parentSearch}"`
107928
+ );
107929
+ }
107930
+ }
107931
+ if (!resolvedParent && params.type !== "epic") {
107932
+ try {
107933
+ const session = await getActiveSession(projectRoot);
107934
+ if (session?.scope?.type === "epic" && session.scope.epicId) {
107935
+ resolvedParent = session.scope.epicId;
107936
+ }
107937
+ } catch {
107938
+ }
107939
+ }
107807
107940
  const result = await addTask(
107808
107941
  {
107809
107942
  title: params.title,
107810
107943
  description: params.description,
107811
- parentId: params.parent || null,
107944
+ parentId: resolvedParent,
107812
107945
  depends: params.depends,
107813
107946
  priority: params.priority || "medium",
107814
107947
  labels: params.labels,
@@ -107828,7 +107961,8 @@ async function taskCreate(projectRoot, params) {
107828
107961
  data: {
107829
107962
  task: taskToRecord(result.task),
107830
107963
  duplicate: result.duplicate ?? false,
107831
- dryRun: params.dryRun
107964
+ dryRun: params.dryRun,
107965
+ ...result.warnings?.length && { warnings: result.warnings }
107832
107966
  }
107833
107967
  };
107834
107968
  } catch (err) {
@@ -111605,7 +111739,7 @@ var init_nexus2 = __esm({
111605
111739
  async function orchestrateClassify(request, context, projectRoot) {
111606
111740
  try {
111607
111741
  const { getCleoCantWorkflowsDir: getCleoCantWorkflowsDir2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
111608
- const { readFileSync: readFileSync101, readdirSync: readdirSync42, existsSync: existsSync131 } = await import("node:fs");
111742
+ const { readFileSync: readFileSync102, readdirSync: readdirSync42, existsSync: existsSync131 } = await import("node:fs");
111609
111743
  const { join: join131 } = await import("node:path");
111610
111744
  const workflowsDir = getCleoCantWorkflowsDir2();
111611
111745
  const combined = `${request} ${context ?? ""}`.toLowerCase();
@@ -111614,7 +111748,7 @@ async function orchestrateClassify(request, context, projectRoot) {
111614
111748
  const files = readdirSync42(workflowsDir).filter((f2) => f2.endsWith(".cant"));
111615
111749
  for (const file2 of files) {
111616
111750
  try {
111617
- const src = readFileSync101(join131(workflowsDir, file2), "utf-8");
111751
+ const src = readFileSync102(join131(workflowsDir, file2), "utf-8");
111618
111752
  const teamMatch = /^team\s+(\S+):/m.exec(src);
111619
111753
  if (!teamMatch) continue;
111620
111754
  const teamName = teamMatch[1];
@@ -111634,7 +111768,7 @@ async function orchestrateClassify(request, context, projectRoot) {
111634
111768
  const files = readdirSync42(localCantDir).filter((f2) => f2.endsWith(".cant"));
111635
111769
  for (const file2 of files) {
111636
111770
  try {
111637
- const src = readFileSync101(join131(localCantDir, file2), "utf-8");
111771
+ const src = readFileSync102(join131(localCantDir, file2), "utf-8");
111638
111772
  const teamMatch = /^team\s+(\S+):/m.exec(src);
111639
111773
  if (!teamMatch) continue;
111640
111774
  const teamName = teamMatch[1];
@@ -113916,7 +114050,9 @@ var init_tasks4 = __esm({
113916
114050
  exact: params?.exact,
113917
114051
  status: params?.status,
113918
114052
  includeArchive: params?.includeArchive,
113919
- offset: params?.offset
114053
+ offset: params?.offset,
114054
+ fields: params?.fields,
114055
+ verbose: params?.verbose
113920
114056
  }
113921
114057
  );
113922
114058
  return wrapResult(result, "query", "tasks", operation, startTime);
@@ -114067,7 +114203,8 @@ var init_tasks4 = __esm({
114067
114203
  size: params?.size,
114068
114204
  notes: params?.notes,
114069
114205
  files: params?.files,
114070
- dryRun: params?.dryRun
114206
+ dryRun: params?.dryRun,
114207
+ parentSearch: params?.parentSearch
114071
114208
  });
114072
114209
  return wrapResult(result, "mutate", "tasks", operation, startTime);
114073
114210
  }
@@ -116151,7 +116288,7 @@ var init_cli = __esm({
116151
116288
 
116152
116289
  // packages/cleo/src/cli/index.ts
116153
116290
  init_internal();
116154
- import { readFileSync as readFileSync100 } from "node:fs";
116291
+ import { readFileSync as readFileSync101 } from "node:fs";
116155
116292
  import { dirname as dirname28, join as join130 } from "node:path";
116156
116293
  import { fileURLToPath as fileURLToPath5 } from "node:url";
116157
116294
 
@@ -117156,6 +117293,13 @@ var ADD_PARAMS = [
117156
117293
  required: false,
117157
117294
  description: "Position within sibling group",
117158
117295
  cli: { flag: "position", parse: parseInt }
117296
+ },
117297
+ {
117298
+ name: "parentSearch",
117299
+ type: "string",
117300
+ required: false,
117301
+ description: "Resolve parent by title substring instead of exact ID (T090)",
117302
+ cli: { flag: "parent-search" }
117159
117303
  }
117160
117304
  ];
117161
117305
  function registerAddCommand(program) {
@@ -117179,18 +117323,37 @@ function registerAddCommand(program) {
117179
117323
  params["labels"] = opts["labels"].split(",").map((s3) => s3.trim());
117180
117324
  if (opts["files"])
117181
117325
  params["files"] = opts["files"].split(",").map((s3) => s3.trim());
117182
- if (opts["acceptance"])
117183
- params["acceptance"] = opts["acceptance"].split("|").map((s3) => s3.trim()).filter(Boolean);
117326
+ if (opts["acceptance"]) {
117327
+ const raw = opts["acceptance"];
117328
+ if (raw.trimStart().startsWith("[")) {
117329
+ try {
117330
+ const parsed = JSON.parse(raw);
117331
+ params["acceptance"] = Array.isArray(parsed) ? parsed.map((s3) => String(s3).trim()).filter(Boolean) : [raw];
117332
+ } catch {
117333
+ params["acceptance"] = raw.split("|").map((s3) => s3.trim()).filter(Boolean);
117334
+ }
117335
+ } else {
117336
+ params["acceptance"] = raw.split("|").map((s3) => s3.trim()).filter(Boolean);
117337
+ }
117338
+ }
117184
117339
  if (opts["depends"])
117185
117340
  params["depends"] = opts["depends"].split(",").map((s3) => s3.trim());
117186
117341
  if (opts["notes"] !== void 0) params["notes"] = opts["notes"];
117187
117342
  if (opts["position"] !== void 0) params["position"] = opts["position"];
117188
117343
  if (opts["dryRun"] !== void 0) params["dryRun"] = opts["dryRun"];
117344
+ if (opts["parentSearch"] !== void 0) params["parentSearch"] = opts["parentSearch"];
117189
117345
  const response = await dispatchRaw("mutate", "tasks", "add", params);
117190
117346
  if (!response.success) {
117191
117347
  handleRawError(response, { command: "add", operation: "tasks.add" });
117192
117348
  }
117193
117349
  const data = response.data;
117350
+ const dataWarnings = data?.warnings;
117351
+ if (dataWarnings?.length) {
117352
+ for (const w2 of dataWarnings) {
117353
+ process.stderr.write(`\u26A0 ${w2}
117354
+ `);
117355
+ }
117356
+ }
117194
117357
  if (data?.duplicate) {
117195
117358
  cliOutput(data, {
117196
117359
  command: "add",
@@ -117209,6 +117372,93 @@ function registerAddCommand(program) {
117209
117372
  });
117210
117373
  }
117211
117374
 
117375
+ // packages/cleo/src/cli/commands/add-batch.ts
117376
+ init_cli();
117377
+ init_renderers();
117378
+ import { readFileSync as readFileSync96 } from "node:fs";
117379
+ function registerAddBatchCommand(program) {
117380
+ program.command("add-batch").description("Create multiple tasks atomically from a JSON file").option("--file <path>", "Path to JSON file (array of task objects). Use - for stdin.").option("--parent <parentId>", "Default parent for all tasks (overridden by per-task parent)").option("--dry-run", "Preview what would be created without making changes").action(async (opts) => {
117381
+ const filePath = opts["file"];
117382
+ const defaultParent = opts["parent"];
117383
+ const dryRun = opts["dryRun"];
117384
+ let raw;
117385
+ if (!filePath || filePath === "-") {
117386
+ const chunks = [];
117387
+ for await (const chunk of process.stdin) {
117388
+ chunks.push(chunk);
117389
+ }
117390
+ raw = Buffer.concat(chunks).toString("utf-8");
117391
+ } else {
117392
+ raw = readFileSync96(filePath, "utf-8");
117393
+ }
117394
+ let tasks2;
117395
+ try {
117396
+ const parsed = JSON.parse(raw);
117397
+ tasks2 = Array.isArray(parsed) ? parsed : [parsed];
117398
+ } catch {
117399
+ process.stderr.write("Error: Invalid JSON input. Expected an array of task objects.\n");
117400
+ process.exit(2);
117401
+ return;
117402
+ }
117403
+ if (tasks2.length === 0) {
117404
+ process.stderr.write("Error: No tasks in input.\n");
117405
+ process.exit(2);
117406
+ return;
117407
+ }
117408
+ const results = [];
117409
+ let failed = 0;
117410
+ for (const task of tasks2) {
117411
+ const params = {
117412
+ title: task.title,
117413
+ ...task.description && { description: task.description },
117414
+ parent: task.parent ?? defaultParent,
117415
+ ...task.type && { type: task.type },
117416
+ ...task.priority && { priority: task.priority },
117417
+ ...task.size && { size: task.size },
117418
+ ...task.acceptance?.length && { acceptance: task.acceptance },
117419
+ ...task.depends?.length && { depends: task.depends },
117420
+ ...task.labels?.length && { labels: task.labels },
117421
+ ...task.phase && { phase: task.phase },
117422
+ ...task.notes && { notes: task.notes },
117423
+ ...task.files?.length && { files: task.files },
117424
+ ...dryRun && { dryRun: true }
117425
+ };
117426
+ const response = await dispatchRaw("mutate", "tasks", "add", params);
117427
+ if (response.success) {
117428
+ const data = response.data;
117429
+ const taskData = data?.task;
117430
+ results.push({ title: task.title, id: taskData?.id });
117431
+ } else {
117432
+ failed++;
117433
+ results.push({
117434
+ title: task.title,
117435
+ error: response.error?.message ?? "Unknown error"
117436
+ });
117437
+ }
117438
+ }
117439
+ const output = {
117440
+ total: tasks2.length,
117441
+ created: tasks2.length - failed,
117442
+ failed,
117443
+ dryRun: dryRun ?? false,
117444
+ results
117445
+ };
117446
+ if (failed > 0) {
117447
+ cliOutput(output, {
117448
+ command: "add-batch",
117449
+ message: `${failed} of ${tasks2.length} tasks failed`,
117450
+ operation: "tasks.add-batch"
117451
+ });
117452
+ process.exit(1);
117453
+ } else {
117454
+ cliOutput(output, {
117455
+ command: "add-batch",
117456
+ operation: "tasks.add-batch"
117457
+ });
117458
+ }
117459
+ });
117460
+ }
117461
+
117212
117462
  // packages/cleo/src/cli/commands/admin.ts
117213
117463
  init_cli();
117214
117464
  function registerAdminCommand(program) {
@@ -117496,7 +117746,7 @@ agent ${agentId}:
117496
117746
  try {
117497
117747
  const { AgentRegistryAccessor: AgentRegistryAccessor2, getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
117498
117748
  const { createRuntime } = await import("@cleocode/runtime");
117499
- const { existsSync: existsSync131, readFileSync: readFileSync101 } = await import("node:fs");
117749
+ const { existsSync: existsSync131, readFileSync: readFileSync102 } = await import("node:fs");
117500
117750
  const { join: join131 } = await import("node:path");
117501
117751
  await getDb4();
117502
117752
  const registry2 = new AgentRegistryAccessor2(process.cwd());
@@ -117519,7 +117769,7 @@ agent ${agentId}:
117519
117769
  let cantValidation = null;
117520
117770
  const cantPath = opts["cant"] ?? join131(".cleo", "agents", `${agentId}.cant`);
117521
117771
  if (existsSync131(cantPath)) {
117522
- profile = readFileSync101(cantPath, "utf-8");
117772
+ profile = readFileSync102(cantPath, "utf-8");
117523
117773
  try {
117524
117774
  const cantModule = await import("@cleocode/cant");
117525
117775
  const validate = "validate" in cantModule ? cantModule.validate : null;
@@ -118559,7 +118809,7 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
118559
118809
  });
118560
118810
  agent.command("install <path>").description("Install an agent from a .cantz archive or agent directory").option("--global", "Install to global tier (~/.local/share/cleo/cant/agents/)").action(async (sourcePath, opts) => {
118561
118811
  try {
118562
- const { existsSync: existsSync131, mkdirSync: mkdirSync31, cpSync, readFileSync: readFileSync101, rmSync: rmSync3, statSync: statSync22 } = await import("node:fs");
118812
+ const { existsSync: existsSync131, mkdirSync: mkdirSync31, cpSync, readFileSync: readFileSync102, rmSync: rmSync3, statSync: statSync22 } = await import("node:fs");
118563
118813
  const { join: join131, basename: basename19, resolve: resolve16 } = await import("node:path");
118564
118814
  const { homedir: homedir7 } = await import("node:os");
118565
118815
  const { tmpdir: tmpdir3 } = await import("node:os");
@@ -118678,7 +118928,7 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
118678
118928
  }
118679
118929
  let registered = false;
118680
118930
  try {
118681
- const persona = readFileSync101(join131(targetDir, "persona.cant"), "utf-8");
118931
+ const persona = readFileSync102(join131(targetDir, "persona.cant"), "utf-8");
118682
118932
  const descMatch = persona.match(/description:\s*"([^"]+)"/);
118683
118933
  const displayName = descMatch?.[1] ?? agentName;
118684
118934
  const { AgentRegistryAccessor: AgentRegistryAccessor2, getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
@@ -120277,7 +120527,7 @@ function registerBugCommand(program) {
120277
120527
 
120278
120528
  // packages/cleo/src/cli/commands/cant.ts
120279
120529
  init_renderers();
120280
- import { existsSync as existsSync128, mkdirSync as mkdirSync29, readFileSync as readFileSync96, writeFileSync as writeFileSync23 } from "node:fs";
120530
+ import { existsSync as existsSync128, mkdirSync as mkdirSync29, readFileSync as readFileSync97, writeFileSync as writeFileSync23 } from "node:fs";
120281
120531
  import { dirname as dirname25, isAbsolute as isAbsolute3, join as join124, resolve as resolve15 } from "node:path";
120282
120532
  function registerCantCommand(program) {
120283
120533
  const cant = program.command("cant").description("CANT DSL tooling");
@@ -120355,7 +120605,7 @@ function registerCantCommand(program) {
120355
120605
  }
120356
120606
  try {
120357
120607
  const mod = await loadMigrateEngine();
120358
- const content = readFileSync96(filePath, "utf-8");
120608
+ const content = readFileSync97(filePath, "utf-8");
120359
120609
  const result = mod.migrateMarkdown(content, filePath, {
120360
120610
  write: isWrite,
120361
120611
  verbose: isVerbose,
@@ -121150,7 +121400,7 @@ function registerDetectCommand(program) {
121150
121400
  // packages/cleo/src/cli/commands/detect-drift.ts
121151
121401
  init_src();
121152
121402
  init_renderers();
121153
- import { existsSync as existsSync129, readdirSync as readdirSync41, readFileSync as readFileSync97 } from "node:fs";
121403
+ import { existsSync as existsSync129, readdirSync as readdirSync41, readFileSync as readFileSync98 } from "node:fs";
121154
121404
  import { dirname as dirname26, join as join125 } from "node:path";
121155
121405
  function findProjectRoot() {
121156
121406
  let currentDir = process.cwd();
@@ -121170,7 +121420,7 @@ function registerDetectDriftCommand(program) {
121170
121420
  const isCleoRepo = existsSync129(join125(projectRoot, "src", "cli", "commands")) || existsSync129(join125(projectRoot, "packages", "cleo", "src"));
121171
121421
  const safeRead = (filePath) => {
121172
121422
  try {
121173
- return readFileSync97(filePath, "utf-8");
121423
+ return readFileSync98(filePath, "utf-8");
121174
121424
  } catch {
121175
121425
  return "";
121176
121426
  }
@@ -122088,6 +122338,20 @@ var FIND_PARAMS = [
122088
122338
  required: false,
122089
122339
  description: "Skip first N results",
122090
122340
  cli: { flag: "offset", parse: parseInt }
122341
+ },
122342
+ {
122343
+ name: "fields",
122344
+ type: "string",
122345
+ required: false,
122346
+ description: "Comma-separated additional fields to include (e.g. labels,acceptance,notes,description)",
122347
+ cli: { flag: "fields" }
122348
+ },
122349
+ {
122350
+ name: "verbose",
122351
+ type: "boolean",
122352
+ required: false,
122353
+ description: "Include all task fields (same as cleo list output)",
122354
+ cli: { flag: "verbose", short: "-v" }
122091
122355
  }
122092
122356
  ];
122093
122357
  function registerFindCommand(program) {
@@ -122107,6 +122371,8 @@ function registerFindCommand(program) {
122107
122371
  if (opts["includeArchive"] !== void 0) params["includeArchive"] = opts["includeArchive"];
122108
122372
  if (limit !== void 0) params["limit"] = limit;
122109
122373
  if (offset !== void 0) params["offset"] = offset;
122374
+ if (opts["fields"] !== void 0) params["fields"] = opts["fields"];
122375
+ if (opts["verbose"] !== void 0) params["verbose"] = opts["verbose"];
122110
122376
  const response = await dispatchRaw("query", "tasks", "find", params);
122111
122377
  if (!response.success) {
122112
122378
  handleRawError(response, { command: "find", operation: "tasks.find" });
@@ -122134,12 +122400,12 @@ init_src();
122134
122400
  init_src2();
122135
122401
  init_renderers();
122136
122402
  import { execFileSync as execFileSync16 } from "node:child_process";
122137
- import { existsSync as existsSync130, mkdirSync as mkdirSync30, readFileSync as readFileSync98, writeFileSync as writeFileSync24 } from "node:fs";
122403
+ import { existsSync as existsSync130, mkdirSync as mkdirSync30, readFileSync as readFileSync99, writeFileSync as writeFileSync24 } from "node:fs";
122138
122404
  import { dirname as dirname27, join as join127 } from "node:path";
122139
122405
  function getChangelogSource(cwd) {
122140
122406
  const configPath = getConfigPath(cwd);
122141
122407
  try {
122142
- const config2 = JSON.parse(readFileSync98(configPath, "utf-8"));
122408
+ const config2 = JSON.parse(readFileSync99(configPath, "utf-8"));
122143
122409
  return config2?.release?.changelog?.source ?? "CHANGELOG.md";
122144
122410
  } catch {
122145
122411
  return "CHANGELOG.md";
@@ -122148,7 +122414,7 @@ function getChangelogSource(cwd) {
122148
122414
  function getEnabledPlatforms(cwd) {
122149
122415
  const configPath = getConfigPath(cwd);
122150
122416
  try {
122151
- const config2 = JSON.parse(readFileSync98(configPath, "utf-8"));
122417
+ const config2 = JSON.parse(readFileSync99(configPath, "utf-8"));
122152
122418
  const outputs = config2?.release?.changelog?.outputs ?? [];
122153
122419
  return outputs.filter((o) => o.enabled);
122154
122420
  } catch {
@@ -122277,7 +122543,7 @@ function registerGenerateChangelogCommand(program) {
122277
122543
  if (!existsSync130(sourcePath)) {
122278
122544
  throw new CleoError(4 /* NOT_FOUND */, `Changelog source not found: ${sourcePath}`);
122279
122545
  }
122280
- const sourceContent = readFileSync98(sourcePath, "utf-8");
122546
+ const sourceContent = readFileSync99(sourcePath, "utf-8");
122281
122547
  const repoSlug = getGitHubRepoSlug();
122282
122548
  const results = [];
122283
122549
  if (targetPlatform) {
@@ -125333,7 +125599,7 @@ init_src2();
125333
125599
  init_cli();
125334
125600
  init_renderers();
125335
125601
  function registerStickyCommand(program) {
125336
- const sticky = program.command("sticky").alias("note").description("Manage sticky notes - quick project-wide ephemeral captures");
125602
+ const sticky = program.command("sticky").description("Manage sticky notes - quick project-wide ephemeral captures");
125337
125603
  sticky.command("add <content>").alias("jot").description("Create a new sticky note").option("--tag <tag>", "Add a tag (can be used multiple times)", collect, []).option("--color <color>", "Sticky color: yellow|blue|green|red|purple", "yellow").option("--priority <priority>", "Priority: low|medium|high", "medium").action(async (content, opts) => {
125338
125604
  try {
125339
125605
  await dispatchFromCli(
@@ -125369,7 +125635,7 @@ function registerStickyCommand(program) {
125369
125635
  return;
125370
125636
  }
125371
125637
  const data = response.data;
125372
- if (!data || !data.stickies || data.stickies.length === 0) {
125638
+ if (!data?.stickies || data.stickies.length === 0) {
125373
125639
  cliOutput(
125374
125640
  { stickies: [], total: 0 },
125375
125641
  { command: "sticky list", message: "No sticky notes found", operation: "sticky.list" }
@@ -125581,11 +125847,11 @@ function registerTestingCommand(program) {
125581
125847
  init_internal();
125582
125848
  init_cli();
125583
125849
  init_renderers();
125584
- import { readFileSync as readFileSync99 } from "node:fs";
125850
+ import { readFileSync as readFileSync100 } from "node:fs";
125585
125851
  function readPayload(opts, textKey, fileKey) {
125586
125852
  const text3 = opts[textKey];
125587
125853
  const file2 = opts[fileKey];
125588
- if (file2) return readFileSync99(file2, "utf-8");
125854
+ if (file2) return readFileSync100(file2, "utf-8");
125589
125855
  return text3;
125590
125856
  }
125591
125857
  function registerTokenCommand(program) {
@@ -126171,7 +126437,7 @@ var codeCommand = defineCommand({
126171
126437
  // packages/cleo/src/cli/index.ts
126172
126438
  function getPackageVersion() {
126173
126439
  const pkgPath = join130(dirname28(fileURLToPath5(import.meta.url)), "../../package.json");
126174
- const pkg = JSON.parse(readFileSync100(pkgPath, "utf-8"));
126440
+ const pkg = JSON.parse(readFileSync101(pkgPath, "utf-8"));
126175
126441
  return pkg.version;
126176
126442
  }
126177
126443
  var CLI_VERSION = getPackageVersion();
@@ -126179,6 +126445,7 @@ var rootShim = new ShimCommand();
126179
126445
  registerAgentCommand(rootShim);
126180
126446
  registerAgentsCommand(rootShim);
126181
126447
  registerAddCommand(rootShim);
126448
+ registerAddBatchCommand(rootShim);
126182
126449
  registerListCommand(rootShim);
126183
126450
  registerShowCommand(rootShim);
126184
126451
  registerFindCommand(rootShim);