@oh-my-pi/pi-coding-agent 15.6.0 → 15.7.1
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 +35 -0
- package/dist/types/capability/rule-buckets.d.ts +30 -0
- package/dist/types/capability/rule.d.ts +7 -0
- package/dist/types/cli/completion-gen.d.ts +80 -0
- package/dist/types/commands/complete.d.ts +6 -0
- package/dist/types/commands/completions.d.ts +13 -0
- package/dist/types/commands/setup.d.ts +10 -1
- package/dist/types/config/settings-schema.d.ts +170 -10
- package/dist/types/discovery/builtin-defaults.d.ts +1 -0
- package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
- package/dist/types/edit/hashline/index.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +11 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/segment-track.d.ts +22 -0
- package/dist/types/modes/components/welcome.d.ts +21 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/setup-wizard/index.d.ts +16 -0
- package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
- package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
- package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
- package/dist/types/modes/theme/shimmer.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +11 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/tiny/device.d.ts +78 -0
- package/dist/types/tiny/dtype.d.ts +85 -0
- package/dist/types/tiny/models.d.ts +6 -6
- package/dist/types/tiny/text.d.ts +15 -0
- package/dist/types/tiny/title-client.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/index.d.ts +0 -1
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +17 -0
- package/package.json +9 -9
- package/src/capability/rule-buckets.ts +64 -0
- package/src/capability/rule.ts +8 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/setup-cli.ts +5 -3
- package/src/cli-commands.ts +2 -0
- package/src/cli.ts +1 -7
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/setup.ts +29 -4
- package/src/config/settings-schema.ts +70 -11
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/index.ts +48 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
- package/src/discovery/builtin-rules/rs-result-type.md +19 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
- package/src/discovery/builtin-rules/ts-import-type.md +42 -0
- package/src/discovery/builtin-rules/ts-no-any.md +56 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/index.ts +1 -0
- package/src/edit/hashline/block-resolver.ts +14 -0
- package/src/edit/hashline/diff.ts +4 -1
- package/src/edit/hashline/execute.ts +2 -1
- package/src/edit/hashline/index.ts +1 -0
- package/src/eval/py/kernel.ts +37 -15
- package/src/eval/py/runtime.ts +57 -28
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -12
- package/src/export/ttsr.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +7 -8
- package/src/main.ts +18 -1
- package/src/modes/components/hook-selector.ts +15 -17
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +5 -1
- package/src/modes/components/welcome.ts +47 -42
- package/src/modes/controllers/input-controller.ts +12 -21
- package/src/modes/interactive-mode.ts +17 -5
- package/src/modes/setup-wizard/index.ts +88 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +69 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +299 -0
- package/src/modes/setup-wizard/scenes/types.ts +48 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
- package/src/modes/theme/shimmer.ts +5 -0
- package/src/modes/theme/theme.ts +44 -20
- package/src/modes/types.ts +6 -1
- package/src/prompts/system/orchestrate-notice.md +1 -1
- package/src/prompts/tools/read.md +4 -0
- package/src/sdk.ts +5 -15
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/tiny/device.ts +117 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +7 -6
- package/src/tiny/text.ts +36 -1
- package/src/tiny/title-client.ts +58 -3
- package/src/tiny/worker.ts +93 -29
- package/src/tools/bash.ts +16 -13
- package/src/tools/eval.ts +9 -4
- package/src/tools/index.ts +0 -11
- package/src/tools/read.ts +1 -0
- package/src/tools/renderers.ts +0 -2
- package/src/tui/code-cell.ts +6 -1
- package/src/tui/output-block.ts +199 -38
- package/dist/types/tools/recipe/index.d.ts +0 -46
- package/dist/types/tools/recipe/render.d.ts +0 -36
- package/dist/types/tools/recipe/runner.d.ts +0 -60
- package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
- package/dist/types/tools/recipe/runners/index.d.ts +0 -2
- package/dist/types/tools/recipe/runners/just.d.ts +0 -2
- package/dist/types/tools/recipe/runners/make.d.ts +0 -2
- package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
- package/dist/types/tools/recipe/runners/task.d.ts +0 -2
- package/src/prompts/tools/recipe.md +0 -16
- package/src/tools/recipe/index.ts +0 -81
- package/src/tools/recipe/render.ts +0 -19
- package/src/tools/recipe/runner.ts +0 -219
- package/src/tools/recipe/runners/cargo.ts +0 -131
- package/src/tools/recipe/runners/index.ts +0 -8
- package/src/tools/recipe/runners/just.ts +0 -73
- package/src/tools/recipe/runners/make.ts +0 -101
- package/src/tools/recipe/runners/pkg.ts +0 -167
- package/src/tools/recipe/runners/task.ts +0 -72
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `omp __complete <kind> [-- <prefix>]` — dynamic completion candidates.
|
|
3
|
+
*
|
|
4
|
+
* Hidden helper invoked by the generated shell completion scripts to resolve
|
|
5
|
+
* values that can't be baked into the script: the live model catalog and
|
|
6
|
+
* on-disk sessions. Output is one `value\tdescription` line per candidate
|
|
7
|
+
* (tab-separated); shells that show descriptions parse the tab, bash uses the
|
|
8
|
+
* first field. The import surface is kept deliberately narrow so a TAB press
|
|
9
|
+
* doesn't pay for the full agent boot.
|
|
10
|
+
*/
|
|
11
|
+
import { type GeneratedProvider, getBundledModels, getBundledProviders } from "@oh-my-pi/pi-ai/models";
|
|
12
|
+
import { Command } from "@oh-my-pi/pi-utils/cli";
|
|
13
|
+
import { SessionManager } from "../session/session-manager";
|
|
14
|
+
|
|
15
|
+
export default class Complete extends Command {
|
|
16
|
+
static hidden = true;
|
|
17
|
+
static strict = false;
|
|
18
|
+
|
|
19
|
+
async run(): Promise<void> {
|
|
20
|
+
const argv = this.argv.filter(token => token !== "--");
|
|
21
|
+
const kind = argv[0];
|
|
22
|
+
const prefix = argv.length > 1 ? argv[argv.length - 1] : "";
|
|
23
|
+
if (kind === "models") {
|
|
24
|
+
completeModels(prefix);
|
|
25
|
+
} else if (kind === "sessions") {
|
|
26
|
+
await completeSessions(prefix);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Strip control chars that would corrupt the tab-separated line protocol. */
|
|
32
|
+
function clean(text: string): string {
|
|
33
|
+
return text.replace(/[\t\r\n]+/g, " ").trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function completeModels(prefix: string): void {
|
|
37
|
+
const needle = prefix.toLowerCase();
|
|
38
|
+
const seen = new Set<string>();
|
|
39
|
+
const lines: string[] = [];
|
|
40
|
+
for (const provider of getBundledProviders()) {
|
|
41
|
+
for (const model of getBundledModels(provider as GeneratedProvider)) {
|
|
42
|
+
// Offer both the fully-qualified `provider/id` and the bare `id`
|
|
43
|
+
// (matches the fuzzy resolution `--model` accepts).
|
|
44
|
+
const candidates = [`${model.provider}/${model.id}`, model.id];
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
if (seen.has(candidate)) continue;
|
|
47
|
+
seen.add(candidate);
|
|
48
|
+
if (needle && !candidate.toLowerCase().includes(needle)) continue;
|
|
49
|
+
lines.push(`${candidate}\t${model.provider}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
lines.sort();
|
|
54
|
+
if (lines.length > 0) process.stdout.write(`${lines.join("\n")}\n`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function completeSessions(prefix: string): Promise<void> {
|
|
58
|
+
const sessions = await SessionManager.list(process.cwd());
|
|
59
|
+
const lines: string[] = [];
|
|
60
|
+
for (const session of sessions) {
|
|
61
|
+
if (prefix && !session.id.startsWith(prefix)) continue;
|
|
62
|
+
const label = clean(session.title ?? session.firstMessage ?? "").slice(0, 72);
|
|
63
|
+
lines.push(`${session.id}\t${label}`);
|
|
64
|
+
}
|
|
65
|
+
if (lines.length > 0) process.stdout.write(`${lines.join("\n")}\n`);
|
|
66
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `omp completions <bash|zsh|fish>` — print a shell completion script.
|
|
3
|
+
*
|
|
4
|
+
* The script is derived entirely from the declarative command/flag metadata
|
|
5
|
+
* (see `cli/completion-gen.ts`), so it never drifts from the actual CLI surface.
|
|
6
|
+
*/
|
|
7
|
+
import { APP_NAME, VERSION } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { Args, type CliConfig, Command, type CommandCtor } from "@oh-my-pi/pi-utils/cli";
|
|
9
|
+
import { buildSpec, generateCompletion, type Shell } from "../cli/completion-gen";
|
|
10
|
+
import { commands } from "../cli-commands";
|
|
11
|
+
|
|
12
|
+
/** Entry name of the default command whose flags become top-level completions. */
|
|
13
|
+
const ROOT_COMMAND = "launch";
|
|
14
|
+
const SHELLS = ["bash", "zsh", "fish"] as const;
|
|
15
|
+
|
|
16
|
+
export default class Completions extends Command {
|
|
17
|
+
static description = "Print a shell completion script (bash, zsh, or fish)";
|
|
18
|
+
|
|
19
|
+
static args = {
|
|
20
|
+
shell: Args.string({
|
|
21
|
+
description: "Target shell",
|
|
22
|
+
required: true,
|
|
23
|
+
options: SHELLS,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
static examples = [
|
|
28
|
+
`# zsh — eval at startup, or write to a file in $fpath\n eval "$(${APP_NAME} completions zsh)"`,
|
|
29
|
+
`# bash\n eval "$(${APP_NAME} completions bash)"`,
|
|
30
|
+
`# fish\n ${APP_NAME} completions fish > ~/.config/fish/completions/${APP_NAME}.fish`,
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
async run(): Promise<void> {
|
|
34
|
+
const shell = this.argv[0];
|
|
35
|
+
if (!isShell(shell)) {
|
|
36
|
+
process.stderr.write(`Usage: ${APP_NAME} completions <${SHELLS.join("|")}>\n`);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Load every command class so we can read its static flag/arg descriptors,
|
|
42
|
+
// and collect aliases from both the registration table and the class.
|
|
43
|
+
const loaded = await Promise.all(commands.map(async entry => ({ entry, Cmd: await entry.load() })));
|
|
44
|
+
const map = new Map<string, CommandCtor>();
|
|
45
|
+
const aliasMap = new Map<string, readonly string[]>();
|
|
46
|
+
for (const { entry, Cmd } of loaded) {
|
|
47
|
+
map.set(entry.name, Cmd);
|
|
48
|
+
const merged = new Set<string>([...(Cmd.aliases ?? []), ...(entry.aliases ?? [])]);
|
|
49
|
+
aliasMap.set(entry.name, [...merged]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const config: CliConfig = { bin: APP_NAME, version: VERSION, commands: map };
|
|
53
|
+
const spec = buildSpec(config, ROOT_COMMAND, aliasMap);
|
|
54
|
+
process.stdout.write(generateCompletion(shell, spec));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isShell(value: string | undefined): value is Shell {
|
|
59
|
+
return value === "bash" || value === "zsh" || value === "fish";
|
|
60
|
+
}
|
package/src/commands/setup.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Run onboarding setup or install dependencies for optional features.
|
|
3
3
|
*/
|
|
4
4
|
import { Args, Command, Flags, renderCommandHelp } from "@oh-my-pi/pi-utils/cli";
|
|
5
|
+
import { parseArgs } from "../cli/args";
|
|
5
6
|
import { runSetupCommand, type SetupCommandArgs, type SetupComponent } from "../cli/setup-cli";
|
|
7
|
+
import { runRootCommand } from "../main";
|
|
6
8
|
import { initTheme } from "../modes/theme/theme";
|
|
7
9
|
|
|
8
10
|
const COMPONENTS: SetupComponent[] = ["python", "stt"];
|
|
9
11
|
|
|
12
|
+
export interface OnboardingSetupDependencies {
|
|
13
|
+
runRoot?: typeof runRootCommand;
|
|
14
|
+
stdinIsTTY?: boolean;
|
|
15
|
+
stdoutIsTTY?: boolean;
|
|
16
|
+
writeStderr?: (text: string) => void;
|
|
17
|
+
exit?: (code: number) => never;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runOnboardingSetup(deps: OnboardingSetupDependencies = {}): Promise<void> {
|
|
21
|
+
const stdinIsTTY = deps.stdinIsTTY ?? process.stdin.isTTY;
|
|
22
|
+
const stdoutIsTTY = deps.stdoutIsTTY ?? process.stdout.isTTY;
|
|
23
|
+
if (!stdinIsTTY || !stdoutIsTTY) {
|
|
24
|
+
(deps.writeStderr ?? (text => process.stderr.write(text)))("omp setup requires an interactive TTY.\n");
|
|
25
|
+
(deps.exit ?? process.exit)(1);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await (deps.runRoot ?? runRootCommand)(parseArgs([]), [], { forceSetupWizard: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
export default class Setup extends Command {
|
|
11
|
-
static description = "
|
|
32
|
+
static description = "Run onboarding setup or install dependencies for optional features";
|
|
12
33
|
|
|
13
34
|
static args = {
|
|
14
35
|
component: Args.string({
|
|
15
|
-
description: "
|
|
36
|
+
description: "Optional component to install",
|
|
16
37
|
required: false,
|
|
17
38
|
options: COMPONENTS,
|
|
18
39
|
}),
|
|
@@ -26,7 +47,11 @@ export default class Setup extends Command {
|
|
|
26
47
|
async run(): Promise<void> {
|
|
27
48
|
const { args, flags } = await this.parse(Setup);
|
|
28
49
|
if (!args.component) {
|
|
29
|
-
|
|
50
|
+
if (flags.check || flags.json) {
|
|
51
|
+
renderCommandHelp("omp", "setup", Setup);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await runOnboardingSetup();
|
|
30
55
|
return;
|
|
31
56
|
}
|
|
32
57
|
const cmd: SetupCommandArgs = {
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { TASK_SIMPLE_MODES } from "../task/simple-mode";
|
|
3
3
|
import { getThinkingLevelMetadata } from "../thinking";
|
|
4
|
+
import {
|
|
5
|
+
TINY_MODEL_DEVICE_DEFAULT,
|
|
6
|
+
TINY_MODEL_DEVICE_SETTING_OPTIONS,
|
|
7
|
+
TINY_MODEL_DEVICE_SETTING_VALUES,
|
|
8
|
+
} from "../tiny/device";
|
|
9
|
+
import {
|
|
10
|
+
TINY_MODEL_DTYPE_DEFAULT,
|
|
11
|
+
TINY_MODEL_DTYPE_SETTING_OPTIONS,
|
|
12
|
+
TINY_MODEL_DTYPE_SETTING_VALUES,
|
|
13
|
+
} from "../tiny/dtype";
|
|
4
14
|
import {
|
|
5
15
|
ONLINE_MEMORY_MODEL_KEY,
|
|
6
16
|
ONLINE_TINY_TITLE_MODEL_KEY,
|
|
@@ -243,6 +253,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
243
253
|
// General settings (no UI)
|
|
244
254
|
// ────────────────────────────────────────────────────────────────────────
|
|
245
255
|
lastChangelogVersion: { type: "string", default: undefined },
|
|
256
|
+
setupVersion: { type: "number", default: 0 },
|
|
246
257
|
|
|
247
258
|
// Auth broker — credentials proxied through a remote `omp auth-broker serve`
|
|
248
259
|
// host. Hidden from the UI; populate via env vars or hand-edited config.yml.
|
|
@@ -999,6 +1010,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
999
1010
|
},
|
|
1000
1011
|
},
|
|
1001
1012
|
|
|
1013
|
+
"startup.setupWizard": {
|
|
1014
|
+
type: "boolean",
|
|
1015
|
+
default: true,
|
|
1016
|
+
ui: {
|
|
1017
|
+
tab: "interaction",
|
|
1018
|
+
label: "Setup Wizard",
|
|
1019
|
+
description: "Show newly added onboarding steps once per setup version",
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
|
|
1002
1023
|
"startup.checkUpdate": {
|
|
1003
1024
|
type: "boolean",
|
|
1004
1025
|
default: true,
|
|
@@ -1694,6 +1715,26 @@ export const SETTINGS_SCHEMA = {
|
|
|
1694
1715
|
},
|
|
1695
1716
|
},
|
|
1696
1717
|
|
|
1718
|
+
"ttsr.builtinRules": {
|
|
1719
|
+
type: "boolean",
|
|
1720
|
+
default: true,
|
|
1721
|
+
ui: {
|
|
1722
|
+
tab: "context",
|
|
1723
|
+
label: "Builtin Rules",
|
|
1724
|
+
description: "Load the default rules shipped with the agent (override individually with ttsr.disabledRules)",
|
|
1725
|
+
},
|
|
1726
|
+
},
|
|
1727
|
+
|
|
1728
|
+
"ttsr.disabledRules": {
|
|
1729
|
+
type: "array",
|
|
1730
|
+
default: [] as string[],
|
|
1731
|
+
ui: {
|
|
1732
|
+
tab: "context",
|
|
1733
|
+
label: "Disabled Rules",
|
|
1734
|
+
description: "Rule names to ignore entirely (applies to bundled defaults and your own rules)",
|
|
1735
|
+
},
|
|
1736
|
+
},
|
|
1737
|
+
|
|
1697
1738
|
// ────────────────────────────────────────────────────────────────────────
|
|
1698
1739
|
// Editing
|
|
1699
1740
|
// ────────────────────────────────────────────────────────────────────────
|
|
@@ -2025,7 +2066,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2025
2066
|
value: "write",
|
|
2026
2067
|
label: "Write",
|
|
2027
2068
|
description:
|
|
2028
|
-
"Auto-approve read-only and write tools; require confirmation for exec tools such as bash, eval, browser, task,
|
|
2069
|
+
"Auto-approve read-only and write tools; require confirmation for exec tools such as bash, eval, browser, task, and ssh.",
|
|
2029
2070
|
},
|
|
2030
2071
|
{
|
|
2031
2072
|
value: "yolo",
|
|
@@ -2203,16 +2244,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
2203
2244
|
description: "Enable the tts tool for xAI Grok Voice speech synthesis",
|
|
2204
2245
|
},
|
|
2205
2246
|
},
|
|
2206
|
-
"recipe.enabled": {
|
|
2207
|
-
type: "boolean",
|
|
2208
|
-
default: true,
|
|
2209
|
-
ui: {
|
|
2210
|
-
tab: "tools",
|
|
2211
|
-
label: "Recipe",
|
|
2212
|
-
description:
|
|
2213
|
-
"Enable the recipe tool when a justfile / package.json / Cargo.toml / Makefile / Taskfile is present",
|
|
2214
|
-
},
|
|
2215
|
-
},
|
|
2216
2247
|
|
|
2217
2248
|
"inspect_image.enabled": {
|
|
2218
2249
|
type: "boolean",
|
|
@@ -2915,6 +2946,30 @@ export const SETTINGS_SCHEMA = {
|
|
|
2915
2946
|
options: TINY_TITLE_MODEL_OPTIONS,
|
|
2916
2947
|
},
|
|
2917
2948
|
},
|
|
2949
|
+
"providers.tinyModelDevice": {
|
|
2950
|
+
type: "enum",
|
|
2951
|
+
values: TINY_MODEL_DEVICE_SETTING_VALUES,
|
|
2952
|
+
default: TINY_MODEL_DEVICE_DEFAULT,
|
|
2953
|
+
ui: {
|
|
2954
|
+
tab: "providers",
|
|
2955
|
+
label: "Tiny Model Device",
|
|
2956
|
+
description:
|
|
2957
|
+
"ONNX execution provider for local tiny models (titles + memory). Default picks DirectML on Windows, CUDA on Linux x64, CPU elsewhere. The PI_TINY_DEVICE env var overrides this.",
|
|
2958
|
+
options: TINY_MODEL_DEVICE_SETTING_OPTIONS,
|
|
2959
|
+
},
|
|
2960
|
+
},
|
|
2961
|
+
"providers.tinyModelDtype": {
|
|
2962
|
+
type: "enum",
|
|
2963
|
+
values: TINY_MODEL_DTYPE_SETTING_VALUES,
|
|
2964
|
+
default: TINY_MODEL_DTYPE_DEFAULT,
|
|
2965
|
+
ui: {
|
|
2966
|
+
tab: "providers",
|
|
2967
|
+
label: "Tiny Model Precision",
|
|
2968
|
+
description:
|
|
2969
|
+
"ONNX quantization/precision for local tiny models. Default uses each model's shipped dtype (q4); lower precision is faster, higher is more faithful. The PI_TINY_DTYPE env var overrides this.",
|
|
2970
|
+
options: TINY_MODEL_DTYPE_SETTING_OPTIONS,
|
|
2971
|
+
},
|
|
2972
|
+
},
|
|
2918
2973
|
"providers.memoryModel": {
|
|
2919
2974
|
type: "enum",
|
|
2920
2975
|
values: TINY_MEMORY_MODEL_VALUES,
|
|
@@ -3298,6 +3353,10 @@ export interface TtsrSettings {
|
|
|
3298
3353
|
interruptMode: "never" | "prose-only" | "tool-only" | "always";
|
|
3299
3354
|
repeatMode: "once" | "after-gap";
|
|
3300
3355
|
repeatGap: number;
|
|
3356
|
+
/** Bucketing-only (read by bucketRules, not the TtsrManager). */
|
|
3357
|
+
builtinRules?: boolean;
|
|
3358
|
+
/** Bucketing-only (read by bucketRules, not the TtsrManager). */
|
|
3359
|
+
disabledRules?: string[];
|
|
3301
3360
|
}
|
|
3302
3361
|
|
|
3303
3362
|
export interface ExaSettings {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builtin Defaults Provider
|
|
3
|
+
*
|
|
4
|
+
* Ships a curated set of default rules (mostly TTSR conventions) embedded into
|
|
5
|
+
* the binary. Registered at the lowest priority so any user/project/tool rule
|
|
6
|
+
* with the same `name` overrides the bundled copy (first-wins dedup by name).
|
|
7
|
+
*
|
|
8
|
+
* Users disable bundled rules three ways:
|
|
9
|
+
* - flip `ttsr.builtinRules` off (drops the whole set),
|
|
10
|
+
* - list a name in `ttsr.disabledRules` (drops one rule),
|
|
11
|
+
* - define a same-named rule in any higher-priority source (overrides it).
|
|
12
|
+
* The first two are enforced in `bucketRules` (see capability/rule-buckets.ts).
|
|
13
|
+
*/
|
|
14
|
+
import { registerProvider } from "../capability";
|
|
15
|
+
import { BUILTIN_DEFAULTS_PROVIDER_ID, type Rule, ruleCapability } from "../capability/rule";
|
|
16
|
+
import type { LoadContext, LoadResult } from "../capability/types";
|
|
17
|
+
import { BUILTIN_RULE_SOURCES } from "./builtin-rules";
|
|
18
|
+
import { buildRuleFromMarkdown, createSourceMeta } from "./helpers";
|
|
19
|
+
|
|
20
|
+
const DISPLAY_NAME = "Builtin Defaults";
|
|
21
|
+
// Lowest priority: every other rule provider wins a name conflict.
|
|
22
|
+
const PRIORITY = 1;
|
|
23
|
+
|
|
24
|
+
async function loadRules(_ctx: LoadContext): Promise<LoadResult<Rule>> {
|
|
25
|
+
const items = BUILTIN_RULE_SOURCES.map(({ name, content }) => {
|
|
26
|
+
const virtualPath = `${BUILTIN_DEFAULTS_PROVIDER_ID}:${name}.md`;
|
|
27
|
+
const source = createSourceMeta(BUILTIN_DEFAULTS_PROVIDER_ID, virtualPath, "user");
|
|
28
|
+
return buildRuleFromMarkdown(name, content, virtualPath, source, { ruleName: name });
|
|
29
|
+
});
|
|
30
|
+
return { items };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
registerProvider<Rule>(ruleCapability.id, {
|
|
34
|
+
id: BUILTIN_DEFAULTS_PROVIDER_ID,
|
|
35
|
+
displayName: DISPLAY_NAME,
|
|
36
|
+
description: "Default rules shipped with the agent (disable via ttsr.builtinRules / ttsr.disabledRules)",
|
|
37
|
+
priority: PRIORITY,
|
|
38
|
+
load: loadRules,
|
|
39
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled default rules shipped with the coding agent.
|
|
3
|
+
*
|
|
4
|
+
* Each markdown source is embedded via `with { type: "text" }` so it survives
|
|
5
|
+
* `bun build --compile` (the compiled binary ships no loose rule files; only
|
|
6
|
+
* the embedded text). The native source/tarball installs read the same modules.
|
|
7
|
+
*
|
|
8
|
+
* Registered by the lowest-priority `builtin-defaults` rule provider so any
|
|
9
|
+
* user/project/tool rule with the same name overrides the bundled copy.
|
|
10
|
+
*/
|
|
11
|
+
import rsBoxLeak from "./rs-box-leak.md" with { type: "text" };
|
|
12
|
+
import rsFuturePrelude from "./rs-future-prelude.md" with { type: "text" };
|
|
13
|
+
import rsLazylock from "./rs-lazylock.md" with { type: "text" };
|
|
14
|
+
import rsMatchErgonomics from "./rs-match-ergonomics.md" with { type: "text" };
|
|
15
|
+
import rsParkingLot from "./rs-parking-lot.md" with { type: "text" };
|
|
16
|
+
import rsResultType from "./rs-result-type.md" with { type: "text" };
|
|
17
|
+
import tsBareCatch from "./ts-bare-catch.md" with { type: "text" };
|
|
18
|
+
import tsImportType from "./ts-import-type.md" with { type: "text" };
|
|
19
|
+
import tsNoAny from "./ts-no-any.md" with { type: "text" };
|
|
20
|
+
import tsNoDynamicImport from "./ts-no-dynamic-import.md" with { type: "text" };
|
|
21
|
+
import tsNoReturnType from "./ts-no-return-type.md" with { type: "text" };
|
|
22
|
+
import tsNoTinyFunctions from "./ts-no-tiny-functions.md" with { type: "text" };
|
|
23
|
+
import tsPromiseWithResolvers from "./ts-promise-with-resolvers.md" with { type: "text" };
|
|
24
|
+
import tsSetMap from "./ts-set-map.md" with { type: "text" };
|
|
25
|
+
|
|
26
|
+
/** A bundled rule's stable name and raw markdown (frontmatter + body). */
|
|
27
|
+
export interface BuiltinRuleSource {
|
|
28
|
+
name: string;
|
|
29
|
+
content: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** All bundled default rules, ordered by name. */
|
|
33
|
+
export const BUILTIN_RULE_SOURCES: readonly BuiltinRuleSource[] = [
|
|
34
|
+
{ name: "rs-box-leak", content: rsBoxLeak },
|
|
35
|
+
{ name: "rs-future-prelude", content: rsFuturePrelude },
|
|
36
|
+
{ name: "rs-lazylock", content: rsLazylock },
|
|
37
|
+
{ name: "rs-match-ergonomics", content: rsMatchErgonomics },
|
|
38
|
+
{ name: "rs-parking-lot", content: rsParkingLot },
|
|
39
|
+
{ name: "rs-result-type", content: rsResultType },
|
|
40
|
+
{ name: "ts-bare-catch", content: tsBareCatch },
|
|
41
|
+
{ name: "ts-import-type", content: tsImportType },
|
|
42
|
+
{ name: "ts-no-any", content: tsNoAny },
|
|
43
|
+
{ name: "ts-no-dynamic-import", content: tsNoDynamicImport },
|
|
44
|
+
{ name: "ts-no-return-type", content: tsNoReturnType },
|
|
45
|
+
{ name: "ts-no-tiny-functions", content: tsNoTinyFunctions },
|
|
46
|
+
{ name: "ts-promise-with-resolvers", content: tsPromiseWithResolvers },
|
|
47
|
+
{ name: "ts-set-map", content: tsSetMap },
|
|
48
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Never use Box::leak - it intentionally leaks memory
|
|
3
|
+
condition: "Box::leak"
|
|
4
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Never use `Box::leak` to satisfy a lifetime. It intentionally leaks the allocation for the rest of the process.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
- The allocation is never freed.
|
|
12
|
+
- It hides ownership bugs.
|
|
13
|
+
- It turns lifetime errors into process lifetime growth.
|
|
14
|
+
- It makes tests pass while production memory grows.
|
|
15
|
+
|
|
16
|
+
## Use instead
|
|
17
|
+
|
|
18
|
+
| Need | Use |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| Shared async/thread data | `Arc<T>` or owned values |
|
|
21
|
+
| Global lazy state | `LazyLock<T>` or `OnceLock<T>` |
|
|
22
|
+
| Text escaping a scope | `String` / `Arc<str>` |
|
|
23
|
+
| `'static` callback | `move` closure with owned captures |
|
|
24
|
+
| FFI pointer | Explicit owner that frees on drop |
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
```rust
|
|
29
|
+
// Bad — leaking to manufacture 'static.
|
|
30
|
+
fn label(id: u64) -> &'static str {
|
|
31
|
+
Box::leak(Box::new(format!("item_{id}")))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Good — return owned data.
|
|
35
|
+
fn label(id: u64) -> String {
|
|
36
|
+
format!("item_{id}")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Bad — leaking before spawn.
|
|
40
|
+
let state = Box::leak(Box::new(state));
|
|
41
|
+
tokio::spawn(async move { use_state(state) });
|
|
42
|
+
|
|
43
|
+
// Good — share owned state.
|
|
44
|
+
let state = Arc::new(state);
|
|
45
|
+
tokio::spawn(async move { use_state(&state) });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If `Box::leak` looks necessary, fix ownership instead.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use Future not std::future::Future - it's in the prelude
|
|
3
|
+
condition: "std::future::Future"
|
|
4
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use `Future` directly instead of `std::future::Future` in type positions.
|
|
8
|
+
|
|
9
|
+
Rust 2024 includes `Future` in the standard prelude. Older editions can import it once with `use std::future::Future;`. Repeating the fully qualified path makes signatures harder to read without adding safety.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
```rust
|
|
14
|
+
// Bad — fully qualified in every signature.
|
|
15
|
+
fn fetch() -> impl std::future::Future<Output = Result<Data>> { ... }
|
|
16
|
+
fn poll(fut: Pin<&mut dyn std::future::Future<Output = i32>>) { ... }
|
|
17
|
+
|
|
18
|
+
// Good — use the prelude or one import.
|
|
19
|
+
fn fetch() -> impl Future<Output = Result<Data>> { ... }
|
|
20
|
+
fn poll(fut: Pin<&mut dyn Future<Output = i32>>) { ... }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Pre-2024 edition? Add `use std::future::Future;` at the top.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prefer std::sync::LazyLock over OnceLock and once_cell
|
|
3
|
+
condition:
|
|
4
|
+
- "once_cell::"
|
|
5
|
+
- "OnceLock::new"
|
|
6
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Prefer `std::sync::LazyLock` over `OnceLock` and the `once_cell` crate when the initializer is known at declaration time.
|
|
10
|
+
|
|
11
|
+
`LazyLock` stores the cell and initializer together. There is no separate `init()` function, no repeated `get_or_init`, and no missing initialization path.
|
|
12
|
+
|
|
13
|
+
## once_cell → std
|
|
14
|
+
|
|
15
|
+
```rust
|
|
16
|
+
// Before
|
|
17
|
+
use once_cell::sync::Lazy;
|
|
18
|
+
static CONFIG: Lazy<String> = Lazy::new(|| "value".to_string());
|
|
19
|
+
|
|
20
|
+
// After
|
|
21
|
+
use std::sync::LazyLock;
|
|
22
|
+
static CONFIG: LazyLock<String> = LazyLock::new(|| "value".to_string());
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## OnceLock → LazyLock
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
// Before — fixed initializer hidden in accessor.
|
|
29
|
+
use std::sync::OnceLock;
|
|
30
|
+
static SETTINGS: OnceLock<Settings> = OnceLock::new();
|
|
31
|
+
fn settings() -> &'static Settings {
|
|
32
|
+
SETTINGS.get_or_init(Settings::load)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// After — initializer lives with the static.
|
|
36
|
+
use std::sync::LazyLock;
|
|
37
|
+
static SETTINGS: LazyLock<Settings> = LazyLock::new(Settings::load);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Keep OnceLock when runtime input is required
|
|
41
|
+
|
|
42
|
+
```rust
|
|
43
|
+
use std::sync::OnceLock;
|
|
44
|
+
static DATABASE: OnceLock<Database> = OnceLock::new();
|
|
45
|
+
|
|
46
|
+
fn init_database(url: &str) {
|
|
47
|
+
let _ = DATABASE.set(Database::connect(url));
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Do not add `once_cell` for new code. Use the standard library equivalent.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use match ergonomics instead of ref/ref mut patterns
|
|
3
|
+
condition:
|
|
4
|
+
- "\\(ref mut "
|
|
5
|
+
- "\\(ref [a-z_]"
|
|
6
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Use match ergonomics instead of explicit `ref` / `ref mut` patterns. Borrow the scrutinee and let bindings receive references.
|
|
10
|
+
|
|
11
|
+
## Shared references
|
|
12
|
+
|
|
13
|
+
```rust
|
|
14
|
+
// Before
|
|
15
|
+
match value {
|
|
16
|
+
Some(ref item) => println!("{item}"),
|
|
17
|
+
None => {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// After
|
|
21
|
+
match &value {
|
|
22
|
+
Some(item) => println!("{item}"),
|
|
23
|
+
None => {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if let Some(item) = &value {
|
|
27
|
+
println!("{item}");
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Mutable references
|
|
32
|
+
|
|
33
|
+
```rust
|
|
34
|
+
// Before
|
|
35
|
+
match value {
|
|
36
|
+
Some(ref mut item) => *item += 1,
|
|
37
|
+
None => {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// After
|
|
41
|
+
match &mut value {
|
|
42
|
+
Some(item) => *item += 1,
|
|
43
|
+
None => {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if let Some(item) = &mut value {
|
|
47
|
+
*item += 1;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Result
|
|
52
|
+
|
|
53
|
+
```rust
|
|
54
|
+
// Before
|
|
55
|
+
match result {
|
|
56
|
+
Ok(ref data) => println!("{data}"),
|
|
57
|
+
Err(ref err) => eprintln!("{err}"),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// After
|
|
61
|
+
match &result {
|
|
62
|
+
Ok(data) => println!("{data}"),
|
|
63
|
+
Err(err) => eprintln!("{err}"),
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Modern Rust rarely needs `ref` in patterns. Borrow the value being matched.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use parking_lot instead of std::sync for Mutex/RwLock
|
|
3
|
+
condition:
|
|
4
|
+
- "\\.lock\\(\\)\\.unwrap\\(\\)"
|
|
5
|
+
- "\\.read\\(\\)\\.unwrap\\(\\)"
|
|
6
|
+
- "\\.write\\(\\)\\.unwrap\\(\\)"
|
|
7
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Use `parking_lot::{Mutex, RwLock}` instead of `std::sync::{Mutex, RwLock}` when code immediately unwraps lock results.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
- `lock()`, `read()`, and `write()` return guards directly.
|
|
15
|
+
- No poisoning error path to unwrap.
|
|
16
|
+
- Guards are smaller and faster in common contention cases.
|
|
17
|
+
- The call site shows locking, not error handling boilerplate.
|
|
18
|
+
|
|
19
|
+
## Migration
|
|
20
|
+
|
|
21
|
+
```rust
|
|
22
|
+
// Before
|
|
23
|
+
use std::sync::Mutex;
|
|
24
|
+
let data = Mutex::new(Vec::new());
|
|
25
|
+
let guard = data.lock().unwrap();
|
|
26
|
+
|
|
27
|
+
// After
|
|
28
|
+
use parking_lot::Mutex;
|
|
29
|
+
let data = Mutex::new(Vec::new());
|
|
30
|
+
let guard = data.lock();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Equivalents
|
|
34
|
+
|
|
35
|
+
| std::sync | parking_lot |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `Mutex<T>` | `Mutex<T>` |
|
|
38
|
+
| `RwLock<T>` | `RwLock<T>` |
|
|
39
|
+
| `Condvar` | `Condvar` |
|
|
40
|
+
| `Once` | `Once` |
|
|
41
|
+
|
|
42
|
+
## Keep async locks async
|
|
43
|
+
|
|
44
|
+
Use `tokio::sync::Mutex` / `tokio::sync::RwLock` when a guard is held across `.await` or the lock belongs to async coordination.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Result type aliases must include a defaulted error type parameter
|
|
3
|
+
condition: "type\\s+Result<[A-Za-z_]\\w*>\\s*="
|
|
4
|
+
scope: "tool:edit(*.rs), tool:write(*.rs)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`Result` aliases must expose the error type as a defaulted parameter.
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
pub type Result<T, E = anyhow::Error> = std::result::Result<T, E>;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Never write:
|
|
14
|
+
|
|
15
|
+
```rust
|
|
16
|
+
type Result<T> = std::result::Result<T, anyhow::Error>;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The default keeps common call sites short while preserving escape hatches for precise errors.
|