@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
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
import { APP_NAME, adjustHsv, getProjectDir, hsvToRgb, isEnoent, logger, postmortem, prompt } from "@oh-my-pi/pi-utils";
|
|
36
36
|
import chalk from "chalk";
|
|
37
37
|
import { KeybindingsManager } from "../config/keybindings";
|
|
38
|
+
import { MODEL_ROLES, type ModelRole } from "../config/model-registry";
|
|
38
39
|
import { isSettingsInitialized, Settings, settings } from "../config/settings";
|
|
39
40
|
import type {
|
|
40
41
|
ExtensionUIContext,
|
|
@@ -57,7 +58,7 @@ import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" wit
|
|
|
57
58
|
import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compact-instructions.md" with {
|
|
58
59
|
type: "text",
|
|
59
60
|
};
|
|
60
|
-
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
61
|
+
import type { AgentSession, AgentSessionEvent, ResolvedRoleModel } from "../session/agent-session";
|
|
61
62
|
import { HistoryStorage } from "../session/history-storage";
|
|
62
63
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
63
64
|
import { getRecentSessions } from "../session/session-manager";
|
|
@@ -80,7 +81,7 @@ import { DynamicBorder } from "./components/dynamic-border";
|
|
|
80
81
|
import type { EvalExecutionComponent } from "./components/eval-execution";
|
|
81
82
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
82
83
|
import type { HookInputComponent } from "./components/hook-input";
|
|
83
|
-
import type { HookSelectorComponent } from "./components/hook-selector";
|
|
84
|
+
import type { HookSelectorComponent, HookSelectorSlider } from "./components/hook-selector";
|
|
84
85
|
import { StatusLineComponent } from "./components/status-line";
|
|
85
86
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
86
87
|
import { WelcomeComponent, type LspServerInfo as WelcomeLspServerInfo } from "./components/welcome";
|
|
@@ -115,7 +116,14 @@ import {
|
|
|
115
116
|
onThemeChange,
|
|
116
117
|
theme,
|
|
117
118
|
} from "./theme/theme";
|
|
118
|
-
import type {
|
|
119
|
+
import type {
|
|
120
|
+
CompactionQueuedMessage,
|
|
121
|
+
InteractiveModeContext,
|
|
122
|
+
InteractiveModeInitOptions,
|
|
123
|
+
SubmittedUserInput,
|
|
124
|
+
TodoItem,
|
|
125
|
+
TodoPhase,
|
|
126
|
+
} from "./types";
|
|
119
127
|
import { UiHelpers } from "./utils/ui-helpers";
|
|
120
128
|
|
|
121
129
|
const HINT_SHIMMER_PALETTE: ShimmerPalette = {
|
|
@@ -324,8 +332,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
324
332
|
#eventBus?: EventBus;
|
|
325
333
|
#eventBusUnsubscribers: Array<() => void> = [];
|
|
326
334
|
#welcomeComponent?: WelcomeComponent;
|
|
327
|
-
#todoClosingTimeout?: NodeJS.Timeout;
|
|
328
|
-
#todoClosingState: "idle" | "playing" | "done" = "idle";
|
|
329
335
|
|
|
330
336
|
constructor(
|
|
331
337
|
session: AgentSession,
|
|
@@ -369,7 +375,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
369
375
|
this.ui.requestRender(true);
|
|
370
376
|
};
|
|
371
377
|
this.editor.onAutocompleteUpdate = () => {
|
|
372
|
-
this.ui.requestRender();
|
|
378
|
+
this.ui.requestRender(false, { allowUnknownViewportMutation: true });
|
|
373
379
|
};
|
|
374
380
|
this.#syncEditorMaxHeight();
|
|
375
381
|
this.#resizeHandler = () => {
|
|
@@ -432,7 +438,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
432
438
|
this.#observerRegistry = new SessionObserverRegistry();
|
|
433
439
|
}
|
|
434
440
|
|
|
435
|
-
|
|
441
|
+
playWelcomeIntro(): void {
|
|
442
|
+
this.#welcomeComponent?.playIntro(() => this.ui.requestRender());
|
|
443
|
+
}
|
|
444
|
+
async init(options: InteractiveModeInitOptions = {}): Promise<void> {
|
|
436
445
|
if (this.isInitialized) return;
|
|
437
446
|
|
|
438
447
|
this.keybindings = logger.time("InteractiveMode.init:keybindings", () => KeybindingsManager.create());
|
|
@@ -490,7 +499,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
490
499
|
this.ui.addChild(new Spacer(1));
|
|
491
500
|
this.ui.addChild(this.#welcomeComponent);
|
|
492
501
|
this.ui.addChild(new Spacer(1));
|
|
493
|
-
|
|
502
|
+
if (!options.suppressWelcomeIntro) {
|
|
503
|
+
this.playWelcomeIntro();
|
|
504
|
+
}
|
|
494
505
|
|
|
495
506
|
// Add changelog if provided
|
|
496
507
|
if (this.#changelogMarkdown) {
|
|
@@ -1035,28 +1046,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1035
1046
|
#renderTodoList(): void {
|
|
1036
1047
|
this.todoContainer.clear();
|
|
1037
1048
|
const phases = this.todoPhases.filter(phase => phase.tasks.length > 0);
|
|
1038
|
-
if (phases.length === 0)
|
|
1039
|
-
this.#stopTodoClosingAnimation();
|
|
1040
|
-
this.#todoClosingState = "idle";
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
// When every visible task is completed or abandoned, fold the panel
|
|
1045
|
-
// away with a brief celebratory animation (see
|
|
1046
|
-
// #startTodoClosingAnimation). State machine guards against replaying
|
|
1047
|
-
// on every re-render once the animation has finished.
|
|
1048
|
-
const allClosed = phases.every(phase =>
|
|
1049
|
-
phase.tasks.every(t => t.status === "completed" || t.status === "abandoned"),
|
|
1050
|
-
);
|
|
1051
|
-
if (allClosed) {
|
|
1052
|
-
if (this.#todoClosingState === "done") return;
|
|
1053
|
-
if (this.#todoClosingState === "idle") this.#startTodoClosingAnimation(phases);
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
// Any open task here means the close animation is no longer applicable.
|
|
1057
|
-
this.#stopTodoClosingAnimation();
|
|
1058
|
-
this.#todoClosingState = "idle";
|
|
1059
|
-
|
|
1049
|
+
if (phases.length === 0) return;
|
|
1060
1050
|
const indent = " ";
|
|
1061
1051
|
const hook = theme.tree.hook;
|
|
1062
1052
|
const lines = ["", indent + theme.bold(theme.fg("accent", "Todos"))];
|
|
@@ -1098,87 +1088,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1098
1088
|
this.todoContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
1099
1089
|
}
|
|
1100
1090
|
|
|
1101
|
-
/**
|
|
1102
|
-
* Play a short "all done" close animation: a celebratory bright frame,
|
|
1103
|
-
* a brief dim transition, then a row-by-row vertical collapse until the
|
|
1104
|
-
* panel is empty. Triggered from #renderTodoList exactly once per
|
|
1105
|
-
* open-to-all-closed transition; #todoClosingState gates re-entry.
|
|
1106
|
-
*
|
|
1107
|
-
* While playing, the animator owns the panel container; #renderTodoList
|
|
1108
|
-
* returns early. Subsequent renders with state === "done" keep the
|
|
1109
|
-
* panel hidden until a fresh open task flips state back to "idle".
|
|
1110
|
-
*/
|
|
1111
|
-
#startTodoClosingAnimation(phases: TodoPhase[]): void {
|
|
1112
|
-
this.#stopTodoClosingAnimation();
|
|
1113
|
-
this.#todoClosingState = "playing";
|
|
1114
|
-
|
|
1115
|
-
const indent = " ";
|
|
1116
|
-
const hook = theme.tree.hook;
|
|
1117
|
-
const snapshot: string[] = ["", `${indent}Todos ${theme.status.success}`];
|
|
1118
|
-
for (let i = 0; i < phases.length; i++) {
|
|
1119
|
-
const phase = phases[i];
|
|
1120
|
-
snapshot.push(`${indent}${hook} ${formatPhaseDisplayName(phase.name, i + 1)}`);
|
|
1121
|
-
for (let j = 0; j < phase.tasks.length; j++) {
|
|
1122
|
-
const task = phase.tasks[j];
|
|
1123
|
-
const mark = task.status === "abandoned" ? theme.status.aborted : theme.status.success;
|
|
1124
|
-
const prefix = `${indent}${j === 0 ? hook : " "} `;
|
|
1125
|
-
snapshot.push(`${prefix}${mark} ${task.content}`);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
// Frame schedule (tint, drop-from-bottom, hold-ms). Frame 0 holds long
|
|
1130
|
-
// enough for the user to actually read the final checkmarks before the
|
|
1131
|
-
// fade starts; later frames fade and progressively drop rows from the
|
|
1132
|
-
// bottom for the collapse effect. Total runtime ≈ 1.4s.
|
|
1133
|
-
const frames = [
|
|
1134
|
-
{ tint: "success" as const, drop: 0, holdMs: 900 },
|
|
1135
|
-
{ tint: "success" as const, drop: 0, holdMs: 150 },
|
|
1136
|
-
{ tint: "muted" as const, drop: 1, holdMs: 90 },
|
|
1137
|
-
{ tint: "muted" as const, drop: 2, holdMs: 90 },
|
|
1138
|
-
{ tint: "dim" as const, drop: 3, holdMs: 80 },
|
|
1139
|
-
{ tint: "dim" as const, drop: 4, holdMs: 80 },
|
|
1140
|
-
];
|
|
1141
|
-
|
|
1142
|
-
let frameIdx = 0;
|
|
1143
|
-
const tick = (): void => {
|
|
1144
|
-
if (this.#todoClosingState !== "playing") return;
|
|
1145
|
-
if (frameIdx >= frames.length) {
|
|
1146
|
-
this.todoContainer.clear();
|
|
1147
|
-
this.#stopTodoClosingAnimation();
|
|
1148
|
-
this.#todoClosingState = "done";
|
|
1149
|
-
this.ui.requestRender();
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
const { tint, drop, holdMs } = frames[frameIdx];
|
|
1153
|
-
const visibleCount = Math.max(0, snapshot.length - drop);
|
|
1154
|
-
this.todoContainer.clear();
|
|
1155
|
-
if (visibleCount > 0) {
|
|
1156
|
-
const visible = snapshot.slice(0, visibleCount);
|
|
1157
|
-
const painted = visible.map((line, idx) => {
|
|
1158
|
-
if (idx === 1) {
|
|
1159
|
-
// Header row gets a bold flourish on the opening tick.
|
|
1160
|
-
const colored = theme.fg(tint, line);
|
|
1161
|
-
return frameIdx === 0 ? theme.bold(colored) : colored;
|
|
1162
|
-
}
|
|
1163
|
-
return theme.fg(tint, line);
|
|
1164
|
-
});
|
|
1165
|
-
this.todoContainer.addChild(new Text(painted.join("\n"), 1, 0));
|
|
1166
|
-
}
|
|
1167
|
-
this.ui.requestRender();
|
|
1168
|
-
frameIdx++;
|
|
1169
|
-
this.#todoClosingTimeout = setTimeout(tick, holdMs);
|
|
1170
|
-
};
|
|
1171
|
-
|
|
1172
|
-
tick();
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
#stopTodoClosingAnimation(): void {
|
|
1176
|
-
if (this.#todoClosingTimeout) {
|
|
1177
|
-
clearTimeout(this.#todoClosingTimeout);
|
|
1178
|
-
this.#todoClosingTimeout = undefined;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
1091
|
async #loadTodoList(): Promise<void> {
|
|
1183
1092
|
this.todoPhases = this.session.getTodoPhases();
|
|
1184
1093
|
this.#renderTodoList();
|
|
@@ -1702,6 +1611,20 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1702
1611
|
}
|
|
1703
1612
|
}
|
|
1704
1613
|
|
|
1614
|
+
async #applyPlanExecutionModel(entry: ResolvedRoleModel | undefined): Promise<void> {
|
|
1615
|
+
if (!entry) return;
|
|
1616
|
+
try {
|
|
1617
|
+
await this.session.applyRoleModel(entry);
|
|
1618
|
+
this.statusLine.invalidate();
|
|
1619
|
+
this.updateEditorBorderColor();
|
|
1620
|
+
this.showStatus(`Continuing with ${entry.role}: ${entry.model.name || entry.model.id}`);
|
|
1621
|
+
} catch (error) {
|
|
1622
|
+
this.showWarning(
|
|
1623
|
+
`Could not switch to the ${entry.role} model: ${error instanceof Error ? error.message : String(error)}`,
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1705
1628
|
async #approvePlan(
|
|
1706
1629
|
planContent: string,
|
|
1707
1630
|
options: {
|
|
@@ -1710,6 +1633,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1710
1633
|
title: string;
|
|
1711
1634
|
preserveContext?: boolean;
|
|
1712
1635
|
compactBeforeExecute?: boolean;
|
|
1636
|
+
executionModel?: ResolvedRoleModel;
|
|
1713
1637
|
},
|
|
1714
1638
|
): Promise<void> {
|
|
1715
1639
|
await renameApprovedPlanFile({
|
|
@@ -1791,6 +1715,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1791
1715
|
return;
|
|
1792
1716
|
}
|
|
1793
1717
|
|
|
1718
|
+
await this.#applyPlanExecutionModel(options.executionModel);
|
|
1719
|
+
|
|
1794
1720
|
// Approved plans land in a fresh (or compacted) session whose first user-visible
|
|
1795
1721
|
// turn is the synthetic plan-approved prompt — that path bypasses the
|
|
1796
1722
|
// input-controller's title generation. Seed an auto-name from the plan title
|
|
@@ -2106,13 +2032,38 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2106
2032
|
contextUsage?.percent != null
|
|
2107
2033
|
? `Approve and keep context (${contextUsage.percent.toFixed(1)}%)`
|
|
2108
2034
|
: "Approve and keep context";
|
|
2035
|
+
|
|
2036
|
+
// Model-tier slider: let the operator pick which configured role model
|
|
2037
|
+
// (smol/default/slow/…) executes the approved plan. Left/right move it from
|
|
2038
|
+
// any list position. Hidden when fewer than two role models resolve — a lone
|
|
2039
|
+
// tier is no choice. `selectedTierIndex` tracks the live slider position.
|
|
2040
|
+
const cycle = this.session.getRoleModelCycle(this.session.settings.get("cycleOrder"));
|
|
2041
|
+
let selectedTierIndex = cycle?.currentIndex ?? 0;
|
|
2042
|
+
const slider: HookSelectorSlider | undefined =
|
|
2043
|
+
cycle && cycle.models.length > 1
|
|
2044
|
+
? {
|
|
2045
|
+
caption: "continue with",
|
|
2046
|
+
index: cycle.currentIndex,
|
|
2047
|
+
segments: cycle.models.map(entry => ({
|
|
2048
|
+
label: entry.role,
|
|
2049
|
+
color: MODEL_ROLES[entry.role as ModelRole]?.color,
|
|
2050
|
+
detail: entry.model.name || entry.model.id,
|
|
2051
|
+
})),
|
|
2052
|
+
onChange: index => {
|
|
2053
|
+
selectedTierIndex = index;
|
|
2054
|
+
},
|
|
2055
|
+
}
|
|
2056
|
+
: undefined;
|
|
2057
|
+
const helpText = slider ? `${this.#getPlanReviewHelpText()} ◂/▸ model` : this.#getPlanReviewHelpText();
|
|
2058
|
+
|
|
2109
2059
|
const choice = await this.showHookSelector(
|
|
2110
2060
|
"Plan mode - next step",
|
|
2111
2061
|
["Approve and execute", "Approve and compact context", keepContextLabel, "Refine plan"],
|
|
2112
2062
|
{
|
|
2113
|
-
helpText
|
|
2063
|
+
helpText,
|
|
2114
2064
|
onExternalEditor: () => void this.#openPlanInExternalEditor(planFilePath),
|
|
2115
2065
|
},
|
|
2066
|
+
{ slider },
|
|
2116
2067
|
);
|
|
2117
2068
|
|
|
2118
2069
|
if (choice === "Approve and execute" || choice === "Approve and compact context" || choice === keepContextLabel) {
|
|
@@ -2123,12 +2074,21 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2123
2074
|
this.showError(`Plan file not found at ${planFilePath}`);
|
|
2124
2075
|
return;
|
|
2125
2076
|
}
|
|
2077
|
+
// Capture the operator's tier choice and hand it to #approvePlan, which
|
|
2078
|
+
// applies it AFTER #exitPlanMode. #exitPlanMode restores
|
|
2079
|
+
// #planModePreviousModelState (the model from before plan mode), so
|
|
2080
|
+
// applying the slider choice any earlier would be silently reverted —
|
|
2081
|
+
// the bug that made "continue with slow" keep executing on the default
|
|
2082
|
+
// model. Deferred application also survives newSession()/compaction.
|
|
2083
|
+
const executionModel =
|
|
2084
|
+
cycle && selectedTierIndex !== cycle.currentIndex ? cycle.models[selectedTierIndex] : undefined;
|
|
2126
2085
|
await this.#approvePlan(latestPlanContent, {
|
|
2127
2086
|
planFilePath,
|
|
2128
2087
|
finalPlanFilePath,
|
|
2129
2088
|
title: details.title,
|
|
2130
2089
|
preserveContext: choice !== "Approve and execute",
|
|
2131
2090
|
compactBeforeExecute: choice === "Approve and compact context",
|
|
2091
|
+
executionModel,
|
|
2132
2092
|
});
|
|
2133
2093
|
} catch (error) {
|
|
2134
2094
|
this.showError(
|
|
@@ -2311,7 +2271,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2311
2271
|
this.ui.requestRender(true);
|
|
2312
2272
|
};
|
|
2313
2273
|
nextEditor.onAutocompleteUpdate = () => {
|
|
2314
|
-
this.ui.requestRender();
|
|
2274
|
+
this.ui.requestRender(false, { allowUnknownViewportMutation: true });
|
|
2315
2275
|
};
|
|
2316
2276
|
nextEditor.setMaxHeight(this.#computeEditorMaxHeight());
|
|
2317
2277
|
if (this.historyStorage) {
|
|
@@ -2904,8 +2864,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2904
2864
|
title: string,
|
|
2905
2865
|
options: string[],
|
|
2906
2866
|
dialogOptions?: ExtensionUIDialogOptions,
|
|
2867
|
+
extra?: { slider?: HookSelectorSlider },
|
|
2907
2868
|
): Promise<string | undefined> {
|
|
2908
|
-
return this.#extensionUiController.showHookSelector(title, options, dialogOptions);
|
|
2869
|
+
return this.#extensionUiController.showHookSelector(title, options, dialogOptions, extra);
|
|
2909
2870
|
}
|
|
2910
2871
|
|
|
2911
2872
|
hideHookSelector(): void {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autocomplete for internal-url schemes (skill://, rule://, omp://, local://,
|
|
3
|
+
* memory://, agent://, artifact://) while composing a prompt.
|
|
4
|
+
*
|
|
5
|
+
* Detection here MUST stay in sync with the generic URL-scheme trigger in the
|
|
6
|
+
* TUI editor (`packages/tui/src/components/editor.ts`); the editor fires the
|
|
7
|
+
* popup, this module decides whether there are candidates to show.
|
|
8
|
+
*/
|
|
9
|
+
import type { AutocompleteItem } from "@oh-my-pi/pi-tui";
|
|
10
|
+
import { InternalUrlRouter } from "../internal-urls/router";
|
|
11
|
+
|
|
12
|
+
/** Upper bound on candidates surfaced in the dropdown. */
|
|
13
|
+
const MAX_URL_SUGGESTIONS = 25;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A URL token ending at the cursor: a known internal scheme followed by one or
|
|
17
|
+
* two slashes and the partially typed host/path. The boundary/rest character
|
|
18
|
+
* classes mirror the editor trigger so both agree on what counts as a token.
|
|
19
|
+
*/
|
|
20
|
+
const URL_TOKEN_RE = /(?:^|[\s"'`(<=])([a-z][a-z0-9+.-]*:\/{1,2}[^\s"'`()<>]*)$/i;
|
|
21
|
+
const SCHEME_SPLIT_RE = /^([a-z][a-z0-9+.-]*):\/{1,2}(.*)$/i;
|
|
22
|
+
|
|
23
|
+
export interface InternalUrlContext {
|
|
24
|
+
/** Lowercased scheme (e.g. `local`). */
|
|
25
|
+
scheme: string;
|
|
26
|
+
/** Text typed after the slashes so far (host + path); may be empty. */
|
|
27
|
+
query: string;
|
|
28
|
+
/** Exact buffer token from its boundary to the cursor (the completion prefix). */
|
|
29
|
+
token: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Subsequence fuzzy match: `hum` matches `humanizer`, `lp` matches `local-plan`.
|
|
33
|
+
function fuzzyMatch(query: string, target: string): boolean {
|
|
34
|
+
if (query.length === 0) return true;
|
|
35
|
+
if (query.length > target.length) return false;
|
|
36
|
+
let q = 0;
|
|
37
|
+
for (let t = 0; t < target.length && q < query.length; t += 1) {
|
|
38
|
+
if (query[q] === target[t]) q += 1;
|
|
39
|
+
}
|
|
40
|
+
return q === query.length;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Higher is better: exact > prefix > substring > scattered subsequence.
|
|
44
|
+
function fuzzyScore(query: string, target: string): number {
|
|
45
|
+
if (query.length === 0) return 1;
|
|
46
|
+
if (target === query) return 100;
|
|
47
|
+
if (target.startsWith(query)) return 80;
|
|
48
|
+
if (target.includes(query)) return 60;
|
|
49
|
+
let q = 0;
|
|
50
|
+
let gaps = 0;
|
|
51
|
+
let last = -1;
|
|
52
|
+
for (let t = 0; t < target.length && q < query.length; t += 1) {
|
|
53
|
+
if (query[q] === target[t]) {
|
|
54
|
+
if (last >= 0 && t - last > 1) gaps += 1;
|
|
55
|
+
last = t;
|
|
56
|
+
q += 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (q !== query.length) return 0;
|
|
60
|
+
return Math.max(1, 40 - gaps * 5);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Detect a completable internal-url token immediately before the cursor.
|
|
65
|
+
* Returns `null` when the text is not a `scheme://` token whose scheme is
|
|
66
|
+
* registered with a completion-capable handler.
|
|
67
|
+
*/
|
|
68
|
+
export function extractInternalUrlContext(textBeforeCursor: string): InternalUrlContext | null {
|
|
69
|
+
const tokenMatch = URL_TOKEN_RE.exec(textBeforeCursor);
|
|
70
|
+
if (!tokenMatch) return null;
|
|
71
|
+
const token = tokenMatch[1]!;
|
|
72
|
+
const parts = SCHEME_SPLIT_RE.exec(token);
|
|
73
|
+
if (!parts) return null;
|
|
74
|
+
const scheme = parts[1]!.toLowerCase();
|
|
75
|
+
if (!InternalUrlRouter.instance().completionSchemes().includes(scheme)) return null;
|
|
76
|
+
return { scheme, query: parts[2] ?? "", token };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Suggestions for the internal-url token ending at the cursor, or `null` when
|
|
81
|
+
* the text is not such a token or no candidate matches the typed query.
|
|
82
|
+
*/
|
|
83
|
+
export async function getInternalUrlSuggestions(
|
|
84
|
+
textBeforeCursor: string,
|
|
85
|
+
): Promise<{ items: AutocompleteItem[]; prefix: string } | null> {
|
|
86
|
+
const ctx = extractInternalUrlContext(textBeforeCursor);
|
|
87
|
+
if (!ctx) return null;
|
|
88
|
+
|
|
89
|
+
const candidates = await InternalUrlRouter.instance().complete(ctx.scheme, ctx.query);
|
|
90
|
+
if (!candidates || candidates.length === 0) return null;
|
|
91
|
+
|
|
92
|
+
const query = ctx.query.toLowerCase();
|
|
93
|
+
const scored: Array<{ item: AutocompleteItem; score: number }> = [];
|
|
94
|
+
for (const candidate of candidates) {
|
|
95
|
+
const target = candidate.value.toLowerCase();
|
|
96
|
+
if (!fuzzyMatch(query, target)) continue;
|
|
97
|
+
scored.push({
|
|
98
|
+
item: {
|
|
99
|
+
value: `${ctx.scheme}://${candidate.value}`,
|
|
100
|
+
label: candidate.label ?? candidate.value,
|
|
101
|
+
...(candidate.description ? { description: candidate.description } : {}),
|
|
102
|
+
},
|
|
103
|
+
score: fuzzyScore(query, target),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (scored.length === 0) return null;
|
|
107
|
+
|
|
108
|
+
scored.sort((a, b) => b.score - a.score);
|
|
109
|
+
return {
|
|
110
|
+
items: scored.slice(0, MAX_URL_SUGGESTIONS).map(entry => entry.item),
|
|
111
|
+
prefix: ctx.token,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Whether `prefix` (the token a completion was offered for) is an internal-url token. */
|
|
116
|
+
export function isInternalUrlPrefix(prefix: string): boolean {
|
|
117
|
+
return extractInternalUrlContext(prefix) !== null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Replace the internal-url token with the selected candidate, appending a
|
|
122
|
+
* trailing space (matching `@` file-reference behavior) so the user can keep
|
|
123
|
+
* typing.
|
|
124
|
+
*/
|
|
125
|
+
export function applyInternalUrlCompletion(
|
|
126
|
+
lines: string[],
|
|
127
|
+
cursorLine: number,
|
|
128
|
+
cursorCol: number,
|
|
129
|
+
item: AutocompleteItem,
|
|
130
|
+
prefix: string,
|
|
131
|
+
): { lines: string[]; cursorLine: number; cursorCol: number } {
|
|
132
|
+
const currentLine = lines[cursorLine] || "";
|
|
133
|
+
const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
|
|
134
|
+
const afterCursor = currentLine.slice(cursorCol);
|
|
135
|
+
const insert = `${item.value} `;
|
|
136
|
+
const newLines = [...lines];
|
|
137
|
+
newLines[cursorLine] = beforePrefix + insert + afterCursor;
|
|
138
|
+
return {
|
|
139
|
+
lines: newLines,
|
|
140
|
+
cursorLine,
|
|
141
|
+
cursorCol: beforePrefix.length + insert.length,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import orchestrateNotice from "../prompts/system/orchestrate-notice.md" with { type: "text" };
|
|
2
|
+
import { createGradientHighlighter } from "./gradient-highlight";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* "orchestrate" keyword support.
|
|
6
|
+
*
|
|
7
|
+
* Typing the standalone word in the input editor paints it with a cool
|
|
8
|
+
* teal→violet gradient ({@link highlightOrchestrate}); submitting a message that
|
|
9
|
+
* mentions it appends a hidden {@link ORCHESTRATE_NOTICE} that switches the model
|
|
10
|
+
* into multi-agent orchestration mode. Matching is word-bounded and
|
|
11
|
+
* case-insensitive, so "orchestrated"/"orchestrating" never trigger either
|
|
12
|
+
* behavior. Replaces the former `/orchestrate` slash command.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Detection: standalone keyword, any case. Non-global so `.test` stays stateless.
|
|
16
|
+
const ORCHESTRATE_WORD = /\borchestrate\b/i;
|
|
17
|
+
|
|
18
|
+
/** Hidden system notice appended after a user message that mentions "orchestrate". */
|
|
19
|
+
export const ORCHESTRATE_NOTICE: string = orchestrateNotice.trim();
|
|
20
|
+
|
|
21
|
+
/** Whether `text` contains the standalone keyword "orchestrate" (any case). */
|
|
22
|
+
export function containsOrchestrate(text: string): boolean {
|
|
23
|
+
return ORCHESTRATE_WORD.test(text);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Highlight every standalone "orchestrate" in `text` for editor display with a
|
|
28
|
+
* cool teal→violet gradient (hue 150..280), visually distinct from ultrathink's
|
|
29
|
+
* full-spectrum rainbow.
|
|
30
|
+
*/
|
|
31
|
+
export const highlightOrchestrate: (text: string) => string = createGradientHighlighter({
|
|
32
|
+
probe: /orchestrate/i,
|
|
33
|
+
highlight: /\borchestrate\b/gi,
|
|
34
|
+
stops: 14,
|
|
35
|
+
hue: t => 150 + t * 130,
|
|
36
|
+
});
|
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
import { formatKeyHints, type KeybindingsManager } from "../config/keybindings";
|
|
9
9
|
import { isSettingsInitialized, settings } from "../config/settings";
|
|
10
10
|
import { applyEmojiCompletion, getEmojiSuggestions, isEmojiPrefix, tryEmojiInlineReplace } from "./emoji-autocomplete";
|
|
11
|
+
import {
|
|
12
|
+
applyInternalUrlCompletion,
|
|
13
|
+
getInternalUrlSuggestions,
|
|
14
|
+
isInternalUrlPrefix,
|
|
15
|
+
} from "./internal-url-autocomplete";
|
|
11
16
|
|
|
12
17
|
interface PromptActionDefinition {
|
|
13
18
|
id: string;
|
|
@@ -128,6 +133,9 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
135
|
|
|
136
|
+
const urlSuggestions = await getInternalUrlSuggestions(textBeforeCursor);
|
|
137
|
+
if (urlSuggestions) return urlSuggestions;
|
|
138
|
+
|
|
131
139
|
if (!isSettingsInitialized() || settings.get("emojiAutocomplete")) {
|
|
132
140
|
const emojiSuggestions = getEmojiSuggestions(textBeforeCursor);
|
|
133
141
|
if (emojiSuggestions) return emojiSuggestions;
|
|
@@ -170,6 +178,10 @@ export class PromptActionAutocompleteProvider implements AutocompleteProvider {
|
|
|
170
178
|
};
|
|
171
179
|
}
|
|
172
180
|
|
|
181
|
+
if (isInternalUrlPrefix(prefix)) {
|
|
182
|
+
return applyInternalUrlCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
183
|
+
}
|
|
184
|
+
|
|
173
185
|
if (isEmojiPrefix(prefix)) {
|
|
174
186
|
return applyEmojiCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
175
187
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Settings } from "../../config/settings";
|
|
2
|
+
import type { InteractiveModeContext } from "../types";
|
|
3
|
+
import { glyphSetupScene } from "./scenes/glyph";
|
|
4
|
+
import { providersSetupScene } from "./scenes/providers";
|
|
5
|
+
import { themeSetupScene } from "./scenes/theme";
|
|
6
|
+
import type { SetupScene } from "./scenes/types";
|
|
7
|
+
import { SetupWizardComponent } from "./wizard-overlay";
|
|
8
|
+
|
|
9
|
+
export type { SetupScene, SetupSceneController, SetupSceneHost, SetupSceneResult } from "./scenes/types";
|
|
10
|
+
|
|
11
|
+
export const ALL_SCENES = [
|
|
12
|
+
providersSetupScene,
|
|
13
|
+
glyphSetupScene,
|
|
14
|
+
themeSetupScene,
|
|
15
|
+
] as const satisfies readonly SetupScene[];
|
|
16
|
+
|
|
17
|
+
export const CURRENT_SETUP_VERSION = ALL_SCENES.reduce((max, scene) => Math.max(max, scene.minVersion), 0);
|
|
18
|
+
|
|
19
|
+
export interface SetupSceneSelectionOptions {
|
|
20
|
+
resuming?: boolean;
|
|
21
|
+
isTTY?: boolean;
|
|
22
|
+
skipEnv?: string;
|
|
23
|
+
setupWizardEnabled?: boolean;
|
|
24
|
+
force?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function setupSkipEnvEnabled(value: string | undefined): boolean {
|
|
28
|
+
if (value === undefined) return false;
|
|
29
|
+
const normalized = value.trim().toLowerCase();
|
|
30
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function selectSetupScenes(
|
|
34
|
+
storedVersion: number,
|
|
35
|
+
scenes: readonly SetupScene[],
|
|
36
|
+
ctx?: InteractiveModeContext,
|
|
37
|
+
options: SetupSceneSelectionOptions = {},
|
|
38
|
+
): Promise<SetupScene[]> {
|
|
39
|
+
const isTTY = options.isTTY ?? (process.stdin.isTTY && process.stdout.isTTY);
|
|
40
|
+
if (!isTTY) return [];
|
|
41
|
+
if (!options.force) {
|
|
42
|
+
if (options.resuming) return [];
|
|
43
|
+
if (setupSkipEnvEnabled(options.skipEnv ?? Bun.env.OMP_SKIP_SETUP)) return [];
|
|
44
|
+
if (options.setupWizardEnabled === false) return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const selected: SetupScene[] = [];
|
|
48
|
+
for (const scene of scenes) {
|
|
49
|
+
if (!options.force && scene.minVersion <= storedVersion) continue;
|
|
50
|
+
if (scene.shouldRun) {
|
|
51
|
+
if (!ctx) continue;
|
|
52
|
+
if (!(await scene.shouldRun(ctx))) continue;
|
|
53
|
+
}
|
|
54
|
+
selected.push(scene);
|
|
55
|
+
}
|
|
56
|
+
return selected;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function markSetupWizardComplete(
|
|
60
|
+
settings: Settings,
|
|
61
|
+
version: number = CURRENT_SETUP_VERSION,
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
settings.set("setupVersion", version);
|
|
64
|
+
await settings.flush();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function runSetupWizard(
|
|
68
|
+
ctx: InteractiveModeContext,
|
|
69
|
+
scenes: readonly SetupScene[] = ALL_SCENES,
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
if (scenes.length === 0) return;
|
|
72
|
+
const component = new SetupWizardComponent(ctx, scenes);
|
|
73
|
+
const overlay = ctx.ui.showOverlay(component, {
|
|
74
|
+
width: "100%",
|
|
75
|
+
maxHeight: "100%",
|
|
76
|
+
anchor: "top-left",
|
|
77
|
+
margin: 0,
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
await component.run();
|
|
81
|
+
await markSetupWizardComplete(ctx.settings);
|
|
82
|
+
} finally {
|
|
83
|
+
component.dispose();
|
|
84
|
+
ctx.ui.setFocus(component);
|
|
85
|
+
overlay.hide();
|
|
86
|
+
}
|
|
87
|
+
ctx.playWelcomeIntro();
|
|
88
|
+
}
|