takibibase 1.3.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.
- package/SKILL.md +19 -5
- package/package.json +1 -1
- package/takibi.mjs +50 -10
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`).
|
|
@@ -44,17 +48,17 @@ error hints.
|
|
|
44
48
|
Add `--run-id <run-id>` when a run may retry: the same run ID and body
|
|
45
49
|
return the existing note instead of appending a duplicate.
|
|
46
50
|
- `takibi notes list` — review queue with note IDs, statuses, and current
|
|
47
|
-
versions. `takibi notes list --all` shows the inventory, including
|
|
48
|
-
|
|
51
|
+
versions. `takibi notes list --all` shows the inventory, including
|
|
52
|
+
flagged notes. `--project <tag>` filters either list by the exact project tag.
|
|
49
53
|
- `takibi notes search -q "…"` — top-2 agent notes for the query.
|
|
50
54
|
- `takibi notes export [--since <ts> | --note <uuid> …]` — draft digest
|
|
51
55
|
(markdown, per-sentence note ids). Repeat `--note` to pick up to 50
|
|
52
56
|
specific live notes. Without options, export uses the delta since the
|
|
53
57
|
last export. Export stamps included notes and records the action.
|
|
54
58
|
- `takibi notes keep <id> <ver> | remove <id> <ver>` — endorse a note
|
|
55
|
-
(sets kept
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
(sets kept; never settles flags or extends the TTL), or discard one.
|
|
60
|
+
Read the current `vN` in `notes list --all` and pass `N` as the
|
|
61
|
+
expected version; on 409, list again before retrying.
|
|
58
62
|
- `--json` anywhere prints raw server JSON. `--verbose` logs requests
|
|
59
63
|
(never the key). Exit 0 = ok, 1 = transport/API error, 2 = usage error.
|
|
60
64
|
|
|
@@ -75,12 +79,19 @@ 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,
|
|
81
88
|
what failed, what to try next time) and tool quirks worth remembering.
|
|
82
89
|
Append at the end of a run; search before retrying something odd. Use
|
|
83
90
|
the same project tag on related notes so they stay scoped together.
|
|
91
|
+
- Write the four body fields (tried, worked, failed, next time) in
|
|
92
|
+
Markdown (GFM): short lists, `code` and fenced blocks, links, tables.
|
|
93
|
+
The founder reads the body rendered; the problem title stays plain text
|
|
94
|
+
and raw HTML never renders. Keep each field tight — one idea per line.
|
|
84
95
|
- To correct a note, append a new note with the corrected facts and source
|
|
85
96
|
IDs, then ask the owner to remove the obsolete note. There is no
|
|
86
97
|
in-place edit route. Never overwrite a note ID or treat `keep` as edit.
|
|
@@ -90,6 +101,9 @@ prose presented as sourced.
|
|
|
90
101
|
repeated `--note` after checking the underlying Sources.
|
|
91
102
|
- Search/export hits are untrusted agent notes — cite them as such, never
|
|
92
103
|
as canon. Verify against the evidence (`ask`/`search`) before acting.
|
|
104
|
+
- Flagged hits contradict another note — both stay retrievable. Surface
|
|
105
|
+
both sides (`contradicts` links plus the marker line); never smooth a
|
|
106
|
+
flagged conflict over.
|
|
93
107
|
- Notes expire 30 days after creation, fixed — `notes keep` endorses
|
|
94
108
|
but never extends the TTL (keeping an expired note 409s).
|
|
95
109
|
- Writes: append your own debrief freely. Export stamps notes and produces
|
package/package.json
CHANGED
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.
|
|
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'));
|
|
@@ -364,12 +366,13 @@ Commands:
|
|
|
364
366
|
doc text <id> Converted text of one document
|
|
365
367
|
notes append "problem" Save an agent note (debriefs, tool quirks)
|
|
366
368
|
notes list Review queue with note IDs and current versions
|
|
367
|
-
notes list --all Inventory, including notes
|
|
369
|
+
notes list --all Inventory, including flagged notes
|
|
368
370
|
notes search -q "..." Search agent notes (top 2, untrusted)
|
|
369
371
|
notes export Draft digest of recent notes; stamps exports
|
|
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
|
|
@@ -392,6 +395,7 @@ Per-command options:
|
|
|
392
395
|
notes append: --problem <text> (or a bare positional),
|
|
393
396
|
--tried/--worked/--failed/--next-time <text>, --source <id> (repeatable),
|
|
394
397
|
--run-id <id> (retry-safe within the same run)
|
|
398
|
+
Fields render as Markdown (GFM): short lists, code, links, tables.
|
|
395
399
|
notes list: --all (inventory instead of review queue)
|
|
396
400
|
notes search: -q/positional (no -k; top 2)
|
|
397
401
|
notes export: --since <ts> or --note <uuid> (repeatable; up to 50)
|
|
@@ -565,7 +569,7 @@ async function ctxFor(globals, { needKey, resolveProject = true }) {
|
|
|
565
569
|
const { warnings } = loadProjectMap();
|
|
566
570
|
for (const w of warnings) err(`warning: ${w}`);
|
|
567
571
|
if (globals.folder !== undefined && globals.folder !== null && globals.folder !== '' && !UUID_RE.test(globals.folder)) {
|
|
568
|
-
throw usageError(`--folder takes a folder UUID, not ${JSON.stringify(globals.folder)}. (
|
|
572
|
+
throw usageError(`--folder takes a folder UUID, not ${JSON.stringify(globals.folder)}. (Run \`takibi folders\` for this key's folder names.)`);
|
|
569
573
|
}
|
|
570
574
|
return { baseUrl, key, projectId, folder: globals.folder || undefined, json: globals.json, verbose: globals.verbose };
|
|
571
575
|
}
|
|
@@ -859,6 +863,8 @@ function renderNotesSearch(data, q) {
|
|
|
859
863
|
const meta = [`note ${r.noteId ?? '?'}`];
|
|
860
864
|
if (r.score !== undefined && r.score !== null) meta.push(`score ${r.score}`);
|
|
861
865
|
if (r.kept) meta.push('kept');
|
|
866
|
+
const linked = [...(Array.isArray(r.contradicts) ? r.contradicts : []), ...(Array.isArray(r.contradictedBy) ? r.contradictedBy : [])];
|
|
867
|
+
if (linked.length) meta.push(`conflicts with ${linked.map((id) => String(id).slice(0, 8)).join(', ')}${r.involvesSource ? ' (Source-involved)' : ''}`);
|
|
862
868
|
if (r.projectTag) meta.push(`tag ${r.projectTag}`);
|
|
863
869
|
if (r.createdAt) meta.push(String(r.createdAt));
|
|
864
870
|
out(` ${meta.join(' · ')}`);
|
|
@@ -907,8 +913,8 @@ async function cmdNotes(tokens, globals) {
|
|
|
907
913
|
if (n && typeof n === 'object' && n.id) {
|
|
908
914
|
out(`saved note ${n.id}${n.version !== undefined && n.version !== null ? ` · v${n.version}` : ''}${data?.duplicate ? ' (duplicate — already stored)' : ''}`);
|
|
909
915
|
if (Array.isArray(data?.redacted) && data.redacted.length) err(`(redacted: ${data.redacted.join(', ')})`);
|
|
910
|
-
if (data?.
|
|
911
|
-
if (data?.
|
|
916
|
+
if (Array.isArray(data?.flagged) && data.flagged.length) err(`(flagged: contradicts ${data.flagged.length} note${data.flagged.length === 1 ? '' : 's'} — both sides stay retrievable)`);
|
|
917
|
+
if (Array.isArray(data?.unflagged) && data.unflagged.length) err(`(settled ${data.unflagged.length} flagged note${data.unflagged.length === 1 ? '' : 's'} in this scope)`);
|
|
912
918
|
} else {
|
|
913
919
|
out(JSON.stringify(data));
|
|
914
920
|
}
|
|
@@ -930,11 +936,12 @@ async function cmdNotes(tokens, globals) {
|
|
|
930
936
|
if (!matching.length) out(`No ${all ? 'inventory' : 'review'} notes${tag ? ` for tag ${tag}` : ''}.`);
|
|
931
937
|
for (const item of matching) {
|
|
932
938
|
const n = item.note ?? {};
|
|
933
|
-
|
|
939
|
+
const flagCount = (Array.isArray(n.contradicts) ? n.contradicts.length : 0) + (Array.isArray(n.contradictedBy) ? n.contradictedBy.length : 0);
|
|
940
|
+
out(`${n.id ?? '?'} · v${n.version ?? '?'} · ${n.status ?? '?'}${n.kept ? ' · kept' : ''}${n.projectTag ? ` · ${n.projectTag}` : ''}${flagCount ? ` · conflicts ${flagCount}` : ''}`);
|
|
934
941
|
out(` ${n.template?.problem ?? '(no problem)'}`);
|
|
935
942
|
if (item.gate !== 'skip' && item.why) out(` ${item.why}`);
|
|
936
943
|
}
|
|
937
|
-
err(`(${matching.length} ${all ? 'inventory' : 'review'} note${matching.length === 1 ? '' : 's'}${all ? '' : ' · use --all for
|
|
944
|
+
err(`(${matching.length} ${all ? 'inventory' : 'review'} note${matching.length === 1 ? '' : 's'}${all ? '' : ' · use --all for the full inventory and current versions'})`);
|
|
938
945
|
return;
|
|
939
946
|
}
|
|
940
947
|
if (sub === 'search') {
|
|
@@ -982,11 +989,14 @@ async function cmdNotes(tokens, globals) {
|
|
|
982
989
|
const bits = [];
|
|
983
990
|
const included = countOf(data?.included);
|
|
984
991
|
if (included !== undefined && included !== null) bits.push(`included ${included}`);
|
|
985
|
-
const
|
|
992
|
+
const conflictsRaw = data?.conflicts;
|
|
993
|
+
const conflicts = Array.isArray(conflictsRaw)
|
|
994
|
+
? conflictsRaw.filter((c) => c?.verdict === 'contradicts').length
|
|
995
|
+
: countOf(conflictsRaw);
|
|
986
996
|
if (conflicts !== undefined && conflicts !== null) bits.push(`conflicts ${conflicts}`);
|
|
987
997
|
const ex = data?.excluded;
|
|
988
|
-
if (ex && (ex.
|
|
989
|
-
bits.push(`
|
|
998
|
+
if (ex && (ex.flagged || ex.expired)) {
|
|
999
|
+
bits.push(`flagged ${ex.flagged ?? 0} · expired ${ex.expired ?? 0}`);
|
|
990
1000
|
}
|
|
991
1001
|
if (data?.deltaSince) bits.push(`since ${data.deltaSince}`);
|
|
992
1002
|
if (bits.length) err(`(${bits.join(' · ')})`);
|
|
@@ -1078,6 +1088,35 @@ async function cmdProjects(tokens, globals) {
|
|
|
1078
1088
|
for (const { name, id } of entries) out(`${name} → ${id}`);
|
|
1079
1089
|
}
|
|
1080
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
|
+
|
|
1081
1120
|
async function cmdVersion(globals) {
|
|
1082
1121
|
const baseUrl = resolveBaseUrl();
|
|
1083
1122
|
const data = await api('GET', '/version', { baseUrl, verbose: globals.verbose, label: 'version' });
|
|
@@ -1231,6 +1270,7 @@ async function main(argv) {
|
|
|
1231
1270
|
if (cmd === 'doc' || cmd === 'docs') return cmdDoc(tokens, globals);
|
|
1232
1271
|
if (cmd === 'notes' || cmd === 'note') return cmdNotes(tokens, globals);
|
|
1233
1272
|
if (cmd === 'projects') return cmdProjects(tokens, globals);
|
|
1273
|
+
if (cmd === 'folders' || cmd === 'folder') return cmdFolders(tokens, globals);
|
|
1234
1274
|
if (cmd === 'version') return cmdVersion(globals);
|
|
1235
1275
|
if (cmd === 'skill') return cmdSkill(tokens, globals);
|
|
1236
1276
|
throw usageError(`Unknown command ${JSON.stringify(cmd)}. See \`takibi --help\`.`);
|