brainclaw 1.23.0 → 1.25.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 (40) hide show
  1. package/dist/brainclaw-vscode.vsix +0 -0
  2. package/dist/cli/register-cloud.js +121 -13
  3. package/dist/cli/register-code-map.js +9 -2
  4. package/dist/commands/cloud.js +534 -39
  5. package/dist/commands/code-map.js +119 -2
  6. package/dist/commands/mcp-catalog.js +46 -0
  7. package/dist/commands/mcp.js +54 -2
  8. package/dist/core/code-map/backend.js +158 -1
  9. package/dist/core/code-map/export.js +212 -0
  10. package/dist/core/code-map/freshness.js +3 -2
  11. package/dist/core/code-map/impact.js +377 -0
  12. package/dist/core/code-map/indexes.js +27 -3
  13. package/dist/core/code-map/lang/typescript/config.js +271 -0
  14. package/dist/core/code-map/lang/typescript/index.js +20 -4
  15. package/dist/core/code-map/query.js +76 -13
  16. package/dist/core/code-map/refresh.js +0 -0
  17. package/dist/core/code-map/resolve.js +1 -0
  18. package/dist/core/code-map/types.js +15 -0
  19. package/dist/core/federation-emit.js +283 -0
  20. package/dist/core/federation-grant-transport.js +196 -0
  21. package/dist/core/federation-grant.js +223 -0
  22. package/dist/core/federation-keyring.js +39 -0
  23. package/dist/core/federation-opaque-ids.js +111 -0
  24. package/dist/core/federation-outbox-v2.js +36 -2
  25. package/dist/core/federation-pairing.js +87 -12
  26. package/dist/core/federation-pull.js +523 -0
  27. package/dist/core/federation-push.js +287 -0
  28. package/dist/core/federation-rotation.js +124 -0
  29. package/dist/core/federation-state.js +81 -6
  30. package/dist/core/protocol-tool-policy.js +3 -0
  31. package/dist/core/worktree.js +89 -2
  32. package/dist/facts.js +14 -11
  33. package/dist/facts.json +13 -10
  34. package/docs/cli.md +8 -0
  35. package/docs/code-map.md +24 -1
  36. package/docs/design/federation-onboarding-usecases.md +254 -0
  37. package/docs/design/pairing-v3-brief.md +80 -0
  38. package/docs/integrations/mcp.md +5 -2
  39. package/docs/mcp-schema-changelog.md +11 -1
  40. package/package.json +1 -1
Binary file
@@ -1,4 +1,4 @@
1
- import { runCloudStatus, runCloudConnect, runCloudAwait, runCloudDisconnect, } from '../commands/cloud.js';
1
+ import { runCloudStatus, runCloudConnect, runCloudAwait, runCloudDisconnect, runCloudPull, runCloudPush, runCloudGrant, runCloudGrantWeb, runCloudRotate, runCloudAcceptSoloRisk, } from '../commands/cloud.js';
2
2
  /** Adresse du cloud. Fournie par `--url`, sans quoi la commande demande de la préciser. */
3
3
  const DEFAULT_URL_HINT = 'https://<votre-déploiement>.workers.dev';
4
4
  function fail(message) {
@@ -24,14 +24,125 @@ export function registerCloudCommands(program) {
24
24
  runCloudStatus({ json: options.json });
25
25
  });
26
26
  cloud
27
- .command('connect <invite-code>')
28
- .description("Rejoint un projet cloud : réclame l'invitation, prouve la possession de l'identité et atteste la clé de chiffrement de cet appareil")
29
- .requiredOption('--url <url>', `Adresse du déploiement cloud (ex. ${DEFAULT_URL_HINT})`)
30
- .requiredOption('--agent <id>', "Identifiant d'agent à enrôler (opaque, 4 à 64 caractères)")
27
+ .command('push')
28
+ .description('Projette les plans et la mémoire projet vers le cloud : scelle, met en file, puis envoie')
29
+ .option('--url <url>', `Adresse du déploiement cloud (ex. ${DEFAULT_URL_HINT})`)
30
+ .option('--dry-run', "N'écrit ni n'envoie rien : rapporte ce qui partirait")
31
+ .option('--api-key <key>', 'Clé porteuse exigée par l ingestion (ou BRAINCLAW_CLOUD_API_KEY)')
32
+ .option('--limit <n>', 'Borne le lot envoyé', (v) => Number.parseInt(v, 10))
31
33
  .option('--json', 'Sortie JSON')
