@figs-so/cli 0.1.2 → 0.1.3
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/README.md +9 -8
- package/figs.mjs +79 -42
- package/package.json +1 -2
- package/GUIDE.template.md +0 -147
package/README.md
CHANGED
|
@@ -9,14 +9,15 @@ manager's window into the agents a company runs as back-office employees.
|
|
|
9
9
|
## Use
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx @figs-so/cli@latest login
|
|
13
|
-
npx @figs-so/cli@latest workspaces
|
|
14
|
-
npx @figs-so/cli@latest init --workspace <
|
|
15
|
-
npx @figs-so/cli@latest doctor
|
|
16
|
-
npx @figs-so/cli@latest push
|
|
12
|
+
npx @figs-so/cli@latest login # browser approve (device flow)
|
|
13
|
+
npx @figs-so/cli@latest workspaces # list your workspaces (shows the slug)
|
|
14
|
+
npx @figs-so/cli@latest init --workspace <slug> # writes .figs/config.json + GUIDE.md
|
|
15
|
+
npx @figs-so/cli@latest doctor # validate .figs/ before pushing
|
|
16
|
+
npx @figs-so/cli@latest push # publish .figs/ to Figs
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The full, always-current guide + `agent.json` schema is served at **`<endpoint>/llms.txt`**
|
|
20
|
+
(`figs init` drops a thin pointer to it at `.figs/GUIDE.md`).
|
|
20
21
|
|
|
21
22
|
## Commands
|
|
22
23
|
|
|
@@ -24,8 +25,8 @@ After `init`, read **`.figs/GUIDE.md`** for the full agent integration guide.
|
|
|
24
25
|
|---|---|
|
|
25
26
|
| `figs status [--json]` | login / workspace / agent state |
|
|
26
27
|
| `figs login` | device-flow browser approve (or `figs login <token>`) |
|
|
27
|
-
| `figs workspaces [--
|
|
28
|
-
| `figs init --workspace <id>` | generate identity UUID + config + GUIDE.md |
|
|
28
|
+
| `figs workspaces [--json]` | list your workspaces (read-only; create one in the web app) |
|
|
29
|
+
| `figs init --workspace <slug-or-id>` | resolve the workspace, generate identity UUID + config + pointer GUIDE.md |
|
|
29
30
|
| `figs doctor` | validate `.figs/` against the contract |
|
|
30
31
|
| `figs push` | one-way publish of `.figs/` |
|
|
31
32
|
| `figs version` | print version + check for updates |
|
package/figs.mjs
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
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 workspaces
|
|
9
|
-
* figs init --workspace <id> [--endpoint <url>]
|
|
8
|
+
* figs workspaces list the user's workspaces [--json]
|
|
9
|
+
* figs init --workspace <slug-or-id> [--endpoint <url>]
|
|
10
10
|
* create .figs/config.json + GUIDE.md (generates a stable agent id)
|
|
11
11
|
* figs doctor validate .figs/ against the contract before pushing
|
|
12
12
|
* figs push one-way push the .figs/ spine to the ingest endpoint
|
|
@@ -28,13 +28,10 @@ import {
|
|
|
28
28
|
writeFileSync,
|
|
29
29
|
} from "node:fs"
|
|
30
30
|
import { homedir } from "node:os"
|
|
31
|
-
import {
|
|
32
|
-
import { fileURLToPath } from "node:url"
|
|
31
|
+
import { join } from "node:path"
|
|
33
32
|
import { randomUUID } from "node:crypto"
|
|
34
33
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const VERSION = "0.1.2"
|
|
34
|
+
const VERSION = "0.1.3"
|
|
38
35
|
// Going-forward default; override with FIGS_ENDPOINT or .figs/config.json endpoint
|
|
39
36
|
// (e.g. FIGS_ENDPOINT=http://localhost:3000 for local dev).
|
|
40
37
|
const DEFAULT_ENDPOINT = "https://app.figs.so"
|
|
@@ -148,7 +145,7 @@ async function checkVersion({ force = false, hardFail = false } = {}) {
|
|
|
148
145
|
if (cmd === "login") await login(process.argv[3])
|
|
149
146
|
else if (cmd === "status") await status()
|
|
150
147
|
else if (cmd === "workspaces") await workspaces()
|
|
151
|
-
else if (cmd === "init") init()
|
|
148
|
+
else if (cmd === "init") await init()
|
|
152
149
|
else if (cmd === "doctor") await doctor()
|
|
153
150
|
else if (cmd === "push") await push()
|
|
154
151
|
else if (cmd === "version" || cmd === "--version") {
|
|
@@ -263,40 +260,88 @@ async function status() {
|
|
|
263
260
|
row("cli", VERSION)
|
|
264
261
|
}
|
|
265
262
|
|
|
266
|
-
/**
|
|
263
|
+
/**
|
|
264
|
+
* List the user's workspaces (read-only). Creating a workspace is a human,
|
|
265
|
+
* web-side action — the agent only ever joins one it was pointed at. Surfaces
|
|
266
|
+
* the slug, which is what `figs init --workspace <slug>` takes.
|
|
267
|
+
*/
|
|
267
268
|
async function workspaces() {
|
|
268
|
-
const createName = flag("--create")
|
|
269
|
-
if (createName) {
|
|
270
|
-
const { workspace: ws } = await api("POST", "/api/workspaces", {
|
|
271
|
-
name: createName,
|
|
272
|
-
})
|
|
273
|
-
if (JSON_OUT) return void console.log(JSON.stringify(ws, null, 2))
|
|
274
|
-
console.log(
|
|
275
|
-
`figs: ✓ created "${ws.name}" (${ws.id}) — you're the owner`,
|
|
276
|
-
)
|
|
277
|
-
return
|
|
278
|
-
}
|
|
279
|
-
|
|
280
269
|
const { workspaces: list = [] } = await api("GET", "/api/workspaces")
|
|
281
270
|
if (JSON_OUT) return void console.log(JSON.stringify(list, null, 2))
|
|
282
271
|
if (list.length === 0) {
|
|
283
|
-
console.log(
|
|
272
|
+
console.log("figs: no workspaces yet — create one in the Figs web app, then re-run.")
|
|
284
273
|
return
|
|
285
274
|
}
|
|
286
275
|
console.log(`figs: ${list.length} workspace${list.length === 1 ? "" : "s"}`)
|
|
287
276
|
for (const w of list) {
|
|
288
|
-
console.log(` ${String(w.role).padEnd(6)} ${w.
|
|
277
|
+
console.log(` ${String(w.role).padEnd(6)} ${w.slug} — ${w.name}`)
|
|
289
278
|
}
|
|
279
|
+
console.log(" → figs init --workspace <slug>")
|
|
290
280
|
}
|
|
291
281
|
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
function isUuid(s) {
|
|
283
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
284
|
+
String(s),
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The local `.figs/GUIDE.md` is a thin, endpoint-relative pointer — the full,
|
|
290
|
+
* always-current guide + `agent.json` schema is served at `${endpoint}/llms.txt`
|
|
291
|
+
* (one source, no frozen copy to drift). Just enough here to act.
|
|
292
|
+
*/
|
|
293
|
+
function guideStub(endpoint) {
|
|
294
|
+
return `<!-- Written by \`figs init\`. Full, always-current guide + schema: ${endpoint}/llms.txt -->
|
|
295
|
+
# Figs — quickstart for this agent
|
|
296
|
+
|
|
297
|
+
You are an AI employee. **Figs** is the read-only window your manager uses to see what you're
|
|
298
|
+
responsible for, what you've done, and what you need from them. You publish to it; it never
|
|
299
|
+
writes back into this repo.
|
|
300
|
+
|
|
301
|
+
**Full guide + \`agent.json\` schema → ${endpoint}/llms.txt** — read this before authoring your charter.
|
|
302
|
+
|
|
303
|
+
Lifecycle:
|
|
304
|
+
1. \`figs login\` a human approves in a browser — the token is theirs, never yours.
|
|
305
|
+
2. \`figs init --workspace <slug>\` done — wrote config.json (your identity UUID) + this file.
|
|
306
|
+
3. author \`.figs/agent.json\` derive it from this repo's CLAUDE.md / MEMORY.md, then \`figs doctor\`.
|
|
307
|
+
4. \`figs push\` you now appear in the org chart, self-described.
|
|
308
|
+
|
|
309
|
+
Every run (wire this into your run loop, not a one-off):
|
|
310
|
+
append a line to runs.jsonl · record blockers in asks.jsonl · copy reports into artifacts/ · \`figs push\`.
|
|
311
|
+
|
|
312
|
+
Commit config.json + agent.json (identity + charter). runs.jsonl / asks.jsonl / artifacts/ are a
|
|
313
|
+
transient outbox — gitignored, aggregated remotely. The sync is one-way and never deletes on the server.
|
|
314
|
+
`
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function init() {
|
|
318
|
+
const workspaceArg = flag("--workspace")
|
|
319
|
+
if (!workspaceArg) {
|
|
320
|
+
die("usage: figs init --workspace <slug-or-id> [--endpoint <url>]")
|
|
296
321
|
}
|
|
322
|
+
const endpoint = (flag("--endpoint") || resolveEndpoint()).replace(/\/+$/, "")
|
|
323
|
+
|
|
324
|
+
// A UUID is the canonical workspace id (used as-is, no network). A slug is the
|
|
325
|
+
// human-friendly handle — resolve it to its UUID via the API, and store the
|
|
326
|
+
// UUID so a later workspace rename can't break this agent's pushes.
|
|
327
|
+
let workspaceId = workspaceArg
|
|
328
|
+
if (!isUuid(workspaceArg)) {
|
|
329
|
+
if (!getToken()) {
|
|
330
|
+
die("not logged in — run `figs login` first (resolving a workspace slug needs auth; or pass the workspace UUID)")
|
|
331
|
+
}
|
|
332
|
+
const r = await request("GET", "/api/workspaces", null, getToken())
|
|
333
|
+
if (!r.ok) {
|
|
334
|
+
die(`could not resolve workspace "${workspaceArg}" (${r.status}): ${r.data.error ?? r.data.raw ?? ""}`)
|
|
335
|
+
}
|
|
336
|
+
const list = r.data.workspaces ?? []
|
|
337
|
+
const match = list.find((w) => w.slug === workspaceArg || w.id === workspaceArg)
|
|
338
|
+
if (!match) {
|
|
339
|
+
die(`no workspace matching "${workspaceArg}" — run \`figs workspaces\` to see yours`)
|
|
340
|
+
}
|
|
341
|
+
workspaceId = match.id
|
|
342
|
+
}
|
|
343
|
+
|
|
297
344
|
const existing = readJson(join(repoDir, "config.json"), null)
|
|
298
|
-
const endpoint =
|
|
299
|
-
flag("--endpoint") || existing?.endpoint || DEFAULT_ENDPOINT
|
|
300
345
|
const agentId = existing?.agentId || randomUUID()
|
|
301
346
|
mkdirSync(repoDir, { recursive: true })
|
|
302
347
|
writeFileSync(
|
|
@@ -320,22 +365,14 @@ function init() {
|
|
|
320
365
|
].join("\n"),
|
|
321
366
|
)
|
|
322
367
|
}
|
|
323
|
-
|
|
324
|
-
let guide = ""
|
|
325
|
-
try {
|
|
326
|
-
writeFileSync(
|
|
327
|
-
join(repoDir, "GUIDE.md"),
|
|
328
|
-
readFileSync(join(cliDir, "GUIDE.template.md"), "utf8"),
|
|
329
|
-
)
|
|
330
|
-
guide = " + GUIDE.md"
|
|
331
|
-
} catch {
|
|
332
|
-
// template not shipped (dev) — non-fatal
|
|
333
|
-
}
|
|
368
|
+
writeFileSync(join(repoDir, "GUIDE.md"), guideStub(endpoint))
|
|
334
369
|
|
|
335
370
|
console.log(
|
|
336
|
-
`figs: ✓ .figs/config.json + .gitignore
|
|
371
|
+
`figs: ✓ .figs/config.json + .gitignore + GUIDE.md written (agentId ${agentId})`,
|
|
372
|
+
)
|
|
373
|
+
console.log(
|
|
374
|
+
` read .figs/GUIDE.md (full guide: ${endpoint}/llms.txt), author .figs/agent.json, then \`figs doctor\`.`,
|
|
337
375
|
)
|
|
338
|
-
console.log(" read .figs/GUIDE.md, then author .figs/agent.json and run `figs doctor`.")
|
|
339
376
|
}
|
|
340
377
|
|
|
341
378
|
/** Validate the local .figs/ payload against the contract — no write. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@figs-so/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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": {
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"figs.mjs",
|
|
11
|
-
"GUIDE.template.md",
|
|
12
11
|
"README.md"
|
|
13
12
|
],
|
|
14
13
|
"engines": {
|
package/GUIDE.template.md
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
<!-- This file is emitted by `figs init`. Re-run `figs init` to refresh it. -->
|
|
2
|
-
# Figs — how this `.figs/` folder works
|
|
3
|
-
|
|
4
|
-
You are an AI employee. **Figs** is the window your manager uses to see what you're
|
|
5
|
-
responsible for, what you've done, and what you need from them. You publish your state to it;
|
|
6
|
-
it's a **read-only mirror** — it never reaches back into your repo.
|
|
7
|
-
|
|
8
|
-
Schema details here mirror Figs' contract (the source of truth). After you edit anything, run
|
|
9
|
-
**`figs doctor`** — it validates `.figs/` against the live contract and tells you what's wrong.
|
|
10
|
-
|
|
11
|
-
## The model: `.figs/` is your `dist/`
|
|
12
|
-
|
|
13
|
-
Everything you want visible goes in the `.figs/` folder, and `figs push` publishes it.
|
|
14
|
-
*If it's in `.figs/`, it's shared; if not, it's private.* The sync is **one-way,
|
|
15
|
-
append-mostly, and never deletes** on the server — the remote is the durable record, so a run
|
|
16
|
-
your manager signed off on doesn't vanish because you cleaned up locally.
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
.figs/
|
|
20
|
-
config.json # { endpoint, workspaceId, agentId } — written by `figs init` (commit it)
|
|
21
|
-
agent.json # who you are: your charter/spine — you write this (commit it)
|
|
22
|
-
runs.jsonl # what you did, one line per run — you append (gitignored)
|
|
23
|
-
asks.jsonl # what you need from a human — you append (gitignored)
|
|
24
|
-
artifacts/ # the reports you produced — you copy in (gitignored)
|
|
25
|
-
GUIDE.md # this file
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
**Commit `config.json` + `agent.json`** (your identity + charter, non-secret). The activity
|
|
29
|
-
files are a transient outbox — `figs init` gitignores them; the server aggregates them.
|
|
30
|
-
|
|
31
|
-
## `agent.json` — your charter (the spine)
|
|
32
|
-
|
|
33
|
-
Write this by reading **your own repo** — your `CLAUDE.md` / `MEMORY.md` already say who you
|
|
34
|
-
are. Derive it, don't invent it, and keep it current as your role changes. **Do not put an
|
|
35
|
-
`id` here** — your identity UUID lives in `config.json` and the CLI attaches it on push.
|
|
36
|
-
|
|
37
|
-
| Field | Req | What it is |
|
|
38
|
-
|---|---|---|
|
|
39
|
-
| `name` | ✅ | Display name (e.g. "Reconciliation"). |
|
|
40
|
-
| `type` | | `"agent"` (default) or `"human"`. |
|
|
41
|
-
| `role` | | One-line title (bilingual is fine). |
|
|
42
|
-
| `status` | | Free text — your current state (e.g. `"in_dev"`, `"healthy"`). |
|
|
43
|
-
| `mandate` | | **Your 工作執掌** — one sentence: what you're accountable for. Shown loudest. |
|
|
44
|
-
| `avatar` | | `{ "seed": "<string>" }` — seeds your avatar. |
|
|
45
|
-
| `org` | | `{ "department": "...", "manager": "<member email>" }`. **`department` groups you in the org chart.** |
|
|
46
|
-
| `runtime` | | e.g. `"Claude Code"`. |
|
|
47
|
-
| `cadence` | | e.g. `"Monthly"`, `"Quarterly"`. |
|
|
48
|
-
| `method` | | `string[]` — **how you work**, numbered steps. The "How it works" section. |
|
|
49
|
-
| `properties` | | `[{ "k": "...", "v": "..." }]` — free-form facts shown on your card. |
|
|
50
|
-
| `units` | | `[]` — the things you're responsible for (a customer, a job). Optional; omit if none. |
|
|
51
|
-
|
|
52
|
-
**A `unit`:** `{ id, name, subtitle?, status?, period?, detail?, stats?: [{l,v}] }`. The `id`
|
|
53
|
-
is how your runs link to it (a run's `unit` matches a unit `id`).
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
{
|
|
57
|
-
"name": "Reconciliation",
|
|
58
|
-
"type": "agent",
|
|
59
|
-
"role": "對帳專員 · Reconciliation Officer",
|
|
60
|
-
"status": "in_dev",
|
|
61
|
-
"avatar": { "seed": "Reconciliation" },
|
|
62
|
-
"org": { "department": "Finance Ops · CSR", "manager": "you@company.com" },
|
|
63
|
-
"runtime": "Claude Code",
|
|
64
|
-
"cadence": "Monthly",
|
|
65
|
-
"mandate": "Reconciles open invoices every month — flags what doesn't match for review.",
|
|
66
|
-
"method": [
|
|
67
|
-
"Pull the WT-side open invoices and the customer-side statement for the month.",
|
|
68
|
-
"Match on PO / delivery-number keys within tolerance.",
|
|
69
|
-
"Classify every key — matched / needs-review / WT-only / cust-only — with a 'why'.",
|
|
70
|
-
"Surface discrepancies. Never write back to the source."
|
|
71
|
-
],
|
|
72
|
-
"properties": [{ "k": "Department", "v": "Finance Ops · CSR" }],
|
|
73
|
-
"units": [
|
|
74
|
-
{
|
|
75
|
-
"id": "compal", "name": "Compal", "subtitle": "仁寶電腦",
|
|
76
|
-
"status": "88% matched · 31 keys flagged", "period": "2025-11",
|
|
77
|
-
"stats": [{ "l": "Matched", "v": "2,161 keys" }, { "l": "Needs review", "v": "31 keys" }]
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## `runs.jsonl` — what you did (append one line per run)
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{ "id": "compal-2025-11", "ts": "2026-05-28T23:41:26Z", "unit": "compal", "period": "2025-11", "result": "88% matched · 31 keys flagged", "status": "ok", "artifact": "compal-2025-11.html" }
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
- `id` ✅ and `ts` ✅ (ISO-8601 with offset) are required. `status`: `ok | warn | fail` (default `ok`).
|
|
90
|
-
- `unit` links to a unit `id`. `result` is the one-line outcome. `artifact` is a file in `artifacts/`.
|
|
91
|
-
- **Idempotent by `id`** — re-pushing the same id updates that run, never duplicates. Use a stable id.
|
|
92
|
-
|
|
93
|
-
## `asks.jsonl` — what you need from a human (append)
|
|
94
|
-
|
|
95
|
-
Raise your hand when you're stuck. Assemble the full context so the human can act without
|
|
96
|
-
re-gathering anything.
|
|
97
|
-
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"id": "quanta-bridge", "ts": "2026-05-28T21:05:00Z",
|
|
101
|
-
"type": "confirm-assumption", "status": "open", "unit": "quanta",
|
|
102
|
-
"title": "No bridge rule for prefixed invoice numbers",
|
|
103
|
-
"found": "~180 rows can't be matched safely; guessing risks false matches.",
|
|
104
|
-
"need": "Confirm the bridge rule for prefixed invoice numbers.",
|
|
105
|
-
"options": ["Strip the alpha prefix", "Use a mapping you provide", "Treat as out-of-scope"],
|
|
106
|
-
"details": [ { "l": "Amount at risk", "v": "$52.9M" } ],
|
|
107
|
-
"refs": [ { "label": "Quanta report", "artifact": "quanta-2025-11.html" } ]
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
- Required: `id`, `type`, `title`. `type`: `blocked | needs-decision | confirm-assumption | sign-off`.
|
|
112
|
-
- Optional context (each renders only if present): `found`, `need`, `options[]`, `details[]`, `refs[]`.
|
|
113
|
-
- **You own the lifecycle.** Close an ask by appending `{ "id": "quanta-bridge", "status": "resolved" }`
|
|
114
|
-
on a later run. Folded by `id`. Strictly one-way — the human acts in their own workflow.
|
|
115
|
-
|
|
116
|
-
## `artifacts/` — your reports
|
|
117
|
-
|
|
118
|
-
Drop the report a run produced here and point to it from the run's `artifact` (filename only).
|
|
119
|
-
Supported: **`.html` `.md` `.txt` `.json`** and images (`.png .jpg .gif .webp .svg`), **≤ 3 MB**
|
|
120
|
-
(compress larger images). HTML/markdown render in a sandboxed viewer; the file is shown
|
|
121
|
-
exactly as you produced it.
|
|
122
|
-
|
|
123
|
-
## Lifecycle
|
|
124
|
-
|
|
125
|
-
**One-time setup** (per repo):
|
|
126
|
-
|
|
127
|
-
1. **Log in** *(human step — a token is a secret)*: run `figs login`; your human opens the
|
|
128
|
-
printed link, signs in, and clicks **Approve**. (Fallback: `figs login <token>`.)
|
|
129
|
-
2. **`figs init --workspace <id>`** — generates your identity UUID and writes `config.json`
|
|
130
|
-
+ `.gitignore` + this `GUIDE.md`. Get the workspace id from `figs workspaces`.
|
|
131
|
-
3. **Author `agent.json`** from your own `CLAUDE.md` / `MEMORY.md`, then **`figs doctor`**.
|
|
132
|
-
4. **`figs push`** — you now appear in the org chart, self-described.
|
|
133
|
-
|
|
134
|
-
**Every run** (wire this into your run loop, not a one-off):
|
|
135
|
-
|
|
136
|
-
1. Append a line to `runs.jsonl`; record any blockers in `asks.jsonl`.
|
|
137
|
-
2. Copy the run's report into `artifacts/`.
|
|
138
|
-
3. `figs push`.
|
|
139
|
-
|
|
140
|
-
## Rules
|
|
141
|
-
|
|
142
|
-
- **One-way, never deletes.** You publish; Figs mirrors. Deleting locally doesn't delete remote.
|
|
143
|
-
- **You own your identity.** The UUID in `config.json` is yours — commit it so everyone running
|
|
144
|
-
this repo pushes to the *same* you.
|
|
145
|
-
- **Idempotent.** Re-running `figs push` is always safe; records fold by `id`.
|
|
146
|
-
- **The token is the human's job.** Never enter or generate auth tokens yourself.
|
|
147
|
-
- **Keep your charter honest.** Update `agent.json` when what you do changes, then `figs doctor`.
|