gentle-pi 3.7.0 → 4.0.0

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 (316) hide show
  1. package/README.md +37 -6
  2. package/assets/agents/gentle-ai-explore.md +4 -4
  3. package/assets/agents/gentle-ai-verify.md +6 -4
  4. package/assets/agents/gentle-ai-worker.md +7 -9
  5. package/assets/orchestrator-delegation.md +40 -41
  6. package/assets/orchestrator-memory.md +1 -22
  7. package/assets/orchestrator-skills.md +1 -1
  8. package/assets/orchestrator.md +12 -24
  9. package/assets/support/strict-tdd-verify.md +4 -266
  10. package/assets/support/strict-tdd.md +8 -360
  11. package/bin/gentle-shell.mjs +254 -29
  12. package/docs/delegated-verification.md +26 -1
  13. package/docs/gentle-agents-activity.md +24 -0
  14. package/docs/gentle-shell.md +92 -28
  15. package/docs/native-authority-architecture.md +2 -2
  16. package/docs/prompt-history.md +280 -0
  17. package/docs/readme-reference.md +112 -206
  18. package/docs/telemetry.md +1 -1
  19. package/docs/yolo-mode.md +86 -0
  20. package/extensions/child-context.ts +26 -0
  21. package/extensions/child-safety.ts +23 -0
  22. package/extensions/gentle-agents.ts +429 -262
  23. package/extensions/gentle-ai.ts +781 -683
  24. package/extensions/gentle-shell.ts +1375 -88
  25. package/extensions/gentle-stats.ts +101 -0
  26. package/extensions/gentle-todo.ts +18 -7
  27. package/extensions/history/atomic-write.ts +38 -0
  28. package/extensions/history/hide-prompts.ts +183 -0
  29. package/extensions/history/index.ts +1419 -0
  30. package/extensions/history/load-shared-history.ts +39 -0
  31. package/extensions/history/selector-helpers.ts +538 -0
  32. package/extensions/history/session-scan.ts +233 -0
  33. package/extensions/history/store.ts +1119 -0
  34. package/extensions/nan-provider.ts +6 -0
  35. package/extensions/quiet-tools.ts +179 -87
  36. package/extensions/resume-hint.ts +60 -0
  37. package/extensions/skill-registry.ts +16 -12
  38. package/extensions/startup-banner.ts +60 -31
  39. package/lib/agent-assets.ts +604 -0
  40. package/lib/agent-profile-pin.ts +12 -0
  41. package/lib/agents-message-delivery.ts +181 -0
  42. package/lib/agents-protocol.ts +60 -0
  43. package/lib/agents-runner.ts +96 -98
  44. package/lib/agents-view.ts +26 -3
  45. package/lib/agents-widget.ts +16 -9
  46. package/lib/append-system-prompt.ts +21 -0
  47. package/lib/bounded-writer-admission.ts +147 -0
  48. package/lib/card-style-policy.ts +60 -0
  49. package/lib/child-context-files.ts +166 -0
  50. package/lib/codemode-renderer.ts +185 -0
  51. package/lib/command-palette-catalog.ts +3 -9
  52. package/lib/command-palette.ts +25 -14
  53. package/lib/destructive-command-guard.ts +144 -0
  54. package/lib/gentle-ai-elapsed-store.ts +87 -0
  55. package/lib/gentle-ai-renderer.ts +196 -39
  56. package/lib/gentle-shell-launcher.ts +24 -13
  57. package/lib/gentle-shell-resume-hint.ts +176 -0
  58. package/lib/history-capture-policy.ts +95 -0
  59. package/lib/model-routing-authority.ts +5 -1
  60. package/lib/nan-provider.ts +227 -0
  61. package/lib/native-review-cli.ts +49 -108
  62. package/lib/odd-phase-inference.ts +231 -0
  63. package/lib/odd-phase.ts +141 -0
  64. package/lib/overlay-repaint.ts +26 -0
  65. package/lib/pi-tui-keys.ts +53 -0
  66. package/lib/review-candidate-view-owner.ts +67 -17
  67. package/lib/review-candidate-view.ts +112 -25
  68. package/lib/review-reminder-receipt.ts +48 -8
  69. package/lib/review-risk-assessment.ts +156 -11
  70. package/lib/review-sidebar-state.ts +223 -0
  71. package/lib/selection-engine.ts +515 -0
  72. package/lib/session-messaging-grants.ts +135 -0
  73. package/lib/session-worktree-registry.ts +14 -2
  74. package/lib/shell-bar.ts +179 -24
  75. package/lib/shell-card.ts +287 -18
  76. package/lib/shell-changes-view.ts +2 -1
  77. package/lib/shell-prompt.ts +102 -6
  78. package/lib/shell-sidebar-layout.ts +78 -14
  79. package/lib/shell-sidebar.ts +15 -1
  80. package/lib/shell-todo.ts +23 -13
  81. package/lib/shell-usage-view.ts +9 -4
  82. package/lib/shell-usage.ts +66 -10
  83. package/lib/stats-collector.ts +381 -0
  84. package/lib/stats-view.ts +431 -0
  85. package/lib/theme-customization.ts +52 -0
  86. package/lib/vim-editor-adapter.ts +379 -0
  87. package/lib/vim-normal-engine.ts +154 -0
  88. package/lib/vim-operator-engine.ts +416 -0
  89. package/lib/vim-policy.ts +49 -0
  90. package/lib/vim-visual-engine.ts +107 -0
  91. package/lib/visual-customization-policy.ts +108 -0
  92. package/lib/visual-customize-view.ts +330 -0
  93. package/lib/visual-profiles.ts +228 -0
  94. package/lib/yolo-session-policy.ts +240 -0
  95. package/package.json +20 -8
  96. package/runtime/gentle-shell-launcher.mjs +23 -12
  97. package/runtime/gentle-shell-resume-hint.mjs +177 -0
  98. package/runtime/native-review-cli.mjs +49 -108
  99. package/runtime/review-risk-assessment.mjs +154 -9
  100. package/scripts/build-runtime-modules.mjs +1 -0
  101. package/scripts/gentle-ai-installer.mjs +14 -13
  102. package/scripts/mirror-odd-routing.mjs +2 -2
  103. package/scripts/run-test-suite.mjs +76 -0
  104. package/scripts/test-packed-runner.mjs +31 -14
  105. package/scripts/verify-package-files.mjs +11 -22
  106. package/skills/branch-pr/SKILL.md +24 -52
  107. package/skills/chained-pr/SKILL.md +31 -15
  108. package/skills/chained-pr/references/chaining-details.md +31 -20
  109. package/skills/gentle-ai/SKILL.md +9 -15
  110. package/skills/issue-creation/SKILL.md +8 -2
  111. package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
  112. package/skills/work-unit-commits/SKILL.md +4 -3
  113. package/tests/agent-profiles.test.ts +18 -0
  114. package/tests/agents-fake-child.ts +2 -2
  115. package/tests/agents-message-delivery.test.ts +106 -0
  116. package/tests/agents-protocol.test.ts +40 -0
  117. package/tests/agents-runner.test.ts +378 -89
  118. package/tests/agents-view-thread-identity.test.ts +169 -0
  119. package/tests/agents-view.test.ts +8 -2
  120. package/tests/agents-widget.test.ts +154 -15
  121. package/tests/append-system-prompt-route.test.ts +160 -0
  122. package/tests/append-system-prompt.test.ts +46 -0
  123. package/tests/artifact-language.test.ts +19 -213
  124. package/tests/ask-user-question.test.ts +44 -1
  125. package/tests/asset-installation-runtime.test.ts +5 -16
  126. package/tests/autonomous-guard.test.ts +69 -1
  127. package/tests/bounded-writer-admission.test.ts +95 -0
  128. package/tests/branch-pr-skill.test.ts +43 -0
  129. package/tests/card-style-policy.test.ts +55 -0
  130. package/tests/chained-pr-skill.test.ts +124 -0
  131. package/tests/child-context-files.test.ts +255 -0
  132. package/tests/child-safety.test.ts +82 -0
  133. package/tests/codemode-rendering.test.ts +491 -0
  134. package/tests/command-palette.test.ts +39 -3
  135. package/tests/delegated-key-learnings-contract.test.ts +0 -76
  136. package/tests/destructive-command-guard.test.ts +84 -0
  137. package/tests/devbinary/native-review-parity.devtest.ts +170 -2
  138. package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
  139. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
  140. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
  141. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
  142. package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
  143. package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
  144. package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
  145. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
  146. package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
  147. package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
  148. package/tests/generic-agent-tools.test.ts +54 -0
  149. package/tests/gentle-agents.test.ts +1047 -252
  150. package/tests/gentle-ai-binary.test.ts +3 -3
  151. package/tests/gentle-ai-elapsed-store.test.ts +68 -0
  152. package/tests/gentle-ai-installer.test.ts +68 -54
  153. package/tests/gentle-ai-renderer.test.ts +487 -8
  154. package/tests/gentle-ai.test.ts +288 -65
  155. package/tests/gentle-card-text.ts +2 -1
  156. package/tests/gentle-shell-bin.test.ts +651 -114
  157. package/tests/gentle-shell-launcher.test.ts +99 -52
  158. package/tests/gentle-shell-resume-hint.test.ts +270 -0
  159. package/tests/gentle-shell.test.ts +3839 -187
  160. package/tests/gentle-stats.test.ts +152 -0
  161. package/tests/gentle-theme.test.ts +4 -1
  162. package/tests/gentle-todo.test.ts +80 -8
  163. package/tests/history-atomic-write.test.ts +57 -0
  164. package/tests/history-capture-policy.test.ts +102 -0
  165. package/tests/history-command-registration.test.ts +164 -0
  166. package/tests/history-dedupe-entries.test.ts +123 -0
  167. package/tests/history-delete-backfill.test.ts +190 -0
  168. package/tests/history-delete-confirm.test.ts +460 -0
  169. package/tests/history-dispatch.test.ts +180 -0
  170. package/tests/history-drain-hidden.test.ts +110 -0
  171. package/tests/history-drain-order.test.ts +98 -0
  172. package/tests/history-expanded-globals.test.ts +62 -0
  173. package/tests/history-gc.test.ts +832 -0
  174. package/tests/history-header-layout.test.ts +265 -0
  175. package/tests/history-hide-prompts.test.ts +275 -0
  176. package/tests/history-lazy-windowing.test.ts +508 -0
  177. package/tests/history-legacy-migrate-v2.test.ts +297 -0
  178. package/tests/history-load-shared-history.test.ts +53 -0
  179. package/tests/history-max-results-cap.test.ts +76 -0
  180. package/tests/history-multi-reader.test.ts +203 -0
  181. package/tests/history-off-path.test.ts +170 -0
  182. package/tests/history-openflow-integration.test.ts +173 -0
  183. package/tests/history-overlay-margin.test.ts +326 -0
  184. package/tests/history-preview-layout.test.ts +93 -0
  185. package/tests/history-registry.test.ts +143 -0
  186. package/tests/history-scope-delete.test.ts +411 -0
  187. package/tests/history-search-caret-keys.test.ts +142 -0
  188. package/tests/history-seed-bootstrap.test.ts +170 -0
  189. package/tests/history-seed-regen.test.ts +129 -0
  190. package/tests/history-selector-windowing.test.ts +94 -0
  191. package/tests/history-session-scan-directory.test.ts +87 -0
  192. package/tests/history-session-scan-extract.test.ts +583 -0
  193. package/tests/history-session-writer.test.ts +351 -0
  194. package/tests/history-store-paths.test.ts +79 -0
  195. package/tests/history-tombstone-exact.test.ts +139 -0
  196. package/tests/history-wheel-mouse.test.ts +242 -0
  197. package/tests/inprocess-reviewer.test.ts +29 -19
  198. package/tests/issue-creation-skill.test.ts +61 -0
  199. package/tests/model-routing-authority.test.ts +16 -0
  200. package/tests/nan-provider.test.ts +471 -0
  201. package/tests/native-review-capability-contract.test.ts +7 -1
  202. package/tests/native-review-cli.test.ts +6 -120
  203. package/tests/native-review-parity-runtime.test.ts +100 -3
  204. package/tests/odd-integration.test.ts +67 -0
  205. package/tests/odd-phase-inference.test.ts +213 -0
  206. package/tests/odd-phase-loader.test.ts +253 -0
  207. package/tests/odd-phase.test.ts +307 -0
  208. package/tests/odd-routing-canonical-ratchet.test.ts +11 -6
  209. package/tests/odd-routing-contract.test.ts +86 -35
  210. package/tests/orchestrator-budget.test.ts +14 -39
  211. package/tests/orchestrator-rdd-ownership.test.ts +3 -3
  212. package/tests/overlay-repaint.test.ts +74 -0
  213. package/tests/package-manifest.test.ts +252 -115
  214. package/tests/packed-runner-owned-path.test.ts +46 -0
  215. package/tests/persona-single-channel.test.ts +6 -6
  216. package/tests/provider-defect-handoff.test.ts +3 -11
  217. package/tests/quiet-bash-runtime.test.ts +76 -0
  218. package/tests/quiet-tool-rendering.test.ts +409 -184
  219. package/tests/rdd-aware-verification-contract.test.ts +76 -1
  220. package/tests/rdd-status-line.test.ts +9 -4
  221. package/tests/resume-hint-extension.test.ts +122 -0
  222. package/tests/review-agent-end-preflight.test.ts +176 -12
  223. package/tests/review-candidate-owner-retry.test.ts +22 -1
  224. package/tests/review-candidate-view.test.ts +298 -0
  225. package/tests/review-contract-prompt.test.ts +108 -43
  226. package/tests/review-controller-lock-status.test.ts +0 -1
  227. package/tests/review-controller-native-routing.test.ts +611 -5
  228. package/tests/review-controller-workspace-root.test.ts +163 -4
  229. package/tests/review-host-relay-routing.test.ts +338 -2
  230. package/tests/review-integration-v2-forward.test.ts +200 -0
  231. package/tests/review-ledger-contract.test.ts +10 -34
  232. package/tests/review-reminder-receipt.test.ts +47 -1
  233. package/tests/review-risk-assessment.test.ts +498 -6
  234. package/tests/review-sidebar-state.test.ts +402 -0
  235. package/tests/run-test-suite.test.ts +124 -0
  236. package/tests/runtime-harness.mjs +145 -786
  237. package/tests/runtime-metrics-children.test.ts +16 -23
  238. package/tests/selection-engine.test.ts +421 -0
  239. package/tests/session-messaging-grants.test.ts +255 -0
  240. package/tests/session-worktree-registry.test.ts +77 -0
  241. package/tests/shell-bar.test.ts +382 -1
  242. package/tests/shell-card.test.ts +353 -1
  243. package/tests/shell-changes-view.test.ts +52 -0
  244. package/tests/shell-prompt.test.ts +94 -2
  245. package/tests/shell-sidebar-layout.test.ts +325 -21
  246. package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
  247. package/tests/shell-todo.test.ts +87 -1
  248. package/tests/shell-usage-view.test.ts +27 -0
  249. package/tests/shell-usage.test.ts +73 -0
  250. package/tests/skill-registry.test.ts +50 -1
  251. package/tests/startup-banner.test.ts +130 -2
  252. package/tests/stats-collector.test.ts +195 -0
  253. package/tests/stats-view.test.ts +202 -0
  254. package/tests/telemetry-trigger.test.ts +81 -20
  255. package/tests/theme-customization.test.ts +72 -0
  256. package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
  257. package/tests/vim-editor-adapter.test.ts +804 -0
  258. package/tests/vim-normal-engine.test.ts +101 -0
  259. package/tests/vim-operator-engine.test.ts +215 -0
  260. package/tests/vim-policy.test.ts +19 -0
  261. package/tests/vim-visual-engine.test.ts +52 -0
  262. package/tests/visual-customization-policy.test.ts +110 -0
  263. package/tests/visual-customize-view.test.ts +418 -0
  264. package/tests/visual-profiles.test.ts +87 -0
  265. package/tests/yolo-customize.test.ts +256 -0
  266. package/tests/yolo-mode-runtime.test.ts +161 -0
  267. package/tests/yolo-mode.test.ts +261 -0
  268. package/tests/yolo-session-policy.test.ts +59 -0
  269. package/themes/Gentle.json +2 -1
  270. package/themes/Gentleman-Cute.json +2 -1
  271. package/themes/Gentleman-Sexy.json +2 -1
  272. package/assets/agents/sdd-apply.md +0 -159
  273. package/assets/agents/sdd-archive.md +0 -228
  274. package/assets/agents/sdd-design.md +0 -49
  275. package/assets/agents/sdd-explore.md +0 -48
  276. package/assets/agents/sdd-init.md +0 -56
  277. package/assets/agents/sdd-onboard.md +0 -52
  278. package/assets/agents/sdd-proposal.md +0 -64
  279. package/assets/agents/sdd-remediate.md +0 -37
  280. package/assets/agents/sdd-research.md +0 -49
  281. package/assets/agents/sdd-spec.md +0 -192
  282. package/assets/agents/sdd-status.md +0 -54
  283. package/assets/agents/sdd-tasks.md +0 -108
  284. package/assets/agents/sdd-verify.md +0 -124
  285. package/assets/chains/sdd-full.chain.md +0 -83
  286. package/assets/chains/sdd-plan.chain.md +0 -56
  287. package/assets/chains/sdd-verify.chain.md +0 -43
  288. package/assets/sdd-orchestrator-workflow.md +0 -319
  289. package/assets/support/sdd-status-contract.md +0 -77
  290. package/docs/assets/diagrams/sdd-cycle.svg +0 -14
  291. package/extensions/sdd-init.ts +0 -816
  292. package/lib/openspec-deltas.ts +0 -156
  293. package/lib/sdd-preflight.ts +0 -1066
  294. package/lib/sdd-research-capabilities.ts +0 -94
  295. package/lib/sdd-status.ts +0 -26
  296. package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
  297. package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
  298. package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
  299. package/tests/openspec-deltas.test.ts +0 -209
  300. package/tests/sdd-agent-tools.test.ts +0 -156
  301. package/tests/sdd-archive-replay.test.ts +0 -82
  302. package/tests/sdd-classical-continuation.test.ts +0 -74
  303. package/tests/sdd-execution-routing-contract.test.ts +0 -44
  304. package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
  305. package/tests/sdd-native-managed-uptake.test.ts +0 -243
  306. package/tests/sdd-no-attempts-contract.test.ts +0 -15
  307. package/tests/sdd-odd-integration.test.ts +0 -33
  308. package/tests/sdd-optional-research.test.ts +0 -124
  309. package/tests/sdd-planning-routing-contract.test.ts +0 -45
  310. package/tests/sdd-preflight-rpc-input.test.ts +0 -125
  311. package/tests/sdd-preflight.test.ts +0 -541
  312. package/tests/sdd-research-capabilities.test.ts +0 -114
  313. package/tests/sdd-research-live.test.ts +0 -241
  314. package/tests/sdd-selection-transport.test.ts +0 -653
  315. package/tests/sdd-status.test.ts +0 -9
  316. package/tests/sdd-task-truth.test.ts +0 -43
