@howaboua/pi-ask 0.0.4 → 0.0.6
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/CHANGELOG.md +45 -0
- package/README.md +3 -1
- package/ask/contracts.ts +2 -4
- package/ask/normalize.ts +2 -2
- package/ask/tool.ts +11 -3
- package/ask/tui-input.ts +145 -0
- package/ask/tui-render.ts +273 -0
- package/ask/tui.ts +15 -417
- package/biome.json +1 -1
- package/changelog.ts +337 -0
- package/index.ts +86 -6
- package/package.json +18 -9
package/changelog.ts
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// Generated extension packages copy this file during changelog:extensions.
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import {
|
|
9
|
+
DynamicBorder,
|
|
10
|
+
type ExtensionAPI,
|
|
11
|
+
getAgentDir,
|
|
12
|
+
getMarkdownTheme,
|
|
13
|
+
} from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
15
|
+
|
|
16
|
+
const ENTRY_TYPE = "@howaboua/pi-stuff/changelog";
|
|
17
|
+
const REGISTRATION_CHANNEL = "@howaboua/pi-stuff/changelog/register/v1";
|
|
18
|
+
const STATE_FILENAME = "howaboua-pi-stuff-changelog.json";
|
|
19
|
+
const SUPPRESS_KEY = "suppress";
|
|
20
|
+
const LOCK_STALE_MS = 30_000;
|
|
21
|
+
const LOCK_TIMEOUT_MS = 2_000;
|
|
22
|
+
|
|
23
|
+
interface PackageMetadata {
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface PackageRegistration extends PackageMetadata {
|
|
29
|
+
changelogPath: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface RegistrationEvent {
|
|
33
|
+
accept: () => void;
|
|
34
|
+
registration: PackageRegistration;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ChangelogEntry {
|
|
38
|
+
content: string;
|
|
39
|
+
version: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ChangelogEntryData {
|
|
43
|
+
markdown: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type ChangelogState = Record<string, boolean | string>;
|
|
47
|
+
|
|
48
|
+
function packageRegistration(): PackageRegistration {
|
|
49
|
+
const packageDirectory = fileURLToPath(new URL(".", import.meta.url));
|
|
50
|
+
const metadata = JSON.parse(
|
|
51
|
+
readFileSync(join(packageDirectory, "package.json"), "utf8"),
|
|
52
|
+
) as Partial<PackageMetadata>;
|
|
53
|
+
if (typeof metadata.name !== "string" || typeof metadata.version !== "string")
|
|
54
|
+
throw new Error(`Invalid package metadata in ${packageDirectory}`);
|
|
55
|
+
return {
|
|
56
|
+
name: metadata.name,
|
|
57
|
+
version: metadata.version,
|
|
58
|
+
changelogPath: join(packageDirectory, "CHANGELOG.md"),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isRegistrationEvent(value: unknown): value is RegistrationEvent {
|
|
63
|
+
if (!value || typeof value !== "object") return false;
|
|
64
|
+
const event = value as Partial<RegistrationEvent>;
|
|
65
|
+
return (
|
|
66
|
+
typeof event.accept === "function" &&
|
|
67
|
+
typeof event.registration?.name === "string" &&
|
|
68
|
+
typeof event.registration.version === "string" &&
|
|
69
|
+
typeof event.registration.changelogPath === "string"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseChangelog(markdown: string): ChangelogEntry[] {
|
|
74
|
+
const entries: ChangelogEntry[] = [];
|
|
75
|
+
let currentLines: string[] = [];
|
|
76
|
+
let currentVersion: string | undefined;
|
|
77
|
+
|
|
78
|
+
for (const line of markdown.split("\n")) {
|
|
79
|
+
const version = line.match(/^##\s+\[?(\d+\.\d+\.\d+)\]?(?:\s.*)?$/)?.[1];
|
|
80
|
+
if (version) {
|
|
81
|
+
if (currentVersion)
|
|
82
|
+
entries.push({
|
|
83
|
+
content: currentLines.join("\n").trim(),
|
|
84
|
+
version: currentVersion,
|
|
85
|
+
});
|
|
86
|
+
currentVersion = version;
|
|
87
|
+
currentLines = [line];
|
|
88
|
+
} else if (currentVersion) {
|
|
89
|
+
currentLines.push(line);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (currentVersion)
|
|
94
|
+
entries.push({
|
|
95
|
+
content: currentLines.join("\n").trim(),
|
|
96
|
+
version: currentVersion,
|
|
97
|
+
});
|
|
98
|
+
return entries;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function compareVersions(left: string, right: string): number {
|
|
102
|
+
const leftParts = left.split(".").map(Number);
|
|
103
|
+
const rightParts = right.split(".").map(Number);
|
|
104
|
+
for (let index = 0; index < 3; index++) {
|
|
105
|
+
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
106
|
+
if (difference !== 0) return difference;
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isStableVersion(version: string): boolean {
|
|
112
|
+
return /^\d+\.\d+\.\d+$/.test(version);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function statePath(): string {
|
|
116
|
+
return join(getAgentDir(), STATE_FILENAME);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function readState(path: string): Promise<ChangelogState> {
|
|
120
|
+
let text: string;
|
|
121
|
+
try {
|
|
122
|
+
text = await readFile(path, "utf8");
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
const parsed = JSON.parse(text) as unknown;
|
|
128
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
129
|
+
throw new Error(`${path} must contain a JSON object`);
|
|
130
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
131
|
+
if (name === SUPPRESS_KEY) {
|
|
132
|
+
if (typeof value !== "boolean")
|
|
133
|
+
throw new Error(`${path} ${SUPPRESS_KEY} must be a boolean`);
|
|
134
|
+
} else if (typeof value !== "string") {
|
|
135
|
+
throw new Error(`${path} version for ${name} must be a string`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return parsed as ChangelogState;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function acquireLock(path: string): Promise<() => Promise<void>> {
|
|
142
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
143
|
+
while (true) {
|
|
144
|
+
try {
|
|
145
|
+
await mkdir(path);
|
|
146
|
+
return () => rm(path, { force: true, recursive: true });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
149
|
+
const info = await stat(path).catch(() => undefined);
|
|
150
|
+
if (info && Date.now() - info.mtimeMs > LOCK_STALE_MS) {
|
|
151
|
+
await rm(path, { force: true, recursive: true });
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (Date.now() >= deadline)
|
|
155
|
+
throw new Error(`Timed out waiting for ${path}`);
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function writeState(path: string, state: ChangelogState): Promise<void> {
|
|
162
|
+
await mkdir(dirname(path), { mode: 0o700, recursive: true });
|
|
163
|
+
const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
164
|
+
try {
|
|
165
|
+
const ordered = Object.fromEntries(
|
|
166
|
+
Object.entries(state).sort(([left], [right]) =>
|
|
167
|
+
left.localeCompare(right),
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
await writeFile(temporaryPath, `${JSON.stringify(ordered, null, 2)}\n`, {
|
|
171
|
+
encoding: "utf8",
|
|
172
|
+
mode: 0o600,
|
|
173
|
+
});
|
|
174
|
+
await rename(temporaryPath, path);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
await rm(temporaryPath, { force: true });
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function packageMarkdown(
|
|
182
|
+
registration: PackageRegistration,
|
|
183
|
+
entries: ChangelogEntry[],
|
|
184
|
+
): string {
|
|
185
|
+
const body = (entry: ChangelogEntry) =>
|
|
186
|
+
entry.content
|
|
187
|
+
.replace(/^##[^\n]*\n*/, "")
|
|
188
|
+
.replace(/^###\s+(?:Changes|Minor Changes|Patch Changes)\s*\n*/gim, "")
|
|
189
|
+
.replace(/^###\s+(.+)$/gm, "**$1**");
|
|
190
|
+
if (entries.length === 1) {
|
|
191
|
+
const [entry] = entries;
|
|
192
|
+
if (!entry) return "";
|
|
193
|
+
return `## ${registration.name} ${entry.version}\n\n${body(entry)}`;
|
|
194
|
+
}
|
|
195
|
+
return [
|
|
196
|
+
`## ${registration.name}`,
|
|
197
|
+
...entries.map((entry) => `**${entry.version}**\n\n${body(entry)}`),
|
|
198
|
+
].join("\n\n");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function claimUpdates(
|
|
202
|
+
registrations: Iterable<PackageRegistration>,
|
|
203
|
+
): Promise<{ errors: string[]; markdown?: string }> {
|
|
204
|
+
const stableRegistrations = [...registrations]
|
|
205
|
+
.filter((registration) => isStableVersion(registration.version))
|
|
206
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
207
|
+
if (stableRegistrations.length === 0) return { errors: [] };
|
|
208
|
+
const path = statePath();
|
|
209
|
+
await mkdir(dirname(path), { mode: 0o700, recursive: true });
|
|
210
|
+
const release = await acquireLock(`${path}.lock`);
|
|
211
|
+
try {
|
|
212
|
+
const state = await readState(path);
|
|
213
|
+
const errors: string[] = [];
|
|
214
|
+
const sections: string[] = [];
|
|
215
|
+
let changed = false;
|
|
216
|
+
if (state[SUPPRESS_KEY] === true) {
|
|
217
|
+
for (const registration of stableRegistrations) {
|
|
218
|
+
const seenVersion = state[registration.name];
|
|
219
|
+
if (
|
|
220
|
+
typeof seenVersion !== "string" ||
|
|
221
|
+
compareVersions(registration.version, seenVersion) > 0
|
|
222
|
+
) {
|
|
223
|
+
state[registration.name] = registration.version;
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (changed) await writeState(path, state);
|
|
228
|
+
return { errors };
|
|
229
|
+
}
|
|
230
|
+
for (const registration of stableRegistrations) {
|
|
231
|
+
const seenVersion = state[registration.name];
|
|
232
|
+
if (
|
|
233
|
+
typeof seenVersion === "string" &&
|
|
234
|
+
compareVersions(registration.version, seenVersion) <= 0
|
|
235
|
+
)
|
|
236
|
+
continue;
|
|
237
|
+
try {
|
|
238
|
+
const entries = parseChangelog(
|
|
239
|
+
await readFile(registration.changelogPath, "utf8"),
|
|
240
|
+
).filter(
|
|
241
|
+
(entry) =>
|
|
242
|
+
compareVersions(entry.version, registration.version) <= 0 &&
|
|
243
|
+
(typeof seenVersion === "string"
|
|
244
|
+
? compareVersions(entry.version, seenVersion) > 0
|
|
245
|
+
: entry.version === registration.version),
|
|
246
|
+
);
|
|
247
|
+
if (entries.length === 0) {
|
|
248
|
+
errors.push(
|
|
249
|
+
`No ${registration.version} changelog entry found for ${registration.name}`,
|
|
250
|
+
);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
sections.push(packageMarkdown(registration, entries));
|
|
254
|
+
state[registration.name] = registration.version;
|
|
255
|
+
changed = true;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
errors.push(
|
|
258
|
+
`Could not read the ${registration.name} changelog: ${error instanceof Error ? error.message : String(error)}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (changed) await writeState(path, state);
|
|
263
|
+
return {
|
|
264
|
+
errors,
|
|
265
|
+
...(sections.length > 0 ? { markdown: sections.join("\n\n") } : {}),
|
|
266
|
+
};
|
|
267
|
+
} finally {
|
|
268
|
+
await release();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function registerCoordinator(
|
|
273
|
+
pi: ExtensionAPI,
|
|
274
|
+
registration: PackageRegistration,
|
|
275
|
+
): void {
|
|
276
|
+
let accepted = false;
|
|
277
|
+
pi.events.emit(REGISTRATION_CHANNEL, {
|
|
278
|
+
registration,
|
|
279
|
+
accept: () => {
|
|
280
|
+
accepted = true;
|
|
281
|
+
},
|
|
282
|
+
} satisfies RegistrationEvent);
|
|
283
|
+
if (accepted) return;
|
|
284
|
+
|
|
285
|
+
const registrations = new Map([[registration.name, registration]]);
|
|
286
|
+
pi.events.on(REGISTRATION_CHANNEL, (value) => {
|
|
287
|
+
if (!isRegistrationEvent(value)) return;
|
|
288
|
+
registrations.set(value.registration.name, value.registration);
|
|
289
|
+
value.accept();
|
|
290
|
+
});
|
|
291
|
+
pi.registerEntryRenderer<ChangelogEntryData>(
|
|
292
|
+
ENTRY_TYPE,
|
|
293
|
+
(entry, _options, theme) => {
|
|
294
|
+
if (!entry.data) return undefined;
|
|
295
|
+
const container = new Container();
|
|
296
|
+
container.addChild(new DynamicBorder());
|
|
297
|
+
container.addChild(
|
|
298
|
+
new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0),
|
|
299
|
+
);
|
|
300
|
+
container.addChild(new Spacer(1));
|
|
301
|
+
container.addChild(
|
|
302
|
+
new Markdown(entry.data.markdown.trim(), 1, 0, getMarkdownTheme()),
|
|
303
|
+
);
|
|
304
|
+
container.addChild(new Spacer(1));
|
|
305
|
+
container.addChild(new DynamicBorder());
|
|
306
|
+
return container;
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
pi.on("session_start", async (event, ctx) => {
|
|
310
|
+
if (event.reason === "reload" || ctx.mode !== "tui") return;
|
|
311
|
+
if (
|
|
312
|
+
ctx.sessionManager
|
|
313
|
+
.getEntries()
|
|
314
|
+
.some((entry) =>
|
|
315
|
+
["branch_summary", "compaction", "message"].includes(entry.type),
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
return;
|
|
319
|
+
try {
|
|
320
|
+
const update = await claimUpdates(registrations.values());
|
|
321
|
+
if (update.markdown)
|
|
322
|
+
pi.appendEntry<ChangelogEntryData>(ENTRY_TYPE, {
|
|
323
|
+
markdown: update.markdown,
|
|
324
|
+
});
|
|
325
|
+
for (const error of update.errors) ctx.ui.notify(error, "warning");
|
|
326
|
+
} catch (error) {
|
|
327
|
+
ctx.ui.notify(
|
|
328
|
+
`Could not update Howaboua changelog state: ${error instanceof Error ? error.message : String(error)}`,
|
|
329
|
+
"warning",
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export default function howabouaPackageChangelog(pi: ExtensionAPI): void {
|
|
336
|
+
registerCoordinator(pi, packageRegistration());
|
|
337
|
+
}
|
package/index.ts
CHANGED
|
@@ -3,17 +3,33 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { loadAskConfig } from "./ask/config.js";
|
|
5
5
|
import { createAskTool } from "./ask/tool.js";
|
|
6
|
+
import registerPackageChangelog from "./changelog.js";
|
|
6
7
|
|
|
7
8
|
export { createAskTool } from "./ask/tool.js";
|
|
8
9
|
|
|
9
10
|
const PROMPTS_DIR = join(dirname(fileURLToPath(import.meta.url)), "prompts");
|
|
11
|
+
const REALTIME_VOICE_PROMPT_CHANNEL =
|
|
12
|
+
"@howaboua/pi-codex-conversion/realtime-voice-prompt/v1";
|
|
13
|
+
const CODE_MODE_PACKAGE = "@howaboua/pi-codex-conversion";
|
|
14
|
+
const CODE_MODE_MODULE = `${CODE_MODE_PACKAGE}/code-mode`;
|
|
10
15
|
|
|
11
|
-
export default function humanInTheLoop(pi: ExtensionAPI): void {
|
|
12
|
-
pi
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
export default async function humanInTheLoop(pi: ExtensionAPI): Promise<void> {
|
|
17
|
+
registerPackageChangelog(pi);
|
|
18
|
+
const ask = createAskTool({
|
|
19
|
+
onBlockedChange: (state) => {
|
|
20
|
+
pi.events.emit(REALTIME_VOICE_PROMPT_CHANNEL, {
|
|
21
|
+
id: state.id,
|
|
22
|
+
active: state.active,
|
|
23
|
+
prompt: state.prompt,
|
|
24
|
+
});
|
|
25
|
+
pi.events.emit("herdr:blocked", {
|
|
26
|
+
active: state.active,
|
|
27
|
+
label: state.label,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
pi.registerTool(ask);
|
|
32
|
+
await registerAskInCodeMode(pi, ask);
|
|
17
33
|
pi.on("resources_discover", () => {
|
|
18
34
|
const config = loadAskConfig();
|
|
19
35
|
return {
|
|
@@ -24,3 +40,67 @@ export default function humanInTheLoop(pi: ExtensionAPI): void {
|
|
|
24
40
|
};
|
|
25
41
|
});
|
|
26
42
|
}
|
|
43
|
+
|
|
44
|
+
async function registerAskInCodeMode(
|
|
45
|
+
pi: ExtensionAPI,
|
|
46
|
+
ask: ReturnType<typeof createAskTool>,
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
try {
|
|
49
|
+
const { adaptToolForCodeMode, registerCodeModeExtensionTools } =
|
|
50
|
+
await import("@howaboua/pi-codex-conversion/code-mode");
|
|
51
|
+
const registration = registerCodeModeExtensionTools(pi, () => [
|
|
52
|
+
adaptToolForCodeMode(ask, {
|
|
53
|
+
blocking: true,
|
|
54
|
+
usage:
|
|
55
|
+
"await tools.ask({ prompts: [{ title, body?, multiple?, choices?: [{ label, description? }] }], handoff? })",
|
|
56
|
+
}),
|
|
57
|
+
]);
|
|
58
|
+
pi.on("session_shutdown", () => registration.unregister());
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (isMissingCodeModeExtension(error)) return;
|
|
61
|
+
if (isOutdatedCodeModeExtension(error)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"Update " +
|
|
64
|
+
CODE_MODE_PACKAGE +
|
|
65
|
+
" to 3.0.24 or newer to use Pi Ask with it",
|
|
66
|
+
{ cause: error },
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isMissingCodeModeExtension(error: unknown): boolean {
|
|
74
|
+
if (
|
|
75
|
+
!error ||
|
|
76
|
+
typeof error !== "object" ||
|
|
77
|
+
!("code" in error) ||
|
|
78
|
+
(error.code !== "ERR_MODULE_NOT_FOUND" &&
|
|
79
|
+
error.code !== "MODULE_NOT_FOUND") ||
|
|
80
|
+
!("message" in error) ||
|
|
81
|
+
typeof error.message !== "string"
|
|
82
|
+
)
|
|
83
|
+
return false;
|
|
84
|
+
const missing = error.message.match(
|
|
85
|
+
/Cannot find (?:package|module) ['"]([^'"]+)['"]/,
|
|
86
|
+
)?.[1];
|
|
87
|
+
return missing === CODE_MODE_PACKAGE || missing === CODE_MODE_MODULE;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isOutdatedCodeModeExtension(error: unknown): boolean {
|
|
91
|
+
if (
|
|
92
|
+
!error ||
|
|
93
|
+
typeof error !== "object" ||
|
|
94
|
+
!("code" in error) ||
|
|
95
|
+
!("message" in error) ||
|
|
96
|
+
typeof error.message !== "string"
|
|
97
|
+
)
|
|
98
|
+
return false;
|
|
99
|
+
return (
|
|
100
|
+
(error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED" ||
|
|
101
|
+
error.code === "ERR_UNSUPPORTED_DIR_IMPORT") &&
|
|
102
|
+
(error.message.includes(CODE_MODE_MODULE) ||
|
|
103
|
+
(error.message.includes("Package subpath './code-mode'") &&
|
|
104
|
+
error.message.includes(CODE_MODE_PACKAGE)))
|
|
105
|
+
);
|
|
106
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@howaboua/pi-ask",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Interactive decisions, review triage, and human handoffs for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"README.md",
|
|
36
36
|
"LICENSE",
|
|
37
37
|
"tsconfig.json",
|
|
38
|
-
"biome.json"
|
|
38
|
+
"biome.json",
|
|
39
|
+
"changelog.ts",
|
|
40
|
+
"CHANGELOG.md"
|
|
39
41
|
],
|
|
40
42
|
"pi": {
|
|
41
43
|
"extensions": [
|
|
@@ -43,16 +45,23 @@
|
|
|
43
45
|
]
|
|
44
46
|
},
|
|
45
47
|
"peerDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"@earendil-works/pi-
|
|
48
|
+
"@howaboua/pi-codex-conversion": ">=3.0.24",
|
|
49
|
+
"@earendil-works/pi-coding-agent": ">=0.84.3",
|
|
50
|
+
"@earendil-works/pi-tui": ">=0.84.3",
|
|
48
51
|
"typebox": "*"
|
|
49
52
|
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"@howaboua/pi-codex-conversion": {
|
|
55
|
+
"optional": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
50
58
|
"devDependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@earendil-works/pi-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
59
|
+
"@howaboua/pi-codex-conversion": "3.0.24",
|
|
60
|
+
"@biomejs/biome": "^2.5.10",
|
|
61
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
62
|
+
"@earendil-works/pi-tui": "0.84.4",
|
|
63
|
+
"typebox": "1.3.18",
|
|
64
|
+
"typescript": "^7.0.2"
|
|
56
65
|
},
|
|
57
66
|
"scripts": {
|
|
58
67
|
"check": "bun run lint && bun run typecheck && bun run test",
|