@oh-my-pi/pi-coding-agent 13.17.6 → 13.19.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 +72 -0
- package/package.json +7 -11
- package/src/autoresearch/git.ts +25 -30
- package/src/autoresearch/tools/log-experiment.ts +61 -74
- package/src/cli/args.ts +0 -1
- package/src/commit/agentic/agent.ts +0 -3
- package/src/commit/agentic/index.ts +19 -22
- 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/changelog/index.ts +5 -9
- package/src/commit/pipeline.ts +10 -12
- package/src/config/keybindings.ts +7 -6
- package/src/config/settings-schema.ts +45 -1
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +4 -16
- package/src/extensibility/custom-commands/bundled/review/index.ts +43 -41
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
- package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
- package/src/index.ts +1 -0
- package/src/internal-urls/types.ts +1 -1
- package/src/main.ts +24 -2
- package/src/modes/acp/acp-event-mapper.ts +0 -1
- 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/session-observer-overlay.ts +472 -0
- package/src/modes/components/settings-defs.ts +19 -0
- package/src/modes/components/status-line.ts +15 -61
- package/src/modes/controllers/command-controller.ts +1 -0
- 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 +3 -0
- package/src/modes/controllers/selector-controller.ts +26 -0
- package/src/modes/interactive-mode.ts +195 -43
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +0 -42
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/keybinding-matchers.ts +9 -0
- package/src/prompts/agents/designer.md +1 -1
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +1 -1
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +1 -1
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/system/custom-system-prompt.md +5 -0
- package/src/prompts/system/system-prompt.md +6 -0
- package/src/prompts/tools/read.md +27 -18
- package/src/sdk.ts +28 -13
- package/src/secrets/index.ts +1 -1
- package/src/secrets/obfuscator.ts +24 -16
- package/src/session/agent-session.ts +75 -30
- package/src/session/artifacts.ts +2 -2
- package/src/session/session-manager.ts +15 -5
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +28 -0
- package/src/task/index.ts +89 -79
- package/src/task/types.ts +25 -0
- package/src/task/worktree.ts +127 -145
- package/src/tools/exit-plan-mode.ts +1 -0
- package/src/tools/fetch.ts +173 -98
- package/src/tools/gh.ts +120 -297
- package/src/tools/index.ts +0 -4
- package/src/tools/path-utils.ts +12 -1
- package/src/tools/read.ts +74 -85
- package/src/tools/renderers.ts +0 -2
- package/src/utils/external-editor.ts +11 -5
- package/src/utils/git.ts +1400 -0
- package/src/web/search/render.ts +6 -4
- 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/prompts/tools/fetch.md +0 -11
- package/src/tools/gh-cli.ts +0 -125
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
3
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
4
3
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
4
|
+
import * as git from "../../../utils/git";
|
|
5
5
|
|
|
6
6
|
const TARGET_TOKENS = 30000;
|
|
7
7
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -136,10 +136,7 @@ const gitFileDiffSchema = Type.Object({
|
|
|
136
136
|
staged: Type.Optional(Type.Boolean({ description: "Use staged changes (default: true)" })),
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
export function createGitFileDiffTool(
|
|
140
|
-
git: ControlledGit,
|
|
141
|
-
state: CommitAgentState,
|
|
142
|
-
): CustomTool<typeof gitFileDiffSchema> {
|
|
139
|
+
export function createGitFileDiffTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitFileDiffSchema> {
|
|
143
140
|
return {
|
|
144
141
|
name: "git_file_diff",
|
|
145
142
|
label: "Git File Diff",
|
|
@@ -167,7 +164,7 @@ export function createGitFileDiffTool(
|
|
|
167
164
|
|
|
168
165
|
if (uncachedFiles.length > 0) {
|
|
169
166
|
for (const file of uncachedFiles) {
|
|
170
|
-
const diff = await git.
|
|
167
|
+
const diff = await git.diff(cwd, { cached: staged, files: [file] });
|
|
171
168
|
if (diff) {
|
|
172
169
|
diffs.set(file, diff);
|
|
173
170
|
state.diffCache.set(cacheKey(file), diff);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { ControlledGit } from "../../../commit/git";
|
|
3
2
|
import type { DiffHunk, FileHunks } from "../../../commit/types";
|
|
4
3
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
4
|
+
import * as git from "../../../utils/git";
|
|
5
5
|
|
|
6
6
|
const gitHunkSchema = Type.Object({
|
|
7
7
|
file: Type.String({ description: "File path" }),
|
|
@@ -15,7 +15,7 @@ function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] {
|
|
|
15
15
|
return fileHunks.hunks.filter(hunk => wanted.has(hunk.index + 1));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function createGitHunkTool(
|
|
18
|
+
export function createGitHunkTool(cwd: string): CustomTool<typeof gitHunkSchema> {
|
|
19
19
|
return {
|
|
20
20
|
name: "git_hunk",
|
|
21
21
|
label: "Git Hunk",
|
|
@@ -23,7 +23,7 @@ export function createGitHunkTool(git: ControlledGit): CustomTool<typeof gitHunk
|
|
|
23
23
|
parameters: gitHunkSchema,
|
|
24
24
|
async execute(_toolCallId, params) {
|
|
25
25
|
const staged = params.staged ?? true;
|
|
26
|
-
const hunks = await git.
|
|
26
|
+
const hunks = await git.diff.hunks(cwd, [params.file], { cached: staged });
|
|
27
27
|
const fileHunks = hunks.find(entry => entry.filename === params.file) ?? {
|
|
28
28
|
filename: params.file,
|
|
29
29
|
isBinary: false,
|
|
@@ -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;
|
|
@@ -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
|
}
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { renderPromptTemplate } from "../config/prompt-templates";
|
|
|
7
7
|
import { Settings } from "../config/settings";
|
|
8
8
|
import { discoverAuthStorage } from "../sdk";
|
|
9
9
|
import { loadProjectContextFiles } from "../system-prompt";
|
|
10
|
+
import * as git from "../utils/git";
|
|
10
11
|
import { runAgenticCommit } from "./agentic";
|
|
11
12
|
import {
|
|
12
13
|
extractScopeCandidates,
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
validateSummary,
|
|
17
18
|
} from "./analysis";
|
|
18
19
|
import { runChangelogFlow } from "./changelog";
|
|
19
|
-
import { ControlledGit } from "./git";
|
|
20
20
|
import { runMapReduceAnalysis, shouldUseMapReduce } from "./map-reduce";
|
|
21
21
|
import { formatCommitMessage } from "./message";
|
|
22
22
|
import { resolvePrimaryModel, resolveSmolModel } from "./model-selection";
|
|
@@ -57,12 +57,11 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
57
57
|
thinkingLevel: smolThinkingLevel,
|
|
58
58
|
} = await resolveSmolModel(settings, modelRegistry, primaryModel, primaryApiKey);
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
let stagedFiles = await git.getStagedFiles();
|
|
60
|
+
let stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
|
|
62
61
|
if (stagedFiles.length === 0) {
|
|
63
62
|
process.stdout.write("No staged changes detected, staging all changes...\n");
|
|
64
|
-
await git.
|
|
65
|
-
stagedFiles = await git.
|
|
63
|
+
await git.stage.files(cwd);
|
|
64
|
+
stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
|
|
66
65
|
}
|
|
67
66
|
if (stagedFiles.length === 0) {
|
|
68
67
|
process.stderr.write("No changes to commit.\n");
|
|
@@ -71,7 +70,6 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
71
70
|
|
|
72
71
|
if (!args.noChangelog) {
|
|
73
72
|
await runChangelogFlow({
|
|
74
|
-
git,
|
|
75
73
|
cwd,
|
|
76
74
|
model: primaryModel,
|
|
77
75
|
apiKey: primaryApiKey,
|
|
@@ -82,11 +80,11 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
82
80
|
});
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
const diff = await git.
|
|
86
|
-
const stat = await git.
|
|
87
|
-
const numstat = await git.
|
|
83
|
+
const diff = await git.diff(cwd, { cached: true });
|
|
84
|
+
const stat = await git.diff(cwd, { stat: true, cached: true });
|
|
85
|
+
const numstat = await git.diff.numstat(cwd, { cached: true });
|
|
88
86
|
const scopeCandidates = extractScopeCandidates(numstat).scopeCandidates;
|
|
89
|
-
const recentCommits = await git.
|
|
87
|
+
const recentCommits = await git.log.subjects(cwd, RECENT_COMMITS_COUNT);
|
|
90
88
|
const contextFiles = await loadProjectContextFiles({ cwd });
|
|
91
89
|
const formattedContextFiles = contextFiles.map(file => ({
|
|
92
90
|
path: path.relative(cwd, file.path),
|
|
@@ -131,10 +129,10 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
131
129
|
return;
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
await git.commit(commitMessage);
|
|
132
|
+
await git.commit(cwd, commitMessage);
|
|
135
133
|
process.stdout.write("Commit created.\n");
|
|
136
134
|
if (args.push) {
|
|
137
|
-
await git.push();
|
|
135
|
+
await git.push(cwd);
|
|
138
136
|
process.stdout.write("Pushed to remote.\n");
|
|
139
137
|
}
|
|
140
138
|
}
|
|
@@ -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
|
}
|
|
@@ -194,6 +194,15 @@ export const SETTINGS_SCHEMA = {
|
|
|
194
194
|
// ────────────────────────────────────────────────────────────────────────
|
|
195
195
|
lastChangelogVersion: { type: "string", default: undefined },
|
|
196
196
|
|
|
197
|
+
autoResume: {
|
|
198
|
+
type: "boolean",
|
|
199
|
+
default: false,
|
|
200
|
+
ui: {
|
|
201
|
+
tab: "interaction",
|
|
202
|
+
label: "Auto Resume",
|
|
203
|
+
description: "Automatically resume the most recent session in the current directory",
|
|
204
|
+
},
|
|
205
|
+
},
|
|
197
206
|
shellPath: { type: "string", default: undefined },
|
|
198
207
|
|
|
199
208
|
extensions: { type: "array", default: EMPTY_STRING_ARRAY },
|
|
@@ -797,6 +806,38 @@ export const SETTINGS_SCHEMA = {
|
|
|
797
806
|
|
|
798
807
|
"compaction.remoteEndpoint": { type: "string", default: undefined },
|
|
799
808
|
|
|
809
|
+
// Idle compaction
|
|
810
|
+
"compaction.idleEnabled": {
|
|
811
|
+
type: "boolean",
|
|
812
|
+
default: false,
|
|
813
|
+
ui: {
|
|
814
|
+
tab: "context",
|
|
815
|
+
label: "Idle Compaction",
|
|
816
|
+
description: "Compact context while idle when token count exceeds threshold",
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
|
|
820
|
+
"compaction.idleThresholdTokens": {
|
|
821
|
+
type: "number",
|
|
822
|
+
default: 200000,
|
|
823
|
+
ui: {
|
|
824
|
+
tab: "context",
|
|
825
|
+
label: "Idle Compaction Threshold",
|
|
826
|
+
description: "Token count above which idle compaction triggers",
|
|
827
|
+
submenu: true,
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
|
|
831
|
+
"compaction.idleTimeoutSeconds": {
|
|
832
|
+
type: "number",
|
|
833
|
+
default: 300,
|
|
834
|
+
ui: {
|
|
835
|
+
tab: "context",
|
|
836
|
+
label: "Idle Compaction Delay",
|
|
837
|
+
description: "Seconds to wait while idle before compacting",
|
|
838
|
+
submenu: true,
|
|
839
|
+
},
|
|
840
|
+
},
|
|
800
841
|
// Branch summaries
|
|
801
842
|
"branchSummary.enabled": {
|
|
802
843
|
type: "boolean",
|
|
@@ -1207,7 +1248,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1207
1248
|
"fetch.enabled": {
|
|
1208
1249
|
type: "boolean",
|
|
1209
1250
|
default: true,
|
|
1210
|
-
ui: { tab: "tools", label: "
|
|
1251
|
+
ui: { tab: "tools", label: "Read URLs", description: "Allow the read tool to fetch and process URLs" },
|
|
1211
1252
|
},
|
|
1212
1253
|
|
|
1213
1254
|
"github.enabled": {
|
|
@@ -1701,6 +1742,9 @@ export interface CompactionSettings {
|
|
|
1701
1742
|
autoContinue: boolean;
|
|
1702
1743
|
remoteEnabled: boolean;
|
|
1703
1744
|
remoteEndpoint: string | undefined;
|
|
1745
|
+
idleEnabled: boolean;
|
|
1746
|
+
idleThresholdTokens: number;
|
|
1747
|
+
idleTimeoutSeconds: number;
|
|
1704
1748
|
}
|
|
1705
1749
|
|
|
1706
1750
|
export interface ContextPromotionSettings {
|
|
@@ -2,26 +2,14 @@ import { renderPromptTemplate } from "../../../../config/prompt-templates";
|
|
|
2
2
|
import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types";
|
|
3
3
|
import type { HookCommandContext } from "../../../../extensibility/hooks/types";
|
|
4
4
|
import ciGreenRequestTemplate from "../../../../prompts/ci-green-request.md" with { type: "text" };
|
|
5
|
+
import * as git from "../../../../utils/git";
|
|
5
6
|
|
|
6
7
|
async function getHeadTag(api: CustomCommandAPI): Promise<string | undefined> {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"HEAD",
|
|
11
|
-
"--sort=-version:refname",
|
|
12
|
-
"--format=%(refname:strip=2)",
|
|
13
|
-
"refs/tags",
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
if (result.code !== 0 || result.killed) {
|
|
8
|
+
try {
|
|
9
|
+
return (await git.ref.tags(api.cwd))[0];
|
|
10
|
+
} catch {
|
|
17
11
|
return undefined;
|
|
18
12
|
}
|
|
19
|
-
|
|
20
|
-
const tag = result.stdout
|
|
21
|
-
.split("\n")
|
|
22
|
-
.map(line => line.trim())
|
|
23
|
-
.find(Boolean);
|
|
24
|
-
return tag || undefined;
|
|
25
13
|
}
|
|
26
14
|
|
|
27
15
|
export class GreenCommand implements CustomCommand {
|