@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.
Files changed (3) hide show
  1. package/SPEC.md +2 -2
  2. package/figs.mjs +31 -2
  3. 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 ?? []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@figs-so/cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.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": {