zam-core 0.3.3 → 0.3.5

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/dist/index.d.ts CHANGED
@@ -1,163 +1,299 @@
1
1
  import { Database } from 'libsql';
2
2
 
3
+ /**
4
+ * Learning Analytics
5
+ *
6
+ * Progress statistics, competence tracking, and session summaries.
7
+ * Ported from PoC's `stats` command with additions for FSRS and symbiosis modes.
8
+ */
9
+
10
+ interface UserStats {
11
+ userId: string;
12
+ totalTokens: number;
13
+ cardsInDeck: number;
14
+ dueToday: number;
15
+ blocked: number;
16
+ mature: number;
17
+ avgStability: number | null;
18
+ totalSessions: number;
19
+ lastSession: string | null;
20
+ }
21
+ interface DomainCompetence {
22
+ domain: string;
23
+ totalCards: number;
24
+ matureCards: number;
25
+ avgStability: number;
26
+ retentionRate: number;
27
+ suggestedMode: "shadowing" | "copilot" | "autonomy";
28
+ }
29
+ /**
30
+ * Get overall learning stats for a user (ported from PoC's `stats` command).
31
+ */
32
+ declare function getUserStats(db: Database, userId: string): UserStats;
33
+ /**
34
+ * Get competence per domain for a user.
35
+ * Used to suggest symbiosis mode transitions.
36
+ */
37
+ declare function getDomainCompetence(db: Database, userId: string): DomainCompetence[];
38
+
39
+ /**
40
+ * Azure DevOps connector — fetches work items from ADO boards.
41
+ */
42
+ interface ADOConfig {
43
+ orgUrl: string;
44
+ project: string;
45
+ pat: string;
46
+ }
47
+ interface WorkItem {
48
+ id: number;
49
+ title: string;
50
+ state: string;
51
+ type: string;
52
+ assignedTo: string;
53
+ }
54
+ /** Load ADO config from credentials file. Returns null if not configured. */
55
+ declare function loadADOConfig(): ADOConfig | null;
56
+ /**
57
+ * Fetch active work items assigned to the current user.
58
+ * Uses WIQL to query, then batch-fetches work item details.
59
+ */
60
+ declare function fetchActiveWorkItems(config: ADOConfig): Promise<WorkItem[]>;
61
+
62
+ /**
63
+ * Credential store — reads/writes ~/.zam/credentials.json
64
+ *
65
+ * Connector secrets (Turso URL/token, ADO PAT, etc.) live here instead of
66
+ * inside the SQLite database. This ensures credentials survive db deletion,
67
+ * which is required when migrating from plain SQLite to a libsql embedded
68
+ * replica (Turso cloud sync).
69
+ */
70
+ interface TursoCredentials {
71
+ url: string;
72
+ token: string;
73
+ }
74
+ interface ADOCredentials {
75
+ org_url: string;
76
+ project: string;
77
+ pat: string;
78
+ }
79
+ interface Credentials {
80
+ turso?: Partial<TursoCredentials>;
81
+ ado?: Partial<ADOCredentials>;
82
+ }
83
+ /** Load credentials from ~/.zam/credentials.json. Returns empty object if missing. */
84
+ declare function loadCredentials(path?: string): Credentials;
85
+ /** Save credentials to ~/.zam/credentials.json. */
86
+ declare function saveCredentials(creds: Credentials, path?: string): void;
87
+ /** Get complete Turso credentials, or null if incomplete. */
88
+ declare function getTursoCredentials(path?: string): TursoCredentials | null;
89
+ /** Set Turso credentials. */
90
+ declare function setTursoCredentials(url: string, token: string, path?: string): void;
91
+ /** Clear Turso credentials. */
92
+ declare function clearTursoCredentials(path?: string): void;
93
+ /** Get complete ADO credentials, or null if incomplete. */
94
+ declare function getADOCredentials(path?: string): ADOCredentials | null;
95
+ /** Set ADO credentials. */
96
+ declare function setADOCredentials(orgUrl: string, project: string, pat: string, path?: string): void;
97
+ /** Clear ADO credentials. */
98
+ declare function clearADOCredentials(path?: string): void;
99
+
3
100
  interface ConnectionOptions {
4
101
  /** Path to the SQLite database file. Defaults to ~/.zam/zam.db */
5
102
  dbPath?: string;
6
103
  /** If true, create the directory and run schema migrations on open */
7
104
  initialize?: boolean;
8
- /** Turso sync URL for cloud replication (e.g. libsql://db-name.turso.io) */
105
+ /** Turso sync URL for embedded replica mode (e.g. libsql://db-name.turso.io) */
9
106
  syncUrl?: string;
10
- /** Turso auth token for cloud replication */
107
+ /** Turso auth token for direct remote or embedded replica access */
11
108
  authToken?: string;
109
+ /** If false, ignore ~/.zam/credentials.json and force the local/default database. */
110
+ useConfiguredCloud?: boolean;
12
111
  }
13
112
  /**
14
113
  * Open (or create) the ZAM database.
15
- * Uses WAL mode for concurrent access from AI CLI and user CLI.
16
- * When syncUrl is provided, enables embedded replica sync with Turso.
114
+ * Uses configured Turso credentials for the default database when present.
115
+ * Falls back to local SQLite and WAL mode when no cloud credentials exist.
116
+ * When syncUrl is provided explicitly, enables embedded replica sync with Turso.
17
117
  */
18
118
  declare function openDatabase(options?: ConnectionOptions): Database;
19
119
  /**
20
- * Open the database with Turso cloud sync auto-detected from stored settings.
21
- * Reads turso.url and turso.token from user_config. If present, reopens
22
- * the database with embedded replica sync enabled.
120
+ * Open the database with Turso cloud credentials auto-detected.
121
+ * Credentials live in ~/.zam/credentials.json (NOT in the db), so a fresh
122
+ * machine only has to collect missing secrets instead of bootstrapping local
123
+ * state first.
23
124
  */
24
125
  declare function openDatabaseWithSync(options?: Omit<ConnectionOptions, "syncUrl" | "authToken">): Database;
25
126
  /** Get the default database path */
26
127
  declare function getDefaultDbPath(): string;
27
128
 
28
129
  /**
29
- * Token repositorytyped wrappers around the tokens table.
130
+ * Goal file parser reads markdown files with YAML-style frontmatter.
30
131
  *
31
- * Tokens are atomic knowledge concepts with Bloom taxonomy levels
32
- * and optional symbiosis modes (shadowing / copilot / autonomy).
132
+ * Goals are persisted as markdown files in the personal repo.
133
+ * Each file has simple key: value frontmatter (no nested structures)
134
+ * and a markdown body with description, tasks, and token references.
33
135
  */
