@oh-my-pi/pi-coding-agent 13.18.0 → 14.0.2
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 +316 -1
- package/package.json +86 -24
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +116 -30
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +123 -178
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -8
- package/src/commit/agentic/index.ts +22 -26
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/tools/git-file-diff.ts +3 -6
- package/src/commit/agentic/tools/git-hunk.ts +3 -3
- package/src/commit/agentic/tools/git-overview.ts +6 -9
- package/src/commit/agentic/tools/index.ts +6 -8
- package/src/commit/agentic/tools/propose-commit.ts +4 -7
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/split-commit.ts +4 -4
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/changelog/index.ts +5 -9
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +13 -16
- package/src/config/keybindings.ts +7 -6
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +98 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +6 -18
- package/src/extensibility/custom-commands/bundled/review/index.ts +45 -43
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +4 -2
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
- package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +125 -47
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/footer.ts +9 -29
- package/src/modes/components/hook-editor.ts +3 -3
- package/src/modes/components/hook-selector.ts +6 -1
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/session-observer-overlay.ts +472 -0
- package/src/modes/components/settings-defs.ts +24 -0
- package/src/modes/components/status-line.ts +15 -61
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +4 -2
- package/src/modes/controllers/event-controller.ts +59 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +15 -8
- package/src/modes/controllers/selector-controller.ts +26 -0
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +278 -69
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +0 -42
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +4 -2
- package/src/modes/utils/keybinding-matchers.ts +9 -0
- package/src/prompts/system/custom-system-prompt.md +5 -0
- package/src/prompts/system/system-prompt.md +8 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +216 -165
- package/src/secrets/index.ts +1 -1
- package/src/secrets/obfuscator.ts +25 -17
- package/src/session/agent-session.ts +381 -286
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +15 -5
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +25 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +32 -4
- package/src/task/index.ts +91 -82
- package/src/task/template.ts +2 -2
- package/src/task/types.ts +25 -0
- package/src/task/worktree.ts +131 -149
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +3 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +130 -308
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +211 -146
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/external-editor.ts +11 -5
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +1400 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/web/search/render.ts +6 -4
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -210
- package/src/commit/git/operations.ts +0 -54
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/gh-cli.ts +0 -125
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { CommitAgentState, GitOverviewSnapshot } from "../../../commit/agentic/state";
|
|
3
3
|
import { extractScopeCandidates } from "../../../commit/analysis/scope";
|
|
4
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
5
4
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
5
|
+
import * as git from "../../../utils/git";
|
|
6
6
|
|
|
7
7
|
const EXCLUDED_LOCK_FILES = new Set([
|
|
8
8
|
"Cargo.lock",
|
|
@@ -47,10 +47,7 @@ const gitOverviewSchema = Type.Object({
|
|
|
47
47
|
include_untracked: Type.Optional(Type.Boolean({ description: "Include untracked files when staged=false" })),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
export function createGitOverviewTool(
|
|
51
|
-
git: ControlledGit,
|
|
52
|
-
state: CommitAgentState,
|
|
53
|
-
): CustomTool<typeof gitOverviewSchema> {
|
|
50
|
+
export function createGitOverviewTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitOverviewSchema> {
|
|
54
51
|
return {
|
|
55
52
|
name: "git_overview",
|
|
56
53
|
label: "Git Overview",
|
|
@@ -58,13 +55,13 @@ export function createGitOverviewTool(
|
|
|
58
55
|
parameters: gitOverviewSchema,
|
|
59
56
|
async execute(_toolCallId, params) {
|
|
60
57
|
const staged = params.staged ?? true;
|
|
61
|
-
const allFiles =
|
|
58
|
+
const allFiles = await git.diff.changedFiles(cwd, { cached: staged });
|
|
62
59
|
const { filtered: files, excluded } = filterExcludedFiles(allFiles);
|
|
63
|
-
const stat = await git.
|
|
64
|
-
const allNumstat = await git.
|
|
60
|
+
const stat = await git.diff(cwd, { stat: true, cached: staged });
|
|
61
|
+
const allNumstat = await git.diff.numstat(cwd, { cached: staged });
|
|
65
62
|
const numstat = allNumstat.filter(entry => !isExcludedFile(entry.path));
|
|
66
63
|
const scopeResult = extractScopeCandidates(numstat);
|
|
67
|
-
const untrackedFiles = !staged && params.include_untracked ? await git.
|
|
64
|
+
const untrackedFiles = !staged && params.include_untracked ? await git.ls.untracked(cwd) : undefined;
|
|
68
65
|
const snapshot: GitOverviewSnapshot = {
|
|
69
66
|
files,
|
|
70
67
|
stat,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
2
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
3
2
|
import type { ModelRegistry } from "../../../config/model-registry";
|
|
4
3
|
import type { Settings } from "../../../config/settings";
|
|
5
4
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
@@ -15,7 +14,6 @@ import { createSplitCommitTool } from "./split-commit";
|
|
|
15
14
|
|
|
16
15
|
export interface CommitToolOptions {
|
|
17
16
|
cwd: string;
|
|
18
|
-
git: ControlledGit;
|
|
19
17
|
authStorage: AuthStorage;
|
|
20
18
|
modelRegistry: ModelRegistry;
|
|
21
19
|
settings: Settings;
|
|
@@ -27,10 +25,10 @@ export interface CommitToolOptions {
|
|
|
27
25
|
|
|
28
26
|
export function createCommitTools(options: CommitToolOptions): Array<CustomTool<any, any>> {
|
|
29
27
|
const tools: Array<CustomTool<any, any>> = [
|
|
30
|
-
createGitOverviewTool(options.
|
|
31
|
-
createGitFileDiffTool(options.
|
|
32
|
-
createGitHunkTool(options.
|
|
33
|
-
createRecentCommitsTool(options.
|
|
28
|
+
createGitOverviewTool(options.cwd, options.state),
|
|
29
|
+
createGitFileDiffTool(options.cwd, options.state),
|
|
30
|
+
createGitHunkTool(options.cwd),
|
|
31
|
+
createRecentCommitsTool(options.cwd),
|
|
34
32
|
];
|
|
35
33
|
|
|
36
34
|
if (options.enableAnalyzeFiles ?? true) {
|
|
@@ -48,8 +46,8 @@ export function createCommitTools(options: CommitToolOptions): Array<CustomTool<
|
|
|
48
46
|
|
|
49
47
|
tools.push(
|
|
50
48
|
createProposeChangelogTool(options.state, options.changelogTargets),
|
|
51
|
-
createProposeCommitTool(options.
|
|
52
|
-
createSplitCommitTool(options.
|
|
49
|
+
createProposeCommitTool(options.cwd, options.state),
|
|
50
|
+
createSplitCommitTool(options.cwd, options.state, options.changelogTargets),
|
|
53
51
|
);
|
|
54
52
|
|
|
55
53
|
return tools;
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
validateTypeConsistency,
|
|
10
10
|
} from "../../../commit/agentic/validation";
|
|
11
11
|
import { validateAnalysis } from "../../../commit/analysis/validation";
|
|
12
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
13
12
|
import type { CommitType, ConventionalAnalysis, ConventionalDetail } from "../../../commit/types";
|
|
14
13
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
14
|
+
import * as git from "../../../utils/git";
|
|
15
15
|
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
|
16
16
|
|
|
17
17
|
const proposeCommitSchema = Type.Object({
|
|
@@ -49,10 +49,7 @@ function normalizeDetails(
|
|
|
49
49
|
}));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export function createProposeCommitTool(
|
|
53
|
-
git: ControlledGit,
|
|
54
|
-
state: CommitAgentState,
|
|
55
|
-
): CustomTool<typeof proposeCommitSchema> {
|
|
52
|
+
export function createProposeCommitTool(cwd: string, state: CommitAgentState): CustomTool<typeof proposeCommitSchema> {
|
|
56
53
|
return {
|
|
57
54
|
name: "propose_commit",
|
|
58
55
|
label: "Propose Commit",
|
|
@@ -72,8 +69,8 @@ export function createProposeCommitTool(
|
|
|
72
69
|
|
|
73
70
|
const summaryValidation = validateSummaryRules(summary);
|
|
74
71
|
const analysisValidation = validateAnalysis(analysis);
|
|
75
|
-
const stagedFiles = state.overview?.files ?? (await git.
|
|
76
|
-
const diffText = state.diffText ?? (await git.
|
|
72
|
+
const stagedFiles = state.overview?.files ?? (await git.diff.changedFiles(cwd, { cached: true }));
|
|
73
|
+
const diffText = state.diffText ?? (await git.diff(cwd, { cached: true }));
|
|
77
74
|
const typeValidation = validateTypeConsistency(params.type, stagedFiles, {
|
|
78
75
|
diffText,
|
|
79
76
|
summary,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
3
2
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
3
|
+
import * as git from "../../../utils/git";
|
|
4
4
|
|
|
5
5
|
const recentCommitsSchema = Type.Object({
|
|
6
6
|
count: Type.Optional(Type.Number({ description: "Number of commits to fetch", minimum: 1, maximum: 50 })),
|
|
@@ -25,7 +25,7 @@ function extractScope(subject: string): string | null {
|
|
|
25
25
|
return match?.[1]?.trim() ?? null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export function createRecentCommitsTool(
|
|
28
|
+
export function createRecentCommitsTool(cwd: string): CustomTool<typeof recentCommitsSchema> {
|
|
29
29
|
return {
|
|
30
30
|
name: "recent_commits",
|
|
31
31
|
label: "Recent Commits",
|
|
@@ -33,7 +33,7 @@ export function createRecentCommitsTool(git: ControlledGit): CustomTool<typeof r
|
|
|
33
33
|
parameters: recentCommitsSchema,
|
|
34
34
|
async execute(_toolCallId, params) {
|
|
35
35
|
const count = params.count ?? 8;
|
|
36
|
-
const commits = await git.
|
|
36
|
+
const commits = await git.log.subjects(cwd, count);
|
|
37
37
|
const verbs: Record<string, number> = {};
|
|
38
38
|
const scopes: Record<string, number> = {};
|
|
39
39
|
const lengths: number[] = [];
|
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
validateTypeConsistency,
|
|
11
11
|
} from "../../../commit/agentic/validation";
|
|
12
12
|
import { validateScope } from "../../../commit/analysis/validation";
|
|
13
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
14
13
|
import type { ConventionalDetail } from "../../../commit/types";
|
|
15
14
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
15
|
+
import * as git from "../../../utils/git";
|
|
16
16
|
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
|
17
17
|
|
|
18
18
|
const hunkSelectorSchema = Type.Union([
|
|
@@ -64,7 +64,7 @@ function normalizeDetails(
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export function createSplitCommitTool(
|
|
67
|
-
|
|
67
|
+
cwd: string,
|
|
68
68
|
state: CommitAgentState,
|
|
69
69
|
changelogTargets: string[],
|
|
70
70
|
): CustomTool<typeof splitCommitSchema> {
|
|
@@ -74,13 +74,13 @@ export function createSplitCommitTool(
|
|
|
74
74
|
description: "Propose multiple atomic commits for unrelated changes.",
|
|
75
75
|
parameters: splitCommitSchema,
|
|
76
76
|
async execute(_toolCallId, params) {
|
|
77
|
-
const stagedFiles = state.overview?.files ?? (await git.
|
|
77
|
+
const stagedFiles = state.overview?.files ?? (await git.diff.changedFiles(cwd, { cached: true }));
|
|
78
78
|
const stagedSet = new Set(stagedFiles);
|
|
79
79
|
const changelogSet = new Set(changelogTargets);
|
|
80
80
|
const usedFiles = new Set<string>();
|
|
81
81
|
const errors: string[] = [];
|
|
82
82
|
const warnings: string[] = [];
|
|
83
|
-
const diffText = await git.
|
|
83
|
+
const diffText = await git.diff(cwd, { cached: true });
|
|
84
84
|
|
|
85
85
|
const commits: SplitCommitGroup[] = params.commits.map((commit, index) => {
|
|
86
86
|
const scope = commit.scope?.trim() || null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { stripTypePrefix } from "../../commit/analysis/summary";
|
|
2
2
|
import { validateSummary } from "../../commit/analysis/validation";
|
|
3
3
|
import type { CommitType, ConventionalDetail } from "../../commit/types";
|
|
4
|
-
import { normalizeUnicode } from "../../
|
|
4
|
+
import { normalizeUnicode } from "../../edit/normalize";
|
|
5
5
|
|
|
6
6
|
export const SUMMARY_MAX_CHARS = 72;
|
|
7
7
|
export const MAX_DETAIL_ITEMS = 6;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { Type } from "@sinclair/typebox";
|
|
5
6
|
import analysisSystemPrompt from "../../commit/prompts/analysis-system.md" with { type: "text" };
|
|
6
7
|
import analysisUserPrompt from "../../commit/prompts/analysis-user.md" with { type: "text" };
|
|
7
8
|
import type { ChangelogCategory, ConventionalAnalysis } from "../../commit/types";
|
|
8
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "../utils";
|
|
11
11
|
|
|
@@ -76,7 +76,7 @@ export async function generateConventionalAnalysis({
|
|
|
76
76
|
stat,
|
|
77
77
|
diff,
|
|
78
78
|
}: ConventionalAnalysisInput): Promise<ConventionalAnalysis> {
|
|
79
|
-
const
|
|
79
|
+
const userContent = prompt.render(analysisUserPrompt, {
|
|
80
80
|
context_files: contextFiles && contextFiles.length > 0 ? contextFiles : undefined,
|
|
81
81
|
user_context: userContext,
|
|
82
82
|
types_description: typesDescription,
|
|
@@ -89,8 +89,8 @@ export async function generateConventionalAnalysis({
|
|
|
89
89
|
const response = await completeSimple(
|
|
90
90
|
model,
|
|
91
91
|
{
|
|
92
|
-
systemPrompt:
|
|
93
|
-
messages: [{ role: "user", content:
|
|
92
|
+
systemPrompt: prompt.render(analysisSystemPrompt),
|
|
93
|
+
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
94
94
|
tools: [ConventionalAnalysisTool],
|
|
95
95
|
},
|
|
96
96
|
{ apiKey, maxTokens: 2400, reasoning: toReasoningEffort(thinkingLevel) },
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { Type } from "@sinclair/typebox";
|
|
5
6
|
import summarySystemPrompt from "../../commit/prompts/summary-system.md" with { type: "text" };
|
|
6
7
|
import summaryUserPrompt from "../../commit/prompts/summary-user.md" with { type: "text" };
|
|
7
8
|
import type { CommitSummary } from "../../commit/types";
|
|
8
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { extractTextContent, extractToolCall } from "../utils";
|
|
11
11
|
|
|
@@ -44,7 +44,7 @@ export async function generateSummary({
|
|
|
44
44
|
userContext,
|
|
45
45
|
}: SummaryInput): Promise<CommitSummary> {
|
|
46
46
|
const systemPrompt = renderSummaryPrompt({ commitType, scope, maxChars });
|
|
47
|
-
const userPrompt =
|
|
47
|
+
const userPrompt = prompt.render(summaryUserPrompt, {
|
|
48
48
|
user_context: userContext,
|
|
49
49
|
details: details.join("\n"),
|
|
50
50
|
stat,
|
|
@@ -73,7 +73,7 @@ function renderSummaryPrompt({
|
|
|
73
73
|
maxChars: number;
|
|
74
74
|
}): string {
|
|
75
75
|
const scopePrefix = scope ? `(${scope})` : "";
|
|
76
|
-
return
|
|
76
|
+
return prompt.render(summarySystemPrompt, {
|
|
77
77
|
commit_type: commitType,
|
|
78
78
|
scope_prefix: scopePrefix,
|
|
79
79
|
chars: String(maxChars),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { type TSchema, Type } from "@sinclair/typebox";
|
|
5
6
|
import changelogSystemPrompt from "../../commit/prompts/changelog-system.md" with { type: "text" };
|
|
6
7
|
import changelogUserPrompt from "../../commit/prompts/changelog-user.md" with { type: "text" };
|
|
7
8
|
import { CHANGELOG_CATEGORIES, type ChangelogCategory, type ChangelogGenerationResult } from "../../commit/types";
|
|
8
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { extractTextContent, extractToolCall, parseJsonPayload } from "../utils";
|
|
11
11
|
|
|
@@ -48,7 +48,7 @@ export async function generateChangelogEntries({
|
|
|
48
48
|
stat,
|
|
49
49
|
diff,
|
|
50
50
|
}: ChangelogPromptInput): Promise<ChangelogGenerationResult> {
|
|
51
|
-
const
|
|
51
|
+
const userContent = prompt.render(changelogUserPrompt, {
|
|
52
52
|
changelog_path: changelogPath,
|
|
53
53
|
is_package_changelog: isPackageChangelog,
|
|
54
54
|
existing_entries: existingEntries,
|
|
@@ -58,8 +58,8 @@ export async function generateChangelogEntries({
|
|
|
58
58
|
const response = await completeSimple(
|
|
59
59
|
model,
|
|
60
60
|
{
|
|
61
|
-
systemPrompt:
|
|
62
|
-
messages: [{ role: "user", content:
|
|
61
|
+
systemPrompt: prompt.render(changelogSystemPrompt),
|
|
62
|
+
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
63
63
|
tools: [changelogTool],
|
|
64
64
|
},
|
|
65
65
|
{ apiKey, maxTokens: 1200, reasoning: toReasoningEffort(thinkingLevel) },
|
|
@@ -2,8 +2,8 @@ import * as path from "node:path";
|
|
|
2
2
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import type { ControlledGit } from "../../commit/git";
|
|
6
5
|
import { CHANGELOG_CATEGORIES } from "../../commit/types";
|
|
6
|
+
import * as git from "../../utils/git";
|
|
7
7
|
import { detectChangelogBoundaries } from "./detect";
|
|
8
8
|
import { generateChangelogEntries } from "./generate";
|
|
9
9
|
import { parseUnreleasedSection } from "./parse";
|
|
@@ -13,7 +13,6 @@ const CHANGELOG_SECTIONS = CHANGELOG_CATEGORIES;
|
|
|
13
13
|
const DEFAULT_MAX_DIFF_CHARS = 120_000;
|
|
14
14
|
|
|
15
15
|
export interface ChangelogFlowInput {
|
|
16
|
-
git: ControlledGit;
|
|
17
16
|
cwd: string;
|
|
18
17
|
model: Model<Api>;
|
|
19
18
|
apiKey: string;
|
|
@@ -25,7 +24,6 @@ export interface ChangelogFlowInput {
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
export interface ChangelogProposalInput {
|
|
28
|
-
git: ControlledGit;
|
|
29
27
|
cwd: string;
|
|
30
28
|
proposals: Array<{
|
|
31
29
|
path: string;
|
|
@@ -40,7 +38,6 @@ export interface ChangelogProposalInput {
|
|
|
40
38
|
* Update CHANGELOG.md entries for staged changes.
|
|
41
39
|
*/
|
|
42
40
|
export async function runChangelogFlow({
|
|
43
|
-
git,
|
|
44
41
|
cwd,
|
|
45
42
|
model,
|
|
46
43
|
apiKey,
|
|
@@ -58,9 +55,9 @@ export async function runChangelogFlow({
|
|
|
58
55
|
const updated: string[] = [];
|
|
59
56
|
for (const boundary of boundaries) {
|
|
60
57
|
onProgress?.(`Generating entries for ${boundary.changelogPath}…`);
|
|
61
|
-
const diff = await git.
|
|
58
|
+
const diff = await git.diff(cwd, { cached: true, files: boundary.files });
|
|
62
59
|
if (!diff.trim()) continue;
|
|
63
|
-
const stat = await git.
|
|
60
|
+
const stat = await git.diff(cwd, { stat: true, cached: true, files: boundary.files });
|
|
64
61
|
const diffForPrompt = truncateDiff(diff, maxDiffChars ?? DEFAULT_MAX_DIFF_CHARS);
|
|
65
62
|
const changelogContent = await Bun.file(boundary.changelogPath).text();
|
|
66
63
|
let unreleased: { startLine: number; endLine: number; entries: Record<string, string[]> };
|
|
@@ -87,7 +84,7 @@ export async function runChangelogFlow({
|
|
|
87
84
|
const updatedContent = applyChangelogEntries(changelogContent, unreleased, generated.entries);
|
|
88
85
|
if (!dryRun) {
|
|
89
86
|
await Bun.write(boundary.changelogPath, updatedContent);
|
|
90
|
-
await git.
|
|
87
|
+
await git.stage.files(cwd, [path.relative(cwd, boundary.changelogPath)]);
|
|
91
88
|
}
|
|
92
89
|
updated.push(boundary.changelogPath);
|
|
93
90
|
}
|
|
@@ -99,7 +96,6 @@ export async function runChangelogFlow({
|
|
|
99
96
|
* Apply changelog entries provided by the commit agent.
|
|
100
97
|
*/
|
|
101
98
|
export async function applyChangelogProposals({
|
|
102
|
-
git,
|
|
103
99
|
cwd,
|
|
104
100
|
proposals,
|
|
105
101
|
dryRun,
|
|
@@ -132,7 +128,7 @@ export async function applyChangelogProposals({
|
|
|
132
128
|
const updatedContent = applyChangelogEntries(changelogContent, unreleased, normalized, normalizedDeletions);
|
|
133
129
|
if (!dryRun) {
|
|
134
130
|
await Bun.write(proposal.path, updatedContent);
|
|
135
|
-
await git.
|
|
131
|
+
await git.stage.files(cwd, [path.relative(cwd, proposal.path)]);
|
|
136
132
|
}
|
|
137
133
|
updated.push(proposal.path);
|
|
138
134
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, AssistantMessage, Message, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import fileObserverSystemPrompt from "../../commit/prompts/file-observer-system.md" with { type: "text" };
|
|
5
6
|
import fileObserverUserPrompt from "../../commit/prompts/file-observer-user.md" with { type: "text" };
|
|
6
7
|
import type { FileDiff, FileObservation } from "../../commit/types";
|
|
7
8
|
import { isExcludedFile } from "../../commit/utils/exclusions";
|
|
8
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { truncateToTokenLimit } from "./utils";
|
|
11
11
|
|
|
@@ -38,7 +38,7 @@ export async function runMapPhase({
|
|
|
38
38
|
config,
|
|
39
39
|
}: MapPhaseInput): Promise<FileObservation[]> {
|
|
40
40
|
const filtered = files.filter(file => !isExcludedFile(file.filename));
|
|
41
|
-
const systemPrompt =
|
|
41
|
+
const systemPrompt = prompt.render(fileObserverSystemPrompt);
|
|
42
42
|
const maxFileTokens = config?.maxFileTokens ?? MAX_FILE_TOKENS;
|
|
43
43
|
const maxConcurrency = config?.maxConcurrency ?? MAX_CONCURRENCY;
|
|
44
44
|
const timeoutMs = config?.timeoutMs ?? MAP_PHASE_TIMEOUT_MS;
|
|
@@ -56,14 +56,14 @@ export async function runMapPhase({
|
|
|
56
56
|
|
|
57
57
|
const contextHeader = generateContextHeader(filtered, file.filename);
|
|
58
58
|
const truncated = truncateToTokenLimit(file.content, maxFileTokens);
|
|
59
|
-
const
|
|
59
|
+
const userContent = prompt.render(fileObserverUserPrompt, {
|
|
60
60
|
filename: file.filename,
|
|
61
61
|
diff: truncated,
|
|
62
62
|
context_header: contextHeader,
|
|
63
63
|
});
|
|
64
64
|
const request = {
|
|
65
65
|
systemPrompt,
|
|
66
|
-
messages: [{ role: "user", content:
|
|
66
|
+
messages: [{ role: "user", content: userContent, timestamp: Date.now() }] as Message[],
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
const response = await withRetry(
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { Type } from "@sinclair/typebox";
|
|
5
6
|
import reduceSystemPrompt from "../../commit/prompts/reduce-system.md" with { type: "text" };
|
|
6
7
|
import reduceUserPrompt from "../../commit/prompts/reduce-user.md" with { type: "text" };
|
|
7
8
|
import type { ChangelogCategory, ConventionalAnalysis, FileObservation } from "../../commit/types";
|
|
8
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "../utils";
|
|
11
11
|
|
|
@@ -67,7 +67,7 @@ export async function runReducePhase({
|
|
|
67
67
|
scopeCandidates,
|
|
68
68
|
typesDescription,
|
|
69
69
|
}: ReducePhaseInput): Promise<ConventionalAnalysis> {
|
|
70
|
-
const
|
|
70
|
+
const userContent = prompt.render(reduceUserPrompt, {
|
|
71
71
|
types_description: typesDescription,
|
|
72
72
|
observations: observations.flatMap(obs => obs.observations.map(line => `- ${obs.file}: ${line}`)).join("\n"),
|
|
73
73
|
stat,
|
|
@@ -76,8 +76,8 @@ export async function runReducePhase({
|
|
|
76
76
|
const response = await completeSimple(
|
|
77
77
|
model,
|
|
78
78
|
{
|
|
79
|
-
systemPrompt:
|
|
80
|
-
messages: [{ role: "user", content:
|
|
79
|
+
systemPrompt: prompt.render(reduceSystemPrompt),
|
|
80
|
+
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
81
81
|
tools: [ReduceTool],
|
|
82
82
|
},
|
|
83
83
|
{ apiKey, maxTokens: 2400, reasoning: toReasoningEffort(thinkingLevel) },
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
4
|
-
import { getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { getProjectDir, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { ModelRegistry } from "../config/model-registry";
|
|
6
|
-
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
7
6
|
import { Settings } from "../config/settings";
|
|
8
7
|
import { discoverAuthStorage } from "../sdk";
|
|
9
8
|
import { loadProjectContextFiles } from "../system-prompt";
|
|
9
|
+
import * as git from "../utils/git";
|
|
10
10
|
import { runAgenticCommit } from "./agentic";
|
|
11
11
|
import {
|
|
12
12
|
extractScopeCandidates,
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
validateSummary,
|
|
17
17
|
} from "./analysis";
|
|
18
18
|
import { runChangelogFlow } from "./changelog";
|
|
19
|
-
import { ControlledGit } from "./git";
|
|
20
19
|
import { runMapReduceAnalysis, shouldUseMapReduce } from "./map-reduce";
|
|
21
20
|
import { formatCommitMessage } from "./message";
|
|
22
21
|
import { resolvePrimaryModel, resolveSmolModel } from "./model-selection";
|
|
@@ -26,7 +25,7 @@ import type { CommitCommandArgs, ConventionalAnalysis } from "./types";
|
|
|
26
25
|
|
|
27
26
|
const SUMMARY_MAX_CHARS = 72;
|
|
28
27
|
const RECENT_COMMITS_COUNT = 8;
|
|
29
|
-
const TYPES_DESCRIPTION =
|
|
28
|
+
const TYPES_DESCRIPTION = prompt.render(typesDescriptionPrompt);
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
* Execute the omp commit pipeline for staged changes.
|
|
@@ -57,12 +56,11 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
57
56
|
thinkingLevel: smolThinkingLevel,
|
|
58
57
|
} = await resolveSmolModel(settings, modelRegistry, primaryModel, primaryApiKey);
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
let stagedFiles = await git.getStagedFiles();
|
|
59
|
+
let stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
|
|
62
60
|
if (stagedFiles.length === 0) {
|
|
63
61
|
process.stdout.write("No staged changes detected, staging all changes...\n");
|
|
64
|
-
await git.
|
|
65
|
-
stagedFiles = await git.
|
|
62
|
+
await git.stage.files(cwd);
|
|
63
|
+
stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
|
|
66
64
|
}
|
|
67
65
|
if (stagedFiles.length === 0) {
|
|
68
66
|
process.stderr.write("No changes to commit.\n");
|
|
@@ -71,7 +69,6 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
71
69
|
|
|
72
70
|
if (!args.noChangelog) {
|
|
73
71
|
await runChangelogFlow({
|
|
74
|
-
git,
|
|
75
72
|
cwd,
|
|
76
73
|
model: primaryModel,
|
|
77
74
|
apiKey: primaryApiKey,
|
|
@@ -82,11 +79,11 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
82
79
|
});
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
const diff = await git.
|
|
86
|
-
const stat = await git.
|
|
87
|
-
const numstat = await git.
|
|
82
|
+
const diff = await git.diff(cwd, { cached: true });
|
|
83
|
+
const stat = await git.diff(cwd, { stat: true, cached: true });
|
|
84
|
+
const numstat = await git.diff.numstat(cwd, { cached: true });
|
|
88
85
|
const scopeCandidates = extractScopeCandidates(numstat).scopeCandidates;
|
|
89
|
-
const recentCommits = await git.
|
|
86
|
+
const recentCommits = await git.log.subjects(cwd, RECENT_COMMITS_COUNT);
|
|
90
87
|
const contextFiles = await loadProjectContextFiles({ cwd });
|
|
91
88
|
const formattedContextFiles = contextFiles.map(file => ({
|
|
92
89
|
path: path.relative(cwd, file.path),
|
|
@@ -131,10 +128,10 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
131
128
|
return;
|
|
132
129
|
}
|
|
133
130
|
|
|
134
|
-
await git.commit(commitMessage);
|
|
131
|
+
await git.commit(cwd, commitMessage);
|
|
135
132
|
process.stdout.write("Commit created.\n");
|
|
136
133
|
if (args.push) {
|
|
137
|
-
await git.push();
|
|
134
|
+
await git.push(cwd);
|
|
138
135
|
process.stdout.write("Pushed to remote.\n");
|
|
139
136
|
}
|
|
140
137
|
}
|
|
@@ -238,7 +235,7 @@ async function generateSummaryWithRetry(input: {
|
|
|
238
235
|
}
|
|
239
236
|
|
|
240
237
|
function buildRetryContext(base: string | undefined, errors: string[]): string {
|
|
241
|
-
return
|
|
238
|
+
return prompt.render(summaryRetryPrompt, {
|
|
242
239
|
base_context: base,
|
|
243
240
|
errors: errors.join("; "),
|
|
244
241
|
});
|
|
@@ -37,6 +37,7 @@ interface AppKeybindings {
|
|
|
37
37
|
"app.session.tree": true;
|
|
38
38
|
"app.session.fork": true;
|
|
39
39
|
"app.session.resume": true;
|
|
40
|
+
"app.session.observe": true;
|
|
40
41
|
"app.session.togglePath": true;
|
|
41
42
|
"app.session.toggleSort": true;
|
|
42
43
|
"app.session.rename": true;
|
|
@@ -144,6 +145,10 @@ export const KEYBINDINGS = {
|
|
|
144
145
|
defaultKeys: [],
|
|
145
146
|
description: "Resume session",
|
|
146
147
|
},
|
|
148
|
+
"app.session.observe": {
|
|
149
|
+
defaultKeys: "ctrl+s",
|
|
150
|
+
description: "Observe subagent sessions",
|
|
151
|
+
},
|
|
147
152
|
"app.session.togglePath": {
|
|
148
153
|
defaultKeys: "ctrl+p",
|
|
149
154
|
description: "Toggle session path display",
|
|
@@ -214,6 +219,7 @@ const KEYBINDING_NAME_MIGRATIONS = {
|
|
|
214
219
|
tree: "app.session.tree",
|
|
215
220
|
fork: "app.session.fork",
|
|
216
221
|
resume: "app.session.resume",
|
|
222
|
+
observeSessions: "app.session.observe",
|
|
217
223
|
toggleSTT: "app.stt.toggle",
|
|
218
224
|
// TUI editor (old names for backward compatibility)
|
|
219
225
|
cursorUp: "tui.editor.cursorUp",
|
|
@@ -260,9 +266,6 @@ function isLegacyKeybindingName(key: string): key is keyof typeof KEYBINDING_NAM
|
|
|
260
266
|
return key in KEYBINDING_NAME_MIGRATIONS;
|
|
261
267
|
}
|
|
262
268
|
|
|
263
|
-
/**
|
|
264
|
-
* Normalize input to KeybindingsConfig, validating types.
|
|
265
|
-
*/
|
|
266
269
|
function toKeybindingsConfig(value: unknown): KeybindingsConfig {
|
|
267
270
|
if (typeof value !== "object" || value === null) {
|
|
268
271
|
return {};
|
|
@@ -270,15 +273,13 @@ function toKeybindingsConfig(value: unknown): KeybindingsConfig {
|
|
|
270
273
|
|
|
271
274
|
const config: KeybindingsConfig = {};
|
|
272
275
|
for (const [key, val] of Object.entries(value)) {
|
|
273
|
-
// Allow undefined, string (KeyId), or array of strings
|
|
274
276
|
if (val === undefined) {
|
|
275
277
|
config[key] = undefined;
|
|
276
278
|
} else if (typeof val === "string") {
|
|
277
279
|
config[key] = val as KeyId;
|
|
278
280
|
} else if (Array.isArray(val) && val.every(v => typeof v === "string")) {
|
|
279
|
-
config[key] = val as
|
|
281
|
+
config[key] = val as KeyId[];
|
|
280
282
|
}
|
|
281
|
-
// Silently skip invalid entries
|
|
282
283
|
}
|
|
283
284
|
return config;
|
|
284
285
|
}
|