pi-anchor 0.3.0 → 0.5.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.
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840 430" width="840" height="430">
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840 410" width="840" height="410">
2
2
  <defs>
3
3
  <filter id="shadow" x="-5%" y="-5%" width="110%" height="115%" filterUnits="userSpaceOnUse">
4
4
  <feDropShadow dx="0" dy="12" stdDeviation="16" flood-color="#000000" flood-opacity="0.45" />
@@ -10,78 +10,79 @@
10
10
  </defs>
11
11
 
12
12
  <g filter="url(#shadow)">
13
- <rect x="20" y="15" width="800" height="390" rx="12" fill="#1e2326" stroke="#343f44" stroke-width="1.2" />
13
+ <rect x="20" y="15" width="800" height="370" rx="12" fill="#1e2326" stroke="#343f44" stroke-width="1.2" />
14
14
  <path d="M 20,27 A 12,12 0 0,1 32,15 L 808,15 A 12,12 0 0,1 820,27 L 820,52 L 20,52 Z" fill="url(#headerGrad)" stroke="#343f44" stroke-width="1" />
15
15
  <line x1="20" y1="52" x2="820" y2="52" stroke="#2d353b" stroke-width="1" />
16
16
  <circle cx="42" cy="33.5" r="5.5" fill="#e67e80" />
17
17
  <circle cx="60" cy="33.5" r="5.5" fill="#dbbc7f" />
18
18
  <circle cx="78" cy="33.5" r="5.5" fill="#a7c080" />
19
19
  <text x="420" y="38" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="500" fill="#859289">
20
- ⌖ anchor (v0.3.0) · Compact · Autonomous · Persistent
20
+ ⌖ anchor (v0.5.0) · Below-Editor Sticky Strip · Progress Bar · Session Teleport
21
21
  </text>
22
22
  </g>
23
23
 
24
24
  <g font-family="'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace" font-size="13" xml:space="preserve">
25
25
  <text x="45" y="84">
26
- <tspan fill="#a7c080" font-weight="bold">~/Workspace</tspan>
27
- <tspan fill="#7fbbb3"> ❯ </tspan>
28
- <tspan fill="#d3c6aa" font-weight="bold">anchor</tspan>
26
+ <tspan fill="#7fbbb3" font-weight="bold">⌖ Notes (3):</tspan>
29
27
  </text>
30
28
 
31
29
  <text x="45" y="116">
32
- <tspan fill="#7fbbb3" font-weight="bold">⌖ Anchors </tspan>
33
- <tspan fill="#859289">(3 active, 1 quiet · 0 repo clutter · 0 idle tokens):</tspan>
34
- </text>
35
-
36
- <text x="45" y="152">
37
30
  <tspan fill="#859289"> 01 </tspan>
38
- <tspan fill="#e67e80" font-weight="bold">[P0] </tspan>
39
- <tspan fill="#d3c6aa">Migrate auth to HttpOnly cookies [src/auth.ts]</tspan>
40
- <tspan fill="#7fbbb3"> anchor </tspan>
41
- <tspan fill="#e67e80" font-weight="bold">Today </tspan>
42
- <tspan fill="#7a8478">Today 10:02</tspan>
31
+ <tspan fill="#a7c080" font-weight="bold">[■■□□ 2/4] </tspan>
32
+ <tspan fill="#d3c6aa">Refactor auth flow </tspan>
33
+ <tspan fill="#7fbbb3">app · auth.ts ↪ </tspan>
34
+ <tspan fill="#7a8478">25m ago</tspan>
43
35
  </text>
44
36
 
45
- <text x="45" y="182">
37
+ <text x="45" y="144">
46
38
  <tspan fill="#859289"> 02 </tspan>
47
- <tspan fill="#dbbc7f" font-weight="bold">[P1] </tspan>
48
- <tspan fill="#d3c6aa">Add concurrent file lock unit tests </tspan>
49
- <tspan fill="#7fbbb3"> anchor </tspan>
50
- <tspan fill="#dbbc7f">Tomorrow </tspan>
51
- <tspan fill="#7a8478">Yesterday 21:34</tspan>
39
+ <tspan fill="#d3c6aa">Double-check rate limit fallback on timeout </tspan>
40
+ <tspan fill="#7fbbb3">app · gateway.ts ↪ </tspan>
41
+ <tspan fill="#7a8478">3h ago</tspan>
52
42
  </text>
