aws-runtime-bridge 1.7.36 → 1.7.37

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.
@@ -26,6 +26,13 @@ interface GitStashItem {
26
26
  createdAt: string | null;
27
27
  }
28
28
  type GitDiffFileStatus = 'modified' | 'added' | 'deleted' | 'renamed';
29
+ interface GitDiffSummaryFile {
30
+ path: string;
31
+ status: GitDiffFileStatus;
32
+ additions: number;
33
+ deletions: number;
34
+ staged: boolean;
35
+ }
29
36
  /**
30
37
  * 判断 Git diff 汇总行是否应该展示在差异树中。
31
38
  * 普通 modified 且文本增删均为 0 通常是 filemode/元数据噪声,避免显示为“无差异内容”。
@@ -61,6 +68,15 @@ export declare function parseLatestGitStash(output: string): LatestGitStash | nu
61
68
  * 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
62
69
  */
63
70
  export declare function parseGitStatusStagedPaths(output: string): Set<string>;
71
+ /**
72
+ * 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
73
+ */
74
+ export declare function parseGitDiffSummary(nameStatusOutput: string, numstatOutput: string, stagedPaths?: Set<string>, forceStaged?: boolean): GitDiffSummaryFile[];
75
+ /**
76
+ * 合并工作区与暂存区差异汇总。
77
+ * 主流程:以路径去重,保留工作区展示顺序;同一路径若也存在暂存变更则标记为 staged 并累加统计。
78
+ */
79
+ export declare function mergeGitDiffSummaryFiles(unstagedFiles: GitDiffSummaryFile[], stagedFiles: GitDiffSummaryFile[]): GitDiffSummaryFile[];
64
80
  export declare function normalizeGitCommitMessage(message: unknown): string;
65
81
  export declare function createScopedGitPathspecArgs(context: Pick<GitRepositoryContext, 'relativeWorkspacePath'>): string[];
66
82
  export declare function createScopedFilePathspecArgs(filePath: string, context: Pick<GitRepositoryContext, 'relativeWorkspacePath'>): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/routes/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,eAAO,MAAM,SAAS,4CAAW,CAAC;AAIlC;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAOnD;AAED,UAAU,oBAAoB;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAsB,gCAAgC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3F;AAED,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,KAAK,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAkBtE;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,OAAO,GACpB,OAAO,CAMT;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAK3E;AAED,wBAAgB,uCAAuC,IAAI,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKjE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmCvE;AAkBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYzE;AA+KD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAmBrE;AAkDD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAElH;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAErI"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/routes/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,eAAO,MAAM,SAAS,4CAAW,CAAC;AAIlC;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAOnD;AAED,UAAU,oBAAoB;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAsB,gCAAgC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3F;AAED,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,KAAK,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAUD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,OAAO,GACpB,OAAO,CAMT;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAK3E;AAED,wBAAgB,uCAAuC,IAAI,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKjE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmCvE;AAkBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYzE;AA+KD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAmBrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,GAAG,CAAC,MAAM,CAAa,EACpC,WAAW,UAAQ,GAClB,kBAAkB,EAAE,CAuCtB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,kBAAkB,EAAE,EACnC,WAAW,EAAE,kBAAkB,EAAE,GAChC,kBAAkB,EAAE,CAwBtB;AAMD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAElH;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAErI"}
@@ -308,7 +308,7 @@ export function parseGitStatusStagedPaths(output) {
308
308
  /**
309
309
  * 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
310
310
  */
311
- function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new Set()) {
311
+ export function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new Set(), forceStaged = false) {
312
312
  const statusMap = new Map();
313
313
  const nameStatusLines = nameStatusOutput.split('\n').filter(line => line.trim());
314
314
  for (const line of nameStatusLines) {
@@ -338,10 +338,35 @@ function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new
338
338
  if (!filePath || !shouldIncludeGitDiffSummaryFile(status, additions, deletions, isBinaryDiff)) {
339
339
  continue;
340
340
  }
341
- files.push({ path: filePath, status, additions, deletions, staged: stagedPaths.has(filePath) });
341
+ files.push({ path: filePath, status, additions, deletions, staged: forceStaged || stagedPaths.has(filePath) });
342
342
  }
343
343
  return files;
344
344
  }
