@damper/cli 0.9.3 → 0.9.5

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.
@@ -174,12 +174,14 @@ export async function postTaskFlow(options) {
174
174
  let taskStatus;
175
175
  let taskTitle;
176
176
  let hasCommits = false;
177
+ let isPublicTask = false;
177
178
  try {
178
179
  const api = createDamperApi(apiKey);
179
180
  const task = await api.getTask(taskId);
180
181
  taskStatus = task.status;
181
182
  taskTitle = task.title;
182
183
  hasCommits = (task.commits?.length ?? 0) > 0;
184
+ isPublicTask = !!task.publicUrl;
183
185
  }
184
186
  catch {
185
187
  console.log(pc.yellow('Could not fetch task status from Damper.'));
@@ -254,6 +256,10 @@ export async function postTaskFlow(options) {
254
256
  console.log(pc.yellow('⚠ Unpushed commits detected'));
255
257
  }
256
258
  console.log();
259
+ // Offer to add to changelog if task is completed
260
+ if (taskStatus === 'done') {
261
+ await offerChangelogFlow({ taskId, taskTitle, apiKey, isPublic: isPublicTask, confirm, select });
262
+ }
257
263
  // Offer actions based on state
258
264
  const hasChanges = hasUnpushedCommits || hasUncommittedChanges;
259
265
  let worktreeRemoved = false;
@@ -441,6 +447,67 @@ export async function postTaskFlow(options) {
441
447
  }
442
448
  console.log();
443
449
  }
450
+ /**
451
+ * Ask user if they want to add the completed task to a changelog
452
+ */
453
+ async function offerChangelogFlow(options) {
454
+ const { taskId, taskTitle, apiKey, isPublic, confirm, select } = options;
455
+ const { input } = await import('@inquirer/prompts');
456
+ const addToChangelog = await confirm({
457
+ message: 'Add this task to a changelog?',
458
+ default: isPublic,
459
+ });
460
+ if (!addToChangelog)
461
+ return;
462
+ try {
463
+ const { createDamperApi } = await import('./damper-api.js');
464
+ const api = createDamperApi(apiKey);
465
+ // Fetch draft changelogs
466
+ const { changelogs } = await api.listChangelogs({ status: 'draft', limit: 10 });
467
+ let changelogId;
468
+ if (changelogs.length > 0) {
469
+ const choice = await select({
470
+ message: 'Which changelog?',
471
+ choices: [
472
+ ...changelogs.map((cl) => ({
473
+ name: `${cl.title}${cl.version ? ` (${cl.version})` : ''} — ${cl.roadmapItemCount} item${cl.roadmapItemCount !== 1 ? 's' : ''}`,
474
+ value: cl.id,
475
+ })),
476
+ { name: 'Create new changelog', value: '__new__' },
477
+ ],
478
+ });
479
+ if (choice === '__new__') {
480
+ const title = await input({
481
+ message: 'Changelog title:',
482
+ default: `Release ${new Date().toISOString().slice(0, 10)}`,
483
+ });
484
+ const result = await api.createChangelog({ title });
485
+ changelogId = result.id;
486
+ console.log(pc.green(`✓ Created changelog: ${title}`));
487
+ }
488
+ else {
489
+ changelogId = choice;
490
+ }
491
+ }
492
+ else {
493
+ // No drafts — create one
494
+ const title = await input({
495
+ message: 'No draft changelogs found. Create one — title:',
496
+ default: `Release ${new Date().toISOString().slice(0, 10)}`,
497
+ });
498
+ const result = await api.createChangelog({ title });
499
+ changelogId = result.id;
500
+ console.log(pc.green(`✓ Created changelog: ${title}`));
501
+ }
502
+ // Add the task
503
+ const result = await api.addToChangelog(changelogId, [taskId]);
504
+ console.log(pc.green(`✓ Added "${taskTitle || taskId}" to changelog (${result.roadmapItemCount} total items)\n`));
505
+ }
506
+ catch (err) {
507
+ const error = err;
508
+ console.log(pc.yellow(`Could not add to changelog: ${error.message}\n`));
509
+ }
510
+ }
444
511
  /**
445
512
  * Launch Claude to review and complete a task
446
513
  */
@@ -37,6 +37,7 @@ export interface TaskDetail extends Task {
37
37
  }>;
38
38
  lockedBy?: string | null;
39
39
  lockedAt?: string | null;
40
+ publicUrl?: string | null;
40
41
  }