53
43
 
54
- <text x="45" y="212">
44
+ <text x="45" y="172">
55
45
  <tspan fill="#859289"> 03 </tspan>
56
- <tspan fill="#dbbc7f" font-weight="bold">[P1] </tspan>
57
- <tspan fill="#d3c6aa">Polish CLI interactive dashboard &amp; TUI </tspan>
58
- <tspan fill="#7fbbb3"> Desktop </tspan>
59
- <tspan fill="#a7c080">In 2d </tspan>
60
- <tspan fill="#7a8478">Today 10:02</tspan>
46
+ <tspan fill="#859289">Read external architecture notes first </tspan>
47
+ <tspan fill="#7a8478">College/优化 · notes.md </tspan>
48
+ <tspan fill="#56635f">8d ago · quiet</tspan>
61
49
  </text>
62
50
 
63
- <text x="45" y="242">
64
- <tspan fill="#859289"> 04 </tspan>
65
- <tspan fill="#7a8478" font-weight="bold">[Quiet] </tspan>
66
- <tspan fill="#859289">Review external architecture reference notes </tspan>
67
- <tspan fill="#7a8478"> College/Opt </tspan>
68
- <tspan fill="#7a8478">Quiet (0 tok) </tspan>
69
- <tspan fill="#56635f">09-28 14:20</tspan>
51
+ <!-- Simulated Pi Input Box + Below-Editor Persistent Sticky Strip -->
52
+ <rect x="45" y="204" width="750" height="44" rx="6" fill="#232a2e" stroke="#4f5b58" stroke-width="1.2" />
53
+ <text x="62" y="231">
54
+ <tspan fill="#a7c080" font-weight="bold">❯ </tspan>
55
+ <tspan fill="#d3c6aa">pin: Refactor auth flow: 1. Extract interface 2. HttpOnly cookie 3. Unit tests</tspan>
70
56
  </text>
71
57
 
72
- <rect x="45" y="274" width="750" height="58" rx="6" fill="#232a2e" stroke="#3a464c" stroke-width="1" />
73
- <text x="62" y="298" fill="#a7c080" font-weight="bold">⚡ Real-Time Autonomous Settlement (on `git commit` or passing `verifyCommand`):</text>
74
- <text x="62" y="319" fill="#d3c6aa"> git commit -m "feat(auth): migrate auth to HttpOnly cookies" ➔ ⌖ Auto-settled #anc-1</text>
58
+ <!-- Below-Editor Sticky Strip -->
59
+ <rect x="45" y="256" width="750" height="36" rx="6" fill="#292b28" stroke="#a7c080" stroke-width="1" stroke-dasharray="4,3" />
60
+ <text x="62" y="279">
61
+ <tspan fill="#a7c080" font-weight="bold">⌖ </tspan>
62
+ <tspan fill="#859289">#1 </tspan>
63
+ <tspan fill="#dbbc7f" font-weight="bold">[■■□□ 2/4] </tspan>
64
+ <tspan fill="#d3c6aa">Refactor auth flow </tspan>
65
+ <tspan fill="#7fbbb3">▸ HttpOnly cookie </tspan>
66
+ <tspan fill="#859289">(app · auth.ts ↪)</tspan>
67
+ </text>
75
68
 
76
- <text x="45" y="366" fill="#7a8478">
77
- <tspan> Commands: </tspan>
78
- <tspan fill="#7fbbb3" font-weight="bold">`/pin &lt;task&gt;`</tspan>
79
- <tspan> · </tspan>
80
- <tspan fill="#7fbbb3" font-weight="bold">`/anchor`</tspan>
81
- <tspan> · </tspan>
82
- <tspan fill="#7fbbb3" font-weight="bold">`/done 1`</tspan>
83
- <tspan> · </tspan>
84
- <tspan fill="#7fbbb3" font-weight="bold">`/anchor undo`</tspan>
69
+ <text x="45" y="324" fill="#859289">
70
+ <tspan> • </tspan>
71
+ <tspan fill="#d3c6aa" font-weight="bold">0ms Instant Stick</tspan>
72
+ <tspan>: Type </tspan>
73
+ <tspan fill="#7fbbb3">`pin: &lt;note&gt;`</tspan>
74
+ <tspan> (or </tspan>
75
+ <tspan fill="#7fbbb3">`/pin`</tspan>
76
+ <tspan>) — docked below editor, never collides with aboveEditor HUDs.</tspan>
77
+ </text>
78
+ <text x="45" y="348" fill="#859289">
79
+ <tspan> • </tspan>
80
+ <tspan fill="#d3c6aa" font-weight="bold">Session Teleport (↪)</tspan>
81
+ <tspan>: Select any note in </tspan>
82
+ <tspan fill="#7fbbb3">`/anchor`</tspan>
83
+ <tspan> or run </tspan>
84
+ <tspan fill="#7fbbb3">`/anchor jump 1`</tspan>
85
+ <tspan> to resume its conversation.</tspan>
85
86
  </text>
