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.
- package/README.md +51 -80
- package/assets/agents/review-readability.md +0 -2
- package/assets/agents/review-reliability.md +0 -2
- package/assets/agents/review-resilience.md +0 -2
- package/assets/agents/review-risk.md +0 -2
- package/assets/agents/sdd-apply.md +5 -3
- package/assets/agents/sdd-proposal.md +2 -0
- package/assets/agents/sdd-research.md +54 -0
- package/assets/agents/sdd-status.md +4 -4
- package/assets/agents/sdd-tasks.md +2 -3
- package/assets/agents/sdd-verify.md +21 -1
- package/assets/chains/sdd-full.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +1 -1
- package/assets/orchestrator-delegation.md +54 -248
- package/assets/orchestrator-memory.md +2 -0
- package/assets/orchestrator.md +22 -38
- package/assets/sdd-orchestrator-workflow.md +30 -37
- package/assets/support/sdd-status-contract.md +7 -7
- package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
- package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
- package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
- package/docs/native-authority-architecture.md +9 -11
- package/docs/review-integration.md +27 -373
- package/extensions/ask-user-choice.ts +151 -0
- package/extensions/gentle-ai.ts +1778 -3673
- package/extensions/quiet-tools.ts +515 -32
- package/extensions/sdd-init.ts +4 -8
- package/lib/gentle-ai-renderer.ts +70 -0
- package/lib/model-routing-authority.ts +133 -0
- package/lib/native-review-cli.ts +372 -940
- package/lib/opaque-pi-reviewer-adapter.ts +284 -0
- package/lib/review-candidate-view.ts +341 -132
- package/lib/review-host-relay.ts +210 -68
- package/lib/review-integration-v2.ts +839 -307
- package/lib/review-last-event-controller.ts +35 -0
- package/lib/sdd-preflight.ts +177 -66
- package/lib/sdd-status.ts +66 -111
- package/lib/terminal-theme.ts +1 -1
- package/package.json +83 -82
- package/runtime/gentle-ai-binary.mjs +1 -1
- package/runtime/native-review-cli.mjs +322 -890
- package/runtime/review-integration-v2.mjs +789 -257
- package/runtime/review-relay-contract.mjs +1 -1
- package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +4 -5
- package/scripts/gentle-ai-installer.mjs +75 -22
- package/scripts/maintainer/provider-relay-matrix.mjs +195 -11
- package/scripts/test-packed-runner.mjs +4 -7
- package/scripts/verify-package-files.mjs +12 -13
- package/skills/_shared/review-ledger-contract.md +8 -14
- package/skills/chained-pr/SKILL.md +3 -0
- package/skills/cognitive-doc-design/SKILL.md +1 -1
- package/skills/comment-writer/SKILL.md +1 -1
- package/skills/gentle-ai/SKILL.md +7 -74
- package/skills/judgment-day/SKILL.md +5 -7
- package/skills/rdd-defect-workflow/SKILL.md +3 -3
- package/skills/release/SKILL.md +3 -3
- package/skills/skill-registry/SKILL.md +1 -1
- package/skills/work-unit-commits/SKILL.md +3 -1
- package/tests/artifact-language.test.ts +24 -11
- package/tests/ask-user-choice.test.ts +264 -0
- package/tests/codegraph-tools.test.ts +3 -3
- package/tests/crosslane/cross-lane.mjs +15 -1168
- package/tests/delegated-key-learnings-contract.test.ts +8 -6
- package/tests/devbinary/native-review-parity.devtest.ts +167 -250
- package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
- package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
- package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
- package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
- package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
- package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
- package/tests/gentle-ai-binary.test.ts +83 -13
- package/tests/gentle-ai-dev-binary.test.ts +51 -228
- package/tests/gentle-ai-installer.test.ts +137 -47
- package/tests/gentle-ai.test.ts +820 -131
- package/tests/gentle-theme.test.ts +133 -0
- package/tests/maintainer/provider-relay.maintest.ts +340 -4
- package/tests/model-routing-authority.test.ts +257 -0
- package/tests/native-review-capability-contract.test.ts +28 -1
- package/tests/native-review-cli.test.ts +586 -962
- package/tests/native-review-consent.test.ts +150 -66
- package/tests/native-review-parity-runtime.test.ts +80 -359
- package/tests/native-review-parity.test.ts +674 -925
- package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
- package/tests/orchestrator-budget.test.ts +132 -88
- package/tests/orchestrator-rdd-ownership.test.ts +103 -0
- package/tests/package-manifest.test.ts +37 -35
- package/tests/provider-defect-handoff.test.ts +15 -118
- package/tests/quiet-tool-rendering.test.ts +1055 -28
- package/tests/review-authority-recovery-docs.test.ts +1 -2
- package/tests/review-candidate-view.test.ts +409 -3
- package/tests/review-compact-contract.test.ts +4 -30
- package/tests/review-controller-lock-status.test.ts +2 -2
- package/tests/review-controller-native-recovery.test.ts +363 -1450
- package/tests/review-controller-native-routing.test.ts +1251 -5642
- package/tests/review-controller-retired-ops.test.ts +1 -1
- package/tests/review-controller-workspace-root.test.ts +222 -71
- package/tests/review-controller.test.ts +26 -816
- package/tests/review-corrected-finalize-binding.test.ts +112 -153
- package/tests/review-dispatch-hydration-gap.test.ts +1 -53
- package/tests/review-gate.test.ts +0 -45
- package/tests/review-host-relay-restart-parity.test.ts +360 -0
- package/tests/review-host-relay-routing.test.ts +117 -82
- package/tests/review-host-relay.test.ts +242 -8
- package/tests/review-integration-v2-forward.test.ts +576 -553
- package/tests/review-integration-v2.test.ts +239 -165
- package/tests/review-last-event-closure.test.ts +408 -0
- package/tests/review-ledger-contract.test.ts +97 -35
- package/tests/review-recovered-lineage-routing.test.ts +0 -47
- package/tests/review-relay-transport-agent.test.ts +119 -46
- package/tests/review-snapshot.test.ts +3 -2
- package/tests/runtime-harness.mjs +361 -196
- package/tests/sdd-agent-tools.test.ts +36 -0
- package/tests/sdd-preflight.test.ts +81 -15
- package/tests/sdd-status.test.ts +109 -110
- package/tests/skill-collision-prefixes.test.ts +5 -8
- package/tests/writer-edit-surface-scope.test.ts +230 -0
- package/themes/Gentleman-Cute.json +94 -0
- package/themes/Gentleman-Sexy.json +92 -0
- package/lib/git-commit-transaction.ts +0 -861
- package/runtime/git-commit-transaction.mjs +0 -862
- package/scripts/run-git-commit-transaction.mjs +0 -35
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
- package/tests/git-commit-transaction.test.ts +0 -530
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { keyHint, type AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
3
|
+
import { sanitizeTerminalText } from "./terminal-theme.ts";
|
|
4
|
+
|
|
5
|
+
type GentleAiLifecycleColor = "warning" | "success" | "error" | "dim";
|
|
6
|
+
|
|
7
|
+
export interface GentleAiRenderTheme {
|
|
8
|
+
bold(value: string): string;
|
|
9
|
+
fg(color: GentleAiLifecycleColor, value: string): string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GentleAiRenderState {
|
|
13
|
+
lifecycleComponent?: boolean; genericLocked?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface GentleAiRenderContext {
|
|
17
|
+
argsComplete?: boolean;
|
|
18
|
+
executionStarted?: boolean;
|
|
19
|
+
isPartial?: boolean;
|
|
20
|
+
isError?: boolean;
|
|
21
|
+
lastComponent?: unknown;
|
|
22
|
+
state?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getGentleAiRenderState(state: unknown): GentleAiRenderState | undefined {
|
|
26
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) return undefined;
|
|
27
|
+
const rowState = state as Record<string, unknown>, existing = rowState.gentleAiRender;
|
|
28
|
+
if (existing && typeof existing === "object" && !Array.isArray(existing)) return existing as GentleAiRenderState;
|
|
29
|
+
return (rowState.gentleAiRender = {} as GentleAiRenderState);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface GentleAiResultRenderOptions {
|
|
33
|
+
expanded?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function renderGentleAiResult(
|
|
37
|
+
result: AgentToolResult<unknown>,
|
|
38
|
+
options: GentleAiResultRenderOptions,
|
|
39
|
+
): Text {
|
|
40
|
+
const textItems = result.content.flatMap((content) => (content.type === "text" ? [sanitizeTerminalText(content.text)] : []));
|
|
41
|
+
const text = textItems.join("\n");
|
|
42
|
+
const hasExpandableText = textItems.some((item) => item.length > 0);
|
|
43
|
+
const output = !hasExpandableText ? "" : options.expanded ? text : keyHint("app.tools.expand", "to expand");
|
|
44
|
+
return new Text(output, 0, 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function renderGentleAiLifecycleCall(
|
|
48
|
+
operationPath: string,
|
|
49
|
+
theme: GentleAiRenderTheme,
|
|
50
|
+
context?: GentleAiRenderContext,
|
|
51
|
+
detail?: string,
|
|
52
|
+
): Text {
|
|
53
|
+
const status = context?.isError
|
|
54
|
+
? "failed"
|
|
55
|
+
: context?.argsComplete === false
|
|
56
|
+
? "preparing"
|
|
57
|
+
: !context?.executionStarted || context.isPartial
|
|
58
|
+
? "running"
|
|
59
|
+
: "completed";
|
|
60
|
+
const color = status === "preparing" || status === "running" ? "warning" : status === "completed" ? "success" : "error";
|
|
61
|
+
const lines = [theme.fg(color, theme.bold(`🌹︎ Gentle AI · ${status} · ${operationPath}`))];
|
|
62
|
+
if (detail) lines.push(theme.fg("dim", sanitizeTerminalText(detail)));
|
|
63
|
+
const state = getGentleAiRenderState(context?.state);
|
|
64
|
+
const component = context?.lastComponent instanceof Text && (!state || state.lifecycleComponent === true)
|
|
65
|
+
? context.lastComponent
|
|
66
|
+
: new Text("", 0, 0);
|
|
67
|
+
if (state) state.lifecycleComponent = true;
|
|
68
|
+
component.setText(lines.join("\n"));
|
|
69
|
+
return component;
|
|
70
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
export const THINKING_LEVELS = [
|
|
5
|
+
"off",
|
|
6
|
+
"minimal",
|
|
7
|
+
"low",
|
|
8
|
+
"medium",
|
|
9
|
+
"high",
|
|
10
|
+
"xhigh",
|
|
11
|
+
"max",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
15
|
+
|
|
16
|
+
export interface AgentRoutingEntry {
|
|
17
|
+
model?: string;
|
|
18
|
+
thinking?: ThinkingLevel;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type AgentModelConfig = Record<string, AgentRoutingEntry>;
|
|
22
|
+
|
|
23
|
+
export type ModelConfigFileResult =
|
|
24
|
+
| { status: "missing" }
|
|
25
|
+
| { status: "invalid"; path: string }
|
|
26
|
+
| { status: "valid"; config: AgentModelConfig };
|
|
27
|
+
|
|
28
|
+
const SAFE_MODEL_ID_PATTERN = /^[A-Za-z0-9._~:@/+%-]+$/;
|
|
29
|
+
const SAFE_AGENT_NAME_PATTERN = /^[A-Za-z0-9._:@/+%-]+$/;
|
|
30
|
+
|
|
31
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
36
|
+
try {
|
|
37
|
+
await access(path);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isThinkingLevel(value: unknown): value is ThinkingLevel {
|
|
45
|
+
return (
|
|
46
|
+
typeof value === "string" &&
|
|
47
|
+
(THINKING_LEVELS as readonly string[]).includes(value)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function normalizeModelId(value: unknown): string | undefined {
|
|
52
|
+
if (typeof value !== "string") return undefined;
|
|
53
|
+
const model = value.trim();
|
|
54
|
+
if (model.length === 0) return undefined;
|
|
55
|
+
if (!SAFE_MODEL_ID_PATTERN.test(model)) return undefined;
|
|
56
|
+
return model;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function normalizeRoutingEntry(value: unknown): AgentRoutingEntry | undefined {
|
|
60
|
+
if (typeof value === "string") {
|
|
61
|
+
const model = normalizeModelId(value);
|
|
62
|
+
return model ? { model } : undefined;
|
|
63
|
+
}
|
|
64
|
+
if (!isRecord(value)) return undefined;
|
|
65
|
+
const model = normalizeModelId(value.model);
|
|
66
|
+
const thinking = isThinkingLevel(value.thinking) ? value.thinking : undefined;
|
|
67
|
+
if (!model && !thinking) {
|
|
68
|
+
return Object.keys(value).length === 0 ? {} : undefined;
|
|
69
|
+
}
|
|
70
|
+
return { model, thinking };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizeModelConfig(value: unknown): AgentModelConfig | undefined {
|
|
74
|
+
if (!isRecord(value)) return undefined;
|
|
75
|
+
const cleaned: AgentModelConfig = {};
|
|
76
|
+
for (const [name, entryValue] of Object.entries(value)) {
|
|
77
|
+
if (!SAFE_AGENT_NAME_PATTERN.test(name)) continue;
|
|
78
|
+
const entry = normalizeRoutingEntry(entryValue);
|
|
79
|
+
if (entry) cleaned[name] = entry;
|
|
80
|
+
}
|
|
81
|
+
return cleaned;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseModelConfigFileValue(value: Record<string, unknown>): AgentModelConfig {
|
|
85
|
+
const config: AgentModelConfig = {};
|
|
86
|
+
for (const [name, entryValue] of Object.entries(value)) {
|
|
87
|
+
const entry = normalizeRoutingEntry(entryValue);
|
|
88
|
+
if (entry) config[name] = entry;
|
|
89
|
+
}
|
|
90
|
+
return config;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function readModelConfigFile(path: string): ModelConfigFileResult {
|
|
94
|
+
if (!existsSync(path)) return { status: "missing" };
|
|
95
|
+
try {
|
|
96
|
+
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
97
|
+
if (!isRecord(parsed)) return { status: "invalid", path };
|
|
98
|
+
return { status: "valid", config: parseModelConfigFileValue(parsed) };
|
|
99
|
+
} catch {
|
|
100
|
+
return { status: "invalid", path };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function readModelConfigFileAsync(
|
|
105
|
+
path: string,
|
|
106
|
+
): Promise<ModelConfigFileResult> {
|
|
107
|
+
if (!(await pathExists(path))) return { status: "missing" };
|
|
108
|
+
try {
|
|
109
|
+
const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
|
|
110
|
+
if (!isRecord(parsed)) return { status: "invalid", path };
|
|
111
|
+
return { status: "valid", config: parseModelConfigFileValue(parsed) };
|
|
112
|
+
} catch {
|
|
113
|
+
return { status: "invalid", path };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function readSavedModelConfig(
|
|
118
|
+
globalPath: string,
|
|
119
|
+
projectPath: string,
|
|
120
|
+
): ModelConfigFileResult {
|
|
121
|
+
const globalResult = readModelConfigFile(globalPath);
|
|
122
|
+
if (globalResult.status !== "missing") return globalResult;
|
|
123
|
+
return readModelConfigFile(projectPath);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function readSavedModelConfigAsync(
|
|
127
|
+
globalPath: string,
|
|
128
|
+
projectPath: string,
|
|
129
|
+
): Promise<ModelConfigFileResult> {
|
|
130
|
+
const globalResult = await readModelConfigFileAsync(globalPath);
|
|
131
|
+
if (globalResult.status !== "missing") return globalResult;
|
|
132
|
+
return readModelConfigFileAsync(projectPath);
|
|
133
|
+
}
|