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
@@ -0,0 +1,121 @@
1
+ # byan_publish -- Google Docs brandés, headless (service account)
2
+
3
+ Le tool MCP `byan_publish` crée un Google Doc brandé depuis un objet de contenu,
4
+ via un **service account que tu fournis** (modèle open-source, voir plus bas) :
5
+ auth JWT durable, sans navigateur, sans refresh token, sans expiration 7 jours.
6
+ C'est le chemin headless (cron, batch, sans humain) -- distinct du connecteur
7
+ OAuth `gdrive`/gw.
8
+
9
+ ## Modèle open-source : chacun fournit SA clé
10
+
11
+ BYAN est open-source : **aucune clé ne ship** dans le paquet npm. Chaque
12
+ utilisateur fournit SA propre clé service account, sur sa machine. La clé vit
13
+ dans `~/.byan/google-sa.json` (hors dépôt, 0600) et son chemin est persisté dans
14
+ `~/.byan/credentials.json` (`GOOGLE_APPLICATION_CREDENTIALS`) ; le MCP server la
15
+ lit via resolve-config.
16
+
17
+ ## Le setup (à l'install ou à la demande)
18
+
19
+ L'installeur byan **propose** ce setup (opt-in, défaut non) et il existe aussi en
20
+ commande dédiée :
21
+
22
+ ```bash
23
+ npm run setup-gdoc
24
+ ```
25
+
26
+ Il te guide vers la console Google, importe le JSON téléchargé dans
27
+ `~/.byan/google-sa.json` (0600), et persiste le chemin (+ `GDOC_TEMPLATE_ID` /
28
+ `GDOC_LOGO_PNG_URL` optionnels). Tu peux aussi tout faire à la main (poser le
29
+ fichier + éditer `~/.byan/credentials.json`).
30
+
31
+ ## La seule étape manuelle : créer la clé service account
32
+
33
+ byan ne peut pas créer cette clé à ta place (Google Cloud IAM est hors de portée
34
+ de ses outils). ~2 minutes, une fois :
35
+
36
+ 1. Projet Google Cloud (idéalement l'org acadenice.fr) :
37
+ `https://console.cloud.google.com/projectcreate`
38
+ 2. Activer les APIs **Google Docs** et **Google Drive** :
39
+ `https://console.cloud.google.com/apis/library`
40
+ 3. Créer un **service account** :
41
+ `https://console.cloud.google.com/iam-admin/serviceaccounts`
42
+ 4. Sur ce service account -> onglet **Keys** -> *Add key* -> *Create new key* ->
43
+ **JSON** -> télécharger le fichier.
44
+ 5. Déposer le JSON sur la machine et pointer `GOOGLE_APPLICATION_CREDENTIALS`
45
+ dessus, soit en variable d'environnement, soit dans `~/.byan/credentials.json` :
46
+
47
+ ```json
48
+ { "GOOGLE_APPLICATION_CREDENTIALS": "/home/<user>/.byan/google-sa.json" }
49
+ ```
50
+
51
+ La clé reste hors du dépôt (env ou `~/.byan/`), donc pas commitée.
52
+
53
+ ## Utilisation
54
+
55
+ ```jsonc
56
+ // byan_publish
57
+ {
58
+ "title": "Bilan d'évaluation - Alice",
59
+ "sections": [
60
+ { "heading": "Bloc 1 - Analyse", "body": "Acquis. ..." },
61
+ { "heading": "Bloc 2 - Conception", "body": "En cours. ..." }
62
+ ],
63
+ "resources": [
64
+ { "label": "Référentiel", "url": "https://..." }
65
+ ],
66
+ "shareWith": "alice@example.org", // optionnel
67
+ "role": "reader" // reader | commenter | writer
68
+ }
69
+ ```
70
+
71
+ Retour en cas de succès : `{ ok: true, documentId, url, mode, shared }`.
72
+ En cas de souci, le tool dégrade en `{ ok: false, reason, message }` plutôt que
73
+ de planter :
74
+
75
+ | reason | sens |
76
+ |--------|------|
77
+ | `no-credentials` | `GOOGLE_APPLICATION_CREDENTIALS` non défini |
78
+ | `bad-credentials` | fichier introuvable ou JSON invalide (client_email + private_key requis) |
79
+ | `dep-missing` | `googleapis` non installé -> `npm install googleapis google-auth-library` |
80
+ | `invalid-content` | `title` manquant |
81
+ | `api-error` | erreur renvoyée par l'API Google (message en clair) |
82
+
83
+ ## Branding : programmatique (défaut) ou template
84
+
85
+ - **Programmatique** (défaut, sans config) : doc construit depuis le code, titre
86
+ et en-têtes colorés avec la palette AcadéNice (marine `#0e2656`, teal `#24947a`,
87
+ turquoise `#4cccb8`). Logo optionnel via `GDOC_LOGO_PNG_URL` (ou `logoPngUrl` en
88
+ argument) : une URL **PNG** publique, insérée en haut du doc ; absente, le doc
89
+ est brandé par les couleurs seules (Google Docs n'accepte pas le SVG).
90
+ - **Template** (`GDOC_TEMPLATE_ID` ou `templateId` en argument) : le doc est une
91
+ **copie** d'un Google Doc template brandé (logo + palette dans le template), puis
92
+ les `{{PLACEHOLDERS}}` sont remplis (`{{TITLE}}`, `{{BLOCS}}`, `{{RESSOURCES}}`,
93
+ plus tout `{{KEY}}` passé via `fields`). C'est le mode recommandé pour un logo :
94
+ Google Docs n'insère pas de SVG, donc le logo vit dans le template (image déjà
95
+ posée), pas réinséré à chaque publication.
96
+
97
+ ## Portée et limites (honnêtes)
98
+
99
+ - Scopes : `documents` + `drive.file` (par-fichier, le plus étroit suffisant pour
100
+ créer + partager). Pas le scope large `drive`.
101
+ - Propriété des fichiers, selon le type de projet GCP :
102
+ - **Projet standalone (hors Workspace)** : le service account possède les
103
+ fichiers qu'il crée, dans son propre Drive (quota ~15 Go, valeur rapportée
104
+ par la communauté) ; partage-les via `shareWith` ; supprimer le service
105
+ account supprime ses fichiers.
106
+ - **Projet rattaché à une org Workspace (le setup recommandé plus haut)** : un
107
+ service account ne peut pas posséder d'assets Drive de l'org (Google IAM,
108
+ `service-account-overview`). Dans ce cas, crée le doc dans un **Shared Drive**
109
+ possédé par l'org (cible un dossier parent), ou impersonne un utilisateur via
110
+ la délégation domaine. Increment suivant côté code : un `GDOC_PARENT_FOLDER_ID`
111
+ (Shared Drive) ; en attendant, le partage `shareWith` reste fonctionnel et le
112
+ doc vit dans le stockage du service account.
113
+ - La délégation domaine n'est pas requise pour le mode standalone (le SA agit
114
+ pour lui-même). Elle ne sert qu'à faire posséder le fichier par un utilisateur.
115
+
116
+ ## Contenu RNCP (adaptateur)
117
+
118
+ `byan_publish` est générique. Pour publier un retour d'évaluation RNCP, un
119
+ adaptateur transforme l'objet eval (couverture par bloc + synthèse) en
120
+ `{ title, sections, resources }` puis appelle `byan_publish`. Cet adaptateur
121
+ vit là où la chaîne RNCP est définie -- hors de ce module générique.
@@ -13,7 +13,7 @@ events:
13
13
  | FD phase / event | Effect on the board |
