@jaimevalasek/aioson 1.5.1 → 1.6.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 (131) hide show
  1. package/README.md +6 -0
  2. package/docs/design-previews/aurora-command-ui-website.html +884 -0
  3. package/docs/design-previews/aurora-command-ui.html +682 -0
  4. package/docs/design-previews/bold-editorial-ui-website.html +658 -0
  5. package/docs/design-previews/bold-editorial-ui.html +717 -0
  6. package/docs/design-previews/clean-saas-ui-website.html +1202 -0
  7. package/docs/design-previews/clean-saas-ui.html +549 -0
  8. package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
  9. package/docs/design-previews/cognitive-core-ui.html +463 -0
  10. package/docs/design-previews/glassmorphism-ui-website.html +572 -0
  11. package/docs/design-previews/glassmorphism-ui.html +886 -0
  12. package/docs/design-previews/index.html +699 -0
  13. package/docs/design-previews/interface-design-website.html +1187 -0
  14. package/docs/design-previews/interface-design.html +513 -0
  15. package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
  16. package/docs/design-previews/neo-brutalist-ui.html +797 -0
  17. package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
  18. package/docs/design-previews/premium-command-center-ui.html +552 -0
  19. package/docs/design-previews/warm-craft-ui-website.html +684 -0
  20. package/docs/design-previews/warm-craft-ui.html +739 -0
  21. package/docs/en/cli-reference.md +20 -9
  22. package/docs/pt/README.md +7 -0
  23. package/docs/pt/agent-sharding.md +132 -0
  24. package/docs/pt/agentes.md +8 -2
  25. package/docs/pt/busca-de-contexto.md +129 -0
  26. package/docs/pt/cache-de-contexto.md +156 -0
  27. package/docs/pt/comandos-cli.md +28 -0
  28. package/docs/pt/design-hybrid-forge.md +107 -0
  29. package/docs/pt/inicio-rapido.md +54 -3
  30. package/docs/pt/inteligencia-adaptativa.md +324 -0
  31. package/docs/pt/monitor-de-contexto.md +104 -0
  32. package/docs/pt/recuperacao-de-sessao.md +125 -0
  33. package/docs/pt/sandbox.md +125 -0
  34. package/docs/pt/skills.md +98 -6
  35. package/package.json +1 -1
  36. package/src/agent-loader.js +280 -0
  37. package/src/cli.js +94 -0
  38. package/src/commands/agent-loader.js +85 -0
  39. package/src/commands/context-cache.js +90 -0
  40. package/src/commands/context-monitor.js +92 -0
  41. package/src/commands/context-search.js +66 -0
  42. package/src/commands/design-hybrid-options.js +385 -0
  43. package/src/commands/health.js +214 -0
  44. package/src/commands/init.js +54 -13
  45. package/src/commands/install.js +52 -13
  46. package/src/commands/learning-evolve.js +355 -0
  47. package/src/commands/live.js +34 -0
  48. package/src/commands/recovery.js +43 -0
  49. package/src/commands/sandbox.js +37 -0
  50. package/src/commands/setup-context.js +22 -2
  51. package/src/commands/setup.js +178 -0
  52. package/src/commands/skill.js +79 -32
  53. package/src/commands/tool-registry-cmd.js +232 -0
  54. package/src/commands/update.js +7 -0
  55. package/src/constants.js +9 -0
  56. package/src/context-cache.js +159 -0
  57. package/src/context-search.js +326 -0
  58. package/src/design-variation-catalog.js +503 -0
  59. package/src/i18n/messages/en.js +32 -2
  60. package/src/i18n/messages/es.js +30 -2
  61. package/src/i18n/messages/fr.js +30 -2
  62. package/src/i18n/messages/pt-BR.js +32 -2
  63. package/src/install-animation.js +260 -0
  64. package/src/install-profile.js +143 -0
  65. package/src/install-wizard.js +474 -0
  66. package/src/installer.js +38 -10
  67. package/src/parser.js +7 -1
  68. package/src/recovery-context-session.js +154 -0
  69. package/src/runtime-store.js +97 -1
  70. package/src/sandbox.js +177 -0
  71. package/src/tool-executor.js +94 -0
  72. package/src/updater.js +11 -3
  73. package/template/.aioson/agents/analyst.md +58 -3
  74. package/template/.aioson/agents/architect.md +38 -0
  75. package/template/.aioson/agents/design-hybrid-forge.md +127 -0
  76. package/template/.aioson/agents/dev.md +103 -0
  77. package/template/.aioson/agents/deyvin.md +57 -0
  78. package/template/.aioson/agents/pm.md +58 -0
  79. package/template/.aioson/agents/product.md +28 -0
  80. package/template/.aioson/agents/qa.md +79 -0
  81. package/template/.aioson/agents/setup.md +65 -3
  82. package/template/.aioson/agents/sheldon.md +107 -6
  83. package/template/.aioson/agents/tester.md +156 -0
  84. package/template/.aioson/config.md +15 -0
  85. package/template/.aioson/context/forensics/.gitkeep +0 -0
  86. package/template/.aioson/context/seeds/seed-example.md +27 -0
  87. package/template/.aioson/context/user-profile.md +42 -0
  88. package/template/.aioson/locales/en/agents/setup.md +33 -1
  89. package/template/.aioson/locales/es/agents/setup.md +33 -1
  90. package/template/.aioson/locales/fr/agents/setup.md +33 -1
  91. package/template/.aioson/locales/pt-BR/agents/setup.md +33 -1
  92. package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
  93. package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
  94. package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
  95. package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
  96. package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
  97. package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
  98. package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
  99. package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
  100. package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
  101. package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
  102. package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
  103. package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
  104. package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
  105. package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
  106. package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
  107. package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
  108. package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
  109. package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
  110. package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
  111. package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
  112. package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
  113. package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
  114. package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
  115. package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
  116. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +45 -0
  117. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
  118. package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
  119. package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
  120. package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
  121. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +66 -0
  122. package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
  123. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +144 -0
  124. package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
  125. package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
  126. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +291 -0
  127. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +117 -0
  128. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +188 -0
  129. package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
  130. package/template/AGENTS.md +23 -1
  131. package/template/CLAUDE.md +1 -0
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const readline = require('node:readline/promises');
5
+ const { installTemplate, readInstallProfile } = require('../installer');
6
+ const { detectFramework } = require('../detector');
7
+ const { detectSystemLanguage } = require('./setup-context');
8
+ const { runSetupContext } = require('./setup-context');
9
+ const { resolvePromptTool } = require('../prompt-tool');
10
+ const { normalizeBoolean } = require('../context-writer');
11
+ const { runInstallWizard } = require('../install-wizard');
12
+
13
+ async function ask(rl, question, fallback = '') {
14
+ const suffix = fallback ? ` (${fallback})` : '';
15
+ const value = await rl.question(`${question}${suffix}: `);
16
+ const cleaned = String(value || '').trim();
17
+ if (!cleaned) return fallback;
18
+ return cleaned;
19
+ }
20
+
21
+ async function runSetup({ args, options, logger, t }) {
22
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
23
+ const dryRun = Boolean(options['dry-run']);
24
+ const force = Boolean(options.force);
25
+ const defaultsMode = Boolean(options.defaults);
26
+ const promptTool = resolvePromptTool(options.tool);
27
+
28
+ // Step 1 — detect install profile (wizard if first time in TTY)
29
+ const isTTY = process.stdin.isTTY && process.stdout.isTTY;
30
+ let installProfile = null;
31
+
32
+ if (!dryRun && isTTY) {
33
+ const existingProfile = await readInstallProfile(targetDir);
34
+ if (!existingProfile) {
35
+ installProfile = await runInstallWizard({});
36
+ } else {
37
+ installProfile = existingProfile;
38
+ }
39
+ }
40
+
41
+ // Step 2 — install template
42
+ logger.log(t('setup.installing'));
43
+ const installResult = await installTemplate(targetDir, {
44
+ overwrite: force,
45
+ dryRun,
46
+ mode: 'install',
47
+ installProfile
48
+ });
49
+ logger.log(t('setup.installed', { count: installResult.copied.length }));
50
+
51
+ // Step 3 — detect framework and system language
52
+ const detection = await detectFramework(targetDir);
53
+ const detectedFramework = detection.framework;
54
+ const detectedInstalled = detection.installed;
55
+ const systemLang = detectSystemLanguage();
56
+
57
+ // Build setup:context options by merging detected state with explicit flags
58
+ const contextOptions = { defaults: true };
59
+
60
+ // Propagate any explicit overrides the user passed to `setup`
61
+ const passthroughFlags = [
62
+ 'project-name', 'project-type', 'framework', 'framework-installed',
63
+ 'classification', 'lang', 'language', 'profile', 'backend', 'frontend',
64
+ 'database', 'auth', 'uiux', 'design-skill', 'test-runner',
65
+ 'web3-enabled', 'web3-networks', 'contract-framework',
66
+ 'wallet-provider', 'indexer', 'rpc-provider',
67
+ 'queues', 'storage', 'websockets', 'payments', 'email', 'cache', 'search'
68
+ ];
69
+ for (const flag of passthroughFlags) {
70
+ if (Object.prototype.hasOwnProperty.call(options, flag)) {
71
+ contextOptions[flag] = options[flag];
72
+ }
73
+ }
74
+
75
+ // Apply language: explicit flag > system detection
76
+ if (!contextOptions.lang && !contextOptions.language) {
77
+ contextOptions.lang = systemLang;
78
+ }
79
+
80
+ // For greenfield projects (nothing detected), ask minimal interactive questions
81
+ // unless --defaults is set or the user already passed --framework
82
+ const isGreenfield = !detectedFramework;
83
+ const frameworkProvided = Object.prototype.hasOwnProperty.call(options, 'framework');
84
+
85
+ if (!defaultsMode && isGreenfield && !frameworkProvided) {
86
+ const rl = readline.createInterface({
87
+ input: process.stdin,
88
+ output: process.stdout
89
+ });
90
+
91
+ try {
92
+ logger.log(t('setup.no_framework_detected'));
93
+
94
+ const projectName = await ask(
95
+ rl,
96
+ t('setup.q_project_name'),
97
+ path.basename(targetDir) || 'my-project'
98
+ );
99
+ if (projectName !== path.basename(targetDir)) {
100
+ contextOptions['project-name'] = projectName;
101
+ }
102
+
103
+ const framework = await ask(rl, t('setup.q_framework'), '');
104
+ if (framework) {
105
+ contextOptions.framework = framework;
106
+ contextOptions['framework-installed'] = 'false';
107
+ }
108
+
109
+ const detectedLang = contextOptions.lang || systemLang;
110
+ const lang = await ask(rl, t('setup.q_lang'), detectedLang);
111
+ contextOptions.lang = lang;
112
+ } finally {
113
+ rl.close();
114
+ }
115
+ } else if (!defaultsMode && detectedFramework) {
116
+ // Existing project with detected framework — confirm before proceeding
117
+ const rl = readline.createInterface({
118
+ input: process.stdin,
119
+ output: process.stdout
120
+ });
121
+
122
+ try {
123
+ logger.log(
124
+ t('setup.framework_detected', {
125
+ framework: detectedFramework,
126
+ installed: String(detectedInstalled)
127
+ })
128
+ );
129
+
130
+ const confirmed = normalizeBoolean(
131
+ await ask(rl, t('setup.q_confirm_framework'), 'true'),
132
+ true
133
+ );
134
+
135
+ if (!confirmed) {
136
+ const override = await ask(rl, t('setup.q_override_framework'), detectedFramework);
137
+ contextOptions.framework = override;
138
+ contextOptions['framework-installed'] = await ask(
139
+ rl,
140
+ t('setup.q_framework_installed'),
141
+ 'false'
142
+ );
143
+ }
144
+
145
+ const detectedLang = contextOptions.lang || systemLang;
146
+ const lang = await ask(rl, t('setup.q_lang'), detectedLang);
147
+ contextOptions.lang = lang;
148
+ } finally {
149
+ rl.close();
150
+ }
151
+ }
152
+
153
+ // Step 4 — run setup:context with fully resolved options
154
+ logger.log(t('setup.writing_context'));
155
+ const contextResult = await runSetupContext({
156
+ args: [targetDir],
157
+ options: contextOptions,
158
+ logger,
159
+ t
160
+ });
161
+
162
+ if (!dryRun) {
163
+ logger.log('');
164
+ logger.log(t('setup.done'));
165
+ logger.log(t('setup.step_agents'));
166
+ logger.log(t('setup.step_agent_prompt', { tool: promptTool }));
167
+ }
168
+
169
+ return {
170
+ ok: true,
171
+ targetDir,
172
+ installResult,
173
+ contextResult,
174
+ detection
175
+ };
176
+ }
177
+
178
+ module.exports = { runSetup };
@@ -16,6 +16,47 @@ function resolveTargetDir(args) {
16
16
  return path.resolve(process.cwd(), args[0] || '.');
17
17
  }
