@unbrained/pm-cli 2026.9.23 → 2026.9.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +6 -20
  3. package/CHANGELOG.md +37 -0
  4. package/dist/cli/error-guidance.d.ts +9 -0
  5. package/dist/cli/error-guidance.js +1 -1
  6. package/dist/cli/help-content.js +1 -1
  7. package/dist/cli-bundle/bundle-manifest.json +153 -153
  8. package/dist/cli-bundle/chunks/{chunk-KOUT5SAA.js → chunk-25KTCTBL.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-E6ZES2X7.js → chunk-3IB4THWX.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-PFWCAI4X.js → chunk-445ARBKY.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-VO7DHENB.js → chunk-5AP67ZXH.js} +3 -3
  12. package/dist/cli-bundle/chunks/{chunk-SXRMM72R.js → chunk-COFQCKF4.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-WBB5B7G5.js → chunk-CZ3VJJEA.js} +2 -2
  14. package/dist/cli-bundle/chunks/{chunk-OBTHQ6UX.js → chunk-GAU55CWI.js} +28 -28
  15. package/dist/cli-bundle/chunks/{chunk-ZSVINTXT.js → chunk-GOWZEBTH.js} +2 -2
  16. package/dist/cli-bundle/chunks/{chunk-7IABMUB6.js → chunk-JDHWZYZU.js} +73 -73
  17. package/dist/cli-bundle/chunks/{chunk-ZWSWR2GX.js → chunk-P7ZSJOWH.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-6WBR6GMW.js → chunk-YABJDOOX.js} +63 -62
  19. package/dist/cli-bundle/chunks/{chunk-NBGSRFZE.js → chunk-YDIGMRLZ.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-list-query-IFPQ4YRD.js → register-list-query-IZ7ZVTDE.js} +2 -2
  21. package/dist/cli-bundle/chunks/{register-mutation-C3O7OSXW.js → register-mutation-IPAZTFDZ.js} +2 -2
  22. package/dist/cli-bundle/chunks/{register-operations-LGO7DBZ7.js → register-operations-APN33LNI.js} +2 -2
  23. package/dist/cli-bundle/chunks/{register-setup-4EW2PPZD.js → register-setup-HPHIKG5U.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/{chunk-YE6F5HGF.js → chunk-ABA3KRPY.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-S5JYIFFS.js → chunk-HQPTDJLF.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-FIQYVDLL.js → chunk-IXNNMP2R.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-KRIHJH5U.js +75 -0
  28. package/dist/cli-bundle/focused-chunks/{chunk-LZOEUNHR.js → chunk-MK5AKKHK.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-6CWR2QFM.js → chunk-NRHTXBTI.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-L554BKFH.js → chunk-NVVB2HJC.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-PIHQNL32.js → chunk-O7MIBEMK.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-BKXYQQDT.js → chunk-OY3VHCTX.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-N2JYYEHM.js → chunk-PMPFLA6X.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-HJA6OHWT.js → chunk-Q355NPLH.js} +28 -28
  35. package/dist/cli-bundle/focused-chunks/{chunk-BQSKFRL2.js → chunk-TLF6N56J.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-QREDJ77O.js → chunk-UIFVIE5F.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-KMNH4G6Y.js → chunk-V2X37U6J.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-E5AOEXF5.js → chunk-XKH2FC5D.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/{chunk-BULUJEOL.js → chunk-Y2M4QLAA.js} +2 -2
  40. package/dist/cli-bundle/main.js +22 -22
  41. package/dist/cli-bundle/sdk-authoring.js +1 -1
  42. package/dist/cli-bundle/sdk-contracts.js +1 -1
  43. package/dist/cli-bundle/sdk-core.js +30 -45
  44. package/dist/cli-bundle/sdk-governance.js +1 -1
  45. package/dist/cli-bundle/sdk-graph.js +1 -1
  46. package/dist/cli-bundle/sdk-merge.js +58 -42
  47. package/dist/cli-bundle/sdk-query.js +1 -1
  48. package/dist/cli-bundle/sdk-runtime.js +1 -1
  49. package/dist/cli-bundle/sdk-testing.js +1 -1
  50. package/dist/cli-bundle/sdk.js +9 -10
  51. package/dist/core/policy/workflow-policy-store.js +1 -1
  52. package/dist/core/shared/errors.d.ts +13 -0
  53. package/dist/core/shared/errors.js +1 -1
  54. package/dist/sdk/completion/bash.js +1 -1
  55. package/dist/sdk/governance/health.d.ts +1 -1
  56. package/dist/sdk/governance/health.js +1 -1
  57. package/dist/sdk/init-agent-guidance.d.ts +2 -1
  58. package/dist/sdk/init-agent-guidance.js +2 -2
  59. package/dist/sdk/merge/install.js +2 -2
  60. package/dist/sdk/merge/worktree-config.d.ts +9 -0
  61. package/dist/sdk/merge/worktree-config.js +2 -0
  62. package/dist/sdk/public-surface.json +1 -1
  63. package/docs/AGENT_RECOVERY_AND_GUIDANCE.md +49 -0
  64. package/docs/CHATGPT_CODEX_PLUGIN_IMPLEMENTATION.md +2 -0
  65. package/docs/CLAUDE_CODE_PLUGIN.md +18 -7
  66. package/docs/CODEX_PLUGIN.md +5 -12
  67. package/docs/DEFECT_RECURRENCE.md +8 -1
  68. package/docs/DEPENDENCY_KIND_CONTRACT.md +27 -0
  69. package/docs/MERGE_SAFETY.md +28 -5
  70. package/docs/README.md +1 -0
  71. package/docs/RELEASE_RELIABILITY.md +70 -0
  72. package/docs/RELEASING.md +4 -0
  73. package/docs/TESTING.md +17 -0
  74. package/docs/VALIDATION_PORTABILITY.md +20 -11
  75. package/marketplace.json +2 -2
  76. package/package.json +6 -5
  77. package/packages/pm-beads/package.json +1 -1
  78. package/packages/pm-calendar/package.json +1 -1
  79. package/packages/pm-command-kit/package.json +1 -1
  80. package/packages/pm-digital-twin/package.json +1 -1
  81. package/packages/pm-governance-audit/package.json +1 -1
  82. package/packages/pm-guide-shell/package.json +1 -1
  83. package/packages/pm-kanban/package.json +1 -1
  84. package/packages/pm-lifecycle-hooks/package.json +1 -1
  85. package/packages/pm-linked-test-adapters/package.json +1 -1
  86. package/packages/pm-search-advanced/package.json +1 -1
  87. package/packages/pm-templates/package.json +1 -1
  88. package/packages/pm-todos/package.json +1 -1
  89. package/packages/pm-vcs/package.json +1 -1
  90. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  91. package/plugins/pm-claude/README.md +6 -5
  92. package/plugins/pm-claude/hooks/hooks.json +1 -1
  93. package/plugins/pm-claude/hooks/session-start.mjs +9 -6
  94. package/plugins/pm-claude/package.json +9 -0
  95. package/plugins/pm-claude/scripts/plugin-runtime.mjs +84 -0
  96. package/plugins/pm-claude/scripts/pm-mcp-server.mjs +39 -96
  97. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  98. package/plugins/pm-codex/.mcp.json +0 -3
  99. package/plugins/pm-codex/README.md +1 -1
  100. package/plugins/pm-codex/package.json +9 -0
  101. package/plugins/pm-codex/scripts/plugin-runtime.mjs +84 -0
  102. package/plugins/pm-codex/scripts/pm-mcp-server.mjs +38 -99
  103. package/dist/cli-bundle/focused-chunks/chunk-3JAEZ7WI.js +0 -59
@@ -1,3 +1,3 @@
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]="e5ae3e66-4a45-5c62-9144-46f8f5bff7cf")}catch(e2){}})();import fs from"node:fs/promises";import path from"node:path";import readline from"node:readline/promises";import{stdin as input,stdout as output}from"node:process";import{runActiveOnWriteHooks}from"../core/extensions/index.js";import{pathExists,writeFileAtomic}from"../core/fs/fs-utils.js";const INIT_AGENT_GUIDANCE_MODE_VALUES=["ask","add","skip","status"];const AGENT_GUIDANCE_TARGET_FILENAMES=["AGENTS.md","CLAUDE.md"];const AGENT_GUIDANCE_REQUIRED_TOKENS=["pm init","pm context","pm search","pm create","pm claim","pm files","pm docs","pm test --run","pm close","pm release","pm_author"];const AGENT_GUIDANCE_REQUIRED_TOKEN_THRESHOLD=8;const AGENT_GUIDANCE_TEMPLATE_VERSION=1;const AGENT_GUIDANCE_START_MARKER_PREFIX="<!-- pm-cli:agent-guidance:start:";const AGENT_GUIDANCE_START_MARKER=`<!-- pm-cli:agent-guidance:start:v${AGENT_GUIDANCE_TEMPLATE_VERSION} -->`;const AGENT_GUIDANCE_END_MARKER="<!-- pm-cli:agent-guidance:end -->";const AGENT_GUIDANCE_ADD_LATER_HINT="Add workflow guidance later: pm init --agent-guidance add";const _testOnly={toPortableRelativePath,ensureTrailingNewline,detectLineEnding,findGuidanceBlockRange,buildAgentGuidanceBlock,upsertAgentGuidanceBlock,resolveProjectRoot,resolveTargetGuidancePath,parsePromptChoice,pushUnique,promptForGuidanceWrite,normalizeAgentGuidanceState,applyAgentGuidanceState,writeGuidanceFile,setAgentGuidanceReadlineFactoryForTests};let createAgentGuidanceReadlineInterface=()=>readline.createInterface({input,output});function setAgentGuidanceReadlineFactoryForTests(factory){createAgentGuidanceReadlineInterface=factory??(()=>readline.createInterface({input,output}))}function toPortableRelativePath(projectRoot,targetPath){const relative=path.relative(projectRoot,targetPath);if(relative.length===0){return path.basename(targetPath)}return relative.split(path.sep).join("/")}function ensureTrailingNewline(value){return value.endsWith("\n")?value:`${value}
2
- `}function detectLineEnding(value){return value.includes("\r\n")?"\r\n":"\n"}function findGuidanceBlockRange(content){const startIndex=content.indexOf(AGENT_GUIDANCE_START_MARKER_PREFIX);if(startIndex===-1){return null}const endMarkerIndex=content.indexOf(AGENT_GUIDANCE_END_MARKER,startIndex);if(endMarkerIndex===-1){return null}return{start_index:startIndex,end_index:endMarkerIndex+AGENT_GUIDANCE_END_MARKER.length}}function hasGuidanceMarker(content){return findGuidanceBlockRange(content)!==null}function buildAgentGuidanceBlock(lineEnding,projectTestCommand="<your project test command>"){const lines=[AGENT_GUIDANCE_START_MARKER,"## pm Workflow (Agent Quickstart)","",'- Cold start with the measured canonical orientation: `pm context --limit 10 --for orient`; before item mutation, also run `pm search "<keywords>" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`.',"- Claim and execute: `pm claim <id>` then `pm update <id> --status in_progress`.",`- Link evidence while coding: \`pm files <id> --add ...\`, \`pm docs <id> --add ...\`, \`pm test <id> --add command="${projectTestCommand}"\`.`,'- Verify and close: `pm test <id> --run --progress`, then atomically record closure evidence with `pm close <id> "<reason>" --resolution "<what changed>" --expected "<expected outcome>" --actual "<observed outcome>" --validate-close warn`, then `pm release <id>`.',"- Author identity is automatic for supported agent harnesses; use `--author` only for an explicit override.","",AGENT_GUIDANCE_END_MARKER,""];return lines.join(lineEnding)}function upsertAgentGuidanceBlock(existingContent,projectTestCommand="<your project test command>"){const lineEnding=detectLineEnding(existingContent);const nextBlock=buildAgentGuidanceBlock(lineEnding,projectTestCommand);const existingRange=findGuidanceBlockRange(existingContent);if(existingRange){const trailingContent=existingContent.slice(existingRange.end_index);const normalizedTrailingContent=trailingContent.startsWith("\r\n")?trailingContent.slice(2):trailingContent.startsWith("\n")?trailingContent.slice(1):trailingContent;const nextContent2=ensureTrailingNewline(`${existingContent.slice(0,existingRange.start_index)}${nextBlock}${normalizedTrailingContent}`);return{next_content:nextContent2,changed:nextContent2!==existingContent}}const separator=existingContent.length===0?"":existingContent.endsWith("\n")?"\n":"\n\n";const nextContent=ensureTrailingNewline(`${existingContent}${separator}${nextBlock}`);return{next_content:nextContent,changed:nextContent!==existingContent}}async function resolveProjectTestCommand(projectRoot){if(await pathExists(path.join(projectRoot,"scripts","run-tests.mjs"))){return"node scripts/run-tests.mjs test"}let packageDocument;try{packageDocument=JSON.parse(await fs.readFile(path.join(projectRoot,"package.json"),"utf8"))}catch{return"<your project test command>"}const manifest=normalizeProjectPackageManifest(packageDocument);if(manifest===void 0){return"<your project test command>"}const packageManager=manifest.packageManager?.split("@")[0];if(packageManager==="pnpm"){return"pnpm test"}if(packageManager==="bun"){return"bun run test"}if(packageManager==="yarn"){return"yarn test"}if(packageManager==="npm")return"npm test";if(await pathExists(path.join(projectRoot,"pnpm-lock.yaml"))){return"pnpm test"}if(await pathExists(path.join(projectRoot,"bun.lock"))||await pathExists(path.join(projectRoot,"bun.lockb"))){return"bun run test"}if(await pathExists(path.join(projectRoot,"yarn.lock")))return"yarn test";return"npm test"}function normalizeProjectPackageManifest(value){if(typeof value!=="object"||value===null||!("scripts"in value)){return void 0}const scripts=value.scripts;if(typeof scripts!=="object"||scripts===null||!("test"in scripts)||typeof scripts.test!=="string"||scripts.test.trim().length===0){return void 0}return{scripts:{test:scripts.test},..."packageManager"in value&&typeof value.packageManager==="string"?{packageManager:value.packageManager}:{}}}function resolveProjectRoot(pmRoot,cwd){const parent=path.dirname(pmRoot);if(path.basename(pmRoot)==="pm"&&path.basename(parent)===".agents"){return path.dirname(parent)}return path.resolve(cwd,pmRoot)}function resolveTargetGuidancePath(scans,projectRoot){const existingAgents=scans.find(entry=>path.basename(entry.file_path)==="AGENTS.md"&&entry.exists);if(existingAgents){return existingAgents.file_path}const existingAny=scans.find(entry=>entry.exists);if(existingAny){return existingAny.file_path}return path.join(projectRoot,"AGENTS.md")}function parsePromptChoice(answer,currentDefault){const normalized=answer.trim().toLowerCase();if(normalized.length===0){return currentDefault}if(normalized==="y"||normalized==="yes"){return true}if(normalized==="n"||normalized==="no"){return false}return currentDefault}async function promptForGuidanceWrite(targetRelativePath){const rl=createAgentGuidanceReadlineInterface();try{output.write("\nAgent guidance check\n");output.write("No AGENTS.md/CLAUDE.md file currently contains compact pm workflow guidance.\n");const answer=await rl.question(`Add a compact pm workflow section to ${targetRelativePath}? [Y/n] `);output.write("\n");return parsePromptChoice(answer,true)}finally{rl.close()}}function normalizeAgentGuidanceState(settings){const current=settings.agent_guidance;return{prompt_completed:current?.prompt_completed===true,declined:current?.declined===true,declined_at:typeof current?.declined_at==="string"?current.declined_at:"",template_version:typeof current?.template_version==="number"&&Number.isInteger(current.template_version)&&current.template_version>0?current.template_version:AGENT_GUIDANCE_TEMPLATE_VERSION,last_checked_files:Array.isArray(current?.last_checked_files)?[...new Set(current.last_checked_files.map(value=>value.trim()).filter(value=>value.length>0))].sort((left,right)=>left.localeCompare(right)):[]}}async function scanGuidanceFiles(projectRoot){const scans=[];for(const filename of AGENT_GUIDANCE_TARGET_FILENAMES){const filePath=path.join(projectRoot,filename);const exists=await pathExists(filePath);if(!exists){scans.push({file_path:filePath,exists,has_guidance:false,has_marker:false});continue}const content=await fs.readFile(filePath,"utf8");const contentLower=content.toLowerCase();const tokenHits=AGENT_GUIDANCE_REQUIRED_TOKENS.filter(token=>contentLower.includes(token));const hasMarker=hasGuidanceMarker(content);scans.push({file_path:filePath,exists,has_guidance:hasMarker||tokenHits.length>=AGENT_GUIDANCE_REQUIRED_TOKEN_THRESHOLD,has_marker:hasMarker})}return scans}function pushUnique(target,value){if(!target.includes(value)){target.push(value)}}function applyAgentGuidanceState(settings,currentState){const existing=normalizeAgentGuidanceState(settings);const changed=JSON.stringify(existing)!==JSON.stringify(currentState);if(changed){settings.agent_guidance=currentState}return{changed,state:currentState}}async function writeGuidanceFile(filePath,projectRoot=path.dirname(filePath)){const exists=await pathExists(filePath);const currentContent=exists?await fs.readFile(filePath,"utf8"):"";const nextContent=upsertAgentGuidanceBlock(currentContent,await resolveProjectTestCommand(projectRoot));if(!nextContent.changed){return{changed:false,warnings:[]}}await writeFileAtomic(filePath,nextContent.next_content);return{changed:true,warnings:await runActiveOnWriteHooks({path:filePath,scope:"project",op:"init:agent_guidance_file"})}}function buildInitAgentGuidanceSummary(params){return{mode:params.mode,present:params.scans.some(entry=>entry.has_guidance),prompted:params.prompted,applied:params.applied,skipped:params.skipped,declined:params.state.declined,prompt_completed:params.state.prompt_completed,template_version:params.state.template_version,target_file:params.targetRelativePath,checked_files:params.scans.map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path)),files_with_guidance:params.scans.filter(entry=>entry.has_guidance).map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path)),missing_files:params.scans.filter(entry=>!entry.exists).map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path))}}async function refreshGuidanceScansAfterApply(applied,scans,projectRoot){return applied?await scanGuidanceFiles(projectRoot):scans}function markAgentGuidanceRunState(flow,checkedFiles,partial){flow.state={...flow.state,...partial,template_version:AGENT_GUIDANCE_TEMPLATE_VERSION,last_checked_files:checkedFiles}}async function addAgentGuidanceBlockIfMissing(flow,context){if(context.presentBefore){return}const writeResult=await writeGuidanceFile(context.targetPath,context.projectRoot);flow.warnings.push(...writeResult.warnings);if(writeResult.changed){flow.applied=true;flow.warnings.push(`agent_guidance:added:${context.targetRelativePath}`)}}function handleAgentGuidanceStatusMode(flow,presentBefore){if(!presentBefore){flow.warnings.push("agent_guidance:missing");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}}function handleAgentGuidanceSkipMode(flow,checkedFiles){flow.skipped=true;markAgentGuidanceRunState(flow,checkedFiles,{prompt_completed:true,declined:true,declined_at:flow.state.declined_at||new Date().toISOString()});flow.warnings.push("agent_guidance:explicit_skip");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function handleAgentGuidanceAddMode(flow,context){await addAgentGuidanceBlockIfMissing(flow,context);markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:false,declined_at:""})}function handlePresentAgentGuidance(flow,checkedFiles){if(flow.state.declined){markAgentGuidanceRunState(flow,checkedFiles,{prompt_completed:true,declined:false,declined_at:""})}}function handleDeclinedAgentGuidance(flow){flow.skipped=true;flow.warnings.push("agent_guidance:skipped_declined");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function handleInteractiveAgentGuidancePrompt(flow,context){flow.prompted=true;const approved=await promptForGuidanceWrite(context.targetRelativePath);if(approved){await addAgentGuidanceBlockIfMissing(flow,context);markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:false,declined_at:""});return}flow.skipped=true;markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:true,declined_at:new Date().toISOString()});flow.warnings.push("agent_guidance:declined");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}function handleNonInteractiveMissingAgentGuidance(flow){flow.warnings.push("agent_guidance:missing_non_interactive");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function applyAgentGuidanceMode(initialState,context){const flow={warnings:[],nextSteps:[],prompted:false,applied:false,skipped:false,state:initialState};if(context.mode==="status"){handleAgentGuidanceStatusMode(flow,context.presentBefore);return flow}if(context.mode==="skip"){handleAgentGuidanceSkipMode(flow,context.checkedFiles);return flow}if(context.mode==="add"){await handleAgentGuidanceAddMode(flow,context);return flow}if(context.presentBefore){handlePresentAgentGuidance(flow,context.checkedFiles);return flow}if(flow.state.prompt_completed&&flow.state.declined){handleDeclinedAgentGuidance(flow);return flow}if(context.interactive){await handleInteractiveAgentGuidancePrompt(flow,context);return flow}handleNonInteractiveMissingAgentGuidance(flow);return flow}async function runInitAgentGuidance(options){const projectRoot=resolveProjectRoot(options.pm_root,options.cwd);let scans=await scanGuidanceFiles(projectRoot);const targetPath=resolveTargetGuidancePath(scans,projectRoot);const targetRelativePath=toPortableRelativePath(projectRoot,targetPath);const checkedFiles=scans.map(entry=>toPortableRelativePath(projectRoot,entry.file_path));const presentBefore=scans.some(entry=>entry.has_guidance);const flow=await applyAgentGuidanceMode(normalizeAgentGuidanceState(options.settings),{mode:options.mode,interactive:options.interactive,targetPath,targetRelativePath,checkedFiles,presentBefore,projectRoot});const stateUpdate=applyAgentGuidanceState(options.settings,flow.state);scans=await refreshGuidanceScansAfterApply(flow.applied,scans,projectRoot);const summary=buildInitAgentGuidanceSummary({mode:options.mode,scans,projectRoot,targetRelativePath,prompted:flow.prompted,applied:flow.applied,skipped:flow.skipped,state:flow.state});return{summary,warnings:flow.warnings,next_steps:flow.nextSteps,settings_changed:stateUpdate.changed}}export{INIT_AGENT_GUIDANCE_MODE_VALUES,_testOnly,resolveProjectTestCommand,runInitAgentGuidance};
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]="f0c60332-623f-52c0-b217-acb15fcd2218")}catch(e2){}})();import fs from"node:fs/promises";import path from"node:path";import readline from"node:readline/promises";import{stdin as input,stdout as output}from"node:process";import{runActiveOnWriteHooks}from"../core/extensions/index.js";import{pathExists,writeFileAtomic}from"../core/fs/fs-utils.js";const INIT_AGENT_GUIDANCE_MODE_VALUES=["ask","add","skip","status"];const AGENT_GUIDANCE_TARGET_FILENAMES=["AGENTS.md","CLAUDE.md"];const AGENT_GUIDANCE_REQUIRED_TOKENS=["pm init","pm context","pm search","pm create","pm claim","pm files","pm docs","pm test --run","pm close","pm release","pm_author"];const AGENT_GUIDANCE_REQUIRED_TOKEN_THRESHOLD=8;const AGENT_GUIDANCE_TEMPLATE_VERSION=1;const AGENT_GUIDANCE_START_MARKER_PREFIX="<!-- pm-cli:agent-guidance:start:";const AGENT_GUIDANCE_START_MARKER=`<!-- pm-cli:agent-guidance:start:v${AGENT_GUIDANCE_TEMPLATE_VERSION} -->`;const AGENT_GUIDANCE_END_MARKER="<!-- pm-cli:agent-guidance:end -->";const AGENT_GUIDANCE_ADD_LATER_HINT="Add workflow guidance later: pm init --agent-guidance add";const _testOnly={toPortableRelativePath,ensureTrailingNewline,detectLineEnding,findGuidanceBlockRange,buildAgentGuidanceBlock,upsertAgentGuidanceBlock,resolveProjectRoot,resolveTargetGuidancePath,parsePromptChoice,pushUnique,promptForGuidanceWrite,normalizeAgentGuidanceState,applyAgentGuidanceState,writeGuidanceFile,setAgentGuidanceReadlineFactoryForTests};let createAgentGuidanceReadlineInterface=()=>readline.createInterface({input,output});function setAgentGuidanceReadlineFactoryForTests(factory){createAgentGuidanceReadlineInterface=factory??(()=>readline.createInterface({input,output}))}function toPortableRelativePath(projectRoot,targetPath){const relative=path.relative(projectRoot,targetPath);if(relative.length===0){return path.basename(targetPath)}return relative.split(path.sep).join("/")}function ensureTrailingNewline(value){return value.endsWith("\n")?value:`${value}
2
+ `}function detectLineEnding(value){return value.includes("\r\n")?"\r\n":"\n"}function findGuidanceBlockRange(content){const startIndex=content.indexOf(AGENT_GUIDANCE_START_MARKER_PREFIX);if(startIndex===-1){return null}const endMarkerIndex=content.indexOf(AGENT_GUIDANCE_END_MARKER,startIndex);if(endMarkerIndex===-1){return null}return{start_index:startIndex,end_index:endMarkerIndex+AGENT_GUIDANCE_END_MARKER.length}}function hasGuidanceMarker(content){return findGuidanceBlockRange(content)!==null}function buildAgentGuidanceBlock(lineEnding,projectTestCommand="<your project test command>"){const lines=[AGENT_GUIDANCE_START_MARKER,"## pm Workflow (Agent Quickstart)","",'- Cold start with the measured canonical orientation: `pm context --limit 10 --for orient`; before item mutation, also run `pm search "<keywords>" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`.',"- Claim and start atomically: `pm claim <id> --start`.",`- Link evidence while coding: \`pm files <id> --add ...\`, \`pm docs <id> --add ...\`, \`pm test <id> --add command="${projectTestCommand}"\`.`,'- Verify and close: `pm test <id> --run --progress`, then atomically record closure evidence with `pm close <id> "<reason>" --resolution "<what changed>" --expected "<expected outcome>" --actual "<observed outcome>" --validate-close warn`, then `pm release <id>`.',"- Author identity is automatic for supported agent harnesses; use `--author` only for an explicit override.","",AGENT_GUIDANCE_END_MARKER,""];return lines.join(lineEnding)}function upsertAgentGuidanceBlock(existingContent,projectTestCommand="<your project test command>"){const lineEnding=detectLineEnding(existingContent);const nextBlock=buildAgentGuidanceBlock(lineEnding,projectTestCommand);const existingRange=findGuidanceBlockRange(existingContent);if(existingRange){const trailingContent=existingContent.slice(existingRange.end_index);const normalizedTrailingContent=trailingContent.startsWith("\r\n")?trailingContent.slice(2):trailingContent.startsWith("\n")?trailingContent.slice(1):trailingContent;const nextContent2=ensureTrailingNewline(`${existingContent.slice(0,existingRange.start_index)}${nextBlock}${normalizedTrailingContent}`);return{next_content:nextContent2,changed:nextContent2!==existingContent}}const separator=existingContent.length===0?"":existingContent.endsWith("\n")?"\n":"\n\n";const nextContent=ensureTrailingNewline(`${existingContent}${separator}${nextBlock}`);return{next_content:nextContent,changed:nextContent!==existingContent}}async function resolveProjectTestCommand(projectRoot){if(await pathExists(path.join(projectRoot,"scripts","run-tests.mjs"))){return"node scripts/run-tests.mjs test"}let packageDocument;try{packageDocument=JSON.parse(await fs.readFile(path.join(projectRoot,"package.json"),"utf8"))}catch{return"<your project test command>"}const manifest=normalizeProjectPackageManifest(packageDocument);if(manifest===void 0){return"<your project test command>"}const packageManager=manifest.packageManager?.split("@")[0];if(packageManager==="pnpm"){return"pnpm test"}if(packageManager==="bun"){return"bun run test"}if(packageManager==="yarn"){return"yarn test"}if(packageManager==="npm")return"npm test";if(await pathExists(path.join(projectRoot,"pnpm-lock.yaml"))){return"pnpm test"}if(await pathExists(path.join(projectRoot,"bun.lock"))||await pathExists(path.join(projectRoot,"bun.lockb"))){return"bun run test"}if(await pathExists(path.join(projectRoot,"yarn.lock")))return"yarn test";return"npm test"}function normalizeProjectPackageManifest(value){if(typeof value!=="object"||value===null||!("scripts"in value)){return void 0}const scripts=value.scripts;if(typeof scripts!=="object"||scripts===null||!("test"in scripts)||typeof scripts.test!=="string"||scripts.test.trim().length===0){return void 0}return{scripts:{test:scripts.test},..."packageManager"in value&&typeof value.packageManager==="string"?{packageManager:value.packageManager}:{}}}function resolveProjectRoot(pmRoot,cwd){const parent=path.dirname(pmRoot);if(path.basename(pmRoot)==="pm"&&path.basename(parent)===".agents"){return path.dirname(parent)}return path.resolve(cwd,pmRoot)}function resolveTargetGuidancePath(scans,projectRoot){const existingAgents=scans.find(entry=>path.basename(entry.file_path)==="AGENTS.md"&&entry.exists);if(existingAgents){return existingAgents.file_path}const existingAny=scans.find(entry=>entry.exists);if(existingAny){return existingAny.file_path}return path.join(projectRoot,"AGENTS.md")}function parsePromptChoice(answer,currentDefault){const normalized=answer.trim().toLowerCase();if(normalized.length===0){return currentDefault}if(normalized==="y"||normalized==="yes"){return true}if(normalized==="n"||normalized==="no"){return false}return currentDefault}async function promptForGuidanceWrite(targetRelativePath){const rl=createAgentGuidanceReadlineInterface();try{output.write("\nAgent guidance check\n");output.write("No AGENTS.md/CLAUDE.md file currently contains compact pm workflow guidance.\n");const answer=await rl.question(`Add a compact pm workflow section to ${targetRelativePath}? [Y/n] `);output.write("\n");return parsePromptChoice(answer,true)}finally{rl.close()}}function normalizeAgentGuidanceState(settings){const current=settings.agent_guidance;return{prompt_completed:current?.prompt_completed===true,declined:current?.declined===true,declined_at:typeof current?.declined_at==="string"?current.declined_at:"",template_version:typeof current?.template_version==="number"&&Number.isInteger(current.template_version)&&current.template_version>0?current.template_version:AGENT_GUIDANCE_TEMPLATE_VERSION,last_checked_files:Array.isArray(current?.last_checked_files)?[...new Set(current.last_checked_files.map(value=>value.trim()).filter(value=>value.length>0))].sort((left,right)=>left.localeCompare(right)):[]}}async function scanGuidanceFiles(projectRoot){const scans=[];const testCommand=await resolveProjectTestCommand(projectRoot);for(const filename of AGENT_GUIDANCE_TARGET_FILENAMES){const filePath=path.join(projectRoot,filename);const exists=await pathExists(filePath);if(!exists){scans.push({file_path:filePath,exists,has_guidance:false,has_marker:false});continue}const content=await fs.readFile(filePath,"utf8");const contentLower=content.toLowerCase();const tokenHits=AGENT_GUIDANCE_REQUIRED_TOKENS.filter(token=>contentLower.includes(token));const hasMarker=hasGuidanceMarker(content);scans.push({file_path:filePath,exists,has_guidance:hasMarker||tokenHits.length>=AGENT_GUIDANCE_REQUIRED_TOKEN_THRESHOLD,has_marker:hasMarker,outdated:hasMarker&&upsertAgentGuidanceBlock(content,testCommand).changed})}return scans}function pushUnique(target,value){if(!target.includes(value)){target.push(value)}}function applyAgentGuidanceState(settings,currentState){const existing=normalizeAgentGuidanceState(settings);const changed=JSON.stringify(existing)!==JSON.stringify(currentState);if(changed){settings.agent_guidance=currentState}return{changed,state:currentState}}async function writeGuidanceFile(filePath,projectRoot=path.dirname(filePath)){const exists=await pathExists(filePath);const currentContent=exists?await fs.readFile(filePath,"utf8"):"";const nextContent=upsertAgentGuidanceBlock(currentContent,await resolveProjectTestCommand(projectRoot));if(!nextContent.changed){return{changed:false,warnings:[]}}await writeFileAtomic(filePath,nextContent.next_content);return{changed:true,warnings:await runActiveOnWriteHooks({path:filePath,scope:"project",op:"init:agent_guidance_file"})}}function buildInitAgentGuidanceSummary(params){return{mode:params.mode,present:params.scans.some(entry=>entry.has_guidance),prompted:params.prompted,applied:params.applied,skipped:params.skipped,declined:params.state.declined,prompt_completed:params.state.prompt_completed,template_version:params.state.template_version,target_file:params.targetRelativePath,checked_files:params.scans.map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path)),files_with_guidance:params.scans.filter(entry=>entry.has_guidance).map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path)),missing_files:params.scans.filter(entry=>!entry.exists).map(entry=>toPortableRelativePath(params.projectRoot,entry.file_path))}}async function refreshGuidanceScansAfterApply(applied,scans,projectRoot){return applied?await scanGuidanceFiles(projectRoot):scans}function markAgentGuidanceRunState(flow,checkedFiles,partial){flow.state={...flow.state,...partial,template_version:AGENT_GUIDANCE_TEMPLATE_VERSION,last_checked_files:checkedFiles}}async function addAgentGuidanceBlockIfMissing(flow,context){if(context.presentBefore){return}const writeResult=await writeGuidanceFile(context.targetPath,context.projectRoot);flow.warnings.push(...writeResult.warnings);if(writeResult.changed){flow.applied=true;flow.warnings.push(`agent_guidance:added:${context.targetRelativePath}`)}}function handleAgentGuidanceStatusMode(flow,presentBefore){if(!presentBefore){flow.warnings.push("agent_guidance:missing");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}}function handleAgentGuidanceSkipMode(flow,checkedFiles){flow.skipped=true;markAgentGuidanceRunState(flow,checkedFiles,{prompt_completed:true,declined:true,declined_at:flow.state.declined_at||new Date().toISOString()});flow.warnings.push("agent_guidance:explicit_skip");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function handleAgentGuidanceAddMode(flow,context){await addAgentGuidanceBlockIfMissing(flow,context);markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:false,declined_at:""})}function handlePresentAgentGuidance(flow,checkedFiles){if(flow.state.declined){markAgentGuidanceRunState(flow,checkedFiles,{prompt_completed:true,declined:false,declined_at:""})}}function handleDeclinedAgentGuidance(flow){flow.skipped=true;flow.warnings.push("agent_guidance:skipped_declined");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function handleInteractiveAgentGuidancePrompt(flow,context){flow.prompted=true;const approved=await promptForGuidanceWrite(context.targetRelativePath);if(approved){await addAgentGuidanceBlockIfMissing(flow,context);markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:false,declined_at:""});return}flow.skipped=true;markAgentGuidanceRunState(flow,context.checkedFiles,{prompt_completed:true,declined:true,declined_at:new Date().toISOString()});flow.warnings.push("agent_guidance:declined");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}function handleNonInteractiveMissingAgentGuidance(flow){flow.warnings.push("agent_guidance:missing_non_interactive");pushUnique(flow.nextSteps,AGENT_GUIDANCE_ADD_LATER_HINT)}async function applyAgentGuidanceMode(initialState,context){const flow={warnings:[],nextSteps:[],prompted:false,applied:false,skipped:false,state:initialState};if(context.mode==="status"){handleAgentGuidanceStatusMode(flow,context.presentBefore);return flow}if(context.mode==="skip"){handleAgentGuidanceSkipMode(flow,context.checkedFiles);return flow}if(context.mode==="add"){await handleAgentGuidanceAddMode(flow,context);return flow}if(context.presentBefore){handlePresentAgentGuidance(flow,context.checkedFiles);return flow}if(flow.state.prompt_completed&&flow.state.declined){handleDeclinedAgentGuidance(flow);return flow}if(context.interactive){await handleInteractiveAgentGuidancePrompt(flow,context);return flow}handleNonInteractiveMissingAgentGuidance(flow);return flow}async function runInitAgentGuidance(options){const projectRoot=resolveProjectRoot(options.pm_root,options.cwd);let scans=await scanGuidanceFiles(projectRoot);const targetPath=resolveTargetGuidancePath(scans,projectRoot);const targetRelativePath=toPortableRelativePath(projectRoot,targetPath);const checkedFiles=scans.map(entry=>toPortableRelativePath(projectRoot,entry.file_path));const presentBefore=scans.some(entry=>entry.has_guidance);const flow=await applyAgentGuidanceMode(normalizeAgentGuidanceState(options.settings),{mode:options.mode,interactive:options.interactive,targetPath,targetRelativePath,checkedFiles,presentBefore,projectRoot});if(options.mode==="add"){for(const scan of scans.filter(entry=>entry.outdated)){const refreshed=await writeGuidanceFile(scan.file_path,projectRoot);flow.applied||=refreshed.changed;flow.warnings.push(...refreshed.warnings)}}const stateUpdate=applyAgentGuidanceState(options.settings,flow.state);scans=await refreshGuidanceScansAfterApply(flow.applied,scans,projectRoot);const outdated=scans.filter(entry=>entry.outdated).map(entry=>toPortableRelativePath(projectRoot,entry.file_path));if(outdated.length>0){flow.warnings.push(`agent_guidance_outdated:${outdated.join(",")}`);pushUnique(flow.nextSteps,"pm init --agent-guidance add")}const summary=buildInitAgentGuidanceSummary({mode:options.mode,scans,projectRoot,targetRelativePath,prompted:flow.prompted,applied:flow.applied,skipped:flow.skipped,state:flow.state});return{summary,warnings:flow.warnings,next_steps:flow.nextSteps,settings_changed:stateUpdate.changed}}export{INIT_AGENT_GUIDANCE_MODE_VALUES,_testOnly,resolveProjectTestCommand,runInitAgentGuidance};
3
3
  //# sourceMappingURL=init-agent-guidance.js.map