14
14
  |------------------|---------------------|
15
15
  | DISCOVERY (project confirmed) | create-or-fetch the Leantime project |
16
- | DISPATCH (per backlog feature) | one task per feature |
16
+ | DISPATCH (per backlog feature) | one task per feature, with effort (storypoints), priority and a description |
17
17
  | BUILD (feature starts) | task -> column `doing` |
18
18
  | REVIEW needs-rework / VALIDATE KO | task -> column `blocked` |
19
19
  | VALIDATE OK | task -> column `review` |
@@ -23,6 +23,16 @@ The FD lifecycle columns (`todo|doing|blocked|review|done`) are resolved to the
23
23
  project's configured Leantime status ids at call time (statuses are per-project
24
24
  ints), with a conservative fallback when the labels cannot be read.
25
25
 
26
+ Each created task also carries the BYAN signal, not just a bare title:
27
+
28
+ - **effort** -- `storypoints` from the feature's complexity: a finite `complexity`
29
+ (0-100) bucketed on the Fibonacci scale (`<=15->2`, `16-39->5`, `40-69->8`,
30
+ `>=70->13`), else derived from priority (`P1->8`, `P2->5`, `P3->3`, default 3).
31
+ - **priority** -- `P1`/`P2`/`P3` mapped to Leantime `3`/`2`/`1` (omitted when unknown).
32
+ - **description** -- `BYAN FD <id> -- <headline>`, plus `[complexity:N]` when known.
33
+ It also carries the complexity if the Leantime effort field name differs from
34
+ the presumed `storypoints` (a `[UNVERIFIED]` wire detail, see Troubleshooting).
35
+
26
36
  ## Automatic sync (the leantime-fd-sync hook)
27
37
 
28
38
  You do not call the `byan_leantime_*` tools by hand. A Claude Code `PostToolUse`
@@ -18,7 +18,19 @@ const path = require('path');
18
18
  const fs = require('fs-extra');
19
19
 
