@kikedealba/recap 0.9.1 → 0.11.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.
@@ -3,9 +3,6 @@ import path from 'node:path';
3
3
  import { agents } from '@kikedealba/kit';
4
4
  import { defineManifest, isInstalled, readManifest, registerTool } from '@kikedealba/kit/registry';
5
5
  import { bitaCommand } from "../bita/client.js";
6
- import { readText } from "../core/fsutil.js";
7
- import { isRecord } from "../core/json.js";
8
- import { home } from "../core/paths.js";
9
6
  import { stableCommand } from "../core/self.js";
10
7
  import { HOOK_EVENTS, MEETING_KINDS } from "../bita/hook.js";
11
8
  import { PACKAGE_ROOT, VERSION } from "../version.js";
@@ -47,24 +44,29 @@ export async function bitaLinked(config) {
47
44
  return false;
48
45
  return (await bitaCommand(config)) !== null;
49
46
  }
50
- export function commandSources() {
51
- const dir = path.join(PACKAGE_ROOT, 'commands');
47
+ export const MAIN_SKILL = 'recap';
48
+ export const CLAUDE_PLUGIN = { marketplace: 'KikeDeAlba/recap', marketplaceName: 'recap', plugin: 'recap' };
49
+ export function userSkillSources() {
50
+ const dir = path.join(PACKAGE_ROOT, 'skills');
52
51
  let names = [];
53
52
  try {
54
- names = readdirSync(dir).filter((name) => name.endsWith('.md')).sort();
53
+ names = readdirSync(dir, { withFileTypes: true })
54
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith(`${MAIN_SKILL}-`))
55
+ .map((entry) => entry.name)
56
+ .sort();
55
57
  }
56
58
  catch {
57
59
  return [];
58
60
  }
59
- return names.map((name) => ({ name: name.slice(0, -3), file: path.join(dir, name) }));
61
+ return names.map((name) => ({ name, dir: path.join(dir, name) }));
60
62
  }