18
18
 
19
+ async function copyRecursive(src, dest) {
20
+ const stat = await fs.stat(src);
21
+ if (stat.isDirectory()) {
22
+ await ensureDir(dest);
23
+ const entries = await fs.readdir(src);
24
+ for (const entry of entries) {
25
+ await copyRecursive(path.join(src, entry), path.join(dest, entry));
26
+ }
27
+ return;
28
+ }
29
+
30
+ await ensureDir(path.dirname(dest));
31
+ await fs.copyFile(src, dest);
32
+ }
33
+
34
+ async function replaceDirectory(srcDir, destDir) {
35
+ await fs.rm(destDir, { recursive: true, force: true });
36
+ await copyRecursive(srcDir, destDir);
37
+ }
38
+
39
+ async function readJsonIfExists(filePath) {
40
+ if (!(await exists(filePath))) return null;
41
+ try {
42
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ async function writeSkillMeta(destDir, patch) {
49
+ const metaPath = path.join(destDir, '.skill-meta.json');
50
+ const existing = await readJsonIfExists(metaPath) || {};
51
+ const merged = {
52
+ ...existing,
53
+ ...patch
54
+ };
55
+
56
+ await fs.writeFile(metaPath, JSON.stringify(merged, null, 2), 'utf8');
57
+ return merged;
58
+ }
59
+
19
60
  /**
20
61
  * Parse YAML frontmatter from a SKILL.md file.
21
62
  */
@@ -44,17 +85,7 @@ async function distributeToTool(targetDir, slug, skillDir) {
44
85
  for (const toolPath of TOOL_TARGETS) {
45
86
  const toolSkillDir = path.join(targetDir, toolPath, slug);
46
87
  try {
47
- await ensureDir(toolSkillDir);
48
- // Copy all files from the installed skill dir
49
- const entries = await fs.readdir(skillDir);
50
- for (const entry of entries) {
51
- const src = path.join(skillDir, entry);
52
- const dest = path.join(toolSkillDir, entry);
53
- const stat = await fs.stat(src);
54
- if (stat.isFile()) {
55
- await fs.copyFile(src, dest);
56
- }
57
- }
88
+ await replaceDirectory(skillDir, toolSkillDir);
58
89
  results.push({ tool: toolPath, ok: true });
59
90
  } catch (err) {
60
91
  results.push({ tool: toolPath, ok: false, error: err.message });
@@ -187,12 +218,13 @@ async function installFromNpm(targetDir, slug, options, logger) {
187
218
 
188
219
  // Copy to .aioson/installed-skills/{slug}/
189
220
  const destDir = path.join(targetDir, INSTALLED_SKILLS_DIR, slug);
190
- await ensureDir(destDir);
191
-
192
- const entries = await fs.readdir(sourceDir);
193
- for (const entry of entries) {
194
- await fs.copyFile(path.join(sourceDir, entry), path.join(destDir, entry));
195
- }
221
+ await replaceDirectory(sourceDir, destDir);
222
+ await writeSkillMeta(destDir, {
223
+ source: 'npm',
224
+ sourcePackage: '@tech-leads-club/agent-skills',
225
+ sourcePath: path.relative(targetDir, sourceDir),
226
+ installedAt: new Date().toISOString()
227
+ });
196
228
 
197
229
  resolve({ ok: true, sourceDir, destDir });
198
230
  });
@@ -248,15 +280,16 @@ async function installFromCloud(targetDir, slug, options, logger) {
248
280
  ].join('\n');
249
281
 
250
282
  const destDir = path.join(targetDir, INSTALLED_SKILLS_DIR, slug);
283
+ await fs.rm(destDir, { recursive: true, force: true });
251
284
  await ensureDir(destDir);
252
285
  await fs.writeFile(path.join(destDir, 'SKILL.md'), fm, 'utf8');
253
286
 
254
287
  // Write meta
255
- await fs.writeFile(path.join(destDir, '.skill-meta.json'), JSON.stringify({
288
+ await writeSkillMeta(destDir, {
256
289
  source: 'cloud',
257
290
  cloudSlug: snapshot.skill.slug,
258
291
  installedAt: new Date().toISOString()
259
- }, null, 2), 'utf8');
292
+ });
260
293
 
261
294
  return { ok: true, destDir };
262
295
  }
@@ -271,30 +304,32 @@ async function installFromLocal(targetDir, slug, filePath, logger) {
271
304
  }
272
305
 
273
306
  const destDir = path.join(targetDir, INSTALLED_SKILLS_DIR, slug);
274
- await ensureDir(destDir);
275
-
276
307
  const stat = await fs.stat(absPath);
308
+ const samePath = path.resolve(absPath) === path.resolve(destDir);
309
+
310
+ if (!samePath) {
311
+ await fs.rm(destDir, { recursive: true, force: true });
312
+ await ensureDir(destDir);
313
+ } else if (!stat.isDirectory()) {
314
+ return { ok: false, error: 'Local self-install only supports skill directories' };
315
+ }
316
+
277
317
  if (stat.isDirectory()) {
278
- // Copy entire directory
279
- const entries = await fs.readdir(absPath);
280
- for (const entry of entries) {
281
- const src = path.join(absPath, entry);
282
- const srcStat = await fs.stat(src);
283
- if (srcStat.isFile()) {
284
- await fs.copyFile(src, path.join(destDir, entry));
285
- }
318
+ if (!samePath) {
319
+ await replaceDirectory(absPath, destDir);
286
320
  }
287
321
  } else {
288
322
  // Single file — copy as SKILL.md
323
+ await ensureDir(destDir);
289
324
  await fs.copyFile(absPath, path.join(destDir, 'SKILL.md'));
290
325
  }
291
326
 
292
327
  // Write meta
293
- await fs.writeFile(path.join(destDir, '.skill-meta.json'), JSON.stringify({
328
+ await writeSkillMeta(destDir, {
294
329
  source: 'local',
295
330
  sourcePath: filePath,
296
331
  installedAt: new Date().toISOString()
297
- }, null, 2), 'utf8');
332
+ });
298
333
 
299
334
  return { ok: true, destDir };
300
335
  }
@@ -429,17 +464,27 @@ async function runSkillList({ args, options = {}, logger, t }) {
429
464
  const fm = parseSkillFrontmatter(raw);
430
465
 
431
466
  let source = 'unknown';
467
+ let meta = null;
432
468
  try {
433
469
  const metaRaw = await fs.readFile(path.join(skillsDir, slug, '.skill-meta.json'), 'utf8');
434
- const meta = JSON.parse(metaRaw);
470
+ meta = JSON.parse(metaRaw);
435
471
  source = meta.source || 'unknown';
436
472
  } catch { /* no meta */ }
437
473
 
474
+ const author = meta?.author?.name || meta?.author_name || null;
475
+ const model =
476
+ meta?.generator?.model ||
477
+ meta?.generation?.model ||
478
+ meta?.generated_by_model ||
479
+ null;
480
+
438
481
  installed.push({
439
482
  slug,
440
483
  name: fm.name || slug,
441
484
  description: fm.description || '',
442
485
  source,
486
+ author,
487
+ model,
443
488
  path: path.relative(targetDir, path.join(skillsDir, slug))
444
489
  });
445
490
  }
@@ -472,6 +517,8 @@ async function runSkillList({ args, options = {}, logger, t }) {
472
517
  if (s.description) {
473
518
  logger.log(` ${s.description.slice(0, 100)}`);
474
519
  }
520
+ if (s.author) logger.log(` author: ${s.author}`);
521
+ if (s.model) logger.log(` model: ${s.model}`);
475
522
  logger.log(` ${s.path}/SKILL.md`);
476
523
  logger.log('');
477
524
  }
@@ -0,0 +1,232 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const {
5
+ openRuntimeDb,
6
+ registerDynamicTool,
7
+ unregisterDynamicTool,
8
+ getDynamicTool,
9
+ listDynamicTools
10
+ } = require('../runtime-store');
11
+ const { executeTool } = require('../tool-executor');
12
+
13
+ const TOOL_NAME_RE = /^[a-z][a-z0-9_]*$/;
14
+
15
+ async function runToolRegistry({ args = [], options = {}, logger = console, t = (k) => k } = {}) {
16
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
17
+ const sub = options.sub || 'list';
18
+
19
+ if (sub === 'register') return handleRegister(projectDir, options, logger);
20
+ if (sub === 'list') return handleList(projectDir, options, logger);
21
+ if (sub === 'call') return handleCall(projectDir, options, logger);
22
+ if (sub === 'unregister') return handleUnregister(projectDir, options, logger);
23
+ if (sub === 'show') return handleShow(projectDir, options, logger);
24
+
25
+ logger.error(`Subcomando desconhecido: ${sub}. Disponíveis: register, list, call, unregister, show`);
26
+ return { ok: false, error: 'unknown_sub' };
27
+ }
28
+
29
+ async function handleRegister(projectDir, options, logger) {
30
+ const name = String(options.name || '').trim();
31
+ const description = String(options.description || options.desc || '').trim();
32
+ const handlerType = String(options.type || 'shell').trim();
33
+ const handlerCode = options.cmd ? String(options.cmd) : null;
34
+ const handlerPath = options.path ? String(options.path) : null;
35
+ const squadSlug = options.squad ? String(options.squad) : null;
36
+ const registeredBy = options.by ? String(options.by) : null;
37
+
38
+ if (!name) {
39
+ logger.error('--name é obrigatório');
40
+ return { ok: false, error: 'name_required' };
41
+ }
42
+ if (!TOOL_NAME_RE.test(name) || name.length > 64) {
43
+ logger.error(`Nome inválido: "${name}". Use apenas letras minúsculas, números e _ (máx 64 chars, começando com letra)`);
44
+ return { ok: false, error: 'invalid_name' };
45
+ }
46
+ if (!description) {
47
+ logger.error('--description é obrigatório');
48
+ return { ok: false, error: 'description_required' };
49
+ }
50
+ if (!['shell', 'script'].includes(handlerType)) {
51
+ logger.error('--type deve ser "shell" ou "script"');
52
+ return { ok: false, error: 'invalid_type' };
53
+ }
54
+ if (handlerType === 'shell' && !handlerCode) {
55
+ logger.error('--cmd é obrigatório para tools do tipo shell');
56
+ return { ok: false, error: 'cmd_required' };
57
+ }
58
+ if (handlerType === 'script' && !handlerPath) {
59
+ logger.error('--path é obrigatório para tools do tipo script');
60
+ return { ok: false, error: 'path_required' };
61
+ }
62
+
63
+ const handle = await openRuntimeDb(projectDir);
64
+ if (!handle) {
65
+ logger.error('Runtime store não encontrado. Execute aioson runtime:init primeiro.');
66
+ return { ok: false, error: 'no_runtime' };
67
+ }
68
+ const { db } = handle;
69
+
70
+ try {
71
+ registerDynamicTool(db, {
72
+ name,
73
+ description,
74
+ handlerType,
75
+ handlerCode,
76
+ handlerPath,
77
+ squadSlug,
78
+ registeredBy
79
+ });
80
+ logger.log(`Tool registrada: ${name} (${handlerType})`);
81
+ return { ok: true, name, handlerType };
82
+ } finally {
83
+ db.close();
84
+ }
85
+ }
86
+
87
+ async function handleList(projectDir, options, logger) {
88
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
89
+ if (!handle) {
90
+ logger.log('Nenhum runtime store encontrado.');
91
+ return { ok: true, tools: [] };
92
+ }
93
+ const { db } = handle;
94
+
95
+ try {
96
+ const tools = listDynamicTools(db, options.squad || null);
97
+ if (tools.length === 0) {
98
+ logger.log('Nenhuma tool registrada neste projeto.');
99
+ return { ok: true, tools: [] };
100
+ }
101
+
102
+ logger.log(`Tools registradas (${tools.length}):`);
103
+ logger.log('');
104
+ for (const tool of tools) {
105
+ const scope = tool.squad_slug ? ` [squad:${tool.squad_slug}]` : '';
106
+ logger.log(` ${tool.name}${scope}`);
107
+ logger.log(` ${tool.description}`);
108
+ logger.log(` tipo: ${tool.handler_type} | registrada: ${tool.registered_at.slice(0, 10)}`);
109
+ logger.log('');
110
+ }
111
+ return { ok: true, tools };
112
+ } finally {
113
+ db.close();
114
+ }
115
+ }
116
+
117
+ async function handleCall(projectDir, options, logger) {
118
+ const name = String(options.name || '').trim();
119
+ if (!name) {
120
+ logger.error('--name é obrigatório');
121
+ return { ok: false, error: 'name_required' };
122
+ }
123
+
124
+ let input = {};
125
+ if (options.input) {
126
+ try {
127
+ input = JSON.parse(String(options.input));
128
+ } catch {
129
+ logger.error('--input deve ser JSON válido');
130
+ return { ok: false, error: 'invalid_input' };
131
+ }
132
+ }
133
+
134
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
135
+ if (!handle) {
136
+ logger.error('Runtime store não encontrado.');
137
+ return { ok: false, error: 'no_runtime' };
138
+ }
139
+ const { db } = handle;
140
+
141
+ let tool;
142
+ try {
143
+ tool = getDynamicTool(db, name);
144
+ } finally {
145
+ db.close();
146
+ }
147
+
148
+ if (!tool) {
149
+ logger.error(`Tool não encontrada: ${name}`);
150
+ return { ok: false, error: 'tool_not_found' };
151
+ }
152
+
153
+ const timeoutMs = options.timeout ? Number(options.timeout) * 1000 : undefined;
154
+ const result = executeTool(tool, input, { projectDir, timeoutMs });
155
+
156
+ if (result.stdout) logger.log(result.stdout);
157
+ if (result.stderr) logger.error(result.stderr);
158
+
159
+ if (!result.ok) {
160
+ logger.error(`Tool falhou (exit ${result.exitCode})${result.error ? `: ${result.error}` : ''}`);
161
+ return { ok: false, exitCode: result.exitCode, error: result.error };
162
+ }
163
+
164
+ return { ok: true, exitCode: result.exitCode, stdout: result.stdout };
165
+ }
166
+
167
+ async function handleUnregister(projectDir, options, logger) {
168
+ const name = String(options.name || '').trim();
169
+ if (!name) {
170
+ logger.error('--name é obrigatório');
171
+ return { ok: false, error: 'name_required' };
172
+ }
173
+
174
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
175
+ if (!handle) {
176
+ logger.error('Runtime store não encontrado.');
177
+ return { ok: false, error: 'no_runtime' };
178
+ }
179
+ const { db } = handle;
180
+
181
+ try {
182
+ const tool = getDynamicTool(db, name);
183
+ if (!tool) {
184
+ logger.error(`Tool não encontrada: ${name}`);
185
+ return { ok: false, error: 'tool_not_found' };
186
+ }
187
+
188
+ unregisterDynamicTool(db, name);
189
+ logger.log(`Tool removida: ${name}`);
190
+ return { ok: true, name };
191
+ } finally {
192
+ db.close();
193
+ }
194
+ }
195
+
196
+ async function handleShow(projectDir, options, logger) {
197
+ const name = String(options.name || '').trim();
198
+ if (!name) {
199
+ logger.error('--name é obrigatório');
200
+ return { ok: false, error: 'name_required' };
201
+ }
202
+
203
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
204
+ if (!handle) {
205
+ logger.error('Runtime store não encontrado.');
206
+ return { ok: false, error: 'no_runtime' };
207
+ }
208
+ const { db } = handle;
209
+
210
+ try {
211
+ const tool = getDynamicTool(db, name);
212
+ if (!tool) {
213
+ logger.error(`Tool não encontrada: ${name}`);
214
+ return { ok: false, error: 'tool_not_found' };
215
+ }
216
+
217
+ logger.log(`Tool: ${tool.name}`);
218
+ logger.log(`Descrição: ${tool.description}`);
219
+ logger.log(`Tipo: ${tool.handler_type}`);
220
+ if (tool.handler_code) logger.log(`Comando: ${tool.handler_code}`);
221
+ if (tool.handler_path) logger.log(`Script: ${tool.handler_path}`);
222
+ if (tool.squad_slug) logger.log(`Squad: ${tool.squad_slug}`);
223
+ if (tool.registered_by) logger.log(`Registrada por: ${tool.registered_by}`);
224
+ logger.log(`Registrada em: ${tool.registered_at}`);
225
+
226
+ return { ok: true, tool };
227
+ } finally {
228
+ db.close();
229
+ }
230
+ }
231
+
232
+ module.exports = { runToolRegistry };
@@ -28,6 +28,9 @@ async function runUpdate({ args, options, logger, t }) {
28
28
  requestedLanguage ||
29
29
  (context.parsed && context.data && context.data.conversation_language
30
30
  ? context.data.conversation_language
31
+ : null) ||
32
+ (result.savedProfile && result.savedProfile.locale
33
+ ? result.savedProfile.locale
31
34
  : 'en');
32
35
  localeSync = await applyAgentLocale(targetDir, language, { dryRun });
33
36
  }
@@ -35,6 +38,10 @@ async function runUpdate({ args, options, logger, t }) {
35
38
  logger.log(t('update.done_at', { targetDir }));
36
39
  logger.log(t('update.files_updated', { count: result.copied.length }));
37
40
  logger.log(t('update.backups_created', { count: result.backedUp.length }));
41
+ if (!dryRun) {
42
+ logger.log('');
43
+ logger.log(t('update.reconfigure_hint'));
44
+ }
38
45
  if (localeSync) {
39
46
  if (dryRun) {
40
47
  logger.log(t('locale_apply.dry_run_applied', { locale: localeSync.locale }));
package/src/constants.js CHANGED
@@ -33,6 +33,7 @@ const MANAGED_FILES = [
33
33
  '.aioson/agents/squad.md',
34
34
  '.aioson/agents/orache.md',
35
35
  '.aioson/agents/genome.md',
36
+ '.aioson/agents/design-hybrid-forge.md',
36
37
  '.aioson/agents/profiler-researcher.md',
37
38
  '.aioson/agents/profiler-enricher.md',
38
39
  '.aioson/agents/profiler-forge.md',
@@ -349,6 +350,14 @@ const AGENT_DEFINITIONS = [
349
350
  dependsOn: [],
350
351
  output: '.aioson/genomes/[slug].md + .aioson/genomes/[slug].meta.json + optional binding in .aioson/squads/{slug}/squad.md or .aioson/squads/{slug}/squad.manifest.json'
351
352
  },
353
+ {
354
+ id: 'design-hybrid-forge',
355
+ displayName: 'Design Hybrid Forge',
356
+ command: '@design-hybrid-forge',
357
+ path: '.aioson/agents/design-hybrid-forge.md',
358
+ dependsOn: ['.aioson/context/project.context.md'],
359
+ output: '.aioson/installed-skills/{hybrid-slug}/SKILL.md + .aioson/installed-skills/{hybrid-slug}/references/ + .aioson/installed-skills/{hybrid-slug}/previews/ + .aioson/installed-skills/{hybrid-slug}/.skill-meta.json'
360
+ },
352
361
  {
353
362
  id: 'profiler-researcher',
354
363
  displayName: 'Profiler Researcher',