345
+ /**
346
+ * 合并工作区与暂存区差异汇总。
347
+ * 主流程:以路径去重,保留工作区展示顺序;同一路径若也存在暂存变更则标记为 staged 并累加统计。
348
+ */
349
+ export function mergeGitDiffSummaryFiles(unstagedFiles, stagedFiles) {
350
+ const fileMap = new Map();
351
+ for (const file of unstagedFiles) {
352
+ fileMap.set(file.path, { ...file });
353
+ }
354
+ for (const stagedFile of stagedFiles) {
355
+ const existingFile = fileMap.get(stagedFile.path);
356
+ if (!existingFile) {
357
+ fileMap.set(stagedFile.path, { ...stagedFile, staged: true });
358
+ continue;
359
+ }
360
+ fileMap.set(stagedFile.path, {
361
+ ...existingFile,
362
+ status: existingFile.status === 'modified' ? stagedFile.status : existingFile.status,
363
+ additions: existingFile.additions + stagedFile.additions,
364
+ deletions: existingFile.deletions + stagedFile.deletions,
365
+ staged: true,
366
+ });
367
+ }
368
+ return [...fileMap.values()];
369
+ }
345
370
  function normalizeCommitHash(commitHash) {
346
371
  return String(commitHash || '').trim();
347
372
  }
@@ -630,12 +655,23 @@ gitRouter.post('/git/diff', validateToken, async (req, res) => {
630
655
  res.status(400).json({ error: result.stderr || 'git diff failed' });
631
656
  return;
632
657
  }
658
+ const stagedNameStatusResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['diff', '--cached', '--name-status'], context));
659
+ if (stagedNameStatusResult.exitCode !== 0) {
660
+ res.status(400).json({ error: stagedNameStatusResult.stderr || 'git staged diff failed' });
661
+ return;
662
+ }
663
+ const stagedNumstatResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['diff', '--cached', '--numstat'], context));
664
+ if (stagedNumstatResult.exitCode !== 0) {
665
+ res.status(400).json({ error: stagedNumstatResult.stderr || 'git staged diff failed' });
666
+ return;
667
+ }
633
668
  const statusResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['status', '--porcelain=v1'], context));
634
669
  if (statusResult.exitCode !== 0) {
635
670
  res.status(400).json({ error: statusResult.stderr || 'git status failed' });
636
671
  return;
637
672
  }
