@cullumco/cadence 0.1.3 → 0.1.5
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +114 -66
- package/dist/cadence.js +58 -17
- package/dist/cli.js +216 -23
- package/dist/config.js +76 -0
- package/dist/hook.js +37 -18
- package/dist/inject.js +21 -3
- package/dist/posttool.js +57 -4
- package/dist/providers/activity.js +45 -4
- package/dist/providers/{ambient.js → environment.js} +25 -5
- package/dist/providers/esoteric.js +77 -0
- package/dist/providers/git.js +4 -3
- package/dist/providers/intent.js +37 -0
- package/dist/providers/moon.js +51 -0
- package/dist/providers/music.js +15 -3
- package/dist/providers/selfreport.js +6 -1
- package/dist/providers/spotify.js +142 -0
- package/dist/session-start.js +38 -8
- package/dist/signals-view.js +40 -8
- package/dist/spotify-auth.js +136 -0
- package/dist/stop.js +9 -6
- package/dist/types.js +1 -0
- package/dist/vibe.js +10 -6
- package/package.json +2 -2
- package/skills/pause/SKILL.md +16 -0
- package/skills/resume/SKILL.md +12 -0
- package/skills/setup/SKILL.md +43 -0
- package/skills/state/SKILL.md +3 -3
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cadence",
|
|
3
3
|
"displayName": "Cadence",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"description": "Ambient context for Claude Code: embodied signals, cadence dials, and finish-line guardrails. macOS-only alpha.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Cullum&Co",
|
|
8
8
|
"url": "https://cullum.co"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://
|
|
10
|
+
"homepage": "https://cadence.cullum.co/",
|
|
11
11
|
"repository": "https://github.com/cullumco/cadence",
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"keywords": [
|
package/README.md
CHANGED
|
@@ -9,10 +9,12 @@ listening to, what you told it, how you want it to respond — into every prompt
|
|
|
9
9
|
then asks Claude to *read your prompt through that lens*. The agent stops being
|
|
10
10
|
deaf to the room.
|
|
11
11
|
|
|
12
|
-
**
|
|
13
|
-
|
|
12
|
+
**Built for the Mac (alpha).** The richest ambient signals read the Mac around
|
|
13
|
+
you. Other platforms still get the dial-movers — prompt intent, self-report,
|
|
14
|
+
git, time/day, typing tempo — and can link Spotify for music
|
|
15
|
+
(`cadence spotify connect`).
|
|
14
16
|
|
|
15
|
-
A [Cullum&Co](https://cullum.co) project · [
|
|
17
|
+
A [Cullum&Co](https://cullum.co) project · [cadence.cullum.co](https://cadence.cullum.co)
|
|
16
18
|
|
|
17
19
|
## What it does
|
|
18
20
|
|
|
@@ -21,8 +23,8 @@ Before Claude sees your prompt, Cadence injects a `<user_state>` block:
|
|
|
21
23
|
```
|
|
22
24
|
<user_state>
|
|
23
25
|
signals:
|
|
24
|
-
music: "
|
|
25
|
-
vibe:
|
|
26
|
+
music: "You Fail Me" — Converge (Spotify)
|
|
27
|
+
vibe: aggressive, energetic
|
|
26
28
|
self_report: "two beers, shipping"
|
|
27
29
|
cadence: # inferred from signals, advisory
|
|
28
30
|
{ pace=fast tone=warm posture=decisive proactivity=act-freely }
|
|
@@ -45,17 +47,20 @@ defers to what you actually typed.
|
|
|
45
47
|
options, a trade-off table, and a closing "Would you like me to implement one
|
|
46
48
|
of these?"
|
|
47
49
|
|
|
48
|
-
**With Cadence, shipping cadence** — hardcore at 3 commits/hr,
|
|
49
|
-
`"ship mode"` →
|
|
50
|
-
|
|
51
|
-
the diff, tests
|
|
50
|
+
**With Cadence, shipping cadence** — hardcore at 3 commits/hr, a "let's ship
|
|
51
|
+
it" earlier in your prompt (or `cadence report "ship mode"`) →
|
|
52
|
+
`{ pace=fast posture=decisive proactivity=act-freely }`. You get the call,
|
|
53
|
+
made: exponential backoff with jitter, three attempts, here's the diff, tests
|
|
54
|
+
pass.
|
|
52
55
|
|
|
53
|
-
**With Cadence, thinking cadence** — ambient music,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
**With Cadence, thinking cadence** — ambient music, "thinking through
|
|
57
|
+
tradeoffs" in your own words → `{ pace=deliberate posture=exploratory }`. You
|
|
58
|
+
get the options laid out patiently, trade-offs actually explored, no pressure
|
|
59
|
+
to pick one yet.
|
|
57
60
|
|
|
58
|
-
Same words. The room around them changed, and the agent finally saw it.
|
|
61
|
+
Same words. The room around them changed, and the agent finally saw it. No
|
|
62
|
+
setup required for the intent read — your prompt itself is a signal; a
|
|
63
|
+
deliberate self-report just outranks it.
|
|
59
64
|
|
|
60
65
|
## How it works
|
|
61
66
|
|
|
@@ -75,13 +80,19 @@ Same words. The room around them changed, and the agent finally saw it.
|
|
|
75
80
|
the hook payload: `activity: { min_since_prompt=45 prompt_len=123 }`.
|
|
76
81
|
- **music** — what's playing (via macOS now-playing, any player), turned into
|
|
77
82
|
a clean *vibe* (mood words) via [MusicBrainz](https://musicbrainz.org). No
|
|
78
|
-
Spotify login, no API key, no Premium.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
Spotify login, no API key, no Premium on macOS. Off the Mac, link Spotify
|
|
84
|
+
once with `cadence spotify connect` (browser OAuth, opt-in). Music moves
|
|
85
|
+
three dials — energy → pace + posture, organic texture → warm tone.
|
|
86
|
+
- **self-report** — what you tell it: `cadence report "two beers, shipping"`.
|
|
87
|
+
- **intent** — read from the prompt you just typed: "let's ship this" →
|
|
88
|
+
decisive/act-freely, "help me debug" → verify-first. Cross-platform, no
|
|
89
|
+
setup; this is what makes the same prompt read differently per room.
|
|
90
|
+
|
|
91
|
+
Time/day, self-report, git, and prompt intent move the dials (git reads
|
|
92
|
+
*what you're doing*: 3+ commits/hr → fast pace, mid-conflict → verify-first);
|
|
93
|
+
the rest render as context the agent reads (flavor). Self-report outranks
|
|
94
|
+
prompt intent outranks git — your deliberate "I'm shipping" beats a stray
|
|
95
|
+
"ship" in a prompt, which beats a mid-conflict read.
|
|
85
96
|
2. **Dials** — four independent knobs, each `low | medium | high`, inferred from
|
|
86
97
|
the signals (or pinned by you):
|
|
87
98
|
- **pace** — deliberate ↔ fast
|
|
@@ -92,17 +103,20 @@ Same words. The room around them changed, and the agent finally saw it.
|
|
|
92
103
|
*read* your prompt. Generated fresh each time; always ends "if my words
|
|
93
104
|
clearly mean otherwise, follow my words."
|
|
94
105
|
|
|
95
|
-
The dials
|
|
106
|
+
The dials move mostly independently — high-energy-but-mellow music reads as
|
|
96
107
|
"fast pace, warm tone," something a single ship/think/debug label could never
|
|
97
|
-
express.
|
|
108
|
+
express. Music is the deliberate exception: it moves pace, posture, and tone
|
|
109
|
+
together (you move *with* the music) but never proactivity — whether to act
|
|
110
|
+
without checking in stays your call, not the soundtrack's.
|
|
98
111
|
|
|
99
112
|
## Requirements
|
|
100
113
|
|
|
101
|
-
- **
|
|
102
|
-
now-playing
|
|
103
|
-
Mac around you. On other platforms
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
- **Built for the Mac.** The richest ambient probes — music via AppleScript
|
|
115
|
+
now-playing, battery, dark mode, displays, wifi, Focus/DND, focused app —
|
|
116
|
+
read the Mac around you. On other platforms Cadence still runs and still
|
|
117
|
+
moves the dials: prompt intent, self-report, git, time/day, and typing tempo
|
|
118
|
+
work anywhere, Spotify can be linked for music, and the Mac-only probes
|
|
119
|
+
degrade silently.
|
|
106
120
|
- **Node 20+**
|
|
107
121
|
- Claude Code for the alpha adapter
|
|
108
122
|
|
|
@@ -114,15 +128,20 @@ In Claude Code:
|
|
|
114
128
|
/plugin marketplace add cullumco/cadence
|
|
115
129
|
/plugin install cadence@cadence
|
|
116
130
|
/reload-plugins
|
|
117
|
-
/cadence:
|
|
131
|
+
/cadence:setup
|
|
118
132
|
```
|
|
119
133
|
|
|
120
|
-
|
|
134
|
+
`/cadence:setup` is a short conversation, not a wizard — tell Claude how you
|
|
135
|
+
work, pick which signals you're willing to share, and see exactly what gets
|
|
136
|
+
injected. Or skip it and just set a self-report:
|
|
121
137
|
|
|
122
138
|
```text
|
|
123
139
|
/cadence:state shipping, locked in
|
|
124
140
|
```
|
|
125
141
|
|
|
142
|
+
Change your mind anytime: `/cadence:pause` silences everything instantly,
|
|
143
|
+
`/cadence:resume` brings it back.
|
|
144
|
+
|
|
126
145
|
Alpha testers running from source — while `@cullumco/cadence` is pending npm
|
|
127
146
|
publish — see [`ALPHA.md`](ALPHA.md).
|
|
128
147
|
|
|
@@ -138,14 +157,22 @@ track, looks the artist's vibe up on MusicBrainz once, and caches it forever at
|
|
|
138
157
|
`~/.cadence/vibe-cache.json`. If nothing's playing, the music signal is simply
|
|
139
158
|
absent.
|
|
140
159
|
|
|
160
|
+
**Off macOS?** Run `cadence spotify connect <clientId>` once: register a Spotify
|
|
161
|
+
app (add `http://127.0.0.1:8888/callback` as a redirect URI), and Cadence does
|
|
162
|
+
the browser OAuth and stores a refresh token. From then on it reads your
|
|
163
|
+
currently-playing track cross-platform — identity only, vibe still from
|
|
164
|
+
MusicBrainz, no audio-features.
|
|
165
|
+
|
|
141
166
|
## Daily use
|
|
142
167
|
|
|
143
168
|
```bash
|
|
144
|
-
cadence
|
|
145
|
-
cadence
|
|
169
|
+
cadence report "two beers, shipping" # set self-reported state (expires in 2h)
|
|
170
|
+
cadence report # print current self-report
|
|
146
171
|
cadence clear # clear it
|
|
147
172
|
cadence test # preview exactly what the hook would inject
|
|
148
173
|
cadence signals # every signal — live value, or why it's absent
|
|
174
|
+
cadence pause # silence all hooks (state survives untouched)
|
|
175
|
+
cadence resume # start reading the room again
|
|
149
176
|
```
|
|
150
177
|
|
|
151
178
|
`cadence signals` is the legibility view: it never goes silent. Every signal
|
|
@@ -153,13 +180,23 @@ Cadence knows how to read is listed with its live value, or the exact reason
|
|
|
153
180
|
it's absent — opt-in not taken, below a render threshold, missing permission
|
|
154
181
|
(Focus needs Full Disk Access), or platform-gated.
|
|
155
182
|
|
|
156
|
-
From inside Claude Code, the plugin
|
|
183
|
+
From inside Claude Code, the plugin skills cover the same ground without
|
|
184
|
+
leaving the conversation:
|
|
157
185
|
|
|
158
186
|
```text
|
|
187
|
+
/cadence:setup # guided, conversational setup — shape the
|
|
188
|
+
# influence and pick your opt-in signals
|
|
159
189
|
/cadence:state two beers, shipping
|
|
160
|
-
/cadence:try
|
|
190
|
+
/cadence:try # what is Cadence seeing right now?
|
|
191
|
+
/cadence:pause # instant silence — prompts go through untouched
|
|
192
|
+
/cadence:resume # back to reading the room
|
|
161
193
|
```
|
|
162
194
|
|
|
195
|
+
`/cadence:setup` is the recommended first run inside Claude Code: instead of a
|
|
196
|
+
fixed wizard, you tell Claude how you work in plain language and it drives the
|
|
197
|
+
CLI for you — state, dial pins, and which opt-in signals you're willing to
|
|
198
|
+
share.
|
|
199
|
+
|
|
163
200
|
### Driving the dials by hand
|
|
164
201
|
|
|
165
202
|
The dials are inferred, but you can pin any of them — your pin wins, the rest
|
|
@@ -200,52 +237,63 @@ state through other agent surfaces.
|
|
|
200
237
|
|
|
201
238
|
## Alpha release checklist
|
|
202
239
|
|
|
203
|
-
|
|
240
|
+
`@cullumco/cadence` is live on npm; each release is the same gated flow:
|
|
204
241
|
|
|
205
242
|
```bash
|
|
206
|
-
npm run verify:alpha
|
|
207
|
-
npm publish
|
|
208
|
-
|
|
243
|
+
npm run verify:alpha # build + plugin validate + tests + dry-pack + install smoke test
|
|
244
|
+
npm run release:alpha # the full gate, then npm publish
|
|
245
|
+
# then: write .github/releases/vX.Y.Z.md and dispatch the Release workflow —
|
|
246
|
+
# it creates the tag + GitHub Release from that file
|
|
247
|
+
gh workflow run Release -f tag=vX.Y.Z -f target=<bump-commit>
|
|
209
248
|
```
|
|
210
249
|
|
|
211
|
-
|
|
212
|
-
`
|
|
213
|
-
|
|
214
|
-
the canonical install at the top of this README starts working end-to-end.
|
|
215
|
-
|
|
216
|
-
A GitHub Actions workflow exists at `.github/workflows/alpha.yml` but is
|
|
217
|
-
currently disabled — re-enable with `gh workflow enable Alpha` when you want
|
|
218
|
-
the gate to run on every push to `main`.
|
|
250
|
+
CI runs the same gate on every push/PR to `main`
|
|
251
|
+
(`.github/workflows/verify.yml`); `.github/workflows/release.yml` turns each
|
|
252
|
+
release into a tag + GitHub Release whose body is the committed notes file.
|
|
219
253
|
|
|
220
254
|
## What's next
|
|
221
255
|
|
|
222
256
|
See [`BACKLOG.md`](BACKLOG.md). Highlights:
|
|
223
257
|
|
|
224
|
-
- **Git nudges** — the
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
258
|
+
- **Git nudges** — *shipped:* they move the dials from *what you said* to *what
|
|
259
|
+
you're actually doing* (`3+ commits/hr → fast pace`, `mid-conflict →
|
|
260
|
+
verify-first`), applied below self-report so your explicit word still wins.
|
|
261
|
+
- **Prompt intent** — *shipped:* ship/think/debug read straight from the prompt
|
|
262
|
+
you just typed, so the "same prompt, different room" behavior fires without a
|
|
263
|
+
separate `cadence report` step.
|
|
264
|
+
- **Opt-in signals** — anything privacy-adjacent stays off until you turn it on
|
|
265
|
+
(`cadence enable <signal>`):
|
|
266
|
+
- **typing tempo** — *shipped (opt-in):* prompt rhythm beyond length —
|
|
267
|
+
rapid-fire short prompts read fast, one long considered prompt reads
|
|
268
|
+
deliberate.
|
|
269
|
+
- **focused app** — *shipped (opt-in, macOS):* the frontmost non-terminal app
|
|
270
|
+
(a browser, Slack, a PDF) renders as flavor. Read at prompt-submit, so it
|
|
271
|
+
only speaks when something other than your terminal/IDE is genuinely in
|
|
272
|
+
front. Flavor for now; a dial nudge stays a candidate.
|
|
273
|
+
- **esoteric flavor** — *shipped (opt-in):* `moon` phase (computed offline)
|
|
274
|
+
and a daily `horoscope` for your sign. Render-only — they color the room,
|
|
275
|
+
they never steer the work.
|
|
233
276
|
- **deeper Focus** — manual + scheduled Focus detection ship now; geofenced/
|
|
234
277
|
iPhone-synced Focus leaves no local trace and stays undetectable.
|
|
235
|
-
- **
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
- **Calendar density** — intentionally *not* built: Cadence targets solo
|
|
279
|
+
builders deep in a project, not people racing between meetings.
|
|
280
|
+
- **After-the-fact injection** — shipped: a `PostToolUse` hook watches git-ish
|
|
281
|
+
commands and speaks once per transition — entering/leaving a merge/rebase
|
|
282
|
+
conflict, and destructive-op thrash (reset --hard streaks, force-pushes).
|
|
283
|
+
Next material event: failing-test transitions.
|
|
240
284
|
|
|
241
285
|
## Caveats
|
|
242
286
|
|
|
243
|
-
- **
|
|
244
|
-
displays, wifi,
|
|
245
|
-
self-report
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
287
|
+
- **Built for the Mac.** The richest ambient probes (music-via-OS, battery,
|
|
288
|
+
dark mode, displays, wifi, Focus, focused app) are macOS. Other platforms
|
|
289
|
+
keep the dial-movers — intent, self-report, git, time/day, typing tempo,
|
|
290
|
+
linked Spotify — and the rest degrades silently.
|
|
291
|
+
- **Spotify's audio-features API is not used** — Spotify deprecated it for new
|
|
292
|
+
apps (2024) and gated dev-mode behind Premium (2026), so vibe comes from
|
|
293
|
+
MusicBrainz, not Spotify. On macOS, Cadence reads what's playing at the OS
|
|
294
|
+
level (no Spotify account at all). The only Spotify API call is the still-live
|
|
295
|
+
`currently-playing` endpoint, and only if you opt in via `cadence spotify` to
|
|
296
|
+
get music off the Mac — identity only, never audio-features.
|
|
249
297
|
|
|
250
298
|
## License
|
|
251
299
|
|
package/dist/cadence.js
CHANGED
|
@@ -18,9 +18,10 @@ const LEVELS = ["low", "medium", "high"];
|
|
|
18
18
|
*
|
|
19
19
|
* Signals you can read (any subset present):
|
|
20
20
|
* report { text } ← you said it; trust it most
|
|
21
|
+
* intent { kind } ← read from the prompt you just typed
|
|
21
22
|
* music { vibe, energy } ← energy 0–1 drives pace; vibe colors tone
|
|
22
|
-
* git { commitsLastHour, ... } ← work rhythm
|
|
23
|
-
* activity { minSinceLastPrompt, promptLength }
|
|
23
|
+
* git { commitsLastHour, ... } ← work rhythm
|
|
24
|
+
* activity { minSinceLastPrompt, promptLength, tempo }
|
|
24
25
|
*
|
|
25
26
|
* A working baseline is below so it runs end-to-end. The mapping is yours.
|
|
26
27
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
@@ -35,8 +36,9 @@ export function deriveCadence(state) {
|
|
|
35
36
|
const music = state.signals.find((s) => s.source === "music");
|
|
36
37
|
const report = state.signals.find((s) => s.source === "self_report");
|
|
37
38
|
const git = state.signals.find((s) => s.source === "git");
|
|
39
|
+
const intent = state.signals.find((s) => s.source === "intent");
|
|
38
40
|
const activity = state.signals.find((s) => s.source === "activity");
|
|
39
|
-
const
|
|
41
|
+
const environment = state.signals.find((s) => s.source === "environment");
|
|
40
42
|
// Start neutral; each signal nudges individual dials.
|
|
41
43
|
const c = {
|
|
42
44
|
pace: "medium",
|
|
@@ -44,31 +46,40 @@ export function deriveCadence(state) {
|
|
|
44
46
|
posture: "medium",
|
|
45
47
|
proactivity: "medium",
|
|
46
48
|
};
|
|
47
|
-
// ──
|
|
49
|
+
// ── environment → soft nudges FIRST (weakest), so stronger signals below win ──
|
|
48
50
|
// Atmosphere, not orders: it colors the default, then music/self-report/git
|
|
49
51
|
// can override. "It's late" shouldn't beat "I'm shipping."
|
|
50
|
-
if (
|
|
51
|
-
if (
|
|
52
|
+
if (environment) {
|
|
53
|
+
if (environment.hour >= 22 || environment.hour < 6)
|
|
52
54
|
c.pace = "low"; // late → gentler
|
|
53
|
-
if (
|
|
55
|
+
if (environment.partOfDay === "early morning")
|
|
54
56
|
c.pace = "low"; // easing in
|
|
55
|
-
if (
|
|
57
|
+
if (environment.isWeekend)
|
|
56
58
|
c.tone = "low"; // looser on weekends
|
|
57
|
-
if (
|
|
59
|
+
if (environment.weather && /rain|snow|fog|storm|cloud/.test(environment.weather)) {
|
|
58
60
|
c.tone = "low"; // gloomy out → warmer in
|
|
59
61
|
}
|
|
60
|
-
if (
|
|
62
|
+
if (environment.onBattery)
|
|
61
63
|
c.pace = "high"; // mobile/untethered → quick hits
|
|
62
64
|
}
|
|
63
|
-
// ── music
|
|
65
|
+
// ── music → pace + posture + tone (move WITH the music) ───────────────────
|
|
66
|
+
// Deliberately moves three dials, not one: a track has a tempo (pace), an
|
|
67
|
+
// intensity (decisive vs. spacious posture), and a texture (warm tone). It
|
|
68
|
+
// leaves PROACTIVITY alone — whether to act without checking in is the user's
|
|
69
|
+
// call (self-report/intent/git), never the soundtrack's. See CLAUDE.md.
|
|
64
70
|
if (music?.energy != null) {
|
|
65
71
|
if (music.energy >= 0.7)
|
|
66
|
-
c.pace = "high";
|
|
72
|
+
c.pace = "high"; // driving → fast
|
|
67
73
|
else if (music.energy <= 0.4)
|
|
68
|
-
c.pace = "low";
|
|
74
|
+
c.pace = "low"; // mellow → deliberate
|
|
75
|
+
if (music.energy >= 0.75)
|
|
76
|
+
c.posture = "high"; // high intensity → decisive momentum
|
|
77
|
+
else if (music.energy <= 0.35)
|
|
78
|
+
c.posture = "low"; // ambient → spacious, exploratory
|
|
69
79
|
}
|
|
70
|
-
//
|
|
71
|
-
if (music?.
|
|
80
|
+
// organic/acoustic texture, or mellow vibe words, warm the tone
|
|
81
|
+
if ((music?.acoustic != null && music.acoustic >= 0.5) ||
|
|
82
|
+
(music?.vibe && /\b(calm|chilled|ethereal|romantic|warm|sexy)\b/.test(music.vibe))) {
|
|
72
83
|
c.tone = "low";
|
|
73
84
|
}
|
|
74
85
|
// ── git → pace / proactivity (what you're DOING, not what you said) ───────
|
|
@@ -81,6 +92,29 @@ export function deriveCadence(state) {
|
|
|
81
92
|
if (git.conflicted)
|
|
82
93
|
c.proactivity = "low"; // verify, don't barrel
|
|
83
94
|
}
|
|
95
|
+
// ── prompt intent → posture / proactivity / tone (what you JUST typed) ────
|
|
96
|
+
// Read from the live prompt, so the "same prompt, different room" behavior
|
|
97
|
+
// fires without a separate CLI step. Stronger than git (what you're doing),
|
|
98
|
+
// weaker than self-report below (a deliberate, out-of-band declaration), so
|
|
99
|
+
// an explicit `cadence report "thinking"` still beats a stray "ship it".
|
|
100
|
+
if (intent?.kind) {
|
|
101
|
+
if (intent.kind === "ship") {
|
|
102
|
+
c.posture = "high";
|
|
103
|
+
c.proactivity = "high";
|
|
104
|
+
c.pace = "high";
|
|
105
|
+
}
|
|
106
|
+
else if (intent.kind === "think") {
|
|
107
|
+
c.posture = "low";
|
|
108
|
+
c.pace = "low";
|
|
109
|
+
}
|
|
110
|
+
else if (intent.kind === "debug") {
|
|
111
|
+
c.posture = "low";
|
|
112
|
+
c.proactivity = "low";
|
|
113
|
+
}
|
|
114
|
+
else if (intent.kind === "focus") {
|
|
115
|
+
c.tone = "high";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
84
118
|
// ── self-report → posture / proactivity / tone (you know your state) ──────
|
|
85
119
|
if (report) {
|
|
86
120
|
const t = report.text.toLowerCase();
|
|
@@ -103,8 +137,15 @@ export function deriveCadence(state) {
|
|
|
103
137
|
c.tone = "high";
|
|
104
138
|
}
|
|
105
139
|
// Still-dormant candidate nudges (see BACKLOG):
|
|
106
|
-
//
|
|
107
|
-
// ── activity → pace (
|
|
140
|
+
// environment focus on → proactivity high (heads-down = fewer check-ins)
|
|
141
|
+
// ── activity → pace (motor tempo + return-from-break) ─────────────────────
|
|
142
|
+
// typing tempo (opt-in): rapid-fire short prompts read as fast; one long
|
|
143
|
+
// considered prompt reads as deliberate. Only set when the user opted in.
|
|
144
|
+
if (activity?.tempo === "rapid")
|
|
145
|
+
c.pace = "high";
|
|
146
|
+
else if (activity?.tempo === "considered")
|
|
147
|
+
c.pace = "low";
|
|
148
|
+
// a long gap since the last prompt = returning from a break = slow back down.
|
|
108
149
|
if (activity?.minSinceLastPrompt != null && activity.minSinceLastPrompt > 30) {
|
|
109
150
|
c.pace = "low";
|
|
110
151
|
}
|