@figs-so/cli 0.1.5 → 0.1.7

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/README.md +4 -0
  2. package/figs.mjs +105 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -25,10 +25,14 @@ The full, always-current guide + `agent.json` schema is served at **`<endpoint>/
25
25
  |---|---|
26
26
  | `figs status [--json]` | login / workspace / agent state |
27
27
  | `figs login` | device-flow browser approve (or `figs login <token>`) |
28
+ | `figs logout` | remove the locally-saved token (`~/.figs/credentials.json`) |
28
29
  | `figs workspaces [--json]` | list your workspaces (read-only; create one in the web app) |
29
30
  | `figs init --workspace <slug-or-id>` | resolve the workspace, generate identity UUID + config + pointer GUIDE.md |
30
31
  | `figs doctor` | validate `.figs/` against the contract |
31
32
  | `figs push` | one-way publish of `.figs/` |
32
33
  | `figs version` | print version + check for updates |
34
+ | `figs help [<command>]` | usage (bare `figs` shows it too) |
35
+
36
+ Global: `-h` / `--help` on any command (or `figs help <command>`), `-v` / `--version` for the version. Unknown commands exit non-zero.
33
37
 
34
38
  Override the endpoint with `FIGS_ENDPOINT` (e.g. `http://localhost:3000` for local dev).
package/figs.mjs CHANGED
@@ -5,15 +5,18 @@
5
5
  * figs status show login / workspace / agent state [--json]
6
6
  * figs login browser approve (device flow) — agent never sees the token
7
7
  * figs login <token> fallback: save a token you pasted (~/.figs/credentials.json)
8
+ * figs logout remove the locally-saved token (~/.figs/credentials.json)
8
9
  * figs workspaces list the user's workspaces [--json]
9
10
  * figs init --workspace <slug-or-id> [--endpoint <url>]
10
11
  * create .figs/config.json + GUIDE.md (generates a stable agent id)
11
12
  * figs doctor validate .figs/ against the contract before pushing
12
13
  * figs push one-way push the .figs/ spine to the ingest endpoint
13
14
  * figs version print the CLI version (and check for updates)
15
+ * figs help [<command>] usage; `-h`/`--help` on any command, `-v` for version
14
16
  *
15
17
  * Designed to be driven by an agent: non-interactive, clear output, `--json`
16
- * on read commands, and errors that say what to do next.
18
+ * on read commands, `-h`/`--help`/`help` everywhere, and errors that say what to
19
+ * do next. Bare `figs` prints help; unknown commands exit non-zero.
17
20
  *
18
21
  * Auth is the *user* (a token, configured once per machine). Identity is the
19
22
  * *agent* — a UUID generated by `init`, stored in the committed, non-secret
@@ -25,13 +28,14 @@ import {
25
28
  existsSync,
26
29
  mkdirSync,
27
30
  readFileSync,
31
+ rmSync,
28
32
  writeFileSync,
29
33
  } from "node:fs"
30
34
  import { homedir } from "node:os"
31
35
  import { join } from "node:path"
32
36
  import { randomUUID } from "node:crypto"
33
37
 
34
- const VERSION = "0.1.5"
38
+ const VERSION = "0.1.7"
35
39
  // Going-forward default; override with FIGS_ENDPOINT or .figs/config.json endpoint
36
40
  // (e.g. FIGS_ENDPOINT=http://localhost:3000 for local dev).
37
41
  const DEFAULT_ENDPOINT = "https://app.figs.so"
@@ -39,8 +43,39 @@ const DEFAULT_ENDPOINT = "https://app.figs.so"
39
43
  const repoDir = join(process.cwd(), ".figs")
40
44
  const globalDir = join(homedir(), ".figs")
41
45
  const globalCreds = join(globalDir, "credentials.json")
42
- const cmd = process.argv[2] ?? "status"
46
+ const cmd = process.argv[2] ?? "help"
43
47
  const JSON_OUT = process.argv.includes("--json")
48
+ const WANTS_HELP = process.argv.slice(2).some((a) => a === "-h" || a === "--help")
49
+
50
+ /** Command registry — single source for dispatch + `figs help`. */
51
+ const COMMANDS = {
52
+ status: { args: "[--json]", desc: "show login / workspace / agent state" },
53
+ login: {
54
+ args: "[<token>]",
55
+ desc: "log in — browser device-flow, or save a pasted token",
56
+ more: [
57
+ "no arg → device flow: a human approves in a browser (you never see the token).",
58
+ "<token> → save a token you already have to ~/.figs/credentials.json.",
59
+ ],
60
+ },
61
+ logout: { args: "", desc: "remove the locally-saved token (~/.figs/credentials.json)" },
62
+ workspaces: { args: "[--json]", desc: "list your workspaces (read-only; shows the slug)" },
63
+ init: {
64
+ args: "--workspace <slug-or-id> [--endpoint <url>]",
65
+ desc: "set up .figs/ here (identity UUID + config + pointer GUIDE.md)",
66
+ more: [
67
+ "--workspace takes a slug (resolved to its UUID) or a raw UUID — get it from `figs workspaces`.",
68
+ ],
69
+ },
70
+ doctor: { args: "", desc: "validate .figs/ against the live contract before pushing" },
71
+ push: {
72
+ args: "",
73
+ desc: "publish .figs/ — spine to /api/ingest, artifacts to /api/artifacts",
74
+ more: ["Idempotent (records fold by id). Exits non-zero if an artifact upload is rejected."],
75
+ },
76
+ version: { args: "", desc: "print the CLI version and check for updates" },
77
+ help: { args: "[<command>]", desc: "show this help, or detailed help for one command" },
78
+ }
44
79
 
