gentle-pi 2.2.0 → 2.3.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 (130) hide show
  1. package/README.md +51 -80
  2. package/assets/agents/review-readability.md +0 -2
  3. package/assets/agents/review-reliability.md +0 -2
  4. package/assets/agents/review-resilience.md +0 -2
  5. package/assets/agents/review-risk.md +0 -2
  6. package/assets/agents/sdd-apply.md +5 -3
  7. package/assets/agents/sdd-proposal.md +2 -0
  8. package/assets/agents/sdd-research.md +54 -0
  9. package/assets/agents/sdd-status.md +4 -4
  10. package/assets/agents/sdd-tasks.md +2 -3
  11. package/assets/agents/sdd-verify.md +21 -1
  12. package/assets/chains/sdd-full.chain.md +1 -1
  13. package/assets/chains/sdd-verify.chain.md +1 -1
  14. package/assets/orchestrator-delegation.md +54 -248
  15. package/assets/orchestrator-memory.md +2 -0
  16. package/assets/orchestrator.md +22 -38
  17. package/assets/sdd-orchestrator-workflow.md +30 -37
  18. package/assets/support/sdd-status-contract.md +7 -7
  19. package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
  20. package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
  21. package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
  22. package/docs/native-authority-architecture.md +9 -11
  23. package/docs/review-integration.md +27 -373
  24. package/extensions/ask-user-choice.ts +151 -0
  25. package/extensions/gentle-ai.ts +1778 -3673
  26. package/extensions/quiet-tools.ts +515 -32
  27. package/extensions/sdd-init.ts +4 -8
  28. package/lib/gentle-ai-renderer.ts +70 -0
  29. package/lib/model-routing-authority.ts +133 -0
  30. package/lib/native-review-cli.ts +372 -940
  31. package/lib/opaque-pi-reviewer-adapter.ts +284 -0
  32. package/lib/review-candidate-view.ts +341 -132
  33. package/lib/review-host-relay.ts +210 -68
  34. package/lib/review-integration-v2.ts +839 -307
  35. package/lib/review-last-event-controller.ts +35 -0
  36. package/lib/sdd-preflight.ts +177 -66
  37. package/lib/sdd-status.ts +66 -111
  38. package/lib/terminal-theme.ts +1 -1
  39. package/package.json +83 -82
  40. package/runtime/gentle-ai-binary.mjs +1 -1
  41. package/runtime/native-review-cli.mjs +322 -890
  42. package/runtime/review-integration-v2.mjs +789 -257
  43. package/runtime/review-relay-contract.mjs +1 -1
  44. package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +4 -5
  45. package/scripts/gentle-ai-installer.mjs +75 -22
  46. package/scripts/maintainer/provider-relay-matrix.mjs +195 -11
  47. package/scripts/test-packed-runner.mjs +4 -7
  48. package/scripts/verify-package-files.mjs +12 -13
  49. package/skills/_shared/review-ledger-contract.md +8 -14
  50. package/skills/chained-pr/SKILL.md +3 -0
  51. package/skills/cognitive-doc-design/SKILL.md +1 -1
  52. package/skills/comment-writer/SKILL.md +1 -1
  53. package/skills/gentle-ai/SKILL.md +7 -74
  54. package/skills/judgment-day/SKILL.md +5 -7
  55. package/skills/rdd-defect-workflow/SKILL.md +3 -3
  56. package/skills/release/SKILL.md +3 -3
  57. package/skills/skill-registry/SKILL.md +1 -1
  58. package/skills/work-unit-commits/SKILL.md +3 -1
  59. package/tests/artifact-language.test.ts +24 -11
  60. package/tests/ask-user-choice.test.ts +264 -0
  61. package/tests/codegraph-tools.test.ts +3 -3
  62. package/tests/crosslane/cross-lane.mjs +15 -1168
  63. package/tests/delegated-key-learnings-contract.test.ts +8 -6
  64. package/tests/devbinary/native-review-parity.devtest.ts +167 -250
  65. package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
  66. package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
  67. package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
  68. package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
  69. package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
  70. package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
  71. package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
  72. package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
  73. package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
  74. package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
  75. package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
  76. package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
  77. package/tests/gentle-ai-binary.test.ts +83 -13
  78. package/tests/gentle-ai-dev-binary.test.ts +51 -228
  79. package/tests/gentle-ai-installer.test.ts +137 -47
  80. package/tests/gentle-ai.test.ts +820 -131
  81. package/tests/gentle-theme.test.ts +133 -0
  82. package/tests/maintainer/provider-relay.maintest.ts +340 -4
  83. package/tests/model-routing-authority.test.ts +257 -0
  84. package/tests/native-review-capability-contract.test.ts +28 -1
  85. package/tests/native-review-cli.test.ts +586 -962
  86. package/tests/native-review-consent.test.ts +150 -66
  87. package/tests/native-review-parity-runtime.test.ts +80 -359
  88. package/tests/native-review-parity.test.ts +674 -925
  89. package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
  90. package/tests/orchestrator-budget.test.ts +132 -88
  91. package/tests/orchestrator-rdd-ownership.test.ts +103 -0
  92. package/tests/package-manifest.test.ts +37 -35
  93. package/tests/provider-defect-handoff.test.ts +15 -118
  94. package/tests/quiet-tool-rendering.test.ts +1055 -28
  95. package/tests/review-authority-recovery-docs.test.ts +1 -2
  96. package/tests/review-candidate-view.test.ts +409 -3
  97. package/tests/review-compact-contract.test.ts +4 -30
  98. package/tests/review-controller-lock-status.test.ts +2 -2
  99. package/tests/review-controller-native-recovery.test.ts +363 -1450
  100. package/tests/review-controller-native-routing.test.ts +1251 -5642
  101. package/tests/review-controller-retired-ops.test.ts +1 -1
  102. package/tests/review-controller-workspace-root.test.ts +222 -71
  103. package/tests/review-controller.test.ts +26 -816
  104. package/tests/review-corrected-finalize-binding.test.ts +112 -153
  105. package/tests/review-dispatch-hydration-gap.test.ts +1 -53
  106. package/tests/review-gate.test.ts +0 -45
  107. package/tests/review-host-relay-restart-parity.test.ts +360 -0
  108. package/tests/review-host-relay-routing.test.ts +117 -82
  109. package/tests/review-host-relay.test.ts +242 -8
  110. package/tests/review-integration-v2-forward.test.ts +576 -553
  111. package/tests/review-integration-v2.test.ts +239 -165
  112. package/tests/review-last-event-closure.test.ts +408 -0
  113. package/tests/review-ledger-contract.test.ts +97 -35
  114. package/tests/review-recovered-lineage-routing.test.ts +0 -47
  115. package/tests/review-relay-transport-agent.test.ts +119 -46
  116. package/tests/review-snapshot.test.ts +3 -2
  117. package/tests/runtime-harness.mjs +361 -196
  118. package/tests/sdd-agent-tools.test.ts +36 -0
  119. package/tests/sdd-preflight.test.ts +81 -15
  120. package/tests/sdd-status.test.ts +109 -110
  121. package/tests/skill-collision-prefixes.test.ts +5 -8
  122. package/tests/writer-edit-surface-scope.test.ts +230 -0
  123. package/themes/Gentleman-Cute.json +94 -0
  124. package/themes/Gentleman-Sexy.json +92 -0
  125. package/lib/git-commit-transaction.ts +0 -861
  126. package/runtime/git-commit-transaction.mjs +0 -862
  127. package/scripts/run-git-commit-transaction.mjs +0 -35
  128. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
  129. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
  130. package/tests/git-commit-transaction.test.ts +0 -530
