@lugom.io/hefesto 0.3.0 → 1.0.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 (73) hide show
  1. package/agents/hefesto-argos.md +51 -237
  2. package/agents/hefesto-athena.md +59 -339
  3. package/agents/hefesto-hermes.md +39 -71
  4. package/bin/install.js +105 -69
  5. package/hooks/hefesto-check-update.cjs +32 -11
  6. package/hooks/hefesto-statusline.cjs +8 -17
  7. package/hooks/hefesto-workflow.cjs +68 -0
  8. package/package.json +12 -2
  9. package/skills/hefesto-context/SKILL.md +59 -26
  10. package/skills/hefesto-debug/SKILL.md +54 -0
  11. package/skills/hefesto-design/SKILL.md +133 -143
  12. package/skills/hefesto-execute/SKILL.md +133 -0
  13. package/skills/hefesto-init/SKILL.md +94 -59
  14. package/skills/hefesto-init/references/api.md +116 -0
  15. package/skills/hefesto-init/references/cli.md +91 -0
  16. package/skills/hefesto-init/references/mobile.md +69 -0
  17. package/skills/hefesto-init/references/web.md +246 -0
  18. package/skills/hefesto-new-feature/SKILL.md +75 -41
  19. package/skills/hefesto-security/SKILL.md +89 -0
  20. package/skills/hefesto-security/references/boundaries-and-bypasses.md +152 -0
  21. package/skills/hefesto-security/references/secrets-detection.md +121 -0
  22. package/skills/hefesto-security/references/severity-and-judgment.md +176 -0
  23. package/skills/hefesto-simplify/SKILL.md +82 -0
  24. package/templates/TPL-CLAUDE.md +54 -0
  25. package/templates/TPL-CONFIG.json +19 -0
  26. package/templates/TPL-DESIGN.md +305 -0
  27. package/templates/{FEATURE.md → TPL-FEATURE.md} +13 -6
  28. package/templates/TPL-PROJECT.md +50 -0
  29. package/templates/{RECON.md → TPL-RECON.md} +10 -4
  30. package/templates/{RESEARCH.md → TPL-RESEARCH.md} +15 -15
  31. package/templates/TPL-SECURITY.md +42 -0
  32. package/templates/TPL-SIMPLIFY.md +40 -0
  33. package/templates/{STATE.md → TPL-STATE.md} +0 -6
  34. package/templates/TPL-VERDICT.md +34 -0
  35. package/skills/hefesto-design/data/animations.csv +0 -21
  36. package/skills/hefesto-design/data/anti-patterns.csv +0 -41
  37. package/skills/hefesto-design/data/charts.csv +0 -26
  38. package/skills/hefesto-design/data/colors.csv +0 -108
  39. package/skills/hefesto-design/data/components.csv +0 -31
  40. package/skills/hefesto-design/data/google-fonts.csv +0 -56
  41. package/skills/hefesto-design/data/icons.csv +0 -23
  42. package/skills/hefesto-design/data/landing-pages.csv +0 -28
  43. package/skills/hefesto-design/data/products.csv +0 -46
  44. package/skills/hefesto-design/data/spacing.csv +0 -16
  45. package/skills/hefesto-design/data/styles.csv +0 -53
  46. package/skills/hefesto-design/data/typography.csv +0 -41
  47. package/skills/hefesto-design/data/ux-rules.csv +0 -61
  48. package/skills/hefesto-design/references/accessibility.md +0 -335
  49. package/skills/hefesto-design/references/aesthetics.md +0 -343
  50. package/skills/hefesto-design/references/anti-patterns.md +0 -107
  51. package/skills/hefesto-design/references/checklist.md +0 -66
  52. package/skills/hefesto-design/references/color-psychology.md +0 -203
  53. package/skills/hefesto-design/references/component-specs.md +0 -318
  54. package/skills/hefesto-design/references/polish.md +0 -339
  55. package/skills/hefesto-design/references/token-architecture.md +0 -394
  56. package/skills/hefesto-design/references/ux-rules.md +0 -349
  57. package/skills/hefesto-design/scripts/__pycache__/audit.cpython-314.pyc +0 -0
  58. package/skills/hefesto-design/scripts/__pycache__/contrast.cpython-314.pyc +0 -0
  59. package/skills/hefesto-design/scripts/__pycache__/core.cpython-314.pyc +0 -0
  60. package/skills/hefesto-design/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  61. package/skills/hefesto-design/scripts/__pycache__/search.cpython-314.pyc +0 -0
  62. package/skills/hefesto-design/scripts/__pycache__/validate_tokens.cpython-314.pyc +0 -0
  63. package/skills/hefesto-design/scripts/audit.py +0 -450
  64. package/skills/hefesto-design/scripts/contrast.py +0 -195
  65. package/skills/hefesto-design/scripts/core.py +0 -155
  66. package/skills/hefesto-design/scripts/design_system.py +0 -311
  67. package/skills/hefesto-design/scripts/search.py +0 -235
  68. package/skills/hefesto-design/scripts/validate_tokens.py +0 -274
  69. package/skills/hefesto-update/SKILL.md +0 -34
  70. package/templates/DESIGN.md +0 -137
  71. package/templates/PROJECT.md +0 -28
  72. package/templates/ROADMAP.md +0 -23
  73. package/templates/VERDICT.md +0 -52
