ftown 0.25.2 → 0.26.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 (108) hide show
  1. package/dist/command-rpc.d.ts +3 -0
  2. package/dist/command-rpc.js +41 -0
  3. package/dist/command-rpc.js.map +1 -1
  4. package/dist/coordination/board.d.ts +12 -0
  5. package/dist/coordination/board.js +240 -0
  6. package/dist/coordination/board.js.map +1 -0
  7. package/dist/coordination/card-store.d.ts +9 -0
  8. package/dist/coordination/card-store.js +188 -0
  9. package/dist/coordination/card-store.js.map +1 -0
  10. package/dist/coordination/contract.d.ts +236 -0
  11. package/dist/coordination/contract.js +63 -0
  12. package/dist/coordination/contract.js.map +1 -0
  13. package/dist/coordination/format.d.ts +5 -0
  14. package/dist/coordination/format.js +60 -0
  15. package/dist/coordination/format.js.map +1 -0
  16. package/dist/coordination/tree-events.d.ts +31 -0
  17. package/dist/coordination/tree-events.js +226 -0
  18. package/dist/coordination/tree-events.js.map +1 -0
  19. package/dist/coordination/wiring.d.ts +48 -0
  20. package/dist/coordination/wiring.js +143 -0
  21. package/dist/coordination/wiring.js.map +1 -0
  22. package/dist/coordination-cli.d.ts +34 -0
  23. package/dist/coordination-cli.js +460 -0
  24. package/dist/coordination-cli.js.map +1 -0
  25. package/dist/ftown-cli.js +12 -2
  26. package/dist/ftown-cli.js.map +1 -1
  27. package/dist/index.js +33 -7
  28. package/dist/index.js.map +1 -1
  29. package/dist/local-api-server.d.ts +6 -1
  30. package/dist/local-api-server.js +282 -5
  31. package/dist/local-api-server.js.map +1 -1
  32. package/dist/mail-delivery.d.ts +16 -2
  33. package/dist/mail-delivery.js +331 -77
  34. package/dist/mail-delivery.js.map +1 -1
  35. package/dist/mail-store.d.ts +7 -0
  36. package/dist/mail-store.js +71 -1
  37. package/dist/mail-store.js.map +1 -1
  38. package/dist/pull-request-events.d.ts +5 -0
  39. package/dist/pull-request-events.js +21 -1
  40. package/dist/pull-request-events.js.map +1 -1
  41. package/dist/quota/agy.d.ts +16 -0
  42. package/dist/quota/agy.js +287 -0
  43. package/dist/quota/agy.js.map +1 -0
  44. package/dist/quota/annotate.d.ts +5 -0
  45. package/dist/quota/annotate.js +56 -0
  46. package/dist/quota/annotate.js.map +1 -0
  47. package/dist/quota/claude.d.ts +27 -0
  48. package/dist/quota/claude.js +265 -0
  49. package/dist/quota/claude.js.map +1 -0
  50. package/dist/quota/codex.d.ts +25 -0
  51. package/dist/quota/codex.js +378 -0
  52. package/dist/quota/codex.js.map +1 -0
  53. package/dist/quota/format.d.ts +6 -0
  54. package/dist/quota/format.js +113 -0
  55. package/dist/quota/format.js.map +1 -0
  56. package/dist/quota/grok.d.ts +97 -0
  57. package/dist/quota/grok.js +563 -0
  58. package/dist/quota/grok.js.map +1 -0
  59. package/dist/quota/models-agy.d.ts +10 -0
  60. package/dist/quota/models-agy.js +144 -0
  61. package/dist/quota/models-agy.js.map +1 -0
  62. package/dist/quota/models-claude.d.ts +10 -0
  63. package/dist/quota/models-claude.js +227 -0
  64. package/dist/quota/models-claude.js.map +1 -0
  65. package/dist/quota/models-codex.d.ts +18 -0
  66. package/dist/quota/models-codex.js +305 -0
  67. package/dist/quota/models-codex.js.map +1 -0
  68. package/dist/quota/models-grok.d.ts +19 -0
  69. package/dist/quota/models-grok.js +272 -0
  70. package/dist/quota/models-grok.js.map +1 -0
  71. package/dist/quota/models-muse.d.ts +13 -0
  72. package/dist/quota/models-muse.js +200 -0
  73. package/dist/quota/models-muse.js.map +1 -0
  74. package/dist/quota/models-opencode.d.ts +51 -0
  75. package/dist/quota/models-opencode.js +400 -0
  76. package/dist/quota/models-opencode.js.map +1 -0
  77. package/dist/quota/muse.d.ts +38 -0
  78. package/dist/quota/muse.js +225 -0
  79. package/dist/quota/muse.js.map +1 -0
  80. package/dist/quota/opencode.d.ts +7 -0
  81. package/dist/quota/opencode.js +47 -0
  82. package/dist/quota/opencode.js.map +1 -0
  83. package/dist/quota/routing-config.d.ts +18 -0
  84. package/dist/quota/routing-config.js +438 -0
  85. package/dist/quota/routing-config.js.map +1 -0
  86. package/dist/quota/routing-service.d.ts +24 -0
  87. package/dist/quota/routing-service.js +118 -0
  88. package/dist/quota/routing-service.js.map +1 -0
  89. package/dist/quota/routing.d.ts +10 -0
  90. package/dist/quota/routing.js +180 -0
  91. package/dist/quota/routing.js.map +1 -0
  92. package/dist/quota/types.d.ts +304 -0
  93. package/dist/quota/types.js +45 -0
  94. package/dist/quota/types.js.map +1 -0
  95. package/dist/quota-cli.d.ts +80 -0
  96. package/dist/quota-cli.js +462 -0
  97. package/dist/quota-cli.js.map +1 -0
  98. package/dist/security/bridge/permissions.js +24 -2
  99. package/dist/security/bridge/permissions.js.map +1 -1
  100. package/dist/types.d.ts +7 -3
  101. package/dist/wire-types.d.ts +17 -1
  102. package/dist/wire-types.js +1 -1
  103. package/dist/wire-types.js.map +1 -1
  104. package/package.json +3 -2
  105. package/skills/ftown/SKILL.md +31 -5
  106. package/skills/ftown/references/orchestrator.md +17 -7
  107. package/skills/ftown/references/quota.md +177 -0
  108. package/skills/ftown/references/sessions.md +45 -3
