@klarkxy/dsh-git-commit 0.2.5 → 0.3.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/lib/client.js CHANGED
@@ -1364,25 +1364,18 @@ ${scope} {
1364
1364
  }
1365
1365
  //#endregion
1366
1366
  //#region ../dsh-plugin-kit/src/contracts.ts
1367
- /**
1368
- * Remote contribution for model-settings change notifications.
1369
- * Provided by `@klarkxy/dsh-model-hub` (`ModelSettingsChangesController`);
1370
- * subscribers degrade silently when that package is not installed.
1371
- * The stream carries invalidations only — `ready` is never a settings snapshot.
1372
- */
1373
- const MODEL_SETTINGS_CHANGES_REMOTE = {
1374
- package: "@klarkxy/dsh-model-hub",
1375
- descriptors: [{
1376
- id: "modelSettingsChanges/watch",
1377
- service: "modelSettingsChangesController",
1378
- namespace: "modelSettingsChanges",
1379
- method: "watch",
1380
- mode: "stream",
1381
- invocation: { kind: "direct" },
1382
- parameters: [],
1383
- cancellation: { parameter: "signal" }
1384
- }]
1385
- };
1367
+ /** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
1368
+ function modelSettingsChangesIdentity(packageName) {
1369
+ const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
1370
+ const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
1371
+ if (!parts.length) throw new Error("Model settings package name has no RPC segment");
1372
+ const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
1373
+ if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
1374
+ return {
1375
+ service: segment,
1376
+ namespace: segment
1377
+ };
1378
+ }
1386
1379
  //#endregion
1387
1380
  //#region ../dsh-plugin-kit/src/model-settings-client.ts
1388
1381
  /** This stream carries invalidations only; ready is never a settings snapshot. */
@@ -1397,17 +1390,44 @@ function parseModelSettingsChangeFrame(value) {
1397
1390
  };
1398
1391
  throw new Error("Invalid model settings change frame");
1399
1392
  }
1400
- MODEL_SETTINGS_CHANGES_REMOTE.package, MODEL_SETTINGS_CHANGES_REMOTE.descriptors.map((descriptor) => ({
1401
- ...descriptor,
1402
- result: {
1403
- mode: "strict",
1404
- typeSymbol: "ModelSettings.ChangeFrame",
1405
- create: () => ({ parse: parseModelSettingsChangeFrame })
1406
- }
1407
- }));
1393
+ /** One owner's contribution. Each package mounts only its own endpoint. */
1394
+ function modelSettingsChangesRemote(packageName, identity = modelSettingsChangesIdentity(packageName)) {
1395
+ return {
1396
+ package: packageName,
1397
+ descriptors: [{
1398
+ id: `${identity.namespace}/watch`,
1399
+ service: identity.service,
1400
+ namespace: identity.namespace,
1401
+ method: "watch",
1402
+ mode: "stream",
1403
+ invocation: { kind: "direct" },
1404
+ parameters: [],
1405
+ cancellation: { parameter: "signal" },
1406
+ result: {
1407
+ mode: "strict",
1408
+ typeSymbol: "ModelSettings.ChangeFrame",
1409
+ create: () => ({ parse: parseModelSettingsChangeFrame })
1410
+ }
1411
+ }]
1412
+ };
1413
+ }
1414
+ /** Mount from a synchronous apply. Disposal waits for an in-flight mount and
1415
+ * then withdraws it, so the contribution cannot outlive the plugin. */
1416
+ function mountModelSettingsChanges(ctx, packageName) {
1417
+ const contribution = modelSettingsChangesRemote(packageName);
1418
+ ctx.inject(["remote"], (injected) => {
1419
+ const scope = injected;
1420
+ const mounting = scope.remote.$mount(contribution);
1421
+ mounting.catch(() => {});
1422
+ scope.effect(() => async () => {
1423
+ await (await mounting.catch(() => void 0))?.();
1424
+ });
1425
+ });
1426
+ }
1408
1427
  /** Native RemoteStream owns carrier reconnects; no timers or browser transport.
1409
1428
  * A factory shares one logical stream while at least one subscriber remains. */
