@damper/cli 0.6.9 → 0.6.11

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.
@@ -49,24 +49,25 @@ export async function statusCommand() {
49
49
  console.log();
50
50
  for (const wt of projectWorktrees) {
51
51
  const exists = fs.existsSync(wt.path);
52
- const worktreeName = wt.path.split('/').pop() || wt.path;
53
- // Try to get task status
52
+ const shortTaskId = wt.taskId.slice(0, 8);
53
+ // Try to get task status and title
54
54
  let taskStatus = '';
55
+ let taskTitle = '';
55
56
  if (api) {
56
57
  try {
57
58
  const task = await api.getTask(wt.taskId);
58
59
  const statusColor = task.status === 'done' ? pc.green : task.status === 'in_progress' ? pc.yellow : pc.dim;
59
60
  taskStatus = statusColor(`[${task.status}]`);
61
+ taskTitle = task.title;
60
62
  }
61
63
  catch {
62
64
  taskStatus = pc.dim('[unknown]');
63
65
  }
64
66
  }
65
67
  const statusIcon = exists ? pc.green('●') : pc.red('○');
66
- console.log(` ${statusIcon} ${pc.cyan(`#${wt.taskId}`)} ${taskStatus}`);
67
- console.log(` ${pc.dim('Path:')} ${worktreeName}`);
68
- console.log(` ${pc.dim('Branch:')} ${wt.branch}`);
69
- console.log(` ${pc.dim('Created:')} ${new Date(wt.createdAt).toLocaleDateString()}`);
68
+ const titlePart = taskTitle ? ` ${taskTitle}` : '';
69
+ console.log(` ${statusIcon} ${pc.cyan(`#${shortTaskId}`)}${titlePart} ${taskStatus}`);
70
+ console.log(` ${wt.branch} ${pc.dim('·')} ${pc.dim(`created ${new Date(wt.createdAt).toLocaleDateString()}`)}`);
70
71
  console.log();
71
72
  }
72
73
  }
@@ -159,7 +159,7 @@ export async function createWorktree(options) {
159
159
  const slug = slugify(taskTitle);
160
160
  // Put worktrees in .worktrees folder inside the project
161
161
  const worktreesDir = path.join(projectRoot, '.worktrees');
162
- const worktreePath = path.join(worktreesDir, shortTaskId);
162
+ const worktreePath = path.join(worktreesDir, `${shortTaskId}-${slug}`);
163
163
  const branchName = `feature/${slug}`;
164
164
  // Ensure .worktrees is gitignored
165
165
  const gitignorePath = path.join(projectRoot, '.gitignore');
@@ -1,50 +1,57 @@
1
1
  import { select, confirm, input, Separator } from '@inquirer/prompts';
2
2
  import pc from 'picocolors';
3
+ function shortId(id) {
4
+ return id.slice(0, 8);
5
+ }
3
6
  function getTypeIcon(type) {
4
7
  switch (type) {
5
- case 'bug': return pc.red('bug');
6
- case 'feature': return pc.green('feature');
7
- case 'improvement': return pc.blue('improvement');
8
- default: return pc.gray('task');
8
+ case 'bug': return '🐛';
9
+ case 'feature': return '';
10
+ case 'improvement': return '💡';
11
+ default: return '📌';
9
12
  }
10
13
  }
11
14
  function getPriorityIcon(priority) {
12
15
  switch (priority) {
13
- case 'high': return pc.red('!');
14
- case 'medium': return pc.yellow('~');
15
- default: return pc.dim('-');
16
+ case 'high': return '🔴 ';
17
+ case 'medium': return '🟡 ';
18
+ default: return '';
16
19
  }
17
20
  }
18
21
  function formatTaskChoice(choice) {
19
22
  const { task } = choice;
20
23
  const typeIcon = getTypeIcon(task.type);
21
24
  const priorityIcon = getPriorityIcon(task.priority);
25
+ const id = `${pc.dim('#')}${pc.cyan(shortId(task.id))}`;
22
26
  if (choice.type === 'in_progress') {
23
27
  const worktreeName = choice.worktree.path.split('/').pop() || choice.worktree.path;
24
- let description = `${pc.dim('#')}${pc.cyan(task.id)} ${task.title} ${pc.dim(`[${typeIcon}]`)} ${priorityIcon}`;
25
- description += `\n ${pc.dim(`Worktree: ${worktreeName}`)}`;
28
+ let description = `${priorityIcon}${typeIcon} ${id} ${task.title}`;
29
+ description += `\n ${pc.dim(worktreeName)}`;
26
30
  if (choice.lastNote) {
27
- // Truncate long notes
28
31
  const note = choice.lastNote.length > 60 ? choice.lastNote.slice(0, 60) + '...' : choice.lastNote;
29
- description += `\n ${pc.dim(`Last: "${note}"`)}`;
32
+ description += `\n ${pc.dim(`Last: "${note}"`)}`;
30
33
  }
31
34
  return description;
32
35
  }
33
36
  if (choice.type === 'locked') {
34
- let description = `${pc.dim('#')}${pc.cyan(task.id)} ${task.title} ${pc.dim(`[${typeIcon}]`)} ${priorityIcon}`;
35
- description += pc.yellow(` [locked by ${choice.lockedBy}]`);
37
+ let description = `${priorityIcon}${typeIcon} ${id} ${task.title}`;
38
+ description += `\n ${pc.yellow(`locked by ${choice.lockedBy}`)}`;
36
39
  return description;
37
40
  }
38
41
  // Available task
39
- let description = `${pc.dim('#')}${pc.cyan(task.id)} ${task.title} ${pc.dim(`[${typeIcon}]`)} ${priorityIcon}`;
42
+ let description = `${priorityIcon}${typeIcon} ${id} ${task.title}`;
43
+ const meta = [];
40
44
  if (task.quarter) {
41
- description += ` ${pc.dim(task.quarter)}`;
45
+ meta.push(task.quarter);
42
46
  }
43
47
  if (task.hasImplementationPlan) {
44
- description += pc.dim(' [plan]');
48
+ meta.push('has plan');
45
49
  }
46
50
  if (task.subtaskProgress) {
47
- description += pc.dim(` [${task.subtaskProgress.done}/${task.subtaskProgress.total}]`);
51
+ meta.push(`${task.subtaskProgress.done}/${task.subtaskProgress.total} subtasks`);
52
+ }
53
+ if (meta.length > 0) {
54
+ description += `\n ${pc.dim(meta.join(' · '))}`;
48
55
  }
49
56
  return description;
50
57
  }
@@ -97,7 +104,7 @@ export async function pickTask(options) {
97
104
  }
98
105
  const choices = [];
99
106
  if (inProgressChoices.length > 0) {
100
- choices.push(new Separator(pc.bold('\n--- In Progress (your worktrees) ---')));
107
+ choices.push(new Separator(pc.bold(`\n--- In Progress (${inProgressChoices.length}) ---`)));
101
108
  for (const choice of inProgressChoices) {
102
109
  choices.push({
103
110
  name: formatTaskChoice(choice),
@@ -106,7 +113,7 @@ export async function pickTask(options) {
106
113
  }
107
114
  }
108
115
  if (availableChoices.length > 0) {
109
- choices.push(new Separator(pc.bold('\n--- Available Tasks ---')));
116
+ choices.push(new Separator(pc.bold(`\n--- Available (${availableChoices.length}) ---`)));
110
117
  for (const choice of availableChoices) {
111
118
  choices.push({
112
119
  name: formatTaskChoice(choice),
@@ -115,7 +122,7 @@ export async function pickTask(options) {
115
122
  }
116
123
  }
117
124
  if (lockedChoices.length > 0) {
118
- choices.push(new Separator(pc.bold('\n--- Locked Tasks (can take over) ---')));
125
+ choices.push(new Separator(pc.bold(`\n--- Locked (${lockedChoices.length}) ---`)));
119
126
  for (const choice of lockedChoices) {
120
127
  choices.push({
121
128
  name: formatTaskChoice(choice),
@@ -139,7 +146,7 @@ export async function pickTask(options) {
139
146
  const selected = await select({
140
147
  message: 'Select a task to work on:',
141
148
  choices: choices,
142
- pageSize: 15,
149
+ pageSize: 20,
143
150
  });
144
151
  if (selected.type === 'create_new') {
145
152
  return handleCreateNewTask(api);
@@ -190,7 +197,7 @@ async function handleCreateNewTask(api) {
190
197
  });
191
198
  console.log(pc.dim('\nCreating task in Damper...'));
192
199
  const task = await api.createTask(title.trim(), type);
193
- console.log(pc.green(`✓ Created task #${task.id}: ${task.title}`));
200
+ console.log(pc.green(`✓ Created task #${shortId(task.id)}: ${task.title}`));
194
201
  return {
195
202
  task,
196
203
  isResume: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/cli",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "CLI tool for orchestrating Damper task workflows with Claude Code",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {