@figs-so/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +2 -1
  2. package/SPEC.md +6 -2
  3. package/figs.mjs +21 -27
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -73,7 +73,8 @@ are shorthand for exactly that (always current, no version drift). Prefer a real
73
73
  | `figs login` / `logout` | device-flow browser approve / remove local token |
74
74
  | `figs workspaces [--json]` | list your workspaces (create one in the web app) |
75
75
  | `figs init [--workspace <slug>]` | generate identity + write `.figs/` (omit the flag: uses your only workspace, else lists them) |
76
- | **`figs report --result "…"`** | record a runstamps id + timestamp, auto-captures the session trace, `--attach`es artifacts, pushes itself (`--resolves <ask-id>` closes an ask in the same stroke) |
76
+ | **`figs inbox [<ask-id>]`** | start every session here your humans' answers/verdicts, verbatim, with the next command per ask; with an id: the full zero-context handoff package (thread + artifacts restored) |
77
+ | **`figs report --result "…"`** | record a run — **one job, one stable `--id`** (re-reporting an id folds progress onto that job's row); stamps the timestamp, auto-captures the session trace, `--attach`es artifacts, pushes itself |
77
78
  | **`figs ask <type> --title "…"`** | raise a self-contained ask (`blocked` · `needs-decision` · `sign-off` · `fyi`) — options/details/attachments, pushed so a human sees it |
78
79
  | **`figs resolve <ask-id>`** | close an ask — `--chosen` verbatim-checked against its options, `--withdrawn` for the un-ask |
79
80
  | `figs push` | the bare transport — the verbs call it automatically; type it yourself after hand-edits or `--no-push` |
package/SPEC.md CHANGED
@@ -82,7 +82,12 @@ Use **`steps`** *or* **`responsibilities`** depending on shape — a fixed pipel
82
82
 
83
83
  ## 5. `runs.jsonl` — activity
84
84
 
85
- One JSON object per line (JSON Lines). Each is something the agent did.
85
+ One JSON object per line (JSON Lines). **One record = one job** — a unit of work the agent's
86
+ *manager* would recognize ("recon — Acme — November"), under a **stable, meaningful id**
87
+ (`recon-acme-2026-11`); the runs list reads as the job list. Records **fold by `id`** (same
88
+ merge as asks): re-reporting a job's id layers progress onto its row (`status` evolves
89
+ blocked-ish `warn` → `ok`) — sittings/sessions are agent plumbing and never mint records.
90
+ Closing an ask is **not** a job: that's a `resolution` in `asks.jsonl` (§6), never a run.
86
91
 
87
92
  | Field | Type | Req | Meaning |
88
93
  |---|---|:--:|---|
@@ -93,7 +98,6 @@ One JSON object per line (JSON Lines). Each is something the agent did.
93
98
  | `result` | string | | One-line outcome. |
94
99
  | `status` | `"ok"` \| `"warn"` \| `"fail"` | | Default `"ok"`. **Outcome, never lifecycle** — a run is a complete fact when reported; nothing "closes" a run. |