package/bin/install.js CHANGED
@@ -20,14 +20,19 @@ const red = '\x1b[31m';
20
20
  const dim = '\x1b[2m';
21
21
  const reset = '\x1b[0m';
22
22
 
23
- const banner = '\n' +
24
- red + ' ██╗ ██╗███████╗███████╗███████╗███████╗████████╗ ██████╗\n' +
23
+ const banner =
24
+ '\n' +
25
+ red +
26
+ ' ██╗ ██╗███████╗███████╗███████╗███████╗████████╗ ██████╗\n' +
25
27
  ' ██║ ██║██╔════╝██╔════╝██╔════╝██╔════╝╚══██╔══╝██╔═══██╗\n' +
26
28
  ' ███████║█████╗ █████╗ █████╗ ███████╗ ██║ ██║ ██║\n' +
27
29
  ' ██╔══██║██╔══╝ ██╔══╝ ██╔══╝ ╚════██║ ██║ ██║ ██║\n' +
28
30
  ' ██║ ██║███████╗██║ ███████╗███████║ ██║ ╚██████╔╝\n' +
29
- ' ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝\n' + reset +
30
- dim + ' Toolkit spec-driven + story-driven para agentes AI\n' + reset;
31
+ ' ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝\n' +
32
+ reset +
33
+ dim +
34
+ ' Toolkit spec-driven + story-driven para agentes AI\n' +
35
+ reset;
31
36
 
32
37
  // ── CLI Args ────────────────────────────────────────────────────────────────
33
38
 
@@ -41,6 +46,25 @@ const hasGemini = args.includes('--gemini');
41
46
  const hasCodex = args.includes('--codex');
42
47
  const hasAll = args.includes('--all');
43
48
 
49
+ const knownArgs = new Set([
50
+ '--global',
51
+ '-g',
52
+ '--uninstall',
53
+ '-u',
54
+ '--help',
55
+ '-h',
56
+ '--claude',
57
+ '--gemini',
58
+ '--codex',
59
+ '--all',
60
+ ]);
61
+ const unknownArgs = args.filter((a) => a.startsWith('-') && !knownArgs.has(a));
62
+ if (unknownArgs.length > 0) {
63
+ console.warn(
64
+ `⚠ Opção desconhecida: ${unknownArgs.join(', ')}. Use --help para ver opções válidas.`
65
+ );
66
+ }
67
+
44
68
  // ── Help ────────────────────────────────────────────────────────────────────
45
69
 
