@damper/cli 0.9.18 → 0.9.20
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/dist/services/claude.js +34 -5
- package/dist/ui/task-picker.js +39 -3
- package/package.json +1 -1
package/dist/services/claude.js
CHANGED
|
@@ -335,7 +335,7 @@ export async function postTaskFlow(options) {
|
|
|
335
335
|
console.log(pc.dim('Cleaning up merge state...'));
|
|
336
336
|
await execa('git', ['reset', '--merge'], { cwd, stdio: 'pipe' }).catch(() => { });
|
|
337
337
|
}
|
|
338
|
-
await launchClaudeForMerge({ cwd, apiKey });
|
|
338
|
+
await launchClaudeForMerge({ cwd, apiKey, taskTitle });
|
|
339
339
|
// Verify merge was completed (origin/main should be ancestor of HEAD)
|
|
340
340
|
try {
|
|
341
341
|
await execa('git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'], { cwd, stdio: 'pipe' });
|
|
@@ -582,15 +582,44 @@ export async function launchClaudeForReview(options) {
|
|
|
582
582
|
/**
|
|
583
583
|
* Launch Claude to resolve merge conflicts
|
|
584
584
|
* Uses --allowedTools to restrict Claude to only git/file operations
|
|
585
|
-
* so it doesn't pick up task context and try to work on the task
|
|
585
|
+
* so it doesn't pick up task context and try to work on the task.
|
|
586
|
+
* Runs with --dangerously-skip-permissions for fully autonomous resolution.
|
|
586
587
|
*/
|
|
587
588
|
async function launchClaudeForMerge(options) {
|
|
588
|
-
const { cwd, apiKey } = options;
|
|
589
|
-
const
|
|
589
|
+
const { cwd, apiKey, taskTitle } = options;
|
|
590
|
+
const taskContext = taskTitle
|
|
591
|
+
? `You are merging a feature branch for task "${taskTitle}" into main.`
|
|
592
|
+
: 'You are merging a feature branch into main.';
|
|
593
|
+
const prompt = [
|
|
594
|
+
`MERGE CONFLICT RESOLUTION — Your ONLY job is to resolve merge conflicts.`,
|
|
595
|
+
'',
|
|
596
|
+
taskContext,
|
|
597
|
+
'',
|
|
598
|
+
'Steps:',
|
|
599
|
+
'1. Run: git merge origin/main --no-edit',
|
|
600
|
+
'2. If there are conflicts, identify all conflicted files with: git diff --name-only --diff-filter=U',
|
|
601
|
+
'3. Read each conflicted file and resolve the conflicts:',
|
|
602
|
+
' - For CLAUDE.md: keep the main (origin/main) version entirely — the feature branch appends a temporary task section that must not be merged',
|
|
603
|
+
' - For code conflicts: combine both sides logically, preserving the intent of both the feature branch changes and main branch updates',
|
|
604
|
+
' - For package.json / lock files: accept the version that satisfies both sides, preferring the newer version',
|
|
605
|
+
'4. Stage all resolved files with: git add <file>',
|
|
606
|
+
'5. Commit the merge with: git commit --no-edit',
|
|
607
|
+
'6. Verify no conflicts remain with: git diff --name-only --diff-filter=U',
|
|
608
|
+
'',
|
|
609
|
+
'IMPORTANT:',
|
|
610
|
+
'- Do NOT read TASK_CONTEXT.md or work on the task itself',
|
|
611
|
+
'- Do NOT use any MCP tools — only use Bash, Read, Write, Edit, Glob, Grep',
|
|
612
|
+
'- Do NOT make any changes beyond resolving the merge conflicts',
|
|
613
|
+
'- If a conflict is too complex to resolve confidently, leave it and explain what needs manual attention',
|
|
614
|
+
].join('\n');
|
|
590
615
|
const mergeLabel = 'Resolving merge conflicts';
|
|
591
616
|
setTerminalTitle(mergeLabel);
|
|
592
617
|
await new Promise((resolve) => {
|
|
593
|
-
const child = spawn('claude', [
|
|
618
|
+
const child = spawn('claude', [
|
|
619
|
+
'--allowedTools', 'Bash,Read,Write,Edit,Glob,Grep',
|
|
620
|
+
'--dangerously-skip-permissions',
|
|
621
|
+
prompt,
|
|
622
|
+
], {
|
|
594
623
|
cwd,
|
|
595
624
|
stdio: 'inherit',
|
|
596
625
|
env: { ...process.env, DAMPER_API_KEY: apiKey },
|
package/dist/ui/task-picker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { search, select, confirm, input, Separator } from '@inquirer/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
|
+
import * as readline from 'readline';
|
|
3
4
|
import { shortId, shortIdRaw, getTypeIcon, getPriorityIcon, formatEffort, formatProgressCompact, formatDueDate, sectionHeader, relativeTime, getTerminalWidth, padEnd, padStart, } from './format.js';
|
|
4
5
|
// Layout constants (terminal column widths)
|
|
5
6
|
const CURSOR_WIDTH = 2; // inquirer select prefix (❯ or )
|
|
@@ -265,15 +266,50 @@ export async function pickTask(options) {
|
|
|
265
266
|
action,
|
|
266
267
|
};
|
|
267
268
|
}
|
|
269
|
+
function multilineInput(message) {
|
|
270
|
+
return new Promise((resolve) => {
|
|
271
|
+
process.stdout.write(`${pc.bold(pc.green('?'))} ${pc.bold(message)} ${pc.dim('(Enter to skip)')}\n`);
|
|
272
|
+
const lines = [];
|
|
273
|
+
let hintShown = false;
|
|
274
|
+
let hintTimer = null;
|
|
275
|
+
const rl = readline.createInterface({
|
|
276
|
+
input: process.stdin,
|
|
277
|
+
output: process.stdout,
|
|
278
|
+
prompt: '',
|
|
279
|
+
});
|
|
280
|
+
const showHint = () => {
|
|
281
|
+
if (!hintShown) {
|
|
282
|
+
hintShown = true;
|
|
283
|
+
process.stdout.write(pc.dim(' (Press Enter on an empty line to submit)\n'));
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
rl.on('line', (line) => {
|
|
287
|
+
if (hintTimer)
|
|
288
|
+
clearTimeout(hintTimer);
|
|
289
|
+
if (lines.length === 0 && line === '') {
|
|
290
|
+
rl.close();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (line === '' && lines[lines.length - 1] === '') {
|
|
294
|
+
lines.pop(); // remove trailing empty line
|
|
295
|
+
rl.close();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
lines.push(line);
|
|
299
|
+
hintTimer = setTimeout(showHint, 50);
|
|
300
|
+
});
|
|
301
|
+
rl.on('close', () => {
|
|
302
|
+
resolve(lines.join('\n'));
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
268
306
|
async function handleCreateNewTask(api, searchTerm) {
|
|
269
307
|
const title = await input({
|
|
270
308
|
message: 'Task title:',
|
|
271
309
|
default: searchTerm || undefined,
|
|
272
310
|
validate: (value) => value.trim().length > 0 || 'Title is required',
|
|
273
311
|
});
|
|
274
|
-
const description = await
|
|
275
|
-
message: 'Description (optional, press Enter to skip):',
|
|
276
|
-
});
|
|
312
|
+
const description = await multilineInput('Description (optional):');
|
|
277
313
|
const type = await select({
|
|
278
314
|
message: 'Task type:',
|
|
279
315
|
choices: [
|