34
-
35
- type BloomLevel$1 = 1 | 2 | 3 | 4 | 5;
36
- type SymbiosisMode = "shadowing" | "copilot" | "autonomy";
37
- interface Token {
38
- id: string;
39
- slug: string;
40
- concept: string;
41
- domain: string;
42
- bloom_level: BloomLevel$1;
43
- context: string;
44
- symbiosis_mode: SymbiosisMode | null;
45
- created_at: string;
46
- updated_at: string;
47
- deprecated_at: string | null;
48
- }
49
- interface CreateTokenInput {
136
+ type GoalStatus = "active" | "completed" | "paused" | "abandoned";
137
+ interface Goal {
50
138
  slug: string;
51
- concept: string;
52
- domain?: string;
53
- bloom_level?: BloomLevel$1;
54
- context?: string;
55
- symbiosis_mode?: SymbiosisMode | null;
56
- }
57
- interface ListTokensOptions {
58
- domain?: string;
139
+ title: string;
140
+ status: GoalStatus;
141
+ parent: string | null;
142
+ created: string;
143
+ updated: string;
144
+ body: string;
145
+ filePath: string;
59
146
  }
60
- interface ScoredToken extends Token {
61
- score: number;
147
+ interface GoalFrontmatter {
148
+ title?: string;
149
+ status?: string;
150
+ parent?: string;
151
+ created?: string;
152
+ updated?: string;
62
153
  }
63
154
  /**
64
- * Create a new knowledge token.
65
- * Throws if a token with the same slug already exists.
66
- */
67
- declare function createToken(db: Database, input: CreateTokenInput): Token;
68
- /**
69
- * Look up a token by its unique slug.
70
- * Returns undefined if not found.
71
- */
72
- declare function getTokenBySlug(db: Database, slug: string): Token | undefined;
73
- /**
74
- * Look up a token by its ULID.
75
- * Returns undefined if not found.
155
+ * Parse a goal markdown file into a Goal object.
156
+ *
157
+ * Expected format:
158
+ * ```
159
+ * ---
160
+ * title: Learn Rust fundamentals
161
+ * status: active
162
+ * parent: become-systems-programmer
163
+ * created: 2026-03-28
164
+ * updated: 2026-03-28
165
+ * ---
166
+ *
167
+ * ## Description
168
+ * ...
169
+ * ```
170
+ *
171
+ * @param content - Raw file content
172
+ * @param slug - Goal slug (derived from filename by caller)
173
+ * @param filePath - Absolute path to the file
76
174
  */
77
- declare function getTokenById(db: Database, id: string): Token | undefined;
175
+ declare function parseGoalFile(content: string, slug: string, filePath: string): Goal;
78
176
  /**
79
- * Mark a token as deprecated. Deprecated tokens are excluded from review queues
80
- * and search results but are not deleted — they can still be consulted.
81
- *
82
- * Throws if the token does not exist or is already deprecated.
177
+ * Serialize a Goal back to markdown with frontmatter.
83
178
  */
84
- declare function deprecateToken(db: Database, slug: string): Token;
179
+ declare function serializeGoal(goal: Goal): string;
85
180
  /**
86
- * Fuzzy search for tokens by keyword query.
87
- *
88
- * Ported from the PoC's find-token command: splits the query into word
89
- * tokens, scores each database token by word overlap plus a substring
90
- * bonus on the concept field, and returns all matches sorted by score
91
- * descending.
181
+ * Extract tasks (checklist items) from goal body.
182
+ * Returns items like { text: "Complete Rustlings", done: false }.
92
183
  */
93
- declare function findTokens(db: Database, query: string): ScoredToken[];
184
+ declare function extractTasks(body: string): Array<{
185
+ text: string;
186
+ done: boolean;
187
+ }>;
94
188
  /**
95
- * List all tokens, optionally filtered by domain.
96
- * Results are ordered by bloom_level then slug.
189
+ * Extract token references from goal body.
190
+ * Looks for lines like `- token/slug` under a "## Tokens" section.
97
191
  */
98
- declare function listTokens(db: Database, options?: ListTokensOptions): Token[];
192
+ declare function extractTokenRefs(body: string): string[];
99
193
 
100
194
  /**
101
- * Prerequisite repositorytyped wrappers around the prerequisites table.
195
+ * Goal Enginemanages goal lifecycle via markdown files.
102
196
  *
103
- * Models the dependency graph: "to learn token A, first know token B."
197
+ * Goals live as markdown files in a directory (typically the personal repo's
198
+ * goals/ folder). The engine reads, creates, and updates these files.
199
+ * It does not depend on the database — goals are git-tracked, not DB-tracked.
104
200
  */
105
201
 
106
- interface Prerequisite {
107
- token_id: string;
108
- requires_id: string;
202
+ interface GoalSummary {
203
+ slug: string;
204
+ title: string;
205
+ status: GoalStatus;
206
+ parent: string | null;
207
+ taskCount: number;
208
+ tasksDone: number;
209
+ tokenCount: number;
109
210
  }
110
- /** A prerequisite row joined with the token it points to. */
111
- interface PrerequisiteWithToken extends Prerequisite {
211
+ interface CreateGoalInput {
112
212
  slug: string;
113
- concept: string;
114
- domain: string;
115
- bloom_level: number;
213
+ title: string;
214
+ status?: GoalStatus;
215
+ parent?: string;
216
+ description?: string;
116
217
  }
117
218
  /**
118
- * Add a prerequisite edge: tokenId requires requiresId.
119
- *
120
- * Idempotent — silently ignores duplicate edges.
121
- * Throws if either token ID does not exist (FK constraint).
122
- * Throws if a token is declared as its own prerequisite.
219
+ * List all goals in the goals directory.
220
+ * Returns summaries sorted by status (active first) then title.
123
221
  */
124
- declare function addPrerequisite(db: Database, tokenId: string, requiresId: string): void;
222
+ declare function listGoals(goalsDir: string): GoalSummary[];
125
223
  /**
126
- * Get the direct prerequisites of a token "what does token X require?"
127
- *
128
- * Returns prerequisite rows joined with the required token's details.
224
+ * Get a single goal by slug (filename without .md).
225
+ * Returns undefined if the file doesn't exist.
129
226
  */
130
- declare function getPrerequisites(db: Database, tokenId: string): PrerequisiteWithToken[];
227
+ declare function getGoal(goalsDir: string, slug: string): Goal | undefined;
131
228
  /**
132
- * Get the direct dependents of a token "what depends on token X?"
133
- *
134
- * Returns prerequisite rows joined with the dependent token's details.
229
+ * Create a new goal file. Throws if a goal with this slug already exists.
135
230
  */
136
- declare function getDependents(db: Database, tokenId: string): PrerequisiteWithToken[];
231
+ declare function createGoal(goalsDir: string, input: CreateGoalInput): Goal;
232
+ /**
233
+ * Update a goal's status. Writes the updated file back to disk.
234
+ */
235
+ declare function updateGoalStatus(goalsDir: string, slug: string, status: GoalStatus): Goal;
236
+ /**
237
+ * Get the goal tree — goals organized by parent relationships.
238
+ * Returns root goals (no parent) with nested children.
239
+ */
240
+ declare function getGoalTree(goalsDir: string): Array<GoalSummary & {
241
+ children: GoalSummary[];
242
+ }>;
137
243
 
138
244
  /**
139
- * Card repository typed wrappers around the cards table.
245
+ * Agent skills: task recipes the agent learns from user guidance.
140
246
  *
141
- * Each card tracks one user's scheduling state for one token,
142
- * using FSRS fields (stability, difficulty, elapsed_days, etc.).
247
+ * When the agent cannot execute a step, it admits it, asks for guidance,
248
+ * and saves the successful approach here. Skills are linked to tokens so
249
+ * FSRS decay naturally resurfaces them for review — automation ≠ retention.
143
250
  */
144
251
 
145
- type CardState$1 = "new" | "learning" | "review" | "relearning";
146
- interface Card {
252
+ type SkillSource = "learned" | "builtin";
253
+ interface AgentSkill {
147
254
  id: string;
148
- token_id: string;
149
- user_id: string;
150
- stability: number;
151
- difficulty: number;
152
- elapsed_days: number;
153
- scheduled_days: number;
154
- reps: number;
155
- lapses: number;
156
- state: CardState$1;
157
- due_at: string;
158
- last_review_at: string | null;
159
- blocked: number;
160
- }
255
+ slug: string;
256
+ description: string;
257
+ steps: string[];
258
+ token_slugs: string[];
259
+ source: SkillSource;
260
+ created_at: string;
261
+ updated_at: string;
262
+ }
263
+ interface CreateAgentSkillInput {
264
+ slug: string;
265
+ description: string;
266
+ steps: string[];
267
+ token_slugs?: string[];
268
+ source?: SkillSource;
269
+ }
270
+ declare function createAgentSkill(db: Database, input: CreateAgentSkillInput): AgentSkill;
271
+ declare function getAgentSkill(db: Database, slug: string): AgentSkill | undefined;
272
+ declare function listAgentSkills(db: Database): AgentSkill[];
273
+
274
+ /**
275
+ * Card repository — typed wrappers around the cards table.
276
+ *
277
+ * Each card tracks one user's scheduling state for one token,
278
+ * using FSRS fields (stability, difficulty, elapsed_days, etc.).
279
+ */
280
+
281
+ type CardState$1 = "new" | "learning" | "review" | "relearning";
282
+ interface Card {
283
+ id: string;
284
+ token_id: string;
285
+ user_id: string;
286
+ stability: number;
287
+ difficulty: number;
288
+ elapsed_days: number;
289
+ scheduled_days: number;
290
+ reps: number;
291
+ lapses: number;
292
+ state: CardState$1;
293
+ due_at: string;
294
+ last_review_at: string | null;
295
+ blocked: number;
296
+ }
161
297
  interface UpdateCardInput {
162
298
  stability?: number;
163
299
  difficulty?: number;
@@ -170,6 +306,13 @@ interface UpdateCardInput {
170
306
  last_review_at?: string | null;
171
307
  blocked?: number;
172
308
  }
309
+ interface CardDeletionImpact {
310
+ review_logs: number;
311
+ }
312
+ interface DeleteCardResult {
313
+ card: Card;
314
+ impact: CardDeletionImpact;
315
+ }
173
316
  /** A due card joined with its token details. */
174
317
  interface DueCard extends Card {
175
318
  slug: string;
@@ -197,6 +340,10 @@ declare function ensureCard(db: Database, tokenId: string, userId: string): Card
197
340
  * Get a card by token+user. Returns undefined if no card exists.
198
341
  */
199
342
  declare function getCard(db: Database, tokenId: string, userId: string): Card | undefined;
343
+ /**
344
+ * Get a card by its ULID.
345
+ */
346
+ declare function getCardById(db: Database, cardId: string): Card | undefined;
200
347
  /**
201
348
  * Update a card's scheduling fields.
202
349
  *
@@ -204,6 +351,14 @@ declare function getCard(db: Database, tokenId: string, userId: string): Card |
204
351
  * does not exist.
205
352
  */
206
353
  declare function updateCard(db: Database, cardId: string, updates: UpdateCardInput): Card;
354
+ /**
355
+ * Preview the review-log rows that will be removed when deleting a user's card.
356
+ */
357
+ declare function getCardDeletionImpact(db: Database, tokenId: string, userId: string): CardDeletionImpact;
358
+ /**
359
+ * Delete one user's card for a token. Review logs cascade via FK.
360
+ */
361
+ declare function deleteCardForUser(db: Database, tokenId: string, userId: string): DeleteCardResult;
207
362
  /**
208
363
  * Get all cards that are due for review.
209
364
  *
@@ -222,6 +377,44 @@ declare function getDueCards(db: Database, userId: string, now?: string): DueCar
222
377
  */
223
378
  declare function getBlockedCards(db: Database, userId: string): BlockedCard[];
224
379
 
380
+ /**
381
+ * Prerequisite repository — typed wrappers around the prerequisites table.
382
+ *
383
+ * Models the dependency graph: "to learn token A, first know token B."
384
+ */
385
+
386
+ interface Prerequisite {
387
+ token_id: string;
388
+ requires_id: string;
389
+ }
390
+ /** A prerequisite row joined with the token it points to. */
391
+ interface PrerequisiteWithToken extends Prerequisite {
392
+ slug: string;
393
+ concept: string;
394
+ domain: string;
395
+ bloom_level: number;
396
+ }
397
+ /**
398
+ * Add a prerequisite edge: tokenId requires requiresId.
399
+ *
400
+ * Idempotent — silently ignores duplicate edges.
401
+ * Throws if either token ID does not exist (FK constraint).
402
+ * Throws if a token is declared as its own prerequisite.
403
+ */
404
+ declare function addPrerequisite(db: Database, tokenId: string, requiresId: string): void;
405
+ /**
406
+ * Get the direct prerequisites of a token — "what does token X require?"
407
+ *
408
+ * Returns prerequisite rows joined with the required token's details.
409
+ */
410
+ declare function getPrerequisites(db: Database, tokenId: string): PrerequisiteWithToken[];
411
+ /**
412
+ * Get the direct dependents of a token — "what depends on token X?"
413
+ *
414
+ * Returns prerequisite rows joined with the dependent token's details.
415
+ */
416
+ declare function getDependents(db: Database, tokenId: string): PrerequisiteWithToken[];
417
+
225
418
  /**
226
419
  * Review log repository — typed wrappers around the review_logs table.
227
420
  *
@@ -356,36 +549,6 @@ declare function logStep(db: Database, input: LogStepInput): SessionStep;
356
549
  */
357
550
  declare function getSessionSummary(db: Database, sessionId: string): SessionSummary;
358
551
 
359
- /**
360
- * Agent skills: task recipes the agent learns from user guidance.
361
- *
362
- * When the agent cannot execute a step, it admits it, asks for guidance,
363
- * and saves the successful approach here. Skills are linked to tokens so
364
- * FSRS decay naturally resurfaces them for review — automation ≠ retention.
365
- */
366
-
367
- type SkillSource = "learned" | "builtin";
368
- interface AgentSkill {
369
- id: string;
370
- slug: string;
371
- description: string;
372
- steps: string[];
373
- token_slugs: string[];
374
- source: SkillSource;
375
- created_at: string;
376
- updated_at: string;
377
- }
378
- interface CreateAgentSkillInput {
379
- slug: string;
380
- description: string;
381
- steps: string[];
382
- token_slugs?: string[];
383
- source?: SkillSource;
384
- }
385
- declare function createAgentSkill(db: Database, input: CreateAgentSkillInput): AgentSkill;
386
- declare function getAgentSkill(db: Database, slug: string): AgentSkill | undefined;
387
- declare function listAgentSkills(db: Database): AgentSkill[];
388
-
389
552
  /**
390
553
  * User settings — key/value store backed by the user_config table.
391
554
  */
@@ -407,56 +570,299 @@ declare function setSetting(db: Database, key: string, value: string): void;
407
570
  declare function deleteSetting(db: Database, key: string): boolean;
408
571
 
409
572
  /**
410
- * FSRS-5Free Spaced Repetition Scheduler (v5)
573
+ * Token repository typed wrappers around the tokens table.
411
574
  *
412
- * Pure-function implementation of the FSRS algorithm that replaces
413
- * the PoC's SM-2 scheduler. This is the mathematical heart of ZAM's
414
- * spaced-repetition engine.
575
+ * Tokens are atomic knowledge concepts with Bloom taxonomy levels
576
+ * and optional symbiosis modes (shadowing / copilot / autonomy).
577
+ */
578
+
579
+ type BloomLevel$1 = 1 | 2 | 3 | 4 | 5;
580
+ type SymbiosisMode = "shadowing" | "copilot" | "autonomy";
581
+ interface Token {
582
+ id: string;
583
+ slug: string;
584
+ concept: string;
585
+ domain: string;
586
+ bloom_level: BloomLevel$1;
587
+ context: string;
588
+ symbiosis_mode: SymbiosisMode | null;
589
+ source_link: string | null;
590
+ question: string | null;
591
+ created_at: string;
592
+ updated_at: string;
593
+ deprecated_at: string | null;
594
+ }
595
+ interface CreateTokenInput {
596
+ slug: string;
597
+ concept: string;
598
+ domain?: string;
599
+ bloom_level?: BloomLevel$1;
600
+ context?: string;
601
+ symbiosis_mode?: SymbiosisMode | null;
602
+ source_link?: string | null;
603
+ question?: string | null;
604
+ }
605
+ interface UpdateTokenInput {
606
+ concept?: string;
607
+ domain?: string;
608
+ bloom_level?: BloomLevel$1;
609
+ context?: string;
610
+ symbiosis_mode?: SymbiosisMode | null;
611
+ source_link?: string | null;
612
+ question?: string | null;
613
+ }
614
+ interface ListTokensOptions {
615
+ domain?: string;
616
+ }
617
+ interface TokenDeleteImpact {
618
+ cards: number;
619
+ review_logs: number;
620
+ prerequisite_edges_from_token: number;
621
+ prerequisite_edges_to_token: number;
622
+ session_steps: number;
623
+ sessions_touched: number;
624
+ agent_skills: number;
625
+ }
626
+ interface DeleteTokenResult {
627
+ token: Token;
628
+ impact: TokenDeleteImpact;
629
+ }
630
+ interface ScoredToken extends Token {
631
+ score: number;
632
+ }
633
+ /**
634
+ * Create a new knowledge token.
635
+ * Throws if a token with the same slug already exists.
636
+ */
637
+ declare function createToken(db: Database, input: CreateTokenInput): Token;
638
+ /**
639
+ * Look up a token by its unique slug.
640
+ * Returns undefined if not found.
641
+ */
642
+ declare function getTokenBySlug(db: Database, slug: string): Token | undefined;
643
+ /**
644
+ * Look up a token by its ULID.
645
+ * Returns undefined if not found.
646
+ */
647
+ declare function getTokenById(db: Database, id: string): Token | undefined;
648
+ /**
649
+ * Update mutable fields on a token.
415
650
  *
416
- * Reference: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm
651
+ * Slug is intentionally immutable in v1 because it is referenced by other
652
+ * parts of the system (for example agent skill metadata).
417
653
  */
418
- /** 1 = Again (forgot), 2 = Hard, 3 = Good, 4 = Easy */
419
- type Rating = 1 | 2 | 3 | 4;
420
- type CardState = "new" | "learning" | "review" | "relearning";
421
- interface SchedulingCard {
422
- /** Memory stability in days — expected half-life of recall probability. */
423
- stability: number;
424
- /** Intrinsic difficulty on a 1–10 scale. */
425
- difficulty: number;
426
- /** Days elapsed since the last review. */
427
- elapsedDays: number;
428
- /** Currently scheduled interval in days. */
429
- scheduledDays: number;
430
- /** Count of successful consecutive reviews. */
431
- reps: number;
432
- /** Times the card was forgotten (rated Again). */
433
- lapses: number;
434
- /** Current learning state. */
435
- state: CardState;
436
- /** When the card is next due. */
437
- dueAt: Date;
438
- /** When the card was last reviewed (null for new cards). */
439
- lastReviewAt: Date | null;
654
+ declare function updateToken(db: Database, slug: string, updates: UpdateTokenInput): Token;
655
+ /**
656
+ * Mark a token as deprecated. Deprecated tokens are excluded from review queues
657
+ * and search results but are not deleted — they can still be consulted.
658
+ *
659
+ * Throws if the token does not exist or is already deprecated.
660
+ */
661
+ declare function deprecateToken(db: Database, slug: string): Token;
662
+ /**
663
+ * Preview the rows that will be removed or updated when deleting a token.
664
+ */
665
+ declare function getTokenDeleteImpact(db: Database, slug: string): TokenDeleteImpact;
666
+ /**
667
+ * Hard-delete a token and clean up non-FK references that point at its slug.
668
+ */
669
+ declare function deleteToken(db: Database, slug: string): DeleteTokenResult;
670
+ /**
671
+ * Fuzzy search for tokens by keyword query.
672
+ *
673
+ * Ported from the PoC's find-token command: splits the query into word
674
+ * tokens, scores each database token by word overlap plus a substring
675
+ * bonus on the concept field, and returns all matches sorted by score
676
+ * descending.
677
+ */
678
+ declare function findTokens(db: Database, query: string): ScoredToken[];
679
+ /**
680
+ * List all tokens, optionally filtered by domain.
681
+ * Results are ordered by bloom_level then slug.
682
+ */
683
+ declare function listTokens(db: Database, options?: ListTokensOptions): Token[];
684
+
685
+ /**
686
+ * Monitor log analyzer — maps observed shell commands to token ratings.
687
+ *
688
+ * Pure functions, no DB or filesystem access. Takes parsed command records
689
+ * and a token-to-pattern mapping, returns ratings with evidence.
690
+ */
691
+ interface MonitorEvent {
692
+ type: "command_start" | "command_end" | "monitor_meta";
693
+ ts: string;
694
+ seq?: number;
695
+ pid?: number;
696
+ command?: string;
697
+ cwd?: string;
698
+ exit_code?: number;
699
+ event?: "start" | "stop";
700
+ session_id?: string;
701
+ shell?: string;
440
702
  }
441
- interface FSRSParameters {
442
- /** 19 optimised weight parameters (w0 – w18). */
443
- w: number[];
444
- /** Target retention rate, e.g. 0.9 means we aim for 90% recall. */
445
- requestRetention: number;
703
+ interface CommandRecord {
704
+ seq: number;
705
+ pid: number;
706
+ command: string;
707
+ cwd: string;
708
+ startedAt: string;
709
+ endedAt: string | null;
710
+ durationMs: number | null;
711
+ exitCode: number | null;
446
712
  }
447
- interface FSRS {
448
- /** Return a fully updated card after applying a rating. Pure function. */
449
- schedule(card: SchedulingCard, rating: Rating, now?: Date): SchedulingCard;
450
- /** The parameters baked into this instance. */
451
- readonly params: Readonly<FSRSParameters>;
713
+ interface TokenPattern {
714
+ slug: string;
715
+ patterns: string[];
716
+ }
717
+ interface ObservationRating {
718
+ tokenSlug: string;
719
+ rating: 1 | 2 | 3 | 4 | null;
720
+ confidence: "high" | "medium" | "low";
721
+ evidence: {
722
+ matchedCommands: number;
723
+ helpSeeking: boolean;
724
+ errorCount: number;
725
+ selfCorrections: number;
726
+ medianGapMs: number | null;
727
+ thinkingGapMs: number | null;
728
+ };
729
+ matchedCommandTexts: string[];
730
+ }
731
+ interface AnalysisResult {
732
+ ratings: ObservationRating[];
733
+ unmatchedCommands: string[];
734
+ timeSpan: {
735
+ start: string;
736
+ end: string;
737
+ durationMs: number;
738
+ } | null;
739
+ }
740
+ /**
741
+ * Parse a JSONL string into MonitorEvent objects.
742
+ * Skips malformed lines silently.
743
+ */
744
+ declare function parseMonitorLog(jsonl: string): MonitorEvent[];
745
+ /**
746
+ * Pair command_start and command_end events by (pid, seq) into CommandRecords.
747
+ */
748
+ declare function pairCommands(events: MonitorEvent[]): CommandRecord[];
749
+ /**
750
+ * Analyze observed commands against token patterns and produce ratings.
751
+ */
752
+ declare function analyzeObservation(commands: CommandRecord[], tokenPatterns: TokenPattern[]): AnalysisResult;
753
+
754
+ /**
755
+ * Monitor I/O — read/write JSONL files for shell observation.
756
+ *
757
+ * Monitor logs live at ~/.zam/monitor/<session-id>.jsonl.
758
+ * Separated from analyzer.ts so the analyzer remains pure-function testable.
759
+ */
760
+
761
+ /** Get the monitor directory path. */
762
+ declare function getMonitorDir(): string;
763
+ /** Get the JSONL file path for a session. */
764
+ declare function getMonitorPath(sessionId: string): string;
765
+ /** Ensure the monitor directory exists (mode 0700 for privacy). */
766
+ declare function ensureMonitorDir(): void;
767
+ /** Append a single event to the session's JSONL file. */
768
+ declare function writeMonitorEvent(sessionId: string, event: MonitorEvent): void;
769
+ /** Read and parse all events from a session's monitor log. */
770
+ declare function readMonitorLog(sessionId: string): MonitorEvent[];
771
+ /** Check if a monitor log exists for a session. */
772
+ declare function monitorLogExists(sessionId: string): boolean;
773
+ /** Get basic stats about a monitor log without full parsing. */
774
+ declare function getMonitorLogStats(sessionId: string): {
775
+ exists: boolean;
776
+ sizeBytes: number;
777
+ lineCount: number;
778
+ };
779
+
780
+ /**
781
+ * Shell hook code generation for zsh, bash, and PowerShell.
782
+ *
783
+ * Pure functions that return shell code strings. The CLI command
784
+ * `zam monitor start/stop` calls these and prints to stdout.
785
+ */
786
+ /**
787
+ * Generate zsh hooks that capture commands to a JSONL file.
788
+ * Uses $EPOCHREALTIME for sub-second timestamp precision.
789
+ */
790
+ declare function generateZshHooks(monitorFile: string, sessionId: string): string;
791
+ /**
792
+ * Generate bash hooks that capture commands to a JSONL file.
793
+ * Uses DEBUG trap for preexec, PROMPT_COMMAND for precmd.
794
+ */
795
+ declare function generateBashHooks(monitorFile: string, sessionId: string): string;
796
+ /**
797
+ * Generate PowerShell hooks that capture completed commands to a JSONL file.
798
+ * PowerShell has no zsh-style preexec hook, so this records the most recent
799
+ * history item from the prompt function after each command completes.
800
+ */
801
+ declare function generatePowerShellHooks(monitorFile: string, sessionId: string): string;
802
+ /** Generate zsh code to remove monitor hooks. */
803
+ declare function generateZshUnhooks(): string;
804
+ /** Generate bash code to remove monitor hooks. */
805
+ declare function generateBashUnhooks(): string;
806
+ /** Generate PowerShell code to remove monitor hooks. */
807
+ declare function generatePowerShellUnhooks(): string;
808
+
809
+ /**
810
+ * Skill Discovery — identifies recurring non-standard command patterns
811
+ * across multiple sessions and proposes them as minimal reusable skills.
812
+ *
813
+ * The key insight from Increment 2: "The human's demonstrated competence
814
+ * is the gate for automation — not the other way around." A pattern must
815
+ * appear consistently across sessions before being proposed as a skill.
816
+ *
817
+ * Pure functions — no DB access. Callers provide command records and
818
+ * existing skills; this module returns proposed skills.
819
+ */
820
+
821
+ interface CommandSequence {
822
+ /** The ordered command prefixes forming the pattern (e.g., ["git checkout", "npm install", "npm run build"]) */
823
+ steps: string[];
824
+ /** How many sessions contained this sequence */
825
+ sessionCount: number;
826
+ /** Total occurrences across all sessions */
827
+ totalOccurrences: number;
828
+ /** Example full commands from the most recent occurrence */
829
+ examples: string[];
830
+ }
831
+ interface SkillProposal {
832
+ /** Suggested slug for the skill */
833
+ slug: string;
834
+ /** Human-readable description of what the pattern does */
835
+ description: string;
836
+ /** The command steps forming the skill */
837
+ steps: string[];
838
+ /** How many sessions demonstrated this pattern */
839
+ sessionCount: number;
840
+ /** Confidence that this is a real, repeatable skill */
841
+ confidence: "high" | "medium" | "low";
842
+ /** Example commands from actual usage */
843
+ examples: string[];
844
+ }
845
+ interface DiscoveryOptions {
846
+ /** Minimum number of sessions a pattern must appear in (default: 2) */
847
+ minSessions?: number;
848
+ /** Minimum sequence length to consider (default: 2) */
849
+ minSequenceLength?: number;
850
+ /** Maximum sequence length to consider (default: 5) */
851
+ maxSequenceLength?: number;
852
+ /** Existing skill slugs to exclude from proposals */
853
+ existingSkillSlugs?: string[];
452
854
  }
453
855
  /**
454
- * Create an FSRS scheduler instance.
856
+ * Discover recurring command patterns across multiple sessions.
455
857
  *
456
- * All scheduling is done through pure functions no side effects,
457
- * no database access, no mutation of the input card.
858
+ * Takes a map of session ID command records, finds command sequences
859
+ * that appear in multiple sessions, and proposes them as skills.
860
+ *
861
+ * @param sessionCommands - Map of session ID to that session's commands
862
+ * @param options - Discovery configuration
863
+ * @returns Array of skill proposals, sorted by confidence then session count
458
864
  */
459
- declare function createFSRS(params?: Partial<FSRSParameters>): FSRS;
865
+ declare function discoverSkills(sessionCommands: Map<string, CommandRecord[]>, options?: DiscoveryOptions): SkillProposal[];
460
866
 
461
867
  /**
462
868
  * Cascade Block & Unblock — prerequisite-aware blocking logic.
@@ -513,104 +919,56 @@ declare function cascadeBlock(db: Database, userId: string, tokenSlug: string):
513
919
  declare function unblockReady(db: Database, userId: string): UnblockResult;
514
920
 
515
921
  /**
516
- * Reorder items so no domain appears more than `maxConsecutive` times in a row.
517
- *
518
- * Algorithm: group items by domain, then round-robin across domain groups.
519
- * Each round picks one item from each non-exhausted domain. Within a domain,
520
- * the original order is preserved (so urgency sorting survives).
521
- *
522
- * If a domain has more items than others, its extras will appear after all
523
- * other domains are exhausted — but the `maxConsecutive` cap is still
524
- * respected by inserting items from the largest remaining domains first.
922
+ * FSRS-5 Free Spaced Repetition Scheduler (v5)
525
923
  *
526
- * @param items - Array of items to interleave. Not mutated.
527
- * @param maxConsecutive - Max consecutive items from the same domain. Defaults to 2.
528
- * @returns A new array with the same items in interleaved order.
529
- */
530
- declare function interleave<T extends {
531
- domain: string;
532
- }>(items: T[], maxConsecutive?: number): T[];
533
-
534
- /**
535
- * Review Queue Builder — assembles a session's review queue.
924
+ * Pure-function implementation of the FSRS algorithm that replaces
925
+ * the PoC's SM-2 scheduler. This is the mathematical heart of ZAM's
926
+ * spaced-repetition engine.
536
927
  *
537
- * Combines due-card fetching, new-card selection, urgency sorting,
538
- * and cross-domain interleaving into a single ready-to-review queue.
928
+ * Reference: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm
539
929
  */
540
-
541
- interface ReviewQueueOptions {
542
- userId: string;
543
- maxNew?: number;
544
- maxReviews?: number;
545
- now?: Date;
930
+ /** 1 = Again (forgot), 2 = Hard, 3 = Good, 4 = Easy */
931
+ type Rating = 1 | 2 | 3 | 4;
932
+ type CardState = "new" | "learning" | "review" | "relearning";
933
+ interface SchedulingCard {
934
+ /** Memory stability in days — expected half-life of recall probability. */
935
+ stability: number;
936
+ /** Intrinsic difficulty on a 1–10 scale. */
937
+ difficulty: number;
938
+ /** Days elapsed since the last review. */
939
+ elapsedDays: number;
940
+ /** Currently scheduled interval in days. */
941
+ scheduledDays: number;
942
+ /** Count of successful consecutive reviews. */
943
+ reps: number;
944
+ /** Times the card was forgotten (rated Again). */
945
+ lapses: number;
946
+ /** Current learning state. */
947
+ state: CardState;
948
+ /** When the card is next due. */
949
+ dueAt: Date;
950
+ /** When the card was last reviewed (null for new cards). */
951
+ lastReviewAt: Date | null;
546
952
  }
547
- interface ReviewQueueItem {
548
- cardId: string;
549
- tokenId: string;
550
- slug: string;
551
- concept: string;
552
- domain: string;
553
- bloomLevel: number;
554
- state: string;
555
- dueAt: string;
953
+ interface FSRSParameters {
954
+ /** 19 optimised weight parameters (w0 – w18). */
955
+ w: number[];
956
+ /** Target retention rate, e.g. 0.9 means we aim for 90% recall. */
957
+ requestRetention: number;
556
958
  }
557
- interface ReviewQueue {
558
- items: ReviewQueueItem[];
559
- newCount: number;
560
- reviewCount: number;
561
- relearnCount: number;
562
- totalDomains: string[];
959
+ interface FSRS {
960
+ /** Return a fully updated card after applying a rating. Pure function. */
961
+ schedule(card: SchedulingCard, rating: Rating, now?: Date): SchedulingCard;
962
+ /** The parameters baked into this instance. */
963
+ readonly params: Readonly<FSRSParameters>;
563
964
  }
564
965
  /**
565
- * Build a review queue for a user's study session.
566
- *
567
- * The queue is assembled in stages:
568
- * 1. Fetch all due cards (not blocked, due_at <= now, state in review/relearning/learning)
569
- * 2. Fetch new cards (state = 'new', not blocked) up to maxNew
570
- * 3. Sort overdue cards by urgency — most overdue first
571
- * 4. Apply cross-domain interleaving to prevent same-domain streaks
572
- * 5. Intersperse new cards at regular intervals (every 5th position)
573
- * 6. Cap total at maxReviews
574
- *
575
- * @param db - Database connection
576
- * @param options - Queue building options
577
- * @returns The assembled review queue with metadata
578
- */
579
- declare function buildReviewQueue(db: Database, options: ReviewQueueOptions): ReviewQueue;
580
-
581
- /**
582
- * Active Recall Prompt Generation
966
+ * Create an FSRS scheduler instance.
583
967
  *
584
- * Generates review prompts from tokens, adapting the question style
585
- * to the token's Bloom taxonomy level. This is NOT an LLM call —
586
- * it's template-based prompt assembly for the CLI and bridge.
587
- */
588
- type BloomLevel = 1 | 2 | 3 | 4 | 5;
589
- interface RecallPrompt {
590
- cardId: string;
591
- tokenId: string;
592
- slug: string;
593
- question: string;
594
- concept: string;
595
- domain: string;
596
- bloomLevel: BloomLevel;
597
- bloomVerb: string;
598
- hints: string[];
599
- }
600
- interface PromptInput {
601
- cardId: string;
602
- tokenId: string;
603
- slug: string;
604
- concept: string;
605
- domain: string;
606
- bloomLevel: BloomLevel;
607
- }
608
- /**
609
- * Generate a recall prompt for a token at its Bloom level.
610
- * When called from the CLI, the prompt is rendered in the terminal.
611
- * When called from the AI bridge, the JSON is returned for the AI to present conversationally.
968
+ * All scheduling is done through pure functions no side effects,
969
+ * no database access, no mutation of the input card.
612
970
  */
613
- declare function generatePrompt(input: PromptInput): RecallPrompt;
971
+ declare function createFSRS(params?: Partial<FSRSParameters>): FSRS;
614
972
 
615
973
  /**
616
974
  * Rating Evaluator
@@ -645,354 +1003,272 @@ interface EvaluateResult {
645
1003
  */
646
1004
  declare function evaluateRating(db: Database, input: EvaluateInput): EvaluateResult;
647
1005
 
648
- /**
649
- * Learning Analytics
650
- *
651
- * Progress statistics, competence tracking, and session summaries.
652
- * Ported from PoC's `stats` command with additions for FSRS and symbiosis modes.
653
- */
654
-
655
- interface UserStats {
1006
+ type ReviewActionType = "rate" | "skip" | "edit-token" | "deprecate-token" | "delete-token" | "delete-card" | "stop";
1007
+ interface ExecuteReviewActionInput {
1008
+ cardId: string;
656
1009
  userId: string;
657
- totalTokens: number;
658
- cardsInDeck: number;
659
- dueToday: number;
660
- blocked: number;
661
- mature: number;
662
- avgStability: number | null;
663
- totalSessions: number;
664
- lastSession: string | null;
1010
+ action: ReviewActionType;
1011
+ rating?: Rating;
1012
+ tokenUpdates?: UpdateTokenInput;
665
1013
  }
666
- interface DomainCompetence {
667
- domain: string;
668
- totalCards: number;
669
- matureCards: number;
670
- avgStability: number;
671
- retentionRate: number;
672
- suggestedMode: "shadowing" | "copilot" | "autonomy";
1014
+ interface ReviewActionResult {
1015
+ action: ReviewActionType;
1016
+ token: Token;
1017
+ evaluation?: EvaluateResult;
1018
+ blocked?: CascadeBlockResult;
1019
+ updatedToken?: Token;
1020
+ deletedToken?: DeleteTokenResult;
1021
+ deletedCard?: DeleteCardResult;
1022
+ skipped?: boolean;
1023
+ stopped?: boolean;
673
1024
  }
674
- /**
675
- * Get overall learning stats for a user (ported from PoC's `stats` command).
676
- */
677
- declare function getUserStats(db: Database, userId: string): UserStats;
678
- /**
679
- * Get competence per domain for a user.
680
- * Used to suggest symbiosis mode transitions.
681
- */
682
- declare function getDomainCompetence(db: Database, userId: string): DomainCompetence[];
1025
+ declare function executeReviewAction(db: Database, input: ExecuteReviewActionInput): ReviewActionResult;
683
1026
 
684
1027
  /**
685
- * Monitor log analyzer — maps observed shell commands to token ratings.
1028
+ * Active Recall Prompt Generation
686
1029
  *
687
- * Pure functions, no DB or filesystem access. Takes parsed command records
688
- * and a token-to-pattern mapping, returns ratings with evidence.
1030
+ * Generates review prompts from tokens, adapting the question style
1031
+ * to the token's Bloom taxonomy level. This is NOT an LLM call —
1032
+ * it's template-based prompt assembly for the CLI and bridge.
689
1033
  */
690
- interface MonitorEvent {
691
- type: "command_start" | "command_end" | "monitor_meta";
692
- ts: string;
693
- seq?: number;
694
- pid?: number;
695
- command?: string;
696
- cwd?: string;
697
- exit_code?: number;
698
- event?: "start" | "stop";
699
- session_id?: string;
700
- shell?: string;
701
- }
702
- interface CommandRecord {
703
- seq: number;
704
- pid: number;
705
- command: string;
706
- cwd: string;
707
- startedAt: string;
708
- endedAt: string | null;
709
- durationMs: number | null;
710
- exitCode: number | null;
711
- }
712
- interface TokenPattern {
1034
+ type BloomLevel = 1 | 2 | 3 | 4 | 5;
1035
+ interface RecallPrompt {
1036
+ cardId: string;
1037
+ tokenId: string;
713
1038
  slug: string;
714
- patterns: string[];
715
- }
716
- interface ObservationRating {
717
- tokenSlug: string;
718
- rating: 1 | 2 | 3 | 4 | null;
719
- confidence: "high" | "medium" | "low";
720
- evidence: {
721
- matchedCommands: number;
722
- helpSeeking: boolean;
723
- errorCount: number;
724
- selfCorrections: number;
725
- medianGapMs: number | null;
726
- thinkingGapMs: number | null;
727
- };
728
- matchedCommandTexts: string[];
1039
+ question: string;
1040
+ concept: string;
1041
+ domain: string;
1042
+ bloomLevel: BloomLevel;
1043
+ bloomVerb: string;
1044
+ hints: string[];
1045
+ sourceLink?: string | null;
729
1046
  }
730
- interface AnalysisResult {
731
- ratings: ObservationRating[];
732
- unmatchedCommands: string[];
733
- timeSpan: {
734
- start: string;
735
- end: string;
736
- durationMs: number;
737
- } | null;
1047
+ interface PromptInput {
1048
+ cardId: string;
1049
+ tokenId: string;
1050
+ slug: string;
1051
+ concept: string;
1052
+ domain: string;
1053
+ bloomLevel: BloomLevel;
1054
+ sourceLink?: string | null;
1055
+ question?: string | null;
738
1056
  }
739
1057
  /**
740
- * Parse a JSONL string into MonitorEvent objects.
741
- * Skips malformed lines silently.
1058
+ * Generate a template-based concept-free recall cue using the slug and domain.
742
1059
  */
743
- declare function parseMonitorLog(jsonl: string): MonitorEvent[];
1060
+ declare function generateConceptFreeCue(bloomLevel: BloomLevel, slug: string, _domain: string): string;
744
1061
  /**
745
- * Pair command_start and command_end events by (pid, seq) into CommandRecords.
1062
+ * Generate a recall prompt for a token at its Bloom level.
1063
+ * When called from the CLI, the prompt is rendered in the terminal.
1064
+ * When called from the AI bridge, the JSON is returned for the AI to present conversationally.
746
1065
  */
747
- declare function pairCommands(events: MonitorEvent[]): CommandRecord[];
1066
+ declare function generatePrompt(input: PromptInput): RecallPrompt;
1067
+
1068
+ interface ResolvedReference {
1069
+ sourceType: "local" | "remote_web" | "dynamic_search";
1070
+ content: string;
1071
+ filePath?: string;
1072
+ url?: string;
1073
+ }
748
1074
  /**
749
- * Analyze observed commands against token patterns and produce ratings.
1075
+ * A source reference resolved and bounded for inclusion in a review payload.
1076
+ * Same shape as ResolvedReference plus the originating link and a truncation flag.
750
1077
  */
751
- declare function analyzeObservation(commands: CommandRecord[], tokenPatterns: TokenPattern[]): AnalysisResult;
752
-
1078
+ interface ReviewContext {
1079
+ sourceLink: string;
1080
+ sourceType: ResolvedReference["sourceType"];
1081
+ content: string;
1082
+ filePath?: string;
1083
+ url?: string;
1084
+ truncated: boolean;
1085
+ }
1086
+ /** Default cap on resolved content length, so bridge JSON / terminal output stays bounded. */
1087
+ declare const DEFAULT_REVIEW_CONTEXT_MAX_CHARS = 6000;
753
1088
  /**
754
- * Monitor I/O read/write JSONL files for shell observation.
755
- *
756
- * Monitor logs live at ~/.zam/monitor/<session-id>.jsonl.
757
- * Separated from analyzer.ts so the analyzer remains pure-function testable.
1089
+ * Resolves a given token's source_link into readable textual content.
758
1090
  */
759
-
760
- /** Get the monitor directory path. */
761
- declare function getMonitorDir(): string;
762
- /** Get the JSONL file path for a session. */
763
- declare function getMonitorPath(sessionId: string): string;
764
- /** Ensure the monitor directory exists (mode 0700 for privacy). */
765
- declare function ensureMonitorDir(): void;
766
- /** Append a single event to the session's JSONL file. */
767
- declare function writeMonitorEvent(sessionId: string, event: MonitorEvent): void;
768
- /** Read and parse all events from a session's monitor log. */
769
- declare function readMonitorLog(sessionId: string): MonitorEvent[];
770
- /** Check if a monitor log exists for a session. */
771
- declare function monitorLogExists(sessionId: string): boolean;
772
- /** Get basic stats about a monitor log without full parsing. */
773
- declare function getMonitorLogStats(sessionId: string): {
774
- exists: boolean;
775
- sizeBytes: number;
776
- lineCount: number;
777
- };
778
-
1091
+ declare function resolveReference(sourceLink: string): Promise<ResolvedReference>;
779
1092
  /**
780
- * Shell hook code generation for zsh and bash.
1093
+ * Resolve a token's source_link into bounded, review-ready context.
781
1094
  *
782
- * Pure functions that return shell code strings. The CLI command
783
- * `zam monitor start/stop` calls these and prints to stdout.
784
- * The user wraps with `eval "$(zam monitor start ...)"`.
1095
+ * Wraps {@link resolveReference} for the review/bridge flow: returns `null`
1096
+ * for empty links and caps content length so the surrounding payload (bridge
1097
+ * JSON or terminal output) stays manageable, flagging when truncation occurred.
785
1098
  */
1099
+ declare function resolveReviewContext(sourceLink: string | null | undefined, opts?: {
1100
+ maxChars?: number;
1101
+ }): Promise<ReviewContext | null>;
786
1102
  /**
787
- * Generate zsh hooks that capture commands to a JSONL file.
788
- * Uses $EPOCHREALTIME for sub-second timestamp precision.
1103
+ * Normalizes a path, stripping anchors and converting separators.
789
1104
  */
790
- declare function generateZshHooks(monitorFile: string, sessionId: string): string;
1105
+ declare function normalizePath(p: string): string;
791
1106
  /**
792
- * Generate bash hooks that capture commands to a JSONL file.
793
- * Uses DEBUG trap for preexec, PROMPT_COMMAND for precmd.
1107
+ * Checks if a token's source_link references a changed file.
794
1108
  */
795
- declare function generateBashHooks(monitorFile: string, sessionId: string): string;
796
- /** Generate zsh code to remove monitor hooks. */
797
- declare function generateZshUnhooks(): string;
798
- /** Generate bash code to remove monitor hooks. */
799
- declare function generateBashUnhooks(): string;
1109
+ declare function matchesFilePath(sourceLink: string | null, changedFile: string): boolean;
800
1110
 
801
1111
  /**
802
- * Skill Discovery identifies recurring non-standard command patterns
803
- * across multiple sessions and proposes them as minimal reusable skills.
804
- *
805
- * The key insight from Increment 2: "The human's demonstrated competence
806
- * is the gate for automation — not the other way around." A pattern must
807
- * appear consistently across sessions before being proposed as a skill.
1112
+ * Reorder items so no domain appears more than `maxConsecutive` times in a row.
808
1113
  *
809
- * Pure functions no DB access. Callers provide command records and
810
- * existing skills; this module returns proposed skills.
811
- */
812
-
813
- interface CommandSequence {
814
- /** The ordered command prefixes forming the pattern (e.g., ["git checkout", "npm install", "npm run build"]) */
815
- steps: string[];
816
- /** How many sessions contained this sequence */
817
- sessionCount: number;
818
- /** Total occurrences across all sessions */
819
- totalOccurrences: number;
820
- /** Example full commands from the most recent occurrence */
821
- examples: string[];
822
- }
823
- interface SkillProposal {
824
- /** Suggested slug for the skill */
825
- slug: string;
826
- /** Human-readable description of what the pattern does */
827
- description: string;
828
- /** The command steps forming the skill */
829
- steps: string[];
830
- /** How many sessions demonstrated this pattern */
831
- sessionCount: number;
832
- /** Confidence that this is a real, repeatable skill */
833
- confidence: "high" | "medium" | "low";
834
- /** Example commands from actual usage */
835
- examples: string[];
836
- }
837
- interface DiscoveryOptions {
838
- /** Minimum number of sessions a pattern must appear in (default: 2) */
839
- minSessions?: number;
840
- /** Minimum sequence length to consider (default: 2) */
841
- minSequenceLength?: number;
842
- /** Maximum sequence length to consider (default: 5) */
843
- maxSequenceLength?: number;
844
- /** Existing skill slugs to exclude from proposals */
845
- existingSkillSlugs?: string[];
846
- }
847
- /**
848
- * Discover recurring command patterns across multiple sessions.
1114
+ * Algorithm: group items by domain, then round-robin across domain groups.
1115
+ * Each round picks one item from each non-exhausted domain. Within a domain,
1116
+ * the original order is preserved (so urgency sorting survives).
849
1117
  *
850
- * Takes a map of session ID command records, finds command sequences
851
- * that appear in multiple sessions, and proposes them as skills.
1118
+ * If a domain has more items than others, its extras will appear after all
1119
+ * other domains are exhausted but the `maxConsecutive` cap is still
1120
+ * respected by inserting items from the largest remaining domains first.
852
1121
  *
853
- * @param sessionCommands - Map of session ID to that session's commands
854
- * @param options - Discovery configuration
855
- * @returns Array of skill proposals, sorted by confidence then session count
1122
+ * @param items - Array of items to interleave. Not mutated.
1123
+ * @param maxConsecutive - Max consecutive items from the same domain. Defaults to 2.
1124
+ * @returns A new array with the same items in interleaved order.
856
1125
  */
857
- declare function discoverSkills(sessionCommands: Map<string, CommandRecord[]>, options?: DiscoveryOptions): SkillProposal[];
1126
+ declare function interleave<T extends {
1127
+ domain: string;
1128
+ }>(items: T[], maxConsecutive?: number): T[];
858
1129
 
859
1130
  /**
860
- * Goal file parserreads markdown files with YAML-style frontmatter.
1131
+ * Review Queue Builderassembles a session's review queue.
861
1132
  *
862
- * Goals are persisted as markdown files in the personal repo.
863
- * Each file has simple key: value frontmatter (no nested structures)
864
- * and a markdown body with description, tasks, and token references.
1133
+ * Combines due-card fetching, new-card selection, urgency sorting,
1134
+ * and cross-domain interleaving into a single ready-to-review queue.
865
1135
  */
866
- type GoalStatus = "active" | "completed" | "paused" | "abandoned";
867
- interface Goal {
1136
+
1137
+ interface ReviewQueueOptions {
1138
+ userId: string;
1139
+ maxNew?: number;
1140
+ maxReviews?: number;
1141
+ now?: Date;
1142
+ }
1143
+ interface ReviewQueueItem {
1144
+ cardId: string;
1145
+ tokenId: string;
868
1146
  slug: string;
869
- title: string;
870
- status: GoalStatus;
871
- parent: string | null;
872
- created: string;
873
- updated: string;
874
- body: string;
875
- filePath: string;
1147
+ concept: string;
1148
+ domain: string;
1149
+ bloomLevel: number;
1150
+ state: string;
1151
+ dueAt: string;
1152
+ sourceLink: string | null;
1153
+ question: string | null;
876
1154
  }
877
- interface GoalFrontmatter {
878
- title?: string;
879
- status?: string;
880
- parent?: string;
881
- created?: string;
882
- updated?: string;
1155
+ interface ReviewQueue {
1156
+ items: ReviewQueueItem[];
1157
+ newCount: number;
1158
+ reviewCount: number;
1159
+ relearnCount: number;
1160
+ totalDomains: string[];
883
1161
  }
884
1162
  /**
885
- * Parse a goal markdown file into a Goal object.
886
- *
887
- * Expected format:
888
- * ```
889
- * ---
890
- * title: Learn Rust fundamentals
891
- * status: active
892
- * parent: become-systems-programmer
893
- * created: 2026-03-28
894
- * updated: 2026-03-28
895
- * ---
1163
+ * Build a review queue for a user's study session.
896
1164
  *
897
- * ## Description
898
- * ...
899
- * ```
1165
+ * The queue is assembled in stages:
1166
+ * 1. Fetch all due cards (not blocked, due_at <= now, state in review/relearning/learning)
1167
+ * 2. Fetch new cards (state = 'new', not blocked) up to maxNew
1168
+ * 3. Sort overdue cards by urgency — most overdue first
1169
+ * 4. Apply cross-domain interleaving to prevent same-domain streaks
1170
+ * 5. Intersperse new cards at regular intervals (every 5th position)
1171
+ * 6. Cap total at maxReviews
900
1172
  *
901
- * @param content - Raw file content
902
- * @param slug - Goal slug (derived from filename by caller)
903
- * @param filePath - Absolute path to the file
1173
+ * @param db - Database connection
1174
+ * @param options - Queue building options
1175
+ * @returns The assembled review queue with metadata
904
1176
  */
905
- declare function parseGoalFile(content: string, slug: string, filePath: string): Goal;
1177
+ declare function buildReviewQueue(db: Database, options: ReviewQueueOptions): ReviewQueue;
1178
+
906
1179
  /**
907
- * Serialize a Goal back to markdown with frontmatter.
1180
+ * Get the path to ZAM's internal package SKILL.md.
908
1181
  */
909
- declare function serializeGoal(goal: Goal): string;
1182
+ declare function getPackageSkillPath(agent?: "default" | "claude" | "codex"): string;
910
1183
  /**
911
- * Extract tasks (checklist items) from goal body.
912
- * Returns items like { text: "Complete Rustlings", done: false }.
1184
+ * Distribute the ZAM active-recall training skill globally.
1185
+ * Copies SKILL.md into global directories for supported coding agents.
913
1186
  */
914
- declare function extractTasks(body: string): Array<{
915
- text: string;
916
- done: boolean;
1187
+ declare function distributeGlobalSkills(home?: string): Array<{
1188
+ name: string;
1189
+ path: string;
1190
+ success: boolean;
917
1191
  }>;
918
1192
  /**
919
- * Extract token references from goal body.
920
- * Looks for lines like `- token/slug` under a "## Tokens" section.
1193
+ * Inject the ZAM shell observation hook into user profile scripts so monitoring
1194
+ * is automatically initialized on startup.
921
1195
  */
922
- declare function extractTokenRefs(body: string): string[];
1196
+ declare function injectShellHooks(): Array<{
1197
+ shell: string;
1198
+ file: string;
1199
+ success: boolean;
1200
+ alreadyHooked: boolean;
1201
+ }>;
923
1202
 
1203
+ type SupportedLocale = "en" | "de" | "es" | "fr" | "pt" | "zh" | "ja";
924
1204
  /**
925
- * Goal Engine manages goal lifecycle via markdown files.
926
- *
927
- * Goals live as markdown files in a directory (typically the personal repo's
928
- * goals/ folder). The engine reads, creates, and updates these files.
929
- * It does not depend on the database — goals are git-tracked, not DB-tracked.
1205
+ * Clean and map raw locale string (e.g., "de_DE.UTF-8" or "en-US") to SupportedLocale.
930
1206
  */
931
-
932
- interface GoalSummary {
933
- slug: string;
934
- title: string;
935
- status: GoalStatus;
936
- parent: string | null;
937
- taskCount: number;
938
- tasksDone: number;
939
- tokenCount: number;
940
- }
941
- interface CreateGoalInput {
942
- slug: string;
943
- title: string;
944
- status?: GoalStatus;
945
- parent?: string;
946
- description?: string;
947
- }
1207
+ declare function normalizeLocale(raw: string): SupportedLocale;
948
1208
  /**
949
- * List all goals in the goals directory.
950
- * Returns summaries sorted by status (active first) then title.
1209
+ * Detect the operating system's active language code dynamically.
951
1210
  */
952
- declare function listGoals(goalsDir: string): GoalSummary[];
1211
+ declare function detectSystemLocale(): SupportedLocale;
1212
+
1213
+ type TranslationKey = "welcome" | "new_review_relearn" | "domains" | "instruction" | "quit_hint" | "offline_warning" | "offline_instruction" | "nothing_due" | "evaluating" | "generating_question" | "translating" | "prompt_answer" | "session_ended" | "session_complete" | "cards_rated" | "avg_rating" | "forgot" | "feedback_title" | "answer_title" | "keep_waiting" | "local_ai_working" | "wait_warning" | "wait_info" | "keep_waiting_llm" | "proceeding_offline" | "eval_skipped";
953
1214
  /**
954
- * Get a single goal by slug (filename without .md).
955
- * Returns undefined if the file doesn't exist.
1215
+ * Format and interpolate a translation string with key-value params.
956
1216
  */
957
- declare function getGoal(goalsDir: string, slug: string): Goal | undefined;
1217
+ declare function t(locale: SupportedLocale, key: TranslationKey, params?: Record<string, string | number>): string;
1218
+
1219
+ interface InstallResult {
1220
+ success: boolean;
1221
+ message: string;
1222
+ }
958
1223
  /**
959
- * Create a new goal file. Throws if a goal with this slug already exists.
1224
+ * Check if a command is executable on the system.
960
1225
  */
961
- declare function createGoal(goalsDir: string, input: CreateGoalInput): Goal;
1226
+ declare function hasCommand(cmd: string): boolean;
962
1227
  /**
963
- * Update a goal's status. Writes the updated file back to disk.
1228
+ * Install FastFlowLM via winget on Windows.
964
1229
  */
965
- declare function updateGoalStatus(goalsDir: string, slug: string, status: GoalStatus): Goal;
1230
+ declare function installFastFlowLM(): InstallResult;
966
1231
  /**
967
- * Get the goal tree goals organized by parent relationships.
968
- * Returns root goals (no parent) with nested children.
1232
+ * Install Ollama via Homebrew on macOS.
969
1233
  */
970
- declare function getGoalTree(goalsDir: string): Array<GoalSummary & {
971
- children: GoalSummary[];
972
- }>;
1234
+ declare function installOllama(): InstallResult;
973
1235
 
1236
+ interface SystemProfile {
1237
+ os: "windows" | "macos" | "linux" | "unknown";
1238
+ arch: "x64" | "arm64" | "unknown";
1239
+ hasRyzenNPU: boolean;
1240
+ hasAppleSilicon: boolean;
1241
+ recommendedRunner: "fastflowlm" | "ollama" | "generic";
1242
+ recommendedModel: string;
1243
+ }
974
1244
  /**
975
- * Azure DevOps connector fetches work items from ADO boards.
1245
+ * Profile the active system hardware and software capabilities.
976
1246
  */
1247
+ declare function getSystemProfile(): SystemProfile;
977
1248
 
978
- interface ADOConfig {
979
- orgUrl: string;
980
- project: string;
981
- pat: string;
1249
+ interface RepoPaths {
1250
+ personal: string | null;
1251
+ team: string | null;
1252
+ org: string | null;
982
1253
  }
983
- interface WorkItem {
984
- id: number;
985
- title: string;
986
- state: string;
987
- type: string;
988
- assignedTo: string;
989
- }
990
- /** Load ADO config from user settings. Returns null if not configured. */
991
- declare function loadADOConfig(db: Database): ADOConfig | null;
992
1254
  /**
993
- * Fetch active work items assigned to the current user.
994
- * Uses WIQL to query, then batch-fetches work item details.
1255
+ * Resolve absolute paths for personal, team, and organization repositories.
1256
+ * Personal falls back to personal.workspace_dir if repo.personal is not set.
995
1257
  */
996
- declare function fetchActiveWorkItems(config: ADOConfig): Promise<WorkItem[]>;
1258
+ declare function getRepoPaths(db: Database): RepoPaths;
1259
+ /**
1260
+ * Resolve a specific repo's path, or null if not configured.
1261
+ */
1262
+ declare function resolveRepoPath(db: Database, type: "personal" | "team" | "org"): string | null;
1263
+ /**
1264
+ * Resolve paths to all existing "/beliefs" directories in the hierarchy,
1265
+ * sorted from most specific (personal) to most general (org).
1266
+ */
1267
+ declare function resolveAllBeliefPaths(db: Database): string[];
1268
+ /**
1269
+ * Resolve paths to all existing "/goals" directories in the hierarchy,
1270
+ * sorted from most specific (personal) to most general (org).
1271
+ */
1272
+ declare function resolveAllGoalPaths(db: Database): string[];
997
1273
 
998
- export { type ADOConfig, type AgentSkill, type AnalysisResult, type BloomLevel$1 as BloomLevel, type Card, type CardState$1 as CardState, type CascadeBlockResult, type CommandRecord, type CommandSequence, type CreateAgentSkillInput, type CreateGoalInput, type CreateReviewInput, type CreateSessionInput, type CreateTokenInput, type DiscoveryOptions, type DomainCompetence, type EvaluateInput, type EvaluateResult, type ExecutionContext, type FSRSParameters, type Goal, type GoalFrontmatter, type GoalStatus, type GoalSummary, type LogStepInput, type MonitorEvent, type ObservationRating, type Prerequisite, type PrerequisiteWithToken, type PromptInput, type Rating, type RecallPrompt, type ReviewLog, type ReviewQueue, type ReviewQueueItem, type ReviewQueueOptions, type SchedulingCard, type Session, type SessionStep, type SessionSummary, type SkillProposal, type SkillSource, type SymbiosisMode, type Token, type TokenPattern, type UnblockResult, type UpdateCardInput, type UserSetting, type UserStats, type WorkItem, addPrerequisite, analyzeObservation, buildReviewQueue, cascadeBlock, createAgentSkill, createFSRS, createGoal, createToken, deleteSetting, deprecateToken, discoverSkills, endSession, ensureCard, ensureMonitorDir, evaluateRating, extractTasks, extractTokenRefs, fetchActiveWorkItems, findTokens, generateBashHooks, generateBashUnhooks, generatePrompt, generateZshHooks, generateZshUnhooks, getAgentSkill, getAllSettings, getAllSettingsDetailed, getBlockedCards, getCard, getDefaultDbPath, getDependents, getDomainCompetence, getDueCards, getGoal, getGoalTree, getMonitorDir, getMonitorLogStats, getMonitorPath, getPrerequisites, getReviewsForCard, getReviewsForUser, getSessionSummary, getSetting, getTokenById, getTokenBySlug, getUserStats, interleave, listAgentSkills, listGoals, listTokens, loadADOConfig, logReview, logStep, monitorLogExists, openDatabase, openDatabaseWithSync, pairCommands, parseGoalFile, parseMonitorLog, readMonitorLog, serializeGoal, setSetting, startSession, unblockReady, updateCard, updateGoalStatus, writeMonitorEvent };
1274
+ export { type ADOConfig, type ADOCredentials, type AgentSkill, type AnalysisResult, type BloomLevel$1 as BloomLevel, type Card, type CardDeletionImpact, type CardState$1 as CardState, type CascadeBlockResult, type CommandRecord, type CommandSequence, type CreateAgentSkillInput, type CreateGoalInput, type CreateReviewInput, type CreateSessionInput, type CreateTokenInput, type Credentials, DEFAULT_REVIEW_CONTEXT_MAX_CHARS, type DeleteCardResult, type DeleteTokenResult, type DiscoveryOptions, type DomainCompetence, type EvaluateInput, type EvaluateResult, type ExecuteReviewActionInput, type ExecutionContext, type FSRSParameters, type Goal, type GoalFrontmatter, type GoalStatus, type GoalSummary, type InstallResult, type LogStepInput, type MonitorEvent, type ObservationRating, type Prerequisite, type PrerequisiteWithToken, type PromptInput, type Rating, type RecallPrompt, type RepoPaths, type ResolvedReference, type ReviewActionResult, type ReviewActionType, type ReviewContext, type ReviewLog, type ReviewQueue, type ReviewQueueItem, type ReviewQueueOptions, type SchedulingCard, type Session, type SessionStep, type SessionSummary, type SkillProposal, type SkillSource, type SupportedLocale, type SymbiosisMode, type SystemProfile, type Token, type TokenDeleteImpact, type TokenPattern, type TranslationKey, type TursoCredentials, type UnblockResult, type UpdateCardInput, type UpdateTokenInput, type UserSetting, type UserStats, type WorkItem, addPrerequisite, analyzeObservation, buildReviewQueue, cascadeBlock, clearADOCredentials, clearTursoCredentials, createAgentSkill, createFSRS, createGoal, createToken, deleteCardForUser, deleteSetting, deleteToken, deprecateToken, detectSystemLocale, discoverSkills, distributeGlobalSkills, endSession, ensureCard, ensureMonitorDir, evaluateRating, executeReviewAction, extractTasks, extractTokenRefs, fetchActiveWorkItems, findTokens, generateBashHooks, generateBashUnhooks, generateConceptFreeCue, generatePowerShellHooks, generatePowerShellUnhooks, generatePrompt, generateZshHooks, generateZshUnhooks, getADOCredentials, getAgentSkill, getAllSettings, getAllSettingsDetailed, getBlockedCards, getCard, getCardById, getCardDeletionImpact, getDefaultDbPath, getDependents, getDomainCompetence, getDueCards, getGoal, getGoalTree, getMonitorDir, getMonitorLogStats, getMonitorPath, getPackageSkillPath, getPrerequisites, getRepoPaths, getReviewsForCard, getReviewsForUser, getSessionSummary, getSetting, getSystemProfile, getTokenById, getTokenBySlug, getTokenDeleteImpact, getTursoCredentials, getUserStats, hasCommand, injectShellHooks, installFastFlowLM, installOllama, interleave, listAgentSkills, listGoals, listTokens, loadADOConfig, loadCredentials, logReview, logStep, matchesFilePath, monitorLogExists, normalizeLocale, normalizePath, openDatabase, openDatabaseWithSync, pairCommands, parseGoalFile, parseMonitorLog, readMonitorLog, resolveAllBeliefPaths, resolveAllGoalPaths, resolveReference, resolveRepoPath, resolveReviewContext, saveCredentials, serializeGoal, setADOCredentials, setSetting, setTursoCredentials, startSession, t, unblockReady, updateCard, updateGoalStatus, updateToken, writeMonitorEvent };