ganbatte-os 0.2.26 → 0.2.27

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.
@@ -1,16 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // slack-notify.js — Slack notification tool via Incoming Webhooks (zero-dep)
3
+ // slack-notify.js — Slack notification tool (zero-dep)
4
4
  // Usage: node slack-notify.js <command> [--options]
5
- // Auth: SLACK_WEBHOOK_URL env var (Incoming Webhook URL)
5
+ // Auth:
6
+ // - Webhook commands (send, task-done, sprint-summary): SLACK_WEBHOOK_URL
7
+ // - Web API commands (fetch-thread, find-thread): SLACK_BOT_TOKEN
6
8
 
7
9
  const WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL
8
-
9
- if (!WEBHOOK_URL) {
10
- // Graceful skip — no webhook configured is not an error
11
- console.log(JSON.stringify({ skipped: true, reason: 'SLACK_WEBHOOK_URL not set' }))
12
- process.exit(0)
13
- }
10
+ const BOT_TOKEN = process.env.SLACK_BOT_TOKEN
14
11
 
15
12
  function parseArgs(argv) {
16
13
  const result = { _: [] }
@@ -35,6 +32,19 @@ function parseArgs(argv) {
35
32
  const args = parseArgs(process.argv.slice(2))
36
33
  const [cmd, sub, ...rest] = args._
37
34
 
35
+ const WEBHOOK_COMMANDS = new Set(['send', 'task-done', 'sprint-summary'])
36
+ const API_COMMANDS = new Set(['fetch-thread', 'find-thread'])
37
+
38
+ if (WEBHOOK_COMMANDS.has(cmd) && !WEBHOOK_URL) {
39
+ console.log(JSON.stringify({ skipped: true, reason: 'SLACK_WEBHOOK_URL not set' }))
40
+ process.exit(0)
41
+ }
42
+
43
+ if (API_COMMANDS.has(cmd) && !BOT_TOKEN) {
44
+ console.log(JSON.stringify({ error: 'SLACK_BOT_TOKEN not set (required for Web API commands)' }))
45
+ process.exit(0)
46
+ }
47
+
38
48
  async function sendWebhook(payload) {
39
49
  if (args['dry-run']) {
40
50
  return { _dry_run: true, url: WEBHOOK_URL.replace(/\/[^/]{6,}$/, '/***'), payload }
@@ -53,6 +63,22 @@ async function sendWebhook(payload) {
53
63
  return { sent: false, status: res.status, body: text }
54
64
  }
55
65
 
66
+ async function slackApi(method, params) {
67
+ if (args['dry-run']) {
68
+ return { _dry_run: true, method, params: { ...params, token: '***' } }
69
+ }
70
+ const body = new URLSearchParams(params).toString()
71
+ const res = await fetch(`https://slack.com/api/${method}`, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'Content-Type': 'application/x-www-form-urlencoded',
75
+ 'Authorization': `Bearer ${BOT_TOKEN}`,
76
+ },
77
+ body,
78
+ })
79
+ return res.json()
80
+ }
81
+
56
82
  function escapeSlack(text) {
57
83
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
58
84
  }
@@ -155,6 +181,55 @@ async function main() {
155
181
  break
156
182
  }
157
183
 
184
+ case 'fetch-thread': {
185
+ const channel = args['channel-id'] || process.env.SLACK_CHANNEL_ID_WEEKLY
186
+ const ts = args['thread-ts'] || args.ts
187
+ if (!channel || !ts) {
188
+ result = { error: '--channel-id (or SLACK_CHANNEL_ID_WEEKLY) and --thread-ts required' }
189
+ break
190
+ }
191
+ const limit = args.limit || '100'
192
+ const api = await slackApi('conversations.replies', { channel, ts, limit })
193
+ if (!api.ok) {
194
+ result = { ok: false, error: api.error, needed: api.needed }
195
+ break
196
+ }
197
+ const messages = (api.messages || []).map(m => ({
198
+ user: m.user,
199
+ ts: m.ts,
200
+ text: m.text || '',
201
+ reply_count: m.reply_count,
202
+ }))
203
+ result = { ok: true, count: messages.length, messages }
204
+ break
205
+ }
206
+
207
+ case 'find-thread': {
208
+ const channel = args['channel-id'] || process.env.SLACK_CHANNEL_ID_WEEKLY
209
+ const pattern = args.pattern
210
+ if (!channel || !pattern) {
211
+ result = { error: '--channel-id (or SLACK_CHANNEL_ID_WEEKLY) and --pattern required' }
212
+ break
213
+ }
214
+ const api = await slackApi('conversations.history', { channel, limit: args.limit || '50' })
215
+ if (!api.ok) {
216
+ result = { ok: false, error: api.error, needed: api.needed }
217
+ break
218
+ }
219
+ const re = new RegExp(pattern, 'i')
220
+ const match = (api.messages || []).find(m => re.test(m.text || ''))
221
+ if (!match) {
222
+ result = { found: false }
223
+ break
224
+ }
225
+ result = {
226
+ found: true,
227
+ ts: match.ts,
228
+ text_preview: (match.text || '').slice(0, 200),
229
+ }
230
+ break
231
+ }
232
+
158
233
  default:
159
234
  result = {
160
235
  error: cmd ? `Unknown command: ${cmd}` : 'No command provided',
@@ -162,6 +237,12 @@ async function main() {
162
237
  send: 'send --text "Hello *bold* _italic_" | send --blocks-file payload.json',
163
238
  'task-done': 'task-done --task T-001 --commit abc1234 --author "Name" [--sprint "S01"] [--track backend] [--message "feat: ..."]',
164
239
  'sprint-summary': 'sprint-summary --file sprint-status.json',
240
+ 'fetch-thread': 'fetch-thread --channel-id C... --thread-ts 1234567890.123456 [--limit 100]',
241
+ 'find-thread': 'find-thread --channel-id C... --pattern "regex" [--limit 50]',
242
+ },
243
+ auth: {
244
+ webhook_commands: 'send, task-done, sprint-summary — require SLACK_WEBHOOK_URL',
245
+ api_commands: 'fetch-thread, find-thread — require SLACK_BOT_TOKEN (scopes: groups:history for private channels)',
165
246
  },
166
247
  formatting: {
167
248
  bold: '*text*',
@@ -1,5 +1,58 @@
1
1
  # weekly-update — Changelog
2
2
 
3
+ ## 2026-04-20 — v1.2.0 (modos `simple` e `detailed`, controlados por env)
4
+
5
+ Motivação: na thread "Conversa semanal" de 2026-04-17, PO (U07M07M6R42) e PM (U0ADE3FT9HB) pediram explicitamente resumo em linguagem não-técnica. Citações literais:
6
+
7
+ - PM: "consegue trazer um resumo em linguem nao técnica pra mim ahaha, por favor?"
8
+ - PO: "Pensa que está em uma reunião com a gente, o que você compartilharia?"
9
+
10
+ ### Novo argumento e env var
11
+
12
+ - `$ARGUMENTS --mode simple|detailed` sobrescreve via linha de comando.
13
+ - `WEEKLY_UPDATE_MODE=simple|detailed` no `.env` define o padrão do projeto.
14
+ - Se ambos ausentes, fallback para `detailed` (comportamento anterior).
15
+ - Skill não pergunta mais — resolve via env/arg e informa o modo no início da Phase 2.
16
+
17
+ ### Modo `simple` (novo)
18
+
19
+ Audiência não-técnica (PO/PM/clientes). Regras obrigatórias:
20
+
21
+ 1. Zero IDs de task (nada de T-084, T-110).
22
+ 2. Zero jargão técnico. Lista banida: API, endpoint, FK, hook, webhook, migration, deploy, pipeline, CI/CD, TypeScript, Zod, backend, frontend, SPA routing, commit, branch, merge, PR, rebase, drift, build, pre-commit.
23
+ 3. Bullets curtos em vez de parágrafos densos.
24
+ 4. Foco no valor entregue, não em como foi feito.
25
+ 5. Tom de reunião casual (primeira pessoa, contrações como "pra", "to", "tá").
26
+ 6. Alvo: 150-250 palavras.
27
+
28
+ Calibração humanizer: conversacional (intensidade alta), contra os 30 pontos de padrões AI.
29
+
30
+ ### Modo `detailed` (comportamento anterior, preservado)
31
+
32
+ Audiência técnica (tech lead, outros devs). Permite IDs de task, jargão apropriado, narrativa em parágrafos, dependências explícitas. Alvo: 300-500 palavras.
33
+
34
+ ### Checklist de aprovação expandido
35
+
36
+ Novos itens na Phase 4:
37
+ - Modo confirmado antes do envio.
38
+ - Em `simple`: nenhum termo banido aparece no texto final.
39
+ - Em `simple`: nenhum ID de task (T-XXX) aparece no texto final.
40
+
41
+ ### Arquivos afetados
42
+
43
+ - `.gos/skills/weekly-update/SKILL.md` — Gate 3 novo, Phase 2 refatorada com templates por modo, Phase 3 com calibração por modo, Phase 4 checklist expandido.
44
+ - `.env-example` — nova var `WEEKLY_UPDATE_MODE=simple`.
45
+ - `.env` local Ganbatte — adicionado `WEEKLY_UPDATE_MODE=simple`.
46
+
47
+ ### Companion: `slack-notify.js` ganhou `fetch-thread` e `find-thread`
48
+
49
+ Comandos Web API via `SLACK_BOT_TOKEN` (scope `groups:history` para canais privados). Usado pela skill e disponível para auditoria de threads passadas. Allow-rules adicionadas em `~/.claude/settings.json`:
50
+
51
+ - `Bash(node .gos/scripts/tools/slack-notify.js fetch-thread:*)`
52
+ - `Bash(node .gos/scripts/tools/slack-notify.js find-thread:*)`
53
+
54
+ ---
55
+
3
56
  ## 2026-04-17 — v2.0 (Web API, thread obrigatória, auto-descoberta)
4
57
 
5
58
  Mudanças estruturais motivadas por incidente em produção: o envio via incoming webhook caiu fora da thread "Conversa semanal" e gerou mensagem isolada no canal #cnpq-tech.
@@ -3,15 +3,17 @@ name: weekly-update
3
3
  description: >
4
4
  Gera resumo das tasks concluidas nos ultimos 10 dias no ClickUp, humaniza o texto
5
5
  em pt-BR e posta como resposta na thread "Conversa semanal" do Slack (#cspo-tech).
6
+ Suporta dois modos: simple (curto, ~150 palavras) e detailed (completo, ~400 palavras).
6
7
  Requer aprovacao do usuario antes do envio.
7
8
  description_pt-BR: >
8
9
  Resumo semanal das tasks concluidas, humanizado com anti-AI patterns,
9
- com aprovacao antes de postar na Conversa semanal do Slack.
10
- argument-hint: "[link da mensagem 'Conversa semanal' no Slack (opcional)]"
10
+ com aprovacao antes de postar na Conversa semanal do Slack. Modo simple ou detailed.
11
+ argument-hint: "[--mode simple|detailed] [link da mensagem 'Conversa semanal' no Slack]"
11
12
  type: prompt
12
- version: "1.1.0"
13
+ version: "1.2.0"
13
14
  env:
14
15
  - WEEKLY_UPDATE
16
+ - WEEKLY_UPDATE_MODE
15
17
  - SLACK_WEBHOOK_CSPO_TECH
16
18
  categories: [reporting, slack, clickup, weekly]
17
19
  allowedTools:
@@ -87,6 +89,28 @@ Se o valor retornado for `__MISSING__` ou vazio:
87
89
 
88
90
  Se ambos os gates passarem, prosseguir normalmente.
89
91
 
92
+ ### Gate 3 — Resolver modo de escrita
93
+
94
+ Determinar `MODE` nesta ordem de precedência:
95
+
96
+ 1. **`$ARGUMENTS`** (maior prioridade):
97
+ - Contém `--mode simple` → `MODE=simple`
98
+ - Contém `--mode detailed` → `MODE=detailed`
99
+ 2. **Variável de ambiente `WEEKLY_UPDATE_MODE`** (default do projeto):
100
+ ```bash
101
+ echo "${WEEKLY_UPDATE_MODE:-}"
102
+ ```
103
+ - Valor `simple` → `MODE=simple`
104
+ - Valor `detailed` → `MODE=detailed`
105
+ - Vazio ou qualquer outro valor → cair para passo 3
106
+ 3. **Fallback final**: `MODE=detailed`.
107
+
108
+ **Não perguntar ao usuário.** O padrão fica no `.env` (recomendado: `WEEKLY_UPDATE_MODE=simple` alinhado ao pedido da PO/PM na thread "Conversa semanal"). Só sobrescrever via argumento quando precisar de um envio técnico pontual.
109
+
110
+ Informar ao usuário o modo resolvido no início da Phase 2 (ex: "Modo resolvido: `simple` (via env)").
111
+
112
+ **Contexto histórico (por que existem dois modos):** a PO (não-técnica) e o PM (não-técnico) na thread "Conversa semanal" pediram explicitamente "resumo em linguagem não técnica" e "pensa que está em uma reunião com a gente, o que você compartilharia?". O modo `simple` foi desenhado para atender esse pedido. O `detailed` segue para audiência técnica (tech lead, outros devs).
113
+
90
114
  ---
91
115
 
92
116
  ## Phase 1 — Buscar tasks concluidas no ClickUp (ultimos 10 dias)
@@ -112,13 +136,13 @@ Se nenhuma task encontrada: informar ao usuario e encerrar.
112
136
 
113
137
  ## Phase 2 — Gerar resumo pt-BR
114
138
 
115
- ### Template padrao
139
+ O template e as regras variam por `MODE`. Secoes marcadas com (?) sao opcionais — incluir somente se houver conteudo relevante.
116
140
 
117
- O resumo DEVE seguir este template. Secoes marcadas com (?) sao opcionais — incluir somente se houver conteudo relevante.
141
+ ### Template (comum aos dois modos)
118
142
 
119
143
  ```
120
144
  *O que foi feito*
121
- [Narrativa agrupada por tema, tom conversacional]
145
+ [Conteudo conforme regras do modo]
122
146
 
123
147
  *Desafios encontrados* (?)
124
148
  [Dependencias, bloqueios, decisoes dificeis]
@@ -133,17 +157,74 @@ O resumo DEVE seguir este template. Secoes marcadas com (?) sao opcionais — in
133
157
  [Pedidos de alinhamento, decisao, recurso]
134
158
  ```
135
159
 
136
- ### Regras de redacao
160
+ Regras comuns aos dois modos:
161
+ - Sem emojis.
162
+ - Titulos de secao em negrito Slack: `*titulo*`.
163
+ - Uma linha em branco entre titulo e corpo (Slack renderiza colado sem isso).
164
+
165
+ ---
137
166
 
138
- - Agrupar entregas por **tema** (nao listar tasks uma a uma)
139
- - Tom: conversacional, direto, como se estivesse contando para um colega nao tecnico
140
- - Foco em **o que foi entregue e por que importa**, nao em nomes de tasks ou IDs
141
- - Sem jargao tecnico (trocar "API endpoint" por "integracao", "migration" por "ajuste no banco", etc.)
142
- - Sem emojis
143
- - Os titulos das secoes usam negrito Slack: `*titulo*`
167
+ ### Modo `detailed` Relatório executivo (audiência técnica)
144
168
 
145
- Exemplo de tom desejado:
146
- > Nessa semana finalizei a integração do formulário de coleta com validação dos campos obrigatórios, ajustei o layout da tela de dashboard pra funcionar melhor em tablets, e corrigi um problema no fluxo de login que impedia usuários com email corporativo de acessar o sistema.
169
+ Audiência: tech lead, outros devs, stakeholders técnicos.
170
+
171
+ Regras:
172
+ - Permite referenciar **IDs de task** (T-082, T-110, etc.) quando importa para rastreio.
173
+ - Permite **termos técnicos** quando a audiência é técnica (API, FK, hook, TypeScript, deploy, Storybook, webhook).
174
+ - Narrativa em **parágrafos** agrupados por tema; bullets só para listas de tasks ou bloqueios.
175
+ - Explica **decisões** e **trade-offs** quando houver (por que X e não Y).
176
+ - Inclui dependências explícitas ("T-084 depende da T-082 nova").
177
+ - Tamanho alvo: 300-500 palavras.
178
+
179
+ Exemplo de tom (extraído do envio real 2026-04-17):
180
+ > *O que foi feito*
181
+ >
182
+ > A semana concentrou na frente de Storybook e infra de deploy. Criei a conta Vercel pra hospedar o Storybook separado, customizei a home com branding Fractus, corrigi o SPA routing e atualizei o README. Pluguei Vercel Analytics e Speed Insights nos dois apps.
183
+
184
+ ---
185
+
186
+ ### Modo `simple` — Linguagem acessível (audiência não-técnica)
187
+
188
+ Audiência: PO/PM sem background técnico, clientes, time de negócio.
189
+
190
+ Regras **obrigatórias** (violar qualquer uma implica reescrever):
191
+
192
+ 1. **Zero IDs de task.** Nunca mencionar "T-084", "T-110", "task X". Só o que foi entregue, em linguagem de valor.
193
+ 2. **Zero jargão técnico.** Banidos: API, endpoint, FK, foreign key, hook, webhook, migration, deploy, pipeline, CI/CD, TypeScript, Zod, Storybook (a menos que seja nome conhecido do produto), backend, frontend, regex, SPA routing, commit, branch, merge, PR, rebase, drift, build. Traduzir cada um:
194
+ - "API de diagnóstico" → "formulário de diagnóstico" ou "ferramenta pra coletar dados do diagnóstico"
195
+ - "Deploy no Vercel" → "publicar a página online"
196
+ - "Corrigi o SPA routing" → "arrumei a navegação entre telas que estava quebrada"
197
+ - "Hook de pre-commit" → "trava automática que impede erros de passar"
198
+ - "Migration" → "ajuste no banco de dados" (se precisar) ou omitir
199
+ - "Regras de negócio em doc próprio" → "organizei as regras do produto num documento pra validar com o time"
200
+ 3. **Bullets curtos**, não parágrafos densos. Cada bullet com 1 linha, no máximo 2. Inspirado no tom do PM do time:
201
+ > • Sigo trabalhando nas telas do figma.
202
+ > • Nessa semana liberei PRD Novo e novo padrão no Figma.
203
+ > • Agora to trabalhando nuns ajustes que o Adri pediu.
204
+ 4. **Foco no valor entregue**, não em como foi feito. Em vez de "configurei X e corrigi Y", usar "agora Z funciona / está disponível / está mais rápido".
205
+ 5. **Tom de reunião casual.** Primeira pessoa (eu/a gente), contrações permitidas ("pra", "to", "tá"), sem formalismo corporativo.
206
+ 6. Tamanho alvo: 150-250 palavras.
207
+
208
+ Teste de validação (aplicar mentalmente antes de aprovar):
209
+ - Se minha mãe lesse esse texto, ela entenderia o que eu fiz essa semana? Se não, reescrever.
210
+ - Se o PO leu e vai perguntar "o que é X?" — termo X é jargão, trocar.
211
+
212
+ Exemplo de tom (re-escrita do envio 2026-04-17 em modo simple):
213
+ > *O que foi feito*
214
+ >
215
+ > • Coloquei o catálogo de componentes do Fractus online, com a cara nova (logo e cores).
216
+ > • Organizei as regras do produto num documento próprio pra gente revisar junto na reunião de terça.
217
+ > • Arrumei umas travas automáticas que impedem erros de passar adiante — ficou mais seguro pra todo mundo mexer no código.
218
+ > • Entreguei pro Adriano Morais a ferramenta que extrai contatos de grupo do WhatsApp.
219
+ >
220
+ > *Pendências*
221
+ >
222
+ > Cinco coisas dependem de decisão da PO/PM pra destravar:
223
+ > 1. Templates/Formulários — aguardando validação do Adriano Morais.
224
+ > 2. Impacto — precisa definir quais métricas entram no MVP.
225
+ > 3. Faturamento — regra de consolidação ainda em aberto.
226
+ > 4. Acesso do Financiador — permissões não definidas.
227
+ > 5. Toggle Ativo — comportamento ao desativar ainda não documentado.
147
228
 
148
229
  ## Phase 3 — Humanizar
149
230
 
@@ -166,10 +247,18 @@ Carregar e aplicar:
166
247
  1. `.gos/libraries/content/ai-writing-patterns.md` — catálogo de 26 padrões
167
248
  2. `.gos/skills/humanizer/SKILL.md` — processo de 5 passos
168
249
 
169
- Calibração: **Relatório executivo** (intensidade média)
170
- - Cortar enchimento e inflação
171
- - Manter formalidade leve
172
- - Não forçar primeira pessoa se não couber
250
+ Calibração varia por `MODE`:
251
+
252
+ - **`detailed`** **Relatório executivo** (intensidade média)
253
+ - Cortar enchimento e inflação
254
+ - Manter formalidade leve
255
+ - Não forçar primeira pessoa se não couber
256
+
257
+ - **`simple`** — **Conversacional casual** (intensidade alta)
258
+ - Primeira pessoa obrigatória (eu/a gente)
259
+ - Contrações permitidas e encorajadas (pra, to, tá, né)
260
+ - Cortar qualquer palavra que soe de relatório corporativo
261
+ - Após a passagem de humanização, validar também a regra de zero jargão técnico (Phase 2, regra 2) — se qualquer termo banido reaparecer, reescrever
173
262
 
174
263
  Processo:
175
264
  1. Identificar padrões presentes no resumo (especialmente P01, P03, P04, P07, P10, P15, P17, P19, P22)
@@ -190,10 +279,13 @@ Apresentar ao usuário:
190
279
  ### Checklist antes de aprovar
191
280
 
192
281
  Verificar e apresentar junto com o texto:
282
+ - [ ] Modo de escrita confirmado (`simple` ou `detailed`)
193
283
  - [ ] Acentuação correta em todo o texto (não, é, há, também, além, etc.)
194
284
  - [ ] Sem uso de hífen (-) onde deveria ser travessão (—)
195
285
  - [ ] Assinatura do autor presente no final
196
286
  - [ ] Catálogo ai-writing-patterns.md foi carregado e aplicado
287
+ - [ ] Se `MODE=simple`: nenhum termo banido apareceu (API, endpoint, FK, hook, deploy, migration, TypeScript, SPA, commit, branch, PR, merge, build, etc.)
288
+ - [ ] Se `MODE=simple`: nenhum ID de task (T-XXX) no texto
197
289
 
198
290
  Perguntar com AskUserQuestion:
199
291
  - **"Aprovar e enviar"** — prosseguir para Phase 5
@@ -255,8 +347,10 @@ Verificar resposta:
255
347
  | Período | Últimos 10 dias |
256
348
  | Slack canal | `#cspo-tech` |
257
349
  | Webhook env var | `SLACK_WEBHOOK_CSPO_TECH` |
258
- | Calibração humanizer | Relatório executivo (média) |
350
+ | Calibração humanizer | `detailed`: Relatório executivo (média) · `simple`: Conversacional (alta) |
259
351
  | Score máximo AI | 30 |
260
352
  | Template | *O que foi feito* / *Desafios* / *Em andamento* / *Pendências* / *Ajuda?* |
353
+ | Modos | `--mode simple` (leigos, sem jargão) · `--mode detailed` (técnicos) |
354
+ | Default (env) | `WEEKLY_UPDATE_MODE=simple` no `.env`; fallback se ausente: `detailed` |
261
355
  | Ortografia | OBRIGATÓRIA — acentos pt-BR, crase, travessão |
262
356
  | Assinatura | `— {git config user.name}` no final |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ganbatte-os",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "Framework operacional para design-to-code, squads de entrega e sprint sync com ClickUp.",
5
5
  "bin": {
6
6
  "gos": ".gos/scripts/cli/gos-cli.js"