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/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(index_exports, {
|
|
|
44
44
|
detectRecurrence: () => detectRecurrence,
|
|
45
45
|
detectTargetDate: () => detectTargetDate,
|
|
46
46
|
evaluateAnchorDecay: () => evaluateAnchorDecay,
|
|
47
|
+
extractPathsFromText: () => extractPathsFromText,
|
|
47
48
|
findMatchedAnchors: () => findMatchedAnchors,
|
|
48
49
|
formatCreationTime: () => formatCreationTime,
|
|
49
50
|
formatOrigin: () => formatOrigin,
|
|
@@ -59,6 +60,7 @@ __export(index_exports, {
|
|
|
59
60
|
globToRegExp: () => globToRegExp,
|
|
60
61
|
groupAnchorsByQuadrant: () => groupAnchorsByQuadrant,
|
|
61
62
|
hasActiveUI: () => hasActiveUI,
|
|
63
|
+
isAnchorTargetingWorkspace: () => isAnchorTargetingWorkspace,
|
|
62
64
|
isSameOrSubPath: () => isSameOrSubPath,
|
|
63
65
|
makeSafeTaskAnnotation: () => makeSafeTaskAnnotation,
|
|
64
66
|
matchAnchorAgainstTouchedFiles: () => matchAnchorAgainstTouchedFiles,
|
|
@@ -68,9 +70,11 @@ __export(index_exports, {
|
|
|
68
70
|
renderActiveAnchorsContext: () => renderActiveAnchorsContext,
|
|
69
71
|
renderColdStartAnchorsContext: () => renderColdStartAnchorsContext,
|
|
70
72
|
runPhysicalVerification: () => runPhysicalVerification,
|
|
73
|
+
sortAnchorsForDisplay: () => sortAnchorsForDisplay,
|
|
71
74
|
stripAnsi: () => stripAnsi,
|
|
72
75
|
sweepStore: () => sweepStore,
|
|
73
76
|
truncateToWidth: () => truncateToWidth,
|
|
77
|
+
tryRealtimeAutoSettle: () => tryRealtimeAutoSettle,
|
|
74
78
|
updateAnchorStatusBar: () => updateAnchorStatusBar,
|
|
75
79
|
updateStartupBanner: () => updateStartupBanner
|
|
76
80
|
});
|
|
@@ -78,23 +82,23 @@ module.exports = __toCommonJS(index_exports);
|
|
|
78
82
|
|
|
79
83
|
// src/types.ts
|
|
80
84
|
var EPHEMERAL_DECAY_POLICY = {
|
|
81
|
-
activeDays:
|
|
82
|
-
//
|
|
83
|
-
sleepDays:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// 48 hours total before auto-clearing
|
|
85
|
+
activeDays: 7,
|
|
86
|
+
// 7 days active before quiet sleep
|
|
87
|
+
sleepDays: 30,
|
|
88
|
+
graveyardDays: 9999
|
|
89
|
+
// Never silently auto-delete unfinished tasks
|
|
87
90
|
};
|
|
88
91
|
var DURABLE_DECAY_POLICY = {
|
|
89
|
-
activeDays:
|
|
92
|
+
activeDays: 7,
|
|
93
|
+
// 7 days active before quiet sleep
|
|
90
94
|
sleepDays: 30,
|
|
91
95
|
graveyardDays: 9999
|
|
92
96
|
// Never permanently auto-dropped; preserved indefinitely
|
|
93
97
|
};
|
|
94
98
|
var DEFAULT_DECAY_POLICY = {
|
|
95
|
-
activeDays:
|
|
96
|
-
sleepDays:
|
|
97
|
-
graveyardDays:
|
|
99
|
+
activeDays: 7,
|
|
100
|
+
sleepDays: 30,
|
|
101
|
+
graveyardDays: 9999
|
|
98
102
|
};
|
|
99
103
|
|
|
100
104
|
// src/store.ts
|
|
@@ -133,6 +137,39 @@ function isSameOrSubPath(anchorCwd, targetCwd) {
|
|
|
133
137
|
const cmpT = isWinStyle ? normT.toLowerCase() : normT;
|
|
134
138
|
return cmpA === cmpT || cmpT.startsWith(cmpA + "/") || cmpA.startsWith(cmpT + "/");
|
|
135
139
|
}
|
|
140
|
+
function extractPathsFromText(text) {
|
|
141
|
+
if (!text) return [];
|
|
142
|
+
const results = /* @__PURE__ */ new Set();
|
|
143
|
+
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;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = regex.exec(text)) !== null) {
|
|
146
|
+
const cleaned = normalizePath(match[0].replace(/[.:;,'")\]}>]+$/, ""));
|
|
147
|
+
if (cleaned && cleaned.length >= 3) {
|
|
148
|
+
results.add(cleaned);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return Array.from(results);
|
|
152
|
+
}
|
|
153
|
+
function isAnchorTargetingWorkspace(anchor, targetCwd) {
|
|
154
|
+
if (!targetCwd) return true;
|
|
155
|
+
const normTarget = targetCwd.trim().replace(/\\/g, "/").replace(/\/+$/, "");
|
|
156
|
+
const targetBase = import_node_path.default.basename(normTarget).toLowerCase();
|
|
157
|
+
if (anchor.project && anchor.project !== "global") {
|
|
158
|
+
const normProj = anchor.project.trim().replace(/\\/g, "/").replace(/\/+$/, "");
|
|
159
|
+
if (isSameOrSubPath(normProj, normTarget)) return true;
|
|
160
|
+
const projBase = import_node_path.default.basename(normProj).toLowerCase();
|
|
161
|
+
if (projBase && projBase === targetBase) return true;
|
|
162
|
+
}
|
|
163
|
+
if (anchor.files && anchor.files.length > 0) {
|
|
164
|
+
for (const f of anchor.files) {
|
|
165
|
+
if (isSameOrSubPath(f, normTarget)) return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (anchor.cwd && isSameOrSubPath(anchor.cwd, normTarget)) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
136
173
|
function matchAnchorAgainstTouchedFiles(anchor, touchedFiles) {
|
|
137
174
|
if (!anchor.files || anchor.files.length === 0) return null;
|
|
138
175
|
const normTouched = touchedFiles.map((f) => normalizePath(f, anchor.cwd)).filter(Boolean);
|
|
@@ -235,10 +272,10 @@ function detectDurability(title) {
|
|
|
235
272
|
function getEphemeralDecayPolicy(title) {
|
|
236
273
|
const lower = title.toLowerCase();
|
|
237
274
|
if (lower.includes("\u5927\u540E\u5929")) {
|
|
238
|
-
return { activeDays:
|
|
275
|
+
return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
|
|
239
276
|
}
|
|
240
277
|
if (lower.includes("\u540E\u5929")) {
|
|
241
|
-
return { activeDays:
|
|
278
|
+
return { activeDays: 7, sleepDays: 30, graveyardDays: 9999 };
|
|
242
279
|
}
|
|
243
280
|
return { ...EPHEMERAL_DECAY_POLICY };
|
|
244
281
|
}
|
|
@@ -549,7 +586,17 @@ var AnchorStore = class {
|
|
|
549
586
|
const state = this.loadState();
|
|
550
587
|
const now = Date.now();
|
|
551
588
|
const id = this.generateId(state);
|
|
552
|
-
const
|
|
589
|
+
const explicitFiles = (input.files || []).map((f) => normalizePath(f)).filter(Boolean);
|
|
590
|
+
const inferredFiles = explicitFiles.length > 0 ? explicitFiles : extractPathsFromText(`${input.title} ${input.description || ""}`);
|
|
591
|
+
let cwd = input.cwd ? normalizePath(input.cwd) : "";
|
|
592
|
+
if (input.project && (/^[a-zA-Z]:[\\/]/.test(input.project.trim()) || input.project.trim().startsWith("/"))) {
|
|
593
|
+
cwd = normalizePath(input.project.trim());
|
|
594
|
+
} else if (inferredFiles.length > 0 && (/^[a-zA-Z]:\//.test(inferredFiles[0]) || inferredFiles[0].startsWith("/"))) {
|
|
595
|
+
const firstDir = import_node_path2.default.dirname(inferredFiles[0]);
|
|
596
|
+
if (firstDir && firstDir !== ".") {
|
|
597
|
+
cwd = normalizePath(firstDir);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
553
600
|
const projectName = input.project ? input.project.trim() : cwd ? import_node_path2.default.basename(cwd) : "global";
|
|
554
601
|
const recurrence = input.recurrence || detectRecurrence(input.title);
|
|
555
602
|
const targetDate = input.targetDate || detectTargetDate(input.title, now);
|
|
@@ -569,7 +616,7 @@ var AnchorStore = class {
|
|
|
569
616
|
createdAt: now,
|
|
570
617
|
updatedAt: now,
|
|
571
618
|
lastTouchedAt: now,
|
|
572
|
-
files:
|
|
619
|
+
files: inferredFiles,
|
|
573
620
|
tags: (input.tags || []).map((t) => t.trim()).filter(Boolean),
|
|
574
621
|
verifyCommand: input.verifyCommand?.trim() || void 0,
|
|
575
622
|
decay: input.decay ? { ...input.decay } : { ...decayPolicy }
|
|
@@ -608,7 +655,7 @@ var AnchorStore = class {
|
|
|
608
655
|
if (filter?.status && a.status !== filter.status) return false;
|
|
609
656
|
if (filter?.priority && a.priority !== filter.priority) return false;
|
|
610
657
|
if (!filter?.all && targetCwd) {
|
|
611
|
-
if (a.files.length > 0 && a.cwd && !
|
|
658
|
+
if (a.files.length > 0 && (a.cwd || a.project) && !isAnchorTargetingWorkspace(a, targetCwd)) {
|
|
612
659
|
return false;
|
|
613
660
|
}
|
|
614
661
|
}
|
|
@@ -928,20 +975,66 @@ function runPhysicalVerification(anchor, cwd) {
|
|
|
928
975
|
return { success: false, output: String(err.stderr || err.stdout || err.message).trim() };
|
|
929
976
|
}
|
|
930
977
|
}
|
|
931
|
-
function
|
|
978
|
+
function tryRealtimeAutoSettle(store, observer, cwd, executedCommand) {
|
|
979
|
+
const candidates = store.list({ cwd }).filter((a) => a.status === "active" || a.status === "sleeping");
|
|
980
|
+
if (candidates.length === 0) return [];
|
|
981
|
+
const settledList = [];
|
|
982
|
+
const cmdTrimmed = (executedCommand || "").trim();
|
|
983
|
+
for (const a of candidates) {
|
|
984
|
+
if (observer.hasCommitted()) {
|
|
985
|
+
const commitMatch = observer.matchesCommit(a);
|
|
986
|
+
if (commitMatch.matched && (!cwd || isAnchorTargetingWorkspace(a, cwd))) {
|
|
987
|
+
const settled = store.settle(a.id, {
|
|
988
|
+
settledBy: "verification-test",
|
|
989
|
+
summary: `Auto-settled via Git commit: ${commitMatch.message}`,
|
|
990
|
+
touchedFiles: observer.getModifiedFiles()
|
|
991
|
+
});
|
|
992
|
+
settledList.push({ anchor: settled, reason: `git commit ("${commitMatch.message}")` });
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (a.verifyCommand && cmdTrimmed && cmdTrimmed.includes(a.verifyCommand.trim())) {
|
|
997
|
+
const settled = store.settle(a.id, {
|
|
998
|
+
settledBy: "verification-test",
|
|
999
|
+
summary: `Auto-settled via passing command: ${a.verifyCommand}`,
|
|
1000
|
+
touchedFiles: observer.getModifiedFiles()
|
|
1001
|
+
});
|
|
1002
|
+
settledList.push({ anchor: settled, reason: `verifyCommand (${a.verifyCommand})` });
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return settledList;
|
|
1006
|
+
}
|
|
1007
|
+
function generateSettlementProposals(store, touchedFiles, cwd, observer) {
|
|
932
1008
|
const activeAndSleeping = store.list({ cwd }).filter((a) => a.status === "active" || a.status === "sleeping");
|
|
933
1009
|
const matches = findMatchedAnchors(activeAndSleeping, touchedFiles);
|
|
1010
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
934
1011
|
const proposals = [];
|
|
935
1012
|
for (const match of matches) {
|
|
1013
|
+
seenIds.add(match.anchor.id);
|
|
936
1014
|
const fileList = match.matchedFiles.slice(0, 3).join(", ");
|
|
937
|
-
const moreSuffix = match.matchedFiles.length > 3 ? `
|
|
1015
|
+
const moreSuffix = match.matchedFiles.length > 3 ? ` (+${match.matchedFiles.length - 3} more)` : "";
|
|
938
1016
|
proposals.push({
|
|
939
1017
|
anchor: match.anchor,
|
|
940
1018
|
matchedFiles: match.matchedFiles,
|
|
941
|
-
reason:
|
|
1019
|
+
reason: `Modified ${fileList}${moreSuffix}`,
|
|
942
1020
|
recommendedAction: match.score >= 0.85 ? "settle" : "defer"
|
|
943
1021
|
});
|
|
944
1022
|
}
|
|
1023
|
+
if (observer && observer.hasCommitted()) {
|
|
1024
|
+
for (const a of activeAndSleeping) {
|
|
1025
|
+
if (seenIds.has(a.id)) continue;
|
|
1026
|
+
const cm = observer.matchesCommit(a);
|
|
1027
|
+
if (cm.matched) {
|
|
1028
|
+
seenIds.add(a.id);
|
|
1029
|
+
proposals.push({
|
|
1030
|
+
anchor: a,
|
|
1031
|
+
matchedFiles: touchedFiles.slice(0, 3),
|
|
1032
|
+
reason: `Matched git commit: "${cm.message}"`,
|
|
1033
|
+
recommendedAction: "settle"
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
945
1038
|
return proposals;
|
|
946
1039
|
}
|
|
947
1040
|
function formatSettlementCard(proposal) {
|
|
@@ -989,6 +1082,27 @@ function groupAnchorsByQuadrant(anchors, now = Date.now()) {
|
|
|
989
1082
|
}
|
|
990
1083
|
return groups;
|
|
991
1084
|
}
|
|
1085
|
+
var PRIORITY_RANK = { p0: 0, p1: 1, p2: 2 };
|
|
1086
|
+
function sortAnchorsForDisplay(anchors, cwd, now = Date.now()) {
|
|
1087
|
+
const todayStr = getTodayDateString(now);
|
|
1088
|
+
return [...anchors].sort((a, b) => {
|
|
1089
|
+
if (a.status !== b.status) {
|
|
1090
|
+
return a.status === "active" ? -1 : 1;
|
|
1091
|
+
}
|
|
1092
|
+
if (cwd) {
|
|
1093
|
+
const aMatch = isAnchorTargetingWorkspace(a, cwd) ? 0 : 1;
|
|
1094
|
+
const bMatch = isAnchorTargetingWorkspace(b, cwd) ? 0 : 1;
|
|
1095
|
+
if (aMatch !== bMatch) return aMatch - bMatch;
|
|
1096
|
+
}
|
|
1097
|
+
const aDue = a.targetDate && a.targetDate <= todayStr ? 0 : 1;
|
|
1098
|
+
const bDue = b.targetDate && b.targetDate <= todayStr ? 0 : 1;
|
|
1099
|
+
if (aDue !== bDue) return aDue - bDue;
|
|
1100
|
+
const pA = PRIORITY_RANK[a.priority] ?? 1;
|
|
1101
|
+
const pB = PRIORITY_RANK[b.priority] ?? 1;
|
|
1102
|
+
if (pA !== pB) return pA - pB;
|
|
1103
|
+
return a.createdAt - b.createdAt;
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
992
1106
|
var ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
993
1107
|
function stripAnsi(str) {
|
|
994
1108
|
return str.replace(ANSI_REGEX, "");
|
|
@@ -1117,11 +1231,19 @@ function formatRemainingTtl(anchor, now = Date.now()) {
|
|
|
1117
1231
|
}
|
|
1118
1232
|
function formatOrigin(anchor) {
|
|
1119
1233
|
if (typeof anchor === "object") {
|
|
1120
|
-
if (
|
|
1121
|
-
|
|
1234
|
+
if (anchor.project && anchor.project !== "global") {
|
|
1235
|
+
const normProj = anchor.project.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1236
|
+
if (normProj.includes("/")) {
|
|
1237
|
+
const parts = normProj.split("/").filter(Boolean);
|
|
1238
|
+
return parts.slice(-2).join("/");
|
|
1239
|
+
}
|
|
1240
|
+
return normProj;
|
|
1241
|
+
}
|
|
1242
|
+
if (!anchor.cwd) return "global";
|
|
1243
|
+
return import_node_path3.default.basename(anchor.cwd.replace(/\\/g, "/")) || "global";
|
|
1122
1244
|
}
|
|
1123
1245
|
if (!anchor) return "global";
|
|
1124
|
-
return import_node_path3.default.basename(anchor) || "global";
|
|
1246
|
+
return import_node_path3.default.basename(anchor.replace(/\\/g, "/")) || "global";
|
|
1125
1247
|
}
|
|
1126
1248
|
function updateAnchorStatusBar(ctx, store) {
|
|
1127
1249
|
if (!hasActiveUI(ctx)) return;
|
|
@@ -1176,51 +1298,47 @@ function updateStartupBanner(ctx, store) {
|
|
|
1176
1298
|
}
|
|
1177
1299
|
async function openAnchorDashboard(ctx, store) {
|
|
1178
1300
|
if (!hasActiveUI(ctx)) return;
|
|
1179
|
-
const
|
|
1180
|
-
if (
|
|
1301
|
+
const allUnfinished = store.list({ cwd: ctx.cwd, all: true }).filter((a) => a.status === "active" || a.status === "sleeping");
|
|
1302
|
+
if (allUnfinished.length === 0) {
|
|
1181
1303
|
ctx.ui.notify("\u2316 No active anchors. Use /pin <task> to record.", "info");
|
|
1182
1304
|
return;
|
|
1183
1305
|
}
|
|
1184
|
-
const
|
|
1185
|
-
const sortedList = [
|
|
1186
|
-
...groups.today,
|
|
1187
|
-
...groups.upcoming,
|
|
1188
|
-
...groups.habits,
|
|
1189
|
-
...groups.backlog
|
|
1190
|
-
];
|
|
1306
|
+
const sortedList = sortAnchorsForDisplay(allUnfinished, ctx.cwd);
|
|
1191
1307
|
const optionMap = /* @__PURE__ */ new Map();
|
|
1192
1308
|
const displayOptions = [];
|
|
1193
1309
|
const termCols = process.stdout?.columns || 96;
|
|
1194
1310
|
const isNarrow = termCols < 88;
|
|
1195
|
-
const titleWidth = isNarrow ? Math.max(18, termCols - 36) :
|
|
1311
|
+
const titleWidth = isNarrow ? Math.max(18, termCols - 36) : 36;
|
|
1196
1312
|
for (let i = 0; i < sortedList.length; i++) {
|
|
1197
1313
|
const a = sortedList[i];
|
|
1198
1314
|
const num = a.id.replace(/^anc-/, "").padStart(2, "0");
|
|
1199
|
-
const
|
|
1315
|
+
const badge = a.status === "sleeping" ? padToWidth("[Quiet]", 10) : padToWidth(`[${a.priority.toUpperCase()}]`, 10);
|
|
1200
1316
|
const origin = formatOrigin(a);
|
|
1201
|
-
const target = formatTargetDate(a);
|
|
1317
|
+
const target = a.status === "sleeping" ? "Quiet (0 tok)" : formatTargetDate(a);
|
|
1202
1318
|
const created = formatCreationTime(a.createdAt);
|
|
1203
1319
|
const titleWithFiles = a.files && a.files.length > 0 ? `${a.title} [${a.files.slice(0, 1).join(", ")}]` : a.title;
|
|
1204
1320
|
const colNum = `${num} `;
|
|
1205
1321
|
const colTitle = padToWidth(titleWithFiles, titleWidth);
|
|
1206
|
-
const colOrigin = padToWidth(origin,
|
|
1207
|
-
const colTarget = padToWidth(target,
|
|
1322
|
+
const colOrigin = padToWidth(origin, 12);
|
|
1323
|
+
const colTarget = padToWidth(target, 14);
|
|
1208
1324
|
const colCreated = created;
|
|
1209
|
-
const label = isNarrow ? `${colNum}${
|
|
1325
|
+
const label = isNarrow ? `${colNum}${badge}${colTitle} ${colTarget}`.trimEnd() : `${colNum}${badge}${colTitle} ${colOrigin} ${colTarget} ${colCreated}`.trimEnd();
|
|
1210
1326
|
optionMap.set(label, a);
|
|
1211
1327
|
displayOptions.push(label);
|
|
1212
1328
|
}
|
|
1213
|
-
const selected = await ctx.ui.select("\u2316 Anchors (select to
|
|
1329
|
+
const selected = await ctx.ui.select("\u2316 Anchors (select to settle, wake, or inspect):", displayOptions);
|
|
1214
1330
|
if (!selected) return;
|
|
1215
1331
|
const anchor = optionMap.get(selected);
|
|
1216
1332
|
if (!anchor) return;
|
|
1333
|
+
const actions = [
|
|
1334
|
+
"\u2713 Settle (Mark completed and archive)",
|
|
1335
|
+
...anchor.status === "sleeping" ? ["\u26A1 Wake up (Activate for current session)"] : [],
|
|
1336
|
+
"\u2139 View details & associated files",
|
|
1337
|
+
"\u21A9 Cancel"
|
|
1338
|
+
];
|
|
1217
1339
|
const action = await ctx.ui.select(
|
|
1218
1340
|
`\u2316 Anchor #${anchor.id}: "${anchor.title}"`,
|
|
1219
|
-
|
|
1220
|
-
"\u2713 Settle (Mark completed and archive)",
|
|
1221
|
-
"\u2139 View details & associated files",
|
|
1222
|
-
"\u21A9 Cancel"
|
|
1223
|
-
]
|
|
1341
|
+
actions
|
|
1224
1342
|
);
|
|
1225
1343
|
if (!action || action.startsWith("\u21A9")) return;
|
|
1226
1344
|
if (action.startsWith("\u2713")) {
|
|
@@ -1228,9 +1346,13 @@ async function openAnchorDashboard(ctx, store) {
|
|
|
1228
1346
|
const successMsg = anchor.recurrence === "daily" ? `\u2316 Completed for today: "${anchor.title}" (resets tomorrow)` : `\u2316 Settled #${anchor.id}: "${anchor.title}" (/anchor undo to revert)`;
|
|
1229
1347
|
ctx.ui.notify(successMsg, "info");
|
|
1230
1348
|
updateAnchorStatusBar(ctx, store);
|
|
1349
|
+
} else if (action.startsWith("\u26A1")) {
|
|
1350
|
+
store.touch(anchor.id);
|
|
1351
|
+
ctx.ui.notify(`\u2316 Woke up #${anchor.id}: "${anchor.title}"`, "info");
|
|
1352
|
+
updateAnchorStatusBar(ctx, store);
|
|
1231
1353
|
} else if (action.startsWith("\u2139")) {
|
|
1232
1354
|
const details = [
|
|
1233
|
-
`#${anchor.id} [${anchor.priority.toUpperCase()}] ${anchor.title}`,
|
|
1355
|
+
`#${anchor.id} [${anchor.priority.toUpperCase()} \xB7 ${anchor.status.toUpperCase()}] ${anchor.title}`,
|
|
1234
1356
|
anchor.description ? `Description: ${anchor.description}` : null,
|
|
1235
1357
|
anchor.targetDate ? `Target Date: ${anchor.targetDate} (${formatTargetDate(anchor)})` : null,
|
|
1236
1358
|
anchor.files && anchor.files.length > 0 ? `Files: ${anchor.files.join(", ")}` : null,
|
|
@@ -1549,6 +1671,7 @@ var AnchorProtocol = class {
|
|
|
1549
1671
|
detectRecurrence,
|
|
1550
1672
|
detectTargetDate,
|
|
1551
1673
|
evaluateAnchorDecay,
|
|
1674
|
+
extractPathsFromText,
|
|
1552
1675
|
findMatchedAnchors,
|
|
1553
1676
|
formatCreationTime,
|
|
1554
1677
|
formatOrigin,
|
|
@@ -1564,6 +1687,7 @@ var AnchorProtocol = class {
|
|
|
1564
1687
|
globToRegExp,
|
|
1565
1688
|
groupAnchorsByQuadrant,
|
|
1566
1689
|
hasActiveUI,
|
|
1690
|
+
isAnchorTargetingWorkspace,
|
|
1567
1691
|
isSameOrSubPath,
|
|
1568
1692
|
makeSafeTaskAnnotation,
|
|
1569
1693
|
matchAnchorAgainstTouchedFiles,
|
|
@@ -1573,9 +1697,11 @@ var AnchorProtocol = class {
|
|
|
1573
1697
|
renderActiveAnchorsContext,
|
|
1574
1698
|
renderColdStartAnchorsContext,
|
|
1575
1699
|
runPhysicalVerification,
|
|
1700
|
+
sortAnchorsForDisplay,
|
|
1576
1701
|
stripAnsi,
|
|
1577
1702
|
sweepStore,
|
|
1578
1703
|
truncateToWidth,
|
|
1704
|
+
tryRealtimeAutoSettle,
|
|
1579
1705
|
updateAnchorStatusBar,
|
|
1580
1706
|
updateStartupBanner
|
|
1581
1707
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as AnchorStatus, b as Anchor, A as AnchorStore, T as TouchMatchResult, S as SettlementProposal } from './store-
|
|
2
|
-
export { c as AnchorDecayPolicy, d as AnchorDurability, e as AnchorEvidence, f as AnchorPriority, g as AnchorStoreState, D as DEFAULT_DECAY_POLICY, h as DURABLE_DECAY_POLICY, E as EPHEMERAL_DECAY_POLICY, i as acquireSyncLock, j as atomicRenameWithRetry, k as detectDurability, l as detectRecurrence, m as detectTargetDate, n as getDefaultStorageDir, o as getEphemeralDecayPolicy, p as getTodayDateString } from './store-
|
|
1
|
+
import { a as AnchorStatus, b as Anchor, A as AnchorStore, T as TouchMatchResult, S as SettlementProposal } from './store-t2_TDhJn.cjs';
|
|
2
|
+
export { c as AnchorDecayPolicy, d as AnchorDurability, e as AnchorEvidence, f as AnchorPriority, g as AnchorStoreState, D as DEFAULT_DECAY_POLICY, h as DURABLE_DECAY_POLICY, E as EPHEMERAL_DECAY_POLICY, i as acquireSyncLock, j as atomicRenameWithRetry, k as detectDurability, l as detectRecurrence, m as detectTargetDate, n as getDefaultStorageDir, o as getEphemeralDecayPolicy, p as getTodayDateString } from './store-t2_TDhJn.cjs';
|
|
3
3
|
import { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
4
4
|
|
|
5
5
|
interface DecayEvaluation {
|
|
@@ -35,6 +35,16 @@ declare function normalizePath(filePath: string, baseCwd?: string): string;
|
|
|
35
35
|
* Compare two workspace CWDs safely (case-insensitive on Windows, supports subdirectory scoping)
|
|
36
36
|
*/
|
|
37
37
|
declare function isSameOrSubPath(anchorCwd?: string, targetCwd?: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Extract explicit file or directory paths mentioned in natural language title/description
|
|
40
|
+
* (e.g. "D:/College/优化/EXTERNAL_ARCHITECTURE_INSPIRATIONS.md", "src/auth/jwt.ts")
|
|
41
|
+
*/
|
|
42
|
+
declare function extractPathsFromText(text?: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Determine whether an anchor specifically targets a given workspace directory (`targetCwd`),
|
|
45
|
+
* checking `anchor.cwd`, `anchor.project`, and `anchor.files`.
|
|
46
|
+
*/
|
|
47
|
+
declare function isAnchorTargetingWorkspace(anchor: Anchor, targetCwd?: string): boolean;
|
|
38
48
|
/**
|
|
39
49
|
* Match touched files against an anchor's registered physical patterns
|
|
40
50
|
* Only anchors with explicit files/globs/prefixes are matched against file touches.
|
|
@@ -47,6 +57,35 @@ declare function matchAnchorAgainstTouchedFiles(anchor: Anchor, touchedFiles: st
|
|
|
47
57
|
*/
|
|
48
58
|
declare function findMatchedAnchors(anchors: Anchor[], touchedFiles: string[]): TouchMatchResult[];
|
|
49
59
|
|
|
60
|
+
declare class SessionTouchObserver {
|
|
61
|
+
private touchedFiles;
|
|
62
|
+
private modifiedFiles;
|
|
63
|
+
private inspectedFiles;
|
|
64
|
+
private committed;
|
|
65
|
+
private commitMessages;
|
|
66
|
+
clear(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Observe and record a tool call invocation, separating inspection from mutation
|
|
69
|
+
*/
|
|
70
|
+
recordToolCall(toolName: string, input: Record<string, unknown>): void;
|
|
71
|
+
/**
|
|
72
|
+
* Add a file path manually (e.g. from git status diff)
|
|
73
|
+
*/
|
|
74
|
+
addTouchedFile(filePath: string, isModified?: boolean): void;
|
|
75
|
+
getModifiedFiles(): string[];
|
|
76
|
+
getInspectedFiles(): string[];
|
|
77
|
+
getTouchedFiles(): string[];
|
|
78
|
+
hasCommitted(): boolean;
|
|
79
|
+
getCommitMessages(): string[];
|
|
80
|
+
/**
|
|
81
|
+
* Check if any commit message explicitly references or resolves an anchor
|
|
82
|
+
*/
|
|
83
|
+
matchesCommit(anchor: Anchor): {
|
|
84
|
+
matched: boolean;
|
|
85
|
+
message?: string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
50
89
|
/**
|
|
51
90
|
* Execute physical verification test command for an anchor
|
|
52
91
|
*/
|
|
@@ -55,9 +94,18 @@ declare function runPhysicalVerification(anchor: Anchor, cwd: string): {
|
|
|
55
94
|
output: string;
|
|
56
95
|
};
|
|
57
96
|
/**
|
|
58
|
-
*
|
|
97
|
+
* Real-time autonomous settlement during active session:
|
|
98
|
+
* Triggered immediately when a non-error shell command runs `git commit` or a matching `verifyCommand`.
|
|
99
|
+
* Works for BOTH file-bound and file-less (`files: []`) anchors!
|
|
59
100
|
*/
|
|
60
|
-
declare function
|
|
101
|
+
declare function tryRealtimeAutoSettle(store: AnchorStore, observer: SessionTouchObserver, cwd?: string, executedCommand?: string): Array<{
|
|
102
|
+
anchor: Anchor;
|
|
103
|
+
reason: string;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* Generate settlement candidates based on files touched or commits made during the session
|
|
107
|
+
*/
|
|
108
|
+
declare function generateSettlementProposals(store: AnchorStore, touchedFiles: string[], cwd?: string, observer?: SessionTouchObserver): SettlementProposal[];
|
|
61
109
|
/**
|
|
62
110
|
* Format settlement proposal into terminal-ready card
|
|
63
111
|
*/
|
|
@@ -82,6 +130,14 @@ declare function classifyAnchor(anchor: Anchor, now?: number): AnchorQuadrant;
|
|
|
82
130
|
* Group active anchors by cognitive quadrant
|
|
83
131
|
*/
|
|
84
132
|
declare function groupAnchorsByQuadrant(anchors: Anchor[], now?: number): GroupedAnchors;
|
|
133
|
+
/**
|
|
134
|
+
* Sort unfinished anchors into a single compact, priority-driven engineering list:
|
|
135
|
+
* 1. Active before Sleeping (quiet)
|
|
136
|
+
* 2. Matching current workspace before other workspaces
|
|
137
|
+
* 3. Due Today / Overdue before open-ended
|
|
138
|
+
* 4. Priority (P0 -> P1 -> P2)
|
|
139
|
+
*/
|
|
140
|
+
declare function sortAnchorsForDisplay(anchors: Anchor[], cwd?: string, now?: number): Anchor[];
|
|
85
141
|
/**
|
|
86
142
|
* Strip ANSI escape codes from string
|
|
87
143
|
*/
|
|
@@ -134,7 +190,7 @@ declare function formatRelativeTime(timestamp: number, now?: number): string;
|
|
|
134
190
|
*/
|
|
135
191
|
declare function formatRemainingTtl(anchor: Anchor, now?: number): string | undefined;
|
|
136
192
|
/**
|
|
137
|
-
* Format origin
|
|
193
|
+
* Format origin workspace / project label concisely
|
|
138
194
|
*/
|
|
139
195
|
declare function formatOrigin(anchor: Anchor | string): string;
|
|
140
196
|
/**
|
|
@@ -148,39 +204,10 @@ declare function updateAnchorStatusBar(ctx: ExtensionContext, store: AnchorStore
|
|
|
148
204
|
*/
|
|
149
205
|
declare function updateStartupBanner(ctx: ExtensionContext, store: AnchorStore): void;
|
|
150
206
|
/**
|
|
151
|
-
* Clean,
|
|
207
|
+
* Clean, flat priority-sorted engineering checklist (includes both active and quiet/sleeping tasks so nothing is ever lost)
|
|
152
208
|
*/
|
|
153
209
|
declare function openAnchorDashboard(ctx: ExtensionContext, store: AnchorStore): Promise<void>;
|
|
154
210
|
|
|
155
|
-
declare class SessionTouchObserver {
|
|
156
|
-
private touchedFiles;
|
|
157
|
-
private modifiedFiles;
|
|
158
|
-
private inspectedFiles;
|
|
159
|
-
private committed;
|
|
160
|
-
private commitMessages;
|
|
161
|
-
clear(): void;
|
|
162
|
-
/**
|
|
163
|
-
* Observe and record a tool call invocation, separating inspection from mutation
|
|
164
|
-
*/
|
|
165
|
-
recordToolCall(toolName: string, input: Record<string, unknown>): void;
|
|
166
|
-
/**
|
|
167
|
-
* Add a file path manually (e.g. from git status diff)
|
|
168
|
-
*/
|
|
169
|
-
addTouchedFile(filePath: string, isModified?: boolean): void;
|
|
170
|
-
getModifiedFiles(): string[];
|
|
171
|
-
getInspectedFiles(): string[];
|
|
172
|
-
getTouchedFiles(): string[];
|
|
173
|
-
hasCommitted(): boolean;
|
|
174
|
-
getCommitMessages(): string[];
|
|
175
|
-
/**
|
|
176
|
-
* Check if any commit message explicitly references or resolves an anchor
|
|
177
|
-
*/
|
|
178
|
-
matchesCommit(anchor: Anchor): {
|
|
179
|
-
matched: boolean;
|
|
180
|
-
message?: string;
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
211
|
/**
|
|
185
212
|
* Universal multi-language comment generator.
|
|
186
213
|
* Maps file extension to safe comment syntax.
|
|
@@ -245,4 +272,4 @@ declare class AnchorProtocol {
|
|
|
245
272
|
};
|
|
246
273
|
}
|
|
247
274
|
|
|
248
|
-
export { type AgentAdapter, Anchor, AnchorProtocol, type AnchorQuadrant, AnchorStatus, AnchorStore, COMMENT_FORMATS, type DecayEvaluation, type GroupedAnchors, SessionTouchObserver, SettlementProposal, type SweepResult, TouchMatchResult, classifyAnchor, evaluateAnchorDecay, findMatchedAnchors, formatCreationTime, formatOrigin, formatRelativeTime, formatRemainingTtl, formatSettlementCard, formatTargetDate, generateSettlementProposals, getDisplayWidth, globToRegExp, groupAnchorsByQuadrant, hasActiveUI, isSameOrSubPath, makeSafeTaskAnnotation, matchAnchorAgainstTouchedFiles, normalizePath, openAnchorDashboard, padToWidth, renderActiveAnchorsContext, renderColdStartAnchorsContext, runPhysicalVerification, stripAnsi, sweepStore, truncateToWidth, updateAnchorStatusBar, updateStartupBanner };
|
|
275
|
+
export { type AgentAdapter, Anchor, AnchorProtocol, type AnchorQuadrant, AnchorStatus, AnchorStore, COMMENT_FORMATS, type DecayEvaluation, type GroupedAnchors, SessionTouchObserver, SettlementProposal, type SweepResult, TouchMatchResult, classifyAnchor, evaluateAnchorDecay, extractPathsFromText, findMatchedAnchors, formatCreationTime, formatOrigin, formatRelativeTime, formatRemainingTtl, formatSettlementCard, formatTargetDate, generateSettlementProposals, getDisplayWidth, globToRegExp, groupAnchorsByQuadrant, hasActiveUI, isAnchorTargetingWorkspace, isSameOrSubPath, makeSafeTaskAnnotation, matchAnchorAgainstTouchedFiles, normalizePath, openAnchorDashboard, padToWidth, renderActiveAnchorsContext, renderColdStartAnchorsContext, runPhysicalVerification, sortAnchorsForDisplay, stripAnsi, sweepStore, truncateToWidth, tryRealtimeAutoSettle, updateAnchorStatusBar, updateStartupBanner };
|