86
87
  </g>
87
88
  </svg>
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840 430" width="840" height="430">
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840 410" width="840" height="410">
2
2
  <defs>
3
3
  <filter id="shadow" x="-5%" y="-5%" width="110%" height="115%" filterUnits="userSpaceOnUse">
4
4
  <feDropShadow dx="0" dy="12" stdDeviation="16" flood-color="#000000" flood-opacity="0.45" />
@@ -10,78 +10,79 @@
10
10
  </defs>
11
11
 
12
12
  <g filter="url(#shadow)">
13
- <rect x="20" y="15" width="800" height="390" rx="12" fill="#1e2326" stroke="#343f44" stroke-width="1.2" />
13
+ <rect x="20" y="15" width="800" height="370" rx="12" fill="#1e2326" stroke="#343f44" stroke-width="1.2" />
14
14
  <path d="M 20,27 A 12,12 0 0,1 32,15 L 808,15 A 12,12 0 0,1 820,27 L 820,52 L 20,52 Z" fill="url(#headerGrad)" stroke="#343f44" stroke-width="1" />
15
15
  <line x1="20" y1="52" x2="820" y2="52" stroke="#2d353b" stroke-width="1" />
16
16
  <circle cx="42" cy="33.5" r="5.5" fill="#e67e80" />
17
17
  <circle cx="60" cy="33.5" r="5.5" fill="#dbbc7f" />
18
18
  <circle cx="78" cy="33.5" r="5.5" fill="#a7c080" />
19
19
  <text x="420" y="38" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="500" fill="#859289">
20
- ⌖ anchor (v0.3.0) · 小巧 · 自主 · 持久
20
+ ⌖ anchor (v0.5.0) · 输入框下方常驻便签条 · 迷你进度条 · 原对话一键穿越
21
21
  </text>
22
22
  </g>
23
23
 
24
24
  <g font-family="'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace" font-size="13" xml:space="preserve">
25
25
  <text x="45" y="84">
26
- <tspan fill="#a7c080" font-weight="bold">~/Workspace</tspan>
27
- <tspan fill="#7fbbb3"> ❯ </tspan>
28
- <tspan fill="#d3c6aa" font-weight="bold">anchor</tspan>
26
+ <tspan fill="#7fbbb3" font-weight="bold">⌖ Notes (3):</tspan>
29
27
  </text>
30
28
 
31
29
  <text x="45" y="116">
32
- <tspan fill="#7fbbb3" font-weight="bold">⌖ Anchors </tspan>
33
- <tspan fill="#859289">(3 active, 1 quiet · 0 仓库污染 · 0 闲置 Token):</tspan>
34
- </text>
35
-
36
- <text x="45" y="152">
37
30
  <tspan fill="#859289"> 01 </tspan>
38
- <tspan fill="#e67e80" font-weight="bold">[P0] </tspan>
39
- <tspan fill="#d3c6aa">重构鉴权模块为 HttpOnly Cookie [src/auth.ts]</tspan>
40
- <tspan fill="#7fbbb3"> anchor </tspan>
41
- <tspan fill="#e67e80" font-weight="bold">Today </tspan>
42
- <tspan fill="#7a8478">Today 10:02</tspan>
31
+ <tspan fill="#a7c080" font-weight="bold">[■■□□ 2/4] </tspan>
32
+ <tspan fill="#d3c6aa">重构登录鉴权流 </tspan>
33
+ <tspan fill="#7fbbb3">app · auth.ts ↪ </tspan>
34
+ <tspan fill="#7a8478">25m ago</tspan>
43
35
  </text>
44
36
 
45
- <text x="45" y="182">
37
+ <text x="45" y="144">
46
38
  <tspan fill="#859289"> 02 </tspan>
47
- <tspan fill="#dbbc7f" font-weight="bold">[P1] </tspan>
48
- <tspan fill="#d3c6aa">补充并发文件锁单元测试 </tspan>
49
- <tspan fill="#7fbbb3"> anchor </tspan>
50
- <tspan fill="#dbbc7f">Tomorrow </tspan>
51
- <tspan fill="#7a8478">Yesterday 21:34</tspan>
39
+ <tspan fill="#d3c6aa">检查网关超时后的降级重试边界 </tspan>
40
+ <tspan fill="#7fbbb3">app · gateway.ts ↪ </tspan>
41
+ <tspan fill="#7a8478">3h ago</tspan>
52
42
  </text>
53
43
 
54
- <text x="45" y="212">
44
+ <text x="45" y="172">
55
45
  <tspan fill="#859289"> 03 </tspan>
56
- <tspan fill="#dbbc7f" font-weight="bold">[P1] </tspan>
57
- <tspan fill="#d3c6aa">打磨 CLI 交互式极简工程列表 </tspan>
58
- <tspan fill="#7fbbb3"> Desktop </tspan>
59
- <tspan fill="#a7c080">In 2d </tspan>
60
- <tspan fill="#7a8478">Today 10:02</tspan>
46
+ <tspan fill="#859289">College 重构前置查阅外部架构参考库 </tspan>
47
+ <tspan fill="#7a8478">College/优化 · notes.md </tspan>
48
+ <tspan fill="#56635f">8d ago · quiet</tspan>
61
49
  </text>
62
50
 
63
- <text x="45" y="242">
64
- <tspan fill="#859289"> 04 </tspan>
65
- <tspan fill="#7a8478" font-weight="bold">[Quiet] </tspan>
66
- <tspan fill="#859289">College 重构前置查阅外部架构参考库 </tspan>
67
- <tspan fill="#7a8478"> College/优化 </tspan>
68
- <tspan fill="#7a8478">Quiet (0 tok) </tspan>
69
- <tspan fill="#56635f">09-28 14:20</tspan>
51
+ <!-- Simulated Pi Input Box + Below-Editor Persistent Sticky Strip -->
52
+ <rect x="45" y="204" width="750" height="44" rx="6" fill="#232a2e" stroke="#4f5b58" stroke-width="1.2" />
53
+ <text x="62" y="231">
54
+ <tspan fill="#a7c080" font-weight="bold">❯ </tspan>
55
+ <tspan fill="#d3c6aa">便签: 重构登录鉴权流: 1. 抽出接口 2. 替换Cookie 3. 补单测 4. 压测</tspan>
70
56
  </text>
71
57
 
72
- <rect x="45" y="274" width="750" height="58" rx="6" fill="#232a2e" stroke="#3a464c" stroke-width="1" />
73
- <text x="62" y="298" fill="#a7c080" font-weight="bold">⚡ 会话内实时自主核销(执行 git commit 或测试通过当场自动结案):</text>
74
- <text x="62" y="319" fill="#d3c6aa"> git commit -m "feat(auth): 重构鉴权模块为 HttpOnly Cookie" ➔ ⌖ Auto-settled #anc-1</text>
58
+ <!-- Below-Editor Sticky Strip -->
59
+ <rect x="45" y="256" width="750" height="36" rx="6" fill="#292b28" stroke="#a7c080" stroke-width="1" stroke-dasharray="4,3" />
60
+ <text x="62" y="279">
61
+ <tspan fill="#a7c080" font-weight="bold">⌖ </tspan>
62
+ <tspan fill="#859289">#1 </tspan>
63
+ <tspan fill="#dbbc7f" font-weight="bold">[■■□□ 2/4] </tspan>
64
+ <tspan fill="#d3c6aa">重构登录鉴权流 </tspan>
65
+ <tspan fill="#7fbbb3">▸ 补单测 </tspan>
66
+ <tspan fill="#859289">(app · auth.ts ↪)</tspan>
67
+ </text>
75
68
 
