@damper/cli 0.9.9 → 0.9.10

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.
@@ -219,7 +219,7 @@ export declare class DamperApi {
219
219
  title: string;
220
220
  }>;
221
221
  }>;
222
- createTask(title: string, type?: 'bug' | 'feature' | 'improvement' | 'task', description?: string): Promise<Task>;
222
+ createTask(title: string, type?: 'bug' | 'feature' | 'improvement' | 'task', description?: string, isPublic?: boolean): Promise<Task>;
223
223
  deleteTask(taskId: string): Promise<{
224
224
  id: string;
225
225
  deleted: boolean;
@@ -189,8 +189,8 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
189
189
  return this.request('POST', `/api/agent/changelogs/${changelogId}/items`, { taskIds });
190
190
  }
191
191
  // Create task
192
- async createTask(title, type = 'task', description) {
193
- return this.request('POST', '/api/agent/tasks', { title, type, status: 'planned', description });
192
+ async createTask(title, type = 'task', description, isPublic) {
193
+ return this.request('POST', '/api/agent/tasks', { title, type, status: 'planned', description, isPublic });
194
194
  }
195
195
  // Delete task (only planned, no commits)
196
196
  async deleteTask(taskId) {
@@ -27,7 +27,7 @@ ${planSection}
27
27
  1. **Do NOT commit or complete tasks without explicit user confirmation** - Always ask the user before running \`git commit\` or calling \`complete_task\`
28
28
  2. Use \`add_commit\` after each git commit
29
29
  3. Use \`add_note\` ONLY for non-obvious approach decisions (e.g. "Decision: chose X because Y")
30
- 4. When user confirms: call \`complete_task\` with a one-line summary
30
+ 4. When user confirms: call \`complete_task\` with summary and \`reviewInstructions\` (what to test/verify)
31
31
  5. If stopping early: call \`abandon_task\` with what remains and blockers
32
32
 
33
33
  The CLI just bootstrapped this environment - YOU handle the task lifecycle.
@@ -174,11 +174,19 @@ export async function pickTask(options) {
174
174
  }
175
175
  }
176
176
  console.log(pc.bold(`\nProject: ${project}`));
177
+ // Track last search term for passing to create flow
178
+ let lastSearchTerm = '';
177
179
  // Build choices with search filtering support
178
180
  const buildChoices = (term) => {
179
181
  const lowerTerm = term?.toLowerCase().trim() || '';
182
+ lastSearchTerm = term?.trim() || '';
180
183
  const matches = (task) => !lowerTerm || task.title.toLowerCase().includes(lowerTerm) || task.id.includes(lowerTerm);
181
184
  const filtered = [];
185
+ // Always show "Create new task" at the top
186
+ const createLabel = lastSearchTerm
187
+ ? pc.green(`+ New task: "${lastSearchTerm}"`)
188
+ : pc.green('+ Create new task');
189
+ filtered.push({ name: createLabel, value: { type: 'create_new' } });
182
190
  const filteredInProgress = inProgressChoices.filter(c => matches(c.task));
183
191
  if (filteredInProgress.length > 0) {
184
192
  filtered.push(new Separator(`\n${sectionHeader(`In Progress (${filteredInProgress.length})`)}`));
@@ -200,9 +208,6 @@ export async function pickTask(options) {
200
208
  filtered.push({ name: formatTaskChoice(choice, titleWidth, layout), value: choice });
201
209
  }
202
210
  }
203
- // Always show "Create new task"
204
- filtered.push(new Separator(''));
205
- filtered.push({ name: pc.green('+ Create new task'), value: { type: 'create_new' } });
206
211
  return filtered;
207
212
  };
208
213
  const selected = await search({
@@ -211,7 +216,7 @@ export async function pickTask(options) {
211
216
  pageSize: 20,
212
217
  });
213
218
  if (selected.type === 'create_new') {
214
- return handleCreateNewTask(api);
219
+ return handleCreateNewTask(api, lastSearchTerm);
215
220
  }
216
221
  if (selected.type === 'in_progress') {
217
222
  const action = await select({
@@ -260,41 +265,14 @@ export async function pickTask(options) {
260
265
  action,
261
266
  };
262
267
  }
263
- async function generateTaskTitle(instructions, type) {
264
- try {
265
- const { execa } = await import('execa');
266
- const prompt = [
267
- `Generate a task title for a ${type} task.`,
268
- 'Rules:',
269
- '- Max 60 characters, sentence case, no trailing period',
270
- '- Verb prefix matching type: bug → "Fix …", feature → "Add …", improvement → "Improve …", task → "Set up …" / "Update …"',
271
- '- Specific enough to understand without reading a description',
272
- '- Examples: "Add dark mode support", "Fix login timeout on slow connections", "Improve search result ranking"',
273
- 'Return ONLY the title, nothing else.',
274
- '',
275
- `User input: ${instructions}`,
276
- ].join('\n');
277
- const { stdout } = await execa('claude', ['--print', '--model', 'haiku', prompt], {
278
- stdio: 'pipe',
279
- timeout: 15000,
280
- });
281
- const title = stdout.trim();
282
- if (title && title.length <= 80 && !title.includes('\n')) {
283
- return title;
284
- }
285
- return null;
286
- }
287
- catch {
288
- return null;
289
- }
290
- }
291
- async function handleCreateNewTask(api) {
292
- const instructions = await input({
293
- message: 'What needs to be done?',
294
- validate: (value) => value.trim().length > 0 || 'Instructions are required',
268
+ async function handleCreateNewTask(api, searchTerm) {
269
+ const title = await input({
270
+ message: 'Task title:',
271
+ default: searchTerm || undefined,
272
+ validate: (value) => value.trim().length > 0 || 'Title is required',
295
273
  });
296
- const details = await input({
297
- message: 'Additional details (optional, press Enter to skip):',
274
+ const description = await input({
275
+ message: 'Description (optional, press Enter to skip):',
298
276
  });
299
277
  const type = await select({
300
278
  message: 'Task type:',
@@ -305,24 +283,17 @@ async function handleCreateNewTask(api) {
305
283
  { name: 'Task', value: 'task' },
306
284
  ],
307
285
  });
308
- const trimmed = instructions.trim();
309
- const trimmedDetails = details.trim();
310
- const fullInput = trimmedDetails ? `${trimmed}\n\n${trimmedDetails}` : trimmed;
311
- // Generate title with Claude, fall back to truncation
312
- console.log(pc.dim('\nGenerating title...'));
313
- const generatedTitle = await generateTaskTitle(fullInput, type);
314
- let title;
315
- if (generatedTitle) {
316
- title = generatedTitle;
317
- }
318
- else {
319
- const maxTitleLen = 60;
320
- title = trimmed.length <= maxTitleLen
321
- ? trimmed
322
- : trimmed.slice(0, trimmed.lastIndexOf(' ', maxTitleLen) || maxTitleLen) + '…';
323
- }
286
+ const isPublic = await select({
287
+ message: 'Visibility:',
288
+ choices: [
289
+ { name: 'Public', value: true },
290
+ { name: 'Private', value: false },
291
+ ],
292
+ });
293
+ const trimmedTitle = title.trim();
294
+ const trimmedDescription = description.trim() || undefined;
324
295
  console.log(pc.dim('Creating task in Damper...'));
325
- const task = await api.createTask(title, type, fullInput);
296
+ const task = await api.createTask(trimmedTitle, type, trimmedDescription, isPublic);
326
297
  console.log(pc.green(`✓ Created task #${shortIdRaw(task.id)}: ${task.title}`));
327
298
  return {
328
299
  task,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/cli",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "description": "CLI tool for orchestrating Damper task workflows with Claude Code",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {