@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.
- package/README.md +2 -1
- package/SPEC.md +6 -2
- package/figs.mjs +21 -27
- 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
|
|
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).
|
|
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 —
|
|
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
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|