1410
- function createModelSettingsSubscriber(ctx, namespace) {
1429
+ function createModelSettingsSubscriber(ctx, packageName, identity = modelSettingsChangesIdentity(packageName), accept = (ns) => ns === packageName) {
1430
+ const endpoint = identity;
1411
1431
  const listeners = /* @__PURE__ */ new Set();
1412
1432
  let injection;
1413
1433
  let current;
@@ -1433,12 +1453,18 @@ function createModelSettingsSubscriber(ctx, namespace) {
1433
1453
  };
1434
1454
  const start = () => {
1435
1455
  const life = ++lifecycle;
1436
- injection = ctx.inject(["remote", "remote.modelSettingsChanges"], (injected) => {
1456
+ injection = ctx.inject(["remote", `remote.${endpoint.namespace}`], (injected) => {
1437
1457
  if (life !== lifecycle || !listeners.size) return;
1438
1458
  const scope = injected;
1459
+ const service = scope.remote[endpoint.namespace];
1460
+ if (!service || !("watch" in service) || !service.watch) {
1461
+ report("Model settings sync service unavailable");
1462
+ return;
1463
+ }
1464
+ const watch = service.watch;
1439
1465
  const stream = scope.remote.$stream({
1440
1466
  name: "Model settings sync",
1441
- open: (signal) => scope.remote.modelSettingsChanges.watch(signal),
1467
+ open: (signal) => watch(signal),
1442
1468
  ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
1443
1469
  });
1444
1470
  const session = {
@@ -1473,7 +1499,7 @@ function createModelSettingsSubscriber(ctx, namespace) {
1473
1499
  invalidate();
1474
1500
  } else {
1475
1501
  if (!ready) throw new Error("Model settings sync change arrived before ready");
1476
- if (namespace === "*" || frame.ns === namespace) invalidate();
1502
+ if (accept(frame.ns)) invalidate();
1477
1503
  }
1478
1504
  }
1479
1505
  } catch (error) {
@@ -2345,6 +2371,7 @@ function GitCommitSettingsPanel(props) {
2345
2371
  }
2346
2372
  function apply(ctx) {
2347
2373
  const client = ctx;
2374
+ mountModelSettingsChanges(ctx, PLUGIN_NAME);
2348
2375
  const settingsSubscriber = createModelSettingsSubscriber(ctx, PLUGIN_NAME);
2349
2376
  const subscribeSettings = (change, error) => settingsSubscriber.subscribe(change, error);
2350
2377
  ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
@@ -1,5 +1,5 @@
1
- import { ModelRoute, defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
2
1
  import { RpcResult as RpcResult$1 } from "@klarkxy/dsh-plugin-kit/contracts";
2
+ import { ModelRoute, defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
3
3
  //#region src/contracts.d.ts
4
4
  type RpcResult<T = unknown> = Extract<RpcResult$1<T>, {
5
5
  ok: true;
package/lib/index.d.ts CHANGED
@@ -49,6 +49,9 @@ interface GitCommitServiceDeps {
49
49
  readonly llm: LlmTextCaller;
50
50
  readonly host?: unknown;
51
51
  readonly sessions: SessionStoreLike;
52
+ readonly workingDirectory: {
53
+ get(session: SessionLike): string;
54
+ };
52
55
  readonly agents?: AgentStoreLike;
53
56
  readonly run?: GitRunner;
54
57
  readonly preview?: (root: string, path: string) => Promise<UntrackedPreview>;
@@ -111,7 +114,7 @@ declare function parsePlan(text: string, files: readonly ChangedFile[]): PlanGro
111
114
  //#endregion
112
115
  //#region src/index.d.ts
113
116
  declare const name = "@klarkxy/dsh-git-commit";
114
- declare const inject: readonly ["llm", "sessions", "agents", "storageDomain", "connection", "webServer", "sessionProjections", "agentDefaultModel"];
117
+ declare const inject: readonly ["llm", "sessions", "workingDirectory", "agents", "storageDomain", "connection", "webServer", "sessionProjections", "agentDefaultModel"];
115
118
  declare module '@deepseek-ai/cordis' {
116
119
  interface Context {
117
120
  gitCommit: GitCommitService;
package/lib/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { DEFAULT_MAX_OUTPUT_TOKENS, MAX_MANIFEST_CHARS, MAX_UNTRACKED_PREVIEW_CHARS, PLUGIN_NAME, RPC_CHANNEL, SETTINGS_KEY, defaultSettings, normalizeFallback, normalizeModelRoute } from "./contracts.js";
2
2
  import { registerHostRpc } from "@klarkxy/dsh-plugin-kit/host-rpc";
3
+ import { modelSettingsChangesIdentity } from "@klarkxy/dsh-plugin-kit/contracts";
4
+ import { createModelSettingsChanges } from "@klarkxy/dsh-plugin-kit/model-settings-changes";
3
5
  import { notifyModelSettingsChanged, registerModelSettings } from "@klarkxy/dsh-plugin-kit/model-settings";
4
6
  import { modelRouteOverride } from "@klarkxy/dsh-model-route";
5
7
  import { callLlmText, resolveFeatureModel } from "@klarkxy/dsh-plugin-kit";
@@ -771,7 +773,7 @@ var GitCommitService = class {
771
773
  async resolveRepo(sessionId, signal) {
772
774
  const session = this.deps.sessions.get(sessionId);
773
775
  if (!session) throw new CommitRunError("no-session", "dsh-git-commit: session is not live on this host");
774
- const cwd = normalizeCwd(session.header?.cwd);
776
+ const cwd = normalizeCwd(this.deps.workingDirectory.get(session));
775
777
  if (!cwd) throw new CommitRunError("no-cwd", "dsh-git-commit: session has no working directory");
776
778
  try {
777
779
  return await repoInfo(this.run, cwd, signal);
@@ -828,13 +830,13 @@ var GitCommitService = class {
828
830
  await this.waitForSettings(signal);
829
831
  if (this.disposed || signal?.aborted) throw new CommitRunError("cancelled", "dsh-git-commit: plugin is no longer active");
830
832
  const session = this.deps.sessions.get(sessionId);
831
- const sessionCwd = normalizeCwd(session?.header?.cwd);
833
+ const sessionCwd = session ? normalizeCwd(this.deps.workingDirectory.get(session)) : void 0;
832
834
  const resolved = await this.resolveRepo(sessionId, signal);
833
835
  const assertReady = () => {
834
836
  signal?.throwIfAborted();
835
837
  if (this.disposed || !this.deps.llm) throw new CommitRunError("cancelled", "dsh-git-commit: plugin is no longer active");
836
838
  const currentSession = this.deps.sessions.get(sessionId);
837
- if (currentSession !== session || normalizeCwd(currentSession?.header?.cwd) !== sessionCwd) throw new CommitRunError("session-changed", "dsh-git-commit: session workspace changed during planning");
839
+ if (currentSession !== session || (currentSession ? normalizeCwd(this.deps.workingDirectory.get(currentSession)) : void 0) !== sessionCwd) throw new CommitRunError("session-changed", "dsh-git-commit: session workspace changed during planning");
838
840
  if (this.deps.agents?.get(sessionId)?.status === "running") throw new CommitRunError("session-running", "dsh-git-commit: wait for the session turn to finish");
839
841
  };
840
842
  assertReady();
@@ -905,7 +907,11 @@ var GitCommitService = class {
905
907
  };
906
908
  let executed;
907
909
  try {
908
- executed = await commitGroups(this.run, resolved.root, effective, selected, signal, async () => {
910
+ const commitRun = (args, options) => {
911
+ if (args[0] === "add" || args[0] === "commit") assertReady();
912
+ return this.run(args, options);
913
+ };
914
+ executed = await commitGroups(commitRun, resolved.root, effective, selected, signal, async () => {
909
915
  assertReady();
910
916
  if (await this.run([
911
917
  "symbolic-ref",
@@ -917,6 +923,7 @@ var GitCommitService = class {
917
923
  }).catch(() => "DETACHED") !== branchRef) throw new CommitRunError("workspace-changed", "dsh-git-commit: branch changed during the commit run");
918
924
  if (await pathVersions(this.run, resolved.root, paths, signal) !== originalVersions) throw new CommitRunError("workspace-changed", "dsh-git-commit: selected file changed during the commit run");
919
925
  await workingTreeChanges(this.run, resolved.root, signal);
926
+ assertReady();
920
927
  });
921
928
  } catch (error) {
922
929
  if (!(error instanceof PartialCommitError)) throw error;
@@ -991,6 +998,7 @@ const name = PLUGIN_NAME;
991
998
  const inject = [
992
999
  "llm",
993
1000
  "sessions",
1001
+ "workingDirectory",
994
1002
  "agents",
995
1003
  "storageDomain",
996
1004
  "connection",
@@ -1087,6 +1095,7 @@ async function handleRpc(service, endpoint, payload, signal) {
1087
1095
  }
1088
1096
  }
1089
1097
  function apply(ctx) {
1098
+ createModelSettingsChanges(ctx, modelSettingsChangesIdentity(PLUGIN_NAME), PLUGIN_NAME);
1090
1099
  const host = ctx;
1091
1100
  const domainPromise = host.storageDomain.open(gitCommitDomain);
1092
1101
  const tablePromise = domainPromise.then((domain) => domain.table("settings"));
@@ -1096,6 +1105,7 @@ function apply(ctx) {
1096
1105
  llm: host.llm,
1097
1106
  host,
1098
1107
  sessions: host.sessions,
1108
+ workingDirectory: host.workingDirectory,
1099
1109
  agents: host.agents,
1100
1110
  settings: {
1101
1111
  load: async () => parseSettings((await tablePromise).get(SETTINGS_KEY)),
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/git.ts","../src/plan.ts","../src/service.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { constants } from 'node:fs'\nimport { copyFile, lstat, mkdtemp, open, readFile, readlink, realpath, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { isAbsolute, join, relative, resolve } from 'node:path'\nimport { GIT_TIMEOUT_MS, MAX_UNTRACKED_PREVIEW_CHARS } from './contracts.ts'\n\nexport interface GitRunOptions {\n readonly cwd: string\n readonly signal?: AbortSignal\n readonly timeoutMs?: number\n}\n\nexport class GitError extends Error {\n readonly stderr: string\n constructor(message: string, stderr = '') {\n super(message)\n this.name = 'GitError'\n this.stderr = stderr\n }\n}\n\nexport type GitRunner = (args: readonly string[], options: GitRunOptions) => Promise<string>\n\n/** NUL separator used by `git status -z` without embedding a control byte in this source file. */\nconst NUL = String.fromCharCode(0)\n\n/** Spawn git without a shell; arguments are passed literally. */\nexport const execGit: GitRunner = (args, options) => new Promise((resolve, reject) => {\n try {\n execFile('git', [...args], {\n cwd: options.cwd,\n encoding: 'utf8',\n maxBuffer: 32 * 1024 * 1024,\n timeout: options.timeoutMs ?? GIT_TIMEOUT_MS,\n signal: options.signal,\n windowsHide: true,\n env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_LITERAL_PATHSPECS: '1' },\n }, (error, stdout, stderr) => {\n if (error) {\n if (options.signal?.aborted) {\n reject(Object.assign(new Error('dsh-git-commit: git command cancelled'), { code: 'cancelled' }))\n return\n }\n reject(Object.assign(new GitError(stderr.trim() || error.message, stderr), { code: (error as NodeJS.ErrnoException).code }))\n return\n }\n resolve(stdout)\n })\n } catch (error) {\n reject(error)\n }\n})\n\nexport function isGitMissing(error: unknown): boolean {\n return typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n}\n\nexport type ChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed' | 'copied' | 'untracked'\n\nexport interface ChangedFile {\n readonly path: string\n readonly status: ChangeStatus\n /** Rename source in porcelain -z output; committed with the destination. */\n readonly origin?: string\n /** True when the index already stages this path. */\n readonly staged: boolean\n}\n\nexport function statusLetter(kind: ChangeStatus): string {\n switch (kind) {\n case 'added': return 'A'\n case 'deleted': return 'D'\n case 'renamed': return 'R'\n case 'copied': return 'C'\n case 'untracked': return '?'\n default: return 'M'\n }\n}\n\n/**\n * Parse `git status --porcelain=v1 -z --untracked-files=all` output.\n * In -z mode rename/copy entries put the destination path first, then a NUL, then the origin path.\n */\nexport function parsePorcelain(output: string): ChangedFile[] {\n const tokens = output.split(NUL)\n const files: ChangedFile[] = []\n let index = 0\n while (index < tokens.length) {\n const token = tokens[index]\n index += 1\n if (!token) continue\n if (token.length < 4) continue\n const x = token[0] ?? ' '\n const y = token[1] ?? ' '\n const path = token.slice(3)\n if (x === '!' && y === '!') continue\n if (x === '?' && y === '?') {\n files.push({ path, status: 'untracked', staged: false })\n continue\n }\n if (x === 'R' || x === 'C' || y === 'R' || y === 'C') {\n const origin = tokens[index]\n index += 1\n if (!origin) throw new GitError('dsh-git-commit: incomplete rename status')\n files.push({ path, origin, status: x === 'C' || y === 'C' ? 'copied' : 'renamed', staged: x !== ' ' && x !== '?' })\n continue\n }\n if (x === 'U' || y === 'U' || (x === 'A' && y === 'A') || (x === 'D' && y === 'D')) {\n throw new GitError(`dsh-git-commit: resolve merge conflicts before committing (${path})`)\n }\n const status: ChangeStatus = x === 'D' || y === 'D' ? 'deleted' : x === 'A' || y === 'A' ? 'added' : 'modified'\n files.push({ path, status, staged: x !== ' ' && x !== '?' })\n }\n return files\n}\n\nexport interface RepoInfo {\n readonly root: string\n readonly branch: string\n}\n\nexport async function repoInfo(run: GitRunner, cwd: string, signal?: AbortSignal): Promise<RepoInfo> {\n const root = (await run(['rev-parse', '--show-toplevel'], { cwd, signal })).trim()\n let branch = (await run(['symbolic-ref', '--quiet', '--short', 'HEAD'], { cwd: root, signal }).catch(() => '')).trim()\n if (!branch) branch = (await run(['rev-parse', '--short', 'HEAD'], { cwd: root, signal }).catch(() => '')).trim()\n return { root, branch: branch || 'HEAD' }\n}\n\nexport async function workingTreeChanges(run: GitRunner, root: string, signal?: AbortSignal): Promise<ChangedFile[]> {\n const output = await run(['status', '--porcelain=v1', '-z', '--untracked-files=all'], { cwd: root, signal })\n return parsePorcelain(output)\n}\n\n/** Exact index/worktree content fingerprint, including untracked file bytes. */\nexport async function workspaceSnapshot(run: GitRunner, root: string, signal?: AbortSignal): Promise<{ files: ChangedFile[]; fingerprint: string }> {\n const status = await run(['status', '--porcelain=v1', '-z', '--untracked-files=all'], { cwd: root, signal })\n const files = parsePorcelain(status)\n const hash = createHash('sha256').update(status)\n hash.update(await run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: root, signal }).catch(() => 'DETACHED'))\n hash.update(await run(['rev-parse', 'HEAD'], { cwd: root, signal }).catch(() => 'unborn'))\n hash.update(await run(['diff', '--cached', '--binary', '--no-ext-diff'], { cwd: root, signal }))\n hash.update(await run(['diff', '--binary', '--no-ext-diff'], { cwd: root, signal }))\n for (const file of files) {\n if (file.status !== 'untracked') continue\n hash.update(file.path)\n hash.update(await worktreePathDigest(run, root, file.path, signal))\n }\n return { files, fingerprint: hash.digest('hex') }\n}\n\n/** A worktree content check for groups waiting behind earlier commits. */\nexport async function pathVersions(run: GitRunner, root: string, paths: readonly string[], signal?: AbortSignal): Promise<string> {\n const hash = createHash('sha256')\n for (const path of paths) {\n hash.update(path)\n hash.update(await worktreePathDigest(run, root, path, signal))\n }\n return hash.digest('hex')\n}\n\nasync function worktreePathDigest(run: GitRunner, root: string, path: string, signal?: AbortSignal): Promise<string> {\n const absolute = resolve(root, path)\n let stat\n try {\n stat = await lstat(absolute)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'\n throw error\n }\n const hash = createHash('sha256').update(String(stat.mode))\n if (stat.isSymbolicLink()) hash.update(await readlink(absolute))\n else if (stat.isFile()) {\n const handle = await open(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))\n try {\n if (!(await handle.stat()).isFile() || !(await lstat(absolute)).isFile()) throw new GitError(`dsh-git-commit: file changed while reading (${path})`)\n for await (const chunk of handle.createReadStream({ autoClose: false })) {\n signal?.throwIfAborted()\n hash.update(chunk)\n }\n if (!(await lstat(absolute)).isFile()) throw new GitError(`dsh-git-commit: file changed while reading (${path})`)\n } finally {\n await handle.close()\n }\n } else if (stat.isDirectory()) {\n hash.update(await run(['-C', absolute, 'rev-parse', 'HEAD'], { cwd: root, signal }))\n hash.update(await run(['-C', absolute, 'status', '--porcelain=v1', '-z'], { cwd: root, signal }))\n }\n return hash.digest('hex')\n}\n\nexport async function recentSubjects(run: GitRunner, root: string, count: number, signal?: AbortSignal): Promise<string[]> {\n const output = await run(['log', `-${count}`, '--pretty=%s'], { cwd: root, signal }).catch(() => '')\n return output.split('\\n').map(line => line.trim()).filter(Boolean)\n}\n\n/** Combined staged+worktree stat and diff against HEAD for the given paths. */\nexport async function diffAgainstHead(\n run: GitRunner, root: string, paths: readonly string[], signal?: AbortSignal,\n): Promise<{ stat: string; diff: string }> {\n const args = paths.length > 0 ? ['--', ...paths] : []\n const stat = await run(['diff', 'HEAD', '--stat', '--no-color', ...args], { cwd: root, signal }).catch(() => '')\n const diff = await run(['diff', 'HEAD', '--no-color', '--no-ext-diff', ...args], { cwd: root, signal }).catch(() => '')\n return { stat, diff }\n}\n\nexport interface UntrackedPreview {\n readonly path: string\n readonly preview: string\n readonly binary: boolean\n}\n\nexport async function untrackedPreview(root: string, path: string): Promise<UntrackedPreview> {\n try {\n const absolute = resolve(root, path)\n const rootReal = await realpath(root)\n const withinRoot = async () => {\n const target = await realpath(absolute)\n const rel = relative(rootReal, target)\n return rel !== '' && rel !== '..' && !rel.startsWith(`..${String.fromCharCode(47)}`)\n && !rel.startsWith(`..${String.fromCharCode(92)}`) && !isAbsolute(rel)\n }\n if (!await withinRoot()) return { path, preview: '', binary: true }\n if (!(await lstat(absolute)).isFile()) return { path, preview: '', binary: true }\n const handle = await open(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))\n let buffer: Buffer\n try {\n const bounded = Buffer.alloc(MAX_UNTRACKED_PREVIEW_CHARS * 4)\n const { bytesRead } = await handle.read(bounded, 0, bounded.length, 0)\n buffer = bounded.subarray(0, bytesRead)\n } finally {\n await handle.close()\n }\n if (!await withinRoot() || !(await lstat(absolute)).isFile()) return { path, preview: '', binary: true }\n if (buffer.includes(0)) return { path, preview: '', binary: true }\n const text = buffer.toString('utf8')\n return { path, preview: text.slice(0, MAX_UNTRACKED_PREVIEW_CHARS), binary: false }\n } catch {\n return { path, preview: '', binary: true }\n }\n}\n\nexport interface ExecutedCommit {\n readonly hash: string\n readonly skipped: boolean\n}\n\n/** Known commits or an uncertain current group must survive a rejected run. */\nexport class PartialCommitError extends Error {\n readonly commits: readonly ExecutedCommit[]\n readonly cause: unknown\n readonly uncertain?: { readonly message: string; readonly files: readonly string[] }\n constructor(commits: readonly ExecutedCommit[], cause: unknown, uncertain?: { readonly message: string; readonly files: readonly string[] }) {\n super(cause instanceof Error ? cause.message : String(cause))\n this.name = 'PartialCommitError'\n this.commits = commits\n this.cause = cause\n this.uncertain = uncertain\n }\n}\n\nasync function indexBytes(path: string): Promise<Buffer | undefined> {\n try { return await readFile(path) }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n}\n\nasync function headOid(run: GitRunner, root: string): Promise<string> {\n try { return (await run(['rev-parse', '--verify', 'HEAD'], { cwd: root })).trim() }\n catch (error) {\n if (error instanceof GitError && /Needed a single revision|not a valid object name HEAD/u.test(error.stderr)) return ''\n throw error\n }\n}\n\n/**\n * Commit each group from the worktree. `--only` keeps other staged paths out of\n * this commit; an exact index backup protects the current group on failure.\n */\nexport async function commitGroups(\n run: GitRunner,\n root: string,\n groups: ReadonlyArray<{ message: string; files: readonly string[] }>,\n selected: readonly ChangedFile[],\n signal?: AbortSignal,\n verify?: (paths: readonly string[]) => Promise<void>,\n): Promise<ExecutedCommit[]> {\n const commits: ExecutedCommit[] = []\n let partialFailure: PartialCommitError | undefined\n const fail = (error: unknown): never => {\n if (error instanceof PartialCommitError) { partialFailure = error; throw error }\n if (commits.length === 0) throw error\n partialFailure = new PartialCommitError(commits, error)\n throw partialFailure\n }\n for (const group of groups) {\n if (group.files.length === 0) continue\n const paths = group.files.flatMap(path => {\n const file = selected.find(file => file.path === path)\n return file?.status === 'renamed' && file.origin ? [path, file.origin] : [path]\n })\n let backupDir: string | undefined\n try {\n signal?.throwIfAborted()\n await verify?.(paths)\n const gitPath = (await run(['rev-parse', '--git-path', 'index'], { cwd: root, signal })).trim()\n const indexPath = isAbsolute(gitPath) ? gitPath : resolve(root, gitPath)\n backupDir = await mkdtemp(join(tmpdir(), 'dsh-git-commit-'))\n const backupPath = join(backupDir, 'index')\n let hadIndex = true\n try {\n await copyFile(indexPath, backupPath)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n hadIndex = false\n }\n const head = await headOid(run, root)\n const originalIndex = await indexBytes(indexPath)\n if (hadIndex ? !originalIndex?.equals(await readFile(backupPath)) : originalIndex !== undefined) {\n throw new GitError('dsh-git-commit: index changed before committing; retry')\n }\n let stagedIndex = originalIndex\n let committed = false\n let commitInvoked = false\n try {\n signal?.throwIfAborted()\n await run(['add', '-A', '--', ...group.files], { cwd: root, signal })\n stagedIndex = await indexBytes(indexPath)\n signal?.throwIfAborted()\n commitInvoked = true\n await run(['commit', '--quiet', '--only', '-m', group.message, '--', ...paths], { cwd: root, signal })\n committed = true\n let hash = ''\n try {\n // Result collection and index cleanup must survive request cancellation/unload.\n hash = (await run(['rev-parse', '--short', 'HEAD'], { cwd: root })).trim()\n } catch (error) {\n // The commit object exists. A failed hash read must not look like \"nothing was committed\".\n commits.push({ hash: '', skipped: false })\n throw error\n }\n if (!hash) {\n commits.push({ hash: '', skipped: false })\n throw new GitError('dsh-git-commit: commit was created but its hash could not be read')\n }\n commits.push({ hash, skipped: false })\n } catch (error) {\n if (!committed) {\n let currentHead: string\n try { currentHead = await headOid(run, root) }\n catch {\n // A failed inspection cannot prove the rejected commit left HEAD\n // unchanged. Keep its original diagnostic and do not restore.\n if (commitInvoked) throw new PartialCommitError(commits, error, { message: group.message, files: [...group.files] })\n throw error\n }\n if (currentHead !== head) {\n // The rejected command may have completed, or another owner may have\n // advanced HEAD. Preserve both HEAD and index without attributing it.\n if (commitInvoked) throw new PartialCommitError(commits, error, { message: group.message, files: [...group.files] })\n throw error\n }\n const currentIndex = await indexBytes(indexPath)\n const indexUntouched = currentIndex === undefined ? stagedIndex === undefined : currentIndex.equals(stagedIndex ?? Buffer.alloc(0))\n if (currentHead === head && indexUntouched) {\n if (hadIndex) await copyFile(backupPath, indexPath)\n else await rm(indexPath, { force: true })\n }\n }\n // A created commit stays. Only this group's uncommitted index is eligible for restore.\n throw error\n }\n } catch (error) {\n fail(error)\n } finally {\n if (backupDir) {\n try { await rm(backupDir, { recursive: true, force: true }) }\n catch (error) { fail(partialFailure ?? error) }\n }\n }\n }\n return commits\n}\n\n/** One deterministic commit for every selected path; used when the model plan is unavailable. */\nexport function fallbackGroup(files: readonly ChangedFile[], localeHint: 'zh' | 'en'): { message: string; files: readonly string[] } {\n const message = localeHint === 'zh'\n ? `chore: 提交 ${files.length} 个文件的修改`\n : `chore: commit changes to ${files.length} file${files.length === 1 ? '' : 's'}`\n return { message, files: files.map(file => file.path) }\n}\n","import { MAX_MANIFEST_CHARS, MAX_MESSAGE_CHARS } from './contracts.ts'\nimport { statusLetter, type ChangedFile, type UntrackedPreview } from './git.ts'\n\nexport interface PlanGroup {\n readonly message: string\n readonly files: readonly string[]\n}\n\nexport interface PlanInput {\n readonly files: readonly ChangedFile[]\n readonly recentSubjects: readonly string[]\n readonly stat: string\n readonly diff: string\n readonly untracked: readonly UntrackedPreview[]\n}\n\nexport function planSystemPrompt(): string {\n return [\n 'You split a git working-tree change set into a small number of coherent commits.',\n 'Return exactly one JSON object: {\"groups\":[{\"message\":\"<commit message>\",\"files\":[\"<path>\",...]}]}.',\n 'Rules:',\n '- Every listed file must appear in exactly one group; do not invent paths.',\n '- One to five groups. Group files that belong to the same logical change.',\n '- Order groups so earlier commits do not depend on later ones.',\n '- Each message is a single line of at most 72 characters, no trailing period.',\n '- Match the language and style of the recent commit subjects when provided; otherwise use English conventional-commit style.',\n '- Return no Markdown, code fence, or explanation.',\n ].join('\\n')\n}\n\nfunction truncateMiddle(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text\n const half = Math.floor((maxChars - 40) / 2)\n return `${text.slice(0, half)}\\n… [${text.length - maxChars} chars omitted] …\\n${text.slice(-half)}`\n}\n\nexport function buildPlanInput(input: PlanInput): string {\n const sections: string[] = []\n sections.push('Scope: all current workspace changes of the repository.')\n if (input.recentSubjects.length > 0) {\n sections.push(['Recent commit subjects (style reference):', ...input.recentSubjects.map(s => `- ${s}`)].join('\\n'))\n }\n const manifest = input.files.map(file => `- ${statusLetter(file.status)} ${file.origin ? `${file.origin} ${file.status === 'copied' ? '=>' : '->'} ` : ''}${file.path}`).join('\\n')\n sections.push(`Changed files:\\n${manifest}`)\n if (input.stat.trim()) sections.push(`Diff stat:\\n${input.stat.trim()}`)\n if (input.diff.trim()) sections.push(`Diff against HEAD:\\n${input.diff}`)\n for (const untracked of input.untracked) {\n sections.push(untracked.binary\n ? `New binary file: ${untracked.path}`\n : `New file ${untracked.path}:\\n${untracked.preview}`)\n }\n return truncateMiddle(sections.join('\\n\\n'), MAX_MANIFEST_CHARS)\n}\n\nexport function sanitizeMessage(raw: unknown): string | undefined {\n if (typeof raw !== 'string') return undefined\n const line = raw.replace(/\\s+/gu, ' ').trim().replace(/\\.+$/u, '')\n if (!line) return undefined\n return line.length > MAX_MESSAGE_CHARS ? line.slice(0, MAX_MESSAGE_CHARS).trim() : line\n}\n\n/** Extract the first balanced top-level JSON object from model output. */\nexport function extractJsonObject(text: string): string | undefined {\n const fenced = /```(?:json)?\\s*([\\s\\S]*?)```/u.exec(text)\n const candidate = fenced?.[1] ?? text\n const start = candidate.indexOf('{')\n if (start === -1) return undefined\n let depth = 0\n let inString = false\n let escaped = false\n for (let index = start; index < candidate.length; index += 1) {\n const char = candidate[index]\n if (inString) {\n if (escaped) escaped = false\n else if (char === '\\\\') escaped = true\n else if (char === '\"') inString = false\n continue\n }\n if (char === '\"') inString = true\n else if (char === '{') depth += 1\n else if (char === '}') {\n depth -= 1\n if (depth === 0) return candidate.slice(start, index + 1)\n }\n }\n return undefined\n}\n\n/**\n * Validate the model plan against the exact change set. Returns undefined when\n * the output is malformed, covers the wrong files, or duplicates a file.\n */\nexport function parsePlan(text: string, files: readonly ChangedFile[]): PlanGroup[] | undefined {\n const json = extractJsonObject(text)\n if (!json) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(json)\n } catch {\n return undefined\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined\n const groups = (parsed as { groups?: unknown }).groups\n if (!Array.isArray(groups) || groups.length === 0 || groups.length > 8) return undefined\n const allowed = new Set(files.map(file => file.path))\n const seen = new Set<string>()\n const plan: PlanGroup[] = []\n for (const group of groups) {\n if (!group || typeof group !== 'object') return undefined\n const message = sanitizeMessage((group as { message?: unknown }).message)\n const paths = (group as { files?: unknown }).files\n if (!message || !Array.isArray(paths) || paths.length === 0) return undefined\n const groupFiles: string[] = []\n for (const path of paths) {\n if (typeof path !== 'string' || !allowed.has(path) || seen.has(path)) return undefined\n seen.add(path)\n groupFiles.push(path)\n }\n plan.push({ message, files: groupFiles })\n }\n if (seen.size !== allowed.size) return undefined\n return plan\n}\n","import { callLlmText, resolveFeatureModel, type LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { modelRouteOverride } from '@klarkxy/dsh-model-route'\nimport {\n DEFAULT_MAX_OUTPUT_TOKENS, RECENT_LOG_COUNT,\n defaultSettings, normalizeFallback, type CommitGroupResult, type CommitModelInfo, type CommitRunResult,\n type GitCommitSettings, type GitCommitStatus, type PartialCommitResult,\n} from './contracts.ts'\nimport {\n commitGroups, diffAgainstHead, execGit, fallbackGroup, isGitMissing, PartialCommitError, pathVersions, recentSubjects, repoInfo,\n untrackedPreview, workspaceSnapshot, workingTreeChanges, type ChangedFile, type ExecutedCommit, type GitRunner, type UntrackedPreview,\n} from './git.ts'\nimport { buildPlanInput, parsePlan, planSystemPrompt, type PlanGroup } from './plan.ts'\n\nexport interface SessionLike {\n readonly id: string\n readonly header?: { readonly cwd?: string }\n}\n\nexport interface SessionStoreLike {\n get(id: string): SessionLike | undefined\n}\n\nexport interface AgentStoreLike {\n get(id: string): { readonly status?: string } | undefined\n}\n\nexport interface GitCommitServiceDeps {\n readonly plugin: string\n readonly onSettingsChanged?: (revision: number) => void\n readonly llm: LlmTextCaller\n readonly host?: unknown\n readonly sessions: SessionStoreLike\n readonly agents?: AgentStoreLike\n readonly run?: GitRunner\n readonly preview?: (root: string, path: string) => Promise<UntrackedPreview>\n /** Plugin-page settings row; absent keeps the shared purpose route. */\n readonly settings?: {\n load(): Promise<GitCommitSettings>\n save(settings: GitCommitSettings): Promise<void>\n }\n}\n\nexport class CommitRunError extends Error {\n readonly code: string\n /** Commits already created when this run stopped. Absent when nothing was written. */\n readonly partial?: PartialCommitResult\n constructor(code: string, message: string, partial?: PartialCommitResult) {\n super(message)\n this.name = code === 'cancelled' ? 'AbortError' : 'CommitRunError'\n this.code = code\n this.partial = partial\n }\n}\n\nfunction modelInfo(route: { provider: string; model: string } | undefined, source: CommitModelInfo['source']): CommitModelInfo | undefined {\n if (!route?.provider || !route.model) return undefined\n return { provider: route.provider, model: route.model, source }\n}\n\nfunction looksChinese(subjects: readonly string[]): boolean {\n return subjects.some(subject => /[一-鿿]/u.test(subject))\n}\n\nfunction normalizeCwd(cwd: string | undefined): string | undefined {\n if (typeof cwd !== 'string') return undefined\n const normalized = cwd.trim().replaceAll('\\\\', '/')\n if (!normalized) return undefined\n return normalized === '/' || /^[A-Za-z]:\\/$/.test(normalized) ? normalized : normalized.replace(/\\/+$/, '')\n}\n\nexport class GitCommitService {\n private readonly deps: GitCommitServiceDeps\n private readonly run: GitRunner\n private readonly preview: (root: string, path: string) => Promise<UntrackedPreview>\n private running: Promise<unknown> | undefined\n private settings: GitCommitSettings\n private settingsPending: Promise<void> = Promise.resolve()\n private readonly settingsReady: Promise<void>\n private settingsFailure: CommitRunError | undefined\n private settingsAdopted = false\n private disposed = false\n /** Aborted on unload so an in-flight model plan receives the same signal as a cancelled request. */\n private readonly lifetime = new AbortController()\n\n constructor(deps: GitCommitServiceDeps) {\n this.deps = deps\n this.run = deps.run ?? execGit\n this.preview = deps.preview ?? untrackedPreview\n this.settings = defaultSettings()\n // A missing row is resolved by storage; an unreadable row must not select a different model.\n this.settingsReady = deps.settings?.load().then(\n loaded => { if (loaded && !this.settingsAdopted) this.settings = structuredClone(loaded) },\n () => {\n if (!this.settingsAdopted) this.settingsFailure = new CommitRunError(\n 'settings-unavailable', 'dsh-git-commit: saved settings could not be read; reload the plugin before retrying',\n )\n },\n ) ?? Promise.resolve()\n }\n\n /** A refreshed row wins over a pending initial read. */\n adoptSettings(next: GitCommitSettings): void {\n if (this.disposed) throw new CommitRunError('disposed', 'dsh-git-commit: plugin disabled')\n this.settingsAdopted = true\n this.settingsFailure = undefined\n this.settings = structuredClone(next)\n }\n\n /** Existing synchronous snapshot API; host operations wait for initialization first. */\n getSettings(): GitCommitSettings {\n this.workSignal()\n if (this.settingsFailure) throw this.settingsFailure\n return structuredClone(this.settings)\n }\n\n async ready(): Promise<void> {\n this.workSignal()\n await this.settingsReady\n this.workSignal()\n if (this.settingsFailure) throw this.settingsFailure\n }\n\n /** Request cancellation and plugin unload share one signal. A disposed service rejects before any new work. */\n private workSignal(signal?: AbortSignal): AbortSignal {\n if (this.disposed || this.lifetime.signal.aborted) {\n throw new CommitRunError('disposed', 'dsh-git-commit: plugin is no longer active')\n }\n return signal ? AbortSignal.any([signal, this.lifetime.signal]) : this.lifetime.signal\n }\n\n private async waitForSettings(signal?: AbortSignal): Promise<void> {\n signal?.throwIfAborted()\n if (!signal) { await this.ready(); return }\n let onAbort: () => void = () => {}\n const aborted = new Promise<never>((_, reject) => {\n onAbort = () => {\n try { signal.throwIfAborted() }\n catch (error) { reject(error) }\n }\n signal.addEventListener('abort', onAbort, { once: true })\n if (signal.aborted) onAbort()\n })\n try { await Promise.race([this.settingsReady, aborted]) }\n finally { signal.removeEventListener('abort', onAbort) }\n signal.throwIfAborted()\n if (this.settingsFailure) throw this.settingsFailure\n }\n\n private selectedModel(sessionId: string) {\n const page = modelRouteOverride(this.settings.model)\n return { route: resolveFeatureModel(this.deps.host, page, sessionId), source: page ? 'page' as const : 'default' as const }\n }\n\n async updateSettings(patch: Omit<GitCommitSettings, 'revision'>, expectedRevision: number): Promise<GitCommitSettings> {\n const detached = structuredClone(patch)\n const result = this.settingsPending.then(async () => {\n await this.ready()\n if (this.disposed) throw new CommitRunError('disposed', 'dsh-git-commit: plugin disabled')\n if (expectedRevision !== this.settings.revision) {\n throw new CommitRunError('stale', 'dsh-git-commit: settings changed; reload and retry')\n }\n const next: GitCommitSettings = {\n revision: expectedRevision + 1,\n model: detached.model,\n allowFallback: normalizeFallback(detached.allowFallback),\n }\n await this.deps.settings?.save(next)\n this.settings = next\n try { this.deps.onSettingsChanged?.(next.revision) } catch {}\n return structuredClone(next)\n })\n this.settingsPending = result.then(() => {}, () => {})\n return result\n }\n\n start(): void {}\n\n async dispose(): Promise<void> {\n this.disposed = true\n this.lifetime.abort()\n await this.settingsReady\n await this.settingsPending\n await this.running?.catch(() => {})\n }\n\n private async resolveRepo(sessionId: string, signal?: AbortSignal): Promise<{ root: string; branch: string }> {\n const session = this.deps.sessions.get(sessionId)\n if (!session) throw new CommitRunError('no-session', 'dsh-git-commit: session is not live on this host')\n const cwd = normalizeCwd(session.header?.cwd)\n if (!cwd) throw new CommitRunError('no-cwd', 'dsh-git-commit: session has no working directory')\n try {\n return await repoInfo(this.run, cwd, signal)\n } catch (error) {\n signal?.throwIfAborted()\n if (isGitMissing(error)) throw new CommitRunError('no-git', 'dsh-git-commit: git executable not found')\n throw new CommitRunError('not-a-repo', 'dsh-git-commit: session directory is not inside a git repository')\n }\n }\n\n async status(sessionId: string, signal?: AbortSignal): Promise<GitCommitStatus> {\n const work = this.workSignal(signal)\n await this.waitForSettings(work)\n let resolved\n try {\n resolved = await this.resolveRepo(sessionId, work)\n } catch (error) {\n if (error instanceof CommitRunError) {\n return { available: false, reason: error.code, detail: error.message, workspace: { files: 0 } }\n }\n throw error\n }\n const changes = await workingTreeChanges(this.run, resolved.root, work)\n work.throwIfAborted()\n const selected = this.selectedModel(sessionId)\n const model = modelInfo(selected.route, selected.source)\n return {\n available: true,\n root: resolved.root,\n branch: resolved.branch,\n workspace: { files: changes.length },\n ...(model ? { model } : {}),\n }\n }\n\n async commit(sessionId: string, signal?: AbortSignal): Promise<CommitRunResult> {\n if (this.disposed || this.lifetime.signal.aborted) {\n throw new CommitRunError('disposed', 'dsh-git-commit: plugin is no longer active')\n }\n if (this.running) throw new CommitRunError('busy', 'dsh-git-commit: a commit run is already in progress')\n const work = this.workSignal(signal)\n const run = this.runCommit(sessionId, work)\n this.running = run\n try {\n return await run\n } catch (error) {\n if (error instanceof CommitRunError && error.partial) throw error\n if (work.aborted) throw new CommitRunError('cancelled', 'dsh-git-commit: commit run cancelled')\n throw error\n } finally {\n this.running = undefined\n }\n }\n\n private async runCommit(sessionId: string, signal: AbortSignal): Promise<CommitRunResult> {\n await this.waitForSettings(signal)\n if (this.disposed || signal?.aborted) throw new CommitRunError('cancelled', 'dsh-git-commit: plugin is no longer active')\n const session = this.deps.sessions.get(sessionId)\n const sessionCwd = normalizeCwd(session?.header?.cwd)\n const resolved = await this.resolveRepo(sessionId, signal)\n const assertReady = () => {\n signal?.throwIfAborted()\n if (this.disposed || !this.deps.llm) throw new CommitRunError('cancelled', 'dsh-git-commit: plugin is no longer active')\n const currentSession = this.deps.sessions.get(sessionId)\n if (currentSession !== session || normalizeCwd(currentSession?.header?.cwd) !== sessionCwd) {\n throw new CommitRunError('session-changed', 'dsh-git-commit: session workspace changed during planning')\n }\n if (this.deps.agents?.get(sessionId)?.status === 'running') {\n throw new CommitRunError('session-running', 'dsh-git-commit: wait for the session turn to finish')\n }\n }\n assertReady()\n const initial = await workspaceSnapshot(this.run, resolved.root, signal)\n const branchRef = await this.run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: resolved.root, signal }).catch(() => 'DETACHED')\n const selected: ChangedFile[] = initial.files\n if (selected.length === 0) {\n throw new CommitRunError('no-changes', 'dsh-git-commit: working tree is clean')\n }\n const paths = selected.flatMap(file => file.status === 'renamed' && file.origin ? [file.path, file.origin] : [file.path])\n const originalVersions = await pathVersions(this.run, resolved.root, paths, signal)\n const [{ stat, diff }, subjects] = await Promise.all([\n diffAgainstHead(this.run, resolved.root, paths, signal),\n recentSubjects(this.run, resolved.root, RECENT_LOG_COUNT, signal),\n ])\n const untracked: UntrackedPreview[] = []\n for (const file of selected) {\n assertReady()\n if (file.status === 'untracked') untracked.push(await this.preview(resolved.root, file.path))\n }\n\n let groups: PlanGroup[] | undefined\n let planFailure = 'no model route was available'\n let model: CommitModelInfo | undefined\n const planned = this.selectedModel(sessionId)\n if (planned.route) {\n try {\n const result = await callLlmText(this.deps.llm, {\n plugin: this.deps.plugin,\n route: planned.route,\n system: planSystemPrompt(),\n text: buildPlanInput({ files: selected, recentSubjects: subjects, stat, diff, untracked }),\n maxTokens: DEFAULT_MAX_OUTPUT_TOKENS,\n signal,\n sessionId,\n })\n model = modelInfo(result, planned.source)\n groups = parsePlan(result.text, selected)\n if (!groups) planFailure = 'the model returned a plan that does not cover the change set'\n } catch (error) {\n assertReady()\n if (error instanceof CommitRunError) throw error\n planFailure = error instanceof Error ? error.message : String(error)\n }\n }\n\n // Without a plan the run would write commits nobody asked for, so it stops\n // here — before the index or the worktree is touched. A single fallback\n // commit stays available, but only when the settings row asked for it.\n if (!groups && !this.settings.allowFallback) {\n throw new CommitRunError('plan-unavailable', `dsh-git-commit: no commit plan (${planFailure})`)\n }\n\n assertReady()\n const current = await workspaceSnapshot(this.run, resolved.root, signal)\n if (current.fingerprint !== initial.fingerprint) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: workspace changed during planning; retry the commit')\n }\n const effective = groups ?? [fallbackGroup(selected, looksChinese(subjects) ? 'zh' : 'en')]\n const describe = (executed: readonly ExecutedCommit[]): CommitGroupResult[] => {\n const commits: CommitGroupResult[] = []\n for (let index = 0; index < executed.length; index += 1) {\n const result = executed[index]\n const group = effective[index]\n if (!result || !group || result.skipped) continue\n commits.push({ hash: result.hash, message: group.message, files: [...group.files] })\n }\n return commits\n }\n let executed: ExecutedCommit[]\n try {\n executed = await commitGroups(this.run, resolved.root, effective, selected, signal, async () => {\n assertReady()\n const currentBranchRef = await this.run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: resolved.root, signal }).catch(() => 'DETACHED')\n if (currentBranchRef !== branchRef) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: branch changed during the commit run')\n }\n if (await pathVersions(this.run, resolved.root, paths, signal) !== originalVersions) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: selected file changed during the commit run')\n }\n // A new merge conflict must never be passed to git add or git commit.\n await workingTreeChanges(this.run, resolved.root, signal)\n })\n } catch (error) {\n if (!(error instanceof PartialCommitError)) throw error\n const commits = describe(error.commits)\n const cause = error.cause\n const cancelled = signal?.aborted\n || (typeof cause === 'object' && cause !== null && (cause as { code?: unknown }).code === 'cancelled')\n || (cause instanceof Error && cause.name === 'AbortError')\n const code = cause instanceof CommitRunError ? cause.code : cancelled ? 'cancelled' : 'commit-failed'\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new CommitRunError(code, message, {\n branch: resolved.branch,\n commits,\n fallback: groups === undefined,\n failure: { code, message },\n remaining: effective.slice(error.commits.length + (error.uncertain ? 1 : 0)).map(group => ({ message: group.message, files: [...group.files] })),\n ...(error.uncertain ? { uncertain: [error.uncertain] } : {}),\n ...(model ? { model } : {}),\n })\n }\n const commits = describe(executed)\n if (commits.length === 0) throw new CommitRunError('no-changes', 'dsh-git-commit: nothing was committed')\n return {\n branch: resolved.branch,\n commits,\n fallback: groups === undefined,\n ...(model ? { model } : {}),\n }\n }\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport {\n SETTINGS_KEY, defaultSettings, normalizeModelRoute, type GitCommitSettings,\n} from './contracts.ts'\n\nexport { SETTINGS_KEY }\n\nconst modelRouteSchema = z.object({\n provider: z.string().max(250),\n model: z.string().max(250),\n reasoningEffort: z.string().max(80).optional(),\n}).strict()\n\n/** Version 1 stored no settings row, so a missing record fills defaults. */\nexport const settingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n model: modelRouteSchema,\n // Rows written before the flag existed read as the default: reject, do not commit.\n allowFallback: z.boolean().default(false),\n}).strict()\n\nexport const updateSettingsSchema = z.object({\n expectedRevision: z.number().int().nonnegative(),\n settings: z.object({ model: z.unknown(), allowFallback: z.unknown().optional() }).strict(),\n}).strict()\n\nexport const gitCommitDomain = defineDomain({\n name: 'dsh_editor_git_commit',\n version: 1,\n tables: {\n settings: domainTable<string, GitCommitSettings>(settingsSchema),\n },\n})\n\nexport function parseSettings(value: unknown): GitCommitSettings {\n const parsed = settingsSchema.safeParse(value)\n if (!parsed.success) return defaultSettings()\n return {\n revision: parsed.data.revision,\n model: normalizeModelRoute(parsed.data.model),\n allowFallback: parsed.data.allowFallback,\n }\n}\n\nexport type { GitCommitSettings }\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { registerModelSettings, notifyModelSettingsChanged, type ModelSettingsEvents } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport {\n PLUGIN_NAME, RPC_CHANNEL, SETTINGS_KEY, normalizeFallback, normalizeModelRoute,\n type CommitRunResult, type GitCommitSettings, type GitCommitStatus, type RpcResult,\n} from './contracts.ts'\nimport { CommitRunError, GitCommitService, type AgentStoreLike, type SessionStoreLike } from './service.ts'\nimport { gitCommitDomain, parseSettings, updateSettingsSchema } from './storage.ts'\n\nexport const name = PLUGIN_NAME\nexport const inject = ['llm', 'sessions', 'agents', 'storageDomain', 'connection', 'webServer', 'sessionProjections', 'agentDefaultModel'] as const\nexport { GitCommitService } from './service.ts'\nexport { parsePorcelain } from './git.ts'\nexport { parsePlan, buildPlanInput } from './plan.ts'\nexport { defaultSettings, normalizeFallback, normalizeModelRoute } from './contracts.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context { gitCommit: GitCommitService }\n}\n\ntype DomainTableHandle = {\n get(key: string): unknown\n put(key: string, value: unknown): Promise<void>\n}\n\ntype DomainHandle = {\n table(name: 'settings'): DomainTableHandle\n close(): Promise<void>\n}\n\ntype Host = Context & HostRpcContext & {\n llm: LlmTextCaller\n sessions: SessionStoreLike\n agents: AgentStoreLike\n storageDomain: { open: (spec: typeof gitCommitDomain) => Promise<DomainHandle> }\n}\n\nfunction stringField(payload: unknown, key: string): string | undefined {\n if (!payload || typeof payload !== 'object') return undefined\n const value = (payload as Record<string, unknown>)[key]\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\nfunction fail(error: unknown): RpcResult<never> {\n if (error instanceof CommitRunError) {\n return {\n ok: false,\n error: {\n code: error.code,\n message: error.message,\n ...(error.partial ? { partial: error.partial } : {}),\n },\n }\n }\n const message = error instanceof Error ? error.message : String(error)\n return { ok: false, error: { code: 'internal', message } }\n}\n\nexport async function handleRpc(\n service: GitCommitService,\n endpoint: string,\n payload: unknown,\n signal: AbortSignal,\n): Promise<RpcResult> {\n try {\n switch (endpoint) {\n case 'status': {\n const sessionId = stringField(payload, 'sessionId')\n if (!sessionId) return { ok: false, error: { code: 'bad-request', message: 'sessionId is required' } }\n const value: GitCommitStatus = await service.status(sessionId, signal)\n return { ok: true, value }\n }\n case 'settings': {\n await service.ready()\n return { ok: true, value: service.getSettings() }\n }\n case 'settings.update': {\n const parsed = updateSettingsSchema.safeParse(payload)\n if (!parsed.success) return { ok: false, error: { code: 'bad-request', message: 'settings patch is invalid' } }\n const value: GitCommitSettings = await service.updateSettings(\n {\n model: normalizeModelRoute(parsed.data.settings.model),\n allowFallback: normalizeFallback(parsed.data.settings.allowFallback),\n },\n parsed.data.expectedRevision,\n )\n return { ok: true, value }\n }\n case 'commit': {\n const sessionId = stringField(payload, 'sessionId')\n if (!sessionId) return { ok: false, error: { code: 'bad-request', message: 'sessionId is required' } }\n const value: CommitRunResult = await service.commit(sessionId, signal)\n return { ok: true, value }\n }\n default:\n return { ok: false, error: { code: 'not-found', message: `unknown endpoint: ${endpoint}` } }\n }\n } catch (error) {\n return fail(error)\n }\n}\n\nexport function apply(ctx: Context): void {\n const host = ctx as Host\n // Publish immediately; service operations wait for the initial stored row.\n const domainPromise = host.storageDomain.open(gitCommitDomain)\n const tablePromise = domainPromise.then(domain => domain.table('settings'))\n const service = new GitCommitService({\n plugin: PLUGIN_NAME,\n onSettingsChanged: revision => notifyModelSettingsChanged(ctx, PLUGIN_NAME, revision),\n llm: host.llm,\n host,\n sessions: host.sessions,\n agents: host.agents,\n settings: {\n load: async () => parseSettings((await tablePromise).get(SETTINGS_KEY)),\n save: async settings => { await (await tablePromise).put(SETTINGS_KEY, settings) },\n },\n })\n service.start()\n ctx.provide('gitCommit', service)\n ctx.effect(() => async () => {\n await service.dispose()\n await (await domainPromise).close()\n }, 'git-commit.lifecycle')\n ctx.effect(() => registerHostRpc(host, RPC_CHANNEL, (endpoint, payload, signal) => handleRpc(service, endpoint, payload, signal)), 'git-commit.rpc')\n ctx.effect(() => registerModelSettings(ctx as unknown as ModelSettingsEvents, {\n ns: PLUGIN_NAME, title: { zh: 'Git 提交', en: 'Git Commit' },\n async read(signal) {\n await service.ready(); signal.throwIfAborted()\n const settings = service.getSettings()\n return { revision: settings.revision, fields: [{ path: ['model'], label: { zh: '提交模型', en: 'Commit model' }, current: settings.model,\n followLabel: { zh: '当前会话 / 宿主默认模型', en: 'Current conversation / host default' }, allowFollow: true }] }\n },\n async write(path, route, expectedRevision, signal) {\n if (path.length !== 1 || path[0] !== 'model') throw new Error('Unknown Git Commit model setting')\n await service.ready(); signal.throwIfAborted()\n return service.updateSettings({ ...service.getSettings(), model: route }, expectedRevision)\n },\n }), 'git-commit.model-settings')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/git.ts","../src/plan.ts","../src/service.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { constants } from 'node:fs'\nimport { copyFile, lstat, mkdtemp, open, readFile, readlink, realpath, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { isAbsolute, join, relative, resolve } from 'node:path'\nimport { GIT_TIMEOUT_MS, MAX_UNTRACKED_PREVIEW_CHARS } from './contracts.ts'\n\nexport interface GitRunOptions {\n readonly cwd: string\n readonly signal?: AbortSignal\n readonly timeoutMs?: number\n}\n\nexport class GitError extends Error {\n readonly stderr: string\n constructor(message: string, stderr = '') {\n super(message)\n this.name = 'GitError'\n this.stderr = stderr\n }\n}\n\nexport type GitRunner = (args: readonly string[], options: GitRunOptions) => Promise<string>\n\n/** NUL separator used by `git status -z` without embedding a control byte in this source file. */\nconst NUL = String.fromCharCode(0)\n\n/** Spawn git without a shell; arguments are passed literally. */\nexport const execGit: GitRunner = (args, options) => new Promise((resolve, reject) => {\n try {\n execFile('git', [...args], {\n cwd: options.cwd,\n encoding: 'utf8',\n maxBuffer: 32 * 1024 * 1024,\n timeout: options.timeoutMs ?? GIT_TIMEOUT_MS,\n signal: options.signal,\n windowsHide: true,\n env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_LITERAL_PATHSPECS: '1' },\n }, (error, stdout, stderr) => {\n if (error) {\n if (options.signal?.aborted) {\n reject(Object.assign(new Error('dsh-git-commit: git command cancelled'), { code: 'cancelled' }))\n return\n }\n reject(Object.assign(new GitError(stderr.trim() || error.message, stderr), { code: (error as NodeJS.ErrnoException).code }))\n return\n }\n resolve(stdout)\n })\n } catch (error) {\n reject(error)\n }\n})\n\nexport function isGitMissing(error: unknown): boolean {\n return typeof error === 'object' && error !== null && (error as { code?: unknown }).code === 'ENOENT'\n}\n\nexport type ChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed' | 'copied' | 'untracked'\n\nexport interface ChangedFile {\n readonly path: string\n readonly status: ChangeStatus\n /** Rename source in porcelain -z output; committed with the destination. */\n readonly origin?: string\n /** True when the index already stages this path. */\n readonly staged: boolean\n}\n\nexport function statusLetter(kind: ChangeStatus): string {\n switch (kind) {\n case 'added': return 'A'\n case 'deleted': return 'D'\n case 'renamed': return 'R'\n case 'copied': return 'C'\n case 'untracked': return '?'\n default: return 'M'\n }\n}\n\n/**\n * Parse `git status --porcelain=v1 -z --untracked-files=all` output.\n * In -z mode rename/copy entries put the destination path first, then a NUL, then the origin path.\n */\nexport function parsePorcelain(output: string): ChangedFile[] {\n const tokens = output.split(NUL)\n const files: ChangedFile[] = []\n let index = 0\n while (index < tokens.length) {\n const token = tokens[index]\n index += 1\n if (!token) continue\n if (token.length < 4) continue\n const x = token[0] ?? ' '\n const y = token[1] ?? ' '\n const path = token.slice(3)\n if (x === '!' && y === '!') continue\n if (x === '?' && y === '?') {\n files.push({ path, status: 'untracked', staged: false })\n continue\n }\n if (x === 'R' || x === 'C' || y === 'R' || y === 'C') {\n const origin = tokens[index]\n index += 1\n if (!origin) throw new GitError('dsh-git-commit: incomplete rename status')\n files.push({ path, origin, status: x === 'C' || y === 'C' ? 'copied' : 'renamed', staged: x !== ' ' && x !== '?' })\n continue\n }\n if (x === 'U' || y === 'U' || (x === 'A' && y === 'A') || (x === 'D' && y === 'D')) {\n throw new GitError(`dsh-git-commit: resolve merge conflicts before committing (${path})`)\n }\n const status: ChangeStatus = x === 'D' || y === 'D' ? 'deleted' : x === 'A' || y === 'A' ? 'added' : 'modified'\n files.push({ path, status, staged: x !== ' ' && x !== '?' })\n }\n return files\n}\n\nexport interface RepoInfo {\n readonly root: string\n readonly branch: string\n}\n\nexport async function repoInfo(run: GitRunner, cwd: string, signal?: AbortSignal): Promise<RepoInfo> {\n const root = (await run(['rev-parse', '--show-toplevel'], { cwd, signal })).trim()\n let branch = (await run(['symbolic-ref', '--quiet', '--short', 'HEAD'], { cwd: root, signal }).catch(() => '')).trim()\n if (!branch) branch = (await run(['rev-parse', '--short', 'HEAD'], { cwd: root, signal }).catch(() => '')).trim()\n return { root, branch: branch || 'HEAD' }\n}\n\nexport async function workingTreeChanges(run: GitRunner, root: string, signal?: AbortSignal): Promise<ChangedFile[]> {\n const output = await run(['status', '--porcelain=v1', '-z', '--untracked-files=all'], { cwd: root, signal })\n return parsePorcelain(output)\n}\n\n/** Exact index/worktree content fingerprint, including untracked file bytes. */\nexport async function workspaceSnapshot(run: GitRunner, root: string, signal?: AbortSignal): Promise<{ files: ChangedFile[]; fingerprint: string }> {\n const status = await run(['status', '--porcelain=v1', '-z', '--untracked-files=all'], { cwd: root, signal })\n const files = parsePorcelain(status)\n const hash = createHash('sha256').update(status)\n hash.update(await run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: root, signal }).catch(() => 'DETACHED'))\n hash.update(await run(['rev-parse', 'HEAD'], { cwd: root, signal }).catch(() => 'unborn'))\n hash.update(await run(['diff', '--cached', '--binary', '--no-ext-diff'], { cwd: root, signal }))\n hash.update(await run(['diff', '--binary', '--no-ext-diff'], { cwd: root, signal }))\n for (const file of files) {\n if (file.status !== 'untracked') continue\n hash.update(file.path)\n hash.update(await worktreePathDigest(run, root, file.path, signal))\n }\n return { files, fingerprint: hash.digest('hex') }\n}\n\n/** A worktree content check for groups waiting behind earlier commits. */\nexport async function pathVersions(run: GitRunner, root: string, paths: readonly string[], signal?: AbortSignal): Promise<string> {\n const hash = createHash('sha256')\n for (const path of paths) {\n hash.update(path)\n hash.update(await worktreePathDigest(run, root, path, signal))\n }\n return hash.digest('hex')\n}\n\nasync function worktreePathDigest(run: GitRunner, root: string, path: string, signal?: AbortSignal): Promise<string> {\n const absolute = resolve(root, path)\n let stat\n try {\n stat = await lstat(absolute)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing'\n throw error\n }\n const hash = createHash('sha256').update(String(stat.mode))\n if (stat.isSymbolicLink()) hash.update(await readlink(absolute))\n else if (stat.isFile()) {\n const handle = await open(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))\n try {\n if (!(await handle.stat()).isFile() || !(await lstat(absolute)).isFile()) throw new GitError(`dsh-git-commit: file changed while reading (${path})`)\n for await (const chunk of handle.createReadStream({ autoClose: false })) {\n signal?.throwIfAborted()\n hash.update(chunk)\n }\n if (!(await lstat(absolute)).isFile()) throw new GitError(`dsh-git-commit: file changed while reading (${path})`)\n } finally {\n await handle.close()\n }\n } else if (stat.isDirectory()) {\n hash.update(await run(['-C', absolute, 'rev-parse', 'HEAD'], { cwd: root, signal }))\n hash.update(await run(['-C', absolute, 'status', '--porcelain=v1', '-z'], { cwd: root, signal }))\n }\n return hash.digest('hex')\n}\n\nexport async function recentSubjects(run: GitRunner, root: string, count: number, signal?: AbortSignal): Promise<string[]> {\n const output = await run(['log', `-${count}`, '--pretty=%s'], { cwd: root, signal }).catch(() => '')\n return output.split('\\n').map(line => line.trim()).filter(Boolean)\n}\n\n/** Combined staged+worktree stat and diff against HEAD for the given paths. */\nexport async function diffAgainstHead(\n run: GitRunner, root: string, paths: readonly string[], signal?: AbortSignal,\n): Promise<{ stat: string; diff: string }> {\n const args = paths.length > 0 ? ['--', ...paths] : []\n const stat = await run(['diff', 'HEAD', '--stat', '--no-color', ...args], { cwd: root, signal }).catch(() => '')\n const diff = await run(['diff', 'HEAD', '--no-color', '--no-ext-diff', ...args], { cwd: root, signal }).catch(() => '')\n return { stat, diff }\n}\n\nexport interface UntrackedPreview {\n readonly path: string\n readonly preview: string\n readonly binary: boolean\n}\n\nexport async function untrackedPreview(root: string, path: string): Promise<UntrackedPreview> {\n try {\n const absolute = resolve(root, path)\n const rootReal = await realpath(root)\n const withinRoot = async () => {\n const target = await realpath(absolute)\n const rel = relative(rootReal, target)\n return rel !== '' && rel !== '..' && !rel.startsWith(`..${String.fromCharCode(47)}`)\n && !rel.startsWith(`..${String.fromCharCode(92)}`) && !isAbsolute(rel)\n }\n if (!await withinRoot()) return { path, preview: '', binary: true }\n if (!(await lstat(absolute)).isFile()) return { path, preview: '', binary: true }\n const handle = await open(absolute, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))\n let buffer: Buffer\n try {\n const bounded = Buffer.alloc(MAX_UNTRACKED_PREVIEW_CHARS * 4)\n const { bytesRead } = await handle.read(bounded, 0, bounded.length, 0)\n buffer = bounded.subarray(0, bytesRead)\n } finally {\n await handle.close()\n }\n if (!await withinRoot() || !(await lstat(absolute)).isFile()) return { path, preview: '', binary: true }\n if (buffer.includes(0)) return { path, preview: '', binary: true }\n const text = buffer.toString('utf8')\n return { path, preview: text.slice(0, MAX_UNTRACKED_PREVIEW_CHARS), binary: false }\n } catch {\n return { path, preview: '', binary: true }\n }\n}\n\nexport interface ExecutedCommit {\n readonly hash: string\n readonly skipped: boolean\n}\n\n/** Known commits or an uncertain current group must survive a rejected run. */\nexport class PartialCommitError extends Error {\n readonly commits: readonly ExecutedCommit[]\n readonly cause: unknown\n readonly uncertain?: { readonly message: string; readonly files: readonly string[] }\n constructor(commits: readonly ExecutedCommit[], cause: unknown, uncertain?: { readonly message: string; readonly files: readonly string[] }) {\n super(cause instanceof Error ? cause.message : String(cause))\n this.name = 'PartialCommitError'\n this.commits = commits\n this.cause = cause\n this.uncertain = uncertain\n }\n}\n\nasync function indexBytes(path: string): Promise<Buffer | undefined> {\n try { return await readFile(path) }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n}\n\nasync function headOid(run: GitRunner, root: string): Promise<string> {\n try { return (await run(['rev-parse', '--verify', 'HEAD'], { cwd: root })).trim() }\n catch (error) {\n if (error instanceof GitError && /Needed a single revision|not a valid object name HEAD/u.test(error.stderr)) return ''\n throw error\n }\n}\n\n/**\n * Commit each group from the worktree. `--only` keeps other staged paths out of\n * this commit; an exact index backup protects the current group on failure.\n */\nexport async function commitGroups(\n run: GitRunner,\n root: string,\n groups: ReadonlyArray<{ message: string; files: readonly string[] }>,\n selected: readonly ChangedFile[],\n signal?: AbortSignal,\n verify?: (paths: readonly string[]) => Promise<void>,\n): Promise<ExecutedCommit[]> {\n const commits: ExecutedCommit[] = []\n let partialFailure: PartialCommitError | undefined\n const fail = (error: unknown): never => {\n if (error instanceof PartialCommitError) { partialFailure = error; throw error }\n if (commits.length === 0) throw error\n partialFailure = new PartialCommitError(commits, error)\n throw partialFailure\n }\n for (const group of groups) {\n if (group.files.length === 0) continue\n const paths = group.files.flatMap(path => {\n const file = selected.find(file => file.path === path)\n return file?.status === 'renamed' && file.origin ? [path, file.origin] : [path]\n })\n let backupDir: string | undefined\n try {\n signal?.throwIfAborted()\n await verify?.(paths)\n const gitPath = (await run(['rev-parse', '--git-path', 'index'], { cwd: root, signal })).trim()\n const indexPath = isAbsolute(gitPath) ? gitPath : resolve(root, gitPath)\n backupDir = await mkdtemp(join(tmpdir(), 'dsh-git-commit-'))\n const backupPath = join(backupDir, 'index')\n let hadIndex = true\n try {\n await copyFile(indexPath, backupPath)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n hadIndex = false\n }\n const head = await headOid(run, root)\n const originalIndex = await indexBytes(indexPath)\n if (hadIndex ? !originalIndex?.equals(await readFile(backupPath)) : originalIndex !== undefined) {\n throw new GitError('dsh-git-commit: index changed before committing; retry')\n }\n let stagedIndex = originalIndex\n let committed = false\n let commitInvoked = false\n try {\n signal?.throwIfAborted()\n await run(['add', '-A', '--', ...group.files], { cwd: root, signal })\n stagedIndex = await indexBytes(indexPath)\n signal?.throwIfAborted()\n commitInvoked = true\n await run(['commit', '--quiet', '--only', '-m', group.message, '--', ...paths], { cwd: root, signal })\n committed = true\n let hash = ''\n try {\n // Result collection and index cleanup must survive request cancellation/unload.\n hash = (await run(['rev-parse', '--short', 'HEAD'], { cwd: root })).trim()\n } catch (error) {\n // The commit object exists. A failed hash read must not look like \"nothing was committed\".\n commits.push({ hash: '', skipped: false })\n throw error\n }\n if (!hash) {\n commits.push({ hash: '', skipped: false })\n throw new GitError('dsh-git-commit: commit was created but its hash could not be read')\n }\n commits.push({ hash, skipped: false })\n } catch (error) {\n if (!committed) {\n let currentHead: string\n try { currentHead = await headOid(run, root) }\n catch {\n // A failed inspection cannot prove the rejected commit left HEAD\n // unchanged. Keep its original diagnostic and do not restore.\n if (commitInvoked) throw new PartialCommitError(commits, error, { message: group.message, files: [...group.files] })\n throw error\n }\n if (currentHead !== head) {\n // The rejected command may have completed, or another owner may have\n // advanced HEAD. Preserve both HEAD and index without attributing it.\n if (commitInvoked) throw new PartialCommitError(commits, error, { message: group.message, files: [...group.files] })\n throw error\n }\n const currentIndex = await indexBytes(indexPath)\n const indexUntouched = currentIndex === undefined ? stagedIndex === undefined : currentIndex.equals(stagedIndex ?? Buffer.alloc(0))\n if (currentHead === head && indexUntouched) {\n if (hadIndex) await copyFile(backupPath, indexPath)\n else await rm(indexPath, { force: true })\n }\n }\n // A created commit stays. Only this group's uncommitted index is eligible for restore.\n throw error\n }\n } catch (error) {\n fail(error)\n } finally {\n if (backupDir) {\n try { await rm(backupDir, { recursive: true, force: true }) }\n catch (error) { fail(partialFailure ?? error) }\n }\n }\n }\n return commits\n}\n\n/** One deterministic commit for every selected path; used when the model plan is unavailable. */\nexport function fallbackGroup(files: readonly ChangedFile[], localeHint: 'zh' | 'en'): { message: string; files: readonly string[] } {\n const message = localeHint === 'zh'\n ? `chore: 提交 ${files.length} 个文件的修改`\n : `chore: commit changes to ${files.length} file${files.length === 1 ? '' : 's'}`\n return { message, files: files.map(file => file.path) }\n}\n","import { MAX_MANIFEST_CHARS, MAX_MESSAGE_CHARS } from './contracts.ts'\nimport { statusLetter, type ChangedFile, type UntrackedPreview } from './git.ts'\n\nexport interface PlanGroup {\n readonly message: string\n readonly files: readonly string[]\n}\n\nexport interface PlanInput {\n readonly files: readonly ChangedFile[]\n readonly recentSubjects: readonly string[]\n readonly stat: string\n readonly diff: string\n readonly untracked: readonly UntrackedPreview[]\n}\n\nexport function planSystemPrompt(): string {\n return [\n 'You split a git working-tree change set into a small number of coherent commits.',\n 'Return exactly one JSON object: {\"groups\":[{\"message\":\"<commit message>\",\"files\":[\"<path>\",...]}]}.',\n 'Rules:',\n '- Every listed file must appear in exactly one group; do not invent paths.',\n '- One to five groups. Group files that belong to the same logical change.',\n '- Order groups so earlier commits do not depend on later ones.',\n '- Each message is a single line of at most 72 characters, no trailing period.',\n '- Match the language and style of the recent commit subjects when provided; otherwise use English conventional-commit style.',\n '- Return no Markdown, code fence, or explanation.',\n ].join('\\n')\n}\n\nfunction truncateMiddle(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text\n const half = Math.floor((maxChars - 40) / 2)\n return `${text.slice(0, half)}\\n… [${text.length - maxChars} chars omitted] …\\n${text.slice(-half)}`\n}\n\nexport function buildPlanInput(input: PlanInput): string {\n const sections: string[] = []\n sections.push('Scope: all current workspace changes of the repository.')\n if (input.recentSubjects.length > 0) {\n sections.push(['Recent commit subjects (style reference):', ...input.recentSubjects.map(s => `- ${s}`)].join('\\n'))\n }\n const manifest = input.files.map(file => `- ${statusLetter(file.status)} ${file.origin ? `${file.origin} ${file.status === 'copied' ? '=>' : '->'} ` : ''}${file.path}`).join('\\n')\n sections.push(`Changed files:\\n${manifest}`)\n if (input.stat.trim()) sections.push(`Diff stat:\\n${input.stat.trim()}`)\n if (input.diff.trim()) sections.push(`Diff against HEAD:\\n${input.diff}`)\n for (const untracked of input.untracked) {\n sections.push(untracked.binary\n ? `New binary file: ${untracked.path}`\n : `New file ${untracked.path}:\\n${untracked.preview}`)\n }\n return truncateMiddle(sections.join('\\n\\n'), MAX_MANIFEST_CHARS)\n}\n\nexport function sanitizeMessage(raw: unknown): string | undefined {\n if (typeof raw !== 'string') return undefined\n const line = raw.replace(/\\s+/gu, ' ').trim().replace(/\\.+$/u, '')\n if (!line) return undefined\n return line.length > MAX_MESSAGE_CHARS ? line.slice(0, MAX_MESSAGE_CHARS).trim() : line\n}\n\n/** Extract the first balanced top-level JSON object from model output. */\nexport function extractJsonObject(text: string): string | undefined {\n const fenced = /```(?:json)?\\s*([\\s\\S]*?)```/u.exec(text)\n const candidate = fenced?.[1] ?? text\n const start = candidate.indexOf('{')\n if (start === -1) return undefined\n let depth = 0\n let inString = false\n let escaped = false\n for (let index = start; index < candidate.length; index += 1) {\n const char = candidate[index]\n if (inString) {\n if (escaped) escaped = false\n else if (char === '\\\\') escaped = true\n else if (char === '\"') inString = false\n continue\n }\n if (char === '\"') inString = true\n else if (char === '{') depth += 1\n else if (char === '}') {\n depth -= 1\n if (depth === 0) return candidate.slice(start, index + 1)\n }\n }\n return undefined\n}\n\n/**\n * Validate the model plan against the exact change set. Returns undefined when\n * the output is malformed, covers the wrong files, or duplicates a file.\n */\nexport function parsePlan(text: string, files: readonly ChangedFile[]): PlanGroup[] | undefined {\n const json = extractJsonObject(text)\n if (!json) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(json)\n } catch {\n return undefined\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined\n const groups = (parsed as { groups?: unknown }).groups\n if (!Array.isArray(groups) || groups.length === 0 || groups.length > 8) return undefined\n const allowed = new Set(files.map(file => file.path))\n const seen = new Set<string>()\n const plan: PlanGroup[] = []\n for (const group of groups) {\n if (!group || typeof group !== 'object') return undefined\n const message = sanitizeMessage((group as { message?: unknown }).message)\n const paths = (group as { files?: unknown }).files\n if (!message || !Array.isArray(paths) || paths.length === 0) return undefined\n const groupFiles: string[] = []\n for (const path of paths) {\n if (typeof path !== 'string' || !allowed.has(path) || seen.has(path)) return undefined\n seen.add(path)\n groupFiles.push(path)\n }\n plan.push({ message, files: groupFiles })\n }\n if (seen.size !== allowed.size) return undefined\n return plan\n}\n","import { callLlmText, resolveFeatureModel, type LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { modelRouteOverride } from '@klarkxy/dsh-model-route'\nimport {\n DEFAULT_MAX_OUTPUT_TOKENS, RECENT_LOG_COUNT,\n defaultSettings, normalizeFallback, type CommitGroupResult, type CommitModelInfo, type CommitRunResult,\n type GitCommitSettings, type GitCommitStatus, type PartialCommitResult,\n} from './contracts.ts'\nimport {\n commitGroups, diffAgainstHead, execGit, fallbackGroup, isGitMissing, PartialCommitError, pathVersions, recentSubjects, repoInfo,\n untrackedPreview, workspaceSnapshot, workingTreeChanges, type ChangedFile, type ExecutedCommit, type GitRunner, type UntrackedPreview,\n} from './git.ts'\nimport { buildPlanInput, parsePlan, planSystemPrompt, type PlanGroup } from './plan.ts'\n\nexport interface SessionLike {\n readonly id: string\n readonly header?: { readonly cwd?: string }\n}\n\nexport interface SessionStoreLike {\n get(id: string): SessionLike | undefined\n}\n\nexport interface AgentStoreLike {\n get(id: string): { readonly status?: string } | undefined\n}\n\nexport interface GitCommitServiceDeps {\n readonly plugin: string\n readonly onSettingsChanged?: (revision: number) => void\n readonly llm: LlmTextCaller\n readonly host?: unknown\n readonly sessions: SessionStoreLike\n readonly workingDirectory: { get(session: SessionLike): string }\n readonly agents?: AgentStoreLike\n readonly run?: GitRunner\n readonly preview?: (root: string, path: string) => Promise<UntrackedPreview>\n /** Plugin-page settings row; absent keeps the shared purpose route. */\n readonly settings?: {\n load(): Promise<GitCommitSettings>\n save(settings: GitCommitSettings): Promise<void>\n }\n}\n\nexport class CommitRunError extends Error {\n readonly code: string\n /** Commits already created when this run stopped. Absent when nothing was written. */\n readonly partial?: PartialCommitResult\n constructor(code: string, message: string, partial?: PartialCommitResult) {\n super(message)\n this.name = code === 'cancelled' ? 'AbortError' : 'CommitRunError'\n this.code = code\n this.partial = partial\n }\n}\n\nfunction modelInfo(route: { provider: string; model: string } | undefined, source: CommitModelInfo['source']): CommitModelInfo | undefined {\n if (!route?.provider || !route.model) return undefined\n return { provider: route.provider, model: route.model, source }\n}\n\nfunction looksChinese(subjects: readonly string[]): boolean {\n return subjects.some(subject => /[一-鿿]/u.test(subject))\n}\n\nfunction normalizeCwd(cwd: string | undefined): string | undefined {\n if (typeof cwd !== 'string') return undefined\n const normalized = cwd.trim().replaceAll('\\\\', '/')\n if (!normalized) return undefined\n return normalized === '/' || /^[A-Za-z]:\\/$/.test(normalized) ? normalized : normalized.replace(/\\/+$/, '')\n}\n\nexport class GitCommitService {\n private readonly deps: GitCommitServiceDeps\n private readonly run: GitRunner\n private readonly preview: (root: string, path: string) => Promise<UntrackedPreview>\n private running: Promise<unknown> | undefined\n private settings: GitCommitSettings\n private settingsPending: Promise<void> = Promise.resolve()\n private readonly settingsReady: Promise<void>\n private settingsFailure: CommitRunError | undefined\n private settingsAdopted = false\n private disposed = false\n /** Aborted on unload so an in-flight model plan receives the same signal as a cancelled request. */\n private readonly lifetime = new AbortController()\n\n constructor(deps: GitCommitServiceDeps) {\n this.deps = deps\n this.run = deps.run ?? execGit\n this.preview = deps.preview ?? untrackedPreview\n this.settings = defaultSettings()\n // A missing row is resolved by storage; an unreadable row must not select a different model.\n this.settingsReady = deps.settings?.load().then(\n loaded => { if (loaded && !this.settingsAdopted) this.settings = structuredClone(loaded) },\n () => {\n if (!this.settingsAdopted) this.settingsFailure = new CommitRunError(\n 'settings-unavailable', 'dsh-git-commit: saved settings could not be read; reload the plugin before retrying',\n )\n },\n ) ?? Promise.resolve()\n }\n\n /** A refreshed row wins over a pending initial read. */\n adoptSettings(next: GitCommitSettings): void {\n if (this.disposed) throw new CommitRunError('disposed', 'dsh-git-commit: plugin disabled')\n this.settingsAdopted = true\n this.settingsFailure = undefined\n this.settings = structuredClone(next)\n }\n\n /** Existing synchronous snapshot API; host operations wait for initialization first. */\n getSettings(): GitCommitSettings {\n this.workSignal()\n if (this.settingsFailure) throw this.settingsFailure\n return structuredClone(this.settings)\n }\n\n async ready(): Promise<void> {\n this.workSignal()\n await this.settingsReady\n this.workSignal()\n if (this.settingsFailure) throw this.settingsFailure\n }\n\n /** Request cancellation and plugin unload share one signal. A disposed service rejects before any new work. */\n private workSignal(signal?: AbortSignal): AbortSignal {\n if (this.disposed || this.lifetime.signal.aborted) {\n throw new CommitRunError('disposed', 'dsh-git-commit: plugin is no longer active')\n }\n return signal ? AbortSignal.any([signal, this.lifetime.signal]) : this.lifetime.signal\n }\n\n private async waitForSettings(signal?: AbortSignal): Promise<void> {\n signal?.throwIfAborted()\n if (!signal) { await this.ready(); return }\n let onAbort: () => void = () => {}\n const aborted = new Promise<never>((_, reject) => {\n onAbort = () => {\n try { signal.throwIfAborted() }\n catch (error) { reject(error) }\n }\n signal.addEventListener('abort', onAbort, { once: true })\n if (signal.aborted) onAbort()\n })\n try { await Promise.race([this.settingsReady, aborted]) }\n finally { signal.removeEventListener('abort', onAbort) }\n signal.throwIfAborted()\n if (this.settingsFailure) throw this.settingsFailure\n }\n\n private selectedModel(sessionId: string) {\n const page = modelRouteOverride(this.settings.model)\n return { route: resolveFeatureModel(this.deps.host, page, sessionId), source: page ? 'page' as const : 'default' as const }\n }\n\n async updateSettings(patch: Omit<GitCommitSettings, 'revision'>, expectedRevision: number): Promise<GitCommitSettings> {\n const detached = structuredClone(patch)\n const result = this.settingsPending.then(async () => {\n await this.ready()\n if (this.disposed) throw new CommitRunError('disposed', 'dsh-git-commit: plugin disabled')\n if (expectedRevision !== this.settings.revision) {\n throw new CommitRunError('stale', 'dsh-git-commit: settings changed; reload and retry')\n }\n const next: GitCommitSettings = {\n revision: expectedRevision + 1,\n model: detached.model,\n allowFallback: normalizeFallback(detached.allowFallback),\n }\n await this.deps.settings?.save(next)\n this.settings = next\n try { this.deps.onSettingsChanged?.(next.revision) } catch {}\n return structuredClone(next)\n })\n this.settingsPending = result.then(() => {}, () => {})\n return result\n }\n\n start(): void {}\n\n async dispose(): Promise<void> {\n this.disposed = true\n this.lifetime.abort()\n await this.settingsReady\n await this.settingsPending\n await this.running?.catch(() => {})\n }\n\n private async resolveRepo(sessionId: string, signal?: AbortSignal): Promise<{ root: string; branch: string }> {\n const session = this.deps.sessions.get(sessionId)\n if (!session) throw new CommitRunError('no-session', 'dsh-git-commit: session is not live on this host')\n const cwd = normalizeCwd(this.deps.workingDirectory.get(session))\n if (!cwd) throw new CommitRunError('no-cwd', 'dsh-git-commit: session has no working directory')\n try {\n return await repoInfo(this.run, cwd, signal)\n } catch (error) {\n signal?.throwIfAborted()\n if (isGitMissing(error)) throw new CommitRunError('no-git', 'dsh-git-commit: git executable not found')\n throw new CommitRunError('not-a-repo', 'dsh-git-commit: session directory is not inside a git repository')\n }\n }\n\n async status(sessionId: string, signal?: AbortSignal): Promise<GitCommitStatus> {\n const work = this.workSignal(signal)\n await this.waitForSettings(work)\n let resolved\n try {\n resolved = await this.resolveRepo(sessionId, work)\n } catch (error) {\n if (error instanceof CommitRunError) {\n return { available: false, reason: error.code, detail: error.message, workspace: { files: 0 } }\n }\n throw error\n }\n const changes = await workingTreeChanges(this.run, resolved.root, work)\n work.throwIfAborted()\n const selected = this.selectedModel(sessionId)\n const model = modelInfo(selected.route, selected.source)\n return {\n available: true,\n root: resolved.root,\n branch: resolved.branch,\n workspace: { files: changes.length },\n ...(model ? { model } : {}),\n }\n }\n\n async commit(sessionId: string, signal?: AbortSignal): Promise<CommitRunResult> {\n if (this.disposed || this.lifetime.signal.aborted) {\n throw new CommitRunError('disposed', 'dsh-git-commit: plugin is no longer active')\n }\n if (this.running) throw new CommitRunError('busy', 'dsh-git-commit: a commit run is already in progress')\n const work = this.workSignal(signal)\n const run = this.runCommit(sessionId, work)\n this.running = run\n try {\n return await run\n } catch (error) {\n if (error instanceof CommitRunError && error.partial) throw error\n if (work.aborted) throw new CommitRunError('cancelled', 'dsh-git-commit: commit run cancelled')\n throw error\n } finally {\n this.running = undefined\n }\n }\n\n private async runCommit(sessionId: string, signal: AbortSignal): Promise<CommitRunResult> {\n await this.waitForSettings(signal)\n if (this.disposed || signal?.aborted) throw new CommitRunError('cancelled', 'dsh-git-commit: plugin is no longer active')\n const session = this.deps.sessions.get(sessionId)\n const sessionCwd = session ? normalizeCwd(this.deps.workingDirectory.get(session)) : undefined\n const resolved = await this.resolveRepo(sessionId, signal)\n const assertReady = () => {\n signal?.throwIfAborted()\n if (this.disposed || !this.deps.llm) throw new CommitRunError('cancelled', 'dsh-git-commit: plugin is no longer active')\n const currentSession = this.deps.sessions.get(sessionId)\n if (currentSession !== session || (currentSession ? normalizeCwd(this.deps.workingDirectory.get(currentSession)) : undefined) !== sessionCwd) {\n throw new CommitRunError('session-changed', 'dsh-git-commit: session workspace changed during planning')\n }\n if (this.deps.agents?.get(sessionId)?.status === 'running') {\n throw new CommitRunError('session-running', 'dsh-git-commit: wait for the session turn to finish')\n }\n }\n assertReady()\n const initial = await workspaceSnapshot(this.run, resolved.root, signal)\n const branchRef = await this.run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: resolved.root, signal }).catch(() => 'DETACHED')\n const selected: ChangedFile[] = initial.files\n if (selected.length === 0) {\n throw new CommitRunError('no-changes', 'dsh-git-commit: working tree is clean')\n }\n const paths = selected.flatMap(file => file.status === 'renamed' && file.origin ? [file.path, file.origin] : [file.path])\n const originalVersions = await pathVersions(this.run, resolved.root, paths, signal)\n const [{ stat, diff }, subjects] = await Promise.all([\n diffAgainstHead(this.run, resolved.root, paths, signal),\n recentSubjects(this.run, resolved.root, RECENT_LOG_COUNT, signal),\n ])\n const untracked: UntrackedPreview[] = []\n for (const file of selected) {\n assertReady()\n if (file.status === 'untracked') untracked.push(await this.preview(resolved.root, file.path))\n }\n\n let groups: PlanGroup[] | undefined\n let planFailure = 'no model route was available'\n let model: CommitModelInfo | undefined\n const planned = this.selectedModel(sessionId)\n if (planned.route) {\n try {\n const result = await callLlmText(this.deps.llm, {\n plugin: this.deps.plugin,\n route: planned.route,\n system: planSystemPrompt(),\n text: buildPlanInput({ files: selected, recentSubjects: subjects, stat, diff, untracked }),\n maxTokens: DEFAULT_MAX_OUTPUT_TOKENS,\n signal,\n sessionId,\n })\n model = modelInfo(result, planned.source)\n groups = parsePlan(result.text, selected)\n if (!groups) planFailure = 'the model returned a plan that does not cover the change set'\n } catch (error) {\n assertReady()\n if (error instanceof CommitRunError) throw error\n planFailure = error instanceof Error ? error.message : String(error)\n }\n }\n\n // Without a plan the run would write commits nobody asked for, so it stops\n // here — before the index or the worktree is touched. A single fallback\n // commit stays available, but only when the settings row asked for it.\n if (!groups && !this.settings.allowFallback) {\n throw new CommitRunError('plan-unavailable', `dsh-git-commit: no commit plan (${planFailure})`)\n }\n\n assertReady()\n const current = await workspaceSnapshot(this.run, resolved.root, signal)\n if (current.fingerprint !== initial.fingerprint) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: workspace changed during planning; retry the commit')\n }\n const effective = groups ?? [fallbackGroup(selected, looksChinese(subjects) ? 'zh' : 'en')]\n const describe = (executed: readonly ExecutedCommit[]): CommitGroupResult[] => {\n const commits: CommitGroupResult[] = []\n for (let index = 0; index < executed.length; index += 1) {\n const result = executed[index]\n const group = effective[index]\n if (!result || !group || result.skipped) continue\n commits.push({ hash: result.hash, message: group.message, files: [...group.files] })\n }\n return commits\n }\n let executed: ExecutedCommit[]\n try {\n const commitRun: GitRunner = (args, options) => {\n // Async index preparation can yield after verification. Recheck the\n // session immediately before either Git mutation is dispatched.\n if (args[0] === 'add' || args[0] === 'commit') assertReady()\n return this.run(args, options)\n }\n executed = await commitGroups(commitRun, resolved.root, effective, selected, signal, async () => {\n assertReady()\n const currentBranchRef = await this.run(['symbolic-ref', '--quiet', 'HEAD'], { cwd: resolved.root, signal }).catch(() => 'DETACHED')\n if (currentBranchRef !== branchRef) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: branch changed during the commit run')\n }\n if (await pathVersions(this.run, resolved.root, paths, signal) !== originalVersions) {\n throw new CommitRunError('workspace-changed', 'dsh-git-commit: selected file changed during the commit run')\n }\n // A new merge conflict must never be passed to git add or git commit.\n await workingTreeChanges(this.run, resolved.root, signal)\n assertReady()\n })\n } catch (error) {\n if (!(error instanceof PartialCommitError)) throw error\n const commits = describe(error.commits)\n const cause = error.cause\n const cancelled = signal?.aborted\n || (typeof cause === 'object' && cause !== null && (cause as { code?: unknown }).code === 'cancelled')\n || (cause instanceof Error && cause.name === 'AbortError')\n const code = cause instanceof CommitRunError ? cause.code : cancelled ? 'cancelled' : 'commit-failed'\n const message = cause instanceof Error ? cause.message : String(cause)\n throw new CommitRunError(code, message, {\n branch: resolved.branch,\n commits,\n fallback: groups === undefined,\n failure: { code, message },\n remaining: effective.slice(error.commits.length + (error.uncertain ? 1 : 0)).map(group => ({ message: group.message, files: [...group.files] })),\n ...(error.uncertain ? { uncertain: [error.uncertain] } : {}),\n ...(model ? { model } : {}),\n })\n }\n const commits = describe(executed)\n if (commits.length === 0) throw new CommitRunError('no-changes', 'dsh-git-commit: nothing was committed')\n return {\n branch: resolved.branch,\n commits,\n fallback: groups === undefined,\n ...(model ? { model } : {}),\n }\n }\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport {\n SETTINGS_KEY, defaultSettings, normalizeModelRoute, type GitCommitSettings,\n} from './contracts.ts'\n\nexport { SETTINGS_KEY }\n\nconst modelRouteSchema = z.object({\n provider: z.string().max(250),\n model: z.string().max(250),\n reasoningEffort: z.string().max(80).optional(),\n}).strict()\n\n/** Version 1 stored no settings row, so a missing record fills defaults. */\nexport const settingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n model: modelRouteSchema,\n // Rows written before the flag existed read as the default: reject, do not commit.\n allowFallback: z.boolean().default(false),\n}).strict()\n\nexport const updateSettingsSchema = z.object({\n expectedRevision: z.number().int().nonnegative(),\n settings: z.object({ model: z.unknown(), allowFallback: z.unknown().optional() }).strict(),\n}).strict()\n\nexport const gitCommitDomain = defineDomain({\n name: 'dsh_editor_git_commit',\n version: 1,\n tables: {\n settings: domainTable<string, GitCommitSettings>(settingsSchema),\n },\n})\n\nexport function parseSettings(value: unknown): GitCommitSettings {\n const parsed = settingsSchema.safeParse(value)\n if (!parsed.success) return defaultSettings()\n return {\n revision: parsed.data.revision,\n model: normalizeModelRoute(parsed.data.model),\n allowFallback: parsed.data.allowFallback,\n }\n}\n\nexport type { GitCommitSettings }\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { modelSettingsChangesIdentity } from '@klarkxy/dsh-plugin-kit/contracts'\nimport { createModelSettingsChanges } from '@klarkxy/dsh-plugin-kit/model-settings-changes'\nimport { registerModelSettings, notifyModelSettingsChanged, type ModelSettingsEvents } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport {\n PLUGIN_NAME, RPC_CHANNEL, SETTINGS_KEY, normalizeFallback, normalizeModelRoute,\n type CommitRunResult, type GitCommitSettings, type GitCommitStatus, type RpcResult,\n} from './contracts.ts'\nimport { CommitRunError, GitCommitService, type AgentStoreLike, type GitCommitServiceDeps, type SessionStoreLike } from './service.ts'\nimport { gitCommitDomain, parseSettings, updateSettingsSchema } from './storage.ts'\n\nexport const name = PLUGIN_NAME\nexport const inject = ['llm', 'sessions', 'workingDirectory', 'agents', 'storageDomain', 'connection', 'webServer', 'sessionProjections', 'agentDefaultModel'] as const\nexport { GitCommitService } from './service.ts'\nexport { parsePorcelain } from './git.ts'\nexport { parsePlan, buildPlanInput } from './plan.ts'\nexport { defaultSettings, normalizeFallback, normalizeModelRoute } from './contracts.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context { gitCommit: GitCommitService }\n}\n\ntype DomainTableHandle = {\n get(key: string): unknown\n put(key: string, value: unknown): Promise<void>\n}\n\ntype DomainHandle = {\n table(name: 'settings'): DomainTableHandle\n close(): Promise<void>\n}\n\ntype Host = Context & HostRpcContext & {\n llm: LlmTextCaller\n sessions: SessionStoreLike\n workingDirectory: GitCommitServiceDeps['workingDirectory']\n agents: AgentStoreLike\n storageDomain: { open: (spec: typeof gitCommitDomain) => Promise<DomainHandle> }\n}\n\nfunction stringField(payload: unknown, key: string): string | undefined {\n if (!payload || typeof payload !== 'object') return undefined\n const value = (payload as Record<string, unknown>)[key]\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\nfunction fail(error: unknown): RpcResult<never> {\n if (error instanceof CommitRunError) {\n return {\n ok: false,\n error: {\n code: error.code,\n message: error.message,\n ...(error.partial ? { partial: error.partial } : {}),\n },\n }\n }\n const message = error instanceof Error ? error.message : String(error)\n return { ok: false, error: { code: 'internal', message } }\n}\n\nexport async function handleRpc(\n service: GitCommitService,\n endpoint: string,\n payload: unknown,\n signal: AbortSignal,\n): Promise<RpcResult> {\n try {\n switch (endpoint) {\n case 'status': {\n const sessionId = stringField(payload, 'sessionId')\n if (!sessionId) return { ok: false, error: { code: 'bad-request', message: 'sessionId is required' } }\n const value: GitCommitStatus = await service.status(sessionId, signal)\n return { ok: true, value }\n }\n case 'settings': {\n await service.ready()\n return { ok: true, value: service.getSettings() }\n }\n case 'settings.update': {\n const parsed = updateSettingsSchema.safeParse(payload)\n if (!parsed.success) return { ok: false, error: { code: 'bad-request', message: 'settings patch is invalid' } }\n const value: GitCommitSettings = await service.updateSettings(\n {\n model: normalizeModelRoute(parsed.data.settings.model),\n allowFallback: normalizeFallback(parsed.data.settings.allowFallback),\n },\n parsed.data.expectedRevision,\n )\n return { ok: true, value }\n }\n case 'commit': {\n const sessionId = stringField(payload, 'sessionId')\n if (!sessionId) return { ok: false, error: { code: 'bad-request', message: 'sessionId is required' } }\n const value: CommitRunResult = await service.commit(sessionId, signal)\n return { ok: true, value }\n }\n default:\n return { ok: false, error: { code: 'not-found', message: `unknown endpoint: ${endpoint}` } }\n }\n } catch (error) {\n return fail(error)\n }\n}\n\nexport function apply(ctx: Context): void {\n createModelSettingsChanges(ctx, modelSettingsChangesIdentity(PLUGIN_NAME), PLUGIN_NAME)\n const host = ctx as Host\n // Publish immediately; service operations wait for the initial stored row.\n const domainPromise = host.storageDomain.open(gitCommitDomain)\n const tablePromise = domainPromise.then(domain => domain.table('settings'))\n const service = new GitCommitService({\n plugin: PLUGIN_NAME,\n onSettingsChanged: revision => notifyModelSettingsChanged(ctx, PLUGIN_NAME, revision),\n llm: host.llm,\n host,\n sessions: host.sessions,\n workingDirectory: host.workingDirectory,\n agents: host.agents,\n settings: {\n load: async () => parseSettings((await tablePromise).get(SETTINGS_KEY)),\n save: async settings => { await (await tablePromise).put(SETTINGS_KEY, settings) },\n },\n })\n service.start()\n ctx.provide('gitCommit', service)\n ctx.effect(() => async () => {\n await service.dispose()\n await (await domainPromise).close()\n }, 'git-commit.lifecycle')\n ctx.effect(() => registerHostRpc(host, RPC_CHANNEL, (endpoint, payload, signal) => handleRpc(service, endpoint, payload, signal)), 'git-commit.rpc')\n ctx.effect(() => registerModelSettings(ctx as unknown as ModelSettingsEvents, {\n ns: PLUGIN_NAME, title: { zh: 'Git 提交', en: 'Git Commit' },\n async read(signal) {\n await service.ready(); signal.throwIfAborted()\n const settings = service.getSettings()\n return { revision: settings.revision, fields: [{ path: ['model'], label: { zh: '提交模型', en: 'Commit model' }, current: settings.model,\n followLabel: { zh: '当前会话 / 宿主默认模型', en: 'Current conversation / host default' }, allowFollow: true }] }\n },\n async write(path, route, expectedRevision, signal) {\n if (path.length !== 1 || path[0] !== 'model') throw new Error('Unknown Git Commit model setting')\n await service.ready(); signal.throwIfAborted()\n return service.updateSettings({ ...service.getSettings(), model: route }, expectedRevision)\n },\n }), 'git-commit.model-settings')\n}\n"],"mappings":"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"}