@unbrained/pm-cli 2026.8.27 → 2026.8.29

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 (148) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +49 -1
  3. package/dist/cli/error-guidance.js +61 -7
  4. package/dist/cli/register-annotations.js +58 -41
  5. package/dist/cli-bundle/bundle-manifest.json +142 -142
  6. package/dist/cli-bundle/chunks/{chunk-TNX6HC54.js → chunk-2MKLQL2E.js} +2 -2
  7. package/dist/cli-bundle/chunks/{chunk-SKXLJIEK.js → chunk-2UETTHY6.js} +49 -49
  8. package/dist/cli-bundle/chunks/{chunk-SHMDY36D.js → chunk-3TEF3HM5.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-SH6P7FXI.js → chunk-7SKMLSMX.js} +2 -2
  10. package/dist/cli-bundle/chunks/chunk-7X6OZEIV.js +34 -0
  11. package/dist/cli-bundle/chunks/{chunk-CVBBGWW5.js → chunk-CMIEDWAY.js} +54 -50
  12. package/dist/cli-bundle/chunks/{chunk-3OO3W6FW.js → chunk-GNWPTEBW.js} +64 -61
  13. package/dist/cli-bundle/chunks/chunk-MTTKAUBX.js +2 -0
  14. package/dist/cli-bundle/chunks/chunk-N6OR6FAR.js +13 -0
  15. package/dist/cli-bundle/chunks/{chunk-R4ETAOJC.js → chunk-TGNAA66W.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-52EKTW6V.js → chunk-WS2GJQVG.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-list-query-EUWM6VII.js → register-list-query-OMKJ5ONN.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-FD4HSAVU.js → register-mutation-QBSNCOY3.js} +4 -4
  19. package/dist/cli-bundle/chunks/{register-operations-HRMNFEC3.js → register-operations-UC77FQZC.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-setup-33GNICLX.js → register-setup-GLQ5SYV3.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-HC7ODMH3.js → chunk-3AVAWVZA.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/chunk-3UMBF26I.js +158 -0
  23. package/dist/cli-bundle/focused-chunks/{chunk-YVVZ3LQ6.js → chunk-73EDXGYE.js} +16 -15
  24. package/dist/cli-bundle/focused-chunks/{chunk-4BR5UU52.js → chunk-AXZ5AJAF.js} +45 -45
  25. package/dist/cli-bundle/focused-chunks/{chunk-MXTYGECH.js → chunk-G6PATS5E.js} +3 -3
  26. package/dist/cli-bundle/focused-chunks/{chunk-SUBSWYW3.js → chunk-JZYPPMXF.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-LLNTHF5X.js +2 -0
  28. package/dist/cli-bundle/focused-chunks/chunk-LYFWQMVC.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-RKOKG43G.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/chunk-TDK3OCX3.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-EKX37ZHA.js → chunk-THEPQMLX.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-FC2AXLB5.js → chunk-XXOWRJZ5.js} +2 -2
  33. package/dist/cli-bundle/main.js +14 -14
  34. package/dist/cli-bundle/sdk-authoring.js +1 -1
  35. package/dist/cli-bundle/sdk-contracts.js +2 -2
  36. package/dist/cli-bundle/sdk-core.js +31 -32
  37. package/dist/cli-bundle/sdk-governance.js +1 -1
  38. package/dist/cli-bundle/sdk-graph.js +1 -1
  39. package/dist/cli-bundle/sdk-merge.js +34 -32
  40. package/dist/cli-bundle/sdk-query.js +1 -1
  41. package/dist/cli-bundle/sdk-runtime.js +1 -1
  42. package/dist/cli-bundle/sdk-testing.js +1 -1
  43. package/dist/cli-bundle/sdk.js +10 -10
  44. package/dist/core/diagnostics/remediation.js +16 -6
  45. package/dist/core/history/drift-scan.d.ts +2 -0
  46. package/dist/core/history/drift-scan.js +72 -36
  47. package/dist/core/history/history.d.ts +3 -3
  48. package/dist/core/history/history.js +5 -5
  49. package/dist/core/history/replay.d.ts +1 -1
  50. package/dist/core/history/replay.js +50 -19
  51. package/dist/core/item/actionability.d.ts +7 -1
  52. package/dist/core/item/actionability.js +44 -11
  53. package/dist/core/item/dependency-reference.d.ts +10 -0
  54. package/dist/core/item/dependency-reference.js +21 -0
  55. package/dist/core/item/parse.d.ts +4 -0
  56. package/dist/core/item/parse.js +38 -16
  57. package/dist/core/shared/author.js +69 -17
  58. package/dist/mcp/tool-definitions.js +5 -5
  59. package/dist/sdk/agent/command-recovery.js +3 -3
  60. package/dist/sdk/agent/task-transcript-contracts.d.ts +52 -0
  61. package/dist/sdk/agent/task-transcript-contracts.js +198 -0
  62. package/dist/sdk/annotations.d.ts +5 -2
  63. package/dist/sdk/annotations.js +66 -36
  64. package/dist/sdk/cli-bootstrap.d.ts +2 -8
  65. package/dist/sdk/cli-bootstrap.js +7 -67
  66. package/dist/sdk/cli-contracts/bootstrap-command-scanner.d.ts +23 -0
  67. package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +80 -0
  68. package/dist/sdk/cli-contracts/flag-contracts.js +5 -2
  69. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +5 -5
  70. package/dist/sdk/cli-contracts/tool-parameter-tables.js +15 -2
  71. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  72. package/dist/sdk/cli-contracts/tool-schema.js +16 -3
  73. package/dist/sdk/comments.d.ts +4 -0
  74. package/dist/sdk/comments.js +2 -2
  75. package/dist/sdk/completion.js +9 -5
  76. package/dist/sdk/contracts.d.ts +1 -0
  77. package/dist/sdk/contracts.js +3 -2
  78. package/dist/sdk/dependency-provenance.d.ts +51 -2
  79. package/dist/sdk/dependency-provenance.js +91 -7
  80. package/dist/sdk/extension/install-sources.d.ts +8 -1
  81. package/dist/sdk/extension/install-sources.js +132 -39
  82. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -14
  83. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +86 -38
  84. package/dist/sdk/governance/health.js +86 -22
  85. package/dist/sdk/governance/validate-history-drift.js +5 -2
  86. package/dist/sdk/graph/assembly.js +51 -22
  87. package/dist/sdk/history-repair.d.ts +1 -1
  88. package/dist/sdk/history-repair.js +20 -10
  89. package/dist/sdk/index.d.ts +1 -0
  90. package/dist/sdk/index.js +3 -2
  91. package/dist/sdk/learnings.d.ts +4 -0
  92. package/dist/sdk/learnings.js +7 -4
  93. package/dist/sdk/lifecycle/close.js +4 -3
  94. package/dist/sdk/lifecycle/create.js +43 -19
  95. package/dist/sdk/linked-artifacts.js +21 -31
  96. package/dist/sdk/merge/reconcile.js +3 -16
  97. package/dist/sdk/notes.d.ts +4 -0
  98. package/dist/sdk/notes.js +2 -2
  99. package/dist/sdk/query/next.d.ts +6 -0
  100. package/dist/sdk/query/next.js +7 -2
  101. package/dist/sdk/runtime-input.js +5 -2
  102. package/dist/sdk/runtime.d.ts +6 -6
  103. package/dist/sdk/runtime.js +8 -8
  104. package/dist/sdk/test/entry.d.ts +1 -1
  105. package/dist/sdk/test/entry.js +5 -9
  106. package/dist/sdk/test/execution.d.ts +3 -0
  107. package/dist/sdk/test/execution.js +54 -13
  108. package/docs/COMMANDS.md +4 -3
  109. package/docs/CONTEXT_RECOVERY_PRIMITIVES.md +67 -0
  110. package/docs/MERGE_SAFETY.md +23 -10
  111. package/docs/OUTPUT_TOKEN_ACCOUNTING.md +20 -7
  112. package/docs/README.md +1 -0
  113. package/docs/RELEASING.md +9 -1
  114. package/docs/SDK.md +12 -0
  115. package/docs/SDK_CONTEXT_INTEGRITY.md +12 -1
  116. package/docs/SDK_RUNTIME_BOUNDARIES.md +10 -0
  117. package/docs/TESTING.md +6 -2
  118. package/docs/agent-task-token-baseline.json +97 -11
  119. package/docs/agent-task-transcripts.json +211 -0
  120. package/docs/generated/FLAG_LEXICON_BUDGETS.md +3 -3
  121. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +13 -4
  122. package/docs/performance/cli-transport-overhead.md +10 -2
  123. package/marketplace.json +2 -2
  124. package/package.json +8 -8
  125. package/packages/pm-beads/package.json +1 -1
  126. package/packages/pm-calendar/package.json +1 -1
  127. package/packages/pm-command-kit/package.json +1 -1
  128. package/packages/pm-digital-twin/package.json +1 -1
  129. package/packages/pm-governance-audit/package.json +1 -1
  130. package/packages/pm-guide-shell/package.json +1 -1
  131. package/packages/pm-kanban/package.json +1 -1
  132. package/packages/pm-lifecycle-hooks/package.json +1 -1
  133. package/packages/pm-linked-test-adapters/package.json +1 -1
  134. package/packages/pm-search-advanced/package.json +1 -1
  135. package/packages/pm-templates/package.json +1 -1
  136. package/packages/pm-todos/package.json +1 -1
  137. package/packages/pm-vcs/package.json +1 -1
  138. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  139. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  140. package/sdk/public-surface.json +297 -36
  141. package/dist/cli-bundle/chunks/chunk-MFNTKMTI.js +0 -13
  142. package/dist/cli-bundle/chunks/chunk-OS27HHBN.js +0 -35
  143. package/dist/cli-bundle/chunks/chunk-QTO7USTH.js +0 -2
  144. package/dist/cli-bundle/focused-chunks/chunk-2AGZ5BRT.js +0 -2
  145. package/dist/cli-bundle/focused-chunks/chunk-AQ5IYEZZ.js +0 -2
  146. package/dist/cli-bundle/focused-chunks/chunk-XDPYBQCF.js +0 -156
  147. package/dist/cli-bundle/focused-chunks/chunk-Y5A7SJJ7.js +0 -2
  148. package/dist/cli-bundle/focused-chunks/chunk-YHWHX6YY.js +0 -2
@@ -4,7 +4,7 @@
4
4
  * Implements the pm completion command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !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]="c8446891-4e65-54c3-9826-eb40f4922f3b")}catch(e){}}();
7
+ !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]="4bf2b42b-91f3-5850-b1f1-d8a01525860c")}catch(e){}}();
8
8
  import { EXIT_CODE, PmCliError } from "./runtime-primitives.js";
9
9
  import { listPmCommandsForTier } from "./agent-capability-contracts.js";
10
10
  import { SCAFFOLD_CAPABILITIES } from "./extension/scaffold.js";
@@ -601,13 +601,13 @@ export function generateBashScript(itemTypes = [], tags = [], eagerTagExpansion
601
601
  ` COMPREPLY=(${compgen(`${PACKAGE_LIFECYCLE_ACTIONS} ${PACKAGE_LIFECYCLE_FLAGS} ${UPGRADE_FLAGS}`)})`,
602
602
  " ;;",
603
603
  " comments)",
604
- ` COMPREPLY=(${compgen("--add --body --stdin --file --edit --delete --limit --full-history --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
604
+ ` COMPREPLY=(${compgen("--add --body --stdin --file --edit --delete --limit --full-history --if-absent --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
605
605
  " ;;",
606
606
  " notes)",
607
- ` COMPREPLY=(${compgen("--add --add-json --stdin --file --edit --delete --limit --since --event-type --include-meta --full-history --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
607
+ ` COMPREPLY=(${compgen("--add --add-json --stdin --file --edit --delete --limit --since --event-type --include-meta --full-history --if-absent --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
608
608
  " ;;",
609
609
  " learnings)",
610
- ` COMPREPLY=(${compgen("--add --stdin --file --edit --delete --limit --full-history --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
610
+ ` COMPREPLY=(${compgen("--add --stdin --file --edit --delete --limit --full-history --if-absent --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
611
611
  " ;;",
612
612
  " files)",
613
613
  ` COMPREPLY=(${compgen("discover lookup --add --add-glob --remove --migrate --list --apply --note --append-stable --validate-paths --scope --limit --offset --no-truncate --strict-read --explain --lines --decision-depth --author --message --force --json --quiet --no-changed-fields --pm-path --path --no-extensions --no-pager --profile --help")})`,
@@ -1370,6 +1370,7 @@ ${zshSearchRuntimeFieldFlags} '--json[Output JSON]' \\
1370
1370
  '--delete[Delete the comment at 1-based index]:index' \\
1371
1371
  '--limit[Return only latest n entries]:number' \\
1372
1372
  '--full-history[Return complete post-mutation history]' \\
1373
+ '--if-absent[Append only when the resolved author and text are absent]' \\
1373
1374
  '--author[Entry author (falls back to PM_AUTHOR/settings)]:author' \\
1374
1375
  '--message[History message]:message' \\
1375
1376
  '--force[Force override]' \\
@@ -1390,6 +1391,7 @@ ${zshSearchRuntimeFieldFlags} '--json[Output JSON]' \\
1390
1391
  '--event-type[Return structured events with this top-level type]:event_type' \\
1391
1392
  '--include-meta[Include result counts and truncation metadata]' \\
1392
1393
  '--full-history[Return complete post-mutation history]' \\
1394
+ '--if-absent[Append only when the resolved author and text are absent]' \\
1393
1395
  '--author[Entry author (falls back to PM_AUTHOR/settings)]:author' \\
1394
1396
  '--message[History message]:message' \\
1395
1397
  '--force[Force override]' \\
@@ -1406,6 +1408,7 @@ ${zshSearchRuntimeFieldFlags} '--json[Output JSON]' \\
1406
1408
  '--delete[Delete the entry at a 1-based index]:index' \\
1407
1409
  '--limit[Return only latest n entries]:number' \\
1408
1410
  '--full-history[Return complete post-mutation history]' \\
1411
+ '--if-absent[Append only when the resolved author and text are absent]' \\
1409
1412
  '--author[Entry author (falls back to PM_AUTHOR/settings)]:author' \\
1410
1413
  '--message[History message]:message' \\
1411
1414
  '--force[Force override]' \\
@@ -2452,6 +2455,7 @@ complete -c pm -n '__fish_seen_subcommand_from comments' -l stdin -d 'Read comme
2452
2455
  complete -c pm -n '__fish_seen_subcommand_from comments' -l file -d 'Read comment text from file (supports multiline markdown)' -r
2453
2456
  complete -c pm -n '__fish_seen_subcommand_from comments' -l edit -d 'Replace the comment at 1-based index (text from positional/--add/--stdin/--file)' -r
2454
2457
  complete -c pm -n '__fish_seen_subcommand_from comments' -l delete -d 'Delete the comment at 1-based index' -r
2458
+ complete -c pm -n '__fish_seen_subcommand_from comments notes learnings' -l if-absent -d 'Append only when the resolved author and text are absent'
2455
2459
  complete -c pm -n '__fish_seen_subcommand_from notes' -l stdin -d 'Read entry text from stdin'
2456
2460
  complete -c pm -n '__fish_seen_subcommand_from notes' -l file -d 'Read entry text from file' -r
2457
2461
  complete -c pm -n '__fish_seen_subcommand_from notes' -l edit -d 'Replace the entry at a 1-based index' -r
@@ -2794,4 +2798,4 @@ export function runCompletion(shell, itemTypes = [], tags = [], eagerTagExpansio
2794
2798
  };
2795
2799
  }
2796
2800
  //# sourceMappingURL=completion.js.map
2797
- //# debugId=c8446891-4e65-54c3-9826-eb40f4922f3b
2801
+ //# debugId=4bf2b42b-91f3-5850-b1f1-d8a01525860c
@@ -13,6 +13,7 @@ export * from "./flag-invocation-contracts.js";
13
13
  export * from "./cli-contracts/flag-lexicon-contracts.js";
14
14
  export * from "./output-token-accounting.js";
15
15
  export * from "./output-contracts.js";
16
+ export * from "./agent/task-transcript-contracts.js";
16
17
  export * from "./read-output-contracts.js";
17
18
  export * from "./agent/refusal-closure.js";
18
19
  export * from "./agent/closed-domain-contracts.js";
@@ -4,7 +4,7 @@
4
4
  * Exposes command and action contract data without loading SDK runtime code.
5
5
  */
6
6
 
7
- !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]="8bdc03e5-c5cd-5842-b438-cf7bc79863f0")}catch(e){}}();
7
+ !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]="b47a3cc6-caba-5de8-8813-a4e6112dbe61")}catch(e){}}();
8
8
  export * from "./cli-contracts.js";
9
9
  export * from "./cli-contracts/grammar-contracts.js";
10
10
  export * from "./cli-contracts/command-exit-contracts.js";
@@ -15,9 +15,10 @@ export * from "./flag-invocation-contracts.js";
15
15
  export * from "./cli-contracts/flag-lexicon-contracts.js";
16
16
  export * from "./output-token-accounting.js";
17
17
  export * from "./output-contracts.js";
18
+ export * from "./agent/task-transcript-contracts.js";
18
19
  export * from "./read-output-contracts.js";
19
20
  export * from "./agent/refusal-closure.js";
20
21
  export * from "./agent/closed-domain-contracts.js";
21
22
  export * from "./agent/tracker-preflight-contracts.js";
22
23
  //# sourceMappingURL=contracts.js.map
23
- //# debugId=8bdc03e5-c5cd-5842-b438-cf7bc79863f0
24
+ //# debugId=b47a3cc6-caba-5de8-8813-a4e6112dbe61
@@ -1,9 +1,58 @@
1
+ export { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
1
2
  /** Provenance value declaring that a dependency id belongs to another workspace. */
2
3
  export declare const EXTERNAL_DEPENDENCY_SOURCE_KIND = "global";
3
4
  /** Human-facing alias accepted wherever external dependency provenance is parsed. */
4
5
  export declare const EXTERNAL_DEPENDENCY_SOURCE_KIND_ALIAS = "external";
5
- /** Return whether a dependency provenance value identifies an external workspace target. */
6
- export declare function isExternalDependencySourceKind(sourceKind: string | undefined): boolean;
6
+ /** Stable external lifecycle values understood without coupling core to a provider. */
7
+ export declare const EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES: readonly ["open", "closed", "unknown"];
8
+ /** External lifecycle value returned by a registered dependency resolver. */
9
+ export type ExternalDependencyResolutionStatus = (typeof EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES)[number];
10
+ /** Provider-owned external dependency data before the SDK applies output bounds. */
11
+ export interface ExternalDependencyResolverResult {
12
+ /** Provider lifecycle state normalized to the universal external status vocabulary. */
13
+ status: ExternalDependencyResolutionStatus;
14
+ /** Optional bounded human context for the remote dependency. */
15
+ title?: string;
16
+ /** Optional canonical provider URL or locator used as evidence. */
17
+ source?: string;
18
+ /** Optional provider observation timestamp; invalid timestamps are replaced by SDK time. */
19
+ checkedAt?: string;
20
+ }
21
+ /** Package-provided resolver contract for one or more external dependency locator families. */
22
+ export interface ExternalDependencyResolver {
23
+ /** Stable resolver identity surfaced in agent-facing evidence. */
24
+ name: string;
25
+ /** Return whether this resolver owns the supplied external locator. */
26
+ supports(reference: string): boolean;
27
+ /** Resolve the locator without exposing provider credentials or unbounded response data. */
28
+ resolve(reference: string): Promise<ExternalDependencyResolverResult | null>;
29
+ }
30
+ /** Bounded provider-neutral evidence returned by the public resolver registry. */
31
+ export interface ExternalDependencyResolution {
32
+ /** Original normalized dependency locator. */
33
+ id: string;
34
+ /** Provider lifecycle state. */
35
+ status: ExternalDependencyResolutionStatus;
36
+ /** Whether trusted provider evidence proves the blocker terminal. */
37
+ resolved: boolean;
38
+ /** Bounded remote title, or null when the provider did not supply one. */
39
+ title: string | null;
40
+ /** Bounded evidence locator. */
41
+ source: string;
42
+ /** Observation timestamp supplied by the provider or SDK clock. */
43
+ checked_at: string;
44
+ /** Registered resolver identity that produced this evidence. */
45
+ resolver: string;
46
+ }
47
+ /** Deterministic clock override for external resolver acceptance and tests. */
48
+ export interface ExternalDependencyResolutionOptions {
49
+ /** Return the timestamp used when provider evidence omits or corrupts checkedAt. */
50
+ now?: () => string;
51
+ }
52
+ /** Register a package-owned external dependency resolver and return its idempotent disposer. */
53
+ export declare function registerExternalDependencyResolver(resolver: ExternalDependencyResolver): () => void;
54
+ /** Resolve one external locator through registered package providers with bounded provider-neutral evidence. */
55
+ export declare function resolveExternalDependencyReference(reference: string, options?: ExternalDependencyResolutionOptions): Promise<ExternalDependencyResolution | null>;
7
56
  /** Canonicalize external provenance while preserving other named source kinds. */
8
57
  export declare function normalizeDependencySourceKind(sourceKind: string | undefined): string | undefined;
9
58
  /**
@@ -5,17 +5,101 @@
5
5
  * items outside the current workspace.
6
6
  */
7
7
 
8
- !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]="1b2f1ac3-82b3-58a2-9f40-2dd95ab1b2bc")}catch(e){}}();
8
+ !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]="f3f3beb2-1d60-52fe-88ce-1ab3cb9dba0d")}catch(e){}}();
9
9
  import { normalizeItemId } from "../core/item/id.js";
10
+ import { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
11
+ export { isExternalDependencyReference, isExternalDependencySourceKind, } from "../core/item/dependency-reference.js";
10
12
  /** Provenance value declaring that a dependency id belongs to another workspace. */
11
13
  export const EXTERNAL_DEPENDENCY_SOURCE_KIND = "global";
12
14
  /** Human-facing alias accepted wherever external dependency provenance is parsed. */
13
15
  export const EXTERNAL_DEPENDENCY_SOURCE_KIND_ALIAS = "external";
14
- /** Return whether a dependency provenance value identifies an external workspace target. */
15
- export function isExternalDependencySourceKind(sourceKind) {
16
- const normalized = sourceKind?.trim().toLowerCase();
17
- return (normalized === EXTERNAL_DEPENDENCY_SOURCE_KIND ||
18
- normalized === EXTERNAL_DEPENDENCY_SOURCE_KIND_ALIAS);
16
+ /** Stable external lifecycle values understood without coupling core to a provider. */
17
+ export const EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES = [
18
+ "open",
19
+ "closed",
20
+ "unknown",
21
+ ];
22
+ const externalDependencyResolvers = new Map();
23
+ const MAX_EXTERNAL_TITLE_LENGTH = 240;
24
+ const MAX_EXTERNAL_SOURCE_LENGTH = 2_048;
25
+ /** Normalize and cap provider strings before they reach agent-facing output. */
26
+ function boundedExternalText(value, fallback, maximumLength) {
27
+ return (value?.trim() || fallback).slice(0, maximumLength);
28
+ }
29
+ /** Reject provider payloads that violate the runtime resolver-result contract. */
30
+ function isExternalDependencyResolverResult(value) {
31
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
32
+ return false;
33
+ }
34
+ const candidate = value;
35
+ return (typeof candidate.status === "string" &&
36
+ (candidate.title == null || typeof candidate.title === "string") &&
37
+ (candidate.source == null || typeof candidate.source === "string") &&
38
+ (candidate.checkedAt == null || typeof candidate.checkedAt === "string"));
39
+ }
40
+ /** Register a package-owned external dependency resolver and return its idempotent disposer. */
41
+ export function registerExternalDependencyResolver(resolver) {
42
+ const name = resolver.name.trim();
43
+ if (name.length === 0) {
44
+ throw new Error("External dependency resolver name must not be empty");
45
+ }
46
+ if (externalDependencyResolvers.has(name)) {
47
+ throw new Error(`External dependency resolver already registered: ${name}`);
48
+ }
49
+ const registeredResolver = {
50
+ name,
51
+ supports: (reference) => resolver.supports(reference),
52
+ resolve: (reference) => resolver.resolve(reference),
53
+ };
54
+ externalDependencyResolvers.set(name, registeredResolver);
55
+ return () => {
56
+ if (externalDependencyResolvers.get(name) === registeredResolver) {
57
+ externalDependencyResolvers.delete(name);
58
+ }
59
+ };
60
+ }
61
+ /** Resolve one external locator through registered package providers with bounded provider-neutral evidence. */
62
+ export async function resolveExternalDependencyReference(reference, options = {}) {
63
+ const id = reference.trim();
64
+ if (!isExternalDependencyReference(id)) {
65
+ return null;
66
+ }
67
+ const resolvers = [...externalDependencyResolvers.values()].filter((resolver) => {
68
+ try {
69
+ return resolver.supports(id);
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ });
75
+ const outcomes = await Promise.allSettled(resolvers.map((resolver) => Promise.resolve().then(() => resolver.resolve(id))));
76
+ for (const [index, outcome] of outcomes.entries()) {
77
+ if (outcome.status !== "fulfilled" ||
78
+ !isExternalDependencyResolverResult(outcome.value)) {
79
+ continue;
80
+ }
81
+ const resolver = resolvers[index];
82
+ const candidate = outcome.value;
83
+ const status = EXTERNAL_DEPENDENCY_RESOLUTION_STATUSES.includes(candidate.status)
84
+ ? candidate.status
85
+ : "unknown";
86
+ const title = boundedExternalText(candidate.title, "", MAX_EXTERNAL_TITLE_LENGTH);
87
+ const source = boundedExternalText(candidate.source, id, MAX_EXTERNAL_SOURCE_LENGTH);
88
+ const observedAtMs = Date.parse(candidate.checkedAt?.trim() ?? "");
89
+ const checkedAt = Number.isFinite(observedAtMs)
90
+ ? new Date(observedAtMs).toISOString()
91
+ : (options.now?.() ?? new Date().toISOString());
92
+ return {
93
+ id,
94
+ status,
95
+ resolved: status === "closed",
96
+ title: title.length > 0 ? title : null,
97
+ source,
98
+ checked_at: checkedAt,
99
+ resolver: resolver.name,
100
+ };
101
+ }
102
+ return null;
19
103
  }
20
104
  /** Canonicalize external provenance while preserving other named source kinds. */
21
105
  export function normalizeDependencySourceKind(sourceKind) {
@@ -39,4 +123,4 @@ export function normalizeDependencySeedId(id, prefix, sourceKind) {
39
123
  : normalizeItemId(trimmed, prefix);
40
124
  }
41
125
  //# sourceMappingURL=dependency-provenance.js.map
42
- //# debugId=1b2f1ac3-82b3-58a2-9f40-2dd95ab1b2bc
126
+ //# debugId=f3f3beb2-1d60-52fe-88ce-1ab3cb9dba0d
@@ -107,7 +107,7 @@ export declare function wrapNpmPackResolutionError(spec: string, error: unknown)
107
107
  declare function resolveNpmPackSpec(spec: string): Promise<string>;
108
108
  /** Implements normalize npm local file alias spec for the public runtime surface of this module. */
109
109
  export declare function normalizeNpmLocalFileAliasSpec(spec: string, cwd?: string): string;
110
- declare function parsePackedNpmPackage(stdout: string, packDirectory: string): {
110
+ declare function parsePackedNpmPackage(stdout: string, packDirectory: string, expectedPackage?: string): {
111
111
  tarball: string;
112
112
  package?: string;
113
113
  version?: string;
@@ -118,6 +118,13 @@ declare function resolveNpmSourceDirectory(source: NpmInstallSource): Promise<{
118
118
  version?: string;
119
119
  cleanup: () => Promise<void>;
120
120
  }>;
121
+ /**
122
+ * Resolve an npm source with an injectable, argv-safe package-manager runner.
123
+ *
124
+ * Local package directories remain in place, while local and registry archives
125
+ * share the same bounded validation, dependency installation, and cleanup
126
+ * contract. Registry artifacts must also exist inside the requested pack root.
127
+ */
121
128
  declare function resolveNpmSourceDirectoryWithRunner(source: NpmInstallSource, npmRunner: typeof runNpmCommand): Promise<{
122
129
  directory: string;
123
130
  package?: string;
@@ -4,7 +4,7 @@
4
4
  * Implements extension package-management support for Install Sources.
5
5
  */
6
6
 
7
- !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]="89b84412-aa15-5868-95b3-c8d898b1f480")}catch(e){}}();
7
+ !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]="05f56edb-c08d-570c-b8b5-641fc694331e")}catch(e){}}();
8
8
  import { execFile } from "node:child_process";