61
63
  export function integration() {
62
64
  return {
63
65
  tool: 'recap',
64
66
  version: VERSION,
65
67
  description: 'Record meetings and turn them into minutes with recap',
66
- skills: [{ name: 'recap', dir: path.join(PACKAGE_ROOT, 'skills', 'recap') }],
67
- commands: commandSources(),
68
+ skills: [{ name: MAIN_SKILL, dir: path.join(PACKAGE_ROOT, 'skills', MAIN_SKILL) }],
69
+ userSkills: userSkillSources(),
68
70
  claude: {
69
71
  permissions: {
70
72
  allow: ['Bash(recap status:*)', 'Bash(recap list:*)', 'Bash(recap show:*)', 'Bash(recap wait:*)', 'Bash(recap prompt:*)', 'Bash(recap --version)'],
@@ -72,19 +74,9 @@ export function integration() {
72
74
  },
73
75
  };
74
76
  }
75
- export function claudePluginInstalled() {
76
- const claudeHome = process.env['CLAUDE_CONFIG_DIR'] ?? path.join(home(), '.claude');
77
- const text = readText(path.join(claudeHome, 'plugins', 'installed_plugins.json'));
78
- if (text === null)
79
- return false;
80
- try {
81
- const value = JSON.parse(text);
82
- const plugins = isRecord(value) && isRecord(value['plugins']) ? value['plugins'] : isRecord(value) ? value : {};
83
- return Object.keys(plugins).some((key) => key.startsWith('recap@'));
84
- }
85
- catch {
86
- return text.includes('"recap@');
87
- }
77
+ export function legacyClaudeIntegration() {
78
+ const { claude: _permissions, ...base } = integration();
79
+ return { ...base, commands: (base.userSkills ?? []).map((skill) => ({ name: skill.name, file: path.join(skill.dir, 'SKILL.md') })) };
88
80
  }
89
81
  export function parseAgents(raw) {
90
82
  if (raw === undefined || raw === 'detected')
@@ -97,16 +89,19 @@ export function parseAgents(raw) {
97
89
  throw new Error(`Unknown agent ${invalid.join(', ')}; use ${agents.AGENTS.join(', ')}, detected, all or none`);
98
90
  return wanted;
99
91
  }
100
- export async function installAgents(choice, exec) {
92
+ export async function installAgents(choice, options = {}) {
101
93
  if (choice === 'none')
102
94
  return [];
103
- let list = choice === 'detected' ? await agents.detectAgents() : choice === 'all' ? [...agents.AGENTS] : choice;
95
+ const list = choice === 'detected' ? await agents.detectAgents(options.ctx, agents.agentHomes(options.ctx, options.homes), options.locate) : choice === 'all' ? [...agents.AGENTS] : choice;
104
96
  const steps = [];
105
- if (list.includes('claude') && claudePluginInstalled()) {
106
- list = list.filter((agent) => agent !== 'claude');
107
- steps.push({ agent: 'claude', item: 'integration', state: 'present', detail: 'the recap plugin for Claude Code is installed; it already carries the skill and commands' });
97
+ if (list.includes('claude')) {
98
+ const plugin = await agents.installClaudePlugin(CLAUDE_PLUGIN, options);
99
+ steps.push(...plugin);
100
+ if (plugin.every((step) => step.state === 'installed' || step.state === 'present'))
101
+ steps.push(...(await agents.removeLegacyClaude(legacyClaudeIntegration(), options)));
108
102
  }
109
- if (list.length === 0)
103
+ const others = list.filter((agent) => agent !== 'claude');
104
+ if (others.length === 0)
110
105
  return steps;
111
- return [...steps, ...(await agents.installIntegration(integration(), { agents: list, ...(exec ? { exec } : {}) }))];
106
+ return [...steps, ...(await agents.installIntegration(integration(), { ...options, agents: others }))];
112
107
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kikedealba/recap",
3
- "version": "0.9.1",
4
- "description": "Meeting recorder CLI: local transcription with whisper.cpp, minutes written by Claude Code, live answers and bita integration.",
3
+ "version": "0.11.0",
4
+ "description": "Meeting recorder CLI: local transcription with whisper.cpp, minutes written by Claude Code, live answers, bita timers and inkwell docs.",
5
5
  "license": "MIT",
6
6
  "author": "Jesus Enrique De Alba Gaytan",
7
7
  "homepage": "https://github.com/KikeDeAlba/recap#readme",
@@ -35,7 +35,6 @@
35
35
  "dist",
36
36
  "Resources/*.md",
37
37
  "skills",
38
- "commands",
39
38
  "README.md",
40
39
  "LICENSE"
41
40
  ],
@@ -50,7 +49,7 @@
50
49
  "recap": "node src/bin/recap.ts"
51
50
  },
52
51
  "dependencies": {
53
- "@kikedealba/kit": "^0.2.0"
52
+ "@kikedealba/kit": "^0.3.0"
54
53
  },
55
54
  "devDependencies": {
56
55
  "@types/node": "^24.0.0",
@@ -6,21 +6,26 @@ description: Graba reuniones con la CLI `recap` y las convierte en minuta (resum
6
6
  # recap
7
7
 
8
8
  `recap` graba reuniones (en macOS, con Recap.app) y las procesa localmente en
9
- macOS, Windows y Linux. Todo vive en una carpeta por reunión bajo `~/Recap` (o
10
- la raíz configurada): `meeting.json`, la grabación, `transcript.md`, `frames/` y
9
+ macOS, Windows y Linux. Cada reunión es una carpeta bajo `~/Recap` (o la raíz
10
+ configurada) con `meeting.json`, la grabación, `transcript.md`, `frames/` y
11
11
  `summary.md`.
12
12
 
13
- Fuera de macOS no se puede grabar (`start` falla con `CAPTURE_UNAVAILABLE`):
14
- la reunión se graba con cualquier otra app y se procesa con
15
- `recap import <archivo> [--title "…"] [--in-person|--remote] --json`.
13
+ Quién hace qué: bita mide el tiempo, recap graba y escribe la minuta, inkwell
14
+ guarda la documentación, tally vuelca el tiempo a Jira y atl habla con Jira y
15
+ Confluence. Este es el único lugar donde se describe el flujo de una reunión;
16
+ las demás herramientas enlazan aquí.
16
17
 
17
18
  Todos los comandos aceptan `--json` y devuelven un sobre
18
19
  `{schemaVersion, ok, command, data, error}`. Usa `--json` siempre que vayas a
19
20
  leer el resultado; el texto plano es para mostrarlo tal cual.
20
21
 
21
- ## Modos
22
+ Fuera de macOS no se puede grabar (`start` falla con `CAPTURE_UNAVAILABLE`):
23
+ la reunión se graba con otra app y se procesa con
24
+ `recap import <archivo> [--title "…"] [--in-person|--remote] --json`.
22
25
 
23
- Hay que elegir el modo siempre; no tiene valor por defecto.
26
+ ## Modo
27
+
28
+ Hay que elegirlo siempre; no tiene valor por defecto.
24
29
 
25
30
  | Modo | Flag | Cuándo |
26
31
  |---|---|---|
@@ -28,132 +33,49 @@ Hay que elegir el modo siempre; no tiene valor por defecto.
28
33
  | Presencial | `--in-person` | En sala, en oficina, en persona, junta física, comida, "aquí con" |
29
34
 
30
35
  En remota se graba pantalla, audio del sistema y micrófono; en presencial solo
31
- el micrófono, sin pantalla ni capturas. Si el contexto no deja claro cuál es,
32
- pregunta con una sola línea ("¿remota o presencial?") antes de arrancar.
33
-
34
- ## Ciclo
35
-
36
- ```sh
37
- recap start --remote "Planeación sprint 42"
38
- recap status
39
- recap stop
40
- ```
41
-
42
- - `start` falla con `ALREADY_RECORDING` si ya hay una grabación activa: no
43
- arranques otra, avísalo.
44
- - `stop` cierra el archivo y lanza el procesamiento en segundo plano
45
- (transcripción, capturas y minuta). Tarda de segundos a unos minutos según la
46
- duración. `recap status` y `recap show <id>` dicen en qué etapa va.
47
- - `recap discard` descarta la grabación activa y borra su carpeta. Solo si el
48
- usuario lo pide explícitamente.
49
-
50
- ## Liberar espacio
51
-
52
- Solo si el usuario lo pide; todos aceptan `<id>` o `--bita-entry <id>` y `--json`.
53
-
54
- ```sh
55
- recap compress-video <id> --preset light|medium|max # recomprime el video en HEVC (solo remotas)
56
- recap strip-video <id> # quita el video y deja recording.m4a con ambas pistas de audio
57
- recap prune <id> --intermediates # borra mic.wav, system.wav, transcript-mic/system.json y live/chunks
58
- recap delete <id> # borra la carpeta completa de la reunión
59
- ```
60
-
61
- `compress-video` y `strip-video` aceptan `--prune-intermediates`. Fallan con
62
- `NOT_REMOTE` en reuniones presenciales, `NO_VIDEO` si ya no hay video,
63
- `MEETING_ACTIVE` mientras se graba y `NOT_SMALLER` si la compresión no ahorra
64
- espacio (el original se conserva). `recap list --json` trae `storage` y
65
- `hasVideo` de cada reunión.
66
-
67
- Errores típicos y qué hacer:
68
-
69
- | Código | Causa | Acción |
70
- |---|---|---|
71
- | `SCREEN_DENIED` | Recap.app sin permiso de grabación de pantalla | Pide activarlo en Configuración > Privacidad y seguridad > Grabación de pantalla y audio del sistema |
72
- | `MICROPHONE_DENIED` | Sin permiso de micrófono | Igual, en Micrófono |
73
- | `RECORDER_TIMEOUT` | Un diálogo de permisos quedó esperando | `recap setup` |
74
- | `DEPENDENCY_MISSING`, `MODEL_MISSING` | Falta ffmpeg, whisper-cli, claude o el modelo | `recap setup --install-deps` |
75
- | `CAPTURE_UNAVAILABLE` | No hay grabador en este equipo (Windows, Linux, o falta Recap.app) | Graba con otra app y usa `recap import <archivo>`; en mac, `recap setup` |
76
-
77
- ## Con bita
78
-
79
- Si `recap status --json` trae `data.bitaLinked: true` (recap está suscrito a
80
- los eventos de bita desde `recap setup`), **arranca y para las reuniones desde
81
- bita**, no desde recap: así el tiempo queda medido y la grabación sigue al
82
- contador.
83
-
84
- ```sh
85
- bita start "<título>" --kind remote-meeting
86
- bita start "<título>" --kind in-person-meeting
87
- bita stop <id> --json # sin --did y sin escribir la página
88
- recap wait --bita-entry <id> --json # espera a que todo quede listo
89
- ```
90
-
91
- Al parar, recap hace todo lo demás sin que nadie lo pida:
92
-
93
- - transcribe y escribe la minuta;
94
- - le pone al contador un título real, si el que tenía era genérico;
95
- - le asigna el proyecto si no tenía y la conversación lo deja claro;
96
- - crea la página en ese proyecto, o agrega una sección a la que ya tenía, y la escribe;
97
- - pasa pendientes y preguntas abiertas al backlog;
98
- - deja la minuta en la sección «Reunión» de la entrada.
99
-
100
- El resultado está en `data.wrapup` de `recap wait`. Si `wrapup.projectResolved`
101
- es false, pregunta el proyecto y aplícalo con `bita amend`, y mueve la página y
102
- el backlog con inkwell si está instalado (`inkwell page move --project`,
103
- `inkwell backlog edit --project`) o, si no, con `bita docs page move --project`
104
- y `bita backlog edit --project`.
105
-
106
- Las páginas, las propuestas y el backlog se escriben con inkwell cuando está
107
- instalado y ya migró los documentos de bita; si no, con `bita docs`/`bita backlog`.
108
-
109
- ## En vivo
110
-
111
- Mientras se graba, recap transcribe por tramos de 5 a 20 s en
112
- `live/transcript.jsonl` (una línea `{startMs, endMs, channel, text}`; `mic` es
113
- Sala y `system` Remotos). Es aproximada: la transcripción de después del stop
114
- (`transcript.md`) sigue siendo la fuente de verdad.
115
-
116
- Para responder una pregunta que hicieron en la reunión, con las páginas de bita
117
- y los repos del proyecto (`bita project repo ls`):
118
-
119
- ```sh
120
- recap ask --active --json # la última pregunta de la transcripción
121
- recap ask --active --question "¿cómo se despliega X?" --json
122
- recap ask --meeting <id> --question "..." --json-stream # eventos question/progress/delta/source/done
123
- recap ask --sources --project <p> --json # qué consultaría, sin llamar a claude
124
- ```
125
-
126
- La respuesta lleva `found` y `sources` (página, `archivo:línea` o commit) y se
127
- guarda en `live/answers.jsonl`. Con `found: false` no está documentado: no lo
128
- completes por tu cuenta. Se configura con `recap config get|set`
129
- (`live.enabled`, `live.openWindow`, `live.proposals`, `live.maxChunkSeconds`,
130
- `live.assistModel`, `live.autoAsk`, `live.autoAskModel`,
131
- `live.autoAskMinSeconds`).
132
-
133
- Con `live.autoAsk` (encendido por omisión), el live-worker detecta solo las
134
- preguntas técnicas de la reunión y las responde sin que nadie las pida: esas
135
- respuestas llevan `auto: true` en `live/answers.jsonl`. Mientras se responde
136
- algo, `live/asking.json` dice qué pregunta va (`{question, startedAt, auto}`);
137
- solo corre una respuesta a la vez y una manual detiene a la automática.
138
-
139
- ## Cambios propuestos a la documentación
140
-
141
- Si la reunión está ligada a bita, la etapa `proposals` (antes de `wrapup`)
142
- detecta los cambios explícitos y firmes a páginas que ya existen y los deja en
143
- la rama `proposal/meeting-<entrada>` de los docs, sin tocar `main`. Ideas,
144
- dudas y lo que se corrigió después no entran.
145
-
146
- ```sh
147
- recap proposals ls <id> --json # o --bita-entry <id>
148
- recap proposals show <id> <n> --json # markdown, citas y diff
149
- recap proposals accept <id> <n> [--md <archivo editado>] --json
150
- recap proposals reject <id> <n> --json
151
- ```
152
-
153
- Acepta o rechaza **solo cuando el usuario lo pida**. Si `accept` deja la
154
- propuesta en `stale`, la página cambió desde entonces: edítala sobre la versión
155
- actual y acéptala con `--md`. Cuando no queda ninguna pendiente, recap borra la
156
- rama. Usa `/recap-proposals` para revisarlas en la sesión.
36
+ el micrófono. Si el contexto no deja claro cuál es, pregunta con una sola línea
37
+ ("¿remota o presencial?") antes de arrancar.
38
+
39
+ ## Flujo de una reunión
40
+
41
+ 1. **Ver el estado**: `recap status --json`. Si ya se está grabando
42
+ (`ALREADY_RECORDING` al arrancar), no arranques otra: avísalo.
43
+ 2. **Arrancar.** Si `data.bitaLinked` es true, recap sigue a los cronómetros
44
+ de bita: arranca por bita para que el tiempo también quede medido.
45
+
46
+ ```sh
47
+ bita start "<título>" --kind remote-meeting # o --kind in-person-meeting
48
+ recap start --remote "<título>" # solo si bitaLinked es false
49
+ ```
50
+
51
+ 3. **Durante la reunión**: `recap ask --active --json` responde la última
52
+ pregunta con las páginas de inkwell y los repos del proyecto. Detalle en
53
+ [live.md](live.md).
54
+ 4. **Parar.** Con cronómetro, para el cronómetro y espera; sin él, para recap:
55
+
56
+ ```sh
57
+ bita stop <id> --json && recap wait --bita-entry <id> --json
58
+ recap stop --json && recap wait --json
59
+ ```
60
+
61
+ `stop` lanza el procesamiento en segundo plano (transcripción, capturas,
62
+ minuta y cierre). Tarda de uno a cinco minutos por hora de reunión: corre
63
+ `recap wait` con un timeout amplio. El monitor del plugin de Claude Code
64
+ avisa en la sesión cuando termina, así que no hace falta sondear.
65
+ 5. **Cierre automático** (con bita e inkwell): recap le pone título y proyecto
66
+ al cronómetro, crea o completa la página en inkwell, pasa pendientes y
67
+ hallazgos al backlog y deja la minuta en la nota de la entrada. El
68
+ resultado viene en `data.wrapup` de `recap wait`; si
69
+ `wrapup.projectResolved` es false, pregunta el proyecto. Detalle y
70
+ correcciones en [wrapup.md](wrapup.md).
71
+ 6. **Después**: los cambios que la reunión propuso a la documentación se
72
+ revisan con `/recap-proposals` ([proposals.md](proposals.md)), y el tiempo
73
+ de la reunión llega a Jira con tally, no desde recap.
74
+
75
+ Cambiar el título o el proyecto del cronómetro mientras se graba
76
+ (`bita amend`) no corta la grabación; solo un cambio de `--kind` que cruce la
77
+ línea de reunión la arranca o la detiene. `recap discard` borra la grabación
78
+ activa: solo si el usuario lo pide.
157
79
 
158
80
  ## Consultar reuniones
159
81
 
@@ -166,17 +88,35 @@ recap show <id> --path
166
88
  Para responder qué se habló o qué se acordó, lee `summary.md` de las reuniones
167
89
  que apliquen. Si hace falta precisión (una cifra, quién dijo qué), ve a
168
90
  `transcript.md`, que lleva el minuto de cada párrafo. Cita la reunión y el
169
- minuto en la respuesta. En reuniones remotas, **Sala** es el micrófono local y
170
- **Remotos** el audio de la llamada; no hay separación por persona.
91
+ minuto. En remotas, **Sala** es el micrófono local y **Remotos** el audio de la
92
+ llamada; no hay separación por persona.
171
93
 
172
94
  ## Regenerar la minuta
173
95
 
174
96
  - Sin intervención: `recap process <id> --from summarize`.
175
- - Dentro de la sesión, cuando el usuario quiere dirigir el enfoque o corregir
176
- algo: usa `/recap-summarize`.
97
+ - Dentro de la sesión, cuando el usuario quiere dirigir el enfoque: `/recap-summarize`.
98
+ - Si una etapa falló, `recap process <id>` retoma desde ahí.
99
+
100
+ ## Errores típicos
101
+
102
+ | Código | Causa | Acción |
103
+ |---|---|---|
104
+ | `SCREEN_DENIED` | Recap.app sin permiso de grabación de pantalla | Configuración > Privacidad y seguridad > Grabación de pantalla y audio del sistema |
105
+ | `MICROPHONE_DENIED` | Sin permiso de micrófono | Igual, en Micrófono |
106
+ | `RECORDER_TIMEOUT` | Un diálogo de permisos quedó esperando | `recap setup` |
107
+ | `DEPENDENCY_MISSING`, `MODEL_MISSING` | Falta ffmpeg, whisper-cli, claude o el modelo | `recap setup --install-deps` |
108
+ | `CAPTURE_UNAVAILABLE` | No hay grabador en este equipo | Graba con otra app y usa `recap import <archivo>`; en mac, `recap setup` |
109
+
110
+ ## Más detalle, bajo demanda
111
+
112
+ - [wrapup.md](wrapup.md): qué hace el cierre con bita e inkwell, sin inkwell, y cómo corregir el proyecto.
113
+ - [live.md](live.md): transcripción en vivo, `recap ask` y su configuración.
114
+ - [proposals.md](proposals.md): cambios propuestos a la documentación.
115
+ - [storage.md](storage.md): liberar espacio (comprimir, quitar video, borrar).
177
116
 
178
117
  ## Lo que no hace
179
118
 
180
119
  - No separa hablantes por persona: los nombres salen del contexto.
181
120
  - No sube nada a la nube salvo la transcripción que lee Claude al resumir, al
182
121
  responder con `recap ask` y al buscar cambios propuestos.
122
+ - No toca Jira.
@@ -0,0 +1,31 @@
1
+ # En vivo
2
+
3
+ Mientras se graba, recap transcribe por tramos de 5 a 20 s en
4
+ `live/transcript.jsonl` (una línea `{startMs, endMs, channel, text}`; `mic` es
5
+ Sala y `system` Remotos). Es aproximada: la transcripción de después del stop
6
+ (`transcript.md`) sigue siendo la fuente de verdad.
7
+
8
+ Para responder una pregunta que hicieron en la reunión, con las páginas de
9
+ inkwell y los repos del proyecto (`bita project repo ls`); sin inkwell responde
10
+ solo con los repos:
11
+
12
+ ```sh
13
+ recap ask --active --json # la última pregunta de la transcripción
14
+ recap ask --active --question "¿cómo se despliega X?" --json
15
+ recap ask --meeting <id> --question "..." --json-stream # eventos question/progress/delta/source/done
16
+ recap ask --sources --project <p> --json # qué consultaría, sin llamar a claude
17
+ ```
18
+
19
+ La respuesta lleva `found` y `sources` (página, `archivo:línea` o commit) y se
20
+ guarda en `live/answers.jsonl`. Con `found: false` no está documentado: no lo
21
+ completes por tu cuenta.
22
+
23
+ Con `live.autoAsk` (encendido por omisión), el live-worker detecta solo las
24
+ preguntas técnicas de la reunión y las responde sin que nadie las pida: esas
25
+ respuestas llevan `auto: true` en `live/answers.jsonl`. Mientras se responde
26
+ algo, `live/asking.json` dice qué pregunta va (`{question, startedAt, auto}`);
27
+ una respuesta manual detiene a la automática.
28
+
29
+ Se configura con `recap config get|set`: `live.enabled`, `live.openWindow`,
30
+ `live.proposals`, `live.maxChunkSeconds`, `live.assistModel`, `live.autoAsk`,
31
+ `live.autoAskModel`, `live.autoAskMinSeconds` y `live.autoAskConcurrency`.
@@ -0,0 +1,19 @@
1
+ # Cambios propuestos a la documentación
2
+
3
+ Si la reunión está ligada a bita e inkwell está instalado, la etapa `proposals`
4
+ (antes de `wrapup`) detecta los cambios explícitos y firmes a páginas de
5
+ inkwell que ya existen y los deja en la rama `proposal/meeting-<entrada>` de
6
+ sus docs (`inkwell git propose`), sin tocar `main`. Ideas, dudas y lo que se
7
+ corrigió después no entran.
8
+
9
+ ```sh
10
+ recap proposals ls <id> --json # o --bita-entry <id>
11
+ recap proposals show <id> <n> --json # markdown, citas y diff
12
+ recap proposals accept <id> <n> [--md <archivo editado>] --json
13
+ recap proposals reject <id> <n> --json
14
+ ```
15
+
16
+ Acepta o rechaza **solo cuando el usuario lo pida**. Si `accept` deja la
17
+ propuesta en `stale`, la página cambió desde entonces: edítala sobre la versión
18
+ actual y acéptala con `--md`. Cuando no queda ninguna pendiente, recap borra la
19
+ rama. `/recap-proposals` las revisa una por una en la sesión.
@@ -0,0 +1,16 @@
1
+ # Liberar espacio
2
+
3
+ Solo si el usuario lo pide. Todos aceptan `<id>` o `--bita-entry <id>` y `--json`.
4
+
5
+ ```sh
6
+ recap compress-video <id> --preset light|medium|max # recomprime el video en HEVC (solo remotas)
7
+ recap strip-video <id> # quita el video y deja recording.m4a con ambas pistas de audio
8
+ recap prune <id> --intermediates # borra mic.wav, system.wav, transcript-mic/system.json y live/chunks
9
+ recap delete <id> # borra la carpeta completa de la reunión
10
+ ```
11
+
12
+ `compress-video` y `strip-video` aceptan `--prune-intermediates`. Fallan con
13
+ `NOT_REMOTE` en reuniones presenciales, `NO_VIDEO` si ya no hay video,
14
+ `MEETING_ACTIVE` mientras se graba y `NOT_SMALLER` si la compresión no ahorra
15
+ espacio (el original se conserva). `recap list --json` trae `storage` y
16
+ `hasVideo` de cada reunión.
@@ -0,0 +1,49 @@
1
+ # Cierre de una reunión con bita e inkwell
2
+
3
+ Si `recap status --json` trae `data.bitaLinked: true` (recap está suscrito a
4
+ los eventos de bita desde `recap setup`), las reuniones se arrancan y se paran
5
+ desde bita. recap solo le pide a bita `projects`, `project repo ls`,
6
+ `entries get` y `amend` (título y proyecto).
7
+
8
+ Al parar el cronómetro, recap hace todo lo demás sin que nadie lo pida:
9
+
10
+ - transcribe y escribe la minuta;
11
+ - le pone al cronómetro un título real, si el que tenía era genérico (`bita amend`);
12
+ - le asigna el proyecto si no tenía y la conversación lo deja claro (`bita amend`);
13
+ - crea la página en inkwell, ligada a la entrada (`inkwell page new --from-entry`),
14
+ o agrega una sección a la que ya tenía (`inkwell page ls --entry`), y la escribe;
15
+ - pasa pendientes y hallazgos al backlog de inkwell (`inkwell backlog add`);
16
+ - deja la minuta en la sección «Reunión» de la nota de la entrada
17
+ (`inkwell note save <entrada> --section Reunión --md …`).
18
+
19
+ El resultado está en `data.wrapup` de `recap wait`: `title`, `project`,
20
+ `projectResolved`, `pageId`, `pageCreated` y `backlogKeys`.
21
+
22
+ ## Corregir el proyecto
23
+
24
+ Si `wrapup.projectResolved` es false, pregunta solo «¿De qué proyecto fue?» y,
25
+ con la respuesta:
26
+
27
+ ```sh
28
+ bita amend <entryId> --project <X>
29
+ inkwell page move <pageId> --project <X>
30
+ inkwell backlog edit <CLAVE> --project <X> # una por cada valor de wrapup.backlogKeys
31
+ ```
32
+
33
+ ## Sin inkwell, o con un inkwell viejo
34
+
35
+ La reunión igual queda procesada: las etapas `proposals` y `wrapup` quedan en
36
+ `skipped` con el motivo en `stages.<etapa>.reason` y la pista
37
+ `npm i -g @kikedealba/inkwell && inkwell setup`; la minuta se queda en
38
+ `summary.md` de la carpeta de la reunión. Si inkwell no tiene la capacidad
39
+ `docs.entry-notes`, se escriben la página y el backlog, pero la minuta no pasa
40
+ a la nota (queda anotado en `process.log`). Después de instalar o actualizar
41
+ inkwell, `recap process <id> --from proposals` lo completa.
42
+
43
+ ## Qué responder al usuario
44
+
45
+ - Título y duración.
46
+ - Con bita e inkwell: el proyecto, la página (`wrapup.pageId`) y cuántos
47
+ pendientes y hallazgos quedaron en el backlog.
48
+ - Sin bita o sin inkwell: los **Acuerdos** y **Pendientes** de la minuta, tal cual.
49
+ - Si una etapa falló, su error y la sugerencia `recap process <id>`.
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: recap-ask
2
3
  description: Responde la última pregunta de la reunión en curso con las páginas y repos del proyecto
3
4
  argument-hint: [pregunta, opcional]
4
5
  allowed-tools: Bash(recap ask:*)
6
+ disable-model-invocation: true
5
7
  ---
6
8
 
7
9
  Responde en vivo una pregunta de la reunión que se está grabando.
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: recap-list
2
3
  description: Lista las reuniones grabadas, o muestra la minuta de una
3
4
  argument-hint: [id, parte del título o "last"]
4
5
  allowed-tools: Bash(recap list:*), Bash(recap show:*)
6
+ disable-model-invocation: true
5
7
  ---
6
8
 
7
9
  **Sin `$ARGUMENTS`**, lista las reuniones recientes:
@@ -1,12 +1,16 @@
1
1
  ---
2
+ name: recap-proposals
2
3
  description: Revisa los cambios a la documentación que propuso una reunión
3
4
  argument-hint: [id de la reunión o "last"]
4
5
  allowed-tools: Bash(recap proposals:*), Bash(recap show:*), Read, Write
6
+ disable-model-invocation: true
5
7
  ---
6
8
 
7
9
  Al cerrar una reunión ligada a bita, recap deja los cambios explícitos a
8
- páginas existentes como propuestas en la rama `proposal/meeting-<entrada>` de
9
- los docs. Nada llega a `main` sin aceptarse.
10
+ páginas existentes de inkwell como propuestas en la rama
11
+ `proposal/meeting-<entrada>` de sus docs. Nada llega a `main` sin aceptarse.
12
+ Aceptar o rechazar necesita inkwell instalado (`npm i -g @kikedealba/inkwell &&
13
+ inkwell setup`).
10
14
 
11
15
  1. La reunión es `$ARGUMENTS`, o `last` si viene vacío. Lista sus propuestas:
12
16
 
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: recap-start
2
3
  description: Empieza a grabar una reunión (remota o presencial)
3
4
  argument-hint: [remota|presencial] [título]
4
5
  allowed-tools: Bash(recap start:*), Bash(recap status:*), Bash(recap import:*), Bash(bita start:*)
6
+ disable-model-invocation: true
5
7
  ---
6
8
 
7
9
  Estado actual:
@@ -1,6 +1,8 @@
1
1
  ---
2
+ name: recap-status
2
3
  description: Muestra si se está grabando y en qué va la última reunión
3
4
  allowed-tools: Bash(recap status:*)
5
+ disable-model-invocation: true
4
6
  ---
5
7
 
6
8
  !`recap status --json`
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: recap-stop
3
+ description: Termina la grabación y deja la reunión documentada
4
+ argument-hint: [--no-wait para no esperar el resultado]
5
+ allowed-tools: Bash(recap stop:*), Bash(recap status:*), Bash(recap wait:*), Bash(recap show:*), Bash(bita stop:*), Bash(bita ls:*), Bash(bita amend:*), Bash(inkwell page move:*), Bash(inkwell backlog edit:*)
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ Detén la grabación activa y deja la reunión documentada.
10
+
11
+ Grabación actual:
12
+
13
+ !`recap status --json`
14
+
15
+ Si no hay grabación activa (`NOT_RECORDING`), dilo en una línea y termina.
16
+
17
+ ## Si la grabación sigue a un cronómetro de bita
18
+
19
+ Cuando `data.active.bitaEntryId` trae un id, para **el cronómetro**, no recap,
20
+ sin escribir la página:
21
+
22
+ ```
23
+ bita stop <id> --json
24
+ recap wait --bita-entry <id> --json
25
+ ```
26
+
27
+ El evento de bita detiene la grabación y recap hace lo demás: transcribe,
28
+ escribe la minuta, le pone título y proyecto al cronómetro (`bita amend`),
29
+ crea o completa la página en inkwell, pasa pendientes y hallazgos al backlog
30
+ de inkwell y deja la minuta en la nota de la entrada.
31
+
32
+ Sin inkwell, las etapas `proposals` y `wrapup` quedan en `skipped` con su
33
+ motivo (`stages.wrapup.reason`) y la minuta se queda en la carpeta de la
34
+ reunión; sugiere `npm i -g @kikedealba/inkwell && inkwell setup` y después
35
+ `recap process <id> --from proposals`.
36
+
37
+ `recap wait` tarda de uno a cinco minutos en una reunión de una hora: córrelo con
38
+ un timeout amplio (15 min). Si después de un minuto `recap status` sigue
39
+ grabando, el evento no llegó: párala con `recap stop`.
40
+
41
+ ## Si es una grabación suelta
42
+
43
+ ```
44
+ recap stop --json
45
+ recap wait --json
46
+ ```
47
+
48
+ ## Qué responder
49
+
50
+ Con el resultado de `recap wait` responde:
51
+
52
+ - título y duración;
53
+ - con bita e inkwell: el proyecto, la página (`wrapup.pageId`) y cuántos
54
+ pendientes y hallazgos quedaron en el backlog;
55
+ - sin bita o sin inkwell: los **Acuerdos** y **Pendientes** de la minuta, tal cual.
56
+
57
+ Si una etapa falló, muestra su error y sugiere `recap process <id>`: retoma
58
+ desde la etapa que falló.
59
+
60
+ **Si `wrapup.projectResolved` es false**, pregunta solo «¿De qué proyecto fue?»
61
+ y, con la respuesta:
62
+
63
+ ```
64
+ bita amend <entryId> --project <X>
65
+ inkwell page move <pageId> --project <X>
66
+ inkwell backlog edit <CLAVE> --project <X> # una por cada valor de wrapup.backlogKeys
67
+ ```
68
+
69
+ El tiempo de la reunión se vuelca a Jira después con tally, no desde aquí.
70
+
71
+ Con `--no-wait` en `$ARGUMENTS`, solo para y responde en una línea que el
72
+ procesamiento sigue en segundo plano. El monitor del plugin avisa en la sesión
73
+ cuando la reunión termine de procesarse.
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: recap-summarize
2
3
  description: Rehace la minuta de una reunión dentro de la sesión, con indicaciones
3
4
  argument-hint: [id o "last"] [indicaciones, p. ej. "enfócate en los riesgos"]
4
5
  allowed-tools: Bash(recap prompt:*), Bash(recap save-summary:*), Bash(recap show:*), Read, Write
6
+ disable-model-invocation: true
5
7
  ---
6
8
 
7
9
  Rehaz la minuta de una reunión aquí mismo, en lugar del `claude -p` sin