@damper/cli 0.1.1 → 0.1.3
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 +4 -0
- package/dist/commands/cleanup.js +35 -3
- package/dist/commands/setup.js +1 -1
- package/dist/services/worktree.js +12 -9
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -17,8 +17,12 @@ Claude then handles the task lifecycle via Damper MCP - logging commits, adding
|
|
|
17
17
|
# Run directly with npx (recommended)
|
|
18
18
|
npx @damper/cli
|
|
19
19
|
|
|
20
|
+
# Alternative: use the 'damper' command name
|
|
21
|
+
npx damper
|
|
22
|
+
|
|
20
23
|
# Or install globally
|
|
21
24
|
npm install -g @damper/cli
|
|
25
|
+
damper
|
|
22
26
|
```
|
|
23
27
|
|
|
24
28
|
### Prerequisites
|
package/dist/commands/cleanup.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import { confirm, checkbox } from '@inquirer/prompts';
|
|
3
|
+
import { execa } from 'execa';
|
|
3
4
|
import pc from 'picocolors';
|
|
4
5
|
import { getWorktrees, cleanupStaleWorktrees, removeWorktree } from '../services/state.js';
|
|
5
6
|
import { createDamperApi } from '../services/damper-api.js';
|
|
6
7
|
import { removeWorktreeDir } from '../services/worktree.js';
|
|
8
|
+
async function hasUncommittedChanges(worktreePath) {
|
|
9
|
+
try {
|
|
10
|
+
const { stdout } = await execa('git', ['status', '--porcelain'], {
|
|
11
|
+
cwd: worktreePath,
|
|
12
|
+
stdio: 'pipe',
|
|
13
|
+
});
|
|
14
|
+
return stdout.trim().length > 0;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
7
20
|
export async function cleanupCommand() {
|
|
8
21
|
// Clean up stale entries first
|
|
9
22
|
const stale = cleanupStaleWorktrees();
|
|
@@ -37,6 +50,8 @@ export async function cleanupCommand() {
|
|
|
37
50
|
});
|
|
38
51
|
continue;
|
|
39
52
|
}
|
|
53
|
+
// Check for uncommitted changes
|
|
54
|
+
const uncommitted = await hasUncommittedChanges(wt.path);
|
|
40
55
|
if (api) {
|
|
41
56
|
try {
|
|
42
57
|
const task = await api.getTask(wt.taskId);
|
|
@@ -45,6 +60,7 @@ export async function cleanupCommand() {
|
|
|
45
60
|
worktree: wt,
|
|
46
61
|
task,
|
|
47
62
|
reason: 'completed',
|
|
63
|
+
hasUncommittedChanges: uncommitted,
|
|
48
64
|
});
|
|
49
65
|
}
|
|
50
66
|
else if (task.status === 'planned' && !task.lockedBy) {
|
|
@@ -53,6 +69,7 @@ export async function cleanupCommand() {
|
|
|
53
69
|
worktree: wt,
|
|
54
70
|
task,
|
|
55
71
|
reason: 'abandoned',
|
|
72
|
+
hasUncommittedChanges: uncommitted,
|
|
56
73
|
});
|
|
57
74
|
}
|
|
58
75
|
}
|
|
@@ -61,6 +78,7 @@ export async function cleanupCommand() {
|
|
|
61
78
|
candidates.push({
|
|
62
79
|
worktree: wt,
|
|
63
80
|
reason: 'manual',
|
|
81
|
+
hasUncommittedChanges: uncommitted,
|
|
64
82
|
});
|
|
65
83
|
}
|
|
66
84
|
}
|
|
@@ -69,6 +87,7 @@ export async function cleanupCommand() {
|
|
|
69
87
|
candidates.push({
|
|
70
88
|
worktree: wt,
|
|
71
89
|
reason: 'manual',
|
|
90
|
+
hasUncommittedChanges: uncommitted,
|
|
72
91
|
});
|
|
73
92
|
}
|
|
74
93
|
}
|
|
@@ -99,10 +118,15 @@ export async function cleanupCommand() {
|
|
|
99
118
|
reasonBadge = pc.dim('[manual]');
|
|
100
119
|
break;
|
|
101
120
|
}
|
|
121
|
+
// Warning for uncommitted changes
|
|
122
|
+
const uncommittedWarning = c.hasUncommittedChanges
|
|
123
|
+
? pc.red(' ⚠ uncommitted changes')
|
|
124
|
+
: '';
|
|
102
125
|
return {
|
|
103
|
-
name: `${description} ${reasonBadge}\n ${pc.dim(worktreeName)}`,
|
|
126
|
+
name: `${description} ${reasonBadge}${uncommittedWarning}\n ${pc.dim(worktreeName)}`,
|
|
104
127
|
value: c,
|
|
105
|
-
|
|
128
|
+
// Don't auto-check if there are uncommitted changes
|
|
129
|
+
checked: (c.reason === 'completed' || c.reason === 'missing') && !c.hasUncommittedChanges,
|
|
106
130
|
};
|
|
107
131
|
});
|
|
108
132
|
const selected = await checkbox({
|
|
@@ -114,9 +138,17 @@ export async function cleanupCommand() {
|
|
|
114
138
|
console.log(pc.dim('\nNo worktrees selected. Nothing to clean up.\n'));
|
|
115
139
|
return;
|
|
116
140
|
}
|
|
141
|
+
// Check for uncommitted changes in selected worktrees
|
|
142
|
+
const withUncommitted = selected.filter(c => c.hasUncommittedChanges);
|
|
143
|
+
if (withUncommitted.length > 0) {
|
|
144
|
+
console.log(pc.yellow(`\n⚠ Warning: ${withUncommitted.length} worktree(s) have uncommitted changes that will be lost!`));
|
|
145
|
+
}
|
|
117
146
|
// Confirm
|
|
147
|
+
const confirmMessage = withUncommitted.length > 0
|
|
148
|
+
? `Remove ${selected.length} worktree(s)? (${withUncommitted.length} with uncommitted changes)`
|
|
149
|
+
: `Remove ${selected.length} worktree(s)?`;
|
|
118
150
|
const confirmed = await confirm({
|
|
119
|
-
message:
|
|
151
|
+
message: confirmMessage,
|
|
120
152
|
default: false,
|
|
121
153
|
});
|
|
122
154
|
if (!confirmed) {
|
package/dist/commands/setup.js
CHANGED
|
@@ -17,7 +17,7 @@ export async function setupCommand() {
|
|
|
17
17
|
const apiKey = getConfiguredApiKey();
|
|
18
18
|
if (mcpConfigured && apiKey) {
|
|
19
19
|
console.log(pc.green('✓ Damper MCP configured'));
|
|
20
|
-
console.log(pc.dim(` API key: ${
|
|
20
|
+
console.log(pc.dim(` API key: ${'*'.repeat(12)} (${apiKey.length} chars)`));
|
|
21
21
|
// Verify the API key works
|
|
22
22
|
console.log(pc.dim('\nVerifying API key...'));
|
|
23
23
|
try {
|
|
@@ -165,16 +165,19 @@ export async function createWorktree(options) {
|
|
|
165
165
|
}
|
|
166
166
|
// Copy .env files
|
|
167
167
|
const envFiles = findEnvFiles(projectRoot);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
if (envFiles.length > 0) {
|
|
169
|
+
console.log(pc.dim(`Copying ${envFiles.length} .env file(s) to worktree...`));
|
|
170
|
+
for (const envFile of envFiles) {
|
|
171
|
+
const source = path.join(projectRoot, envFile);
|
|
172
|
+
const target = path.join(worktreePath, envFile);
|
|
173
|
+
// Ensure parent directory exists
|
|
174
|
+
const targetDir = path.dirname(target);
|
|
175
|
+
if (!fs.existsSync(targetDir)) {
|
|
176
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
console.log(pc.dim(` ${envFile}`));
|
|
179
|
+
await fs.promises.copyFile(source, target);
|
|
175
180
|
}
|
|
176
|
-
console.log(pc.dim(`Copying ${envFile}...`));
|
|
177
|
-
await fs.promises.copyFile(source, target);
|
|
178
181
|
}
|
|
179
182
|
// Save to state
|
|
180
183
|
const worktreeState = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@damper/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "CLI tool for orchestrating Damper task workflows with Claude Code",
|
|
5
5
|
"author": "Damper <hello@usedamper.com>",
|
|
6
6
|
"repository": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"homepage": "https://usedamper.com",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
14
|
+
"damper": "./dist/index.js",
|
|
14
15
|
"damper-cli": "./dist/index.js"
|
|
15
16
|
},
|
|
16
17
|
"main": "./dist/index.js",
|