claude-dev-env 2.5.0 → 2.7.1

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 (153) hide show
  1. package/CLAUDE.md +20 -57
  2. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -1
  3. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +12 -2
  4. package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +309 -0
  5. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +540 -0
  6. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +206 -70
  7. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/__init__.py +1 -0
  8. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/_repo_test_helpers.py +76 -0
  9. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_baseline_import_isolation.py +248 -0
  10. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +309 -0
  11. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +91 -58
  12. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +202 -0
  13. package/_shared/pr-loop/worker-spawn.md +3 -1
  14. package/agents/CLAUDE.md +1 -1
  15. package/agents/code-verifier.md +36 -7
  16. package/bin/codex-compat.mjs +104 -0
  17. package/bin/codex-compat.test.mjs +51 -0
  18. package/codex-capability-map.json +13 -0
  19. package/docs/CODE_RULES.md +2 -0
  20. package/docs/codex-compatibility.md +25 -0
  21. package/docs/nas-ssh-invocation.md +96 -12
  22. package/docs/references/code-review-enforcement.md +31 -6
  23. package/hooks/blocking/CLAUDE.md +3 -0
  24. package/hooks/blocking/config/code_review_enforcement_constants.py +40 -10
  25. package/hooks/blocking/config/test_code_review_enforcement_constants.py +56 -3
  26. package/hooks/blocking/eli11_reply_enforcer.py +479 -0
  27. package/hooks/blocking/gh_body_arg_blocker.py +1 -1
  28. package/hooks/blocking/nas_ssh_binary_enforcer.py +8 -46
  29. package/hooks/blocking/shell_substitution_blocker.py +129 -0
  30. package/hooks/blocking/state_description_blocker.py +1 -1
  31. package/hooks/blocking/stop_dispatcher.py +1 -1
  32. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +2 -3
  33. package/hooks/blocking/test_eli11_reply_enforcer.py +457 -0
  34. package/hooks/blocking/test_shell_substitution_blocker.py +124 -0
  35. package/hooks/blocking/test_stop_dispatcher.py +23 -0
  36. package/hooks/blocking/test_unscoped_search_blocker.py +102 -0
  37. package/hooks/blocking/test_verdict_directory_write_blocker.py +4 -8
  38. package/hooks/blocking/unscoped_search_blocker.py +391 -0
  39. package/hooks/git-hooks/CLAUDE.md +3 -0
  40. package/hooks/git-hooks/conftest.py +30 -0
  41. package/hooks/git-hooks/gate_utils.py +2 -2
  42. package/hooks/git-hooks/git_hooks_constants/__init__.py +41 -2
  43. package/hooks/git-hooks/pre_push.py +75 -4
  44. package/hooks/git-hooks/pre_push_base_reference.py +166 -0
  45. package/hooks/git-hooks/test_config.py +0 -15
  46. package/hooks/git-hooks/test_gate_utils.py +3 -15
  47. package/hooks/git-hooks/test_pre_commit.py +1 -15
  48. package/hooks/git-hooks/test_pre_push.py +236 -27
  49. package/hooks/git-hooks/test_pre_push_base_reference.py +339 -0
  50. package/hooks/hooks.json +0 -12
  51. package/hooks/hooks_constants/CLAUDE.md +5 -1
  52. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +4 -4
  53. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +101 -0
  54. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +2 -8
  55. package/hooks/hooks_constants/shell_command_segments.py +82 -0
  56. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +67 -0
  57. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -0
  58. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +5 -6
  59. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +1 -0
  60. package/hooks/hooks_constants/unscoped_search_blocker_constants.py +153 -0
  61. package/package.json +4 -2
  62. package/rules/CLAUDE.md +17 -23
  63. package/rules/agent-spawn-protocol.md +6 -6
  64. package/rules/anti-corollary-tests.md +1 -1
  65. package/rules/bdd.md +1 -1
  66. package/rules/cleanup-temp-files.md +10 -4
  67. package/rules/code-standards.md +7 -0
  68. package/rules/conservative-action.md +1 -5
  69. package/rules/context7.md +0 -4
  70. package/rules/destructive-commands.md +47 -0
  71. package/rules/doc-inventory-integrity.md +48 -0
  72. package/rules/doc-prose-cuts.md +58 -0
  73. package/rules/docstring-prose-matches-implementation.md +10 -2
  74. package/rules/durable-post-artifacts.md +0 -4
  75. package/rules/eli11-replies.md +31 -0
  76. package/rules/explore-thoroughly.md +4 -4
  77. package/rules/falsify-before-green.md +68 -0
  78. package/rules/file-global-constants.md +1 -1
  79. package/rules/filesystem-search.md +51 -0
  80. package/rules/gh-cli-conventions.md +27 -0
  81. package/rules/git-workflow.md +26 -0
  82. package/rules/hedging-claims.md +9 -0
  83. package/rules/long-horizon-autonomy.md +0 -4
  84. package/rules/measurement-denominators.md +48 -0
  85. package/rules/nas-ssh-invocation.md +23 -5
  86. package/rules/parallel-tools.md +2 -2
  87. package/rules/plain-illustrative-docstrings.md +3 -7
  88. package/rules/plain-language.md +2 -0
  89. package/rules/proof-of-work-pr-comments.md +0 -4
  90. package/rules/re-stage-before-commit.md +2 -0
  91. package/rules/research-mode.md +10 -0
  92. package/rules/shell-invocation.md +21 -0
  93. package/rules/testing.md +4 -0
  94. package/rules/verified-commit-gate-skip.md +3 -27
  95. package/rules/verify-before-asking.md +5 -0
  96. package/rules/windows-filesystem-safe.md +1 -1
  97. package/rules/workers-done-before-complete.md +4 -0
  98. package/scripts/CLAUDE.md +3 -3
  99. package/scripts/Migrate-ShellPolicy.ps1 +1 -1
  100. package/scripts/codex_capability_bridge.py +171 -0
  101. package/scripts/codex_compat_materializer.py +1087 -0
  102. package/scripts/codex_compat_watcher.py +502 -0
  103. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  104. package/scripts/dev_env_scripts_constants/code_review_constants.py +37 -0
  105. package/scripts/dev_env_scripts_constants/grok_worker_constants.py +79 -13
  106. package/scripts/grok_headless_runner.py +213 -16
  107. package/scripts/invoke_code_review.py +11 -4
  108. package/scripts/resolve_worker_spawn.py +56 -10
  109. package/scripts/spawn_grok_batch.py +43 -22
  110. package/scripts/sync_to_cursor/rules.py +0 -10
  111. package/scripts/test_grok_headless_runner.py +592 -10
  112. package/scripts/test_invoke_code_review.py +143 -0
  113. package/scripts/test_invoke_code_review_chain.py +1 -1
  114. package/scripts/test_invoke_code_review_contract.py +1 -1
  115. package/scripts/test_resolve_worker_spawn.py +179 -15
  116. package/scripts/test_spawn_grok_batch.py +225 -22
  117. package/scripts/tests/test_code_review_constants.py +80 -0
  118. package/scripts/tests/test_codex_capability_bridge.py +91 -0
  119. package/scripts/tests/test_codex_compat_materializer.py +632 -0
  120. package/scripts/tests/test_codex_compat_watcher.py +599 -0
  121. package/scripts/tests/test_sync_to_cursor.py +0 -1
  122. package/skills/autoconverge/workflow/converge.contract.test.mjs +28 -6
  123. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +73 -0
  124. package/skills/autoconverge/workflow/converge.mjs +64 -15
  125. package/skills/bugteam/reference/copilot-gap-analysis.md +1 -1
  126. package/skills/condensing-instructions/SKILL.md +42 -51
  127. package/skills/fresh-branch/CLAUDE.md +1 -1
  128. package/skills/fresh-branch/SKILL.md +5 -6
  129. package/skills/fresh-branch/scripts/create_fresh_branch.py +42 -24
  130. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -3
  131. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +30 -126
  132. package/skills/grok-spawn/SKILL.md +5 -3
  133. package/skills/grok-spawn/reference/flag-profiles.md +3 -1
  134. package/skills/orchestrator/SKILL.md +23 -9
  135. package/skills/orchestrator-refresh/SKILL.md +20 -1
  136. package/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
  137. package/skills/session-log/SKILL.md +1 -1
  138. package/rules/claude-md-orphan-file.md +0 -28
  139. package/rules/cleanup-command-forms.md +0 -23
  140. package/rules/code-reviews.md +0 -11
  141. package/rules/env-var-table-code-drift.md +0 -10
  142. package/rules/gh-body-file.md +0 -5
  143. package/rules/gh-paginate.md +0 -3
  144. package/rules/hook-prose-matches-detector.md +0 -15
  145. package/rules/no-historical-clutter.md +0 -26
  146. package/rules/no-inline-destructive-literals.md +0 -9
  147. package/rules/no-justification-noise.md +0 -61
  148. package/rules/package-inventory-stale-entry.md +0 -25
  149. package/rules/right-sized-engineering.md +0 -28
  150. package/rules/self-contained-docs.md +0 -17
  151. package/rules/shell-invocation-policy.md +0 -5
  152. package/rules/state-what-is.md +0 -25
  153. package/rules/tdd.md +0 -7
