diff-easy 0.1.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/LICENSE +22 -0
- package/README.md +142 -0
- package/dist/cli/cmux.d.ts +9 -0
- package/dist/cli/cmux.js +274 -0
- package/dist/cli/comment.d.ts +2 -0
- package/dist/cli/comment.js +91 -0
- package/dist/cli/github.d.ts +84 -0
- package/dist/cli/github.js +364 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +397 -0
- package/dist/cli/turns.d.ts +14 -0
- package/dist/cli/turns.js +333 -0
- package/dist/cli/utils.d.ts +27 -0
- package/dist/cli/utils.js +204 -0
- package/dist/client/assets/arc-CHYjdFTk.js +1 -0
- package/dist/client/assets/architecture-7EHR7CIX-weHBYDgM.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-DRa2msuJ.js +36 -0
- package/dist/client/assets/array-BifhSqXX.js +1 -0
- package/dist/client/assets/blockDiagram-GPEHLZMM-BTdKsXy5.js +132 -0
- package/dist/client/assets/c4Diagram-AAUBKEIU-CwTvCkOg.js +10 -0
- package/dist/client/assets/channel-Cv40nQW1.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-BFsjrTtc.js +1 -0
- package/dist/client/assets/chunk-3OPIFGDE-V_xVw1dq.js +62 -0
- package/dist/client/assets/chunk-4BX2VUAB-DoLO6LLy.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-CHgBwbdB.js +1 -0
- package/dist/client/assets/chunk-5ZQYHXKU-DMixAgQG.js +2 -0
- package/dist/client/assets/chunk-727SXJPM-C-3FZTmm.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-DpQZhXt-.js +231 -0
- package/dist/client/assets/chunk-BSJP7CBP-BnO9E_G4.js +1 -0
- package/dist/client/assets/chunk-CSCIHK7Q-BxItphyO.js +124 -0
- package/dist/client/assets/chunk-FMBD7UC4-4hEcfvfT.js +15 -0
- package/dist/client/assets/chunk-KSCS5N6A-DnArkpOI.js +10 -0
- package/dist/client/assets/chunk-L5ZTLDWV-DQoYJ1BW.js +1 -0
- package/dist/client/assets/chunk-LZXEDZCA-CdCevBX-.js +2 -0
- package/dist/client/assets/chunk-ND2GUHAM-Dt89U_nU.js +1 -0
- package/dist/client/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
- package/dist/client/assets/chunk-NZK2D7GU-C2Ev_f_w.js +1 -0
- package/dist/client/assets/chunk-O5CBEL6O-BeZ3MFnJ.js +70 -0
- package/dist/client/assets/chunk-QZHKN3VN-BmDOSWHp.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-gLuInTk4.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-DPbDc8_T.js +32 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-DOpVKUrb.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOpVKUrb.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-CCPyalms.js +1 -0
- package/dist/client/assets/cytoscape.esm-FqbQrHcz.js +321 -0
- package/dist/client/assets/dagre-BM42HDAG-6jrYMjX3.js +4 -0
- package/dist/client/assets/dagre-Bx709z4p.js +1 -0
- package/dist/client/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-dCQ_KoXl.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-B3Xj4-XE.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-DaGgF2yL.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-CF_c4tPT.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-BgBHbJkT.js +24 -0
- package/dist/client/assets/dist-7wVkzZne.js +1 -0
- package/dist/client/assets/erDiagram-TEJ5UH35-BZgX1grR.js +85 -0
- package/dist/client/assets/eventmodeling-FCH6USID-BaI6RNKr.js +1 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-BFNUa12V.js +162 -0
- package/dist/client/assets/ganttDiagram-6RSMTGT7-C8t6BThI.js +292 -0
- package/dist/client/assets/gitGraph-WXDBUCRP-BlicfDkM.js +1 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-BmnIXuXP.js +106 -0
- package/dist/client/assets/graphlib-B8gBHxth.js +1 -0
- package/dist/client/assets/index-BfVtbKTZ.css +2 -0
- package/dist/client/assets/index-p8zzfjdu.js +87 -0
- package/dist/client/assets/info-J43DQDTF-DHLG38N3.js +1 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-oQ-lq-45.js +2 -0
- package/dist/client/assets/init-D6jRqBbL.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-DrODlBLD.js +70 -0
- package/dist/client/assets/journeyDiagram-JHISSGLW-Bxg5wCQz.js +139 -0
- package/dist/client/assets/kanban-definition-UN3LZRKU-DAdvUDIB.js +89 -0
- package/dist/client/assets/katex-Vhh-h91d.js +257 -0
- package/dist/client/assets/line-D8kNd46u.js +1 -0
- package/dist/client/assets/linear-jwGZSrg4.js +1 -0
- package/dist/client/assets/mermaid-parser.core-PVPfUfyU.js +4 -0
- package/dist/client/assets/mermaid.core-DwdP6CFg.js +9 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-DeOoL56Q.js +96 -0
- package/dist/client/assets/ordinal-hYBb2elL.js +1 -0
- package/dist/client/assets/packet-YPE3B663-BBEaxdJP.js +1 -0
- package/dist/client/assets/path-BWPyau1x.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-fvTRFm-2.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-oep1px2r.js +30 -0
- package/dist/client/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/client/assets/prism-clojure-_z-zoNcE.js +1 -0
- package/dist/client/assets/prism-csharp-XPn2bw0d.js +1 -0
- package/dist/client/assets/prism-dart-CDv16Fbu.js +1 -0
- package/dist/client/assets/prism-elixir-DhHhV5Rj.js +1 -0
- package/dist/client/assets/prism-haskell-5hq1Nh9c.js +1 -0
- package/dist/client/assets/prism-hcl-B6Z0Ouoo.js +1 -0
- package/dist/client/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/client/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/client/assets/prism-nix-C88dTX93.js +1 -0
- package/dist/client/assets/prism-perl-Lsbi34sH.js +1 -0
- package/dist/client/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/client/assets/prism-protobuf-yzSHlhwy.js +1 -0
- package/dist/client/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/client/assets/prism-scala-VkM3wCOL.js +1 -0
- package/dist/client/assets/prism-solidity-DTom4k5p.js +1 -0
- package/dist/client/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/client/assets/prism-vim-BgpkjCK5.js +1 -0
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-D9l66CHb.js +7 -0
- package/dist/client/assets/radar-GUYGQ44K-DAU5EPVd.js +1 -0
- package/dist/client/assets/requirementDiagram-4Y6WPE33-helpQlwb.js +84 -0
- package/dist/client/assets/rough.esm-CSKSodPl.js +1 -0
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-C9sNuuvA.js +40 -0
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-BP0bhCwQ.js +162 -0
- package/dist/client/assets/src-CDAPGf2l.js +1 -0
- package/dist/client/assets/stateDiagram-AJRCARHV-CsFizpla.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Bvn8tanl.js +1 -0
- package/dist/client/assets/timeline-definition-PNZ67QCA-BnkmHs1F.js +120 -0
- package/dist/client/assets/treeView-BLDUP644-DD9HZO5W.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-BBKuQ1lo.js +1 -0
- package/dist/client/assets/vennDiagram-CIIHVFJN-BPFTHflF.js +34 -0
- package/dist/client/assets/wardley-L42UT6IY-y88gDkwb.js +1 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-BlcA7IVh.js +78 -0
- package/dist/client/assets/xychartDiagram-2RQKCTM6-DX5vUCRH.js +7 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/index.html +14 -0
- package/dist/client/logo.png +0 -0
- package/dist/client/site-data/blobs/080c0e6/cHVibGljL3NpdGUtZGF0YS9vZy1pbWFnZS5wbmc.png +0 -0
- package/dist/client/site-data/blobs/55f23a1/bGFuZGluZy9wdWJsaWMvZGlmaXQvbG9nby5wbmc.png +0 -0
- package/dist/client/site-data/blobs/66ff7c6/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/blobs/e6977fe/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/manifest.json +1 -0
- package/dist/client/site-data/og-image.png +0 -0
- package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
- package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
- package/dist/server/file-watcher.d.ts +33 -0
- package/dist/server/file-watcher.js +340 -0
- package/dist/server/generated-file-check.d.ts +17 -0
- package/dist/server/generated-file-check.js +280 -0
- package/dist/server/git-diff.d.ts +94 -0
- package/dist/server/git-diff.js +909 -0
- package/dist/server/grouping/cache.d.ts +3 -0
- package/dist/server/grouping/cache.js +22 -0
- package/dist/server/grouping/claudeCli.d.ts +10 -0
- package/dist/server/grouping/claudeCli.js +95 -0
- package/dist/server/grouping/digest.d.ts +15 -0
- package/dist/server/grouping/digest.js +38 -0
- package/dist/server/grouping/reconcile.d.ts +6 -0
- package/dist/server/grouping/reconcile.js +34 -0
- package/dist/server/grouping/types.d.ts +9 -0
- package/dist/server/grouping/types.js +1 -0
- package/dist/server/server.d.ts +37 -0
- package/dist/server/server.js +1283 -0
- package/dist/shared/grouping.d.ts +4 -0
- package/dist/shared/grouping.js +170 -0
- package/dist/types/diff.d.ts +263 -0
- package/dist/types/diff.js +1 -0
- package/dist/types/watch.d.ts +59 -0
- package/dist/types/watch.js +8 -0
- package/dist/utils/commentFormatting.d.ts +6 -0
- package/dist/utils/commentFormatting.js +90 -0
- package/dist/utils/commentImports.d.ts +11 -0
- package/dist/utils/commentImports.js +386 -0
- package/dist/utils/createId.d.ts +1 -0
- package/dist/utils/createId.js +5 -0
- package/dist/utils/diffMode.d.ts +3 -0
- package/dist/utils/diffMode.js +13 -0
- package/dist/utils/diffSelection.d.ts +13 -0
- package/dist/utils/diffSelection.js +39 -0
- package/dist/utils/editorOptions.d.ts +60 -0
- package/dist/utils/editorOptions.js +171 -0
- package/dist/utils/fileUtils.d.ts +12 -0
- package/dist/utils/fileUtils.js +21 -0
- package/dist/utils/hubConfig.d.ts +32 -0
- package/dist/utils/hubConfig.js +51 -0
- package/dist/utils/suggestionUtils.d.ts +19 -0
- package/dist/utils/suggestionUtils.js +31 -0
- package/dist/utils/turnLog.d.ts +47 -0
- package/dist/utils/turnLog.js +133 -0
- package/package.json +105 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { GroupingResult } from './types.js';
|
|
2
|
+
export declare function readCache(baseDir: string, targetHash: string, model: string): GroupingResult | null;
|
|
3
|
+
export declare function writeCache(baseDir: string, targetHash: string, model: string, result: GroupingResult): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
function fileFor(baseDir, targetHash, model) {
|
|
4
|
+
const safe = `${targetHash}-${model}`.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
5
|
+
return join(baseDir, 'grouping', `${safe}.json`);
|
|
6
|
+
}
|
|
7
|
+
export function readCache(baseDir, targetHash, model) {
|
|
8
|
+
const file = fileFor(baseDir, targetHash, model);
|
|
9
|
+
if (!existsSync(file))
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function writeCache(baseDir, targetHash, model, result) {
|
|
19
|
+
const file = fileFor(baseDir, targetHash, model);
|
|
20
|
+
mkdirSync(join(baseDir, 'grouping'), { recursive: true });
|
|
21
|
+
writeFileSync(file, JSON.stringify(result), 'utf8');
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DiffResponse } from '../../types/diff.js';
|
|
2
|
+
import type { GroupingResult } from './types.js';
|
|
3
|
+
/** Injectable so tests never spawn a process. */
|
|
4
|
+
export interface ClaudeRunner {
|
|
5
|
+
run(prompt: string, model: string): Promise<string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function groupWithClaude(diff: DiffResponse, intent: string | null, opts: {
|
|
8
|
+
model: string;
|
|
9
|
+
runner?: ClaudeRunner;
|
|
10
|
+
}): Promise<GroupingResult>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/server/grouping/claudeCli.ts
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { groupSmart } from '../../shared/grouping.js';
|
|
4
|
+
import { buildDigest } from './digest.js';
|
|
5
|
+
import { reconcile } from './reconcile.js';
|
|
6
|
+
// User-invoked (they click Group and wait), and a cold `claude -p` start with a
|
|
7
|
+
// multi-file digest routinely exceeds 20s — a short timeout made the AI path
|
|
8
|
+
// almost always fall back to heuristic. 60s gives real calls room to finish.
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
10
|
+
function buildPrompt(digest) {
|
|
11
|
+
return [
|
|
12
|
+
'You are grouping the changed files of a code diff for review.',
|
|
13
|
+
'Return ONLY a JSON object, no prose, of the form:',
|
|
14
|
+
'{"groups":[{"label":"short intent phrase","rationale":"one line","files":["path"]}],"noise":["generated/path"]}',
|
|
15
|
+
'Rules: use ONLY the paths listed below; put generated/lockfile/build-output paths in "noise";',
|
|
16
|
+
'order groups so the most important thing to review is first; every non-noise path in exactly one group.',
|
|
17
|
+
'',
|
|
18
|
+
digest,
|
|
19
|
+
].join('\n');
|
|
20
|
+
}
|
|
21
|
+
/** Extract a JSON object from the CLI envelope, tolerant of surrounding prose. */
|
|
22
|
+
function extractGrouping(stdout) {
|
|
23
|
+
const envelope = JSON.parse(stdout);
|
|
24
|
+
if (typeof envelope.result !== 'string')
|
|
25
|
+
throw new Error('envelope missing result');
|
|
26
|
+
const inner = envelope.result;
|
|
27
|
+
const start = inner.indexOf('{');
|
|
28
|
+
const end = inner.lastIndexOf('}');
|
|
29
|
+
if (start === -1 || end === -1 || end < start)
|
|
30
|
+
throw new Error('no JSON object in output');
|
|
31
|
+
return JSON.parse(inner.slice(start, end + 1));
|
|
32
|
+
}
|
|
33
|
+
const defaultRunner = {
|
|
34
|
+
run(prompt, model) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const child = spawn('claude', ['-p', '--output-format', 'json', '--model', model], {
|
|
37
|
+
// Capture stderr so a failing call reports *why* instead of silently
|
|
38
|
+
// falling back to heuristic with no diagnostic.
|
|
39
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
40
|
+
// Mark this Claude session as diff-easy-internal so our own turn-capture
|
|
41
|
+
// hook (the `snapshot` command) no-ops instead of recording the grouping
|
|
42
|
+
// call as a spurious agent turn. See src/cli/turns.ts:runSnapshot.
|
|
43
|
+
env: { ...process.env, DIFIT_INTERNAL_CLAUDE: '1' },
|
|
44
|
+
});
|
|
45
|
+
let out = '';
|
|
46
|
+
let err = '';
|
|
47
|
+
let settled = false;
|
|
48
|
+
const timer = setTimeout(() => {
|
|
49
|
+
if (settled)
|
|
50
|
+
return;
|
|
51
|
+
settled = true;
|
|
52
|
+
child.kill('SIGKILL');
|
|
53
|
+
reject(new Error(`claude timed out after ${DEFAULT_TIMEOUT_MS}ms`));
|
|
54
|
+
}, DEFAULT_TIMEOUT_MS);
|
|
55
|
+
child.stdout.on('data', (d) => (out += d.toString()));
|
|
56
|
+
child.stderr.on('data', (d) => (err += d.toString()));
|
|
57
|
+
child.on('error', (e) => {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
if (settled)
|
|
60
|
+
return;
|
|
61
|
+
settled = true;
|
|
62
|
+
reject(e);
|
|
63
|
+
});
|
|
64
|
+
child.on('close', (code) => {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
if (settled)
|
|
67
|
+
return;
|
|
68
|
+
settled = true;
|
|
69
|
+
if (code === 0)
|
|
70
|
+
resolve(out);
|
|
71
|
+
else
|
|
72
|
+
reject(new Error(`claude exited ${code}: ${err.trim().slice(0, 300) || '(no stderr)'}`));
|
|
73
|
+
});
|
|
74
|
+
child.stdin.end(prompt);
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
export async function groupWithClaude(diff, intent, opts) {
|
|
79
|
+
const runner = opts.runner ?? defaultRunner;
|
|
80
|
+
const { text, changedPaths } = buildDigest(diff, intent);
|
|
81
|
+
try {
|
|
82
|
+
const stdout = await runner.run(buildPrompt(text), opts.model);
|
|
83
|
+
const raw = extractGrouping(stdout);
|
|
84
|
+
const { groups, noise } = reconcile(raw, changedPaths);
|
|
85
|
+
return { mode: 'ai', groups, noise, model: opts.model, generatedAt: Date.now() };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// Any failure (process error, timeout, unparseable output, invalid shape)
|
|
89
|
+
// degrades gracefully to the heuristic provider rather than surfacing an error.
|
|
90
|
+
// Log why, so "grouped without AI" is diagnosable instead of a silent mystery.
|
|
91
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
92
|
+
console.warn(`[grouping] claude grouping failed for ${changedPaths.length} files; using heuristic. Reason: ${reason}`);
|
|
93
|
+
return { ...groupSmart(diff.files), fallbackFrom: 'ai' };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DiffResponse } from '../../types/diff.js';
|
|
2
|
+
export interface Digest {
|
|
3
|
+
text: string;
|
|
4
|
+
changedPaths: string[];
|
|
5
|
+
}
|
|
6
|
+
/** Hunk headers add function/scope context but drive most of the model's output
|
|
7
|
+
* tokens (which dominate latency). They pay off on small diffs and starve large
|
|
8
|
+
* ones — a 64-file digest with 8 headers/file made the `claude -p` call exceed
|
|
9
|
+
* the 60s timeout, while paths alone grouped the same files in ~18s. So scale
|
|
10
|
+
* the per-file header budget down as the file count grows. */
|
|
11
|
+
export declare function hunkHeaderBudget(fileCount: number): number;
|
|
12
|
+
/** Compact, token-bounded representation of a diff for the grouping model.
|
|
13
|
+
* We send file paths + status + line counts + (for smaller diffs) a few hunk
|
|
14
|
+
* headers, not the full diff, to keep latency/cost low. */
|
|
15
|
+
export declare function buildDigest(diff: DiffResponse, intent: string | null): Digest;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Hunk headers add function/scope context but drive most of the model's output
|
|
2
|
+
* tokens (which dominate latency). They pay off on small diffs and starve large
|
|
3
|
+
* ones — a 64-file digest with 8 headers/file made the `claude -p` call exceed
|
|
4
|
+
* the 60s timeout, while paths alone grouped the same files in ~18s. So scale
|
|
5
|
+
* the per-file header budget down as the file count grows. */
|
|
6
|
+
export function hunkHeaderBudget(fileCount) {
|
|
7
|
+
if (fileCount > 40)
|
|
8
|
+
return 0;
|
|
9
|
+
if (fileCount > 15)
|
|
10
|
+
return 3;
|
|
11
|
+
return 8;
|
|
12
|
+
}
|
|
13
|
+
/** Compact, token-bounded representation of a diff for the grouping model.
|
|
14
|
+
* We send file paths + status + line counts + (for smaller diffs) a few hunk
|
|
15
|
+
* headers, not the full diff, to keep latency/cost low. */
|
|
16
|
+
export function buildDigest(diff, intent) {
|
|
17
|
+
const changedPaths = diff.files.map((f) => f.path);
|
|
18
|
+
const perFileHeaders = hunkHeaderBudget(diff.files.length);
|
|
19
|
+
const lines = [];
|
|
20
|
+
if (intent && intent.trim()) {
|
|
21
|
+
lines.push('INTENT (the change was made to accomplish this):');
|
|
22
|
+
lines.push(intent.trim().slice(0, 2000));
|
|
23
|
+
lines.push('');
|
|
24
|
+
}
|
|
25
|
+
lines.push(`CHANGED FILES (${diff.files.length}):`);
|
|
26
|
+
for (const file of diff.files) {
|
|
27
|
+
lines.push(`${file.status} ${file.path} (+${file.additions} -${file.deletions})`);
|
|
28
|
+
if (perFileHeaders === 0)
|
|
29
|
+
continue;
|
|
30
|
+
const headers = file.chunks
|
|
31
|
+
.map((c) => c.header)
|
|
32
|
+
.filter((h) => typeof h === 'string' && h.length > 0)
|
|
33
|
+
.slice(0, perFileHeaders);
|
|
34
|
+
for (const h of headers)
|
|
35
|
+
lines.push(` ${h}`);
|
|
36
|
+
}
|
|
37
|
+
return { text: lines.join('\n'), changedPaths };
|
|
38
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function asStringArray(value) {
|
|
2
|
+
if (!Array.isArray(value))
|
|
3
|
+
return [];
|
|
4
|
+
return value.filter((v) => typeof v === 'string');
|
|
5
|
+
}
|
|
6
|
+
/** Force the model's loose output onto the real changed-file set. */
|
|
7
|
+
export function reconcile(raw, changedPaths) {
|
|
8
|
+
const changed = new Set(changedPaths);
|
|
9
|
+
const assigned = new Set();
|
|
10
|
+
const noise = asStringArray(raw.noise).filter((p) => changed.has(p));
|
|
11
|
+
for (const p of noise)
|
|
12
|
+
assigned.add(p);
|
|
13
|
+
const groups = [];
|
|
14
|
+
for (const g of Array.isArray(raw.groups) ? raw.groups : []) {
|
|
15
|
+
const files = asStringArray(g.files).filter((p) => changed.has(p) && !assigned.has(p));
|
|
16
|
+
if (files.length === 0)
|
|
17
|
+
continue;
|
|
18
|
+
for (const p of files)
|
|
19
|
+
assigned.add(p);
|
|
20
|
+
groups.push({
|
|
21
|
+
id: `g${groups.length + 1}`,
|
|
22
|
+
label: typeof g.label === 'string' && g.label.trim()
|
|
23
|
+
? g.label.trim()
|
|
24
|
+
: `Group ${groups.length + 1}`,
|
|
25
|
+
rationale: typeof g.rationale === 'string' ? g.rationale : undefined,
|
|
26
|
+
files,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const leftover = changedPaths.filter((p) => !assigned.has(p));
|
|
30
|
+
if (leftover.length > 0) {
|
|
31
|
+
groups.push({ id: `g${groups.length + 1}`, label: 'Other', files: leftover });
|
|
32
|
+
}
|
|
33
|
+
return { groups, noise };
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Server } from 'http';
|
|
2
|
+
import { DiffMode } from '../types/watch.js';
|
|
3
|
+
import { type CommentImport, type DiffSelection } from '@/types/diff.js';
|
|
4
|
+
interface ServerOptions {
|
|
5
|
+
selection?: DiffSelection;
|
|
6
|
+
stdinDiff?: string;
|
|
7
|
+
preferredPort?: number;
|
|
8
|
+
host?: string;
|
|
9
|
+
openBrowser?: boolean;
|
|
10
|
+
ignoreWhitespace?: boolean;
|
|
11
|
+
clearComments?: boolean;
|
|
12
|
+
commentImports?: CommentImport[];
|
|
13
|
+
keepAlive?: boolean;
|
|
14
|
+
diffMode?: DiffMode;
|
|
15
|
+
repoPath?: string;
|
|
16
|
+
contextLines?: number;
|
|
17
|
+
/** Hub mode: start with no workspace and let the UI pick one at runtime. */
|
|
18
|
+
hub?: boolean;
|
|
19
|
+
/** CLI `--pr` launch: the PR's title/body, for grouping intent. */
|
|
20
|
+
prTitle?: string;
|
|
21
|
+
prBody?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Disable git's optional index-refresh lock for every git child this process
|
|
25
|
+
* spawns. Read commands (git status/diff) otherwise opportunistically rewrite
|
|
26
|
+
* `.git/index`'s stat cache; the file watcher treats that as a change and
|
|
27
|
+
* triggers an endless reload loop (fetch → index rewrite → reload → fetch …).
|
|
28
|
+
* `??=` respects an explicit operator override.
|
|
29
|
+
*/
|
|
30
|
+
export declare function disableGitOptionalLocks(): void;
|
|
31
|
+
export declare function startServer(options: ServerOptions): Promise<{
|
|
32
|
+
port: number;
|
|
33
|
+
url: string;
|
|
34
|
+
isEmpty?: boolean;
|
|
35
|
+
server?: Server;
|
|
36
|
+
}>;
|
|
37
|
+
export {};
|