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,264 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import askUserChoice from "../extensions/ask-user-choice.ts";
|
|
4
|
+
|
|
5
|
+
interface ChoiceResult {
|
|
6
|
+
content: Array<{ type: string; text: string }>;
|
|
7
|
+
details: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ChoiceOptionSchema {
|
|
11
|
+
additionalProperties?: boolean;
|
|
12
|
+
properties?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ChoiceParameters {
|
|
16
|
+
additionalProperties?: boolean;
|
|
17
|
+
properties?: {
|
|
18
|
+
question?: unknown;
|
|
19
|
+
options?: {
|
|
20
|
+
minItems?: number;
|
|
21
|
+
maxItems?: number;
|
|
22
|
+
items?: ChoiceOptionSchema;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ChoiceTool {
|
|
28
|
+
name: string;
|
|
29
|
+
parameters: ChoiceParameters;
|
|
30
|
+
execute: (...args: unknown[]) => Promise<ChoiceResult>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ChoiceLifecycleEvent {
|
|
34
|
+
channel: string;
|
|
35
|
+
data: { active: boolean };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type BeforeAgentStart = (event: unknown, ctx: { mode: string }) => void | Promise<void>;
|
|
39
|
+
|
|
40
|
+
function registerChoiceTool(
|
|
41
|
+
initialTools: string[] = [],
|
|
42
|
+
onLifecycleEvent?: (event: ChoiceLifecycleEvent) => void,
|
|
43
|
+
) {
|
|
44
|
+
let activeTools = initialTools;
|
|
45
|
+
let runtimeActionsAllowed = false;
|
|
46
|
+
let getActiveToolsCalls = 0;
|
|
47
|
+
let setActiveToolsCalls = 0;
|
|
48
|
+
let tool: ChoiceTool | undefined;
|
|
49
|
+
const hooks: BeforeAgentStart[] = [];
|
|
50
|
+
const registeredToolNames: string[] = [];
|
|
51
|
+
const emittedEvents: ChoiceLifecycleEvent[] = [];
|
|
52
|
+
const pi = {
|
|
53
|
+
getActiveTools: () => {
|
|
54
|
+
getActiveToolsCalls++;
|
|
55
|
+
if (!runtimeActionsAllowed) {
|
|
56
|
+
throw new Error("runtime actions are unavailable while the extension is loading");
|
|
57
|
+
}
|
|
58
|
+
return activeTools;
|
|
59
|
+
},
|
|
60
|
+
setActiveTools: (names: string[]) => {
|
|
61
|
+
setActiveToolsCalls++;
|
|
62
|
+
if (!runtimeActionsAllowed) {
|
|
63
|
+
throw new Error("runtime actions are unavailable while the extension is loading");
|
|
64
|
+
}
|
|
65
|
+
activeTools = names;
|
|
66
|
+
},
|
|
67
|
+
registerTool: (candidate: unknown) => {
|
|
68
|
+
tool = candidate as ChoiceTool;
|
|
69
|
+
registeredToolNames.push(tool.name);
|
|
70
|
+
},
|
|
71
|
+
on: (event: string, handler: BeforeAgentStart) => {
|
|
72
|
+
if (event === "before_agent_start") hooks.push(handler);
|
|
73
|
+
},
|
|
74
|
+
events: {
|
|
75
|
+
emit(channel: string, data: { active: boolean }) {
|
|
76
|
+
const event = { channel, data };
|
|
77
|
+
emittedEvents.push(event);
|
|
78
|
+
onLifecycleEvent?.(event);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
askUserChoice(pi as never);
|
|
83
|
+
assert.ok(tool, "ask_user_choice must register");
|
|
84
|
+
return {
|
|
85
|
+
hooks,
|
|
86
|
+
tool,
|
|
87
|
+
registeredToolNames,
|
|
88
|
+
activeTools: () => [...activeTools],
|
|
89
|
+
emittedEvents: () => [...emittedEvents],
|
|
90
|
+
runtimeActionCalls: () => ({ getActiveTools: getActiveToolsCalls, setActiveTools: setActiveToolsCalls }),
|
|
91
|
+
allowRuntimeActions: () => {
|
|
92
|
+
runtimeActionsAllowed = true;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const options = [
|
|
98
|
+
{ label: "Authorize observed hash", description: "Accept the baseline hash observed in this runtime.", value: "authorize_observed_hash" },
|
|
99
|
+
{ label: "Preserve requested hash", description: "Keep the hash from the original request.", value: "preserve_requested_hash" },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
function tuiContext(inputs: readonly string[], rendered: { value: string }) {
|
|
103
|
+
return {
|
|
104
|
+
mode: "tui",
|
|
105
|
+
ui: {
|
|
106
|
+
custom: async (factory: (tui: { requestRender(): void }, theme: { fg(_color: string, text: string): string; bold(text: string): string }, keybindings: unknown, done: (value: unknown) => void) => { render(width: number): string[]; handleInput(data: string): void }) => {
|
|
107
|
+
let result: unknown;
|
|
108
|
+
const component = factory(
|
|
109
|
+
{ requestRender() {} },
|
|
110
|
+
{ fg: (_color, text) => text, bold: (text) => text },
|
|
111
|
+
{},
|
|
112
|
+
(value) => {
|
|
113
|
+
result = value;
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
rendered.value = component.render(100).join("\n");
|
|
117
|
+
for (const input of inputs) component.handleInput(input);
|
|
118
|
+
return result;
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
test("ask_user_choice registers without runtime actions or overriding the open questionnaire", () => {
|
|
125
|
+
const registration = registerChoiceTool(["read", "ask_user_question"]);
|
|
126
|
+
|
|
127
|
+
assert.deepEqual(registration.registeredToolNames, ["ask_user_choice"]);
|
|
128
|
+
assert.deepEqual(registration.runtimeActionCalls(), { getActiveTools: 0, setActiveTools: 0 });
|
|
129
|
+
assert.deepEqual(registration.activeTools(), ["read", "ask_user_question"]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("ask_user_choice exposes a strict closed single-select schema", () => {
|
|
133
|
+
const { tool } = registerChoiceTool();
|
|
134
|
+
const optionsSchema = tool.parameters.properties?.options;
|
|
135
|
+
const optionSchema = optionsSchema?.items;
|
|
136
|
+
|
|
137
|
+
assert.equal(tool.name, "ask_user_choice");
|
|
138
|
+
assert.equal(tool.parameters.additionalProperties, false);
|
|
139
|
+
assert.deepEqual(Object.keys(tool.parameters.properties ?? {}).sort(), ["options", "question"]);
|
|
140
|
+
assert.equal(optionsSchema?.minItems, 2);
|
|
141
|
+
assert.equal(optionsSchema?.maxItems, 4);
|
|
142
|
+
assert.equal(optionSchema?.additionalProperties, false);
|
|
143
|
+
assert.deepEqual(Object.keys(optionSchema?.properties ?? {}).sort(), ["description", "label", "value"]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("ask_user_choice handles a closed Kilo hash decision with an opaque envelope value", async () => {
|
|
147
|
+
const { tool } = registerChoiceTool(["read"]);
|
|
148
|
+
const rendered = { value: "" };
|
|
149
|
+
const result = await tool.execute("call", { question: "Proceed?", options }, new AbortController().signal, undefined, tuiContext(["\x1b[B", "\r"], rendered));
|
|
150
|
+
assert.match(rendered.value, /Proceed\?|Authorize observed hash|Accept the baseline hash|Preserve requested hash|Keep the hash/);
|
|
151
|
+
assert.doesNotMatch(rendered.value, /Type something|authorize_observed_hash|preserve_requested_hash/);
|
|
152
|
+
assert.deepEqual(result.details.selection, { value: "preserve_requested_hash", label: "Preserve requested hash", index: 2 });
|
|
153
|
+
assert.equal(result.content[0]?.text, "User selected: 2. Preserve requested hash (value: preserve_requested_hash)");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("ask_user_choice cancels without a value and remains unavailable outside the TUI", async () => {
|
|
157
|
+
const { tool } = registerChoiceTool();
|
|
158
|
+
const rendered = { value: "" };
|
|
159
|
+
const cancelled = await tool.execute("call", { question: "Proceed?", options }, new AbortController().signal, undefined, tuiContext(["\x1b"], rendered));
|
|
160
|
+
assert.equal(cancelled.details.selection, undefined);
|
|
161
|
+
assert.equal(cancelled.details.cancelled, true);
|
|
162
|
+
await assert.rejects(
|
|
163
|
+
() => tool.execute("call", { question: "Proceed?", options }, new AbortController().signal, undefined, { mode: "print" }),
|
|
164
|
+
/unavailable outside the interactive TUI/,
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("ask_user_choice emits a private balanced lifecycle around selection and cancellation", async () => {
|
|
169
|
+
const sequence: string[] = [];
|
|
170
|
+
const selectedRegistration = registerChoiceTool([], ({ data }) => {
|
|
171
|
+
sequence.push(data.active ? "active" : "inactive");
|
|
172
|
+
});
|
|
173
|
+
const selected = await selectedRegistration.tool.execute(
|
|
174
|
+
"call",
|
|
175
|
+
{ question: "private choice question", options },
|
|
176
|
+
new AbortController().signal,
|
|
177
|
+
undefined,
|
|
178
|
+
{
|
|
179
|
+
mode: "tui",
|
|
180
|
+
ui: {
|
|
181
|
+
custom: async () => {
|
|
182
|
+
sequence.push("custom");
|
|
183
|
+
return { value: "preserve_requested_hash", label: "Preserve requested hash", index: 2 };
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
assert.equal(selected.details.selection?.value, "preserve_requested_hash");
|
|
189
|
+
assert.deepEqual(sequence, ["active", "custom", "inactive"]);
|
|
190
|
+
assert.deepEqual(selectedRegistration.emittedEvents(), [
|
|
191
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: true } },
|
|
192
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: false } },
|
|
193
|
+
]);
|
|
194
|
+
assert.doesNotMatch(
|
|
195
|
+
JSON.stringify(selectedRegistration.emittedEvents()),
|
|
196
|
+
/private choice question|Authorize observed hash|Accept the baseline hash|preserve_requested_hash/,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const cancelledRegistration = registerChoiceTool();
|
|
200
|
+
const cancelled = await cancelledRegistration.tool.execute(
|
|
201
|
+
"call",
|
|
202
|
+
{ question: "Proceed?", options },
|
|
203
|
+
new AbortController().signal,
|
|
204
|
+
undefined,
|
|
205
|
+
{ mode: "tui", ui: { custom: async () => undefined } },
|
|
206
|
+
);
|
|
207
|
+
assert.equal(cancelled.details.cancelled, true);
|
|
208
|
+
assert.deepEqual(cancelledRegistration.emittedEvents(), [
|
|
209
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: true } },
|
|
210
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: false } },
|
|
211
|
+
]);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("ask_user_choice settles its lifecycle after a custom UI error and emits nothing outside the TUI", async () => {
|
|
215
|
+
const failedRegistration = registerChoiceTool();
|
|
216
|
+
const customError = new Error("custom UI failed");
|
|
217
|
+
await assert.rejects(
|
|
218
|
+
failedRegistration.tool.execute(
|
|
219
|
+
"call",
|
|
220
|
+
{ question: "Proceed?", options },
|
|
221
|
+
new AbortController().signal,
|
|
222
|
+
undefined,
|
|
223
|
+
{ mode: "tui", ui: { custom: async () => { throw customError; } } },
|
|
224
|
+
),
|
|
225
|
+
(error) => error === customError,
|
|
226
|
+
);
|
|
227
|
+
assert.deepEqual(failedRegistration.emittedEvents(), [
|
|
228
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: true } },
|
|
229
|
+
{ channel: "gentle-pi:ask-user-choice:blocked", data: { active: false } },
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
const nonTuiRegistration = registerChoiceTool();
|
|
233
|
+
let customCalled = false;
|
|
234
|
+
await assert.rejects(
|
|
235
|
+
nonTuiRegistration.tool.execute(
|
|
236
|
+
"call",
|
|
237
|
+
{ question: "Proceed?", options },
|
|
238
|
+
new AbortController().signal,
|
|
239
|
+
undefined,
|
|
240
|
+
{
|
|
241
|
+
mode: "print",
|
|
242
|
+
ui: {
|
|
243
|
+
custom: async () => {
|
|
244
|
+
customCalled = true;
|
|
245
|
+
return undefined;
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
),
|
|
250
|
+
/unavailable outside the interactive TUI/,
|
|
251
|
+
);
|
|
252
|
+
assert.equal(customCalled, false);
|
|
253
|
+
assert.deepEqual(nonTuiRegistration.emittedEvents(), []);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("ask_user_choice is offered only for interactive TUI turns and preserves the open questionnaire", async () => {
|
|
257
|
+
const registration = registerChoiceTool(["read", "ask_user_question"]);
|
|
258
|
+
registration.allowRuntimeActions();
|
|
259
|
+
|
|
260
|
+
for (const hook of registration.hooks) await hook({}, { mode: "tui" });
|
|
261
|
+
assert.deepEqual(registration.activeTools(), ["read", "ask_user_question", "ask_user_choice"]);
|
|
262
|
+
for (const hook of registration.hooks) await hook({}, { mode: "print" });
|
|
263
|
+
assert.deepEqual(registration.activeTools(), ["read", "ask_user_question"]);
|
|
264
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
|
-
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir, tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import test from "node:test";
|
|
@@ -11,14 +11,14 @@ import codeGraphTools, {
|
|
|
11
11
|
} from "../extensions/codegraph-tools.ts";
|
|
12
12
|
|
|
13
13
|
function workspace(t: test.TestContext): string {
|
|
14
|
-
const cwd = mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-"));
|
|
14
|
+
const cwd = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-")));
|
|
15
15
|
execFileSync("git", ["init", "-b", "main"], { cwd, stdio: "ignore" });
|
|
16
16
|
t.after(() => rmSync(cwd, { recursive: true, force: true }));
|
|
17
17
|
return cwd;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
test("CodeGraph tool rejects non-project, nested-project, HOME, and temporary workspaces before init", async (t) => {
|
|
21
|
-
const nonProject = mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-non-project-"));
|
|
21
|
+
const nonProject = realpathSync(mkdtempSync(join(tmpdir(), "gentle-pi-codegraph-non-project-")));
|
|
22
22
|
t.after(() => rmSync(nonProject, { recursive: true, force: true }));
|
|
23
23
|
const root = workspace(t);
|
|
24
24
|
const nested = join(root, "nested");
|