45
80
  function die(msg) {
46
81
  console.error(`figs: ${msg}`)
@@ -142,19 +177,51 @@ async function checkVersion({ force = false, hardFail = false } = {}) {
142
177
  }
143
178
  }
144
179
 
145
- if (cmd === "login") await login(process.argv[3])
180
+ /** `figs help [cmd]` top-level usage, or one command's detail. */
181
+ function printHelp(name) {
182
+ const pad = 36
183
+ if (name && name !== "-h" && name !== "--help") {
184
+ const c = COMMANDS[name]
185
+ if (!c) {
186
+ console.log(`figs: no such command "${name}"\n`)
187
+ return printHelp()
188
+ }
189
+ console.log(`Usage: figs ${name}${c.args ? " " + c.args : ""}\n`)
190
+ console.log(` ${c.desc}`)
191
+ if (c.more) for (const line of c.more) console.log(` ${line}`)
192
+ return
193
+ }
194
+ console.log("figs — publish your AI agent's state to Figs (https://figs.so)\n")
195
+ console.log("Usage: figs <command> [options]\n")
196
+ console.log("Commands:")
197
+ for (const [n, c] of Object.entries(COMMANDS)) {
198
+ console.log(` ${`${n} ${c.args}`.trim().padEnd(pad)} ${c.desc}`)
199
+ }
200
+ console.log("\nGlobal flags:")
201
+ console.log(` ${"-h, --help".padEnd(pad)} show help (or \`figs help <command>\`)`)
202
+ console.log(` ${"-v, --version".padEnd(pad)} print the CLI version`)
203
+ console.log("\nEnvironment:")
204
+ console.log(` ${"FIGS_ENDPOINT".padEnd(pad)} override the API endpoint (e.g. http://localhost:3000)`)
205
+ console.log(` ${"FIGS_TOKEN".padEnd(pad)} use this token instead of ~/.figs/credentials.json`)
206
+ console.log(`\nEndpoint: ${resolveEndpoint()}`)
207
+ console.log(`Guide: ${resolveEndpoint()}/llms.txt`)
208
+ }
209
+
210
+ if (cmd === "help" || cmd === "-h" || cmd === "--help") printHelp(process.argv[3])
211
+ else if (cmd === "version" || cmd === "--version" || cmd === "-v" || cmd === "-V") {
212
+ console.log(VERSION)
213
+ await checkVersion({ force: true })
214
+ } else if (WANTS_HELP) printHelp(cmd)
215
+ else if (cmd === "login") await login(process.argv[3])
216
+ else if (cmd === "logout") logout()
146
217
  else if (cmd === "status") await status()
147
218
  else if (cmd === "workspaces") await workspaces()
148
219
  else if (cmd === "init") await init()
149
220
  else if (cmd === "doctor") await doctor()
150
221
  else if (cmd === "push") await push()
151
- else if (cmd === "version" || cmd === "--version") {
152
- console.log(VERSION)
153
- await checkVersion({ force: true })
154
- } else {
155
- die(
156
- `unknown command "${cmd}" — try: status, login, workspaces, init, doctor, push`,
157
- )
222
+ else {
223
+ console.error(`figs: unknown command "${cmd}" — run \`figs help\` for usage`)
224
+ process.exit(1)
158
225
  }
159
226
 
160
227
  function sleep(ms) {
@@ -206,6 +273,33 @@ async function login(token) {
206
273
  die("timed out waiting for approval — run `figs login` again")
207
274
  }
208
275
 
276
+ /**
277
+ * `figs logout` — remove the locally-saved token (`~/.figs/credentials.json`).
278
+ * This only clears *this machine's* copy; the token still exists server-side
279
+ * until you revoke it in Settings. A token supplied via the FIGS_TOKEN env var
280
+ * can't be removed here (unset the env var to fully log out).
281
+ */
282
+ function logout() {
283
+ if (existsSync(globalCreds)) {
284
+ try {
285
+ rmSync(globalCreds)
286
+ } catch (e) {
287
+ die(`could not remove ${globalCreds}: ${e?.message || e}`)
288
+ }
289
+ console.log("figs: ✓ logged out — removed ~/.figs/credentials.json")
290
+ } else {
291
+ console.log("figs: not logged in — no ~/.figs/credentials.json to remove")
292
+ }
293
+ if (process.env.FIGS_TOKEN) {
294
+ console.warn(
295
+ "figs: ! FIGS_TOKEN is still set in your environment — `unset FIGS_TOKEN` to fully log out",
296
+ )
297
+ }
298
+ console.log(
299
+ ` the token still exists server-side — revoke it at ${resolveEndpoint()}/settings`,
300
+ )
301
+ }
302
+
209
303
  /** Show where setup stands — login, workspace, charter. Drives the agent's next step. */
210
304
  async function status() {
211
305
  const token = getToken()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@figs-so/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Figs CLI — publish your AI agent's state to Figs (figs.so). Run by the agent.",
5
5
  "type": "module",
6
6
  "bin": {