@unbrained/pm-cli 2026.5.27 → 2026.5.28

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.
@@ -1,10 +1,16 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8864ae8d-f08f-5358-b195-244f8b441eae")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4547acbc-0dcb-5ccd-9900-5384c6a1e956")}catch(e){}}();
3
3
  import { Option } from "commander";
4
4
  import { EXIT_CODE } from "../core/shared/constants.js";
5
5
  import { PmCliError } from "../core/shared/errors.js";
6
6
  import { isPureSnakeCaseAlias } from "../core/shared/option-alias-visibility.js";
7
7
  import { CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, } from "../sdk/cli-contracts.js";
8
+ import { BUILTIN_ITEM_TYPE_VALUES } from "../types/index.js";
9
+ // Lowercase set of built-in type names ("epic", "feature", ...) used by the
10
+ // `pm create` positional guard (pm-edge #1, 2026-05-28): if the single
11
+ // positional exactly matches a known type AND no --title was given, we throw
12
+ // instead of silently creating a Task titled with the type name.
13
+ const BUILTIN_TYPE_NAME_LOOKUP = new Set(BUILTIN_ITEM_TYPE_VALUES.map((value) => value.toLowerCase()));
8
14
  import { collect, extractUpdateManyMutationOptionSource, formatHookWarnings, getGlobalOptions, invalidateSearchCachesForMutation, normalizeCreateOptions, normalizeUpdateOptions, printError, printResult, writeStdout, } from "./registration-helpers.js";
9
15
  import { createLazyModule } from "../core/shared/lazy-module.js";
10
16
  const loadMutationCommandsModule = createLazyModule(() => import("./commands/index.js"));
@@ -82,6 +88,31 @@ export function registerMutationCommands(program) {
82
88
  else if (typeof typeOrTitle === "string" && typeOrTitle.length > 0) {
83
89
  positionalTitle = typeOrTitle;
84
90
  }
91
+ // pm-edge #1 (2026-05-28): when the sole positional matches a known
92
+ // item type AND no --title was supplied, refuse early instead of
93
+ // silently creating a Task titled with the type name (e.g. `pm create
94
+ // Epic` would previously produce a Task literally titled "Epic"). The
95
+ // guard fires only for the ambiguous single-positional case so the
96
+ // documented `pm create <type> <title>` flow stays a never-block.
97
+ if (positionalType === undefined &&
98
+ typeof positionalTitle === "string" &&
99
+ positionalTitle.length > 0 &&
100
+ options.title === undefined &&
101
+ options.type === undefined &&
102
+ BUILTIN_TYPE_NAME_LOOKUP.has(positionalTitle.trim().toLowerCase())) {
103
+ const matchedType = positionalTitle.trim();
104
+ throw new PmCliError(`pm create needs a title — "${matchedType}" looks like an item type, not a title. Use either: pm create ${matchedType} "<title>" or pm create "<title>" --type ${matchedType}.`, EXIT_CODE.USAGE, {
105
+ code: "create_positional_type_without_title",
106
+ why: "Without this guard the single positional is used as the title and the type defaults to Task — so the command would silently create a Task literally titled \"" + matchedType + "\".",
107
+ examples: [
108
+ `pm create ${matchedType} "Wire up SSO for the agent harness"`,
109
+ `pm create "Wire up SSO for the agent harness" --type ${matchedType}`,
110
+ ],
111
+ nextSteps: [
112
+ `Re-run with both type and title: pm create ${matchedType} "<title>"`,
113
+ ],
114
+ });
115
+ }
85
116
  if (typeof positionalType === "string" && positionalType.length > 0 && options.type === undefined) {
86
117
  options.type = positionalType;
87
118
  }
@@ -287,7 +318,7 @@ export function registerMutationCommands(program) {
287
318
  printError(`profile:command=update-many took_ms=${Date.now() - startedAt}`);
288
319
  }
289
320
  });
290
- program
321
+ const closeCommand = program
291
322
  .command("close")
292
323
  .argument("<id>", "Item id")
293
324
  .argument("[text]", "Close reason text (alias: --reason)")
@@ -296,8 +327,17 @@ export function registerMutationCommands(program) {
296
327
  .option("--author <value>", "Mutation author")
297
328
  .option("--message <value>", "History message")
298
329
  .option("--validate-close [mode]", 'Validate closure metadata before close: "off", "warn", or "strict" (default: settings governance preset)')
330
+ .option("--resolution <value>", "Set the closure resolution summary inline (same field --validate-close strict checks; previously required a prior pm update)")
331
+ .option("--expected-result <value>", "Set the expected-result note inline (closure validation field)")
332
+ .option("--actual-result <value>", "Set the actual-result note inline (closure validation field)")
299
333
  .option("--force", "Force ownership override")
300
- .description("Close an item with a required reason.")
334
+ .description("Close an item with a required reason.");
335
+ // pm-fl0c #11 (2026-05-28): expose snake_case aliases alongside the canonical
336
+ // kebab-case so agents using --expected_result/--actual_result do not get an
337
+ // Unknown option error; the rendered help stays clean (aliases hidden).
338
+ addHiddenOption(closeCommand, "--expected_result <value>", "Alias for --expected-result", false);
339
+ addHiddenOption(closeCommand, "--actual_result <value>", "Alias for --actual-result", false);
340
+ closeCommand
301
341
  .action(async (id, text, options, command) => {
302
342
  const globalOptions = getGlobalOptions(command);
303
343
  const startedAt = Date.now();
@@ -319,6 +359,10 @@ export function registerMutationCommands(program) {
319
359
  ],
320
360
  });
321
361
  }
362
+ const pickInlineString = (canonical, snake) => {
363
+ const value = typeof canonical === "string" ? canonical : typeof snake === "string" ? snake : undefined;
364
+ return value !== undefined ? value : undefined;
365
+ };
322
366
  const result = await runClose(id, resolvedText, {
323
367
  author: typeof options.author === "string" ? options.author : undefined,
324
368
  message: typeof options.message === "string" ? options.message : undefined,
@@ -328,6 +372,9 @@ export function registerMutationCommands(program) {
328
372
  ? options.validateClose
329
373
  : undefined,
330
374
  force: Boolean(options.force),
375
+ resolution: typeof options.resolution === "string" ? options.resolution : undefined,
376
+ expectedResult: pickInlineString(options.expectedResult, options.expected_result),
377
+ actualResult: pickInlineString(options.actualResult, options.actual_result),
331
378
  }, globalOptions);
332
379
  await invalidateSearchCachesForMutation(globalOptions, result);
333
380
  printResult(result, globalOptions);
@@ -1004,4 +1051,4 @@ export function registerMutationCommands(program) {
1004
1051
  });
1005
1052
  }
1006
1053
  //# sourceMappingURL=register-mutation.js.map
1007
- //# debugId=8864ae8d-f08f-5358-b195-244f8b441eae
1054
+ //# debugId=4547acbc-0dcb-5ccd-9900-5384c6a1e956