20
20
  // Keys the credentials file understands. Any other key passed in is ignored.
21
- const KNOWN_KEYS = ['BYAN_API_URL', 'BYAN_API_TOKEN', 'LEANTIME_API_URL', 'LEANTIME_API_TOKEN'];
21
+ // The Google Docs publish keys (byan_publish) live here too so a per-user
22
+ // service-account setup persists alongside the byan_web / Leantime config; the
23
+ // MCP server reads them via resolve-config. GOOGLE_APPLICATION_CREDENTIALS is a
24
+ // PATH to the SA JSON (not the key itself) -- the key file stays separate, 0600.
25
+ const KNOWN_KEYS = [
26
+ 'BYAN_API_URL',
27
+ 'BYAN_API_TOKEN',
28
+ 'LEANTIME_API_URL',
29
+ 'LEANTIME_API_TOKEN',
30
+ 'GOOGLE_APPLICATION_CREDENTIALS',
31
+ 'GDOC_TEMPLATE_ID',
32
+ 'GDOC_LOGO_PNG_URL',
33
+ ];
22
34
 
23
35
  function credentialsDir(homedir = os.homedir()) {
24
36
  return path.join(homedir, '.byan');
@@ -27,6 +27,13 @@ const path = require('path');
27
27
  const fs = require('fs-extra');
28
28
 
29
29
  const MCP_SERVER_REL_PATH = '_byan/mcp/byan-mcp-server/server.js';
30
+ // Channel entrypoint: a SEPARATE MCP server (research preview) spawned by Claude
31
+ // Code via --dangerously-load-development-channels. Same relative-path discipline
32
+ // as the main server (relative to projectRoot, never absolute) so the entry is
33
+ // portable across machines/OSes and stays valid when the repo is moved or shipped
34
+ // via npm. The entry is INERT by default: registering it in .mcp.json does NOT
35
+ // enable the channel (that needs the explicit --dangerously-load flag at launch).
36
+ const MCP_CHANNEL_REL_PATH = '_byan/mcp/byan-mcp-server/channel-entry.js';
30
37
  const TOKEN_PLACEHOLDER = '${BYAN_API_TOKEN}';
31
38
  const LEANTIME_URL_PLACEHOLDER = '${LEANTIME_API_URL}';
32
39
  const LEANTIME_TOKEN_PLACEHOLDER = '${LEANTIME_API_TOKEN}';
@@ -87,15 +94,66 @@ function mergeByanEntry(existingConfig, { apiUrl, token } = {}) {
87
94
  delete env.BYAN_API_URL;
88
95
  delete env.BYAN_API_TOKEN;
89
96
 
97
+ // The canonical RELATIVE path is forced on `args` AFTER ...existing so it always
98
+ // wins -- this REPAIRS a stale entry that carried an absolute path (a pre-2.37.x
99
+ // install): relative survives a moved / npm-shipped repo, absolute does not, and
100
+ // Claude Code spawns the server with cwd=projectRoot so the relative path resolves
101
+ // identically. `command` is only F1-orthogonal (the interpreter, not a path), so a
102
+ // user-chosen command is preserved; we default to 'node' only when absent. Other
103
+ // pre-existing keys are still preserved.
90
104
  const entry = {
91
- command: 'node',
92
- args: [MCP_SERVER_REL_PATH],
93
105
  ...existing,
106
+ command: existing.command || 'node',
107
+ args: [MCP_SERVER_REL_PATH],
94
108
  };
95
109
  if (Object.keys(env).length > 0) entry.env = env;
96
110
  else delete entry.env;
97
111
 
98
112
  cfg.mcpServers.byan = entry;
113
+
114
+ // The channel entry is written alongside byan from the SAME merge so there is
115
+ // a single source of truth for the byan MCP registration. It is inert.
116
+ return mergeChannelEntry(cfg);
117
+ }
118
+
119
+ /**
120
+ * Pure merge — no I/O. Adds the `byan-channel` MCP server entry alongside byan.
121
+ *
122
+ * This entry is a Claude Code research-preview channel (CC v2.1.80+). It is
123
+ * INERT by default: present in .mcp.json but only loaded when the user launches
124
+ * with `claude --dangerously-load-development-channels server:byan-channel`.
125
+ * Registering it does NOT auto-activate anything — this function writes NO
126
+ * channelsEnabled / allowedChannelPlugins / --dangerously-load flag.
127
+ *
128
+ * Same portability discipline as the byan entry: a RELATIVE path (never
129
+ * absolute), no secret in env (the channel resolves its own config at boot via
130
+ * resolve-config.js: env -> ~/.byan/credentials.json -> defaults). An existing
131
+ * byan-channel entry is preserved (command/args/env) so the merge is idempotent.
132
+ *
133
+ * @param {object} existingConfig — current parsed config
134
+ * @returns {object} new merged config
135
+ */
136
+ function mergeChannelEntry(existingConfig) {
137
+ const cfg = existingConfig && typeof existingConfig === 'object' ? { ...existingConfig } : {};
138
+ cfg.mcpServers = { ...(cfg.mcpServers || {}) };
139
+
140
+ const existing = cfg.mcpServers['byan-channel'] || {};
141
+
142
+ // The canonical RELATIVE path + the empty (secret-free) env are forced AFTER
143
+ // ...existing so they always win, normalizing a stale entry (absolute path, or
144
+ // a stray env) while preserving any other pre-existing key. env stays empty: the
145
+ // channel resolves BYAN_API_URL/TOKEN itself via resolve-config.js -- writing a
146
+ // secret here would land it in tracked git, and the channel has no legitimate
147
+ // env contract of its own (Leantime refs live on the byan entry). `command` is
148
+ // F1-orthogonal so a user-chosen interpreter is preserved, 'node' only as the
149
+ // default. Idempotent: re-running yields the same entry.
150
+ cfg.mcpServers['byan-channel'] = {
151
+ ...existing,
152
+ command: existing.command || 'node',
153
+ args: [MCP_CHANNEL_REL_PATH],
154
+ env: {},
155
+ };
156
+
99
157
  return cfg;
100
158
  }
101
159
 
@@ -141,14 +199,20 @@ function mergeLeantimeRefs(existingConfig) {
141
199
  env.LEANTIME_API_URL = LEANTIME_URL_PLACEHOLDER;
142
200
  env.LEANTIME_API_TOKEN = LEANTIME_TOKEN_PLACEHOLDER;
143
201
 
202
+ // Same discipline as mergeByanEntry: args forced to the canonical RELATIVE path
203
+ // (normalizes a stale absolute one), command preserved (F1-orthogonal), env
204
+ // carries the merged Leantime refs.
144
205
  cfg.mcpServers.byan = {
145
- command: 'node',
146
- args: [MCP_SERVER_REL_PATH],
147
206
  ...existing,
207
+ command: existing.command || 'node',
208
+ args: [MCP_SERVER_REL_PATH],
148
209
  env,
149
210
  };
150
211
 
151
- return cfg;
212
+ // Keep the channel entry coherent with the byan entry: ensureLeantimeRefs runs
213
+ // after ensureMcpConfig in the installer, but routing through mergeChannelEntry
214
+ // here makes the byan-channel registration robust to call order (idempotent).
215
+ return mergeChannelEntry(cfg);
152
216
  }
153
217
 
154
218
  /**
@@ -252,12 +316,14 @@ module.exports = {
252
316
  ensureMcpConfig,
253
317
  readMcpConfig,
254
318
  mergeByanEntry,
319
+ mergeChannelEntry,
255
320
  mergeLeantimeRefs,
256
321
  ensureLeantimeRefs,
257
322
  addMcpEntry,
258
323
  removeMcpEntry,
259
324
  looksLikeSecret,
260
325
  MCP_SERVER_REL_PATH,
326
+ MCP_CHANNEL_REL_PATH,
261
327
  TOKEN_PLACEHOLDER,
262
328
  LEANTIME_URL_PLACEHOLDER,
263
329
  LEANTIME_TOKEN_PLACEHOLDER,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.29.2",
3
+ "version": "2.38.0",
4
4
  "description": "BYAN v2.8 - Intelligent AI agent creator with ELO trust system + scientific fact-check + Hermes universal dispatcher + native Claude Code integration (hooks, skills, MCP server). Multi-platform (Claude Code, Codex). Merise Agile + TDD + 71 Mantras. ~54% LLM cost savings.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "test:e2e": "jest install/__tests__/post-install.e2e.test.js",
19
19
  "setup-turbo-whisper": "node install/setup-turbo-whisper.js",
20
20
  "setup-rtk": "node install/setup-rtk.js",
21
+ "setup-gdoc": "node install/setup-gdoc.js",
21
22
  "byan": "echo \"BYAN agent installed. Open Claude Code (or Codex) in this project.\"",
22
23
  "version": "node scripts/sync-install-version.js && git add install/package.json",
23
24
  "app:dev": "npm --prefix app run dev",
@@ -63,6 +64,7 @@
63
64
  "install/setup-turbo-whisper.js",
64
65
  "install/setup-parakeet.js",
65
66
  "install/setup-rtk.js",
67
+ "install/setup-gdoc.js",
66
68
  "install/install.sh",
67
69
  "install/package.json",
68
70
  "update-byan-agent/bin/",
@@ -1,8 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "byan": {
4
- "command": "node",
5
- "args": ["{{PROJECT_ROOT}}/_byan/mcp/byan-mcp-server/server.js"]
6
- }
7
- }
8
- }