create-byan-agent 2.29.2 → 2.38.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 (48) hide show
  1. package/CHANGELOG.md +361 -0
  2. package/install/bin/create-byan-agent-v2.js +44 -1
  3. package/install/lib/claude-native-setup.js +12 -38
  4. package/install/lib/gdoc-setup.js +210 -0
  5. package/install/lib/mcp-extensions/gdrive.js +27 -2
  6. package/install/lib/platforms/claude-code.js +28 -19
  7. package/install/lib/rtk-integration.js +18 -8
  8. package/install/package.json +1 -1
  9. package/install/packages/platform-config/lib/credentials.js +13 -1
  10. package/install/packages/platform-config/lib/mcp-config.js +71 -5
  11. package/install/setup-gdoc.js +41 -0
  12. package/install/setup-rtk.js +1 -1
  13. package/install/templates/.claude/CLAUDE.md +16 -4
  14. package/install/templates/.claude/hooks/inject-delivery-default.js +46 -0
  15. package/install/templates/.claude/hooks/inject-soul.js +4 -3
  16. package/install/templates/.claude/hooks/inject-tao.js +65 -25
  17. package/install/templates/.claude/hooks/inject-voice-anchor.js +102 -0
  18. package/install/templates/.claude/hooks/leantime-fd-sync.js +12 -1
  19. package/install/templates/.claude/hooks/lib/delivery-contract.js +143 -0
  20. package/install/templates/.claude/hooks/lib/punt-detect.js +143 -0
  21. package/install/templates/.claude/hooks/punt-guard.js +126 -0
  22. package/install/templates/.claude/hooks/strict-stop-guard.js +29 -6
  23. package/install/templates/.claude/rules/portable-core.md +81 -0
  24. package/install/templates/.claude/settings.json +13 -1
  25. package/install/templates/.claude/skills/byan-hermes-dispatch/SKILL.md +16 -2
  26. package/install/templates/_byan/_config/delivery-default.json +22 -0
  27. package/install/templates/_byan/mcp/byan-mcp-server/channel-entry.js +46 -0
  28. package/install/templates/_byan/mcp/byan-mcp-server/lib/channel-poll.js +128 -0
  29. package/install/templates/_byan/mcp/byan-mcp-server/lib/channel-server.js +234 -0
  30. package/install/templates/_byan/mcp/byan-mcp-server/lib/completeness-evidence.js +159 -0
  31. package/install/templates/_byan/mcp/byan-mcp-server/lib/gdoc-client.js +203 -0
  32. package/install/templates/_byan/mcp/byan-mcp-server/lib/gdoc-content.js +203 -0
  33. package/install/templates/_byan/mcp/byan-mcp-server/lib/leantime-fd-core.js +60 -2
  34. package/install/templates/_byan/mcp/byan-mcp-server/lib/leantime-sync.js +4 -1
  35. package/install/templates/_byan/mcp/byan-mcp-server/lib/precommit-gate.js +68 -2
  36. package/install/templates/_byan/mcp/byan-mcp-server/lib/resolve-config.js +15 -2
  37. package/install/templates/_byan/mcp/byan-mcp-server/lib/strict-mode.js +78 -1
  38. package/install/templates/_byan/mcp/byan-mcp-server/lib/sync-rules.js +1 -1
  39. package/install/templates/_byan/mcp/byan-mcp-server/package.json +2 -0
  40. package/install/templates/_byan/mcp/byan-mcp-server/server.js +70 -0
  41. package/install/templates/_byan/mcp/byan-mcp-server/skill-bundles-manifest.json +1 -1
  42. package/install/templates/dist/skill-bundles/byan-hermes-dispatch.zip +0 -0
  43. package/install/templates/docs/google-docs-publish.md +121 -0
  44. package/install/templates/docs/leantime-integration.md +11 -1
  45. package/node_modules/byan-platform-config/lib/credentials.js +13 -1
  46. package/node_modules/byan-platform-config/lib/mcp-config.js +71 -5
  47. package/package.json +3 -1
  48. package/install/templates/.mcp.json.tmpl +0 -8