76
- <text x="45" y="366" fill="#7a8478">
77
- <tspan> 常用命令: </tspan>
78
- <tspan fill="#7fbbb3" font-weight="bold">`/pin &lt;任务&gt;`</tspan>
79
- <tspan> · </tspan>
80
- <tspan fill="#7fbbb3" font-weight="bold">`/anchor`</tspan>
81
- <tspan> · </tspan>
82
- <tspan fill="#7fbbb3" font-weight="bold">`/done 1`</tspan>
83
- <tspan> · </tspan>
84
- <tspan fill="#7fbbb3" font-weight="bold">`/anchor undo`</tspan>
69
+ <text x="45" y="324" fill="#859289">
70
+ <tspan> • </tspan>
71
+ <tspan fill="#d3c6aa" font-weight="bold">输入框下方常驻 1 行便签条</tspan>
72
+ <tspan>:采用 </tspan>
73
+ <tspan fill="#7fbbb3">belowEditor</tspan>
74
+ <tspan> 挂载,与上方 </tspan>
75
+ <tspan fill="#7fbbb3">pi-lingual</tspan>
76
+ <tspan> 零冲突;敲 </tspan>
77
+ <tspan fill="#7fbbb3">便签: ...</tspan>
78
+ <tspan> 0ms 瞬贴。</tspan>
79
+ </text>
80
+ <text x="45" y="348" fill="#859289">
81
+ <tspan> • </tspan>
82
+ <tspan fill="#d3c6aa" font-weight="bold">原对话一键穿越 (↪)</tspan>
83
+ <tspan>:自动记录贴便签时的 Session,在 </tspan>
84
+ <tspan fill="#7fbbb3">/anchor</tspan>
85
+ <tspan> 中点击即可直接切回当时的对话上下文。</tspan>
85
86
  </text>
86
87
  </g>
87
88
  </svg>
package/bin/anchor.js CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Anchor CLI - Minimalist task protocol for AI coding workflows
5
- * Production-ready executable with dual-timeline & 4-quadrant cognitive grouping
4
+ * Anchor CLI — Minimalist terminal sticky notes for AI coding
6
5
  */
7
6
 
8
7
  import {
9
8
  AnchorStore,
10
9
  formatOrigin,
11
- formatTargetDate,
12
- formatCreationTime,
10
+ formatProgressBar,
11
+ formatRelativeAge,
13
12
  sortAnchorsForDisplay,
14
13
  padToWidth
15
14
  } from '../dist/index.js';