9
9
  import fs from "node:fs/promises";
10
10
  import os from "node:os";
@@ -237,6 +237,14 @@ function npmPackageNameFromSpec(spec) {
237
237
  const unscoped = withoutAlias.match(/^([^/@\s]+)/);
238
238
  return unscoped?.[1] ?? withoutAlias;
239
239
  }
240
+ /** Return whether an npm spec is a complete local-file alias, including unchanged UNC targets. */
241
+ function isNpmLocalFileAliasSpec(spec) {
242
+ const marker = "@file:";
243
+ const markerIndex = spec.lastIndexOf(marker);
244
+ return (markerIndex > 0 &&
245
+ spec.slice(0, markerIndex).trim().length > 0 &&
246
+ spec.slice(markerIndex + marker.length).trim().length > 0);
247
+ }
240
248
  /** Implements check whether npm not found error for the public runtime surface of this module. */
241
249
  export function isNpmNotFoundError(error) {
242
250
  const message = error instanceof Error ? error.message : String(error);
@@ -358,14 +366,11 @@ async function resolveNpmPackSpec(spec) {
358
366
  export function normalizeNpmLocalFileAliasSpec(spec, cwd = process.cwd()) {
359
367
  const marker = "@file:";
360
368
  const markerIndex = spec.lastIndexOf(marker);
361
- if (markerIndex <= 0) {
369
+ if (!isNpmLocalFileAliasSpec(spec)) {
362
370
  return spec;
363
371
  }
364
372
  const packageName = spec.slice(0, markerIndex);
365
373
  const target = spec.slice(markerIndex + marker.length);
366
- if (packageName.trim().length === 0 || target.trim().length === 0) {
367
- return spec;
368
- }
369
374
  // `file://host/share` (exactly two leading slashes) is a UNC / network spec,
370
375
  // not a local path — leave it for npm to resolve.
371
376
  if (target.startsWith("//") && !target.startsWith("///")) {
@@ -397,38 +402,117 @@ export function normalizeNpmLocalFileAliasSpec(spec, cwd = process.cwd()) {
397
402
  : decodedPath;
398
403
  return `${packageName}@${nativePath}`;
399
404
  }
400
- function parsePackedNpmPackage(stdout, packDirectory) {
405
+ /**
406
+ * Parse npm pack output and bind the reported artifact to its isolated target.
407
+ *
408
+ * Supports current JSON metadata and the legacy filename-only form while
409
+ * refusing absolute or traversing results before the filesystem is consulted.
410
+ */
411
+ function resolvePackedTarball(packDirectory, filename) {
412
+ const tarball = path.resolve(packDirectory, filename);
413
+ const relativeTarball = path.relative(packDirectory, tarball);
414
+ if (relativeTarball.length === 0 ||
415
+ relativeTarball === ".." ||
416
+ relativeTarball.startsWith(`..${path.sep}`) ||
417
+ path.isAbsolute(relativeTarball)) {
418
+ throw new PmCliError("npm pack reported a package archive outside its isolated destination.", EXIT_CODE.GENERIC_FAILURE, {
419
+ code: "npm_package_archive_unsafe",
420
+ required: "Retry with a trusted npm client that writes its archive inside the requested pack destination.",
421
+ why: "pm will not read a package-manager artifact outside the isolated directory created for this install.",
422
+ });
423
+ }
424
+ return tarball;
425
+ }
426
+ function decodePackedNpmCandidate(value, key) {
427
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
428
+ return undefined;
429
+ }
430
+ const record = value;
431
+ if (typeof record.filename !== "string" ||
432
+ record.filename.trim().length === 0) {
433
+ return undefined;
434
+ }
435
+ return {
436
+ ...(key === undefined ? {} : { key }),
437
+ filename: record.filename,
438
+ ...(typeof record.name === "string" ? { name: record.name } : {}),
439
+ ...(typeof record.version === "string" ? { version: record.version } : {}),
440
+ };
441
+ }
442
+ function collectPackedNpmCandidates(parsed) {
443
+ const entries = Array.isArray(parsed)
444
+ ? parsed.map((value) => ({ value }))
445
+ : typeof parsed === "object" && parsed !== null
446
+ ? Object.entries(parsed).map(([key, value]) => ({ key, value }))
447
+ : [];
448
+ return entries.flatMap(({ key, value }) => {
449
+ const candidate = decodePackedNpmCandidate(value, key);
450
+ return candidate ? [candidate] : [];
451
+ });
452
+ }
453
+ function selectPackedNpmCandidate(candidates, expectedPackage) {
454
+ const normalizedExpected = expectedPackage?.trim().toLowerCase();
455
+ const matchingCandidates = normalizedExpected
456
+ ? candidates.filter((candidate) => {
457
+ const normalizedName = candidate.name?.trim().toLowerCase();
458
+ return normalizedName !== undefined
459
+ ? normalizedName === normalizedExpected
460
+ : candidate.key?.trim().toLowerCase() === normalizedExpected;
461
+ })
462
+ : [];
463
+ if (matchingCandidates.length === 1)
464
+ return matchingCandidates[0];
465
+ if (candidates.length !== 1)
466
+ return undefined;
467
+ const soleCandidate = candidates[0];
468
+ const soleIdentity = soleCandidate.name ?? soleCandidate.key;
469
+ return normalizedExpected &&
470
+ soleIdentity !== undefined &&
471
+ soleIdentity.trim().toLowerCase() !== normalizedExpected
472
+ ? undefined
473
+ : soleCandidate;
474
+ }
475
+ function parsePackedNpmPackage(stdout, packDirectory, expectedPackage) {
476
+ let parsed;
401
477
  try {
402
- const parsed = JSON.parse(stdout);
403
- const first = Array.isArray(parsed) ? parsed[0] : undefined;
404
- if (first &&
405
- typeof first.filename === "string" &&
406
- first.filename.trim().length > 0) {
407
- return {
408
- tarball: path.resolve(packDirectory, first.filename),
409
- package: typeof first.name === "string" ? first.name : undefined,
410
- version: typeof first.version === "string" ? first.version : undefined,
411
- };
412
- }
478
+ parsed = JSON.parse(stdout);
413
479
  }
414
480
  catch {
415
- // Fall back to the last stdout line for older npm output.
416
- }
417
- const lastLine = stdout
418
- .split(/\r?\n/)
419
- .map((line) => line.trim())
420
- .filter((line) => line.length > 0)
421
- .at(-1);
422
- if (!lastLine) {
423
- throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
481
+ const filename = stdout
482
+ .split(/\r?\n/)
483
+ .map((line) => line.trim())
484
+ .filter((line) => line.length > 0)
485
+ .at(-1);
486
+ if (!filename) {
487
+ throw new PmCliError("npm pack did not report a tarball filename.", EXIT_CODE.GENERIC_FAILURE);
488
+ }
489
+ return { tarball: resolvePackedTarball(packDirectory, filename) };
490
+ }
491
+ const candidates = collectPackedNpmCandidates(parsed);
492
+ const selected = selectPackedNpmCandidate(candidates, expectedPackage);
493
+ if (!selected) {
494
+ throw new PmCliError("npm pack --json returned an unsupported or ambiguous package receipt.", EXIT_CODE.GENERIC_FAILURE, {
495
+ code: "npm_pack_json_shape_unsupported",
496
+ required: "Use an npm client that returns an array of package receipts or an object keyed by package name, with exactly one requested package entry containing filename.",
497
+ why: `Well-formed JSON contained ${candidates.length} usable package receipt(s), but pm could not select ${expectedPackage ?? "one package"} deterministically.`,
498
+ });
424
499
  }
425
500
  return {
426
- tarball: path.resolve(packDirectory, lastLine),
501
+ tarball: resolvePackedTarball(packDirectory, selected.filename),
502
+ package: selected.name ?? selected.key,
503
+ version: selected.version,
427
504
  };
428
505
  }
429
506
  async function resolveNpmSourceDirectory(source) {
430
507
  return resolveNpmSourceDirectoryWithRunner(source, runNpmCommand);
431
508
  }
509
+ /**
510
+ * Resolve an npm source with an injectable, argv-safe package-manager runner.
511
+ *
512
+ * Local package directories remain in place, while local and registry archives
513
+ * share the same bounded validation, dependency installation, and cleanup
514
+ * contract. Registry artifacts must also exist inside the requested pack root.
515
+ */
432
516
  async function resolveNpmSourceDirectoryWithRunner(source, npmRunner) {
433
517
  const localPackageRoot = await resolveLocalNpmPackagePath(source.spec);
434
518
  if (localPackageRoot) {
@@ -451,9 +535,7 @@ async function resolveNpmSourceDirectoryWithRunner(source, npmRunner) {
451
535
  }
452
536
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "pm-npm-package-source-"));
453
537
  const packDirectory = path.join(tempRoot, "pack");
454
- const extractDirectory = path.join(tempRoot, "extract");
455
538
  await fs.mkdir(packDirectory, { recursive: true });
456
- await fs.mkdir(extractDirectory, { recursive: true });
457
539
  try {
458
540
  const packSpec = await resolveNpmPackSpec(source.spec);
459
541
  const packStdout = await npmRunner([
@@ -463,17 +545,28 @@ async function resolveNpmSourceDirectoryWithRunner(source, npmRunner) {
463
545
  "--pack-destination",
464
546
  packDirectory,
465
547
  ]);
466
- const packed = parsePackedNpmPackage(packStdout, packDirectory);
467
- await execFileAsync("tar", ["-xzf", packed.tarball, "-C", extractDirectory], { encoding: "utf8" });
468
- const packageRoot = path.join(extractDirectory, "package");
469
- await installNpmPackageRuntimeDependencies(packageRoot);
470
- const directory = await resolvePackageExtensionDirectory(packageRoot, source.input);
548
+ const packed = parsePackedNpmPackage(packStdout, packDirectory, isNpmLocalFileAliasSpec(source.spec)
549
+ ? undefined
550
+ : npmPackageNameFromSpec(source.spec));
551
+ if (!(await pathExists(packed.tarball))) {
552
+ throw new PmCliError("npm pack did not create its reported package archive.", EXIT_CODE.GENERIC_FAILURE, {
553
+ code: "npm_package_archive_missing",
554
+ required: "Retry the npm package install with a healthy registry and writable temporary directory.",
555
+ why: "The package manager reported a tarball filename that was absent before validation, so pm cannot safely extract or install it.",
556
+ });
557
+ }
558
+ const extracted = await extractLocalPackageArchive(packed.tarball);
471
559
  return {
472
- directory,
473
- package: packed.package,
474
- version: packed.version,
560
+ directory: extracted.directory,
561
+ package: packed.package ?? extracted.package,
562
+ version: packed.version ?? extracted.version,
475
563
  cleanup: async () => {
476
- await fs.rm(tempRoot, { recursive: true, force: true });
564
+ try {
565
+ await extracted.cleanup();
566
+ }
567
+ finally {
568
+ await fs.rm(tempRoot, { recursive: true, force: true });
569
+ }
477
570
  },
478
571
  };
479
572
  }
@@ -962,4 +1055,4 @@ export const _testOnlyInstallSources = {
962
1055
  runNpmCommand,
963
1056
  };
964
1057
  //# sourceMappingURL=install-sources.js.map
965
- //# debugId=89b84412-aa15-5868-95b3-c8d898b1f480
1058
+ //# debugId=05f56edb-c08d-570c-b8b5-641fc694331e