@pithos-kit/squiggle 0.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.
- package/README.md +93 -0
- package/extensions/squiggle.ts +298 -0
- package/index.ts +1 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# squiggle
|
|
2
|
+
|
|
3
|
+
Quietly polish grammar and spelling in your pi prompts.
|
|
4
|
+
|
|
5
|
+
The extension intercepts user input, shows a `squiggling...` spinner while processing, corrects spelling and grammar using a configured model, shows a colored diff, and submits the corrected prompt automatically without confirmation. Named after the red squiggle from your favorite spell-checker.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pi install npm:@pithos-kit/squiggle
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Pin to a version:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pi install npm:@pithos-kit/squiggle@<version>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For local development from a checkout of [`pithos-kit`](https://github.com/anton-kochev/pithos-kit):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pi install ./pithos.squiggle
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Project-local install:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pi install ./pithos.squiggle -l
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Temporary test run:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pi -e ./pithos.squiggle
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Pithos `.pithos` config
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
pi:
|
|
41
|
+
extensions:
|
|
42
|
+
"@pithos-kit/squiggle": "npm:0.4.0"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
Create `.pi/squiggle.json` in your project:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mode": "on",
|
|
52
|
+
"model": "openai-codex/gpt-5.4-mini",
|
|
53
|
+
"maxInputChars": 500
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
|
|
59
|
+
- `mode`: `"on"` or `"off"`
|
|
60
|
+
- `model`: pi model spec in `provider/model` format
|
|
61
|
+
- `maxInputChars`: maximum input length to send to the correction model
|
|
62
|
+
|
|
63
|
+
Environment variables override the config file:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
SQUIGGLE_MODE=off pi
|
|
67
|
+
SQUIGGLE_MODEL=openai-codex/gpt-5.4-mini pi
|
|
68
|
+
SQUIGGLE_MAX_CHARS=1000 pi
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Commands
|
|
72
|
+
|
|
73
|
+
Inside pi:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
/squiggle toggle # switch between on/off
|
|
77
|
+
/squiggle --help # show toggle usage
|
|
78
|
+
/squiggle-status # show status
|
|
79
|
+
/squiggle-status --help # show status-command usage
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`-h` is accepted wherever `--help` is shown.
|
|
83
|
+
|
|
84
|
+
The toggle state is saved in the current pi session and overrides `.pi/squiggle.json` and environment configuration for that session.
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
This package imports pi runtime packages as peer dependencies:
|
|
89
|
+
|
|
90
|
+
- `@earendil-works/pi-ai`
|
|
91
|
+
- `@earendil-works/pi-coding-agent`
|
|
92
|
+
|
|
93
|
+
Do not bundle those dependencies; pi provides them at runtime.
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { complete, type UserMessage } from "@earendil-works/pi-ai";
|
|
4
|
+
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
const SQUIGGLE_HELP = `Usage: /squiggle toggle
|
|
7
|
+
|
|
8
|
+
Toggle Squiggle on or off for the current session.
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--help, -h Show this help`;
|
|
12
|
+
|
|
13
|
+
const SQUIGGLE_STATUS_HELP = `Usage: /squiggle-status
|
|
14
|
+
|
|
15
|
+
Show whether Squiggle is enabled and which correction model it uses.
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--help, -h Show this help`;
|
|
19
|
+
|
|
20
|
+
export default function squiggle(pi: ExtensionAPI) {
|
|
21
|
+
let runtimeMode: SquiggleConfig["mode"] | undefined;
|
|
22
|
+
|
|
23
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
24
|
+
runtimeMode = restoreRuntimeMode(ctx);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
pi.registerCommand("squiggle", {
|
|
28
|
+
description: "Toggle squiggle on/off",
|
|
29
|
+
handler: async (args, ctx) => {
|
|
30
|
+
if (isHelpRequest(args)) return emitHelp(ctx, SQUIGGLE_HELP);
|
|
31
|
+
|
|
32
|
+
const command = args.trim().toLowerCase();
|
|
33
|
+
if (command !== "toggle") {
|
|
34
|
+
ctx.ui.notify("Usage: /squiggle toggle", "warning");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const config = loadEffectiveConfig(ctx.cwd, runtimeMode);
|
|
39
|
+
runtimeMode = config.mode === "on" ? "off" : "on";
|
|
40
|
+
persistRuntimeMode(pi, runtimeMode);
|
|
41
|
+
ctx.ui.notify(formatStatus(ctx, loadEffectiveConfig(ctx.cwd, runtimeMode)), "info");
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
pi.registerCommand("squiggle-status", {
|
|
46
|
+
description: "Show whether squiggle is loaded",
|
|
47
|
+
handler: async (args, ctx) => {
|
|
48
|
+
if (isHelpRequest(args)) return emitHelp(ctx, SQUIGGLE_STATUS_HELP);
|
|
49
|
+
ctx.ui.notify(formatStatus(ctx, loadEffectiveConfig(ctx.cwd, runtimeMode)), "info");
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
pi.on("input", async (event, ctx) => {
|
|
54
|
+
if (event.source === "extension") return { action: "continue" };
|
|
55
|
+
|
|
56
|
+
const config = loadEffectiveConfig(ctx.cwd, runtimeMode);
|
|
57
|
+
if (config.mode === "off") return { action: "continue" };
|
|
58
|
+
if (!event.text.trim()) return { action: "continue" };
|
|
59
|
+
|
|
60
|
+
const stopIndicator = startSquiggleIndicator(ctx);
|
|
61
|
+
const corrected = await correctWithModel(event.text, ctx, config).finally(stopIndicator);
|
|
62
|
+
if (!corrected || corrected === event.text) return { action: "continue" };
|
|
63
|
+
|
|
64
|
+
if (ctx.hasUI) ctx.ui.notify(formatColoredDiff(event.text, corrected), "info");
|
|
65
|
+
|
|
66
|
+
// In interactive mode, `transform` changes what the agent receives, but the
|
|
67
|
+
// already-submitted prompt may still be rendered as originally typed. To make
|
|
68
|
+
// the visible user message corrected too, swallow the original input and
|
|
69
|
+
// resubmit the corrected text as an extension-originated user message. The
|
|
70
|
+
// source guard above prevents a correction loop.
|
|
71
|
+
if (event.source === "interactive") {
|
|
72
|
+
pi.sendUserMessage(corrected);
|
|
73
|
+
return { action: "handled" };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { action: "transform", text: corrected };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isHelpRequest(args: string): boolean {
|
|
81
|
+
const normalized = args.trim();
|
|
82
|
+
return normalized === "--help" || normalized === "-h";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function emitHelp(ctx: ExtensionCommandContext, text: string): void {
|
|
86
|
+
if (ctx.hasUI) ctx.ui.notify(text, "info");
|
|
87
|
+
else console.log(text);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const CORRECTION_PROMPT = `You are a conservative grammar and spelling corrector for user prompts sent to a coding assistant.
|
|
91
|
+
|
|
92
|
+
Task:
|
|
93
|
+
- Correct spelling, grammar, capitalization, and punctuation.
|
|
94
|
+
- Preserve the user's meaning, tone, language, and intent.
|
|
95
|
+
- Do not answer the prompt.
|
|
96
|
+
- Do not add explanations, quotes, prefixes, markdown fences, or alternatives.
|
|
97
|
+
- If the input is already acceptable, return it unchanged.
|
|
98
|
+
- Return only the corrected prompt text.`;
|
|
99
|
+
|
|
100
|
+
const DEFAULT_CORRECTION_MODEL = "openai-codex/gpt-5.4-mini";
|
|
101
|
+
const DEFAULT_MAX_LLM_INPUT_CHARS = 500;
|
|
102
|
+
|
|
103
|
+
type SquiggleConfig = {
|
|
104
|
+
mode: "on" | "off";
|
|
105
|
+
model: string;
|
|
106
|
+
maxInputChars: number;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
async function correctWithModel(input: string, ctx: ExtensionContext, config: SquiggleConfig): Promise<string | null> {
|
|
110
|
+
const model = selectCorrectionModel(ctx, config);
|
|
111
|
+
if (!model) return null;
|
|
112
|
+
if (input.length > config.maxInputChars) return null;
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
116
|
+
if (!auth.ok || !auth.apiKey) return null;
|
|
117
|
+
|
|
118
|
+
const userMessage: UserMessage = {
|
|
119
|
+
role: "user",
|
|
120
|
+
content: [{ type: "text", text: input }],
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const response = await complete(
|
|
125
|
+
model,
|
|
126
|
+
{ systemPrompt: CORRECTION_PROMPT, messages: [userMessage] },
|
|
127
|
+
{ apiKey: auth.apiKey, headers: auth.headers },
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (response.stopReason === "aborted") return null;
|
|
131
|
+
|
|
132
|
+
return response.content
|
|
133
|
+
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
134
|
+
.map((c) => c.text)
|
|
135
|
+
.join("\n")
|
|
136
|
+
.trim();
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function loadConfig(cwd: string): SquiggleConfig {
|
|
143
|
+
const fileConfig = readConfigFile(cwd);
|
|
144
|
+
return {
|
|
145
|
+
mode: normalizeMode(process.env.SQUIGGLE_MODE ?? fileConfig.mode) ?? "on",
|
|
146
|
+
model: process.env.SQUIGGLE_MODEL ?? fileConfig.model ?? DEFAULT_CORRECTION_MODEL,
|
|
147
|
+
maxInputChars: normalizePositiveInt(process.env.SQUIGGLE_MAX_CHARS ?? fileConfig.maxInputChars) ?? DEFAULT_MAX_LLM_INPUT_CHARS,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function loadEffectiveConfig(cwd: string, runtimeMode: SquiggleConfig["mode"] | undefined): SquiggleConfig {
|
|
152
|
+
const config = loadConfig(cwd);
|
|
153
|
+
return { ...config, mode: runtimeMode ?? config.mode };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function restoreRuntimeMode(ctx: ExtensionContext): SquiggleConfig["mode"] | undefined {
|
|
157
|
+
for (const entry of [...ctx.sessionManager.getEntries()].reverse()) {
|
|
158
|
+
if (entry.type !== "custom" || entry.customType !== "squiggle-mode") continue;
|
|
159
|
+
const data = (entry as { data?: { mode?: unknown } }).data;
|
|
160
|
+
return normalizeMode(data?.mode);
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function persistRuntimeMode(pi: ExtensionAPI, mode: SquiggleConfig["mode"]): void {
|
|
166
|
+
pi.appendEntry("squiggle-mode", { mode });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function formatStatus(ctx: ExtensionContext, config: SquiggleConfig): string {
|
|
170
|
+
return `squiggle is ${config.mode} (${formatModel(selectCorrectionModel(ctx, config))}).`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function readConfigFile(cwd: string): Partial<SquiggleConfig> {
|
|
174
|
+
const path = join(cwd, ".pi", "squiggle.json");
|
|
175
|
+
if (!existsSync(path)) return {};
|
|
176
|
+
try {
|
|
177
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
|
178
|
+
return {
|
|
179
|
+
mode: typeof parsed.mode === "string" ? normalizeMode(parsed.mode) : undefined,
|
|
180
|
+
model: typeof parsed.model === "string" ? parsed.model : undefined,
|
|
181
|
+
maxInputChars: normalizePositiveInt(parsed.maxInputChars),
|
|
182
|
+
};
|
|
183
|
+
} catch {
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function normalizeMode(value: unknown): SquiggleConfig["mode"] | undefined {
|
|
189
|
+
return value === "on" || value === "off" ? value : undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function normalizePositiveInt(value: unknown): number | undefined {
|
|
193
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
194
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function selectCorrectionModel(ctx: ExtensionContext, config: SquiggleConfig) {
|
|
198
|
+
const configured = parseModelSpec(config.model);
|
|
199
|
+
if (configured) {
|
|
200
|
+
const model = ctx.modelRegistry.find(configured.provider, configured.model);
|
|
201
|
+
if (model) return model;
|
|
202
|
+
}
|
|
203
|
+
return ctx.model;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function parseModelSpec(spec: string): { provider: string; model: string } | null {
|
|
207
|
+
const slash = spec.indexOf("/");
|
|
208
|
+
if (slash <= 0 || slash === spec.length - 1) return null;
|
|
209
|
+
return { provider: spec.slice(0, slash), model: spec.slice(slash + 1) };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function formatModel(model: ReturnType<typeof selectCorrectionModel>): string {
|
|
213
|
+
return model ? `${model.provider}/${model.id}` : "no model";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function startSquiggleIndicator(ctx: ExtensionContext): () => void {
|
|
217
|
+
if (!ctx.hasUI) return () => {};
|
|
218
|
+
|
|
219
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
220
|
+
let frame = 0;
|
|
221
|
+
let timer: ReturnType<typeof setInterval> | undefined;
|
|
222
|
+
|
|
223
|
+
const render = () => {
|
|
224
|
+
const theme = ctx.ui.theme;
|
|
225
|
+
ctx.ui.setStatus("squiggle", theme.fg("accent", frames[frame]!) + theme.fg("dim", " squiggling..."));
|
|
226
|
+
frame = (frame + 1) % frames.length;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
render();
|
|
230
|
+
timer = setInterval(render, 120);
|
|
231
|
+
|
|
232
|
+
return () => {
|
|
233
|
+
if (timer) clearInterval(timer);
|
|
234
|
+
ctx.ui.setStatus("squiggle", undefined);
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
type DiffOp = {
|
|
239
|
+
type: "same" | "add" | "remove";
|
|
240
|
+
text: string;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
function formatColoredDiff(before: string, after: string): string {
|
|
244
|
+
const same = "\x1b[90;3m";
|
|
245
|
+
const added = "\x1b[32;3m";
|
|
246
|
+
const removed = "\x1b[31;3m";
|
|
247
|
+
const reset = "\x1b[0m";
|
|
248
|
+
|
|
249
|
+
return diffChars(before.trim(), after.trim())
|
|
250
|
+
.map((op) => {
|
|
251
|
+
if (op.type === "add") return `${added}${op.text}${reset}`;
|
|
252
|
+
if (op.type === "remove") return `${removed}${op.text}${reset}`;
|
|
253
|
+
return `${same}${op.text}${reset}`;
|
|
254
|
+
})
|
|
255
|
+
.join("");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function diffChars(before: string, after: string): DiffOp[] {
|
|
259
|
+
const beforeChars = Array.from(before);
|
|
260
|
+
const afterChars = Array.from(after);
|
|
261
|
+
const rows = beforeChars.length + 1;
|
|
262
|
+
const cols = afterChars.length + 1;
|
|
263
|
+
const dp: number[][] = Array.from({ length: rows }, () => Array(cols).fill(0));
|
|
264
|
+
|
|
265
|
+
for (let i = beforeChars.length - 1; i >= 0; i--) {
|
|
266
|
+
for (let j = afterChars.length - 1; j >= 0; j--) {
|
|
267
|
+
dp[i]![j] = beforeChars[i] === afterChars[j] ? dp[i + 1]![j + 1]! + 1 : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const ops: DiffOp[] = [];
|
|
272
|
+
let i = 0;
|
|
273
|
+
let j = 0;
|
|
274
|
+
while (i < beforeChars.length || j < afterChars.length) {
|
|
275
|
+
if (i < beforeChars.length && j < afterChars.length && beforeChars[i] === afterChars[j]) {
|
|
276
|
+
pushDiffOp(ops, "same", afterChars[j]!);
|
|
277
|
+
i++;
|
|
278
|
+
j++;
|
|
279
|
+
} else if (j < afterChars.length && (i === beforeChars.length || dp[i]![j + 1]! > dp[i + 1]![j]!)) {
|
|
280
|
+
pushDiffOp(ops, "add", afterChars[j]!);
|
|
281
|
+
j++;
|
|
282
|
+
} else if (i < beforeChars.length) {
|
|
283
|
+
pushDiffOp(ops, "remove", beforeChars[i]!);
|
|
284
|
+
i++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return ops;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function pushDiffOp(ops: DiffOp[], type: DiffOp["type"], text: string) {
|
|
292
|
+
const last = ops.at(-1);
|
|
293
|
+
if (last?.type === type) {
|
|
294
|
+
last.text += text;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
ops.push({ type, text });
|
|
298
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./extensions/squiggle.ts";
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pithos-kit/squiggle",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Quietly polish grammar and spelling in your Pi prompts.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi-extension",
|
|
8
|
+
"pithos",
|
|
9
|
+
"grammar",
|
|
10
|
+
"writing"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test test/*.test.ts"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/anton-kochev/pithos-kit.git",
|
|
20
|
+
"directory": "pithos.squiggle"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@earendil-works/pi-ai": "*",
|
|
27
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
28
|
+
},
|
|
29
|
+
"pithosKit": {
|
|
30
|
+
"displayName": "Squiggle",
|
|
31
|
+
"summary": "Quietly polish grammar and spelling in user prompts.",
|
|
32
|
+
"minimumPi": ">=0.83.0",
|
|
33
|
+
"commands": [
|
|
34
|
+
{
|
|
35
|
+
"name": "squiggle",
|
|
36
|
+
"usage": "/squiggle [toggle|--help]",
|
|
37
|
+
"summary": "Toggle prompt correction."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "squiggle-status",
|
|
41
|
+
"usage": "/squiggle-status [--help]",
|
|
42
|
+
"summary": "Show correction status and configuration."
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"tools": [],
|
|
46
|
+
"prompts": [],
|
|
47
|
+
"skills": [],
|
|
48
|
+
"themes": [],
|
|
49
|
+
"agents": [],
|
|
50
|
+
"configuration": [
|
|
51
|
+
{
|
|
52
|
+
"kind": "file",
|
|
53
|
+
"key": ".pi/squiggle.json",
|
|
54
|
+
"summary": "Project correction mode, model, and input limit."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"kind": "environment",
|
|
58
|
+
"key": "SQUIGGLE_MODE",
|
|
59
|
+
"summary": "Override correction mode."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"kind": "environment",
|
|
63
|
+
"key": "SQUIGGLE_MODEL",
|
|
64
|
+
"summary": "Override the correction model."
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"kind": "environment",
|
|
68
|
+
"key": "SQUIGGLE_MAX_CHARS",
|
|
69
|
+
"summary": "Override the maximum corrected input length."
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"pi": {
|
|
74
|
+
"extensions": [
|
|
75
|
+
"./extensions"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"files": [
|
|
79
|
+
"index.ts",
|
|
80
|
+
"extensions",
|
|
81
|
+
"README.md"
|
|
82
|
+
]
|
|
83
|
+
}
|