k0ntext 3.3.0 → 3.5.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/README.md +240 -26
- package/dist/agents/cleanup-agent.d.ts.map +1 -1
- package/dist/agents/cleanup-agent.js +18 -6
- package/dist/agents/cleanup-agent.js.map +1 -1
- package/dist/agents/drift-agent.d.ts +7 -0
- package/dist/agents/drift-agent.d.ts.map +1 -1
- package/dist/agents/drift-agent.js +29 -8
- package/dist/agents/drift-agent.js.map +1 -1
- package/dist/cli/commands/cleanup.d.ts.map +1 -1
- package/dist/cli/commands/cleanup.js +8 -1
- package/dist/cli/commands/cleanup.js.map +1 -1
- package/dist/cli/commands/drift-detect.d.ts.map +1 -1
- package/dist/cli/commands/drift-detect.js +21 -1
- package/dist/cli/commands/drift-detect.js.map +1 -1
- package/dist/cli/commands/restore.d.ts +12 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +261 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/sync-templates.d.ts +15 -0
- package/dist/cli/commands/sync-templates.d.ts.map +1 -0
- package/dist/cli/commands/sync-templates.js +181 -0
- package/dist/cli/commands/sync-templates.js.map +1 -0
- package/dist/cli/commands/version-check.d.ts +12 -0
- package/dist/cli/commands/version-check.d.ts.map +1 -0
- package/dist/cli/commands/version-check.js +133 -0
- package/dist/cli/commands/version-check.js.map +1 -0
- package/dist/cli/generate.d.ts +5 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +80 -16
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/index.js +145 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl/index.d.ts +4 -0
- package/dist/cli/repl/index.d.ts.map +1 -1
- package/dist/cli/repl/index.js +113 -95
- package/dist/cli/repl/index.js.map +1 -1
- package/dist/cli/repl/tui/panels/config.d.ts +71 -0
- package/dist/cli/repl/tui/panels/config.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/config.js +392 -0
- package/dist/cli/repl/tui/panels/config.js.map +1 -0
- package/dist/cli/repl/tui/panels/drift.d.ts +95 -0
- package/dist/cli/repl/tui/panels/drift.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/drift.js +353 -0
- package/dist/cli/repl/tui/panels/drift.js.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts +86 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.js +254 -0
- package/dist/cli/repl/tui/panels/indexing.js.map +1 -0
- package/dist/cli/repl/tui/panels/search.d.ts +66 -0
- package/dist/cli/repl/tui/panels/search.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/search.js +215 -0
- package/dist/cli/repl/tui/panels/search.js.map +1 -0
- package/dist/cli/utils/backup-manager.d.ts +94 -0
- package/dist/cli/utils/backup-manager.d.ts.map +1 -0
- package/dist/cli/utils/backup-manager.js +230 -0
- package/dist/cli/utils/backup-manager.js.map +1 -0
- package/dist/cli/utils/file-detector.d.ts +87 -0
- package/dist/cli/utils/file-detector.d.ts.map +1 -0
- package/dist/cli/utils/file-detector.js +131 -0
- package/dist/cli/utils/file-detector.js.map +1 -0
- package/dist/cli/utils/index.d.ts +9 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/index.js +9 -0
- package/dist/cli/utils/index.js.map +1 -0
- package/dist/cli/utils/modification-prompt.d.ts +41 -0
- package/dist/cli/utils/modification-prompt.d.ts.map +1 -0
- package/dist/cli/utils/modification-prompt.js +84 -0
- package/dist/cli/utils/modification-prompt.js.map +1 -0
- package/dist/cli/version/checker.d.ts +47 -0
- package/dist/cli/version/checker.d.ts.map +1 -0
- package/dist/cli/version/checker.js +143 -0
- package/dist/cli/version/checker.js.map +1 -0
- package/dist/cli/version/comparator.d.ts +46 -0
- package/dist/cli/version/comparator.d.ts.map +1 -0
- package/dist/cli/version/comparator.js +99 -0
- package/dist/cli/version/comparator.js.map +1 -0
- package/dist/cli/version/index.d.ts +11 -0
- package/dist/cli/version/index.d.ts.map +1 -0
- package/dist/cli/version/index.js +11 -0
- package/dist/cli/version/index.js.map +1 -0
- package/dist/cli/version/parser.d.ts +38 -0
- package/dist/cli/version/parser.d.ts.map +1 -0
- package/dist/cli/version/parser.js +90 -0
- package/dist/cli/version/parser.js.map +1 -0
- package/dist/cli/version/prompt.d.ts +40 -0
- package/dist/cli/version/prompt.d.ts.map +1 -0
- package/dist/cli/version/prompt.js +162 -0
- package/dist/cli/version/prompt.js.map +1 -0
- package/dist/cli/version/types.d.ts +89 -0
- package/dist/cli/version/types.d.ts.map +1 -0
- package/dist/cli/version/types.js +7 -0
- package/dist/cli/version/types.js.map +1 -0
- package/dist/db/client.d.ts +64 -2
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +148 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/schema.d.ts +41 -2
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +77 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/mcp.js +2 -2
- package/dist/mcp.js.map +1 -1
- package/dist/template-engine/data-transformer.d.ts +17 -0
- package/dist/template-engine/data-transformer.d.ts.map +1 -0
- package/dist/template-engine/data-transformer.js +343 -0
- package/dist/template-engine/data-transformer.js.map +1 -0
- package/dist/template-engine/engine.d.ts +74 -0
- package/dist/template-engine/engine.d.ts.map +1 -0
- package/dist/template-engine/engine.js +183 -0
- package/dist/template-engine/engine.js.map +1 -0
- package/dist/template-engine/helpers.d.ts +81 -0
- package/dist/template-engine/helpers.d.ts.map +1 -0
- package/dist/template-engine/helpers.js +153 -0
- package/dist/template-engine/helpers.js.map +1 -0
- package/dist/template-engine/index.d.ts +10 -0
- package/dist/template-engine/index.d.ts.map +1 -0
- package/dist/template-engine/index.js +10 -0
- package/dist/template-engine/index.js.map +1 -0
- package/dist/template-engine/types.d.ts +147 -0
- package/dist/template-engine/types.d.ts.map +1 -0
- package/dist/template-engine/types.js +7 -0
- package/dist/template-engine/types.js.map +1 -0
- package/dist/template-sync/comparator.d.ts +138 -0
- package/dist/template-sync/comparator.d.ts.map +1 -0
- package/dist/template-sync/comparator.js +353 -0
- package/dist/template-sync/comparator.js.map +1 -0
- package/dist/template-sync/conflict-resolver.d.ts +112 -0
- package/dist/template-sync/conflict-resolver.d.ts.map +1 -0
- package/dist/template-sync/conflict-resolver.js +328 -0
- package/dist/template-sync/conflict-resolver.js.map +1 -0
- package/dist/template-sync/engine.d.ts +93 -0
- package/dist/template-sync/engine.d.ts.map +1 -0
- package/dist/template-sync/engine.js +350 -0
- package/dist/template-sync/engine.js.map +1 -0
- package/dist/template-sync/hasher.d.ts +67 -0
- package/dist/template-sync/hasher.d.ts.map +1 -0
- package/dist/template-sync/hasher.js +94 -0
- package/dist/template-sync/hasher.js.map +1 -0
- package/dist/template-sync/index.d.ts +20 -0
- package/dist/template-sync/index.d.ts.map +1 -0
- package/dist/template-sync/index.js +14 -0
- package/dist/template-sync/index.js.map +1 -0
- package/dist/template-sync/manifest.d.ts +131 -0
- package/dist/template-sync/manifest.d.ts.map +1 -0
- package/dist/template-sync/manifest.js +309 -0
- package/dist/template-sync/manifest.js.map +1 -0
- package/dist/template-sync/merger.d.ts +125 -0
- package/dist/template-sync/merger.d.ts.map +1 -0
- package/dist/template-sync/merger.js +371 -0
- package/dist/template-sync/merger.js.map +1 -0
- package/dist/template-sync/scanner.d.ts +106 -0
- package/dist/template-sync/scanner.d.ts.map +1 -0
- package/dist/template-sync/scanner.js +196 -0
- package/dist/template-sync/scanner.js.map +1 -0
- package/dist/template-sync/types.d.ts +199 -0
- package/dist/template-sync/types.d.ts.map +1 -0
- package/dist/template-sync/types.js +30 -0
- package/dist/template-sync/types.js.map +1 -0
- package/package.json +2 -1
- package/src/agents/cleanup-agent.ts +21 -6
- package/src/agents/drift-agent.ts +31 -8
- package/src/cli/commands/cleanup.ts +9 -1
- package/src/cli/commands/drift-detect.ts +24 -1
- package/src/cli/commands/restore.ts +318 -0
- package/src/cli/commands/sync-templates.ts +210 -0
- package/src/cli/commands/version-check.ts +158 -0
- package/src/cli/generate.ts +99 -17
- package/src/cli/index.ts +167 -1
- package/src/cli/repl/index.ts +118 -105
- package/src/cli/repl/tui/panels/config.ts +457 -0
- package/src/cli/repl/tui/panels/drift.ts +458 -0
- package/src/cli/repl/tui/panels/indexing.ts +324 -0
- package/src/cli/repl/tui/panels/search.ts +272 -0
- package/src/cli/utils/backup-manager.ts +275 -0
- package/src/cli/utils/file-detector.ts +181 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/cli/utils/modification-prompt.ts +112 -0
- package/src/cli/version/checker.ts +172 -0
- package/src/cli/version/comparator.ts +106 -0
- package/src/cli/version/index.ts +11 -0
- package/src/cli/version/parser.ts +101 -0
- package/src/cli/version/prompt.ts +208 -0
- package/src/cli/version/types.ts +95 -0
- package/src/db/client.ts +220 -3
- package/src/db/schema.ts +109 -2
- package/src/mcp.ts +2 -2
- package/src/template-engine/data-transformer.ts +367 -0
- package/src/template-engine/engine.ts +213 -0
- package/src/template-engine/helpers.ts +163 -0
- package/src/template-engine/index.ts +10 -0
- package/src/template-engine/types.ts +158 -0
- package/src/template-sync/comparator.ts +452 -0
- package/src/template-sync/conflict-resolver.ts +401 -0
- package/src/template-sync/engine.ts +417 -0
- package/src/template-sync/hasher.ts +104 -0
- package/src/template-sync/index.ts +60 -0
- package/src/template-sync/manifest.ts +358 -0
- package/src/template-sync/merger.ts +454 -0
- package/src/template-sync/scanner.ts +254 -0
- package/src/template-sync/types.ts +247 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflict Resolver
|
|
3
|
+
*
|
|
4
|
+
* Handles interactive conflict resolution for template sync.
|
|
5
|
+
* Uses @inquirer/prompts for user interaction.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { confirm, select, checkbox } from '@inquirer/prompts';
|
|
10
|
+
import type { FileComparison, ResolutionChoice, ResolutionResult } from './types.js';
|
|
11
|
+
import { TemplateMerger } from './merger.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolution options for a single conflict
|
|
15
|
+
*/
|
|
16
|
+
export interface ResolutionOptions {
|
|
17
|
+
/** Whether to show diffs by default */
|
|
18
|
+
showDiff?: boolean;
|
|
19
|
+
/** Whether to allow batch resolution */
|
|
20
|
+
allowBatch?: boolean;
|
|
21
|
+
/** Default choice */
|
|
22
|
+
defaultChoice?: ResolutionChoice;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Batch resolution strategy
|
|
27
|
+
*/
|
|
28
|
+
export type BatchStrategy = 'keep-all' | 'overwrite-all' | 'individual';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Handles interactive conflict resolution
|
|
32
|
+
*/
|
|
33
|
+
export class ConflictResolver {
|
|
34
|
+
constructor(
|
|
35
|
+
private projectRoot: string = process.cwd(),
|
|
36
|
+
private templateRoot: string
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve conflicts interactively
|
|
41
|
+
*
|
|
42
|
+
* @param conflicts - Array of conflicting comparisons
|
|
43
|
+
* @param options - Resolution options
|
|
44
|
+
* @returns Array of resolution results
|
|
45
|
+
*/
|
|
46
|
+
async resolveConflicts(
|
|
47
|
+
conflicts: FileComparison[],
|
|
48
|
+
options: ResolutionOptions = {}
|
|
49
|
+
): Promise<ResolutionResult[]> {
|
|
50
|
+
if (conflicts.length === 0) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const results: ResolutionResult[] = [];
|
|
55
|
+
const allowBatch = options.allowBatch !== false;
|
|
56
|
+
|
|
57
|
+
// Show summary
|
|
58
|
+
this.showConflictSummary(conflicts);
|
|
59
|
+
|
|
60
|
+
// Ask for batch vs individual resolution
|
|
61
|
+
if (allowBatch && conflicts.length > 1) {
|
|
62
|
+
const strategy = await this.promptBatchStrategy(conflicts.length);
|
|
63
|
+
|
|
64
|
+
if (strategy === 'keep-all') {
|
|
65
|
+
// Keep all local versions
|
|
66
|
+
for (const conflict of conflicts) {
|
|
67
|
+
results.push({ path: conflict.path, choice: 'keep-local' });
|
|
68
|
+
}
|
|
69
|
+
return results;
|
|
70
|
+
} else if (strategy === 'overwrite-all') {
|
|
71
|
+
// Overwrite all with templates
|
|
72
|
+
for (const conflict of conflicts) {
|
|
73
|
+
results.push({ path: conflict.path, choice: 'overwrite' });
|
|
74
|
+
}
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
// Otherwise, continue to individual resolution
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Individual resolution
|
|
81
|
+
for (const conflict of conflicts) {
|
|
82
|
+
const result = await this.resolveConflict(conflict, options);
|
|
83
|
+
results.push(result);
|
|
84
|
+
|
|
85
|
+
// Exit if user cancels
|
|
86
|
+
if (result.choice === 'skip') {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve a single conflict
|
|
96
|
+
*
|
|
97
|
+
* @param conflict - File comparison in conflict
|
|
98
|
+
* @param options - Resolution options
|
|
99
|
+
* @returns Resolution result
|
|
100
|
+
*/
|
|
101
|
+
async resolveConflict(
|
|
102
|
+
conflict: FileComparison,
|
|
103
|
+
options: ResolutionOptions = {}
|
|
104
|
+
): Promise<ResolutionResult> {
|
|
105
|
+
// Show file info
|
|
106
|
+
this.showConflictInfo(conflict);
|
|
107
|
+
|
|
108
|
+
// Ask for resolution
|
|
109
|
+
const choice = await this.promptResolution(conflict, options);
|
|
110
|
+
|
|
111
|
+
if (choice === 'show-diff') {
|
|
112
|
+
// Show diff and re-prompt
|
|
113
|
+
await this.showDiff(conflict);
|
|
114
|
+
return await this.resolveConflict(conflict, options);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
path: conflict.path,
|
|
119
|
+
choice
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Prompt for batch resolution strategy
|
|
125
|
+
*
|
|
126
|
+
* @param count - Number of conflicts
|
|
127
|
+
* @returns Batch strategy
|
|
128
|
+
*/
|
|
129
|
+
private async promptBatchStrategy(count: number): Promise<BatchStrategy> {
|
|
130
|
+
return await select({
|
|
131
|
+
message: `Resolve ${count} conflicts:`,
|
|
132
|
+
choices: [
|
|
133
|
+
{
|
|
134
|
+
name: 'Keep all local versions',
|
|
135
|
+
value: 'keep-all',
|
|
136
|
+
description: 'Skip updating all conflicted files'
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'Overwrite all with templates',
|
|
140
|
+
value: 'overwrite-all',
|
|
141
|
+
description: 'Replace all local files with template versions (backups created)'
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'Resolve individually',
|
|
145
|
+
value: 'individual',
|
|
146
|
+
description: 'Review and resolve each conflict separately'
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Prompt for resolution of a single conflict
|
|
154
|
+
*
|
|
155
|
+
* @param conflict - File comparison
|
|
156
|
+
* @param options - Resolution options
|
|
157
|
+
* @returns Resolution choice
|
|
158
|
+
*/
|
|
159
|
+
private async promptResolution(
|
|
160
|
+
conflict: FileComparison,
|
|
161
|
+
options: ResolutionOptions
|
|
162
|
+
): Promise<ResolutionChoice> {
|
|
163
|
+
const baseChoices = [
|
|
164
|
+
{
|
|
165
|
+
name: 'Show diff',
|
|
166
|
+
value: 'show-diff' as const,
|
|
167
|
+
description: 'View differences before deciding'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'Keep local version',
|
|
171
|
+
value: 'keep-local' as const,
|
|
172
|
+
description: 'Skip updating this file (preserves your changes)'
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'Overwrite with template',
|
|
176
|
+
value: 'overwrite' as const,
|
|
177
|
+
description: 'Replace local file with template version (backup created)'
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
const skipChoice = {
|
|
182
|
+
name: 'Skip for now',
|
|
183
|
+
value: 'skip' as const,
|
|
184
|
+
description: 'Leave unchanged and continue to next file'
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const choices = options.defaultChoice !== 'skip'
|
|
188
|
+
? [...baseChoices, skipChoice]
|
|
189
|
+
: baseChoices;
|
|
190
|
+
|
|
191
|
+
return await select<ResolutionChoice>({
|
|
192
|
+
message: 'How would you like to resolve this?',
|
|
193
|
+
choices,
|
|
194
|
+
default: options.defaultChoice ?? 'keep-local'
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Show conflict information
|
|
200
|
+
*
|
|
201
|
+
* @param conflict - File comparison
|
|
202
|
+
*/
|
|
203
|
+
private showConflictInfo(conflict: FileComparison): void {
|
|
204
|
+
console.log('');
|
|
205
|
+
console.log(chalk.cyan(`File: ${conflict.path}`));
|
|
206
|
+
console.log(chalk.dim(`State: ${this.formatState(conflict.state)}`));
|
|
207
|
+
|
|
208
|
+
if (conflict.userModified) {
|
|
209
|
+
console.log(chalk.yellow(' ⚠ You have modified this file'));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Show file info if hashes differ
|
|
213
|
+
if (conflict.templateHash !== conflict.localHash) {
|
|
214
|
+
console.log(chalk.dim(` Template hash: ${conflict.templateHash}`));
|
|
215
|
+
console.log(chalk.dim(` Local hash: ${conflict.localHash}`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Show conflict summary
|
|
221
|
+
*
|
|
222
|
+
* @param conflicts - Array of conflicts
|
|
223
|
+
*/
|
|
224
|
+
private showConflictSummary(conflicts: FileComparison[]): void {
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log(chalk.yellow(`⚠ ${conflicts.length} conflict(s) detected:\n`));
|
|
227
|
+
|
|
228
|
+
for (const conflict of conflicts) {
|
|
229
|
+
const icon = conflict.userModified ? '⚠' : '→';
|
|
230
|
+
const userFlag = conflict.userModified ? ' [modified]' : '';
|
|
231
|
+
console.log(chalk.dim(` ${icon} ${conflict.path}${userFlag}`));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Show diff for a conflict
|
|
239
|
+
*
|
|
240
|
+
* @param conflict - File comparison
|
|
241
|
+
*/
|
|
242
|
+
private async showDiff(conflict: FileComparison): Promise<void> {
|
|
243
|
+
const merger = new TemplateMerger(this.projectRoot, this.templateRoot, {
|
|
244
|
+
generateDiffs: true
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const templatePath = path.join(this.templateRoot, conflict.path);
|
|
248
|
+
const localPath = path.join(this.projectRoot, '.claude', conflict.path);
|
|
249
|
+
|
|
250
|
+
const diff = await merger.generateDiff(localPath, templatePath);
|
|
251
|
+
|
|
252
|
+
console.log('');
|
|
253
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
254
|
+
console.log(chalk.bold('Diff:'));
|
|
255
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
256
|
+
console.log(chalk.dim(diff));
|
|
257
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
258
|
+
console.log('');
|
|
259
|
+
|
|
260
|
+
// Wait for user to acknowledge
|
|
261
|
+
await confirm({
|
|
262
|
+
message: 'Press Enter to continue...',
|
|
263
|
+
default: true
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Format file state for display
|
|
269
|
+
*
|
|
270
|
+
* @param state - File state
|
|
271
|
+
* @returns Formatted state string
|
|
272
|
+
*/
|
|
273
|
+
private formatState(state: FileComparison['state']): string {
|
|
274
|
+
const stateLabels: Record<FileComparison['state'], string> = {
|
|
275
|
+
identical: 'Identical to template',
|
|
276
|
+
'safe-update': 'Template updated (safe to apply)',
|
|
277
|
+
conflict: 'Modified in both template and locally',
|
|
278
|
+
new: 'New file in template',
|
|
279
|
+
deleted: 'Removed from template',
|
|
280
|
+
'user-only': 'User-only file (not in template)'
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
return stateLabels[state] || state;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Apply resolution results using merger
|
|
288
|
+
*
|
|
289
|
+
* @param resolutions - Array of resolution results
|
|
290
|
+
* @param conflicts - Original conflicts for reference
|
|
291
|
+
* @returns Results of applied resolutions
|
|
292
|
+
*/
|
|
293
|
+
async applyResolutions(
|
|
294
|
+
resolutions: ResolutionResult[],
|
|
295
|
+
conflicts: FileComparison[]
|
|
296
|
+
): Promise<{ applied: number; skipped: number; errors: number }> {
|
|
297
|
+
let applied = 0;
|
|
298
|
+
let skipped = 0;
|
|
299
|
+
let errors = 0;
|
|
300
|
+
|
|
301
|
+
const merger = new TemplateMerger(this.projectRoot, this.templateRoot);
|
|
302
|
+
|
|
303
|
+
for (const resolution of resolutions) {
|
|
304
|
+
const conflict = conflicts.find(c => c.path === resolution.path);
|
|
305
|
+
if (!conflict) continue;
|
|
306
|
+
|
|
307
|
+
if (resolution.choice === 'overwrite') {
|
|
308
|
+
const templatePath = path.join(this.templateRoot, conflict.path);
|
|
309
|
+
const localPath = path.join(this.projectRoot, '.claude', conflict.path);
|
|
310
|
+
|
|
311
|
+
const result = await merger.overwriteFile(templatePath, localPath);
|
|
312
|
+
if (result.success) {
|
|
313
|
+
applied++;
|
|
314
|
+
} else {
|
|
315
|
+
errors++;
|
|
316
|
+
console.error(chalk.red(`Error overwriting ${conflict.path}: ${result.error}`));
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
skipped++;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return { applied, skipped, errors };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Prompt for archival of removed files
|
|
328
|
+
*
|
|
329
|
+
* @param removedFiles - Array of removed file paths
|
|
330
|
+
* @returns true if user wants to archive
|
|
331
|
+
*/
|
|
332
|
+
async promptArchiveRemoved(removedFiles: string[]): Promise<boolean> {
|
|
333
|
+
if (removedFiles.length === 0) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log('');
|
|
338
|
+
console.log(chalk.yellow(`The following ${removedFiles.length} file(s) have been removed from the template:`));
|
|
339
|
+
for (const file of removedFiles.slice(0, 5)) {
|
|
340
|
+
console.log(chalk.dim(` - ${file}`));
|
|
341
|
+
}
|
|
342
|
+
if (removedFiles.length > 5) {
|
|
343
|
+
console.log(chalk.dim(` ... and ${removedFiles.length - 5} more`));
|
|
344
|
+
}
|
|
345
|
+
console.log('');
|
|
346
|
+
|
|
347
|
+
return await confirm({
|
|
348
|
+
message: 'Archive these files to .k0ntext/archive/ instead of deleting?',
|
|
349
|
+
default: true
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Show dry run results
|
|
355
|
+
*
|
|
356
|
+
* @param comparisons - File comparisons
|
|
357
|
+
*/
|
|
358
|
+
showDryRunResults(comparisons: FileComparison[]): void {
|
|
359
|
+
console.log('');
|
|
360
|
+
console.log(chalk.bold('Dry Run Results:\n'));
|
|
361
|
+
|
|
362
|
+
const byState = comparisons.reduce((acc, c) => {
|
|
363
|
+
acc[c.state] = acc[c.state] ?? [];
|
|
364
|
+
acc[c.state].push(c);
|
|
365
|
+
return acc;
|
|
366
|
+
}, {} as Record<string, FileComparison[]>);
|
|
367
|
+
|
|
368
|
+
const stateOrder: FileComparison['state'][] = [
|
|
369
|
+
'new',
|
|
370
|
+
'safe-update',
|
|
371
|
+
'conflict',
|
|
372
|
+
'identical',
|
|
373
|
+
'user-only',
|
|
374
|
+
'deleted'
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
for (const state of stateOrder) {
|
|
378
|
+
const files = byState[state];
|
|
379
|
+
if (!files || files.length === 0) continue;
|
|
380
|
+
|
|
381
|
+
const stateLabel = this.formatState(state);
|
|
382
|
+
const icon = state === 'conflict' ? '⚠' : state === 'new' ? '+' : state === 'safe-update' ? '→' : '•';
|
|
383
|
+
|
|
384
|
+
console.log(chalk.cyan(`${icon} ${stateLabel}: ${files.length}`));
|
|
385
|
+
|
|
386
|
+
for (const file of files.slice(0, 5)) {
|
|
387
|
+
const userFlag = file.userModified ? ' [modified]' : '';
|
|
388
|
+
console.log(chalk.dim(` ${file.path}${userFlag}`));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (files.length > 5) {
|
|
392
|
+
console.log(chalk.dim(` ... and ${files.length - 5} more`));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log('');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Import path for dynamic use
|
|
401
|
+
import path from 'path';
|