46
70
  if (hasHelp) {
@@ -144,14 +168,12 @@ function removeIfExists(targetPath) {
144
168
  // ── Default Config ──────────────────────────────────────────────────────────
145
169
 
146
170
  function createDefaultConfig() {
147
- return {
148
- version: '0.1.0',
149
- project: { name: '', language: 'pt-BR' },
150
- runtime: 'claude',
151
- feature: { id_prefix: 'FEAT', counter: 0 },
152
- research: { id_prefix: 'RES', counter: 0 },
153
- lifecycle: { auto_update_state: true },
154
- };
171
+ const templatePath = path.join(PKG_ROOT, 'templates', 'TPL-CONFIG.json');
172
+ const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
173
+ const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
174
+ // --all: getSelectedRuntimes()[0] é 'claude' (runtime primário).
175
+ // Config armazena apenas o primário; todos os runtimes recebem os arquivos instalados.
176
+ return { ...template, version: pkg.version, runtime: getSelectedRuntimes()[0] || 'claude' };
155
177
  }
156
178
 
157
179
  // ── Install ─────────────────────────────────────────────────────────────────
@@ -172,12 +194,15 @@ function installHefesto() {
172
194
  ensureDir(path.join(hefestoDir, 'research'));
173
195
 
174
196
  // Copiar templates como arquivos iniciais do projeto
175
- const templateFiles = ['PROJECT.md', 'STATE.md', 'ROADMAP.md'];
176
- for (const file of templateFiles) {
177
- const src = path.join(templatesDir, file);
178
- const dest = path.join(hefestoDir, file);
179
- if (fs.existsSync(src)) {
180
- fs.copyFileSync(src, dest);
197
+ const templateFiles = [
198
+ { src: 'TPL-PROJECT.md', dest: 'PROJECT.md' },
199
+ { src: 'TPL-STATE.md', dest: 'STATE.md' },
200
+ ];
201
+ for (const { src, dest } of templateFiles) {
202
+ const srcPath = path.join(templatesDir, src);
203
+ const destPath = path.join(hefestoDir, dest);
204
+ if (fs.existsSync(srcPath)) {
205
+ fs.copyFileSync(srcPath, destPath);
181
206
  }
182
207
  }
183
208
 
@@ -187,7 +212,7 @@ function installHefesto() {
187
212
  // Criar config.json
188
213
  fs.writeFileSync(
189
214
  path.join(hefestoDir, 'config.json'),
190
- JSON.stringify(createDefaultConfig(), null, 2) + '\n',
215
+ JSON.stringify(createDefaultConfig(), null, 2) + '\n'
191
216
  );
192
217
 
193
218
  console.log(` ✅ .hefesto/ criado com scaffold do projeto.`);
@@ -210,9 +235,11 @@ function installRuntime(runtime) {
210
235
  // Skills
211
236
  const srcSkills = path.join(PKG_ROOT, 'skills');
212
237
  if (fs.existsSync(srcSkills)) {
213
- const destSkills = getSkillsDestDir(runtime, runtimeDir);
238
+ const destSkills = getSkillsDestDir(runtimeDir);
214
239
  copySkills(srcSkills, destSkills);
215
- console.log(` ✅ Skills instaladas em ${path.relative(process.cwd(), destSkills) || destSkills}`);
240
+ console.log(
241
+ ` ✅ Skills instaladas em ${path.relative(process.cwd(), destSkills) || destSkills}`
242
+ );
216
243
  }
217
244
 
218
245
  // Agents
@@ -220,18 +247,17 @@ function installRuntime(runtime) {
220
247
  if (fs.existsSync(srcAgents)) {
221
248
  const destAgents = path.join(runtimeDir, 'agents');
222
249
  copyAgents(srcAgents, destAgents);
223
- console.log(` ✅ Agents instalados em ${path.relative(process.cwd(), destAgents) || destAgents}`);
224
- }
225
-
226
- // Agent memory directories (apenas Claude Code)
227
- if (runtime === 'claude') {
228
- installAgentMemory(runtimeDir);
250
+ console.log(
251
+ ` ✅ Agents instalados em ${path.relative(process.cwd(), destAgents) || destAgents}`
252
+ );
229
253
  }
230
254
 
231
255
  // Cleanup: remove legacy commands (migrated to skills)
232
256
  const legacyCmds = path.join(runtimeDir, 'commands', 'hefesto');
233
257
  if (removeIfExists(legacyCmds)) {
234
- console.log(` ♻️ Commands legados removidos de ${path.relative(process.cwd(), legacyCmds) || legacyCmds}`);
258
+ console.log(
259
+ ` ♻️ Commands legados removidos de ${path.relative(process.cwd(), legacyCmds) || legacyCmds}`
260
+ );
235
261
  const cmdsParent = path.join(runtimeDir, 'commands');
236
262
  if (fs.existsSync(cmdsParent) && fs.readdirSync(cmdsParent).length === 0) {
237
263
  fs.rmSync(cmdsParent, { recursive: true });
@@ -244,7 +270,7 @@ function installRuntime(runtime) {
244
270
  }
245
271
  }
246
272
 
247
- function getSkillsDestDir(_runtime, runtimeDir) {
273
+ function getSkillsDestDir(runtimeDir) {
248
274
  // Claude: .claude/skills/
249
275
  // Gemini: .gemini/skills/
250
276
  // Codex: .codex/skills/
@@ -279,18 +305,6 @@ function copyAgents(srcDir, destDir) {
279
305
  }
280
306
  }
281
307
 
282
- // ── Agent Memory ─────────────────────────────────────────────────────────────
283
-
284
- function installAgentMemory(runtimeDir) {
285
- const agentMemoryDir = path.join(runtimeDir, 'agent-memory');
286
- const agentNames = ['hefesto-athena', 'hefesto-argos'];
287
-
288
- for (const name of agentNames) {
289
- ensureDir(path.join(agentMemoryDir, name));
290
- }
291
- console.log(` ✅ Agent memory criado em ${path.relative(process.cwd(), agentMemoryDir) || agentMemoryDir}`);
292
- }
293
-
294
308
  // ── Hooks ───────────────────────────────────────────────────────────────────
295
309
 
296
310
  function installHooks(runtimeDir) {
@@ -299,7 +313,11 @@ function installHooks(runtimeDir) {
299
313
 
300
314
  // Copiar hooks
301
315
  ensureDir(destHooks);
302
- for (const file of ['hefesto-statusline.cjs', 'hefesto-check-update.cjs']) {
316
+ for (const file of [
317
+ 'hefesto-statusline.cjs',
318
+ 'hefesto-check-update.cjs',
319
+ 'hefesto-workflow.cjs',
320
+ ]) {
303
321
  const src = path.join(srcHooks, file);
304
322
  if (fs.existsSync(src)) {
305
323
  fs.copyFileSync(src, path.join(destHooks, file));
@@ -332,15 +350,35 @@ function installHooks(runtimeDir) {
332
350
 
333
351
  // Remover hook hefesto anterior se existir
334
352
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
335
- h => !JSON.stringify(h).includes('hefesto-check-update'),
353
+ (h) => !JSON.stringify(h).includes('hefesto-check-update')
336
354
  );
337
355
 
338
356
  settings.hooks.SessionStart.push({
339
357
  matcher: 'startup',
340
- hooks: [{
341
- type: 'command',
342
- command: checkUpdateCmd,
343
- }],
358
+ hooks: [
359
+ {
360
+ type: 'command',
361
+ command: checkUpdateCmd,
362
+ },
363
+ ],
364
+ });
365
+
366
+ // PreToolUse hook (workflow enforcement)
367
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
368
+
369
+ // Remover hook hefesto-workflow anterior se existir
370
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
371
+ (h) => !JSON.stringify(h).includes('hefesto-workflow')
372
+ );
373
+
374
+ settings.hooks.PreToolUse.push({
375
+ matcher: 'Write|Edit',
376
+ hooks: [
377
+ {
378
+ type: 'command',
379
+ command: `node "${hooksDir}/hefesto-workflow.cjs"`,
380
+ },
381
+ ],
344
382
  });
345
383
 
346
384
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
@@ -350,7 +388,11 @@ function installHooks(runtimeDir) {
350
388
  function uninstallHooks(runtimeDir) {
351
389
  // Remover arquivos de hooks
352
390
  const hooksDir = path.join(runtimeDir, 'hooks');
353
- for (const file of ['hefesto-statusline.cjs', 'hefesto-check-update.cjs']) {
391
+ for (const file of [
392
+ 'hefesto-statusline.cjs',
393
+ 'hefesto-check-update.cjs',
394
+ 'hefesto-workflow.cjs',
395
+ ]) {
354
396
  const hookPath = path.join(hooksDir, file);
355
397
  if (fs.existsSync(hookPath)) {
356
398
  fs.unlinkSync(hookPath);
@@ -372,13 +414,23 @@ function uninstallHooks(runtimeDir) {
372
414
  // Remover hook de SessionStart do hefesto
373
415
  if (settings.hooks?.SessionStart) {
374
416
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
375
- h => !JSON.stringify(h).includes('hefesto-check-update'),
417
+ (h) => !JSON.stringify(h).includes('hefesto-check-update')
376
418
  );
377
419
  if (settings.hooks.SessionStart.length === 0) {
378
420
  delete settings.hooks.SessionStart;
379
421
  }
380
422
  }
381
423
 
424
+ // Remover hook de PreToolUse do hefesto
425
+ if (settings.hooks?.PreToolUse) {
426
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
427
+ (h) => !JSON.stringify(h).includes('hefesto-workflow')
428
+ );
429
+ if (settings.hooks.PreToolUse.length === 0) {
430
+ delete settings.hooks.PreToolUse;
431
+ }
432
+ }
433
+
382
434
  // Limpar hooks vazio
383
435
  if (settings.hooks && Object.keys(settings.hooks).length === 0) {
384
436
  delete settings.hooks;
@@ -418,7 +470,9 @@ function uninstallRuntime(runtime) {
418
470
  // Remover legacy commands/hefesto/ (se existir de versão anterior)
419
471
  const commandsDir = path.join(runtimeDir, 'commands', 'hefesto');
420
472
  if (removeIfExists(commandsDir)) {
421
- console.log(` ✅ Commands legados removidos de ${path.relative(process.cwd(), commandsDir) || commandsDir}`);
473
+ console.log(
474
+ ` ✅ Commands legados removidos de ${path.relative(process.cwd(), commandsDir) || commandsDir}`
475
+ );
422
476
  const cmdsParent = path.join(runtimeDir, 'commands');
423
477
  if (fs.existsSync(cmdsParent) && fs.readdirSync(cmdsParent).length === 0) {
424
478
  fs.rmSync(cmdsParent, { recursive: true });
@@ -426,7 +480,7 @@ function uninstallRuntime(runtime) {
426
480
  }
427
481
 
428
482
  // Remover skills hefesto-*
429
- const skillsDir = getSkillsDestDir(runtime, runtimeDir);
483
+ const skillsDir = getSkillsDestDir(runtimeDir);
430
484
  if (fs.existsSync(skillsDir)) {
431
485
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
432
486
  for (const entry of entries) {
@@ -454,24 +508,6 @@ function uninstallRuntime(runtime) {
454
508
  }
455
509
  }
456
510
 
457
- // Remover agent-memory hefesto-* (apenas Claude Code)
458
- if (runtime === 'claude') {
459
- const agentMemoryDir = path.join(runtimeDir, 'agent-memory');
460
- if (fs.existsSync(agentMemoryDir)) {
461
- const memEntries = fs.readdirSync(agentMemoryDir, { withFileTypes: true });
462
- for (const entry of memEntries) {
463
- if (entry.isDirectory() && entry.name.startsWith('hefesto-')) {
464
- fs.rmSync(path.join(agentMemoryDir, entry.name), { recursive: true });
465
- console.log(` ✅ Agent memory ${entry.name} removido.`);
466
- }
467
- }
468
- // Remove agent-memory/ se vazio
469
- if (fs.readdirSync(agentMemoryDir).length === 0) {
470
- fs.rmSync(agentMemoryDir, { recursive: true });
471
- }
472
- }
473
- }
474
-
475
511
  // Remover hooks (apenas Claude Code)
476
512
  if (runtime === 'claude') {
477
513
  uninstallHooks(runtimeDir);
@@ -7,6 +7,10 @@ const path = require('path');
7
7
  const os = require('os');
8
8
  const { spawn } = require('child_process');
9
9
 
10
+ // Drain stdin (hook runner pode enviar dados)
11
+ process.stdin.resume();
12
+ process.stdin.on('data', () => {});
13
+
10
14
  const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
11
15
  const cacheDir = path.join(configDir, 'cache');
12
16
  const cacheFile = path.join(cacheDir, 'hefesto-update-check.json');
@@ -17,44 +21,61 @@ if (!fs.existsSync(cacheDir)) {
17
21
  }
18
22
 
19
23
  // Rodar check em background (não bloqueia a sessão)
20
- const child = spawn(process.execPath, ['-e', `
24
+ const child = spawn(
25
+ process.execPath,
26
+ [
27
+ '-e',
28
+ `
21
29
  const fs = require('fs');
22
30
  const { execSync } = require('child_process');
23
31
  const cacheFile = ${JSON.stringify(cacheFile)};
24
32
 
25
33
  let installed = '0.0.0';
26
34
  try {
27
- const pkg = JSON.parse(execSync('npm ls hefesto --json 2>/dev/null', {
35
+ const pkg = JSON.parse(execSync('npm ls @lugom.io/hefesto --json 2>/dev/null', {
28
36
  encoding: 'utf8', timeout: 10000, windowsHide: true
29
37
  }));
30
- installed = pkg.dependencies?.hefesto?.version || '0.0.0';
38
+ installed = pkg.dependencies?.['@lugom.io/hefesto']?.version || '0.0.0';
31
39
  } catch (_) {
32
40
  // Se não conseguir detectar versão instalada, tentar package.json local
33
41
  try {
34
- const localPkg = JSON.parse(fs.readFileSync('node_modules/hefesto/package.json', 'utf8'));
42
+ const localPkg = JSON.parse(fs.readFileSync('node_modules/@lugom.io/hefesto/package.json', 'utf8'));
35
43
  installed = localPkg.version || '0.0.0';
36
44
  } catch (_) {}
37
45
  }
38
46
 
39
47
  let latest = null;
40
48
  try {
41
- latest = execSync('npm view hefesto version', {
49
+ latest = execSync('npm view @lugom.io/hefesto version', {
42
50
  encoding: 'utf8', timeout: 10000, windowsHide: true
43
51
  }).trim();
44
52
  } catch (_) {}
45
53
 
54
+ function semverLt(a, b) {
55
+ const pa = a.split('.').map(s => parseInt(s, 10) || 0);
56
+ const pb = b.split('.').map(s => parseInt(s, 10) || 0);
57
+ for (let i = 0; i < 3; i++) {
58
+ if ((pa[i] || 0) < (pb[i] || 0)) return true;
59
+ if ((pa[i] || 0) > (pb[i] || 0)) return false;
60
+ }
61
+ return false;
62
+ }
63
+
46
64
  const result = {
47
- update_available: latest && installed !== latest && latest !== '0.0.0',
65
+ update_available: latest && latest !== '0.0.0' && installed !== latest && semverLt(installed, latest),
48
66
  installed,
49
67
  latest: latest || 'unknown',
50
68
  checked: Math.floor(Date.now() / 1000),
51
69
  };
52
70
 
53
71
  fs.writeFileSync(cacheFile, JSON.stringify(result));
54
- `], {
55
- stdio: 'ignore',
56
- windowsHide: true,
57
- detached: true,
58
- });
72
+ `,
73
+ ],
74
+ {
75
+ stdio: 'ignore',
76
+ windowsHide: true,
77
+ detached: true,
78
+ }
79
+ );
59
80
 
60
81
  child.unref();
@@ -9,36 +9,25 @@ const os = require('os');
9
9
  let input = '';
10
10
  const stdinTimeout = setTimeout(() => process.exit(0), 3000);
11
11
  process.stdin.setEncoding('utf8');
12
- process.stdin.on('data', chunk => input += chunk);
12
+ process.stdin.on('data', (chunk) => (input += chunk));
13
13
  process.stdin.on('end', () => {
14
14
  clearTimeout(stdinTimeout);
15
15
  try {
16
16
  const data = JSON.parse(input);
17
17
  const model = data.model?.display_name || 'Claude';
18
18
  const dir = data.workspace?.current_dir || process.cwd();
19
- const session = data.session_id || '';
20
19
  const remaining = data.context_window?.remaining_percentage;
21
20
 
22
21
  // Context window (normalizado para contexto utilizável)
23
22
  const AUTO_COMPACT_BUFFER_PCT = 16.5;
24
23
  let ctx = '';
25
24
  if (remaining != null) {
26
- const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
25
+ const usableRemaining = Math.max(
26
+ 0,
27
+ ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100
28
+ );
27
29
  const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
28
30
 
29
- // Bridge file para o context-monitor
30
- if (session) {
31
- try {
32
- const bridgePath = path.join(os.tmpdir(), `hefesto-ctx-${session}.json`);
33
- fs.writeFileSync(bridgePath, JSON.stringify({
34
- session_id: session,
35
- remaining_percentage: remaining,
36
- used_pct: used,
37
- timestamp: Math.floor(Date.now() / 1000),
38
- }));
39
- } catch (_) {}
40
- }
41
-
42
31
  // Barra de progresso (10 segmentos)
43
32
  const filled = Math.floor(used / 10);
44
33
  const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
@@ -68,7 +57,9 @@ process.stdin.on('end', () => {
68
57
  }
69
58
 
70
59
  const dirname = path.basename(dir);
71
- process.stdout.write(`${updateMsg}\x1b[1;31m⚒ HEFESTO\x1b[0m │ \x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
60
+ process.stdout.write(
61
+ `${updateMsg}\x1b[1;31m⚒ HEFESTO\x1b[0m │ \x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`
62
+ );
72
63
  } catch (_) {
73
64
  // Falha silenciosa
74
65
  }
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ // Hefesto Workflow Enforcement
3
+ // PreToolUse hook: avisa quando Write/Edit é usado sem feature ativa
4
+ // Comportamento: WARN only, nunca BLOCK — Hefesto guia, não bloqueia
5
+
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+
9
+ let input = '';
10
+ const stdinTimeout = setTimeout(() => process.exit(0), 3000);
11
+
12
+ process.stdin.setEncoding('utf8');
13
+ process.stdin.on('data', (chunk) => (input += chunk));
14
+ process.stdin.on('end', () => {
15
+ clearTimeout(stdinTimeout);
16
+ try {
17
+ const data = JSON.parse(input);
18
+ const toolName = data.tool_name;
19
+ const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
20
+
21
+ // Só verificar Write e Edit
22
+ if (toolName !== 'Write' && toolName !== 'Edit') return;
23
+
24
+ // Se não tem file_path, não tem como verificar
25
+ if (!filePath) return;
26
+
27
+ const basename = path.basename(filePath);
28
+ const normalized = filePath.replace(/\\/g, '/');
29
+
30
+ // Escape hatches — sempre permitir:
31
+ // .hefesto/ (state files do próprio Hefesto)
32
+ if (normalized.includes('.hefesto/')) return;
33
+ // Configs de runtime (.claude/, .gemini/, .codex/)
34
+ if (
35
+ normalized.includes('.claude/') ||
36
+ normalized.includes('.gemini/') ||
37
+ normalized.includes('.codex/')
38
+ )
39
+ return;
40
+ // Testes (test, spec, __tests__)
41
+ if (/test|spec|__tests__/i.test(normalized)) return;
42
+ // Dotfiles e package.json
43
+ if (basename.startsWith('.') || basename === 'package.json' || basename === 'package-lock.json')
44
+ return;
45
+ // Markdown docs
46
+ if (basename.endsWith('.md')) return;
47
+
48
+ // Verificar se .hefesto/STATE.md existe
49
+ const statePath = path.join(process.cwd(), '.hefesto', 'STATE.md');
50
+ if (!fs.existsSync(statePath)) return;
51
+
52
+ // Ler STATE.md e checar se tem feature ativa
53
+ const state = fs.readFileSync(statePath, 'utf8');
54
+ const featureSection = state.match(/## Feature Ativa\s*\n([\s\S]*?)(?=\n## |\n---|$)/);
55
+
56
+ if (!featureSection) return;
57
+
58
+ const content = featureSection[1].trim();
59
+ if (content && content !== 'Nenhuma.' && content !== 'Nenhuma') return;
60
+
61
+ // Sem feature ativa — emitir warning via stderr
62
+ process.stderr.write(
63
+ '\n⚒ HEFESTO: Nenhuma feature ativa. Considere usar /hefesto-new-feature antes de implementar.\n'
64
+ );
65
+ } catch (_) {
66
+ // Silent failure — nunca quebrar o runtime
67
+ }
68
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lugom.io/hefesto",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, Gemini and Codex by lugom.io.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,10 @@
8
8
  },
9
9
  "scripts": {
10
10
  "test": "node --test tests/**/*.test.js",
11
+ "lint": "eslint bin/ hooks/ tests/",
12
+ "lint:fix": "eslint bin/ hooks/ tests/ --fix",
13
+ "format": "prettier --write .",
14
+ "format:check": "prettier --check .",
11
15
  "sandbox": "HEFESTO=$PWD && mkdir -p $HEFESTO/tmp/hefesto-sandbox/src && cd $HEFESTO/tmp/hefesto-sandbox && test -f package.json || echo '{\"name\":\"sandbox\",\"type\":\"module\"}' > package.json && node $HEFESTO/bin/install.js && claude"
12
16
  },
13
17
  "engines": {
@@ -33,5 +37,11 @@
33
37
  "codex-cli"
34
38
  ],
35
39
  "author": "lugom.io",
36
- "license": "MIT"
40
+ "license": "MIT",
41
+ "devDependencies": {
42
+ "@eslint/js": "^10.0.1",
43
+ "eslint": "^10.1.0",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "prettier": "^3.8.1"
46
+ }
37
47
  }