41
42
  export interface ContextSection {
42
43
  section: string;
@@ -76,6 +77,16 @@ export interface Module {
76
77
  tags?: string[];
77
78
  updatedAt: string;
78
79
  }
80
+ export interface Changelog {
81
+ id: string;
82
+ title: string;
83
+ version?: string | null;
84
+ date?: string | null;
85
+ status: 'draft' | 'published';
86
+ publishedAt?: string | null;
87
+ roadmapItemCount: number;
88
+ contentPreview: string;
89
+ }
79
90
  export interface AgentInstructions {
80
91
  format: 'markdown' | 'section';
81
92
  content: string;
@@ -105,6 +116,17 @@ export declare class DamperApi {
105
116
  quarter?: string;
106
117
  sort?: 'importance' | 'newest' | 'votes';
107
118
  limit?: number;
119
+ offset?: number;
120
+ }): Promise<{
121
+ project: string;
122
+ tasks: Task[];
123
+ total: number;
124
+ }>;
125
+ listAllTasks(filters?: {
126
+ status?: 'planned' | 'in_progress' | 'done' | 'all';
127
+ type?: 'bug' | 'feature' | 'improvement' | 'task';
128
+ quarter?: string;
129
+ sort?: 'importance' | 'newest' | 'votes';
108
130
  }): Promise<{
109
131
  project: string;
110
132
  tasks: Task[];
@@ -171,6 +193,32 @@ export declare class DamperApi {
171
193
  }>;
172
194
  getModule(name: string): Promise<Module>;
173
195
  getAgentInstructions(format?: 'markdown' | 'section'): Promise<AgentInstructions>;
196
+ listChangelogs(filters?: {
197
+ status?: 'draft' | 'published';
198
+ limit?: number;
199
+ sort?: 'newest' | 'oldest';
200
+ }): Promise<{
201
+ changelogs: Changelog[];
202
+ }>;
203
+ createChangelog(data: {
204
+ title: string;
205
+ content?: string;
206
+ version?: string;
207
+ status?: 'draft' | 'published';
208
+ }): Promise<{
209
+ id: string;
210
+ title: string;
211
+ status: string;
212
+ }>;
213
+ addToChangelog(changelogId: string, taskIds: string[]): Promise<{
214
+ id: string;
215
+ title: string;
216
+ roadmapItemCount: number;
217
+ addedItems: Array<{
218
+ id: string;
219
+ title: string;
220
+ }>;
221
+ }>;
174
222
  createTask(title: string, type?: 'bug' | 'feature' | 'improvement' | 'task', description?: string): Promise<Task>;
175
223
  deleteTask(taskId: string): Promise<{
176
224
  id: string;
@@ -40,9 +40,23 @@ export class DamperApi {
40
40
  params.set('sort', filters.sort);
41
41
  if (filters?.limit)
42
42
  params.set('limit', String(filters.limit));
43
+ if (filters?.offset)
44
+ params.set('offset', String(filters.offset));
43
45
  const query = params.toString();
44
46
  const data = await this.request('GET', `/api/agent/tasks${query ? `?${query}` : ''}`);
45
- return { project: data.project.name, tasks: data.tasks };
47
+ return { project: data.project.name, tasks: data.tasks, total: data.total };
48
+ }
49
+ async listAllTasks(filters) {
50
+ const pageSize = 100;
51
+ const first = await this.listTasks({ ...filters, limit: pageSize });
52
+ const allTasks = [...first.tasks];
53
+ while (allTasks.length < first.total) {
54
+ const page = await this.listTasks({ ...filters, limit: pageSize, offset: allTasks.length });
55
+ allTasks.push(...page.tasks);
56
+ if (page.tasks.length === 0)
57
+ break; // safety: avoid infinite loop
58
+ }
59
+ return { project: first.project, tasks: allTasks };
46
60
  }
47
61
  async getTask(taskId) {
48
62
  return this.request('GET', `/api/agent/tasks/${taskId}`);
@@ -156,6 +170,24 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
156
170
  lastModified,
157
171
  };
158
172
  }
173
+ // Changelogs
174
+ async listChangelogs(filters) {
175
+ const params = new URLSearchParams();
176
+ if (filters?.status)
177
+ params.set('status', filters.status);
178
+ if (filters?.limit)
179
+ params.set('limit', String(filters.limit));
180
+ if (filters?.sort)
181
+ params.set('sort', filters.sort);
182
+ const query = params.toString();
183
+ return this.request('GET', `/api/agent/changelogs${query ? `?${query}` : ''}`);
184
+ }
185
+ async createChangelog(data) {
186
+ return this.request('POST', '/api/agent/changelogs', data);
187
+ }
188
+ async addToChangelog(changelogId, taskIds) {
189
+ return this.request('POST', `/api/agent/changelogs/${changelogId}/items`, { taskIds });
190
+ }
159
191
  // Create task
160
192
  async createTask(title, type = 'task', description) {
161
193
  return this.request('POST', '/api/agent/tasks', { title, type, status: 'planned', description });
@@ -118,12 +118,11 @@ function formatTaskChoice(choice, titleWidth, layout) {
118
118
  }
119
119
  export async function pickTask(options) {
120
120
  const { api, worktrees, typeFilter, statusFilter } = options;
121
- // Fetch tasks from Damper
122
- const { tasks, project } = await api.listTasks({
121
+ // Fetch all tasks from Damper (paginated)
122
+ const { tasks, project } = await api.listAllTasks({
123
123
  status: statusFilter || 'all',
124
124
  type: typeFilter,
125
125
  sort: 'importance',
126
- limit: 100,
127
126
  });
128
127
  // Filter out completed tasks unless specifically requested
129
128
  const availableTasks = tasks.filter(t => statusFilter === 'done' || statusFilter === 'all' ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/cli",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "CLI tool for orchestrating Damper task workflows with Claude Code",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {