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.
- package/README.md +22 -18
- package/README_zh.md +22 -18
- package/assets/hero-en.svg +39 -60
- package/assets/hero-zh.svg +39 -60
- package/bin/anchor.js +38 -51
- package/dist/{chunk-G72UHPBN.js → chunk-Q7IILJFA.js} +165 -43
- package/dist/extension.cjs +188 -54
- package/dist/extension.d.cts +1 -1
- package/dist/extension.d.ts +1 -1
- package/dist/extension.js +34 -16
- package/dist/index.cjs +169 -43
- package/dist/index.d.cts +63 -36
- package/dist/index.d.ts +63 -36
- package/dist/index.js +9 -1
- package/dist/{store-BNKjzhS_.d.cts → store-t2_TDhJn.d.cts} +2 -1
- package/dist/{store-BNKjzhS_.d.ts → store-t2_TDhJn.d.ts} +2 -1
- package/package.json +2 -2
- package/src/extension.ts +43 -19
- package/src/matcher.ts +51 -0
- package/src/settlement.ts +72 -6
- package/src/store.ts +25 -9
- package/src/tui.ts +77 -29
- package/src/types.ts +7 -7
package/dist/extension.cjs
CHANGED
|
@@ -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:
|
|
48
|
-
//
|
|
49
|
-
sleepDays:
|
|
50
|
-
|
|
51
|
-
|
|
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:
|
|
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:
|
|
227
|
+
return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
|
|
195
228
|
}
|
|
196
229
|
if (lower.includes("\u540E\u5929")) {
|
|
197
|
-
return { activeDays:
|
|
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
|
|
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:
|
|
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 && !
|
|
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
|
|
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 ? `
|
|
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:
|
|
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 (
|
|
1291
|
-
|
|
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
|
|
1350
|
-
if (
|
|
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
|
|
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) :
|
|
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
|
|
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,
|
|
1377
|
-
const colTarget = padToWidth(target,
|
|
1488
|
+
const colOrigin = padToWidth(origin, 12);
|
|
1489
|
+
const colTarget = padToWidth(target, 14);
|
|
1378
1490
|
const colCreated = created;
|
|
1379
|
-
const label = isNarrow ? `${colNum}${
|
|
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
|
|
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
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1456
|
-
|
|
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
|
-
|
|
1479
|
-
|
|
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;
|
package/dist/extension.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
-
import { A as AnchorStore } from './store-
|
|
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
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
-
import { A as AnchorStore } from './store-
|
|
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-
|
|
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
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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;
|