@cpenned/cli 0.1.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/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/client.js +58 -0
- package/dist/client.js.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/format.js +174 -0
- package/dist/format.js.map +1 -0
- package/dist/index.js +526 -0
- package/dist/index.js.map +1 -0
- package/dist/skills.js +84 -0
- package/dist/skills.js.map +1 -0
- package/package.json +57 -0
- package/skills/open-brain-cli/SKILL.md +116 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chris Pennington
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @cpenned/cli
|
|
2
|
+
|
|
3
|
+
`ob` - a terminal client for Open Brain. It is a thin, full-coverage wrapper
|
|
4
|
+
over the [`/v1` HTTP API](../../README.md#v1-http-api--docs): every command maps
|
|
5
|
+
to one endpoint, so the API + guard layer stay the single source of truth. Same
|
|
6
|
+
contract as the [MCP server](../mcp/README.md), different front end.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
The CLI is published to npm. Install it globally and you have the `ob` bin on
|
|
11
|
+
your `PATH` - no clone or build required:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @cpenned/cli
|
|
15
|
+
ob --help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### From source (development)
|
|
19
|
+
|
|
20
|
+
Building from the monorepo instead:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run build -w @cpenned/cli # emits packages/cli/dist/index.js
|
|
24
|
+
node packages/cli/dist/index.js --help
|
|
25
|
+
npm link -w @cpenned/cli # …or link it onto PATH, then: ob --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configure
|
|
29
|
+
|
|
30
|
+
`ob` needs the deployment origin and an `obr_` API key. **Mint the key from the
|
|
31
|
+
web app (Settings → New API key)** so the CLI hits the same vault as the web UI
|
|
32
|
+
and email. Then save it:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
ob config set --url https://<deployment>.convex.site --key obr_...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This writes `~/.open-brain/config.json` (chmod 600). Resolution order is
|
|
39
|
+
**environment over file**, so you can override per-command or in CI without
|
|
40
|
+
touching the file:
|
|
41
|
+
|
|
42
|
+
| Variable | Meaning |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `OPEN_BRAIN_API_URL` | Deployment origin (`.convex.site` on Convex) |
|
|
45
|
+
| `OPEN_BRAIN_API_KEY` | `obr_` API key |
|
|
46
|
+
| `OPEN_BRAIN_CLIENT_TYPE` | Stamped as `X-Client` (default `cli`) |
|
|
47
|
+
|
|
48
|
+
Inspect with `ob config show` (the key is masked) or `ob config path`.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
Every command prints a calm, human-readable summary. Add `--json` to any command
|
|
53
|
+
to get the raw API payload for piping into `jq` or scripts.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Views
|
|
57
|
+
ob today
|
|
58
|
+
ob view upcoming --tz America/New_York
|
|
59
|
+
|
|
60
|
+
# Tasks
|
|
61
|
+
ob task create "Email the landlord" --garden <gardenId> --when today --priority high
|
|
62
|
+
ob task list --garden <gardenId> --status todo in_progress
|
|
63
|
+
ob task get <taskId>
|
|
64
|
+
ob task update <taskId> --deadline 2026-07-01
|
|
65
|
+
ob task done <taskId>
|
|
66
|
+
ob task rm <taskId>
|
|
67
|
+
ob task tag-add <taskId> --tag <tagId> <tagId>
|
|
68
|
+
echo '[{"gardenId":"...","title":"a"},{"gardenId":"...","title":"b"}]' \
|
|
69
|
+
| ob task create-batch --partial
|
|
70
|
+
|
|
71
|
+
# Projects / gardens / tags (same create|list|get|update|rm shape)
|
|
72
|
+
ob project create "Q3 launch" --garden <gardenId>
|
|
73
|
+
ob garden create "Health" --context "fitness, doctor, meds"
|
|
74
|
+
ob tag create urgent --color "#c1502e"
|
|
75
|
+
|
|
76
|
+
# Reviews
|
|
77
|
+
ob review queue
|
|
78
|
+
ob review garden <gardenId> # deep review
|
|
79
|
+
ob review request <gardenId> # force into the queue
|
|
80
|
+
ob review task <taskId> # mark reviewed
|
|
81
|
+
|
|
82
|
+
# AI (needs the relevant provider keys configured on the deployment)
|
|
83
|
+
ob find authentication bug
|
|
84
|
+
ob triage "call dentist, buy milk, draft the Q3 deck"
|
|
85
|
+
|
|
86
|
+
# API keys (needs the keys:manage scope)
|
|
87
|
+
ob key create "ci runner" --scope tasks:write reviews:*
|
|
88
|
+
ob key list
|
|
89
|
+
ob key rotate <keyId>
|
|
90
|
+
ob key revoke <keyId>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Run `ob <group> --help` (e.g. `ob task --help`) for the full option list of any
|
|
94
|
+
command group.
|
|
95
|
+
|
|
96
|
+
## Agent skills
|
|
97
|
+
|
|
98
|
+
The CLI ships with the `open-brain-cli` agent skill so an assistant (e.g. Claude
|
|
99
|
+
Code) can drive `ob` on your machine. Install it into your skills directory:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
ob skills list # what's bundled, and whether it's installed
|
|
103
|
+
ob skills install # copy into ~/.claude/skills
|
|
104
|
+
ob skills install --force # overwrite an existing copy
|
|
105
|
+
ob skills uninstall # remove it again
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Skills install into `~/.claude/skills` by default; pass `--dir <path>` to target
|
|
109
|
+
another location. Restart your agent afterwards so it picks up the new skill.
|
|
110
|
+
|
|
111
|
+
## Develop
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run typecheck -w @cpenned/cli
|
|
115
|
+
npm test -w @cpenned/cli # vitest: client, config, format
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The CLI is intentionally logic-light - it builds request bodies/queries and
|
|
119
|
+
renders responses. All validation, the status machine, ownership, and rate
|
|
120
|
+
limits are enforced server-side by `/v1`.
|
|
121
|
+
|
|
122
|
+
## Publishing
|
|
123
|
+
|
|
124
|
+
MIT-licensed and publish-ready (`files`, `publishConfig`, `prepublishOnly`
|
|
125
|
+
build). To cut a release: bump the version, then `npm publish -w @cpenned/cli`
|
|
126
|
+
from the repo root (`@cpenned` is the owner's npm username scope, so no org is
|
|
127
|
+
needed; you must be authenticated as that user). `prepublishOnly` runs the build
|
|
128
|
+
(bundle skills + `tsc`) first.
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ApiError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class OpenBrainClient {
|
|
10
|
+
config;
|
|
11
|
+
fetchFn;
|
|
12
|
+
constructor(config, fetchFn = fetch) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.fetchFn = fetchFn;
|
|
15
|
+
}
|
|
16
|
+
async request(method, path, opts = {}) {
|
|
17
|
+
const url = new URL(this.config.baseUrl + path);
|
|
18
|
+
if (opts.query) {
|
|
19
|
+
for (const [key, value] of Object.entries(opts.query)) {
|
|
20
|
+
if (value !== undefined && value !== null)
|
|
21
|
+
url.searchParams.set(key, String(value));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const headers = {
|
|
25
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
26
|
+
"X-Client": this.config.clientType,
|
|
27
|
+
};
|
|
28
|
+
if (opts.body !== undefined)
|
|
29
|
+
headers["Content-Type"] = "application/json";
|
|
30
|
+
if (opts.timezone)
|
|
31
|
+
headers["X-Timezone"] = opts.timezone;
|
|
32
|
+
const res = await this.fetchFn(url.toString(), {
|
|
33
|
+
method,
|
|
34
|
+
headers,
|
|
35
|
+
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
36
|
+
});
|
|
37
|
+
const text = await res.text();
|
|
38
|
+
const data = text ? JSON.parse(text) : null;
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const message = data?.error?.message ?? res.statusText ?? `HTTP ${res.status}`;
|
|
41
|
+
throw new ApiError(res.status, message);
|
|
42
|
+
}
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
get(path, query) {
|
|
46
|
+
return this.request("GET", path, { query });
|
|
47
|
+
}
|
|
48
|
+
post(path, body, timezone) {
|
|
49
|
+
return this.request("POST", path, { body, timezone });
|
|
50
|
+
}
|
|
51
|
+
patch(path, body) {
|
|
52
|
+
return this.request("PATCH", path, { body });
|
|
53
|
+
}
|
|
54
|
+
del(path, body) {
|
|
55
|
+
return this.request("DELETE", path, { body });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// CLI configuration. Resolution order: environment variables override the
|
|
2
|
+
// on-disk config file (~/.open-brain/config.json), which `ob config set`
|
|
3
|
+
// writes. This lets you keep a key on disk for daily use yet override it per
|
|
4
|
+
// command (e.g. point at a different deployment) without editing the file.
|
|
5
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
const CONFIG_DIR = join(homedir(), ".open-brain");
|
|
9
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
10
|
+
export function configPath() {
|
|
11
|
+
return CONFIG_FILE;
|
|
12
|
+
}
|
|
13
|
+
export function readStoredConfig() {
|
|
14
|
+
if (!existsSync(CONFIG_FILE))
|
|
15
|
+
return {};
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function saveConfig(patch) {
|
|
24
|
+
const next = { ...readStoredConfig() };
|
|
25
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
26
|
+
if (value !== undefined && value !== "")
|
|
27
|
+
next[key] = value;
|
|
28
|
+
}
|
|
29
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
30
|
+
writeFileSync(CONFIG_FILE, `${JSON.stringify(next, null, 2)}\n`);
|
|
31
|
+
chmodSync(CONFIG_FILE, 0o600); // contains the secret key
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
34
|
+
const normalizeBaseUrl = (url) => url.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
35
|
+
// Pure resolver (env over file). Exported for testing; loadConfig wires the
|
|
36
|
+
// real file in.
|
|
37
|
+
export function resolveConfig(env, file) {
|
|
38
|
+
const baseUrl = env.OPEN_BRAIN_API_URL ?? file.url;
|
|
39
|
+
const apiKey = env.OPEN_BRAIN_API_KEY ?? file.apiKey;
|
|
40
|
+
if (!baseUrl) {
|
|
41
|
+
throw new Error("No API URL configured. Run `ob config set --url https://<deployment>.convex.site` or set OPEN_BRAIN_API_URL.");
|
|
42
|
+
}
|
|
43
|
+
if (!apiKey) {
|
|
44
|
+
throw new Error("No API key configured. Run `ob config set --key obr_...` or set OPEN_BRAIN_API_KEY.");
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
baseUrl: normalizeBaseUrl(baseUrl),
|
|
48
|
+
apiKey,
|
|
49
|
+
clientType: env.OPEN_BRAIN_CLIENT_TYPE ?? file.clientType ?? "cli",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function loadConfig(env = process.env) {
|
|
53
|
+
return resolveConfig(env, readStoredConfig());
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAiBjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AAClD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,UAAU,UAAU;IACxB,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAiB,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,MAAM,IAAI,GAAiB,EAAE,GAAG,gBAAgB,EAAE,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,IAAI,CAAC,GAAyB,CAAC,GAAG,KAAK,CAAC;IACnF,CAAC;IACD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,0BAA0B;IACzD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAU,EAAE,CAC/C,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAE/C,4EAA4E;AAC5E,gBAAgB;AAChB,MAAM,UAAU,aAAa,CAAC,GAAsB,EAAE,IAAkB;IACtE,MAAM,OAAO,GAAG,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,GAAG,CAAC;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,MAAM,CAAC;IACrD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,8GAA8G,CAC/G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;QAClC,MAAM;QACN,UAAU,EAAE,GAAG,CAAC,sBAAsB,IAAI,IAAI,CAAC,UAAU,IAAI,KAAK;KACnE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Output rendering. With --json we print the raw API payload (for piping into
|
|
2
|
+
// jq/scripts); otherwise we render a calm, human-readable summary and steer
|
|
3
|
+
// power users to --json for full detail. The renderer recognises the API's
|
|
4
|
+
// response envelopes (list pagination, search, review queue, issued key,
|
|
5
|
+
// triage) and falls back to a key/value dump for single resources.
|
|
6
|
+
import { ApiError } from "./client.js";
|
|
7
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
8
|
+
const wrap = (code, s) => useColor ? `\x1b[${code}m${s}\x1b[0m` : s;
|
|
9
|
+
const dim = (s) => wrap(2, s);
|
|
10
|
+
const bold = (s) => wrap(1, s);
|
|
11
|
+
const red = (s) => wrap(31, s);
|
|
12
|
+
const green = (s) => wrap(32, s);
|
|
13
|
+
const yellow = (s) => wrap(33, s);
|
|
14
|
+
const isRec = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15
|
+
function fmtDate(value) {
|
|
16
|
+
if (typeof value === "number")
|
|
17
|
+
return new Date(value).toISOString().slice(0, 10);
|
|
18
|
+
return String(value);
|
|
19
|
+
}
|
|
20
|
+
function label(item) {
|
|
21
|
+
return String(item.title ?? item.name ?? item.id ?? "(item)");
|
|
22
|
+
}
|
|
23
|
+
function meta(item) {
|
|
24
|
+
const bits = [];
|
|
25
|
+
if (typeof item.status === "string")
|
|
26
|
+
bits.push(item.status);
|
|
27
|
+
if (isRec(item.when) && typeof item.when.type === "string")
|
|
28
|
+
bits.push(item.when.type);
|
|
29
|
+
else if (typeof item.when === "string")
|
|
30
|
+
bits.push(item.when);
|
|
31
|
+
if (typeof item.priority === "string" && item.priority !== "none")
|
|
32
|
+
bits.push(`!${item.priority}`);
|
|
33
|
+
if (item.deadline)
|
|
34
|
+
bits.push(`due ${fmtDate(item.deadline)}`);
|
|
35
|
+
if (Array.isArray(item.scopes))
|
|
36
|
+
bits.push(item.scopes.join(","));
|
|
37
|
+
if (item.archived === true)
|
|
38
|
+
bits.push("archived");
|
|
39
|
+
if (typeof item.prefix === "string")
|
|
40
|
+
bits.push(`${item.prefix}…`);
|
|
41
|
+
if (typeof item.color === "string")
|
|
42
|
+
bits.push(item.color);
|
|
43
|
+
return bits.join(" ");
|
|
44
|
+
}
|
|
45
|
+
function renderList(items) {
|
|
46
|
+
if (items.length === 0) {
|
|
47
|
+
console.log(dim("(none)"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
for (const item of items) {
|
|
51
|
+
if (!isRec(item)) {
|
|
52
|
+
console.log(String(item));
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const parts = [];
|
|
56
|
+
if (item.id)
|
|
57
|
+
parts.push(dim(String(item.id)));
|
|
58
|
+
parts.push(bold(label(item)));
|
|
59
|
+
const m = meta(item);
|
|
60
|
+
if (m)
|
|
61
|
+
parts.push(dim(m));
|
|
62
|
+
console.log(parts.join(" "));
|
|
63
|
+
}
|
|
64
|
+
console.log(dim(`${items.length} item${items.length === 1 ? "" : "s"}`));
|
|
65
|
+
}
|
|
66
|
+
function renderItem(obj) {
|
|
67
|
+
const keys = Object.keys(obj);
|
|
68
|
+
const width = keys.reduce((w, k) => Math.max(w, k.length), 0);
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
const value = obj[key];
|
|
71
|
+
const shown = value === null || value === undefined
|
|
72
|
+
? dim("—")
|
|
73
|
+
: isRec(value) || Array.isArray(value)
|
|
74
|
+
? JSON.stringify(value)
|
|
75
|
+
: String(value);
|
|
76
|
+
console.log(`${dim(`${key}:`.padEnd(width + 1))} ${shown}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function renderIssuedKey(obj) {
|
|
80
|
+
console.log(green("API key issued."));
|
|
81
|
+
if (obj.id)
|
|
82
|
+
console.log(`${dim("id: ")} ${obj.id}`);
|
|
83
|
+
console.log(`${dim("secret:")} ${bold(String(obj.secret))}`);
|
|
84
|
+
console.log(yellow("Copy the secret now — it is never shown again."));
|
|
85
|
+
}
|
|
86
|
+
function renderTriage(obj) {
|
|
87
|
+
const created = Array.isArray(obj.created) ? obj.created : [];
|
|
88
|
+
console.log(green(`Triaged into ${created.length} task(s).`));
|
|
89
|
+
if (created.length)
|
|
90
|
+
renderList(created);
|
|
91
|
+
if (Array.isArray(obj.clarifications) && obj.clarifications.length) {
|
|
92
|
+
console.log(yellow("Clarifications needed (resolve, then re-run):"));
|
|
93
|
+
for (const c of obj.clarifications) {
|
|
94
|
+
const q = isRec(c) ? (c.question ?? c.prompt ?? JSON.stringify(c)) : String(c);
|
|
95
|
+
console.log(` - ${q}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(obj.errors) && obj.errors.length) {
|
|
99
|
+
console.log(red("Errors:"));
|
|
100
|
+
for (const e of obj.errors)
|
|
101
|
+
console.log(` - ${JSON.stringify(e)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function render(data) {
|
|
105
|
+
if (data === null || data === undefined) {
|
|
106
|
+
console.log(dim("(done)"));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!isRec(data)) {
|
|
110
|
+
if (Array.isArray(data))
|
|
111
|
+
renderList(data);
|
|
112
|
+
else
|
|
113
|
+
console.log(String(data));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Cursor-paginated list envelope.
|
|
117
|
+
if (Array.isArray(data.items)) {
|
|
118
|
+
renderList(data.items);
|
|
119
|
+
if (data.hasMore)
|
|
120
|
+
console.log(dim(`… more (cursor: ${data.nextCursor})`));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Hybrid search result.
|
|
124
|
+
if (Array.isArray(data.results)) {
|
|
125
|
+
renderList(data.results);
|
|
126
|
+
if (data.mode)
|
|
127
|
+
console.log(dim(`mode: ${data.mode}`));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Review queue.
|
|
131
|
+
if (Array.isArray(data.tasks) || Array.isArray(data.projects)) {
|
|
132
|
+
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
|
|
133
|
+
const projects = Array.isArray(data.projects) ? data.projects : [];
|
|
134
|
+
if (tasks.length) {
|
|
135
|
+
console.log(bold("Tasks"));
|
|
136
|
+
renderList(tasks);
|
|
137
|
+
}
|
|
138
|
+
if (projects.length) {
|
|
139
|
+
console.log(bold("Projects"));
|
|
140
|
+
renderList(projects);
|
|
141
|
+
}
|
|
142
|
+
if (!tasks.length && !projects.length)
|
|
143
|
+
console.log(dim("Nothing due."));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (typeof data.secret === "string") {
|
|
147
|
+
renderIssuedKey(data);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(data.created) || Array.isArray(data.clarifications)) {
|
|
151
|
+
renderTriage(data);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
renderItem(data);
|
|
155
|
+
}
|
|
156
|
+
export function printResult(data, opts) {
|
|
157
|
+
if (opts.json) {
|
|
158
|
+
console.log(JSON.stringify(data ?? null, null, 2));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
render(data);
|
|
162
|
+
}
|
|
163
|
+
export function printError(err) {
|
|
164
|
+
if (err instanceof ApiError) {
|
|
165
|
+
console.error(red(`Error ${err.status}: ${err.message}`));
|
|
166
|
+
}
|
|
167
|
+
else if (err instanceof Error) {
|
|
168
|
+
console.error(red(err.message));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.error(red(String(err)));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Open Brain CLI. A thin, full-coverage terminal client over the /v1 HTTP API:
|
|
3
|
+
// every command maps to one endpoint, so the API + guard layer stay the single
|
|
4
|
+
// source of truth. Auth/headers live in client.ts; rendering in format.ts.
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { OpenBrainClient } from "./client.js";
|
|
9
|
+
import { configPath, loadConfig, readStoredConfig, saveConfig } from "./config.js";
|
|
10
|
+
import { printError, printResult } from "./format.js";
|
|
11
|
+
import { defaultSkillsDir, installSkills, listBundledSkills, uninstallSkills, } from "./skills.js";
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("ob")
|
|
15
|
+
.description("Open Brain CLI — manage tasks, projects, gardens over the /v1 API")
|
|
16
|
+
.version("0.1.0")
|
|
17
|
+
.option("--json", "output the raw JSON response (for piping into jq/scripts)");
|
|
18
|
+
const num = (v) => (v === undefined ? undefined : Number(v));
|
|
19
|
+
const csv = (v) => Array.isArray(v) && v.length ? v.join(",") : undefined;
|
|
20
|
+
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
21
|
+
const whenOf = (v) => typeof v === "string" ? { type: v } : undefined;
|
|
22
|
+
async function run(fn) {
|
|
23
|
+
try {
|
|
24
|
+
const data = await fn(new OpenBrainClient(loadConfig()));
|
|
25
|
+
printResult(data, { json: Boolean(program.opts().json) });
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
printError(err);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function readStdin() {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
let data = "";
|
|
35
|
+
process.stdin.setEncoding("utf8");
|
|
36
|
+
process.stdin.on("data", (chunk) => {
|
|
37
|
+
data += chunk;
|
|
38
|
+
});
|
|
39
|
+
process.stdin.on("end", () => resolve(data));
|
|
40
|
+
process.stdin.on("error", reject);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function readJsonInput(file) {
|
|
44
|
+
const text = file ? readFileSync(file, "utf8") : await readStdin();
|
|
45
|
+
return JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
// --- config ---------------------------------------------------------------
|
|
48
|
+
const config = program.command("config").description("Manage CLI credentials");
|
|
49
|
+
config
|
|
50
|
+
.command("set")
|
|
51
|
+
.description("Save the deployment URL / API key to ~/.open-brain/config.json")
|
|
52
|
+
.option("--url <url>", "deployment origin, e.g. https://<deployment>.convex.site")
|
|
53
|
+
.option("--key <key>", "API key (obr_...)")
|
|
54
|
+
.option("--client <type>", "client type stamped on requests (default cli)")
|
|
55
|
+
.action((o) => {
|
|
56
|
+
saveConfig({ url: str(o.url), apiKey: str(o.key), clientType: str(o.client) });
|
|
57
|
+
console.log(`Saved to ${configPath()}`);
|
|
58
|
+
});
|
|
59
|
+
config
|
|
60
|
+
.command("show")
|
|
61
|
+
.description("Show the stored config (the key is masked)")
|
|
62
|
+
.action(() => {
|
|
63
|
+
const c = readStoredConfig();
|
|
64
|
+
const masked = c.apiKey ? `${c.apiKey.slice(0, 8)}…${c.apiKey.slice(-4)}` : undefined;
|
|
65
|
+
printResult({ path: configPath(), url: c.url, apiKey: masked, clientType: c.clientType }, { json: Boolean(program.opts().json) });
|
|
66
|
+
});
|
|
67
|
+
config
|
|
68
|
+
.command("path")
|
|
69
|
+
.description("Print the config file path")
|
|
70
|
+
.action(() => console.log(configPath()));
|
|
71
|
+
// --- skills ----------------------------------------------------------------
|
|
72
|
+
// Local-only: install the agent skills bundled with this CLI into the user's
|
|
73
|
+
// Claude Code skills directory so an assistant can drive `ob` on their machine.
|
|
74
|
+
// No API call — these commands only touch the local filesystem.
|
|
75
|
+
const skills = program
|
|
76
|
+
.command("skills")
|
|
77
|
+
.description("Install the agent skills bundled with this CLI (~/.claude/skills)");
|
|
78
|
+
const wantsJson = () => Boolean(program.opts().json);
|
|
79
|
+
function unknownSkills(names) {
|
|
80
|
+
if (!names.length)
|
|
81
|
+
return [];
|
|
82
|
+
const have = new Set(listBundledSkills().map((s) => s.name));
|
|
83
|
+
return names.filter((n) => !have.has(n));
|
|
84
|
+
}
|
|
85
|
+
skills
|
|
86
|
+
.command("list")
|
|
87
|
+
.description("List the bundled skills and whether each is installed")
|
|
88
|
+
.option("--dir <path>", "skills directory to check (default ~/.claude/skills)")
|
|
89
|
+
.action((o) => {
|
|
90
|
+
const dir = str(o.dir) ?? defaultSkillsDir();
|
|
91
|
+
const items = listBundledSkills().map((s) => ({
|
|
92
|
+
name: s.name,
|
|
93
|
+
installed: existsSync(join(dir, s.name)),
|
|
94
|
+
description: s.description,
|
|
95
|
+
}));
|
|
96
|
+
if (wantsJson())
|
|
97
|
+
return printResult({ dir, skills: items }, { json: true });
|
|
98
|
+
if (!items.length)
|
|
99
|
+
return console.log("No skills are bundled with this CLI build.");
|
|
100
|
+
console.log(`Skills directory: ${dir}`);
|
|
101
|
+
for (const s of items) {
|
|
102
|
+
console.log(`${s.installed ? "✓" : "·"} ${s.name} — ${s.description}`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
skills
|
|
106
|
+
.command("install [names...]")
|
|
107
|
+
.description("Copy bundled skills into ~/.claude/skills (name one or more for a subset)")
|
|
108
|
+
.option("--dir <path>", "target skills directory (default ~/.claude/skills)")
|
|
109
|
+
.option("--force", "overwrite skills that are already installed")
|
|
110
|
+
.action((names, o) => {
|
|
111
|
+
const unknown = unknownSkills(names);
|
|
112
|
+
if (unknown.length) {
|
|
113
|
+
printError(new Error(`Unknown skill(s): ${unknown.join(", ")}. Try 'ob skills list'.`));
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const results = installSkills({
|
|
118
|
+
dir: str(o.dir),
|
|
119
|
+
force: Boolean(o.force),
|
|
120
|
+
only: names,
|
|
121
|
+
});
|
|
122
|
+
if (wantsJson())
|
|
123
|
+
return printResult({ skills: results }, { json: true });
|
|
124
|
+
if (!results.length)
|
|
125
|
+
return console.log("No skills are bundled with this CLI build.");
|
|
126
|
+
for (const r of results) {
|
|
127
|
+
if (r.status === "skipped") {
|
|
128
|
+
console.log(`Skipped ${r.name} (already at ${r.dest}; use --force to overwrite)`);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const verb = r.status === "overwritten" ? "Updated" : "Installed";
|
|
132
|
+
console.log(`${verb} ${r.name} → ${r.dest}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log("Restart Claude Code (or your agent) to pick up the changes.");
|
|
136
|
+
});
|
|
137
|
+
skills
|
|
138
|
+
.command("uninstall [names...]")
|
|
139
|
+
.description("Remove installed Open Brain skills from ~/.claude/skills")
|
|
140
|
+
.option("--dir <path>", "target skills directory (default ~/.claude/skills)")
|
|
141
|
+
.action((names, o) => {
|
|
142
|
+
const unknown = unknownSkills(names);
|
|
143
|
+
if (unknown.length) {
|
|
144
|
+
printError(new Error(`Unknown skill(s): ${unknown.join(", ")}. Try 'ob skills list'.`));
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const results = uninstallSkills({ dir: str(o.dir), only: names });
|
|
149
|
+
if (wantsJson())
|
|
150
|
+
return printResult({ skills: results }, { json: true });
|
|
151
|
+
for (const r of results) {
|
|
152
|
+
console.log(r.removed
|
|
153
|
+
? `Removed ${r.name} from ${r.dest}`
|
|
154
|
+
: `${r.name} was not installed (${r.dest})`);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
// --- tasks -----------------------------------------------------------------
|
|
158
|
+
const task = program.command("task").description("Create, list and update tasks");
|
|
159
|
+
function taskBody(o) {
|
|
160
|
+
return {
|
|
161
|
+
title: str(o.title),
|
|
162
|
+
gardenId: str(o.garden),
|
|
163
|
+
projectId: str(o.project),
|
|
164
|
+
notes: str(o.notes),
|
|
165
|
+
when: whenOf(o.when),
|
|
166
|
+
deferDate: str(o.defer),
|
|
167
|
+
deadline: str(o.deadline),
|
|
168
|
+
priority: str(o.priority),
|
|
169
|
+
estimateMinutes: num(o.estimate),
|
|
170
|
+
tagIds: Array.isArray(o.tag) ? o.tag : undefined,
|
|
171
|
+
status: str(o.status),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const withTaskWriteOpts = (cmd) => cmd
|
|
175
|
+
.option("--garden <id>", "garden id")
|
|
176
|
+
.option("--project <id>", "project id")
|
|
177
|
+
.option("--notes <markdown>", "notes (markdown)")
|
|
178
|
+
.option("--when <bucket>", "today | anytime | someday")
|
|
179
|
+
.option("--defer <date>", "defer date (ISO/epoch/natural language)")
|
|
180
|
+
.option("--deadline <date>", "deadline (ISO/epoch/natural language)")
|
|
181
|
+
.option("--priority <p>", "none | low | medium | high")
|
|
182
|
+
.option("--estimate <minutes>", "estimate in minutes")
|
|
183
|
+
.option("--tag <id...>", "tag ids");
|
|
184
|
+
withTaskWriteOpts(task.command("create <title>").description("Create a task (must belong to a garden)")).action((title, o) => run((c) => c.post("/v1/tasks", { ...taskBody({ ...o, title }) })));
|
|
185
|
+
task
|
|
186
|
+
.command("list")
|
|
187
|
+
.description("List/filter tasks (or multi-get with --ids)")
|
|
188
|
+
.option("--ids <id...>", "fetch specific task ids")
|
|
189
|
+
.option("--garden <id>")
|
|
190
|
+
.option("--project <id>")
|
|
191
|
+
.option("--status <s...>", "backlog|todo|in_progress|done|canceled")
|
|
192
|
+
.option("--priority <p...>", "none|low|medium|high")
|
|
193
|
+
.option("--when <w...>", "today|anytime|someday")
|
|
194
|
+
.option("--tag <id...>", "tag ids")
|
|
195
|
+
.option("--q <text>", "substring text filter")
|
|
196
|
+
.option("--deferred <mode>", "respect | all")
|
|
197
|
+
.option("--include-canceled")
|
|
198
|
+
.option("--sort <spec>", "field:dir,field:dir")
|
|
199
|
+
.option("--limit <n>")
|
|
200
|
+
.option("--cursor <cursor>")
|
|
201
|
+
.action((o) => run((c) => c.get("/v1/tasks", {
|
|
202
|
+
ids: csv(o.ids),
|
|
203
|
+
gardenId: str(o.garden),
|
|
204
|
+
projectId: str(o.project),
|
|
205
|
+
status: csv(o.status),
|
|
206
|
+
priority: csv(o.priority),
|
|
207
|
+
when: csv(o.when),
|
|
208
|
+
tag: csv(o.tag),
|
|
209
|
+
q: str(o.q),
|
|
210
|
+
deferred: str(o.deferred),
|
|
211
|
+
includeCanceled: o.includeCanceled ? true : undefined,
|
|
212
|
+
sort: str(o.sort),
|
|
213
|
+
limit: num(o.limit),
|
|
214
|
+
cursor: str(o.cursor),
|
|
215
|
+
})));
|
|
216
|
+
task
|
|
217
|
+
.command("get <id>")
|
|
218
|
+
.description("Fetch one task (full detail)")
|
|
219
|
+
.action((id) => run((c) => c.get(`/v1/tasks/${id}`)));
|
|
220
|
+
withTaskWriteOpts(task.command("update <id>").description("Patch task fields and/or drive status"))
|
|
221
|
+
.option("--title <title>", "new title")
|
|
222
|
+
.option("--status <status>", "backlog|todo|in_progress|done|canceled")
|
|
223
|
+
.action((id, o) => {
|
|
224
|
+
const body = taskBody(o);
|
|
225
|
+
delete body.tagIds;
|
|
226
|
+
return run((c) => c.patch(`/v1/tasks/${id}`, body));
|
|
227
|
+
});
|
|
228
|
+
task
|
|
229
|
+
.command("rm <id>")
|
|
230
|
+
.description("Soft-delete (cancel) a task")
|
|
231
|
+
.action((id) => run((c) => c.del(`/v1/tasks/${id}`)));
|
|
232
|
+
task
|
|
233
|
+
.command("done <id>")
|
|
234
|
+
.description("Mark a task done (shortcut for update --status done)")
|
|
235
|
+
.action((id) => run((c) => c.patch(`/v1/tasks/${id}`, { status: "done" })));
|
|
236
|
+
task
|
|
237
|
+
.command("tag-add <id>")
|
|
238
|
+
.description("Attach tags to a task")
|
|
239
|
+
.requiredOption("--tag <id...>", "tag ids")
|
|
240
|
+
.action((id, o) => run((c) => c.post(`/v1/tasks/${id}/tags`, { tagIds: o.tag })));
|
|
241
|
+
task
|
|
242
|
+
.command("tag-remove <id>")
|
|
243
|
+
.description("Detach tags from a task")
|
|
244
|
+
.requiredOption("--tag <id...>", "tag ids")
|
|
245
|
+
.action((id, o) => run((c) => c.del(`/v1/tasks/${id}/tags`, { tagIds: o.tag })));
|
|
246
|
+
task
|
|
247
|
+
.command("create-batch")
|
|
248
|
+
.description("Create many tasks from a JSON array (file or stdin)")
|
|
249
|
+
.option("--file <path>", "JSON file; omit to read stdin")
|
|
250
|
+
.option("--partial", "best-effort per item instead of all-or-nothing")
|
|
251
|
+
.action((o) => run(async (c) => c.post("/v1/tasks/batch", {
|
|
252
|
+
items: await readJsonInput(str(o.file)),
|
|
253
|
+
partial: o.partial ? true : undefined,
|
|
254
|
+
})));
|
|
255
|
+
task
|
|
256
|
+
.command("update-batch")
|
|
257
|
+
.description("Patch many tasks from a JSON array of { id, ...fields }")
|
|
258
|
+
.option("--file <path>", "JSON file; omit to read stdin")
|
|
259
|
+
.option("--partial", "best-effort per item")
|
|
260
|
+
.action((o) => run(async (c) => c.patch("/v1/tasks/batch", {
|
|
261
|
+
items: await readJsonInput(str(o.file)),
|
|
262
|
+
partial: o.partial ? true : undefined,
|
|
263
|
+
})));
|
|
264
|
+
// --- projects --------------------------------------------------------------
|
|
265
|
+
const project = program.command("project").description("Manage projects");
|
|
266
|
+
function projectBody(o) {
|
|
267
|
+
return {
|
|
268
|
+
name: str(o.name),
|
|
269
|
+
gardenId: str(o.garden),
|
|
270
|
+
notes: str(o.notes),
|
|
271
|
+
when: whenOf(o.when),
|
|
272
|
+
deferDate: str(o.defer),
|
|
273
|
+
deadline: str(o.deadline),
|
|
274
|
+
estimateMinutes: num(o.estimate),
|
|
275
|
+
status: str(o.status),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const withProjectWriteOpts = (cmd) => cmd
|
|
279
|
+
.option("--garden <id>", "garden id")
|
|
280
|
+
.option("--notes <markdown>")
|
|
281
|
+
.option("--when <bucket>", "today | anytime | someday")
|
|
282
|
+
.option("--defer <date>")
|
|
283
|
+
.option("--deadline <date>")
|
|
284
|
+
.option("--estimate <minutes>");
|
|
285
|
+
withProjectWriteOpts(project.command("create <name>").description("Create a project in a garden")).action((name, o) => run((c) => c.post("/v1/projects", projectBody({ ...o, name }))));
|
|
286
|
+
project
|
|
287
|
+
.command("list")
|
|
288
|
+
.description("List/filter projects (or multi-get with --ids)")
|
|
289
|
+
.option("--ids <id...>")
|
|
290
|
+
.option("--garden <id>")
|
|
291
|
+
.option("--status <s...>")
|
|
292
|
+
.option("--deferred <mode>", "respect | all")
|
|
293
|
+
.option("--include-canceled")
|
|
294
|
+
.option("--sort <spec>")
|
|
295
|
+
.option("--limit <n>")
|
|
296
|
+
.option("--cursor <cursor>")
|
|
297
|
+
.action((o) => run((c) => c.get("/v1/projects", {
|
|
298
|
+
ids: csv(o.ids),
|
|
299
|
+
gardenId: str(o.garden),
|
|
300
|
+
status: csv(o.status),
|
|
301
|
+
deferred: str(o.deferred),
|
|
302
|
+
includeCanceled: o.includeCanceled ? true : undefined,
|
|
303
|
+
sort: str(o.sort),
|
|
304
|
+
limit: num(o.limit),
|
|
305
|
+
cursor: str(o.cursor),
|
|
306
|
+
})));
|
|
307
|
+
project
|
|
308
|
+
.command("get <id>")
|
|
309
|
+
.description("Fetch one project")
|
|
310
|
+
.action((id) => run((c) => c.get(`/v1/projects/${id}`)));
|
|
311
|
+
withProjectWriteOpts(project.command("update <id>").description("Patch project fields and/or status"))
|
|
312
|
+
.option("--name <name>")
|
|
313
|
+
.option("--status <status>")
|
|
314
|
+
.action((id, o) => run((c) => c.patch(`/v1/projects/${id}`, projectBody(o))));
|
|
315
|
+
project
|
|
316
|
+
.command("rm <id>")
|
|
317
|
+
.description("Soft-delete (drop) a project")
|
|
318
|
+
.action((id) => run((c) => c.del(`/v1/projects/${id}`)));
|
|
319
|
+
// --- gardens ---------------------------------------------------------------
|
|
320
|
+
const garden = program.command("garden").description("Manage life-domain gardens");
|
|
321
|
+
function gardenBody(o) {
|
|
322
|
+
return {
|
|
323
|
+
name: str(o.name),
|
|
324
|
+
notes: str(o.notes),
|
|
325
|
+
context: str(o.context),
|
|
326
|
+
color: str(o.color),
|
|
327
|
+
icon: str(o.icon),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const withGardenWriteOpts = (cmd) => cmd
|
|
331
|
+
.option("--notes <text>")
|
|
332
|
+
.option("--context <markdown>", "context AI triage uses to assign tasks here")
|
|
333
|
+
.option("--color <color>")
|
|
334
|
+
.option("--icon <icon>");
|
|
335
|
+
withGardenWriteOpts(garden.command("create <name>").description("Create a garden")).action((name, o) => run((c) => c.post("/v1/gardens", gardenBody({ ...o, name }))));
|
|
336
|
+
garden
|
|
337
|
+
.command("list")
|
|
338
|
+
.description("List gardens (or multi-get with --ids)")
|
|
339
|
+
.option("--ids <id...>")
|
|
340
|
+
.option("--archived", "include archived gardens")
|
|
341
|
+
.option("--limit <n>")
|
|
342
|
+
.action((o) => run((c) => c.get("/v1/gardens", {
|
|
343
|
+
ids: csv(o.ids),
|
|
344
|
+
includeArchived: o.archived ? true : undefined,
|
|
345
|
+
limit: num(o.limit),
|
|
346
|
+
})));
|
|
347
|
+
garden
|
|
348
|
+
.command("get <id>")
|
|
349
|
+
.description("Fetch one garden (incl. markdown context)")
|
|
350
|
+
.action((id) => run((c) => c.get(`/v1/gardens/${id}`)));
|
|
351
|
+
withGardenWriteOpts(garden.command("update <id>").description("Patch garden fields"))
|
|
352
|
+
.option("--name <name>")
|
|
353
|
+
.option("--archived <bool>", "true | false")
|
|
354
|
+
.action((id, o) => run((c) => c.patch(`/v1/gardens/${id}`, {
|
|
355
|
+
...gardenBody(o),
|
|
356
|
+
archived: o.archived === undefined ? undefined : String(o.archived) === "true",
|
|
357
|
+
})));
|
|
358
|
+
garden
|
|
359
|
+
.command("rm <id>")
|
|
360
|
+
.description("Soft-delete (archive) a garden")
|
|
361
|
+
.action((id) => run((c) => c.del(`/v1/gardens/${id}`)));
|
|
362
|
+
// --- tags ------------------------------------------------------------------
|
|
363
|
+
const tag = program.command("tag").description("Manage vault-scoped tags");
|
|
364
|
+
tag
|
|
365
|
+
.command("create <name>")
|
|
366
|
+
.description("Create a tag")
|
|
367
|
+
.option("--color <color>")
|
|
368
|
+
.action((name, o) => run((c) => c.post("/v1/tags", { name, color: str(o.color) })));
|
|
369
|
+
tag
|
|
370
|
+
.command("list")
|
|
371
|
+
.description("List tags (or multi-get with --ids)")
|
|
372
|
+
.option("--ids <id...>")
|
|
373
|
+
.option("--limit <n>")
|
|
374
|
+
.action((o) => run((c) => c.get("/v1/tags", { ids: csv(o.ids), limit: num(o.limit) })));
|
|
375
|
+
tag
|
|
376
|
+
.command("get <id>")
|
|
377
|
+
.description("Fetch one tag")
|
|
378
|
+
.action((id) => run((c) => c.get(`/v1/tags/${id}`)));
|
|
379
|
+
tag
|
|
380
|
+
.command("update <id>")
|
|
381
|
+
.description("Rename or recolor a tag")
|
|
382
|
+
.option("--name <name>")
|
|
383
|
+
.option("--color <color>")
|
|
384
|
+
.action((id, o) => run((c) => c.patch(`/v1/tags/${id}`, { name: str(o.name), color: str(o.color) })));
|
|
385
|
+
tag
|
|
386
|
+
.command("rm <id>")
|
|
387
|
+
.description("Delete a tag (detaches it from every task)")
|
|
388
|
+
.action((id) => run((c) => c.del(`/v1/tags/${id}`)));
|
|
389
|
+
// --- saved lists -----------------------------------------------------------
|
|
390
|
+
const listFilters = (o) => ({
|
|
391
|
+
q: str(o.q),
|
|
392
|
+
status: csv(o.status),
|
|
393
|
+
priority: csv(o.priority),
|
|
394
|
+
when: csv(o.when),
|
|
395
|
+
gardenId: str(o.garden),
|
|
396
|
+
tagIds: csv(o.tag),
|
|
397
|
+
includeCanceled: o.includeCanceled ? true : undefined,
|
|
398
|
+
sort: str(o.sort),
|
|
399
|
+
});
|
|
400
|
+
const withListFilters = (cmd) => cmd
|
|
401
|
+
.option("--q <text>")
|
|
402
|
+
.option("--status <s...>", "backlog|todo|in_progress|done|canceled")
|
|
403
|
+
.option("--priority <p...>", "none|low|medium|high")
|
|
404
|
+
.option("--when <w...>", "today|anytime|someday")
|
|
405
|
+
.option("--garden <id>")
|
|
406
|
+
.option("--tag <id...>")
|
|
407
|
+
.option("--include-canceled")
|
|
408
|
+
.option("--sort <spec>", "field:dir,field:dir");
|
|
409
|
+
const lists = program.command("lists").description("Saved smart lists");
|
|
410
|
+
withListFilters(lists.command("create <name>").description("Save a named set of task filters")).action((name, o) => run((c) => c.post("/v1/lists", { name, filters: listFilters(o) })));
|
|
411
|
+
lists
|
|
412
|
+
.command("ls")
|
|
413
|
+
.description("List saved lists (or multi-get with --ids)")
|
|
414
|
+
.option("--ids <id...>")
|
|
415
|
+
.action((o) => run((c) => c.get("/v1/lists", { ids: csv(o.ids) })));
|
|
416
|
+
lists
|
|
417
|
+
.command("get <id>")
|
|
418
|
+
.description("Fetch one saved list")
|
|
419
|
+
.action((id) => run((c) => c.get(`/v1/lists/${id}`)));
|
|
420
|
+
withListFilters(lists
|
|
421
|
+
.command("update <id>")
|
|
422
|
+
.description("Rename a saved list and/or replace its filters")
|
|
423
|
+
.option("--name <name>")).action((id, o) => {
|
|
424
|
+
const f = listFilters(o);
|
|
425
|
+
const hasFilter = Object.values(f).some((v) => v !== undefined);
|
|
426
|
+
return run((c) => c.patch(`/v1/lists/${id}`, {
|
|
427
|
+
name: str(o.name),
|
|
428
|
+
filters: hasFilter ? f : undefined,
|
|
429
|
+
}));
|
|
430
|
+
});
|
|
431
|
+
lists
|
|
432
|
+
.command("rm <id>")
|
|
433
|
+
.description("Delete a saved list")
|
|
434
|
+
.action((id) => run((c) => c.del(`/v1/lists/${id}`)));
|
|
435
|
+
// --- views -----------------------------------------------------------------
|
|
436
|
+
program
|
|
437
|
+
.command("view <name>")
|
|
438
|
+
.description("A computed view: today | upcoming | anytime | someday | overdue")
|
|
439
|
+
.option("--tz <iana>", "IANA timezone (defaults to the system timezone)")
|
|
440
|
+
.option("--limit <n>")
|
|
441
|
+
.option("--cursor <cursor>")
|
|
442
|
+
.action((name, o) => run((c) => c.get(`/v1/views/${name}`, {
|
|
443
|
+
tz: str(o.tz) ?? systemTz(),
|
|
444
|
+
limit: num(o.limit),
|
|
445
|
+
cursor: str(o.cursor),
|
|
446
|
+
})));
|
|
447
|
+
program
|
|
448
|
+
.command("today")
|
|
449
|
+
.description("Shortcut for `view today`")
|
|
450
|
+
.option("--tz <iana>")
|
|
451
|
+
.option("--limit <n>")
|
|
452
|
+
.action((o) => run((c) => c.get("/v1/views/today", { tz: str(o.tz) ?? systemTz(), limit: num(o.limit) })));
|
|
453
|
+
function systemTz() {
|
|
454
|
+
try {
|
|
455
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// --- reviews ---------------------------------------------------------------
|
|
462
|
+
const review = program.command("review").description("The review system");
|
|
463
|
+
review
|
|
464
|
+
.command("queue")
|
|
465
|
+
.description("Items due for review now")
|
|
466
|
+
.option("--garden <id>", "restrict to one garden")
|
|
467
|
+
.option("--limit <n>")
|
|
468
|
+
.action((o) => run((c) => c.get("/v1/reviews", { gardenId: str(o.garden), limit: num(o.limit) })));
|
|
469
|
+
review
|
|
470
|
+
.command("garden <id>")
|
|
471
|
+
.description("Deep-review: every active item under a garden")
|
|
472
|
+
.action((id) => run((c) => c.get(`/v1/gardens/${id}/review`)));
|
|
473
|
+
review
|
|
474
|
+
.command("request <gardenId>")
|
|
475
|
+
.description("Force a garden's active items into the due-review queue")
|
|
476
|
+
.action((id) => run((c) => c.post(`/v1/gardens/${id}/request-review`)));
|
|
477
|
+
review
|
|
478
|
+
.command("task <id>")
|
|
479
|
+
.description("Mark a task reviewed (restarts its review clock)")
|
|
480
|
+
.action((id) => run((c) => c.post(`/v1/tasks/${id}/reviewed`)));
|
|
481
|
+
review
|
|
482
|
+
.command("project <id>")
|
|
483
|
+
.description("Mark a project reviewed")
|
|
484
|
+
.action((id) => run((c) => c.post(`/v1/projects/${id}/reviewed`)));
|
|
485
|
+
// --- AI --------------------------------------------------------------------
|
|
486
|
+
program
|
|
487
|
+
.command("find <query...>")
|
|
488
|
+
.description("Hybrid keyword + semantic search over active tasks")
|
|
489
|
+
.option("--limit <n>")
|
|
490
|
+
.option("--deadline-before <date>", "Only tasks due on or before this (epoch ms or date string)")
|
|
491
|
+
.option("--deadline-after <date>", "Only tasks due on or after this (epoch ms or date string)")
|
|
492
|
+
.action((query, o) => run((c) => c.post("/v1/ai/search", {
|
|
493
|
+
query: query.join(" "),
|
|
494
|
+
limit: num(o.limit),
|
|
495
|
+
deadlineBefore: str(o.deadlineBefore),
|
|
496
|
+
deadlineAfter: str(o.deadlineAfter),
|
|
497
|
+
})));
|
|
498
|
+
program
|
|
499
|
+
.command("triage <input...>")
|
|
500
|
+
.description("Triage a brain dump into garden-assigned tasks")
|
|
501
|
+
.action((input) => run((c) => c.post("/v1/ai/triage", { input: input.join(" ") })));
|
|
502
|
+
// --- keys ------------------------------------------------------------------
|
|
503
|
+
const key = program.command("key").description("Manage API keys (keys:manage scope)");
|
|
504
|
+
key
|
|
505
|
+
.command("create <name>")
|
|
506
|
+
.description("Mint an API key (secret shown once)")
|
|
507
|
+
.requiredOption("--scope <scope...>", "scopes, e.g. * or tasks:write reviews:*")
|
|
508
|
+
.option("--client <type>", "cli | mcp | web | integration")
|
|
509
|
+
.action((name, o) => run((c) => c.post("/v1/keys", { name, scopes: o.scope, clientType: str(o.client) })));
|
|
510
|
+
key
|
|
511
|
+
.command("list")
|
|
512
|
+
.description("List the vault's API keys (secrets are never returned)")
|
|
513
|
+
.action(() => run((c) => c.get("/v1/keys")));
|
|
514
|
+
key
|
|
515
|
+
.command("rotate <id>")
|
|
516
|
+
.description("Issue a new secret and revoke the old one")
|
|
517
|
+
.action((id) => run((c) => c.post(`/v1/keys/${id}/rotate`)));
|
|
518
|
+
key
|
|
519
|
+
.command("revoke <id>")
|
|
520
|
+
.description("Soft-revoke an API key")
|
|
521
|
+
.action((id) => run((c) => c.del(`/v1/keys/${id}`)));
|
|
522
|
+
program.parseAsync().catch((err) => {
|
|
523
|
+
printError(err);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
});
|
|
526
|
+
//# 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/dist/skills.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Bundled agent skills shipped with the CLI. `ob skills install` copies them
|
|
2
|
+
// into the user's Claude Code skills directory (~/.claude/skills) so an
|
|
3
|
+
// assistant can drive `ob` on the user's own machine. These are pure local
|
|
4
|
+
// file operations — no API or auth involved. The skill content is copied into
|
|
5
|
+
// this package at build time from the repo's .agents/skills (see
|
|
6
|
+
// scripts/bundle-skills.mjs), which stays the single source of truth.
|
|
7
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmSync, } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
// Resolved relative to the compiled file (dist/skills.js) so it points at the
|
|
12
|
+
// package's bundled `skills/` dir regardless of where the CLI is installed.
|
|
13
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const bundledSkillsDir = join(here, "..", "skills");
|
|
15
|
+
export function defaultSkillsDir() {
|
|
16
|
+
return join(homedir(), ".claude", "skills");
|
|
17
|
+
}
|
|
18
|
+
export function listBundledSkills(srcDir = bundledSkillsDir) {
|
|
19
|
+
if (!existsSync(srcDir))
|
|
20
|
+
return [];
|
|
21
|
+
return readdirSync(srcDir)
|
|
22
|
+
.filter((name) => existsSync(join(srcDir, name, "SKILL.md")))
|
|
23
|
+
.map((name) => ({
|
|
24
|
+
name,
|
|
25
|
+
description: readDescription(join(srcDir, name, "SKILL.md")),
|
|
26
|
+
path: join(srcDir, name),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
// Pull the `description:` out of a skill's YAML frontmatter for display.
|
|
30
|
+
// Handles both inline (`description: text`) and the indented block form the
|
|
31
|
+
// skills use, where the value spills onto the following lines.
|
|
32
|
+
function readDescription(skillFile) {
|
|
33
|
+
const fm = readFileSync(skillFile, "utf8").match(/^---\n([\s\S]*?)\n---/);
|
|
34
|
+
if (!fm)
|
|
35
|
+
return "";
|
|
36
|
+
const lines = fm[1].split("\n");
|
|
37
|
+
const start = lines.findIndex((l) => /^description:/.test(l));
|
|
38
|
+
if (start === -1)
|
|
39
|
+
return "";
|
|
40
|
+
const parts = [lines[start].replace(/^description:\s*/, "")];
|
|
41
|
+
for (let i = start + 1; i < lines.length && !/^\S/.test(lines[i]); i++) {
|
|
42
|
+
parts.push(lines[i].trim());
|
|
43
|
+
}
|
|
44
|
+
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
45
|
+
}
|
|
46
|
+
export function installSkills(opts = {}) {
|
|
47
|
+
const root = opts.dir ?? defaultSkillsDir();
|
|
48
|
+
const wanted = pick(listBundledSkills(opts.srcDir), opts.only);
|
|
49
|
+
if (wanted.length)
|
|
50
|
+
mkdirSync(root, { recursive: true });
|
|
51
|
+
return wanted.map((skill) => {
|
|
52
|
+
const dest = join(root, skill.name);
|
|
53
|
+
const exists = existsSync(dest);
|
|
54
|
+
if (exists && !opts.force)
|
|
55
|
+
return { name: skill.name, status: "skipped", dest };
|
|
56
|
+
if (exists)
|
|
57
|
+
rmSync(dest, { recursive: true, force: true });
|
|
58
|
+
cpSync(skill.path, dest, {
|
|
59
|
+
recursive: true,
|
|
60
|
+
filter: (p) => !lstatSync(p).isSymbolicLink(),
|
|
61
|
+
});
|
|
62
|
+
return { name: skill.name, status: exists ? "overwritten" : "installed", dest };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function uninstallSkills(opts = {}) {
|
|
66
|
+
const root = opts.dir ?? defaultSkillsDir();
|
|
67
|
+
const names = opts.only?.length
|
|
68
|
+
? opts.only
|
|
69
|
+
: listBundledSkills(opts.srcDir).map((s) => s.name);
|
|
70
|
+
return names.map((name) => {
|
|
71
|
+
const dest = join(root, name);
|
|
72
|
+
const removed = existsSync(dest);
|
|
73
|
+
if (removed)
|
|
74
|
+
rmSync(dest, { recursive: true, force: true });
|
|
75
|
+
return { name, removed, dest };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function pick(skills, only) {
|
|
79
|
+
if (!only?.length)
|
|
80
|
+
return skills;
|
|
81
|
+
const set = new Set(only);
|
|
82
|
+
return skills.filter((s) => set.has(s.name));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cpenned/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Open Brain CLI - a terminal client over the /v1 HTTP API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Chris Pennington",
|
|
8
|
+
"homepage": "https://github.com/cpenned/open-brain/tree/main/packages/cli#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/cpenned/open-brain.git",
|
|
12
|
+
"directory": "packages/cli"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/cpenned/open-brain/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"cli",
|
|
19
|
+
"open-brain",
|
|
20
|
+
"tasks",
|
|
21
|
+
"todo",
|
|
22
|
+
"productivity",
|
|
23
|
+
"gtd"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"ob": "./dist/index.js",
|
|
30
|
+
"open-brain": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"skills",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "node scripts/bundle-skills.mjs && tsc -p tsconfig.json",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"start": "node dist/index.js",
|
|
47
|
+
"prepublishOnly": "npm run build"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"commander": "^12.1.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.0.0",
|
|
54
|
+
"typescript": "^5.6.0",
|
|
55
|
+
"vitest": "^4.1.8"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: open-brain-cli
|
|
3
|
+
description:
|
|
4
|
+
Drive the Open Brain `ob` CLI to manage tasks, projects, gardens, tags,
|
|
5
|
+
views, reviews, search, and triage from the terminal. Use when asked to
|
|
6
|
+
capture/list/update to-dos, plan a day, or script against Open Brain over
|
|
7
|
+
the /v1 API. Not for editing the Open Brain codebase itself.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Open Brain CLI (`ob`)
|
|
11
|
+
|
|
12
|
+
A thin terminal client over the Open Brain `/v1` HTTP API. Every command maps to
|
|
13
|
+
one endpoint; the API + guard layer enforce validation, the status machine,
|
|
14
|
+
ownership, and scopes. Build artifact: `packages/cli/dist/index.js` (bin `ob`).
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- Capturing or editing tasks/projects/gardens/tags from the shell.
|
|
19
|
+
- Pulling a view (today/upcoming/…), the review queue, or search results.
|
|
20
|
+
- Scripting Open Brain (pipe `--json` into `jq`).
|
|
21
|
+
|
|
22
|
+
## When not to use
|
|
23
|
+
|
|
24
|
+
- Modifying the Open Brain app/backend code (that is normal repo work).
|
|
25
|
+
- Bulk/agent workflows where the MCP server is already connected — prefer the
|
|
26
|
+
`open-brain-mcp` tools there.
|
|
27
|
+
|
|
28
|
+
## Setup (once)
|
|
29
|
+
|
|
30
|
+
The CLI needs the deployment origin and an `obr_` API key. Mint the key from the
|
|
31
|
+
web app (**Settings → New API key**) so it targets your real vault.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ob config set --url https://<deployment>.convex.site --key obr_...
|
|
35
|
+
ob config show # verify (key masked)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Env vars override the saved file: `OPEN_BRAIN_API_URL`, `OPEN_BRAIN_API_KEY`,
|
|
39
|
+
`OPEN_BRAIN_CLIENT_TYPE`. The URL is the `.convex.site` origin (not `.cloud`).
|
|
40
|
+
|
|
41
|
+
Get `ob` on PATH with a global install (`npm i -g @cpenned/cli`). Working in
|
|
42
|
+
the repo instead? Build the package (`npm run build -w @cpenned/cli`) and call
|
|
43
|
+
`node packages/cli/dist/index.js`, or `npm link -w @cpenned/cli` for the `ob` bin.
|
|
44
|
+
|
|
45
|
+
## Command map
|
|
46
|
+
|
|
47
|
+
| Area | Commands |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| tasks | `task create <title>`, `task list`, `task get <id>`, `task update <id>`, `task rm <id>`, `task done <id>`, `task tag-add/<tag-remove> <id> --tag …`, `task create-batch`/`update-batch` |
|
|
50
|
+
| projects | `project create <name>`, `project list/get/update/rm` |
|
|
51
|
+
| gardens | `garden create <name>`, `garden list/get/update/rm` |
|
|
52
|
+
| tags | `tag create <name>`, `tag list/get/update`, `tag rm <id>` (detaches from every task) |
|
|
53
|
+
| lists | `lists create <name> [--status/--priority/--when/--garden/--tag/--q/--sort]`, `lists ls/get/update/rm` (saved smart lists) |
|
|
54
|
+
| views | `view <today\|upcoming\|anytime\|someday\|overdue>`, `today` |
|
|
55
|
+
| reviews | `review queue`, `review garden <id>`, `review request <id>`, `review task <id>`, `review project <id>` |
|
|
56
|
+
| AI | `find <query…>`, `triage <input…>` |
|
|
57
|
+
| keys | `key create <name> --scope …`, `key list/rotate/revoke` |
|
|
58
|
+
| config | `config set/show/path` |
|
|
59
|
+
|
|
60
|
+
`task create`/`project create` require `--garden <id>`. Run `ob <group> --help`
|
|
61
|
+
for the full option list (e.g. `ob task --help`).
|
|
62
|
+
|
|
63
|
+
## Common workflows
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Plan the day
|
|
67
|
+
ob today
|
|
68
|
+
ob view upcoming
|
|
69
|
+
|
|
70
|
+
# Capture
|
|
71
|
+
ob garden list # get a garden id
|
|
72
|
+
ob task create "Email the landlord" --garden <g> --when today --priority high
|
|
73
|
+
ob triage "call dentist, buy milk, draft Q3 deck" # AI splits into tasks
|
|
74
|
+
|
|
75
|
+
# Progress an item
|
|
76
|
+
ob task list --garden <g> --status todo in_progress
|
|
77
|
+
ob task done <taskId>
|
|
78
|
+
|
|
79
|
+
# Saved smart lists
|
|
80
|
+
ob lists create "Hot" --priority high --status todo in_progress
|
|
81
|
+
ob lists ls # then open one in the web app
|
|
82
|
+
|
|
83
|
+
# Review cadence
|
|
84
|
+
ob review queue
|
|
85
|
+
ob review task <taskId> # mark reviewed → restarts its clock
|
|
86
|
+
|
|
87
|
+
# Find something
|
|
88
|
+
ob find quarterly report
|
|
89
|
+
ob find taxes --deadline-before friday # narrow hits to a due window
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Scripting
|
|
93
|
+
|
|
94
|
+
Add `--json` to ANY command for the raw API payload:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
ob task list --status todo --json | jq -r '.items[].title'
|
|
98
|
+
TODAY=$(ob today --json | jq '.items | length')
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Lists use a `{ items, hasMore, nextCursor }` envelope — page with `--cursor`.
|
|
102
|
+
|
|
103
|
+
## Gotchas
|
|
104
|
+
|
|
105
|
+
- The CLI acts on the vault behind its key. A key from `apiKeys:bootstrap` is a
|
|
106
|
+
separate empty vault; use a key minted in the web Settings page to share data
|
|
107
|
+
with the web UI and email.
|
|
108
|
+
- Dates (`--defer`, `--deadline`) accept ISO, epoch ms, or relative shorthand
|
|
109
|
+
(`today`, `tomorrow`, `+3d`, `+2w`, `+1m`). `--when` is the intent bucket
|
|
110
|
+
(today/anytime/someday), no date.
|
|
111
|
+
- Statuses are `backlog | todo | in_progress | done | canceled`; `--include-canceled`
|
|
112
|
+
surfaces canceled items, which list filters hide by default.
|
|
113
|
+
- Status transitions are enforced server-side; an illegal move returns an error,
|
|
114
|
+
not a silent no-op.
|
|
115
|
+
- Scopes gate commands: `key` needs `keys:manage`; `find`/`triage` need
|
|
116
|
+
`ai:invoke`. A `*` (owner) key unlocks everything.
|