takibibase 1.5.0 → 1.7.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 (3) hide show
  1. package/SKILL.md +7 -0
  2. package/package.json +1 -1
  3. package/takibi.mjs +35 -2
package/SKILL.md CHANGED
@@ -23,6 +23,10 @@ error hints.
23
23
  - Projects: `takibi projects` lists what this key can reach. `--project`
24
24
  takes a name or a UUID; single-grant keys may omit it.
25
25
  `takibi projects --add <name> <uuid>` keeps local aliases for offline use.
26
+ - Folders: `takibi folders [--project …]` lists folder names → uuids for
27
+ `--folder` (only folders this key's grants touch; single-grant keys may
28
+ omit `--project`). Discovery flow: `projects` → `folders` → scoped
29
+ `ask`/`search`/`doc list`.
26
30
  - First probe: `takibi version` (needs no key; shows build + `jev` status).
27
31
  - Update notice: the CLI polls the npm registry once a day and nudges on
28
32
  stderr when behind (never on `--json`).
@@ -75,6 +79,9 @@ back to `search`, try `doc text` on the hits — then either answer from
75
79
  evidence or say the evidence is not there. Never fill gaps with generated
76
80
  prose presented as sourced.
77
81
 
82
+ Split bundled asks: one narrow ask per field — a combined question
83
+ abstains honestly instead of answering halfway.
84
+
78
85
  ## Notes (agent scratchpad, not canon)
79
86
 
80
87
  - When to use: end-of-run debriefs (problem, what you tried, what worked,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "takibibase",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Thin CLI for the Takibi API: ask, search, tasks, docs, notes. Single file, zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
package/takibi.mjs CHANGED
@@ -15,6 +15,8 @@
15
15
  * reads the API first and --project resolves names through it. A LOCAL
16
16
  * map (~/.takibi/projects, `name=uuid` lines) remains as the offline
17
17
  * fallback and for custom aliases. UUIDs pass straight through.
18
+ * `folders` lists granted folders via GET /v1/projects/:id/folders the
19
+ * same way (single-grant keys may omit --project; the CLI defaults it).
18
20
  * - There is no GET /v1/tasks/:id, so `tasks get` lists and filters.
19
21
  * - Account-only routes (doc download, uploads, deletes) answer 401, not
20
22
  * 403, to Bearer callers; `doc download` refuses client-side with a hint.
@@ -34,7 +36,7 @@ import { homedir } from 'node:os';
34
36
  import { join } from 'node:path';
35
37
 
36
38
  /** Baked fallback; the published package re-reads package.json next door. */
37
- const BAKED_VERSION = '1.5.0';
39
+ const BAKED_VERSION = '1.7.0';
38
40
  const CLI_INFO = (() => {
39
41
  try {
40
42
  const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
@@ -370,6 +372,7 @@ Commands:
370
372
  notes keep <id> <ver> Endorse a note (expected version)
371
373
  notes remove <id> <ver> Discard a note (expected version)
372
374
  projects Granted projects (names -> uuids for --project)
375
+ folders Granted folders (names -> uuids for --folder)
373
376
  version What build is serving + jev wired|unwired (no key needed)
374
377
  skill Print the takibi-use agent skill (pipe it to an agent)
375
378
  skill --install Install the skill into agent skills dirs
@@ -566,7 +569,7 @@ async function ctxFor(globals, { needKey, resolveProject = true }) {
566
569
  const { warnings } = loadProjectMap();
567
570
  for (const w of warnings) err(`warning: ${w}`);
568
571
  if (globals.folder !== undefined && globals.folder !== null && globals.folder !== '' && !UUID_RE.test(globals.folder)) {
569
- throw usageError(`--folder takes a folder UUID, not ${JSON.stringify(globals.folder)}. (Folder names are not resolvable — keys cannot list folders.)`);
572
+ throw usageError(`--folder takes a folder UUID, not ${JSON.stringify(globals.folder)}. (Run \`takibi folders\` for this key's folder names.)`);
570
573
  }
571
574
  return { baseUrl, key, projectId, folder: globals.folder || undefined, json: globals.json, verbose: globals.verbose };
572
575
  }
@@ -1085,6 +1088,35 @@ async function cmdProjects(tokens, globals) {
1085
1088
  for (const { name, id } of entries) out(`${name} → ${id}`);
1086
1089
  }
1087
1090
 
1091
+ async function cmdFolders(tokens, globals) {
1092
+ if (tokens.length > 0) throw usageError(`Unexpected ${JSON.stringify(tokens[0])}. Usage: takibi folders [--project <name-or-uuid>]`);
1093
+ const ctx = await ctxFor(globals, { needKey: true });
1094
+ let projectId = ctx.projectId;
1095
+ if (!projectId) {
1096
+ // The server cannot default a path param, so single-grant keys resolve
1097
+ // their one project client-side (ask/search default server-side).
1098
+ const data = await api('GET', '/v1/projects', { baseUrl: ctx.baseUrl, key: ctx.key, verbose: ctx.verbose, label: 'projects' });
1099
+ const ids = [...new Set((data?.projects ?? []).map((p) => p?.id).filter(Boolean))];
1100
+ // Tag-only grants list no collections, so an empty list is ambiguous:
1101
+ // the key may hold nothing, or hold tags. Say both, accurately.
1102
+ if (ids.length === 0) throw new CliError('This key lists no projects.', { hint: 'Tag-only keys cannot list collections — pass --project <uuid> explicitly (ask the account owner for the id), or grant this profile a collection or folder.' });
1103
+ if (ids.length > 1) throw usageError('This key spans projects — pass --project <name-or-uuid>.');
1104
+ projectId = ids[0];
1105
+ }
1106
+ const data = await api('GET', `/v1/projects/${projectId}/folders`, { baseUrl: ctx.baseUrl, key: ctx.key, verbose: ctx.verbose, label: 'folders' });
1107
+ if (ctx.json) {
1108
+ out(JSON.stringify(data, null, 2));
1109
+ return;
1110
+ }
1111
+ const entries = data?.folders ?? [];
1112
+ if (!entries.length) {
1113
+ out('No folders found.');
1114
+ err('(This project has no folders this key can reach. Tag-only keys see only folders holding their tagged files.)');
1115
+ return;
1116
+ }
1117
+ for (const f of entries) out(`${f.name} → ${f.id}`);
1118
+ }
1119
+
1088
1120
  async function cmdVersion(globals) {
1089
1121
  const baseUrl = resolveBaseUrl();
1090
1122
  const data = await api('GET', '/version', { baseUrl, verbose: globals.verbose, label: 'version' });
@@ -1238,6 +1270,7 @@ async function main(argv) {
1238
1270
  if (cmd === 'doc' || cmd === 'docs') return cmdDoc(tokens, globals);
1239
1271
  if (cmd === 'notes' || cmd === 'note') return cmdNotes(tokens, globals);
1240
1272
  if (cmd === 'projects') return cmdProjects(tokens, globals);
1273
+ if (cmd === 'folders' || cmd === 'folder') return cmdFolders(tokens, globals);
1241
1274
  if (cmd === 'version') return cmdVersion(globals);
1242
1275
  if (cmd === 'skill') return cmdSkill(tokens, globals);
1243
1276
  throw usageError(`Unknown command ${JSON.stringify(cmd)}. See \`takibi --help\`.`);