ricord 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/commands/ricord-flush.md +29 -0
  4. package/commands/ricord-init.md +129 -0
  5. package/commands/ricord-lint.md +64 -0
  6. package/commands/ricord-query.md +71 -0
  7. package/dist/cli/auth.d.ts +16 -0
  8. package/dist/cli/auth.js +42 -0
  9. package/dist/cli/auth.js.map +1 -0
  10. package/dist/cli/bundle.d.ts +25 -0
  11. package/dist/cli/bundle.js +179 -0
  12. package/dist/cli/bundle.js.map +1 -0
  13. package/dist/cli/cache.d.ts +18 -0
  14. package/dist/cli/cache.js +39 -0
  15. package/dist/cli/cache.js.map +1 -0
  16. package/dist/cli/cli.d.ts +21 -0
  17. package/dist/cli/cli.js +355 -0
  18. package/dist/cli/cli.js.map +1 -0
  19. package/dist/cli/client.d.ts +12 -0
  20. package/dist/cli/client.js +35 -0
  21. package/dist/cli/client.js.map +1 -0
  22. package/dist/cli/commands/build.d.ts +44 -0
  23. package/dist/cli/commands/build.js +437 -0
  24. package/dist/cli/commands/build.js.map +1 -0
  25. package/dist/cli/commands/curate.d.ts +32 -0
  26. package/dist/cli/commands/curate.js +154 -0
  27. package/dist/cli/commands/curate.js.map +1 -0
  28. package/dist/cli/commands/doctor.d.ts +16 -0
  29. package/dist/cli/commands/doctor.js +92 -0
  30. package/dist/cli/commands/doctor.js.map +1 -0
  31. package/dist/cli/commands/ingest.d.ts +25 -0
  32. package/dist/cli/commands/ingest.js +121 -0
  33. package/dist/cli/commands/ingest.js.map +1 -0
  34. package/dist/cli/commands/install.d.ts +16 -0
  35. package/dist/cli/commands/install.js +82 -0
  36. package/dist/cli/commands/install.js.map +1 -0
  37. package/dist/cli/commands/pull.d.ts +24 -0
  38. package/dist/cli/commands/pull.js +104 -0
  39. package/dist/cli/commands/pull.js.map +1 -0
  40. package/dist/cli/commands/push.d.ts +28 -0
  41. package/dist/cli/commands/push.js +164 -0
  42. package/dist/cli/commands/push.js.map +1 -0
  43. package/dist/cli/commands/rollup.d.ts +21 -0
  44. package/dist/cli/commands/rollup.js +118 -0
  45. package/dist/cli/commands/rollup.js.map +1 -0
  46. package/dist/cli/commands/setup.d.ts +7 -0
  47. package/dist/cli/commands/setup.js +43 -0
  48. package/dist/cli/commands/setup.js.map +1 -0
  49. package/dist/cli/commands/sync.d.ts +15 -0
  50. package/dist/cli/commands/sync.js +63 -0
  51. package/dist/cli/commands/sync.js.map +1 -0
  52. package/dist/cli/commands/watch.d.ts +17 -0
  53. package/dist/cli/commands/watch.js +87 -0
  54. package/dist/cli/commands/watch.js.map +1 -0
  55. package/dist/cli/config.d.ts +29 -0
  56. package/dist/cli/config.js +52 -0
  57. package/dist/cli/config.js.map +1 -0
  58. package/dist/cli/extract.d.ts +101 -0
  59. package/dist/cli/extract.js +216 -0
  60. package/dist/cli/extract.js.map +1 -0
  61. package/dist/cli/ingest.d.ts +48 -0
  62. package/dist/cli/ingest.js +74 -0
  63. package/dist/cli/ingest.js.map +1 -0
  64. package/dist/cli/ledger.d.ts +44 -0
  65. package/dist/cli/ledger.js +67 -0
  66. package/dist/cli/ledger.js.map +1 -0
  67. package/dist/cli/llm.d.ts +21 -0
  68. package/dist/cli/llm.js +138 -0
  69. package/dist/cli/llm.js.map +1 -0
  70. package/dist/cli/parse.d.ts +13 -0
  71. package/dist/cli/parse.js +188 -0
  72. package/dist/cli/parse.js.map +1 -0
  73. package/dist/cli/run-explore.d.ts +56 -0
  74. package/dist/cli/run-explore.js +229 -0
  75. package/dist/cli/run-explore.js.map +1 -0
  76. package/dist/cli/summarize.d.ts +15 -0
  77. package/dist/cli/summarize.js +49 -0
  78. package/dist/cli/summarize.js.map +1 -0
  79. package/dist/cli/uninstall.d.ts +6 -0
  80. package/dist/cli/uninstall.js +277 -0
  81. package/dist/cli/uninstall.js.map +1 -0
  82. package/dist/cli/walk.d.ts +13 -0
  83. package/dist/cli/walk.js +62 -0
  84. package/dist/cli/walk.js.map +1 -0
  85. package/dist/cli/walker.d.ts +14 -0
  86. package/dist/cli/walker.js +120 -0
  87. package/dist/cli/walker.js.map +1 -0
  88. package/dist/hooks/pre-compact.d.ts +15 -0
  89. package/dist/hooks/pre-compact.js +127 -0
  90. package/dist/hooks/pre-compact.js.map +1 -0
  91. package/dist/hooks/pre-tool-use.d.ts +15 -0
  92. package/dist/hooks/pre-tool-use.js +25 -0
  93. package/dist/hooks/pre-tool-use.js.map +1 -0
  94. package/dist/hooks/session-end.d.ts +21 -0
  95. package/dist/hooks/session-end.js +186 -0
  96. package/dist/hooks/session-end.js.map +1 -0
  97. package/dist/hooks/session-start.d.ts +15 -0
  98. package/dist/hooks/session-start.js +233 -0
  99. package/dist/hooks/session-start.js.map +1 -0
  100. package/dist/hooks/turn-end-post.d.ts +17 -0
  101. package/dist/hooks/turn-end-post.js +66 -0
  102. package/dist/hooks/turn-end-post.js.map +1 -0
  103. package/dist/hooks/turn-end.d.ts +29 -0
  104. package/dist/hooks/turn-end.js +295 -0
  105. package/dist/hooks/turn-end.js.map +1 -0
  106. package/dist/index.d.ts +24 -0
  107. package/dist/index.js +1547 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/init.d.ts +45 -0
  110. package/dist/init.js +839 -0
  111. package/dist/init.js.map +1 -0
  112. package/dist/lib/active-project.d.ts +14 -0
  113. package/dist/lib/active-project.js +65 -0
  114. package/dist/lib/active-project.js.map +1 -0
  115. package/dist/lib/buffer.d.ts +34 -0
  116. package/dist/lib/buffer.js +79 -0
  117. package/dist/lib/buffer.js.map +1 -0
  118. package/dist/scripts/compile.d.ts +25 -0
  119. package/dist/scripts/compile.js +185 -0
  120. package/dist/scripts/compile.js.map +1 -0
  121. package/dist/scripts/config.d.ts +30 -0
  122. package/dist/scripts/config.js +68 -0
  123. package/dist/scripts/config.js.map +1 -0
  124. package/dist/scripts/flush.d.ts +23 -0
  125. package/dist/scripts/flush.js +230 -0
  126. package/dist/scripts/flush.js.map +1 -0
  127. package/dist/scripts/lint.d.ts +21 -0
  128. package/dist/scripts/lint.js +242 -0
  129. package/dist/scripts/lint.js.map +1 -0
  130. package/dist/scripts/utils.d.ts +43 -0
  131. package/dist/scripts/utils.js +165 -0
  132. package/dist/scripts/utils.js.map +1 -0
  133. package/package.json +74 -0
  134. package/scripts/postinstall.mjs +56 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * `ricord pull [--out .ricord/pages/] [--merge] [--force]` — pull cloud