@@ -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]="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}
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]="8f999825-ace7-55cd-b83c-8c6c4b94fd9f")}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";import{gitWorkspaceEnvironment,resolveMergeDriverConfigScope}from"./worktree-config.js";import{discoverProjectRuntimeVersionPins}from"../environment/project-runtime-compatibility.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,env:gitWorkspaceEnvironment(),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 isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,version){const exactPins=discoverProjectRuntimeVersionPins(workspaceRoot).filter(pin=>pin.constraint==="exact");if(exactPins.some(pin=>pin.version!==version))return false;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 false}return true}async function isPortableMergeDriverCommand(configured,expectedSuffix,workspaceRoot){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==="string"?manifest.bin:manifest.bin?.pm;if(!await isMergeDriverWorkspaceCompatible(packageRoot,workspaceRoot,manifest.version))return false;return manifest.name==="@unbrained/pm-cli"&&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=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};
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 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}`}`;try{const{stdout}=await execFileAsync("git",["config","--get",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),encoding:"utf8",windowsHide:true,timeout:1e4});const configured=stdout.trim();if(!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));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{const configScope=await resolveMergeDriverConfigScope(canonicalWorkspaceRoot);for(const entry of gitConfigEntries){await execFileAsync("git",["config",configScope,entry.key,entry.value],{cwd:canonicalWorkspaceRoot,env:gitWorkspaceEnvironment(),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
@@ -0,0 +1,9 @@
1
+ /** Preserve caller tools and preferences while binding Git repository discovery and writes to the explicit cwd. */
2
+ export declare function gitWorkspaceEnvironment(): NodeJS.ProcessEnv;
3
+ /**
4
+ * Keep simple clones on local config. Linked worktrees opt into Git's native
5
+ * per-worktree layer, first preserving the main worktree's special core keys.
6
+ * Drivers already installed in the main tree are copied into its layer so
7
+ * later installations cannot replace the main tree's runtime selection.
8
+ */
9
+ export declare function resolveMergeDriverConfigScope(workspaceRoot: string): Promise<"--local" | "--worktree">;
@@ -0,0 +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]="725f175b-c548-5700-8e85-ea0b5dce3747")}catch(e2){}})();import{execFile}from"node:child_process";import path from"node:path";import{promisify}from"node:util";const execFileAsync=promisify(execFile);function gitWorkspaceEnvironment(){const env={...process.env};for(const key of["GIT_DIR","GIT_WORK_TREE","GIT_COMMON_DIR","GIT_INDEX_FILE","GIT_OBJECT_DIRECTORY","GIT_ALTERNATE_OBJECT_DIRECTORIES","GIT_NAMESPACE","GIT_CEILING_DIRECTORIES","GIT_DISCOVERY_ACROSS_FILESYSTEM"]){delete env[key]}return env}async function readLocalGitConfig(cwd,key){try{const type=key==="core.bare"||key==="extensions.worktreeConfig"?["--type=bool"]:[];return(await execFileAsync("git",["config","--local",...type,"--get",key],{cwd,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim()}catch(error){if(typeof error==="object"&&error!==null&&"code"in error&&error.code===1)return void 0;throw error}}async function resolveMergeDriverConfigScope(workspaceRoot){if(await readLocalGitConfig(workspaceRoot,"extensions.worktreeConfig")==="true")return"--worktree";const{stdout}=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-dir","--git-common-dir"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4});const[gitDir,commonDir]=stdout.trim().split(/\r?\n/);if(gitDir===commonDir)return"--local";const mainConfig=path.join(commonDir,"config.worktree");for(const key of["core.bare","core.worktree"]){const value=await readLocalGitConfig(workspaceRoot,key);if(value===void 0||key==="core.bare"&&value!=="true")continue;await execFileAsync("git",["config","--file",mainConfig,key,value],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4});await execFileAsync("git",["config","--local","--unset-all",key],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})}for(const driver of["pm-item-toon","pm-item-markdown","pm-history","pm-relationship","pm-json"]){for(const field of["name","driver"]){const key=`merge.${driver}.${field}`;const value=await readLocalGitConfig(workspaceRoot,key);if(value!==void 0)await execFileAsync("git",["config","--file",mainConfig,key,value],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})}}await execFileAsync("git",["config","--local","extensions.worktreeConfig","true"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4});return"--worktree"}export{gitWorkspaceEnvironment,resolveMergeDriverConfigScope};
2
+ //# sourceMappingURL=worktree-config.js.map