chat-recall 0.0.1 → 0.4.8

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.
Files changed (94) hide show
  1. package/LICENSE +93 -0
  2. package/dist/auto-update.d.ts +52 -0
  3. package/dist/auto-update.d.ts.map +1 -0
  4. package/dist/auto-update.js +157 -0
  5. package/dist/auto-update.js.map +1 -0
  6. package/dist/cli.d.ts +6 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +26102 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/client-events.d.ts +11 -0
  11. package/dist/client-events.d.ts.map +1 -0
  12. package/dist/client-events.js +77 -0
  13. package/dist/client-events.js.map +1 -0
  14. package/dist/collector-migrate.d.ts +27 -0
  15. package/dist/collector-migrate.d.ts.map +1 -0
  16. package/dist/collector-migrate.js +60 -0
  17. package/dist/collector-migrate.js.map +1 -0
  18. package/dist/device-auth.d.ts +37 -0
  19. package/dist/device-auth.d.ts.map +1 -0
  20. package/dist/device-auth.js +161 -0
  21. package/dist/device-auth.js.map +1 -0
  22. package/dist/escalate.d.ts +99 -0
  23. package/dist/escalate.d.ts.map +1 -0
  24. package/dist/escalate.js +219 -0
  25. package/dist/escalate.js.map +1 -0
  26. package/dist/http.d.ts +14 -0
  27. package/dist/http.d.ts.map +1 -0
  28. package/dist/http.js +49 -0
  29. package/dist/http.js.map +1 -0
  30. package/dist/install-skills.d.ts +51 -0
  31. package/dist/install-skills.d.ts.map +1 -0
  32. package/dist/install-skills.js +223 -0
  33. package/dist/install-skills.js.map +1 -0
  34. package/dist/intent-drain.d.ts +25 -0
  35. package/dist/intent-drain.d.ts.map +1 -0
  36. package/dist/intent-drain.js +189 -0
  37. package/dist/intent-drain.js.map +1 -0
  38. package/dist/keyring.d.ts +28 -0
  39. package/dist/keyring.d.ts.map +1 -0
  40. package/dist/keyring.js +163 -0
  41. package/dist/keyring.js.map +1 -0
  42. package/dist/mcp.d.ts +8 -0
  43. package/dist/mcp.d.ts.map +1 -0
  44. package/dist/mcp.js +23665 -0
  45. package/dist/mcp.js.map +1 -0
  46. package/dist/project-tasks.d.ts +54 -0
  47. package/dist/project-tasks.d.ts.map +1 -0
  48. package/dist/project-tasks.js +204 -0
  49. package/dist/project-tasks.js.map +1 -0
  50. package/dist/repair.d.ts +73 -0
  51. package/dist/repair.d.ts.map +1 -0
  52. package/dist/repair.js +267 -0
  53. package/dist/repair.js.map +1 -0
  54. package/dist/self-restart.d.ts +91 -0
  55. package/dist/self-restart.d.ts.map +1 -0
  56. package/dist/self-restart.js +137 -0
  57. package/dist/self-restart.js.map +1 -0
  58. package/dist/service-installer.d.ts +32 -0
  59. package/dist/service-installer.d.ts.map +1 -0
  60. package/dist/service-installer.js +231 -0
  61. package/dist/service-installer.js.map +1 -0
  62. package/dist/sync-client.d.ts +234 -0
  63. package/dist/sync-client.d.ts.map +1 -0
  64. package/dist/sync-client.js +1985 -0
  65. package/dist/sync-client.js.map +1 -0
  66. package/dist/sync-ledger.d.ts +131 -0
  67. package/dist/sync-ledger.d.ts.map +1 -0
  68. package/dist/sync-ledger.js +366 -0
  69. package/dist/sync-ledger.js.map +1 -0
  70. package/dist/update-notice.d.ts +16 -0
  71. package/dist/update-notice.d.ts.map +1 -0
  72. package/dist/update-notice.js +107 -0
  73. package/dist/update-notice.js.map +1 -0
  74. package/dist/verify-deep.d.ts +79 -0
  75. package/dist/verify-deep.d.ts.map +1 -0
  76. package/dist/verify-deep.js +97 -0
  77. package/dist/verify-deep.js.map +1 -0
  78. package/dist/verify-repair.d.ts +21 -0
  79. package/dist/verify-repair.d.ts.map +1 -0
  80. package/dist/verify-repair.js +58 -0
  81. package/dist/verify-repair.js.map +1 -0
  82. package/dist/watch.js +21550 -0
  83. package/hooks/chat_recall_escalate_hook.sh +51 -0
  84. package/hooks/chat_recall_resume_hook.sh +69 -0
  85. package/hooks/chat_recall_save_hook.sh +254 -0
  86. package/hooks/chat_recall_wakeup_hook.sh +39 -0
  87. package/package.json +38 -4
  88. package/skills/chat-recall/SKILL.md +76 -0
  89. package/skills/chat-recall-changes/SKILL.md +50 -0
  90. package/skills/chat-recall-find/SKILL.md +64 -0
  91. package/skills/chat-recall-memory/SKILL.md +55 -0
  92. package/skills/chat-recall-project/SKILL.md +75 -0
  93. package/skills/chat-recall-resume/SKILL.md +62 -0
  94. package/README.md +0 -7
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Install the bundled chat-recall Agent Skills into every local AI tool.
3
+ *
4
+ * chat-recall ships a set of SKILL.md skills (../skills, bundled in the npm
5
+ * package) that teach any agent when/how to use the recall_* MCP tools. Every
6
+ * supported tool reads the SAME SKILL.md shape from its own skills dir, so one
7
+ * set covers Claude Code, Gemini, Codex, OpenCode and Antigravity. We drop the
8
+ * files straight into each tool's dir (the only mechanism that works across all
9
+ * of them — Claude *plugins* are Claude-only).
10
+ *
11
+ * Idempotent + updatable + uninstallable despite drop-in having no native
12
+ * versioning: each installed skill dir gets a `.chat-recall-managed` marker
13
+ * holding the version. We only ever overwrite/remove dirs that carry OUR marker,
14
+ * so a user's own skill of the same name is never clobbered.
15
+ */
16
+ import { readdirSync, existsSync, mkdirSync, rmSync, cpSync, writeFileSync, readFileSync, statSync, realpathSync } from 'fs';
17
+ import { join, basename } from 'path';
18
+ import { claudeHomeDirs } from '@chat-recall/engine/core/tool-paths.js';
19
+ import { claudeBackend } from '@chat-recall/engine/core/backends/claude.js';
20
+ import { geminiBackend } from '@chat-recall/engine/core/backends/gemini.js';
21
+ import { opencodeBackend } from '@chat-recall/engine/core/backends/opencode.js';
22
+ import { codexBackend } from '@chat-recall/engine/core/backends/codex.js';
23
+ import { agyBackend } from '@chat-recall/engine/core/backends/agy.js';
24
+ // NOTE: Antigravity (agy) has no skills dir of its own — it reads Gemini's
25
+ // ~/.gemini/skills (same decision as team-merge.installPathFor). So the Gemini
26
+ // target covers agy; we surface agy only for the "available" hint.
27
+ const MARKER = '.chat-recall-managed';
28
+ /** Bundled skills source dir: <package-root>/skills (dist/install-skills.js → ..). */
29
+ export function skillsSourceDir() {
30
+ return join(import.meta.dirname, '..', 'skills');
31
+ }
32
+ function version() {
33
+ try {
34
+ return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version || '0.0.0';
35
+ }
36
+ catch {
37
+ return '0.0.0';
38
+ }
39
+ }
40
+ /** The skill directory names we ship (any dir under skills/ containing SKILL.md). */
41
+ export function bundledSkillNames() {
42
+ const src = skillsSourceDir();
43
+ if (!existsSync(src))
44
+ return [];
45
+ return readdirSync(src, { withFileTypes: true })
46
+ .filter((e) => e.isDirectory() && existsSync(join(src, e.name, 'SKILL.md')))
47
+ .map((e) => e.name)
48
+ .sort();
49
+ }
50
+ /** Resolve symlinks so two targets that name the SAME directory collapse to one.
51
+ * A dir that does not exist yet has nothing to resolve — use it as written. */
52
+ function realPathOrSelf(p) {
53
+ try {
54
+ return realpathSync(p);
55
+ }
56
+ catch {
57
+ return p;
58
+ }
59
+ }
60
+ /**
61
+ * Claude profile targets: one per home returned by `claudeHomeDirs()`.
62
+ *
63
+ * Claude is the only backend with profiles. `CLAUDE_CONFIG_DIR=~/.claude-work`
64
+ * makes that directory the ENTIRE config root for the session — Claude Code
65
+ * reads skills from `<that home>/skills` and nowhere else. So installing into
66
+ * `~/.claude/skills` alone means a profile session sees zero recall skills,
67
+ * while its transcripts index normally and every other check looks green.
68
+ *
69
+ * Deduped by REAL path because the usual multi-profile setup symlinks each
70
+ * profile's `skills/` back to the primary. Without this, one directory would be
71
+ * reported as four separate installs.
72
+ */
73
+ function claudeSkillTargets() {
74
+ const targets = [];
75
+ const seen = new Set();
76
+ for (const home of claudeHomeDirs()) {
77
+ const dir = join(home, 'skills');
78
+ const key = realPathOrSelf(dir);
79
+ if (seen.has(key))
80
+ continue;
81
+ seen.add(key);
82
+ const primary = targets.length === 0;
83
+ targets.push({
84
+ id: primary ? 'claude' : `claude:${basename(home)}`,
85
+ label: primary ? 'Claude Code' : `Claude Code (${basename(home)})`,
86
+ dir,
87
+ // Judge each profile on ITS OWN transcripts, not the primary's: a home
88
+ // with no projects/ is a directory someone made, not a Claude install.
89
+ available: existsSync(join(home, 'projects')),
90
+ });
91
+ }
92
+ // A machine with no Claude home at all still gets the primary target, so
93
+ // `--all-tools` can install ahead of first launch.
94
+ if (targets.length === 0) {
95
+ targets.push({ id: 'claude', label: 'Claude Code', dir: claudeBackend.skillsDir(), available: claudeBackend.isAvailable() });
96
+ }
97
+ return targets;
98
+ }
99
+ /** Per-tool skills dir. agy keeps its own (~/.gemini/antigravity-cli/skills) AND
100
+ * we also cover ~/.agents/skills (tool-neutral) so any future consumer sees them. */
101
+ export function skillTargets() {
102
+ return [
103
+ // Per-tool dirs ONLY. We deliberately do NOT also write ~/.agents/skills:
104
+ // some tools (e.g. Gemini) read BOTH ~/.agents/skills and their own dir, so
105
+ // installing to both produces "skill conflict" duplicate warnings. Every tool
106
+ // we support has its own dir, so per-tool coverage is complete.
107
+ // Antigravity (agy) has no dir of its own — it reads Gemini's ~/.gemini/skills
108
+ // (same mapping as team-merge), so the Gemini target covers it.
109
+ ...claudeSkillTargets(),
110
+ { id: 'gemini', label: 'Gemini CLI / Antigravity', dir: geminiBackend.skillsDir(), available: geminiBackend.isAvailable() || agyBackend.isAvailable() },
111
+ { id: 'codex', label: 'Codex', dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
112
+ { id: 'opencode', label: 'OpenCode', dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
113
+ ];
114
+ }
115
+ /** True if a dir is a chat-recall-managed skill (safe to overwrite/remove). */
116
+ function isManaged(dir) {
117
+ return existsSync(join(dir, MARKER));
118
+ }
119
+ /** Install (or update) the bundled skills into every target tool.
120
+ * @param onlyAvailable install only to tools detected on this machine (default true). */
121
+ export function installSkills(opts = {}) {
122
+ const onlyAvailable = opts.onlyAvailable ?? true;
123
+ const src = skillsSourceDir();
124
+ const names = bundledSkillNames();
125
+ const v = version();
126
+ const perTarget = [];
127
+ for (const t of skillTargets()) {
128
+ const installed = [];
129
+ const skippedUserOwned = [];
130
+ if (onlyAvailable && !t.available) {
131
+ perTarget.push({ ...t, installed, skippedUserOwned });
132
+ continue;
133
+ }
134
+ for (const name of names) {
135
+ const dest = join(t.dir, name);
136
+ // Never clobber a user's own skill that happens to share the name.
137
+ if (existsSync(dest) && !isManaged(dest)) {
138
+ skippedUserOwned.push(name);
139
+ continue;
140
+ }
141
+ mkdirSync(t.dir, { recursive: true });
142
+ rmSync(dest, { recursive: true, force: true });
143
+ cpSync(join(src, name), dest, { recursive: true });
144
+ writeFileSync(join(dest, MARKER), `${v}\n`);
145
+ installed.push(name);
146
+ }
147
+ perTarget.push({ ...t, installed, skippedUserOwned });
148
+ }
149
+ return { version: v, perTarget };
150
+ }
151
+ /** Remove only chat-recall-managed skills (identified by the marker). */
152
+ export function uninstallSkills() {
153
+ const names = new Set(bundledSkillNames());
154
+ const perTarget = [];
155
+ for (const t of skillTargets()) {
156
+ const removed = [];
157
+ if (!existsSync(t.dir)) {
158
+ perTarget.push({ id: t.id, label: t.label, removed });
159
+ continue;
160
+ }
161
+ for (const entry of readdirSync(t.dir, { withFileTypes: true })) {
162
+ if (!entry.isDirectory())
163
+ continue;
164
+ const dir = join(t.dir, entry.name);
165
+ // Remove anything we manage; restrict to our known names as a safety belt.
166
+ if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith('chat-recall'))) {
167
+ rmSync(dir, { recursive: true, force: true });
168
+ removed.push(entry.name);
169
+ }
170
+ }
171
+ perTarget.push({ id: t.id, label: t.label, removed });
172
+ }
173
+ return { perTarget };
174
+ }
175
+ /** True if any AVAILABLE tool is missing a bundled skill or has an out-of-date
176
+ * version. The MCP boot uses this to refresh only when needed (cheap: stat a
177
+ * marker per skill), so it doesn't churn the disk on every tool launch. */
178
+ export function skillsNeedRefresh() {
179
+ const cur = version();
180
+ const names = bundledSkillNames();
181
+ if (names.length === 0)
182
+ return false;
183
+ for (const t of skillTargets()) {
184
+ if (!t.available)
185
+ continue;
186
+ for (const name of names) {
187
+ const marker = join(t.dir, name, MARKER);
188
+ if (!existsSync(marker))
189
+ return true;
190
+ try {
191
+ if (readFileSync(marker, 'utf-8').trim() !== cur)
192
+ return true;
193
+ }
194
+ catch {
195
+ return true;
196
+ }
197
+ }
198
+ }
199
+ return false;
200
+ }
201
+ /** For `doctor`: which tools have the current skill version installed. */
202
+ export function skillStatus() {
203
+ const names = bundledSkillNames();
204
+ const cur = version();
205
+ return skillTargets().map((t) => {
206
+ let installed = 0;
207
+ let ver = null;
208
+ for (const name of names) {
209
+ const marker = join(t.dir, name, MARKER);
210
+ if (existsSync(marker)) {
211
+ installed++;
212
+ try {
213
+ ver = readFileSync(marker, 'utf-8').trim();
214
+ }
215
+ catch { /* ignore */ }
216
+ }
217
+ }
218
+ return { id: t.id, label: t.label, available: t.available, installed, version: installed === names.length ? ver : (installed ? `${ver} (partial)` : null) };
219
+ }).filter((s) => s.available || s.installed > 0);
220
+ }
221
+ // Silence unused import in some toolchains; statSync kept for future mtime checks.
222
+ void statSync;
223
+ //# sourceMappingURL=install-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../src/install-skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Cross-tool sync intent drainer (Model B, local executor side).
3
+ *
4
+ * The web UI (local or SaaS) enqueues intents on the server. This runs on the
5
+ * user's machine — where the actual `~/.claude` / `~/.config/opencode` dirs
6
+ * live — polls each logged-in server for pending intents, performs the
7
+ * filesystem copy via the engine executor, and acks status back.
8
+ *
9
+ * Invoked on a short interval from the watch daemon, and once via the
10
+ * `chat-recall toolkit drain` command.
11
+ */
12
+ export interface DrainResult {
13
+ processed: number;
14
+ done: number;
15
+ errored: number;
16
+ }
17
+ /**
18
+ * Poll every logged-in server once, execute pending intents locally, ack each.
19
+ * Best-effort: network failures are swallowed (the intent stays pending and is
20
+ * retried on the next drain since it was never acked).
21
+ */
22
+ export declare function drainSyncIntents(opts?: {
23
+ verbose?: boolean;
24
+ }): Promise<DrainResult>;
25
+ //# sourceMappingURL=intent-drain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-drain.d.ts","sourceRoot":"","sources":["../src/intent-drain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA+DH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAmDD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA4D7F"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Cross-tool sync intent drainer (Model B, local executor side).
3
+ *
4
+ * The web UI (local or SaaS) enqueues intents on the server. This runs on the
5
+ * user's machine — where the actual `~/.claude` / `~/.config/opencode` dirs
6
+ * live — polls each logged-in server for pending intents, performs the
7
+ * filesystem copy via the engine executor, and acks status back.
8
+ *
9
+ * Invoked on a short interval from the watch daemon, and once via the
10
+ * `chat-recall toolkit drain` command.
11
+ */
12
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';
13
+ import { fetchWithTimeout } from './http.js';
14
+ import { join } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+ import { loadAllCredentials, syncIncremental } from './sync-client.js';
17
+ // Concrete module, NOT the engine barrel: the barrel statically re-exports
18
+ // MemoryIndex -> @lancedb/lancedb, and a static barrel import hoists that
19
+ // native dep to boot-time in the published bundle (breaks fresh installs).
20
+ import { executeSyncAll, executeCopy, } from '@chat-recall/engine/core/toolkit-sync.js';
21
+ import { pushProjectTaskStatuses } from './project-tasks.js';
22
+ /** Apply a code recommendation locally. Currently: append a rule to the
23
+ * project's CLAUDE.md (idempotent — skips if the exact rule is already there). */
24
+ function applyCodeRecommendation(intent) {
25
+ try {
26
+ const meta = JSON.parse(intent.name || '{}');
27
+ if (intent.artifact_type === 'write_tasks_file') {
28
+ if (!meta.rootPath || !meta.content)
29
+ return { status: 'error', result: JSON.stringify({ error: 'missing rootPath or content' }) };
30
+ const file = join(meta.rootPath, meta.filename || 'CODE_TASKS.md');
31
+ writeFileSync(file, meta.content); // overwrite — it's a regenerated task list
32
+ return { status: 'done', result: JSON.stringify({ wrote: file }) };
33
+ }
34
+ if (intent.artifact_type === 'append_claude_md') {
35
+ const text = meta.payload?.text?.trim();
36
+ const isGlobal = meta.global || meta.payload?.global;
37
+ if (!text)
38
+ return { status: 'error', result: JSON.stringify({ error: 'missing text' }) };
39
+ // global → ~/.claude/CLAUDE.md (user-wide); else the project's CLAUDE.md.
40
+ let file;
41
+ if (isGlobal) {
42
+ const dir = join(homedir(), '.claude');
43
+ try {
44
+ mkdirSync(dir, { recursive: true });
45
+ }
46
+ catch { /* exists */ }
47
+ file = join(dir, 'CLAUDE.md');
48
+ }
49
+ else {
50
+ if (!meta.rootPath)
51
+ return { status: 'error', result: JSON.stringify({ error: 'missing rootPath' }) };
52
+ file = join(meta.rootPath, 'CLAUDE.md');
53
+ }
54
+ const existing = existsSync(file) ? readFileSync(file, 'utf8') : '';
55
+ if (existing.includes(text))
56
+ return { status: 'done', result: JSON.stringify({ skipped: 'rule already present', file }) };
57
+ const block = `${existing && !existing.endsWith('\n') ? '\n' : ''}\n## Rule (added by chat-recall recommendation)\n${text}\n`;
58
+ if (existing)
59
+ appendFileSync(file, block);
60
+ else
61
+ writeFileSync(file, `# ${(meta.rootPath?.split('/').pop()) || 'Global'} instructions\n${block}`);
62
+ return { status: 'done', result: JSON.stringify({ appended: file }) };
63
+ }
64
+ return { status: 'error', result: JSON.stringify({ error: `unsupported code_apply action: ${intent.artifact_type}` }) };
65
+ }
66
+ catch (e) {
67
+ return { status: 'error', result: JSON.stringify({ error: e instanceof Error ? e.message : 'failed' }) };
68
+ }
69
+ }
70
+ /** Run one intent and return the (status, result-json) to ack with. */
71
+ async function runIntent(intent, ctx) {
72
+ try {
73
+ if (intent.kind === 'recheck_session') {
74
+ // The server has a thin/absent copy of this session and is asking us to
75
+ // re-verify from THIS machine (disk + shadow = the fullest local truth)
76
+ // and re-push if we have more. repairSession does exactly that, scoped to
77
+ // the requesting server, writing (not a dry run).
78
+ const id = intent.name;
79
+ if (!id)
80
+ return { status: 'error', result: JSON.stringify({ error: 'recheck missing session id' }) };
81
+ const { repairSession } = await import('./repair.js');
82
+ const r = await repairSession(id, { dryRun: false, server: ctx.base });
83
+ return { status: r.status === 'error' ? 'error' : 'done', result: JSON.stringify(r) };
84
+ }
85
+ if (intent.kind === 'code_apply') {
86
+ return applyCodeRecommendation(intent);
87
+ }
88
+ if (intent.kind === 'sync_all') {
89
+ const report = await executeSyncAll();
90
+ const status = report.failed.length > 0 ? 'error' : 'done';
91
+ return { status, result: JSON.stringify({ copied: report.copied.length, skipped: report.skipped.length, failed: report.failed }) };
92
+ }
93
+ // copy
94
+ if (!intent.artifact_type || !intent.name || !intent.from_tool || !intent.to_tool) {
95
+ return { status: 'error', result: JSON.stringify({ error: 'copy intent missing fields' }) };
96
+ }
97
+ const r = await executeCopy(intent.artifact_type, intent.name, intent.from_tool, intent.to_tool);
98
+ // 409 (already exists) is a benign skip, not a failure.
99
+ const status = r.ok || r.status === 409 ? 'done' : 'error';
100
+ return { status, result: JSON.stringify(r) };
101
+ }
102
+ catch (e) {
103
+ return { status: 'error', result: JSON.stringify({ error: e instanceof Error ? e.message : 'failed' }) };
104
+ }
105
+ }
106
+ import { fileURLToPath } from 'node:url';
107
+ import { dirname } from 'node:path';
108
+ let ownVersion = '0.0.0';
109
+ try {
110
+ const dir = dirname(fileURLToPath(import.meta.url));
111
+ ownVersion = JSON.parse(readFileSync(join(dir, '../package.json'), 'utf-8')).version || ownVersion;
112
+ }
113
+ catch {
114
+ try {
115
+ // @ts-ignore
116
+ if (typeof __CLI_VERSION__ === 'string')
117
+ ownVersion = __CLI_VERSION__;
118
+ }
119
+ catch { }
120
+ }
121
+ /**
122
+ * Poll every logged-in server once, execute pending intents locally, ack each.
123
+ * Best-effort: network failures are swallowed (the intent stays pending and is
124
+ * retried on the next drain since it was never acked).
125
+ */
126
+ export async function drainSyncIntents(opts = {}) {
127
+ const out = { processed: 0, done: 0, errored: 0 };
128
+ for (const cred of loadAllCredentials()) {
129
+ const base = cred.serverUrl.replace(/\/+$/, '');
130
+ const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
131
+ let pending;
132
+ try {
133
+ const res = await fetchWithTimeout(`${base}/api/sync-intents/pending`, { headers: authHeaders });
134
+ if (!res.ok)
135
+ continue;
136
+ const data = (await res.json());
137
+ pending = data.intents || [];
138
+ if (data.cli && data.cli.version) {
139
+ const { planAutoUpdate, runAutoUpdate } = await import('./auto-update.js');
140
+ const plan = planAutoUpdate(base, { cli: data.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
141
+ if (plan.update) {
142
+ void runAutoUpdate(base, authHeaders, ownVersion).catch(() => { });
143
+ }
144
+ }
145
+ }
146
+ catch {
147
+ continue; // server unreachable — try again next tick
148
+ }
149
+ for (const intent of pending) {
150
+ const { status, result } = await runIntent(intent, { base });
151
+ out.processed++;
152
+ if (status === 'done')
153
+ out.done++;
154
+ else
155
+ out.errored++;
156
+ if (opts.verbose) {
157
+ const label = intent.kind === 'sync_all' ? 'sync_all'
158
+ : intent.kind === 'recheck_session' ? `recheck ${intent.name}`
159
+ : `${intent.artifact_type} "${intent.name}" ${intent.from_tool}→${intent.to_tool}`;
160
+ console.error(`[sync-intent] ${label}: ${status}`);
161
+ }
162
+ try {
163
+ await fetchWithTimeout(`${base}/api/sync-intents/${intent.id}/ack`, {
164
+ method: 'POST',
165
+ headers: { 'content-type': 'application/json', ...authHeaders },
166
+ body: JSON.stringify({ status, result }),
167
+ });
168
+ }
169
+ catch {
170
+ // Ack failed — leave it; it'll be re-pulled and re-run (copy is
171
+ // idempotent: an already-present target acks as a 409 skip).
172
+ }
173
+ }
174
+ // Read-back: push any SECURITY_TASKS.md / CODE_TASKS.md edits (checkbox/
175
+ // status) up to the server — the file→server half of the two-way sync.
176
+ try {
177
+ await pushProjectTaskStatuses(base, authHeaders, { verbose: opts.verbose });
178
+ }
179
+ catch { /* never let read-back abort the drain */ }
180
+ }
181
+ if (out.processed > 0) {
182
+ try {
183
+ await syncIncremental({ scope: 'changed' });
184
+ }
185
+ catch { /* best-effort */ }
186
+ }
187
+ return out;
188
+ }
189
+ //# sourceMappingURL=intent-drain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-drain.js","sourceRoot":"","sources":["../src/intent-drain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export type KeyringBackendId = 'macos-keychain' | 'libsecret' | 'windows-dpapi' | 'none';
2
+ export interface KeyringProbe {
3
+ backend: KeyringBackendId;
4
+ available: boolean;
5
+ /** Actionable one-liner when unavailable — shown verbatim to the user. */
6
+ hint?: string;
7
+ }
8
+ /** Injectable process runner (tests pass a fake; nothing else should). */
9
+ export interface Runner {
10
+ /** Run `file args…`, optionally writing `input` to stdin. Returns stdout. */
11
+ run(file: string, args: string[], input?: string): string;
12
+ platform: NodeJS.Platform;
13
+ }
14
+ /** Which backend this platform uses, and whether it's actually usable here. */
15
+ export declare function probeKeyring(r?: Runner): KeyringProbe;
16
+ /** Thrown when the keyring is unusable. Callers surface .message as-is. */
17
+ export declare class KeyringUnavailableError extends Error {
18
+ }
19
+ export declare function keyringSet(secret: string, r?: Runner): void;
20
+ /** The stored passphrase, or null when absent. Throws only if unusable. */
21
+ export declare function keyringGet(r?: Runner): string | null;
22
+ /** Remove the stored passphrase. True if something was removed. */
23
+ export declare function keyringDelete(r?: Runner): boolean;
24
+ /** Single-quote for the `security -i` command line (POSIX-ish tokenizer). */
25
+ export declare function shQuote(s: string): string;
26
+ /** Single-quote for PowerShell, where '' is the escaped quote. */
27
+ export declare function psQuote(s: string): string;
28
+ //# sourceMappingURL=keyring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyring.d.ts","sourceRoot":"","sources":["../src/keyring.ts"],"names":[],"mappings":"AA+BA,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,CAAC;AAEzF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,0EAA0E;AAC1E,MAAM,WAAW,MAAM;IACrB,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B;AAaD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,CAAC,GAAE,MAAmB,GAAG,YAAY,CAyBjE;AAED,2EAA2E;AAC3E,qBAAa,uBAAwB,SAAQ,KAAK;CAAG;AAiBrD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAE,MAAmB,GAAG,IAAI,CAiBvE;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,CAAC,GAAE,MAAmB,GAAG,MAAM,GAAG,IAAI,CAmBhE;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,CAAC,GAAE,MAAmB,GAAG,OAAO,CAgB7D;AAID,6EAA6E;AAC7E,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC"}