opencode-dux 1.0.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 +21 -0
- package/README.md +452 -0
- package/dist/agents/descriptions.d.ts +6 -0
- package/dist/agents/designer.d.ts +2 -0
- package/dist/agents/explorer.d.ts +2 -0
- package/dist/agents/fixer.d.ts +2 -0
- package/dist/agents/index.d.ts +22 -0
- package/dist/agents/interpreter.d.ts +2 -0
- package/dist/agents/librarian.d.ts +2 -0
- package/dist/agents/oracle.d.ts +2 -0
- package/dist/agents/orchestrator.d.ts +27 -0
- package/dist/agents/overrides.d.ts +18 -0
- package/dist/agents/prompt-blocks.d.ts +97 -0
- package/dist/agents/steward.d.ts +3 -0
- package/dist/cli/config-io.d.ts +24 -0
- package/dist/cli/config-manager.d.ts +4 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1006 -0
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/mcps.d.ts +13 -0
- package/dist/cli/model-key-normalization.d.ts +1 -0
- package/dist/cli/paths.d.ts +35 -0
- package/dist/cli/providers.d.ts +137 -0
- package/dist/cli/skills.d.ts +22 -0
- package/dist/cli/system.d.ts +5 -0
- package/dist/cli/types.d.ts +38 -0
- package/dist/config/constants.d.ts +12 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/loader.d.ts +40 -0
- package/dist/config/runtime-preset.d.ts +12 -0
- package/dist/config/schema.d.ts +281 -0
- package/dist/config/utils.d.ts +10 -0
- package/dist/discovery/local/types.d.ts +79 -0
- package/dist/discovery/local.d.ts +73 -0
- package/dist/discovery/mcp-servers.d.ts +88 -0
- package/dist/discovery/skills.d.ts +94 -0
- package/dist/hooks/apply-patch/codec.d.ts +7 -0
- package/dist/hooks/apply-patch/errors.d.ts +25 -0
- package/dist/hooks/apply-patch/execution-context.d.ts +27 -0
- package/dist/hooks/apply-patch/index.d.ts +15 -0
- package/dist/hooks/apply-patch/matching.d.ts +26 -0
- package/dist/hooks/apply-patch/operations.d.ts +3 -0
- package/dist/hooks/apply-patch/patch.d.ts +2 -0
- package/dist/hooks/apply-patch/prepared-changes.d.ts +17 -0
- package/dist/hooks/apply-patch/resolution.d.ts +19 -0
- package/dist/hooks/apply-patch/rewrite.d.ts +7 -0
- package/dist/hooks/apply-patch/test-helpers.d.ts +6 -0
- package/dist/hooks/apply-patch/types.d.ts +80 -0
- package/dist/hooks/auto-update-checker/cache.d.ts +11 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +32 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +11 -0
- package/dist/hooks/auto-update-checker/index.d.ts +18 -0
- package/dist/hooks/auto-update-checker/types.d.ts +22 -0
- package/dist/hooks/chat-headers.d.ts +16 -0
- package/dist/hooks/context-pressure-reminder/index.d.ts +33 -0
- package/dist/hooks/delegate-task-retry/guidance.d.ts +2 -0
- package/dist/hooks/delegate-task-retry/hook.d.ts +8 -0
- package/dist/hooks/delegate-task-retry/index.d.ts +4 -0
- package/dist/hooks/delegate-task-retry/patterns.d.ts +11 -0
- package/dist/hooks/filter-available-skills/index.d.ts +32 -0
- package/dist/hooks/foreground-fallback/index.d.ts +72 -0
- package/dist/hooks/image-hook.d.ts +5 -0
- package/dist/hooks/index.d.ts +14 -0
- package/dist/hooks/json-error-recovery/hook.d.ts +18 -0
- package/dist/hooks/json-error-recovery/index.d.ts +1 -0
- package/dist/hooks/phase-reminder/index.d.ts +26 -0
- package/dist/hooks/post-file-tool-nudge/index.d.ts +19 -0
- package/dist/hooks/task-session-manager/index.d.ts +52 -0
- package/dist/hooks/todo-continuation/index.d.ts +53 -0
- package/dist/hooks/todo-continuation/todo-hygiene.d.ts +35 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +31782 -0
- package/dist/mcp/context7.d.ts +6 -0
- package/dist/mcp/grep-app.d.ts +6 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/types.d.ts +12 -0
- package/dist/mcp/websearch.d.ts +9 -0
- package/dist/skills/registry.d.ts +29 -0
- package/dist/subscriptions/accounts-store.d.ts +57 -0
- package/dist/subscriptions/index.d.ts +13 -0
- package/dist/subscriptions/neuralwatt-scraper.d.ts +14 -0
- package/dist/subscriptions/opencode-go-scraper.d.ts +27 -0
- package/dist/subscriptions/types.d.ts +115 -0
- package/dist/subscriptions/usage-service.d.ts +74 -0
- package/dist/tools/ast-grep/cli.d.ts +15 -0
- package/dist/tools/ast-grep/constants.d.ts +25 -0
- package/dist/tools/ast-grep/downloader.d.ts +5 -0
- package/dist/tools/ast-grep/index.d.ts +10 -0
- package/dist/tools/ast-grep/tools.d.ts +3 -0
- package/dist/tools/ast-grep/types.d.ts +30 -0
- package/dist/tools/ast-grep/utils.d.ts +4 -0
- package/dist/tools/delegate.d.ts +14 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/preset-manager.d.ts +27 -0
- package/dist/tools/smartfetch/binary.d.ts +3 -0
- package/dist/tools/smartfetch/cache.d.ts +6 -0
- package/dist/tools/smartfetch/constants.d.ts +12 -0
- package/dist/tools/smartfetch/index.d.ts +3 -0
- package/dist/tools/smartfetch/network.d.ts +38 -0
- package/dist/tools/smartfetch/secondary-model.d.ts +28 -0
- package/dist/tools/smartfetch/tool.d.ts +3 -0
- package/dist/tools/smartfetch/types.d.ts +122 -0
- package/dist/tools/smartfetch/utils.d.ts +18 -0
- package/dist/tui-state.d.ts +168 -0
- package/dist/tui.d.ts +37 -0
- package/dist/tui.js +1896 -0
- package/dist/utils/agent-variant.d.ts +63 -0
- package/dist/utils/compat.d.ts +30 -0
- package/dist/utils/env.d.ts +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/internal-initiator.d.ts +6 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/polling.d.ts +21 -0
- package/dist/utils/session-manager.d.ts +55 -0
- package/dist/utils/session.d.ts +90 -0
- package/dist/utils/subagent-depth.d.ts +35 -0
- package/dist/utils/system-collapse.d.ts +6 -0
- package/dist/utils/task.d.ts +4 -0
- package/dist/utils/zip-extractor.d.ts +1 -0
- package/index.ts +1 -0
- package/opencode-dux.schema.json +634 -0
- package/package.json +103 -0
- package/src/agents/descriptions.ts +55 -0
- package/src/agents/designer.test.ts +86 -0
- package/src/agents/designer.ts +154 -0
- package/src/agents/display-name.test.ts +186 -0
- package/src/agents/explorer.test.ts +79 -0
- package/src/agents/explorer.ts +144 -0
- package/src/agents/fixer.test.ts +79 -0
- package/src/agents/fixer.ts +145 -0
- package/src/agents/index.test.ts +472 -0
- package/src/agents/index.ts +248 -0
- package/src/agents/interpreter.ts +136 -0
- package/src/agents/librarian.test.ts +80 -0
- package/src/agents/librarian.ts +145 -0
- package/src/agents/oracle.test.ts +89 -0
- package/src/agents/oracle.ts +184 -0
- package/src/agents/orchestrator.test.ts +116 -0
- package/src/agents/orchestrator.ts +574 -0
- package/src/agents/overrides.ts +95 -0
- package/src/agents/prompt-blocks.test.ts +114 -0
- package/src/agents/prompt-blocks.ts +640 -0
- package/src/agents/steward.ts +146 -0
- package/src/cli/config-io.test.ts +536 -0
- package/src/cli/config-io.ts +473 -0
- package/src/cli/config-manager.test.ts +141 -0
- package/src/cli/config-manager.ts +4 -0
- package/src/cli/index.ts +88 -0
- package/src/cli/install.ts +282 -0
- package/src/cli/mcps.test.ts +62 -0
- package/src/cli/mcps.ts +39 -0
- package/src/cli/model-key-normalization.test.ts +21 -0
- package/src/cli/model-key-normalization.ts +60 -0
- package/src/cli/paths.test.ts +167 -0
- package/src/cli/paths.ts +144 -0
- package/src/cli/providers.test.ts +118 -0
- package/src/cli/providers.ts +141 -0
- package/src/cli/skills.test.ts +111 -0
- package/src/cli/skills.ts +103 -0
- package/src/cli/system.test.ts +91 -0
- package/src/cli/system.ts +180 -0
- package/src/cli/types.ts +43 -0
- package/src/config/constants.ts +58 -0
- package/src/config/index.ts +4 -0
- package/src/config/loader.test.ts +1194 -0
- package/src/config/loader.ts +269 -0
- package/src/config/model-resolution.test.ts +176 -0
- package/src/config/runtime-preset.test.ts +61 -0
- package/src/config/runtime-preset.ts +37 -0
- package/src/config/schema.ts +248 -0
- package/src/config/utils.test.ts +41 -0
- package/src/config/utils.ts +23 -0
- package/src/discovery/local/types.ts +85 -0
- package/src/discovery/local.ts +322 -0
- package/src/discovery/mcp-servers.ts +804 -0
- package/src/discovery/skills.ts +959 -0
- package/src/hooks/apply-patch/codec.test.ts +184 -0
- package/src/hooks/apply-patch/codec.ts +352 -0
- package/src/hooks/apply-patch/errors.ts +117 -0
- package/src/hooks/apply-patch/execution-context.ts +432 -0
- package/src/hooks/apply-patch/hook.test.ts +768 -0
- package/src/hooks/apply-patch/index.ts +126 -0
- package/src/hooks/apply-patch/matching.test.ts +215 -0
- package/src/hooks/apply-patch/matching.ts +586 -0
- package/src/hooks/apply-patch/operations.test.ts +1535 -0
- package/src/hooks/apply-patch/operations.ts +3 -0
- package/src/hooks/apply-patch/patch.ts +9 -0
- package/src/hooks/apply-patch/prepared-changes.ts +400 -0
- package/src/hooks/apply-patch/resolution.test.ts +420 -0
- package/src/hooks/apply-patch/resolution.ts +437 -0
- package/src/hooks/apply-patch/rewrite.ts +496 -0
- package/src/hooks/apply-patch/test-helpers.ts +52 -0
- package/src/hooks/apply-patch/types.ts +111 -0
- package/src/hooks/auto-update-checker/cache.test.ts +179 -0
- package/src/hooks/auto-update-checker/cache.ts +188 -0
- package/src/hooks/auto-update-checker/checker.test.ts +159 -0
- package/src/hooks/auto-update-checker/checker.ts +308 -0
- package/src/hooks/auto-update-checker/constants.ts +33 -0
- package/src/hooks/auto-update-checker/index.test.ts +282 -0
- package/src/hooks/auto-update-checker/index.ts +225 -0
- package/src/hooks/auto-update-checker/types.ts +26 -0
- package/src/hooks/chat-headers.test.ts +236 -0
- package/src/hooks/chat-headers.ts +97 -0
- package/src/hooks/context-pressure-reminder/index.test.ts +179 -0
- package/src/hooks/context-pressure-reminder/index.ts +137 -0
- package/src/hooks/delegate-task-retry/guidance.ts +41 -0
- package/src/hooks/delegate-task-retry/hook.ts +23 -0
- package/src/hooks/delegate-task-retry/index.test.ts +38 -0
- package/src/hooks/delegate-task-retry/index.ts +7 -0
- package/src/hooks/delegate-task-retry/patterns.ts +79 -0
- package/src/hooks/filter-available-skills/index.test.ts +297 -0
- package/src/hooks/filter-available-skills/index.ts +160 -0
- package/src/hooks/foreground-fallback/index.test.ts +624 -0
- package/src/hooks/foreground-fallback/index.ts +374 -0
- package/src/hooks/image-hook.ts +6 -0
- package/src/hooks/index.ts +17 -0
- package/src/hooks/json-error-recovery/hook.ts +73 -0
- package/src/hooks/json-error-recovery/index.test.ts +111 -0
- package/src/hooks/json-error-recovery/index.ts +6 -0
- package/src/hooks/phase-reminder/index.test.ts +74 -0
- package/src/hooks/phase-reminder/index.ts +85 -0
- package/src/hooks/post-file-tool-nudge/index.test.ts +94 -0
- package/src/hooks/post-file-tool-nudge/index.ts +63 -0
- package/src/hooks/task-session-manager/index.test.ts +833 -0
- package/src/hooks/task-session-manager/index.ts +434 -0
- package/src/hooks/todo-continuation/index.test.ts +3026 -0
- package/src/hooks/todo-continuation/index.ts +878 -0
- package/src/hooks/todo-continuation/todo-hygiene.test.ts +204 -0
- package/src/hooks/todo-continuation/todo-hygiene.ts +207 -0
- package/src/index.ts +1672 -0
- package/src/mcp/context7.ts +14 -0
- package/src/mcp/grep-app.ts +11 -0
- package/src/mcp/index.test.ts +96 -0
- package/src/mcp/index.ts +66 -0
- package/src/mcp/types.ts +16 -0
- package/src/mcp/websearch.ts +47 -0
- package/src/skills/codemap/README.md +60 -0
- package/src/skills/codemap/SKILL.md +174 -0
- package/src/skills/codemap/scripts/codemap.mjs +483 -0
- package/src/skills/codemap/scripts/codemap.test.ts +129 -0
- package/src/skills/registry.ts +218 -0
- package/src/skills/simplify/README.md +19 -0
- package/src/skills/simplify/SKILL.md +138 -0
- package/src/subscriptions/accounts-store.test.ts +236 -0
- package/src/subscriptions/accounts-store.ts +184 -0
- package/src/subscriptions/index.ts +30 -0
- package/src/subscriptions/neuralwatt-scraper.ts +108 -0
- package/src/subscriptions/opencode-go-scraper.ts +301 -0
- package/src/subscriptions/types.ts +145 -0
- package/src/subscriptions/usage-service.test.ts +202 -0
- package/src/subscriptions/usage-service.ts +651 -0
- package/src/tools/ast-grep/cli.ts +257 -0
- package/src/tools/ast-grep/constants.ts +214 -0
- package/src/tools/ast-grep/downloader.ts +131 -0
- package/src/tools/ast-grep/index.ts +24 -0
- package/src/tools/ast-grep/tools.ts +117 -0
- package/src/tools/ast-grep/types.ts +51 -0
- package/src/tools/ast-grep/utils.ts +126 -0
- package/src/tools/delegate-handoff.test.ts +18 -0
- package/src/tools/delegate.ts +508 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/preset-manager.test.ts +795 -0
- package/src/tools/preset-manager.ts +332 -0
- package/src/tools/smartfetch/binary.ts +58 -0
- package/src/tools/smartfetch/cache.test.ts +34 -0
- package/src/tools/smartfetch/cache.ts +112 -0
- package/src/tools/smartfetch/constants.ts +29 -0
- package/src/tools/smartfetch/index.ts +8 -0
- package/src/tools/smartfetch/network.test.ts +178 -0
- package/src/tools/smartfetch/network.ts +614 -0
- package/src/tools/smartfetch/secondary-model.test.ts +85 -0
- package/src/tools/smartfetch/secondary-model.ts +276 -0
- package/src/tools/smartfetch/tool.test.ts +60 -0
- package/src/tools/smartfetch/tool.ts +832 -0
- package/src/tools/smartfetch/types.ts +135 -0
- package/src/tools/smartfetch/utils.test.ts +24 -0
- package/src/tools/smartfetch/utils.ts +456 -0
- package/src/tui-state.test.ts +867 -0
- package/src/tui-state.ts +1255 -0
- package/src/tui.test.ts +336 -0
- package/src/tui.ts +1539 -0
- package/src/utils/agent-variant.test.ts +244 -0
- package/src/utils/agent-variant.ts +187 -0
- package/src/utils/compat.ts +91 -0
- package/src/utils/env.ts +12 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/internal-initiator.ts +28 -0
- package/src/utils/logger.test.ts +220 -0
- package/src/utils/logger.ts +136 -0
- package/src/utils/polling.test.ts +191 -0
- package/src/utils/polling.ts +67 -0
- package/src/utils/session-manager.test.ts +173 -0
- package/src/utils/session-manager.ts +356 -0
- package/src/utils/session.test.ts +110 -0
- package/src/utils/session.ts +389 -0
- package/src/utils/subagent-depth.test.ts +170 -0
- package/src/utils/subagent-depth.ts +75 -0
- package/src/utils/system-collapse.test.ts +86 -0
- package/src/utils/system-collapse.ts +24 -0
- package/src/utils/task.test.ts +24 -0
- package/src/utils/task.ts +20 -0
- package/src/utils/zip-extractor.ts +102 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
applyHits,
|
|
6
|
+
deriveNewContent,
|
|
7
|
+
locateChunk,
|
|
8
|
+
readFileLines,
|
|
9
|
+
resolveChunkStart,
|
|
10
|
+
resolveUpdateChunks,
|
|
11
|
+
} from './resolution';
|
|
12
|
+
import { createTempDir, DEFAULT_OPTIONS, writeFixture } from './test-helpers';
|
|
13
|
+
import type { PatchChunk } from './types';
|
|
14
|
+
|
|
15
|
+
describe('apply-patch/resolution', () => {
|
|
16
|
+
test('readFileLines removes the final synthetic empty line', async () => {
|
|
17
|
+
const root = await createTempDir();
|
|
18
|
+
const file = path.join(root, 'sample.txt');
|
|
19
|
+
await writeFixture(root, 'sample.txt', 'alpha\nbeta\n');
|
|
20
|
+
|
|
21
|
+
expect(await readFileLines(file)).toEqual(['alpha', 'beta']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('resolveChunkStart uses change_context as an anchor when present', () => {
|
|
25
|
+
const chunk: PatchChunk = {
|
|
26
|
+
old_lines: [],
|
|
27
|
+
new_lines: ['middle'],
|
|
28
|
+
change_context: 'anchor',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
expect(resolveChunkStart(['top', 'anchor', 'bottom'], chunk, 0)).toBe(2);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('locateChunk rescues prefix/suffix and preserves new_lines', () => {
|
|
35
|
+
const chunk: PatchChunk = {
|
|
36
|
+
old_lines: [
|
|
37
|
+
'const title = "Hola";',
|
|
38
|
+
'old-value',
|
|
39
|
+
'const footer = "Fin";',
|
|
40
|
+
],
|
|
41
|
+
new_lines: [
|
|
42
|
+
'const title = “Hola”;',
|
|
43
|
+
'new-value',
|
|
44
|
+
'const footer = “Fin”;',
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const resolved = locateChunk(
|
|
49
|
+
['top', 'const title = “Hola”;', 'stale-value', 'const footer = “Fin”;'],
|
|
50
|
+
'sample.txt',
|
|
51
|
+
chunk,
|
|
52
|
+
0,
|
|
53
|
+
DEFAULT_OPTIONS,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(resolved.rewritten).toBe(true);
|
|
57
|
+
expect(resolved.canonical_old_lines).toEqual([
|
|
58
|
+
'const title = “Hola”;',
|
|
59
|
+
'stale-value',
|
|
60
|
+
'const footer = “Fin”;',
|
|
61
|
+
]);
|
|
62
|
+
expect(resolved.canonical_new_lines).toEqual(chunk.new_lines);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('locateChunk canonicalizes a tolerant unicode match', () => {
|
|
66
|
+
const chunk: PatchChunk = {
|
|
67
|
+
old_lines: ['const title = "Hola";'],
|
|
68
|
+
new_lines: ['const title = "Hola mundo";'],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const resolved = locateChunk(
|
|
72
|
+
['const title = “Hola”;'],
|
|
73
|
+
'sample.txt',
|
|
74
|
+
chunk,
|
|
75
|
+
0,
|
|
76
|
+
DEFAULT_OPTIONS,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(resolved.rewritten).toBe(true);
|
|
80
|
+
expect(resolved.matchComparator).toBe('unicode');
|
|
81
|
+
expect(resolved.canonical_old_lines).toEqual(['const title = “Hola”;']);
|
|
82
|
+
expect(resolved.canonical_new_lines).toEqual([
|
|
83
|
+
'const title = "Hola mundo";',
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('locateChunk canonicalizes a tolerant trim-end match', () => {
|
|
88
|
+
const chunk: PatchChunk = {
|
|
89
|
+
old_lines: ['alpha'],
|
|
90
|
+
new_lines: ['omega'],
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const resolved = locateChunk(
|
|
94
|
+
['alpha '],
|
|
95
|
+
'sample.txt',
|
|
96
|
+
chunk,
|
|
97
|
+
0,
|
|
98
|
+
DEFAULT_OPTIONS,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
expect(resolved.rewritten).toBe(true);
|
|
102
|
+
expect(resolved.matchComparator).toBe('trim-end');
|
|
103
|
+
expect(resolved.canonical_old_lines).toEqual(['alpha ']);
|
|
104
|
+
expect(resolved.canonical_new_lines).toEqual(['omega']);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('locateChunk no longer rescues a trim-only stale patch', () => {
|
|
108
|
+
const chunk: PatchChunk = {
|
|
109
|
+
old_lines: ['alpha'],
|
|
110
|
+
new_lines: ['omega'],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
expect(() =>
|
|
114
|
+
locateChunk([' alpha '], 'sample.txt', chunk, 0, DEFAULT_OPTIONS),
|
|
115
|
+
).toThrow('Failed to find expected lines');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('locateChunk no longer canonicalizes a dangerous indented case', () => {
|
|
119
|
+
const chunk: PatchChunk = {
|
|
120
|
+
old_lines: ['enabled: false'],
|
|
121
|
+
new_lines: ['enabled: true'],
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
expect(() =>
|
|
125
|
+
locateChunk(
|
|
126
|
+
['root:', ' child:', ' enabled: false', 'done: true'],
|
|
127
|
+
'sample.yml',
|
|
128
|
+
chunk,
|
|
129
|
+
0,
|
|
130
|
+
DEFAULT_OPTIONS,
|
|
131
|
+
),
|
|
132
|
+
).toThrow('Failed to find expected lines');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('locateChunk preserves a real final blank line when it exists in the file', () => {
|
|
136
|
+
const chunk: PatchChunk = {
|
|
137
|
+
old_lines: ['alpha', ''],
|
|
138
|
+
new_lines: ['omega', ''],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const resolved = locateChunk(
|
|
142
|
+
['alpha', ''],
|
|
143
|
+
'sample.txt',
|
|
144
|
+
chunk,
|
|
145
|
+
0,
|
|
146
|
+
DEFAULT_OPTIONS,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(resolved.canonical_old_lines).toEqual(['alpha', '']);
|
|
150
|
+
expect(resolved.canonical_new_lines).toEqual(['omega', '']);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('locateChunk fails if the patch adds a non-existent final blank line', () => {
|
|
154
|
+
const chunk: PatchChunk = {
|
|
155
|
+
old_lines: ['alpha', ''],
|
|
156
|
+
new_lines: ['omega', ''],
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
expect(() =>
|
|
160
|
+
locateChunk(['alpha'], 'sample.txt', chunk, 0, DEFAULT_OPTIONS),
|
|
161
|
+
).toThrow('Failed to find expected lines');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('deriveNewContent resolves EOF updates', async () => {
|
|
165
|
+
const root = await createTempDir();
|
|
166
|
+
const file = path.join(root, 'sample.txt');
|
|
167
|
+
await writeFixture(root, 'sample.txt', 'alpha\nbeta');
|
|
168
|
+
|
|
169
|
+
expect(
|
|
170
|
+
await deriveNewContent(
|
|
171
|
+
file,
|
|
172
|
+
[
|
|
173
|
+
{
|
|
174
|
+
old_lines: ['beta'],
|
|
175
|
+
new_lines: ['omega'],
|
|
176
|
+
is_end_of_file: true,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
DEFAULT_OPTIONS,
|
|
180
|
+
),
|
|
181
|
+
).toBe('alpha\nomega');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('deriveNewContent preserves CRLF while rebuilding content', async () => {
|
|
185
|
+
const root = await createTempDir();
|
|
186
|
+
const file = path.join(root, 'sample.txt');
|
|
187
|
+
await writeFixture(root, 'sample.txt', 'alpha\r\nbeta\r\ngamma\r\n');
|
|
188
|
+
|
|
189
|
+
expect(
|
|
190
|
+
await deriveNewContent(
|
|
191
|
+
file,
|
|
192
|
+
[
|
|
193
|
+
{
|
|
194
|
+
old_lines: ['alpha', 'beta', 'gamma'],
|
|
195
|
+
new_lines: ['alpha', 'BETA', 'gamma'],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
DEFAULT_OPTIONS,
|
|
199
|
+
),
|
|
200
|
+
).toBe('alpha\r\nBETA\r\ngamma\r\n');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('deriveNewContent inserts an anchored block without moving it to EOF', async () => {
|
|
204
|
+
const root = await createTempDir();
|
|
205
|
+
const file = path.join(root, 'sample.txt');
|
|
206
|
+
await writeFixture(root, 'sample.txt', 'top\nanchor\nbottom\n');
|
|
207
|
+
|
|
208
|
+
expect(
|
|
209
|
+
await deriveNewContent(
|
|
210
|
+
file,
|
|
211
|
+
[
|
|
212
|
+
{
|
|
213
|
+
old_lines: [],
|
|
214
|
+
new_lines: ['middle'],
|
|
215
|
+
change_context: 'anchor',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
DEFAULT_OPTIONS,
|
|
219
|
+
),
|
|
220
|
+
).toBe('top\nanchor\nmiddle\nbottom\n');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('deriveNewContent supports pure insertion at EOF with a single anchor', async () => {
|
|
224
|
+
const root = await createTempDir();
|
|
225
|
+
const file = path.join(root, 'sample.txt');
|
|
226
|
+
await writeFixture(root, 'sample.txt', 'top\nanchor\n');
|
|
227
|
+
|
|
228
|
+
expect(
|
|
229
|
+
await deriveNewContent(
|
|
230
|
+
file,
|
|
231
|
+
[
|
|
232
|
+
{
|
|
233
|
+
old_lines: [],
|
|
234
|
+
new_lines: ['middle'],
|
|
235
|
+
change_context: 'anchor',
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
DEFAULT_OPTIONS,
|
|
239
|
+
),
|
|
240
|
+
).toBe('top\nanchor\nmiddle\n');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('resolveUpdateChunks canonicalizes EOF insertion with a tolerant anchor', async () => {
|
|
244
|
+
const root = await createTempDir();
|
|
245
|
+
const file = path.join(root, 'sample.txt');
|
|
246
|
+
await writeFixture(root, 'sample.txt', 'top\n“anchor”\n');
|
|
247
|
+
|
|
248
|
+
const { resolved } = await resolveUpdateChunks(
|
|
249
|
+
file,
|
|
250
|
+
[
|
|
251
|
+
{
|
|
252
|
+
old_lines: [],
|
|
253
|
+
new_lines: ['middle'],
|
|
254
|
+
change_context: '"anchor"',
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
DEFAULT_OPTIONS,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(resolved[0]).toMatchObject({
|
|
261
|
+
canonical_change_context: '“anchor”',
|
|
262
|
+
rewritten: true,
|
|
263
|
+
strategy: 'anchor',
|
|
264
|
+
matchComparator: 'unicode',
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('resolveUpdateChunks canonicalizes non-EOF insertion with a trim-end anchor', async () => {
|
|
269
|
+
const root = await createTempDir();
|
|
270
|
+
const file = path.join(root, 'sample.txt');
|
|
271
|
+
await writeFixture(root, 'sample.txt', 'top\nanchor \nbottom\n');
|
|
272
|
+
|
|
273
|
+
const { resolved } = await resolveUpdateChunks(
|
|
274
|
+
file,
|
|
275
|
+
[
|
|
276
|
+
{
|
|
277
|
+
old_lines: [],
|
|
278
|
+
new_lines: ['middle'],
|
|
279
|
+
change_context: 'anchor',
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
DEFAULT_OPTIONS,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
expect(resolved[0]).toMatchObject({
|
|
286
|
+
canonical_change_context: 'anchor ',
|
|
287
|
+
rewritten: true,
|
|
288
|
+
strategy: 'anchor',
|
|
289
|
+
matchComparator: 'trim-end',
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test('deriveNewContent fails if a pure insertion cannot find its anchor', async () => {
|
|
294
|
+
const root = await createTempDir();
|
|
295
|
+
const file = path.join(root, 'sample.txt');
|
|
296
|
+
await writeFixture(root, 'sample.txt', 'top\nbottom\n');
|
|
297
|
+
|
|
298
|
+
await expect(
|
|
299
|
+
deriveNewContent(
|
|
300
|
+
file,
|
|
301
|
+
[
|
|
302
|
+
{
|
|
303
|
+
old_lines: [],
|
|
304
|
+
new_lines: ['middle'],
|
|
305
|
+
change_context: 'anchor',
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
DEFAULT_OPTIONS,
|
|
309
|
+
),
|
|
310
|
+
).rejects.toThrow('Failed to find insertion anchor');
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('deriveNewContent fails if a pure insertion has an ambiguous anchor', async () => {
|
|
314
|
+
const root = await createTempDir();
|
|
315
|
+
const file = path.join(root, 'sample.txt');
|
|
316
|
+
await writeFixture(
|
|
317
|
+
root,
|
|
318
|
+
'sample.txt',
|
|
319
|
+
'top\nanchor\none\nsplit\nanchor\ntwo\n',
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
await expect(
|
|
323
|
+
deriveNewContent(
|
|
324
|
+
file,
|
|
325
|
+
[
|
|
326
|
+
{
|
|
327
|
+
old_lines: [],
|
|
328
|
+
new_lines: ['middle'],
|
|
329
|
+
change_context: 'anchor',
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
DEFAULT_OPTIONS,
|
|
333
|
+
),
|
|
334
|
+
).rejects.toThrow('Insertion anchor was ambiguous');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test('deriveNewContent fails if a tolerant insertion anchor is ambiguous', async () => {
|
|
338
|
+
const root = await createTempDir();
|
|
339
|
+
const file = path.join(root, 'sample.txt');
|
|
340
|
+
await writeFixture(root, 'sample.txt', 'top\n“anchor”\n"anchor"\n');
|
|
341
|
+
|
|
342
|
+
await expect(
|
|
343
|
+
deriveNewContent(
|
|
344
|
+
file,
|
|
345
|
+
[
|
|
346
|
+
{
|
|
347
|
+
old_lines: [],
|
|
348
|
+
new_lines: ['middle'],
|
|
349
|
+
change_context: '"anchor"',
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
DEFAULT_OPTIONS,
|
|
353
|
+
),
|
|
354
|
+
).rejects.toThrow('Insertion anchor was ambiguous');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test('deriveNewContent fails if a later chunk remains ambiguous', async () => {
|
|
358
|
+
const root = await createTempDir();
|
|
359
|
+
const file = path.join(root, 'sample.txt');
|
|
360
|
+
await writeFixture(
|
|
361
|
+
root,
|
|
362
|
+
'sample.txt',
|
|
363
|
+
'alpha\none\nomega\nsplit\nleft\nstale-one\nright\ngap\nleft\nstale-two\nright\n',
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
await expect(
|
|
367
|
+
deriveNewContent(
|
|
368
|
+
file,
|
|
369
|
+
[
|
|
370
|
+
{
|
|
371
|
+
old_lines: ['one'],
|
|
372
|
+
new_lines: ['ONE'],
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
old_lines: ['left', 'old', 'right'],
|
|
376
|
+
new_lines: ['left', 'new', 'right'],
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
DEFAULT_OPTIONS,
|
|
380
|
+
),
|
|
381
|
+
).rejects.toThrow('ambiguous');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('deriveNewContent rescues a stale EOF and preserves the final update', async () => {
|
|
385
|
+
const root = await createTempDir();
|
|
386
|
+
const file = path.join(root, 'sample.txt');
|
|
387
|
+
await writeFixture(root, 'sample.txt', 'alpha\nstale\nomega');
|
|
388
|
+
|
|
389
|
+
expect(
|
|
390
|
+
await deriveNewContent(
|
|
391
|
+
file,
|
|
392
|
+
[
|
|
393
|
+
{
|
|
394
|
+
old_lines: ['alpha', 'old', 'omega'],
|
|
395
|
+
new_lines: ['alpha', 'new', 'omega'],
|
|
396
|
+
is_end_of_file: true,
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
DEFAULT_OPTIONS,
|
|
400
|
+
),
|
|
401
|
+
).toBe('alpha\nnew\nomega');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('applyHits preserves the final newline', () => {
|
|
405
|
+
expect(
|
|
406
|
+
applyHits(['start', 'end'], [{ start: 0, del: 1, add: ['next'] }]),
|
|
407
|
+
).toBe('next\nend\n');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('applyHits can preserve a file without a final newline', () => {
|
|
411
|
+
expect(
|
|
412
|
+
applyHits(
|
|
413
|
+
['start', 'end'],
|
|
414
|
+
[{ start: 0, del: 1, add: ['next'] }],
|
|
415
|
+
'\n',
|
|
416
|
+
false,
|
|
417
|
+
),
|
|
418
|
+
).toBe('next\nend');
|
|
419
|
+
});
|
|
420
|
+
});
|