multi-forge 0.2.0__py3-none-any.whl

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 (311) hide show
  1. forge/__init__.py +3 -0
  2. forge/_extensions/agents/.gitkeep +0 -0
  3. forge/_extensions/commands/.gitkeep +0 -0
  4. forge/_extensions/skills/analyze/SKILL.md +87 -0
  5. forge/_extensions/skills/challenge/SKILL.md +91 -0
  6. forge/_extensions/skills/consensus/SKILL.md +120 -0
  7. forge/_extensions/skills/consensus/resources/code_consensus_evaluation.md +94 -0
  8. forge/_extensions/skills/consensus/resources/consensus_evaluation.md +70 -0
  9. forge/_extensions/skills/consensus/resources/synthesis.md +101 -0
  10. forge/_extensions/skills/debate/SKILL.md +116 -0
  11. forge/_extensions/skills/debate/resources/code_debate_evaluation.md +101 -0
  12. forge/_extensions/skills/debate/resources/debate_evaluation.md +90 -0
  13. forge/_extensions/skills/panel/SKILL.md +141 -0
  14. forge/_extensions/skills/panel/resources/synthesis.md +103 -0
  15. forge/_extensions/skills/qa/SKILL.md +704 -0
  16. forge/_extensions/skills/qa/resources/checklist/0-enable.md +78 -0
  17. forge/_extensions/skills/qa/resources/checklist/1-preflight.md +24 -0
  18. forge/_extensions/skills/qa/resources/checklist/10-resume.md +143 -0
  19. forge/_extensions/skills/qa/resources/checklist/11-config.md +150 -0
  20. forge/_extensions/skills/qa/resources/checklist/12-search.md +58 -0
  21. forge/_extensions/skills/qa/resources/checklist/13-guard.md +237 -0
  22. forge/_extensions/skills/qa/resources/checklist/14-workflow.md +305 -0
  23. forge/_extensions/skills/qa/resources/checklist/15-skills.md +155 -0
  24. forge/_extensions/skills/qa/resources/checklist/16-handoff.md +224 -0
  25. forge/_extensions/skills/qa/resources/checklist/17-info.md +50 -0
  26. forge/_extensions/skills/qa/resources/checklist/18-disable.md +84 -0
  27. forge/_extensions/skills/qa/resources/checklist/19-uninstall.md +146 -0
  28. forge/_extensions/skills/qa/resources/checklist/2-extensions.md +188 -0
  29. forge/_extensions/skills/qa/resources/checklist/20-cleanup.md +36 -0
  30. forge/_extensions/skills/qa/resources/checklist/3-auth.md +234 -0
  31. forge/_extensions/skills/qa/resources/checklist/4-proxy.md +481 -0
  32. forge/_extensions/skills/qa/resources/checklist/5-session.md +541 -0
  33. forge/_extensions/skills/qa/resources/checklist/6-hooks.md +275 -0
  34. forge/_extensions/skills/qa/resources/checklist/7-costs.md +309 -0
  35. forge/_extensions/skills/qa/resources/checklist/8-status-line.md +174 -0
  36. forge/_extensions/skills/qa/resources/checklist/9-direct-commands.md +146 -0
  37. forge/_extensions/skills/qa/resources/checklist.md +103 -0
  38. forge/_extensions/skills/qa/resources/report-template.md +62 -0
  39. forge/_extensions/skills/qa/scripts/start-container.sh +529 -0
  40. forge/_extensions/skills/qa/scripts/walkthrough-state.py +1137 -0
  41. forge/_extensions/skills/review/SKILL.md +125 -0
  42. forge/_extensions/skills/review/references/claude-4.6.md +474 -0
  43. forge/_extensions/skills/review/references/claude-4.7.md +710 -0
  44. forge/_extensions/skills/review/references/gemini-3.1.md +546 -0
  45. forge/_extensions/skills/review/references/gpt-5.5.md +490 -0
  46. forge/_extensions/skills/review/references/skills-writing-guide.md +1588 -0
  47. forge/_extensions/skills/review/resources/code-anthropic.md +160 -0
  48. forge/_extensions/skills/review/resources/code-gemini.md +184 -0
  49. forge/_extensions/skills/review/resources/code-openai.md +203 -0
  50. forge/_extensions/skills/review/resources/code.md +160 -0
  51. forge/_extensions/skills/review-docs/SKILL.md +121 -0
  52. forge/_extensions/skills/review-docs/resources/docs-anthropic.md +170 -0
  53. forge/_extensions/skills/review-docs/resources/docs-gemini.md +204 -0
  54. forge/_extensions/skills/review-docs/resources/docs-openai.md +231 -0
  55. forge/_extensions/skills/review-docs/resources/docs.md +170 -0
  56. forge/_extensions/skills/smoke-test/SKILL.md +27 -0
  57. forge/_extensions/skills/smoke-test/scripts/smoke-test.sh +118 -0
  58. forge/_extensions/skills/understand/SKILL.md +148 -0
  59. forge/_extensions/skills/understand/resources/code-anthropic.md +163 -0
  60. forge/_extensions/skills/understand/resources/code-gemini.md +194 -0
  61. forge/_extensions/skills/understand/resources/code-openai.md +181 -0
  62. forge/_extensions/skills/understand/resources/code.md +163 -0
  63. forge/_extensions/skills/understand/resources/docs-anthropic.md +177 -0
  64. forge/_extensions/skills/understand/resources/docs-gemini.md +202 -0
  65. forge/_extensions/skills/understand/resources/docs-openai.md +191 -0
  66. forge/_extensions/skills/understand/resources/docs.md +177 -0
  67. forge/_extensions/skills/walkthrough/SKILL.md +599 -0
  68. forge/_extensions/skills/walkthrough/resources/checklist.md +765 -0
  69. forge/_extensions/skills/walkthrough/scripts/run-in-repo.sh +118 -0
  70. forge/_extensions/skills/walkthrough/scripts/setup-test-repo.sh +198 -0
  71. forge/_extensions/skills/walkthrough/scripts/walkthrough-state.py +1137 -0
  72. forge/backend/__init__.py +174 -0
  73. forge/backend/adapters/__init__.py +38 -0
  74. forge/backend/adapters/litellm.py +158 -0
  75. forge/backend/creation.py +89 -0
  76. forge/backend/registry.py +178 -0
  77. forge/cli/__init__.py +16 -0
  78. forge/cli/auth.py +483 -0
  79. forge/cli/backend.py +298 -0
  80. forge/cli/claude.py +411 -0
  81. forge/cli/config_cmd.py +303 -0
  82. forge/cli/extensions.py +1001 -0
  83. forge/cli/gc.py +165 -0
  84. forge/cli/guard.py +1018 -0
  85. forge/cli/guards.py +106 -0
  86. forge/cli/handoff.py +110 -0
  87. forge/cli/hooks/__init__.py +36 -0
  88. forge/cli/hooks/_group.py +20 -0
  89. forge/cli/hooks/_helpers.py +149 -0
  90. forge/cli/hooks/commands.py +1677 -0
  91. forge/cli/hooks/direct_commands.py +1304 -0
  92. forge/cli/hooks/install.py +232 -0
  93. forge/cli/hooks/policy.py +151 -0
  94. forge/cli/hooks/read_hygiene.py +74 -0
  95. forge/cli/hooks/verification.py +370 -0
  96. forge/cli/logs.py +406 -0
  97. forge/cli/main.py +292 -0
  98. forge/cli/proxy.py +1821 -0
  99. forge/cli/proxy_costs.py +313 -0
  100. forge/cli/search.py +416 -0
  101. forge/cli/session.py +892 -0
  102. forge/cli/session_addendum.py +81 -0
  103. forge/cli/session_fork.py +750 -0
  104. forge/cli/session_handoff.py +141 -0
  105. forge/cli/session_lifecycle.py +2053 -0
  106. forge/cli/session_manage.py +1336 -0
  107. forge/cli/session_memory.py +201 -0
  108. forge/cli/status_line.py +1398 -0
  109. forge/cli/workflow.py +1964 -0
  110. forge/config/__init__.py +110 -0
  111. forge/config/dataclass_utils.py +88 -0
  112. forge/config/defaults/__init__.py +0 -0
  113. forge/config/defaults/backends/__init__.py +0 -0
  114. forge/config/defaults/backends/litellm.yaml +196 -0
  115. forge/config/defaults/templates/__init__.py +0 -0
  116. forge/config/defaults/templates/litellm-anthropic-local.yaml +33 -0
  117. forge/config/defaults/templates/litellm-anthropic.yaml +24 -0
  118. forge/config/defaults/templates/litellm-gemini-flash-local.yaml +37 -0
  119. forge/config/defaults/templates/litellm-gemini-local.yaml +32 -0
  120. forge/config/defaults/templates/litellm-gemini-test.yaml +34 -0
  121. forge/config/defaults/templates/litellm-gemini.yaml +21 -0
  122. forge/config/defaults/templates/litellm-openai-codex-local.yaml +36 -0
  123. forge/config/defaults/templates/litellm-openai-local.yaml +38 -0
  124. forge/config/defaults/templates/litellm-openai.yaml +28 -0
  125. forge/config/defaults/templates/openrouter-anthropic.yaml +23 -0
  126. forge/config/defaults/templates/openrouter-deepseek.yaml +26 -0
  127. forge/config/defaults/templates/openrouter-gemini-flash.yaml +26 -0
  128. forge/config/defaults/templates/openrouter-gemini.yaml +23 -0
  129. forge/config/defaults/templates/openrouter-glm.yaml +23 -0
  130. forge/config/defaults/templates/openrouter-kimi.yaml +30 -0
  131. forge/config/defaults/templates/openrouter-minimax.yaml +26 -0
  132. forge/config/defaults/templates/openrouter-openai-codex.yaml +23 -0
  133. forge/config/defaults/templates/openrouter-openai.yaml +28 -0
  134. forge/config/defaults/templates/openrouter-qwen.yaml +25 -0
  135. forge/config/loader.py +675 -0
  136. forge/config/schema.py +448 -0
  137. forge/core/__init__.py +5 -0
  138. forge/core/auth/__init__.py +67 -0
  139. forge/core/auth/capabilities.py +219 -0
  140. forge/core/auth/credentials_file.py +244 -0
  141. forge/core/auth/protocols.py +18 -0
  142. forge/core/auth/secrets.py +243 -0
  143. forge/core/auth/template_secrets.py +112 -0
  144. forge/core/data/__init__.py +5 -0
  145. forge/core/data/model_catalog.yaml +1522 -0
  146. forge/core/data/pricing.yaml +140 -0
  147. forge/core/data/system_prompt_addendums/__init__.py +0 -0
  148. forge/core/data/system_prompt_addendums/gemini.md +330 -0
  149. forge/core/data/system_prompt_addendums/openai.md +328 -0
  150. forge/core/llm/__init__.py +231 -0
  151. forge/core/llm/clients/__init__.py +14 -0
  152. forge/core/llm/clients/base.py +115 -0
  153. forge/core/llm/clients/litellm.py +619 -0
  154. forge/core/llm/clients/openai_compat.py +244 -0
  155. forge/core/llm/clients/openrouter.py +234 -0
  156. forge/core/llm/credentials.py +439 -0
  157. forge/core/llm/detection.py +86 -0
  158. forge/core/llm/errors.py +44 -0
  159. forge/core/llm/protocols.py +80 -0
  160. forge/core/llm/types.py +176 -0
  161. forge/core/logging.py +146 -0
  162. forge/core/models/__init__.py +91 -0
  163. forge/core/models/catalog.py +467 -0
  164. forge/core/models/pricing.py +165 -0
  165. forge/core/models/types.py +167 -0
  166. forge/core/naming.py +212 -0
  167. forge/core/ops/__init__.py +73 -0
  168. forge/core/ops/context.py +141 -0
  169. forge/core/ops/gc.py +802 -0
  170. forge/core/ops/proxy.py +146 -0
  171. forge/core/ops/resolution.py +135 -0
  172. forge/core/ops/session.py +344 -0
  173. forge/core/ops/session_context.py +548 -0
  174. forge/core/paths.py +38 -0
  175. forge/core/process.py +54 -0
  176. forge/core/reactive/__init__.py +38 -0
  177. forge/core/reactive/cost_tracking.py +300 -0
  178. forge/core/reactive/env.py +180 -0
  179. forge/core/reactive/proxy.py +78 -0
  180. forge/core/reactive/routing.py +622 -0
  181. forge/core/reactive/session_runner.py +185 -0
  182. forge/core/reactive/structured_output.py +62 -0
  183. forge/core/reactive/tagger.py +94 -0
  184. forge/core/reactive/throttle.py +132 -0
  185. forge/core/state/__init__.py +59 -0
  186. forge/core/state/exceptions.py +59 -0
  187. forge/core/state/io.py +140 -0
  188. forge/core/state/lock.py +99 -0
  189. forge/core/state/timestamps.py +60 -0
  190. forge/core/transcript.py +78 -0
  191. forge/core/typing_helpers.py +24 -0
  192. forge/core/workqueue/__init__.py +67 -0
  193. forge/core/workqueue/queue.py +552 -0
  194. forge/core/workqueue/types.py +63 -0
  195. forge/guard/__init__.py +26 -0
  196. forge/guard/deterministic/__init__.py +26 -0
  197. forge/guard/deterministic/base.py +158 -0
  198. forge/guard/deterministic/coding_standards.py +256 -0
  199. forge/guard/deterministic/registry.py +148 -0
  200. forge/guard/deterministic/tdd.py +171 -0
  201. forge/guard/engine.py +216 -0
  202. forge/guard/protocols.py +91 -0
  203. forge/guard/queries.py +96 -0
  204. forge/guard/semantic/__init__.py +34 -0
  205. forge/guard/semantic/promotion.py +18 -0
  206. forge/guard/semantic/supervisor.py +813 -0
  207. forge/guard/semantic/verdict.py +183 -0
  208. forge/guard/store.py +124 -0
  209. forge/guard/team/__init__.py +6 -0
  210. forge/guard/team/config.py +24 -0
  211. forge/guard/team/handlers.py +209 -0
  212. forge/guard/team/prompts.py +41 -0
  213. forge/guard/types.py +125 -0
  214. forge/guard/workflow/__init__.py +17 -0
  215. forge/guard/workflow/branches.py +67 -0
  216. forge/guard/workflow/config.py +63 -0
  217. forge/guard/workflow/divergence.py +113 -0
  218. forge/guard/workflow/policy.py +87 -0
  219. forge/guard/workflow/stages.py +205 -0
  220. forge/install/__init__.py +55 -0
  221. forge/install/cli.py +281 -0
  222. forge/install/exceptions.py +163 -0
  223. forge/install/hooks.py +109 -0
  224. forge/install/installer.py +1037 -0
  225. forge/install/models.py +321 -0
  226. forge/install/preset.py +272 -0
  227. forge/install/settings_merge.py +831 -0
  228. forge/install/tracking.py +238 -0
  229. forge/install/version.py +141 -0
  230. forge/proxy/__init__.py +0 -0
  231. forge/proxy/base_client.py +181 -0
  232. forge/proxy/client_adapter.py +476 -0
  233. forge/proxy/client_factory.py +531 -0
  234. forge/proxy/converters.py +1206 -0
  235. forge/proxy/cost_logger.py +132 -0
  236. forge/proxy/cost_tracker.py +242 -0
  237. forge/proxy/data_models.py +338 -0
  238. forge/proxy/error_hints.py +92 -0
  239. forge/proxy/metrics.py +222 -0
  240. forge/proxy/model_spec.py +158 -0
  241. forge/proxy/proxies.py +333 -0
  242. forge/proxy/proxy_identity.py +134 -0
  243. forge/proxy/proxy_orchestrator.py +1018 -0
  244. forge/proxy/proxy_startup.py +54 -0
  245. forge/proxy/server.py +1561 -0
  246. forge/proxy/utils.py +537 -0
  247. forge/review/__init__.py +6 -0
  248. forge/review/adversarial.py +111 -0
  249. forge/review/consensus.py +236 -0
  250. forge/review/engine.py +356 -0
  251. forge/review/models.py +437 -0
  252. forge/review/resources/__init__.py +5 -0
  253. forge/review/resources/codereview-performance.md +85 -0
  254. forge/review/resources/codereview-quick.md +75 -0
  255. forge/review/resources/codereview-security.md +92 -0
  256. forge/review/resources/codereview.md +85 -0
  257. forge/review/resources/docreview-quick.md +75 -0
  258. forge/review/resources/docreview.md +86 -0
  259. forge/review/resources/thinkdeep.md +89 -0
  260. forge/review/routing.py +368 -0
  261. forge/review/synthesis.py +73 -0
  262. forge/runtime_config.py +438 -0
  263. forge/search/__init__.py +55 -0
  264. forge/search/bm25_store.py +264 -0
  265. forge/search/content_store.py +197 -0
  266. forge/search/engine.py +352 -0
  267. forge/search/exceptions.py +51 -0
  268. forge/search/extractor.py +234 -0
  269. forge/search/index_state.py +295 -0
  270. forge/search/store.py +215 -0
  271. forge/search/tokenizer.py +24 -0
  272. forge/session/__init__.py +130 -0
  273. forge/session/active.py +339 -0
  274. forge/session/artifacts.py +202 -0
  275. forge/session/claude/__init__.py +50 -0
  276. forge/session/claude/cleanup.py +105 -0
  277. forge/session/claude/invoke.py +236 -0
  278. forge/session/claude/paths.py +200 -0
  279. forge/session/cleanup.py +216 -0
  280. forge/session/config.py +34 -0
  281. forge/session/direct_model.py +107 -0
  282. forge/session/effective.py +169 -0
  283. forge/session/exceptions.py +255 -0
  284. forge/session/handoff.py +881 -0
  285. forge/session/handoff_agent.py +544 -0
  286. forge/session/hooks/__init__.py +35 -0
  287. forge/session/hooks/models.py +73 -0
  288. forge/session/hooks/session_start.py +507 -0
  289. forge/session/identity.py +84 -0
  290. forge/session/index.py +553 -0
  291. forge/session/manager.py +1506 -0
  292. forge/session/models.py +572 -0
  293. forge/session/overrides.py +344 -0
  294. forge/session/plan_resolution.py +286 -0
  295. forge/session/prev_sessions.py +128 -0
  296. forge/session/store.py +431 -0
  297. forge/session/validation.py +47 -0
  298. forge/session/worktree/__init__.py +65 -0
  299. forge/session/worktree/cleanup.py +262 -0
  300. forge/session/worktree/config_copy.py +203 -0
  301. forge/session/worktree/create.py +332 -0
  302. forge/sidecar/__init__.py +29 -0
  303. forge/sidecar/container.py +161 -0
  304. forge/sidecar/docker.py +86 -0
  305. forge/sidecar/secrets.py +19 -0
  306. multi_forge-0.2.0.dist-info/METADATA +242 -0
  307. multi_forge-0.2.0.dist-info/RECORD +311 -0
  308. multi_forge-0.2.0.dist-info/WHEEL +4 -0
  309. multi_forge-0.2.0.dist-info/entry_points.txt +2 -0
  310. multi_forge-0.2.0.dist-info/licenses/LICENSE +203 -0
  311. multi_forge-0.2.0.dist-info/licenses/NOTICE +14 -0
