@figs-so/cli 1.1.0 → 1.2.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/SPEC.md +2 -2
- package/figs.mjs +31 -2
- package/package.json +1 -1
package/SPEC.md
CHANGED
|
@@ -156,9 +156,9 @@ edge of its autonomy.
|
|
|
156
156
|
| Field | Type | Req | Meaning |
|
|
157
157
|
|---|---|:--:|---|
|
|
158
158
|
| `id` | string | ✓ | Stable id (upsert key). |
|
|
159
|
-
| `type` | enum | ✓ | `question` \| `sign-off` — **the type is the answer contract**: *question* wants an answer (an option or free text), *sign-off* wants a verdict (approve / request-changes / reject). (`needs-decision` was renamed `question`; `fyi` was retired — a for-the-record note is a settled report, not an ask; `blocked` is the run's `status`, not an ask type.) |
|
|
159
|
+
| `type` | enum | ✓ | `question` \| `sign-off` — **the type is the answer contract**: *question* wants an answer (an option or free text), *sign-off* wants a verdict (approve / request-changes / reject). (`needs-decision` was renamed `question`; `fyi` was retired — a for-the-record note / assumption / heads-up is a `checkpoint` on the job (or a settled `report`), not an ask; `blocked` is the run's `status`, not an ask type.) |
|
|
160
160
|
| `status` | enum | | `"open"` (default) \| `"resolved"` (the need was met) \| `"withdrawn"` (the **agent** retracted it; nobody acted) \| `"rejected"` (a human declined it). **Rejected is terminal** on this id — re-raising is a new ask. |
|
|
161
|
-
| `to` | `"manager"` \| `"builder"` | | Who the ask is addressed to: the human accountable for the **work** (`manager`) or for the **machine** (`builder`). Absent = unaddressed. |
|
|
161
|
+
| `to` | `"manager"` \| `"builder"` | | Who the ask is addressed to: the human accountable for the **work** (`manager`) or for the **machine** (`builder`). Absent = unaddressed; `figs ask` **defaults it to `manager`** (the common case) when omitted, so a reader needn't infer. |
|
|
162
162
|
| `title` | string | ✓ | The ask, in one line. |
|
|
163
163
|
| `unit` | string | | The `Unit.id` this concerns. |
|
|
164
164
|
| `run` | string | | The run `id` this ask was raised during. **Optional** — asks also arise outside runs. |
|
package/figs.mjs
CHANGED
|
@@ -89,10 +89,11 @@ const COMMANDS = {
|
|
|
89
89
|
eg: "figs status --json",
|
|
90
90
|
},
|
|
91
91
|
login: {
|
|
92
|
-
args: "[<token>]",
|
|
93
|
-
flags: [],
|
|
92
|
+
args: "[<token>] [--force]",
|
|
93
|
+
flags: ["--force"],
|
|
94
94
|
desc: "log in — browser device-flow, or save a pasted token",
|
|
95
95
|
more: [
|
|
96
|
+
"already logged in? it's a no-op that points you to `figs link` — pass --force to log in again.",
|
|
96
97
|
"no arg → device flow: opens a browser for a human to approve (you never see the token).",
|
|
97
98
|
"<token> → save a token you already have to ~/.figs/credentials.json.",
|
|
98
99
|
],
|
|
@@ -846,6 +847,17 @@ async function login(token) {
|
|
|
846
847
|
return
|
|
847
848
|
}
|
|
848
849
|
|
|
850
|
+
// Already logged in? Don't start a needless device flow — it strands a cold/headless
|
|
851
|
+
// agent with no human to approve. Token presence is enough to short-circuit; a stale
|
|
852
|
+
// token surfaces on the next link/push. `--force` re-runs the flow deliberately.
|
|
853
|
+
if (!hasFlag("--force") && getToken()) {
|
|
854
|
+
console.log(`figs: ✓ already logged in to ${originOf(resolveEndpoint())}`)
|
|
855
|
+
console.log(
|
|
856
|
+
" `figs status` for details · `figs link` to connect a workspace · `figs login --force` to log in again",
|
|
857
|
+
)
|
|
858
|
+
return
|
|
859
|
+
}
|
|
860
|
+
|
|
849
861
|
const start = await request("POST", "/api/device/start")
|
|
850
862
|
if (!start.ok) die(`could not start login (${start.status})`)
|
|
851
863
|
const d = start.data
|
|
@@ -1824,6 +1836,14 @@ async function reportCmd() {
|
|
|
1824
1836
|
// Announce new-vs-fold only for an explicit --id — that's where a typo means
|
|
1825
1837
|
// "I meant to continue a job but opened a sibling" (auto-ids are always new).
|
|
1826
1838
|
if (idGiven) announceFold("job", id, isNew, " (settled)")
|
|
1839
|
+
// The "why it started": a born-settled job that never checkpointed still shows
|
|
1840
|
+
// "triggered by …" in the manager's timeline when --trigger is set. Nudge it
|
|
1841
|
+
// only on a fresh job (never on a fold/continuation — that would cry wolf).
|
|
1842
|
+
if (isNew && !trigger) {
|
|
1843
|
+
console.log(
|
|
1844
|
+
`figs: tip: \`--trigger '<what set this in motion>'\` records the "why" — your manager sees it on the job's timeline`,
|
|
1845
|
+
)
|
|
1846
|
+
}
|
|
1827
1847
|
// Teaching, never a gate: a settled job with open asks citing it is the
|
|
1828
1848
|
// normal tail-of-job pattern (the ask owns the waiting) — but if the job's
|
|
1829
1849
|
// OUTCOME depends on an answer, in-flight is the honest state. Local fold
|
|
@@ -1887,6 +1907,11 @@ async function checkpointCmd() {
|
|
|
1887
1907
|
console.log(
|
|
1888
1908
|
`figs: new job opened: ${id} (in flight) — checkpoint as you go; \`figs report --id ${id}\` settles it`,
|
|
1889
1909
|
)
|
|
1910
|
+
if (!trigger) {
|
|
1911
|
+
console.log(
|
|
1912
|
+
`figs: tip: add \`--trigger '<what set this in motion>'\` so your manager sees why this job started`,
|
|
1913
|
+
)
|
|
1914
|
+
}
|
|
1890
1915
|
} else if (settledBefore) {
|
|
1891
1916
|
console.warn(
|
|
1892
1917
|
`figs: ! reopening a settled job (${id}) — continue only if it's truly the same job; new work wants a new id`,
|
|
@@ -1937,6 +1962,10 @@ async function askCmd() {
|
|
|
1937
1962
|
const v = flag(f)
|
|
1938
1963
|
if (v) ask[k] = v
|
|
1939
1964
|
}
|
|
1965
|
+
// Default an unaddressed ask to the manager (the work-accountable human) — the common
|
|
1966
|
+
// case; `--to builder` is the explicit exception. Saves the reader from inferring ("guess").
|
|
1967
|
+
// (Hand-authored JSONL may still omit `to` → unaddressed; the verb is sugar that defaults.)
|
|
1968
|
+
if (!ask.to) ask.to = "manager"
|
|
1940
1969
|
const options = flagAll("--option")
|
|
1941
1970
|
if (options.length) ask.options = options
|
|
1942
1971
|
for (const o of ask.options ?? []) {
|