32
- .action(async (inviteCode, options) => {
34
+ .action(async (options) => {
35
+ try {
36
+ await runCloudPush({ ...options, apiKey: options.apiKey });
37
+ }
38
+ catch (err) {
39
+ fail(err instanceof Error ? err.message : String(err));
40
+ }
41
+ });
42
+ cloud
43
+ .command('pull')
44
+ .description('Tire le delta cloud, vérifie chaque enveloppe, puis matérialise les objets acceptés')
45
+ .option('--url <url>', `Adresse du déploiement cloud (ex. ${DEFAULT_URL_HINT})`)
46
+ .option('--api-key <key>', 'Clé porteuse exigée par l ingestion (ou BRAINCLAW_CLOUD_API_KEY)')
47
+ .option('--limit <n>', 'Borne le delta reçu', (v) => Number.parseInt(v, 10))
48
+ .option('--json', 'Sortie JSON')
49
+ .action(async (options) => {
50
+ try {
51
+ await runCloudPull({ ...options, apiKey: options.apiKey });
52
+ }
53
+ catch (err) {
54
+ fail(err instanceof Error ? err.message : String(err));
55
+ }
56
+ });
57
+ cloud
58
+ .command('grant <agentId>')
59
+ .description("Remet des clés d'epoch à un appareil approuvé — seul un détenteur actif le peut")
60
+ .option('--url <url>', `Adresse du déploiement cloud (ex. ${DEFAULT_URL_HINT})`)
61
+ // Le défaut est `current` (dec#163 §1) : un membre invité voit à partir de son
62
+ // arrivée. `all` est réservé à un autre appareil DE LA MÊME personne — c'est un choix
63
+ // explicite, jamais un effet de bord.
64
+ .option('--horizon <all|current>', "Étendue remise : 'all' (vos propres appareils) ou 'current' (invité)", 'current')
65
+ .option('--epoch <n...>', 'Epochs précis à remettre (outrepasse --horizon)', (v, acc) => {
66
+ acc.push(Number.parseInt(v, 10));
67
+ return acc;
68
+ }, [])
69
+ .option('--json', 'Sortie JSON')
70
+ .action(async (agentId, options) => {
71
+ try {
72
+ await runCloudGrant({
73
+ to: agentId,
74
+ url: options.url,
75
+ horizon: options.horizon,
76
+ epochs: options.epoch?.length ? options.epoch : undefined,
77
+ json: options.json,
78
+ });
79
+ }
80
+ catch (err) {
81
+ fail(err instanceof Error ? err.message : String(err));
82
+ }
83
+ });
84
+ cloud
85
+ .command('grant-web <fingerprint>')
86
+ .description("Remet des clés d'epoch à une session NAVIGATEUR — recopiez l'empreinte affichée par le navigateur")
87
+ .option('--url <url>', `Adresse du déploiement cloud (ex. ${DEFAULT_URL_HINT})`)
88
+ .option('--horizon <all|current>', "Étendue remise : 'all' ou 'current'", 'current')
89
+ .option('--epoch <n...>', 'Epochs précis à remettre (outrepasse --horizon)', (v, acc) => {
90
+ acc.push(Number.parseInt(v, 10));
91
+ return acc;
92
+ }, [])
93
+ .option('--api-key <key>', "Clé porteuse exigée par l'ingestion (ou BRAINCLAW_CLOUD_API_KEY)")
94
+ .option('--json', 'Sortie JSON')
95
+ .action(async (fingerprint, options) => {
96
+ try {
97
+ await runCloudGrantWeb({
98
+ fingerprint,
99
+ url: options.url,
100
+ horizon: options.horizon,
101
+ epochs: options.epoch?.length ? options.epoch : undefined,
102
+ apiKey: options.apiKey,
103
+ json: options.json,
104
+ });
105
+ }
106
+ catch (err) {
107
+ fail(err instanceof Error ? err.message : String(err));
108
+ }
109
+ });
110
+ cloud
111
+ .command('rotate')
112
+ .description("Crée l'epoch suivant et y bascule les écritures — ferme la lecture FUTURE à un révoqué")
113
+ // `--force` existe mais n'est PAS le premier réflexe : le refus de quorum nomme
114
+ // d'abord `accept-solo-risk`, qui laisse une trace datée. Forcer n'en laisse aucune.
115
+ .option('--force', 'Passer outre le quorum de récupération SANS consigner de consentement')
116
+ .option('--json', 'Sortie JSON')
117
+ .action(async (options) => {
118
+ try {
119
+ await runCloudRotate(options);
120
+ }
121
+ catch (err) {
122
+ fail(err instanceof Error ? err.message : String(err));
123
+ }
124
+ });
125
+ cloud
126
+ .command('accept-solo-risk')
127
+ .description('Consigne votre acceptation du risque solo (perte de cette machine = perte du passé)')
128
+ .option('--json', 'Sortie JSON')
129
+ .action(async (options) => {
130
+ try {
131
+ await runCloudAcceptSoloRisk(options);
132
+ }
133
+ catch (err) {
134
+ fail(err instanceof Error ? err.message : String(err));
135
+ }
136
+ });
137
+ cloud
138
+ .command('connect <url|code>')
139
+ .description("Rejoint un projet cloud depuis l'URL d'activation ou un code ; attend ensuite l'approbation humaine")
140
+ .option('--url <url>', `Origine du déploiement si vous fournissez un code nu (ex. ${DEFAULT_URL_HINT})`)
141
+ .requiredOption('--agent <id>', "Identifiant d'agent à enrôler (4 à 64 caractères : a-z, A-Z, 0-9, _ ou -)")
142
+ .option('--json', 'Sortie JSON')
143
+ .action(async (activation, options) => {
33
144
  await runCloudConnect({
34
- inviteCode,
145
+ inviteCode: activation,
35
146
  url: options.url,
36
147
  agentId: options.agent,
37
148
  json: options.json,
@@ -39,11 +150,8 @@ export function registerCloudCommands(program) {
39
150
  });
40
151
  cloud
41
152
  .command('await')
42
- .description("Constate l'approbation humaine et active l'appairage local")
43
- // Commande DISTINCTE de connect : l'approbation dépend d'un humain dont le délai
44
- // n'est pas borné. Bloquer indéfiniment sur un tiers ferait un mauvais citoyen dans
45
- // un script, et une interruption doit rester reprenable.
46
- .requiredOption('--url <url>', 'Adresse du déploiement cloud')
153
+ .description("Reprend l'attente d'approbation humaine après une interruption (maximum 15 min)")
154
+ .option('--url <url>', 'Adresse du déploiement cloud (utilise celle mémorisée après appairage)')
47
155
  .action(async (options) => {
48
156
  await runCloudAwait({ url: options.url })
49
157
  .catch((err) => fail(`Erreur : ${err instanceof Error ? err.message : String(err)}`));
@@ -51,7 +159,7 @@ export function registerCloudCommands(program) {
51
159
  cloud
52
160
  .command('disconnect')
53
161
  .description("Retire l'autorisation locale et demande la révocation distante")
54
- .requiredOption('--url <url>', 'Adresse du déploiement cloud')
162
+ .option('--url <url>', 'Adresse du déploiement cloud (utilise celle mémorisée après appairage)')
55
163
  // Effacer le trousseau rend DÉFINITIVEMENT illisible tout ce qui a été scellé sous
56
164
  // ces epochs. Ce n'est donc pas le défaut : on le demande explicitement.
57
165
  .option('--forget-keys', "Efface aussi les clés d'epoch de ce projet (le passé scellé devient illisible ici)")
@@ -3,12 +3,19 @@ export function registerCodeMapCommands(program) {
3
3
  // --- code-map ---
4
4
  program
5
5
  .command('code-map <subcommand> [args...]')
6
- .description('Query the per-project Code Map (status, refresh, find, brief)')
6
+ .description('Query the per-project Code Map (status, refresh, find, brief, impact, export, outline)')
7
7
  .option('--json', 'Output as JSON')
8
8
  .option('--all', 'For refresh: enumerate all supported files (full refresh)')
9
9
  .option('--changed', 'For refresh: only changed files (default)')
10
10
  .option('--cascade', 'For refresh/status in a multi-project workspace: cascade across every nested project (each gets its own store; the root store is scoped to files no child owns)')
11
- .option('--limit <n>', 'Max results for find/brief', (v) => parseInt(v, 10))
11
+ .option('--limit <n>', 'Max results for find/brief/impact/outline', (v) => parseInt(v, 10))
12
+ .option('--depth <n>', 'For impact/export: maximum graph depth (export is hard-capped at 4)', (v) => parseInt(v, 10))
13
+ .option('--direction <direction>', 'For export: outgoing, incoming, or both')
14
+ .option('--format <format>', 'For export: json (default) or mermaid')
15
+ .option('--max-nodes <n>', 'For export: maximum nodes (hard-capped at 100)', (v) => parseInt(v, 10))
16
+ .option('--max-edges <n>', 'For export: maximum edges (hard-capped at 200)', (v) => parseInt(v, 10))
17
+ .option('--min-confidence <n>', 'For export: persisted confidence floor (minimum 0.5)', (v) => parseFloat(v))
18
+ .option('--target-kind <kind>', 'For export: symbol or file (auto-detected by default)')
12
19
  .action((subcommand, args, options) => {
13
20
  void runCodeMap(subcommand, args, options).catch((err) => {
14
21
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);