@@ -258,6 +258,7 @@ const verifyObjectionModule = new Function(
258
258
  `${functionSource('parseLastVerdictFence')}\n` +
259
259
  `${constantLine('VERIFY_OBJECTION_FALLBACK')}\n` +
260
260
  `${functionSource('renderVerifyObjectionLine')}\n` +
261
+ `${functionSource('extractPreFenceProse')}\n` +
261
262
  `${functionSource('extractVerifyObjection')}\n` +
262
263
  'return { extractVerifyObjection, VERIFY_OBJECTION_FALLBACK };',
263
264
  )();
@@ -338,6 +339,78 @@ test('extractVerifyObjection falls back when no finding yields usable text', ()
338
339
  assert.equal(extractVerifyObjection(transcript), VERIFY_OBJECTION_FALLBACK);
339
340
  });
340
341
 
342
+ test('the prose reader anchors on the last CLOSED fence, not a stray unterminated marker', () => {
343
+ const transcript =
344
+ 'check X never showed red\n\n' +
345
+ '```verdict\n{"all_pass": false, "findings": []}\n```\n\n' +
346
+ 'and here I started to restate it\n\n' +
347
+ '```verdict';
348
+ const objection = extractVerifyObjection(transcript);
349
+ assert.equal(objection, 'check X never showed red');
350
+ assert.doesNotMatch(objection, /all_pass/, 'expected the verdict body never to be read back as prose');
351
+ });
352
+
353
+ test('the prose reader skips a scaffolding-only paragraph above the fence', () => {
354
+ const transcript =
355
+ 'check X never showed red\n\n' + '## Verdict\n\n' + '```verdict\n{"all_pass": false, "findings": []}\n```';
356
+ assert.equal(extractVerifyObjection(transcript), 'check X never showed red');
357
+ });
358
+
359
+ const verifyRecoveryPromptModule = new Function(
360
+ `${constantLine('VERIFY_OBJECTION_FALLBACK')}\n` +
361
+ 'const prCoordinates = "owner/repo#1";\n' +
362
+ 'const PRE_COMMIT_GATE_STEP = "";\n' +
363
+ 'const EDIT_SCHEMA = {};\n' +
364
+ 'const TIERS = { sonnetMedium: {} };\n' +
365
+ 'const convergeAgent = (spawnPrompt) => spawnPrompt;\n' +
366
+ `${functionSource('runCodeEditorTask')}\n` +
367
+ 'return { runCodeEditorTask };',
368
+ )();
369
+
370
+ const { runCodeEditorTask: buildCodeEditorPrompt } = verifyRecoveryPromptModule;
371
+
372
+ function buildVerifyRecoveryPrompt(verifyTranscript) {
373
+ return buildCodeEditorPrompt('verify-recover', {
374
+ objection: extractVerifyObjection(verifyTranscript),
375
+ head: 'deadbeefcafe',
376
+ sourceLabel: 'round-1 lens findings',
377
+ attempt: 1,
378
+ });
379
+ }
380
+
381
+ const INCOMPLETE_CHECK_SENTENCE =
382
+ 'This verdict is incomplete: the deliberate break for check_docstring_runon_sentence never showed red, so that gate is unproven rather than failed.';
383
+
384
+ test('an incomplete verdict carries its named check into the fixer prompt instead of the fallback', () => {
385
+ const transcript =
386
+ 'I ran the named gates and read the diff against the task text.\n\n' +
387
+ `${INCOMPLETE_CHECK_SENTENCE}\n\n` +
388
+ '```verdict\n{"all_pass": false, "findings": [], "manifest_sha256": "0f1e2d"}\n```';
389
+ const fixerPrompt = buildVerifyRecoveryPrompt(transcript);
390
+ assert.match(fixerPrompt, /VERIFY-RECOVERY fixer/, 'expected the verify-recovery fixer prompt');
391
+ assert.ok(
392
+ fixerPrompt.includes(INCOMPLETE_CHECK_SENTENCE),
393
+ `expected the fixer prompt to carry the incomplete-check sentence, got:\n${fixerPrompt.slice(0, 600)}`,
394
+ );
395
+ assert.doesNotMatch(
396
+ fixerPrompt,
397
+ /without a parseable verdict/,
398
+ 'expected the incomplete verdict never to reach the fixer as VERIFY_OBJECTION_FALLBACK',
399
+ );
400
+ });
401
+
402
+ test('a genuine code defect still reaches the fixer prompt from findings, unchanged by pre-fence prose', () => {
403
+ const transcript =
404
+ `${INCOMPLETE_CHECK_SENTENCE}\n\n` +
405
+ '```verdict\n{"all_pass": false, "findings": [{"check": "Finding 1", "detail": "boundary still over-blocks"}]}\n```';
406
+ const fixerPrompt = buildVerifyRecoveryPrompt(transcript);
407
+ assert.match(fixerPrompt, /1\. Finding 1 — boundary still over-blocks/);
408
+ assert.ok(
409
+ !fixerPrompt.includes(INCOMPLETE_CHECK_SENTENCE),
410
+ 'expected findings to keep owning a verdict that names a code defect',
411
+ );
412
+ });
413
+
341
414
  test('the verify-recover task in runCodeEditorTask is a clean-coder edit step bound to the verifier objection and leaves changes uncommitted', () => {
342
415
  const recoverBody = functionSource('runCodeEditorTask');
343
416
  assert.match(recoverBody, /agentType:\s*'clean-coder'/, 'expected the fixer to use clean-coder');
@@ -68,13 +68,15 @@ const HEADLESS_EDIT_PREAMBLE =
68
68
  '- When your run was given a result schema, your final action is always the StructuredOutput call. If the poll budget is spent before the awaited signal arrives, call StructuredOutput with the whole time-out result the step documents — for the Copilot gate, the full down result {sha, clean:false, down:true, findings:[]}, never a bare down flag — rather than ending the turn without a result.\n\n'
69
69
 
70
70
  const HEADLESS_READONLY_DESTRUCTIVE_POINTER =
71
- '- Never run a destructive command (rm -rf, git reset --hard, dd, mkfs, chmod -R, a fork bomb) and never place its literal text in a Bash command: this step reads only and edits nothing, so it needs no destructive command. If a step seems to require one, report it as a blocker rather than running it.\n'
71
+ '- Never run a destructive command (rm -rf, git reset --hard, dd, mkfs, chmod -R, a fork bomb) and never place its literal text in a Bash command: this step makes no edit to the tree it reads, so it needs no destructive command. If a step seems to require one, report it as a blocker rather than running it.\n'
72
72
 
73
73
  /**
74
74
  * The read-only preamble a review, verify, or utility agent receives: the full
75
- * edit preamble with the rm-shape-rules bullet dropped, since an agent that edits
76
- * nothing never runs rm and the shape rules add no value to its prompt. The
77
- * one-line destructive pointer keeps the escape-hatch guidance in view. The
75
+ * edit preamble with the rm-shape-rules bullet dropped, since an agent that makes
76
+ * no edit in the tree it reads never runs rm against that tree, and the one file
77
+ * it may touch off that tree a deliberate break at a break site outside it —
78
+ * is an edit rather than a delete, so the shape rules add no value to its
79
+ * prompt. The one-line destructive pointer keeps the escape-hatch guidance in view. The
78
80
  * derivation reads the single rm-shape bullet out of the edit preamble and swaps
79
81
  * the pointer in, so the two preambles share every other clause from one source.
80
82
  */
@@ -381,7 +383,7 @@ function runCodeEditorTask(task, context) {
381
383
  `You are the COMMIT step opening the environment-hardening PR (${context.sourceLabel}) for the change staged in ${context.hardeningRepoPath} on branch ${context.hardeningBranch}. The edit step left the hooks/rules edits in the working tree and the verify step passed, so a verifier verdict already binds to this exact working tree. Do NOT touch the PR's own branch.\n\n` +
382
384
  `Rules:\n` +
383
385
  `- Make NO further file edits of any kind. Any edit changes the surface and invalidates the verdict that unlocks the commit gate, so the push would be blocked. Only commit and push what is already there.\n` +
384
- `- In ${context.hardeningRepoPath}: make ONE commit of the staged hooks/rules change on branch ${context.hardeningBranch}, push it, then open a DRAFT PR. The PR body references the follow-up issue ${context.issueUrl || '(none)'} and states the PR hardens the environment so the deferred violation classes are blocked at Write/Edit time. Honor the gh-body-file rule: write a BOM-free temp file and pass --body-file.\n` +
386
+ `- In ${context.hardeningRepoPath}: make ONE commit of the staged hooks/rules change on branch ${context.hardeningBranch}, push it, then open a DRAFT PR. The PR body references the follow-up issue ${context.issueUrl || '(none)'} and states the PR hardens the environment so the deferred violation classes are blocked at Write/Edit time. Honor the gh-cli-conventions rule: write a BOM-free temp file and pass --body-file.\n` +
385
387
  `- Title the PR as a Conventional Commit — a type prefix (feat, fix, chore, docs, refactor, perf, ci, style, test, build, revert), an optional scope in parentheses, then a colon and a short summary, e.g. "feat(hooks): block the deferred violation class". The target repo's CI validates the PR title as a semantic commit and rejects a non-conforming title.\n\n` +
386
388
  `Return the full https URL of the DRAFT hardening PR in hardeningPrUrl (empty string when no PR was opened) and a one-line summary.`,
387
389
  { label, phase: 'Converge', schema: HARDENING_COMMIT_SCHEMA, agentType: 'clean-coder', ...TIERS.sonnetMedium },
@@ -430,7 +432,7 @@ function runVerifierTask(task, context) {
430
432
  if (task === 'fix-verify') {
431
433
  const findingsBlock = renderFindingsBlock(context.findings)
432
434
  return convergeReadOnlyAgent(
433
- `You are the VERIFY step for ${context.findings.length} finding(s) (${context.sourceLabel}) on ${prCoordinates}, HEAD ${context.head}. The edit step left fixes in the working tree, uncommitted. Do NO edits of any kind — verification only; any edit invalidates the verdict you are about to emit.\n\n` +
435
+ `You are the VERIFY step for ${context.findings.length} finding(s) (${context.sourceLabel}) on ${prCoordinates}, HEAD ${context.head}. The edit step left fixes in the working tree, uncommitted. Make NO edit to the tree under verification — verification only; any edit inside that tree invalidates the verdict you are about to emit.\n\n` +
434
436
  `Findings the working-tree fixes must address:\n${findingsBlock}\n\n` +
435
437
  `Steps:\n` +
436
438
  `1. Resolve the worktree repo root for running tests: REPO=$(git rev-parse --show-toplevel).\n` +
@@ -444,7 +446,7 @@ function runVerifierTask(task, context) {
444
446
  ? context.failures.map((each, position) => `${position + 1}. ${each}`).join('\n')
445
447
  : 'none reported'
446
448
  return convergeReadOnlyAgent(
447
- `You are the VERIFY step for the convergence repair on ${prCoordinates}, HEAD ${context.head}. The edit step left its repair in the working tree (a bot-thread fix uncommitted, and/or a rebase onto origin/main), unpushed. Do NO edits of any kind — verification only; any edit invalidates the verdict you are about to emit.\n\n` +
449
+ `You are the VERIFY step for the convergence repair on ${prCoordinates}, HEAD ${context.head}. The edit step left its repair in the working tree (a bot-thread fix uncommitted, and/or a rebase onto origin/main), unpushed. Make NO edit to the tree under verification — verification only; any edit inside that tree invalidates the verdict you are about to emit.\n\n` +
448
450
  `Concerns the working-tree repair must resolve (the gates the convergence check flagged):\n${failureBlock}\n\n` +
449
451
  `Steps:\n` +
450
452
  `1. Resolve the worktree repo root for running tests: REPO=$(git rev-parse --show-toplevel).\n` +
@@ -454,7 +456,7 @@ function runVerifierTask(task, context) {
454
456
  )
455
457
  }
456
458
  return convergeReadOnlyAgent(
457
- `You are the VERIFY step for an environment-hardening change (${context.sourceLabel}) staged in the working tree of ${context.hardeningRepoPath}. The edit step left the hooks/rules edits uncommitted there. Do NO edits of any kind — verification only; any edit invalidates the verdict you are about to emit.\n\n` +
459
+ `You are the VERIFY step for an environment-hardening change (${context.sourceLabel}) staged in the working tree of ${context.hardeningRepoPath}. The edit step left the hooks/rules edits uncommitted there. Make NO edit to the tree under verification — verification only; any edit inside that tree invalidates the verdict you are about to emit.\n\n` +
458
460
  `Concern the working-tree change must resolve: the edited hooks/rules block the code-standard violation classes from the deferred round at Write/Edit time, and a hook change carries a passing test per CODE_RULES.\n\n` +
459
461
  `Steps:\n` +
460
462
  `1. cd into ${context.hardeningRepoPath}, then resolve its repo root: REPO=$(git rev-parse --show-toplevel).\n` +
@@ -468,7 +470,7 @@ function runVerifierTask(task, context) {
468
470
  " ```verdict\n" +
469
471
  ` {"all_pass": true, "findings": [], "manifest_sha256": "<that hash>"}\n` +
470
472
  " ```\n" +
471
- ` When verification fails, set all_pass to false and list the unresolved concerns in findings; still include the manifest_sha256. The verdict fence must be the last thing in your message.`,
473
+ ` Set all_pass to false when verification fails, and list every code defect you found in findings. When the verdict is incomplete because a check it rests on never showed red, set all_pass to false and name that check in prose directly above the fence rather than in findings. Always include the manifest_sha256. The verdict fence must be the last thing in your message.`,
472
474
  { label, phase: 'Converge', agentType: 'code-verifier', ...TIERS.sonnetMedium },
473
475
  )
474
476
  }
@@ -871,7 +873,7 @@ function buildVerdictFenceSteps(prOwner, prRepo, prNumber) {
871
873
  " ```verdict\n" +
872
874
  ` {"all_pass": true, "findings": [], "manifest_sha256": "<that hash>"}\n` +
873
875
  " ```\n" +
874
- ` When verification fails, set all_pass to false and list the unresolved concerns in findings; still include the manifest_sha256. The verdict fence must be the last thing in your message.`
876
+ ` Set all_pass to false when verification fails, and list every code defect you found in findings. When the verdict is incomplete because a check it rests on never showed red, set all_pass to false and name that check in prose directly above the fence rather than in findings. Always include the manifest_sha256. The verdict fence must be the last thing in your message.`
875
877
  )
876
878
  }
877
879
 
@@ -1343,14 +1345,61 @@ function renderVerifyObjectionLine(eachFinding) {
1343
1345
  return stringifiedFinding === '{}' ? null : stringifiedFinding
1344
1346
  }
1345
1347
 
1348
+ /**
1349
+ * Read the prose paragraph the verifier wrote directly above its last verdict
1350
+ * fence. A verdict that is incomplete — a check it rests on never showed red —
1351
+ * is not a code defect, so the fence contract puts that reason here rather than
1352
+ * in findings; this reader is how the reason survives into the re-fix step.
1353
+ *
1354
+ * Anchors on the same fence parseLastVerdictFence reads — the last CLOSED
1355
+ * verdict fence, not the last bare marker — so a stray unterminated marker after
1356
+ * the real fence can never make this read the verdict body itself as prose. A
1357
+ * candidate paragraph carrying only markdown scaffolding (a heading, a fence
1358
+ * delimiter) is skipped, since handing the fixer a heading is worse than the
1359
+ * generic fallback the caller keeps.
1360
+ *
1361
+ * ::
1362
+ *
1363
+ * 'ran the gates\n\ncheck X never showed red\n\n```verdict\n{}\n```'
1364
+ * -> 'check X never showed red'
1365
+ * '```verdict\n{}\n```' -> null
1366
+ *
1367
+ * @param {string|null|undefined} verifyTranscript the verifier transcript text
1368
+ * @returns {string|null} the last prose paragraph above the final fence, or null when there is none
1369
+ */
1370
+ function extractPreFenceProse(verifyTranscript) {
1371
+ if (typeof verifyTranscript !== 'string') return null
1372
+ const fenceMarker = '```verdict'
1373
+ let lastFenceStart = verifyTranscript.lastIndexOf(fenceMarker)
1374
+ while (lastFenceStart !== -1 && !verifyTranscript.slice(lastFenceStart + fenceMarker.length).includes('```')) {
1375
+ lastFenceStart = verifyTranscript.lastIndexOf(fenceMarker, lastFenceStart - 1)
1376
+ }
1377
+ if (lastFenceStart === -1) return null
1378
+ const proseParagraphs = verifyTranscript
1379
+ .slice(0, lastFenceStart)
1380
+ .split(/\n\s*\n/)
1381
+ .map((eachParagraph) => eachParagraph.trim())
1382
+ .filter((eachParagraph) =>
1383
+ eachParagraph
1384
+ .split('\n')
1385
+ .some((eachLine) => {
1386
+ const trimmedLine = eachLine.trim()
1387
+ return trimmedLine.length > 0 && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('```') && /[A-Za-z]/.test(trimmedLine)
1388
+ }),
1389
+ )
1390
+ return proseParagraphs.length === 0 ? null : proseParagraphs[proseParagraphs.length - 1]
1391
+ }
1392
+
1346
1393
  /**
1347
1394
  * Pull the verifier's stated objections out of a failed verify transcript so the
1348
1395
  * re-fix step knows what the verdict rejected. Reads the last fenced verdict JSON
1349
1396
  * (the same block verdictPassed reads) and renders each finding through
1350
- * renderVerifyObjectionLine into a numbered list. A missing fence, a parse
1351
- * failure, an empty findings list, or a findings list where no entry yields
1352
- * usable text falls back to a generic re-read instruction, so the re-fix step
1353
- * always receives actionable text.
1397
+ * renderVerifyObjectionLine into a numbered list. When findings yields no usable
1398
+ * line the verdict is an incomplete one rather than a code-defect one, so the
1399
+ * prose paragraph above the fence where the fence contract puts the unshown-red
1400
+ * check carries the objection instead. A missing fence, a parse failure, or an
1401
+ * empty findings list with no prose above the fence falls back to a generic
1402
+ * re-read instruction, so the re-fix step always receives actionable text.
1354
1403
  * @param {string|null|undefined} verifyTranscript the failed verifier transcript text
1355
1404
  * @returns {string} a human-readable block of the verifier's objections
1356
1405
  */
@@ -1361,7 +1410,7 @@ function extractVerifyObjection(verifyTranscript) {
1361
1410
  const renderedObjections = allObjections
1362
1411
  .map((eachFinding) => renderVerifyObjectionLine(eachFinding))
1363
1412
  .filter((eachLine) => eachLine !== null)
1364
- if (renderedObjections.length === 0) return VERIFY_OBJECTION_FALLBACK
1413
+ if (renderedObjections.length === 0) return extractPreFenceProse(verifyTranscript) || VERIFY_OBJECTION_FALLBACK
1365
1414
  return renderedObjections.map((eachLine, position) => `${position + 1}. ${eachLine}`).join('\n')
1366
1415
  }
1367
1416
 
@@ -510,4 +510,4 @@ Expected output after the patch lands: a list containing `Line 91: Collection co
510
510
  - Inventory data sources: live `gh api repos/example-org/example-repo/pulls/{101,102}/comments` filtered to `Copilot`; verbatim bodies preserved in the inventory table above.
511
511
  - Original-commit content used to confirm violations: PR #101 head `29117309cf4ec1e83883160d8c819e0843f9c3ac`; PR #102 review-time commit `e4abf52c3a6c724b4e64bfed0d979cd60a2c8bf0`; PR #102 merged head `c9c935a96cc59d39d623dc7eddda3d341007607c`.
512
512
  - CODE_RULES.md sections invoked by the patch plan: §⚡ Magic values; §5 Extended naming rules (collections `all_orders`, `all_users`); §6.5 File length guidance; §7 Right-Sized Engineering; §10 No redundant data fetches (used as analogue for wrapper plumb-through).
513
- - Constraints honored: `gh-body-file` (no `gh ... --body` calls in the new code paths), `no-shell-substitution` (no `$(...)` in the verification commands above; multi-step shell flows are written as separate Bash invocations or `&&`-chained literal strings).
513
+ - Constraints honored: `gh-cli-conventions` (no `gh ... --body` calls in the new code paths), `shell-invocation` (no `$(...)` in the verification commands above; multi-step shell flows are written as separate Bash invocations or `&&`-chained literal strings).
@@ -1,81 +1,72 @@
1
1
  ---
2
2
  name: condensing-instructions
3
3
  description: >-
4
- Rewrite existing instruction documents or turn first drafts and requirements
5
- into compact, high-information operational instructions without changing or
6
- inventing their contract. Use for prompts, skills, policies, runbooks,
7
- agent-facing documents, reference instructions, token reduction,
8
- deduplication, restructuring, or concise instruction authoring.
4
+ Refine an instruction document for Claude 5 generation models: cut rules the
5
+ model's judgment already covers, move detail behind progressive disclosure,
6
+ and route each remaining piece to the system prompt, CLAUDE.md, a skill, or a
7
+ reference. Use for system prompts, CLAUDE.md files, skills, tool
8
+ descriptions, agent instructions, context engineering, prompt slimming, and
9
+ token reduction.
9
10
  ---
10
11
 
11
12
  # Condensing Instructions
12
13
 
13
- Rewrite an existing instruction document or turn a first draft, notes, or requirements into compact operational instructions. Preserve behavior; reduce length only after the contract is complete and unambiguous.
14
+ Cut an instruction document to what a Claude 5 generation model needs, and move the rest to the surface that loads it on demand. Over 80% of Claude Code's system prompt came out for Claude Opus 5 and Claude Fable 5 with no measurable drop in performance, so treat a long instruction document as a place with room to cut.
14
15
 
15
- ## Preserve the full contract
16
+ ## Clear the conflicts first
16
17
 
17
- Treat every execution-relevant detail as binding: required, prohibited, and permitted behavior; safety and authority boundaries; actors, objects, data or resource kinds, and scope; force, negation, and quantifiers; triggers, defaults, conditions, exceptions, thresholds, and precedence; dependencies, prerequisites, order, timing, duration, persistence, and state; failure and recovery behavior; exact strings, names, paths, URLs, identifiers, commands, flags, numbers, placeholders, schemas, and error text; and input, output, formatting, validation, acceptance, and completion requirements.
18
+ Read the system prompt, the CLAUDE.md, and the skills as one body of text and find the lines that pull against each other "leave documentation as appropriate" sitting beside "DO NOT add comments". A conflict makes the model deliberate at length before it acts. Resolve each pair into one statement, or delete both when neither changes what the model does.
18
19
 
19
- Keep rationale or examples only when they define a decision, boundary, exception, exact value, or required behavior. Preserve required frontmatter, tags, wrappers, templates, schemas, and other machine-read structure.
20
+ ## Trade rules for judgment
20
21
 
21
- When rewriting, change wording and structure, not behavior. When authoring, supply organization, wording, and conventional editorial choices, but do not invent material obligations, permissions, exceptions, or defaults. Ask one concise blocking question when missing, contradictory, or ambiguous information would materially change behavior, safety, authority, scope, precedence, failure handling, or output. Otherwise proceed.
22
+ Delete a rule written to block a worst case, such as file deletion. Claude 5 generation models read the surrounding context and decide well without it, and a rigid rule blocks the user who has a real reason to want the behavior it forbids.
22
23
 
23
- If no instruction material or requirements are provided, respond exactly: `Provide the instructions or draft requirements.`
24
+ State the outcome and the signal the model should read:
24
25
 
25
- ## Build a requirement ledger first
26
+ - Cut: "Default to writing no comments. Never write multi-paragraph docstrings or multi-line comment blocks — one short line max. Don't create planning, decision, or analysis documents unless the user asks for them."
27
+ - Keep: "Write code that reads like the surrounding code: match its comment density, naming, and idiom."
26
28
 
27
- Before deleting, merging, or drafting text, record each atomic commitment internally. Capture its force, actor, trigger, action, object or type, required outcome, scope, timing or persistence, dependencies, defaults, exceptions, precedence, failure behavior, and exact literals when applicable.
29
+ ## Design the interface in place of examples
28
30
 
29
- For an existing document, inventory every behaviorally meaningful statement. Distinguish true duplicates from similar rules that apply to different actors, stages, conditions, or scopes. For a first draft, translate each stated goal into observable behavior or an acceptance condition; mark material gaps instead of silently choosing an answer. Do not promote a preference to a hard rule or average conflicting rules.
31
+ Examples constrain exploration. Carry usage in the tool's own shape: expressive names, expressive parameters, and types that signal intent. A status parameter enumerated as `pending`, `in_progress`, `completed` shows correct use with no example attached.
30
32
 
31
- Do not expose the ledger unless requested.
33
+ ## Load detail at the point of use
32
34
 
33
- ## Group rules by the decisions they control
35
+ Move detail out of always-on text and into a surface the model reaches for:
34
36
 
35
- - Group requirements by function or decision point, not by draft order.
36
- - Default to this sequence when it fits: purpose and scope; inputs and prerequisites; operating defaults; procedures and branches; constraints and safety; outputs and failures; verification. Override it when dependencies require another sequence.
37
- - Place a safety rule or other constraint before the first action it governs.
38
- - Within a group, put prerequisites before actions, general rules before narrow exceptions, and production requirements before their checks.
39
- - Put each exception beside the rule it modifies. State precedence when rules overlap.
40
- - Give each rule one authoritative location. State a shared actor, condition, default, or scope once at the narrowest level that covers every affected rule.
41
- - Let a heading carry scope only when every instruction beneath it clearly inherits that scope. Use the fewest headings that preserve navigation.
37
+ - A skill the model calls when the task calls for it. Code review and verification detail belongs here.
38
+ - A linked file the model opens on demand.
39
+ - A tool with deferred loading, where the model searches for the definition before it uses the tool.
42
40
 
43
- ## Write direct, dense rules
41
+ Keep the always-on context lean and let the model pull the rest.
44
42
 
45
- - Use active, imperative language and concrete verbs. Put a condition before its action and an exception immediately after its default.
46
- - Preserve force and quantifiers. Keep `must`, `never`, `only`, `should`, `may`, `all`, `any`, and exact counts distinct.
47
- - Combine statements only when their actor, force, scope, trigger, timing, persistence, and exceptions align.
48
- - Replace true repetition with one rule and a compact list of affected cases. Keep separate statements when repetition protects distinct stages, scopes, or failure modes.
49
- - Use one sentence per decision. Join clauses only when they form one testable rule.
50
- - Use paragraphs for cohesive rules and lists for parallel obligations, mappings, or branches. Avoid decorative headings and structural ceremony.
51
- - Remove non-operative background, history, rationale, transition text, conversational framing, restatement, setup narration, and examples. Remove inventories or folder maps that merely describe the document.
52
- - Use one term for each concept. Define unfamiliar terms at first use. Do not compress complete sentences into fragments, stacked jargon, or vague shorthand.
53
- - Preserve operational literals character for character, including spelling, case, punctuation, quoting, and placeholders. Preserve whether a list is exhaustive or illustrative; never replace an exact enumeration with `etc.` or a broader category.
43
+ ## Say each thing once
54
44
 
55
- ## Imply only what cannot change behavior
45
+ Put tool usage guidance in the tool description alone. Delete the copy that repeats it in the system prompt.
56
46
 
57
- Rely on ordinary language competence and document conventions only when every reasonable reader would take the same action. State a detail when omitting it could change permission, safety, actor, object or type, scope, force, trigger, sequence, timing, persistence, precedence, failure behavior, exact output, or acceptance.
47
+ ## Let memory carry session facts
58
48
 
59
- Match detail to fragility. Specify exact steps for brittle, high-risk, or order-dependent work. For flexible work, state the required outcome and constraints, then leave the method open.
49
+ Claude's automatic memory captures relevant context and carries it across sessions. Delete hand-written memory notes from CLAUDE.md when memory already holds them.
60
50
 
61
- Never use implication to carry a prohibition, exception, dependency, safety boundary, or exact literal. Do not retain obvious advice that constrains nothing, and do not omit a non-obvious rule because it seems intuitive.
51
+ ## Point at rich references
62
52
 
63
- ## Pass the quality gate
53
+ Include files as references with @mentions. Prefer a reference the model can read with no ambiguity, in this order:
64
54
 
65
- Run these checks silently:
55
+ 1. Code from this or another codebase — the highest fidelity specification available.
56
+ 2. A test suite that pins the behavior.
57
+ 3. An HTML artifact or a mockup.
58
+ 4. A rubric, which lets a verification agent score work against a quality standard.
59
+ 5. Prose description or a screenshot.
66
60
 
67
- 1. **Coverage:** Map every ledger item to a clause in the finished document.
68
- 2. **Support:** Map every material clause back to a stated or confirmed requirement; remove invented behavior.
69
- 3. **Fidelity:** Compare force, negation, quantifiers, actor, object or type, scope, conditions, exceptions, dependencies, order, timing, persistence, failure behavior, outputs, and protected literals. Nothing may be weakened, broadened, narrowed, contradicted, or altered character for character where exactness matters.
70
- 4. **Boundary behavior:** Test the normal case, each branch and exception, prohibited cases, missing dependencies, failures, and acceptance checks when present. Each case must yield the intended action.
71
- 5. **Clarity:** Resolve ambiguous references, hidden precedence, scattered exceptions, inconsistent terms, and unclear qualifier scope.
72
- 6. **Density:** Delete any sentence that changes no behavior, safety boundary, interpretation, or validation result. Merge remaining text only when the boundary stays equally clear.
73
- 7. **Independent use:** Reconstruct the ledger from only the finished document. It must stand alone without background or unstated context.
61
+ ## Route what remains
74
62
 
75
- Do not finish until every check passes. If multiple versions pass, prefer fewer words, fewer sections, and lower lookup cost. Shorter text never compensates for lost behavior or precision.
63
+ | Surface | What belongs there |
64
+ |---|---|
65
+ | System prompt | The product Claude works within and the role it plays. For a custom agent, spend real effort here. |
66
+ | CLAUDE.md | A short line on what the repository is, then the gotchas found inside the codebase. Drop anything the model can read off the file structure. Link to skills for the detail. |
67
+ | Skills | Opinions, knowledge, and practices particular to your team or product, written as a guide the model consults. Split a long skill into several files. Constrain only where it matters. |
68
+ | References | Specs, mockups, and codebases pulled in by @mention. |
76
69
 
77
- ## Deliver the requested artifact
70
+ ## Deliver
78
71
 
79
- Preserve the requested format and required syntactic envelope. If no format is specified, use the leanest clear Markdown structure.
80
-
81
- For text supplied in the conversation, output only the finished instruction document. For file tasks, write only the requested paths: edit an existing file in place, and create a new file only when requested. Report changed paths with a concise behavior summary. Do not include the ledger or process narration unless requested.
72
+ Rewrite the document in place and report what moved to which surface. Offer `claude doctor` as a follow-up pass it rightsizes skills and CLAUDE.md files against these same rules.
@@ -1,6 +1,6 @@
1
1
  # fresh-branch
2
2
 
3
- Creates a new branch from fresh-fetched `origin/main` inside an isolated worktree under `Temp/<agent>/<branch-name>`. Does not push, open a PR, or run `checkout -b` in the caller tree.
3
+ Creates a new branch from fresh-fetched `origin/main` inside an isolated worktree under `<repo-root>/.claude/worktrees/<agent>/<branch-name>`. Does not push, open a PR, or run `checkout -b` in the caller tree.
4
4
 
5
5
  ## Key files
6
6
 
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: fresh-branch
3
3
  description: >-
4
- Fresh git branch from origin/main in an isolated temp worktree (never checkout -b in the caller tree).
4
+ Fresh git branch from origin/main in an isolated worktree under the repo's .claude/worktrees/ (never checkout -b in the caller tree).
5
5
  Triggers: fresh branch, new branch from main, /fresh-branch, start fresh, clean branch off main,
6
6
  worktree branch, branch in temp.
7
7
  ---
8
8
 
9
9
  # fresh-branch
10
10
 
11
- Creates a new branch from a fresh-fetched `origin/main` inside an isolated git worktree under the agent temp root. Shared primitive: other skills invoke `/fresh-branch` when they need a clean branch without touching the caller's dirty tree.
11
+ Creates a new branch from a fresh-fetched `origin/main` inside an isolated git worktree under the repository's `.claude/worktrees/<agent>/` root. Shared primitive: other skills invoke `/fresh-branch` when they need a clean branch without touching the caller's dirty tree.
12
12
 
13
13
  **Announce at start:** "Creating a fresh branch from origin/main."
14
14
 
@@ -62,15 +62,14 @@ Optional flags:
62
62
  | Flag | Role |
63
63
  |------|------|
64
64
  | `--repo <path>` | Source repo (default: current directory) |
65
- | `--agent <slug>` | Temp segment: `claude`, `grok`, `cursor`, `codex`, … |
65
+ | `--agent <slug>` | Worktree-root segment: `claude`, `grok`, `cursor`, `codex`, … |
66
66
  | `--base <ref>` | Base ref (default: `origin/main`) |
67
67
 
68
68
  Agent resolution inside the script: `--agent` → `FRESH_BRANCH_AGENT` env → host markers → `claude`.
69
69
 
70
70
  Worktree path:
71
71
 
72
- - Windows: `${USERPROFILE}/AppData/Local/Temp/<agent>/<branch-name>`
73
- - Else: `${tmpdir}/<agent>/<branch-name>`
72
+ - `<repo-root>/.claude/worktrees/<agent>/<branch-name>`, on every platform
74
73
  - If the path exists, the script suffixes `-2`, `-3`, …
75
74
 
76
75
  On exit 0, stdout is one JSON object:
@@ -101,7 +100,7 @@ Further edits for the new branch belong in `worktree_path`, not in the caller's
101
100
 
102
101
  ## Gotchas
103
102
 
104
- - **Dirty caller cwd blocks `checkout -b` and pollutes the tree.** Phase 3 always uses `git worktree add -b` into `Temp/<agent>/…`. If you reconstruct Phase 3 by hand with `checkout -b` in the session cwd, local modifications block the checkout and leave the user on a half-switched branch.
103
+ - **Dirty caller cwd blocks `checkout -b` and pollutes the tree.** Phase 3 always uses `git worktree add -b` into `<repo-root>/.claude/worktrees/<agent>/…`. If you reconstruct Phase 3 by hand with `checkout -b` in the session cwd, local modifications block the checkout and leave the user on a half-switched branch.
105
104
  - **Caller HEAD must stay put.** After success, the original repo's checked-out branch and dirty files are unchanged; only the new worktree has the new branch.
106
105
  - **Branch name collision.** If the branch already exists, the script exits non-zero with `{"error":...}`. Pick a new name; do not delete remote branches unless the user asks.
107
106
  - **Path already occupied.** A leftover folder at the preferred worktree path gets a numeric suffix (`-2`, …); report the path from JSON, not the path you assumed.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Create a fresh branch in an isolated git worktree under the agent scratch root.
2
+ """Create a fresh branch in an isolated git worktree under the repository.
3
3
 
4
4
  ::
5
5
 
@@ -8,9 +8,9 @@
8
8
  "base_commit": "abc...", "agent": "claude", "repo_root": "..."}
9
9
 
10
10
  Never runs ``git checkout -b`` in the caller's working tree. Fetches the base
11
- ref, then ``git worktree add -b --no-track`` into ``Temp/<agent>/<branch>``
12
- (Windows) or ``gettempdir()/<agent>/<branch>`` elsewhere. Exit 0 prints success
13
- JSON; any failure prints ``{"error": ...}`` and exits non-zero.
11
+ ref, then ``git worktree add -b --no-track`` into
12
+ ``<repo-root>/.claude/worktrees/<agent>/<branch>``. Exit 0 prints success JSON;
13
+ any failure prints ``{"error": ...}`` and exits non-zero.
14
14
  """
15
15
 
16
16
  from __future__ import annotations
@@ -20,7 +20,6 @@ import json
20
20
  import os
21
21
  import re
22
22
  import sys
23
- import tempfile
24
23
  from pathlib import Path
25
24
 
26
25
  from fresh_branch_git_commands import (
@@ -33,7 +32,7 @@ from fresh_branch_git_commands import (
33
32
  from fresh_branch_scripts_constants.fresh_branch_cli_constants import (
34
33
  AGENT_SLUG_PATTERN,
35
34
  ALL_AGENT_DETECTION_MARKERS,
36
- ALL_WINDOWS_USER_SCRATCH_PARTS,
35
+ ALL_REPOSITORY_WORKTREE_ROOT_PARTS,
37
36
  DEFAULT_AGENT_SLUG,
38
37
  DEFAULT_BASE_REF,
39
38
  ERROR_AGENT_SLUG_INVALID,
@@ -57,8 +56,6 @@ from fresh_branch_scripts_constants.fresh_branch_cli_constants import (
57
56
  PAYLOAD_KEY_REPO_ROOT,
58
57
  PAYLOAD_KEY_WORKTREE_PATH,
59
58
  UNIQUE_PATH_SUFFIX_START,
60
- USERPROFILE_ENV_VAR,
61
- WINDOWS_PLATFORM_PREFIX,
62
59
  )
63
60
 
64
61
 
@@ -94,28 +91,26 @@ def _detect_agent_slug_from_environment() -> str:
94
91
  return DEFAULT_AGENT_SLUG
95
92
 
96
93
 
97
- def resolve_agent_worktree_root(agent_slug: str) -> Path:
98
- """Return ``Temp/<agent>`` on Windows USERPROFILE, else gettempdir root.
94
+ def resolve_agent_worktree_root(repo_root: Path, agent_slug: str) -> Path:
95
+ """Return the repository's per-agent worktree root.
99
96
 
100
97
  ::
101
98
 
102
- # win32 + USERPROFILE=C:/Users/x -> C:/Users/x/AppData/Local/Temp/grok
103
- resolve_agent_worktree_root("grok")
99
+ # repo_root=/srv/app, agent_slug=grok
100
+ resolve_agent_worktree_root(Path("/srv/app"), "grok")
101
+ # -> /srv/app/.claude/worktrees/grok
102
+
103
+ Every worktree sits under the repository it branches from, so a worktree
104
+ travels with its repository and each agent keeps its own subdirectory.
104
105
 
105
106
  Args:
107
+ repo_root: Absolute path of the repository the branch comes from.
106
108
  agent_slug: Short host label (one path segment).
107
109
 
108
110
  Returns:
109
111
  Directory that should hold per-branch worktree folders.
110
112
  """
111
- if sys.platform.startswith(WINDOWS_PLATFORM_PREFIX):
112
- user_profile = os.environ.get(USERPROFILE_ENV_VAR)
113
- if user_profile:
114
- return Path(user_profile).joinpath(
115
- *ALL_WINDOWS_USER_SCRATCH_PARTS,
116
- agent_slug,
117
- )
118
- return Path(tempfile.gettempdir()) / agent_slug
113
+ return repo_root.joinpath(*ALL_REPOSITORY_WORKTREE_ROOT_PARTS, agent_slug)
119
114
 
120
115
 
121
116
  def resolve_unique_worktree_path(preferred_path: Path) -> Path:
@@ -181,13 +176,33 @@ def create_fresh_branch(
181
176
  ::
182
177
 
183
178
  create_fresh_branch("fix/x", Path("."), "grok", "origin/main")
179
+
180
+ The caller's working tree is never checked out. The worktree lands under
181
+ the repository's ``.claude/worktrees/<agent>/<branch>``, suffixed ``-2``,
182
+ ``-3``, … when that path is already taken.
183
+
184
+ Args:
185
+ branch_name: Branch to create; must be a safe relative path.
186
+ repo_path: Any path inside the repository the branch comes from.
187
+ agent_slug: Short host label naming the worktree-root subdirectory.
188
+ base_ref: Ref the branch starts from, such as ``origin/main``.
189
+
190
+ Returns:
191
+ The success payload: branch, worktree_path, base_ref, base_commit,
192
+ agent, and repo_root.
193
+
194
+ Raises:
195
+ ValueError: When the branch name or the agent slug is unsafe.
196
+ RuntimeError: When git refuses the fetch or the worktree add.
184
197
  """
185
198
  cleaned_branch = _require_safe_branch_name(branch_name)
186
199
  normalized_agent_slug = _normalize_agent_slug(agent_slug)
187
200
  resolved_base_ref, repo_root, base_commit = _resolve_branch_base(
188
201
  repo_path, base_ref,
189
202
  )
190
- worktree_path = _allocate_worktree_path(cleaned_branch, normalized_agent_slug)
203
+ worktree_path = _allocate_worktree_path(
204
+ cleaned_branch, normalized_agent_slug, repo_root,
205
+ )
191
206
  create_worktree_branch(
192
207
  repo_root,
193
208
  branch_name=cleaned_branch,
@@ -241,8 +256,11 @@ def _require_safe_branch_name(branch_name: str) -> str:
241
256
  return cleaned_branch
242
257
 
243
258
 
244
- def _allocate_worktree_path(branch_name: str, agent_slug: str) -> Path:
245
- agent_worktree_root = resolve_agent_worktree_root(agent_slug)
259
+ def _allocate_worktree_path(
260
+ branch_name: str, agent_slug: str, repo_root: Path,
261
+ ) -> Path:
262
+ agent_worktree_root = resolve_agent_worktree_root(repo_root, agent_slug)
263
+ agent_worktree_root.mkdir(parents=True, exist_ok=True)
246
264
  preferred_path = agent_worktree_root / branch_name
247
265
  _assert_path_is_under_agent_root(
248
266
  candidate_path=preferred_path,
@@ -324,7 +342,7 @@ def _parse_arguments() -> argparse.Namespace:
324
342
  parser.add_argument(
325
343
  "--agent",
326
344
  default=None,
327
- help="Host label for Temp/<agent>/ (default: detect from environment).",
345
+ help="Host label for .claude/worktrees/<agent>/ (default: detect from environment).",
328
346
  )
329
347
  parser.add_argument(
330
348
  "--base",
@@ -23,9 +23,7 @@ ALL_AGENT_DETECTION_MARKERS: tuple[tuple[str, str], ...] = (
23
23
  PATH_SEGMENT_CURRENT = "."
24
24
  PATH_SEGMENT_PARENT = ".."
25
25
 
26
- WINDOWS_PLATFORM_PREFIX = "win"
27
- ALL_WINDOWS_USER_SCRATCH_PARTS = ("AppData", "Local", "Temp")
28
- USERPROFILE_ENV_VAR = "USERPROFILE"
26
+ ALL_REPOSITORY_WORKTREE_ROOT_PARTS = (".claude", "worktrees")
29
27
 
30
28
  MAXIMUM_UNIQUE_PATH_ATTEMPTS = 100
31
29
  UNIQUE_PATH_SUFFIX_START = 2