@cleocode/core 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/index.js CHANGED
@@ -10811,8 +10811,9 @@ function getProjectRoot(cwd) {
10811
10811
  if (scope !== void 0) {
10812
10812
  return scope.worktreeRoot;
10813
10813
  }
10814
- if (process.env["CLEO_ROOT"]) {
10815
- return process.env["CLEO_ROOT"];
10814
+ const envRoot = process.env["CLEO_ROOT"] ?? process.env["CLEO_PROJECT_ROOT"];
10815
+ if (envRoot) {
10816
+ return envRoot;
10816
10817
  }
10817
10818
  const cleoDirEnv = process.env["CLEO_DIR"];
10818
10819
  if (cleoDirEnv && isAbsolutePath(cleoDirEnv)) {
@@ -24715,6 +24716,31 @@ function validatePhaseFormat(phase) {
24715
24716
  );
24716
24717
  }
24717
24718
  }
24719
+ function throwCombinedValidationError(issues, options) {
24720
+ const summary = issues.length === 1 ? issues[0].message : `${issues.length} validation issues found:
24721
+ ${issues.map((i, n) => ` ${n + 1}. ${i.message}`).join("\n")}`;
24722
+ const fixes = issues.map((i) => i.fix).filter(Boolean);
24723
+ const fixSummary = fixes.length === 0 ? void 0 : fixes.length === 1 ? fixes[0] : fixes.map((f, n) => `${n + 1}. ${f}`).join("\n");
24724
+ const submittedParams = { title: options.title };
24725
+ if (options.parentId) submittedParams.parent = options.parentId;
24726
+ if (options.type) submittedParams.type = options.type;
24727
+ if (options.priority) submittedParams.priority = options.priority;
24728
+ if (options.size) submittedParams.size = options.size;
24729
+ if (options.description) submittedParams.description = "(provided)";
24730
+ if (options.acceptance?.length)
24731
+ submittedParams.acceptance = `${options.acceptance.length} criteria`;
24732
+ if (options.depends?.length) submittedParams.depends = options.depends;
24733
+ if (options.phase) submittedParams.phase = options.phase;
24734
+ if (options.labels?.length) submittedParams.labels = options.labels;
24735
+ throw new CleoError(6 /* VALIDATION_ERROR */, summary, {
24736
+ fix: fixSummary,
24737
+ details: {
24738
+ field: issues.length === 1 ? issues[0].field : "multiple",
24739
+ issues,
24740
+ submittedParams
24741
+ }
24742
+ });
24743
+ }
24718
24744
  function validateDepends(depends, tasks2) {
24719
24745
  const existingIds = new Set(tasks2.map((t) => t.id));
24720
24746
  for (const depId of depends) {
@@ -24842,46 +24868,69 @@ function findRecentDuplicate(title, phase, tasks2, windowSeconds = 60) {
24842
24868
  }
24843
24869
  async function addTask(options, cwd, accessor) {
24844
24870
  validateTitle(options.title);
24845
- if (options.description && options.title.trim().toLowerCase() === options.description.trim().toLowerCase()) {
24846
- throw new CleoError(
24847
- 6 /* VALIDATION_ERROR */,
24848
- "Title and description must be different (anti-hallucination rule)",
24849
- {
24850
- fix: "Provide --desc with a description different from the title",
24851
- details: { field: "description" }
24852
- }
24853
- );
24854
- }
24855
24871
  if (!options.dryRun) {
24856
24872
  await requireActiveSession("tasks.add", cwd);
24857
24873
  }
24874
+ const issues = [];
24875
+ const warnings = [];
24876
+ if (options.description && options.title.trim().toLowerCase() === options.description.trim().toLowerCase()) {
24877
+ issues.push({
24878
+ field: "description",
24879
+ message: "Title and description must be different (anti-hallucination rule)",
24880
+ fix: "Provide --desc with a description different from the title"
24881
+ });
24882
+ }
24858
24883
  const parentId = options.parentId ?? null;
24859
24884
  if (!options.dryRun && !parentId && options.type !== "epic") {
24860
24885
  const lifecycleMode = await getLifecycleMode(cwd);
24861
24886
  if (lifecycleMode === "strict") {
24862
- throw new CleoError(
24863
- 6 /* VALIDATION_ERROR */,
24864
- '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".',
24865
- {
24866
- fix: 'cleo add "Task title" --parent T### --acceptance "AC1|AC2|AC3"',
24867
- alternatives: [
24868
- {
24869
- action: "Create as epic",
24870
- command: 'cleo add "Epic title" --type epic --priority high'
24871
- }
24872
- ]
24873
- }
24887
+ issues.push({
24888
+ field: "parentId",
24889
+ 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".',
24890
+ fix: 'cleo add "Task title" --parent T### --acceptance "AC1|AC2|AC3"'
24891
+ });
24892
+ } else {
24893
+ warnings.push(
24894
+ "Task created without a parent. Use --parent <epicId> to assign to an epic hierarchy."
24874
24895
  );
24875
24896
  }
24876
24897
  }
24877
24898
  const dataAccessor = accessor ?? await (await Promise.resolve().then(() => (init_data_accessor(), data_accessor_exports))).getAccessor(cwd);
24878
24899
  const status = options.status ?? "pending";
24879
- const priority = normalizePriority(options.priority ?? "medium");
24900
+ let priority;
24901
+ try {
24902
+ priority = normalizePriority(options.priority ?? "medium");
24903
+ } catch (err) {
24904
+ if (err instanceof CleoError) {
24905
+ issues.push({ field: "priority", message: err.message, fix: err.fix });
24906
+ }
24907
+ priority = "medium";
24908
+ }
24880
24909
  const size = options.size ?? "medium";
24881
24910
  let taskType = options.type;
24882
- validateStatus(status);
24883
- validateSize(size);
24884
- if (options.labels?.length) validateLabels(options.labels);
24911
+ try {
24912
+ validateStatus(status);
24913
+ } catch (err) {
24914
+ if (err instanceof CleoError) {
24915
+ issues.push({ field: "status", message: err.message, fix: err.fix });
24916
+ }
24917
+ }
24918
+ try {
24919
+ validateSize(size);
24920
+ } catch (err) {
24921
+ if (err instanceof CleoError) {
24922
+ issues.push({ field: "size", message: err.message, fix: err.fix });
24923
+ }
24924
+ }
24925
+ if (options.labels?.length) {
24926
+ try {
24927
+ validateLabels(options.labels);
24928
+ } catch (err) {
24929
+ if (err instanceof CleoError) {
24930
+ issues.push({ field: "labels", message: err.message, fix: err.fix });
24931
+ }
24932
+ }
24933
+ }
24885
24934
  if (!options.dryRun) {
24886
24935
  const enforcement = await createAcceptanceEnforcement(cwd);
24887
24936
  const acValidation = enforcement.validateCreation({
@@ -24889,17 +24938,28 @@ async function addTask(options, cwd, accessor) {
24889
24938
  priority
24890
24939
  });
24891
24940
  if (!acValidation.valid) {
24892
- throw new CleoError(acValidation.exitCode ?? 6 /* VALIDATION_ERROR */, acValidation.error, {
24941
+ issues.push({
24942
+ field: "acceptance",
24943
+ message: acValidation.error,
24893
24944
  fix: acValidation.fix
24894
24945
  });
24895
24946
  }
24896
24947
  if (options.type === "epic") {
24897
- await validateEpicCreation(
24898
- { acceptance: options.acceptance, description: options.description },
24899
- cwd
24900
- );
24948
+ try {
24949
+ await validateEpicCreation(
24950
+ { acceptance: options.acceptance, description: options.description },
24951
+ cwd
24952
+ );
24953
+ } catch (err) {
24954
+ if (err instanceof CleoError) {
24955
+ issues.push({ field: "epic", message: err.message, fix: err.fix });
24956
+ }
24957
+ }
24901
24958
  }
24902
24959
  }
24960
+ if (issues.length > 0) {
24961
+ throwCombinedValidationError(issues, options);
24962
+ }
24903
24963
  if (options.depends?.length) {
24904
24964
  for (const depId of options.depends) {
24905
24965
  const trimmed = depId.trim();
@@ -25198,7 +25258,7 @@ async function addTask(options, cwd, accessor) {
25198
25258
  after: { title: options.title, status, priority }
25199
25259
  });
25200
25260
  });
25201
- return { task };
25261
+ return { task, ...warnings.length > 0 && { warnings } };
25202
25262
  }
25203
25263
  var NUMERIC_PRIORITY_MAP, VALID_PRIORITIES;
25204
25264
  var init_add = __esm({
@@ -53219,7 +53279,7 @@ function fuzzyScore(query, text3) {
53219
53279
  return 0;
53220
53280
  }
53221
53281
  async function findTasks(options, cwd, accessor) {
53222
- if (!options.query && !options.id) {
53282
+ if (options.query == null && !options.id) {
53223
53283
  throw new CleoError(2 /* INVALID_INPUT */, "Search query or --id is required", {
53224
53284
  fix: 'cleo find "<query>"',
53225
53285
  details: { field: "query" }
@@ -53256,6 +53316,8 @@ async function findTasks(options, cwd, accessor) {
53256
53316
  priority: t.priority,
53257
53317
  type: t.type,
53258
53318
  parentId: t.parentId,
53319
+ depends: t.depends ?? [],
53320
+ size: t.size ?? void 0,
53259
53321
  score: t.id.toUpperCase() === idQuery ? 100 : t.id.toUpperCase().startsWith(idQuery) ? 80 : 50
53260
53322
  }));
53261
53323
  } else if (options.exact) {
@@ -53268,6 +53330,8 @@ async function findTasks(options, cwd, accessor) {
53268
53330
  priority: t.priority,
53269
53331
  type: t.type,
53270
53332
  parentId: t.parentId,
53333
+ depends: t.depends ?? [],
53334
+ size: t.size ?? void 0,
53271
53335
  score: 100
53272
53336
  }));
53273
53337
  } else {
@@ -53286,6 +53350,8 @@ async function findTasks(options, cwd, accessor) {
53286
53350
  priority: t.priority,
53287
53351
  type: t.type,
53288
53352
  parentId: t.parentId,
53353
+ depends: t.depends ?? [],
53354
+ size: t.size ?? void 0,
53289
53355
  score: Math.round(score)
53290
53356
  });
53291
53357
  }