shadok-ai 0.14.7 → 0.14.9
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/context/ledger-reflex.md +15 -7
- package/context/ledger-skill/SKILL.md +42 -2
- package/context/ledger-skill/ledger-core.d.mts +34 -0
- package/context/ledger-skill/ledger-core.mjs +98 -6
- package/context/ledger-skill/ledger.mjs +39 -8
- package/dist/detect.d.ts +7 -1
- package/dist/detect.js +30 -2
- package/dist/detect.js.map +1 -1
- package/dist/ledger.d.ts +49 -8
- package/dist/ledger.js +139 -18
- package/dist/ledger.js.map +1 -1
- package/dist/server.js +24 -5
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/session.js.map +1 -1
- package/dist/tmux.d.ts +1 -1
- package/dist/tmux.js +2 -2
- package/dist/tmux.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +13 -1
package/context/ledger-reflex.md
CHANGED
|
@@ -18,19 +18,27 @@ Then let it steer you:
|
|
|
18
18
|
("de mémoire, à faire — dis-moi si c'est déjà réglé"), don't assert;
|
|
19
19
|
- rows printed under **"related rows — they may NOT answer your question"** are
|
|
20
20
|
leads, not an answer: you are still in the UNKNOWN case above;
|
|
21
|
-
- a **stale** record (days old) → confirm before acting
|
|
21
|
+
- a **stale** record (days old) → confirm before acting; the age shown is when
|
|
22
|
+
the claim was last CONFIRMED, not when the text was last edited;
|
|
23
|
+
- a row tagged **`· fact`** has no lifecycle — `current` or `stale`, never
|
|
24
|
+
"done": don't read its status as a position in open → resolved, and don't
|
|
25
|
+
close it because surrounding work finished. A **`· decision`** stands until
|
|
26
|
+
something supersedes it.
|
|
22
27
|
|
|
23
28
|
**You are also pushed the changes.** Ahead of each turn you are given — a user
|
|
24
29
|
message, or a scheduled/automated run — you may receive a
|
|
25
|
-
`⟦ledger · N updates
|
|
26
|
-
|
|
27
|
-
Treat it as current status
|
|
28
|
-
|
|
29
|
-
carry.
|
|
30
|
+
`⟦ledger · N updates⟧` block listing what sibling agents resolved or decided
|
|
31
|
+
since you last heard — `• [id] entity — status ↳ what happened`, all on one
|
|
32
|
+
line. Treat it as current status
|
|
33
|
+
(don't re-raise what it shows resolved). It is the push half; `check` is still
|
|
34
|
+
there for older or specific items the block didn't carry.
|
|
30
35
|
|
|
31
36
|
When you **resolve, decide, or change** something notable, record it so the next
|
|
32
37
|
agent doesn't redo it:
|
|
33
|
-
`node ~/.claude/skills/shadok-ledger/ledger.mjs record --entity "<name>" --status <resolved|open|in-progress
|
|
38
|
+
`node ~/.claude/skills/shadok-ledger/ledger.mjs record --entity "<name>" --status <resolved|open|in-progress> --note "<line>" --source "<PR#/who>"`.
|
|
39
|
+
Open the note with the sentence that says WHAT happened — it is what siblings get
|
|
40
|
+
pushed. For something with no lifecycle, pass `--kind fact` or `--kind decision`
|
|
41
|
+
instead of a status; to say you re-checked a row and nothing moved, `--verified`.
|
|
34
42
|
To update an item you saw in the block, quote its handle instead of retyping the
|
|
35
43
|
name: `… record --id <id> --status resolved --note "<line>"`.
|
|
36
44
|
|
|
@@ -37,7 +37,14 @@ you nothing.
|
|
|
37
37
|
the search found no row that answers you, only rows sharing some words. You
|
|
38
38
|
are still in the UNKNOWN case above. Read them as leads (they may point at the
|
|
39
39
|
right topic under another name), never as a status to assert.
|
|
40
|
-
- a **stale** row (many days old) → hedge and confirm before acting.
|
|
40
|
+
- a **stale** row (many days old) → hedge and confirm before acting. The age
|
|
41
|
+
shown is when the row's claim was last **confirmed**, not when its text was
|
|
42
|
+
last edited.
|
|
43
|
+
- a row tagged **`· fact`** has no lifecycle: it is `current` or `stale`, never
|
|
44
|
+
"done". Do not read its status as a position in open → resolved, and do not
|
|
45
|
+
close it because the work around it finished — it stays true.
|
|
46
|
+
- a row tagged **`· decision`** is a standing decision, not a phase of a task.
|
|
47
|
+
It holds until something supersedes it.
|
|
41
48
|
|
|
42
49
|
This fires on status-dependent claims (a bug being open, a PR unmerged, a task
|
|
43
50
|
undone, a campaign live). A **durable lesson/constraint** ("don't buy the
|
|
@@ -49,13 +56,46 @@ Record it so the next agent doesn't redo it:
|
|
|
49
56
|
|
|
50
57
|
```
|
|
51
58
|
node ~/.claude/skills/shadok-ledger/ledger.mjs record \
|
|
52
|
-
--entity "<name>" --status <resolved|open|in-progress
|
|
59
|
+
--entity "<name>" --status <resolved|open|in-progress> \
|
|
53
60
|
--note "<one line>" --source "<PR#, session, who>"
|
|
54
61
|
```
|
|
55
62
|
|
|
56
63
|
Re-recording the same entity **supersedes** its row (no duplicates). Log facts —
|
|
57
64
|
resolutions, decisions, state changes, launched actions — never chatter.
|
|
58
65
|
|
|
66
|
+
**Open the note with the one sentence that says what happened.** Its first
|
|
67
|
+
sentence is what siblings are shown in the `⟦ledger⟧` block pushed ahead of their
|
|
68
|
+
messages — the entity name alone cannot tell them what changed about it. Put the
|
|
69
|
+
consequence first, details after.
|
|
70
|
+
|
|
71
|
+
## Is it a task, a fact, or a decision?
|
|
72
|
+
|
|
73
|
+
A **task** has a lifecycle, so a status is the right thing to carry and closing
|
|
74
|
+
it is normal. That is the default and needs no flag. The other two do:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
… record --entity "nightly import window" --kind fact \
|
|
78
|
+
--note "Runs 03:00-03:40 Europe/Paris, after the extraction job."
|
|
79
|
+
… record --entity "ledger stays a state table" --kind decision \
|
|
80
|
+
--note "Supersede, not append, so size is bounded by live topics."
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`--kind` replaces `--status` for these two (they get `current` / `decided`), because
|
|
84
|
+
neither has phases to report. This matters: a **fact** marked `resolved` stops
|
|
85
|
+
being read by anything that filters on open work, while remaining perfectly true
|
|
86
|
+
— which is how a job finished on the 9th was still believed pending on the 12th.
|
|
87
|
+
|
|
88
|
+
When you re-check a row and nothing has changed, say so instead of rewriting it:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
… record --id <id> --verified
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
That refreshes its freshness only. It deliberately does **not** enter anyone's
|
|
95
|
+
pushed block — "I looked, nothing moved" is not news. Conversely, editing a note
|
|
96
|
+
does **not** refresh freshness: only a status (or `--verified`) counts as having
|
|
97
|
+
looked.
|
|
98
|
+
|
|
59
99
|
Every row has a short **id** (shown as `[id]` in `check`/`list` and in the ledger
|
|
60
100
|
block pushed ahead of your messages). To update a row you already saw, quote its
|
|
61
101
|
id instead of retyping the name — no risk of a typo forking a second row:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Types for the skill's pure core, so the SERVER can import the one source of
|
|
2
|
+
// the ledger's logic instead of growing a second copy of it (CLAUDE.md: this
|
|
3
|
+
// file is the single source, no duplication to drift). The core itself stays
|
|
4
|
+
// plain .mjs — it is copied verbatim into ~/.claude/skills/ at every boot and
|
|
5
|
+
// must run with no build step of its own.
|
|
6
|
+
export interface LedgerCoreRow {
|
|
7
|
+
id?: string;
|
|
8
|
+
entity: string;
|
|
9
|
+
status: string;
|
|
10
|
+
kind?: string;
|
|
11
|
+
note?: string;
|
|
12
|
+
source?: string;
|
|
13
|
+
updatedAt?: number;
|
|
14
|
+
verifiedAt?: number;
|
|
15
|
+
}
|
|
16
|
+
export type LedgerKind = "task" | "fact" | "decision";
|
|
17
|
+
export const KINDS: LedgerKind[];
|
|
18
|
+
export function normEntity(s: unknown): string;
|
|
19
|
+
export function normKind(k: unknown): LedgerKind | null;
|
|
20
|
+
export function rowKind(entry: unknown): LedgerKind;
|
|
21
|
+
export function resolveId<T extends LedgerCoreRow>(entries: T[], idOrPrefix: unknown): T | null;
|
|
22
|
+
export function upsertEntry<T extends LedgerCoreRow>(
|
|
23
|
+
entries: T[],
|
|
24
|
+
patch: Partial<LedgerCoreRow> & { verified?: boolean },
|
|
25
|
+
now: number,
|
|
26
|
+
newId?: string,
|
|
27
|
+
): T[];
|
|
28
|
+
export function tokenize(s: unknown): string[];
|
|
29
|
+
export function searchEntries<T extends LedgerCoreRow>(
|
|
30
|
+
entries: T[],
|
|
31
|
+
query: string,
|
|
32
|
+
opts?: { strong?: number; weak?: number },
|
|
33
|
+
): { strong: T[]; weak: T[] };
|
|
34
|
+
export function ageDays(entry: { updatedAt?: number; verifiedAt?: number }, now: number): number;
|
|
@@ -26,6 +26,52 @@ export function resolveId(entries, idOrPrefix) {
|
|
|
26
26
|
return pre.length === 1 ? pre[0] : null;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/* --------------------------------------------------------------------- *
|
|
30
|
+
* A row's KIND: a task, a fact, or a decision.
|
|
31
|
+
*
|
|
32
|
+
* The table used to treat all three identically, and they are not the same
|
|
33
|
+
* object. A TASK has a lifecycle, so a status is the right thing to carry and
|
|
34
|
+
* closing it is normal. A FACT has no lifecycle — only a date on which someone
|
|
35
|
+
* last confirmed it — so closing it makes it DISAPPEAR WHILE IT STAYS TRUE,
|
|
36
|
+
* which is how a finished job was still believed pending three days later. And
|
|
37
|
+
* `decided` was never a phase of the open → in-progress → resolved lifecycle it
|
|
38
|
+
* sat next to: it is a kind wearing a status's clothes.
|
|
39
|
+
*
|
|
40
|
+
* Design: docs/superpowers/specs/2026-10-13-ledger-kind-and-headline.md
|
|
41
|
+
* --------------------------------------------------------------------- */
|
|
42
|
+
|
|
43
|
+
export const KINDS = ["task", "fact", "decision"];
|
|
44
|
+
|
|
45
|
+
/** The status a kind with NO lifecycle gets when none is given, so an agent is
|
|
46
|
+
* never pushed into inventing a word for a thing that has no phases. Keeping
|
|
47
|
+
* `status` always present is what leaves every existing reader — the pushed
|
|
48
|
+
* delta, the GUI viewer, a consumer's recap filter — working untouched. */
|
|
49
|
+
const KIND_STATUS = { fact: "current", decision: "decided" };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Validate a `--kind`: one of KINDS, or null when absent. An unknown one THROWS
|
|
53
|
+
* rather than being stored — a typo that silently became a fourth kind would be
|
|
54
|
+
* invisible for exactly as long as nobody went looking.
|
|
55
|
+
*/
|
|
56
|
+
export function normKind(k) {
|
|
57
|
+
const s = String(k ?? "").trim().toLowerCase();
|
|
58
|
+
if (!s) return null;
|
|
59
|
+
if (!KINDS.includes(s)) throw new Error(`unknown kind "${k}" — use one of: ${KINDS.join(", ")}`);
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* What kind a row IS — read, never written back. An absent `kind` reads as
|
|
65
|
+
* `decision` when the status is `decided` (that is what the word already meant)
|
|
66
|
+
* and as `task` otherwise, so the rows recorded before this existed keep their
|
|
67
|
+
* bytes and still render correctly. Assert only what you saw.
|
|
68
|
+
*/
|
|
69
|
+
export function rowKind(entry) {
|
|
70
|
+
const k = String(entry?.kind ?? "").trim().toLowerCase();
|
|
71
|
+
if (KINDS.includes(k)) return k;
|
|
72
|
+
return normEntity(entry?.status) === "decided" ? "decision" : "task";
|
|
73
|
+
}
|
|
74
|
+
|
|
29
75
|
/**
|
|
30
76
|
* Upsert a row, two ways:
|
|
31
77
|
* - `patch.id` present → UPDATE that exact row in place (resolved via resolveId),
|
|
@@ -35,20 +81,54 @@ export function resolveId(entries, idOrPrefix) {
|
|
|
35
81
|
* its id (the handle is durable across every write), or create a new row with
|
|
36
82
|
* `newId`.
|
|
37
83
|
* `now` (epoch ms) and `newId` are injected so the core stays pure/testable.
|
|
84
|
+
*
|
|
85
|
+
* Two clocks, because an EDIT is not an OBSERVATION. `updatedAt` is when the row
|
|
86
|
+
* was last written; `verifiedAt` is when its claim was last confirmed, and it is
|
|
87
|
+
* what the staleness hedge reads. Rewriting a note used to rejuvenate a row
|
|
88
|
+
* nobody had re-checked in a month, which silently switched that hedge off:
|
|
89
|
+
*
|
|
90
|
+
* create ................................ both move
|
|
91
|
+
* --status given (even the same value) .. both move — re-asserting a status
|
|
92
|
+
* IS confirming it
|
|
93
|
+
* --note / --source / --entity only ..... updatedAt moves, verifiedAt does NOT
|
|
94
|
+
* --verified alone ...................... verifiedAt moves, updatedAt does NOT
|
|
95
|
+
*
|
|
96
|
+
* That last line is deliberate: a row whose `updatedAt` does not move stays out
|
|
97
|
+
* of every sibling's pushed delta, and "I re-checked, nothing changed" is not
|
|
98
|
+
* news — pushing it to every agent is the noise a state table exists to avoid.
|
|
99
|
+
* A row that predates `verifiedAt` inherits its old `updatedAt` as the moment it
|
|
100
|
+
* was last observed, which is the truth and makes the migration lazy, per row.
|
|
38
101
|
*/
|
|
39
102
|
export function upsertEntry(entries, patch, now, newId) {
|
|
103
|
+
const kind = normKind(patch.kind); // refuses an unknown kind before anything is written
|
|
104
|
+
const statusGiven = patch.status != null && String(patch.status).trim() !== "";
|
|
105
|
+
|
|
40
106
|
if (patch.id != null && String(patch.id).trim()) {
|
|
41
107
|
const cur = resolveId(entries, patch.id);
|
|
42
108
|
if (!cur) throw new Error(`no ledger entry with id "${patch.id}"`);
|
|
109
|
+
// `kind` is a property of the ENTITY, not of this write: keep the stored one
|
|
110
|
+
// when none is given, like the id.
|
|
111
|
+
const effKind = kind ?? normKind(cur.kind);
|
|
112
|
+
// Promoting a row to a lifecycle-less kind supplies that kind's status, so
|
|
113
|
+
// `--id X --kind fact` does not leave a stale `resolved` on a fact.
|
|
114
|
+
const status = statusGiven
|
|
115
|
+
? String(patch.status).trim()
|
|
116
|
+
: kind && kind !== rowKind(cur) && KIND_STATUS[kind]
|
|
117
|
+
? KIND_STATUS[kind]
|
|
118
|
+
: cur.status;
|
|
119
|
+
const edited =
|
|
120
|
+
statusGiven || kind != null || patch.note != null || patch.source != null || patch.entity != null;
|
|
121
|
+
const observed = statusGiven || patch.verified === true || status !== cur.status;
|
|
43
122
|
const row = {
|
|
44
123
|
id: cur.id,
|
|
45
124
|
entity:
|
|
46
125
|
patch.entity != null && String(patch.entity).trim() ? String(patch.entity).trim() : cur.entity,
|
|
47
|
-
status
|
|
48
|
-
|
|
126
|
+
status,
|
|
127
|
+
...(effKind ? { kind: effKind } : {}),
|
|
49
128
|
...((patch.note ?? cur.note) ? { note: String(patch.note ?? cur.note).trim() } : {}),
|
|
50
129
|
...((patch.source ?? cur.source) ? { source: String(patch.source ?? cur.source).trim() } : {}),
|
|
51
|
-
updatedAt: now,
|
|
130
|
+
updatedAt: edited ? now : (cur.updatedAt ?? now),
|
|
131
|
+
verifiedAt: observed ? now : (cur.verifiedAt ?? cur.updatedAt ?? now),
|
|
52
132
|
};
|
|
53
133
|
return [...entries.filter((e) => e.id !== cur.id), row];
|
|
54
134
|
}
|
|
@@ -56,13 +136,20 @@ export function upsertEntry(entries, patch, now, newId) {
|
|
|
56
136
|
const key = normEntity(patch.entity);
|
|
57
137
|
if (!key) throw new Error("entity is required");
|
|
58
138
|
const existing = entries.find((e) => normEntity(e.entity) === key);
|
|
139
|
+
const effKind = kind ?? normKind(existing?.kind);
|
|
140
|
+
const status = statusGiven
|
|
141
|
+
? String(patch.status).trim()
|
|
142
|
+
: (effKind && KIND_STATUS[effKind]) || String(patch.status ?? "").trim();
|
|
143
|
+
const observed = statusGiven || patch.verified === true || status !== existing?.status;
|
|
59
144
|
const row = {
|
|
60
145
|
id: existing?.id ?? newId,
|
|
61
146
|
entity: String(patch.entity).trim(),
|
|
62
|
-
status
|
|
147
|
+
status,
|
|
148
|
+
...(effKind ? { kind: effKind } : {}),
|
|
63
149
|
...(patch.note ? { note: String(patch.note).trim() } : {}),
|
|
64
150
|
...(patch.source ? { source: String(patch.source).trim() } : {}),
|
|
65
151
|
updatedAt: now,
|
|
152
|
+
verifiedAt: observed ? now : (existing?.verifiedAt ?? existing?.updatedAt ?? now),
|
|
66
153
|
};
|
|
67
154
|
return [...entries.filter((e) => normEntity(e.entity) !== key), row];
|
|
68
155
|
}
|
|
@@ -181,7 +268,12 @@ export function searchEntries(entries, query, opts = {}) {
|
|
|
181
268
|
};
|
|
182
269
|
}
|
|
183
270
|
|
|
184
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* Whole days since the row's claim was last CONFIRMED — the freshness the reflex
|
|
273
|
+
* hedges on. `verifiedAt` first: reading `updatedAt` meant any edit, down to a
|
|
274
|
+
* reworded note, reset the age of a row nobody had re-checked. Falls back to
|
|
275
|
+
* `updatedAt` for the rows recorded before the field existed.
|
|
276
|
+
*/
|
|
185
277
|
export function ageDays(entry, now) {
|
|
186
|
-
return Math.floor((now - (entry.updatedAt ?? now)) / 86_400_000);
|
|
278
|
+
return Math.floor((now - (entry.verifiedAt ?? entry.updatedAt ?? now)) / 86_400_000);
|
|
187
279
|
}
|
|
@@ -10,7 +10,7 @@ import fs from "node:fs";
|
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
|
-
import { upsertEntry, searchEntries, ageDays, resolveId, normEntity } from "./ledger-core.mjs";
|
|
13
|
+
import { upsertEntry, searchEntries, ageDays, resolveId, normEntity, rowKind, KINDS } from "./ledger-core.mjs";
|
|
14
14
|
|
|
15
15
|
const FILE =
|
|
16
16
|
(process.env.SHADOK_LEDGER_FILE || "").trim() ||
|
|
@@ -42,11 +42,17 @@ function mintId(rows) {
|
|
|
42
42
|
return crypto.randomBytes(4).toString("hex");
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/** Flags that take NO value, listed rather than guessed: inferring "the next arg
|
|
46
|
+
* starts with -- so this one is a boolean" would misread `--note "--foo"`. */
|
|
47
|
+
const BOOL_FLAGS = new Set(["verified"]);
|
|
48
|
+
|
|
45
49
|
/** `--key value` pairs → object. Values may be quoted by the shell already. */
|
|
46
50
|
function parseFlags(args) {
|
|
47
51
|
const out = {};
|
|
48
52
|
for (let i = 0; i < args.length; i++) {
|
|
49
|
-
if (args[i].startsWith("--"))
|
|
53
|
+
if (!args[i].startsWith("--")) continue;
|
|
54
|
+
const key = args[i].slice(2);
|
|
55
|
+
out[key] = BOOL_FLAGS.has(key) ? true : (args[++i] ?? "");
|
|
50
56
|
}
|
|
51
57
|
return out;
|
|
52
58
|
}
|
|
@@ -54,8 +60,17 @@ function parseFlags(args) {
|
|
|
54
60
|
function fmt(e, now) {
|
|
55
61
|
const age = ageDays(e, now);
|
|
56
62
|
const when = age <= 0 ? "today" : age === 1 ? "1d ago" : `${age}d ago`;
|
|
63
|
+
const kind = rowKind(e);
|
|
64
|
+
// Say "verified" wherever the two clocks have parted company — and always for
|
|
65
|
+
// a fact, whose whole state IS its freshness. A bare age next to a row last
|
|
66
|
+
// confirmed a month ago but reworded yesterday is the lie this removes.
|
|
67
|
+
const parted = e.verifiedAt && e.updatedAt && e.verifiedAt !== e.updatedAt;
|
|
68
|
+
const stamp = kind === "fact" || parted ? `verified ${when}` : when;
|
|
69
|
+
// `task` is the default and the overwhelming majority: tagging it would be
|
|
70
|
+
// noise on every line. The two that change how a status must be READ are named.
|
|
71
|
+
const tag = kind === "task" ? "" : ` · ${kind}`;
|
|
57
72
|
const handle = e.id ? `[${e.id}] ` : "";
|
|
58
|
-
const head = `• ${handle}${e.entity} — ${e.status} (${
|
|
73
|
+
const head = `• ${handle}${e.entity} — ${e.status}${tag} (${stamp}${e.source ? ` · ${e.source}` : ""})`;
|
|
59
74
|
return e.note ? `${head}\n ${e.note}` : head;
|
|
60
75
|
}
|
|
61
76
|
|
|
@@ -89,11 +104,18 @@ if (cmd === "check") {
|
|
|
89
104
|
// Two shapes: create/supersede by --entity (+ --status), or update an existing
|
|
90
105
|
// row by its --id handle (change any of status/note/source; entity optional).
|
|
91
106
|
const byId = f.id != null && String(f.id).trim();
|
|
92
|
-
const idHasNoFields =
|
|
93
|
-
|
|
107
|
+
const idHasNoFields =
|
|
108
|
+
f.status == null && f.note == null && f.source == null && f.entity == null &&
|
|
109
|
+
f.kind == null && f.verified == null;
|
|
110
|
+
// A fact or a decision has no lifecycle, so `--kind` stands in for `--status`:
|
|
111
|
+
// requiring one would only invite an invented word.
|
|
112
|
+
const hasStatus = !!f.status || !!f.kind;
|
|
113
|
+
if (byId ? idHasNoFields : !f.entity || !hasStatus) {
|
|
94
114
|
console.error(
|
|
95
115
|
'usage: ledger record --entity "<name>" --status <resolved|open|in-progress|decided> [--note "<line>"] [--source "<ref>"]\n' +
|
|
96
|
-
|
|
116
|
+
` or: ledger record --entity "<name>" --kind <${KINDS.join("|")}> [--note "<line>"] [--source "<ref>"]\n` +
|
|
117
|
+
' or: ledger record --id <handle> [--status <…>] [--kind <…>] [--note "<line>"] [--source "<ref>"]\n' +
|
|
118
|
+
' or: ledger record --id <handle> --verified (still true/current — nothing else changed)',
|
|
97
119
|
);
|
|
98
120
|
process.exit(2);
|
|
99
121
|
}
|
|
@@ -102,7 +124,15 @@ if (cmd === "check") {
|
|
|
102
124
|
try {
|
|
103
125
|
rows = upsertEntry(
|
|
104
126
|
rows0,
|
|
105
|
-
{
|
|
127
|
+
{
|
|
128
|
+
id: f.id,
|
|
129
|
+
entity: f.entity,
|
|
130
|
+
status: f.status,
|
|
131
|
+
kind: f.kind,
|
|
132
|
+
verified: f.verified === true,
|
|
133
|
+
note: f.note,
|
|
134
|
+
source: f.source,
|
|
135
|
+
},
|
|
106
136
|
now,
|
|
107
137
|
mintId(rows0),
|
|
108
138
|
);
|
|
@@ -112,7 +142,8 @@ if (cmd === "check") {
|
|
|
112
142
|
}
|
|
113
143
|
save(rows);
|
|
114
144
|
const row = byId ? resolveId(rows, f.id) : rows.find((r) => normEntity(r.entity) === normEntity(f.entity));
|
|
115
|
-
|
|
145
|
+
const k = rowKind(row);
|
|
146
|
+
console.log(`recorded: ${row.entity} — ${row.status}${k === "task" ? "" : ` · ${k}`} [${row.id}]`);
|
|
116
147
|
} else if (cmd === "list") {
|
|
117
148
|
const rows = searchEntries(load(), "").strong;
|
|
118
149
|
console.log(rows.length ? rows.map((e) => fmt(e, now)).join("\n") : "(ledger empty)");
|
package/dist/detect.d.ts
CHANGED
|
@@ -136,5 +136,11 @@ export interface TypeOptions {
|
|
|
136
136
|
settleMs?: number;
|
|
137
137
|
/** How long to wait for the box to actually read empty after Ctrl-U. */
|
|
138
138
|
clearMs?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Injected context to put in the box BEFORE the message, as its own paste —
|
|
141
|
+
* not concatenated into it. See `typeIntoBox` for why that distinction is a
|
|
142
|
+
* correctness one.
|
|
143
|
+
*/
|
|
144
|
+
prelude?: string;
|
|
139
145
|
}
|
|
140
|
-
export declare function typeIntoBox(pilot: TypingPilot, text: string, { attempts, appearMs, settleMs, clearMs }?: TypeOptions): Promise<boolean>;
|
|
146
|
+
export declare function typeIntoBox(pilot: TypingPilot, text: string, { attempts, appearMs, settleMs, clearMs, prelude }?: TypeOptions): Promise<boolean>;
|
package/dist/detect.js
CHANGED
|
@@ -252,16 +252,42 @@ const MAX_CLEARS = 40;
|
|
|
252
252
|
/** How long one press is given to show. Above the tmux mirror's fast poll
|
|
253
253
|
* (`SCREEN_FAST_MS`), or a press that worked would read as one that did not. */
|
|
254
254
|
const CLEAR_STEP_MS = 400;
|
|
255
|
-
export async function typeIntoBox(pilot, text, { attempts = 6, appearMs = 2_000, settleMs = 400, clearMs = 1_000 } = {}) {
|
|
255
|
+
export async function typeIntoBox(pilot, text, { attempts = 6, appearMs = 2_000, settleMs = 400, clearMs = 1_000, prelude } = {}) {
|
|
256
256
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
257
257
|
// Only "" and the resting state count as an empty box; anything else the
|
|
258
258
|
// box shows once we paste is our paste (whole, or collapsed).
|
|
259
259
|
const before = inputText(pilot.screen());
|
|
260
|
+
if (prelude) {
|
|
261
|
+
// TWO pastes, and the second must be the message. Claude Code keeps only
|
|
262
|
+
// the CURRENT paste inline and leaves every earlier one collapsed, so
|
|
263
|
+
// pasting separately is what puts the injected context inside the
|
|
264
|
+
// `<pasted_content>` wrapper and leaves the user's words outside it.
|
|
265
|
+
// Waiting for the first to land is load-bearing: fired back to back they
|
|
266
|
+
// can reach the pane as one paste event, which is exactly the single
|
|
267
|
+
// blob this exists to stop being.
|
|
268
|
+
pilot.paste(prelude);
|
|
269
|
+
try {
|
|
270
|
+
await pilot.waitFor((s) => inputText(s) !== before, { timeoutMs: appearMs });
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// The prelude never showed. Fall through and paste the message anyway:
|
|
274
|
+
// losing the context is survivable, losing the message is not.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// The SCREEN, not `inputText`: the box's visible text is read off its `❯`
|
|
278
|
+
// line alone, so once a prelude has filled that line everything appended
|
|
279
|
+
// after it lands on continuation lines and `inputText` stops moving. Waiting
|
|
280
|
+
// on it then times out on a paste that arrived perfectly — which is exactly
|
|
281
|
+
// what the first version of this did, on every prelude long enough to wrap.
|
|
282
|
+
const afterPrelude = pilot.screen();
|
|
260
283
|
pilot.paste(text);
|
|
261
284
|
try {
|
|
262
285
|
await pilot.waitFor((s) => {
|
|
263
286
|
const now = inputText(s);
|
|
264
|
-
|
|
287
|
+
if (now === "" || now === before)
|
|
288
|
+
return false; // nothing in the box yet
|
|
289
|
+
// With no prelude this is the original condition, unchanged.
|
|
290
|
+
return !prelude || s !== afterPrelude;
|
|
265
291
|
}, { timeoutMs: appearMs });
|
|
266
292
|
return true;
|
|
267
293
|
}
|
|
@@ -271,6 +297,8 @@ export async function typeIntoBox(pilot, text, { attempts = 6, appearMs = 2_000,
|
|
|
271
297
|
const now = inputText(s);
|
|
272
298
|
return now === "" || now === before;
|
|
273
299
|
};
|
|
300
|
+
// `before` (not `beforeText`) on purpose: a retry re-pastes the prelude,
|
|
301
|
+
// so the box has to come back to what it was before EITHER paste.
|
|
274
302
|
// Ctrl-U clears ONE line, not the box (seen on 2.1.296): a two-line paste
|
|
275
303
|
// takes three presses — the last line, the newline, the first line. Two
|
|
276
304
|
// presses left the first line behind and the next attempt pasted after it,
|
package/dist/detect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
package/dist/ledger.d.ts
CHANGED
|
@@ -18,9 +18,16 @@ export interface LedgerRow {
|
|
|
18
18
|
id?: string;
|
|
19
19
|
entity: string;
|
|
20
20
|
status: string;
|
|
21
|
+
/** `task` | `fact` | `decision` — absent on every row recorded before it
|
|
22
|
+
* existed, which `rowKind` reads rather than rewriting them. */
|
|
23
|
+
kind?: string;
|
|
21
24
|
note?: string;
|
|
22
25
|
source?: string;
|
|
26
|
+
/** When the row was last WRITTEN. */
|
|
23
27
|
updatedAt?: number;
|
|
28
|
+
/** When its claim was last CONFIRMED — the freshness, which an edit must not
|
|
29
|
+
* move. Absent on older rows; `ageDays` falls back to `updatedAt`. */
|
|
30
|
+
verifiedAt?: number;
|
|
24
31
|
}
|
|
25
32
|
/** The per-instance ledger file, keyed by the launch dir (same encoding as
|
|
26
33
|
* channels/crons). Distinct launch dirs → distinct ledgers. */
|
|
@@ -46,17 +53,51 @@ export declare function deltaSince(rows: LedgerRow[], watermark: number, cap: nu
|
|
|
46
53
|
rows: LedgerRow[];
|
|
47
54
|
total: number;
|
|
48
55
|
};
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
56
|
+
/**
|
|
57
|
+
* The one line of the note that says WHAT happened, DERIVED rather than asked
|
|
58
|
+
* for. The block used to carry `[id] entity — status` and nothing else, so the
|
|
59
|
+
* entity *label* was the whole payload — and a label written as a topic name
|
|
60
|
+
* ("report regeneration, 15 test accounts") cannot say what changed about it.
|
|
61
|
+
* That is how work finished on the 9th was still believed pending on the 12th.
|
|
62
|
+
*
|
|
63
|
+
* Derived, because the alternative — a mandatory `--headline` — asks every agent
|
|
64
|
+
* for writing discipline (which nothing enforces) and covers none of the rows
|
|
65
|
+
* already recorded. The note's first sentence needs neither.
|
|
66
|
+
*/
|
|
67
|
+
export declare function headline(note: string | undefined, max?: number): string;
|
|
68
|
+
/**
|
|
69
|
+
* The delta block the agent sees ahead of a prompt. English (it is written into
|
|
70
|
+
* the transcript — repo side). ONE line, within `BLOCK_MAX` (see above): per row
|
|
71
|
+
* `• [id] entity — status (source)`, the kind when it is not the default `task`,
|
|
72
|
+
* and `↳ <headline>` for what actually happened. Rows that do not fit the budget
|
|
73
|
+
* become the overflow pointer rather than a second line.
|
|
74
|
+
*/
|
|
52
75
|
export declare function formatLedgerBlock(rows: LedgerRow[], total: number): string;
|
|
53
|
-
/** Prepend the block as its own leading
|
|
76
|
+
/** Prepend the block as its own leading line. Idempotent.
|
|
77
|
+
*
|
|
78
|
+
* NOT how the block reaches a live agent any more — it goes in as a separate
|
|
79
|
+
* PASTE (`pilot.submit(text, prelude)`), which is what keeps the user's message
|
|
80
|
+
* out of the `<pasted_content>` wrapper. Kept because it is the exact shape of
|
|
81
|
+
* every transcript written before that change, which `stripLedgerBlock` must go
|
|
82
|
+
* on understanding and which the tests build from. */
|
|
54
83
|
export declare function markLedgerBlock(text: string, block: string): string;
|
|
55
|
-
/** Does this text open with a pushed ledger block?
|
|
84
|
+
/** Does this text open with a pushed ledger block?
|
|
85
|
+
*
|
|
86
|
+
* `⟧` CLOSES the header, it no longer ENDS the line: a one-line block continues
|
|
87
|
+
* with its rows after it. Testing `endsWith` here was the first thing the
|
|
88
|
+
* one-line format broke, and it fails open — detection returns false, nothing
|
|
89
|
+
* is stripped, and the whole block is shown to the user in the chat. The old
|
|
90
|
+
* multi-line shape satisfies this test too, so replayed transcripts are
|
|
91
|
+
* unaffected. */
|
|
56
92
|
export declare function hasLedgerBlock(text: string): boolean;
|
|
57
|
-
/** Remove a leading ledger block
|
|
58
|
-
*
|
|
59
|
-
*
|
|
93
|
+
/** Remove a leading ledger block, leaving whatever came after (the ⟦platform⟧
|
|
94
|
+
* header and the user's message). A plain message is returned untouched.
|
|
95
|
+
*
|
|
96
|
+
* The block is one line now, so that is usually all this drops — but it must
|
|
97
|
+
* keep understanding the HISTORICAL multi-line shape (header, `• ` bullets,
|
|
98
|
+
* indented `↳` headlines), because `loadHistory` replays transcripts written
|
|
99
|
+
* before the format changed and a block it fails to strip is a block the user
|
|
100
|
+
* reads in the chat. */
|
|
60
101
|
export declare function stripLedgerBlock(text: string): string;
|
|
61
102
|
/** Where this instance stores its agents' ledger watermarks. Beside the table,
|
|
62
103
|
* same launch-dir encoding, distinct name — writing one must never clobber the
|