@@ -0,0 +1,46 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, mkdirSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+ import { resolveInstalledJitiStaticEntry } from "../scripts/test-packed-runner.mjs";
7
+
8
+ function withAliasedConsumer(run: (consumer: string, sdkManifest: string, jitiRoot: string) => void): void {
9
+ const disposable = mkdtempSync(join(tmpdir(), "packed-owned-path-"));
10
+ try {
11
+ const actual = join(disposable, "actual");
12
+ const consumer = join(disposable, "alias", "consumer");
13
+ const actualConsumer = join(actual, "consumer");
14
+ const sdkRoot = join(actualConsumer, "node_modules", "@earendil-works", "pi-coding-agent");
15
+ const jitiRoot = join(actualConsumer, "node_modules", "jiti");
16
+ mkdirSync(sdkRoot, { recursive: true });
17
+ mkdirSync(join(jitiRoot, "dist"), { recursive: true });
18
+ symlinkSync(actual, join(disposable, "alias"), "dir");
19
+ writeFileSync(join(sdkRoot, "package.json"), JSON.stringify({ name: "@earendil-works/pi-coding-agent", version: "0.87.1", dependencies: { jiti: "2.7.0" } }));
20
+ writeFileSync(join(jitiRoot, "package.json"), JSON.stringify({ name: "jiti", version: "2.7.0", exports: { "./static": { types: "./dist/jiti.d.ts", import: "./dist/jiti.mjs" }, "./package.json": "./package.json" } }));
21
+ run(consumer, join(consumer, "node_modules", "@earendil-works", "pi-coding-agent", "package.json"), jitiRoot);
22
+ } finally {
23
+ rmSync(disposable, { recursive: true, force: true });
24
+ }
25
+ }
26
+
27
+ const checks = { sdkManifest: "sdk-manifest", sdkVersion: "sdk-version", jitiManifest: "jiti-manifest-owned", jitiStaticExport: "jiti-static-export", jitiEntry: "jiti-entry-owned", jitiVersion: "jiti-version" };
28
+
29
+ test("accepts a canonical Jiti manifest beneath an aliased ancestor", () => {
30
+ withAliasedConsumer((consumer, sdkManifest, jitiRoot) => {
31
+ writeFileSync(join(jitiRoot, "dist", "jiti.mjs"), "export const createJiti = () => {};\n");
32
+ const seen: string[] = [];
33
+ const entry = resolveInstalledJitiStaticEntry(consumer, sdkManifest, "0.87.1", (check: string) => seen.push(check), checks);
34
+ assert.equal(entry, join(consumer, "node_modules", "jiti", "dist", "jiti.mjs"));
35
+ assert.equal(realpathSync.native(entry), join(realpathSync.native(jitiRoot), "dist", "jiti.mjs"));
36
+ assert.ok(seen.includes("jiti-manifest-owned"));
37
+ });
38
+ });
39
+
40
+ test("rejects a symlink within the owned Jiti static entry", () => {
41
+ withAliasedConsumer((consumer, sdkManifest, jitiRoot) => {
42
+ writeFileSync(join(jitiRoot, "dist", "real.mjs"), "export const createJiti = () => {};\n");
43
+ symlinkSync("real.mjs", join(jitiRoot, "dist", "jiti.mjs"));
44
+ assert.throws(() => resolveInstalledJitiStaticEntry(consumer, sdkManifest, "0.87.1", () => {}, checks), /refusing symbolic link in owned package/);
45
+ });
46
+ });
@@ -262,7 +262,7 @@ test("Table A rule: wrapper :180/:181 + orchestrator :9,:12 self-description MER
262
262
  );