@@ -17,55 +16,71 @@ import {
17
16
  const store = new AnchorStore();
18
17
  const args = process.argv.slice(2);
19
18
 
20
- // 1. View pending tasks: `anchor` or `anchor list`
19
+ // 1. View sticky notes: `anchor` or `anchor list`
21
20
  if (args.length === 0 || args[0] === 'list' || args[0] === 'ls') {
22
- const allUnfinished = store
21
+ const allNotes = store
23
22
  .list({ cwd: process.cwd(), all: true })
24
23
  .filter(a => a.status === 'active' || a.status === 'sleeping');
25
24
 
26
- if (allUnfinished.length === 0) {
27
- console.log('\n⌖ No active anchors. Use `anchor <task>` to record.\n');
25
+ if (allNotes.length === 0) {
26
+ console.log('\n⌖ No sticky notes. Run `anchor "<note>"` to stick one.\n');
28
27
  process.exit(0);
29
28
  }
30
29
 
31
- const sorted = sortAnchorsForDisplay(allUnfinished, process.cwd());
32
- const activeCount = sorted.filter(a => a.status === 'active').length;
33
- const quietCount = sorted.length - activeCount;
34
- const headerSuffix = quietCount > 0 ? `${activeCount} active, ${quietCount} quiet` : `${activeCount} active`;
35
-
36
- console.log(`\n⌖ Anchors (${headerSuffix}):\n`);
30
+ const sorted = sortAnchorsForDisplay(allNotes, process.cwd());
31
+ console.log(`\n⌖ Notes (${sorted.length}):\n`);
37
32
 
38
33
  sorted.forEach((a, idx) => {
39
34
  const num = String(idx + 1).padStart(2, '0');
40
- const badge = a.status === 'sleeping'
41
- ? padToWidth('[Quiet]', 10)
42
- : padToWidth(`[${a.priority.toUpperCase()}]`, 10);
43
- const origin = formatOrigin(a);
44
- const target = a.status === 'sleeping' ? 'Quiet (0 tok)' : formatTargetDate(a);
45
- const created = formatCreationTime(a.createdAt);
46
-
47
- const titleWithFiles = (a.files && a.files.length > 0)
48
- ? `${a.title} [${a.files.slice(0, 1).join(', ')}]`
49
- : a.title;
35
+ const bar = a.progress ? `${formatProgressBar(a.progress)} ` : '';
36
+ const origin = formatOrigin(a) + (a.sessionFile ? ' ↪' : '');
37
+ const age = a.status === 'sleeping'
38
+ ? `${formatRelativeAge(a.createdAt)} · quiet`
39
+ : formatRelativeAge(a.createdAt);
50
40
 
51
41
  const colNum = ` ${num} `;
52
- const colTitle = padToWidth(titleWithFiles, 36);
53
- const colOrigin = padToWidth(origin, 14);
54
- const colTarget = padToWidth(target, 14);
55
- const colCreated = created;
42
+ const colTitle = padToWidth(`${bar}${a.title}`, 42);
43
+ const colOrigin = padToWidth(origin, 22);
56
44
 
57
- console.log(`${colNum}${badge}${colTitle} ${colOrigin} ${colTarget} ${colCreated}`);
45
+ console.log(`${colNum}${colTitle} ${colOrigin} ${age}`);
58
46
  });
59
47
 
60
- console.log('\n Use `anchor done <id>` to complete, or `anchor undo` to restore.\n');
48
+ console.log('\n Run `anchor step <num>` (+1 step), `anchor done <num>` (tear off), or `anchor undo`.\n');
61
49
  process.exit(0);
62
50
  }
63
51
 
64
- // 2. Undo settlement: `anchor undo`
52
+ // 2. Put back last torn-off note: `anchor undo`
65
53
  if (args[0] === 'undo') {
66
54
  try {
67
55
  const restored = store.undoSettle();
68
- console.log(`⌖ Restored #${restored.id}: "${restored.title}"`);
56
+ console.log(`⌖ Put back #${restored.id.replace(/^anc-/, '')}: "${restored.title}"`);
57
+ } catch (err) {
58
+ console.error(`Error: ${err.message}`);
59
+ process.exit(1);
60
+ }
61
+ process.exit(0);
62
+ }
63
+
64
+ // 3. Advance progress bar: `anchor step <num>`
65
+ if (args[0] === 'step' || args[0] === '+') {
66
+ const idInput = args[1] || '1';
67
+ const delta = args[2] ? parseInt(args[2], 10) : 1;
68
+ try {
69
+ const allNotes = store
70
+ .list({ cwd: process.cwd(), all: true })
71
+ .filter(a => a.status === 'active' || a.status === 'sleeping');
72
+ const sorted = sortAnchorsForDisplay(allNotes, process.cwd());
73
+
74
+ let targetId = idInput;
75
+ if (/^\d+$/.test(idInput)) {
76
+ const seqIndex = parseInt(idInput, 10);
77
+ if (seqIndex >= 1 && seqIndex <= sorted.length) {
78
+ targetId = sorted[seqIndex - 1].id;
79
+ }
80
+ }
81
+
82
+ const { anchor: updated } = store.step(targetId, isNaN(delta) ? 1 : delta);
83
+ console.log(`⌖ Advanced #${updated.id.replace(/^anc-/, '')} to ${formatProgressBar(updated.progress)}: "${updated.title}"`);
69
84
  } catch (err) {
70
85
  console.error(`Error: ${err.message}`);
71
86
  process.exit(1);
@@ -73,53 +88,44 @@ if (args[0] === 'undo') {
73
88
  process.exit(0);
74
89
  }
75
90
 
76
- // 3. Help: `anchor --help` or `anchor -h`
91
+ // 4. Help: `anchor --help`
77
92
  if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
78
93
  console.log(`
79
- ⌖ Anchor CLI - Cross-session task protocol for AI coding agents
94
+ ⌖ Anchor — Digital sticky notes for AI coding sessions
80
95
 
81
96
  Usage:
82
- anchor List active anchors
83
- anchor <task> Pin a new task across sessions
84
- anchor done <id> Settle and evict a task (e.g. anchor done 1)
85
- anchor undo Restore the last settled task
86
-
87
- Options:
88
- -h, --help Show this help message
89
- -v, --version Show version
90
- -d, --due <date> Specify expected completion date (e.g. tomorrow, friday, 2026-09-28)
97
+ anchor View all sticky notes
98
+ anchor "<note>" Stick a new note (supports [1/4] or 1. A 2. B progress steps)
99
+ anchor step <num> Advance progress bar on a note (e.g. anchor step 1)
100
+ anchor done <num> Tear off a completed note (e.g. anchor done 1)
101
+ anchor undo Put back the last torn-off note
91
102
  `);
92
103
  process.exit(0);
93
104
  }
94
105
 
95
- // 4. Close task: `anchor done <id>` or `anchor rm <id>`
106
+ // 5. Tear off note: `anchor done <num>`
96
107
  if (args[0] === 'done' || args[0] === 'rm' || args[0] === 'close') {
97
108
  const idInput = args[1];
98
109
  if (!idInput) {
99
- console.error('Usage: anchor done <id>');
110
+ console.error('Usage: anchor done <num>');
100
111
  process.exit(1);
101
112
  }
102
113
  try {
103
- // 1. Check if idInput is a 1-based sequential display number (e.g. '1', '01')
104
- const allUnfinished = store
114
+ const allNotes = store
105
115
  .list({ cwd: process.cwd(), all: true })
106
116
  .filter(a => a.status === 'active' || a.status === 'sleeping');
107
- const sortedActive = sortAnchorsForDisplay(allUnfinished, process.cwd());
117
+ const sorted = sortAnchorsForDisplay(allNotes, process.cwd());
108
118
 
109
119
  let targetId = idInput;
110
- const numMatch = idInput.match(/^\d+$/);
111
- if (numMatch) {
120
+ if (/^\d+$/.test(idInput)) {
112
121
  const seqIndex = parseInt(idInput, 10);
113
- if (seqIndex >= 1 && seqIndex <= sortedActive.length) {
114
- targetId = sortedActive[seqIndex - 1].id;
122
+ if (seqIndex >= 1 && seqIndex <= sorted.length) {
123
+ targetId = sorted[seqIndex - 1].id;
115
124
  }
116
125
  }
117
126
 
118
- const settled = store.settle(targetId, { settledBy: 'manual-command' });
119
- const successMsg = settled.recurrence === 'daily'
120
- ? `⌖ Completed for today: "${settled.title}" (resets tomorrow)`
121
- : `⌖ Settled: "${settled.title}"`;
122
- console.log(successMsg);
127
+ const settled = store.settle(targetId, { settledBy: 'manual' });
128
+ console.log(`⌖ Torn off: "${settled.title}"`);
123
129
  } catch (err) {
124
130
  console.error(`Error: ${err.message}`);
125
131
  process.exit(1);
@@ -127,24 +133,16 @@ if (args[0] === 'done' || args[0] === 'rm' || args[0] === 'close') {
127
133
  process.exit(0);
128
134
  }
129
135
 
130
- // 5. Pin new task: `anchor <task description> [--due <date>]`
131
- let targetDateFlag;
132
- const dueIdx = args.findIndex(a => a === '--due' || a === '-d');
133
- if (dueIdx !== -1 && args[dueIdx + 1]) {
134
- targetDateFlag = args[dueIdx + 1];
135
- args.splice(dueIdx, 2);
136
- }
137
-
136
+ // 6. Stick a new note: `anchor <note text>`
138
137
  const title = args.join(' ').trim();
139
138
  if (!title) {
140
- console.error('Error: task description cannot be empty.');
139
+ console.error('Error: Note text cannot be empty');
141
140
  process.exit(1);
142
141
  }
143
142
 
144
- const anc = store.create({
143
+ const created = store.create({
145
144
  title,
146
- targetDate: targetDateFlag,
147
145
  cwd: process.cwd()
148
146
  });
149
- const targetBadge = anc.targetDate ? ` · ${formatTargetDate(anc)}` : '';
150
- console.log(`⌖ Pinned #${anc.id}: "${anc.title}" [${anc.project}]${targetBadge}`);
147
+ const bar = created.progress ? ` ${formatProgressBar(created.progress)}` : '';
148
+ console.log(`⌖ Pinned #${created.id.replace(/^anc-/, '')}${bar}: "${created.title}"`);