@unbrained/pm-cli 2026.9.5 → 2026.9.6
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +32 -2
- package/dist/cli/main.js +10 -8
- package/dist/cli/register-operations.js +13 -8
- package/dist/cli-bundle/bundle-manifest.json +150 -158
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
- package/dist/cli-bundle/chunks/{chunk-WWAEGW4V.js → chunk-BAX2BYUW.js} +66 -66
- package/dist/cli-bundle/chunks/{chunk-IBYREGSP.js → chunk-CKOL5U7J.js} +51 -51
- package/dist/cli-bundle/chunks/{chunk-ZRAQS3UA.js → chunk-JT6XF6GE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HUAQITVW.js → chunk-JX4DH4WC.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QPZHQY2O.js → chunk-NPN5I67J.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-66VPKFP2.js → chunk-OF76PSDQ.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
- package/dist/cli-bundle/chunks/{chunk-3HBDTJRK.js → chunk-RWWBOWJ5.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
- package/dist/cli-bundle/chunks/{chunk-SF6SM6U5.js → chunk-WI2AYPA7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-AYKGODWT.js → register-list-query-5JL52D33.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-YZYG53SQ.js → register-mutation-ZPG7KMVP.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-UJCRVGBG.js → register-setup-H4SVAVX7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-QL66QNWG.js → chunk-3BZEOXLT.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-WOCIPMW7.js → chunk-EZR3GHFE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3ASU6WGI.js → chunk-LIJIA4TY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-H5FOGAVD.js → chunk-M6PBRFSH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-B2TTQGFG.js → chunk-NTPY5GSO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +177 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YEF5PQQ2.js → chunk-PEFABF7E.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KWWCBWFO.js → chunk-QTVCLMSZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-4OSHLNUE.js → chunk-QTZRBUYA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +10 -0
- package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +2 -0
- package/dist/cli-bundle/main.js +20 -20
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +37 -37
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +42 -42
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +9 -9
- package/dist/core/extensions/module-graph-snapshot.js +5 -4
- package/dist/core/fs/host-environment-errors.d.ts +27 -0
- package/dist/core/fs/host-environment-errors.js +142 -0
- package/dist/core/output/mutation-projection.js +12 -5
- package/dist/core/store/item-metadata-query-index.js +13 -10
- package/dist/core/store/item-metadata-read-work.d.ts +19 -0
- package/dist/core/store/item-metadata-read-work.js +44 -0
- package/dist/core/store/item-store.js +6 -2
- package/dist/core/validate/fix-planning.d.ts +1 -1
- package/dist/core/validate/fix-planning.js +2 -2
- package/dist/core/validate/missing-link-owners.d.ts +1 -1
- package/dist/core/validate/missing-link-owners.js +5 -3
- package/dist/core/validate/stale-file-classification.d.ts +6 -4
- package/dist/core/validate/stale-file-classification.js +20 -5
- package/dist/mcp/server.js +12 -5
- package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
- package/dist/sdk/cli-contracts/flag-contracts.js +7 -5
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -3
- package/dist/sdk/cli-contracts/grammar-contracts.js +11 -2
- package/dist/sdk/cli-contracts/runtime-contracts.js +5 -2
- package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +254 -5
- package/dist/sdk/cli-contracts/schemas/assurance-definition.js +52 -2
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +18 -6
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +17 -5
- package/dist/sdk/context/usage-ledger.d.ts +41 -0
- package/dist/sdk/context/usage-ledger.js +186 -0
- package/dist/sdk/context-usage.d.ts +18 -9
- package/dist/sdk/context-usage.js +37 -112
- package/dist/sdk/dependency-provenance.d.ts +7 -0
- package/dist/sdk/dependency-provenance.js +14 -4
- package/dist/sdk/duplicates.d.ts +2 -0
- package/dist/sdk/duplicates.js +3 -2
- package/dist/sdk/environment/host-environment-errors.d.ts +5 -27
- package/dist/sdk/environment/host-environment-errors.js +4 -137
- package/dist/sdk/extension/install-runtime.js +9 -6
- package/dist/sdk/extension/install-snapshot.js +7 -6
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +8 -8
- package/dist/sdk/governance/assurance-action.d.ts +3 -2
- package/dist/sdk/governance/assurance-action.js +23 -11
- package/dist/sdk/governance/assurance-runtime.d.ts +2 -0
- package/dist/sdk/governance/assurance-runtime.js +6 -3
- package/dist/sdk/governance/defect-recurrence-coverage.d.ts +93 -0
- package/dist/sdk/governance/defect-recurrence-coverage.js +189 -0
- package/dist/sdk/governance/defect-recurrence-graph.d.ts +33 -0
- package/dist/sdk/governance/defect-recurrence-graph.js +121 -0
- package/dist/sdk/governance/defect-recurrence.d.ts +8 -0
- package/dist/sdk/governance/defect-recurrence.js +118 -17
- package/dist/sdk/governance/health.d.ts +17 -2
- package/dist/sdk/governance/health.js +31 -10
- package/dist/sdk/governance/linked-file-report.d.ts +41 -0
- package/dist/sdk/governance/linked-file-report.js +72 -0
- package/dist/sdk/governance/validate.d.ts +5 -2
- package/dist/sdk/governance/validate.js +21 -37
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +4 -2
- package/dist/sdk/history-repair.d.ts +7 -0
- package/dist/sdk/history-repair.js +48 -13
- package/dist/sdk/lifecycle/close.d.ts +8 -0
- package/dist/sdk/lifecycle/close.js +16 -8
- package/dist/sdk/lifecycle/create.js +9 -12
- package/dist/sdk/lifecycle/update.js +12 -12
- package/dist/sdk/lifecycle-policy.d.ts +3 -0
- package/dist/sdk/lifecycle-policy.js +2 -2
- package/dist/sdk/merge/abandoned-receipts.d.ts +15 -0
- package/dist/sdk/merge/abandoned-receipts.js +47 -0
- package/dist/sdk/merge/driver.js +4 -2
- package/dist/sdk/merge/index.d.ts +1 -0
- package/dist/sdk/merge/index.js +2 -2
- package/dist/sdk/merge/receipt-operation.d.ts +15 -0
- package/dist/sdk/merge/receipt-operation.js +97 -0
- package/dist/sdk/merge/receipt-schema.d.ts +20 -0
- package/dist/sdk/merge/receipt-schema.js +398 -0
- package/dist/sdk/merge/receipts.d.ts +16 -7
- package/dist/sdk/merge/receipts.js +54 -340
- package/dist/sdk/merge/reconcile.d.ts +2 -0
- package/dist/sdk/merge/reconcile.js +11 -4
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
- package/dist/sdk/query/duplicate-candidates.js +71 -0
- package/dist/sdk/query/get.js +7 -7
- package/dist/sdk/query.d.ts +2 -0
- package/dist/sdk/query.js +4 -2
- package/dist/sdk/read-output-session.js +5 -3
- package/dist/sdk/runtime.js +4 -2
- package/dist/sdk/similarity.d.ts +24 -3
- package/dist/sdk/similarity.js +50 -55
- package/docs/AGENT_GUIDE.md +23 -2
- package/docs/BOUNDED_CONTEXT_READS.md +185 -0
- package/docs/CLOSE_EVIDENCE_RECOVERY.md +35 -0
- package/docs/COMMANDS.md +30 -8
- package/docs/CONTEXT_RELEVANCE.md +16 -4
- package/docs/DEFECT_RECURRENCE.md +80 -6
- package/docs/EXTENSIONS.md +18 -19
- package/docs/MERGE_SAFETY.md +50 -0
- package/docs/OUTPUT_PROJECTION_CONTRACTS.md +66 -0
- package/docs/PR_REVIEW_LOOP.md +18 -3
- package/docs/README.md +18 -1
- package/docs/RELEASING.md +6 -2
- package/docs/SDK_LIFECYCLE.md +27 -0
- package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +1 -1
- package/marketplace.json +2 -2
- package/package.json +6 -3
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/README.md +10 -1
- package/packages/pm-governance-audit/extensions/governance-audit/dedupe-audit.ts +50 -65
- package/packages/pm-governance-audit/extensions/governance-audit/index.ts +2 -2
- package/packages/pm-governance-audit/extensions/governance-audit/sdk.ts +1 -0
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +0 -231
- package/dist/cli-bundle/chunks/chunk-SVK5Y64D.js +0 -40
- package/dist/cli-bundle/chunks/chunk-V5GBD5KV.js +0 -9
- package/dist/cli-bundle/chunks/register-operations-KFSCC7QV.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-24IG6ONG.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-3ITLIBKS.js +0 -56
- package/dist/cli-bundle/focused-chunks/chunk-3NQD5YDG.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-MV752SUC.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-PKIPMCFS.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-RJMR2JIF.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-WIUPWIZR.js +0 -177
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* verification.
|
|
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]="
|
|
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]="361b49b6-942e-55d9-9979-d72bc94721c6")}catch(e){}}();
|
|
9
|
+
import { withHostEnvironmentBoundary } from "../fs/host-environment-errors.js";
|
|
9
10
|
import fs from "node:fs/promises";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
/**
|
|
@@ -23,11 +24,11 @@ export async function snapshotExtensionModuleGraph(snapshotRoot, extension) {
|
|
|
23
24
|
throw new Error(`Cannot snapshot extension entry outside its package directory: ${extension.entry_path}`);
|
|
24
25
|
}
|
|
25
26
|
const snapshotDirectory = path.join(snapshotRoot, `${extension.layer}-${extension.directory.replace(/[^a-zA-Z0-9._-]/gu, "_")}`);
|
|
26
|
-
await fs.cp(sourceDirectory, snapshotDirectory, {
|
|
27
|
+
await withHostEnvironmentBoundary("extension_module_graph_snapshot", () => fs.cp(sourceDirectory, snapshotDirectory, {
|
|
27
28
|
recursive: true,
|
|
28
29
|
force: true,
|
|
29
|
-
});
|
|
30
|
+
}));
|
|
30
31
|
return path.join(snapshotDirectory, relativeEntryPath);
|
|
31
32
|
}
|
|
32
33
|
//# sourceMappingURL=module-graph-snapshot.js.map
|
|
33
|
-
//# debugId=
|
|
34
|
+
//# debugId=361b49b6-942e-55d9-9979-d72bc94721c6
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PmCliError } from "../shared/errors.js";
|
|
2
|
+
/** Optional stable codes used by an existing SDK surface for each fault class. */
|
|
3
|
+
export interface HostEnvironmentFaultCodeOverrides {
|
|
4
|
+
/** Code used for quota and storage exhaustion. */
|
|
5
|
+
capacity?: string;
|
|
6
|
+
/** Code used for access policy and read-only filesystems. */
|
|
7
|
+
permission?: string;
|
|
8
|
+
/** Code used for file-descriptor and memory exhaustion. */
|
|
9
|
+
resource?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Context and compatibility controls for a host-environment boundary. */
|
|
12
|
+
export interface HostEnvironmentBoundaryOptions {
|
|
13
|
+
/** Existing surface-specific codes that must remain compatible. */
|
|
14
|
+
codes?: HostEnvironmentFaultCodeOverrides;
|
|
15
|
+
/** Human explanation of why the operation needs host resources. */
|
|
16
|
+
why?: string;
|
|
17
|
+
/** Additional ordered recovery guidance. */
|
|
18
|
+
nextSteps?: readonly string[];
|
|
19
|
+
/** Copyable retry command when one is safe. */
|
|
20
|
+
suggestedRetry?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Return a declared host errno only when it represents recoverable environment state. */
|
|
23
|
+
export declare function classifyHostEnvironmentFault(error: unknown): string | null;
|
|
24
|
+
/** Translate one known host fault into a stable path-redacted SDK refusal. */
|
|
25
|
+
export declare function translateHostEnvironmentFault(error: unknown, operation: string, options?: HostEnvironmentBoundaryOptions): PmCliError | null;
|
|
26
|
+
/** Run one SDK filesystem stage and translate only declared host faults. */
|
|
27
|
+
export declare function withHostEnvironmentBoundary<T>(operation: string, run: () => Promise<T>, options?: HostEnvironmentBoundaryOptions): Promise<T>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/fs/host-environment-errors
|
|
3
|
+
*
|
|
4
|
+
* Classifies host filesystem/resource failures at SDK boundaries without
|
|
5
|
+
* leaking local paths or swallowing genuine implementation defects.
|
|
6
|
+
*/
|
|
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]="1262c682-2b8a-506f-a3e1-155ad682d763")}catch(e){}}();
|
|
9
|
+
import { constants as osConstants } from "node:os";
|
|
10
|
+
import { EXIT_CODE } from "../shared/constants.js";
|
|
11
|
+
import { PmCliError } from "../shared/errors.js";
|
|
12
|
+
const HOST_ENVIRONMENT_FAULTS = new Map([
|
|
13
|
+
[
|
|
14
|
+
"EACCES",
|
|
15
|
+
{
|
|
16
|
+
summary: "was denied filesystem permission",
|
|
17
|
+
recovery: "Grant the required filesystem access, then retry.",
|
|
18
|
+
category: "permission",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"EDQUOT",
|
|
23
|
+
{
|
|
24
|
+
summary: "exceeded the filesystem quota",
|
|
25
|
+
recovery: "Raise or reclaim the filesystem quota, then retry.",
|
|
26
|
+
category: "capacity",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"EMFILE",
|
|
31
|
+
{
|
|
32
|
+
summary: "exhausted the process file-descriptor limit",
|
|
33
|
+
recovery: "Close unused file handles or raise the process limit, then retry.",
|
|
34
|
+
category: "resource",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
"ENFILE",
|
|
39
|
+
{
|
|
40
|
+
summary: "exhausted the host file-descriptor limit",
|
|
41
|
+
recovery: "Free host file descriptors or raise the host limit, then retry.",
|
|
42
|
+
category: "resource",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"ENOMEM",
|
|
47
|
+
{
|
|
48
|
+
summary: "ran out of available memory",
|
|
49
|
+
recovery: "Free memory or reduce concurrent work, then retry.",
|
|
50
|
+
category: "resource",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"ENOSPC",
|
|
55
|
+
{
|
|
56
|
+
summary: "ran out of storage space",
|
|
57
|
+
recovery: "Free storage on the filesystem holding the project, then retry.",
|
|
58
|
+
category: "capacity",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"EPERM",
|
|
63
|
+
{
|
|
64
|
+
summary: "was denied by the host permission policy",
|
|
65
|
+
recovery: "Grant the required filesystem permission, then retry.",
|
|
66
|
+
category: "permission",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"EROFS",
|
|
71
|
+
{
|
|
72
|
+
summary: "targeted a read-only filesystem",
|
|
73
|
+
recovery: "Use a writable project filesystem or remount it read-write, then retry.",
|
|
74
|
+
category: "permission",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
]);
|
|
78
|
+
const OPERATION_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
79
|
+
const HOST_ENVIRONMENT_ERRNO_NAMES = new Map([
|
|
80
|
+
[osConstants.errno.EACCES, "EACCES"],
|
|
81
|
+
[osConstants.errno.EDQUOT, "EDQUOT"],
|
|
82
|
+
[osConstants.errno.EMFILE, "EMFILE"],
|
|
83
|
+
[osConstants.errno.ENFILE, "ENFILE"],
|
|
84
|
+
[osConstants.errno.ENOMEM, "ENOMEM"],
|
|
85
|
+
[osConstants.errno.ENOSPC, "ENOSPC"],
|
|
86
|
+
[osConstants.errno.EPERM, "EPERM"],
|
|
87
|
+
[osConstants.errno.EROFS, "EROFS"],
|
|
88
|
+
].filter(([errno]) => Number.isInteger(errno)));
|
|
89
|
+
const HOST_ENVIRONMENT_FAULT_CODES = {
|
|
90
|
+
capacity: { code: "host_environment_capacity_fault" },
|
|
91
|
+
permission: { code: "host_environment_permission_fault" },
|
|
92
|
+
resource: { code: "host_environment_resource_fault" },
|
|
93
|
+
};
|
|
94
|
+
/** Return a declared host errno only when it represents recoverable environment state. */
|
|
95
|
+
export function classifyHostEnvironmentFault(error) {
|
|
96
|
+
if (typeof error !== "object" || error === null) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const errorRecord = error;
|
|
100
|
+
if (typeof errorRecord.code === "string" &&
|
|
101
|
+
HOST_ENVIRONMENT_FAULTS.has(errorRecord.code)) {
|
|
102
|
+
return errorRecord.code;
|
|
103
|
+
}
|
|
104
|
+
if (typeof errorRecord.errno !== "number" ||
|
|
105
|
+
!Number.isInteger(errorRecord.errno)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return HOST_ENVIRONMENT_ERRNO_NAMES.get(Math.abs(errorRecord.errno)) ?? null;
|
|
109
|
+
}
|
|
110
|
+
/** Translate one known host fault into a stable path-redacted SDK refusal. */
|
|
111
|
+
export function translateHostEnvironmentFault(error, operation, options = {}) {
|
|
112
|
+
const errno = classifyHostEnvironmentFault(error);
|
|
113
|
+
if (errno === null)
|
|
114
|
+
return null;
|
|
115
|
+
if (!OPERATION_PATTERN.test(operation)) {
|
|
116
|
+
throw new PmCliError("Host environment operation labels must be bounded identifiers.", EXIT_CODE.USAGE, { code: "host_environment_operation_invalid", field: "operation" });
|
|
117
|
+
}
|
|
118
|
+
const fault = HOST_ENVIRONMENT_FAULTS.get(errno);
|
|
119
|
+
return new PmCliError(`Host filesystem operation ${operation} ${fault.summary}.`, EXIT_CODE.GENERIC_FAILURE, {
|
|
120
|
+
code: options.codes?.[fault.category] ??
|
|
121
|
+
HOST_ENVIRONMENT_FAULT_CODES[fault.category].code,
|
|
122
|
+
reason: errno,
|
|
123
|
+
required: fault.recovery,
|
|
124
|
+
why: options.why ??
|
|
125
|
+
"The operating system refused a required project filesystem operation.",
|
|
126
|
+
nextSteps: [fault.recovery, ...(options.nextSteps ?? [])],
|
|
127
|
+
...(options.suggestedRetry
|
|
128
|
+
? { recovery: { suggested_retry: options.suggestedRetry } }
|
|
129
|
+
: {}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Run one SDK filesystem stage and translate only declared host faults. */
|
|
133
|
+
export async function withHostEnvironmentBoundary(operation, run, options = {}) {
|
|
134
|
+
try {
|
|
135
|
+
return await run();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw translateHostEnvironmentFault(error, operation, options) ?? error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=host-environment-errors.js.map
|
|
142
|
+
//# debugId=1262c682-2b8a-506f-a3e1-155ad682d763
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* MCP server, and returns the original reference unchanged when nothing is compacted.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
!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]="
|
|
22
|
+
!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]="d6a389b0-7a14-56e7-ac2c-17fcdecc7b29")}catch(e){}}();
|
|
23
23
|
const CHANGED_FIELDS_KEY = "changed_fields";
|
|
24
24
|
const CHANGED_FIELD_COUNT_KEY = "changed_field_count";
|
|
25
25
|
const ROWS_KEY = "rows";
|
|
@@ -115,6 +115,7 @@ function projectIdOnlyResult(result) {
|
|
|
115
115
|
}
|
|
116
116
|
: { id };
|
|
117
117
|
}
|
|
118
|
+
/** Project a recognized item mutation to identity and change counts while preserving warnings and executable recovery. */
|
|
118
119
|
function projectCompactMutationEnvelope(result) {
|
|
119
120
|
if (!isPlainObject(result) || !isPlainObject(result.item))
|
|
120
121
|
return null;
|
|
@@ -137,10 +138,15 @@ function projectCompactMutationEnvelope(result) {
|
|
|
137
138
|
: {}),
|
|
138
139
|
changed_field_count: changedFields.length,
|
|
139
140
|
};
|
|
141
|
+
appendCompactMutationEvidence(result, result.item, compact);
|
|
142
|
+
return compact;
|
|
143
|
+
}
|
|
144
|
+
/** Keep lifecycle context, warnings, and executable recovery alongside the compact identity. */
|
|
145
|
+
function appendCompactMutationEvidence(result, item, compact) {
|
|
140
146
|
const closeReason = typeof result.close_reason === "string"
|
|
141
147
|
? result.close_reason
|
|
142
|
-
: typeof
|
|
143
|
-
?
|
|
148
|
+
: typeof item.close_reason === "string"
|
|
149
|
+
? item.close_reason
|
|
144
150
|
: undefined;
|
|
145
151
|
if (closeReason !== undefined)
|
|
146
152
|
compact.close_reason = closeReason;
|
|
@@ -149,7 +155,8 @@ function projectCompactMutationEnvelope(result) {
|
|
|
149
155
|
compact.recurrence = recurrence;
|
|
150
156
|
if (Array.isArray(result.warnings) && result.warnings.length > 0)
|
|
151
157
|
compact.warnings = result.warnings;
|
|
152
|
-
|
|
158
|
+
if (isPlainObject(result.recovery))
|
|
159
|
+
compact.recovery = result.recovery;
|
|
153
160
|
}
|
|
154
161
|
function compactUpdateManyRows(envelope) {
|
|
155
162
|
const rows = isUpdateManyMutationEnvelope(envelope)
|
|
@@ -208,4 +215,4 @@ export function projectMutationResult(result, options = {}) {
|
|
|
208
215
|
return changed ? projected : result;
|
|
209
216
|
}
|
|
210
217
|
//# sourceMappingURL=mutation-projection.js.map
|
|
211
|
-
//# debugId=
|
|
218
|
+
//# debugId=d6a389b0-7a14-56e7-ac2c-17fcdecc7b29
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Maintains a rebuildable SQLite projection for bounded metadata queries.
|
|
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]="
|
|
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]="530ad63f-f7c5-52c9-9f81-a74b1cd4c36e")}catch(e){}}();
|
|
8
8
|
import fs from "node:fs/promises";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
const QUERY_INDEX_FILENAME = "metadata-query-index.sqlite";
|
|
13
|
-
const QUERY_INDEX_VERSION = "
|
|
13
|
+
const QUERY_INDEX_VERSION = "5";
|
|
14
14
|
function loadDatabaseSync(loadModule) {
|
|
15
15
|
try {
|
|
16
16
|
const loaded = loadModule(["node", "sqlite"].join(":"));
|
|
@@ -102,6 +102,7 @@ function writeMetadata(database, key, value) {
|
|
|
102
102
|
.prepare("INSERT INTO metadata(key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value")
|
|
103
103
|
.run(key, value);
|
|
104
104
|
}
|
|
105
|
+
/** Upsert authoritative metadata and replace dependent search, key and linked-file rows in the caller transaction. */
|
|
105
106
|
function insertRow(database, row) {
|
|
106
107
|
const metadata = row.metadata;
|
|
107
108
|
database
|
|
@@ -128,10 +129,12 @@ function insertRow(database, row) {
|
|
|
128
129
|
database
|
|
129
130
|
.prepare("DELETE FROM item_linked_files WHERE item_id = ?")
|
|
130
131
|
.run(metadata.id);
|
|
131
|
-
|
|
132
|
+
// FTS UNINDEXED columns cannot provide identity lookup. Share the items rowid
|
|
133
|
+
// so both rebuilding and incremental maintenance use a single indexed row.
|
|
134
|
+
database.prepare("DELETE FROM item_search WHERE rowid = (SELECT rowid FROM items WHERE id = ?)").run(metadata.id);
|
|
132
135
|
database
|
|
133
|
-
.prepare("INSERT INTO item_search(id, title, description, tags)
|
|
134
|
-
.run(metadata.id, metadata.title, metadata.description, metadata.tags.join(" "));
|
|
136
|
+
.prepare("INSERT INTO item_search(rowid, id, title, description, tags) SELECT rowid, ?, ?, ?, ? FROM items WHERE id = ?")
|
|
137
|
+
.run(metadata.id, metadata.title, metadata.description, metadata.tags.join(" "), metadata.id);
|
|
135
138
|
const insertMetadataKey = database.prepare("INSERT INTO item_metadata_keys(item_id, key) VALUES (?, ?)");
|
|
136
139
|
for (const key of Object.keys(metadata)) {
|
|
137
140
|
insertMetadataKey.run(metadata.id, key);
|
|
@@ -203,13 +206,13 @@ export async function updateItemMetadataQueryIndex(options) {
|
|
|
203
206
|
}
|
|
204
207
|
database.exec("BEGIN IMMEDIATE");
|
|
205
208
|
const deleteByPath = database.prepare("DELETE FROM items WHERE relative_path = ?");
|
|
206
|
-
const
|
|
207
|
-
const
|
|
209
|
+
const findRowidByPath = database.prepare("SELECT rowid FROM items WHERE relative_path = ?");
|
|
210
|
+
const deleteSearchByRowid = database.prepare("DELETE FROM item_search WHERE rowid = ?");
|
|
208
211
|
for (const relativePath of options.deletedRelativePaths ?? []) {
|
|
209
|
-
const deleted =
|
|
212
|
+
const deleted = findRowidByPath.get(relativePath);
|
|
210
213
|
deleteByPath.run(relativePath);
|
|
211
214
|
if (deleted)
|
|
212
|
-
|
|
215
|
+
deleteSearchByRowid.run(deleted.rowid);
|
|
213
216
|
}
|
|
214
217
|
if (options.row)
|
|
215
218
|
insertRow(database, options.row);
|
|
@@ -470,4 +473,4 @@ export const _testOnly = {
|
|
|
470
473
|
},
|
|
471
474
|
};
|
|
472
475
|
//# sourceMappingURL=item-metadata-query-index.js.map
|
|
473
|
-
//# debugId=
|
|
476
|
+
//# debugId=530ad63f-f7c5-52c9-9f81-a74b1cd4c36e
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Logical metadata work, independent of output projection and cache hit rate. */
|
|
2
|
+
export interface ItemMetadataReadWork {
|
|
3
|
+
/** Completed full, light, or body-bearing item-store enumeration calls. */
|
|
4
|
+
enumeration_calls: number;
|
|
5
|
+
/** Metadata rows returned by those calls, counting repeated enumeration. */
|
|
6
|
+
metadata_rows: number;
|
|
7
|
+
}
|
|
8
|
+
/** Record completed enumeration in each enclosing measurement, without retaining item data. */
|
|
9
|
+
export declare function recordItemMetadataEnumeration(rowCount: number): void;
|
|
10
|
+
/**
|
|
11
|
+
* Measure an awaited operation without changing its result or failure semantics.
|
|
12
|
+
* Concurrent roots remain isolated; nested measurements also contribute to their
|
|
13
|
+
* parents. The returned snapshot excludes work completed after the operation.
|
|
14
|
+
* Counters cover filesystem item-store enumeration APIs, not arbitrary host I/O.
|
|
15
|
+
*/
|
|
16
|
+
export declare function measureItemMetadataReadWork<T>(operation: () => T | Promise<T>): Promise<{
|
|
17
|
+
result: T;
|
|
18
|
+
work: Readonly<ItemMetadataReadWork>;
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/store/item-metadata-read-work
|
|
3
|
+
*
|
|
4
|
+
* Measures successful filesystem metadata enumerations within one async SDK
|
|
5
|
+
* operation. Cache hits count too: materializing a cached corpus is still work.
|
|
6
|
+
*/
|
|
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]="1cd39e2f-41c7-5005-925d-d4539cda15d3")}catch(e){}}();
|
|
9
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import { channel } from "node:diagnostics_channel";
|
|
11
|
+
const observers = new AsyncLocalStorage();
|
|
12
|
+
const enumerations = channel("@unbrained/pm-cli/item-metadata-enumeration/v1");
|
|
13
|
+
/** Record completed enumeration in each enclosing measurement, without retaining item data. */
|
|
14
|
+
export function recordItemMetadataEnumeration(rowCount) {
|
|
15
|
+
enumerations.publish(rowCount);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Measure an awaited operation without changing its result or failure semantics.
|
|
19
|
+
* Concurrent roots remain isolated; nested measurements also contribute to their
|
|
20
|
+
* parents. The returned snapshot excludes work completed after the operation.
|
|
21
|
+
* Counters cover filesystem item-store enumeration APIs, not arbitrary host I/O.
|
|
22
|
+
*/
|
|
23
|
+
export async function measureItemMetadataReadWork(operation) {
|
|
24
|
+
const work = { enumeration_calls: 0, metadata_rows: 0 };
|
|
25
|
+
// A named channel crosses the published query/bundled runtime boundary;
|
|
26
|
+
// async membership prevents concurrent roots from counting each other's work.
|
|
27
|
+
/** Count notifications only while this measurement belongs to the active async scope. */
|
|
28
|
+
const observe = (message) => {
|
|
29
|
+
if (observers.getStore()?.includes(work)) {
|
|
30
|
+
work.enumeration_calls += 1;
|
|
31
|
+
work.metadata_rows += message;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
enumerations.subscribe(observe);
|
|
35
|
+
try {
|
|
36
|
+
const result = await observers.run([...(observers.getStore() ?? []), work], operation);
|
|
37
|
+
return { result, work: { ...work } };
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
enumerations.unsubscribe(observe);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=item-metadata-read-work.js.map
|
|
44
|
+
//# debugId=1cd39e2f-41c7-5005-925d-d4539cda15d3
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Reads and writes tracker storage with format-aware helpers for Item Store.
|
|
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]="
|
|
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]="7cbc256e-cdd8-51de-9c22-08cf2f3cd010")}catch(e){}}();
|
|
8
8
|
import fs from "node:fs/promises";
|
|
9
|
+
import { recordItemMetadataEnumeration } from "./item-metadata-read-work.js";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import { getActiveExtensionRegistrations, projectAfterCommandItemSnapshot, recordAfterCommandAffectedItem, runActiveOnReadHooks, runActiveBeforeMutationHooks, runActiveOnWriteHooks, runActiveServiceOverride, } from "../extensions/index.js";
|
|
11
12
|
import { collectRegisteredItemFieldNames } from "../extensions/item-fields.js";
|
|
@@ -151,6 +152,7 @@ export async function readLocatedItemSnapshot(item, options = {}) {
|
|
|
151
152
|
export async function listAllItemMetadata(pmRoot, preferredFormat, typeToFolder = TYPE_TO_FOLDER, warnings, schema) {
|
|
152
153
|
await assertReadableTrackerRoot(pmRoot);
|
|
153
154
|
const documents = await listAllDocumentsCached(pmRoot, preferredFormat, typeToFolder, warnings, schema);
|
|
155
|
+
recordItemMetadataEnumeration(documents.length);
|
|
154
156
|
return documents.map((document) => document.metadata);
|
|
155
157
|
}
|
|
156
158
|
/**
|
|
@@ -162,6 +164,7 @@ export async function listAllItemMetadata(pmRoot, preferredFormat, typeToFolder
|
|
|
162
164
|
export async function listAllItemMetadataLight(pmRoot, preferredFormat, typeToFolder = TYPE_TO_FOLDER, warnings, schema) {
|
|
163
165
|
await assertReadableTrackerRoot(pmRoot);
|
|
164
166
|
const documents = await listAllDocumentsCachedLight(pmRoot, preferredFormat, typeToFolder, warnings, schema);
|
|
167
|
+
recordItemMetadataEnumeration(documents.length);
|
|
165
168
|
return documents.map((document) => document.metadata);
|
|
166
169
|
}
|
|
167
170
|
/** List item metadata with bodies, optionally bypassing the derived-index fast path when an integrity workflow must re-read authoritative files. */
|
|
@@ -171,6 +174,7 @@ export async function listAllItemMetadataWithBody(pmRoot, preferredFormat, typeT
|
|
|
171
174
|
includeBody: true,
|
|
172
175
|
forceSourceScan: options.forceSourceScan,
|
|
173
176
|
});
|
|
177
|
+
recordItemMetadataEnumeration(candidates.length);
|
|
174
178
|
return candidates.map((candidate) => ({
|
|
175
179
|
...candidate.metadata,
|
|
176
180
|
// includeBody:true guarantees candidate bodies are materialized.
|
|
@@ -687,4 +691,4 @@ export async function deleteItem(params) {
|
|
|
687
691
|
}
|
|
688
692
|
}
|
|
689
693
|
//# sourceMappingURL=item-store.js.map
|
|
690
|
-
//# debugId=
|
|
694
|
+
//# debugId=7cbc256e-cdd8-51de-9c22-08cf2f3cd010
|
|
@@ -97,7 +97,7 @@ export interface StaleLinkPruneRow {
|
|
|
97
97
|
/** Value that configures or reports link kind for this contract. */
|
|
98
98
|
link_kind: "files" | "docs";
|
|
99
99
|
/** Value that configures or reports classification for this contract. */
|
|
100
|
-
classification: "moved" | "deleted";
|
|
100
|
+
classification: "moved" | "deleted" | "malformed";
|
|
101
101
|
}
|
|
102
102
|
/** Plan link prunes for `--prune-missing` (pm-0v2m / GH-184). Only links whose stale path classified as `deleted` are pruned; `moved` links keep their relink candidates in the files-check details instead of being dropped, so recoverable link information is never destroyed. Link removal only — real files are never touched. */
|
|
103
103
|
export declare function planStaleLinkPruneFixes(rows: readonly StaleLinkPruneRow[]): ValidateFixRecord[];
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* explicitly granted.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
!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]="
|
|
19
|
+
!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]="98a73818-c4ef-5bce-8b23-6c40f7312cc3")}catch(e){}}();
|
|
20
20
|
import { EXIT_CODE } from "../shared/constants.js";
|
|
21
21
|
import { PmCliError } from "../shared/errors.js";
|
|
22
22
|
import { resolveEstimateDefaultMinutes } from "./estimate-defaults.js";
|
|
@@ -187,4 +187,4 @@ export function toFixOutputRow(fix) {
|
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
//# sourceMappingURL=fix-planning.js.map
|
|
190
|
-
//# debugId=
|
|
190
|
+
//# debugId=98a73818-c4ef-5bce-8b23-6c40f7312cc3
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the already-collected stale-link rows plus a metadata `lookup` closure.
|
|
14
14
|
*/
|
|
15
15
|
/** Classification assigned to a stale linked path during files validation. */
|
|
16
|
-
export type StaleLinkClassification = "moved" | "deleted";
|
|
16
|
+
export type StaleLinkClassification = "moved" | "deleted" | "malformed";
|
|
17
17
|
/** Documents the stale link owner input payload exchanged by command, SDK, and package integrations. */
|
|
18
18
|
export interface StaleLinkOwnerInput {
|
|
19
19
|
/** Value that configures or reports item id for this contract. */
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
* Pure module (GH-210): no filesystem access and no heavy imports — callers pass
|
|
13
13
|
* the already-collected stale-link rows plus a metadata `lookup` closure.
|
|
14
14
|
*/
|
|
15
|
-
/** `moved` wins over `deleted` when a single path's rows disagree on classification: `moved` means a relink candidate exists, which is the safer, more-actionable signal (relink instead of risk pruning a still-reachable link). In practice all rows for one path share a classification; this is a deterministic tie-break for the degenerate case. */
|
|
15
|
+
/** `malformed` wins over other classifications to prevent unsafe automatic repair. Otherwise `moved` wins over `deleted` when a single path's rows disagree on classification: `moved` means a relink candidate exists, which is the safer, more-actionable signal (relink instead of risk pruning a still-reachable link). In practice all rows for one path share a classification; this is a deterministic tie-break for the degenerate case. */
|
|
16
16
|
|
|
17
|
-
!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]="
|
|
17
|
+
!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]="1a4f19cf-56ba-571a-908c-97278a8284f7")}catch(e){}}();
|
|
18
18
|
function preferClassification(current, next) {
|
|
19
|
+
if (current === "malformed" || next === "malformed")
|
|
20
|
+
return "malformed";
|
|
19
21
|
return current === "moved" || next === "moved" ? "moved" : "deleted";
|
|
20
22
|
}
|
|
21
23
|
function ownerKey(input) {
|
|
@@ -91,4 +93,4 @@ export function summarizeMissingLinkedPathRows(rows) {
|
|
|
91
93
|
return lines;
|
|
92
94
|
}
|
|
93
95
|
//# sourceMappingURL=missing-link-owners.js.map
|
|
94
|
-
//# debugId=
|
|
96
|
+
//# debugId=1a4f19cf-56ba-571a-908c-97278a8284f7
|
|
@@ -6,27 +6,29 @@
|
|
|
6
6
|
* scanned candidate set — the candidates are reported so an agent can relink
|
|
7
7
|
* the item to the new location instead of dropping the link, or
|
|
8
8
|
* - `deleted`: no candidate with that basename exists anywhere in the scan,
|
|
9
|
-
*
|
|
9
|
+
* making it eligible for an explicitly requested prune, or
|
|
10
|
+
* - `malformed`: an ambiguous missing CSV or path:note literal which must be
|
|
11
|
+
* reviewed before any relocation or removal is inferred.
|
|
10
12
|
*
|
|
11
13
|
* Pure module (pm-0v2m / GH-184): no filesystem access — callers pass the
|
|
12
14
|
* already-collected candidate file list from the files check scan.
|
|
13
15
|
*/
|
|
14
16
|
/** Classification labels for linked paths that no longer exist on disk. */
|
|
15
|
-
export type StaleLinkedPathClassification = "moved" | "deleted";
|
|
17
|
+
export type StaleLinkedPathClassification = "moved" | "deleted" | "malformed";
|
|
16
18
|
/** Documents the classified stale linked path payload exchanged by command, SDK, and package integrations. */
|
|
17
19
|
export interface ClassifiedStaleLinkedPath {
|
|
18
20
|
/** Normalized workspace-relative stale linked path. */
|
|
19
21
|
path: string;
|
|
20
22
|
/** Value that configures or reports classification for this contract. */
|
|
21
23
|
classification: StaleLinkedPathClassification;
|
|
22
|
-
/** Candidate new locations (same basename, different path), sorted and capped at `candidateLimit`. Empty for `deleted`. */
|
|
24
|
+
/** Candidate new locations (same basename, different path), sorted and capped at `candidateLimit`. Empty for `deleted` and `malformed`. */
|
|
23
25
|
candidates: string[];
|
|
24
26
|
/** True when more candidates existed than `candidateLimit` allowed. */
|
|
25
27
|
candidates_truncated: boolean;
|
|
26
28
|
}
|
|
27
29
|
/** Fallback stale path candidate limit used when callers do not provide an override. */
|
|
28
30
|
export declare const DEFAULT_STALE_PATH_CANDIDATE_LIMIT = 3;
|
|
29
|
-
/** Classify each stale (missing) linked path as `moved` or `
|
|
31
|
+
/** Classify each stale (missing) linked path as `moved`, `deleted`, or `malformed` by matching its basename against the candidate file list. Output preserves the input order of `stalePaths` (callers pass an already-sorted unique list). A candidate equal to the stale path itself never matches (the path is known missing; an identical candidate would be self-referential noise). */
|
|
30
32
|
export declare function classifyStaleLinkedPaths(stalePaths: readonly string[], candidateFiles: readonly string[], candidateLimit?: number): ClassifiedStaleLinkedPath[];
|
|
31
33
|
/** Render compact `<path>:moved:<top-candidate>` / `<path>:deleted` rows for the files check details (token-efficient counterpart to the full classification objects). */
|
|
32
34
|
export declare function summarizeStaleLinkedPathClassifications(classified: readonly ClassifiedStaleLinkedPath[]): string[];
|
|
@@ -6,20 +6,23 @@
|
|
|
6
6
|
* scanned candidate set — the candidates are reported so an agent can relink
|
|
7
7
|
* the item to the new location instead of dropping the link, or
|
|
8
8
|
* - `deleted`: no candidate with that basename exists anywhere in the scan,
|
|
9
|
-
*
|
|
9
|
+
* making it eligible for an explicitly requested prune, or
|
|
10
|
+
* - `malformed`: an ambiguous missing CSV or path:note literal which must be
|
|
11
|
+
* reviewed before any relocation or removal is inferred.
|
|
10
12
|
*
|
|
11
13
|
* Pure module (pm-0v2m / GH-184): no filesystem access — callers pass the
|
|
12
14
|
* already-collected candidate file list from the files check scan.
|
|
13
15
|
*/
|
|
14
16
|
/** Fallback stale path candidate limit used when callers do not provide an override. */
|
|
15
17
|
|
|
16
|
-
!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]="
|
|
18
|
+
!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]="eb4fe9bd-31b5-5ed3-b401-3e3776ec12e9")}catch(e){}}();
|
|
17
19
|
export const DEFAULT_STALE_PATH_CANDIDATE_LIMIT = 3;
|
|
20
|
+
/** Extract a basename from either portable separator convention. */
|
|
18
21
|
function basenameOf(relativePath) {
|
|
19
22
|
const lastSlash = Math.max(relativePath.lastIndexOf("/"), relativePath.lastIndexOf("\\"));
|
|
20
23
|
return lastSlash === -1 ? relativePath : relativePath.slice(lastSlash + 1);
|
|
21
24
|
}
|
|
22
|
-
/** Classify each stale (missing) linked path as `moved` or `
|
|
25
|
+
/** Classify each stale (missing) linked path as `moved`, `deleted`, or `malformed` by matching its basename against the candidate file list. Output preserves the input order of `stalePaths` (callers pass an already-sorted unique list). A candidate equal to the stale path itself never matches (the path is known missing; an identical candidate would be self-referential noise). */
|
|
23
26
|
export function classifyStaleLinkedPaths(stalePaths, candidateFiles, candidateLimit = DEFAULT_STALE_PATH_CANDIDATE_LIMIT) {
|
|
24
27
|
const limit = Number.isFinite(candidateLimit) && candidateLimit >= 1
|
|
25
28
|
? Math.floor(candidateLimit)
|
|
@@ -39,6 +42,18 @@ export function classifyStaleLinkedPaths(stalePaths, candidateFiles, candidateLi
|
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
return stalePaths.map((stalePath) => {
|
|
45
|
+
// Historical CSV and path:note input must not become a confident basename
|
|
46
|
+
// relocation. Existing files are checked before classification; this only
|
|
47
|
+
// rejects ambiguous missing references, never an existing literal filename.
|
|
48
|
+
if (stalePath.includes(",") ||
|
|
49
|
+
stalePath.replace(/^[a-z]:[\\/]/i, "").includes(":")) {
|
|
50
|
+
return {
|
|
51
|
+
path: stalePath,
|
|
52
|
+
classification: "malformed",
|
|
53
|
+
candidates: [],
|
|
54
|
+
candidates_truncated: false,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
42
57
|
const matches = (candidatesByBasename.get(basenameOf(stalePath)) ?? [])
|
|
43
58
|
.filter((candidate) => candidate !== stalePath)
|
|
44
59
|
.sort((left, right) => left.localeCompare(right));
|
|
@@ -62,7 +77,7 @@ export function classifyStaleLinkedPaths(stalePaths, candidateFiles, candidateLi
|
|
|
62
77
|
export function summarizeStaleLinkedPathClassifications(classified) {
|
|
63
78
|
return classified.map((entry) => entry.classification === "moved"
|
|
64
79
|
? `${entry.path}:moved:${entry.candidates[0]}`
|
|
65
|
-
: `${entry.path}
|
|
80
|
+
: `${entry.path}:${entry.classification}`);
|
|
66
81
|
}
|
|
67
82
|
//# sourceMappingURL=stale-file-classification.js.map
|
|
68
|
-
//# debugId=
|
|
83
|
+
//# debugId=eb4fe9bd-31b5-5ed3-b401-3e3776ec12e9
|
package/dist/mcp/server.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Runs the MCP server adapter that exposes pm actions and contracts to external agents.
|
|
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]="
|
|
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]="1950da06-8c27-5506-bc64-4bf51c1a3f1c")}catch(e){}}();
|
|
9
9
|
import { realpathSync } from "node:fs";
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
11
|
import path from "node:path";
|
|
@@ -410,11 +410,13 @@ async function emitMcpChangeNotifications(action) {
|
|
|
410
410
|
}
|
|
411
411
|
pruneClosedMcpSubscriptionRegistries();
|
|
412
412
|
}
|
|
413
|
+
/** Resolve one tool's workspace and scope SDK attribution to that request without changing process environment. */
|
|
413
414
|
async function handleToolCall(paramsInput, clientInfo, progressiveDiscoveryNegotiated = false) {
|
|
415
|
+
const clientSignals = clientInfo ? { client_info: clientInfo } : {};
|
|
414
416
|
return runWithHarnessDetectionSignals({
|
|
415
417
|
env: process.env,
|
|
416
418
|
argv: process.argv,
|
|
417
|
-
...
|
|
419
|
+
...clientSignals,
|
|
418
420
|
}, async () => {
|
|
419
421
|
const params = asRecordClone(paramsInput);
|
|
420
422
|
const name = readRequiredString(params, "name");
|
|
@@ -454,7 +456,12 @@ async function handleToolCall(paramsInput, clientInfo, progressiveDiscoveryNegot
|
|
|
454
456
|
const workspaceIdentity = (await pathExists(getSettingsPath(pmRoot)))
|
|
455
457
|
? (await readSettings(pmRoot)).agent_identity
|
|
456
458
|
: undefined;
|
|
457
|
-
return
|
|
459
|
+
return runWithHarnessDetectionSignals({
|
|
460
|
+
env: { ...process.env, PM_PATH: pmRoot },
|
|
461
|
+
argv: process.argv,
|
|
462
|
+
cwd,
|
|
463
|
+
...clientSignals,
|
|
464
|
+
}, () => runWithWorkspaceHarnessSignalDescriptors(workspaceIdentity?.harness_signals ?? [], async () => {
|
|
458
465
|
// pm-qxwu: non-breaking detection of typo'd / unexpected top-level keys.
|
|
459
466
|
// additionalProperties stays true so passthrough still works; we only warn.
|
|
460
467
|
// pm-upi0 extends the same mechanism into the nested options object.
|
|
@@ -472,7 +479,7 @@ async function handleToolCall(paramsInput, clientInfo, progressiveDiscoveryNegot
|
|
|
472
479
|
const result = await handler(args);
|
|
473
480
|
void emitMcpChangeNotifications(action);
|
|
474
481
|
return resultContent(result, warnings, args.tokenAccounting === true, progressiveDiscoveryNegotiated);
|
|
475
|
-
}, { probesEnabled: workspaceIdentity?.probes_enabled });
|
|
482
|
+
}, { probesEnabled: workspaceIdentity?.probes_enabled }));
|
|
476
483
|
});
|
|
477
484
|
}
|
|
478
485
|
/** Open one modern subscription through a concrete transport-owned sink. */
|
|
@@ -1225,4 +1232,4 @@ if (isInvokedAsMcpMainModule(process.argv[1], import.meta.url)) {
|
|
|
1225
1232
|
}
|
|
1226
1233
|
/* c8 ignore stop */
|
|
1227
1234
|
//# sourceMappingURL=server.js.map
|
|
1228
|
-
//# debugId=
|
|
1235
|
+
//# debugId=1950da06-8c27-5506-bc64-4bf51c1a3f1c
|
|
@@ -21,6 +21,8 @@ export interface CliFlagContract {
|
|
|
21
21
|
value_type?: "string" | "number" | "boolean";
|
|
22
22
|
/** Marks a presentation-only flag that may change paging or rendering without invalidating continuation. Omission means the flag is query-semantic. */
|
|
23
23
|
cursor_semantics?: "presentation";
|
|
24
|
+
/** Host reservation boundary: inherited flags are forbidden on extensions; root-only flags may have command-local meanings. */
|
|
25
|
+
reservation_scope?: "inherited" | "root_only";
|
|
24
26
|
}
|
|
25
27
|
/** Maps an MCP tool option `param` to the CLI `flag` it forwards to, plus the value semantics (`allowEmpty`, `repeatable`, `booleanish`) the bridge needs to translate a structured tool call into argv. */
|
|
26
28
|
export interface ToolOptionFlagContract {
|