pi-anchor 0.2.4 → 0.3.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.
@@ -44,15 +44,15 @@ var import_node_os = __toESM(require("os"), 1);
44
44
 
45
45
  // src/types.ts
46
46
  var EPHEMERAL_DECAY_POLICY = {
47
- activeDays: 1,
48
- // 24 hours active
49
- sleepDays: 1,
50
- // 24 hours sleeping
51
- graveyardDays: 2
52
- // 48 hours total before auto-clearing
47
+ activeDays: 7,
48
+ // 7 days active before quiet sleep
49
+ sleepDays: 30,
50
+ graveyardDays: 9999
51
+ // Never silently auto-delete unfinished tasks
53
52
  };
54
53
  var DURABLE_DECAY_POLICY = {
55
- activeDays: 3,
54
+ activeDays: 7,
55
+ // 7 days active before quiet sleep
56
56
  sleepDays: 30,
57
57
  graveyardDays: 9999
58
58
  // Never permanently auto-dropped; preserved indefinitely
@@ -89,6 +89,39 @@ function isSameOrSubPath(anchorCwd, targetCwd) {
89
89
  const cmpT = isWinStyle ? normT.toLowerCase() : normT;
90
90
  return cmpA === cmpT || cmpT.startsWith(cmpA + "/") || cmpA.startsWith(cmpT + "/");
91
91
  }
92
+ function extractPathsFromText(text) {
93
+ if (!text) return [];
94
+ const results = /* @__PURE__ */ new Set();
95
+ const regex = /(?:[a-zA-Z]:[\\/][^\s"'`,。;!?、,;)>\]]+|(?:\b(?:src|lib|dist|tests|test|docs|scripts|packages|extensions|bin|assets)[\\/][^\s"'`,。;!?、,;)>\]]+)|(?:\b[\w.-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|rs|go|java|c|cpp|h|cs|md|json|yaml|yml|toml|html|css|sql|sh)\b))/g;
96
+ let match;
97
+ while ((match = regex.exec(text)) !== null) {
98
+ const cleaned = normalizePath(match[0].replace(/[.:;,'")\]}>]+$/, ""));
99
+ if (cleaned && cleaned.length >= 3) {
100
+ results.add(cleaned);
101
+ }
102
+ }
103
+ return Array.from(results);
104
+ }
105
+ function isAnchorTargetingWorkspace(anchor, targetCwd) {
106
+ if (!targetCwd) return true;
107
+ const normTarget = targetCwd.trim().replace(/\\/g, "/").replace(/\/+$/, "");
108
+ const targetBase = import_node_path.default.basename(normTarget).toLowerCase();
109
+ if (anchor.project && anchor.project !== "global") {
110
+ const normProj = anchor.project.trim().replace(/\\/g, "/").replace(/\/+$/, "");
111
+ if (isSameOrSubPath(normProj, normTarget)) return true;
112
+ const projBase = import_node_path.default.basename(normProj).toLowerCase();
113
+ if (projBase && projBase === targetBase) return true;
114
+ }
115
+ if (anchor.files && anchor.files.length > 0) {
116
+ for (const f of anchor.files) {
117
+ if (isSameOrSubPath(f, normTarget)) return true;
118
+ }
119
+ }
120
+ if (anchor.cwd && isSameOrSubPath(anchor.cwd, normTarget)) {
121
+ return true;
122
+ }
123
+ return false;
124
+ }
92
125
  function matchAnchorAgainstTouchedFiles(anchor, touchedFiles) {
93
126
  if (!anchor.files || anchor.files.length === 0) return null;
94
127
  const normTouched = touchedFiles.map((f) => normalizePath(f, anchor.cwd)).filter(Boolean);
@@ -191,10 +224,10 @@ function detectDurability(title) {
191
224
  function getEphemeralDecayPolicy(title) {
192
225
  const lower = title.toLowerCase();
193
226
  if (lower.includes("\u5927\u540E\u5929")) {
194
- return { activeDays: 3, sleepDays: 1, graveyardDays: 4 };
227
+ return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
195
228
  }
196
229
  if (lower.includes("\u540E\u5929")) {
197
- return { activeDays: 2, sleepDays: 1, graveyardDays: 3 };
230
+ return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
198
231
  }
199
232
  return { ...EPHEMERAL_DECAY_POLICY };
200
233
  }
@@ -505,7 +538,17 @@ var AnchorStore = class {
505
538
  const state = this.loadState();
506
539
  const now = Date.now();
507
540
  const id = this.generateId(state);
508
- const cwd = input.cwd ? normalizePath(input.cwd) : "";
541
+ const explicitFiles = (input.files || []).map((f) => normalizePath(f)).filter(Boolean);
542
+ const inferredFiles = explicitFiles.length > 0 ? explicitFiles : extractPathsFromText(`${input.title} ${input.description || ""}`);
543
+ let cwd = input.cwd ? normalizePath(input.cwd) : "";
544
+ if (input.project && (/^[a-zA-Z]:[\\/]/.test(input.project.trim()) || input.project.trim().startsWith("/"))) {
545
+ cwd = normalizePath(input.project.trim());
546
+ } else if (inferredFiles.length > 0 && (/^[a-zA-Z]:\//.test(inferredFiles[0]) || inferredFiles[0].startsWith("/"))) {
547
+ const firstDir = import_node_path2.default.dirname(inferredFiles[0]);
548
+ if (firstDir && firstDir !== ".") {
549
+ cwd = normalizePath(firstDir);
550
+ }
551
+ }
509
552
  const projectName = input.project ? input.project.trim() : cwd ? import_node_path2.default.basename(cwd) : "global";
510
553
  const recurrence = input.recurrence || detectRecurrence(input.title);
511
554
  const targetDate = input.targetDate || detectTargetDate(input.title, now);
@@ -525,7 +568,7 @@ var AnchorStore = class {
525
568
  createdAt: now,
526
569
  updatedAt: now,
527
570
  lastTouchedAt: now,
528
- files: (input.files || []).map((f) => normalizePath(f)).filter(Boolean),
571
+ files: inferredFiles,
529
572
  tags: (input.tags || []).map((t) => t.trim()).filter(Boolean),
530
573
  verifyCommand: input.verifyCommand?.trim() || void 0,
531
574
  decay: input.decay ? { ...input.decay } : { ...decayPolicy }
@@ -564,7 +607,7 @@ var AnchorStore = class {
564
607
  if (filter?.status && a.status !== filter.status) return false;
565
608
  if (filter?.priority && a.priority !== filter.priority) return false;
566
609
  if (!filter?.all && targetCwd) {
567
- if (a.files.length > 0 && a.cwd && !isSameOrSubPath(a.cwd, targetCwd)) {
610
+ if (a.files.length > 0 && (a.cwd || a.project) && !isAnchorTargetingWorkspace(a, targetCwd)) {
568
611
  return false;
569
612
  }
570
613
  }
@@ -1117,20 +1160,66 @@ function runPhysicalVerification(anchor, cwd) {
1117
1160
  return { success: false, output: String(err.stderr || err.stdout || err.message).trim() };
1118
1161
  }
1119
1162
  }
1120
- function generateSettlementProposals(store, touchedFiles, cwd) {
1163
+ function tryRealtimeAutoSettle(store, observer, cwd, executedCommand) {
1164
+ const candidates = store.list({ cwd }).filter((a) => a.status === "active" || a.status === "sleeping");
1165
+ if (candidates.length === 0) return [];
1166
+ const settledList = [];
1167
+ const cmdTrimmed = (executedCommand || "").trim();
1168
+ for (const a of candidates) {
1169
+ if (observer.hasCommitted()) {
1170
+ const commitMatch = observer.matchesCommit(a);
1171
+ if (commitMatch.matched && (!cwd || isAnchorTargetingWorkspace(a, cwd))) {
1172
+ const settled = store.settle(a.id, {
1173
+ settledBy: "verification-test",
1174
+ summary: `Auto-settled via Git commit: ${commitMatch.message}`,
1175
+ touchedFiles: observer.getModifiedFiles()
1176
+ });
1177
+ settledList.push({ anchor: settled, reason: `git commit ("${commitMatch.message}")` });
1178
+ continue;
1179
+ }
1180
+ }
1181
+ if (a.verifyCommand && cmdTrimmed && cmdTrimmed.includes(a.verifyCommand.trim())) {
1182
+ const settled = store.settle(a.id, {
1183
+ settledBy: "verification-test",
1184
+ summary: `Auto-settled via passing command: ${a.verifyCommand}`,
1185
+ touchedFiles: observer.getModifiedFiles()
1186
+ });
1187
+ settledList.push({ anchor: settled, reason: `verifyCommand (${a.verifyCommand})` });
1188
+ }
1189
+ }
1190
+ return settledList;
1191
+ }
1192
+ function generateSettlementProposals(store, touchedFiles, cwd, observer) {
1121
1193
  const activeAndSleeping = store.list({ cwd }).filter((a) => a.status === "active" || a.status === "sleeping");
1122
1194
  const matches = findMatchedAnchors(activeAndSleeping, touchedFiles);
1195
+ const seenIds = /* @__PURE__ */ new Set();
1123
1196
  const proposals = [];
1124
1197
  for (const match of matches) {
1198
+ seenIds.add(match.anchor.id);
1125
1199
  const fileList = match.matchedFiles.slice(0, 3).join(", ");
1126
- const moreSuffix = match.matchedFiles.length > 3 ? ` \u7B49 ${match.matchedFiles.length} \u4E2A\u6587\u4EF6` : "";
1200
+ const moreSuffix = match.matchedFiles.length > 3 ? ` (+${match.matchedFiles.length - 3} more)` : "";
1127
1201
  proposals.push({
1128
1202
  anchor: match.anchor,
1129
1203
  matchedFiles: match.matchedFiles,
1130
- reason: `\u672C\u6B21\u4F1A\u8BDD\u4FEE\u6539\u4E86 ${fileList}${moreSuffix}`,
1204
+ reason: `Modified ${fileList}${moreSuffix}`,
1131
1205
  recommendedAction: match.score >= 0.85 ? "settle" : "defer"
1132
1206
  });
1133
1207
  }
1208
+ if (observer && observer.hasCommitted()) {
1209
+ for (const a of activeAndSleeping) {
1210
+ if (seenIds.has(a.id)) continue;
1211
+ const cm = observer.matchesCommit(a);
1212
+ if (cm.matched) {
1213
+ seenIds.add(a.id);
1214
+ proposals.push({
1215
+ anchor: a,
1216
+ matchedFiles: touchedFiles.slice(0, 3),
1217
+ reason: `Matched git commit: "${cm.message}"`,
1218
+ recommendedAction: "settle"
1219
+ });
1220
+ }
1221
+ }
1222
+ }
1134
1223
  return proposals;
1135
1224
  }
1136
1225
 
@@ -1165,6 +1254,27 @@ function groupAnchorsByQuadrant(anchors, now = Date.now()) {
1165
1254
  }
1166
1255
  return groups;
1167
1256
  }
1257
+ var PRIORITY_RANK = { p0: 0, p1: 1, p2: 2 };
1258
+ function sortAnchorsForDisplay(anchors, cwd, now = Date.now()) {
1259
+ const todayStr = getTodayDateString(now);
1260
+ return [...anchors].sort((a, b) => {
1261
+ if (a.status !== b.status) {
1262
+ return a.status === "active" ? -1 : 1;
1263
+ }
1264
+ if (cwd) {
1265
+ const aMatch = isAnchorTargetingWorkspace(a, cwd) ? 0 : 1;
1266
+ const bMatch = isAnchorTargetingWorkspace(b, cwd) ? 0 : 1;
1267
+ if (aMatch !== bMatch) return aMatch - bMatch;
1268
+ }
1269
+ const aDue = a.targetDate && a.targetDate <= todayStr ? 0 : 1;
1270
+ const bDue = b.targetDate && b.targetDate <= todayStr ? 0 : 1;
1271
+ if (aDue !== bDue) return aDue - bDue;
1272
+ const pA = PRIORITY_RANK[a.priority] ?? 1;
1273
+ const pB = PRIORITY_RANK[b.priority] ?? 1;
1274
+ if (pA !== pB) return pA - pB;
1275
+ return a.createdAt - b.createdAt;
1276
+ });
1277
+ }
1168
1278
  var ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
1169
1279
  function stripAnsi(str) {
1170
1280
  return str.replace(ANSI_REGEX, "");
@@ -1287,11 +1397,19 @@ function formatCreationTime(timestamp, now = Date.now()) {
1287
1397
  }
1288
1398
  function formatOrigin(anchor) {
1289
1399
  if (typeof anchor === "object") {
1290
- if (!anchor.cwd) return anchor.project || "global";
1291
- return import_node_path4.default.basename(anchor.cwd) || "global";
1400
+ if (anchor.project && anchor.project !== "global") {
1401
+ const normProj = anchor.project.replace(/\\/g, "/").replace(/\/+$/, "");
1402
+ if (normProj.includes("/")) {
1403
+ const parts = normProj.split("/").filter(Boolean);
1404
+ return parts.slice(-2).join("/");
1405
+ }
1406
+ return normProj;
1407
+ }
1408
+ if (!anchor.cwd) return "global";
1409
+ return import_node_path4.default.basename(anchor.cwd.replace(/\\/g, "/")) || "global";
1292
1410
  }
1293
1411
  if (!anchor) return "global";
1294
- return import_node_path4.default.basename(anchor) || "global";
1412
+ return import_node_path4.default.basename(anchor.replace(/\\/g, "/")) || "global";
1295
1413
  }
1296
1414
  function updateAnchorStatusBar(ctx, store) {
1297
1415
  if (!hasActiveUI(ctx)) return;
@@ -1346,51 +1464,47 @@ function updateStartupBanner(ctx, store) {
1346
1464
  }
1347
1465
  async function openAnchorDashboard(ctx, store) {
1348
1466
  if (!hasActiveUI(ctx)) return;
1349
- const list = store.list({ status: "active", cwd: ctx.cwd });
1350
- if (list.length === 0) {
1467
+ const allUnfinished = store.list({ cwd: ctx.cwd, all: true }).filter((a) => a.status === "active" || a.status === "sleeping");
1468
+ if (allUnfinished.length === 0) {
1351
1469
  ctx.ui.notify("\u2316 No active anchors. Use /pin <task> to record.", "info");
1352
1470
  return;
1353
1471
  }
1354
- const groups = groupAnchorsByQuadrant(list);
1355
- const sortedList = [
1356
- ...groups.today,
1357
- ...groups.upcoming,
1358
- ...groups.habits,
1359
- ...groups.backlog
1360
- ];
1472
+ const sortedList = sortAnchorsForDisplay(allUnfinished, ctx.cwd);
1361
1473
  const optionMap = /* @__PURE__ */ new Map();
1362
1474
  const displayOptions = [];
1363
1475
  const termCols = process.stdout?.columns || 96;
1364
1476
  const isNarrow = termCols < 88;
1365
- const titleWidth = isNarrow ? Math.max(18, termCols - 36) : 34;
1477
+ const titleWidth = isNarrow ? Math.max(18, termCols - 36) : 36;
1366
1478
  for (let i = 0; i < sortedList.length; i++) {
1367
1479
  const a = sortedList[i];
1368
1480
  const num = a.id.replace(/^anc-/, "").padStart(2, "0");
1369
- const category = padToWidth(`[${classifyAnchor(a)}]`, 12);
1481
+ const badge = a.status === "sleeping" ? padToWidth("[Quiet]", 10) : padToWidth(`[${a.priority.toUpperCase()}]`, 10);
1370
1482
  const origin = formatOrigin(a);
1371
- const target = formatTargetDate(a);
1483
+ const target = a.status === "sleeping" ? "Quiet (0 tok)" : formatTargetDate(a);
1372
1484
  const created = formatCreationTime(a.createdAt);
1373
1485
  const titleWithFiles = a.files && a.files.length > 0 ? `${a.title} [${a.files.slice(0, 1).join(", ")}]` : a.title;
1374
1486
  const colNum = `${num} `;
1375
1487
  const colTitle = padToWidth(titleWithFiles, titleWidth);
1376
- const colOrigin = padToWidth(origin, 10);
1377
- const colTarget = padToWidth(target, 12);
1488
+ const colOrigin = padToWidth(origin, 12);
1489
+ const colTarget = padToWidth(target, 14);
1378
1490
  const colCreated = created;
1379
- const label = isNarrow ? `${colNum}${category}${colTitle} ${colTarget}`.trimEnd() : `${colNum}${category}${colTitle} ${colOrigin} ${colTarget} ${colCreated}`.trimEnd();
1491
+ const label = isNarrow ? `${colNum}${badge}${colTitle} ${colTarget}`.trimEnd() : `${colNum}${badge}${colTitle} ${colOrigin} ${colTarget} ${colCreated}`.trimEnd();
1380
1492
  optionMap.set(label, a);
1381
1493
  displayOptions.push(label);
1382
1494
  }
1383
- const selected = await ctx.ui.select("\u2316 Anchors (select to inspect or settle):", displayOptions);
1495
+ const selected = await ctx.ui.select("\u2316 Anchors (select to settle, wake, or inspect):", displayOptions);
1384
1496
  if (!selected) return;
1385
1497
  const anchor = optionMap.get(selected);
1386
1498
  if (!anchor) return;
1499
+ const actions = [
1500
+ "\u2713 Settle (Mark completed and archive)",
1501
+ ...anchor.status === "sleeping" ? ["\u26A1 Wake up (Activate for current session)"] : [],
1502
+ "\u2139 View details & associated files",
1503
+ "\u21A9 Cancel"
1504
+ ];
1387
1505
  const action = await ctx.ui.select(
1388
1506
  `\u2316 Anchor #${anchor.id}: "${anchor.title}"`,
1389
- [
1390
- "\u2713 Settle (Mark completed and archive)",
1391
- "\u2139 View details & associated files",
1392
- "\u21A9 Cancel"
1393
- ]
1507
+ actions
1394
1508
  );
1395
1509
  if (!action || action.startsWith("\u21A9")) return;
1396
1510
  if (action.startsWith("\u2713")) {
@@ -1398,9 +1512,13 @@ async function openAnchorDashboard(ctx, store) {
1398
1512
  const successMsg = anchor.recurrence === "daily" ? `\u2316 Completed for today: "${anchor.title}" (resets tomorrow)` : `\u2316 Settled #${anchor.id}: "${anchor.title}" (/anchor undo to revert)`;
1399
1513
  ctx.ui.notify(successMsg, "info");
1400
1514
  updateAnchorStatusBar(ctx, store);
1515
+ } else if (action.startsWith("\u26A1")) {
1516
+ store.touch(anchor.id);
1517
+ ctx.ui.notify(`\u2316 Woke up #${anchor.id}: "${anchor.title}"`, "info");
1518
+ updateAnchorStatusBar(ctx, store);
1401
1519
  } else if (action.startsWith("\u2139")) {
1402
1520
  const details = [
1403
- `#${anchor.id} [${anchor.priority.toUpperCase()}] ${anchor.title}`,
1521
+ `#${anchor.id} [${anchor.priority.toUpperCase()} \xB7 ${anchor.status.toUpperCase()}] ${anchor.title}`,
1404
1522
  anchor.description ? `Description: ${anchor.description}` : null,
1405
1523
  anchor.targetDate ? `Target Date: ${anchor.targetDate} (${formatTargetDate(anchor)})` : null,
1406
1524
  anchor.files && anchor.files.length > 0 ? `Files: ${anchor.files.join(", ")}` : null,
@@ -1450,16 +1568,10 @@ function resolveAnchorTarget(store, idOrIndex, cwd) {
1450
1568
  const clean = idOrIndex.trim().replace(/^#/, "");
1451
1569
  if (/^\d+$/.test(clean)) {
1452
1570
  const seqIndex = parseInt(clean, 10);
1453
- const active = store.list({ status: "active", cwd });
1454
- const groups = groupAnchorsByQuadrant(active);
1455
- const sortedActive = [
1456
- ...groups.today,
1457
- ...groups.upcoming,
1458
- ...groups.habits,
1459
- ...groups.backlog
1460
- ];
1461
- if (seqIndex >= 1 && seqIndex <= sortedActive.length) {
1462
- return sortedActive[seqIndex - 1].id;
1571
+ const visible = store.list({ cwd, all: true }).filter((a) => a.status === "active" || a.status === "sleeping");
1572
+ const sorted = sortAnchorsForDisplay(visible, cwd);
1573
+ if (seqIndex >= 1 && seqIndex <= sorted.length) {
1574
+ return sorted[seqIndex - 1].id;
1463
1575
  }
1464
1576
  }
1465
1577
  const direct = store.get(clean);
@@ -1475,8 +1587,12 @@ function extension_default(pi) {
1475
1587
  annotatedThisSession.clear();
1476
1588
  preflightFailures.clear();
1477
1589
  const sweep = sweepStore(store);
1478
- if (sweep.transitionedToSleeping.length > 0) {
1479
- ctx.ui.notify(`\u2316 Anchor: ${sweep.transitionedToSleeping.length} inactive tasks transitioned to sleep`, "info");
1590
+ const sleepingForWorkspace = store.list({ status: "sleeping", all: true }).filter((a) => (a.files.length > 0 || a.project && a.project !== "global" || a.cwd) && isAnchorTargetingWorkspace(a, ctx.cwd));
1591
+ for (const sa of sleepingForWorkspace) {
1592
+ store.touch(sa.id);
1593
+ }
1594
+ if (sweep.transitionedToSleeping.length > 0 && sleepingForWorkspace.length === 0) {
1595
+ ctx.ui.notify(`\u2316 Anchor: ${sweep.transitionedToSleeping.length} inactive task(s) moved to quiet mode (/anchor to view)`, "info");
1480
1596
  }
1481
1597
  const activeAnchors = store.list({ cwd: ctx.cwd }).filter((a) => a.status === "active");
1482
1598
  for (const a of activeAnchors) {
@@ -1545,8 +1661,26 @@ ${contextSnippet}`
1545
1661
  }
1546
1662
  });
1547
1663
  pi.on("tool_result", async (event, ctx) => {
1664
+ const lowerTool = String(event.toolName || "").toLowerCase();
1548
1665
  if (!event.isError) {
1549
1666
  observer.recordToolCall(event.toolName, event.input || {});
1667
+ if (lowerTool === "bash" || lowerTool === "powershell") {
1668
+ const executedCmd = String(event.input?.command || "");
1669
+ if (executedCmd.includes("git commit") || executedCmd.length > 0) {
1670
+ const settledNow = tryRealtimeAutoSettle(store, observer, ctx.cwd, executedCmd);
1671
+ if (settledNow.length > 0) {
1672
+ updateAnchorStatusBar(ctx, store);
1673
+ if (hasActiveUI(ctx)) {
1674
+ for (const item of settledNow) {
1675
+ try {
1676
+ ctx.ui.notify(`\u2316 Auto-settled #${item.anchor.id} ("${item.anchor.title}") via ${item.reason}`, "info");
1677
+ } catch {
1678
+ }
1679
+ }
1680
+ }
1681
+ }
1682
+ }
1683
+ }
1550
1684
  }
1551
1685
  const pathInput = event.input?.path;
1552
1686
  if (typeof pathInput !== "string") return;
@@ -1610,8 +1744,8 @@ ${contextSnippet}`
1610
1744
  }
1611
1745
  }
1612
1746
  const modified = observer.getModifiedFiles();
1613
- if (modified.length === 0) return;
1614
- const proposals = generateSettlementProposals(store, modified, ctx.cwd);
1747
+ if (modified.length === 0 && !observer.hasCommitted()) return;
1748
+ const proposals = generateSettlementProposals(store, modified, ctx.cwd, observer);
1615
1749
  if (proposals.length === 0) return;
1616
1750
  for (const prop of proposals) {
1617
1751
  const a = prop.anchor;
@@ -1,5 +1,5 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
- import { A as AnchorStore } from './store-BNKjzhS_.cjs';
2
+ import { A as AnchorStore } from './store-t2_TDhJn.cjs';
3
3
 
4
4
  /**
5
5
  * Parse inline `--due <date>` / `-d <date>` flags and `done`/`rm`/`close`/`settle` subcommands
@@ -1,5 +1,5 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
- import { A as AnchorStore } from './store-BNKjzhS_.js';
2
+ import { A as AnchorStore } from './store-t2_TDhJn.js';
3
3
 
4
4
  /**
5
5
  * Parse inline `--due <date>` / `-d <date>` flags and `done`/`rm`/`close`/`settle` subcommands
package/dist/extension.js CHANGED
@@ -4,19 +4,21 @@ import {
4
4
  detectTargetDate,
5
5
  findMatchedAnchors,
6
6
  generateSettlementProposals,
7
- groupAnchorsByQuadrant,
8
7
  hasActiveUI,
8
+ isAnchorTargetingWorkspace,
9
9
  isSameOrSubPath,
10
10
  makeSafeTaskAnnotation,
11
11
  normalizePath,
12
12
  openAnchorDashboard,
13
13
  renderColdStartAnchorsContext,
14
14
  runPhysicalVerification,
15
+ sortAnchorsForDisplay,
15
16
  sweepStore,
16
17
  truncateToWidth,
18
+ tryRealtimeAutoSettle,
17
19
  updateAnchorStatusBar,
18
20
  updateStartupBanner
19
- } from "./chunk-G72UHPBN.js";
21
+ } from "./chunk-Q7IILJFA.js";
20
22
 
21
23
  // src/extension.ts
22
24
  import { Type } from "@sinclair/typebox";
@@ -58,16 +60,10 @@ function resolveAnchorTarget(store, idOrIndex, cwd) {
58
60
  const clean = idOrIndex.trim().replace(/^#/, "");
59
61
  if (/^\d+$/.test(clean)) {
60
62
  const seqIndex = parseInt(clean, 10);
61
- const active = store.list({ status: "active", cwd });
62
- const groups = groupAnchorsByQuadrant(active);
63
- const sortedActive = [
64
- ...groups.today,
65
- ...groups.upcoming,
66
- ...groups.habits,
67
- ...groups.backlog
68
- ];
69
- if (seqIndex >= 1 && seqIndex <= sortedActive.length) {
70
- return sortedActive[seqIndex - 1].id;
63
+ const visible = store.list({ cwd, all: true }).filter((a) => a.status === "active" || a.status === "sleeping");
64
+ const sorted = sortAnchorsForDisplay(visible, cwd);
65
+ if (seqIndex >= 1 && seqIndex <= sorted.length) {
66
+ return sorted[seqIndex - 1].id;
71
67
  }
72
68
  }
73
69
  const direct = store.get(clean);
@@ -83,8 +79,12 @@ function extension_default(pi) {
83
79
  annotatedThisSession.clear();
84
80
  preflightFailures.clear();
85
81
  const sweep = sweepStore(store);
86
- if (sweep.transitionedToSleeping.length > 0) {
87
- ctx.ui.notify(`\u2316 Anchor: ${sweep.transitionedToSleeping.length} inactive tasks transitioned to sleep`, "info");
82
+ const sleepingForWorkspace = store.list({ status: "sleeping", all: true }).filter((a) => (a.files.length > 0 || a.project && a.project !== "global" || a.cwd) && isAnchorTargetingWorkspace(a, ctx.cwd));
83
+ for (const sa of sleepingForWorkspace) {
84
+ store.touch(sa.id);
85
+ }
86
+ if (sweep.transitionedToSleeping.length > 0 && sleepingForWorkspace.length === 0) {
87
+ ctx.ui.notify(`\u2316 Anchor: ${sweep.transitionedToSleeping.length} inactive task(s) moved to quiet mode (/anchor to view)`, "info");
88
88
  }
89
89
  const activeAnchors = store.list({ cwd: ctx.cwd }).filter((a) => a.status === "active");
90
90
  for (const a of activeAnchors) {
@@ -153,8 +153,26 @@ ${contextSnippet}`
153
153
  }
154
154
  });
155
155
  pi.on("tool_result", async (event, ctx) => {
156
+ const lowerTool = String(event.toolName || "").toLowerCase();
156
157
  if (!event.isError) {
157
158
  observer.recordToolCall(event.toolName, event.input || {});
159
+ if (lowerTool === "bash" || lowerTool === "powershell") {
160
+ const executedCmd = String(event.input?.command || "");
161
+ if (executedCmd.includes("git commit") || executedCmd.length > 0) {
162
+ const settledNow = tryRealtimeAutoSettle(store, observer, ctx.cwd, executedCmd);
163
+ if (settledNow.length > 0) {
164
+ updateAnchorStatusBar(ctx, store);
165
+ if (hasActiveUI(ctx)) {
166
+ for (const item of settledNow) {
167
+ try {
168
+ ctx.ui.notify(`\u2316 Auto-settled #${item.anchor.id} ("${item.anchor.title}") via ${item.reason}`, "info");
169
+ } catch {
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
158
176
  }
159
177
  const pathInput = event.input?.path;
160
178
  if (typeof pathInput !== "string") return;
@@ -218,8 +236,8 @@ ${contextSnippet}`
218
236
  }
219
237
  }
220
238
  const modified = observer.getModifiedFiles();
221
- if (modified.length === 0) return;
222
- const proposals = generateSettlementProposals(store, modified, ctx.cwd);
239
+ if (modified.length === 0 && !observer.hasCommitted()) return;
240
+ const proposals = generateSettlementProposals(store, modified, ctx.cwd, observer);
223
241
  if (proposals.length === 0) return;
224
242
  for (const prop of proposals) {
225
243
  const a = prop.anchor;