records-cli 0.0.1

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.
@@ -0,0 +1,18 @@
1
+ /** Fixed production origin — not configurable. */
2
+ export declare const RECORDS_ORIGIN = "https://records-api.tolkee.dev";
3
+ export type ClientOptions = {
4
+ baseUrl: string;
5
+ token?: string;
6
+ };
7
+ export declare class ApiError extends Error {
8
+ readonly status: number;
9
+ readonly body: unknown;
10
+ constructor(message: string, status: number, body: unknown);
11
+ }
12
+ export declare function createClient(opts: ClientOptions): {
13
+ request: <T>(path: string, init?: RequestInit & {
14
+ json?: unknown;
15
+ }) => Promise<T>;
16
+ base: string;
17
+ };
18
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,eAAO,MAAM,cAAc,mCAAmC,CAAA;AAE9D,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,qBAAa,QAAS,SAAQ,KAAK;IAG/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO;gBAFtB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO;CAKzB;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa;cAGvB,CAAC,QAChB,MAAM,SACN,WAAW,GAAG;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,KACrC,OAAO,CAAC,CAAC,CAAC;;EAgCd"}
package/dist/client.js ADDED
@@ -0,0 +1,49 @@
1
+ /** Fixed production origin — not configurable. */
2
+ export const RECORDS_ORIGIN = "https://records-api.tolkee.dev";
3
+ export class ApiError extends Error {
4
+ status;
5
+ body;
6
+ constructor(message, status, body) {
7
+ super(message);
8
+ this.status = status;
9
+ this.body = body;
10
+ this.name = "ApiError";
11
+ }
12
+ }
13
+ function normalizeBaseUrl(url) {
14
+ return url.replace(/\/+$/, "");
15
+ }
16
+ export function createClient(opts) {
17
+ const base = normalizeBaseUrl(opts.baseUrl);
18
+ async function request(path, init = {}) {
19
+ const headers = new Headers(init.headers);
20
+ if (!headers.has("Accept"))
21
+ headers.set("Accept", "application/json");
22
+ if (opts.token)
23
+ headers.set("Authorization", `Bearer ${opts.token}`);
24
+ if (init.json !== undefined) {
25
+ headers.set("Content-Type", "application/json");
26
+ }
27
+ const body = init.json !== undefined ? JSON.stringify(init.json) : init.body;
28
+ const res = await fetch(`${base}${path}`, { ...init, headers, body });
29
+ const text = await res.text();
30
+ let parsed = text;
31
+ if (text.length > 0) {
32
+ try {
33
+ parsed = JSON.parse(text);
34
+ }
35
+ catch {
36
+ parsed = text;
37
+ }
38
+ }
39
+ else {
40
+ parsed = null;
41
+ }
42
+ if (!res.ok) {
43
+ throw new ApiError(`HTTP ${res.status}: ${typeof parsed === "object" && parsed !== null && "error" in parsed ? String(parsed.error) : text || res.statusText}`, res.status, parsed);
44
+ }
45
+ return parsed;
46
+ }
47
+ return { request, base };
48
+ }
49
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,gCAAgC,CAAA;AAO9D,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGtB;IACA;IAHX,YACE,OAAe,EACN,MAAc,EACd,IAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAA;QAHL,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAS;QAGtB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAA;IACxB,CAAC;CACF;AAED,SAAS,gBAAgB,CAAC,GAAW;IACnC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAmB;IAC9C,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAE3C,KAAK,UAAU,OAAO,CACpB,IAAY,EACZ,OAAyC,EAAE;QAE3C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAA;QACrE,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QACpE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;QACjD,CAAC;QACD,MAAM,IAAI,GACR,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;QACjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACrE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,IAAI,MAAM,GAAY,IAAI,CAAA;QAC1B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAA;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,IAAI,CAAA;YACf,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,QAAQ,CAChB,QAAQ,GAAG,CAAC,MAAM,KAAK,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAE,MAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,UAAU,EAAE,EACnK,GAAG,CAAC,MAAM,EACV,MAAM,CACP,CAAA;QACH,CAAC;QACD,OAAO,MAAW,CAAA;IACpB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { readFile } from "node:fs/promises";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { Command } from "commander";
7
+ import { ApiError, createClient, RECORDS_ORIGIN } from "./client.js";
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
10
+ function defaultToken() {
11
+ const t = process.env.RECORDS_TOKEN?.trim();
12
+ return t || undefined;
13
+ }
14
+ async function readStdin() {
15
+ const chunks = [];
16
+ for await (const chunk of process.stdin)
17
+ chunks.push(chunk);
18
+ return Buffer.concat(chunks).toString("utf8");
19
+ }
20
+ async function readContentArg(value) {
21
+ if (value === undefined || value === "") {
22
+ throw new Error("Missing --content (use a path, or '-' for stdin)");
23
+ }
24
+ if (value === "-")
25
+ return readStdin();
26
+ return readFile(value, "utf8");
27
+ }
28
+ function printJson(data, pretty) {
29
+ console.log(JSON.stringify(data, null, pretty ? 2 : undefined));
30
+ }
31
+ function printErr(e, pretty) {
32
+ if (e instanceof ApiError) {
33
+ printJson({ error: e.message, status: e.status, body: e.body }, pretty);
34
+ return;
35
+ }
36
+ printJson({ error: e instanceof Error ? e.message : String(e) }, pretty);
37
+ }
38
+ function getContext(cmd) {
39
+ const o = cmd.optsWithGlobals();
40
+ return {
41
+ client: createClient({ baseUrl: RECORDS_ORIGIN, token: o.token }),
42
+ pretty: !!o.pretty,
43
+ };
44
+ }
45
+ const program = new Command();
46
+ program
47
+ .name("records-cli")
48
+ .description("Records command-line interface — list groups, create records, manage keys.")
49
+ .version(pkg.version)
50
+ .configureHelp({ sortSubcommands: true })
51
+ .option("-t, --token <token>", "Bearer token (env RECORDS_TOKEN)", defaultToken())
52
+ .option("-p, --pretty", "Pretty-print JSON", false);
53
+ program
54
+ .command("health")
55
+ .description("Check server reachability")
56
+ .action(async function () {
57
+ const { client, pretty } = getContext(this);
58
+ try {
59
+ const data = await client.request("/health");
60
+ printJson(data, pretty);
61
+ }
62
+ catch (e) {
63
+ printErr(e, pretty);
64
+ process.exitCode = 1;
65
+ }
66
+ });
67
+ const groups = program.command("groups").description("Groups");
68
+ groups
69
+ .command("list")
70
+ .description("List groups; add --with-records for record titles per group")
71
+ .option("--with-records", "Include record titles in each group", false)
72
+ .action(async function () {
73
+ const { client, pretty } = getContext(this);
74
+ const withRecords = this.opts().withRecords;
75
+ const q = withRecords ? "?include=records" : "";
76
+ try {
77
+ const data = await client.request(`/api/groups${q}`);
78
+ printJson(data, pretty);
79
+ }
80
+ catch (e) {
81
+ printErr(e, pretty);
82
+ process.exitCode = 1;
83
+ }
84
+ });
85
+ groups
86
+ .command("get")
87
+ .description("Get one group by numeric id or slug")
88
+ .option("--id <id>", "Numeric group id")
89
+ .option("--slug <slug>", "Group slug")
90
+ .action(async function () {
91
+ const { client, pretty } = getContext(this);
92
+ const opts = this.opts();
93
+ if (opts.id && opts.slug) {
94
+ printJson({ error: "Use only one of --id or --slug" }, pretty);
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+ if (!opts.id && !opts.slug) {
99
+ printJson({ error: "Provide --id or --slug" }, pretty);
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ try {
104
+ if (opts.id) {
105
+ const data = await client.request(`/api/groups/${opts.id}`);
106
+ printJson(data, pretty);
107
+ return;
108
+ }
109
+ const data = await client.request(`/api/groups/slug/${encodeURIComponent(opts.slug)}`);
110
+ printJson(data, pretty);
111
+ }
112
+ catch (e) {
113
+ printErr(e, pretty);
114
+ process.exitCode = 1;
115
+ }
116
+ });
117
+ groups
118
+ .command("create")
119
+ .description("Create a group (requires token)")
120
+ .requiredOption("--title <title>", "Group title")
121
+ .action(async function () {
122
+ const { client, pretty } = getContext(this);
123
+ const title = this.opts().title;
124
+ try {
125
+ const data = await client.request("/api/groups", {
126
+ method: "POST",
127
+ json: { title },
128
+ });
129
+ printJson(data, pretty);
130
+ }
131
+ catch (e) {
132
+ printErr(e, pretty);
133
+ process.exitCode = 1;
134
+ }
135
+ });
136
+ groups
137
+ .command("patch")
138
+ .description("Rename a group by id (requires token)")
139
+ .argument("<id>", "Group id")
140
+ .requiredOption("--title <title>", "New title")
141
+ .action(async function (id) {
142
+ const { client, pretty } = getContext(this);
143
+ const title = this.opts().title;
144
+ try {
145
+ const data = await client.request(`/api/groups/${id}`, {
146
+ method: "PATCH",
147
+ json: { title },
148
+ });
149
+ printJson(data, pretty);
150
+ }
151
+ catch (e) {
152
+ printErr(e, pretty);
153
+ process.exitCode = 1;
154
+ }
155
+ });
156
+ groups
157
+ .command("delete")
158
+ .description("Delete a group by id (requires token)")
159
+ .argument("<id>", "Group id")
160
+ .action(async function (id) {
161
+ const { client, pretty } = getContext(this);
162
+ try {
163
+ await client.request(`/api/groups/${id}`, { method: "DELETE" });
164
+ }
165
+ catch (e) {
166
+ printErr(e, pretty);
167
+ process.exitCode = 1;
168
+ }
169
+ });
170
+ const grec = program
171
+ .command("group-records")
172
+ .description("Records under a group slug");
173
+ grec
174
+ .command("list")
175
+ .description("List record summaries in a group")
176
+ .argument("<groupSlug>", "Group slug")
177
+ .action(async function (groupSlug) {
178
+ const { client, pretty } = getContext(this);
179
+ try {
180
+ const data = await client.request(`/api/groups/${encodeURIComponent(groupSlug)}/records`);
181
+ printJson(data, pretty);
182
+ }
183
+ catch (e) {
184
+ printErr(e, pretty);
185
+ process.exitCode = 1;
186
+ }
187
+ });
188
+ grec
189
+ .command("get")
190
+ .description("Get one record by group slug and record slug")
191
+ .argument("<groupSlug>", "Group slug")
192
+ .argument("<recordSlug>", "Record slug")
193
+ .action(async function (groupSlug, recordSlug) {
194
+ const { client, pretty } = getContext(this);
195
+ try {
196
+ const data = await client.request(`/api/groups/${encodeURIComponent(groupSlug)}/records/${encodeURIComponent(recordSlug)}`);
197
+ printJson(data, pretty);
198
+ }
199
+ catch (e) {
200
+ printErr(e, pretty);
201
+ process.exitCode = 1;
202
+ }
203
+ });
204
+ grec
205
+ .command("create")
206
+ .description("Create a record in a group (requires token)")
207
+ .argument("<groupSlug>", "Group slug")
208
+ .requiredOption("--title <title>", "Record title")
209
+ .requiredOption("--content <path|-", "Markdown body: file path or - for stdin")
210
+ .option("--description <text>", "Optional description")
211
+ .action(async function (groupSlug) {
212
+ const { client, pretty } = getContext(this);
213
+ const opts = this.opts();
214
+ const content = await readContentArg(opts.content);
215
+ const body = { title: opts.title, content };
216
+ if (opts.description !== undefined)
217
+ body.description = opts.description;
218
+ try {
219
+ const data = await client.request(`/api/groups/${encodeURIComponent(groupSlug)}/records`, { method: "POST", json: body });
220
+ printJson(data, pretty);
221
+ }
222
+ catch (e) {
223
+ printErr(e, pretty);
224
+ process.exitCode = 1;
225
+ }
226
+ });
227
+ const records = program
228
+ .command("records")
229
+ .description("Records by numeric id");
230
+ records
231
+ .command("get")
232
+ .description("Get a record by id")
233
+ .argument("<id>", "Record id")
234
+ .action(async function (id) {
235
+ const { client, pretty } = getContext(this);
236
+ try {
237
+ const data = await client.request(`/api/records/${id}`);
238
+ printJson(data, pretty);
239
+ }
240
+ catch (e) {
241
+ printErr(e, pretty);
242
+ process.exitCode = 1;
243
+ }
244
+ });
245
+ records
246
+ .command("patch")
247
+ .description("Update a record (requires token)")
248
+ .argument("<id>", "Record id")
249
+ .option("--title <title>", "New title")
250
+ .option("--content <path|-", "New Markdown body: file path or - for stdin")
251
+ .option("--description <text>", "New description")
252
+ .option("--clear-description", "Set description to null", false)
253
+ .action(async function (id) {
254
+ const { client, pretty } = getContext(this);
255
+ const opts = this.opts();
256
+ const patch = {};
257
+ if (opts.title !== undefined)
258
+ patch.title = opts.title;
259
+ if (opts.content !== undefined)
260
+ patch.content = await readContentArg(opts.content);
261
+ if (opts.clearDescription)
262
+ patch.description = null;
263
+ else if (opts.description !== undefined)
264
+ patch.description = opts.description;
265
+ if (Object.keys(patch).length === 0) {
266
+ printJson({
267
+ error: "Provide at least one of --title, --content, --description, --clear-description",
268
+ }, pretty);
269
+ process.exitCode = 1;
270
+ return;
271
+ }
272
+ try {
273
+ const data = await client.request(`/api/records/${id}`, {
274
+ method: "PATCH",
275
+ json: patch,
276
+ });
277
+ printJson(data, pretty);
278
+ }
279
+ catch (e) {
280
+ printErr(e, pretty);
281
+ process.exitCode = 1;
282
+ }
283
+ });
284
+ records
285
+ .command("delete")
286
+ .description("Delete a record by id (requires token)")
287
+ .argument("<id>", "Record id")
288
+ .action(async function (id) {
289
+ const { client, pretty } = getContext(this);
290
+ try {
291
+ await client.request(`/api/records/${id}`, { method: "DELETE" });
292
+ }
293
+ catch (e) {
294
+ printErr(e, pretty);
295
+ process.exitCode = 1;
296
+ }
297
+ });
298
+ const keys = program.command("keys").description("API keys");
299
+ keys
300
+ .command("list")
301
+ .description("List keys (requires token)")
302
+ .action(async function () {
303
+ const { client, pretty } = getContext(this);
304
+ try {
305
+ const data = await client.request("/api/api-keys");
306
+ printJson(data, pretty);
307
+ }
308
+ catch (e) {
309
+ printErr(e, pretty);
310
+ process.exitCode = 1;
311
+ }
312
+ });
313
+ keys
314
+ .command("get")
315
+ .description("Get one key by id (requires token)")
316
+ .argument("<id>", "Key id")
317
+ .action(async function (id) {
318
+ const { client, pretty } = getContext(this);
319
+ try {
320
+ const data = await client.request(`/api/api-keys/${id}`);
321
+ printJson(data, pretty);
322
+ }
323
+ catch (e) {
324
+ printErr(e, pretty);
325
+ process.exitCode = 1;
326
+ }
327
+ });
328
+ keys
329
+ .command("create")
330
+ .description("Create a key (bootstrap if no keys exist, else requires token)")
331
+ .requiredOption("--name <name>", "Key label")
332
+ .action(async function () {
333
+ const { client, pretty } = getContext(this);
334
+ const name = this.opts().name;
335
+ try {
336
+ const data = await client.request("/api/api-keys", {
337
+ method: "POST",
338
+ json: { name },
339
+ });
340
+ printJson(data, pretty);
341
+ }
342
+ catch (e) {
343
+ printErr(e, pretty);
344
+ process.exitCode = 1;
345
+ }
346
+ });
347
+ keys
348
+ .command("patch")
349
+ .description("Rename or rotate a key (requires token)")
350
+ .argument("<id>", "Key id")
351
+ .option("--name <name>", "New name")
352
+ .option("--rotate", "Rotate secret (new plaintext returned)", false)
353
+ .action(async function (id) {
354
+ const { client, pretty } = getContext(this);
355
+ const opts = this.opts();
356
+ const body = {};
357
+ if (opts.name !== undefined)
358
+ body.name = opts.name;
359
+ if (opts.rotate)
360
+ body.rotate = true;
361
+ if (Object.keys(body).length === 0) {
362
+ printJson({ error: "Provide --name and/or --rotate" }, pretty);
363
+ process.exitCode = 1;
364
+ return;
365
+ }
366
+ try {
367
+ const data = await client.request(`/api/api-keys/${id}`, {
368
+ method: "PATCH",
369
+ json: body,
370
+ });
371
+ printJson(data, pretty);
372
+ }
373
+ catch (e) {
374
+ printErr(e, pretty);
375
+ process.exitCode = 1;
376
+ }
377
+ });
378
+ keys
379
+ .command("delete")
380
+ .description("Delete a key by id (requires token)")
381
+ .argument("<id>", "Key id")
382
+ .action(async function (id) {
383
+ const { client, pretty } = getContext(this);
384
+ try {
385
+ await client.request(`/api/api-keys/${id}`, { method: "DELETE" });
386
+ }
387
+ catch (e) {
388
+ printErr(e, pretty);
389
+ process.exitCode = 1;
390
+ }
391
+ });
392
+ program.parse();
393
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "records-cli",
3
+ "version": "0.0.1",
4
+ "description": "Command-line interface to Records for automation and LLM agents.",
5
+ "type": "module",
6
+ "bin": {
7
+ "records-cli": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skill"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
15
+ "prepublishOnly": "npm run build",
16
+ "dev": "tsc -p tsconfig.json --watch",
17
+ "lint": "eslint",
18
+ "format": "prettier --write \"**/*.{ts,json}\"",
19
+ "typecheck": "tsc --noEmit -p tsconfig.json"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "keywords": [
28
+ "records",
29
+ "cli",
30
+ "llm",
31
+ "knowledge"
32
+ ],
33
+ "dependencies": {
34
+ "commander": "^13.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@eslint/js": "^9.39.2",
38
+ "@types/node": "^22.13.10",
39
+ "eslint": "^9.39.2",
40
+ "globals": "^17.2.0",
41
+ "prettier": "^3.8.1",
42
+ "typescript": "^5.9.3",
43
+ "typescript-eslint": "^8.54.0"
44
+ }
45
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,74 @@
1
+ ---
2
+
3
+ ## name: records-cli
4
+ description: How to record new knowledge for the user via the Records CLI—create groups and records so ideas from the chat become stored documents. Use when the user asks to save, remember, or record something from the conversation, or when you should persist an explanation (lesson, reference, how-to) for later.
5
+ compatibility: Requires the `records-cli` npm package (or a local build). Uses HTTPS against `https://records-api.tolkee.dev` only. Commands that change data need a token; ask the user if you do not have one.
6
+ metadata:
7
+ cli-package: records-cli
8
+ spec-version: "1.0"
9
+
10
+ # Records CLI agent skill
11
+
12
+ ## Install and environment
13
+
14
+ - **Package:** `records-cli` — install with `npm install -g records-cli` or run ad hoc with `npx records-cli`.
15
+ - **Server:** always `https://records-api.tolkee.dev` (not configurable).
16
+ - **Token:** For commands that mutate or list protected data, pass `-t <token>` or set `RECORDS_TOKEN` to the user’s plaintext key.
17
+ - **If you do not already have a key**, ask the user before running protected commands. Do not guess or fabricate a key. Once they share it, use it only for CLI invocations and avoid repeating the full secret in replies when unnecessary.
18
+ - If the user has no key yet, they may create one through their usual Records setup, or—where the server allows it—via the bootstrap flow described in the reference (creating the first key when none exist).
19
+
20
+ ## What Records is for
21
+
22
+ **Records** is how you turn conversation into **persistent knowledge documents**. A **group** is a bucket (topic or project). A **record** inside it is one document: **title**, optional **description**, and **content** (Markdown) that holds the actual knowledge the user wanted to keep.
23
+
24
+ When the user says they want to **save**, **remember**, **note**, or **record** something you explained—do it here: first **survey what already exists**, then **decide placement and granularity**, then **create** (or update) records whose `content` captures the substance in structured Markdown, not just a stub.
25
+
26
+ ### Before creating a record
27
+
28
+ **Do not create blindly.** So you know **where** to put new knowledge—and **how many** records to add—you should:
29
+
30
+ 1. **List all groups** and see what is already there. Run `records-cli groups list --with-records` so each group includes its existing records (titles, slugs, descriptions). That gives you a map of the library.
31
+ 2. **Page through mentally (or explicitly)** the relevant group: if a topic already has records under a matching group slug, run `records-cli group-records list <groupSlug>` when you need the full list or to compare titles.
32
+ 3. **Decide the home for this knowledge**: reuse an existing group if it fits; otherwise create a new group with `records-cli groups create --title "..."`.
33
+ 4. **If something similar already exists**, choose a strategy: one new record vs several focused records; add a new note vs extend an idea with `:::link` to related slugs; avoid duplicate titles when a single updated or linked record is clearer.
34
+
35
+ Only after that planning step should you `**records-cli group-records create ...`** (or `records-cli records patch ...` if you are updating by numeric id).
36
+
37
+ ### Example
38
+
39
+ You teach the user about **math noise for game development** (e.g. Perlin noise, use cases). They say: *“Record that for me.”* You should:
40
+
41
+ 1. **List groups** (`records-cli groups list --with-records`) and check whether a group like **Game dev** (or similar) already exists and what records it contains.
42
+ 2. **Decide**: add one record “Math noise for games” vs split (e.g. “Perlin basics” + “Usage in terrain”); link between them with `:::link` if you split.
43
+ 3. **Create** the group only if no suitable bucket exists; then **create** the new record(s) with a clear **title**, optional **description**, and **content** as full Markdown summarizing what you taught (for example by writing a temp file and passing `--content path`, or piping Markdown to `--content -`).
44
+
45
+ Use the command reference below; see [references/REFERENCE.md](references/REFERENCE.md) for every subcommand, flag, and response shape.
46
+
47
+ ## Markdown `content`: links and embedded HTML
48
+
49
+ Store the body in `**content`** as Markdown. The format allows **custom fenced blocks** for richer reading later (clients that render records should understand these):
50
+
51
+ **Cross-reference another record** — `path` is `groupSlug/recordSlug` (same pattern as in URLs for `group-records get`):
52
+
53
+ ```markdown
54
+ :::link{"path": "game-dev/perlin-noise-basics"} Perlin noise basics :::
55
+ ```
56
+
57
+ The visible **label** sits between the closing `}` and the closing `:::`.
58
+
59
+ **Embed HTML (and optionally JS)** for animations or interactive demos — wrap raw HTML; use `title` for a heading or caption in UIs that show it:
60
+
61
+ ```markdown
62
+ :::html{"title": "2D noise preview"}
63
+ <html>
64
+ <!-- markup / script for interactivity -->
65
+ </html>
66
+ :::
67
+ ```
68
+
69
+ Use `:::html` sparingly and only when plain Markdown is not enough (e.g. small canvas demo, animation). Prefer `:::link` to connect related records instead of duplicating text.
70
+
71
+ ## Further reading
72
+
73
+ - Full commands, flags, and JSON shapes: [references/REFERENCE.md](references/REFERENCE.md).
74
+
@@ -0,0 +1,148 @@
1
+ # Records CLI — technical reference
2
+
3
+ **Global options** (before any subcommand):
4
+
5
+
6
+ | Flag | Env | Purpose |
7
+ | --------------------- | --------------- | ------------------------------------ |
8
+ | `-t, --token <token>` | `RECORDS_TOKEN` | Plaintext key for protected commands |
9
+ | `-p, --pretty` | — | Pretty-print JSON on stdout |
10
+
11
+
12
+ All requests use `**https://records-api.tolkee.dev`** as the origin (fixed).
13
+
14
+ Successful responses are JSON on **stdout** (usually `{ "data": … }`). Failures print JSON with `error` (and `status` / `body` when available) on **stdout**; the process exits non-zero.
15
+
16
+ Convention: `<id>` arguments are **positive integers** unless noted. For `group-records create`, put Markdown in a file or pipe to stdin with `--content -`.
17
+
18
+ ---
19
+
20
+ ## Record content (Markdown)
21
+
22
+ Each record’s `**content`** field is **Markdown** for long-form knowledge: headings, lists, code fences, etc.
23
+
24
+ Renderers may support **custom directive blocks** inside `content`:
25
+
26
+ ### `:::link` — reference another record
27
+
28
+ Links to a record by path `**groupSlug/recordSlug`** (the slugs returned by the CLI and used in `group-records get`).
29
+
30
+ ```markdown
31
+ :::link{"path": "game-dev/perlin-noise"} Read the full Perlin note :::
32
+ ```
33
+
34
+ - `**path**` (JSON string): `groupSlug/recordSlug`.
35
+ - **Label**: text between the closing `}` and the closing `:::` (what readers see as the link text).
36
+
37
+ ### `:::html` — embedded HTML (and scripts)
38
+
39
+ ```markdown
40
+ :::html{"title": "Noise preview"}
41
+ <html>
42
+ <!-- e.g. canvas, SVG, or script-driven interaction -->
43
+ </html>
44
+ :::
45
+ ```
46
+
47
+ - `**title**` (JSON string): optional caption or section title for renderers.
48
+
49
+ ---
50
+
51
+ ## Commands
52
+
53
+ ### `health`
54
+
55
+
56
+ | Command | Notes |
57
+ | -------------------- | ------------------------------------------------------------ |
58
+ | `records-cli health` | No auth. Returns `{ "status": "ok" }` when the server is up. |
59
+
60
+
61
+ ---
62
+
63
+ ### `keys`
64
+
65
+
66
+ | Command | Auth | Notes |
67
+ | -------------------------------------------------------- | ----------------------- | --------------------------------------------------------------- |
68
+ | `records-cli keys list` | Bearer | 200 `{ "data": ApiKeyPublic[] }` |
69
+ | `records-cli keys get <id>` | Bearer | 200 `{ "data": ApiKeyPublic }` or missing |
70
+ | `records-cli keys create --name <name>` | **Bootstrap** or Bearer | 201 `{ "data": { id, name, key } }` — `**key` only on create** |
71
+ | `records-cli keys patch <id> [--name <name>] [--rotate]` | Bearer | 200 `{ "data": … }` — if `--rotate`, response may include `key` |
72
+ | `records-cli keys delete <id>` | Bearer | Success: empty body, exit 0 |
73
+
74
+
75
+ **Create body:** `{ "name": string }` (trimmed, min length 1).
76
+
77
+ **Patch body:** optional `name`; optional `rotate: true` (at least one required).
78
+
79
+ **Public row:** `{ "id": number, "name": string }` — never includes secrets except when creating or rotating.
80
+
81
+ ---
82
+
83
+ ### `groups`
84
+
85
+
86
+ | Command | Auth | Notes |
87
+ | ---------------------------------------------------- | ------ | -------------------------------------------------------------------------- |
88
+ | `records-cli groups list [--with-records]` | No | With `--with-records`: each group may include `"records": RecordSummary[]` |
89
+ | `records-cli groups get (--id <id> | --slug <slug>)` | No | Exactly one of `--id` or `--slug` |
90
+ | `records-cli groups create --title <title>` | Bearer | 201 `{ "data": GroupRow }` |
91
+ | `records-cli groups patch <id> --title <title>` | Bearer | 200 `{ "data": GroupRow }` |
92
+ | `records-cli groups delete <id>` | Bearer | Success: empty body |
93
+
94
+
95
+ **Group row:** `{ "id", "title", "slug" }`
96
+ **With records:** plus `"records": RecordSummary[]`
97
+ **Record summary:** `{ "id", "title", "slug", "description" }` (no `content`)
98
+
99
+ **Create:** `{ "title": string }` — trimmed, min 1.
100
+ **Patch:** `{ "title": string }`.
101
+
102
+ Slugs are generated from titles (unique per scope).
103
+
104
+ ---
105
+
106
+ ### `group-records` (nested under a group slug)
107
+
108
+
109
+ | Command | Auth | Notes |
110
+ | ---------------------------------------------------------------------------------------------------- | ------ | --------------------------------- |
111
+ | `records-cli group-records list <groupSlug>` | No | 200 `{ "data": RecordSummary[] }` |
112
+ | `records-cli group-records get <groupSlug> <recordSlug>` | No | 200 `{ "data": RecordRow }` |
113
+ | `records-cli group-records create <groupSlug> --title <t> --content <path|-> [--description <text>]` | Bearer | 201 `{ "data": RecordRow }` |
114
+
115
+
116
+ **Create body:** `title` (required), `content` (required), optional `description`.
117
+
118
+ ---
119
+
120
+ ### `records` (by numeric id)
121
+
122
+
123
+ | Command | Auth | Notes |
124
+ | ----------------------------------------------------------------------------------------------------- | ------ | ----------------------------------------------- |
125
+ | `records-cli records get <id>` | No | 200 `{ "data": RecordRow }` |
126
+ | `records-cli records patch <id> [--title] [--content <path|->] [--description] [--clear-description]` | Bearer | At least one field; 200 `{ "data": RecordRow }` |
127
+ | `records-cli records delete <id>` | Bearer | Success: empty body |
128
+
129
+
130
+ **Record row:** `{ "id", "title", "description", "slug", "groupId", "content" }`
131
+
132
+ ---
133
+
134
+ ## Status codes
135
+
136
+ Non-success exits with JSON error payload. Typical meanings:
137
+
138
+
139
+ | Code | Typical cause |
140
+ | ---- | ---------------------- |
141
+ | 200 | OK |
142
+ | 201 | Created |
143
+ | 204 | No content (delete) |
144
+ | 400 | Validation failed |
145
+ | 401 | Missing/invalid Bearer |
146
+ | 404 | Not found |
147
+
148
+