638
- const files = parseGitDiffSummary(nameStatusResult.stdout, result.stdout, parseGitStatusStagedPaths(statusResult.stdout));
673
+ const stagedPaths = parseGitStatusStagedPaths(statusResult.stdout);
674
+ const files = mergeGitDiffSummaryFiles(parseGitDiffSummary(nameStatusResult.stdout, result.stdout, stagedPaths), parseGitDiffSummary(stagedNameStatusResult.stdout, stagedNumstatResult.stdout, stagedPaths, true));
639
675
  res.json({
640
676
  ok: true,
641
677
  isGitRepo: context.isGitRepo,
@@ -868,12 +904,28 @@ gitRouter.post('/git/diff-file', validateToken, async (req, res) => {
868
904
  const normalizedCommitHash = normalizeCommitHash(commitHash);
869
905
  const gitArgs = normalizedCommitHash
870
906
  ? ['show', '--format=', '--no-ext-diff', normalizedCommitHash, '--', repositoryRelativeFilePath]
871
- : ['diff', '--', repositoryRelativeFilePath];
872
- const result = await execGitCommand(context.repositoryRootPath, gitArgs);
907
+ : ['diff', 'HEAD', '--', repositoryRelativeFilePath];
908
+ let result = await execGitCommand(context.repositoryRootPath, gitArgs);
909
+ if (!normalizedCommitHash && result.exitCode !== 0) {
910
+ result = await execGitCommand(context.repositoryRootPath, ['diff', '--', repositoryRelativeFilePath]);
911
+ }
873
912
  if (result.exitCode !== 0) {
874
913
  res.status(400).json({ error: result.stderr || 'git diff file failed' });
875
914
  return;
876
915
  }
916
+ if (!normalizedCommitHash && !result.stdout.trim()) {
917
+ const stagedResult = await execGitCommand(context.repositoryRootPath, [
918
+ 'diff',
919
+ '--cached',
920
+ '--',
921
+ repositoryRelativeFilePath,
922
+ ]);
923
+ if (stagedResult.exitCode !== 0) {
924
+ res.status(400).json({ error: stagedResult.stderr || 'git staged diff file failed' });
925
+ return;
926
+ }
927
+ result = stagedResult;
928
+ }
877
929
  res.json({
878
930
  ok: true,
879
931
  filePath: normalizedFilePath,
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, normalizeGitCommitMessage, parseGitStashListLine, parseLatestGitStash, parseGitStatusStagedPaths, } from './git.js';
2
+ import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, mergeGitDiffSummaryFiles, normalizeGitCommitMessage, parseGitDiffSummary, parseGitStashListLine, parseGitStatusStagedPaths, parseLatestGitStash, } from './git.js';
3
3
  describe('git commit helpers', () => {
4
4
  it('normalizes commit messages before executing git commit', () => {
5
5
  expect(normalizeGitCommitMessage(' chore: update dashboard ')).toBe('chore: update dashboard');
@@ -20,6 +20,45 @@ describe('git commit helpers', () => {
20
20
  const stagedPaths = parseGitStatusStagedPaths('M staged.ts\n M unstaged.ts\nA added.ts\n?? untracked.ts\nR old.ts -> renamed.ts\n');
21
21
  expect([...stagedPaths].sort()).toEqual(['added.ts', 'renamed.ts', 'staged.ts']);
22
22
  });
23
+ it('marks cached diff files as staged when parsing staged summaries', () => {
24
+ const files = parseGitDiffSummary('M\tsrc/staged.ts\n', '2\t1\tsrc/staged.ts\n', new Set(), true);
25
+ expect(files).toEqual([
26
+ {
27
+ path: 'src/staged.ts',
28
+ status: 'modified',
29
+ additions: 2,
30
+ deletions: 1,
31
+ staged: true,
32
+ },
33
+ ]);
34
+ });
35
+ it('merges unstaged and staged summaries while preserving staged-only files', () => {
36
+ const unstagedFiles = parseGitDiffSummary('M\tsrc/mixed.ts\nM\tsrc/unstaged.ts\n', '1\t0\tsrc/mixed.ts\n3\t1\tsrc/unstaged.ts\n', new Set(['src/mixed.ts']));
37
+ const stagedFiles = parseGitDiffSummary('M\tsrc/mixed.ts\nA\tsrc/staged-only.ts\n', '2\t1\tsrc/mixed.ts\n5\t0\tsrc/staged-only.ts\n', new Set(['src/mixed.ts', 'src/staged-only.ts']), true);
38
+ expect(mergeGitDiffSummaryFiles(unstagedFiles, stagedFiles)).toEqual([
39
+ {
40
+ path: 'src/mixed.ts',
41
+ status: 'modified',
42
+ additions: 3,
43
+ deletions: 1,
44
+ staged: true,
45
+ },
46
+ {
47
+ path: 'src/unstaged.ts',
48
+ status: 'modified',
49
+ additions: 3,
50
+ deletions: 1,
51
+ staged: false,
52
+ },
53
+ {
54
+ path: 'src/staged-only.ts',
55
+ status: 'added',
56
+ additions: 5,
57
+ deletions: 0,
58
+ staged: true,
59
+ },
60
+ ]);
61
+ });
23
62
  });
24
63
  describe('git stash reference helpers', () => {
25
64
  it('parses stable stash object hashes while preserving the display selector', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.7.36",
3
+ "version": "1.7.37",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",