@damper/cli 0.9.19 → 0.10.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/dist/commands/release.js +4 -30
- package/dist/commands/setup.js +3 -11
- package/dist/commands/start.js +33 -115
- package/dist/index.js +4 -16
- package/dist/services/claude.d.ts +8 -9
- package/dist/services/claude.js +81 -88
- package/dist/services/config.js +13 -3
- package/dist/services/damper-api.d.ts +1 -0
- package/dist/services/git.d.ts +4 -0
- package/dist/services/git.js +11 -0
- package/dist/ui/format.d.ts +1 -1
- package/dist/ui/format.js +5 -5
- package/dist/ui/task-picker.d.ts +0 -3
- package/dist/ui/task-picker.js +6 -36
- package/package.json +2 -3
- package/dist/commands/cleanup.d.ts +0 -1
- package/dist/commands/cleanup.js +0 -203
- package/dist/commands/status.d.ts +0 -1
- package/dist/commands/status.js +0 -94
- package/dist/services/context-bootstrap.d.ts +0 -30
- package/dist/services/context-bootstrap.js +0 -100
- package/dist/services/state.d.ts +0 -22
- package/dist/services/state.js +0 -102
- package/dist/services/worktree.d.ts +0 -40
- package/dist/services/worktree.js +0 -469
- package/dist/templates/CLAUDE_APPEND.md.d.ts +0 -7
- package/dist/templates/CLAUDE_APPEND.md.js +0 -35
- package/dist/templates/TASK_CONTEXT.md.d.ts +0 -17
- package/dist/templates/TASK_CONTEXT.md.js +0 -149
package/dist/commands/release.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { confirm, select } from '@inquirer/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { createDamperApi } from '../services/damper-api.js';
|
|
4
|
-
import {
|
|
5
|
-
import { removeWorktreeDir, getGitRoot } from '../services/worktree.js';
|
|
4
|
+
import { getGitRoot } from '../services/git.js';
|
|
6
5
|
import { getApiKey } from '../services/config.js';
|
|
7
6
|
import { shortId, shortIdRaw, getTypeIcon, getPriorityIcon, formatEffort } from '../ui/format.js';
|
|
8
7
|
export async function releaseCommand() {
|
|
@@ -37,33 +36,26 @@ export async function releaseCommand() {
|
|
|
37
36
|
console.log(pc.bold(`\nProject: ${project}`));
|
|
38
37
|
console.log(pc.dim('Select a task to release back to planned status.\n'));
|
|
39
38
|
// Build choices
|
|
40
|
-
const worktrees = getWorktrees();
|
|
41
39
|
const choices = tasks.map(task => {
|
|
42
|
-
const worktree = worktrees.find(w => w.taskId === task.id);
|
|
43
40
|
const priorityIcon = getPriorityIcon(task.priority);
|
|
44
41
|
const typeIcon = getTypeIcon(task.type);
|
|
45
42
|
const id = shortId(task.id);
|
|
46
43
|
const effort = formatEffort(task.effort);
|
|
47
44
|
const lockedInfo = task.lockedBy ? pc.dim(` (locked by ${task.lockedBy})`) : '';
|
|
48
|
-
const worktreeInfo = worktree ? pc.dim(` [has worktree]`) : '';
|
|
49
45
|
return {
|
|
50
|
-
name: `${priorityIcon}${typeIcon} ${id} ${task.title}${effort ? ` ${effort}` : ''}${lockedInfo}
|
|
51
|
-
value:
|
|
46
|
+
name: `${priorityIcon}${typeIcon} ${id} ${task.title}${effort ? ` ${effort}` : ''}${lockedInfo}`,
|
|
47
|
+
value: task,
|
|
52
48
|
};
|
|
53
49
|
});
|
|
54
|
-
const
|
|
50
|
+
const task = await select({
|
|
55
51
|
message: 'Select task to release:',
|
|
56
52
|
choices,
|
|
57
53
|
});
|
|
58
|
-
const { task, worktree } = selected;
|
|
59
54
|
// Confirm
|
|
60
55
|
console.log();
|
|
61
56
|
console.log(pc.yellow(`This will:`));
|
|
62
57
|
console.log(pc.dim(` • Release the lock on task #${shortIdRaw(task.id)}`));
|
|
63
58
|
console.log(pc.dim(` • Set task status back to "planned"`));
|
|
64
|
-
if (worktree) {
|
|
65
|
-
console.log(pc.dim(` • Optionally remove the worktree`));
|
|
66
|
-
}
|
|
67
59
|
console.log();
|
|
68
60
|
const shouldRelease = await confirm({
|
|
69
61
|
message: 'Release this task?',
|
|
@@ -83,23 +75,5 @@ export async function releaseCommand() {
|
|
|
83
75
|
console.log(pc.red(`\nFailed to release task: ${error.message}\n`));
|
|
84
76
|
return;
|
|
85
77
|
}
|
|
86
|
-
// Offer to remove worktree if exists
|
|
87
|
-
if (worktree) {
|
|
88
|
-
const shouldRemoveWorktree = await confirm({
|
|
89
|
-
message: 'Remove the worktree and branch?',
|
|
90
|
-
default: false,
|
|
91
|
-
});
|
|
92
|
-
if (shouldRemoveWorktree) {
|
|
93
|
-
try {
|
|
94
|
-
await removeWorktreeDir(worktree.path, worktree.projectRoot);
|
|
95
|
-
console.log(pc.green('✓ Worktree and branch removed'));
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
const error = err;
|
|
99
|
-
console.log(pc.yellow(`Could not remove worktree: ${error.message}`));
|
|
100
|
-
removeWorktree(task.id); // At least clean up state
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
78
|
console.log();
|
|
105
79
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -55,17 +55,9 @@ export async function setupCommand(options = {}) {
|
|
|
55
55
|
if (!projectRoot) {
|
|
56
56
|
console.log(pc.yellow('\n⚠️ Not in a git repository'));
|
|
57
57
|
console.log(pc.dim(' Run this command from within a project to configure it.\n'));
|
|
58
|
-
//
|
|
58
|
+
// Can't set up MCP without API key (HTTP transport needs it in headers)
|
|
59
59
|
if (!mcpConfigured) {
|
|
60
|
-
|
|
61
|
-
message: 'Set up Damper MCP globally (without API key)?',
|
|
62
|
-
default: true,
|
|
63
|
-
});
|
|
64
|
-
if (shouldSetupMcp) {
|
|
65
|
-
configureDamperMcp();
|
|
66
|
-
console.log(pc.green('\n✓ Damper MCP configured globally'));
|
|
67
|
-
console.log(pc.dim(' Run setup again from a project directory to add an API key.\n'));
|
|
68
|
-
}
|
|
60
|
+
console.log(pc.dim('\n Run setup from a project directory to configure MCP with an API key.\n'));
|
|
69
61
|
}
|
|
70
62
|
return;
|
|
71
63
|
}
|
|
@@ -142,7 +134,7 @@ export async function setupCommand(options = {}) {
|
|
|
142
134
|
console.log(pc.green(`✓ Saved API key to ${getProjectConfigPath(projectRoot)}`));
|
|
143
135
|
// Set up global MCP if not configured
|
|
144
136
|
if (!mcpConfigured) {
|
|
145
|
-
configureDamperMcp();
|
|
137
|
+
configureDamperMcp(apiKey);
|
|
146
138
|
console.log(pc.green('✓ Configured Damper MCP globally'));
|
|
147
139
|
}
|
|
148
140
|
// Remind about .gitignore
|
package/dist/commands/start.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
1
|
import pc from 'picocolors';
|
|
3
2
|
import { createDamperApi } from '../services/damper-api.js';
|
|
4
|
-
import {
|
|
5
|
-
import { bootstrapContext, refreshContext } from '../services/context-bootstrap.js';
|
|
3
|
+
import { getGitRoot } from '../services/git.js';
|
|
6
4
|
import { pickTask } from '../ui/task-picker.js';
|
|
7
5
|
import { launchClaude, launchClaudeForReview, postTaskFlow, isClaudeInstalled, isDamperMcpConfigured, configureDamperMcp } from '../services/claude.js';
|
|
8
|
-
import { getWorktreesForProject, cleanupStaleWorktrees } from '../services/state.js';
|
|
9
6
|
import { getApiKey, isProjectConfigured, getProjectConfigPath } from '../services/config.js';
|
|
10
7
|
import { shortIdRaw } from '../ui/format.js';
|
|
11
8
|
export async function startCommand(options) {
|
|
@@ -16,10 +13,10 @@ export async function startCommand(options) {
|
|
|
16
13
|
console.log(pc.dim('Install it with: npm install -g @anthropic-ai/claude-code\n'));
|
|
17
14
|
process.exit(1);
|
|
18
15
|
}
|
|
19
|
-
// Get project root
|
|
16
|
+
// Get project root
|
|
20
17
|
let projectRoot;
|
|
21
18
|
try {
|
|
22
|
-
projectRoot = await
|
|
19
|
+
projectRoot = await getGitRoot(process.cwd());
|
|
23
20
|
}
|
|
24
21
|
catch {
|
|
25
22
|
console.log(pc.red('\nError: Not in a git repository.'));
|
|
@@ -39,37 +36,27 @@ export async function startCommand(options) {
|
|
|
39
36
|
}
|
|
40
37
|
process.exit(1);
|
|
41
38
|
}
|
|
42
|
-
// Ensure MCP is configured globally
|
|
39
|
+
// Ensure MCP is configured globally
|
|
43
40
|
if (!isDamperMcpConfigured()) {
|
|
44
41
|
console.log(pc.dim('Configuring Damper MCP...'));
|
|
45
|
-
configureDamperMcp();
|
|
42
|
+
configureDamperMcp(apiKey);
|
|
46
43
|
console.log(pc.green('✓ Damper MCP configured'));
|
|
47
44
|
}
|
|
48
45
|
// Create API client with the project's key
|
|
49
46
|
const api = createDamperApi(apiKey);
|
|
50
|
-
// Clean up stale worktrees
|
|
51
|
-
const stale = cleanupStaleWorktrees();
|
|
52
|
-
if (stale.length > 0) {
|
|
53
|
-
console.log(pc.dim(`Cleaned up ${stale.length} stale worktree reference(s)`));
|
|
54
|
-
}
|
|
55
|
-
// Get existing worktrees for this project
|
|
56
|
-
const worktrees = getWorktreesForProject(projectRoot);
|
|
57
47
|
let taskId;
|
|
58
48
|
let taskTitle;
|
|
59
49
|
let isResume = false;
|
|
60
50
|
let isNewTask = false;
|
|
61
|
-
let worktreePath;
|
|
62
51
|
let forceTakeover = options.force || false;
|
|
63
52
|
if (options.taskId) {
|
|
64
53
|
// Direct task selection
|
|
65
54
|
taskId = options.taskId;
|
|
66
55
|
const task = await api.getTask(taskId);
|
|
67
56
|
taskTitle = task.title;
|
|
68
|
-
// Check if
|
|
69
|
-
|
|
70
|
-
if (existingWorktree && fs.existsSync(existingWorktree.path)) {
|
|
57
|
+
// Check if task is already in progress (resume)
|
|
58
|
+
if (task.status === 'in_progress') {
|
|
71
59
|
isResume = true;
|
|
72
|
-
worktreePath = existingWorktree.path;
|
|
73
60
|
console.log(pc.cyan(`\nResuming task #${shortIdRaw(taskId)}: ${taskTitle}`));
|
|
74
61
|
}
|
|
75
62
|
else {
|
|
@@ -79,11 +66,8 @@ export async function startCommand(options) {
|
|
|
79
66
|
else {
|
|
80
67
|
// Interactive task picker with review loop
|
|
81
68
|
while (true) {
|
|
82
|
-
// Re-fetch worktrees each iteration (handles cleanup between reviews)
|
|
83
|
-
const currentWorktrees = getWorktreesForProject(projectRoot);
|
|
84
69
|
const result = await pickTask({
|
|
85
70
|
api,
|
|
86
|
-
worktrees: currentWorktrees,
|
|
87
71
|
typeFilter: options.type,
|
|
88
72
|
statusFilter: options.status,
|
|
89
73
|
});
|
|
@@ -95,7 +79,6 @@ export async function startCommand(options) {
|
|
|
95
79
|
api,
|
|
96
80
|
apiKey,
|
|
97
81
|
task: result.task,
|
|
98
|
-
worktree: result.worktree,
|
|
99
82
|
projectRoot,
|
|
100
83
|
});
|
|
101
84
|
continue;
|
|
@@ -106,16 +89,10 @@ export async function startCommand(options) {
|
|
|
106
89
|
isResume = result.isResume;
|
|
107
90
|
isNewTask = result.isNewTask || false;
|
|
108
91
|
forceTakeover = result.forceTakeover || options.force || false;
|
|
109
|
-
if (result.worktree) {
|
|
110
|
-
worktreePath = result.worktree.path;
|
|
111
|
-
}
|
|
112
92
|
break;
|
|
113
93
|
}
|
|
114
94
|
}
|
|
115
|
-
if (isResume
|
|
116
|
-
// Resume existing worktree
|
|
117
|
-
console.log(pc.green(`\n✓ Resuming: #${shortIdRaw(taskId)} ${taskTitle}`));
|
|
118
|
-
console.log(pc.dim(` Worktree: ${worktreePath}`));
|
|
95
|
+
if (isResume) {
|
|
119
96
|
// Re-lock the task if it was previously released
|
|
120
97
|
const task = await api.getTask(taskId);
|
|
121
98
|
if (task.status !== 'in_progress' || !task.lockedBy) {
|
|
@@ -134,23 +111,12 @@ export async function startCommand(options) {
|
|
|
134
111
|
throw err;
|
|
135
112
|
}
|
|
136
113
|
}
|
|
137
|
-
// Refresh context with latest from Damper
|
|
138
|
-
console.log(pc.dim('\nRefreshing context from Damper...'));
|
|
139
|
-
await refreshContext({
|
|
140
|
-
api,
|
|
141
|
-
taskId,
|
|
142
|
-
worktreePath,
|
|
143
|
-
yolo: options.yolo,
|
|
144
|
-
});
|
|
145
|
-
console.log(pc.green('✓ Updated TASK_CONTEXT.md with latest notes'));
|
|
146
114
|
}
|
|
147
115
|
else {
|
|
148
116
|
// New task - lock it first
|
|
149
117
|
console.log(pc.dim('\nLocking task in Damper...'));
|
|
150
|
-
let completionChecklist;
|
|
151
118
|
try {
|
|
152
|
-
|
|
153
|
-
completionChecklist = startResult.completionChecklist;
|
|
119
|
+
await api.startTask(taskId, forceTakeover);
|
|
154
120
|
console.log(pc.green(forceTakeover ? '✓ Task lock taken over' : '✓ Task locked'));
|
|
155
121
|
}
|
|
156
122
|
catch (err) {
|
|
@@ -162,39 +128,10 @@ export async function startCommand(options) {
|
|
|
162
128
|
}
|
|
163
129
|
throw err;
|
|
164
130
|
}
|
|
165
|
-
// Create worktree
|
|
166
|
-
console.log(pc.dim('\nSetting up worktree...'));
|
|
167
|
-
const worktreeResult = await createWorktree({
|
|
168
|
-
taskId,
|
|
169
|
-
taskTitle,
|
|
170
|
-
projectRoot,
|
|
171
|
-
apiKey,
|
|
172
|
-
});
|
|
173
|
-
worktreePath = worktreeResult.path;
|
|
174
|
-
if (worktreeResult.isNew) {
|
|
175
|
-
console.log(pc.green(`✓ Created worktree: ${worktreePath}`));
|
|
176
|
-
console.log(pc.dim(` Branch: ${worktreeResult.branch}`));
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
console.log(pc.green(`✓ Using existing worktree: ${worktreePath}`));
|
|
180
|
-
}
|
|
181
|
-
// Bootstrap context
|
|
182
|
-
console.log(pc.dim('\nBootstrapping context from Damper...'));
|
|
183
|
-
const bootstrapResult = await bootstrapContext({
|
|
184
|
-
api,
|
|
185
|
-
taskId,
|
|
186
|
-
worktreePath,
|
|
187
|
-
yolo: options.yolo,
|
|
188
|
-
completionChecklist,
|
|
189
|
-
});
|
|
190
|
-
console.log(pc.green(`✓ Created ${bootstrapResult.taskContextPath}`));
|
|
191
|
-
if (bootstrapResult.claudeMdUpdated) {
|
|
192
|
-
console.log(pc.green('✓ Updated CLAUDE.md with task section'));
|
|
193
|
-
}
|
|
194
131
|
}
|
|
195
|
-
// Launch Claude
|
|
132
|
+
// Launch Claude in project root
|
|
196
133
|
const result = await launchClaude({
|
|
197
|
-
cwd:
|
|
134
|
+
cwd: projectRoot,
|
|
198
135
|
taskId,
|
|
199
136
|
taskTitle,
|
|
200
137
|
apiKey,
|
|
@@ -214,14 +151,12 @@ export async function startCommand(options) {
|
|
|
214
151
|
* Review a task and attempt to complete it via Claude, then return to the picker.
|
|
215
152
|
*/
|
|
216
153
|
async function handleReviewAndComplete(options) {
|
|
217
|
-
const { api, apiKey, task,
|
|
154
|
+
const { api, apiKey, task, projectRoot } = options;
|
|
218
155
|
const { confirm } = await import('@inquirer/prompts');
|
|
219
156
|
const { execa } = await import('execa');
|
|
220
157
|
const taskId = task.id;
|
|
221
|
-
const cwd = worktree?.path || projectRoot;
|
|
222
|
-
const hasWorktree = !!worktree;
|
|
223
158
|
console.log(pc.cyan(`\nReviewing task #${shortIdRaw(taskId)}: ${task.title}`));
|
|
224
|
-
console.log(pc.dim(`Directory: ${
|
|
159
|
+
console.log(pc.dim(`Directory: ${projectRoot}`));
|
|
225
160
|
// Lock the task
|
|
226
161
|
console.log(pc.dim('\nLocking task in Damper...'));
|
|
227
162
|
try {
|
|
@@ -235,7 +170,7 @@ async function handleReviewAndComplete(options) {
|
|
|
235
170
|
}
|
|
236
171
|
// Launch Claude for review
|
|
237
172
|
console.log(pc.dim('\nLaunching Claude for review...\n'));
|
|
238
|
-
await launchClaudeForReview({ cwd, apiKey, taskId });
|
|
173
|
+
await launchClaudeForReview({ cwd: projectRoot, apiKey, taskId });
|
|
239
174
|
console.log(pc.dim('\n─────────────────────────────────────────\n'));
|
|
240
175
|
// Check task status after Claude exits
|
|
241
176
|
let taskStatus;
|
|
@@ -248,51 +183,34 @@ async function handleReviewAndComplete(options) {
|
|
|
248
183
|
}
|
|
249
184
|
if (taskStatus === 'done') {
|
|
250
185
|
console.log(pc.green(`✓ Task #${shortIdRaw(taskId)} completed!`));
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
186
|
+
// Check for unpushed commits and offer to push
|
|
187
|
+
let hasUnpushed = false;
|
|
188
|
+
try {
|
|
189
|
+
const { stdout } = await execa('git', ['log', '@{u}..HEAD', '--oneline'], { cwd: projectRoot, stdio: 'pipe' });
|
|
190
|
+
hasUnpushed = stdout.trim().length > 0;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
254
193
|
try {
|
|
255
|
-
const { stdout } = await execa('git', ['log', '
|
|
194
|
+
const { stdout } = await execa('git', ['log', 'origin/main..HEAD', '--oneline'], { cwd: projectRoot, stdio: 'pipe' });
|
|
256
195
|
hasUnpushed = stdout.trim().length > 0;
|
|
257
196
|
}
|
|
258
197
|
catch {
|
|
259
|
-
|
|
260
|
-
const { stdout } = await execa('git', ['log', 'origin/main..HEAD', '--oneline'], { cwd, stdio: 'pipe' });
|
|
261
|
-
hasUnpushed = stdout.trim().length > 0;
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
// Assume there might be commits
|
|
265
|
-
hasUnpushed = true;
|
|
266
|
-
}
|
|
198
|
+
hasUnpushed = true;
|
|
267
199
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
});
|
|
273
|
-
if (shouldPush) {
|
|
274
|
-
try {
|
|
275
|
-
const { stdout: branch } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, stdio: 'pipe' });
|
|
276
|
-
await execa('git', ['push', '-u', 'origin', branch.trim()], { cwd, stdio: 'inherit' });
|
|
277
|
-
console.log(pc.green('✓ Pushed to remote'));
|
|
278
|
-
}
|
|
279
|
-
catch {
|
|
280
|
-
console.log(pc.red('Failed to push. You can push manually.'));
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const shouldCleanup = await confirm({
|
|
285
|
-
message: 'Remove worktree and branch?',
|
|
200
|
+
}
|
|
201
|
+
if (hasUnpushed) {
|
|
202
|
+
const shouldPush = await confirm({
|
|
203
|
+
message: 'Push unpushed commits?',
|
|
286
204
|
default: true,
|
|
287
205
|
});
|
|
288
|
-
if (
|
|
206
|
+
if (shouldPush) {
|
|
289
207
|
try {
|
|
290
|
-
await
|
|
291
|
-
|
|
208
|
+
const { stdout: branch } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: projectRoot, stdio: 'pipe' });
|
|
209
|
+
await execa('git', ['push', '-u', 'origin', branch.trim()], { cwd: projectRoot, stdio: 'inherit' });
|
|
210
|
+
console.log(pc.green('✓ Pushed to remote'));
|
|
292
211
|
}
|
|
293
|
-
catch
|
|
294
|
-
|
|
295
|
-
console.log(pc.red(`Failed to remove worktree: ${error.message}`));
|
|
212
|
+
catch {
|
|
213
|
+
console.log(pc.red('Failed to push. You can push manually.'));
|
|
296
214
|
}
|
|
297
215
|
}
|
|
298
216
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { startCommand } from './commands/start.js';
|
|
4
|
-
import { statusCommand } from './commands/status.js';
|
|
5
|
-
import { cleanupCommand } from './commands/cleanup.js';
|
|
6
4
|
import { setupCommand } from './commands/setup.js';
|
|
7
5
|
import { releaseCommand } from './commands/release.js';
|
|
8
6
|
import { createRequire } from 'node:module';
|
|
@@ -15,8 +13,6 @@ ${pc.bold('@damper/cli')} - Agent orchestration for Damper tasks
|
|
|
15
13
|
${pc.bold('Usage:')}
|
|
16
14
|
npx @damper/cli Start working on a task (interactive picker)
|
|
17
15
|
npx @damper/cli setup Configure Damper MCP and API key
|
|
18
|
-
npx @damper/cli status Show all in-progress worktrees
|
|
19
|
-
npx @damper/cli cleanup Remove worktrees (completed, abandoned, or in-progress)
|
|
20
16
|
npx @damper/cli release Release a task back to planned status
|
|
21
17
|
|
|
22
18
|
${pc.bold('Options:')}
|
|
@@ -37,15 +33,13 @@ ${pc.bold('Examples:')}
|
|
|
37
33
|
npx @damper/cli # Interactive task picker
|
|
38
34
|
npx @damper/cli --task 42 # Start specific task
|
|
39
35
|
npx @damper/cli --type bug # Show only bugs
|
|
40
|
-
npx @damper/cli status # List all worktrees
|
|
41
|
-
npx @damper/cli cleanup # Clean up completed tasks
|
|
42
36
|
|
|
43
37
|
${pc.bold('Workflow:')}
|
|
44
38
|
1. CLI picks task from Damper roadmap
|
|
45
|
-
2.
|
|
46
|
-
3.
|
|
47
|
-
4.
|
|
48
|
-
5.
|
|
39
|
+
2. Locks task via Damper API
|
|
40
|
+
3. Launches Claude Code with MCP context
|
|
41
|
+
4. Claude handles task lifecycle via MCP
|
|
42
|
+
5. Post-task: push, PR, merge, changelog
|
|
49
43
|
|
|
50
44
|
${pc.dim('More info: https://usedamper.com/docs/cli')}
|
|
51
45
|
`);
|
|
@@ -114,12 +108,6 @@ async function main() {
|
|
|
114
108
|
case 'setup':
|
|
115
109
|
await setupCommand(setupOptions);
|
|
116
110
|
break;
|
|
117
|
-
case 'status':
|
|
118
|
-
await statusCommand();
|
|
119
|
-
break;
|
|
120
|
-
case 'cleanup':
|
|
121
|
-
await cleanupCommand();
|
|
122
|
-
break;
|
|
123
111
|
case 'release':
|
|
124
112
|
await releaseCommand();
|
|
125
113
|
break;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
interface
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
interface McpServerConfigHttp {
|
|
2
|
+
type: 'http';
|
|
3
|
+
url: string;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Check if Damper MCP is configured in Claude settings
|
|
@@ -13,15 +13,14 @@ export declare function isDamperMcpConfigured(): boolean;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function getConfiguredApiKey(): string | undefined;
|
|
15
15
|
/**
|
|
16
|
-
* Get the recommended MCP configuration
|
|
17
|
-
* Note: API key is passed via environment when launching Claude, not stored in config
|
|
16
|
+
* Get the recommended MCP configuration (hosted Streamable HTTP)
|
|
18
17
|
*/
|
|
19
|
-
export declare function getDamperMcpConfig():
|
|
18
|
+
export declare function getDamperMcpConfig(apiKey?: string): McpServerConfigHttp;
|
|
20
19
|
/**
|
|
21
20
|
* Configure Damper MCP in Claude settings (global)
|
|
22
|
-
*
|
|
21
|
+
* With HTTP transport, the API key is stored in the config headers.
|
|
23
22
|
*/
|
|
24
|
-
export declare function configureDamperMcp(): void;
|
|
23
|
+
export declare function configureDamperMcp(apiKey?: string): void;
|
|
25
24
|
/**
|
|
26
25
|
* Launch Claude Code in a directory
|
|
27
26
|
*/
|