pi-anchor 0.2.2 → 0.2.4
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 +16 -5
- package/README_zh.md +16 -5
- package/assets/hero-en.svg +108 -0
- package/assets/hero-zh.svg +108 -0
- package/assets/hero.svg +108 -0
- package/bin/anchor.js +4 -4
- package/dist/extension.cjs +114 -19
- package/dist/extension.d.cts +16 -1
- package/dist/extension.d.ts +16 -1
- package/dist/extension.js +111 -19
- package/dist/index.d.cts +3 -209
- package/dist/index.d.ts +3 -209
- package/dist/store-BNKjzhS_.d.cts +209 -0
- package/dist/store-BNKjzhS_.d.ts +209 -0
- package/package.json +22 -6
- package/src/extension.ts +137 -24
package/dist/extension.d.cts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { A as AnchorStore } from './store-BNKjzhS_.cjs';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Parse inline `--due <date>` / `-d <date>` flags and `done`/`rm`/`close`/`settle` subcommands
|
|
6
|
+
* for `/anchor` and `/pin` slash commands.
|
|
7
|
+
*/
|
|
8
|
+
declare function parseAnchorCommandInput(raw: string, now?: number): {
|
|
9
|
+
mode: 'dashboard' | 'undo' | 'settle' | 'pin';
|
|
10
|
+
targetId?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
targetDate?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Resolve either an explicit anchor ID ('anc-2', '2') or a 1-based visual dashboard index ('01', '1')
|
|
16
|
+
*/
|
|
17
|
+
declare function resolveAnchorTarget(store: AnchorStore, idOrIndex: string, cwd?: string): string | undefined;
|
|
3
18
|
declare function export_default(pi: ExtensionAPI): void;
|
|
4
19
|
|
|
5
|
-
export { export_default as default };
|
|
20
|
+
export { export_default as default, parseAnchorCommandInput, resolveAnchorTarget };
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { A as AnchorStore } from './store-BNKjzhS_.js';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Parse inline `--due <date>` / `-d <date>` flags and `done`/`rm`/`close`/`settle` subcommands
|
|
6
|
+
* for `/anchor` and `/pin` slash commands.
|
|
7
|
+
*/
|
|
8
|
+
declare function parseAnchorCommandInput(raw: string, now?: number): {
|
|
9
|
+
mode: 'dashboard' | 'undo' | 'settle' | 'pin';
|
|
10
|
+
targetId?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
targetDate?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Resolve either an explicit anchor ID ('anc-2', '2') or a 1-based visual dashboard index ('01', '1')
|
|
16
|
+
*/
|
|
17
|
+
declare function resolveAnchorTarget(store: AnchorStore, idOrIndex: string, cwd?: string): string | undefined;
|
|
3
18
|
declare function export_default(pi: ExtensionAPI): void;
|
|
4
19
|
|
|
5
|
-
export { export_default as default };
|
|
20
|
+
export { export_default as default, parseAnchorCommandInput, resolveAnchorTarget };
|
package/dist/extension.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AnchorStore,
|
|
3
3
|
SessionTouchObserver,
|
|
4
|
+
detectTargetDate,
|
|
4
5
|
findMatchedAnchors,
|
|
5
6
|
generateSettlementProposals,
|
|
7
|
+
groupAnchorsByQuadrant,
|
|
6
8
|
hasActiveUI,
|
|
7
9
|
isSameOrSubPath,
|
|
8
10
|
makeSafeTaskAnnotation,
|
|
@@ -22,6 +24,55 @@ import { execSync } from "child_process";
|
|
|
22
24
|
import fs from "fs";
|
|
23
25
|
import path from "path";
|
|
24
26
|
var MUTATION_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "patch", "apply_diff", "create_file", "modify"]);
|
|
27
|
+
function parseAnchorCommandInput(raw, now = Date.now()) {
|
|
28
|
+
const trimmed = (raw || "").trim();
|
|
29
|
+
if (!trimmed || trimmed === "list" || trimmed === "ls") {
|
|
30
|
+
return { mode: "dashboard" };
|
|
31
|
+
}
|
|
32
|
+
if (trimmed === "undo") {
|
|
33
|
+
return { mode: "undo" };
|
|
34
|
+
}
|
|
35
|
+
const settleMatch = trimmed.match(/^(?:done|rm|close|settle)\s+(.+)$/i);
|
|
36
|
+
if (settleMatch) {
|
|
37
|
+
return {
|
|
38
|
+
mode: "settle",
|
|
39
|
+
targetId: settleMatch[1].trim().replace(/^#/, "")
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
let targetDate;
|
|
43
|
+
const tokens = trimmed.split(/\s+/);
|
|
44
|
+
const dueIdx = tokens.findIndex((t) => t === "--due" || t === "-d");
|
|
45
|
+
if (dueIdx !== -1 && tokens[dueIdx + 1]) {
|
|
46
|
+
const rawDue = tokens[dueIdx + 1];
|
|
47
|
+
targetDate = detectTargetDate(rawDue, now) || rawDue;
|
|
48
|
+
tokens.splice(dueIdx, 2);
|
|
49
|
+
}
|
|
50
|
+
const cleanTitle = tokens.join(" ").trim();
|
|
51
|
+
return {
|
|
52
|
+
mode: "pin",
|
|
53
|
+
title: cleanTitle || trimmed,
|
|
54
|
+
targetDate
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function resolveAnchorTarget(store, idOrIndex, cwd) {
|
|
58
|
+
const clean = idOrIndex.trim().replace(/^#/, "");
|
|
59
|
+
if (/^\d+$/.test(clean)) {
|
|
60
|
+
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;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const direct = store.get(clean);
|
|
74
|
+
return direct?.id;
|
|
75
|
+
}
|
|
25
76
|
function extension_default(pi) {
|
|
26
77
|
const store = new AnchorStore();
|
|
27
78
|
const observer = new SessionTouchObserver();
|
|
@@ -33,7 +84,7 @@ function extension_default(pi) {
|
|
|
33
84
|
preflightFailures.clear();
|
|
34
85
|
const sweep = sweepStore(store);
|
|
35
86
|
if (sweep.transitionedToSleeping.length > 0) {
|
|
36
|
-
ctx.ui.notify(
|
|
87
|
+
ctx.ui.notify(`\u2316 Anchor: ${sweep.transitionedToSleeping.length} inactive tasks transitioned to sleep`, "info");
|
|
37
88
|
}
|
|
38
89
|
const activeAnchors = store.list({ cwd: ctx.cwd }).filter((a) => a.status === "active");
|
|
39
90
|
for (const a of activeAnchors) {
|
|
@@ -45,7 +96,7 @@ function extension_default(pi) {
|
|
|
45
96
|
settledBy: "verification-test",
|
|
46
97
|
summary: `Physical pre-flight test passed: ${a.verifyCommand}`
|
|
47
98
|
});
|
|
48
|
-
ctx.ui.notify(`\
|
|
99
|
+
ctx.ui.notify(`\u2316 Verification passed! Auto-settled #${a.id}: "${a.title}"`, "info");
|
|
49
100
|
} else {
|
|
50
101
|
preflightFailures.set(a.id, res.output.slice(-250));
|
|
51
102
|
}
|
|
@@ -377,14 +428,14 @@ Mark as completed and archive?`
|
|
|
377
428
|
}
|
|
378
429
|
});
|
|
379
430
|
pi.registerCommand("anchor", {
|
|
380
|
-
description: "Inspect
|
|
431
|
+
description: "Inspect, pin, or settle active anchors (e.g. /anchor, /anchor done 1, /anchor undo)",
|
|
381
432
|
handler: async (args, ctx) => {
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
433
|
+
const parsed = parseAnchorCommandInput(args);
|
|
434
|
+
if (parsed.mode === "dashboard") {
|
|
384
435
|
await openAnchorDashboard(ctx, store);
|
|
385
436
|
return;
|
|
386
437
|
}
|
|
387
|
-
if (
|
|
438
|
+
if (parsed.mode === "undo") {
|
|
388
439
|
try {
|
|
389
440
|
const restored = store.undoSettle();
|
|
390
441
|
ctx.ui.notify(`\u2316 Restored #${restored.id}: "${restored.title}"`, "info");
|
|
@@ -394,29 +445,68 @@ Mark as completed and archive?`
|
|
|
394
445
|
}
|
|
395
446
|
return;
|
|
396
447
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
448
|
+
if (parsed.mode === "settle" && parsed.targetId) {
|
|
449
|
+
const resolvedId = resolveAnchorTarget(store, parsed.targetId, ctx.cwd);
|
|
450
|
+
if (!resolvedId) {
|
|
451
|
+
ctx.ui.notify(`\u2316 Anchor not found: ${parsed.targetId}`, "error");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const settled = store.settle(resolvedId, { settledBy: "manual-command" });
|
|
455
|
+
ctx.ui.notify(`\u2316 Settled #${settled.id}: "${settled.title}" (/anchor undo to revert)`, "info");
|
|
402
456
|
updateAnchorStatusBar(ctx, store);
|
|
403
457
|
return;
|
|
404
458
|
}
|
|
405
|
-
const
|
|
406
|
-
|
|
459
|
+
const rawTrimmed = (args || "").trim();
|
|
460
|
+
if (/^(?:#?anc-\d+|#\d+)$/i.test(rawTrimmed)) {
|
|
461
|
+
const resolvedId = resolveAnchorTarget(store, rawTrimmed, ctx.cwd);
|
|
462
|
+
if (resolvedId) {
|
|
463
|
+
const settled = store.settle(resolvedId, { settledBy: "manual-command" });
|
|
464
|
+
ctx.ui.notify(`\u2316 Settled #${settled.id}: "${settled.title}" (/anchor undo to revert)`, "info");
|
|
465
|
+
updateAnchorStatusBar(ctx, store);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const anc = store.create({
|
|
470
|
+
title: parsed.title || rawTrimmed,
|
|
471
|
+
targetDate: parsed.targetDate,
|
|
472
|
+
cwd: ctx.cwd
|
|
473
|
+
});
|
|
474
|
+
ctx.ui.notify(`\u2316 Pinned #${anc.id}: "${anc.title}"${anc.targetDate ? ` (due ${anc.targetDate})` : ""}`, "info");
|
|
407
475
|
updateAnchorStatusBar(ctx, store);
|
|
408
476
|
}
|
|
409
477
|
});
|
|
410
478
|
pi.registerCommand("pin", {
|
|
411
|
-
description: "
|
|
479
|
+
description: "Pin a task across sessions (e.g. /pin Refactor auth --due tomorrow)",
|
|
412
480
|
handler: async (args, ctx) => {
|
|
413
|
-
const
|
|
414
|
-
if (!title) {
|
|
481
|
+
const parsed = parseAnchorCommandInput(args);
|
|
482
|
+
if (parsed.mode === "dashboard" || !parsed.title) {
|
|
415
483
|
await openAnchorDashboard(ctx, store);
|
|
416
484
|
return;
|
|
417
485
|
}
|
|
418
|
-
const anc = store.create({
|
|
419
|
-
|
|
486
|
+
const anc = store.create({
|
|
487
|
+
title: parsed.title,
|
|
488
|
+
targetDate: parsed.targetDate,
|
|
489
|
+
cwd: ctx.cwd
|
|
490
|
+
});
|
|
491
|
+
ctx.ui.notify(`\u2316 Pinned #${anc.id}: "${anc.title}"${anc.targetDate ? ` (due ${anc.targetDate})` : ""}`, "info");
|
|
492
|
+
updateAnchorStatusBar(ctx, store);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
pi.registerCommand("done", {
|
|
496
|
+
description: "Complete an active anchor by row number or ID (e.g. /done 1)",
|
|
497
|
+
handler: async (args, ctx) => {
|
|
498
|
+
const target = (args || "").trim();
|
|
499
|
+
if (!target) {
|
|
500
|
+
await openAnchorDashboard(ctx, store);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const resolvedId = resolveAnchorTarget(store, target, ctx.cwd);
|
|
504
|
+
if (!resolvedId) {
|
|
505
|
+
ctx.ui.notify(`\u2316 Anchor not found: ${target}`, "error");
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const settled = store.settle(resolvedId, { settledBy: "manual-command" });
|
|
509
|
+
ctx.ui.notify(`\u2316 Settled #${settled.id}: "${settled.title}" (/anchor undo to revert)`, "info");
|
|
420
510
|
updateAnchorStatusBar(ctx, store);
|
|
421
511
|
}
|
|
422
512
|
});
|
|
@@ -428,5 +518,7 @@ Mark as completed and archive?`
|
|
|
428
518
|
});
|
|
429
519
|
}
|
|
430
520
|
export {
|
|
431
|
-
extension_default as default
|
|
521
|
+
extension_default as default,
|
|
522
|
+
parseAnchorCommandInput,
|
|
523
|
+
resolveAnchorTarget
|
|
432
524
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,213 +1,7 @@
|
|
|
1
|
+
import { a as AnchorStatus, b as Anchor, A as AnchorStore, T as TouchMatchResult, S as SettlementProposal } from './store-BNKjzhS_.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-BNKjzhS_.cjs';
|
|
1
3
|
import { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* Anchor Data Contracts & Type Definitions
|
|
5
|
-
* Single Global Source of Truth with Zero Workspace Contamination.
|
|
6
|
-
*/
|
|
7
|
-
type AnchorStatus = 'active' | 'sleeping' | 'settled' | 'graveyard';
|
|
8
|
-
type AnchorPriority = 'p0' | 'p1' | 'p2';
|
|
9
|
-
type AnchorDurability = 'ephemeral' | 'durable';
|
|
10
|
-
interface AnchorDecayPolicy {
|
|
11
|
-
/** Days before an untouched active anchor transitions to sleeping */
|
|
12
|
-
activeDays: number;
|
|
13
|
-
/** Days an anchor remains sleeping before dropping to graveyard */
|
|
14
|
-
sleepDays: number;
|
|
15
|
-
/** Total maximum days before permanent graveyard sweep (9999 = never) */
|
|
16
|
-
graveyardDays: number;
|
|
17
|
-
}
|
|
18
|
-
interface AnchorEvidence {
|
|
19
|
-
settledAt?: number;
|
|
20
|
-
settledBy?: 'one-tap-settlement' | 'manual-command' | 'verification-test';
|
|
21
|
-
commitHash?: string;
|
|
22
|
-
summary?: string;
|
|
23
|
-
touchedFiles?: string[];
|
|
24
|
-
}
|
|
25
|
-
interface Anchor {
|
|
26
|
-
/** Unique sequential identifier, e.g., 'anc-1' */
|
|
27
|
-
id: string;
|
|
28
|
-
/** Short imperative title of the task */
|
|
29
|
-
title: string;
|
|
30
|
-
/** Optional detailed context or notes */
|
|
31
|
-
description?: string;
|
|
32
|
-
/** Task priority */
|
|
33
|
-
priority: AnchorPriority;
|
|
34
|
-
/** Current lifecycle status */
|
|
35
|
-
status: AnchorStatus;
|
|
36
|
-
/** Durability tier: ephemeral (short-lived reminder, 48h TTL) or durable (long-term architecture vision) */
|
|
37
|
-
durability: AnchorDurability;
|
|
38
|
-
/** Canonical project name (e.g. 'PPT', 'X', or 'global') */
|
|
39
|
-
project: string;
|
|
40
|
-
/** Canonical root directory where the task was anchored (empty string for global tasks) */
|
|
41
|
-
cwd: string;
|
|
42
|
-
/** Creation timestamp in ms */
|
|
43
|
-
createdAt: number;
|
|
44
|
-
/** Last update timestamp in ms */
|
|
45
|
-
updatedAt: number;
|
|
46
|
-
/** Last touched timestamp in ms (resets on file touch) */
|
|
47
|
-
lastTouchedAt: number;
|
|
48
|
-
/** Associated file paths or globs, e.g. ['src/auth/*.ts'] */
|
|
49
|
-
files: string[];
|
|
50
|
-
/** Expected completion date in format 'YYYY-MM-DD' (e.g. '2026-09-24') */
|
|
51
|
-
targetDate?: string;
|
|
52
|
-
/** Optional shell command for automated physical verification (e.g. 'npm test') */
|
|
53
|
-
verifyCommand?: string;
|
|
54
|
-
/** Topic or domain tags, e.g. ['auth', 'security'] */
|
|
55
|
-
tags: string[];
|
|
56
|
-
/** Optional recurrence pattern: 'daily' for recurring daily habits */
|
|
57
|
-
recurrence?: 'daily';
|
|
58
|
-
/** Last completed date string 'YYYY-MM-DD' for recurring tasks */
|
|
59
|
-
lastCompletedDate?: string;
|
|
60
|
-
/** Decay policy overrides */
|
|
61
|
-
decay: AnchorDecayPolicy;
|
|
62
|
-
/** Settlement evidence metadata upon closure */
|
|
63
|
-
evidence?: AnchorEvidence;
|
|
64
|
-
}
|
|
65
|
-
interface AnchorStoreState {
|
|
66
|
-
version: number;
|
|
67
|
-
anchors: Anchor[];
|
|
68
|
-
lastSweepAt: number;
|
|
69
|
-
nextId?: number;
|
|
70
|
-
}
|
|
71
|
-
interface TouchMatchResult {
|
|
72
|
-
anchor: Anchor;
|
|
73
|
-
score: number;
|
|
74
|
-
matchedFiles: string[];
|
|
75
|
-
reason: 'exact-file' | 'dir-prefix' | 'tag-keyword';
|
|
76
|
-
}
|
|
77
|
-
interface SettlementProposal {
|
|
78
|
-
anchor: Anchor;
|
|
79
|
-
matchedFiles: string[];
|
|
80
|
-
reason: string;
|
|
81
|
-
recommendedAction: 'settle' | 'defer';
|
|
82
|
-
}
|
|
83
|
-
declare const EPHEMERAL_DECAY_POLICY: AnchorDecayPolicy;
|
|
84
|
-
declare const DURABLE_DECAY_POLICY: AnchorDecayPolicy;
|
|
85
|
-
declare const DEFAULT_DECAY_POLICY: AnchorDecayPolicy;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Detect whether a task is a short-term ephemeral reminder or a long-term architectural vision
|
|
89
|
-
*/
|
|
90
|
-
declare function detectDurability(title: string): AnchorDurability;
|
|
91
|
-
/**
|
|
92
|
-
* Get custom ephemeral decay policy based on temporal span (e.g. 后天, 大后天)
|
|
93
|
-
*/
|
|
94
|
-
declare function getEphemeralDecayPolicy(title: string): AnchorDecayPolicy;
|
|
95
|
-
/**
|
|
96
|
-
* Detect expected completion date from title keywords or explicit date string, returning 'YYYY-MM-DD'
|
|
97
|
-
*/
|
|
98
|
-
declare function detectTargetDate(titleOrDate: string, now?: number): string | undefined;
|
|
99
|
-
/**
|
|
100
|
-
* Detect recurring patterns, e.g. daily habits
|
|
101
|
-
*/
|
|
102
|
-
declare function detectRecurrence(title: string): 'daily' | undefined;
|
|
103
|
-
/**
|
|
104
|
-
* Get current date string 'YYYY-MM-DD' in local timezone
|
|
105
|
-
*/
|
|
106
|
-
declare function getTodayDateString(timestamp?: number): string;
|
|
107
|
-
/**
|
|
108
|
-
* Resolve the default storage directory for Anchor (~/.anchor).
|
|
109
|
-
* Hierarchy:
|
|
110
|
-
* 1. Process environment: ANCHOR_DIR
|
|
111
|
-
* 2. Canonical user-global store: ~/.anchor/
|
|
112
|
-
* 3. Legacy Pi harness fallback: ~/.pi/agent/anchors/ (migrates automatically if present)
|
|
113
|
-
*/
|
|
114
|
-
declare function getDefaultStorageDir(): string;
|
|
115
|
-
/**
|
|
116
|
-
* Acquire exclusive lockfile using atomic 'wx' file creation (zero external dependencies).
|
|
117
|
-
* Features:
|
|
118
|
-
* - 0 async infection (100% synchronous)
|
|
119
|
-
* - Automatic stale lock eviction (>5000ms or dead PID)
|
|
120
|
-
* - Exponential backoff with jitter
|
|
121
|
-
*/
|
|
122
|
-
declare function acquireSyncLock(lockPath: string, maxWaitMs?: number, staleTimeoutMs?: number): () => void;
|
|
123
|
-
/**
|
|
124
|
-
* Windows-tolerant atomic rename with exponential spin-retry to combat NTFS EBUSY/EPERM file locks
|
|
125
|
-
*/
|
|
126
|
-
declare function atomicRenameWithRetry(tempPath: string, targetPath: string, maxAttempts?: number): void;
|
|
127
|
-
declare class AnchorStore {
|
|
128
|
-
readonly storageDir: string;
|
|
129
|
-
readonly statePath: string;
|
|
130
|
-
readonly archivePath: string;
|
|
131
|
-
readonly graveyardPath: string;
|
|
132
|
-
constructor(customStorageDir?: string);
|
|
133
|
-
get lockPath(): string;
|
|
134
|
-
/**
|
|
135
|
-
* Execute mutation within a cross-process exclusive lock
|
|
136
|
-
*/
|
|
137
|
-
withLock<T>(fn: () => T): T;
|
|
138
|
-
private ensureDirs;
|
|
139
|
-
/**
|
|
140
|
-
* Load store state. If corrupt or missing, returns safe default.
|
|
141
|
-
*/
|
|
142
|
-
loadState(): AnchorStoreState;
|
|
143
|
-
/**
|
|
144
|
-
* Atomically save store state via temp file + atomic rename with Windows NTFS spin-retry
|
|
145
|
-
*/
|
|
146
|
-
saveState(state: AnchorStoreState): void;
|
|
147
|
-
private generateId;
|
|
148
|
-
/**
|
|
149
|
-
* Create a new Anchor with automatic project and cwd tagging
|
|
150
|
-
*/
|
|
151
|
-
create(input: {
|
|
152
|
-
title: string;
|
|
153
|
-
description?: string;
|
|
154
|
-
priority?: AnchorPriority;
|
|
155
|
-
durability?: AnchorDurability;
|
|
156
|
-
recurrence?: 'daily';
|
|
157
|
-
cwd?: string;
|
|
158
|
-
project?: string;
|
|
159
|
-
targetDate?: string;
|
|
160
|
-
files?: string[];
|
|
161
|
-
tags?: string[];
|
|
162
|
-
verifyCommand?: string;
|
|
163
|
-
decay?: AnchorDecayPolicy;
|
|
164
|
-
}): Anchor;
|
|
165
|
-
private findAnchorIndex;
|
|
166
|
-
/**
|
|
167
|
-
* Get an anchor by ID (supports 'anc-1', '#anc-1', '1', '01')
|
|
168
|
-
*/
|
|
169
|
-
get(id: string): Anchor | undefined;
|
|
170
|
-
/**
|
|
171
|
-
* List anchors. By default, if cwd is provided, only returns anchors
|
|
172
|
-
* belonging to this cwd or marked as global (''). Pass all: true for global view.
|
|
173
|
-
*/
|
|
174
|
-
list(filter?: {
|
|
175
|
-
status?: AnchorStatus;
|
|
176
|
-
priority?: AnchorPriority;
|
|
177
|
-
cwd?: string;
|
|
178
|
-
all?: boolean;
|
|
179
|
-
now?: number;
|
|
180
|
-
}): Anchor[];
|
|
181
|
-
/**
|
|
182
|
-
* Update an existing anchor
|
|
183
|
-
*/
|
|
184
|
-
update(id: string, patch: Partial<Omit<Anchor, 'id' | 'createdAt'>>): Anchor;
|
|
185
|
-
/**
|
|
186
|
-
* Touch an anchor to refresh its decay window and reactivate sleeping state
|
|
187
|
-
*/
|
|
188
|
-
touch(id: string, timestamp?: number): Anchor;
|
|
189
|
-
/**
|
|
190
|
-
* Settle an anchor (recurring daily habits complete for today and wake up tomorrow)
|
|
191
|
-
*/
|
|
192
|
-
settle(id: string, evidence?: AnchorEvidence, now?: number): Anchor;
|
|
193
|
-
/**
|
|
194
|
-
* Reverse/undo the last settled anchor, popping it from archive.jsonl back into state.json
|
|
195
|
-
*/
|
|
196
|
-
undoSettle(): Anchor;
|
|
197
|
-
/**
|
|
198
|
-
* Evict anchor to graveyard.jsonl upon total decay expiry
|
|
199
|
-
*/
|
|
200
|
-
dropToGraveyard(id: string, reason: string): Anchor;
|
|
201
|
-
getArchive(filter?: {
|
|
202
|
-
cwd?: string;
|
|
203
|
-
all?: boolean;
|
|
204
|
-
}): Anchor[];
|
|
205
|
-
getGraveyard(filter?: {
|
|
206
|
-
cwd?: string;
|
|
207
|
-
all?: boolean;
|
|
208
|
-
}): Anchor[];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
5
|
interface DecayEvaluation {
|
|
212
6
|
currentStatus: AnchorStatus;
|
|
213
7
|
nextStatus: AnchorStatus;
|
|
@@ -451,4 +245,4 @@ declare class AnchorProtocol {
|
|
|
451
245
|
};
|
|
452
246
|
}
|
|
453
247
|
|
|
454
|
-
export { type AgentAdapter,
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,213 +1,7 @@
|
|
|
1
|
+
import { a as AnchorStatus, b as Anchor, A as AnchorStore, T as TouchMatchResult, S as SettlementProposal } from './store-BNKjzhS_.js';
|
|
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-BNKjzhS_.js';
|
|
1
3
|
import { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* Anchor Data Contracts & Type Definitions
|
|
5
|
-
* Single Global Source of Truth with Zero Workspace Contamination.
|
|
6
|
-
*/
|
|
7
|
-
type AnchorStatus = 'active' | 'sleeping' | 'settled' | 'graveyard';
|
|
8
|
-
type AnchorPriority = 'p0' | 'p1' | 'p2';
|
|
9
|
-
type AnchorDurability = 'ephemeral' | 'durable';
|
|
10
|
-
interface AnchorDecayPolicy {
|
|
11
|
-
/** Days before an untouched active anchor transitions to sleeping */
|
|
12
|
-
activeDays: number;
|
|
13
|
-
/** Days an anchor remains sleeping before dropping to graveyard */
|
|
14
|
-
sleepDays: number;
|
|
15
|
-
/** Total maximum days before permanent graveyard sweep (9999 = never) */
|
|
16
|
-
graveyardDays: number;
|
|
17
|
-
}
|
|
18
|
-
interface AnchorEvidence {
|
|
19
|
-
settledAt?: number;
|
|
20
|
-
settledBy?: 'one-tap-settlement' | 'manual-command' | 'verification-test';
|
|
21
|
-
commitHash?: string;
|
|
22
|
-
summary?: string;
|
|
23
|
-
touchedFiles?: string[];
|
|
24
|
-
}
|
|
25
|
-
interface Anchor {
|
|
26
|
-
/** Unique sequential identifier, e.g., 'anc-1' */
|
|
27
|
-
id: string;
|
|
28
|
-
/** Short imperative title of the task */
|
|
29
|
-
title: string;
|
|
30
|
-
/** Optional detailed context or notes */
|
|
31
|
-
description?: string;
|
|
32
|
-
/** Task priority */
|
|
33
|
-
priority: AnchorPriority;
|
|
34
|
-
/** Current lifecycle status */
|
|
35
|
-
status: AnchorStatus;
|
|
36
|
-
/** Durability tier: ephemeral (short-lived reminder, 48h TTL) or durable (long-term architecture vision) */
|
|
37
|
-
durability: AnchorDurability;
|
|
38
|
-
/** Canonical project name (e.g. 'PPT', 'X', or 'global') */
|
|
39
|
-
project: string;
|
|
40
|
-
/** Canonical root directory where the task was anchored (empty string for global tasks) */
|
|
41
|
-
cwd: string;
|
|
42
|
-
/** Creation timestamp in ms */
|
|
43
|
-
createdAt: number;
|
|
44
|
-
/** Last update timestamp in ms */
|
|
45
|
-
updatedAt: number;
|
|
46
|
-
/** Last touched timestamp in ms (resets on file touch) */
|
|
47
|
-
lastTouchedAt: number;
|
|
48
|
-
/** Associated file paths or globs, e.g. ['src/auth/*.ts'] */
|
|
49
|
-
files: string[];
|
|
50
|
-
/** Expected completion date in format 'YYYY-MM-DD' (e.g. '2026-09-24') */
|
|
51
|
-
targetDate?: string;
|
|
52
|
-
/** Optional shell command for automated physical verification (e.g. 'npm test') */
|
|
53
|
-
verifyCommand?: string;
|
|
54
|
-
/** Topic or domain tags, e.g. ['auth', 'security'] */
|
|
55
|
-
tags: string[];
|
|
56
|
-
/** Optional recurrence pattern: 'daily' for recurring daily habits */
|
|
57
|
-
recurrence?: 'daily';
|
|
58
|
-
/** Last completed date string 'YYYY-MM-DD' for recurring tasks */
|
|
59
|
-
lastCompletedDate?: string;
|
|
60
|
-
/** Decay policy overrides */
|
|
61
|
-
decay: AnchorDecayPolicy;
|
|
62
|
-
/** Settlement evidence metadata upon closure */
|
|
63
|
-
evidence?: AnchorEvidence;
|
|
64
|
-
}
|
|
65
|
-
interface AnchorStoreState {
|
|
66
|
-
version: number;
|
|
67
|
-
anchors: Anchor[];
|
|
68
|
-
lastSweepAt: number;
|
|
69
|
-
nextId?: number;
|
|
70
|
-
}
|
|
71
|
-
interface TouchMatchResult {
|
|
72
|
-
anchor: Anchor;
|
|
73
|
-
score: number;
|
|
74
|
-
matchedFiles: string[];
|
|
75
|
-
reason: 'exact-file' | 'dir-prefix' | 'tag-keyword';
|
|
76
|
-
}
|
|
77
|
-
interface SettlementProposal {
|
|
78
|
-
anchor: Anchor;
|
|
79
|
-
matchedFiles: string[];
|
|
80
|
-
reason: string;
|
|
81
|
-
recommendedAction: 'settle' | 'defer';
|
|
82
|
-
}
|
|
83
|
-
declare const EPHEMERAL_DECAY_POLICY: AnchorDecayPolicy;
|
|
84
|
-
declare const DURABLE_DECAY_POLICY: AnchorDecayPolicy;
|
|
85
|
-
declare const DEFAULT_DECAY_POLICY: AnchorDecayPolicy;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Detect whether a task is a short-term ephemeral reminder or a long-term architectural vision
|
|
89
|
-
*/
|
|
90
|
-
declare function detectDurability(title: string): AnchorDurability;
|
|
91
|
-
/**
|
|
92
|
-
* Get custom ephemeral decay policy based on temporal span (e.g. 后天, 大后天)
|
|
93
|
-
*/
|
|
94
|
-
declare function getEphemeralDecayPolicy(title: string): AnchorDecayPolicy;
|
|
95
|
-
/**
|
|
96
|
-
* Detect expected completion date from title keywords or explicit date string, returning 'YYYY-MM-DD'
|
|
97
|
-
*/
|
|
98
|
-
declare function detectTargetDate(titleOrDate: string, now?: number): string | undefined;
|
|
99
|
-
/**
|
|
100
|
-
* Detect recurring patterns, e.g. daily habits
|
|
101
|
-
*/
|
|
102
|
-
declare function detectRecurrence(title: string): 'daily' | undefined;
|
|
103
|
-
/**
|
|
104
|
-
* Get current date string 'YYYY-MM-DD' in local timezone
|
|
105
|
-
*/
|
|
106
|
-
declare function getTodayDateString(timestamp?: number): string;
|
|
107
|
-
/**
|
|
108
|
-
* Resolve the default storage directory for Anchor (~/.anchor).
|
|
109
|
-
* Hierarchy:
|
|
110
|
-
* 1. Process environment: ANCHOR_DIR
|
|
111
|
-
* 2. Canonical user-global store: ~/.anchor/
|
|
112
|
-
* 3. Legacy Pi harness fallback: ~/.pi/agent/anchors/ (migrates automatically if present)
|
|
113
|
-
*/
|
|
114
|
-
declare function getDefaultStorageDir(): string;
|
|
115
|
-
/**
|
|
116
|
-
* Acquire exclusive lockfile using atomic 'wx' file creation (zero external dependencies).
|
|
117
|
-
* Features:
|
|
118
|
-
* - 0 async infection (100% synchronous)
|
|
119
|
-
* - Automatic stale lock eviction (>5000ms or dead PID)
|
|
120
|
-
* - Exponential backoff with jitter
|
|
121
|
-
*/
|
|
122
|
-
declare function acquireSyncLock(lockPath: string, maxWaitMs?: number, staleTimeoutMs?: number): () => void;
|
|
123
|
-
/**
|
|
124
|
-
* Windows-tolerant atomic rename with exponential spin-retry to combat NTFS EBUSY/EPERM file locks
|
|
125
|
-
*/
|
|
126
|
-
declare function atomicRenameWithRetry(tempPath: string, targetPath: string, maxAttempts?: number): void;
|
|
127
|
-
declare class AnchorStore {
|
|
128
|
-
readonly storageDir: string;
|
|
129
|
-
readonly statePath: string;
|
|
130
|
-
readonly archivePath: string;
|
|
131
|
-
readonly graveyardPath: string;
|
|
132
|
-
constructor(customStorageDir?: string);
|
|
133
|
-
get lockPath(): string;
|
|
134
|
-
/**
|
|
135
|
-
* Execute mutation within a cross-process exclusive lock
|
|
136
|
-
*/
|
|
137
|
-
withLock<T>(fn: () => T): T;
|
|
138
|
-
private ensureDirs;
|
|
139
|
-
/**
|
|
140
|
-
* Load store state. If corrupt or missing, returns safe default.
|
|
141
|
-
*/
|
|
142
|
-
loadState(): AnchorStoreState;
|
|
143
|
-
/**
|
|
144
|
-
* Atomically save store state via temp file + atomic rename with Windows NTFS spin-retry
|
|
145
|
-
*/
|
|
146
|
-
saveState(state: AnchorStoreState): void;
|
|
147
|
-
private generateId;
|
|
148
|
-
/**
|
|
149
|
-
* Create a new Anchor with automatic project and cwd tagging
|
|
150
|
-
*/
|
|
151
|
-
create(input: {
|
|
152
|
-
title: string;
|
|
153
|
-
description?: string;
|
|
154
|
-
priority?: AnchorPriority;
|
|
155
|
-
durability?: AnchorDurability;
|
|
156
|
-
recurrence?: 'daily';
|
|
157
|
-
cwd?: string;
|
|
158
|
-
project?: string;
|
|
159
|
-
targetDate?: string;
|
|
160
|
-
files?: string[];
|
|
161
|
-
tags?: string[];
|
|
162
|
-
verifyCommand?: string;
|
|
163
|
-
decay?: AnchorDecayPolicy;
|
|
164
|
-
}): Anchor;
|
|
165
|
-
private findAnchorIndex;
|
|
166
|
-
/**
|
|
167
|
-
* Get an anchor by ID (supports 'anc-1', '#anc-1', '1', '01')
|
|
168
|
-
*/
|
|
169
|
-
get(id: string): Anchor | undefined;
|
|
170
|
-
/**
|
|
171
|
-
* List anchors. By default, if cwd is provided, only returns anchors
|
|
172
|
-
* belonging to this cwd or marked as global (''). Pass all: true for global view.
|
|
173
|
-
*/
|
|
174
|
-
list(filter?: {
|
|
175
|
-
status?: AnchorStatus;
|
|
176
|
-
priority?: AnchorPriority;
|
|
177
|
-
cwd?: string;
|
|
178
|
-
all?: boolean;
|
|
179
|
-
now?: number;
|
|
180
|
-
}): Anchor[];
|
|
181
|
-
/**
|
|
182
|
-
* Update an existing anchor
|
|
183
|
-
*/
|
|
184
|
-
update(id: string, patch: Partial<Omit<Anchor, 'id' | 'createdAt'>>): Anchor;
|
|
185
|
-
/**
|
|
186
|
-
* Touch an anchor to refresh its decay window and reactivate sleeping state
|
|
187
|
-
*/
|
|
188
|
-
touch(id: string, timestamp?: number): Anchor;
|
|
189
|
-
/**
|
|
190
|
-
* Settle an anchor (recurring daily habits complete for today and wake up tomorrow)
|
|
191
|
-
*/
|
|
192
|
-
settle(id: string, evidence?: AnchorEvidence, now?: number): Anchor;
|
|
193
|
-
/**
|
|
194
|
-
* Reverse/undo the last settled anchor, popping it from archive.jsonl back into state.json
|
|
195
|
-
*/
|
|
196
|
-
undoSettle(): Anchor;
|
|
197
|
-
/**
|
|
198
|
-
* Evict anchor to graveyard.jsonl upon total decay expiry
|
|
199
|
-
*/
|
|
200
|
-
dropToGraveyard(id: string, reason: string): Anchor;
|
|
201
|
-
getArchive(filter?: {
|
|
202
|
-
cwd?: string;
|
|
203
|
-
all?: boolean;
|
|
204
|
-
}): Anchor[];
|
|
205
|
-
getGraveyard(filter?: {
|
|
206
|
-
cwd?: string;
|
|
207
|
-
all?: boolean;
|
|
208
|
-
}): Anchor[];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
5
|
interface DecayEvaluation {
|
|
212
6
|
currentStatus: AnchorStatus;
|
|
213
7
|
nextStatus: AnchorStatus;
|
|
@@ -451,4 +245,4 @@ declare class AnchorProtocol {
|
|
|
451
245
|
};
|
|
452
246
|
}
|
|
453
247
|
|
|
454
|
-
export { type AgentAdapter,
|
|
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 };
|