@@ -0,0 +1,151 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { DynamicBorder } from "@earendil-works/pi-coding-agent";
3
+ import { Container, type SelectItem, SelectList, Text } from "@earendil-works/pi-tui";
4
+ import { type Static, Type } from "typebox";
5
+
6
+ const CHOICE_TOOL_NAME = "ask_user_choice";
7
+ const ASK_USER_CHOICE_BLOCKED_EVENT = "gentle-pi:ask-user-choice:blocked";
8
+
9
+ const ChoiceOptionSchema = Type.Object(
10
+ {
11
+ label: Type.String({ description: "User-facing option label" }),
12
+ description: Type.String({ description: "User-facing option description" }),
13
+ value: Type.String({ description: "Opaque envelope-owned answer token returned only after selection" }),
14
+ },
15
+ { additionalProperties: false },
16
+ );
17
+
18
+ const ChoiceParamsSchema = Type.Object(
19
+ {
20
+ question: Type.String({ description: "The one question to display" }),
21
+ options: Type.Array(ChoiceOptionSchema, {
22
+ minItems: 2,
23
+ maxItems: 4,
24
+ description: "Two to four ordered closed options",
25
+ }),
26
+ },
27
+ { additionalProperties: false },
28
+ );
29
+
30
+ type ChoiceOption = Static<typeof ChoiceOptionSchema>;
31
+ type ChoiceParams = Static<typeof ChoiceParamsSchema>;
32
+
33
+ interface ChoiceSelection {
34
+ value: string;
35
+ label: string;
36
+ index: number;
37
+ }
38
+
39
+ interface ChoiceDetails {
40
+ question: string;
41
+ options: ChoiceOption[];
42
+ selection?: ChoiceSelection;
43
+ cancelled?: true;
44
+ }
45
+
46
+ function reconcileToolAvailability(pi: ExtensionAPI, interactiveTui: boolean): void {
47
+ const active = pi.getActiveTools();
48
+ const isActive = active.includes(CHOICE_TOOL_NAME);
49
+ if (interactiveTui === isActive) return;
50
+ const next = interactiveTui
51
+ ? [...new Set([...active, CHOICE_TOOL_NAME])]
52
+ : active.filter((name) => name !== CHOICE_TOOL_NAME);
53
+ pi.setActiveTools(next);
54
+ }
55
+
56
+ function resultDetails(params: ChoiceParams): ChoiceDetails {
57
+ return { question: params.question, options: params.options };
58
+ }
59
+
60
+ export default function askUserChoice(pi: ExtensionAPI): void {
61
+ pi.registerTool({
62
+ name: CHOICE_TOOL_NAME,
63
+ label: "Ask User Choice",
64
+ description: "Ask one strictly closed single-select question with two to four ordered options. It never accepts free-text or multiple selections.",
65
+ promptGuidelines: [
66
+ "Use ask_user_choice only for one exactly representable closed single-select question with 2-4 ordered options; never use it for free-text or multi-select input.",
67
+ ],
68
+ parameters: ChoiceParamsSchema,
69
+ executionMode: "sequential",
70
+ async execute(_toolCallId, params: ChoiceParams, _signal, _onUpdate, ctx) {
71
+ if (ctx.mode !== "tui") {
72
+ throw new Error("ask_user_choice is unavailable outside the interactive TUI");
73
+ }
74
+
75
+ const items: SelectItem[] = params.options.map((option) => ({
76
+ value: option.value,
77
+ label: option.label,
78
+ description: option.description,
79
+ }));
80
+ let selection: ChoiceSelection | undefined;
81
+ try {
82
+ pi.events.emit(ASK_USER_CHOICE_BLOCKED_EVENT, { active: true });
83
+ selection = await ctx.ui.custom<ChoiceSelection | undefined>((tui, theme, _keybindings, done) => {
84
+ const container = new Container();
85
+ container.addChild(new DynamicBorder((text: string) => theme.fg("accent", text)));
86
+ container.addChild(new Text(theme.fg("accent", theme.bold(params.question)), 1, 0));
87
+ const list = new SelectList(items, items.length, {
88
+ selectedPrefix: (text) => theme.fg("accent", text),
89
+ selectedText: (text) => theme.fg("accent", text),
90
+ description: (text) => theme.fg("muted", text),
91
+ scrollInfo: (text) => theme.fg("dim", text),
92
+ noMatch: (text) => theme.fg("warning", text),
93
+ });
94
+ list.onSelect = (item) => {
95
+ const index = items.indexOf(item);
96
+ done({ value: item.value, label: item.label, index: index + 1 });
97
+ };
98
+ list.onCancel = () => done(undefined);
99
+ container.addChild(list);
100
+ container.addChild(new Text(theme.fg("dim", "↑↓ navigate • Enter select • Esc cancel"), 1, 0));
101
+ container.addChild(new DynamicBorder((text: string) => theme.fg("accent", text)));
102
+ return {
103
+ render: (width) => container.render(width),
104
+ invalidate: () => container.invalidate(),
105
+ handleInput: (data) => {
106
+ list.handleInput(data);
107
+ tui.requestRender();
108
+ },
109
+ };
110
+ });
111
+ } finally {
112
+ pi.events.emit(ASK_USER_CHOICE_BLOCKED_EVENT, { active: false });
113
+ }
114
+
115
+ if (selection === undefined) {
116
+ return {
117
+ content: [{ type: "text", text: "User cancelled the choice" }],
118
+ details: { ...resultDetails(params), cancelled: true },
119
+ };
120
+ }
121
+ return {
122
+ content: [{ type: "text", text: `User selected: ${selection.index}. ${selection.label} (value: ${selection.value})` }],
123
+ details: { ...resultDetails(params), selection },
124
+ };
125
+ },
126
+ renderCall(args, theme) {
127
+ const options = Array.isArray(args.options) ? args.options : [];
128
+ const labels = options
129
+ .map((option) => (typeof option === "object" && option !== null && "label" in option && typeof option.label === "string" ? option.label : ""))
130
+ .filter((label) => label.length > 0);
131
+ return new Text(
132
+ theme.fg("toolTitle", theme.bold("ask_user_choice ")) +
133
+ theme.fg("muted", typeof args.question === "string" ? args.question : "") +
134
+ (labels.length > 0 ? theme.fg("dim", ` (${labels.join(", ")})`) : ""),
135
+ 0,
136
+ 0,
137
+ );
138
+ },
139
+ renderResult(result, _options, theme) {
140
+ const details = result.details as ChoiceDetails | undefined;
141
+ if (details?.selection) {
142
+ return new Text(theme.fg("success", `✓ ${details.selection.index}. ${details.selection.label}`), 0, 0);
143
+ }
144
+ return new Text(theme.fg("warning", "Cancelled"), 0, 0);
145
+ },
146
+ });
147
+
148
+ pi.on("before_agent_start", (_event, ctx) => {
149
+ reconcileToolAvailability(pi, ctx.mode === "tui");
150
+ });
151
+ }