95
100
  | `artifacts` | string[] | | File names under `artifacts/` to attach. Singular `artifact` (string) remains valid shorthand for one — readers normalize to the array (same pattern as `resolution`'s bare-string shorthand). |
96
- | `resolves` | string | | The ask `id` this run executes/closes (the agent did the answered/approved thing and is reporting back — see [§6.1](#61-lifecycle--two-ledgers-split-by-author)). |
97
101
  | `session` | `Session` | | Where/how this ran (see [§5.1](#51-session--runtime-metadata-optional)). Optional, self-reported. |
98
102
 
99
103
  ### 5.1 `Session` — runtime metadata (optional)
package/figs.mjs CHANGED
@@ -113,20 +113,23 @@ const COMMANDS = {
113
113
  report: {
114
114
  args: "--result <text> [options]",
115
115
  flags: [
116
- "--result", "--id", "--unit", "--period", "--status", "--attach",
117
- "--resolves", "--chosen", "--by", "--note", "--no-push",
116
+ "--result", "--id", "--unit", "--period", "--status", "--attach", "--no-push",
118
117
  ],
119
- desc: "record a run — writes one line to runs.jsonl, stamps id/ts/session, pushes",
118
+ desc: "record a run — one job's row in runs.jsonl; stamps id/ts/session, pushes",
120
119
  more: [
120
+ "One run = one JOB — a unit of work your manager would recognize; the runs",
121
+ "list reads as the job list. Give a job a stable, meaningful --id",
122
+ "(recon-acme-2026-11); reporting the same id again folds onto that job's row",
123
+ "(progress evolves: blocked → ok). Sittings/sessions never mint runs —",
124
+ "stopping to wait for a human is not a job.",
121
125
  "You supply the content; the CLI does the bookkeeping (id, real-clock ts, session",
122
126
  "trace from your runtime's own records, validation, artifact copy, push).",
123
127
  "--attach <file> (repeatable) copies the file into artifacts/ and links it.",
124
- "--resolves <ask-id> also closes that ask (with optional --chosen/--by/--note).",
125
- "--id only for deliberate stable ids (re-running the same job updates the same run).",
126
128
  "--no-push writes locally only; `figs push` publishes later.",
129
+ "Closing an ask is `figs resolve` — a close is not a job; never report one.",
127
130
  "Hand-writing runs.jsonl works too — this verb is sugar over the same file.",
128
131
  ],
129
- eg: 'figs report --result "88% matched · 31 flagged" --attach ./acme-2025-11.html',
132
+ eg: 'figs report --id recon-acme-2026-11 --result "88% matched · 31 flagged" --attach ./acme-2025-11.html',
130
133
  },
131
134
  ask: {
132
135
  args: "<type> --title <text> [options]",
@@ -160,7 +163,7 @@ const COMMANDS = {
160
163
  "attached artifacts restored into .figs/artifacts/ (hash-verified) so a fresh",
161
164
  "session can act from the record alone.",
162
165
  "Scope: THIS agent's open asks + human-rejected ones you haven't acknowledged.",
163
- "Reads only — closing still happens via figs resolve / figs report --resolves.",
166
+ "Reads only — closing still happens via figs resolve.",
164
167
  ],
165
168
  eg: "figs inbox",
166
169
  },
@@ -173,7 +176,9 @@ const COMMANDS = {
173
176
  "Three closes, by who ended it: resolved (default — the need was met) ·",
174
177
  "--withdrawn (YOU retracted it; nobody acted) · --rejected (a HUMAN declined",
175
178
  "it — record their out-of-band no; rejected is terminal, re-raising = a new ask).",
176
- "Use `figs report --resolves <ask-id>` instead when a run did the work.",
179
+ "After an answer, fork on what it unlocked: nothing new resolve right away;",
180
+ "real work → do the job, `figs report` it under its own id, THEN resolve —",
181
+ "cite the job id in --note so a reader can find the work.",
177
182
  ],
178
183
  eg: 'figs resolve acme-bridge --chosen "Strip the alpha prefix" --by "Sarah (accounting)"',
179
184
  },
@@ -1166,12 +1171,16 @@ function nextMove(a) {
1166
1171
  const last = a.events[a.events.length - 1]
1167
1172
  if (!last) return "waiting on your human — nothing for you to do"
1168
1173
  if (last.kind === "verdict" && last.verdict === "approved") {
1169
- return `approved — verify any prerequisites in the ask, do it, then: figs report --resolves ${a.id}`
1174
+ return (
1175
+ `approved — verify any prerequisites in the ask, then fork on what it unlocked:` +
1176
+ `\n nothing left to do → figs resolve ${a.id}` +
1177
+ `\n real work → do the job, figs report it under its own --id, then figs resolve ${a.id} --note "job <id>"`
1178
+ )
1170
1179
  }
1171
1180
  if (last.kind === "verdict" && last.verdict === "changes_requested") {
1172
1181
  return `revise, then re-raise on the same id: figs ask ${a.type} --id ${a.id} --title "…" …`
1173
1182
  }
1174
- return `act on the answer, then: figs report --resolves ${a.id} (or figs resolve ${a.id} --chosen "…")`
1183
+ return `act on the answer (real work → figs report it under its own --id), then: figs resolve ${a.id} --chosen "…"`
1175
1184
  }
1176
1185
 
1177
1186
  /** Restore an ask's refs into artifacts/ — hash-verified; never clobbers. */
@@ -1220,7 +1229,7 @@ async function fetchRefs(config, refs) {
1220
1229
  * `figs inbox` — session start. Bare: every ask with thread activity + the
1221
1230
  * next command for each. With an id: the zero-context handoff package (the
1222
1231
  * ask, the whole thread verbatim, refs restored to disk). Pure read — writes
1223
- * nothing to the outbox; closing happens via resolve / report --resolves.
1232
+ * nothing to the outbox; closing happens via resolve.
1224
1233
  */
1225
1234
  async function inboxCmd() {
1226
1235
  requireFigs()
@@ -1293,7 +1302,7 @@ async function inboxCmd() {
1293
1302
  }
1294
1303
  }
1295
1304
 
1296
- // ---------- the resolution fold (shared by `resolve` and `report --resolves`) -
1305
+ // ---------- the resolution fold (`resolve` the one closing verb) ----------
1297
1306
  /**
1298
1307
  * Build the closing fold line. Best-effort, the verified path: fetches this
1299
1308
  * agent's inbox and, when the ask has human events, cites the one acted on —
@@ -1406,16 +1415,6 @@ async function reportCmd() {
1406
1415
  const attached = attachFiles(flagAll("--attach"))
1407
1416
  if (attached.length === 1) run.artifact = attached[0]
1408
1417
  else if (attached.length > 1) run.artifacts = attached
1409
- const resolves = flag("--resolves")
1410
- let resolution = null
1411
- if (resolves) {
1412
- run.resolves = resolves
1413
- resolution = await buildResolution(resolves, {
1414
- chosen: flag("--chosen"),
1415
- by: flag("--by"),
1416
- note: flag("--note"),
1417
- })
1418
- }
1419
1418
  const session = captureSession()
1420
1419
  if (session) run.session = session
1421
1420
 
@@ -1423,11 +1422,6 @@ async function reportCmd() {
1423
1422
  if (issues.length) die(`not written:\n ${issues.join("\n ")}`)
1424
1423
  appendJsonl("runs.jsonl", run)
1425
1424
  console.log(`figs: ✓ run recorded — ${summarize(run)}`)
1426
- if (resolution) {
1427
- for (const w of resolution.warnings) console.warn(`figs: ! ${w}`)
1428
- appendJsonl("asks.jsonl", resolution.line)
1429
- console.log(`figs: ✓ ask ${resolves} ${resolution.line.status}`)
1430
- }
1431
1425
  await autoPush()
1432
1426
  }
1433
1427
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@figs-so/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
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": {