263
263
  assert.match(
264
264
  prompt,
265
- /I am el Gentleman: a Pi-specific coding-agent harness for controlled development, with a senior architect persona\. I run Organic Driven Development by default and SDD\/OpenSpec when explicitly selected, coordinate subagents, use phase artifacts, run commands, and edit files\. I am not a generic chatbot\./,
265
+ /I am el Gentleman: a Pi-specific coding-agent harness for controlled development, with a senior architect persona\. I run Organic Driven Development, coordinate subagents, track substantial work, run commands, and edit files\. I am not a generic chatbot\./,
266
266
  `[${persona}] the richer translated self-description paragraph (orchestrator :9,:12) must survive in the wrapper`,
267
267
  );
268
268
  }
@@ -290,12 +290,12 @@ test("Table A rule: persona-mode selection (trimmed) survives; language clause N
290
290
  }
291
291
  });
292
292
 
293
- test("Table A rule: SDD/OpenSpec artifacts + subagents core-capabilities bullet survives (KEEP once)", () => {
293
+ test("Table A rule: ODD and subagents core-capabilities bullet survives (KEEP once)", () => {
294
294
  for (const persona of ["gentleman", "neutral"] as const) {
295
295
  const prompt = __testing.buildGentlePrompt(persona);
296
296
  assert.match(
297
297
  prompt,
298
- /Mention ODD as the default workflow, SDD\/OpenSpec phase artifacts, and subagents as core capabilities\./,
298
+ /Mention ODD as the development workflow and subagents as a core capability\./,
299
299
  `[${persona}] wrapper :182 rule must survive`,
300
300
  );
301
301
  }
@@ -385,10 +385,10 @@ test("Table B rule: LB5 exceptions kept verbatim in orchestrator (unique)", () =
385
385
  /Ask a subagent to produce Spanish only when its output is intended to be pasted directly to the user/,
386
386
  `[${persona}] LB5 exceptions bullet 2 must remain verbatim`,
387
387
  );
388
- assert.match(
388
+ assert.doesNotMatch(
389
389
  prompt,
390
390
  /SDD\/OpenSpec artifact content may follow the project's established language/,
391
- `[${persona}] LB5 exceptions bullet 3 must remain verbatim`,
391
+ `[${persona}] retired artifact exception must be absent`,
392
392
  );
393
393
  }
394
394
  });
@@ -439,7 +439,7 @@ test("dup guard (exact-string): 'Do not claim portability outside the Pi runtime
439
439
 
440
440
  test("dup guard (exact-string): identity self-description sentence occurs exactly once", () => {
441
441
  const selfDescription =
442
- "I am el Gentleman: a Pi-specific coding-agent harness for controlled development, with a senior architect persona. I run Organic Driven Development by default and SDD/OpenSpec when explicitly selected, coordinate subagents, use phase artifacts, run commands, and edit files. I am not a generic chatbot.";
442
+ "I am el Gentleman: a Pi-specific coding-agent harness for controlled development, with a senior architect persona. I run Organic Driven Development, coordinate subagents, track substantial work, run commands, and edit files. I am not a generic chatbot.";
443
443
  for (const persona of ["gentleman", "neutral"] as const) {
444
444
  const prompt = __testing.buildGentlePrompt(persona);
445
445
  assert.equal(
@@ -17,10 +17,7 @@ import test from "node:test";
17
17
 
18
18
  const REPO_ROOT = join(import.meta.dirname, "..");
19
19
  const DELEGATION_PATH = join(REPO_ROOT, "assets", "orchestrator-delegation.md");
20
- const SDD_WORKFLOW_PATH = join(REPO_ROOT, "assets", "sdd-orchestrator-workflow.md");
21
-
22
20
  const DELEGATION = readFileSync(DELEGATION_PATH, "utf8");
23
- const SDD_WORKFLOW = readFileSync(SDD_WORKFLOW_PATH, "utf8");
24
21
 
25
22
  const CHOICE_TOKENS = ["report_and_continue", "continue_without_reporting", "stop_here"] as const;
26
23
 
@@ -230,17 +227,12 @@ test("orchestrator-delegation.md does NOT reference the rc.3 canon", () => {
230
227
  });
231
228
 
232
229
  // ---------------------------------------------------------------------------
233
- // 3 — SDD points to the single complete handoff contract
230
+ // 3 — The retained delegation contract owns the complete handoff
234
231
  // ---------------------------------------------------------------------------
235
232
 
236
- test("sdd-orchestrator-workflow.md points provider defects to the complete delegation contract", () => {
237
- assert.match(SDD_WORKFLOW, /## Provider Defect Handoff/);
238
- assert.match(
239
- SDD_WORKFLOW,
240
- /The full contract lives in `assets\/orchestrator-delegation\.md` under `#### Gentle AI Provider Defect Handoff \(MANDATORY\)`/i,
241
- );
233
+ test("retained delegation owns the complete provider defect handoff", () => {
242
234
  assert.match(DELEGATION, /^#### Gentle AI Provider Defect Handoff \(MANDATORY\)$/m);
243
- assert.doesNotMatch(SDD_WORKFLOW, /`report_and_continue`|`continue_without_reporting`|`stop_here`/);
235
+ assert.match(DELEGATION, /Both continue choices execute that exact captured decline invocation exactly once/);
244
236
  });
245
237
 
246
238
  test("the complete delegation handoff invokes `gentle-ai review mode disable` exactly once", () => {
@@ -0,0 +1,76 @@
1
+ import assert from "node:assert/strict";
2
+ import { spawnSync } from "node:child_process";
3
+ import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ async function proveNativeBash() {
10
+ const { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices,
11
+ ModelRuntime, SessionManager, SettingsManager } = await import("@earendil-works/pi-coding-agent");
12
+ const cwd = process.cwd();
13
+ const agentDir = process.env.PI_CODING_AGENT_DIR!;
14
+ const shellPath = process.env.GENTLE_TEST_SHELL!;
15
+ // Hide Windows default discovery only inside this isolated child. The explicit
16
+ // shell remains usable; on POSIX the prefix also detects a recreated executor.
17
+ for (const key of Object.keys(process.env)) if (key.toLowerCase() === "path") process.env[key] = "";
18
+ process.env.ProgramFiles = join(cwd, "absent-git");
19
+ process.env["ProgramFiles(x86)"] = join(cwd, "absent-git-x86");
20
+ const modelRuntime = await ModelRuntime.create({ authPath: join(agentDir, "empty-auth.json"),
21
+ modelsPath: null, modelsStorePath: join(agentDir, "models-store.json"), allowModelNetwork: false });
22
+ for (const quiet of [false, true]) {
23
+ const runtime = await createAgentSessionRuntime(async ({ cwd, sessionManager, sessionStartEvent }) => {
24
+ const services = await createAgentSessionServices({ cwd, agentDir, modelRuntime,
25
+ settingsManager: SettingsManager.inMemory({ shellPath, shellCommandPrefix: "printf configured-prefix;",
26
+ retry: { enabled: false }, compaction: { enabled: false } }),
27
+ resourceLoaderOptions: { noExtensions: true, noSkills: true, noPromptTemplates: true,
28
+ noThemes: true, noContextFiles: true,
29
+ additionalExtensionPaths: quiet ? [fileURLToPath(new URL("../extensions/quiet-tools.ts", import.meta.url))] : [] },
30
+ });
31
+ assert.deepEqual(services.resourceLoader.getExtensions().errors, []);
32
+ return { ...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent, tools: ["bash"] })),
33
+ services, diagnostics: services.diagnostics };
34
+ }, { cwd, agentDir, sessionManager: SessionManager.inMemory(cwd) });
35
+ try {
36
+ await runtime.session.bindExtensions({ mode: "print" });
37
+ const bash = runtime.session.agent.state.tools.find(tool => tool.name === "bash");
38
+ assert.ok(bash);
39
+ const result = await bash.execute("configured-shell", { command: "printf native-ok" }, new AbortController().signal);
40
+ assert.equal(result.content.filter(part => part.type === "text").map(part => part.text).join("\n"),
41
+ "configured-prefixnative-ok", `quiet=${quiet}: native effective shell settings must survive`);
42
+ assert.equal(runtime.session.messages.length, 0);
43
+ console.log(`native Bash configured settings: quiet=${quiet} passed`);
44
+ } finally { await runtime.dispose(); }
45
+ }
46
+ }
47
+
48
+ if (process.env.GENTLE_TEST_BASH_CHILD === "1") {
49
+ await proveNativeBash();
50
+ } else {
51
+ test("quiet tools preserve actual SDK configured Bash execution (#107)", (t) => {
52
+ const candidates = process.platform === "win32"
53
+ ? [join(process.env.ProgramFiles ?? "C:\\Program Files", "Git", "bin", "bash.exe"), "C:\\Program Files\\Git\\usr\\bin\\bash.exe"]
54
+ : ["/bin/bash", "/usr/bin/bash"];
55
+ const shell = candidates.find(existsSync);
56
+ if (!shell) { t.skip("requires an installed Bash; no binary installation is performed"); return; }
57
+ const root = mkdtempSync(join(tmpdir(), "gentle-quiet-bash-sdk-"));
58
+ const cwd = join(root, "project");
59
+ const home = join(root, "home");
60
+ const agentDir = join(home, "agent");
61
+ for (const path of [cwd, agentDir]) mkdirSync(path, { recursive: true });
62
+ const env = { ...process.env, HOME: home, USERPROFILE: home, XDG_CONFIG_HOME: join(home, "config"),
63
+ PI_CODING_AGENT_DIR: agentDir, GENTLE_PI_AGENT_HOME: agentDir, GENTLE_PI_CONFIG_HOME: join(home, "gentle-config"),
64
+ PI_OFFLINE: "1", GENTLE_PI_QUIET_TOOLS: "1", GENTLE_TEST_BASH_CHILD: "1", GENTLE_TEST_SHELL: shell };
65
+ try {
66
+ const result = spawnSync(process.execPath, ["--experimental-strip-types", fileURLToPath(import.meta.url)],
67
+ { cwd, env, encoding: "utf8", timeout: 30_000 });
68
+ assert.ifError(result.error);
69
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}\nfixture: ${root}`);
70
+ assert.match(result.stdout, /quiet=false passed/);
71
+ assert.match(result.stdout, /quiet=true passed/);
72
+ } finally {
73
+ rmSync(root, { recursive: true, force: true });
74
+ }
75
+ });
76
+ }