@unbrained/pm-cli 2026.10.18 → 2026.10.19
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 +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/cli/help-content.js +2 -2
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli/register-setup.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/chunk-4CXQSRSP.js +126 -0
- package/dist/cli-bundle/chunks/{chunk-AETP4JEM.js → chunk-4ICKT4GH.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-7WIEAGDS.js → chunk-4MJ6WBMA.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-4XFVAXA4.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-FIJOSX2C.js → chunk-5GZSX5HU.js} +72 -66
- package/dist/cli-bundle/chunks/{chunk-4GA4MUTE.js → chunk-6DS5CAN2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-6VZKYJW7.js → chunk-D3EDNAN7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-DMFM5R4G.js +3 -0
- package/dist/cli-bundle/chunks/chunk-H3XYP24A.js +70 -0
- package/dist/cli-bundle/chunks/chunk-LBEMB3BW.js +6 -0
- package/dist/cli-bundle/chunks/chunk-NX7Q4LGN.js +8 -0
- package/dist/cli-bundle/chunks/chunk-O2ZRRLAP.js +8 -0
- package/dist/cli-bundle/chunks/chunk-OEJ3G7XT.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-PYRREEUB.js → chunk-QBNEZII5.js} +28 -28
- package/dist/cli-bundle/chunks/chunk-QG4LMU7A.js +112 -0
- package/dist/cli-bundle/chunks/chunk-QKAAZWGC.js +3 -0
- package/dist/cli-bundle/chunks/chunk-RUKSEZE7.js +2 -0
- package/dist/cli-bundle/chunks/chunk-TK3G3GAP.js +2 -0
- package/dist/cli-bundle/chunks/chunk-VS565KBX.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-ZZED447K.js → chunk-XH7C3A5N.js} +2 -2
- package/dist/cli-bundle/chunks/indexed-receipt-blobs-BVSHK4WQ.js +4 -0
- package/dist/cli-bundle/chunks/{register-list-query-PUCVYYBD.js → register-list-query-WQUVDJIH.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-IWIBQIO3.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-SUWOZNDU.js +2 -0
- package/dist/cli-bundle/chunks/register-setup-QGM5YJW7.js +2 -0
- package/dist/cli-bundle/chunks/settlement-hook-UNFMCKKQ.js +7 -0
- package/dist/cli-bundle/chunks/stage-7DELI4Z5.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-2MPQUMTF.js → chunk-2IEWB6MB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3TEIACFU.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-5YTOYBTZ.js +65 -0
- package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-BJMPRB7A.js +91 -0
- package/dist/cli-bundle/focused-chunks/{chunk-BFEMT5U3.js → chunk-ENZMATOY.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-EOQ6W25I.js +8 -0
- package/dist/cli-bundle/focused-chunks/{chunk-A4ZHHTEX.js → chunk-FEN7WOOP.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-FOA7XKWP.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-GWTV45SB.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-ZDBS2DCT.js → chunk-H6BVQ63U.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IRVL4UXR.js → chunk-HYMYAGSY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JBJJW2WL.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-JCLC6HKT.js +8 -0
- package/dist/cli-bundle/focused-chunks/{chunk-TB7F4FCR.js → chunk-JDBO7SMR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-TZUIBG3P.js → chunk-KLJN44VY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-L4PL3GZR.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-OV2WVLUR.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-PXDR77TO.js +78 -0
- package/dist/cli-bundle/focused-chunks/chunk-RLRMNFLF.js +3 -0
- package/dist/cli-bundle/focused-chunks/{chunk-Z7YIFCNE.js → chunk-SKYLOPU2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-VVRXF4JO.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-WZTEFFQI.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-Y54ONBEX.js +25 -0
- package/dist/cli-bundle/focused-chunks/indexed-receipt-blobs-QW2VWWJE.js +4 -0
- package/dist/cli-bundle/focused-chunks/settlement-hook-X4FNHTOD.js +7 -0
- package/dist/cli-bundle/focused-chunks/stage-MZSVQYO4.js +2 -0
- package/dist/cli-bundle/main.js +3 -3
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +28 -22
- 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 +66 -53
- 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 +2 -2
- package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +1 -0
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/extension/install-sources.js +3 -3
- package/dist/sdk/extension/shared.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/health.js +1 -1
- package/dist/sdk/history-repair.js +1 -1
- package/dist/sdk/merge/driver.js +7 -1
- package/dist/sdk/merge/indexed-receipt-blobs.d.ts +2 -0
- package/dist/sdk/merge/indexed-receipt-blobs.js +4 -0
- package/dist/sdk/merge/install.d.ts +8 -0
- package/dist/sdk/merge/install.js +3 -3
- package/dist/sdk/merge/receipts.d.ts +6 -0
- package/dist/sdk/merge/receipts.js +2 -2
- package/dist/sdk/merge/reconcile.d.ts +6 -0
- package/dist/sdk/merge/reconcile.js +1 -1
- package/dist/sdk/merge/settlement-hook.d.ts +12 -0
- package/dist/sdk/merge/settlement-hook.js +7 -0
- package/dist/sdk/merge/stage.d.ts +13 -0
- package/dist/sdk/merge/stage.js +2 -0
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/list.js +1 -1
- package/dist/sdk/runtime-extended-actions.js +1 -1
- package/docs/COMMANDS.md +13 -0
- package/docs/CONFIGURATION.md +14 -0
- package/docs/MERGE_SAFETY.md +88 -5
- package/docs/SDK.md +26 -0
- package/docs/TESTING.md +7 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
- package/marketplace.json +1 -1
- package/package.json +1 -1
- 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/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-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-4V6ONLI6.js +0 -3
- package/dist/cli-bundle/chunks/chunk-7PATQWTH.js +0 -20
- package/dist/cli-bundle/chunks/chunk-LUKVNL72.js +0 -2
- package/dist/cli-bundle/chunks/chunk-OJ77ZQN3.js +0 -8
- package/dist/cli-bundle/chunks/chunk-RHQJ6VWC.js +0 -237
- package/dist/cli-bundle/chunks/chunk-RNABUEFB.js +0 -80
- package/dist/cli-bundle/chunks/chunk-TO6CIV4C.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-GGGVBXD5.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-NRZ6Q4QE.js +0 -2
- package/dist/cli-bundle/chunks/register-setup-MV3P3YSF.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-5UKCSZWT.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-B4YLP5ZB.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-FW7CSSKL.js +0 -75
- package/dist/cli-bundle/focused-chunks/chunk-HYPRPSYD.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-L55KQ2KT.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-MASIOFPG.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-RJ3EIHDU.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-SEL4Z2OO.js +0 -167
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="28558863-cc40-53d5-822d-d2f3bb3d2247")}catch{}})();import{BRIEF_HEALTH_DETAIL_LIMIT,HEALTH_WARNING_LIMIT,projectHealthResult,summarizeExtensionList,summarizeHealthCheckDetails,summarizeList,summarizeRecordList,summarizeStringList}from"./health/projection.js";import{runInitAgentGuidance}from"../init-agent-guidance.js";import fs from"node:fs/promises";import path from"node:path";import{resolveTelemetryEnvironmentPolicy}from"../../core/telemetry/policy.js";import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isTerminalStatus}from"../../core/item/status.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{resolveRuntimeStatusRegistry}from"../../core/schema/runtime-schema.js";import{isFileMissingError,readFileIfExists}from"../../core/fs/fs-utils.js";import{activateExtensions,getActiveExtensionRegistrations,loadExtensions,runActiveOnReadHooks}from"../../core/extensions/index.js";import{collectRegisteredItemFieldNames}from"../../core/extensions/item-fields.js";import{KNOWN_EXTENSION_CAPABILITIES}from"../../core/extensions/loader.js";import{enforceHistoryStreamPolicyForItems}from"../../core/history/history-stream-policy.js";import{scanHistoryDrift}from"../../core/history/drift-scan.js";import{scanLockHealth}from"../../core/lock/lock-gc.js";import{readVectorizationStatusLedger,refreshSemanticEmbeddingsForMutatedItems}from"../../core/search/cache.js";import{resolveEmbeddingProviders,resolveProviderConfigSource}from"../../core/search/providers.js";import{resolveSettingsWithSemanticRuntimeDefaults}from"../../core/search/semantic-defaults.js";import{collectStaleVectorizationIds}from"../../core/search/staleness.js";import{buildVectorizationEmbeddingIdentity,hasVectorizationEmbeddingIdentityChanged}from"../../core/search/vectorization-metadata.js";import{resolveVectorStores}from"../../core/search/vector-stores.js";import{EXIT_CODE,PM_CORE_REQUIRED_SUBDIRS,TYPE_TO_FOLDER}from"../../core/shared/constants.js";import{findFirstMergeConflictMarker}from"../../core/shared/conflict-markers.js";import{PmCliError}from"../../core/shared/errors.js";import{toNonEmptyStringOrUndefined}from"../../core/shared/primitives.js";import{nowIso}from"../../core/shared/time.js";import{parseItemDocument}from"../../core/item/item-format.js";import{BASELINE_ITEM_FORMAT_VERSION,CURRENT_ITEM_FORMAT_VERSION,effectiveItemFormatVersion,scanItemFormatVersions}from"../../core/item/item-format-version.js";import{listAllItemMetadataLight,listAllItemMetadataWithBody}from"../../core/store/item-store.js";import{PM_TELEMETRY_SOURCE_CONTEXT_VALUES,TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS,TELEMETRY_SCHEMA_VERSION}from"../../core/telemetry/runtime.js";import{getItemFormatFromPath,getSettingsPath,ITEM_FILE_EXTENSIONS,resolveGlobalPmRoot,resolvePmRoot,resolveWorkspaceRoot}from"../../core/store/paths.js";import{readSettingsWithMetadata}from"../../core/store/settings.js";import{buildRemediationMap,resolveRemediation}from"../../core/diagnostics/remediation.js";import{scanExtensionHostVersions}from"./extension-host-version.js";import{scanHistoryAuthorAttribution}from"../author-attribution.js";import{readManagedExtensionState}from"../extension.js";import{scanProvenanceResolverHealth}from"./provenance-health.js";import{applyStoredExtensionMigrationState}from"../extension/migrations.js";import{inspectExtensionAuthorManifest}from"../extension/author-manifest.js";import{buildCapabilityContractMetadata,buildRegistrationCollisionRemediation,collectUnknownCapabilityGuidance}from"../extension/doctor.js";import{scanStaleInProgressItems}from"./stale-work.js";import{scanTrackedRuntimeCache}from"./tracked-runtime-cache.js";import{buildMergeReceiptEvidenceWarnings,resolveMergeFenceHealthEvidence}from"./health-merge-evidence.js";import{auditMergeDriverConfiguration,findGitWorkspaceRoot}from"../merge/install.js";import{inspectMergeReceiptEvidence,partitionMergeReceipts}from"../merge/receipts.js";import{classifyHistoryMergeReceiptReferences,extractHistoryMergeReceiptDispositions,extractHistoryMergeReceiptReferences}from"./merge-receipt-history.js";import{runHistoryRepair}from"../history-repair.js";import{resolveWorkspaceRelationshipKindRegistry}from"../graph/assembly.js";import{analyzeHierarchyIntegrity}from"../graph/hierarchy-integrity.js";import{buildStatusRoleWarnings,inspectStatusRoleAssignments}from"./status-role-diagnostics.js";const PM_TELEMETRY_SOURCE_CONTEXT_SET=new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES),STALE_VECTORIZATION_SUMMARY_LIMIT=25,TELEMETRY_QUEUE_RELATIVE_PATH=path.join("runtime","telemetry","events.jsonl"),TELEMETRY_STATE_RELATIVE_PATH=path.join("runtime","telemetry","state.json"),TELEMETRY_ENDPOINT_PROBE_TIMEOUT_MS=2500,TELEMETRY_QUEUE_HIGH_WATER_MARK=500,TELEMETRY_QUEUE_HIGH_RETRY_THRESHOLD=TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS-3,TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS=["x-pm-telemetry-max-schema-version","x-pm-telemetry-max-version"];function isAdvisoryHealthWarning(warning,requireMergeDrivers=!1){return warning.startsWith("agent_guidance_")||warning.startsWith("telemetry_")||warning.startsWith("history_stream_over_compact_threshold:")||warning.startsWith("integrity_legacy_hierarchy_")||warning.startsWith("stale_in_progress_items:")||warning.startsWith("provenance_resolver_zero_success:")||warning.startsWith("provenance_value_domain_invalid:")||!requireMergeDrivers&&warning.startsWith("merge_driver_configuration_missing:")}function warningCode(value){const normalized=value.trim(),separator=normalized.indexOf(":");return separator===-1?normalized:normalized.slice(0,separator)}function normalizeExtensionNameForMatch(value){return value.trim().toLowerCase()}function isExpectedUnmanagedExtension(name,directory){const normalizedName=normalizeExtensionNameForMatch(name),normalizedDirectory=normalizeExtensionNameForMatch(directory);return normalizedName.startsWith("builtin-")?!0:normalizedDirectory==="beads"||normalizedDirectory==="todos"}async function isDirectory(targetPath){try{return(await fs.stat(targetPath)).isDirectory()}catch{return!1}}async function listStoredItemDocumentIds(pmRoot){const directories=(await fs.readdir(pmRoot,{withFileTypes:!0})).filter(entry=>entry.isDirectory()),filesByDirectory=await Promise.all(directories.map(entry=>fs.readdir(path.join(pmRoot,entry.name),{withFileTypes:!0})));return new Set(filesByDirectory.flatMap(entries=>entries.filter(entry=>entry.isFile()).filter(entry=>ITEM_FILE_EXTENSIONS.some(extension=>entry.name.endsWith(extension))).map(entry=>path.parse(entry.name).name)))}async function inspectHistoryStream(streamPath,itemId,compactPolicy,liveItemIds){const warnings=await runActiveOnReadHooks({path:streamPath,scope:"project"}),needsClassification=itemId!=="_workspace"&&!liveItemIds.has(itemId);if(!compactPolicy.enabled&&!needsClassification)return{warnings,overThreshold:!1,classification:"live"};let raw;try{raw=await fs.readFile(streamPath,"utf8")}catch{return{warnings,overThreshold:!1,classification:"unreadable"}}let classification="live";if(needsClassification){const lastLine=raw.split(/\r?\n/).map(line=>line.trim()).filter(Boolean).at(-1);try{classification=JSON.parse(lastLine??"{}").op==="delete"?"tombstone":"orphaned"}catch{classification="orphaned"}}const entryCount=compactPolicy.enabled?raw.split(/\r?\n/).filter(line=>line.trim().length>0).length:0;return{warnings,overThreshold:compactPolicy.enabled&&entryCount>compactPolicy.max_entries,classification}}async function countHistoryStreams(pmRoot,compactPolicy,liveItemIds){const historyDir=path.join(pmRoot,"history");if(!await isDirectory(historyDir))return{count:0,warnings:[],over_threshold:[],max_entries:null,tombstones:[],orphaned:[]};const historyFiles=(await fs.readdir(historyDir)).filter(entry=>entry.endsWith(".jsonl")).sort((left,right)=>left.localeCompare(right)),maxEntries=compactPolicy.enabled?compactPolicy.max_entries:null,warnings=[],overThreshold=[],tombstones=[],orphaned=[];for(const fileName of historyFiles){const streamPath=path.join(historyDir,fileName),itemId=fileName.slice(0,-6),inspection=await inspectHistoryStream(streamPath,itemId,compactPolicy,liveItemIds);warnings.push(...inspection.warnings),inspection.classification==="tombstone"&&tombstones.push(itemId),inspection.classification==="orphaned"&&orphaned.push(itemId),inspection.overThreshold&&overThreshold.push(itemId)}return{count:historyFiles.length,warnings:[...warnings,...overThreshold.map(id=>`history_stream_over_compact_threshold:${id}`),...orphaned.map(id=>`history_orphaned_stream:${id}`)],over_threshold:overThreshold,max_entries:maxEntries,tombstones,orphaned}}function normalizeRelativePath(pmRoot,targetPath){return path.relative(pmRoot,targetPath).replaceAll("\\","/")}async function listItemDocumentPaths(pmRoot,typeToFolder){const folders=[...new Set(Object.values(typeToFolder))].sort((left,right)=>left.localeCompare(right)),itemPaths=[];for(const folder of folders){const directoryPath=path.join(pmRoot,folder);let entries=[];try{entries=await fs.readdir(directoryPath)}catch(error){if(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))continue;continue}for(const entry of entries)ITEM_FILE_EXTENSIONS.some(extension=>entry.toLowerCase().endsWith(extension))&&itemPaths.push(path.join(directoryPath,entry))}return itemPaths.sort((left,right)=>normalizeRelativePath(pmRoot,left).localeCompare(normalizeRelativePath(pmRoot,right))),itemPaths}function shouldReportHistoryDirectoryUnreadable(error){return!(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))}async function scanItemIntegrity(pmRoot,typeToFolder,schema){const paths=await listItemDocumentPaths(pmRoot,typeToFolder),unreadable=[],conflictMarkers=[],parseFailures=[],formatVersions=[],extensionFieldNames=collectRegisteredItemFieldNames(getActiveExtensionRegistrations());for(const itemPath of paths){const relativePath=normalizeRelativePath(pmRoot,itemPath);let raw;try{raw=await fs.readFile(itemPath,"utf8")}catch{unreadable.push(relativePath);continue}const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){conflictMarkers.push({path:relativePath,...conflictMarker});continue}try{const parsed=parseItemDocument(raw,{format:getItemFormatFromPath(itemPath),schema,extensionFieldNames});formatVersions.push({ref:relativePath,version:effectiveItemFormatVersion(parsed.metadata)})}catch{parseFailures.push(relativePath)}}return{paths,unreadable,conflictMarkers,parseFailures,formatVersions}}async function scanHistoryIntegrity(pmRoot){const historyDir=path.join(pmRoot,"history"),unreadable=[],conflictMarkers=[],invalidJson=[],mergeReceiptReferences=[],mergeReceiptDispositions=[];let files=[];try{files=(await fs.readdir(historyDir)).filter(entry=>entry.endsWith(".jsonl")).sort((left,right)=>left.localeCompare(right))}catch(error){shouldReportHistoryDirectoryUnreadable(error)&&unreadable.push("history")}for(const fileName of files){const itemId=fileName.slice(0,-6);let raw;try{raw=await fs.readFile(path.join(historyDir,fileName),"utf8")}catch{unreadable.push(itemId);continue}const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){conflictMarkers.push({id:itemId,...conflictMarker});continue}raw.split(/\r?\n/).forEach((line,index)=>{if(line.trim())try{const parsed=JSON.parse(line);mergeReceiptReferences.push(...extractHistoryMergeReceiptReferences(parsed,itemId).map(reference=>({itemId,line:index+1,...reference}))),mergeReceiptDispositions.push(...extractHistoryMergeReceiptDispositions(parsed,itemId,index+1))}catch{invalidJson.push({id:itemId,line:index+1})}})}return{files,unreadable,conflictMarkers,invalidJson,mergeReceiptReferences,mergeReceiptDispositions}}function buildHierarchyIntegrityWarnings(analysis){return[...analysis.cycles.map(cycle=>cycle.legacy_terminal?`integrity_legacy_hierarchy_cycle:${cycle.item_ids.join(",")}`:`integrity_hierarchy_cycle:${cycle.item_ids.join(",")}`),...analysis.cardinality_violations.map(finding=>finding.legacy_terminal?`integrity_legacy_hierarchy_cardinality:${finding.child_id}`:`integrity_hierarchy_cardinality:${finding.child_id}`),...analysis.divergences.map(finding=>finding.legacy_terminal?`integrity_legacy_hierarchy_divergence:${finding.child_id}`:`integrity_hierarchy_divergence:${finding.child_id}`)]}async function buildIntegrityCheck(pmRoot,typeToFolder,schema,settings,requireMergeDrivers,items){const itemScan=await scanItemIntegrity(pmRoot,typeToFolder,schema),historyScan=await scanHistoryIntegrity(pmRoot),formatVersionScan=scanItemFormatVersions(itemScan.formatVersions,CURRENT_ITEM_FORMAT_VERSION,BASELINE_ITEM_FORMAT_VERSION),statusRegistry=resolveRuntimeStatusRegistry(schema),hierarchyIntegrity=analyzeHierarchyIntegrity(items,status=>isTerminalStatus(status,statusRegistry),resolveWorkspaceRelationshipKindRegistry()),trackedRuntimeCache=await scanTrackedRuntimeCache(pmRoot),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),mergeFence=await resolveMergeFenceHealthEvidence(pmRoot,gitWorkspaceRoot,settings,requireMergeDrivers),mergeDriverAudit=gitWorkspaceRoot===null?null:await auditMergeDriverConfiguration(gitWorkspaceRoot),mergeReceiptEvidence=await inspectMergeReceiptEvidence(gitWorkspaceRoot??pmRoot,{includeReconciled:!0,includeLossless:!0,pmRoot}),availableMergeReceiptIds=new Set(mergeReceiptEvidence.receipts.map(receipt=>receipt.id)),historyReceiptClassification=classifyHistoryMergeReceiptReferences(historyScan.mergeReceiptReferences,availableMergeReceiptIds,historyScan.mergeReceiptDispositions),missingMergeReceiptReferences=historyReceiptClassification.missing,pendingMergeReceipts=mergeReceiptEvidence.receipts.filter(receipt=>receipt.state==="pending"),{pendingDecisions:pendingMergeDecisions,lossless:losslessMergeReceipts}=partitionMergeReceipts(pendingMergeReceipts),mergeDriverInstallationMissing=mergeDriverAudit?.status==="missing"&&mergeDriverAudit.missing_keys.length===5&&mergeDriverAudit.drifted_keys.length===0,warnings=[...itemScan.unreadable.map(entry=>`integrity_item_unreadable:${entry}`),...itemScan.conflictMarkers.map(entry=>`integrity_item_conflict_marker:${entry.path}:L${entry.line}`),...itemScan.parseFailures.map(entry=>`integrity_item_parse_failed:${entry}`),...historyScan.unreadable.map(entry=>`integrity_history_unreadable:${entry}`),...historyScan.conflictMarkers.map(entry=>`integrity_history_conflict_marker:${entry.id}:L${entry.line}`),...historyScan.invalidJson.map(entry=>`integrity_history_invalid_json:${entry.id}:L${entry.line}`),...formatVersionScan.outdated.map(entry=>`integrity_item_outdated_format_version:${entry}`),...formatVersionScan.ahead.map(entry=>`integrity_item_ahead_format_version:${entry}`),...buildHierarchyIntegrityWarnings(hierarchyIntegrity),...trackedRuntimeCache.tracked_path_count>0?[`tracked_runtime_cache_files:${trackedRuntimeCache.tracked_path_count}`]:[],...mergeDriverAudit!==null&&mergeDriverAudit.status!=="ok"?[`merge_driver_configuration_${mergeDriverInstallationMissing?"missing":"drift"}:${mergeDriverAudit.missing_keys.length+mergeDriverAudit.drifted_keys.length}`]:[],...mergeFence.warnings,...pendingMergeDecisions.length>0?[`merge_decisions_unreviewed:${pendingMergeDecisions.length}`]:[],...losslessMergeReceipts.length>0?[`merge_receipts_pending:${losslessMergeReceipts.length}`]:[],...buildMergeReceiptEvidenceWarnings({invalidEvidenceCount:mergeReceiptEvidence.invalid_evidence_count,missingHistoryReferenceCount:missingMergeReceiptReferences.length})],normalizedWarnings=[...new Set(warnings)].sort((left,right)=>left.localeCompare(right)),actionableWarnings=normalizedWarnings.filter(warning=>!warning.startsWith("integrity_legacy_hierarchy_"));return{check:{name:"integrity",status:actionableWarnings.length===0?"ok":"warn",ok:actionableWarnings.length===0,details:{checked_item_files:itemScan.paths.length,checked_history_streams:historyScan.files.length,counts:{item_unreadable:itemScan.unreadable.length,item_conflict_markers:itemScan.conflictMarkers.length,item_parse_failures:itemScan.parseFailures.length,history_unreadable:historyScan.unreadable.length,history_conflict_markers:historyScan.conflictMarkers.length,history_invalid_json:historyScan.invalidJson.length,item_outdated_format_version:formatVersionScan.outdated.length,item_ahead_format_version:formatVersionScan.ahead.length,hierarchy_cycles:hierarchyIntegrity.cycles.length,hierarchy_cardinality_violations:hierarchyIntegrity.cardinality_violations.length,hierarchy_parent_divergences:hierarchyIntegrity.divergences.length,tracked_runtime_cache_files:trackedRuntimeCache.tracked_path_count,merge_driver_configuration:mergeDriverAudit===null||mergeDriverAudit.status==="ok"?0:mergeDriverAudit.missing_keys.length+mergeDriverAudit.drifted_keys.length,merge_fence:mergeFence.count,pending_merge_decisions:pendingMergeDecisions.length,lossless_merge_receipts:losslessMergeReceipts.length,invalid_merge_receipt_evidence:mergeReceiptEvidence.invalid_evidence_count,missing_merge_receipt_history_references:missingMergeReceiptReferences.length,accepted_legacy_merge_receipt_references:historyReceiptClassification.acceptedLegacyCount,accepted_missing_merge_receipt_dispositions:historyReceiptClassification.acceptedDispositionCount},item_unreadable:itemScan.unreadable,item_conflict_markers:itemScan.conflictMarkers,item_parse_failures:itemScan.parseFailures,item_outdated_format_version:formatVersionScan.outdated,item_ahead_format_version:formatVersionScan.ahead,hierarchy_integrity:{...hierarchyIntegrity,relations:summarizeRecordList(hierarchyIntegrity.relations,BRIEF_HEALTH_DETAIL_LIMIT)},history_unreadable:historyScan.unreadable,history_conflict_markers:historyScan.conflictMarkers,history_invalid_json:historyScan.invalidJson,tracked_runtime_cache:{tracker_relative_root:trackedRuntimeCache.tracker_relative_root,tracked_paths:trackedRuntimeCache.tracked_paths,tracked_path_count:trackedRuntimeCache.tracked_path_count,remediation_command:trackedRuntimeCache.remediation_command},merge_driver_configuration:mergeDriverAudit===null?null:{...mergeDriverAudit,required:requireMergeDrivers},merge_fence:mergeFence.audit,pending_merge_decision_items:[...new Set(pendingMergeDecisions.map(receipt=>receipt.item_id))].sort((left,right)=>left.localeCompare(right)),lossless_merge_receipt_items:[...new Set(losslessMergeReceipts.map(receipt=>receipt.item_id))].sort((left,right)=>left.localeCompare(right)),invalid_merge_receipt_evidence_details:mergeReceiptEvidence.invalid_evidence,invalid_merge_receipt_evidence_details_truncated:mergeReceiptEvidence.invalid_evidence_truncated,missing_merge_receipt_history_reference_details:missingMergeReceiptReferences.slice(0,HEALTH_WARNING_LIMIT),missing_merge_receipt_history_reference_details_truncated:missingMergeReceiptReferences.length>HEALTH_WARNING_LIMIT}},warnings:normalizedWarnings,gitWorkspaceRoot,pendingMergeReceipts}}function hasActivateExport(moduleRecord){if(typeof moduleRecord.activate=="function")return!0;const defaultExport=moduleRecord.default;return typeof defaultExport!="object"||defaultExport===null?!1:typeof defaultExport.activate=="function"}function summarizeLoadedExtension(extension){const summary={layer:extension.layer,directory:extension.directory,manifest_path:extension.manifest_path,name:extension.name,version:extension.version,entry:extension.entry,priority:extension.priority,entry_path:extension.entry_path,has_activate:hasActivateExport(extension.module)};return Array.isArray(extension.capabilities)&&(summary.capabilities=[...extension.capabilities]),summary}function resolveMigrationId(definition,fallbackIndex){return toNonEmptyStringOrUndefined(definition.id)||`migration-${String(fallbackIndex+1).padStart(3,"0")}`}function resolveMigrationStatus(definition){const normalized=toNonEmptyStringOrUndefined(definition.status)?.toLowerCase();return normalized==="failed"?"failed":normalized==="applied"?"applied":"pending"}function resolveMigrationFailureReason(definition){return toNonEmptyStringOrUndefined(definition.reason)??toNonEmptyStringOrUndefined(definition.error)??toNonEmptyStringOrUndefined(definition.message)}function compareMigrationEntries(left,right){const byLayer=(left.layer??"").localeCompare(right.layer??"");if(byLayer!==0)return byLayer;const byName=(left.name??"").localeCompare(right.name??"");return byName!==0?byName:(left.id??"").localeCompare(right.id??"")}function summarizeMigrationStatuses(migrations){const applied=[],pending=[],failed=[];migrations.forEach((migration,index)=>{const id=resolveMigrationId(migration.definition,index),status=resolveMigrationStatus(migration.definition);if(status==="applied"){applied.push({layer:migration.layer,name:migration.name,id,status});return}if(status==="failed"){failed.push({layer:migration.layer,name:migration.name,id,status,reason:resolveMigrationFailureReason(migration.definition)});return}pending.push({layer:migration.layer,name:migration.name,id,status})}),applied.sort(compareMigrationEntries),pending.sort(compareMigrationEntries),failed.sort(compareMigrationEntries);const warnings=[...failed.map(entry=>`extension_migration_failed:${entry.layer}:${entry.name}:${entry.id}`),...pending.map(entry=>`extension_migration_pending:${entry.layer}:${entry.name}:${entry.id}`)];return{summary:{applied,pending,failed,applied_count:applied.length,pending_count:pending.length,failed_count:failed.length},warnings}}function buildExtensionHealthTriageSummary(warnings,loadFailureCount,activationFailureCount,migrationStatus,managedStateWarningCount,managedExtensionEntriesCount,unmanagedLoadedExtensions,unmanagedExpectedExtensions,unmanagedActionRequiredExtensions){const normalizedWarnings=[...new Set(warnings)].sort((left,right)=>left.localeCompare(right)),warningCodes=[...new Set(normalizedWarnings.map(value=>warningCode(value)))].sort((left,right)=>left.localeCompare(right)),unknownCapabilityCount=normalizedWarnings.filter(warning=>warning.startsWith("extension_capability_unknown:")).length,updateHealthPartial=unmanagedActionRequiredExtensions.length>0,updateHealthCoverage=updateHealthPartial?"partial":"full",remediation=[],registrationCollisionRemediation=buildRegistrationCollisionRemediation(normalizedWarnings,{deactivate:"pm extension --deactivate <name> --project/--global",doctor:"pm extension --doctor --project/--global --detail deep --trace"});return registrationCollisionRemediation&&remediation.push(registrationCollisionRemediation),loadFailureCount>0&&remediation.push("Run pm extension --explore --project and pm extension --explore --global to inspect load failures."),activationFailureCount>0&&remediation.push("Review checks[name=extensions].details.activation.failed in pm health --json for activation error details."),(migrationStatus.failed_count>0||migrationStatus.pending_count>0)&&remediation.push("Resolve pending/failed extension migrations before write commands; use --force only when policy allows."),managedStateWarningCount>0&&remediation.push("Run pm extension --manage --project and pm extension --manage --global to refresh managed-state diagnostics."),unknownCapabilityCount>0&&remediation.push(`Unknown extension capabilities detected. Allowed capabilities: ${KNOWN_EXTENSION_CAPABILITIES.join(", ")}. Review extension_capability_unknown warning details for suggested replacements.`),normalizedWarnings.some(warning=>warning.startsWith("extension_capability_legacy_alias:"))&&remediation.push("Legacy extension capability aliases were auto-remapped to canonical capabilities. Update manifests to canonical names (migration/validation -> schema)."),normalizedWarnings.some(warning=>warning.startsWith("extension_command_definition_legacy_handler_alias:"))&&remediation.push("Extension command definitions using legacy handler were auto-remapped. Update command definitions to use run: (context) => ... for forward compatibility."),updateHealthPartial?remediation.push("Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via pm extension --manage --project/--global --fix-managed-state, pm extension --adopt-all --project/--global, or pm extension --adopt <name>."):unmanagedLoadedExtensions.length>0&&remediation.push("Loaded unmanaged extensions are currently treated as informational. Use pm extension --manage --project/--global --fix-managed-state to adopt them for update checks."),remediation.length===0&&remediation.push("No immediate action required. Re-run pm health after extension configuration changes."),{status:normalizedWarnings.length===0?"ok":"warn",warning_count:normalizedWarnings.length,warning_codes:warningCodes,load_failure_count:loadFailureCount,activation_failure_count:activationFailureCount,migration_failed_count:migrationStatus.failed_count,migration_pending_count:migrationStatus.pending_count,managed_state_warning_count:managedStateWarningCount,managed_extension_entries_count:managedExtensionEntriesCount,unmanaged_loaded_extension_count:unmanagedLoadedExtensions.length,unmanaged_loaded_extensions:unmanagedLoadedExtensions,unmanaged_expected_extension_count:unmanagedExpectedExtensions.length,unmanaged_expected_extensions:unmanagedExpectedExtensions,unmanaged_action_required_extension_count:unmanagedActionRequiredExtensions.length,unmanaged_action_required_extensions:unmanagedActionRequiredExtensions,update_health_coverage:updateHealthCoverage,update_health_partial:updateHealthPartial,unknown_capability_count:unknownCapabilityCount,top_warnings:normalizedWarnings.slice(0,8),remediation}}async function buildExtensionCheck(pmRoot,settings,noExtensionsFlag){const authorManifest=await inspectExtensionAuthorManifest(resolveWorkspaceRoot(pmRoot)),loadResult=await loadExtensions({pmRoot,settings,cwd:process.cwd(),noExtensions:noExtensionsFlag}),loadedSummaries=loadResult.loaded.map(extension=>summarizeLoadedExtension(extension)),hostVersionCensus=await scanExtensionHostVersions(loadResult.loaded,resolveWorkspaceRoot(pmRoot)),activationResult=await activateExtensions({...loadResult,loaded:loadResult.loaded}),[projectManagedState,globalManagedState]=await Promise.all([readManagedExtensionState(loadResult.roots.project),readManagedExtensionState(loadResult.roots.global)]);await applyStoredExtensionMigrationState(pmRoot,activationResult.registrations.migrations);const migrationStatus=summarizeMigrationStatuses(activationResult.registrations.migrations),activationDetails={failed:activationResult.failed,warnings:activationResult.warnings,hook_counts:activationResult.hook_counts,command_override_count:activationResult.command_override_count,command_handler_count:activationResult.command_handler_count,parser_override_count:activationResult.parser_override_count,preflight_override_count:activationResult.preflight_override_count,service_override_count:activationResult.service_override_count,renderer_override_count:activationResult.renderer_override_count,registration_counts:activationResult.registration_counts,registrations:activationResult.registrations,migration_status:migrationStatus.summary,managed_extensions:{project:{path:projectManagedState.path,count:projectManagedState.state.entries.length,entries:projectManagedState.state.entries},global:{path:globalManagedState.path,count:globalManagedState.state.entries.length,entries:globalManagedState.state.entries}}},managedProjectNames=new Set(projectManagedState.state.entries.map(entry=>normalizeExtensionNameForMatch(entry.name))),managedGlobalNames=new Set(globalManagedState.state.entries.map(entry=>normalizeExtensionNameForMatch(entry.name))),unmanagedLoadedEntries=[...new Map(loadResult.loaded.filter(entry=>!(entry.layer==="project"?managedProjectNames:managedGlobalNames).has(normalizeExtensionNameForMatch(entry.name))).map(entry=>[`${entry.layer}:${entry.name}`,{layer:entry.layer,name:entry.name,directory:entry.directory}])).values()].sort((left,right)=>{const leftKey=`${left.layer}:${left.name}`,rightKey=`${right.layer}:${right.name}`;return leftKey.localeCompare(rightKey)}),unmanagedLoadedExtensions=unmanagedLoadedEntries.map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),unmanagedExpectedExtensions=unmanagedLoadedEntries.filter(entry=>isExpectedUnmanagedExtension(entry.name,entry.directory)).map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),unmanagedActionRequiredExtensions=unmanagedLoadedEntries.filter(entry=>!isExpectedUnmanagedExtension(entry.name,entry.directory)).map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),updateCoverageWarnings=unmanagedActionRequiredExtensions.length>0?[`extension_update_health_partial_coverage:skipped_unmanaged:${unmanagedActionRequiredExtensions.length}`]:[],extensionWarnings=[...authorManifest.warnings,...loadResult.warnings,...activationDetails.warnings,...migrationStatus.warnings,...projectManagedState.warnings,...globalManagedState.warnings,...updateCoverageWarnings,...hostVersionCensus.warnings],capabilityGuidance=collectUnknownCapabilityGuidance(extensionWarnings),capabilityContract=buildCapabilityContractMetadata(),extensionTriage=buildExtensionHealthTriageSummary(extensionWarnings,loadResult.failed.length,activationResult.failed.length,migrationStatus.summary,projectManagedState.warnings.length+globalManagedState.warnings.length,projectManagedState.state.entries.length+globalManagedState.state.entries.length,unmanagedLoadedExtensions,unmanagedExpectedExtensions,unmanagedActionRequiredExtensions);return{check:{name:"extensions",status:extensionWarnings.length===0?"ok":"warn",ok:extensionWarnings.length===0,details:{...loadResult,loaded:loadedSummaries,warnings:extensionWarnings,activation:activationDetails,author_manifest:authorManifest,triage:extensionTriage,...migrationStatus.summary.pending_count>0?{remediation_map:{extension_migration_pending:"pm extension migrate --project"}}:{},capability_contract:capabilityContract,capability_guidance:capabilityGuidance,host_version_census:hostVersionCensus}},warnings:extensionWarnings}}function selectStaleItemDetail(values,verboseStaleItems){if(verboseStaleItems)return{values,truncated:!1,total:values.length};const summary=summarizeList(values,STALE_VECTORIZATION_SUMMARY_LIMIT);return{values:summary.values,truncated:summary.truncated,total:values.length}}function telemetryEnvFlagEnabled(envKey){const value=(process.env[envKey]??"").trim().toLowerCase();return value==="1"||value==="true"||value==="yes"||value==="on"}function telemetrySourceContextOverride(){const value=(process.env.PM_TELEMETRY_SOURCE_CONTEXT??"").trim().toLowerCase();return PM_TELEMETRY_SOURCE_CONTEXT_SET.has(value)?value:null}function normalizeEndpointForDisplay(rawEndpoint){const trimmed=rawEndpoint.trim();if(trimmed.length===0)return"";try{const parsed=new URL(trimmed);return parsed.username="",parsed.password="",parsed.search="",parsed.hash="",parsed.toString()}catch{return trimmed}}function parseTelemetryQueue(raw){let validEntries=0,invalidRows=0,totalRows=0,highRetryEntries=0,maxAttempts=0;for(const line of raw.split(`
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="688b552b-3d10-521e-b695-b3edfbfed36e")}catch{}})();import{BRIEF_HEALTH_DETAIL_LIMIT,HEALTH_WARNING_LIMIT,projectHealthResult,summarizeExtensionList,summarizeHealthCheckDetails,summarizeList,summarizeRecordList,summarizeStringList}from"./health/projection.js";import{runInitAgentGuidance}from"../init-agent-guidance.js";import fs from"node:fs/promises";import path from"node:path";import{resolveTelemetryEnvironmentPolicy}from"../../core/telemetry/policy.js";import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isTerminalStatus}from"../../core/item/status.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{resolveRuntimeStatusRegistry}from"../../core/schema/runtime-schema.js";import{isFileMissingError,readFileIfExists}from"../../core/fs/fs-utils.js";import{activateExtensions,getActiveExtensionRegistrations,loadExtensions,runActiveOnReadHooks}from"../../core/extensions/index.js";import{collectRegisteredItemFieldNames}from"../../core/extensions/item-fields.js";import{KNOWN_EXTENSION_CAPABILITIES}from"../../core/extensions/loader.js";import{enforceHistoryStreamPolicyForItems}from"../../core/history/history-stream-policy.js";import{scanHistoryDrift}from"../../core/history/drift-scan.js";import{scanLockHealth}from"../../core/lock/lock-gc.js";import{readVectorizationStatusLedger,refreshSemanticEmbeddingsForMutatedItems}from"../../core/search/cache.js";import{resolveEmbeddingProviders,resolveProviderConfigSource}from"../../core/search/providers.js";import{resolveSettingsWithSemanticRuntimeDefaults}from"../../core/search/semantic-defaults.js";import{collectStaleVectorizationIds}from"../../core/search/staleness.js";import{buildVectorizationEmbeddingIdentity,hasVectorizationEmbeddingIdentityChanged}from"../../core/search/vectorization-metadata.js";import{resolveVectorStores}from"../../core/search/vector-stores.js";import{EXIT_CODE,PM_CORE_REQUIRED_SUBDIRS,TYPE_TO_FOLDER}from"../../core/shared/constants.js";import{findFirstMergeConflictMarker}from"../../core/shared/conflict-markers.js";import{PmCliError}from"../../core/shared/errors.js";import{toNonEmptyStringOrUndefined}from"../../core/shared/primitives.js";import{nowIso}from"../../core/shared/time.js";import{parseItemDocument}from"../../core/item/item-format.js";import{BASELINE_ITEM_FORMAT_VERSION,CURRENT_ITEM_FORMAT_VERSION,effectiveItemFormatVersion,scanItemFormatVersions}from"../../core/item/item-format-version.js";import{listAllItemMetadataLight,listAllItemMetadataWithBody}from"../../core/store/item-store.js";import{PM_TELEMETRY_SOURCE_CONTEXT_VALUES,TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS,TELEMETRY_SCHEMA_VERSION}from"../../core/telemetry/runtime.js";import{getItemFormatFromPath,getSettingsPath,ITEM_FILE_EXTENSIONS,resolveGlobalPmRoot,resolvePmRoot,resolveWorkspaceRoot}from"../../core/store/paths.js";import{readSettingsWithMetadata}from"../../core/store/settings.js";import{buildRemediationMap,resolveRemediation}from"../../core/diagnostics/remediation.js";import{scanExtensionHostVersions}from"./extension-host-version.js";import{scanHistoryAuthorAttribution}from"../author-attribution.js";import{readManagedExtensionState}from"../extension.js";import{scanProvenanceResolverHealth}from"./provenance-health.js";import{applyStoredExtensionMigrationState}from"../extension/migrations.js";import{inspectExtensionAuthorManifest}from"../extension/author-manifest.js";import{buildCapabilityContractMetadata,buildRegistrationCollisionRemediation,collectUnknownCapabilityGuidance}from"../extension/doctor.js";import{scanStaleInProgressItems}from"./stale-work.js";import{scanTrackedRuntimeCache}from"./tracked-runtime-cache.js";import{buildMergeReceiptEvidenceWarnings,resolveMergeFenceHealthEvidence}from"./health-merge-evidence.js";import{auditMergeDriverConfiguration,findGitWorkspaceRoot}from"../merge/install.js";import{inspectMergeReceiptEvidence,partitionMergeReceipts}from"../merge/receipts.js";import{classifyHistoryMergeReceiptReferences,extractHistoryMergeReceiptDispositions,extractHistoryMergeReceiptReferences}from"./merge-receipt-history.js";import{runHistoryRepair}from"../history-repair.js";import{resolveWorkspaceRelationshipKindRegistry}from"../graph/assembly.js";import{analyzeHierarchyIntegrity}from"../graph/hierarchy-integrity.js";import{buildStatusRoleWarnings,inspectStatusRoleAssignments}from"./status-role-diagnostics.js";const PM_TELEMETRY_SOURCE_CONTEXT_SET=new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES),STALE_VECTORIZATION_SUMMARY_LIMIT=25,TELEMETRY_QUEUE_RELATIVE_PATH=path.join("runtime","telemetry","events.jsonl"),TELEMETRY_STATE_RELATIVE_PATH=path.join("runtime","telemetry","state.json"),TELEMETRY_ENDPOINT_PROBE_TIMEOUT_MS=2500,TELEMETRY_QUEUE_HIGH_WATER_MARK=500,TELEMETRY_QUEUE_HIGH_RETRY_THRESHOLD=TELEMETRY_MAX_QUEUE_ENTRY_ATTEMPTS-3,TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS=["x-pm-telemetry-max-schema-version","x-pm-telemetry-max-version"];function isAdvisoryHealthWarning(warning,requireMergeDrivers=!1){return warning.startsWith("agent_guidance_")||warning.startsWith("telemetry_")||warning.startsWith("history_stream_over_compact_threshold:")||warning.startsWith("integrity_legacy_hierarchy_")||warning.startsWith("stale_in_progress_items:")||warning.startsWith("provenance_resolver_zero_success:")||warning.startsWith("provenance_value_domain_invalid:")||!requireMergeDrivers&&warning.startsWith("merge_driver_configuration_missing:")}function warningCode(value){const normalized=value.trim(),separator=normalized.indexOf(":");return separator===-1?normalized:normalized.slice(0,separator)}function normalizeExtensionNameForMatch(value){return value.trim().toLowerCase()}function isExpectedUnmanagedExtension(name,directory){const normalizedName=normalizeExtensionNameForMatch(name),normalizedDirectory=normalizeExtensionNameForMatch(directory);return normalizedName.startsWith("builtin-")?!0:normalizedDirectory==="beads"||normalizedDirectory==="todos"}async function isDirectory(targetPath){try{return(await fs.stat(targetPath)).isDirectory()}catch{return!1}}async function listStoredItemDocumentIds(pmRoot){const directories=(await fs.readdir(pmRoot,{withFileTypes:!0})).filter(entry=>entry.isDirectory()),filesByDirectory=await Promise.all(directories.map(entry=>fs.readdir(path.join(pmRoot,entry.name),{withFileTypes:!0})));return new Set(filesByDirectory.flatMap(entries=>entries.filter(entry=>entry.isFile()).filter(entry=>ITEM_FILE_EXTENSIONS.some(extension=>entry.name.endsWith(extension))).map(entry=>path.parse(entry.name).name)))}async function inspectHistoryStream(streamPath,itemId,compactPolicy,liveItemIds){const warnings=await runActiveOnReadHooks({path:streamPath,scope:"project"}),needsClassification=itemId!=="_workspace"&&!liveItemIds.has(itemId);if(!compactPolicy.enabled&&!needsClassification)return{warnings,overThreshold:!1,classification:"live"};let raw;try{raw=await fs.readFile(streamPath,"utf8")}catch{return{warnings,overThreshold:!1,classification:"unreadable"}}let classification="live";if(needsClassification){const lastLine=raw.split(/\r?\n/).map(line=>line.trim()).filter(Boolean).at(-1);try{classification=JSON.parse(lastLine??"{}").op==="delete"?"tombstone":"orphaned"}catch{classification="orphaned"}}const entryCount=compactPolicy.enabled?raw.split(/\r?\n/).filter(line=>line.trim().length>0).length:0;return{warnings,overThreshold:compactPolicy.enabled&&entryCount>compactPolicy.max_entries,classification}}async function countHistoryStreams(pmRoot,compactPolicy,liveItemIds){const historyDir=path.join(pmRoot,"history");if(!await isDirectory(historyDir))return{count:0,warnings:[],over_threshold:[],max_entries:null,tombstones:[],orphaned:[]};const historyFiles=(await fs.readdir(historyDir)).filter(entry=>entry.endsWith(".jsonl")).sort((left,right)=>left.localeCompare(right)),maxEntries=compactPolicy.enabled?compactPolicy.max_entries:null,warnings=[],overThreshold=[],tombstones=[],orphaned=[];for(const fileName of historyFiles){const streamPath=path.join(historyDir,fileName),itemId=fileName.slice(0,-6),inspection=await inspectHistoryStream(streamPath,itemId,compactPolicy,liveItemIds);warnings.push(...inspection.warnings),inspection.classification==="tombstone"&&tombstones.push(itemId),inspection.classification==="orphaned"&&orphaned.push(itemId),inspection.overThreshold&&overThreshold.push(itemId)}return{count:historyFiles.length,warnings:[...warnings,...overThreshold.map(id=>`history_stream_over_compact_threshold:${id}`),...orphaned.map(id=>`history_orphaned_stream:${id}`)],over_threshold:overThreshold,max_entries:maxEntries,tombstones,orphaned}}function normalizeRelativePath(pmRoot,targetPath){return path.relative(pmRoot,targetPath).replaceAll("\\","/")}async function listItemDocumentPaths(pmRoot,typeToFolder){const folders=[...new Set(Object.values(typeToFolder))].sort((left,right)=>left.localeCompare(right)),itemPaths=[];for(const folder of folders){const directoryPath=path.join(pmRoot,folder);let entries=[];try{entries=await fs.readdir(directoryPath)}catch(error){if(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))continue;continue}for(const entry of entries)ITEM_FILE_EXTENSIONS.some(extension=>entry.toLowerCase().endsWith(extension))&&itemPaths.push(path.join(directoryPath,entry))}return itemPaths.sort((left,right)=>normalizeRelativePath(pmRoot,left).localeCompare(normalizeRelativePath(pmRoot,right))),itemPaths}function shouldReportHistoryDirectoryUnreadable(error){return!(typeof error=="object"&&error!==null&&"code"in error&&isFileMissingError(error))}async function scanItemIntegrity(pmRoot,typeToFolder,schema){const paths=await listItemDocumentPaths(pmRoot,typeToFolder),unreadable=[],conflictMarkers=[],parseFailures=[],formatVersions=[],extensionFieldNames=collectRegisteredItemFieldNames(getActiveExtensionRegistrations());for(const itemPath of paths){const relativePath=normalizeRelativePath(pmRoot,itemPath);let raw;try{raw=await fs.readFile(itemPath,"utf8")}catch{unreadable.push(relativePath);continue}const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){conflictMarkers.push({path:relativePath,...conflictMarker});continue}try{const parsed=parseItemDocument(raw,{format:getItemFormatFromPath(itemPath),schema,extensionFieldNames});formatVersions.push({ref:relativePath,version:effectiveItemFormatVersion(parsed.metadata)})}catch{parseFailures.push(relativePath)}}return{paths,unreadable,conflictMarkers,parseFailures,formatVersions}}async function scanHistoryIntegrity(pmRoot){const historyDir=path.join(pmRoot,"history"),unreadable=[],conflictMarkers=[],invalidJson=[],mergeReceiptReferences=[],mergeReceiptDispositions=[];let files=[];try{files=(await fs.readdir(historyDir)).filter(entry=>entry.endsWith(".jsonl")).sort((left,right)=>left.localeCompare(right))}catch(error){shouldReportHistoryDirectoryUnreadable(error)&&unreadable.push("history")}for(const fileName of files){const itemId=fileName.slice(0,-6);let raw;try{raw=await fs.readFile(path.join(historyDir,fileName),"utf8")}catch{unreadable.push(itemId);continue}const conflictMarker=findFirstMergeConflictMarker(raw);if(conflictMarker){conflictMarkers.push({id:itemId,...conflictMarker});continue}raw.split(/\r?\n/).forEach((line,index)=>{if(line.trim())try{const parsed=JSON.parse(line);mergeReceiptReferences.push(...extractHistoryMergeReceiptReferences(parsed,itemId).map(reference=>({itemId,line:index+1,...reference}))),mergeReceiptDispositions.push(...extractHistoryMergeReceiptDispositions(parsed,itemId,index+1))}catch{invalidJson.push({id:itemId,line:index+1})}})}return{files,unreadable,conflictMarkers,invalidJson,mergeReceiptReferences,mergeReceiptDispositions}}function buildHierarchyIntegrityWarnings(analysis){return[...analysis.cycles.map(cycle=>cycle.legacy_terminal?`integrity_legacy_hierarchy_cycle:${cycle.item_ids.join(",")}`:`integrity_hierarchy_cycle:${cycle.item_ids.join(",")}`),...analysis.cardinality_violations.map(finding=>finding.legacy_terminal?`integrity_legacy_hierarchy_cardinality:${finding.child_id}`:`integrity_hierarchy_cardinality:${finding.child_id}`),...analysis.divergences.map(finding=>finding.legacy_terminal?`integrity_legacy_hierarchy_divergence:${finding.child_id}`:`integrity_hierarchy_divergence:${finding.child_id}`)]}async function buildIntegrityCheck(pmRoot,typeToFolder,schema,settings,requireMergeDrivers,items){const itemScan=await scanItemIntegrity(pmRoot,typeToFolder,schema),historyScan=await scanHistoryIntegrity(pmRoot),formatVersionScan=scanItemFormatVersions(itemScan.formatVersions,CURRENT_ITEM_FORMAT_VERSION,BASELINE_ITEM_FORMAT_VERSION),statusRegistry=resolveRuntimeStatusRegistry(schema),hierarchyIntegrity=analyzeHierarchyIntegrity(items,status=>isTerminalStatus(status,statusRegistry),resolveWorkspaceRelationshipKindRegistry()),trackedRuntimeCache=await scanTrackedRuntimeCache(pmRoot),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),mergeFence=await resolveMergeFenceHealthEvidence(pmRoot,gitWorkspaceRoot,settings,requireMergeDrivers),mergeDriverAudit=gitWorkspaceRoot===null?null:await auditMergeDriverConfiguration(gitWorkspaceRoot),mergeReceiptEvidence=await inspectMergeReceiptEvidence(gitWorkspaceRoot??pmRoot,{includeReconciled:!0,includeLossless:!0,pmRoot}),availableMergeReceiptIds=new Set(mergeReceiptEvidence.durable_receipt_ids),historyReceiptClassification=classifyHistoryMergeReceiptReferences(historyScan.mergeReceiptReferences,availableMergeReceiptIds,historyScan.mergeReceiptDispositions),missingMergeReceiptReferences=historyReceiptClassification.missing,pendingMergeReceipts=mergeReceiptEvidence.receipts.filter(receipt=>receipt.state==="pending"),{pendingDecisions:pendingMergeDecisions,lossless:losslessMergeReceipts}=partitionMergeReceipts(pendingMergeReceipts),mergeDriverInstallationMissing=mergeDriverAudit?.status==="missing"&&mergeDriverAudit.missing_keys.length===5&&mergeDriverAudit.drifted_keys.length===0,warnings=[...itemScan.unreadable.map(entry=>`integrity_item_unreadable:${entry}`),...itemScan.conflictMarkers.map(entry=>`integrity_item_conflict_marker:${entry.path}:L${entry.line}`),...itemScan.parseFailures.map(entry=>`integrity_item_parse_failed:${entry}`),...historyScan.unreadable.map(entry=>`integrity_history_unreadable:${entry}`),...historyScan.conflictMarkers.map(entry=>`integrity_history_conflict_marker:${entry.id}:L${entry.line}`),...historyScan.invalidJson.map(entry=>`integrity_history_invalid_json:${entry.id}:L${entry.line}`),...formatVersionScan.outdated.map(entry=>`integrity_item_outdated_format_version:${entry}`),...formatVersionScan.ahead.map(entry=>`integrity_item_ahead_format_version:${entry}`),...buildHierarchyIntegrityWarnings(hierarchyIntegrity),...trackedRuntimeCache.tracked_path_count>0?[`tracked_runtime_cache_files:${trackedRuntimeCache.tracked_path_count}`]:[],...mergeDriverAudit!==null&&mergeDriverAudit.status!=="ok"?[`merge_driver_configuration_${mergeDriverInstallationMissing?"missing":"drift"}:${mergeDriverAudit.missing_keys.length+mergeDriverAudit.drifted_keys.length}`]:[],...mergeFence.warnings,...pendingMergeDecisions.length>0?[`merge_decisions_unreviewed:${pendingMergeDecisions.length}`]:[],...losslessMergeReceipts.length>0?[`merge_receipts_pending:${losslessMergeReceipts.length}`]:[],...buildMergeReceiptEvidenceWarnings({invalidEvidenceCount:mergeReceiptEvidence.invalid_evidence_count,missingHistoryReferenceCount:missingMergeReceiptReferences.length})],normalizedWarnings=[...new Set(warnings)].sort((left,right)=>left.localeCompare(right)),actionableWarnings=normalizedWarnings.filter(warning=>!warning.startsWith("integrity_legacy_hierarchy_"));return{check:{name:"integrity",status:actionableWarnings.length===0?"ok":"warn",ok:actionableWarnings.length===0,details:{checked_item_files:itemScan.paths.length,checked_history_streams:historyScan.files.length,counts:{item_unreadable:itemScan.unreadable.length,item_conflict_markers:itemScan.conflictMarkers.length,item_parse_failures:itemScan.parseFailures.length,history_unreadable:historyScan.unreadable.length,history_conflict_markers:historyScan.conflictMarkers.length,history_invalid_json:historyScan.invalidJson.length,item_outdated_format_version:formatVersionScan.outdated.length,item_ahead_format_version:formatVersionScan.ahead.length,hierarchy_cycles:hierarchyIntegrity.cycles.length,hierarchy_cardinality_violations:hierarchyIntegrity.cardinality_violations.length,hierarchy_parent_divergences:hierarchyIntegrity.divergences.length,tracked_runtime_cache_files:trackedRuntimeCache.tracked_path_count,merge_driver_configuration:mergeDriverAudit===null||mergeDriverAudit.status==="ok"?0:mergeDriverAudit.missing_keys.length+mergeDriverAudit.drifted_keys.length,merge_fence:mergeFence.count,pending_merge_decisions:pendingMergeDecisions.length,lossless_merge_receipts:losslessMergeReceipts.length,invalid_merge_receipt_evidence:mergeReceiptEvidence.invalid_evidence_count,missing_merge_receipt_history_references:missingMergeReceiptReferences.length,accepted_legacy_merge_receipt_references:historyReceiptClassification.acceptedLegacyCount,accepted_missing_merge_receipt_dispositions:historyReceiptClassification.acceptedDispositionCount},item_unreadable:itemScan.unreadable,item_conflict_markers:itemScan.conflictMarkers,item_parse_failures:itemScan.parseFailures,item_outdated_format_version:formatVersionScan.outdated,item_ahead_format_version:formatVersionScan.ahead,hierarchy_integrity:{...hierarchyIntegrity,relations:summarizeRecordList(hierarchyIntegrity.relations,BRIEF_HEALTH_DETAIL_LIMIT)},history_unreadable:historyScan.unreadable,history_conflict_markers:historyScan.conflictMarkers,history_invalid_json:historyScan.invalidJson,tracked_runtime_cache:{tracker_relative_root:trackedRuntimeCache.tracker_relative_root,tracked_paths:trackedRuntimeCache.tracked_paths,tracked_path_count:trackedRuntimeCache.tracked_path_count,remediation_command:trackedRuntimeCache.remediation_command},merge_driver_configuration:mergeDriverAudit===null?null:{...mergeDriverAudit,required:requireMergeDrivers},merge_fence:mergeFence.audit,pending_merge_decision_items:[...new Set(pendingMergeDecisions.map(receipt=>receipt.item_id))].sort((left,right)=>left.localeCompare(right)),lossless_merge_receipt_items:[...new Set(losslessMergeReceipts.map(receipt=>receipt.item_id))].sort((left,right)=>left.localeCompare(right)),invalid_merge_receipt_evidence_details:mergeReceiptEvidence.invalid_evidence,invalid_merge_receipt_evidence_details_truncated:mergeReceiptEvidence.invalid_evidence_truncated,missing_merge_receipt_history_reference_details:missingMergeReceiptReferences.slice(0,HEALTH_WARNING_LIMIT),missing_merge_receipt_history_reference_details_truncated:missingMergeReceiptReferences.length>HEALTH_WARNING_LIMIT}},warnings:normalizedWarnings,gitWorkspaceRoot,pendingMergeReceipts}}function hasActivateExport(moduleRecord){if(typeof moduleRecord.activate=="function")return!0;const defaultExport=moduleRecord.default;return typeof defaultExport!="object"||defaultExport===null?!1:typeof defaultExport.activate=="function"}function summarizeLoadedExtension(extension){const summary={layer:extension.layer,directory:extension.directory,manifest_path:extension.manifest_path,name:extension.name,version:extension.version,entry:extension.entry,priority:extension.priority,entry_path:extension.entry_path,has_activate:hasActivateExport(extension.module)};return Array.isArray(extension.capabilities)&&(summary.capabilities=[...extension.capabilities]),summary}function resolveMigrationId(definition,fallbackIndex){return toNonEmptyStringOrUndefined(definition.id)||`migration-${String(fallbackIndex+1).padStart(3,"0")}`}function resolveMigrationStatus(definition){const normalized=toNonEmptyStringOrUndefined(definition.status)?.toLowerCase();return normalized==="failed"?"failed":normalized==="applied"?"applied":"pending"}function resolveMigrationFailureReason(definition){return toNonEmptyStringOrUndefined(definition.reason)??toNonEmptyStringOrUndefined(definition.error)??toNonEmptyStringOrUndefined(definition.message)}function compareMigrationEntries(left,right){const byLayer=(left.layer??"").localeCompare(right.layer??"");if(byLayer!==0)return byLayer;const byName=(left.name??"").localeCompare(right.name??"");return byName!==0?byName:(left.id??"").localeCompare(right.id??"")}function summarizeMigrationStatuses(migrations){const applied=[],pending=[],failed=[];migrations.forEach((migration,index)=>{const id=resolveMigrationId(migration.definition,index),status=resolveMigrationStatus(migration.definition);if(status==="applied"){applied.push({layer:migration.layer,name:migration.name,id,status});return}if(status==="failed"){failed.push({layer:migration.layer,name:migration.name,id,status,reason:resolveMigrationFailureReason(migration.definition)});return}pending.push({layer:migration.layer,name:migration.name,id,status})}),applied.sort(compareMigrationEntries),pending.sort(compareMigrationEntries),failed.sort(compareMigrationEntries);const warnings=[...failed.map(entry=>`extension_migration_failed:${entry.layer}:${entry.name}:${entry.id}`),...pending.map(entry=>`extension_migration_pending:${entry.layer}:${entry.name}:${entry.id}`)];return{summary:{applied,pending,failed,applied_count:applied.length,pending_count:pending.length,failed_count:failed.length},warnings}}function buildExtensionHealthTriageSummary(warnings,loadFailureCount,activationFailureCount,migrationStatus,managedStateWarningCount,managedExtensionEntriesCount,unmanagedLoadedExtensions,unmanagedExpectedExtensions,unmanagedActionRequiredExtensions){const normalizedWarnings=[...new Set(warnings)].sort((left,right)=>left.localeCompare(right)),warningCodes=[...new Set(normalizedWarnings.map(value=>warningCode(value)))].sort((left,right)=>left.localeCompare(right)),unknownCapabilityCount=normalizedWarnings.filter(warning=>warning.startsWith("extension_capability_unknown:")).length,updateHealthPartial=unmanagedActionRequiredExtensions.length>0,updateHealthCoverage=updateHealthPartial?"partial":"full",remediation=[],registrationCollisionRemediation=buildRegistrationCollisionRemediation(normalizedWarnings,{deactivate:"pm extension --deactivate <name> --project/--global",doctor:"pm extension --doctor --project/--global --detail deep --trace"});return registrationCollisionRemediation&&remediation.push(registrationCollisionRemediation),loadFailureCount>0&&remediation.push("Run pm extension --explore --project and pm extension --explore --global to inspect load failures."),activationFailureCount>0&&remediation.push("Review checks[name=extensions].details.activation.failed in pm health --json for activation error details."),(migrationStatus.failed_count>0||migrationStatus.pending_count>0)&&remediation.push("Resolve pending/failed extension migrations before write commands; use --force only when policy allows."),managedStateWarningCount>0&&remediation.push("Run pm extension --manage --project and pm extension --manage --global to refresh managed-state diagnostics."),unknownCapabilityCount>0&&remediation.push(`Unknown extension capabilities detected. Allowed capabilities: ${KNOWN_EXTENSION_CAPABILITIES.join(", ")}. Review extension_capability_unknown warning details for suggested replacements.`),normalizedWarnings.some(warning=>warning.startsWith("extension_capability_legacy_alias:"))&&remediation.push("Legacy extension capability aliases were auto-remapped to canonical capabilities. Update manifests to canonical names (migration/validation -> schema)."),normalizedWarnings.some(warning=>warning.startsWith("extension_command_definition_legacy_handler_alias:"))&&remediation.push("Extension command definitions using legacy handler were auto-remapped. Update command definitions to use run: (context) => ... for forward compatibility."),updateHealthPartial?remediation.push("Update-check coverage is partial because unmanaged extensions need adoption. Adopt existing installs via pm extension --manage --project/--global --fix-managed-state, pm extension --adopt-all --project/--global, or pm extension --adopt <name>."):unmanagedLoadedExtensions.length>0&&remediation.push("Loaded unmanaged extensions are currently treated as informational. Use pm extension --manage --project/--global --fix-managed-state to adopt them for update checks."),remediation.length===0&&remediation.push("No immediate action required. Re-run pm health after extension configuration changes."),{status:normalizedWarnings.length===0?"ok":"warn",warning_count:normalizedWarnings.length,warning_codes:warningCodes,load_failure_count:loadFailureCount,activation_failure_count:activationFailureCount,migration_failed_count:migrationStatus.failed_count,migration_pending_count:migrationStatus.pending_count,managed_state_warning_count:managedStateWarningCount,managed_extension_entries_count:managedExtensionEntriesCount,unmanaged_loaded_extension_count:unmanagedLoadedExtensions.length,unmanaged_loaded_extensions:unmanagedLoadedExtensions,unmanaged_expected_extension_count:unmanagedExpectedExtensions.length,unmanaged_expected_extensions:unmanagedExpectedExtensions,unmanaged_action_required_extension_count:unmanagedActionRequiredExtensions.length,unmanaged_action_required_extensions:unmanagedActionRequiredExtensions,update_health_coverage:updateHealthCoverage,update_health_partial:updateHealthPartial,unknown_capability_count:unknownCapabilityCount,top_warnings:normalizedWarnings.slice(0,8),remediation}}async function buildExtensionCheck(pmRoot,settings,noExtensionsFlag){const authorManifest=await inspectExtensionAuthorManifest(resolveWorkspaceRoot(pmRoot)),loadResult=await loadExtensions({pmRoot,settings,cwd:process.cwd(),noExtensions:noExtensionsFlag}),loadedSummaries=loadResult.loaded.map(extension=>summarizeLoadedExtension(extension)),hostVersionCensus=await scanExtensionHostVersions(loadResult.loaded,resolveWorkspaceRoot(pmRoot)),activationResult=await activateExtensions({...loadResult,loaded:loadResult.loaded}),[projectManagedState,globalManagedState]=await Promise.all([readManagedExtensionState(loadResult.roots.project),readManagedExtensionState(loadResult.roots.global)]);await applyStoredExtensionMigrationState(pmRoot,activationResult.registrations.migrations);const migrationStatus=summarizeMigrationStatuses(activationResult.registrations.migrations),activationDetails={failed:activationResult.failed,warnings:activationResult.warnings,hook_counts:activationResult.hook_counts,command_override_count:activationResult.command_override_count,command_handler_count:activationResult.command_handler_count,parser_override_count:activationResult.parser_override_count,preflight_override_count:activationResult.preflight_override_count,service_override_count:activationResult.service_override_count,renderer_override_count:activationResult.renderer_override_count,registration_counts:activationResult.registration_counts,registrations:activationResult.registrations,migration_status:migrationStatus.summary,managed_extensions:{project:{path:projectManagedState.path,count:projectManagedState.state.entries.length,entries:projectManagedState.state.entries},global:{path:globalManagedState.path,count:globalManagedState.state.entries.length,entries:globalManagedState.state.entries}}},managedProjectNames=new Set(projectManagedState.state.entries.map(entry=>normalizeExtensionNameForMatch(entry.name))),managedGlobalNames=new Set(globalManagedState.state.entries.map(entry=>normalizeExtensionNameForMatch(entry.name))),unmanagedLoadedEntries=[...new Map(loadResult.loaded.filter(entry=>!(entry.layer==="project"?managedProjectNames:managedGlobalNames).has(normalizeExtensionNameForMatch(entry.name))).map(entry=>[`${entry.layer}:${entry.name}`,{layer:entry.layer,name:entry.name,directory:entry.directory}])).values()].sort((left,right)=>{const leftKey=`${left.layer}:${left.name}`,rightKey=`${right.layer}:${right.name}`;return leftKey.localeCompare(rightKey)}),unmanagedLoadedExtensions=unmanagedLoadedEntries.map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),unmanagedExpectedExtensions=unmanagedLoadedEntries.filter(entry=>isExpectedUnmanagedExtension(entry.name,entry.directory)).map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),unmanagedActionRequiredExtensions=unmanagedLoadedEntries.filter(entry=>!isExpectedUnmanagedExtension(entry.name,entry.directory)).map(entry=>`${entry.layer}:${entry.name}`).sort((left,right)=>left.localeCompare(right)),updateCoverageWarnings=unmanagedActionRequiredExtensions.length>0?[`extension_update_health_partial_coverage:skipped_unmanaged:${unmanagedActionRequiredExtensions.length}`]:[],extensionWarnings=[...authorManifest.warnings,...loadResult.warnings,...activationDetails.warnings,...migrationStatus.warnings,...projectManagedState.warnings,...globalManagedState.warnings,...updateCoverageWarnings,...hostVersionCensus.warnings],capabilityGuidance=collectUnknownCapabilityGuidance(extensionWarnings),capabilityContract=buildCapabilityContractMetadata(),extensionTriage=buildExtensionHealthTriageSummary(extensionWarnings,loadResult.failed.length,activationResult.failed.length,migrationStatus.summary,projectManagedState.warnings.length+globalManagedState.warnings.length,projectManagedState.state.entries.length+globalManagedState.state.entries.length,unmanagedLoadedExtensions,unmanagedExpectedExtensions,unmanagedActionRequiredExtensions);return{check:{name:"extensions",status:extensionWarnings.length===0?"ok":"warn",ok:extensionWarnings.length===0,details:{...loadResult,loaded:loadedSummaries,warnings:extensionWarnings,activation:activationDetails,author_manifest:authorManifest,triage:extensionTriage,...migrationStatus.summary.pending_count>0?{remediation_map:{extension_migration_pending:"pm extension migrate --project"}}:{},capability_contract:capabilityContract,capability_guidance:capabilityGuidance,host_version_census:hostVersionCensus}},warnings:extensionWarnings}}function selectStaleItemDetail(values,verboseStaleItems){if(verboseStaleItems)return{values,truncated:!1,total:values.length};const summary=summarizeList(values,STALE_VECTORIZATION_SUMMARY_LIMIT);return{values:summary.values,truncated:summary.truncated,total:values.length}}function telemetryEnvFlagEnabled(envKey){const value=(process.env[envKey]??"").trim().toLowerCase();return value==="1"||value==="true"||value==="yes"||value==="on"}function telemetrySourceContextOverride(){const value=(process.env.PM_TELEMETRY_SOURCE_CONTEXT??"").trim().toLowerCase();return PM_TELEMETRY_SOURCE_CONTEXT_SET.has(value)?value:null}function normalizeEndpointForDisplay(rawEndpoint){const trimmed=rawEndpoint.trim();if(trimmed.length===0)return"";try{const parsed=new URL(trimmed);return parsed.username="",parsed.password="",parsed.search="",parsed.hash="",parsed.toString()}catch{return trimmed}}function parseTelemetryQueue(raw){let validEntries=0,invalidRows=0,totalRows=0,highRetryEntries=0,maxAttempts=0;for(const line of raw.split(`
|
|
2
2
|
`)){const trimmed=line.trim();if(trimmed.length!==0){totalRows+=1;try{const parsed=JSON.parse(trimmed);if(typeof parsed=="object"&&parsed!==null&&typeof parsed.attempts=="number"&&Number.isFinite(parsed.attempts)){const attempts=Math.max(0,Math.trunc(parsed.attempts));validEntries+=1,maxAttempts=Math.max(maxAttempts,attempts),attempts>=TELEMETRY_QUEUE_HIGH_RETRY_THRESHOLD&&(highRetryEntries+=1)}else invalidRows+=1}catch{invalidRows+=1}}}return{validEntries,invalidRows,totalRows,highRetryEntries,maxAttempts}}function emptyTelemetryQueueSummary(){return{validEntries:0,invalidRows:0,totalRows:0,highRetryEntries:0,maxAttempts:0}}function parseTelemetryRuntimeState(stateRaw){if(!stateRaw||stateRaw.trim().length===0)return{runtimeState:{},stateParseFailed:!1};try{const parsed=JSON.parse(stateRaw);return typeof parsed=="object"&&parsed!==null&&!Array.isArray(parsed)?{runtimeState:parsed,stateParseFailed:!1}:{runtimeState:{},stateParseFailed:!0}}catch{return{runtimeState:{},stateParseFailed:!0}}}async function maybeProbeTelemetryEndpoint(settings,checkTelemetry){const endpoint=settings.telemetry.endpoint.trim();return!checkTelemetry||!settings.telemetry.enabled||endpoint.length===0||resolveTelemetryEnvironmentPolicy().telemetry_disabled?void 0:{attempted:!0,...await probeTelemetryEndpointHealth(endpoint)}}function collectTelemetryQueueWarnings(settings,queueSummary,runtimeState){const warnings=[];if(queueSummary.invalidRows>0&&warnings.push(`telemetry_queue_invalid_rows:${queueSummary.invalidRows}`),!settings.telemetry.enabled||queueSummary.validEntries===0)return warnings;const lastSuccess=runtimeState.last_successful_flush_at,lastFailure=runtimeState.last_failed_flush_at,activeFailure=lastFailure&&(!lastSuccess||lastFailure>lastSuccess),neverFlushed=!lastSuccess,highWater=queueSummary.validEntries>=TELEMETRY_QUEUE_HIGH_WATER_MARK;return(activeFailure||neverFlushed||highWater)&&warnings.push(`telemetry_queue_pending:${queueSummary.validEntries}`),queueSummary.highRetryEntries>0&&warnings.push(`telemetry_queue_high_retries:${queueSummary.highRetryEntries}`),warnings}function collectTelemetryEndpointWarnings(endpointProbe){return!endpointProbe||endpointProbe.ok!==!1?[]:typeof endpointProbe.status=="number"?[`telemetry_endpoint_probe_http_status:${endpointProbe.status}`]:["telemetry_endpoint_probe_failed"]}function collectTelemetrySchemaWarnings(endpointProbe){const parsedMaxSchemaVersion=Number.parseInt(endpointProbe?.max_schema_version??"",10);return endpointProbe?.ok===!0&&Number.isInteger(parsedMaxSchemaVersion)&&parsedMaxSchemaVersion>TELEMETRY_SCHEMA_VERSION?[`telemetry_schema_version_behind:${parsedMaxSchemaVersion}`]:[]}function collectTelemetryOtelWarnings(settings,runtimeState){const pendingOtelSpans=typeof runtimeState.pending_otel_spans=="number"?runtimeState.pending_otel_spans:0,lastOtelSuccess=runtimeState.last_otel_success_at,lastOtelFailure=runtimeState.last_otel_failure_at;return settings.telemetry.enabled&&pendingOtelSpans>0&&lastOtelFailure&&(!lastOtelSuccess||lastOtelFailure>=lastOtelSuccess)?[`telemetry_otel_export_failing:${pendingOtelSpans}`]:[]}function buildTelemetryEnvOverrideDetails(){return{...resolveTelemetryEnvironmentPolicy(),pm_no_telemetry:telemetryEnvFlagEnabled("PM_NO_TELEMETRY"),telemetry_otel_disabled:telemetryEnvFlagEnabled("PM_TELEMETRY_OTEL_DISABLED"),telemetry_inline_flush:telemetryEnvFlagEnabled("PM_TELEMETRY_INLINE_FLUSH"),telemetry_source_context:telemetrySourceContextOverride()}}function telemetryQueueHasEntries(settings,queueSummary){return settings.telemetry.enabled&&queueSummary.validEntries>0}function isTelemetryQueueDraining(queueHasEntries,warnings){return queueHasEntries&&warnings.every(warning=>!warning.startsWith("telemetry_queue_pending:")&&!warning.startsWith("telemetry_queue_high_retries:"))}async function probeTelemetryEndpointHealth(endpoint){let probeUrl=endpoint;try{const parsed=new URL(endpoint);parsed.pathname="/healthz",parsed.search="",parsed.hash="",probeUrl=parsed.toString()}catch{}try{const response=await fetch(probeUrl,{method:"GET",signal:AbortSignal.timeout(TELEMETRY_ENDPOINT_PROBE_TIMEOUT_MS)});return{probe_url:normalizeEndpointForDisplay(probeUrl),ok:response.ok,status:response.status,max_schema_version:(()=>{for(const headerName of TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS){const value=response.headers.get(headerName)?.trim();if(value&&value.length>0)return value}})()}}catch(error){return{probe_url:normalizeEndpointForDisplay(probeUrl),ok:!1,error:error instanceof Error?error.message:"probe_failed"}}}async function buildTelemetryCheck(settings,options){const globalPmRoot=resolveGlobalPmRoot(process.cwd()),queuePath=path.join(globalPmRoot,TELEMETRY_QUEUE_RELATIVE_PATH),statePath=path.join(globalPmRoot,TELEMETRY_STATE_RELATIVE_PATH),queueRaw=await readFileIfExists(queuePath),queueExists=queueRaw!==null,queueSizeBytes=queueRaw?Buffer.byteLength(queueRaw,"utf8"):0,queueSummary=queueRaw?parseTelemetryQueue(queueRaw):emptyTelemetryQueueSummary(),stateRaw=await readFileIfExists(statePath),{runtimeState,stateParseFailed}=parseTelemetryRuntimeState(stateRaw),endpoint=settings.telemetry.endpoint.trim(),endpointDisplay=normalizeEndpointForDisplay(endpoint),endpointProbe=await maybeProbeTelemetryEndpoint(settings,options.checkTelemetry),warnings=[];stateParseFailed&&warnings.push("telemetry_state_invalid_json"),warnings.push(...collectTelemetryQueueWarnings(settings,queueSummary,runtimeState),...collectTelemetryEndpointWarnings(endpointProbe),...collectTelemetrySchemaWarnings(endpointProbe),...collectTelemetryOtelWarnings(settings,runtimeState));const queueHasEntries=telemetryQueueHasEntries(settings,queueSummary),pendingOtelSpans=typeof runtimeState.pending_otel_spans=="number"?runtimeState.pending_otel_spans:0;return{check:{name:"telemetry",status:warnings.length===0?"ok":"warn",ok:warnings.length===0,details:{enabled:settings.telemetry.enabled,capture_level:settings.telemetry.capture_level,endpoint:endpointDisplay,global_pm_root:globalPmRoot,queue_path:queuePath,queue_exists:queueExists,queue_entries:queueSummary.validEntries,queue_draining:isTelemetryQueueDraining(queueHasEntries,warnings),queue_invalid_rows:queueSummary.invalidRows,queue_rows_total:queueSummary.totalRows,queue_size_bytes:queueSizeBytes,queue_high_retry_entries:queueSummary.highRetryEntries,queue_high_retry_threshold:TELEMETRY_QUEUE_HIGH_RETRY_THRESHOLD,queue_max_attempts:queueSummary.maxAttempts,runtime_state_path:statePath,last_attempted_flush_at:runtimeState.last_attempted_flush_at??null,last_successful_flush_at:runtimeState.last_successful_flush_at??null,last_failed_flush_at:runtimeState.last_failed_flush_at??null,last_failed_flush_error:runtimeState.last_failed_flush_error??null,pending_otel_spans:pendingOtelSpans,last_otel_attempt_at:runtimeState.last_otel_attempt_at??null,last_otel_success_at:runtimeState.last_otel_success_at??null,last_otel_failure_at:runtimeState.last_otel_failure_at??null,last_otel_failure_error:runtimeState.last_otel_failure_error??null,endpoint_probe:endpointProbe??{attempted:!1},env_overrides:buildTelemetryEnvOverrideDetails()}},warnings}}async function buildLocksCheck(pmRoot){let scan;try{scan=await scanLockHealth(pmRoot)}catch(error){const message=error instanceof Error?error.message:String(error);return{check:{name:"locks",status:"warn",ok:!1,details:{active_lock_count:0,stale_lock_count:0,unreadable_lock_count:0,unparseable_lock_count:0,scan_failed:!0,error:message,pm_root:pmRoot}},warnings:[`locks_scan_failed:${message}`]}}const warnings=[];return scan.stale_lock_count>0&&warnings.push(`locks_stale_count:${scan.stale_lock_count}`),scan.unreadable_lock_count>0&&warnings.push(`locks_unreadable:${scan.unreadable_lock_count}`),{check:{name:"locks",status:warnings.length===0?"ok":"warn",ok:warnings.length===0,details:{active_lock_count:scan.active_lock_count,stale_lock_count:scan.stale_lock_count,unreadable_lock_count:scan.unreadable_lock_count,unparseable_lock_count:scan.unparseable_lock_count}},warnings}}async function buildHistoryDriftCheck(params){const cacheHitVerification="metadata";let items=params.items,drift=await scanHistoryDrift(params.pmRoot,items,{cacheHitVerification});const cacheConfirmationCandidates=[...new Set([...drift.hashMismatches,...drift.chainMismatches,...drift.versionSkews])].sort((left,right)=>left.localeCompare(right));let confirmedItems=[],resolvedFalsePositiveItems=[];if(cacheConfirmationCandidates.length>0){items=await listAllItemMetadataWithBody(params.pmRoot,params.settings.item_format,params.typeRegistry.type_to_folder,params.itemReadWarnings,params.settings.schema,{forceSourceScan:!0}),drift=await scanHistoryDrift(params.pmRoot,items),confirmedItems=[...new Set([...drift.hashMismatches,...drift.chainMismatches,...drift.versionSkews])].sort((left,right)=>left.localeCompare(right));const confirmedSet=new Set(confirmedItems);resolvedFalsePositiveItems=cacheConfirmationCandidates.filter(id=>!confirmedSet.has(id))}const{missingStreams,unreadableStreams,hashMismatches,chainMismatches,versionSkews,driftedItems,workspaceStateMismatches,workspaceStateMissing,workspaceStateUnreadable}=drift,warnings=[...missingStreams.map(id=>`history_drift_missing_stream:${id}`),...unreadableStreams.map(id=>`history_drift_unreadable_stream:${id}`),...hashMismatches.map(id=>`history_drift_hash_mismatch:${id}`),...chainMismatches.map(id=>`history_drift_chain_mismatch:${id}`),...versionSkews.map(id=>`history_drift_version_skew:${id}`),...workspaceStateMismatches.map(documentPath=>`history_drift_workspace_state_mismatch:${documentPath}`),...workspaceStateMissing.map(documentPath=>`history_drift_workspace_state_missing:${documentPath}`),...workspaceStateUnreadable.map(documentPath=>`history_drift_workspace_state_unreadable:${documentPath}`)];return{check:{name:"history_drift",status:warnings.length===0?"ok":"warn",ok:warnings.length===0,details:{checked_items:items.length,cache_hit_verification:cacheConfirmationCandidates.length>0?"metadata_then_content_hash":cacheHitVerification,cache_confirmation:{triggered:cacheConfirmationCandidates.length>0,reason:cacheConfirmationCandidates.length>0?"cached_corruption_candidate":null,candidate_items:cacheConfirmationCandidates,confirmed_items:confirmedItems,resolved_false_positive_items:resolvedFalsePositiveItems,authoritative_item_source:cacheConfirmationCandidates.length>0},drifted_items:driftedItems,counts:{drifted:driftedItems.length,missing_streams:missingStreams.length,unreadable_streams:unreadableStreams.length,hash_mismatches:hashMismatches.length,chain_mismatches:chainMismatches.length,version_skews:versionSkews.length,workspace_state_mismatches:workspaceStateMismatches.length,workspace_state_missing:workspaceStateMissing.length,workspace_state_unreadable:workspaceStateUnreadable.length},missing_streams:missingStreams,unreadable_streams:unreadableStreams,hash_mismatches:hashMismatches,chain_mismatches:chainMismatches,version_skews:versionSkews,workspace_state_mismatches:workspaceStateMismatches,workspace_state_missing:workspaceStateMissing,workspace_state_unreadable:workspaceStateUnreadable}},warnings}}function buildVectorizationRuntimeSnapshot(settings,pmRoot){const runtimeDefaults=resolveSettingsWithSemanticRuntimeDefaults(settings),providerResolution=resolveEmbeddingProviders(runtimeDefaults.settings),vectorStoreResolution=resolveVectorStores(runtimeDefaults.settings,pmRoot),runtimeEmbeddingIdentity=providerResolution.active?buildVectorizationEmbeddingIdentity(providerResolution.active.name,providerResolution.active.model):null;return{runtimeDefaults,providerResolution,vectorStoreResolution,runtimeEmbeddingIdentity,semanticRuntimeAvailable:!!(providerResolution.active&&vectorStoreResolution.active)}}function emptyVectorizationRefreshResult(){return{refreshed:[],skipped:[],warnings:[]}}async function refreshStaleVectorizationItems(params){return!params.refreshPolicy.enabled||!params.snapshot.semanticRuntimeAvailable||params.staleBefore.length===0?emptyVectorizationRefreshResult():refreshSemanticEmbeddingsForMutatedItems(params.pmRoot,params.staleBefore,{settings:params.snapshot.runtimeDefaults.settings,apply_runtime_defaults:!1})}function vectorizationEmbeddingIdentityChanged(params){return params.strictVectorizationWarnings&¶ms.semanticRuntimeAvailable&&!!(params.ledgerBefore.embedding&¶ms.runtimeEmbeddingIdentity&&hasVectorizationEmbeddingIdentityChanged(params.ledgerBefore.embedding,params.runtimeEmbeddingIdentity))}function collectVectorizationWarnings(params){const warningSet=new Set([...params.ledgerBefore.warnings,...params.ledgerAfter.warnings]);if(params.strictVectorizationWarnings)for(const warning of params.refreshResult.warnings)warningSet.add(warning);return params.embeddingIdentityChanged&&warningSet.add("vectorization_embedding_identity_changed"),params.strictVectorizationWarnings&¶ms.semanticRuntimeAvailable&¶ms.staleAfter.length>0&&warningSet.add(`vectorization_stale_items_remaining:${params.staleAfter.length}`),[...warningSet].sort((left,right)=>left.localeCompare(right))}function resolveVectorizationRefreshSkippedReason(refreshPolicy,semanticRuntimeAvailable,staleBefore){return refreshPolicy.enabled&&semanticRuntimeAvailable&&staleBefore.length>0?null:refreshPolicy.enabled?semanticRuntimeAvailable?"no_stale_items":"semantic_runtime_unavailable":"refresh_disabled"}function buildVectorizationProviderDetails(settings,snapshot){const activeProvider=snapshot.providerResolution.active?.name??null,activeVectorStore=snapshot.vectorStoreResolution.active?.name??null;return{provider_active:activeProvider,provider_configured:typeof settings.search?.provider=="string"?settings.search.provider:null,provider_source:resolveProviderConfigSource(activeProvider,settings.search?.provider??null),vector_store_active:activeVectorStore,vector_store_configured:typeof settings.vector_store?.adapter=="string"?settings.vector_store.adapter:null,vector_store_source:resolveProviderConfigSource(activeVectorStore,settings.vector_store?.adapter??null)}}function buildVectorizationRefreshPolicyDetails(refreshPolicy){return{enabled:refreshPolicy.enabled,check_only:refreshPolicy.checkOnly,no_refresh:refreshPolicy.noRefresh,refresh_vectors:refreshPolicy.refreshVectors}}function vectorizationRefreshAttempted(refreshPolicy,semanticRuntimeAvailable,staleBefore){return refreshPolicy.enabled&&staleBefore.length>0&&semanticRuntimeAvailable}async function buildVectorizationCheck(pmRoot,settings,items,refreshPolicy,verboseStaleItems){const snapshot=buildVectorizationRuntimeSnapshot(settings,pmRoot),ledgerBefore=await readVectorizationStatusLedger(pmRoot),staleBefore=snapshot.semanticRuntimeAvailable?collectStaleVectorizationIds(items,ledgerBefore.entries):[],refreshResult=await refreshStaleVectorizationItems({pmRoot,snapshot,refreshPolicy,staleBefore}),ledgerAfter=await readVectorizationStatusLedger(pmRoot),staleAfter=snapshot.semanticRuntimeAvailable?collectStaleVectorizationIds(items,ledgerAfter.entries):[],strictVectorizationWarnings=!snapshot.runtimeDefaults.auto_ollama_defaults_applied,embeddingIdentityChanged=vectorizationEmbeddingIdentityChanged({strictVectorizationWarnings,semanticRuntimeAvailable:snapshot.semanticRuntimeAvailable,ledgerBefore,runtimeEmbeddingIdentity:snapshot.runtimeEmbeddingIdentity}),warnings=collectVectorizationWarnings({ledgerBefore,ledgerAfter,refreshResult,strictVectorizationWarnings,semanticRuntimeAvailable:snapshot.semanticRuntimeAvailable,embeddingIdentityChanged,staleAfter}),staleBeforeDetail=selectStaleItemDetail(staleBefore,verboseStaleItems),staleAfterDetail=selectStaleItemDetail(staleAfter,verboseStaleItems),refreshAttempted=vectorizationRefreshAttempted(refreshPolicy,snapshot.semanticRuntimeAvailable,staleBefore);return{check:{name:"vectorization",status:warnings.length===0?"ok":"warn",ok:warnings.length===0,details:{semantic_runtime_available:snapshot.semanticRuntimeAvailable,compatibility_mode_auto_defaults:snapshot.runtimeDefaults.auto_ollama_defaults_applied,auto_ollama_defaults_applied:snapshot.runtimeDefaults.auto_ollama_defaults_applied,auto_ollama_defaults_skipped_reason:snapshot.runtimeDefaults.auto_ollama_defaults_skipped_reason??null,auto_ollama_defaults_remediation:snapshot.runtimeDefaults.auto_ollama_defaults_remediation??null,refresh_policy:buildVectorizationRefreshPolicyDetails(refreshPolicy),...buildVectorizationProviderDetails(settings,snapshot),embedding_identity_changed:embeddingIdentityChanged,embedding_identity_before:ledgerBefore.embedding??null,embedding_identity_runtime:snapshot.runtimeEmbeddingIdentity??null,items:items.length,ledger_entries_before:Object.keys(ledgerBefore.entries).length,stale_items_detail_mode:verboseStaleItems?"full":"summary",stale_items_summary_limit:STALE_VECTORIZATION_SUMMARY_LIMIT,stale_items_before_total:staleBeforeDetail.total,stale_items_before:staleBeforeDetail.values,stale_items_before_truncated:staleBeforeDetail.truncated,refresh_attempted:refreshAttempted,refresh_skipped_reason:resolveVectorizationRefreshSkippedReason(refreshPolicy,snapshot.semanticRuntimeAvailable,staleBefore),refresh_result:refreshResult,ledger_entries_after:Object.keys(ledgerAfter.entries).length,stale_items_after_total:staleAfterDetail.total,stale_items_after:staleAfterDetail.values,stale_items_after_truncated:staleAfterDetail.truncated}},warnings}}function validateSettingsValues(settings){const warnings=[];return settings.id_prefix.trim().length===0&&warnings.push("settings:id_prefix_empty"),settings.locks.ttl_seconds<=0&&warnings.push("settings:locks_ttl_non_positive"),warnings}function resolveVectorRefreshPolicy(options){const checkOnly=options.checkOnly===!0,refreshVectors=options.refreshVectors===!0;if(refreshVectors&&checkOnly)throw new PmCliError("--check-only cannot be combined with --refresh-vectors",EXIT_CODE.USAGE);if(refreshVectors&&options.noRefresh===!0)throw new PmCliError("--no-refresh cannot be combined with --refresh-vectors",EXIT_CODE.USAGE);return{enabled:refreshVectors,checkOnly,noRefresh:!refreshVectors,refreshVectors}}const HEALTH_CLONE_OPTIONAL_DIRECTORIES=new Set(["extensions","locks","search"]);function resolveHealthDirectoryLists(typeRegistry){const requiredDirSet=new Set(PM_CORE_REQUIRED_SUBDIRS.filter(entry=>entry.length>0)),optionalDirSet=new Set,builtinItemFolderSet=new Set(Object.values(TYPE_TO_FOLDER));for(const folder of typeRegistry.folders)requiredDirSet.has(folder)&&!builtinItemFolderSet.has(folder)||(requiredDirSet.delete(folder),optionalDirSet.add(folder));for(const folder of HEALTH_CLONE_OPTIONAL_DIRECTORIES)requiredDirSet.delete(folder),optionalDirSet.add(folder);return{requiredDirs:[...requiredDirSet].sort((left,right)=>left.localeCompare(right)),optionalDirs:[...optionalDirSet].sort((left,right)=>left.localeCompare(right)),optionalDirSet}}async function scanHealthDirectories(pmRoot,typeRegistry,strictDirectories){const{requiredDirs,optionalDirs,optionalDirSet}=resolveHealthDirectoryLists(typeRegistry),missingRequiredDirs=[],missingOptionalDirs=[],hookWarnings=[];for(const relativeDir of[...requiredDirs,...optionalDirs]){const directoryPath=path.join(pmRoot,relativeDir);hookWarnings.push(...await runActiveOnReadHooks({path:directoryPath,scope:"project"})),!await isDirectory(directoryPath)&&(optionalDirSet.has(relativeDir)?missingOptionalDirs.push(relativeDir):missingRequiredDirs.push(relativeDir))}return{requiredDirs,optionalDirs,missingRequiredDirs,missingOptionalDirs,missingDirs:strictDirectories?[...missingRequiredDirs,...missingOptionalDirs]:[...missingRequiredDirs],hookWarnings}}function resolveHealthSkipPolicy(options){const checkOnlyDefaultSummary=options.checkOnly===!0&&options.brief!==!0&&options.summary!==!0&&options.full!==!0,summaryMode=(options.summary===!0||checkOnlyDefaultSummary)&&options.full!==!0,fastProjectionCheckOnly=options.checkOnly===!0&&(options.brief===!0||options.summary===!0)&&options.full!==!0;return{summaryMode,skipIntegrity:(options.skipIntegrity===!0||fastProjectionCheckOnly)&&options.full!==!0,skipDrift:(options.skipDrift===!0||fastProjectionCheckOnly)&&options.full!==!0,skipVectors:(options.skipVectors===!0||fastProjectionCheckOnly)&&options.full!==!0}}async function readHealthItems(params){return params.skipPolicy.skipDrift&¶ms.skipPolicy.skipVectors?listAllItemMetadataLight(params.pmRoot,params.settings.item_format,params.typeRegistry.type_to_folder,params.itemReadWarnings,params.settings.schema):listAllItemMetadataWithBody(params.pmRoot,params.settings.item_format,params.typeRegistry.type_to_folder,params.itemReadWarnings,params.settings.schema)}function buildSkippedHealthCheck(name){return{check:{name,status:"ok",ok:!0,details:{skipped:!0}},warnings:[]}}function buildSettingsHealthCheck(settingsPath,settings,normalizedSettingsReadWarnings){return{name:"settings",status:normalizedSettingsReadWarnings.length===0?"ok":"warn",ok:normalizedSettingsReadWarnings.length===0,details:{path:settingsPath,version:settings.version,id_prefix:settings.id_prefix,locks_ttl_seconds:settings.locks.ttl_seconds,warnings:normalizedSettingsReadWarnings}}}function buildDirectoriesHealthCheck(directoryState,strictDirectories){return{name:"directories",status:directoryState.missingDirs.length===0?"ok":"warn",ok:directoryState.missingDirs.length===0,details:{required:directoryState.requiredDirs,optional:directoryState.optionalDirs,missing_required:directoryState.missingRequiredDirs,missing_optional:directoryState.missingOptionalDirs,missing:directoryState.missingDirs,strict_directories:strictDirectories}}}function buildSettingsValuesHealthCheck(settingWarnings,statusRoleDiagnostics){return{name:"settings_values",status:settingWarnings.length===0?"ok":"warn",ok:settingWarnings.length===0,details:{warnings:settingWarnings,...statusRoleDiagnostics.roleless_status_count>0?{lifecycle_status_roles:statusRoleDiagnostics}:{}}}}const resolveUnknownAuthorEventCount=authorAttribution=>authorAttribution.unknown_event_count??0,resolveActionableUnknownAuthorEventCount=authorAttribution=>authorAttribution.actionable_unknown_event_count??resolveUnknownAuthorEventCount(authorAttribution);function buildStorageHealthCheck(items,settings,historySummary,authorAttribution,staleInProgress,provenance){const unknownAuthorEventCount=resolveUnknownAuthorEventCount(authorAttribution),actionableUnknownAuthorEventCount=resolveActionableUnknownAuthorEventCount(authorAttribution),hasAuthorEvents=unknownAuthorEventCount>0,hasActionableAuthorWarnings=actionableUnknownAuthorEventCount>0,hasStaleInProgressItems=staleInProgress.count>0;return{name:"storage",status:historySummary.over_threshold.length===0&&historySummary.orphaned.length===0&&!hasActionableAuthorWarnings&&!hasStaleInProgressItems?"ok":"warn",ok:historySummary.over_threshold.length===0&&historySummary.orphaned.length===0&&!hasActionableAuthorWarnings&&!hasStaleInProgressItems,details:{items:items.length,history_streams:historySummary.count,tombstones:{retention_policy:"retain_append_only",gc_enabled:!1,count:historySummary.tombstones.length,ids:historySummary.tombstones.slice(0,25),truncated:historySummary.tombstones.length>25},orphaned_history_streams:{count:historySummary.orphaned.length,ids:historySummary.orphaned.slice(0,25),truncated:historySummary.orphaned.length>25},...hasAuthorEvents?{author_attribution:authorAttribution}:{},...hasStaleInProgressItems?{stale_in_progress:staleInProgress}:{},...provenance.outcomes.length>0?{provenance_resolver_outcomes:provenance.outcomes}:{},...provenance.invalid_values.length>0?{provenance_invalid_values:provenance.invalid_values}:{},provenance_sample:provenance.sample,...historySummary.max_entries!==null?{compact_policy:{enabled:settings.history.compact_policy.enabled,max_entries:historySummary.max_entries,trigger:settings.history.compact_policy.trigger,over_threshold_count:historySummary.over_threshold.length,over_threshold:historySummary.over_threshold}}:{}}}}function buildStaleInProgressHealthSummary(scan){const warnings=scan.count===0?[]:[`stale_in_progress_items:${scan.count}`];return{scan,warnings}}function collectHealthWarnings(params){const warnings=[...params.directoryState.missingDirs.map(dir=>`missing_directory:${dir}`),...params.normalizedSettingsReadWarnings,...params.settingWarnings,...params.normalizedItemReadWarnings,...params.telemetryCheck.warnings,...params.extensionCheck.warnings,...params.historyPolicyWarnings,...params.historySummary.warnings,...params.authorAttributionWarnings,...params.staleInProgressWarnings,...params.provenanceWarnings,...params.locksCheck.warnings,...params.integrityCheck.warnings,...params.historyDriftCheck.warnings,...params.vectorizationCheck.warnings,...params.directoryState.hookWarnings];return[...new Set(warnings)]}function extractHistoryDriftedCount(historyDriftCheck){const counts=historyDriftCheck.check.details.counts;return typeof counts?.drifted=="number"?counts.drifted:0}function buildHealthRemediationSources(params){return{settings:params.normalizedSettingsReadWarnings,directories:params.directoryState.missingDirs.map(dir=>`missing_directory:${dir}`).concat(params.directoryState.hookWarnings),settings_values:params.settingWarnings,telemetry:params.telemetryCheck.warnings,extensions:params.extensionCheck.warnings,storage:[...params.historyPolicyWarnings,...params.historySummary.warnings,...params.authorAttributionWarnings,...params.staleInProgressWarnings,...params.provenanceWarnings],locks:params.locksCheck.warnings,integrity:[...params.normalizedItemReadWarnings,...params.integrityCheck.warnings],history_drift:params.historyDriftCheck.warnings,vectorization:params.vectorizationCheck.warnings}}function buildHealthFindings(params){const owners=new Map;for(const[check,warnings]of Object.entries(params.remediationSources))for(const warning of warnings)owners.has(warning)||owners.set(warning,check);const checksByName=new Map(params.checks.map(check=>[check.name,check]));return params.warnings.map(warning=>{const check=owners.get(warning)??"integrity",code=resolveRemediation(warning)?.code??warningCode(warning),details=checksByName.get(check)?.details,remediationMap=typeof details?.remediation_map=="object"&&details.remediation_map!==null?details.remediation_map:{},remediation=code==="agent_guidance_outdated"?"pm init --agent-guidance add":code==="agent_guidance_unreadable"?"Check AGENTS.md and CLAUDE.md read permissions and file types, then retry pm health.":remediationMap[code],severity=isAdvisoryHealthWarning(warning,params.requireMergeDrivers)?"advisory":"gate_failing";return{warning,code,check,severity,...typeof remediation=="string"?{remediation}:severity==="gate_failing"?{disposition:"no_safe_automatic_remediation"}:{}}})}function rewriteBulkHealthRemediation(params){if(params.check.name==="history_drift"&¶ms.historyDriftedCount>1)for(const code of Object.keys(params.remediationMap))code!=="history_drift_merge_receipt"&&code!=="history_drift_version_skew"&&(params.remediationMap[code]="pm history-repair --all");if(params.check.name==="storage"&¶ms.overThresholdCount>1)for(const code of Object.keys(params.remediationMap))params.remediationMap[code]="pm history-compact --all-streams"}function attachHealthRemediationMaps(params){for(const check of params.checks){const remediationMap=buildRemediationMap(params.remediationSources[check.name]);rewriteBulkHealthRemediation({check,remediationMap,historyDriftedCount:params.historyDriftedCount,overThresholdCount:params.overThresholdCount}),Object.keys(remediationMap).length>0&&(check.details={...check.details,remediation_map:remediationMap})}}async function correlateMergeReceiptHistoryDriftItems(params){if(params.gitWorkspaceRoot===null||params.pendingMergeReceipts.length===0)return[];const details=params.historyDriftCheck.check.details,hashMismatches=Array.isArray(details.hash_mismatches)?details.hash_mismatches.filter(entry=>typeof entry=="string"):[],unsafeStreamIds=new Set([details.missing_streams,details.unreadable_streams,details.chain_mismatches].flatMap(value=>Array.isArray(value)?value:[])),receiptsByItem=new Map;for(const receipt of params.pendingMergeReceipts){const receipts=receiptsByItem.get(receipt.item_id)??[];receipts.push(receipt),receiptsByItem.set(receipt.item_id,receipts)}const attributed=[];for(const id of hashMismatches){if(unsafeStreamIds.has(id))continue;const receipts=receiptsByItem.get(id);if(!(!receipts||receipts.length===0))try{await runHistoryRepair(id,{dryRun:!0,mergeReceiptProof:{gitWorkspaceRoot:params.gitWorkspaceRoot,receipts}},params.global),attributed.push(id)}catch{}}return[...new Set(attributed)].sort((left,right)=>left.localeCompare(right))}function applyMergeReceiptHistoryDriftAttribution(historyDriftCheck,attributedItems){const attributed=new Set(attributedItems);historyDriftCheck.warnings=[...historyDriftCheck.warnings.filter(warning=>warning.startsWith("history_drift_hash_mismatch:")?!attributed.has(warning.slice(warning.indexOf(":")+1)):!0),...attributedItems.map(id=>`history_drift_merge_receipt:${id}`)].sort((left,right)=>left.localeCompare(right)),historyDriftCheck.check.details={...historyDriftCheck.check.details,merge_receipt_attributed_items:attributedItems}}async function runHealth(global2,options={}){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settingsPath=getSettingsPath(pmRoot),{settings,warnings:settingsReadWarnings}=await readSettingsWithMetadata(pmRoot),normalizedSettingsReadWarnings=[...new Set(settingsReadWarnings)],typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),strictDirectories=options.strictDirectories===!0,strictExit=options.strictExit===!0||options.failOnWarn===!0,requireMergeDrivers=options.requireMergeDrivers===!0||strictExit,refreshPolicy=resolveVectorRefreshPolicy(options),directoryState=await scanHealthDirectories(pmRoot,typeRegistry,strictDirectories),settingsValueWarnings=validateSettingsValues(settings),telemetryCheck=await buildTelemetryCheck(settings,{checkTelemetry:options.checkTelemetry===!0}),extensionCheck=await buildExtensionCheck(pmRoot,settings,!!global2.noExtensions),skipPolicy=resolveHealthSkipPolicy(options),itemReadWarnings=[],items=await readHealthItems({pmRoot,settings,typeRegistry,skipPolicy,itemReadWarnings}),itemsWithBody=items,statusRoleDiagnostics=inspectStatusRoleAssignments(resolveRuntimeStatusRegistry(settings.schema),items),settingWarnings=[...(await runInitAgentGuidance({pm_root:pmRoot,cwd:process.cwd(),mode:"status",interactive:!1,settings}).catch(()=>({warnings:["agent_guidance_unreadable:AGENTS.md,CLAUDE.md"]}))).warnings.filter(warning=>warning.startsWith("agent_guidance_")),...settingsValueWarnings,...buildStatusRoleWarnings(statusRoleDiagnostics)],historyPolicy=skipPolicy.skipDrift?{warnings:[]}:await enforceHistoryStreamPolicyForItems({pmRoot,settings,itemIds:items.map(item=>item.id),commandLabel:"health"}),historySummary=await countHistoryStreams(pmRoot,settings.history.compact_policy,await listStoredItemDocumentIds(pmRoot)),authorAttribution=await scanHistoryAuthorAttribution(pmRoot,20,options.verboseAuthorEvents===!0),actionableUnknownAuthorEventCount=resolveActionableUnknownAuthorEventCount(authorAttribution),authorAttributionWarnings=actionableUnknownAuthorEventCount>0?[`history_unknown_author_events:${actionableUnknownAuthorEventCount}`]:[],staleInProgress=buildStaleInProgressHealthSummary(await scanStaleInProgressItems(pmRoot,items,{in_progress_status:settings.schema.workflow.in_progress_status,threshold_hours:settings.mutation_guard.stale_in_progress_hours})),provenanceResolverHealth=await scanProvenanceResolverHealth(pmRoot),provenanceWarnings=provenanceResolverHealth.warnings,locksCheck=await buildLocksCheck(pmRoot),integrityCheck=skipPolicy.skipIntegrity&&!requireMergeDrivers?{...buildSkippedHealthCheck("integrity"),gitWorkspaceRoot:null,pendingMergeReceipts:[]}:await buildIntegrityCheck(pmRoot,typeRegistry.type_to_folder,settings.schema,settings,requireMergeDrivers,items),historyDriftCheck=skipPolicy.skipDrift?buildSkippedHealthCheck("history_drift"):await buildHistoryDriftCheck({pmRoot,items:itemsWithBody,settings,typeRegistry,itemReadWarnings}),normalizedItemReadWarnings=[...new Set(itemReadWarnings)],mergeReceiptAttributedHistoryDriftItems=await correlateMergeReceiptHistoryDriftItems({gitWorkspaceRoot:integrityCheck.gitWorkspaceRoot,pendingMergeReceipts:integrityCheck.pendingMergeReceipts,historyDriftCheck,global:global2});applyMergeReceiptHistoryDriftAttribution(historyDriftCheck,mergeReceiptAttributedHistoryDriftItems);const vectorizationCheck=skipPolicy.skipVectors?buildSkippedHealthCheck("vectorization"):await buildVectorizationCheck(pmRoot,settings,itemsWithBody,refreshPolicy,options.verboseStaleItems===!0),checks=[buildSettingsHealthCheck(settingsPath,settings,normalizedSettingsReadWarnings),buildDirectoriesHealthCheck(directoryState,strictDirectories),buildSettingsValuesHealthCheck(settingWarnings,statusRoleDiagnostics),telemetryCheck.check,extensionCheck.check,buildStorageHealthCheck(items,settings,historySummary,authorAttribution,staleInProgress.scan,provenanceResolverHealth),locksCheck.check,integrityCheck.check,historyDriftCheck.check,vectorizationCheck.check],normalizedWarnings=collectHealthWarnings({directoryState,normalizedSettingsReadWarnings,settingWarnings,normalizedItemReadWarnings,telemetryCheck,extensionCheck,historyPolicyWarnings:historyPolicy.warnings,historySummary,authorAttributionWarnings,staleInProgressWarnings:staleInProgress.warnings,provenanceWarnings,locksCheck,integrityCheck,historyDriftCheck,vectorizationCheck}),remediationSources=buildHealthRemediationSources({directoryState,normalizedSettingsReadWarnings,settingWarnings,normalizedItemReadWarnings,telemetryCheck,extensionCheck,historyPolicyWarnings:historyPolicy.warnings,historySummary,authorAttributionWarnings,staleInProgressWarnings:staleInProgress.warnings,provenanceWarnings,locksCheck,integrityCheck,historyDriftCheck,vectorizationCheck});attachHealthRemediationMaps({checks,remediationSources,historyDriftedCount:extractHistoryDriftedCount(historyDriftCheck),overThresholdCount:historySummary.over_threshold.length});const blockingWarnings=normalizedWarnings.filter(warning=>!isAdvisoryHealthWarning(warning,requireMergeDrivers)),findings=buildHealthFindings({warnings:normalizedWarnings,checks,remediationSources,requireMergeDrivers}),failingChecks=new Set(findings.filter(finding=>finding.severity==="gate_failing").map(finding=>finding.check)),warningChecks=new Set(findings.map(finding=>finding.check));for(const check of checks)check.ok=!failingChecks.has(check.name),warningChecks.has(check.name)&&(check.status="warn");const result={ok:blockingWarnings.length===0,verdict:{authority:"ok",...strictExit?{strict_exit:!0}:{},...requireMergeDrivers?{require_merge_drivers:!0}:{},exit_code:strictExit&&blockingWarnings.length>0?EXIT_CODE.GENERIC_FAILURE:0},checks,warnings:normalizedWarnings,findings,failed_because:blockingWarnings,generated_at:nowIso()};return projectHealthResult(result,options,skipPolicy.summaryMode)}const _testOnlyHealthCommand={buildHierarchyIntegrityWarnings,buildExtensionHealthTriageSummary,buildHealthFindings,buildCapabilityContractMetadata,buildStaleInProgressHealthSummary,buildVectorizationProviderDetails,collectUnknownCapabilityGuidance,isAdvisoryHealthWarning,isDirectory,isExpectedUnmanagedExtension,listItemDocumentPaths,normalizeEndpointForDisplay,normalizeExtensionNameForMatch,parseTelemetryQueue,probeTelemetryEndpointHealth,projectHealthResult,resolveActionableUnknownAuthorEventCount,resolveHealthSkipPolicy,resolveUnknownAuthorEventCount,resolveVectorRefreshPolicy,selectStaleItemDetail,summarizeHealthCheckDetails,summarizeExtensionList,summarizeRecordList,summarizeStringList,telemetryEnvFlagEnabled,warningCode};export{_testOnlyHealthCommand,runHealth};
|
|
3
3
|
//# sourceMappingURL=health.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="42f8db8f-d659-58e9-84af-21ced6d6a403")}catch{}})();import{salvageHistoryTail}from"./history/salvage.js";import{resolveHistoryHashAlgorithm}from"../core/history/digest.js";import{realpath}from"node:fs/promises";import path from"node:path";import jsonPatch from"fast-json-patch";import{patchPathToChangedField}from"../core/history/history-diff.js";import{normalizeReplayPatchOps,reanchorHistoryEntries,replayHashVerificationCandidates,resolveHistoryRepairItemHashVersion,toReplayDocument,verifyHistoryChainWithVersion}from"../core/history/replay.js";import{sealHistoryRecord}from"../core/history/history.js";import{classifyHistoryEvent}from"../core/history/event-classification.js";import{scanHistoryDrift}from"../core/history/drift-scan.js";import{invalidateHistoryDriftCache}from"../core/history/drift-cache.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{sha256Hex,stableStringify}from"../core/shared/serialization.js";import{resolveAuthor}from"../core/shared/author.js";import{nowIso}from"../core/shared/time.js";import{listAllItemMetadataWithBody}from"../core/store/item-store.js";import{resolveHistoryWorkspace,resolveHistorySubject}from"./history/subject.js";import{runHistoryMaintenance}from"./history/maintenance.js";import{isOriginalGitStateRestored}from"./merge/receipt-operation.js";import{inspectMergeReceiptEvidence,summarizeMergeReceipt}from"./merge/receipts.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue}from"./merge/three-way.js";import{listInvalidProvenanceHistoryStreamIds,normalizeInvalidHistoryProvenance}from"./governance/provenance-health.js";function replayFieldValue(document,field){return field==="body"?document.body:document.metadata[field]}function isDiagnosticCollectionRow(field,value){if(value===null||typeof value!="object"||Array.isArray(value))return!1;const row=value;return field==="comments"?typeof row.created_at=="string"&&typeof row.author=="string"&&typeof row.text=="string":typeof row.path=="string"||field==="tests"&&typeof row.command=="string"}function diagnosticLinkedRecord(value){const result={...value};if(result.source_kind==="merge_union"&&(result.source_kind="local_mutation"),result.provenance!==null&&typeof result.provenance=="object"&&!Array.isArray(result.provenance)){const provenance={...result.provenance};provenance.source_kind==="merge_union"&&(provenance.source_kind="local_mutation"),result.provenance=provenance}return result}function diagnosticCollectionValue(field,value){if(!Array.isArray(value)||!["comments","docs","tests"].includes(field)||!value.every(row=>isDiagnosticCollectionRow(field,row)))return value;const rows=value.map(row=>field==="comments"?row:diagnosticLinkedRecord(row));return[...new Map(rows.map(row=>[stableStringify(row),row])).values()]}function isReplayValuePreserved(replayed,reconciled){if(replayed===void 0||Object.is(replayed,reconciled))return!0;if(Array.isArray(replayed)){if(!Array.isArray(reconciled))return!1;const matched=new Set;return replayed.every(expected=>{const index=reconciled.findIndex((candidate,candidateIndex)=>!matched.has(candidateIndex)&&isReplayValuePreserved(expected,candidate));return index<0?!1:(matched.add(index),!0)})}if(replayed===null||reconciled===null||typeof replayed!="object"||typeof reconciled!="object"||Array.isArray(reconciled))return!1;const reconciledRecord=reconciled;return Object.entries(replayed).every(([key,value])=>Object.hasOwn(reconciledRecord,key)&&isReplayValuePreserved(value,reconciledRecord[key]))}function collectReconciliationEvents(entries,fields){const remaining=new Set(fields),events=[];for(let index=entries.length-1;index>=0&&remaining.size>0;index-=1){const entry=entries[index],entryFields=new Set;for(const op of normalizeReplayPatchOps(entry.patch))entryFields.add(patchPathToChangedField(op.path)),op.from&&entryFields.add(patchPathToChangedField(op.from));const overlap=[...remaining].filter(field=>entryFields.has(field));if(overlap.length!==0){for(const field of overlap)remaining.delete(field);events.push({index:index+1,ts:entry.ts,author:entry.author,op:entry.op,fields:overlap.sort((left,right)=>left.localeCompare(right))})}}return events.reverse()}function analyzeReconciliationDiscard(entries,finalReplay,currentItemReplay){const reconcilePatch=jsonPatch.compare(finalReplay,currentItemReplay);if(reconcilePatch.length===0)return;const reconciledFields=new Set;for(const op of reconcilePatch)reconciledFields.add(patchPathToChangedField(op.path));const preservedFields=new Set([...reconciledFields].filter(field=>isReplayValuePreserved(diagnosticCollectionValue(field,replayFieldValue(finalReplay,field)),diagnosticCollectionValue(field,replayFieldValue(currentItemReplay,field))))),revertedFields=new Set([...reconciledFields].filter(field=>!preservedFields.has(field))),discardedEvents=collectReconciliationEvents(entries,revertedFields),preservedEvents=collectReconciliationEvents(entries,preservedFields),sortedReconciledFields=[...reconciledFields].sort((left,right)=>left.localeCompare(right)),sortedRevertedFields=[...revertedFields].sort((left,right)=>left.localeCompare(right)),sortedPreservedFields=[...preservedFields].sort((left,right)=>left.localeCompare(right)),discardedAuthors=[...new Set(discardedEvents.map(event=>event.author))].sort((left,right)=>left.localeCompare(right)),preservedAuthors=[...new Set(preservedEvents.map(event=>event.author))].sort((left,right)=>left.localeCompare(right));return{reconciled_field_count:reconciledFields.size,reconciled_fields:sortedReconciledFields,reverted_field_count:revertedFields.size,reverted_fields:sortedRevertedFields,discarded_events:discardedEvents,discarded_authors:discardedAuthors,preserved_field_count:preservedFields.size,preserved_fields:sortedPreservedFields,preserved_events:preservedEvents,preserved_authors:preservedAuthors,recovery_hint:discardedEvents.length>0?"Reconciliation keeps the on-disk item and overwrites these replayed mutations. To recover one, inspect it with pm history <id> --diff --field <field>, then re-apply the lost value with pm update.":null}}async function canonicalRealPath(value){try{return await realpath(value)}catch{return null}}async function verifyReceiptAgainstSnapshot(params){if(await canonicalRealPath(path.resolve(params.gitWorkspaceRoot,params.receipt.item_path))!==params.currentPath)return null;const declaredFields=[...new Set([...params.receipt.fields_from_theirs,...params.receipt.union_fields,...params.receipt.decisions.map(decision=>decision.field)])].sort(),hashes=params.receipt.merged_field_hashes;if(!hashes||declaredFields.length===0)return null;const hashFields=Object.keys(hashes).sort();if(declaredFields.length!==hashFields.length||declaredFields.some((field,index)=>field!==hashFields[index]))return null;const currentMetadata=params.currentItemReplay.metadata;return declaredFields.every(field=>{const value=field==="body"?params.currentItemReplay.body:currentMetadata[field];return[hashItemScalarDecisionValue(value),sha256Hex(stableStringify(encodeItemScalarDecisionValue(value)))].includes(hashes[field])})?{receipt:params.receipt,declaredFields}:null}async function verifyMergeReceiptProof(params){if(params.proof.gitWorkspaceRoot===null)return{trusted:!1,reason:"git_workspace_unavailable",receipt_ids:[]};if(params.itemPath===null||params.currentItemReplay===null)return{trusted:!1,reason:"item_path_unavailable",receipt_ids:[]};const itemReceipts=params.proof.receipts.filter(receipt=>receipt.item_id===params.id);if(itemReceipts.length===0)return{trusted:!1,reason:"no_item_receipts",receipt_ids:[]};const currentPath=await realpath(params.itemPath),trustedReceipts=[];for(const receipt of itemReceipts){const trusted=await verifyReceiptAgainstSnapshot({receipt,gitWorkspaceRoot:params.proof.gitWorkspaceRoot,currentPath,currentItemReplay:params.currentItemReplay});trusted&&trustedReceipts.push(trusted)}const trustedFields=new Set(trustedReceipts.flatMap(({declaredFields})=>declaredFields)),reconciliationFields=params.reconciliation?.reconciled_fields;return trustedReceipts.length>0&&(reconciliationFields===void 0||reconciliationFields.length>0&&reconciliationFields.every(field=>trustedFields.has(field)))?{trusted:!0,reason:"trusted_merge_driver_hash_evidence",receipt_ids:trustedReceipts.map(({receipt})=>receipt.id)}:{trusted:!1,reason:"no_receipt_set_proves_snapshot",receipt_ids:[]}}async function assertHistoryRepairAbandonmentProof(proof,context,chainOk,reanchor){if(proof===void 0)return;const{receipt,gitWorkspaceRoot}=proof,canonicalPath=context.currentItemPath===null?null:path.relative(gitWorkspaceRoot,context.currentItemPath).split(path.sep).join("/");if(!chainOk||reanchor.entriesRehashed!==0||reanchor.entriesPatchRepaired!==0||context.matchedChainBefore!==!0||context.currentItemRawBeforeLock===null||canonicalPath!==receipt.item_path||receipt.operation===void 0||!await isOriginalGitStateRestored(gitWorkspaceRoot,receipt.item_path,receipt.operation,context.currentItemRawBeforeLock))throw new PmCliError("Restored-origin receipt evidence no longer proves the exact clean item snapshot.",EXIT_CODE.CONFLICT,{code:"merge_reconcile_receipt_evidence_untrusted",recovery:{suggested_retry:"pm merge reconcile --dry-run"}})}async function resolveHistoryRepairMergeEvidence(params){const requestedReceiptIds=new Set(params.options.mergeReceiptProof?.receipts.filter(receipt=>receipt.item_id===params.subjectId).map(receipt=>receipt.id)??[]),subjectMergeReceipts=(params.options.mergeReceiptProof?await inspectMergeReceiptEvidence(params.options.mergeReceiptProof.gitWorkspaceRoot??process.cwd(),{includeReconciled:!0,includeLossless:!0,pmRoot:params.pmRoot}):void 0)?.receipts.filter(receipt=>receipt.item_id===params.subjectId&&requestedReceiptIds.has(receipt.id))??[],mergeReceiptProof=params.options.mergeReceiptProof?await verifyMergeReceiptProof({proof:{...params.options.mergeReceiptProof,receipts:subjectMergeReceipts},id:params.subjectId,itemPath:params.itemReplayContext.currentItemPath,currentItemReplay:params.itemReplayContext.currentItemReplay,reconciliation:params.reconciliation}):void 0;if(mergeReceiptProof&&!mergeReceiptProof.trusted&¶ms.options.force!==!0)throw new PmCliError(`Merge receipt evidence for ${params.subjectId} does not prove the exact item snapshot (${mergeReceiptProof.reason}).`,EXIT_CODE.CONFLICT,{code:"merge_reconcile_receipt_evidence_untrusted",required:"Use authoritative clone-local or durable receipt evidence whose canonical path, declared fields, and merged-value hashes match the current item, or review the mismatch and rerun with --force.",recovery:{suggested_retry:"pm merge reconcile --dry-run"}});const provenMergeReceipts=mergeReceiptProof?.trusted?subjectMergeReceipts.filter(receipt=>mergeReceiptProof.receipt_ids.includes(receipt.id)):[];return{mergeReceiptProof,effectiveAuditContext:provenMergeReceipts.length>0&¶ms.options.force!==!0?{...params.options.auditContext,merge:{receipts:provenMergeReceipts.map(summarizeMergeReceipt)}}:params.options.auditContext}}function describeHistoryRepairItemReplay(subject,snapshot,itemHashVersion){const currentItemPath=subject.located?.itemPath??null,loadedItem=snapshot.loadedItem,historyEntries=snapshot.entries;if(!loadedItem)return{currentItemReplay:null,currentItemPath,matchedChainBefore:null,currentItemRawBeforeLock:null,loadedItem:null};const currentItemReplay=toReplayDocument(loadedItem.document),lastOriginalAfterHash=historyEntries[historyEntries.length-1].after_hash;return{currentItemReplay,currentItemPath,matchedChainBefore:replayHashVerificationCandidates(currentItemReplay,itemHashVersion,resolveHistoryHashAlgorithm(historyEntries.at(-1)?.hash_algorithm)).includes(lastOriginalAfterHash),currentItemRawBeforeLock:loadedItem.raw,loadedItem}}function buildHistoryRepairMessage(params){return typeof params.message=="string"&¶ms.message.trim().length>0?params.message:`history-repair re-anchored ${params.entriesRehashed} entr${params.entriesRehashed===1?"y":"ies"}${params.entriesPatchRepaired>0?`, repaired ${params.entriesPatchRepaired} patch(es)`:""}${params.reconcileNeeded?", reconciled chain with on-disk item":""}.`}function buildHistoryRepairEntries(params){const rewrittenEntries=[...params.reanchorEntries];if(!params.changed)return{rewrittenEntries,auditEntryAdded:!1};const afterReplay=params.reconcileNeeded&¶ms.currentItemReplay?params.currentItemReplay:params.finalReplay,algorithm=resolveHistoryHashAlgorithm(params.reanchorEntries.at(-1)?.hash_algorithm),beforeHashes=replayHashVerificationCandidates(params.finalReplay,params.itemHashVersion,algorithm),previousAfterHash=params.reanchorEntries[params.reanchorEntries.length-1].after_hash,semanticIndex=beforeHashes.indexOf(previousAfterHash),afterHashes=replayHashVerificationCandidates(afterReplay,params.itemHashVersion,algorithm),auditEntry={hash_algorithm:algorithm,ts:nowIso(),author:params.author,op:params.auditOperation,patch:params.reconcileNeeded&¶ms.currentItemReplay?jsonPatch.compare(params.finalReplay,params.currentItemReplay):[],before_hash:beforeHashes[semanticIndex],after_hash:afterHashes[semanticIndex],message:params.message,...params.explicitItemHashVersion?{item_hash_version:params.itemHashVersion}:{},...params.auditContext===void 0?{}:{context:params.auditContext}};return rewrittenEntries.push(sealHistoryRecord({...auditEntry,event_class:classifyHistoryEvent(auditEntry)})),{rewrittenEntries,auditEntryAdded:!0}}function collectHistoryRepairWarnings(changed,skippedOps,reconciliation){const warnings=[];return changed||warnings.push("history_repair_no_changes"),skippedOps>0&&warnings.push(`history_repair_skipped_unresolvable_ops:${skippedOps}`),reconciliation&&reconciliation.discarded_events.length>0&&warnings.push(`history_repair_reconcile_discards_events:${reconciliation.discarded_events.length}`,`history_repair_discarded_authors:${reconciliation.discarded_authors.join(",")}`),warnings}async function runHistoryRepair(id,options,global2){const{pmRoot,settings,typeRegistry}=await resolveHistoryWorkspace(global2.path),subject=await resolveHistorySubject(pmRoot,id,settings,typeRegistry.type_to_folder);return options.salvageTail===!0?salvageHistoryTail({pmRoot,subject,settings,typeRegistry,options,author:resolveAuthor(options.author,settings.author_default)}):runHistoryMaintenance({pmRoot,subject,settings,typeRegistry,operation:"history-repair",options,transform:snapshot=>buildHistoryRepairPlan({pmRoot,subject,options},snapshot)})}function shouldAppendForcedHistoryAudit(options,verifiedHistoryEntries,auditContext){const alreadyRecorded=(options.mergeAbandonmentProof!==void 0||options.mergeReceiptProof!==void 0&&options.force!==!0)&&auditContext!==void 0&&verifiedHistoryEntries.some(entry=>entry.record_hash_version!==void 0&&entry.op==="merge_reconcile"&&stableStringify(entry.context)===stableStringify(auditContext));return options.forceAuditEntry===!0&&!alreadyRecorded}async function buildHistoryRepairPlan(params,snapshot){const{pmRoot,subject,options}=params,historyEntries=snapshot.entries,chainBefore=snapshot.verification,chainVersionBefore=verifyHistoryChainWithVersion(historyEntries),provenanceNormalization=options.normalizeProvenance?normalizeInvalidHistoryProvenance(historyEntries):{entries:historyEntries,receipt:{changed:!1,events_changed:0,observations_removed:0,invalid_values:[]}},itemHashVersion=resolveHistoryRepairItemHashVersion(provenanceNormalization.entries),explicitVersionsBefore=new Set(provenanceNormalization.entries.map(entry=>entry.item_hash_version).filter(version=>version!==void 0)),reanchor=reanchorHistoryEntries(provenanceNormalization.entries,itemHashVersion),itemReplayContext=describeHistoryRepairItemReplay(subject,snapshot,itemHashVersion);await assertHistoryRepairAbandonmentProof(options.mergeAbandonmentProof,itemReplayContext,chainBefore.ok,reanchor);const finalReplay=reanchor.finalDocument,finalReplayHashes=replayHashVerificationCandidates(finalReplay,itemHashVersion);let reconcileNeeded=!1;if(itemReplayContext.currentItemReplay!==null){const currentItemReplayHashes=replayHashVerificationCandidates(itemReplayContext.currentItemReplay,itemHashVersion);reconcileNeeded=!finalReplayHashes.some((hash,hashIndex)=>hash===currentItemReplayHashes[hashIndex])}const reconciliation=reconcileNeeded&&itemReplayContext.currentItemReplay?analyzeReconciliationDiscard(reanchor.entries,finalReplay,itemReplayContext.currentItemReplay):void 0,{mergeReceiptProof,effectiveAuditContext}=await resolveHistoryRepairMergeEvidence({options,pmRoot,subjectId:subject.id,itemReplayContext,reconciliation}),changed=[reanchor.entriesRehashed>0,reanchor.entriesPatchRepaired>0,reconcileNeeded,shouldAppendForcedHistoryAudit(options,reanchor.entries,effectiveAuditContext),provenanceNormalization.receipt.changed].some(Boolean),author=snapshot.author,dryRun=!!options.dryRun,repairMessage=buildHistoryRepairMessage({message:options.message,entriesRehashed:reanchor.entriesRehashed,entriesPatchRepaired:reanchor.entriesPatchRepaired,reconcileNeeded}),{rewrittenEntries,auditEntryAdded}=buildHistoryRepairEntries({reanchorEntries:reanchor.entries,changed,reconcileNeeded,currentItemReplay:itemReplayContext.currentItemReplay,finalReplay,author,message:repairMessage,auditOperation:options.auditOperation??"history_repair",auditContext:provenanceNormalization.receipt.changed?{...effectiveAuditContext,provenance_normalization:provenanceNormalization.receipt}:effectiveAuditContext,itemHashVersion,explicitItemHashVersion:reanchor.explicitItemHashVersion}),warnings=collectHistoryRepairWarnings(changed,reanchor.skippedOps,reconciliation);return{changed,rewrittenEntries,beforeWrite:async()=>assertHistoryRepairAbandonmentProof(options.mergeAbandonmentProof,itemReplayContext,chainBefore.ok,reanchor),report:({verification:historyVerify,warnings:writeWarnings})=>({id:subject.id,dry_run:dryRun,changed,history:{path:subject.historyPath,entries_scanned:historyEntries.length,chain_drift_before:!chainBefore.ok,entries_rehashed:reanchor.entriesRehashed,entries_patch_repaired:reanchor.entriesPatchRepaired,converted_replace_to_add:reanchor.convertedReplaceToAdd,skipped_ops:reanchor.skippedOps,reconciled_with_item:reconcileNeeded,audit_entry_added:auditEntryAdded,verify_ok:historyVerify.ok,verify_errors:historyVerify.errors,item_hash_version_before:chainVersionBefore.item_hash_version??null,item_hash_version_after:itemHashVersion,version_disposition:explicitVersionsBefore.size>1?"selected_for_ambiguous_stream":"preserved"},item:{exists:itemReplayContext.currentItemPath!==null,path:itemReplayContext.currentItemPath,matched_chain_before:itemReplayContext.matchedChainBefore},provenance_normalization:{requested:options.normalizeProvenance===!0,...provenanceNormalization.receipt},...reconciliation?{reconciliation}:{},...mergeReceiptProof?{merge_receipt_proof:mergeReceiptProof}:{},warnings:[...new Set([...warnings,...writeWarnings])].sort((left,right)=>left.localeCompare(right)),generated_at:nowIso()})}}function assertHistoryRepairTarget(id,all){if(all&&id!==void 0)throw new PmCliError("history-repair: <id> and --all are mutually exclusive; pass an item id to repair one stream or --all to repair every drifted stream.",EXIT_CODE.USAGE);if(!all&&id===void 0)throw new PmCliError("history-repair: provide an item <id> or pass --all to repair every drifted stream.",EXIT_CODE.USAGE)}async function runHistoryRepairAll(options,global2){if(options.salvageTail===!0)throw new PmCliError("Tail salvage requires one explicit item ID; --all is not supported.",EXIT_CODE.USAGE);const{pmRoot,settings,typeRegistry}=await resolveHistoryWorkspace(global2.path),itemReadWarnings=[],items=await listAllItemMetadataWithBody(pmRoot,settings.item_format,typeRegistry.type_to_folder,itemReadWarnings,settings.schema,{forceSourceScan:!0}),drift=await scanHistoryDrift(pmRoot,items),invalidProvenanceIds=options.normalizeProvenance?await listInvalidProvenanceHistoryStreamIds(pmRoot):[],targetedIds=[...new Set([...drift.driftedItems,...invalidProvenanceIds])].sort((left,right)=>left.localeCompare(right)),streams=[],repairWarnings=[],totals={repaired:0,skipped_clean:0,failed:0};for(const driftedId of targetedIds)try{const result=await runHistoryRepair(driftedId,{...options,auditContext:options.auditContextById?.[driftedId],auditContextById:void 0,mergeReceiptProof:options.mergeReceiptProofById?.[driftedId]??options.mergeReceiptProof,mergeReceiptProofById:void 0},global2),outcome=result.changed?"repaired":"skipped_clean";totals[outcome]+=1,repairWarnings.push(...result.warnings.map(warning=>`${driftedId}:${warning}`)),streams.push({id:driftedId,outcome,entries_rehashed:result.history.entries_rehashed,entries_patch_repaired:result.history.entries_patch_repaired,reconciled_with_item:result.history.reconciled_with_item,...result.reconciliation?{reconciliation:result.reconciliation}:{},...result.merge_receipt_proof?{merge_receipt_proof:result.merge_receipt_proof}:{},warnings:result.warnings})}catch(error){totals.failed+=1,streams.push({id:driftedId,outcome:"failed",error:error instanceof Error?error.message:String(error)})}return options.dryRun||await invalidateHistoryDriftCache(pmRoot),{all:!0,dry_run:!!options.dryRun,scanned_streams:items.length,drifted_streams:drift.driftedItems.length,provenance_invalid_streams:invalidProvenanceIds.length,streams,totals,warnings:[...new Set([...itemReadWarnings,...repairWarnings])].sort((left,right)=>left.localeCompare(right)),generated_at:nowIso()}}export{analyzeReconciliationDiscard,assertHistoryRepairTarget,runHistoryRepair,runHistoryRepairAll};
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a6d2870f-ebba-580a-85e9-3eb44e533d08")}catch{}})();import{salvageHistoryTail}from"./history/salvage.js";import{resolveHistoryHashAlgorithm}from"../core/history/digest.js";import{realpath}from"node:fs/promises";import path from"node:path";import jsonPatch from"fast-json-patch";import{patchPathToChangedField}from"../core/history/history-diff.js";import{normalizeReplayPatchOps,reanchorHistoryEntries,replayHashVerificationCandidates,resolveHistoryRepairItemHashVersion,toReplayDocument,verifyHistoryChainWithVersion}from"../core/history/replay.js";import{sealHistoryRecord}from"../core/history/history.js";import{classifyHistoryEvent}from"../core/history/event-classification.js";import{scanHistoryDrift}from"../core/history/drift-scan.js";import{invalidateHistoryDriftCache}from"../core/history/drift-cache.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{sha256Hex,stableStringify}from"../core/shared/serialization.js";import{resolveAuthor}from"../core/shared/author.js";import{nowIso}from"../core/shared/time.js";import{listAllItemMetadataWithBody}from"../core/store/item-store.js";import{resolveHistoryWorkspace,resolveHistorySubject}from"./history/subject.js";import{runHistoryMaintenance}from"./history/maintenance.js";import{isOriginalGitStateRestored}from"./merge/receipt-operation.js";import{assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,summarizeMergeReceipt}from"./merge/receipts.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue}from"./merge/three-way.js";import{listInvalidProvenanceHistoryStreamIds,normalizeInvalidHistoryProvenance}from"./governance/provenance-health.js";function replayFieldValue(document,field){return field==="body"?document.body:document.metadata[field]}function isDiagnosticCollectionRow(field,value){if(value===null||typeof value!="object"||Array.isArray(value))return!1;const row=value;return field==="comments"?typeof row.created_at=="string"&&typeof row.author=="string"&&typeof row.text=="string":typeof row.path=="string"||field==="tests"&&typeof row.command=="string"}function diagnosticLinkedRecord(value){const result={...value};if(result.source_kind==="merge_union"&&(result.source_kind="local_mutation"),result.provenance!==null&&typeof result.provenance=="object"&&!Array.isArray(result.provenance)){const provenance={...result.provenance};provenance.source_kind==="merge_union"&&(provenance.source_kind="local_mutation"),result.provenance=provenance}return result}function diagnosticCollectionValue(field,value){if(!Array.isArray(value)||!["comments","docs","tests"].includes(field)||!value.every(row=>isDiagnosticCollectionRow(field,row)))return value;const rows=value.map(row=>field==="comments"?row:diagnosticLinkedRecord(row));return[...new Map(rows.map(row=>[stableStringify(row),row])).values()]}function isReplayValuePreserved(replayed,reconciled){if(replayed===void 0||Object.is(replayed,reconciled))return!0;if(Array.isArray(replayed)){if(!Array.isArray(reconciled))return!1;const matched=new Set;return replayed.every(expected=>{const index=reconciled.findIndex((candidate,candidateIndex)=>!matched.has(candidateIndex)&&isReplayValuePreserved(expected,candidate));return index<0?!1:(matched.add(index),!0)})}if(replayed===null||reconciled===null||typeof replayed!="object"||typeof reconciled!="object"||Array.isArray(reconciled))return!1;const reconciledRecord=reconciled;return Object.entries(replayed).every(([key,value])=>Object.hasOwn(reconciledRecord,key)&&isReplayValuePreserved(value,reconciledRecord[key]))}function collectReconciliationEvents(entries,fields){const remaining=new Set(fields),events=[];for(let index=entries.length-1;index>=0&&remaining.size>0;index-=1){const entry=entries[index],entryFields=new Set;for(const op of normalizeReplayPatchOps(entry.patch))entryFields.add(patchPathToChangedField(op.path)),op.from&&entryFields.add(patchPathToChangedField(op.from));const overlap=[...remaining].filter(field=>entryFields.has(field));if(overlap.length!==0){for(const field of overlap)remaining.delete(field);events.push({index:index+1,ts:entry.ts,author:entry.author,op:entry.op,fields:overlap.sort((left,right)=>left.localeCompare(right))})}}return events.reverse()}function analyzeReconciliationDiscard(entries,finalReplay,currentItemReplay){const reconcilePatch=jsonPatch.compare(finalReplay,currentItemReplay);if(reconcilePatch.length===0)return;const reconciledFields=new Set;for(const op of reconcilePatch)reconciledFields.add(patchPathToChangedField(op.path));const preservedFields=new Set([...reconciledFields].filter(field=>isReplayValuePreserved(diagnosticCollectionValue(field,replayFieldValue(finalReplay,field)),diagnosticCollectionValue(field,replayFieldValue(currentItemReplay,field))))),revertedFields=new Set([...reconciledFields].filter(field=>!preservedFields.has(field))),discardedEvents=collectReconciliationEvents(entries,revertedFields),preservedEvents=collectReconciliationEvents(entries,preservedFields),sortedReconciledFields=[...reconciledFields].sort((left,right)=>left.localeCompare(right)),sortedRevertedFields=[...revertedFields].sort((left,right)=>left.localeCompare(right)),sortedPreservedFields=[...preservedFields].sort((left,right)=>left.localeCompare(right)),discardedAuthors=[...new Set(discardedEvents.map(event=>event.author))].sort((left,right)=>left.localeCompare(right)),preservedAuthors=[...new Set(preservedEvents.map(event=>event.author))].sort((left,right)=>left.localeCompare(right));return{reconciled_field_count:reconciledFields.size,reconciled_fields:sortedReconciledFields,reverted_field_count:revertedFields.size,reverted_fields:sortedRevertedFields,discarded_events:discardedEvents,discarded_authors:discardedAuthors,preserved_field_count:preservedFields.size,preserved_fields:sortedPreservedFields,preserved_events:preservedEvents,preserved_authors:preservedAuthors,recovery_hint:discardedEvents.length>0?"Reconciliation keeps the on-disk item and overwrites these replayed mutations. To recover one, inspect it with pm history <id> --diff --field <field>, then re-apply the lost value with pm update.":null}}async function canonicalRealPath(value){try{return await realpath(value)}catch{return null}}async function verifyReceiptAgainstSnapshot(params){if(await canonicalRealPath(path.resolve(params.gitWorkspaceRoot,params.receipt.item_path))!==params.currentPath)return null;const declaredFields=[...new Set([...params.receipt.fields_from_theirs,...params.receipt.union_fields,...params.receipt.decisions.map(decision=>decision.field)])].sort(),hashes=params.receipt.merged_field_hashes;if(!hashes||declaredFields.length===0)return null;const hashFields=Object.keys(hashes).sort();if(declaredFields.length!==hashFields.length||declaredFields.some((field,index)=>field!==hashFields[index]))return null;const currentMetadata=params.currentItemReplay.metadata;return declaredFields.every(field=>{const value=field==="body"?params.currentItemReplay.body:currentMetadata[field];return[hashItemScalarDecisionValue(value),sha256Hex(stableStringify(encodeItemScalarDecisionValue(value)))].includes(hashes[field])})?{receipt:params.receipt,declaredFields}:null}async function verifyMergeReceiptProof(params){if(params.proof.gitWorkspaceRoot===null)return{trusted:!1,reason:"git_workspace_unavailable",receipt_ids:[]};if(params.itemPath===null||params.currentItemReplay===null)return{trusted:!1,reason:"item_path_unavailable",receipt_ids:[]};const itemReceipts=params.proof.receipts.filter(receipt=>receipt.item_id===params.id);if(itemReceipts.length===0)return{trusted:!1,reason:"no_item_receipts",receipt_ids:[]};const currentPath=await realpath(params.itemPath),trustedReceipts=[];for(const receipt of itemReceipts){const trusted=await verifyReceiptAgainstSnapshot({receipt,gitWorkspaceRoot:params.proof.gitWorkspaceRoot,currentPath,currentItemReplay:params.currentItemReplay});trusted&&trustedReceipts.push(trusted)}const trustedFields=new Set(trustedReceipts.flatMap(({declaredFields})=>declaredFields)),reconciliationFields=params.reconciliation?.reconciled_fields;return trustedReceipts.length>0&&(reconciliationFields===void 0||reconciliationFields.length>0&&reconciliationFields.every(field=>trustedFields.has(field)))?{trusted:!0,reason:"trusted_merge_driver_hash_evidence",receipt_ids:trustedReceipts.map(({receipt})=>receipt.id)}:{trusted:!1,reason:"no_receipt_set_proves_snapshot",receipt_ids:[]}}async function assertHistoryRepairAbandonmentProof(proof,context,chainOk,reanchor){if(proof===void 0)return;const{receipt,gitWorkspaceRoot}=proof,canonicalPath=context.currentItemPath===null?null:path.relative(gitWorkspaceRoot,context.currentItemPath).split(path.sep).join("/");if(!chainOk||reanchor.entriesRehashed!==0||reanchor.entriesPatchRepaired!==0||context.matchedChainBefore!==!0||context.currentItemRawBeforeLock===null||canonicalPath!==receipt.item_path||receipt.operation===void 0||!await isOriginalGitStateRestored(gitWorkspaceRoot,receipt.item_path,receipt.operation,context.currentItemRawBeforeLock))throw new PmCliError("Restored-origin receipt evidence no longer proves the exact clean item snapshot.",EXIT_CODE.CONFLICT,{code:"merge_reconcile_receipt_evidence_untrusted",recovery:{suggested_retry:"pm merge reconcile --dry-run"}})}async function resolveHistoryRepairMergeEvidence(params){const requestedReceiptIds=new Set(params.options.mergeReceiptProof?.receipts.filter(receipt=>receipt.item_id===params.subjectId).map(receipt=>receipt.id)??[]),authoritativeReceiptEvidence=params.options.mergeReceiptProof?await inspectMergeReceiptEvidence(params.options.mergeReceiptProof.gitWorkspaceRoot??process.cwd(),{includeReconciled:!0,includeLossless:!0,pmRoot:params.pmRoot}):void 0,subjectMergeReceipts=authoritativeReceiptEvidence?.receipts.filter(receipt=>receipt.item_id===params.subjectId&&requestedReceiptIds.has(receipt.id))??[];assertPortableMergeReceiptReferences(requestedReceiptIds,authoritativeReceiptEvidence,params.options.dryRun===!0);const mergeReceiptProof=params.options.mergeReceiptProof?await verifyMergeReceiptProof({proof:{...params.options.mergeReceiptProof,receipts:subjectMergeReceipts},id:params.subjectId,itemPath:params.itemReplayContext.currentItemPath,currentItemReplay:params.itemReplayContext.currentItemReplay,reconciliation:params.reconciliation}):void 0;if(mergeReceiptProof&&!mergeReceiptProof.trusted&¶ms.options.force!==!0)throw new PmCliError(`Merge receipt evidence for ${params.subjectId} does not prove the exact item snapshot (${mergeReceiptProof.reason}).`,EXIT_CODE.CONFLICT,{code:"merge_reconcile_receipt_evidence_untrusted",required:"Use authoritative clone-local or durable receipt evidence whose canonical path, declared fields, and merged-value hashes match the current item, or review the mismatch and rerun with --force.",recovery:{suggested_retry:"pm merge reconcile --dry-run"}});const provenMergeReceipts=mergeReceiptProof?.trusted?subjectMergeReceipts.filter(receipt=>mergeReceiptProof.receipt_ids.includes(receipt.id)):[];return{mergeReceiptProof,effectiveAuditContext:provenMergeReceipts.length>0&¶ms.options.force!==!0?{...params.options.auditContext,merge:{receipts:provenMergeReceipts.map(summarizeMergeReceipt)}}:params.options.auditContext}}function describeHistoryRepairItemReplay(subject,snapshot,itemHashVersion){const currentItemPath=subject.located?.itemPath??null,loadedItem=snapshot.loadedItem,historyEntries=snapshot.entries;if(!loadedItem)return{currentItemReplay:null,currentItemPath,matchedChainBefore:null,currentItemRawBeforeLock:null,loadedItem:null};const currentItemReplay=toReplayDocument(loadedItem.document),lastOriginalAfterHash=historyEntries[historyEntries.length-1].after_hash;return{currentItemReplay,currentItemPath,matchedChainBefore:replayHashVerificationCandidates(currentItemReplay,itemHashVersion,resolveHistoryHashAlgorithm(historyEntries.at(-1)?.hash_algorithm)).includes(lastOriginalAfterHash),currentItemRawBeforeLock:loadedItem.raw,loadedItem}}function buildHistoryRepairMessage(params){return typeof params.message=="string"&¶ms.message.trim().length>0?params.message:`history-repair re-anchored ${params.entriesRehashed} entr${params.entriesRehashed===1?"y":"ies"}${params.entriesPatchRepaired>0?`, repaired ${params.entriesPatchRepaired} patch(es)`:""}${params.reconcileNeeded?", reconciled chain with on-disk item":""}.`}function buildHistoryRepairEntries(params){const rewrittenEntries=[...params.reanchorEntries];if(!params.changed)return{rewrittenEntries,auditEntryAdded:!1};const afterReplay=params.reconcileNeeded&¶ms.currentItemReplay?params.currentItemReplay:params.finalReplay,algorithm=resolveHistoryHashAlgorithm(params.reanchorEntries.at(-1)?.hash_algorithm),beforeHashes=replayHashVerificationCandidates(params.finalReplay,params.itemHashVersion,algorithm),previousAfterHash=params.reanchorEntries[params.reanchorEntries.length-1].after_hash,semanticIndex=beforeHashes.indexOf(previousAfterHash),afterHashes=replayHashVerificationCandidates(afterReplay,params.itemHashVersion,algorithm),auditEntry={hash_algorithm:algorithm,ts:nowIso(),author:params.author,op:params.auditOperation,patch:params.reconcileNeeded&¶ms.currentItemReplay?jsonPatch.compare(params.finalReplay,params.currentItemReplay):[],before_hash:beforeHashes[semanticIndex],after_hash:afterHashes[semanticIndex],message:params.message,...params.explicitItemHashVersion?{item_hash_version:params.itemHashVersion}:{},...params.auditContext===void 0?{}:{context:params.auditContext}};return rewrittenEntries.push(sealHistoryRecord({...auditEntry,event_class:classifyHistoryEvent(auditEntry)})),{rewrittenEntries,auditEntryAdded:!0}}function collectHistoryRepairWarnings(changed,skippedOps,reconciliation){const warnings=[];return changed||warnings.push("history_repair_no_changes"),skippedOps>0&&warnings.push(`history_repair_skipped_unresolvable_ops:${skippedOps}`),reconciliation&&reconciliation.discarded_events.length>0&&warnings.push(`history_repair_reconcile_discards_events:${reconciliation.discarded_events.length}`,`history_repair_discarded_authors:${reconciliation.discarded_authors.join(",")}`),warnings}async function runHistoryRepair(id,options,global2){const{pmRoot,settings,typeRegistry}=await resolveHistoryWorkspace(global2.path),subject=await resolveHistorySubject(pmRoot,id,settings,typeRegistry.type_to_folder);return options.salvageTail===!0?salvageHistoryTail({pmRoot,subject,settings,typeRegistry,options,author:resolveAuthor(options.author,settings.author_default)}):runHistoryMaintenance({pmRoot,subject,settings,typeRegistry,operation:"history-repair",options,transform:snapshot=>buildHistoryRepairPlan({pmRoot,subject,options},snapshot)})}function shouldAppendForcedHistoryAudit(options,verifiedHistoryEntries,auditContext){const alreadyRecorded=(options.mergeAbandonmentProof!==void 0||options.mergeReceiptProof!==void 0&&options.force!==!0)&&auditContext!==void 0&&verifiedHistoryEntries.some(entry=>entry.record_hash_version!==void 0&&entry.op==="merge_reconcile"&&stableStringify(entry.context)===stableStringify(auditContext));return options.forceAuditEntry===!0&&!alreadyRecorded}async function buildHistoryRepairPlan(params,snapshot){const{pmRoot,subject,options}=params,historyEntries=snapshot.entries,chainBefore=snapshot.verification,chainVersionBefore=verifyHistoryChainWithVersion(historyEntries),provenanceNormalization=options.normalizeProvenance?normalizeInvalidHistoryProvenance(historyEntries):{entries:historyEntries,receipt:{changed:!1,events_changed:0,observations_removed:0,invalid_values:[]}},itemHashVersion=resolveHistoryRepairItemHashVersion(provenanceNormalization.entries),explicitVersionsBefore=new Set(provenanceNormalization.entries.map(entry=>entry.item_hash_version).filter(version=>version!==void 0)),reanchor=reanchorHistoryEntries(provenanceNormalization.entries,itemHashVersion),itemReplayContext=describeHistoryRepairItemReplay(subject,snapshot,itemHashVersion);await assertHistoryRepairAbandonmentProof(options.mergeAbandonmentProof,itemReplayContext,chainBefore.ok,reanchor);const finalReplay=reanchor.finalDocument,finalReplayHashes=replayHashVerificationCandidates(finalReplay,itemHashVersion);let reconcileNeeded=!1;if(itemReplayContext.currentItemReplay!==null){const currentItemReplayHashes=replayHashVerificationCandidates(itemReplayContext.currentItemReplay,itemHashVersion);reconcileNeeded=!finalReplayHashes.some((hash,hashIndex)=>hash===currentItemReplayHashes[hashIndex])}const reconciliation=reconcileNeeded&&itemReplayContext.currentItemReplay?analyzeReconciliationDiscard(reanchor.entries,finalReplay,itemReplayContext.currentItemReplay):void 0,{mergeReceiptProof,effectiveAuditContext}=await resolveHistoryRepairMergeEvidence({options,pmRoot,subjectId:subject.id,itemReplayContext,reconciliation}),changed=[reanchor.entriesRehashed>0,reanchor.entriesPatchRepaired>0,reconcileNeeded,shouldAppendForcedHistoryAudit(options,reanchor.entries,effectiveAuditContext),provenanceNormalization.receipt.changed].some(Boolean),author=snapshot.author,dryRun=!!options.dryRun,repairMessage=buildHistoryRepairMessage({message:options.message,entriesRehashed:reanchor.entriesRehashed,entriesPatchRepaired:reanchor.entriesPatchRepaired,reconcileNeeded}),{rewrittenEntries,auditEntryAdded}=buildHistoryRepairEntries({reanchorEntries:reanchor.entries,changed,reconcileNeeded,currentItemReplay:itemReplayContext.currentItemReplay,finalReplay,author,message:repairMessage,auditOperation:options.auditOperation??"history_repair",auditContext:provenanceNormalization.receipt.changed?{...effectiveAuditContext,provenance_normalization:provenanceNormalization.receipt}:effectiveAuditContext,itemHashVersion,explicitItemHashVersion:reanchor.explicitItemHashVersion}),warnings=collectHistoryRepairWarnings(changed,reanchor.skippedOps,reconciliation);return{changed,rewrittenEntries,beforeWrite:async()=>assertHistoryRepairAbandonmentProof(options.mergeAbandonmentProof,itemReplayContext,chainBefore.ok,reanchor),report:({verification:historyVerify,warnings:writeWarnings})=>({id:subject.id,dry_run:dryRun,changed,history:{path:subject.historyPath,entries_scanned:historyEntries.length,chain_drift_before:!chainBefore.ok,entries_rehashed:reanchor.entriesRehashed,entries_patch_repaired:reanchor.entriesPatchRepaired,converted_replace_to_add:reanchor.convertedReplaceToAdd,skipped_ops:reanchor.skippedOps,reconciled_with_item:reconcileNeeded,audit_entry_added:auditEntryAdded,verify_ok:historyVerify.ok,verify_errors:historyVerify.errors,item_hash_version_before:chainVersionBefore.item_hash_version??null,item_hash_version_after:itemHashVersion,version_disposition:explicitVersionsBefore.size>1?"selected_for_ambiguous_stream":"preserved"},item:{exists:itemReplayContext.currentItemPath!==null,path:itemReplayContext.currentItemPath,matched_chain_before:itemReplayContext.matchedChainBefore},provenance_normalization:{requested:options.normalizeProvenance===!0,...provenanceNormalization.receipt},...reconciliation?{reconciliation}:{},...mergeReceiptProof?{merge_receipt_proof:mergeReceiptProof}:{},warnings:[...new Set([...warnings,...writeWarnings])].sort((left,right)=>left.localeCompare(right)),generated_at:nowIso()})}}function assertHistoryRepairTarget(id,all){if(all&&id!==void 0)throw new PmCliError("history-repair: <id> and --all are mutually exclusive; pass an item id to repair one stream or --all to repair every drifted stream.",EXIT_CODE.USAGE);if(!all&&id===void 0)throw new PmCliError("history-repair: provide an item <id> or pass --all to repair every drifted stream.",EXIT_CODE.USAGE)}async function runHistoryRepairAll(options,global2){if(options.salvageTail===!0)throw new PmCliError("Tail salvage requires one explicit item ID; --all is not supported.",EXIT_CODE.USAGE);const{pmRoot,settings,typeRegistry}=await resolveHistoryWorkspace(global2.path),itemReadWarnings=[],items=await listAllItemMetadataWithBody(pmRoot,settings.item_format,typeRegistry.type_to_folder,itemReadWarnings,settings.schema,{forceSourceScan:!0}),drift=await scanHistoryDrift(pmRoot,items),invalidProvenanceIds=options.normalizeProvenance?await listInvalidProvenanceHistoryStreamIds(pmRoot):[],targetedIds=[...new Set([...drift.driftedItems,...invalidProvenanceIds])].sort((left,right)=>left.localeCompare(right)),streams=[],repairWarnings=[],totals={repaired:0,skipped_clean:0,failed:0};for(const driftedId of targetedIds)try{const result=await runHistoryRepair(driftedId,{...options,auditContext:options.auditContextById?.[driftedId],auditContextById:void 0,mergeReceiptProof:options.mergeReceiptProofById?.[driftedId]??options.mergeReceiptProof,mergeReceiptProofById:void 0},global2),outcome=result.changed?"repaired":"skipped_clean";totals[outcome]+=1,repairWarnings.push(...result.warnings.map(warning=>`${driftedId}:${warning}`)),streams.push({id:driftedId,outcome,entries_rehashed:result.history.entries_rehashed,entries_patch_repaired:result.history.entries_patch_repaired,reconciled_with_item:result.history.reconciled_with_item,...result.reconciliation?{reconciliation:result.reconciliation}:{},...result.merge_receipt_proof?{merge_receipt_proof:result.merge_receipt_proof}:{},warnings:result.warnings})}catch(error){totals.failed+=1,streams.push({id:driftedId,outcome:"failed",error:error instanceof Error?error.message:String(error)})}return options.dryRun||await invalidateHistoryDriftCache(pmRoot),{all:!0,dry_run:!!options.dryRun,scanned_streams:items.length,drifted_streams:drift.driftedItems.length,provenance_invalid_streams:invalidProvenanceIds.length,streams,totals,warnings:[...new Set([...itemReadWarnings,...repairWarnings])].sort((left,right)=>left.localeCompare(right)),generated_at:nowIso()}}export{analyzeReconciliationDiscard,assertHistoryRepairTarget,runHistoryRepair,runHistoryRepairAll};
|
|
2
2
|
//# sourceMappingURL=history-repair.js.map
|
package/dist/sdk/merge/driver.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="74f8a3ee-f0dc-568d-86d1-8c0b9649e2e2")}catch{}})();import{readFile,writeFile}from"node:fs/promises";import path from"node:path";import{getActiveExtensionRegistrations}from"../../core/extensions/index.js";import{collectRegisteredItemFieldNames}from"../../core/extensions/item-fields.js";import{pathExists}from"../../core/fs/fs-utils.js";import{parseItemDocument}from"../../core/item/item-format.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{nowIso}from"../../core/shared/time.js";import{getSettingsPath,resolvePmRoot}from"../../core/store/paths.js";import{readSettings}from"../../core/store/settings.js";import{hashItemScalarDecisionValue,mergeHistoryStreams,mergeItemDocuments,mergeJsonDocuments,mergeRelationshipEventStreams}from"./three-way.js";import{captureMergeReceiptOperation}from"./receipt-operation.js";import{summarizeMergeReceipt,writeMergeReceipt}from"./receipts.js";const MERGE_DRIVER_ARTIFACT_VALUES=["item","history","relationship","json"];function resolveArtifact(value){const normalized=value.trim().toLowerCase();if(MERGE_DRIVER_ARTIFACT_VALUES.includes(normalized))return normalized;throw new PmCliError(`Unknown merge artifact "${value}". Supported values: ${MERGE_DRIVER_ARTIFACT_VALUES.join(", ")}.`,EXIT_CODE.USAGE)}function resolvePreferredSide(value){if(value===void 0)return"ours";const normalized=value.trim().toLowerCase();if(normalized==="ours"||normalized==="theirs")return normalized;throw new PmCliError(`Unknown --prefer value "${value}". Supported values: ours, theirs.`,EXIT_CODE.USAGE)}async function readSideFile(filePath,label){try{return await readFile(filePath,"utf8")}catch{throw new PmCliError(`Cannot read merge ${label} file at ${filePath}.`,EXIT_CODE.NOT_FOUND)}}async function loadOptionalSettings(global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);return await pathExists(getSettingsPath(pmRoot))?readSettings(pmRoot):null}function itemFormatForPath(filePath,settings){return filePath.endsWith(".md")?"json_markdown":filePath.endsWith(".toon")?"toon":settings?.item_format??"toon"}async function preserveIdentityRefusal(merge,sides){try{return merge()}catch(error){if(error instanceof PmCliError&&error.context?.code==="item_identity_conflict"){const line=raw=>raw.endsWith(`
|
|
2
|
+
`)?raw:`${raw}
|
|
3
|
+
`;throw await writeFile(sides.outputPath,`<<<<<<< pm ours
|
|
4
|
+
${line(sides.oursRaw)}||||||| pm base
|
|
5
|
+
${line(sides.baseRaw)}=======
|
|
6
|
+
${line(sides.theirsRaw)}>>>>>>> pm theirs
|
|
7
|
+
`,"utf8"),new PmCliError(`Unresolved identity for ${sides.itemPath}: both snapshots remain in conflict markers. ${error.message}`,error.exitCode,{...error.context,recovery:{suggested_retry:"git status --short"},nextSteps:["Preserve both source snapshots and establish item identity before resolving. Do not stage conflict markers as a completed merge; pm health and storage validation reject them."]})}throw error}}async function runMergeDriver(options,global2){const artifact=resolveArtifact(options.artifact),preferred=resolvePreferredSide(options.prefer),outputPath=path.resolve(options.outputPath??options.oursPath),[baseRaw,oursRaw,theirsRaw]=await Promise.all([readSideFile(options.basePath,"base"),readSideFile(options.oursPath,"ours"),readSideFile(options.theirsPath,"theirs")]);let merged,receiptInput;const conflicts=[],result={},refusalSides={baseRaw,oursRaw,theirsRaw,outputPath,itemPath:options.itemPath??outputPath};if(artifact==="history"){const historyMerge=await preserveIdentityRefusal(()=>mergeHistoryStreams(baseRaw,oursRaw,theirsRaw),refusalSides);merged=historyMerge.merged,result.history={strategy:historyMerge.strategy,common_entries:historyMerge.common_entries,entries_from_ours:historyMerge.entries_from_ours,entries_from_theirs:historyMerge.entries_from_theirs,entries_total:historyMerge.entries_total,reanchored:historyMerge.reanchored}}else if(artifact==="relationship"){const relationshipMerge=mergeRelationshipEventStreams(baseRaw,oursRaw,theirsRaw);merged=relationshipMerge.merged,result.relationship={strategy:relationshipMerge.strategy,common_entries:relationshipMerge.common_entries,entries_from_ours:relationshipMerge.entries_from_ours,entries_from_theirs:relationshipMerge.entries_from_theirs,entries_total:relationshipMerge.entries_total,reanchored:relationshipMerge.reanchored}}else if(artifact==="item"){const settings=await loadOptionalSettings(global2),itemFormat=itemFormatForPath(options.itemPath??outputPath,settings),extensionFieldNames=collectRegisteredItemFieldNames(getActiveExtensionRegistrations()),itemMerge=await preserveIdentityRefusal(()=>mergeItemDocuments(baseRaw,oursRaw,theirsRaw,{format:itemFormat,schema:settings?.schema,extensionFieldNames,preferred,conflictResolution:"latest_document_update"}),refusalSides);if(merged=itemMerge.merged,conflicts.push(...itemMerge.conflict_fields),result.item={fields_from_theirs:itemMerge.fields_from_theirs,union_fields:itemMerge.union_fields,conflict_resolution:"latest_document_update",requested_preference_applied:!1},options.itemPath!==void 0){const mergedDocument=parseItemDocument(merged,{format:itemFormat,schema:settings?.schema,extensionFieldNames}),mergedMetadata=mergedDocument.metadata,receiptFields=[...new Set([...itemMerge.fields_from_theirs,...itemMerge.union_fields,...itemMerge.conflict_decisions.map(decision=>decision.field)])];receiptInput={cwd:process.cwd(),itemPath:options.itemPath,preferred,conflictResolution:"latest_document_update",fieldsFromTheirs:itemMerge.fields_from_theirs,unionFields:itemMerge.union_fields,mergedFieldHashes:Object.fromEntries(receiptFields.map(field=>[field,hashItemScalarDecisionValue(field==="body"?mergedDocument.body:mergedMetadata[field])])),decisions:itemMerge.conflict_decisions,operation:await captureMergeReceiptOperation(process.cwd(),options.itemPath,oursRaw)}}}else{const jsonMerge=mergeJsonDocuments(baseRaw,oursRaw,theirsRaw,{preferred});merged=jsonMerge.merged,conflicts.push(...jsonMerge.conflict_paths),result.json={paths_from_theirs:jsonMerge.paths_from_theirs}}if(await writeFile(outputPath,merged,"utf8"),receiptInput!==void 0){const receipt=await writeMergeReceipt(receiptInput);receipt!==null&&(result.receipt=summarizeMergeReceipt(receipt))}const guidance=[];if(conflicts.length>0){const receiptGuidance=result.receipt===void 0?'Run "pm merge report" to inspect clone-local merge evidence.':`Run "pm merge report" and correlate receipt ${result.receipt.receipt_id} with item ${result.receipt.item_id}; clone-local discarded values are never copied into this result.`;guidance.push(`Both branches changed ${conflicts.join(", ")}; the item driver kept the value from the later document update, with stable value order only as an equal-timestamp tie-break. --prefer does not apply to convergent item scalar conflicts. ${receiptGuidance} Review the merged file, re-apply the discarded change if needed, then "git add" it.`)}return artifact==="history"&&result.history?.reanchored&&guidance.push('The merged history chain was re-anchored. Run "pm merge reconcile --dry-run" to inspect the complete post-merge repair, then "pm merge reconcile" to apply it; use --force only after explicitly accepting reported discards.'),{ok:conflicts.length===0,artifact,output_path:outputPath,requested_preference:preferred,conflicts,...result,guidance,generated_at:nowIso()}}export{MERGE_DRIVER_ARTIFACT_VALUES,runMergeDriver};
|
|
2
8
|
//# sourceMappingURL=driver.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a29ee03b-9955-56c6-a72e-1e93182e5de8")}catch{}})();import{execFile}from"node:child_process";import{realpath}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{isPathOutsideRoot}from"../workspace.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile),OBJECT_ID="[a-f0-9]{40}(?:[a-f0-9]{24})?",INDEX_ENTRY=new RegExp(`^100(?:644|755) (${OBJECT_ID}) 0$`,"u"),BLOB_METADATA=new RegExp(`^(${OBJECT_ID}) blob (\\d+)$`,"u");async function gitBatch(args,ids,options){const pending=execFileAsync("git",args,{...options,encoding:"buffer"}),input=pending.child.stdin;return input!==null&&(input.on("error",()=>{pending.child.kill()}),input.end(`${ids.join(`
|
|
2
|
+
`)}
|
|
3
|
+
`)),(await pending).stdout}function indexedFiles(listing,requested){const files=new Map;for(const record of listing.split("\0")){const separator=record.indexOf(" "),file=record.slice(separator+1),metadata=INDEX_ENTRY.exec(record.slice(0,separator));metadata!==null&&requested.has(file)&&files.set(file,metadata[1])}return files}function blobBatches(metadata,requested,maximumBytes){const batches=[];let batch=[],bytes=0;for(const line of metadata.trimEnd().split(/\r?\n/u)){const match=BLOB_METADATA.exec(line);if(match===null||!requested.has(match[1]))continue;const size=Number(match[2]);!Number.isSafeInteger(size)||size>maximumBytes||(batch.length>0&&bytes+size+128>maximumBytes&&(batches.push(batch),batch=[],bytes=0),batch.push({id:match[1],size}),bytes+=size+128)}return batch.length>0&&batches.push(batch),batches}function decodeBlobs(output,entries,blobs){let offset=0;for(const{id,size}of entries){const start=output.indexOf(10,offset)+1;if(start===0||output.subarray(offset,start-1).toString("ascii")!==`${id} blob ${size}`||output[start+size]!==10)throw new Error("Invalid indexed receipt batch framing");blobs.set(id,output.subarray(start,start+size).toString("utf8")),offset=start+size+1}if(offset!==output.length)throw new Error("Unexpected indexed receipt batch data")}async function readIndexedReceiptBlobs(cwd,directory,fileNames,maximumBytes){const env=gitWorkspaceEnvironment();env.GIT_NO_REPLACE_OBJECTS="1",process.env.GIT_INDEX_FILE!==void 0&&(env.GIT_INDEX_FILE=path.resolve(process.env.GIT_INDEX_FILE));const options={cwd,env,windowsHide:!0,timeout:1e4,maxBuffer:maximumBytes+256};try{const root=(await execFileAsync("git",["rev-parse","--show-toplevel"],{...options,encoding:"utf8"})).stdout.trim(),[workspace,physicalDirectory]=await Promise.all([realpath(root),realpath(directory)]),relative=path.relative(workspace,physicalDirectory).replaceAll("\\","/");if(isPathOutsideRoot(relative))return new Map;options.cwd=workspace;const names=new Map(fileNames.map(file=>[`${relative}/${file}`,file])),listing=(await execFileAsync("git",["ls-files","--stage","-z","--",`:(literal)${relative}/`],{...options,encoding:"utf8"})).stdout,files=indexedFiles(listing,new Set(names.keys()));if(files.size===0)return new Map;const ids=new Set(files.values()),metadata=(await gitBatch(["cat-file","--batch-check"],[...ids],options)).toString("utf8"),blobs=new Map;for(const batch of blobBatches(metadata,ids,maximumBytes))decodeBlobs(await gitBatch(["cat-file","--batch"],batch.map(entry=>entry.id),options),batch,blobs);return new Map([...files].filter(([,id])=>blobs.has(id)).map(([file,id])=>[names.get(file),blobs.get(id)]))}catch{return new Map}}export{readIndexedReceiptBlobs};
|
|
4
|
+
//# sourceMappingURL=indexed-receipt-blobs.js.map
|
|
@@ -13,6 +13,8 @@ export declare const PM_GITATTRIBUTES_V2_END = "# pm-cli:merge-drivers:v2:end";
|
|
|
13
13
|
export interface MergeInstallOptions {
|
|
14
14
|
/** Preview the `.gitattributes` and `git config` changes without writing anything. */
|
|
15
15
|
dryRun?:boolean;
|
|
16
|
+
/** Explicitly install an owned merge-aware pre-commit hook for authenticated lossless settlement. */
|
|
17
|
+
preCommitHook?:boolean;
|
|
16
18
|
}
|
|
17
19
|
/** Installation or preview evidence identifying the selected workspace, fence coverage, and exact Git driver commands. */
|
|
18
20
|
export interface MergeInstallResult {
|
|
@@ -37,6 +39,11 @@ git_config:Array<{
|
|
|
37
39
|
key:string;
|
|
38
40
|
value:string;
|
|
39
41
|
}>;
|
|
42
|
+
/** Explicitly requested clone-local hook; omitted by the default hook-free installation. */
|
|
43
|
+
settlement_hook?:{
|
|
44
|
+
path:string;
|
|
45
|
+
changed:boolean;
|
|
46
|
+
};
|
|
40
47
|
/** Guidance for completing the multi-branch workflow setup. */
|
|
41
48
|
guidance:string[];
|
|
42
49
|
/** ISO 8601 timestamp recording when generated occurred. */
|
|
@@ -144,4 +151,5 @@ export declare function installMergeFence(options:{
|
|
|
144
151
|
pmRoot:string;
|
|
145
152
|
workspaceRoot:string;
|
|
146
153
|
dryRun?:boolean;
|
|
154
|
+
preCommitHook?:boolean;
|
|
147
155
|
}):Promise<MergeInstallResult>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="98a35fdb-bff9-5556-9c1d-1c345cb39171")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isFileMissingError,readFileIfExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{access,readFile,realpath}from"node:fs/promises";import{constants}from"node:fs";import{execFile}from"node:child_process";import path from"node:path";import{promisify}from"node:util";import{pathExists}from"../../core/fs/fs-utils.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{acquireLock}from"../../core/lock/lock.js";import{resolvePmPackageRootFromModule}from"../../core/packages/root.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolveAuthor}from"../../core/shared/author.js";import{nowIso}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../../core/store/settings.js";import{isPathOutsideRoot}from"../workspace.js";import{resolveSourceContextWritePolicy}from"../environment/source-context.js";import{gitWorkspaceEnvironment,resolveMergeDriverConfigScope,writeMergeDriverGitConfig}from"./worktree-config.js";import{discoverProjectRuntimeVersionPins}from"../environment/project-runtime-compatibility.js";const execFileAsync=promisify(execFile),PM_GITATTRIBUTES_START="# pm-cli:merge-drivers:start",PM_GITATTRIBUTES_END="# pm-cli:merge-drivers:end",PM_GITATTRIBUTES_V2_START="# pm-cli:merge-drivers:v2:start",PM_GITATTRIBUTES_V2_END="# pm-cli:merge-drivers:v2:end",MERGE_FENCE_LOCK_ID="merge-fence",MERGE_ATTRIBUTES_LOCK_ID="merge-attributes";function quoteMergeDriverArgument(value){return`'${value.replaceAll("'",`'"'"'`)}'`}async function resolveMatchingBunLauncher(candidate,version){const resolvedCandidate=await realpath(candidate);if(/(?:^|[\\/])bun-node-[^\\/]+[\\/]bun(?:\.exe)?$/iu.test(resolvedCandidate))return;const{stdout}=await execFileAsync(candidate,["--version"],{encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()===version?resolvedCandidate:void 0}async function resolveMergeDriverCliCommand(){const packageRoot=resolvePmPackageRootFromModule(import.meta.url,["../../.."]);let executable=process.execPath,bunLauncherFound=!1;const runtimeIdentity=await realpath(process.execPath),bunVersion=process.versions.bun,pathCandidates=(process.env.PATH??"").split(path.delimiter).filter(directory=>path.isAbsolute(directory)).map(directory=>path.join(directory,bunVersion===void 0?path.basename(process.execPath):process.platform==="win32"?"bun.exe":"bun")),candidates=bunVersion===void 0?pathCandidates:[process.env.npm_execpath??"",...pathCandidates,runtimeIdentity].filter(candidate=>path.isAbsolute(candidate)&&/^(?:bun|bun\.exe)$/iu.test(path.basename(candidate)));for(const candidate of candidates)try{if(await access(candidate,constants.X_OK),bunVersion!==void 0){const launcher=await resolveMatchingBunLauncher(candidate,bunVersion);if(launcher!==void 0){executable=launcher,bunLauncherFound=!0;break}continue}if(candidate!==runtimeIdentity&&await realpath(candidate)===runtimeIdentity){executable=candidate;break}}catch{}if(bunVersion!==void 0&&!bunLauncherFound)throw new PmCliError("Cannot install Bun merge drivers without a durable executable matching the current Bun version.",EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_bun_launcher_unavailable",why:"The current Bun process may be a temporary bunx launcher that Git cannot reuse after this command exits.",nextSteps:["Run with a durable version-matched Bun executable, expose one on an absolute PATH entry, or invoke pm through a Bun runner that publishes an absolute npm_execpath.","Retry pm merge install, then check pm health --strict-exit before merging branches."]});return`${quoteMergeDriverArgument(executable)} ${quoteMergeDriverArgument(path.join(packageRoot,"dist","cli.js"))}`}const MERGE_DRIVER_DEFINITIONS=[{key:"pm-item-toon",name:"pm field-aware TOON item document merge",artifact:"item",itemPath:"%P"},{key:"pm-item-markdown",name:"pm field-aware JSON-markdown item document merge",artifact:"item",itemPath:"%P"},{key:"pm-history",name:"pm append-only history stream merge",artifact:"history",itemPath:void 0},{key:"pm-relationship",name:"pm append-only relationship event store merge",artifact:"relationship",itemPath:void 0},{key:"pm-json",name:"pm key-level JSON config merge",artifact:"json",itemPath:void 0}];function resolveMergeInstallContext(options){const explicitPath=options.explicitPath?.trim(),sourceWorkspaceRoot=options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim(),sourcePmRoot=options.environment.PM_SOURCE_PM_PATH?.trim(),sourcePolicy=resolveSourceContextWritePolicy(options.environment),sourceCoordinatesAvailable=!!(sourceWorkspaceRoot&&sourcePmRoot),sourceContextSelected=explicitPath===void 0&&sourceCoordinatesAvailable&&(options.dryRun||sourcePolicy.source_writes_allowed);if(explicitPath!==void 0){const pmRoot=resolvePmRoot(options.cwd,explicitPath);return{pm_root:pmRoot,workspace_start:pmRoot,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}return sourceContextSelected?{pm_root:path.resolve(sourcePmRoot),workspace_start:path.resolve(sourceWorkspaceRoot),source_coordinates_available:!0,source_context_selected:!0,source_policy:sourcePolicy}:{pm_root:sourceCoordinatesAvailable?resolvePmRoot(options.cwd,".agents/pm"):resolvePmRoot(options.cwd),workspace_start:options.cwd,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}async function findGitWorkspaceRoot(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--show-toplevel"],{cwd,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4});return await realpath(stdout.trim())}catch{return null}}async function resolveGitWorkspaceRoot(cwd){const workspaceRoot=await findGitWorkspaceRoot(cwd);if(workspaceRoot===null)throw new PmCliError("pm merge install requires a git repository (merge drivers are git configuration).",EXIT_CODE.USAGE);return workspaceRoot}function toPosixRelative(fromPath,toPath){return path.relative(fromPath,toPath).replaceAll("\\","/")}function quoteGitAttributePattern(value){return`"${value.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function readSingleQuotedArgument(command,offset){if(command[offset]!=="'")return null;let value="";for(let index=offset+1;index<command.length;index+=1){if(command.startsWith(`'"'"'`,index)){value+="'",index+=4;continue}if(command[index]==="'")return{value,nextOffset:index+1};value+=command[index]}return null}async function isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,version){if(discoverProjectRuntimeVersionPins(workspaceRoot).filter(pin=>pin.constraint==="exact").some(pin=>pin.version!==version))return!1;const driverWorkspace=await findGitWorkspaceRoot(packageRoot);if(driverWorkspace!==null&&driverWorkspace!==await realpath(workspaceRoot)){const commonDirectories=await Promise.all([driverWorkspace,workspaceRoot].map(async cwd=>{const result=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd,env:gitWorkspaceEnvironment(),timeout:1e4});return realpath(result.stdout.trim())}));if(commonDirectories[0]===commonDirectories[1])return!1}return!0}async function isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot){const nodeArgument=readSingleQuotedArgument(configured,0);if(nodeArgument===null||configured[nodeArgument.nextOffset]!==" ")return!1;const cliArgument=readSingleQuotedArgument(configured,nodeArgument.nextOffset+1);if(cliArgument===null||configured.slice(cliArgument.nextOffset)!==expectedSuffix||!/^(?:node|bun)(?:\.exe)?$/iu.test(path.basename(nodeArgument.value))||path.basename(cliArgument.value)!=="cli.js"||path.basename(path.dirname(cliArgument.value))!=="dist"||!await pathExists(nodeArgument.value)||!await pathExists(cliArgument.value))return!1;try{const packageRoot=path.dirname(path.dirname(cliArgument.value)),manifest=JSON.parse(await readFile(path.join(packageRoot,"package.json"),"utf8")),pmBin=typeof manifest.bin=="string"?manifest.bin:manifest.bin?.pm;return await isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,manifest.version)?manifest.name==="@unbrained/pm-cli"&&pmBin==="dist/cli.js":!1}catch{return!1}}function buildMergeAttributePatterns(trackerRelativeRoot,typeFolders){const prefix=trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"",patterns=[`${quoteGitAttributePattern(`${prefix}**/*.toon`)} merge=pm-item-toon`,`${quoteGitAttributePattern(`${prefix}**/*.md`)} merge=pm-item-markdown`];for(const folder of typeFolders)patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.toon`)} merge=pm-item-toon`),patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.md`)} merge=pm-item-markdown`);return patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.jsonl`)} merge=pm-relationship`),patterns.push(`${quoteGitAttributePattern(`${prefix}history/*.jsonl`)} merge=pm-history`),patterns.push(`${quoteGitAttributePattern(`${prefix}settings.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/**`)} merge=text`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/.managed-extensions.json`)} merge=pm-json`),patterns}function managedMergeBlocks(content){const blocks=[];let cursor=0;for(;cursor<content.length;){const legacyStart=content.indexOf(PM_GITATTRIBUTES_START,cursor),versionedStart=content.indexOf(PM_GITATTRIBUTES_V2_START,cursor),start=legacyStart===-1?versionedStart:versionedStart===-1?legacyStart:Math.min(legacyStart,versionedStart);if(start===-1)break;const versioned=start===versionedStart,startMarker=versioned?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START,endMarker=versioned?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END,closing=content.indexOf(endMarker,start+startMarker.length),end=closing===-1?content.length:closing+endMarker.length;blocks.push({start,end,closed:closing!==-1,lines:content.slice(start+startMarker.length,closing===-1?end:closing).split(`
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="da37bda1-1520-5db0-9dad-bb1acc556de8")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isFileMissingError,readFileIfExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{access,readFile,realpath}from"node:fs/promises";import{constants}from"node:fs";import{execFile}from"node:child_process";import path from"node:path";import{promisify}from"node:util";import{pathExists}from"../../core/fs/fs-utils.js";import{resolveItemTypeRegistry}from"../../core/item/type-registry.js";import{acquireLock}from"../../core/lock/lock.js";import{resolvePmPackageRootFromModule}from"../../core/packages/root.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolveAuthor}from"../../core/shared/author.js";import{nowIso}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{readSettings,resolveGovernanceKnobs}from"../../core/store/settings.js";import{isPathOutsideRoot}from"../workspace.js";import{resolveSourceContextWritePolicy}from"../environment/source-context.js";import{gitWorkspaceEnvironment,resolveMergeDriverConfigScope,writeMergeDriverGitConfig}from"./worktree-config.js";import{discoverProjectRuntimeVersionPins}from"../environment/project-runtime-compatibility.js";const execFileAsync=promisify(execFile),PM_GITATTRIBUTES_START="# pm-cli:merge-drivers:start",PM_GITATTRIBUTES_END="# pm-cli:merge-drivers:end",PM_GITATTRIBUTES_V2_START="# pm-cli:merge-drivers:v2:start",PM_GITATTRIBUTES_V2_END="# pm-cli:merge-drivers:v2:end",MERGE_FENCE_LOCK_ID="merge-fence",MERGE_ATTRIBUTES_LOCK_ID="merge-attributes";function quoteMergeDriverArgument(value){return`'${value.replaceAll("'",`'"'"'`)}'`}async function resolveMatchingBunLauncher(candidate,version){const resolvedCandidate=await realpath(candidate);if(/(?:^|[\\/])bun-node-[^\\/]+[\\/]bun(?:\.exe)?$/iu.test(resolvedCandidate))return;const{stdout}=await execFileAsync(candidate,["--version"],{encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()===version?resolvedCandidate:void 0}async function resolveMergeDriverCliCommand(){const packageRoot=resolvePmPackageRootFromModule(import.meta.url,["../../.."]);let executable=process.execPath,bunLauncherFound=!1;const runtimeIdentity=await realpath(process.execPath),bunVersion=process.versions.bun,pathCandidates=(process.env.PATH??"").split(path.delimiter).filter(directory=>path.isAbsolute(directory)).map(directory=>path.join(directory,bunVersion===void 0?path.basename(process.execPath):process.platform==="win32"?"bun.exe":"bun")),candidates=bunVersion===void 0?pathCandidates:[process.env.npm_execpath??"",...pathCandidates,runtimeIdentity].filter(candidate=>path.isAbsolute(candidate)&&/^(?:bun|bun\.exe)$/iu.test(path.basename(candidate)));for(const candidate of candidates)try{if(await access(candidate,constants.X_OK),bunVersion!==void 0){const launcher=await resolveMatchingBunLauncher(candidate,bunVersion);if(launcher!==void 0){executable=launcher,bunLauncherFound=!0;break}continue}if(candidate!==runtimeIdentity&&await realpath(candidate)===runtimeIdentity){executable=candidate;break}}catch{}if(bunVersion!==void 0&&!bunLauncherFound)throw new PmCliError("Cannot install Bun merge drivers without a durable executable matching the current Bun version.",EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_bun_launcher_unavailable",why:"The current Bun process may be a temporary bunx launcher that Git cannot reuse after this command exits.",nextSteps:["Run with a durable version-matched Bun executable, expose one on an absolute PATH entry, or invoke pm through a Bun runner that publishes an absolute npm_execpath.","Retry pm merge install, then check pm health --strict-exit before merging branches."]});return`${quoteMergeDriverArgument(executable)} ${quoteMergeDriverArgument(path.join(packageRoot,"dist","cli.js"))}`}const MERGE_DRIVER_DEFINITIONS=[{key:"pm-item-toon",name:"pm field-aware TOON item document merge",artifact:"item",itemPath:"%P"},{key:"pm-item-markdown",name:"pm field-aware JSON-markdown item document merge",artifact:"item",itemPath:"%P"},{key:"pm-history",name:"pm append-only history stream merge",artifact:"history",itemPath:void 0},{key:"pm-relationship",name:"pm append-only relationship event store merge",artifact:"relationship",itemPath:void 0},{key:"pm-json",name:"pm key-level JSON config merge",artifact:"json",itemPath:void 0}];function resolveMergeInstallContext(options){const explicitPath=options.explicitPath?.trim(),sourceWorkspaceRoot=options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim(),sourcePmRoot=options.environment.PM_SOURCE_PM_PATH?.trim(),sourcePolicy=resolveSourceContextWritePolicy(options.environment),sourceCoordinatesAvailable=!!(sourceWorkspaceRoot&&sourcePmRoot),sourceContextSelected=explicitPath===void 0&&sourceCoordinatesAvailable&&(options.dryRun||sourcePolicy.source_writes_allowed);if(explicitPath!==void 0){const pmRoot=resolvePmRoot(options.cwd,explicitPath);return{pm_root:pmRoot,workspace_start:pmRoot,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}return sourceContextSelected?{pm_root:path.resolve(sourcePmRoot),workspace_start:path.resolve(sourceWorkspaceRoot),source_coordinates_available:!0,source_context_selected:!0,source_policy:sourcePolicy}:{pm_root:sourceCoordinatesAvailable?resolvePmRoot(options.cwd,".agents/pm"):resolvePmRoot(options.cwd),workspace_start:options.cwd,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:!1,source_policy:sourcePolicy}}async function findGitWorkspaceRoot(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--show-toplevel"],{cwd,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4});return await realpath(stdout.trim())}catch{return null}}async function resolveGitWorkspaceRoot(cwd){const workspaceRoot=await findGitWorkspaceRoot(cwd);if(workspaceRoot===null)throw new PmCliError("pm merge install requires a git repository (merge drivers are git configuration).",EXIT_CODE.USAGE);return workspaceRoot}function toPosixRelative(fromPath,toPath){return path.relative(fromPath,toPath).replaceAll("\\","/")}function quoteGitAttributePattern(value){return`"${value.replaceAll("\\","\\\\").replaceAll('"','\\"')}"`}function readSingleQuotedArgument(command,offset){if(command[offset]!=="'")return null;let value="";for(let index=offset+1;index<command.length;index+=1){if(command.startsWith(`'"'"'`,index)){value+="'",index+=4;continue}if(command[index]==="'")return{value,nextOffset:index+1};value+=command[index]}return null}async function isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,version){if(discoverProjectRuntimeVersionPins(workspaceRoot).filter(pin=>pin.constraint==="exact").some(pin=>pin.version!==version))return!1;const driverWorkspace=await findGitWorkspaceRoot(packageRoot);if(driverWorkspace!==null&&driverWorkspace!==await realpath(workspaceRoot)){const commonDirectories=await Promise.all([driverWorkspace,workspaceRoot].map(async cwd=>{const result=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd,env:gitWorkspaceEnvironment(),timeout:1e4});return realpath(result.stdout.trim())}));if(commonDirectories[0]===commonDirectories[1])return!1}return!0}async function isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot){const nodeArgument=readSingleQuotedArgument(configured,0);if(nodeArgument===null||configured[nodeArgument.nextOffset]!==" ")return!1;const cliArgument=readSingleQuotedArgument(configured,nodeArgument.nextOffset+1);if(cliArgument===null||configured.slice(cliArgument.nextOffset)!==expectedSuffix||!/^(?:node|bun)(?:\.exe)?$/iu.test(path.basename(nodeArgument.value))||path.basename(cliArgument.value)!=="cli.js"||path.basename(path.dirname(cliArgument.value))!=="dist"||!await pathExists(nodeArgument.value)||!await pathExists(cliArgument.value))return!1;try{const packageRoot=path.dirname(path.dirname(cliArgument.value)),manifest=JSON.parse(await readFile(path.join(packageRoot,"package.json"),"utf8")),pmBin=typeof manifest.bin=="string"?manifest.bin:manifest.bin?.pm;return await isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,manifest.version)?manifest.name==="@unbrained/pm-cli"&&pmBin==="dist/cli.js":!1}catch{return!1}}function buildMergeAttributePatterns(trackerRelativeRoot,typeFolders){const prefix=trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"",patterns=[`${quoteGitAttributePattern(`${prefix}**/*.toon`)} merge=pm-item-toon`,`${quoteGitAttributePattern(`${prefix}**/*.md`)} merge=pm-item-markdown`];for(const folder of typeFolders)patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.toon`)} merge=pm-item-toon`),patterns.push(`${quoteGitAttributePattern(`${prefix}${folder}/*.md`)} merge=pm-item-markdown`);return patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.jsonl`)} merge=pm-relationship`),patterns.push(`${quoteGitAttributePattern(`${prefix}history/*.jsonl`)} merge=pm-history`),patterns.push(`${quoteGitAttributePattern(`${prefix}settings.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}**/*.json`)} merge=pm-json`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/**`)} merge=text`),patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/.managed-extensions.json`)} merge=pm-json`),patterns}function managedMergeBlocks(content){const blocks=[];let cursor=0;for(;cursor<content.length;){const legacyStart=content.indexOf(PM_GITATTRIBUTES_START,cursor),versionedStart=content.indexOf(PM_GITATTRIBUTES_V2_START,cursor),start=legacyStart===-1?versionedStart:versionedStart===-1?legacyStart:Math.min(legacyStart,versionedStart);if(start===-1)break;const versioned=start===versionedStart,startMarker=versioned?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START,endMarker=versioned?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END,closing=content.indexOf(endMarker,start+startMarker.length),end=closing===-1?content.length:closing+endMarker.length;blocks.push({start,end,closed:closing!==-1,lines:content.slice(start+startMarker.length,closing===-1?end:closing).split(`
|
|
2
2
|
`).map(line=>line.trim()).filter(Boolean)}),cursor=end}return blocks}function blockBelongsToTracker(lines,patterns,trackerRelativeRoot){const ownedPatterns=new Set(patterns),prefix=quoteGitAttributePattern(trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"").slice(0,-1);return lines.some(line=>ownedPatterns.has(line)||line.startsWith(prefix)&&/^[^/]+\/\*\.(?:toon|md)" merge=pm-item-(?:toon|markdown)$/u.test(line.slice(prefix.length)))}async function reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,patterns,dryRun){const gitattributesPath=path.join(workspaceRoot,".gitattributes"),gitDirectory=dryRun?null:(await execFileAsync("git",["rev-parse","--absolute-git-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4})).stdout.trim(),releaseLock=gitDirectory===null?null:await acquireLock(gitDirectory,MERGE_ATTRIBUTES_LOCK_ID,120,"merge-fence",!1,!0,5e3);try{const current=await readFileIfExists(gitattributesPath)??"",block=[PM_GITATTRIBUTES_V2_START,...patterns,PM_GITATTRIBUTES_V2_END].join(`
|
|
3
3
|
`),blocks=managedMergeBlocks(current),owned=blocks.filter(candidate=>blockBelongsToTracker(candidate.lines,patterns,trackerRelativeRoot));owned.length===0&&trackerRelativeRoot===".agents/pm"&&blocks.length===1&&blocks[0].lines.every(line=>!line.includes(".agents/pm/"))&&owned.push(blocks[0]);let next;if(owned.length>0){const first=owned[0];next=current;for(const duplicate of owned.slice(1).reverse())next=`${next.slice(0,duplicate.start)}${next.slice(duplicate.end)}`;next=`${next.slice(0,first.start)}${block}${next.slice(first.end)}`}else{const prefix=current.trimEnd();next=`${prefix.length>0?`${prefix}
|
|
4
4
|
|
|
5
5
|
`:""}${block}
|
|
6
|
-
`}return next===current?{path:gitattributesPath,changed:!1}:(dryRun||await writeFileAtomic(gitattributesPath,next),{path:gitattributesPath,changed:!0})}finally{await releaseLock?.()}}function resolveProjectMergeTypeFolders(settings){const registry=resolveItemTypeRegistry(settings,null);return[...new Set(Object.values(registry.type_to_folder))].sort((left,right)=>left.localeCompare(right))}async function auditMergeAttributeFence(pmRoot,typeFolders){const resolvedRoot=path.resolve(pmRoot);let fenceDirectory=null,fenceContent="",directory=resolvedRoot;for(let depth=0;depth<8;depth+=1){const candidate=path.join(directory,".gitattributes");try{const raw=await readFile(candidate,"utf8");if(raw.includes(PM_GITATTRIBUTES_V2_START)||raw.includes(PM_GITATTRIBUTES_START)){fenceDirectory=directory,fenceContent=raw;break}}catch{}const parent=path.dirname(directory);if(parent===directory)break;directory=parent}if(fenceDirectory===null)return{status:"not_installed",missing_patterns:[],stale_patterns:[],schema_scope:"project"};const fencePath=path.join(fenceDirectory,".gitattributes"),relativeRoot=toPosixRelative(fenceDirectory,resolvedRoot),blocks=managedMergeBlocks(fenceContent),expected=buildMergeAttributePatterns(relativeRoot,typeFolders),owned=blocks.filter(block=>blockBelongsToTracker(block.lines,expected,relativeRoot)),committed=owned.flatMap(block=>block.closed?block.lines:[]),committedSet=new Set(committed),expectedSet=new Set(expected),missing=expected.filter(line=>!committedSet.has(line)),stale=committed.filter(line=>!expectedSet.has(line));return{status:owned.length!==1||!owned[0].closed||missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const missingKeys=[],driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`,expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;try{const{stdout}=await execFileAsync("git",["config","--get",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4}),configured=stdout.trim();await isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot)||driftedKeys.push(key)}catch{missingKeys.push(key)}}return{status:missingKeys.length>0?"missing":driftedKeys.length>0?"drift":"ok",missing_keys:missingKeys,drifted_keys:driftedKeys}}async function refreshMergeAttributeFenceIfInstalled(pmRoot){let workspaceRoot;try{workspaceRoot=await resolveGitWorkspaceRoot(path.resolve(pmRoot))}catch{return{status:"no_git"}}const trackerRelativeRoot=toPosixRelative(workspaceRoot,path.resolve(pmRoot)),lockSettings=await readSettings(pmRoot),owner=resolveAuthor(void 0,lockSettings.author_default),releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,!1,resolveGovernanceKnobs(lockSettings).force_required_for_stale_lock,lockSettings.locks.wait_ms);try{const gitattributesPath=path.join(workspaceRoot,".gitattributes");let current;try{current=await readFile(gitattributesPath,"utf8")}catch(error){if(error instanceof Error&&"code"in error&&isFileMissingError(error))return{status:"not_installed",path:gitattributesPath};throw error}if(!current.includes(PM_GITATTRIBUTES_V2_START)&&!current.includes(PM_GITATTRIBUTES_START))return{status:"not_installed",path:gitattributesPath};const settings=await readSettings(pmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),outcome=await reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),!1);return{status:outcome.changed?"refreshed":"unchanged",path:outcome.path}}finally{await releaseLock()}}async function runMergeInstall(options,global2){const context=resolveMergeInstallContext({cwd:process.cwd(),explicitPath:global2.path,dryRun:options.dryRun===!0,environment:process.env}),pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===!0})}async function installMergeDriverConfiguration(workspaceRoot,entries){const commonDirectory=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim(),release=await acquireLock(commonDirectory,"merge-driver-config",120,"merge-install",!1,!0,5e3);try{const scope=await resolveMergeDriverConfigScope(workspaceRoot);let stdout;try{stdout=(await execFileAsync("git",["config",scope,"--null","--get-regexp","."],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout}catch(error){if(typeof error!="object"||error===null||!("code"in error)||error.code!==1)throw error;stdout=""}const current=new Map(stdout.split("\0").filter(Boolean).map(entry=>{const separator=entry.indexOf(`
|
|
7
|
-
`);return[entry.slice(0,separator),entry.slice(separator+1)]}));for(const entry of entries)current.get(entry.key)!==entry.value&&await writeMergeDriverGitConfig(workspaceRoot,[scope,entry.key,entry.value])}finally{await release()}}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot),workspaceRoot=path.resolve(options.workspaceRoot),dryRun=options.dryRun===!0;let canonicalPmRoot,canonicalWorkspaceRoot;try{[canonicalPmRoot,canonicalWorkspaceRoot]=await Promise.all([realpath(pmRoot),realpath(workspaceRoot)])}catch(error){throw new PmCliError(`Cannot resolve tracker root ${pmRoot} or workspace root ${workspaceRoot}: ${String(error)}`,EXIT_CODE.NOT_FOUND,{code:"merge_root_not_found",nextSteps:["Initialize the tracker and ensure both paths are accessible.",'Retry "pm merge install" from the repository workspace.']})}const trackerRelativeRoot=toPosixRelative(canonicalWorkspaceRoot,canonicalPmRoot);if(isPathOutsideRoot(trackerRelativeRoot))throw new PmCliError(`Tracker root ${pmRoot} is outside the git repository ${workspaceRoot}; there is nothing to configure for this repository.`,EXIT_CODE.USAGE);const settings=await readSettings(canonicalPmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),gitConfigEntries=[],cliCommand=await resolveMergeDriverCliCommand();for(const definition of MERGE_DRIVER_DEFINITIONS)gitConfigEntries.push({key:`merge.${definition.key}.name`,value:definition.name}),gitConfigEntries.push({key:`merge.${definition.key}.driver`,value:`${cliCommand} merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`});if(!dryRun)try{await installMergeDriverConfiguration(canonicalWorkspaceRoot,gitConfigEntries)}catch{throw new PmCliError(`Cannot install repository-local merge drivers in ${canonicalWorkspaceRoot}. Ensure the repository Git config is writable and no other Git process holds its lock, then retry.`,EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_git_config_unwritable",why:"pm merge install must persist clone-local driver commands before publishing their shared .gitattributes mappings.",nextSteps:["Make the repository Git config writable or wait for the process holding its lock to finish.",'Retry "pm merge install"; the operation is idempotent.','Use "pm merge install --dry-run --json" when the workspace is intentionally read-only.']})}const gitattributes=await reconcileGitattributesBlock(canonicalWorkspaceRoot,trackerRelativeRoot,patterns,dryRun);return{ok:!0,dry_run:dryRun,workspace_root:canonicalWorkspaceRoot,pm_root:canonicalPmRoot,gitattributes:{path:gitattributes.path,changed:gitattributes.changed,patterns,schema_scope:"project"},git_config:gitConfigEntries,guidance:["Commit .gitattributes so every branch and collaborator shares the merge mapping.",'git config is per-clone: each collaborator (and each fresh worktree/clone) runs "pm merge install" once.','After merging branches, run "pm merge reconcile --dry-run" to inspect the complete receipt and history repair, then apply "pm merge reconcile"; use --force only after explicitly accepting any discarded values.',"Custom item types registered later refresh this fence automatically (pm schema add-type/remove-type); rerun pm merge install only after out-of-band type-folder edits."],generated_at:nowIso()}}export{PM_GITATTRIBUTES_END,PM_GITATTRIBUTES_START,PM_GITATTRIBUTES_V2_END,PM_GITATTRIBUTES_V2_START,auditMergeAttributeFence,auditMergeDriverConfiguration,buildMergeAttributePatterns,findGitWorkspaceRoot,installMergeFence,refreshMergeAttributeFenceIfInstalled,resolveMergeInstallContext,resolveProjectMergeTypeFolders,runMergeInstall};
|
|
6
|
+
`}return next===current?{path:gitattributesPath,changed:!1}:(dryRun||await writeFileAtomic(gitattributesPath,next),{path:gitattributesPath,changed:!0})}finally{await releaseLock?.()}}function resolveProjectMergeTypeFolders(settings){const registry=resolveItemTypeRegistry(settings,null);return[...new Set(Object.values(registry.type_to_folder))].sort((left,right)=>left.localeCompare(right))}async function auditMergeAttributeFence(pmRoot,typeFolders){const resolvedRoot=path.resolve(pmRoot);let fenceDirectory=null,fenceContent="",directory=resolvedRoot;for(let depth=0;depth<8;depth+=1){const candidate=path.join(directory,".gitattributes");try{const raw=await readFile(candidate,"utf8");if(raw.includes(PM_GITATTRIBUTES_V2_START)||raw.includes(PM_GITATTRIBUTES_START)){fenceDirectory=directory,fenceContent=raw;break}}catch{}const parent=path.dirname(directory);if(parent===directory)break;directory=parent}if(fenceDirectory===null)return{status:"not_installed",missing_patterns:[],stale_patterns:[],schema_scope:"project"};const fencePath=path.join(fenceDirectory,".gitattributes"),relativeRoot=toPosixRelative(fenceDirectory,resolvedRoot),blocks=managedMergeBlocks(fenceContent),expected=buildMergeAttributePatterns(relativeRoot,typeFolders),owned=blocks.filter(block=>blockBelongsToTracker(block.lines,expected,relativeRoot)),committed=owned.flatMap(block=>block.closed?block.lines:[]),committedSet=new Set(committed),expectedSet=new Set(expected),missing=expected.filter(line=>!committedSet.has(line)),stale=committed.filter(line=>!expectedSet.has(line));return{status:owned.length!==1||!owned[0].closed||missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const missingKeys=[],driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`,expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;try{const{stdout}=await execFileAsync("git",["config","--get",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:!0,timeout:1e4}),configured=stdout.trim();await isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot)||driftedKeys.push(key)}catch{missingKeys.push(key)}}return{status:missingKeys.length>0?"missing":driftedKeys.length>0?"drift":"ok",missing_keys:missingKeys,drifted_keys:driftedKeys}}async function refreshMergeAttributeFenceIfInstalled(pmRoot){let workspaceRoot;try{workspaceRoot=await resolveGitWorkspaceRoot(path.resolve(pmRoot))}catch{return{status:"no_git"}}const trackerRelativeRoot=toPosixRelative(workspaceRoot,path.resolve(pmRoot)),lockSettings=await readSettings(pmRoot),owner=resolveAuthor(void 0,lockSettings.author_default),releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,!1,resolveGovernanceKnobs(lockSettings).force_required_for_stale_lock,lockSettings.locks.wait_ms);try{const gitattributesPath=path.join(workspaceRoot,".gitattributes");let current;try{current=await readFile(gitattributesPath,"utf8")}catch(error){if(error instanceof Error&&"code"in error&&isFileMissingError(error))return{status:"not_installed",path:gitattributesPath};throw error}if(!current.includes(PM_GITATTRIBUTES_V2_START)&&!current.includes(PM_GITATTRIBUTES_START))return{status:"not_installed",path:gitattributesPath};const settings=await readSettings(pmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),outcome=await reconcileGitattributesBlock(workspaceRoot,trackerRelativeRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),!1);return{status:outcome.changed?"refreshed":"unchanged",path:outcome.path}}finally{await releaseLock()}}async function runMergeInstall(options,global2){const context=resolveMergeInstallContext({cwd:process.cwd(),explicitPath:global2.path,dryRun:options.dryRun===!0,environment:process.env}),pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===!0,preCommitHook:options.preCommitHook===!0})}async function installMergeDriverConfiguration(workspaceRoot,entries){const commonDirectory=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-common-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim(),release=await acquireLock(commonDirectory,"merge-driver-config",120,"merge-install",!1,!0,5e3);try{const scope=await resolveMergeDriverConfigScope(workspaceRoot);let stdout;try{stdout=(await execFileAsync("git",["config",scope,"--null","--get-regexp","."],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout}catch(error){if(typeof error!="object"||error===null||!("code"in error)||error.code!==1)throw error;stdout=""}const current=new Map(stdout.split("\0").filter(Boolean).map(entry=>{const separator=entry.indexOf(`
|
|
7
|
+
`);return[entry.slice(0,separator),entry.slice(separator+1)]}));for(const entry of entries)current.get(entry.key)!==entry.value&&await writeMergeDriverGitConfig(workspaceRoot,[scope,entry.key,entry.value])}finally{await release()}}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot),workspaceRoot=path.resolve(options.workspaceRoot),dryRun=options.dryRun===!0;let canonicalPmRoot,canonicalWorkspaceRoot;try{[canonicalPmRoot,canonicalWorkspaceRoot]=await Promise.all([realpath(pmRoot),realpath(workspaceRoot)])}catch(error){throw new PmCliError(`Cannot resolve tracker root ${pmRoot} or workspace root ${workspaceRoot}: ${String(error)}`,EXIT_CODE.NOT_FOUND,{code:"merge_root_not_found",nextSteps:["Initialize the tracker and ensure both paths are accessible.",'Retry "pm merge install" from the repository workspace.']})}const trackerRelativeRoot=toPosixRelative(canonicalWorkspaceRoot,canonicalPmRoot);if(isPathOutsideRoot(trackerRelativeRoot))throw new PmCliError(`Tracker root ${pmRoot} is outside the git repository ${workspaceRoot}; there is nothing to configure for this repository.`,EXIT_CODE.USAGE);const settings=await readSettings(canonicalPmRoot),typeFolders=resolveProjectMergeTypeFolders(settings),patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),gitConfigEntries=[],cliCommand=await resolveMergeDriverCliCommand(),settlementHook=options.preCommitHook===!0?await import("./settlement-hook.js").then(async hooks=>({plan:await hooks.prepareMergeSettlementHook(canonicalWorkspaceRoot,`${cliCommand} --pm-path ${quoteMergeDriverArgument(trackerRelativeRoot||".")}`),write:hooks.writeMergeSettlementHook})):void 0;for(const definition of MERGE_DRIVER_DEFINITIONS)gitConfigEntries.push({key:`merge.${definition.key}.name`,value:definition.name}),gitConfigEntries.push({key:`merge.${definition.key}.driver`,value:`${cliCommand} merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`});if(!dryRun)try{await installMergeDriverConfiguration(canonicalWorkspaceRoot,gitConfigEntries)}catch{throw new PmCliError(`Cannot install repository-local merge drivers in ${canonicalWorkspaceRoot}. Ensure the repository Git config is writable and no other Git process holds its lock, then retry.`,EXIT_CODE.DEPENDENCY_FAILED,{code:"merge_git_config_unwritable",why:"pm merge install must persist clone-local driver commands before publishing their shared .gitattributes mappings.",nextSteps:["Make the repository Git config writable or wait for the process holding its lock to finish.",'Retry "pm merge install"; the operation is idempotent.','Use "pm merge install --dry-run --json" when the workspace is intentionally read-only.']})}const gitattributes=await reconcileGitattributesBlock(canonicalWorkspaceRoot,trackerRelativeRoot,patterns,dryRun);return settlementHook!==void 0&&!dryRun&&await settlementHook.write(settlementHook.plan),{ok:!0,dry_run:dryRun,workspace_root:canonicalWorkspaceRoot,pm_root:canonicalPmRoot,gitattributes:{path:gitattributes.path,changed:gitattributes.changed,patterns,schema_scope:"project"},git_config:gitConfigEntries,...settlementHook===void 0?{}:{settlement_hook:{path:settlementHook.plan.path,changed:settlementHook.plan.changed}},guidance:["Commit .gitattributes so every branch and collaborator shares the merge mapping.",'git config is per-clone: each collaborator (and each fresh worktree/clone) runs "pm merge install" once.','After merging branches, run "pm merge reconcile --dry-run" to inspect the complete receipt and history repair, then apply "pm merge reconcile"; use --force only after explicitly accepting any discarded values.',"Custom item types registered later refresh this fence automatically (pm schema add-type/remove-type); rerun pm merge install only after out-of-band type-folder edits."],generated_at:nowIso()}}export{PM_GITATTRIBUTES_END,PM_GITATTRIBUTES_START,PM_GITATTRIBUTES_V2_END,PM_GITATTRIBUTES_V2_START,auditMergeAttributeFence,auditMergeDriverConfiguration,buildMergeAttributePatterns,findGitWorkspaceRoot,installMergeFence,refreshMergeAttributeFenceIfInstalled,resolveMergeInstallContext,resolveProjectMergeTypeFolders,runMergeInstall};
|
|
8
8
|
//# sourceMappingURL=install.js.map
|
|
@@ -109,6 +109,10 @@ generated_at:string;
|
|
|
109
109
|
export interface MergeReceiptEvidenceScan {
|
|
110
110
|
/** Valid receipts that passed bounded-file, schema, and identity validation. */
|
|
111
111
|
receipts:MergeDecisionReceipt[];
|
|
112
|
+
/** Valid counterparts with matching immutable provenance in the active Git index; absent evidence never authorizes a tracked history reference. */
|
|
113
|
+
durable_receipt_ids?:string[];
|
|
114
|
+
/** Valid filesystem sidecars not represented by matching indexed evidence; usable only for a read-only preview before guarded staging. */
|
|
115
|
+
unstaged_durable_receipt_ids?:string[];
|
|
112
116
|
/** Number of JSON receipt candidates that could not be validated safely. */
|
|
113
117
|
invalid_evidence_count:number;
|
|
114
118
|
/** Bounded privacy-safe identities and reason codes for rejected evidence. */
|
|
@@ -178,6 +182,8 @@ includeReconciled?:boolean;
|
|
|
178
182
|
includeLossless?:boolean;
|
|
179
183
|
pmRoot?:string;
|
|
180
184
|
}):Promise<MergeDecisionReceipt[]>;
|
|
185
|
+
/** Refuse audit references unless every requested identity has validated portable evidence. */
|
|
186
|
+
export declare function assertPortableMergeReceiptReferences(receiptIds:Iterable<string>,evidence:Pick<MergeReceiptEvidenceScan,"durable_receipt_ids" | "unstaged_durable_receipt_ids"> | undefined,preview:boolean):void;
|
|
181
187
|
/** Split merge provenance into receipts with discarded values and receipts whose composition was lossless. */
|
|
182
188
|
export declare function partitionMergeReceipts(receipts:MergeDecisionReceipt[]):{
|
|
183
189
|
/** Receipts that require an explicit decision because at least one competing scalar value was discarded. */
|