@@ -50,8 +50,23 @@ export interface Session {
50
50
  /** Historical automation provenance; new sessions cannot set it. */
51
51
  loopId?: string;
52
52
  usage?: SessionUsage;
53
+ card?: {
54
+ state: string;
55
+ headline: string;
56
+ progress?: {
57
+ done: number;
58
+ total: number;
59
+ };
60
+ blockedOn?: {
61
+ kind: string;
62
+ ref?: string;
63
+ text: string;
64
+ };
65
+ next?: string;
66
+ updatedAt: string;
67
+ };
53
68
  }
54
- export type MailType = 'message' | 'task' | 'result' | 'escalation';
69
+ export type MailType = 'message' | 'task' | 'result' | 'escalation' | 'fyi';
55
70
  export declare const MAIL_TYPES: readonly MailType[];
56
71
  /** Per-session inbox message, as returned by GET/POST /api/sessions/:id/inbox. */
57
72
  export interface MailMessage {
@@ -65,6 +80,7 @@ export interface MailMessage {
65
80
  body: string;
66
81
  deliveredAt?: string;
67
82
  deliveredVia?: string;
83
+ nudgedAt?: string;
68
84
  }
69
85
  export declare const AUTOMATION_REMOVED_CODE: "automation_removed";
70
86
  export declare const AUTOMATION_REMOVED_MESSAGE = "Recurring loops and factory automation have been removed";
@@ -1,5 +1,5 @@
1
1
  // Shared wire shapes for the in-process CLI runtime and local bridge API.
2
- export const MAIL_TYPES = ['message', 'task', 'result', 'escalation'];
2
+ export const MAIL_TYPES = ['message', 'task', 'result', 'escalation', 'fyi'];
3
3
  export const AUTOMATION_REMOVED_CODE = 'automation_removed';
4
4
  export const AUTOMATION_REMOVED_MESSAGE = 'Recurring loops and factory automation have been removed';
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"wire-types.js","sourceRoot":"","sources":["../src/wire-types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AA4D1E,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AAgB3F,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAA6B,CAAC;AACrE,MAAM,CAAC,MAAM,0BAA0B,GAAG,0DAA0D,CAAC;AAErG;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,UAAmB;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,CAAc;IAC9C,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"wire-types.js","sourceRoot":"","sources":["../src/wire-types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AA6D1E,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;AAiBlG,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAA6B,CAAC;AACrE,MAAM,CAAC,MAAM,0BAA0B,GAAG,0DAA0D,CAAC;AAErG;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,UAAmB;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,CAAc;IAC9C,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ftown",
3
- "version": "0.25.2",
3
+ "version": "0.26.0",
4
4
  "description": "CLI bridge for ftown — generic PTY-over-Centrifugo relay",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,6 +9,7 @@
9
9
  "ftown": "dist/ftown-cli.js",
10
10
  "ftown-harness": "dist/harness-cli.js",
11
11
  "ftown-env": "dist/ftown-env-cli.js",
12
+ "ftown-quota": "dist/quota-cli.js",
12
13
  "ftown-sessions": "bin/ftown-sessions"
13
14
  },
14
15
  "files": [
@@ -72,5 +73,5 @@
72
73
  "overrides": {
73
74
  "qs": "6.16.1"
74
75
  },
75
- "gitHead": "2d012a347cd62c7aac8834e6519812c05651dac1"
76
+ "gitHead": "033b06c892db214c1104779f68ef4f36e3c567f8"
76
77
  }
@@ -51,6 +51,8 @@ ftown sessions preview --add ./extra.html
51
51
  ftown sessions preview --remove notes.md
52
52
  ftown sessions preview --clear
53
53
  ftown sessions preview-reply --message "The preview is ready."
54
+ ftown status set --state working --headline "Reviewing the API client" --progress 0/1
55
+ ftown board
54
56
  ftown harness mail send <session-id> --type task "review the API client"
55
57
  ftown harness mail read
56
58
  ftown env --help
@@ -60,7 +62,8 @@ ftown start --help
60
62
 
61
63
  Read `references/sessions.md` for the session, mail and terminal command
62
64
  reference. Read `references/orchestrator.md` for practical worker coordination
63
- examples. Each command group also has `--help`.
65
+ examples. Read `references/quota.md` before choosing a CLI, model and effort for delegated
66
+ work (`ftown quota --json`). Each command group also has `--help`.
64
67
 
65
68
  `ftown sessions preview <path...>` registers up to 12 files in the current
66
69
  session's working directory for the dashboard, in this order. Kind is by
@@ -94,10 +97,33 @@ ftown harness mail send --parent --type escalation "Blocked: need the API contra
94
97
  ```
95
98
 
96
99
  Mail arrives as `[ftown mail]` context through harness hooks/extensions where
97
- available. Cursor and shell sessions receive an idle nudge to read their inbox.
98
- Prefer mail for agent communication; use terminal key injection only when the
99
- task needs it. Inspect peers with `ftown sessions list` and
100
- `ftown sessions screen <id> --limit 100`.
100
+ available. Mail is for directed instructions (`--type task`), results
101
+ (`--type result`) and escalations (`--type escalation`). Use `--type fyi` for
102
+ non-urgent notes; `fyi` never wakes the recipient. Progress and status go on
103
+ your card (below), never as mail. Each new batch of mail nudges the recipient
104
+ at most once; un-read mail stays in the inbox — read it with
105
+ `ftown harness mail read`. Prefer mail for agent communication; use terminal
106
+ key injection only when the task needs it. Inspect peers with `ftown board`
107
+ and `ftown sessions list`.
108
+
109
+ ## Status and board
110
+
111
+ Keep your card current with `ftown status set` at meaningful transitions —
112
+ starting, becoming blocked, finishing — not every turn. Set state
113
+ (`working`, `blocked`, `waiting_review`, `done`, `failed`), a short headline,
114
+ progress and any blocker. Writing a card wakes nobody. Parents read
115
+ `ftown board` (one line per child) instead of screening quiet workers, and
116
+ wait with `ftown board --wait --until any-done|any-blocked|any-ready` or
117
+ `ftown events --wait`. Idle workers show on the board and as an `idle` event.
118
+
119
+ ```bash
120
+ ftown status set --state working --headline "Implementing the card store" --progress 1/4
121
+ ftown status set --state blocked --headline "Need the API contract" --on parent --text "Waiting on the contract decision"
122
+ ftown status set --state done --headline "Card store shipped; checks green"
123
+ ftown board
124
+ ftown board --wait --until any-done --timeout 300
125
+ ftown events --wait
126
+ ```
101
127
 
102
128
  ## Human attention and pull requests
103
129
 
@@ -11,20 +11,30 @@ bounded task, clear writable files and acceptance checks; save the returned
11
11
  set it when the task calls for a different harness. Provider flavors require a
12
12
  bridge-host token configured with `ftown env set <flavor>`.
13
13
 
14
+ Before picking a CLI, model and effort for delegated work, run `ftown quota --json`
15
+ and follow the decision procedure in `quota.md` (owner tiers first). Pass the
16
+ choice as `--shell <provider>`; add `--model <id>` only with an id from that
17
+ harness's model list. Never route to `exhausted`.
18
+
14
19
  ```bash
15
20
  ftown sessions list
16
21
  ftown sessions create --parent --workdir /path/to/repo --name review-auth \
17
22
  --prompt "Review auth; read-only. Report concrete findings and validation."
18
- ftown sessions screen <worker-id> --limit 100
19
- ftown sessions running <worker-id>
23
+ ftown board --wait --until any-done --timeout 300
24
+ ftown board
20
25
  ftown harness mail send <worker-id> --type task "Check the expired-token case too"
21
- ftown sessions tell --children "Pause and report status"
22
26
  ```
23
27
 
24
- Use terminal output to investigate a quiet worker. Mail handles results and
25
- escalations; read them with `ftown harness mail read`. When waiting, let incoming
26
- mail resume the conversation rather than repeatedly polling. Parallel tasks need
27
- disjoint write ownership and serialized shared resources.
28
+ Coordinate in a loop: spawn children, wait with
29
+ `ftown board --wait --until any-done --timeout 300` (or `any-blocked`,
30
+ `any-ready`, `all-done`; `ftown events --wait` waits for the next event), act
31
+ on the board and mail, then repeat until every child is done. Children keep
32
+ their cards current; progress goes on the card, never as mail. Use
33
+ `ftown sessions screen <worker-id> --limit 100` only to investigate terminal
34
+ detail. Mail handles directed instructions, results and escalations; read it
35
+ with `ftown harness mail read`. Each new batch nudges the recipient at most
36
+ once, and a nudge may append a one-line digest of child status. Parallel tasks
37
+ need disjoint write ownership and serialized shared resources.
28
38
 
29
39
  After accepting a worker's result, request any concrete follow-up before
30
40
  explicit cleanup with `ftown sessions remove <worker-id>`.
@@ -0,0 +1,177 @@
1
+ # Quota-aware routing
2
+
3
+ Read `../SKILL.md` for identity and reporting. This reference covers choosing the
4
+ CLI, model and effort for delegated work. The owner sets up tiers; you choose.
5
+ `ftown quota` reports data and never chooses for you.
6
+
7
+ ## When to use
8
+
9
+ - Before `ftown sessions create` for a child whose harness you are free to pick.
10
+ - When a worker stopped on a usage limit, before re-routing its task.
11
+ - Not needed when the user named the harness or model. Keep their choice.
12
+
13
+ ## What to run
14
+
15
+ ```bash
16
+ ftown quota --json # full report
17
+ ftown quota claude codex --json # only the named harnesses
18
+ ftown quota --json --no-models # skip model lists (picks become unchecked)
19
+ ftown quota --json --timeout 30 # per-harness limit, 1-600 s, default 15
20
+ ```
21
+
22
+ Harnesses: `claude`, `codex`, `grok`, `muse`, `agy`, `opencode`. If `ftown` is
23
+ not on PATH, use `~/.ftown/ftown quota`. `--models` only affects table mode.
24
+
25
+ A run takes several seconds. It launches hidden local CLIs to read their usage
26
+ and model lists. It makes no model turns. The numbers describe the owner's
27
+ subscription plans (5-hour and weekly windows), not API spend.
28
+
29
+ ## Decision procedure
30
+
31
+ The owner configures tiers in the dashboard ("Model routing..." on a bridge).
32
+ Each tier has a `name`, a `description` of the work it is for, an ordered list of
33
+ `picks` (CLI + model + effort), and free-text `guidance`. The report also has a
34
+ global free-text `steering`.
35
+
36
+ 1. Read `routingConfig.status`. If it is not `ok`, see "When things are missing".
37
+ 2. Match the task to a tier by the tier `description`s. Pick the closest one.
38
+ 3. Walk that tier's `picks` in order. Skip any pick whose `problem` is not null:
39
+ the model is gone or the effort is unsupported.
40
+ 4. Weigh each remaining pick: `availability`, `leftPercent` (null when `unknown`),
41
+ `resetsAt` (null when `unknown`), and `scopedLimits`. A scoped limit (for example
42
+ one model family) can be tighter than the plan-wide figure. Scopes usually name a
43
+ model family that appears in the model id or name, so matching a scope to a pick
44
+ is your judgment; when several match use the one with most used.
45
+ 5. `checked: false` means the harness's model list could not be read, so the pick
46
+ was not verified. It is usable with care: expect a possible launch failure.
47
+ 6. Apply the tier's `guidance` and the global `steering`. They are the owner's
48
+ instructions and outrank the pick order when they conflict. Example: if
49
+ steering says "don't fully exhaust Claude", skip a Claude pick that is `low`
50
+ even when it is first.
51
+ 7. Guidance is free text and may name a class, such as "otherwise any free
52
+ opencode model". Resolve it against `models`: filter that provider's
53
+ `models[].models[]` by `pricing` and choose one that fits the task. If that
54
+ provider's list is empty you cannot resolve the class, so fall back to `routing[]`
55
+ and say so. `pricing` is `free` (zero-priced), `included` (covered by a plan the
56
+ owner holds) or `paid` (billed per use). It is omitted when unknown.
57
+ 8. Launch the choice (next section). Report to the user which tier and pick you
58
+ used, and why if you departed from the pick order.
59
+
60
+ ## Availability values
61
+
62
+ Each pick carries its provider's `availability`. The same values appear in `routing[]`.
63
+
64
+ - `available`: enough left for normal work. Use it.
65
+ - `low`: `leftPercent` below 15 in the binding (tightest) window, or `runway`
66
+ below 0.5 when `runway` is not null. Use only for small tasks, when guidance
67
+ allows it, or when nothing `available` fits.
68
+ - `exhausted`: `leftPercent` 1 or less. Never use it before `resetsAt`.
69
+ - `unknown`: no usable numbers. It is not proof of empty or full quota. It ranks
70
+ below `low` and above `exhausted`; use it as a fallback after `available` and
71
+ `low`, and say so.
72
+
73
+ If a window resets within 10 minutes it is reported as `available` with `leftPercent`
74
+ 100 and `runway` null, even if `usedPercent` was high. `routing[]` is ordered best
75
+ first as available, then low, then unknown, then exhausted.
76
+
77
+ `leftPercent` is percent left in the binding (tightest) window. `windows[].usedPercent`
78
+ in `providers` is percent used. Do not mix them up.
79
+
80
+ ## Turning a pick into a launch
81
+
82
+ Find the provider's entry in `models`. It gives `modelFlag` (for example
83
+ `--model <id>`) and `effortFlag` (for example `--effort <level>`, or null when
84
+ the harness has no effort flag). Use the pick's `model` as the id, verbatim, and
85
+ its `effort` as the level. `effort: null` means omit the effort flag.
86
+
87
+ opencode takes effort as a `#variant` suffix on the model, for example
88
+ `provider/model#high`. Check its `effortFlag` and the model's `efforts` in the report.
89
+
90
+ ```bash
91
+ ftown sessions create --shell <provider> --model <id> --parent \
92
+ --workdir "$PWD" --name review-auth --prompt "..."
93
+ ```
94
+
95
+ Where `ftown sessions create` has no flag for the effort, apply `effortFlag` as
96
+ the harness documents it. Omit `--model` to take the harness default.
97
+
98
+ ## When things are missing
99
+
100
+ - `routingConfig.status` is `missing`: the owner has set no tiers. `tiers` is
101
+ empty. Walk `routing[]` in order (already best first: available, low, unknown,
102
+ exhausted): take the first `available`, else `low` only for small tasks, else
103
+ `unknown` with disclosure, and never use `exhausted` before its `resetsAt`.
104
+ Pick a model from `models` and tell the user no tiers are configured.
105
+ - `routingConfig.status` is `invalid`: `routingConfig.errors` lists the problems.
106
+ Treat it like `missing`, and tell the user the file at `routingConfig.path`
107
+ needs fixing. Still honor `steering` if it is non-empty.
108
+ - An empty tier (no `picks`): use its `guidance` if it has any; otherwise fall
109
+ back to `routing[]`.
110
+ - Every pick unusable (all have a `problem`, or are `exhausted`): follow the
111
+ guidance if it names an alternative. Otherwise try the next closest tier, then
112
+ `routing[]`. If nothing is usable, stop and ask the user rather than launching
113
+ on an exhausted plan.
114
+ - A provider with `status` other than `ok` (`no_data`, `auth`, `unavailable`,
115
+ `error`): read its `message`. Its routing is `unknown`. For `auth`, tell the
116
+ user what to fix. A `models` entry with status other than `ok` has an empty
117
+ `models` list, so its picks are `checked: false`.
118
+ - opencode may expose no plan usage at all, so when it reports no windows its
119
+ availability will be `unknown`. That is expected when it happens. Judge it by
120
+ `pricing` (`free` models are safe to use) and by the owner's guidance, not by
121
+ quota.
122
+
123
+ ## JSON field reference
124
+
125
+ | Field | Meaning |
126
+ |---|---|
127
+ | `generatedAt` | ISO time of the report |
128
+ | `providers[]` | raw readings: `provider`, `status`, `plan`, `windows[]`, `source`, `observedAt`, `message`, optional `scopedLimits[]` |
129
+ | `providers[].windows[]` | `kind` (`five_hour`/`weekly`), `usedPercent` (used, may be null), `resetsAt`, `windowMinutes`, `note` |
130
+ | `scopedLimits[]` | partial limit: `scope` (e.g. a model family), `kind`, `usedPercent`, `resetsAt` |
131
+ | `routing[]` | per provider, best first: `provider`, `availability`, `leftPercent`, `bindingWindow`, `resetsAt`, `resetsInMinutes`, `runway` (1 = on pace, >1 spare, <1 will run out early, null when length or reset unknown), `reason` |
132
+ | `models[]` | per provider: `provider`, `status` (`ok`/`unavailable`/`error`; empty `models` unless `ok`), `models[]`, `source` (`live`/`cached`), `observedAt`, `message`, `modelFlag`, `effortFlag` |
133
+ | `models[].models[]` | `id`, `name`, `isDefault`, `description`, `contextTokens`, `efforts[]`, `defaultEffort`, optional `pricing` |
134
+ | `routingConfig` | `status` (`ok`/`missing`/`invalid`), `path`, `errors[]` |
135
+ | `tiers[]` | `name`, `description`, `picks[]`, `guidance`. Empty unless status is `ok` |
136
+ | `tiers[].picks[]` | `provider`, `model`, `effort`, `modelName`, `availability`, `leftPercent`, `resetsAt`, `scopedLimits[]`, `problem`, `checked` |
137
+ | `steering` | global owner instructions, verbatim, `""` when none |
138
+
139
+ Trimmed example (invented values):
140
+
141
+ ```json
142
+ {
143
+ "routingConfig": { "status": "ok", "path": "/home/u/.ftown/model-routing.json", "errors": [] },
144
+ "steering": "Don't fully exhaust Claude.",
145
+ "tiers": [
146
+ {
147
+ "name": "complex",
148
+ "description": "Hard development and debugging",
149
+ "guidance": "Otherwise any free opencode model.",
150
+ "picks": [
151
+ { "provider": "claude", "model": "model-a", "effort": "high", "modelName": "Model A",
152
+ "availability": "low", "leftPercent": 9, "resetsAt": "2026-01-05T00:00:00Z",
153
+ "scopedLimits": [], "problem": null, "checked": true },
154
+ { "provider": "codex", "model": "model-b", "effort": "medium", "modelName": null,
155
+ "availability": "available", "leftPercent": 70, "resetsAt": "2026-01-06T00:00:00Z",
156
+ "scopedLimits": [], "problem": null, "checked": true }
157
+ ]
158
+ }
159
+ ],
160
+ "models": [
161
+ { "provider": "codex", "status": "ok", "modelFlag": "--model <id>", "effortFlag": "--effort <level>",
162
+ "models": [ { "id": "model-b", "efforts": ["low", "medium", "high"], "pricing": "included" } ] }
163
+ ]
164
+ }
165
+ ```
166
+
167
+ Here the first pick is `low` and steering says not to exhaust Claude, so choose
168
+ the second pick: `codex`, `model-b`, effort `medium`.
169
+
170
+ ## Limits
171
+
172
+ - Grok has only a weekly window. `FTOWN_QUOTA_GROK_NO_REFRESH=1` skips its hidden
173
+ refresh and uses the last logged value.
174
+ - opencode lists only its subscription providers by default. Override with
175
+ `FTOWN_QUOTA_OPENCODE_PROVIDERS=a,b`.
176
+ - The tool does not rank models by capability. The owner's tiers and your
177
+ judgment decide.
@@ -51,6 +51,43 @@ ftown sessions archive
51
51
  ftown sessions revive <session-id>
52
52
  ```
53
53
 
54
+ ## Status, board and events
55
+
56
+ Each session has a card: state (`working`, `blocked`, `waiting_review`,
57
+ `done`, `failed`), headline, optional progress, blocker and next step. Keep
58
+ your own card current at meaningful transitions, not every turn. Writing a
59
+ card wakes nobody. The target defaults to `FTOWN_SESSION_ID`; `--session-id
60
+ <id>` selects another session.
61
+
62
+ ```bash
63
+ # Print my card
64
+ ftown status [--json] [--session-id <id>]
65
+
66
+ # Update my card (omitted fields unchanged; first write needs --state and --headline)
67
+ ftown status set [--state <s>] [--headline <t>] [--progress <d>/<t>|--no-progress] \
68
+ [--on human|parent|external|session:<id>] [--text <t>] [--next <t>|--no-next] [--session-id <id>]
69
+
70
+ # Remove my card
71
+ ftown status clear [--session-id <id>]
72
+
73
+ # One line per child of my tree
74
+ ftown board [--json] [--since <n>] [--ready] [--all] [--session-id <id>]
75
+
76
+ # Wait until a board condition holds
77
+ ftown board [--wait --until any-change|any-done|any-blocked|all-done|any-ready [--timeout <s≤300>]] [--session-id <id>]
78
+
79
+ # Tree events for my tree (status, attention, pr, preview, exit, idle, result, mail)
80
+ ftown events [--json] [--since <n>] [--limit <n>] [--wait [--timeout <s≤300>]] [--session-id <id>]
81
+ ```
82
+
83
+ `--ready` lists only children whose PR checks passed and who are not done;
84
+ `--all` includes archived/completed children; `--since <n>` limits the board
85
+ to children with events after that cursor (the board prints its cursor).
86
+ Pass the cursor back as `--since` to see only what changed.
87
+
88
+ Exit codes for `status`, `board` and `events`: 0 ok, 1 error, 2 usage,
89
+ 3 wait timed out with the condition unmet.
90
+
54
91
  ## Preview session files
55
92
 
56
93
  ```bash
@@ -213,8 +250,12 @@ reports the conversation id and delivers mail at turn end.
213
250
  Grok and Antigravity hooks do not run in the interactive TUI until the folder
214
251
  has been trusted in that CLI's own trust prompt; the owner answers it once per
215
252
  folder, and ftown does not bypass it.
216
- Cursor and shell sessions rely on an idle one-line nudge to run `ftown harness
217
- mail read` instead.
253
+ Cursor and shell sessions rely on a one-line nudge to run `ftown harness
254
+ mail read` instead. Each new batch of mail nudges the recipient at most once;
255
+ un-read mail stays in the inbox. Mail types are `message`, `task`, `result`,
256
+ `escalation` and `fyi`; `fyi` is for non-urgent notes and never wakes the
257
+ recipient. Progress and status go on the session card (see above), never as
258
+ mail.
218
259
 
219
260
  ```bash
220
261
  # Tell a specific session
@@ -265,7 +306,8 @@ ftown sessions list
265
306
  ftown sessions create --prompt "Run tests and report" --workdir "$PWD" --parent
266
307
  # -> note session.id from JSON
267
308
 
268
- ftown sessions screen <child-id> --limit 100
309
+ ftown board --wait --until any-done --timeout 300
310
+ ftown board
269
311
  ftown sessions grep <child-id> --pattern 'FAIL|Error'
270
312
  ```
271
313