3
+ * pages onto local disk (F6 / S7 / G11 tick 28).
4
+ *
5
+ * Three modes:
6
+ * default — write a fresh page if absent; SKIP if already on disk
7
+ * --force — overwrite local files unconditionally
8
+ * --merge — for any file that exists locally AND has been edited since
9
+ * ledger.last_pulled_at, write the server copy as
10
+ * `<slug>.cloud.md` next to the local file so the user can
11
+ * diff + reconcile manually. Otherwise overwrite (cloud wins
12
+ * when there's no local change since the last pull).
13
+ *
14
+ * On success, advances ledger.last_pulled_at.
15
+ */
16
+ import { mkdir, writeFile, rename, stat } from "node:fs/promises";
17
+ import { existsSync } from "node:fs";
18
+ import { join, resolve } from "node:path";
19
+ import kleur from "kleur";
20
+ import { resolveAuth } from "../config.js";
21
+ import { request } from "../client.js";
22
+ import { readLedger, writeLedger } from "../ledger.js";
23
+ export async function pullCommand(opts) {
24
+ const auth = await resolveAuth();
25
+ const ledger = await readLedger(opts.repoRoot);
26
+ const projectId = opts.projectId ?? ledger.project_id;
27
+ const qs = projectId ? `?project_id=${encodeURIComponent(projectId)}` : "";
28
+ const data = await request(auth, `/v1/kb/export${qs}`);
29
+ const outDir = resolve(opts.repoRoot, opts.out);
30
+ await mkdir(outDir, { recursive: true });
31
+ // Index lives at the same dir level as pages — local-first default
32
+ // writes to `.ricord/pages/` so the README goes to .ricord/README.md
33
+ // instead of clobbering the per-repo README. Pull `--out wiki/` keeps
34
+ // the legacy index-in-out-dir behavior.
35
+ const indexTarget = opts.out === ".ricord/pages"
36
+ ? join(opts.repoRoot, ".ricord", "README.md")
37
+ : join(outDir, "README.md");
38
+ await writeFile(indexTarget, data.index ?? "", "utf8");
39
+ const lastPulledAt = ledger.last_pulled_at ?? 0;
40
+ let wrote = 0;
41
+ let skippedExisting = 0;
42
+ let conflicts = 0;
43
+ for (const p of data.pages ?? []) {
44
+ // Strip server's "pages/" prefix when --out already targets a pages dir.
45
+ const rel = opts.out.endsWith("/pages") || opts.out === ".ricord/pages"
46
+ ? p.path.replace(/^pages\//, "")
47
+ : p.path;
48
+ const target = join(outDir, rel);
49
+ await mkdir(resolve(target, ".."), { recursive: true });
50
+ if (existsSync(target)) {
51
+ if (opts.force) {
52
+ // overwrite
53
+ }
54
+ else if (opts.merge) {
55
+ // Conflict if local mtime is newer than the last pull (user edited
56
+ // since last pull) — write a sidecar instead of overwriting.
57
+ const st = await stat(target).catch(() => null);
58
+ const localChangedSinceLastPull = st && st.mtimeMs > lastPulledAt;
59
+ if (localChangedSinceLastPull) {
60
+ const sidecar = target.replace(/\.md$/, ".cloud.md");
61
+ const tmp = `${sidecar}.tmp.${process.pid}`;
62
+ await writeFile(tmp, p.content, "utf8");
63
+ await rename(tmp, sidecar);
64
+ conflicts++;
65
+ console.log(kleur.yellow(` ⚡ conflict: ${rel} — server copy at ${rel.replace(/\.md$/, ".cloud.md")}`));
66
+ continue;
67
+ }
68
+ // safe overwrite — local untouched since last pull
69
+ }
70
+ else {
71
+ skippedExisting++;
72
+ continue;
73
+ }
74
+ }
75
+ const tmp = `${target}.tmp.${process.pid}`;
76
+ await writeFile(tmp, p.content, "utf8");
77
+ await rename(tmp, target);
78
+ wrote++;
79
+ }
80
+ // topics.yaml mirror — write to .ricord/topics.yaml when --out is the
81
+ // canonical local-first path.
82
+ if (data.topics_yaml && opts.out === ".ricord/pages") {
83
+ const tPath = join(opts.repoRoot, ".ricord", "topics.yaml");
84
+ if (!existsSync(tPath) || opts.force) {
85
+ await writeFile(tPath, data.topics_yaml, "utf8");
86
+ }
87
+ }
88
+ await writeLedger(opts.repoRoot, {
89
+ ...ledger,
90
+ project_id: projectId,
91
+ last_pulled_at: Date.now(),
92
+ });
93
+ const parts = [`wrote ${wrote}`];
94
+ if (skippedExisting > 0)
95
+ parts.push(`skipped ${skippedExisting} existing`);
96
+ if (conflicts > 0)
97
+ parts.push(kleur.yellow(`${conflicts} conflict${conflicts === 1 ? "" : "s"}`));
98
+ console.log(kleur.green("✓"), parts.join(", ") + ` → ${opts.out}/`);
99
+ if (conflicts > 0) {
100
+ console.log(kleur.dim(" resolve: diff <slug>.md vs <slug>.cloud.md, keep one, delete the other."));
101
+ }
102
+ return 0;
103
+ }
104
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/cli/commands/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `ricord push [--from .ricord/pages/] [--all]` — local-first sync to
3
+ * the cloud mirror (G11 tick 25).
4
+ *
5
+ * Reads every .md under <from> whose mtime > ledger.last_pushed_at,
6
+ * parses YAML frontmatter, POSTs to /v1/kb/import. Server accepts
7
+ * `id` (preferred) or `slug` lookup. Successful runs advance the
8
+ * pushed-at watermark in the ledger.
9
+ *
10
+ * `--all` ignores the watermark and pushes every file (use after a
11
+ * clean clone or when the ledger is fresh).
12
+ */
13
+ interface PushOpts {
14
+ repoRoot: string;
15
+ from: string;
16
+ projectId?: string;
17
+ /** Optional team UUID override. When set, pages land in the
18
+ * (team_id, project_id) team space instead of personal. Falls back to
19
+ * the team_id recorded in the ledger by `ricord init --team`. */
20
+ teamId?: string;
21
+ /** Force personal scope on this push even if the ledger has a team_id —
22
+ * used to take a personal snapshot of a team-bound repo. */
23
+ noTeam?: boolean;
24
+ dryRun: boolean;
25
+ all?: boolean;
26
+ }
27
+ export declare function pushCommand(opts: PushOpts): Promise<number>;
28
+ export {};
@@ -0,0 +1,164 @@
1
+ /**
2
+ * `ricord push [--from .ricord/pages/] [--all]` — local-first sync to
3
+ * the cloud mirror (G11 tick 25).
4
+ *
5
+ * Reads every .md under <from> whose mtime > ledger.last_pushed_at,
6
+ * parses YAML frontmatter, POSTs to /v1/kb/import. Server accepts
7
+ * `id` (preferred) or `slug` lookup. Successful runs advance the
8
+ * pushed-at watermark in the ledger.
9
+ *
10
+ * `--all` ignores the watermark and pushes every file (use after a
11
+ * clean clone or when the ledger is fresh).
12
+ */
13
+ import { readdir, readFile, stat } from "node:fs/promises";
14
+ import { join, resolve } from "node:path";
15
+ import kleur from "kleur";
16
+ import { resolveAuth } from "../config.js";
17
+ import { request } from "../client.js";
18
+ import { readLedger, writeLedger } from "../ledger.js";
19
+ function parseDoc(text) {
20
+ if (!text.startsWith("---")) {
21
+ return { body: text, frontmatter: {} };
22
+ }
23
+ const end = text.indexOf("\n---", 3);
24
+ if (end === -1)
25
+ return { body: text, frontmatter: {} };
26
+ const fmRaw = text.slice(3, end).trim();
27
+ const body = text.slice(end + 4).replace(/^\s*\n/, "");
28
+ const frontmatter = {};
29
+ const lines = fmRaw.split("\n");
30
+ for (let i = 0; i < lines.length; i++) {
31
+ const line = lines[i];
32
+ const m = line.match(/^([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/);
33
+ if (!m)
34
+ continue;
35
+ const [, k, vRaw] = m;
36
+ if (vRaw && vRaw.startsWith("[") && vRaw.endsWith("]")) {
37
+ // YAML inline array: [a, "b", c]
38
+ const inner = vRaw.slice(1, -1).trim();
39
+ if (inner.length === 0) {
40
+ frontmatter[k] = [];
41
+ }
42
+ else {
43
+ frontmatter[k] = inner.split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
44
+ }
45
+ }
46
+ else if (vRaw === "" || vRaw === undefined) {
47
+ // YAML block list: peek ahead for ` - item` lines.
48
+ const items = [];
49
+ while (i + 1 < lines.length) {
50
+ const next = lines[i + 1];
51
+ const bm = next.match(/^\s+-\s+(.*)$/);
52
+ if (!bm)
53
+ break;
54
+ items.push(bm[1].trim().replace(/^["']|["']$/g, ""));
55
+ i++;
56
+ }
57
+ if (items.length > 0)
58
+ frontmatter[k] = items;
59
+ }
60
+ else if (vRaw && (vRaw.startsWith("\"") || vRaw.startsWith("'"))) {
61
+ frontmatter[k] = vRaw.slice(1, -1);
62
+ }
63
+ else {
64
+ frontmatter[k] = vRaw;
65
+ }
66
+ }
67
+ const topicsRaw = frontmatter.topics;
68
+ const topics = Array.isArray(topicsRaw) ? topicsRaw.filter((t) => typeof t === "string") : undefined;
69
+ const filesRaw = frontmatter.files;
70
+ const files = Array.isArray(filesRaw) ? filesRaw.filter((f) => typeof f === "string") : undefined;
71
+ return {
72
+ id: typeof frontmatter.id === "string" ? frontmatter.id : undefined,
73
+ slug: typeof frontmatter.slug === "string" ? frontmatter.slug : undefined,
74
+ title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
75
+ summary: typeof frontmatter.summary === "string" ? frontmatter.summary : undefined,
76
+ type: typeof frontmatter.type === "string" ? frontmatter.type : undefined,
77
+ topics,
78
+ files,
79
+ body,
80
+ frontmatter,
81
+ };
82
+ }
83
+ export async function pushCommand(opts) {
84
+ const auth = await resolveAuth();
85
+ const ledger = await readLedger(opts.repoRoot);
86
+ const projectId = opts.projectId ?? ledger.project_id;
87
+ if (!projectId) {
88
+ console.error(kleur.red("project_id not recorded; run `ricord init --project <id>` first."));
89
+ return 1;
90
+ }
91
+ // Resolve team scope. Precedence: --no-team wins (force personal) →
92
+ // --team <id> flag → ledger.team_id. The server validates membership and
93
+ // rejects a 403 if the caller isn't in the team, so a stale ledger never
94
+ // silently leaks data into a team space the caller doesn't belong to.
95
+ const teamId = opts.noTeam ? undefined : (opts.teamId ?? ledger.team_id);
96
+ const dir = resolve(opts.repoRoot, opts.from);
97
+ const entries = await readdir(dir).catch(() => []);
98
+ const mds = entries.filter((e) => e.endsWith(".md"));
99
+ if (mds.length === 0) {
100
+ console.log(kleur.yellow(`no markdown files under ${opts.from}/`));
101
+ return 0;
102
+ }
103
+ const watermark = opts.all ? 0 : (ledger.last_pushed_at ?? 0);
104
+ const docs = [];
105
+ let totalScanned = 0;
106
+ let gatedOut = 0;
107
+ for (const f of mds) {
108
+ totalScanned++;
109
+ const fullPath = join(dir, f);
110
+ if (!opts.all && watermark > 0) {
111
+ const st = await stat(fullPath).catch(() => null);
112
+ if (st && st.mtimeMs <= watermark) {
113
+ gatedOut++;
114
+ continue;
115
+ }
116
+ }
117
+ const text = await readFile(fullPath, "utf8");
118
+ const d = parseDoc(text);
119
+ if (!d.slug)
120
+ d.slug = f.replace(/\.md$/, "");
121
+ docs.push(d);
122
+ }
123
+ if (docs.length === 0) {
124
+ console.log(kleur.dim(`nothing to push — ${totalScanned} files on disk, ${gatedOut} unchanged since last push` +
125
+ (opts.all ? "" : " (use --all to force)")));
126
+ return 0;
127
+ }
128
+ if (opts.dryRun) {
129
+ console.log(kleur.dim(`would push ${docs.length} of ${totalScanned} pages:`));
130
+ for (const d of docs)
131
+ console.log(kleur.dim(` ${d.slug}${d.id ? ` (id ${d.id.slice(0, 8)})` : ""}`));
132
+ return 0;
133
+ }
134
+ const out = await request(auth, "/v1/kb/import", {
135
+ method: "POST",
136
+ body: JSON.stringify({
137
+ project_id: projectId,
138
+ ...(teamId ? { team_id: teamId } : {}),
139
+ pages: docs,
140
+ }),
141
+ });
142
+ console.log(kleur.green("✓"), `imported ${out.imported}, skipped ${out.skipped}` +
143
+ (gatedOut > 0 ? kleur.dim(` (gated out ${gatedOut} unchanged)`) : "") +
144
+ (out.errors.length > 0 ? `, ${out.errors.length} error${out.errors.length === 1 ? "" : "s"}` : ""));
145
+ for (const e of out.errors) {
146
+ console.log(kleur.red(` ${e.slug ?? "?"}: ${e.message}`));
147
+ }
148
+ // Advance watermark even on partial success so the next push doesn't
149
+ // re-send files the server already accepted. Files in the error list
150
+ // will be retried on the next push because their mtime stays.
151
+ if (out.imported > 0) {
152
+ await writeLedger(opts.repoRoot, {
153
+ ...ledger,
154
+ project_id: projectId,
155
+ // Persist team_id if it was passed via flag — keeps the ledger in
156
+ // sync with what just shipped, so later pushes don't drift.
157
+ // --no-team is a one-shot override; we don't erase a persisted team_id.
158
+ team_id: opts.noTeam ? ledger.team_id : (teamId ?? ledger.team_id),
159
+ last_pushed_at: Date.now(),
160
+ });
161
+ }
162
+ return out.errors.length === 0 ? 0 : 2;
163
+ }
164
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/cli/commands/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `ricord rollup [--limit N] [--min-sources N] [--push]` — local-first
3
+ * page authoring. Drains the cloud's stub backlog by running a host LLM
4
+ * (Gemini → OpenAI fallback) over each empty page's sources, then writes
5
+ * the rolled-up markdown directly to `.ricord/pages/<slug>.md`.
6
+ *
7
+ * With `--push` (not implemented this tick; tick 25): also POSTs to
8
+ * `/v1/kb/pages/:id/body` so the server mirror updates in the same run.
9
+ *
10
+ * Why: local files are now the source of truth (G11 pivot). This is
11
+ * how an existing cloud account gets bootstrapped into the local layout
12
+ * for ongoing edits.
13
+ */
14
+ interface RollupOpts {
15
+ repoRoot: string;
16
+ limit: number;
17
+ minSources: number;
18
+ push: boolean;
19
+ }
20
+ export declare function rollupCommand(opts: RollupOpts): Promise<number>;
21
+ export {};
@@ -0,0 +1,118 @@
1
+ /**
2
+ * `ricord rollup [--limit N] [--min-sources N] [--push]` — local-first
3
+ * page authoring. Drains the cloud's stub backlog by running a host LLM
4
+ * (Gemini → OpenAI fallback) over each empty page's sources, then writes
5
+ * the rolled-up markdown directly to `.ricord/pages/<slug>.md`.
6
+ *
7
+ * With `--push` (not implemented this tick; tick 25): also POSTs to
8
+ * `/v1/kb/pages/:id/body` so the server mirror updates in the same run.
9
+ *
10
+ * Why: local files are now the source of truth (G11 pivot). This is
11
+ * how an existing cloud account gets bootstrapped into the local layout
12
+ * for ongoing edits.
13
+ */
14
+ import { existsSync } from "node:fs";
15
+ import { mkdir, writeFile } from "node:fs/promises";
16
+ import { join } from "node:path";
17
+ import kleur from "kleur";
18
+ import { resolveAuth } from "../config.js";
19
+ import { request } from "../client.js";
20
+ import { readLedger, ledgerDir } from "../ledger.js";
21
+ import { callHostLLM } from "../llm.js";
22
+ // Slim schema — the full PAGE_ROLLUP schema blows past Gemini's effective
23
+ // generation budget at 16k tokens. Body is the only field we need for
24
+ // search recall + the local markdown file.
25
+ const SLIM_SCHEMA = {
26
+ type: "object",
27
+ required: ["title", "summary", "body"],
28
+ properties: {
29
+ title: { type: "string" },
30
+ summary: { type: "string" },
31
+ body: { type: "string" },
32
+ insufficient_sources: { type: "boolean" },
33
+ reason: { type: "string" },
34
+ },
35
+ };
36
+ function slugify(s) {
37
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "page";
38
+ }
39
+ function frontmatter(stub, out, slug) {
40
+ const title = (out.title ?? stub.title ?? stub.anchor_value).replace(/"/g, '\\"');
41
+ const summary = JSON.stringify(out.summary ?? "");
42
+ return [
43
+ "---",
44
+ `id: ${stub.id}`,
45
+ `slug: ${slug}`,
46
+ `title: ${title}`,
47
+ `summary: ${summary}`,
48
+ `type: ${stub.anchor_type}`,
49
+ `anchor: ${stub.anchor_value.replace(/"/g, '\\"')}`,
50
+ `source_count: ${stub.source_count}`,
51
+ "tags: []",
52
+ "topics: []",
53
+ "files: []",
54
+ "---",
55
+ "",
56
+ `# ${out.title ?? stub.title ?? stub.anchor_value}`,
57
+ "",
58
+ out.body ?? "",
59
+ "",
60
+ ].join("\n");
61
+ }
62
+ export async function rollupCommand(opts) {
63
+ const ledger = await readLedger(opts.repoRoot);
64
+ if (!ledger.project_id) {
65
+ console.error(kleur.red("project_id not recorded — run `ricord init --project <id>` first."));
66
+ return 1;
67
+ }
68
+ const pagesDir = join(ledgerDir(opts.repoRoot), "pages");
69
+ await mkdir(pagesDir, { recursive: true });
70
+ const auth = await resolveAuth();
71
+ const stubsPath = `/v1/kb/pages/stubs?limit=${opts.limit}&min_sources=${opts.minSources}`;
72
+ const stubs = await request(auth, stubsPath);
73
+ console.log(kleur.dim(`[rollup] ${stubs.count} stub${stubs.count === 1 ? "" : "s"} found (limit=${opts.limit}, min_sources=${opts.minSources})`));
74
+ if (stubs.count === 0)
75
+ return 0;
76
+ let wrote = 0;
77
+ let failed = 0;
78
+ let skippedExisting = 0;
79
+ for (const stub of stubs.stubs) {
80
+ const label = `[${stub.anchor_type}] ${stub.title || stub.anchor_value} (${stub.source_count} src)`;
81
+ try {
82
+ const prompt = await request(auth, `/v1/kb/pages/${stub.id}/rollup-prompt`);
83
+ const out = await callHostLLM(prompt.prompt, SLIM_SCHEMA);
84
+ if (out.insufficient_sources || !out.body) {
85
+ console.log(` SKIP ${label} — model said insufficient sources`);
86
+ continue;
87
+ }
88
+ const slug = slugify(out.title ?? stub.title ?? stub.anchor_value);
89
+ const target = join(pagesDir, `${slug}.md`);
90
+ if (existsSync(target)) {
91
+ skippedExisting++;
92
+ console.log(` SKIP ${label} — ${slug}.md already on disk (use \`ricord pull --force\` to overwrite)`);
93
+ continue;
94
+ }
95
+ // Clamp lengths to server schema caps so a future --push lands cleanly.
96
+ if (out.title)
97
+ out.title = out.title.slice(0, 200);
98
+ if (out.summary)
99
+ out.summary = out.summary.slice(0, 2000);
100
+ if (out.body)
101
+ out.body = out.body.slice(0, 16000);
102
+ await writeFile(target, frontmatter(stub, out, slug), "utf8");
103
+ console.log(` OK ${label} → .ricord/pages/${slug}.md (${(out.body ?? "").length} chars)`);
104
+ wrote++;
105
+ // tick 25 will use this to also POST the body.
106
+ if (opts.push) {
107
+ // placeholder hook so the --push flag parses today; tick 25 wires it.
108
+ }
109
+ }
110
+ catch (e) {
111
+ console.log(` FAIL ${label} — ${e.message}`);
112
+ failed++;
113
+ }
114
+ }
115
+ console.log(kleur.green(`[rollup] done. wrote=${wrote} skipped=${skippedExisting} failed=${failed}`));
116
+ return failed > 0 && wrote === 0 ? 1 : 0;
117
+ }
118
+ //# sourceMappingURL=rollup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollup.js","sourceRoot":"","sources":["../../../src/cli/commands/rollup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAkCxC,0EAA0E;AAC1E,sEAAsE;AACtE,2CAA2C;AAC3C,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC;IACtC,UAAU,EAAE;QACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,oBAAoB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QACzC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC3B;CACF,CAAC;AAEF,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC;AACvF,CAAC;AAED,SAAS,WAAW,CAAC,IAAgC,EAAE,GAAwD,EAAE,IAAY;IAC3H,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAClD,OAAO;QACL,KAAK;QACL,OAAO,IAAI,CAAC,EAAE,EAAE;QAChB,SAAS,IAAI,EAAE;QACf,UAAU,KAAK,EAAE;QACjB,YAAY,OAAO,EAAE;QACrB,SAAS,IAAI,CAAC,WAAW,EAAE;QAC3B,WAAW,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE;QACnD,iBAAiB,IAAI,CAAC,YAAY,EAAE;QACpC,UAAU;QACV,YAAY;QACZ,WAAW;QACX,KAAK;QACL,EAAE;QACF,KAAK,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE;QACnD,EAAE;QACF,GAAG,CAAC,IAAI,IAAI,EAAE;QACd,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAgB;IAClD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC,CAAC;QAC9F,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,4BAA4B,IAAI,CAAC,KAAK,gBAAgB,IAAI,CAAC,UAAU,EAAE,CAAC;IAC1F,MAAM,KAAK,GAAG,MAAM,OAAO,CAAY,IAAI,EAAE,SAAS,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,iBAAiB,IAAI,CAAC,KAAK,iBAAiB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAClJ,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEhC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,OAAO,CAAC;QACpG,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAmB,IAAI,EAAE,gBAAgB,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC;YAC9F,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAMvD,CAAC;YACF,IAAI,GAAG,CAAC,oBAAoB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,oCAAoC,CAAC,CAAC;gBACjE,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;YAC5C,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,eAAe,EAAE,CAAC;gBAClB,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,MAAM,IAAI,gEAAgE,CAAC,CAAC;gBACvG,SAAS;YACX,CAAC;YACD,wEAAwE;YACxE,IAAI,GAAG,CAAC,KAAK;gBAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACnD,IAAI,GAAG,CAAC,OAAO;gBAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YAC1D,IAAI,GAAG,CAAC,IAAI;gBAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAClD,MAAM,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAC9D,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,oBAAoB,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC;YAC7F,KAAK,EAAE,CAAC;YACR,+CAA+C;YAC/C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,sEAAsE;YACxE,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,MAAO,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACzD,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,KAAK,YAAY,eAAe,WAAW,MAAM,EAAE,CAAC,CAAC,CAAC;IACtG,OAAO,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `ricord setup` — interactive (TTY) or env-driven (non-TTY) auth setup.
3
+ *
4
+ * Writes ~/.ricord/config.json with api_key + api_url. Non-interactive
5
+ * if both RICORD_API_KEY and (optionally) RICORD_API_URL are set.
6
+ */
7
+ export declare function setupCommand(): Promise<number>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `ricord setup` — interactive (TTY) or env-driven (non-TTY) auth setup.
3
+ *
4
+ * Writes ~/.ricord/config.json with api_key + api_url. Non-interactive
5
+ * if both RICORD_API_KEY and (optionally) RICORD_API_URL are set.
6
+ */
7
+ import { createInterface } from "node:readline/promises";
8
+ import { stdin, stdout } from "node:process";
9
+ import kleur from "kleur";
10
+ import { readConfig, writeConfig } from "../config.js";
11
+ async function prompt(label, fallback) {
12
+ const rl = createInterface({ input: stdin, output: stdout });
13
+ try {
14
+ const ans = await rl.question(`${label}${fallback ? ` (${fallback})` : ""}: `);
15
+ return ans.trim() || (fallback ?? "");
16
+ }
17
+ finally {
18
+ rl.close();
19
+ }
20
+ }
21
+ export async function setupCommand() {
22
+ const existing = await readConfig();
23
+ let apiKey = process.env.RICORD_API_KEY ?? "";
24
+ let apiUrl = process.env.RICORD_API_URL ?? existing.api_url ?? "https://api.ricord.ai";
25
+ if (!apiKey) {
26
+ if (!stdin.isTTY) {
27
+ console.error(kleur.red("Non-interactive setup needs RICORD_API_KEY in the environment."));
28
+ return 1;
29
+ }
30
+ console.log(kleur.bold("ricord setup"));
31
+ console.log(kleur.dim("Find your API key at https://ricord.ai/dashboard/api-keys"));
32
+ apiKey = await prompt("API key (rc_live_…)", existing.api_key);
33
+ }
34
+ if (!apiKey || !apiKey.startsWith("rc_live_")) {
35
+ console.error(kleur.red(`API key looks wrong — expected prefix "rc_live_".`));
36
+ return 1;
37
+ }
38
+ await writeConfig({ ...existing, api_key: apiKey, api_url: apiUrl });
39
+ console.log(kleur.green("✓ saved"), kleur.dim(`~/.ricord/config.json`));
40
+ console.log(kleur.dim(`api_url: ${apiUrl}`));
41
+ return 0;
42
+ }
43
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEvD,KAAK,UAAU,MAAM,CAAC,KAAa,EAAE,QAAiB;IACpD,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC/E,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,QAAQ,GAAG,MAAM,UAAU,EAAE,CAAC;IACpC,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;IAC9C,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,QAAQ,CAAC,OAAO,IAAI,uBAAuB,CAAC;IAEvF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAC5E,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC,CAAC;QACpF,MAAM,GAAG,MAAM,MAAM,CAAC,qBAAqB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,WAAW,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `ricord sync` — incremental sync that picks up where the daemon (F1)
3
+ * normally would. Idempotent; safe to run repeatedly.
4
+ *
5
+ * Phase 1 (this slice): wraps `ingest` with the ledger's last_synced_at
6
+ * gate. Only re-ingests files whose mtime > last_synced_at.
7
+ *
8
+ * Phase 2 (S6 daemon): becomes the work loop the launchd plist fires.
9
+ */
10
+ interface SyncOpts {
11
+ repoRoot: string;
12
+ projectId?: string;
13
+ }
14
+ export declare function syncCommand(opts: SyncOpts): Promise<number>;
15
+ export {};
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `ricord sync` — incremental sync that picks up where the daemon (F1)
3
+ * normally would. Idempotent; safe to run repeatedly.
4
+ *
5
+ * Phase 1 (this slice): wraps `ingest` with the ledger's last_synced_at
6
+ * gate. Only re-ingests files whose mtime > last_synced_at.
7
+ *
8
+ * Phase 2 (S6 daemon): becomes the work loop the launchd plist fires.
9
+ */
10
+ import { stat } from "node:fs/promises";
11
+ import { resolve, relative } from "node:path";
12
+ import kleur from "kleur";
13
+ import { resolveAuth } from "../config.js";
14
+ import { request } from "../client.js";
15
+ import { readLedger, writeLedger } from "../ledger.js";
16
+ import { walkRepo } from "../walk.js";
17
+ export async function syncCommand(opts) {
18
+ const auth = await resolveAuth();
19
+ const ledger = await readLedger(opts.repoRoot);
20
+ const projectId = opts.projectId ?? ledger.project_id;
21
+ if (!projectId) {
22
+ console.error(kleur.red("project_id not recorded — run `ricord ingest --project <id>` once first."));
23
+ return 1;
24
+ }
25
+ const since = ledger.last_synced_at ?? 0;
26
+ const all = await walkRepo(opts.repoRoot);
27
+ const changed = [];
28
+ for (const f of all) {
29
+ try {
30
+ const s = await stat(f.absPath);
31
+ if (s.mtimeMs > since)
32
+ changed.push(f.absPath);
33
+ }
34
+ catch {
35
+ // skip
36
+ }
37
+ }
38
+ if (changed.length === 0) {
39
+ console.log(kleur.dim("nothing changed since last sync."));
40
+ return 0;
41
+ }
42
+ console.log(kleur.bold(`syncing ${changed.length} changed file${changed.length === 1 ? "" : "s"}…`));
43
+ const body = {
44
+ project_id: projectId,
45
+ repo: { remote_url: `file://${resolve(opts.repoRoot)}` },
46
+ batches: [
47
+ {
48
+ cluster_name: "sync",
49
+ files: changed.slice(0, 500).map((p) => ({ path: relative(opts.repoRoot, p) })),
50
+ extracted: { anchors: [] },
51
+ extraction_meta: { client: "ricord-cli/sync" },
52
+ },
53
+ ],
54
+ };
55
+ const out = await request(auth, "/v1/ingest/repo", {
56
+ method: "POST",
57
+ body: JSON.stringify(body),
58
+ });
59
+ await writeLedger(opts.repoRoot, { ...ledger, project_id: projectId, last_synced_at: Date.now() });
60
+ console.log(kleur.green("✓"), `${out.files_indexed} file refs synced (repo ${out.repo_id}).`);
61
+ return 0;
62
+ }
63
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/cli/commands/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `ricord watch [--from .ricord/pages/]` — file watcher that auto-pushes
3
+ * markdown edits to the cloud mirror (G11 tick 26).
4
+ *
5
+ * Uses node:fs.watch (no chokidar dep). Edits are coalesced with a 1.5s
6
+ * debounce so a save burst from an editor doesn't trigger N pushes.
7
+ * Each fire calls pushCommand which mtime-gates against the ledger.
8
+ *
9
+ * Ctrl-C exits cleanly.
10
+ */
11
+ interface WatchOpts {
12
+ repoRoot: string;
13
+ from: string;
14
+ projectId?: string;
15
+ }
16
+ export declare function watchCommand(opts: WatchOpts): Promise<number>;
17
+ export {};