@unbrained/pm-cli 2026.9.16 → 2026.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +35 -0
- package/dist/cli/register-operations.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +149 -149
- package/dist/cli-bundle/chunks/{chunk-Q6KPZGMP.js → chunk-AOD5WE6U.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-A5EKLGOU.js → chunk-CA5DRNGA.js} +29 -29
- package/dist/cli-bundle/chunks/{chunk-UJSB6NO6.js → chunk-CI2JCBN2.js} +75 -75
- package/dist/cli-bundle/chunks/{chunk-DLVCGXO6.js → chunk-FZZVWWQP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-LD2FH2YE.js → chunk-GRFRKHFC.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-IPR2ICDO.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-3UXLVJIM.js → chunk-T4J2X3RK.js} +63 -63
- package/dist/cli-bundle/chunks/{chunk-T526C5DX.js → chunk-WD7BQXA7.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-CPFBDM53.js → chunk-WUDF7HWM.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-X2EVS624.js +3 -0
- package/dist/cli-bundle/chunks/{chunk-GA73HHQG.js → chunk-XFGWFVR3.js} +48 -48
- package/dist/cli-bundle/chunks/chunk-Y2OK7IAD.js +8 -0
- package/dist/cli-bundle/chunks/{register-list-query-ISM35VIX.js → register-list-query-43EQISX7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-PA5M3X77.js → register-mutation-TSZNAKEU.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-KFZIKATY.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-S6MMYN4D.js → register-setup-KTJ4ZE65.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SSG62L7U.js → chunk-246P5AK7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-7D6M45AN.js +30 -0
- package/dist/cli-bundle/focused-chunks/{chunk-BFUXTNZU.js → chunk-7UZHYT5B.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-JZSLDLR4.js → chunk-BVOQRMHF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-LNM3BS45.js → chunk-C6SH62BX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-267N774W.js → chunk-GEFWBVPQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-7S7XWMXC.js → chunk-HRANNGHM.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-GKSAMNBG.js → chunk-LG3SYXBX.js} +16 -16
- package/dist/cli-bundle/focused-chunks/{chunk-OWDDVMAP.js → chunk-LI6IGGB3.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-WUMGGQYN.js → chunk-MMX7BQLH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-NOK652OP.js → chunk-PNCUNPSA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-OCTTDZOU.js → chunk-QKG7JD34.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-G4TKJZ4A.js → chunk-QVVOXVYI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-WHL5CQG3.js → chunk-QYQFBJXD.js} +51 -51
- package/dist/cli-bundle/focused-chunks/{chunk-5KYHDFDN.js → chunk-YJYSFWSW.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-ZDX2BCOT.js +12 -0
- package/dist/cli-bundle/main.js +2 -2
- 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 +25 -25
- 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 +40 -40
- 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 +1 -1
- package/dist/core/config/nested-settings.js +1 -1
- package/dist/core/diagnostics/remediation.js +1 -1
- package/dist/core/shared/secret-rules.d.ts +110 -0
- package/dist/core/shared/secret-rules.js +2 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +1 -1
- package/dist/sdk/cli-contracts/command-aliases.js +1 -1
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
- package/dist/sdk/cli-program.js +1 -1
- package/dist/sdk/context-intent-contracts.js +1 -1
- package/dist/sdk/context-relevance.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/guide-topics.js +1 -1
- package/dist/sdk/lifecycle/event-validation-messages.d.ts +1 -1
- package/dist/sdk/lifecycle/event-validation-messages.js +1 -1
- package/dist/sdk/merge/install.js +2 -2
- package/dist/sdk/mutation-guard.d.ts +8 -1
- package/dist/sdk/mutation-guard.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/next-ready-budget.d.ts +20 -0
- package/dist/sdk/query/next-ready-budget.js +2 -0
- package/dist/sdk/query/next.d.ts +7 -2
- package/dist/sdk/query/next.js +1 -1
- package/dist/sdk/read-output-budget.d.ts +3 -1
- package/dist/sdk/read-output-budget.js +1 -1
- package/dist/sdk/read-output-contracts.js +1 -1
- package/dist/sdk/read-output-session.js +1 -1
- package/dist/sdk/test/execution.d.ts +6 -5
- package/dist/sdk/test/execution.js +2 -2
- package/dist/sdk/test/json-path.d.ts +11 -0
- package/dist/sdk/test/json-path.js +2 -0
- package/docs/BUILD_AND_RELEASE_ACCEPTANCE.md +66 -0
- package/docs/MERGE_SAFETY.md +13 -2
- package/docs/MUTATION_INTEGRITY.md +36 -4
- package/docs/NEXT_SELECTION_BUDGETS.md +93 -0
- package/docs/PACKED_FIRST_RUN.md +50 -0
- package/docs/README.md +2 -0
- package/docs/READ_OUTPUT_CONTRACTS.md +8 -4
- package/docs/agent-task-token-baseline.json +7 -7
- package/docs/generated/AGENT_CAPABILITY_ROUTING.md +1 -1
- package/docs/generated/AGENT_COMMAND_SURFACE.md +3 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +3 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
- package/marketplace.json +2 -2
- package/package.json +10 -8
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/README.md +43 -2
- package/packages/pm-calendar/extensions/calendar/command-definitions.ts +52 -0
- package/packages/pm-calendar/extensions/calendar/index.ts +2 -0
- package/packages/pm-calendar/extensions/calendar/manifest.json +3 -3
- package/packages/pm-calendar/extensions/calendar/shortcuts.ts +15 -0
- 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-codex/.codex-plugin/plugin.json +1 -1
- package/scripts/build-lease.mjs +43 -0
- package/scripts/build.mjs +40 -0
- package/dist/cli-bundle/chunks/chunk-C3MFMGCR.js +0 -20
- package/dist/cli-bundle/chunks/chunk-GX5XSANL.js +0 -8
- package/dist/cli-bundle/chunks/chunk-PTMZALZM.js +0 -3
- package/dist/cli-bundle/chunks/register-operations-MBSG5D22.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-5FSGN4MP.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-Z4PWEIMX.js +0 -30
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6b9c865a-c932-5394-b068-6fcf0dce5d3a")}catch(e2){}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{isFileMissingError}from"../../core/fs/fs-utils.js";import{access,readFile,realpath,writeFile}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";const execFileAsync=promisify(execFile);const PM_GITATTRIBUTES_START="# pm-cli:merge-drivers:start";const PM_GITATTRIBUTES_END="# pm-cli:merge-drivers:end";const PM_GITATTRIBUTES_V2_START="# pm-cli:merge-drivers:v2:start";const PM_GITATTRIBUTES_V2_END="# pm-cli:merge-drivers:v2:end";const MERGE_FENCE_LOCK_ID="merge-fence";function quoteMergeDriverArgument(value){return`'${value.replaceAll("'",`'"'"'`)}'`}async function resolveMergeDriverCliCommand(){const packageRoot=resolvePmPackageRootFromModule(import.meta.url,["../../.."]);let executable=process.execPath;const runtimeIdentity=await realpath(process.execPath);for(const directory of(process.env.PATH??"").split(path.delimiter)){if(!path.isAbsolute(directory))continue;const candidate=path.join(directory,path.basename(process.execPath));try{await access(candidate,constants.X_OK);if(candidate!==runtimeIdentity&&await realpath(candidate)===runtimeIdentity){executable=candidate;break}}catch{}}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();const sourceWorkspaceRoot=options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim();const sourcePmRoot=options.environment.PM_SOURCE_PM_PATH?.trim();const sourcePolicy=resolveSourceContextWritePolicy(options.environment);const sourceCoordinatesAvailable=Boolean(sourceWorkspaceRoot&&sourcePmRoot);const 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:false,source_policy:sourcePolicy}}if(sourceContextSelected){return{pm_root:path.resolve(sourcePmRoot),workspace_start:path.resolve(sourceWorkspaceRoot),source_coordinates_available:true,source_context_selected:true,source_policy:sourcePolicy}}return{pm_root:sourceCoordinatesAvailable?resolvePmRoot(options.cwd,".agents/pm"):resolvePmRoot(options.cwd),workspace_start:options.cwd,source_coordinates_available:sourceCoordinatesAvailable,source_context_selected:false,source_policy:sourcePolicy}}async function findGitWorkspaceRoot(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--show-toplevel"],{cwd,encoding:"utf8",windowsHide:true,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 isPortableMergeDriverCommand(configured,expectedSuffix){const nodeArgument=readSingleQuotedArgument(configured,0);if(nodeArgument===null||configured[nodeArgument.nextOffset]!==" "){return false}const cliArgument=readSingleQuotedArgument(configured,nodeArgument.nextOffset+1);if(cliArgument===null||configured.slice(cliArgument.nextOffset)!==expectedSuffix||!/^node(?:\.exe)?$/iu.test(path.basename(nodeArgument.value))||path.basename(cliArgument.value)!=="cli.js"||path.basename(path.dirname(cliArgument.value))!=="dist"){return false}if(!await pathExists(nodeArgument.value)||!await pathExists(cliArgument.value)){return false}try{const packageRoot=path.dirname(path.dirname(cliArgument.value));const manifest=JSON.parse(await readFile(path.join(packageRoot,"package.json"),"utf8"));const pmBin=typeof manifest.bin==="object"&&manifest.bin!==null&&"pm"in manifest.bin?manifest.bin.pm:void 0;return manifest.name==="@unbrained/pm-cli"&&(manifest.bin==="dist/cli.js"||pmBin==="dist/cli.js")}catch{return false}}function buildMergeAttributePatterns(trackerRelativeRoot,typeFolders){const prefix=trackerRelativeRoot.length>0?`${trackerRelativeRoot}/`:"";const 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`)}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`);patterns.push(`${quoteGitAttributePattern(`${prefix}extensions/.managed-extensions.json`)} merge=pm-json`);return patterns}function removeManagedBlock(content,startMarker,endMarker){const start=content.indexOf(startMarker);const end=content.indexOf(endMarker);return start>=0&&end>start?`${content.slice(0,start)}${content.slice(end+endMarker.length)}`:content.replace(startMarker,"").replace(endMarker,"")}async function reconcileGitattributesBlock(workspaceRoot,patterns,dryRun){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))){throw error}}const block=[PM_GITATTRIBUTES_V2_START,...patterns,PM_GITATTRIBUTES_V2_END].join("\n");const withoutManagedBlock=removeManagedBlock(removeManagedBlock(current,PM_GITATTRIBUTES_V2_START,PM_GITATTRIBUTES_V2_END),PM_GITATTRIBUTES_START,PM_GITATTRIBUTES_END);const prefix=withoutManagedBlock.trimEnd();const next=`${prefix.length>0?`${prefix}
|
|
2
2
|
|
|
3
3
|
`:""}${block}
|
|
4
|
-
`;if(next===current){return{path:gitattributesPath,changed:false}}if(!dryRun){await writeFile(gitattributesPath,next,"utf8")}return{path:gitattributesPath,changed:true}}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;let fenceContent="";let 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");const usesV2=fenceContent.includes(PM_GITATTRIBUTES_V2_START);const startMarker=usesV2?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START;const endMarker=usesV2?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END;const start=fenceContent.indexOf(startMarker);const end=fenceContent.indexOf(endMarker);const committed=end>start?fenceContent.slice(start+startMarker.length,end).split("\n").map(line=>line.trim()).filter(line=>line.length>0):[];const expected=buildMergeAttributePatterns(toPosixRelative(fenceDirectory,resolvedRoot),typeFolders);const committedSet=new Set(committed);const expectedSet=new Set(expected);const missing=expected.filter(line=>!committedSet.has(line));const stale=committed.filter(line=>!expectedSet.has(line));return{status:missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const cliCommand=resolveMergeDriverCliCommand();const missingKeys=[];const driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`;const expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;const expected=`${cliCommand}${expectedSuffix}`;try{const{stdout}=await execFileAsync("git",["config","--local","--get",key],{cwd:workspaceRoot,encoding:"utf8",windowsHide:true,timeout:1e4});const configured=stdout.trim();if(configured!==expected&&!await isPortableMergeDriverCommand(configured,expectedSuffix)){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));const lockSettings=await readSettings(pmRoot);const owner=resolveAuthor(void 0,lockSettings.author_default);const releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,false,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);const typeFolders=resolveProjectMergeTypeFolders(settings);const outcome=await reconcileGitattributesBlock(workspaceRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),false);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===true,environment:process.env});const pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===true})}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot);const workspaceRoot=path.resolve(options.workspaceRoot);const dryRun=options.dryRun===true;let canonicalPmRoot;let 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);const typeFolders=resolveProjectMergeTypeFolders(settings);const patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders);const gitConfigEntries=[];const cliCommand=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{for(const entry of gitConfigEntries){await execFileAsync("git",["config","--local",entry.key,entry.value],{cwd:canonicalWorkspaceRoot,encoding:"utf8",windowsHide:true,timeout:1e4})}}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,patterns,dryRun);return{ok:true,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};
|
|
4
|
+
`;if(next===current){return{path:gitattributesPath,changed:false}}if(!dryRun){await writeFile(gitattributesPath,next,"utf8")}return{path:gitattributesPath,changed:true}}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;let fenceContent="";let 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");const usesV2=fenceContent.includes(PM_GITATTRIBUTES_V2_START);const startMarker=usesV2?PM_GITATTRIBUTES_V2_START:PM_GITATTRIBUTES_START;const endMarker=usesV2?PM_GITATTRIBUTES_V2_END:PM_GITATTRIBUTES_END;const start=fenceContent.indexOf(startMarker);const end=fenceContent.indexOf(endMarker);const committed=end>start?fenceContent.slice(start+startMarker.length,end).split("\n").map(line=>line.trim()).filter(line=>line.length>0):[];const expected=buildMergeAttributePatterns(toPosixRelative(fenceDirectory,resolvedRoot),typeFolders);const committedSet=new Set(committed);const expectedSet=new Set(expected);const missing=expected.filter(line=>!committedSet.has(line));const stale=committed.filter(line=>!expectedSet.has(line));return{status:missing.length>0||stale.length>0?"drift":"ok",path:fencePath,missing_patterns:missing,stale_patterns:stale,schema_scope:"project"}}async function auditMergeDriverConfiguration(workspaceRoot){const cliCommand=await resolveMergeDriverCliCommand();const missingKeys=[];const driftedKeys=[];for(const definition of MERGE_DRIVER_DEFINITIONS){const key=`merge.${definition.key}.driver`;const expectedSuffix=` merge driver ${definition.artifact} "%O" "%A" "%B"${definition.itemPath===void 0?"":` --item-path ${definition.itemPath}`}`;const expected=`${cliCommand}${expectedSuffix}`;try{const{stdout}=await execFileAsync("git",["config","--local","--get",key],{cwd:workspaceRoot,encoding:"utf8",windowsHide:true,timeout:1e4});const configured=stdout.trim();if(configured!==expected&&!await isPortableMergeDriverCommand(configured,expectedSuffix)){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));const lockSettings=await readSettings(pmRoot);const owner=resolveAuthor(void 0,lockSettings.author_default);const releaseLock=await acquireLock(pmRoot,MERGE_FENCE_LOCK_ID,lockSettings.locks.ttl_seconds,owner,false,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);const typeFolders=resolveProjectMergeTypeFolders(settings);const outcome=await reconcileGitattributesBlock(workspaceRoot,buildMergeAttributePatterns(trackerRelativeRoot,typeFolders),false);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===true,environment:process.env});const pmRoot=context.pm_root;await assertInitializedTracker(pmRoot);const workspaceRoot=await resolveGitWorkspaceRoot(context.workspace_start);return installMergeFence({pmRoot,workspaceRoot,dryRun:options.dryRun===true})}async function installMergeFence(options){const pmRoot=path.resolve(options.pmRoot);const workspaceRoot=path.resolve(options.workspaceRoot);const dryRun=options.dryRun===true;let canonicalPmRoot;let 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);const typeFolders=resolveProjectMergeTypeFolders(settings);const patterns=buildMergeAttributePatterns(trackerRelativeRoot,typeFolders);const gitConfigEntries=[];const 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{for(const entry of gitConfigEntries){await execFileAsync("git",["config","--local",entry.key,entry.value],{cwd:canonicalWorkspaceRoot,encoding:"utf8",windowsHide:true,timeout:1e4})}}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,patterns,dryRun);return{ok:true,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};
|
|
5
5
|
//# sourceMappingURL=install.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/mutation-guard
|
|
3
|
+
*
|
|
4
|
+
* Provides SDK-first pre-write provenance and secret-detection guardrails for
|
|
5
|
+
* CLI, MCP, and package-owned mutation dispatchers.
|
|
6
|
+
*/
|
|
7
|
+
import { SECRET_RULES } from "../core/shared/secret-rules.js";
|
|
1
8
|
/** Configurable handling mode for credential-shaped mutation content. */
|
|
2
9
|
export type SecretGuardMode = "off" | "advise" | "block";
|
|
3
10
|
/** Settings consumed by the shared mutation guard. */
|
|
@@ -12,7 +19,7 @@ export interface MutationGuardSettings {
|
|
|
12
19
|
/** One redacted credential-shaped match. Secret values are never returned. */
|
|
13
20
|
export interface SecretGuardFinding {
|
|
14
21
|
/** Stable detector identifier suitable for tests and telemetry. */
|
|
15
|
-
rule:
|
|
22
|
+
rule: (typeof SECRET_RULES)[number]["mutationRule"];
|
|
16
23
|
/** Object path or argument index containing the match. */
|
|
17
24
|
path: string;
|
|
18
25
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
1
|
+
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="eed441f0-e2b5-51e9-b9bf-0f4882a9eeea")}catch(e2){}})();import{SECRET_RULES,acceptsSecretMatch}from"../core/shared/secret-rules.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const PM_MUTATION_ACTION_CONTRACTS=[{action:"append",history_scope:"item"},{action:"claim",history_scope:"item"},{action:"close",history_scope:"item"},{action:"close-many",history_scope:"item"},{action:"close-task",history_scope:"item"},{action:"comments",history_scope:"conditional"},{action:"config",history_scope:"conditional"},{action:"copy",history_scope:"item"},{action:"create",history_scope:"item"},{action:"delete",history_scope:"item"},{action:"deps",history_scope:"conditional"},{action:"discover",history_scope:"item"},{action:"docs",history_scope:"conditional"},{action:"files",history_scope:"conditional"},{action:"focus",history_scope:"conditional"},{action:"gc",history_scope:"conditional"},{action:"history-compact",history_scope:"history"},{action:"history-author-acknowledge",history_scope:"workspace"},{action:"history-redact",history_scope:"history"},{action:"history-repair",history_scope:"history"},{action:"init",history_scope:"workspace"},{action:"install",history_scope:"conditional"},{action:"learnings",history_scope:"conditional"},{action:"notes",history_scope:"conditional"},{action:"package",history_scope:"conditional"},{action:"pause-task",history_scope:"item"},{action:"plan",history_scope:"conditional"},{action:"profile",history_scope:"conditional"},{action:"release",history_scope:"item"},{action:"restore",history_scope:"item"},{action:"schema",history_scope:"conditional"},{action:"start-task",history_scope:"item"},{action:"templates",history_scope:"conditional"},{action:"test",history_scope:"conditional"},{action:"test-all",history_scope:"conditional"},{action:"update",history_scope:"item"},{action:"update-many",history_scope:"item"},{action:"upgrade",history_scope:"conditional"}];const MUTATION_ACTION_CONTRACTS_BY_NAME=new Map(PM_MUTATION_ACTION_CONTRACTS.map(contract=>[contract.action,contract]));function stringLeaves(value,path="$",seen=new WeakSet){if(typeof value==="string"){return[{path,value}]}if(typeof value!=="object"||value===null){return[]}if(seen.has(value)){return[]}seen.add(value);if(Array.isArray(value)){return value.flatMap((entry,index)=>stringLeaves(entry,`${path}[${index}]`,seen))}return Object.entries(value).flatMap(([key,entry])=>stringLeaves(entry,`${path}.${key}`,seen))}function scanMutationSecrets(payload){const findings=[];for(const leaf of stringLeaves(payload)){for(const rule of SECRET_RULES){rule.regex.lastIndex=0;for(const match of leaf.value.matchAll(rule.regex)){if(acceptsSecretMatch(rule,match)){findings.push({rule:rule.mutationRule,path:leaf.path});break}}}}return findings.filter((finding,index,all)=>all.findIndex(candidate=>candidate.rule===finding.rule&&candidate.path===finding.path)===index)}function evaluateMutationGuard(options){const author=options.author.trim()||"unknown";if(options.settings.require_attributed_author&&author.toLowerCase()==="unknown"){throw new PmCliError("Mutation author is required by workspace policy.",EXIT_CODE.USAGE,{code:"mutation_author_required",required:"Pass --author <id>, set PM_AUTHOR, or configure author_default.",nextSteps:["Retry with --author <stable-agent-id>.","Set PM_AUTHOR for this agent session."]})}if(options.settings.secret_guard==="off"){return{author,findings:[],warnings:[],override_applied:false}}let findings;try{findings=scanMutationSecrets(options.payload)}catch{return{author,findings:[],warnings:["secret_guard_scan_failed_open"],override_applied:false}}if(findings.length===0){return{author,findings,warnings:[],override_applied:false}}const ruleSummary=[...new Set(findings.map(finding=>finding.rule))].sort().join(",");if(options.settings.secret_guard==="block"&&options.force!==true){throw new PmCliError(`Mutation blocked by secret guard (${ruleSummary}). No detected value was logged.`,EXIT_CODE.CONFLICT,{code:"mutation_secret_guard_blocked",required:"Remove the credential-shaped content or retry with --force after explicit review.",nextSteps:["Replace the value with a secret-manager reference.","Use pm history-redact if equivalent content already entered history."],recovery:{recovery_mode:"compact",attempted_command:"pm <mutation> [REDACTED]",normalized_args:["[REDACTED]"],suggested_retry:"Remove credential-shaped content, then retry the mutation."}})}return{author,findings,warnings:[`secret_guard_detected:${findings.length}:rules=${ruleSummary}`,...options.settings.secret_guard==="block"&&options.force===true?["secret_guard_force_override"]:[]],override_applied:options.settings.secret_guard==="block"&&options.force===true}}function isMutationAction(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.has(action.trim().toLowerCase())}function listMutationActions(){return[...MUTATION_ACTION_CONTRACTS_BY_NAME.keys()].sort((left,right)=>left.localeCompare(right))}function getMutationActionContract(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.get(action.trim().toLowerCase())}export{PM_MUTATION_ACTION_CONTRACTS,evaluateMutationGuard,getMutationActionContract,isMutationAction,listMutationActions,scanMutationSecrets};
|
|
2
2
|
//# sourceMappingURL=mutation-guard.js.map
|