@@ -0,0 +1,541 @@
1
+ <!-- prereq: 0.3 -->
2
+
3
+ ## 5. Session Management
4
+
5
+ ### 5.1 Start a Session
6
+
7
+ <!-- auto -->
8
+
9
+ ```bash
10
+ cd $FORGE_TEST_REPO
11
+
12
+ # Clean up from previous runs
13
+ forge session delete test-session-1 --force 2>/dev/null || true
14
+
15
+ # Start a new session
16
+ forge session start test-session-1 --no-launch
17
+
18
+ # Verify session created
19
+ ls -la .forge/sessions/
20
+ cat .forge/sessions/test-session-1/forge.session.json | jq '.'
21
+ ```
22
+
23
+ - [ ] Session directory created at `.forge/sessions/test-session-1/`
24
+ - [ ] `forge.session.json` contains `intent` section
25
+ - [ ] `--no-launch` prevents Claude from opening (useful for testing)
26
+
27
+ ### 5.2 List Sessions
28
+
29
+ <!-- auto -->
30
+
31
+ ```bash
32
+ # List all sessions
33
+ forge session list
34
+ ```
35
+
36
+ - [ ] Shows `test-session-1` with status
37
+ - [ ] Shows session directory and last-used timestamp
38
+
39
+ ### 5.3 Show Session Details
40
+
41
+ <!-- auto -->
42
+
43
+ ```bash
44
+ # Show session details
45
+ forge session show test-session-1
46
+ ```
47
+
48
+ - [ ] Shows intent, overrides, confirmed sections
49
+ - [ ] Shows proxy info if running with proxy
50
+
51
+ ### 5.4 Set Session Overrides
52
+
53
+ <!-- auto -->
54
+
55
+ ```bash
56
+ # Set a mid-session override
57
+ forge session set memory.auto_update.enabled true --session test-session-1
58
+
59
+ # Verify override applied
60
+ cat .forge/sessions/test-session-1/forge.session.json | jq '.overrides'
61
+ ```
62
+
63
+ - [ ] Override written to `overrides` section
64
+ - [ ] Original intent unchanged
65
+
66
+ ### 5.5 Reset Overrides
67
+
68
+ <!-- auto -->
69
+
70
+ ```bash
71
+ # Reset overrides to intent
72
+ forge session reset --session test-session-1
73
+
74
+ # Verify reset
75
+ cat .forge/sessions/test-session-1/forge.session.json | jq '.overrides'
76
+ ```
77
+
78
+ - [ ] Overrides section cleared or empty
79
+
80
+ ### 5.6 Fork a Session (default, same directory)
81
+
82
+ <!-- prereq: 2.4, 4.2 -->
83
+
84
+ <!-- requires: api_key -->
85
+
86
+ <!-- human:guided -->
87
+
88
+ In the **container shell**, start the parent session routed through the proxy provisioned in 4.2 (Claude will launch --
89
+ interact briefly, then exit with `/exit`). Then fork it **without `--worktree`** (the default). The fork stays in the
90
+ same directory, so Claude's `--resume --fork-session` finds the parent conversation and carries it over. Ask "where were
91
+ we?" to confirm the conversation context carried over, then exit (`/exit`).
92
+
93
+ ```
94
+ # Clean up from previous runs
95
+ forge session delete test-session-parent --force 2>/dev/null || true
96
+ forge session delete test-session-forked --force 2>/dev/null || true
97
+
98
+ # Start the parent session through the proxy provisioned in 4.2.
99
+ # Interact briefly ("hello"), then exit (/exit).
100
+ forge session start test-session-parent --proxy "$FORGE_QA_OPENAI_PROXY"
101
+
102
+ # Fork the parent session (default: same directory, no worktree).
103
+ # Claude should resume the conversation via --fork-session.
104
+ # Disable auto-memory so "where were we?" tests Forge handoff, not CC memory.
105
+ # Ask "where were we?" to confirm, then exit (/exit).
106
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 forge session fork test-session-parent --name test-session-forked
107
+
108
+ # Verify fork lives in the same directory as parent
109
+ forge session show test-session-forked
110
+ cat "$FORGE_TEST_REPO/.forge/sessions/test-session-forked/forge.session.json" | \
111
+ jq '{is_fork, parent_session, worktree: (.worktree | {path, is_worktree}), confirmed: (.confirmed | {claude_session_id})}'
112
+ ```
113
+
114
+ - [ ] Forked session created in same directory (`$FORGE_TEST_REPO`)
115
+ - [ ] `forge session show` reports type as Fork
116
+ - [ ] Claude conversation carries over (asking "where were we?" reflects parent context)
117
+ - [ ] No `Worktree:` line in fork output (no git worktree created)
118
+ - [ ] Manifest at `$FORGE_TEST_REPO/.forge/sessions/test-session-forked/` (not a separate worktree dir)
119
+ - [ ] Manifest has `is_fork: true`, `parent_session` pointing to parent, `is_worktree: false`
120
+ - [ ] `confirmed.claude_session_id` is populated after fork
121
+
122
+ ### 5.7 Fork a Session with Worktree (`--worktree`)
123
+
124
+ <!-- prereq: 2.4, 4.2 -->
125
+
126
+ <!-- requires: api_key -->
127
+
128
+ <!-- human:guided -->
129
+
130
+ Fork the parent session again, this time with `--worktree` for code isolation. The fork gets its own git worktree and
131
+ branch. Because conversations are project-scoped, the fork starts a fresh Claude session in the new worktree and
132
+ automatically injects a parent handoff context file. Ask "where were we?" to confirm the parent context is present, then
133
+ exit (`/exit`).
134
+
135
+ Note: `fork --worktree` gives the forked session its own Forge root in the new worktree. The fork manifest and parent
136
+ handoff context should both live under the forked worktree's `.forge/` directory.
137
+
138
+ ```
139
+ # Clean up from previous runs
140
+ forge session delete test-session-forked-wt --force 2>/dev/null || true
141
+ WORKTREE_PATH="${FORGE_TEST_REPO}-test-session-forked-wt"
142
+ git worktree remove "$WORKTREE_PATH" --force 2>/dev/null || true
143
+ git branch -D test-session-forked-wt 2>/dev/null || true
144
+
145
+ # Fork with --worktree (creates isolated worktree + branch).
146
+ # Starts fresh Claude with parent handoff context (no --resume attempt).
147
+ # Disable auto-memory so "where were we?" tests Forge handoff, not CC memory.
148
+ # Ask "where were we?" to confirm parent context, then exit (/exit).
149
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 forge session fork test-session-parent --name test-session-forked-wt --worktree --extensions
150
+
151
+ # Verify fork
152
+ forge session show test-session-forked-wt
153
+ WORKTREE_PATH=$(forge session show test-session-forked-wt --json | jq -r '.worktree.path')
154
+ # Manifest lives inside the forked worktree's Forge root
155
+ cat "$WORKTREE_PATH/.forge/sessions/test-session-forked-wt/forge.session.json" | \
156
+ jq '{is_fork, parent_session, worktree: (.worktree | {path, is_worktree}), confirmed: (.confirmed | {claude_session_id})}'
157
+ cat "$WORKTREE_PATH/.forge/prev_sessions/test-session-parent/children/test-session-forked-wt.md"
158
+ ```
159
+
160
+ - [ ] Worktree fork created at `${FORGE_TEST_REPO}-test-session-forked-wt`
161
+ - [ ] `forge session show` reports type as Fork with worktree info
162
+ - [ ] Fork output shows `Extensions:` line confirming auto-install in worktree
163
+ - [ ] Fork output shows `Context:` line with parent handoff file
164
+ - [ ] Asking "where were we?" reflects parent context
165
+ - [ ] Manifest at `${FORGE_TEST_REPO}-test-session-forked-wt/.forge/sessions/test-session-forked-wt/`
166
+ - [ ] Manifest has `is_fork: true`, `parent_session`, `is_worktree: true`
167
+ - [ ] `confirmed.claude_session_id` is populated
168
+ - [ ] Parent handoff file exists at
169
+ `${FORGE_TEST_REPO}-test-session-forked-wt/.forge/prev_sessions/test-session-parent/children/test-session-forked-wt.md`
170
+
171
+ ### 5.8 Incognito Session
172
+
173
+ <!-- requires: api_key -->
174
+
175
+ <!-- human:guided -->
176
+
177
+ Incognito sessions auto-delete on exit, so `--incognito` requires launching Claude (`--no-launch` is mutually
178
+ exclusive). In the **container shell**, launch an incognito session, interact briefly, then exit.
179
+
180
+ ```
181
+ # Clean up from previous runs
182
+ forge session delete test-incognito --force 2>/dev/null || true
183
+
184
+ # Launch an incognito session (auto-deletes on exit).
185
+ # Say "hello", then exit with /exit.
186
+ forge session incognito test-incognito --proxy "$FORGE_QA_OPENAI_PROXY"
187
+
188
+ # After exiting Claude, verify auto-cleanup removed the session
189
+ forge session list
190
+ # Expected: test-incognito should NOT appear (auto-deleted on exit)
191
+ ```
192
+
193
+ - [ ] Incognito session launches successfully
194
+ - [ ] Session auto-deleted after exit (not in `forge session list`)
195
+ - [ ] No `.forge/sessions/test-incognito/` directory remains
196
+
197
+ ### 5.9 Delete a Session
198
+
199
+ <!-- auto -->
200
+
201
+ ```bash
202
+ # Clean up from previous runs
203
+ forge session delete test-session-delete-me --force 2>/dev/null || true
204
+
205
+ # Create a disposable session to delete
206
+ forge session start test-session-delete-me --no-launch
207
+
208
+ # Delete a test session (non-interactive)
209
+ forge session delete test-session-delete-me --force
210
+
211
+ # Verify deletion
212
+ forge session list
213
+ ```
214
+
215
+ - [ ] Session removed from listing
216
+ - [ ] Session directory removed
217
+
218
+ Ref-count delete guard: verify that deleting a co-resident session preserves the shared worktree.
219
+
220
+ ```bash
221
+ # Create a worktree session (owns the worktree)
222
+ forge session delete test-refcount-owner --force 2>/dev/null || true
223
+ forge session delete test-refcount-guest --force 2>/dev/null || true
224
+ git worktree remove "${FORGE_TEST_REPO}-test-refcount-owner" --force 2>/dev/null || true
225
+ git branch -D test-refcount-owner 2>/dev/null || true
226
+
227
+ forge session start test-refcount-owner --worktree --no-launch
228
+ WORKTREE_PATH=$(forge session show test-refcount-owner --json | jq -r '.worktree.path')
229
+
230
+ # Owner manifest lives centrally (root-level project, --worktree keeps parent forge_root)
231
+ OWNER_JSON="$FORGE_TEST_REPO/.forge/sessions/test-refcount-owner/forge.session.json"
232
+ jq '.confirmed.claude_session_id = "fixture-refcount"' "$OWNER_JSON" > /tmp/rc.json && mv /tmp/rc.json "$OWNER_JSON"
233
+
234
+ # --into requires Forge enabled in the target worktree
235
+ cd "$WORKTREE_PATH" && forge extension enable --scope local && cd "$FORGE_TEST_REPO"
236
+
237
+ # Fork into the same worktree (guest, does not own)
238
+ forge session fork test-refcount-owner --name test-refcount-guest --into "$WORKTREE_PATH" --no-launch
239
+
240
+ # Delete the guest — worktree must be preserved
241
+ forge session delete test-refcount-guest --force
242
+
243
+ # Verify worktree still exists
244
+ test -d "$WORKTREE_PATH" && echo "WORKTREE_PRESERVED=true" || echo "WORKTREE_PRESERVED=false"
245
+ forge session list | grep test-refcount-owner
246
+ ```
247
+
248
+ - [ ] Guest session deleted successfully
249
+ - [ ] Worktree directory preserved (owner session still holds a reference)
250
+ - [ ] Owner session still listed and functional
251
+
252
+ ### 5.10 Worktree Session (Isolation)
253
+
254
+ <!-- auto -->
255
+
256
+ ```bash
257
+ # Clean up from previous runs
258
+ forge session delete test-session-worktree --force 2>/dev/null || true
259
+
260
+ # Create a session with a git worktree (no Claude launch)
261
+ forge session start test-session-worktree --worktree --no-launch
262
+
263
+ # Root-level Forge projects keep manifests centrally in the project root's
264
+ # .forge/sessions/, not inside the worktree. The worktree is only the working
265
+ # directory for code isolation.
266
+ WORKTREE_PATH=$(forge session show test-session-worktree --json | jq -r '.worktree.path')
267
+ MANIFEST="$FORGE_TEST_REPO/.forge/sessions/test-session-worktree/forge.session.json"
268
+
269
+ # Verify worktree recorded in manifest
270
+ cat "$MANIFEST" | jq '.worktree'
271
+
272
+ # Verify the worktree path exists on disk
273
+ test -d "$WORKTREE_PATH" && echo "WORKTREE_EXISTS=true" || echo "WORKTREE_EXISTS=false"
274
+
275
+ # Verify it is marked as a worktree session
276
+ cat "$MANIFEST" | jq '.worktree.is_worktree'
277
+ ```
278
+
279
+ - [ ] Worktree session created
280
+ - [ ] Manifest at `$FORGE_TEST_REPO/.forge/sessions/test-session-worktree/` (central)
281
+ - [ ] Manifest contains worktree path + branch
282
+ - [ ] Worktree path exists on disk
283
+ - [ ] `worktree.is_worktree` is `true`
284
+
285
+ ### 5.11 System Prompt Generation
286
+
287
+ <!-- requires: api_key -->
288
+
289
+ <!-- human:guided -->
290
+
291
+ System prompts are injected at launch time (`--system-prompt` is mutually exclusive with `--no-launch`). In the
292
+ **container shell**, launch a session with a custom system prompt, verify the generated file, then exit.
293
+
294
+ ```
295
+ # Clean up from previous runs
296
+ forge session delete test-session-system-prompt --force 2>/dev/null || true
297
+
298
+ # Launch a session with an inline system prompt.
299
+ # Say "hello", then exit with /exit.
300
+ forge session start test-session-system-prompt --proxy "$FORGE_QA_OPENAI_PROXY" --system-prompt "FORGE_MANUAL_TEST_SYSTEM_PROMPT"
301
+
302
+ # After exiting Claude, verify the generated file
303
+ test -f .claude/forge.system-prompt.generated.md && echo "FILE_EXISTS=true" || echo "FILE_EXISTS=false"
304
+ grep -c "FORGE_MANUAL_TEST_SYSTEM_PROMPT" .claude/forge.system-prompt.generated.md
305
+ ```
306
+
307
+ - [ ] Generated system prompt file created at `.claude/forge.system-prompt.generated.md`
308
+ - [ ] Generated file contains the provided prompt text
309
+
310
+ ### 5.12 Session Show
311
+
312
+ <!-- auto -->
313
+
314
+ ```bash
315
+ # Show session by name
316
+ forge session show test-session-1
317
+
318
+ # Show session via FORGE_SESSION env var
319
+ FORGE_SESSION=test-session-1 forge session show
320
+
321
+ # No name and no env var -> guidance message
322
+ forge session show
323
+ ```
324
+
325
+ - [ ] `forge session show <name>` displays session details
326
+ - [ ] `forge session show` without name or env var shows guidance message
327
+
328
+ ### 5.13 Fork with `--strategy` (Context Assembly)
329
+
330
+ <!-- prereq: 5.1 -->
331
+
332
+ <!-- auto -->
333
+
334
+ Verify that `--strategy` controls handoff content density on worktree forks.
335
+
336
+ ```bash
337
+ # Setup: create parent with a mock transcript for handoff generation
338
+ forge session delete test-strat-parent --force 2>/dev/null || true
339
+ forge session delete test-fork-strat-min --force 2>/dev/null || true
340
+ forge session delete test-fork-strat-struct --force 2>/dev/null || true
341
+ git worktree remove "${FORGE_TEST_REPO}-test-fork-strat-min" --force 2>/dev/null || true
342
+ git worktree remove "${FORGE_TEST_REPO}-test-fork-strat-struct" --force 2>/dev/null || true
343
+ git branch -D test-fork-strat-min 2>/dev/null || true
344
+ git branch -D test-fork-strat-struct 2>/dev/null || true
345
+
346
+ forge session start test-strat-parent --no-launch
347
+
348
+ # Inject a fixture transcript so handoff has content to assemble
349
+ PARENT_JSON=".forge/sessions/test-strat-parent/forge.session.json"
350
+ TDIR=".forge/artifacts/test-strat-parent/transcripts"
351
+ mkdir -p "$TDIR"
352
+ cat > "$TDIR/fixture.jsonl" << 'JSONL'
353
+ {"requestId":"r1","timestamp":"2026-01-01T00:00:00Z","message":{"role":"user","content":[{"type":"text","text":"Create a hello function"}]}}
354
+ {"requestId":"r1","timestamp":"2026-01-01T00:00:01Z","message":{"role":"assistant","content":[{"type":"text","text":"I will create a hello function."},{"type":"tool_use","id":"t1","name":"Write","input":{"file_path":"hello.py","content":"def hello(): return 'hi'"}}]}}
355
+ {"requestId":"r1","timestamp":"2026-01-01T00:00:02Z","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"t1","content":"OK"}]}}
356
+ JSONL
357
+ jq --arg tp "$PWD/$TDIR/fixture.jsonl" \
358
+ '.confirmed.transcript_path = $tp | .confirmed.claude_session_id = "fixture-strat"' \
359
+ "$PARENT_JSON" > /tmp/s.json && mv /tmp/s.json "$PARENT_JSON"
360
+
361
+ # Fork with --strategy minimal
362
+ forge session fork test-strat-parent --name test-fork-strat-min --worktree --strategy minimal --no-launch
363
+ HANDOFF_MIN="${FORGE_TEST_REPO}-test-fork-strat-min/.forge/prev_sessions/test-strat-parent/children/test-fork-strat-min.md"
364
+ test -f "$HANDOFF_MIN" && echo "MIN_HANDOFF=true" || echo "MIN_HANDOFF=false"
365
+ wc -l < "$HANDOFF_MIN"
366
+
367
+ # Fork with --strategy structured
368
+ forge session fork test-strat-parent --name test-fork-strat-struct --worktree --strategy structured --no-launch
369
+ HANDOFF_STRUCT="${FORGE_TEST_REPO}-test-fork-strat-struct/.forge/prev_sessions/test-strat-parent/children/test-fork-strat-struct.md"
370
+ test -f "$HANDOFF_STRUCT" && echo "STRUCT_HANDOFF=true" || echo "STRUCT_HANDOFF=false"
371
+ wc -l < "$HANDOFF_STRUCT"
372
+ ```
373
+
374
+ - [ ] Minimal handoff file created at expected path
375
+ - [ ] Structured handoff file created at expected path
376
+ - [ ] Structured handoff contains more content than minimal (higher line count)
377
+
378
+ ### 5.14 Fork with `--inline-plan`
379
+
380
+ <!-- prereq: 5.1 -->
381
+
382
+ <!-- auto -->
383
+
384
+ Verify that `--inline-plan` inlines approved plan content in the handoff context file.
385
+
386
+ ```bash
387
+ # Setup: create parent with a mock plan via confirmed.latest_plan_path
388
+ forge session delete test-plan-parent --force 2>/dev/null || true
389
+ forge session delete test-fork-plan --force 2>/dev/null || true
390
+ git worktree remove "${FORGE_TEST_REPO}-test-fork-plan" --force 2>/dev/null || true
391
+ git branch -D test-fork-plan 2>/dev/null || true
392
+
393
+ forge session start test-plan-parent --no-launch
394
+
395
+ # Create a mock plan file and wire it into the manifest
396
+ mkdir -p .claude/plans
397
+ cat > .claude/plans/test-plan.md << 'PLAN'
398
+ # Approved Plan
399
+
400
+ 1. Create `src/demo.py` with a greet function
401
+ 2. Add unit test in `tests/test_demo.py`
402
+ 3. Run tests to verify
403
+ PLAN
404
+
405
+ PARENT_JSON=".forge/sessions/test-plan-parent/forge.session.json"
406
+ jq '.confirmed.latest_plan_path = ".claude/plans/test-plan.md" | .confirmed.claude_session_id = "fixture-plan"' \
407
+ "$PARENT_JSON" > /tmp/p.json && mv /tmp/p.json "$PARENT_JSON"
408
+
409
+ # Fork with --inline-plan (plan content should appear in handoff)
410
+ forge session fork test-plan-parent --name test-fork-plan --worktree --inline-plan --no-launch
411
+
412
+ HANDOFF="${FORGE_TEST_REPO}-test-fork-plan/.forge/prev_sessions/test-plan-parent/children/test-fork-plan.md"
413
+ test -f "$HANDOFF" && echo "HANDOFF_EXISTS=true" || echo "HANDOFF_EXISTS=false"
414
+ grep -c "Approved Plan" "$HANDOFF"
415
+ grep -c "greet function" "$HANDOFF"
416
+ ```
417
+
418
+ - [ ] Handoff file created in worktree fork
419
+ - [ ] Handoff contains plan heading ("Approved Plan")
420
+ - [ ] Handoff contains plan details ("greet function")
421
+
422
+ ### 5.15 Fork `--into` (Existing Worktree)
423
+
424
+ <!-- prereq: 2.4, 4.2, 5.6 -->
425
+
426
+ <!-- requires: api_key -->
427
+
428
+ <!-- human:guided -->
429
+
430
+ Fork a session into an existing non-main worktree using `--into`. Unlike `--worktree` (which creates a new worktree),
431
+ `--into` reuses an existing one and marks the session as non-owning — the worktree is preserved when the session is
432
+ deleted. In the **container shell**, create a target worktree, fork into it, and interact briefly with Claude to confirm
433
+ parent context, then exit (`/exit`).
434
+
435
+ Note: `--into` targets have their own Forge installation (required), so manifests live in the target worktree's
436
+ `.forge/sessions/` — not centrally. Like `fork --worktree`, the fork manifest belongs to the destination checkout; this
437
+ differs from root-level `session start --worktree`, which keeps the session manifest in the project root's `.forge/`.
438
+
439
+ ```
440
+ # Clean up from previous runs
441
+ forge session delete test-fork-into --force 2>/dev/null || true
442
+ TARGET_WORKTREE="${FORGE_TEST_REPO}-test-into-target"
443
+ git worktree remove "$TARGET_WORKTREE" --force 2>/dev/null || true
444
+ git branch -D test-into-target 2>/dev/null || true
445
+
446
+ # Create a target worktree (simulating an existing feature branch)
447
+ git worktree add "$TARGET_WORKTREE" -b test-into-target
448
+
449
+ # Install Forge extensions in the target worktree (required for --into)
450
+ cd "$TARGET_WORKTREE" && forge extension enable --scope local
451
+ cd "$FORGE_TEST_REPO"
452
+
453
+ # Fork the parent session into the existing worktree.
454
+ # Claude will launch with parent handoff context.
455
+ # Disable auto-memory so "where were we?" tests Forge handoff, not CC memory.
456
+ # Ask "where were we?" to confirm parent context, then exit (/exit).
457
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 forge session fork test-session-parent --name test-fork-into --into "$TARGET_WORKTREE"
458
+
459
+ # Verify fork
460
+ forge session show test-fork-into
461
+ # Manifest lives in target worktree (--into targets are their own forge_root)
462
+ cat "$TARGET_WORKTREE/.forge/sessions/test-fork-into/forge.session.json" | \
463
+ jq '{is_fork, parent_session, worktree: (.worktree | {path, is_worktree, owns_worktree})}'
464
+ ```
465
+
466
+ - [ ] Fork created in existing worktree at `${FORGE_TEST_REPO}-test-into-target`
467
+ - [ ] `forge session show` reports type as Fork with worktree info
468
+ - [ ] Manifest at `${FORGE_TEST_REPO}-test-into-target/.forge/sessions/test-fork-into/` (target's forge_root)
469
+ - [ ] Manifest has `is_fork: true`, `is_worktree: true`, `owns_worktree: false`
470
+ - [ ] Parent handoff context file present in target worktree
471
+ - [ ] Asking "where were we?" reflects parent context from 5.6
472
+
473
+ ### 5.16 Subprocess Proxy (Direct + Proxied Subprocesses)
474
+
475
+ <!-- prereq: 2.4, 4.2 -->
476
+
477
+ <!-- auto -->
478
+
479
+ ```bash
480
+ # Clean up from previous runs
481
+ forge session delete test-subprocess-proxy --force 2>/dev/null || true
482
+
483
+ # Create a session with --subprocess-proxy (direct main, proxied subprocesses)
484
+ forge session start test-subprocess-proxy --subprocess-proxy "$FORGE_QA_GEMINI_PROXY" --no-launch
485
+
486
+ # Verify intent recorded in session manifest
487
+ jq '.intent.subprocess_proxy' .forge/sessions/test-subprocess-proxy/forge.session.json
488
+
489
+ # Verify session is direct mode (no proxy routing for main session)
490
+ jq '{proxy: .intent.proxy, started_with_proxy: .confirmed.started_with_proxy}' \
491
+ .forge/sessions/test-subprocess-proxy/forge.session.json
492
+ ```
493
+
494
+ - [ ] Session created with `--subprocess-proxy` flag (exit 0)
495
+ - [ ] `intent.subprocess_proxy` matches `$FORGE_QA_GEMINI_PROXY` in session manifest
496
+ - [ ] `intent.proxy` is null (main session is direct mode)
497
+ - [ ] `confirmed.started_with_proxy` is null (no proxy for main session)
498
+
499
+ ### 5.17 Subprocess Proxy Mutual Exclusivity
500
+
501
+ <!-- auto -->
502
+
503
+ ```bash
504
+ # Try combining --subprocess-proxy with --proxy (should error)
505
+ forge session start test-invalid-subproxy \
506
+ --subprocess-proxy "$FORGE_QA_GEMINI_PROXY" --proxy "$FORGE_QA_OPENAI_PROXY" --no-launch 2>&1
507
+ echo "EXIT=$?"
508
+ ```
509
+
510
+ - [ ] Error message about mutual exclusivity of `--subprocess-proxy` and `--proxy`
511
+ - [ ] Exit code is non-zero
512
+
513
+ ### 5.18 Subprocess Proxy Inheritance (Fork)
514
+
515
+ <!-- prereq: 5.16 -->
516
+
517
+ <!-- auto -->
518
+
519
+ ```bash
520
+ # Seed confirmed.claude_session_id so fork guard passes
521
+ PARENT_JSON=".forge/sessions/test-subprocess-proxy/forge.session.json"
522
+ jq '.confirmed.claude_session_id = "fixture-subproxy"' "$PARENT_JSON" > /tmp/sp.json \
523
+ && mv /tmp/sp.json "$PARENT_JSON"
524
+
525
+ # Fork the session
526
+ forge session delete test-fork-subproxy --force 2>/dev/null || true
527
+ forge session fork test-subprocess-proxy --name test-fork-subproxy --no-launch
528
+
529
+ # Verify forked session inherits subprocess_proxy
530
+ jq '.intent.subprocess_proxy' .forge/sessions/test-fork-subproxy/forge.session.json
531
+
532
+ # Clean up
533
+ forge session delete test-subprocess-proxy --force 2>/dev/null || true
534
+ forge session delete test-fork-subproxy --force 2>/dev/null || true
535
+ ```
536
+
537
+ - [ ] Forked session inherits `subprocess_proxy` from parent
538
+ - [ ] Child `intent.subprocess_proxy` matches `$FORGE_QA_GEMINI_PROXY`
539
+ - [ ] Both test sessions cleaned up
540
+
541
+ ---