@oh-my-pi/pi-coding-agent 15.5.15 → 15.7.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/CHANGELOG.md +81 -0
- package/dist/types/capability/rule-buckets.d.ts +30 -0
- package/dist/types/capability/rule.d.ts +7 -0
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/completion-gen.d.ts +80 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/tiny-models-cli.d.ts +9 -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/commands/tiny-models.d.ts +22 -0
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/config/model-id-affixes.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +402 -17
- 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/helpers.d.ts +1 -1
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- 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/js/shared/rewrite-imports.d.ts +16 -1
- 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/internal-urls/agent-protocol.d.ts +2 -1
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -1
- package/dist/types/internal-urls/local-protocol.d.ts +2 -1
- package/dist/types/internal-urls/memory-protocol.d.ts +2 -1
- package/dist/types/internal-urls/omp-protocol.d.ts +2 -1
- package/dist/types/internal-urls/router.d.ts +8 -1
- package/dist/types/internal-urls/rule-protocol.d.ts +2 -1
- package/dist/types/internal-urls/skill-protocol.d.ts +2 -1
- package/dist/types/internal-urls/types.d.ts +26 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/resolve.d.ts +2 -1
- package/dist/types/memory-backend/types.d.ts +7 -1
- package/dist/types/mnemosyne/backend.d.ts +4 -0
- package/dist/types/mnemosyne/config.d.ts +29 -0
- package/dist/types/mnemosyne/index.d.ts +3 -0
- package/dist/types/mnemosyne/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -3
- package/dist/types/modes/components/hook-selector.d.ts +27 -0
- package/dist/types/modes/components/index.d.ts +2 -0
- package/dist/types/modes/components/segment-track.d.ts +22 -0
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +6 -0
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +11 -0
- package/dist/types/modes/components/welcome.d.ts +22 -0
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +4 -1
- package/dist/types/modes/gradient-highlight.d.ts +23 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -4
- package/dist/types/modes/internal-url-autocomplete.d.ts +43 -0
- package/dist/types/modes/orchestrate.d.ts +10 -0
- 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/defaults/index.d.ts +8406 -8406
- 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/modes/ultrathink.d.ts +3 -3
- package/dist/types/modes/utils/keybinding-matchers.d.ts +5 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +33 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/render.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 +185 -0
- package/dist/types/tiny/text.d.ts +19 -0
- package/dist/types/tiny/title-client.d.ts +32 -0
- package/dist/types/tiny/title-protocol.d.ts +74 -0
- package/dist/types/tiny/worker.d.ts +2 -0
- package/dist/types/tools/bash.d.ts +3 -2
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/index.d.ts +7 -4
- package/dist/types/tools/memory-edit.d.ts +40 -0
- package/dist/types/tools/{hindsight-recall.d.ts → memory-recall.d.ts} +6 -6
- package/dist/types/tools/{hindsight-reflect.d.ts → memory-reflect.d.ts} +6 -6
- package/dist/types/tools/memory-render.d.ts +60 -0
- package/dist/types/tools/{hindsight-retain.d.ts → memory-retain.d.ts} +6 -6
- package/dist/types/tools/todo-write.d.ts +8 -0
- package/dist/types/tools/tool-result.d.ts +2 -0
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +17 -0
- package/dist/types/utils/title-generator.d.ts +3 -0
- package/package.json +18 -14
- package/scripts/build-binary.ts +1 -0
- 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/tiny-models-cli.ts +127 -0
- package/src/cli-commands.ts +3 -0
- package/src/cli.ts +9 -15
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/setup.ts +29 -4
- package/src/commands/tiny-models.ts +36 -0
- package/src/config/model-equivalence.ts +43 -2
- package/src/config/model-id-affixes.ts +64 -0
- package/src/config/model-registry.ts +84 -10
- package/src/config/settings-schema.ts +275 -15
- 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 +9 -8
- package/src/edit/hashline/execute.ts +2 -1
- package/src/edit/hashline/index.ts +1 -0
- package/src/eval/__tests__/shared-executors.test.ts +36 -0
- package/src/eval/js/shared/local-module-loader.ts +13 -1
- package/src/eval/js/shared/rewrite-imports.ts +31 -26
- 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/agent-protocol.ts +18 -1
- package/src/internal-urls/artifact-protocol.ts +19 -1
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/internal-urls/local-protocol.ts +14 -1
- package/src/internal-urls/memory-protocol.ts +6 -1
- package/src/internal-urls/omp-protocol.ts +5 -1
- package/src/internal-urls/router.ts +20 -1
- package/src/internal-urls/rule-protocol.ts +8 -1
- package/src/internal-urls/skill-protocol.ts +8 -1
- package/src/internal-urls/types.ts +27 -0
- package/src/lsp/render.ts +1 -1
- package/src/main.ts +18 -1
- package/src/mcp/oauth-flow.ts +2 -2
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/resolve.ts +4 -1
- package/src/memory-backend/types.ts +8 -1
- package/src/mnemosyne/backend.ts +374 -0
- package/src/mnemosyne/config.ts +160 -0
- package/src/mnemosyne/index.ts +3 -0
- package/src/mnemosyne/state.ts +548 -0
- package/src/modes/acp/acp-agent.ts +11 -6
- package/src/modes/components/agent-dashboard.ts +4 -4
- package/src/modes/components/custom-editor.ts +3 -2
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/extensions/extension-list.ts +3 -2
- package/src/modes/components/footer.ts +5 -6
- package/src/modes/components/history-search.ts +3 -3
- package/src/modes/components/hook-selector.ts +92 -8
- package/src/modes/components/index.ts +2 -0
- package/src/modes/components/mcp-add-wizard.ts +3 -3
- package/src/modes/components/model-selector.ts +5 -4
- package/src/modes/components/oauth-selector.ts +3 -3
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/session-observer-overlay.ts +19 -13
- package/src/modes/components/session-selector.ts +3 -3
- package/src/modes/components/settings-defs.ts +7 -0
- package/src/modes/components/status-line/context-thresholds.ts +11 -0
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +13 -0
- package/src/modes/components/tool-execution.ts +72 -4
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +3 -3
- package/src/modes/components/welcome.ts +102 -43
- package/src/modes/controllers/command-controller.ts +16 -1
- package/src/modes/controllers/extension-ui-controller.ts +3 -1
- package/src/modes/controllers/input-controller.ts +69 -21
- package/src/modes/gradient-highlight.ts +70 -0
- package/src/modes/interactive-mode.ts +75 -114
- package/src/modes/internal-url-autocomplete.ts +143 -0
- package/src/modes/orchestrate.ts +36 -0
- package/src/modes/prompt-action-autocomplete.ts +12 -0
- 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/modes/ultrathink.ts +9 -53
- package/src/modes/utils/keybinding-matchers.ts +11 -0
- package/src/prompts/system/memory-consolidation-system.md +8 -0
- package/src/prompts/system/memory-extraction-system.md +26 -0
- package/src/prompts/{commands/orchestrate.md → system/orchestrate-notice.md} +6 -17
- package/src/prompts/system/system-prompt.md +2 -0
- package/src/prompts/system/tiny-title-system.md +8 -0
- package/src/prompts/tools/memory-edit.md +8 -0
- package/src/prompts/tools/read.md +4 -0
- package/src/prompts/tools/task.md +4 -7
- package/src/sdk.ts +13 -21
- package/src/session/agent-session.ts +128 -44
- package/src/slash-commands/builtin-registry.ts +18 -1
- package/src/system-prompt.ts +4 -0
- package/src/task/commands.ts +1 -5
- package/src/task/executor.ts +8 -0
- package/src/task/index.ts +2 -0
- package/src/task/render.ts +69 -26
- package/src/tiny/device.ts +117 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +218 -0
- package/src/tiny/text.ts +54 -0
- package/src/tiny/title-client.ts +395 -0
- package/src/tiny/title-protocol.ts +51 -0
- package/src/tiny/worker.ts +587 -0
- package/src/tools/bash.ts +74 -29
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/eval.ts +9 -4
- package/src/tools/index.ts +17 -22
- package/src/tools/memory-edit.ts +59 -0
- package/src/tools/memory-recall.ts +100 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +185 -0
- package/src/tools/memory-retain.ts +91 -0
- package/src/tools/read.ts +1 -0
- package/src/tools/renderers.ts +4 -2
- package/src/tools/todo-write.ts +128 -29
- package/src/tools/tool-result.ts +8 -0
- package/src/tui/code-cell.ts +6 -1
- package/src/tui/output-block.ts +199 -38
- package/src/utils/title-generator.ts +115 -13
- 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/hindsight-recall.ts +0 -69
- package/src/tools/hindsight-reflect.ts +0 -58
- package/src/tools/hindsight-retain.ts +0 -57
- 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,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.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use bare `catch {` when the error binding is unused
|
|
3
|
+
condition: "catch \\(_"
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use bare `catch {}` when the caught value is unused. An underscore-prefixed binding adds noise and still allocates a local name.
|
|
8
|
+
|
|
9
|
+
## Replace
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// Bad
|
|
13
|
+
try {
|
|
14
|
+
await loadConfig();
|
|
15
|
+
} catch (_err) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Good
|
|
20
|
+
try {
|
|
21
|
+
await loadConfig();
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Keep a real name when used
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
try {
|
|
31
|
+
await saveConfig();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
logger.error("save failed", { err });
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Unused error? Bare `catch`. Used error? Name it for what it carries.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Use `import type`, not `import('pkg').Type` in type positions"
|
|
3
|
+
condition: "import\\("
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use top-level `import type` declarations for type-only dependencies. NEVER write `import("pkg").Type` inside source annotations.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
- Top-level imports expose dependencies immediately.
|
|
12
|
+
- Import sorting and deduplication can manage them.
|
|
13
|
+
- Signatures stay readable and reviewable.
|
|
14
|
+
- Re-exports do not inherit noisy inline paths.
|
|
15
|
+
|
|
16
|
+
## Avoid
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Bad — inline imports hide dependencies in signatures.
|
|
20
|
+
function run(client: import("some-sdk").Client, input: import("zod/v4").infer<Schema>): Promise<Output>;
|
|
21
|
+
|
|
22
|
+
// Bad — annotations become path dumps.
|
|
23
|
+
const options: import("some-sdk/config").ClientOptions = { ... };
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Use
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import type { Client } from "some-sdk";
|
|
30
|
+
import type { ClientOptions } from "some-sdk/config";
|
|
31
|
+
import type { infer as Infer } from "zod/v4";
|
|
32
|
+
|
|
33
|
+
function run(client: Client, input: Infer<Schema>): Promise<Output>;
|
|
34
|
+
const options: ClientOptions = { ... };
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Exceptions
|
|
38
|
+
|
|
39
|
+
- Ambient `.d.ts` globals that must not become modules.
|
|
40
|
+
- Generated files whose generator owns import management.
|
|
41
|
+
|
|
42
|
+
In normal `.ts` / `.tsx` source, use `import type`.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Never use `any` in TypeScript annotations or assertions — use `unknown`, generics, or the actual type"
|
|
3
|
+
condition: ": any|as any"
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Never use `: any` or `as any`. They disable type checking exactly where the boundary needs precision.
|
|
8
|
+
|
|
9
|
+
## Use instead
|
|
10
|
+
|
|
11
|
+
- `unknown` for unvalidated input.
|
|
12
|
+
- A domain type when the shape is known.
|
|
13
|
+
- A generic when the caller supplies the shape.
|
|
14
|
+
- A type guard when runtime checks establish shape.
|
|
15
|
+
- `satisfies` for object literals that must match a contract.
|
|
16
|
+
|
|
17
|
+
## Parameters and returns
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Bad
|
|
21
|
+
function readId(value: any): any {
|
|
22
|
+
return value.id;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Good — validate unknown input.
|
|
26
|
+
function readId(value: unknown): string | undefined {
|
|
27
|
+
if (value && typeof value === "object" && "id" in value) {
|
|
28
|
+
const candidate = (value as { id: unknown }).id;
|
|
29
|
+
return typeof candidate === "string" ? candidate : undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Assertions
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Bad
|
|
38
|
+
const root = document.getElementById("root") as any;
|
|
39
|
+
root.innerText = "ready";
|
|
40
|
+
|
|
41
|
+
// Good
|
|
42
|
+
const root = document.getElementById("root") as HTMLElement | null;
|
|
43
|
+
root?.innerText = "ready";
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Object literals
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Bad
|
|
50
|
+
const config = { port: 3000 } as any as ServerConfig;
|
|
51
|
+
|
|
52
|
+
// Good
|
|
53
|
+
const config = { port: 3000 } satisfies ServerConfig;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If a library boundary truly requires an unchecked cast, use `as unknown as T` with a short reason. Never leave a bare `any`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Do not use `await import()` — use static imports unless dynamic loading is unavoidable"
|
|
3
|
+
condition: "await import\\("
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use static imports for modules known at author time. Reach for `await import()` only when the module specifier is genuinely runtime-selected.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
- Static imports fail during build, not under load.
|
|
12
|
+
- Bundlers, type checkers, and tree shakers see them.
|
|
13
|
+
- The dependency graph remains reviewable.
|
|
14
|
+
- Consumers keep precise module types without casts.
|
|
15
|
+
|
|
16
|
+
## Avoid
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Bad — the module path is a literal.
|
|
20
|
+
const { createClient } = await import("some-sdk");
|
|
21
|
+
|
|
22
|
+
// Bad — dynamic import followed by a shape assertion.
|
|
23
|
+
const mod = (await import("./known-module")) as { run?: unknown };
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Use
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { createClient } from "some-sdk";
|
|
30
|
+
import { run } from "./known-module";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Exceptions
|
|
34
|
+
|
|
35
|
+
- Plugin loading from a runtime registry.
|
|
36
|
+
- Platform-specific modules that do not exist everywhere.
|
|
37
|
+
- Test cases that intentionally exercise module loading boundaries.
|
|
38
|
+
|
|
39
|
+
Exception? Add a short comment naming why static import cannot work.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Do not use `ReturnType<typeof fn>` — name the type explicitly"
|
|
3
|
+
condition: "ReturnType<"
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Do not publish contracts through `ReturnType<typeof fn>`. Name the type at the module that owns the value and import that name at consumers.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
- Named types document the contract directly.
|
|
12
|
+
- Consumers stop coupling to implementation helpers.
|
|
13
|
+
- JSDoc and changelog notes attach to the exported type.
|
|
14
|
+
- Type errors point at the intended API boundary.
|
|
15
|
+
|
|
16
|
+
## Avoid
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Bad — opaque and coupled to implementation names.
|
|
20
|
+
type Config = Awaited<ReturnType<typeof loadConfig>>;
|
|
21
|
+
type Message = ReturnType<typeof buildMessage>["message"];
|
|
22
|
+
let service: ReturnType<typeof createService> | undefined;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Use
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// In the module that owns the function:
|
|
29
|
+
export interface LoadedConfig {
|
|
30
|
+
path: string;
|
|
31
|
+
values: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function loadConfig(path: string): Promise<LoadedConfig> { ... }
|
|
35
|
+
|
|
36
|
+
// At the consumer:
|
|
37
|
+
import type { LoadedConfig } from "./config";
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Exceptions
|
|
41
|
+
|
|
42
|
+
- Timer handles: `ReturnType<typeof setTimeout>` / `setInterval`.
|
|
43
|
+
- Generic type utilities where the function is a type parameter.
|
|
44
|
+
|
|
45
|
+
Concrete function? Export a concrete type.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Do not extract 1-2 line functions that only wrap an expression — inline them"
|
|
3
|
+
condition: "\\{\\s*return [^;{}\\n]+;?\\s*\\}|\\b(?:const|let|var)\\s+[\\w$]+\\s*=\\s*(\\([^)]*\\)|[a-zA-Z_$][\\w$]*)\\s*=>\\s*[^{\\n]+$"
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
interruptMode: never
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Do not extract a function whose whole body is one expression or one `return`. Inline it unless the name creates a durable contract.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
- One-line wrappers hide no real behavior.
|
|
13
|
+
- Readers must jump to verify trivial code.
|
|
14
|
+
- The signature freezes a shape too early.
|
|
15
|
+
- Search and type flow work better with inline expressions.
|
|
16
|
+
|
|
17
|
+
## Avoid
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Bad — pure rename, no behavior added.
|
|
21
|
+
function isEmpty(value: string): boolean {
|
|
22
|
+
return value.length === 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getDisplayName = (user: User) => user.profile.displayName;
|
|
26
|
+
|
|
27
|
+
function double(value: number) {
|
|
28
|
+
return value * 2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isEmpty(name)) { ... }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Use
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
if (name.length === 0) { ... }
|
|
38
|
+
const displayName = user.profile.displayName;
|
|
39
|
+
const doubled = value * 2;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Allowed tiny functions
|
|
43
|
+
|
|
44
|
+
- Three or more call sites need lockstep behavior.
|
|
45
|
+
- Exported name represents a stable domain concept.
|
|
46
|
+
- Callback identity matters.
|
|
47
|
+
- Type guard preserves narrowing.
|
|
48
|
+
- Public API, test seam, or DI boundary needs indirection.
|
|
49
|
+
|
|
50
|
+
If none apply, inline it.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use Promise.withResolvers() instead of new Promise() constructor
|
|
3
|
+
condition: "new Promise\\("
|
|
4
|
+
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use `Promise.withResolvers()` instead of `new Promise((resolve, reject) => ...)`. It keeps control flow linear and exposes typed resolver functions without callback nesting.
|
|
8
|
+
|
|
9
|
+
## Basic operation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// Bad
|
|
13
|
+
function delay(ms: number): Promise<void> {
|
|
14
|
+
return new Promise(resolve => {
|
|
15
|
+
setTimeout(resolve, ms);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Good
|
|
20
|
+
function delay(ms: number): Promise<void> {
|
|
21
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
22
|
+
setTimeout(resolve, ms);
|
|
23
|
+
return promise;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Event-based completion
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Bad
|
|
31
|
+
function waitForEvent(emitter: EventEmitter, event: string): Promise<unknown> {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
emitter.once(event, resolve);
|
|
34
|
+
emitter.once("error", reject);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Good
|
|
39
|
+
function waitForEvent(emitter: EventEmitter, event: string): Promise<unknown> {
|
|
40
|
+
const { promise, resolve, reject } = Promise.withResolvers<unknown>();
|
|
41
|
+
emitter.once(event, resolve);
|
|
42
|
+
emitter.once("error", reject);
|
|
43
|
+
return promise;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Stored resolver
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
class Gate {
|
|
51
|
+
#promise: Promise<void>;
|
|
52
|
+
#resolve: () => void;
|
|
53
|
+
|
|
54
|
+
constructor() {
|
|
55
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
56
|
+
this.#promise = promise;
|
|
57
|
+
this.#resolve = resolve;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
open(): void { this.#resolve(); }
|
|
61
|
+
wait(): Promise<void> { return this.#promise; }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use the constructor only when an API specifically requires the executor form.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Prefer Record<K, V> for small static literals; use Set/Map for anything dynamic
|
|
3
|
+
condition: "\\bnew\\s+(Set|Map)\\b"
|
|
4
|
+
scope: "tool:edit(**/*.{ts,tsx}), tool:write(**/*.{ts,tsx})"
|
|
5
|
+
interruptMode: never
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use `Record<K, V>` / `Record<K, true>` for small, static string-keyed lookup tables.
|
|
9
|
+
|
|
10
|
+
Use `Set` / `Map` when keys are dynamic, non-string, inserted or deleted at runtime, or when code needs `.size`, `.clear()`, stable insertion order, or iterator APIs.
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// Static literal → Record
|
|
14
|
+
const LABEL_BY_KIND: Record<string, string> = {
|
|
15
|
+
text: "Text",
|
|
16
|
+
json: "JSON",
|
|
17
|
+
binary: "Binary",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Dynamic membership → Set
|
|
21
|
+
const seen = new Set<string>();
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
if (seen.has(item.id)) continue;
|
|
24
|
+
seen.add(item.id);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Small fixed table? `Record`. Runtime collection? `Set` / `Map`.
|
package/src/discovery/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter-backed {@link BlockResolver} for the hashline `replace block N:`
|
|
3
|
+
* operator. Bridges the pure hashline seam to the native `blockRangeAt`
|
|
4
|
+
* primitive in `@oh-my-pi/pi-natives`, which infers the language from the file
|
|
5
|
+
* path and returns the 1-indexed line span of the syntactic block beginning on
|
|
6
|
+
* the requested line (or `null` when none can be resolved).
|
|
7
|
+
*/
|
|
8
|
+
import type { BlockResolver } from "@oh-my-pi/hashline";
|
|
9
|
+
import { blockRangeAt } from "@oh-my-pi/pi-natives";
|
|
10
|
+
|
|
11
|
+
export const nativeBlockResolver: BlockResolver = ({ path, text, line }) => {
|
|
12
|
+
const range = blockRangeAt({ code: text, path, line });
|
|
13
|
+
return range ? { start: range.startLine, end: range.endLine } : null;
|
|
14
|
+
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import {
|
|
13
13
|
Patch as HashlinePatch,
|
|
14
|
+
missingSnapshotTagMessage,
|
|
14
15
|
normalizeToLF,
|
|
15
16
|
type Patch,
|
|
16
17
|
type PatchSection,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
import { resolveToCwd } from "../../tools/path-utils";
|
|
22
23
|
import { generateDiffString } from "../diff";
|
|
23
24
|
import { readEditFileText } from "../read-file";
|
|
25
|
+
import { nativeBlockResolver } from "./block-resolver";
|
|
24
26
|
|
|
25
27
|
export interface HashlineDiffOptions {
|
|
26
28
|
/**
|
|
@@ -40,10 +42,6 @@ async function readSectionText(absolutePath: string, sectionPath: string): Promi
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
function hasAnchorScoped(section: PatchSection): boolean {
|
|
44
|
-
return section.hasAnchorScopedEdit;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
45
|
function snapshotMatchesCurrent(snapshot: Snapshot, currentText: string): boolean {
|
|
48
46
|
return snapshot.text === currentText;
|
|
49
47
|
}
|
|
@@ -54,9 +52,10 @@ function validateSectionHash(
|
|
|
54
52
|
snapshots: SnapshotStore,
|
|
55
53
|
): string | null {
|
|
56
54
|
if (section.fileHash === undefined) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
// The snapshot tag is mandatory on every section — head/tail inserts
|
|
56
|
+
// included — to keep this preview path in lockstep with the apply path
|
|
57
|
+
// (`Patcher.prepare`), which rejects tagless sections unconditionally.
|
|
58
|
+
return missingSnapshotTagMessage(section.path);
|
|
60
59
|
}
|
|
61
60
|
const snapshot = snapshots.byHash(absolutePath, section.fileHash);
|
|
62
61
|
if (snapshot && snapshotMatchesCurrent(snapshot, text)) return null;
|
|
@@ -76,7 +75,9 @@ export async function computeHashlineSectionDiff(
|
|
|
76
75
|
const normalized = normalizeToLF(content);
|
|
77
76
|
const hashError = validateSectionHash(section, absolutePath, normalized, snapshots);
|
|
78
77
|
if (hashError) return { error: hashError };
|
|
79
|
-
const result = options.streaming
|
|
78
|
+
const result = options.streaming
|
|
79
|
+
? section.applyPartialTo(normalized, nativeBlockResolver)
|
|
80
|
+
: section.applyTo(normalized, nativeBlockResolver);
|
|
80
81
|
if (normalized === result.text) return { error: `No changes would be made to ${section.path}.` };
|
|
81
82
|
return generateDiffString(normalized, result.text);
|
|
82
83
|
} catch (err) {
|
|
@@ -25,6 +25,7 @@ import { outputMeta } from "../../tools/output-meta";
|
|
|
25
25
|
import { generateDiffString } from "../diff";
|
|
26
26
|
import { getFileSnapshotStore } from "../file-snapshot-store";
|
|
27
27
|
import type { EditToolDetails, EditToolPerFileResult, LspBatchRequest } from "../renderer";
|
|
28
|
+
import { nativeBlockResolver } from "./block-resolver";
|
|
28
29
|
import { HashlineFilesystem } from "./filesystem";
|
|
29
30
|
import { type HashlineParams, hashlineEditParamsSchema } from "./params";
|
|
30
31
|
|
|
@@ -133,7 +134,7 @@ export async function executeHashlineSingle(
|
|
|
133
134
|
batchRequest: options.batchRequest,
|
|
134
135
|
});
|
|
135
136
|
const snapshots = getFileSnapshotStore(options.session);
|
|
136
|
-
const patcher = new Patcher({ fs, snapshots });
|
|
137
|
+
const patcher = new Patcher({ fs, snapshots, blockResolver: nativeBlockResolver });
|
|
137
138
|
|
|
138
139
|
// Single-section fast path: prepare, commit, render.
|
|
139
140
|
if (patch.sections.length === 1) {
|
|
@@ -492,6 +492,42 @@ display({"label": "A"})`,
|
|
|
492
492
|
expect(reloaded.output.trim()).toBe("2");
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
+
it("loads TypeScript type-only imports in cells and local modules", async () => {
|
|
496
|
+
using tempDir = TempDir.createSync("@omp-eval-js-type-imports-");
|
|
497
|
+
const sessionFile = path.join(tempDir.path(), "session.jsonl");
|
|
498
|
+
const sessionId = `js-type-imports:${crypto.randomUUID()}`;
|
|
499
|
+
const session = createToolSession(tempDir.path(), sessionFile);
|
|
500
|
+
const typesPath = path.join(tempDir.path(), "types.ts");
|
|
501
|
+
const valuesPath = path.join(tempDir.path(), "values.ts");
|
|
502
|
+
const entryPath = path.join(tempDir.path(), "entry.ts");
|
|
503
|
+
const typesSpec = JSON.stringify(typesPath);
|
|
504
|
+
const entrySpec = JSON.stringify(entryPath);
|
|
505
|
+
await Bun.write(typesPath, "export interface TypeOnly { value: number }\n");
|
|
506
|
+
await Bun.write(valuesPath, "export interface InlineOnly { value: number }\nexport const imported = 41;\n");
|
|
507
|
+
await Bun.write(
|
|
508
|
+
entryPath,
|
|
509
|
+
[
|
|
510
|
+
'import type { TypeOnly } from "./types.ts";',
|
|
511
|
+
'import { type InlineOnly, imported } from "./values.ts";',
|
|
512
|
+
"export const typeOnly = 1;",
|
|
513
|
+
"export const inlineType = imported;",
|
|
514
|
+
"",
|
|
515
|
+
].join("\n"),
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
const result = await executeJs(
|
|
519
|
+
`import type { TypeOnly } from ${typesSpec};\nconst mod = await import(${entrySpec});\nreturn mod.typeOnly + mod.inlineType;`,
|
|
520
|
+
{
|
|
521
|
+
sessionId,
|
|
522
|
+
session,
|
|
523
|
+
sessionFile,
|
|
524
|
+
},
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
expect(result.exitCode).toBe(0);
|
|
528
|
+
expect(result.output.trim()).toBe("42");
|
|
529
|
+
});
|
|
530
|
+
|
|
495
531
|
it("refreshes the Python tool proxy when bridge env appears after kernel warm-up", async () => {
|
|
496
532
|
using tempDir = TempDir.createSync("@omp-eval-py-tool-proxy-");
|
|
497
533
|
const sessionFile = path.join(tempDir.path(), "session.jsonl");
|
|
@@ -88,7 +88,10 @@ export class LocalModuleLoader {
|
|
|
88
88
|
|
|
89
89
|
async #buildLocalModule(modulePath: string): Promise<LocalModuleEntry> {
|
|
90
90
|
const rawSource = fs.readFileSync(modulePath, "utf8");
|
|
91
|
-
const stripped = stripTypeScriptSyntax(rawSource
|
|
91
|
+
const stripped = stripTypeScriptSyntax(rawSource, {
|
|
92
|
+
force: isTypeScriptModulePath(modulePath),
|
|
93
|
+
loader: stripLoaderForPath(modulePath),
|
|
94
|
+
});
|
|
92
95
|
const moduleDir = path.dirname(modulePath);
|
|
93
96
|
const localDeps = new Set<string>();
|
|
94
97
|
for (const specifier of collectModuleSourceSpecifiers(stripped)) {
|
|
@@ -251,6 +254,15 @@ function isLocalPathSpecifier(source: string): boolean {
|
|
|
251
254
|
);
|
|
252
255
|
}
|
|
253
256
|
|
|
257
|
+
function isTypeScriptModulePath(modulePath: string): boolean {
|
|
258
|
+
const ext = path.extname(modulePath);
|
|
259
|
+
return ext === ".ts" || ext === ".tsx" || ext === ".mts";
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function stripLoaderForPath(modulePath: string): "ts" | "tsx" {
|
|
263
|
+
return path.extname(modulePath) === ".tsx" ? "tsx" : "ts";
|
|
264
|
+
}
|
|
265
|
+
|
|
254
266
|
function isManagedLocalModulePath(target: string): boolean {
|
|
255
267
|
return (
|
|
256
268
|
path.isAbsolute(target) &&
|
|
@@ -75,6 +75,7 @@ function parseProgram(code: string): { program: { body: ReadonlyArray<BabelProgr
|
|
|
75
75
|
allowSuperOutsideMethod: true,
|
|
76
76
|
allowUndeclaredExports: true,
|
|
77
77
|
errorRecovery: true,
|
|
78
|
+
plugins: ["typescript"],
|
|
78
79
|
}) as unknown as { program: { body: ReadonlyArray<BabelProgramNode> } };
|
|
79
80
|
} catch {
|
|
80
81
|
return null;
|
|
@@ -178,8 +179,7 @@ export function rewriteImports(code: string): string {
|
|
|
178
179
|
if (node.type !== "CallExpression") return;
|
|
179
180
|
const call = node as unknown as { callee?: { type?: string; start?: number; end?: number } };
|
|
180
181
|
const callee = call.callee;
|
|
181
|
-
if (
|
|
182
|
-
return;
|
|
182
|
+
if (callee?.type !== "Import" || typeof callee.start !== "number" || typeof callee.end !== "number") return;
|
|
183
183
|
edits.push({ start: callee.start, end: callee.end, text: "__omp_import__" });
|
|
184
184
|
});
|
|
185
185
|
|
|
@@ -252,12 +252,7 @@ export function rewriteDynamicImports(code: string, callee = "__omp_import__"):
|
|
|
252
252
|
if (node.type !== "CallExpression") return;
|
|
253
253
|
const call = node as unknown as { callee?: { type?: string; start?: number; end?: number } };
|
|
254
254
|
const callCallee = call.callee;
|
|
255
|
-
if (
|
|
256
|
-
!callCallee ||
|
|
257
|
-
callCallee.type !== "Import" ||
|
|
258
|
-
typeof callCallee.start !== "number" ||
|
|
259
|
-
typeof callCallee.end !== "number"
|
|
260
|
-
) {
|
|
255
|
+
if (callCallee?.type !== "Import" || typeof callCallee.start !== "number" || typeof callCallee.end !== "number") {
|
|
261
256
|
return;
|
|
262
257
|
}
|
|
263
258
|
edits.push({ start: callCallee.start, end: callCallee.end, text: callee });
|
|
@@ -453,38 +448,48 @@ function requiresAsyncWrapper(code: string): boolean {
|
|
|
453
448
|
}
|
|
454
449
|
|
|
455
450
|
/**
|
|
456
|
-
* Strip TypeScript syntax (type annotations, `interface`, `as`,
|
|
457
|
-
* call expressions, etc.) before the import/lexical rewriters parse
|
|
458
|
-
* native transpiler
|
|
459
|
-
*
|
|
451
|
+
* Strip TypeScript syntax (type annotations, type-only imports/exports, `interface`, `as`,
|
|
452
|
+
* `satisfies`, generics in call expressions, etc.) before the import/lexical rewriters parse
|
|
453
|
+
* the code. Bun's native transpiler preserves `import`/`export` declarations, so downstream
|
|
454
|
+
* Babel rewrites still control module resolution.
|
|
460
455
|
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
456
|
+
* Eval cells use a cheap "looks like TS" heuristic to avoid transpiling ordinary JS. Known
|
|
457
|
+
* TypeScript modules pass `force` because a file can contain TS-only module syntax such as
|
|
458
|
+
* `import type` without any value-level type annotations.
|
|
464
459
|
*/
|
|
465
|
-
|
|
466
|
-
|
|
460
|
+
type TypeScriptStripLoader = "ts" | "tsx";
|
|
461
|
+
|
|
462
|
+
const TS_TRANSPILER = new Bun.Transpiler({ loader: "ts" });
|
|
463
|
+
const TSX_TRANSPILER = new Bun.Transpiler({ loader: "tsx" });
|
|
464
|
+
|
|
465
|
+
function stripTypeScript(code: string, options: { force?: boolean; loader?: TypeScriptStripLoader } = {}): string {
|
|
466
|
+
if (!options.force && !LOOKS_LIKE_TS.test(code)) return code;
|
|
467
467
|
try {
|
|
468
|
-
|
|
468
|
+
const transpiler = options.loader === "tsx" ? TSX_TRANSPILER : TS_TRANSPILER;
|
|
469
|
+
return transpiler.transformSync(code);
|
|
469
470
|
} catch {
|
|
470
471
|
// Transpiler failed (e.g. unrecoverable syntax). Hand the original source back so the
|
|
471
472
|
// downstream rewriter / VM surfaces the real error to the user.
|
|
472
473
|
return code;
|
|
473
474
|
}
|
|
474
475
|
}
|
|
475
|
-
export function stripTypeScriptSyntax(
|
|
476
|
-
|
|
476
|
+
export function stripTypeScriptSyntax(
|
|
477
|
+
code: string,
|
|
478
|
+
options: { force?: boolean; loader?: TypeScriptStripLoader } = {},
|
|
479
|
+
): string {
|
|
480
|
+
return stripTypeScript(code, options);
|
|
477
481
|
}
|
|
478
482
|
|
|
479
|
-
// Heuristic:
|
|
480
|
-
// won't match because we require a leading word boundary plus a
|
|
483
|
+
// Heuristic: obvious TS-only tokens, including type-only module syntax. Plain JS using `as`
|
|
484
|
+
// only inside strings won't match because we require a leading word boundary plus a
|
|
485
|
+
// colon/keyword neighbor.
|
|
481
486
|
const LOOKS_LIKE_TS =
|
|
482
|
-
/(?:\binterface\s+\w|\btype\s+\w+\s*=|\b(?:as|satisfies)\s+(?:[A-Z]|\bconst\b)|:\s*(?:string|number|boolean|any|unknown|void|never|object|[A-Z]\w*)\b|<\s*[A-Z]\w*\s*[,>])/;
|
|
487
|
+
/(?:\bimport\s+type\b|\bexport\s+type\b|\b(?:import|export)\s*\{[^}\n]*\btype\s+\w|\binterface\s+\w|\btype\s+\w+\s*=|\b(?:as|satisfies)\s+(?:[A-Z]|\bconst\b)|:\s*(?:string|number|boolean|any|unknown|void|never|object|[A-Z]\w*)\b|<\s*[A-Z]\w*\s*[,>])/;
|
|
483
488
|
|
|
484
489
|
export function wrapCode(code: string): { source: string; asyncWrapped: boolean; finalExpressionReturned: boolean } {
|
|
485
|
-
const
|
|
486
|
-
const
|
|
487
|
-
const importsRewritten = rewriteImports(
|
|
490
|
+
const finalExpression = returnFinalExpression(code);
|
|
491
|
+
const stripped = stripTypeScript(finalExpression.source);
|
|
492
|
+
const importsRewritten = rewriteImports(stripped);
|
|
488
493
|
const needsAsyncWrapper = requiresAsyncWrapper(importsRewritten);
|
|
489
494
|
const rewritten = {
|
|
490
495
|
source: demoteTopLevelLexicals(importsRewritten, { publishGlobals: needsAsyncWrapper }),
|