package/CHANGELOG.md CHANGED
@@ -9,6 +9,367 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [2.38.0] - 2026-06-30
13
+
14
+ ### Added - yanstaller installs the byan-channel MCP entry by default (inert)
15
+
16
+ `npx create-byan-agent` now registers a second MCP server, `byan-channel`,
17
+ alongside `byan` in the project `.mcp.json`. It is a Claude Code RESEARCH PREVIEW
18
+ channel (v2.1.80+) and ships INERT: registering it enables nothing on its own --
19
+ it is only loaded when the user launches
20
+ `claude --dangerously-load-development-channels server:byan-channel`. No
21
+ `channelsEnabled`, no `allowedChannelPlugins`, no auto-activation flag is written.
22
+ Codex is not covered by this feature.
23
+
24
+ - **Port (read-only from byan_web)**: `channel-entry.js`, `lib/channel-server.js`,
25
+ `lib/channel-poll.js` brought into `_byan/mcp/byan-mcp-server/`. The entry
26
+ resolves its own config at boot via `resolve-config.js` (env ->
27
+ `~/.byan/credentials.json` -> defaults), so no secret is needed in `.mcp.json`.
28
+ - **Wiring -- one shape, every writer**: the byan + byan-channel entry shape has a
29
+ single source of truth, `mcpConfig.mergeByanEntry`, with no `.mcp.json` template
30
+ to drift out of sync. Both writers route through it -- `generateMcpConfig` (the
31
+ primary native-setup path, a pure delegation) and `installDirectMCP` -- so they
32
+ are byte-coherent. Each writes a project-relative path (not absolute, not
33
+ `{{PROJECT_ROOT}}`), preserves an existing custom `command` and non-byan env,
34
+ strips `BYAN_API_URL`/`BYAN_API_TOKEN`, and merges into an existing `.mcp.json`
35
+ without clobbering other servers. Additional default MCP servers are registered
36
+ via `addMcpEntry` (mcp-extensions), not a template.
37
+ - **Portable + secret-free**: relative paths survive a moved / npm-shipped repo;
38
+ the channel env is forced empty; no token shape can land in tracked `.mcp.json`.
39
+ - **Shipped**: the 3 runtime files + 2 tests are in `TARGET_ADDITIONS` and
40
+ mirrored to `install/templates`, parity enforced by `byan-sync-template --check`.
41
+ - **Honest post-install message**: states research-preview, version gate, inert
42
+ default, the activation flag, and that Codex is not covered.
43
+
44
+ Files: `install/packages/platform-config/lib/mcp-config.js` (mergeByanEntry /
45
+ mergeChannelEntry / mergeLeantimeRefs -- relative args, command preserved),
46
+ `install/lib/claude-native-setup.js` (generateMcpConfig is a pure delegation to
47
+ the shared merge), `install/lib/platforms/claude-code.js` (installDirectMCP),
48
+ `install/bin/create-byan-agent-v2.js` (message),
49
+ `_byan/mcp/byan-mcp-server/{channel-entry,lib/channel-server,lib/channel-poll}.js`,
50
+ `_byan/mcp/byan-mcp-server/lib/template-sync.js`,
51
+ `_byan/mcp/byan-mcp-server/test/{channel,channel-resolve}.test.js` (+ install
52
+ templates). Adversarial review (bmad-compliance) caught 3 message/coherence gaps,
53
+ all fixed before merge; a follow-up adversarial pass drove removal of the now-dead
54
+ `.mcp.json.tmpl` so the byan/byan-channel shape has a single source of truth.
55
+ jest root 2473/2473, MCP node --test 723/723.
56
+
57
+ ## [2.37.1] - 2026-06-30
58
+
59
+ ### Fixed - strict-stop-guard false positive on mentioned completion markers
60
+
61
+ `claimsCompletion` matched a completion marker ANYWHERE in the assistant message,
62
+ so a turn was blocked even when a marker was merely MENTIONED, not claimed:
63
+ `complete` inside `byan_strict_complete`, `done` inside the `BYAN-BENCH:done`
64
+ marker comment, or an accented marker embedded in another word (`fini` in
65
+ `indefini`, `termine` in `determine`). Hit repeatedly in one session. Hardened: a
66
+ working copy is denoised first (fenced + inline code, HTML comments, snake_case /
67
+ namespaced identifiers stripped), then markers match only as a standalone claim
68
+ with Unicode-aware boundaries and a permissive trailing inflection (`livre` ->
69
+ `livree` / `livres`). Bias toward fewer false blocks -- the pre-commit gate stays
70
+ the hard net for a real premature completion. Genuine claims (`done`,
71
+ `c'est termine`, `feature livree`, `the build is complete`) still fire. +4
72
+ regression tests.
73
+
74
+ Files: `.claude/hooks/strict-stop-guard.js` (+ install template),
75
+ `_byan/mcp/byan-mcp-server/test/strict-hooks.test.js`. MCP node --test 714/714,
76
+ root jest 2466/2466.
77
+
78
+ ## [2.37.0] - 2026-06-30
79
+
80
+ ### Added - Prod-grade + maximal scope as the mechanical default (anti-downgrade)
81
+
82
+ A 4-lens diagnosis workflow established WHY the agent kept drifting toward MVP /
83
+ half-work despite the whole anti-downgrade arsenal: it was OPT-IN (every guard a
84
+ no-op until a scope is locked), the strict loop was self-judged (agent writes the
85
+ contract, grades its own verdict), and the cost model was anchored on human-2010
86
+ time, generating the MVP-split reflex. This ships the mechanical, default-on fix.
87
+
88
+ - **F1 -- delivery-contract anchor (LIVE).** New `inject-delivery-default.js`
89
+ (UserPromptSubmit) + pure lib `delivery-contract.js` re-inject a contract every
90
+ turn: grade=PROD, scope=MAXIMAL (proposing an MVP / short-deliverable /
91
+ dont-block-the-heavy split is forbidden unless the user types an opt-out word
92
+ THIS message), cost yardstick=AI-2026 (estimate in agent-time x10, not
93
+ human-by-hand). Opt-out wordlist in `_byan/_config/delivery-default.json`. The
94
+ opt-out parser is biased toward PROD: a single opt-out word counts only as a
95
+ short directive or with a go-cheap cue, is dropped when negated ("pas de mvp"),
96
+ and a mere mention in a long meta message leaves the anchor armed. Preserved
97
+ across compaction (CLAUDE.md) and mirrored to AGENTS.md (Codex).
98
+ - **F2 -- non-agent completeness judge (built, DISARMED).** `completeness-evidence.js`
99
+ wired additively into strict `complete()` + the pre-commit gate: a "done" claim
100
+ is backed by a non-fabricable artifact (a real test-runner exit, a `git diff`
101
+ vs the locked paths, a file that exists), breaking the judge=defendant loop.
102
+ Ships behind `completenessGate.armed=false` (collect + ledger only) so it does
103
+ not change behavior or self-lock until the ledger validates arming.
104
+ - **F3 -- punt-guard (built, DISARMED).** Stop hook + pure `punt-detect.js`
105
+ flags handing the user a runnable command the agent could run itself, with a
106
+ creds carve-out (git push / npm publish). Ships behind `puntGuard.armed=false`
107
+ (observe + ledger).
108
+
109
+ Caught in build: F1's first live turn revealed a false positive (the anchor
110
+ self-disabled on any message that merely mentioned an opt-out word). Fixed in
111
+ place with the negation + directive-context hardening above + 3 regression tests.
112
+
113
+ Files: `.claude/hooks/inject-delivery-default.js`, `.claude/hooks/lib/delivery-contract.js`,
114
+ `.claude/hooks/punt-guard.js`, `.claude/hooks/lib/punt-detect.js`,
115
+ `_byan/mcp/byan-mcp-server/lib/completeness-evidence.js`, wired into `strict-mode.js`
116
+ + `precommit-gate.js`, `_byan/_config/delivery-default.json`, `.claude/settings.json`,
117
+ CLAUDE.md + AGENTS.md (+ install templates) + tests. MCP node --test 710/710, root
118
+ jest 2466/2466. Strict scope 9f8c6048.
119
+
120
+ ## [2.36.0] - 2026-06-29
121
+
122
+ ### Added - Leantime auto-sync: complexity, priority and description on task creation
123
+
124
+ The FD->Leantime auto-sync (which already creates tasks and drives the
125
+ todo/doing/review/done lifecycle) now enriches each created task with effort,
126
+ priority and a traceable description -- no manual board work.
127
+
128
+ - **F1 -- pure resolvers + enriched intent** (`leantime-fd-core.js`). Two
129
+ exported pure functions: `priorityToLeantime` (P1/P2/P3 -> 3/2/1, omitted when
130
+ unknown) and `complexityToStorypoints` (a finite `item.complexity` bucketed on
131
+ the Fibonacci scale <=15->2 / 16-39->5 / 40-69->8 / >=70->13, else derived from
132
+ priority P1->8 / P2->5 / P3->3, default 3 -- returns a numeric estimate).
133
+ `decideActions` now emits `{ priority?, storypoints, description }` in the
134
+ `task_create` intent (`description` = `BYAN FD <id> -- <headline>` +
135
+ ` [complexity:N]` when finite).
136
+ - **F2 -- carrier + passthrough** (`leantime-sync.js` + the `leantime-fd-sync`
137
+ hook, source + install template). `createTask` accepts and sends `storypoints`;
138
+ the hook passes `description` / `priority` / `storypoints` through to it. The
139
+ lifecycle sync is untouched.
140
+
141
+ Note: `storypoints` is the presumed Leantime effort field, `[UNVERIFIED]` against
142
+ a live instance (Leantime unreachable at build time). An unknown key is at worst
143
+ ignored by `addTicket` (no break), and the complexity is also carried in the
144
+ description as a fallback. Flagged for a live-verify when reachable.
145
+
146
+ Files: `_byan/mcp/byan-mcp-server/lib/leantime-fd-core.js`, `lib/leantime-sync.js`,
147
+ `.claude/hooks/leantime-fd-sync.js` (+ install template), `test/leantime-fd-core.test.js`
148
+ + `test/leantime-sync.test.js` (+7 tests). MCP node --test 696/696, root jest
149
+ 2441/2441. Strict scope 80405c59.
150
+
151
+ ## [2.35.0] - 2026-06-26
152
+
153
+ ### Added - Portable core / native projection doctrine + degradation litmus
154
+
155
+ BYAN's memory/identity is now governed by an explicit architecture doctrine: a
156
+ portable in-repo core, with native Claude features as opportunistic write-through
157
+ accelerators rather than dependencies.
158
+
159
+ - **F1 -- doctrine.** New `.claude/rules/portable-core.md` (+ install template)
160
+ states the boundary: source of truth lives under `_byan/` (+ byan_web); native
161
+ features (prompt caching, `@-import` memory files, hooks, subagent isolation)
162
+ are write-through accelerators; the native AutoMem
163
+ (`~/.claude/projects/<hash>/memory/`) is explicitly out-of-perimeter
164
+ (per-machine, not shippable, not a BYAN source). Carries a feature -> adapter
165
+ -> degraded-path table. Mirrored to `AGENTS.md` (Codex) and pointed to from
166
+ `.claude/CLAUDE.md` WITHOUT an `@-import` (token budget).
167
+ - **F2 -- degradation litmus.** New `.claude/__tests__/portable-core.test.js`
168
+ makes independence mechanical: identity (soul/tao/soul-memory) reconstructs from
169
+ portable `_byan/` artifacts alone, and no critical read path depends on the
170
+ native AutoMem (regression guard).
171
+
172
+ Origin: a memory-integration audit found BYAN runs a parallel memory stack that
173
+ does not touch the native AutoMem -- largely justified by npm portability, but
174
+ undocumented and drift-prone. This codifies the boundary instead of syncing two
175
+ stores (F3 anti-drift guard cut, YAGNI).
176
+
177
+ Files: `.claude/rules/portable-core.md` (+ template), `AGENTS.md`,
178
+ `.claude/CLAUDE.md` (+ template), `.claude/__tests__/portable-core.test.js`.
179
+ root jest 2441/2441, MCP node --test 689/689. Strict mode: scope 591e6e49.
180
+
181
+ ## [2.34.0] - 2026-06-25
182
+
183
+ ### Changed - Context engineering: do more with less (compaction + subagent isolation)
184
+
185
+ Two context-engineering moves from Anthropic's guidance, applied without touching
186
+ BYAN's persistent identity.
187
+
188
+ - **G2 -- compaction directive.** A `## Compact instructions` section in
189
+ `.claude/CLAUDE.md` tells the compaction what to preserve when a long session is
190
+ summarized: the active FD state (phase, backlog, verdicts -- `_byan-output/fd-state.json`),
191
+ the active Strict Mode session, BYAN's soul/tao voice, and recent commits. It
192
+ complements the `pre-compact-save` PreCompact hook (which writes a file snapshot):
193
+ one says what to keep in-context, the other persists a snapshot to disk.
194
+ - **G3 -- subagent isolation doctrine in hermes.** The dispatcher's worktree path
195
+ already capped the subagent's return; the `mcp-worker` path did not, and the
196
+ isolation principle was unnamed. Both spawn paths now cap the return to a
197
+ distilled summary (< 200 words / ~1-2k tokens) -- verbose tool output and
198
+ intermediate reasoning stay in the subagent's own context. A new "Subagent
199
+ isolation (token leverage)" section + a hard rule codify it (Anthropic: a
200
+ subagent may burn ~9k tokens internally yet return ~1-2k).
201
+
202
+ Files: `.claude/CLAUDE.md` (+ template), `.claude/skills/byan-hermes-dispatch/SKILL.md`
203
+ (+ template), skill-bundles manifest + ZIP rebuilt, `.claude/__tests__/claude-md-context-budget.test.js`.
204
+ root jest 2432/2432, MCP node --test 689/689. Source: Anthropic "Effective context engineering for AI agents".
205
+
206
+ ## [2.33.0] - 2026-06-25
207
+
208
+ ### Changed - Keep BYAN's voice alive on long sessions (tao persistence)
209
+
210
+ Hardens the 2.32.0 tao cache-alignment so BYAN's voice does not fade on a long
211
+ session, without re-introducing the per-turn cost.
212
+
213
+ - **F1 -- heart-survival pinned.** A test (`.claude/__tests__/soul-hooks.test.js`)
214
+ now fails if `inject-tao.js` stops being wired under SessionStart with an
215
+ all-sources matcher (so it keeps re-firing on `source: "compact"`) or stops
216
+ emitting the full tao. Claude Code re-fires SessionStart after each compaction
217
+ (per its docs), re-injecting the full tao then; this test guards that floor
218
+ against a silent refactor.
219
+ - **F2 -- periodic refresh.** `inject-voice-anchor.js` re-injects the FULL tao
220
+ every N turns (N via `BYAN_TAO_REFRESH_EVERY`, default 12); the other turns keep
221
+ the compact anchor. A per-turn counter under `_byan-output/` (gitignored), reset
222
+ at SessionStart by `inject-tao.js`, drives the cadence -- so the voice is
223
+ refreshed close to the live edge at least every N turns, between compactions.
224
+ Amortized cost stays well under the pre-2.32.0 per-turn tao.
225
+ - **Honest floor.** The periodic refresh is best-effort: it needs a writable
226
+ counter, and each turn is a separate process. If `_byan-output/` is not writable
227
+ the hook degrades to the anchor (exit 0, no crash) and the SessionStart /
228
+ compaction re-injection remains the floor. A test pins that degradation.
229
+
230
+ Built under BYAN Strict Mode; reviewed by bmad-compliance (one CHANGES round on the
231
+ FS-degradation honesty, then approve). root jest 2430/2430, MCP node --test 689/689.
232
+
233
+ ## [2.32.0] - 2026-06-25
234
+
235
+ ### Changed - Token cost reduction for BYAN's persistent identity (cache + dedup)
236
+
237
+ Cuts the per-turn token cost of BYAN's persistent identity payload without making
238
+ any of it conditional: tao, mantras, skeptic, ELO and fact-check stay applied each
239
+ turn. Two levers attack the transport/representation cost, not the presence.
240
+
241
+ - **Cache-align tao injection.** The full tao was re-injected each turn via a
242
+ UserPromptSubmit hook (~14.9 KB per turn, re-billed at the growing edge of the
243
+ conversation). It now loads once at SessionStart (`inject-tao.js` -> SessionStart,
244
+ the cacheable prefix), and a new `inject-voice-anchor.js` injects a compact
245
+ ~95-token voice anchor each turn (register + signatures + tutoiement + zero-emoji
246
+ + IA-16). Per-turn tao transport drops 14898 -> 383 chars (97.4% lower); the full
247
+ tao stays present, moved to the session prefix.
248
+ - **De-duplicate the persistent doctrine.** `strict-mode.md`, `benchmark.md` and
249
+ `fact-check.md` were `@`-imported by `CLAUDE.md`, force-loading the full files
250
+ into every turn (~5964 tokens). The `@`-imports become lean plain pointers. The
251
+ behavioral summary stays inline in `CLAUDE.md` and the enforcement lives in the
252
+ hooks (`strict-*-guard`, `autobench-stop-guard`, `fact-check-*`); the full rule
253
+ files stay reachable on demand via their skills. The benchmark pointer is
254
+ regenerated from the `sync-rules` renderer. `elo-trust` and `team-doctrine` keep
255
+ their `@`-import.
256
+
257
+ A third idea (output-side filtering) was closed as already covered by RTK + Claude
258
+ Code's native large-output truncation -- a documented redundancy, not a cut.
259
+
260
+ Files: `.claude/hooks/inject-tao.js` (now SessionStart), `.claude/hooks/inject-voice-anchor.js`
261
+ (new), `.claude/settings.json`, `.claude/CLAUDE.md`, `_byan/mcp/byan-mcp-server/lib/sync-rules.js`
262
+ (renderer), `_byan/mcp/byan-mcp-server/lib/template-sync.js` (ships the new hook), `AGENTS.md`
263
+ (regenerated), plus the template mirrors. Built under BYAN Strict Mode; reviewed by bmad-compliance
264
+ (approve, identity line held); root jest 2420/2420, MCP node --test 689/689, zero emoji. Token
265
+ figures are char/4 estimates; `/context` is the exact counter-test.
266
+
267
+ ## [2.31.0] - 2026-06-24
268
+
269
+ ### Added - Install-time service-account key setup for byan_publish
270
+
271
+ BYAN is open-source, so no Google key ships in the npm package: each user
272
+ provides their own service-account key, on their own machine. This release adds
273
+ the install-time setup that wires that key for `byan_publish` (the headless
274
+ Google Docs publisher from 2.30.0).
275
+
276
+ - `install/lib/gdoc-setup.js` (new) : `setupGdocPublish` guides the user to the
277
+ Google Cloud console, imports the downloaded SA JSON into
278
+ `~/.byan/google-sa.json` (mode `0600`, dir `0700`), validates it
279
+ (`client_email` + `private_key`), then persists the path via `writeCredentials`
280
+ (`GOOGLE_APPLICATION_CREDENTIALS` + optional `GDOC_TEMPLATE_ID` /
281
+ `GDOC_LOGO_PNG_URL`). Every side-effecting dep (prompt / fs / writeCredentials)
282
+ is injected; the function stays graceful (a missing, invalid, or unreadable key
283
+ degrades to `configured:false` rather than throwing). Only the key path is
284
+ persisted -- the secret stays on disk in `~/.byan/`, out of the repo.
285
+ - `install/setup-gdoc.js` (new) + `npm run setup-gdoc` : run the setup on demand,
286
+ outside the installer.
287
+ - `install/bin/create-byan-agent-v2.js` : opt-in block (prompt default no, mirrors
288
+ the RTK block; gated by `shouldOfferGdoc` -- TTY only, skipped on
289
+ `BYAN_SKIP_GDOC=1`).
290
+ - `@byan/platform-config` : `KNOWN_KEYS` extended with
291
+ `GOOGLE_APPLICATION_CREDENTIALS`, `GDOC_TEMPLATE_ID`, `GDOC_LOGO_PNG_URL` so
292
+ `writeCredentials` persists them (covered by test).
293
+ - Guide : `docs/google-docs-publish.md` documents the open-source per-user key
294
+ model and the `npm run setup-gdoc` path (mirrored to `install/templates/docs/`).
295
+
296
+ Built under BYAN Strict Mode (scope locked, self-verified) ; secrets and
297
+ never-throws self-reviewed PASS (independent compliance pass to re-run before
298
+ publish, transient API outage at review time). Root jest 2389/0.
299
+
300
+ ## [2.30.0] - 2026-06-24
301
+
302
+ ### Added - byan_publish : Google Docs brandés, headless (service account)
303
+
304
+ New MCP tool `byan_publish` : a byan-owned, headless Google Docs publisher. A
305
+ service-account JWT (durable, no OAuth, no browser, no 7-day refresh-token expiry)
306
+ creates a branded Google Doc from a content object and returns its URL, optionally
307
+ sharing it. Distinct from the gw OAuth connector.
308
+
309
+ - `lib/gdoc-content.js` (pure) : content -> Docs `batchUpdate` requests. Template
310
+ mode (`replaceAllText` over a branded template) or programmatic mode (insert +
311
+ the AcadéNice palette : marine `#0e2656`, teal `#24947a`, turquoise `#4cccb8`).
312
+ - `lib/gdoc-client.js` : service-account auth (google-auth-library JWT, scopes
313
+ `documents` + `drive.file`) + create-or-copy + `batchUpdate` +
314
+ `permissions.create`. googleapis is lazy-loaded so the server boots without it ;
315
+ every failure path returns `{ ok:false, reason }` (no-credentials /
316
+ bad-credentials / invalid-content / dep-missing / api-error) rather than
317
+ throwing. The SA key is read from a path (`GOOGLE_APPLICATION_CREDENTIALS`, via
318
+ resolve-config) and stays on disk -- not serialized into any output.
319
+ - `byan_publish` registered in server.js, kept OUT of `REMOTE_SAFE_TOOLS`
320
+ (network+auth, stdio-only). New deps : `googleapis` + `google-auth-library`.
321
+ - Guide : `docs/google-docs-publish.md` (SA key recipe + usage + branding/template
322
+ + the standalone-vs-Workspace ownership model).
323
+
324
+ The single manual step is creating the SA key (Google Cloud IAM). RNCP/eval
325
+ content plugs in later as a thin adapter. Built under BYAN Strict Mode (scope
326
+ locked, self-verified) ; reviewed by bmad-compliance (security : secrets +
327
+ no-throw PASS, narrow scopes, fact-check floor L1). MCP suite 687/687, root jest
328
+ 2378/0.
329
+
330
+ ## [2.29.4] - 2026-06-24
331
+
332
+ ### Changed - Google Workspace (gdrive) install: durable OAuth "Internal"
333
+
334
+ The installer's Google Workspace setup guide now points to an OAuth consent
335
+ screen in "Internal" mode instead of "External / Testing". "External + Testing"
336
+ expires the refresh token in ~7 days for scopes beyond openid/email/profile (all
337
+ of gw's Drive/Docs/... scopes qualify); "Internal" removes that expiry and skips
338
+ Google app verification (source: developers.google.com/identity/protocols/oauth2).
339
+ The result: ONE durable OAuth client is byan's single Google credential, and the
340
+ claude.ai Drive connector becomes redundant.
341
+
342
+ - `install/lib/mcp-extensions/gdrive.js`: `SETUP_LINKS` + `printSetupGuide` guide
343
+ to Internal, with the durability rationale, the org-Workspace prerequisite, and
344
+ the honest limit (one browser login at setup, org-only). A durability reminder
345
+ fires on the reuse path too.
346
+ - The package `google-workspace-mcp` does not support service accounts (its
347
+ README), so "Internal" is the durable path that keeps its 95+ tools. A
348
+ service-account route (for fully headless publishing) would be a separate,
349
+ byan-owned Google client — out of scope here.
350
+ - New guide: `docs/google-workspace-setup.md` (one-time Internal recipe + the
351
+ mutualization rationale).
352
+
353
+ Reviewed by bmad-compliance (auth domain, fact-check floor L1). 2378 tests green.
354
+
355
+ ## [2.29.3] - 2026-06-24
356
+
357
+ ### Fixed - RTK hook now actually installed (--auto-patch)
358
+
359
+ 2.29.2 wired only the RTK instruction layer, not the transparent hook. `wireHook`
360
+ ran `rtk init -g` with stdio piped (no TTY), but bare `rtk init -g` PROMPTS before
361
+ patching `settings.json`, so the prompt was skipped and rtk wrote `RTK.md` + the
362
+ `@RTK.md` reference WITHOUT the PreToolUse hook — `rtk init --show` reported
363
+ "Hook: not found". `wireHook` now calls `rtk init -g --auto-patch`, which patches
364
+ `settings.json` non-interactively, so the command-rewriting hook is installed. An
365
+ older rtk lacking the flag degrades gracefully (reason `hook-failed`; the install
366
+ still succeeds). `doctor()` and the installer consent prompt were updated to match.
367
+ Verified live on a Debian/zsh host: `rtk init --show` reports "Hook: configured"
368
+ after the fix.
369
+
370
+ If you installed 2.29.x before this and RTK feels inactive, re-run
371
+ `rtk init -g --auto-patch`.
372
+
12
373
  ## [2.29.2] - 2026-06-23
13
374
 
14
375
  ### Fixed - RTK optional install: fail-proof, off-PATH, shell-aware
@@ -17,6 +17,7 @@ const { launchPhase2Chat, generateDefaultConfig } = require('../lib/phase2-chat'
17
17
  const { setupByanWebIntegration, validateByanWebReachability } = require('../lib/byan-web-integration');
18
18
  const { setupLeantimeIntegration, validateLeantimeReachability } = require('../lib/byan-leantime-integration');
19
19
  const { setupRtkIntegration, shouldOfferRtk } = require('../lib/rtk-integration');
20
+ const { setupGdocPublish, shouldOfferGdoc } = require('../lib/gdoc-setup');
20
21
  const { setupClaudeNative } = require('../lib/claude-native-setup');
21
22
  const { setupCodexNative } = require('../lib/codex-native-setup');
22
23
  const { setupMcpExtensions } = require('../lib/mcp-extensions');
@@ -1295,6 +1296,21 @@ async function install(options = {}) {
1295
1296
  console.log(chalk.cyan('Claude Code native setup (hooks, skills, MCP server)'));
1296
1297
  try {
1297
1298
  await setupClaudeNative(projectRoot);
1299
+ // Honest disclosure about the byan-channel entry now in .mcp.json. It is a
1300
+ // Claude Code RESEARCH PREVIEW feature and ships INERT: registering it does
1301
+ // not enable it. We tell the user exactly what it is, what it requires, how
1302
+ // to turn it on, and that Codex is not covered — no overselling.
1303
+ console.log(
1304
+ chalk.gray(
1305
+ ' - byan-channel = Claude Code RESEARCH PREVIEW channel (v2.1.80+), INERT by default.'
1306
+ )
1307
+ );
1308
+ console.log(
1309
+ chalk.gray(
1310
+ ' To activate: claude --dangerously-load-development-channels server:byan-channel'
1311
+ )
1312
+ );
1313
+ console.log(chalk.gray(' Codex is not covered by this feature.'));
1298
1314
  } catch (error) {
1299
1315
  console.log(chalk.red(` ✘ Claude native setup failed: ${error.message}`));
1300
1316
  console.log(
@@ -1378,7 +1394,7 @@ async function install(options = {}) {
1378
1394
  {
1379
1395
  type: 'confirm',
1380
1396
  name: 'proceed',
1381
- message: 'Install rtk now? Runs its official installer (brew/cargo, or a pinned curl|sh) and adds a GLOBAL Claude Code hook via `rtk init -g`.',
1397
+ message: 'Install rtk now? Runs its official installer (brew/cargo, or a pinned curl|sh) and adds a GLOBAL Claude Code hook via `rtk init -g --auto-patch`.',
1382
1398
  default: false,
1383
1399
  },
1384
1400
  ]);
@@ -1400,6 +1416,33 @@ async function install(options = {}) {
1400
1416
  }
1401
1417
  }
1402
1418
 
1419
+ if (needsClaude && shouldOfferGdoc()) {
1420
+ console.log();
1421
+ console.log(chalk.cyan('byan_publish — clé service account Google Docs (optionnel, headless)'));
1422
+ try {
1423
+ const { proceed } = await inquirer.prompt([
1424
+ {
1425
+ type: 'confirm',
1426
+ name: 'proceed',
1427
+ message: 'Configurer ta clé service account maintenant (chacun la sienne ; rien de secret ne ship) ?',
1428
+ default: false,
1429
+ },
1430
+ ]);
1431
+ if (proceed) {
1432
+ const r = await setupGdocPublish({ log: (...a) => console.log(...a) });
1433
+ if (r.configured) {
1434
+ console.log(chalk.green(` ✓ byan_publish prêt (clé : ${r.path})`));
1435
+ } else {
1436
+ console.log(chalk.yellow(` ⚠ byan_publish non configuré (${r.skipReason}) — \`npm run setup-gdoc\` à tout moment`));
1437
+ }
1438
+ } else {
1439
+ console.log(chalk.gray(' byan_publish ignoré — `npm run setup-gdoc` à tout moment pour activer.'));
1440
+ }
1441
+ } catch (error) {
1442
+ console.log(chalk.yellow(` ⚠ byan_publish setup skipped: ${error.message}`));
1443
+ }
1444
+ }
1445
+
1403
1446
  if (needsClaude) {
1404
1447
  console.log();
1405
1448
  console.log(chalk.cyan('Leantime board sync (optional — self-hosted)'));
@@ -9,7 +9,8 @@
9
9
  * - _byan/mcp/byan-mcp-server/** (no node_modules)
10
10
  *
11
11
  * Generates:
12
- * - .mcp.json with the absolute path to the target project's MCP server
12
+ * - .mcp.json with the RELATIVE path to the target project's MCP server
13
+ * (byan + inert byan-channel entries, via the shared pure merge)
13
14
  *
14
15
  * Runs:
15
16
  * - npm install inside the copied MCP server dir (with fallback warning)
@@ -22,6 +23,7 @@ const chalk = require('chalk');
22
23
 
23
24
  const TEMPLATE_ROOT = path.resolve(__dirname, '..', 'templates');
24
25
  const { whitelistMcpServer } = require('./settings-local');
26
+ const { mcpConfig } = require('byan-platform-config');
25
27
 
26
28
  async function copyClaudeHooks(projectRoot) {
27
29
  const src = path.join(TEMPLATE_ROOT, '.claude', 'hooks');
@@ -78,30 +80,15 @@ async function copyMcpServer(projectRoot) {
78
80
  }
79
81
 
80
82
  async function generateMcpConfig(projectRoot, options = {}) {
81
- const tmplPath = path.join(TEMPLATE_ROOT, '.mcp.json.tmpl');
82
83
  const dstPath = path.join(projectRoot, '.mcp.json');
83
84
 
84
- let template;
85
- if (await fs.pathExists(tmplPath)) {
86
- template = await fs.readFile(tmplPath, 'utf8');
87
- } else {
88
- template = JSON.stringify(
89
- {
90
- mcpServers: {
91
- byan: {
92
- command: 'node',
93
- args: ['{{PROJECT_ROOT}}/_byan/mcp/byan-mcp-server/server.js'],
94
- env: { BYAN_API_URL: options.apiUrl || 'http://localhost:3737' },
95
- },
96
- },
97
- },
98
- null,
99
- 2
100
- );
101
- }
102
-
103
- const rendered = template.replace(/\{\{PROJECT_ROOT\}\}/g, projectRoot);
104
-
85
+ // Read-merge-write through the single source of truth for the byan + inert
86
+ // byan-channel entry shape: mcpConfig.mergeByanEntry (relative args, secret-free,
87
+ // existing siblings + custom command + non-byan env preserved, channel added
88
+ // inert). This is byte-coherent with installDirectMCP, which routes through the
89
+ // same merge. Additional default MCP servers are registered via addMcpEntry
90
+ // (mcp-extensions), not here -- so there is no .mcp.json template to drift out
91
+ // of sync with the merge.
105
92
  let existing = {};
106
93
  if (await fs.pathExists(dstPath)) {
107
94
  try {
@@ -111,20 +98,7 @@ async function generateMcpConfig(projectRoot, options = {}) {
111
98
  }
112
99
  }
113
100
 
114
- const merged = JSON.parse(rendered);
115
- merged.mcpServers = { ...(existing.mcpServers || {}), ...merged.mcpServers };
116
-
117
- // Security: BYAN_API_TOKEN is never written into .mcp.json (git-tracked); the
118
- // token reaches the MCP server via .claude/settings.local.json env. Also honor
119
- // the caller's apiUrl: the template URL is only a default.
120
- const byanEntry = merged.mcpServers && merged.mcpServers.byan;
121
- if (byanEntry && byanEntry.env) {
122
- delete byanEntry.env.BYAN_API_TOKEN;
123
- if (options.apiUrl) {
124
- byanEntry.env.BYAN_API_URL = options.apiUrl;
125
- }
126
- }
127
-
101
+ const merged = mcpConfig.mergeByanEntry(existing, { apiUrl: options.apiUrl });
128
102
  await fs.writeJson(dstPath, merged, { spaces: 2 });
129
103
  return { path: dstPath };
130
104
  }
@@ -197,7 +171,7 @@ async function setupClaudeNative(projectRoot, options = {}) {
197
171
  );
198
172
 
199
173
  results.mcpConfig = await generateMcpConfig(projectRoot, options);
200
- log(chalk.green(` .mcp.json generated (absolute path)`));
174
+ log(chalk.green(` byan + inert byan-channel entries in .mcp.json (relative paths)`));
201
175
 
202
176
  // Whitelist the byan MCP server: a project .mcp.json entry is inert in Claude
203
177
  // Code until its id is listed in .claude/settings.local.json.