sinapse-ai 1.25.0 → 1.25.1

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 (100) hide show
  1. package/.claude/settings.json +170 -0
  2. package/.codex/catalog.json +1 -1
  3. package/.codex/scripts/sinapse-codex.js +0 -5
  4. package/.codex/scripts/sync-codex-native.js +4 -37
  5. package/.codex/scripts/validate-codex-native.js +13 -23
  6. package/.codex/tasks/onboard-sinapse-codex.md +1 -1
  7. package/.sinapse-ai/data/entity-registry.yaml +44 -18
  8. package/.sinapse-ai/install-manifest.yaml +3 -3
  9. package/CHANGELOG.md +8 -16
  10. package/bin/cli.js +8 -1
  11. package/bin/commands/help.js +3 -2
  12. package/bin/commands/install.js +25 -14
  13. package/bin/commands/status.js +4 -3
  14. package/bin/commands/uninstall.js +216 -51
  15. package/bin/commands/update.js +8 -4
  16. package/bin/lib/global-provider-adapters.js +73 -10
  17. package/bin/lib/provider-parity.js +22 -0
  18. package/bin/postinstall.js +11 -11
  19. package/docs/framework/codex-parity/codex-integration-process.md +6 -7
  20. package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
  21. package/docs/framework/codex-parity/codex-parity-program.md +4 -4
  22. package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
  23. package/docs/guides/codex-config.md +2 -2
  24. package/docs/guides/development-setup.md +1 -2
  25. package/docs/guides/ide-integration.md +6 -6
  26. package/docs/guides/quality-gates.md +2 -2
  27. package/docs/ide-integration.md +2 -2
  28. package/docs/installation/faq.md +1 -1
  29. package/docs/installation/troubleshooting.md +2 -2
  30. package/docs/installation/v4-quick-start.md +1 -1
  31. package/docs/pt/contributing.md +1 -1
  32. package/docs/pt/guides/development-setup.md +1 -2
  33. package/docs/pt/guides/quality-gates.md +2 -2
  34. package/docs/pt/ide-integration.md +6 -6
  35. package/docs/pt/installation/faq.md +1 -1
  36. package/docs/pt/installation/troubleshooting.md +2 -3
  37. package/docs/pt/installation/v4-quick-start.md +1 -2
  38. package/docs/pt/platforms/claude-code.md +4 -4
  39. package/docs/sinapse-agent-flows/analyst-system.md +1 -1
  40. package/docs/sinapse-agent-flows/architect-system.md +1 -1
  41. package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
  42. package/docs/sinapse-agent-flows/dev-system.md +1 -1
  43. package/docs/sinapse-agent-flows/pm-system.md +1 -1
  44. package/docs/sinapse-agent-flows/qa-system.md +1 -1
  45. package/docs/sinapse-agent-flows/sm-system.md +1 -1
  46. package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
  47. package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
  48. package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
  49. package/package.json +21 -16
  50. package/packages/installer/src/config/ide-configs.js +1 -1
  51. package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
  52. package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
  53. package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
  54. package/packages/installer/src/updater/index.js +24 -7
  55. package/packages/installer/src/wizard/ide-config-generator.js +4 -0
  56. package/packages/installer/src/wizard/index.js +6 -2
  57. package/scripts/validate-codex-command-registry.js +101 -0
  58. package/scripts/validate-provider-adapters.js +117 -1
  59. package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
  60. package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
  61. package/squads/squad-design/agents/ux-designer.md +1 -1
  62. package/.codex/skills/sinapse/SKILL.md +0 -10
  63. package/.codex/skills/sinapse-agent/SKILL.md +0 -22
  64. package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
  65. package/.codex/skills/sinapse-animations/SKILL.md +0 -28
  66. package/.codex/skills/sinapse-architect/SKILL.md +0 -31
  67. package/.codex/skills/sinapse-brand/SKILL.md +0 -28
  68. package/.codex/skills/sinapse-claude/SKILL.md +0 -28
  69. package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
  70. package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
  71. package/.codex/skills/sinapse-content/SKILL.md +0 -28
  72. package/.codex/skills/sinapse-copy/SKILL.md +0 -28
  73. package/.codex/skills/sinapse-council/SKILL.md +0 -28
  74. package/.codex/skills/sinapse-courses/SKILL.md +0 -28
  75. package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
  76. package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
  77. package/.codex/skills/sinapse-design/SKILL.md +0 -28
  78. package/.codex/skills/sinapse-dev/SKILL.md +0 -31
  79. package/.codex/skills/sinapse-devops/SKILL.md +0 -31
  80. package/.codex/skills/sinapse-finance/SKILL.md +0 -28
  81. package/.codex/skills/sinapse-growth/SKILL.md +0 -28
  82. package/.codex/skills/sinapse-loop/SKILL.md +0 -35
  83. package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
  84. package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
  85. package/.codex/skills/sinapse-pm/SKILL.md +0 -31
  86. package/.codex/skills/sinapse-po/SKILL.md +0 -31
  87. package/.codex/skills/sinapse-product/SKILL.md +0 -28
  88. package/.codex/skills/sinapse-qa/SKILL.md +0 -31
  89. package/.codex/skills/sinapse-research/SKILL.md +0 -28
  90. package/.codex/skills/sinapse-sm/SKILL.md +0 -27
  91. package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
  92. package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
  93. package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
  94. package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
  95. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
  96. package/.codex/skills/snps/SKILL.md +0 -10
  97. package/.codex/skills/snps-orqx/SKILL.md +0 -10
  98. /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
  99. /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
  100. /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
@@ -0,0 +1,36 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "algorithm": "sha256",
4
+ "files": {
5
+ "analyst.md": ["795b9209e7bc5bd31a736bb67ee5c0eeb1a95590afd47fb630c81de0dd87c126"],
6
+ "animations-orqx.md": ["f3cce5c6d75a6a872171593dba8bafeae657488950fad63c89f17e7e193e92e0"],
7
+ "architect.md": ["d2e8a9b7b4c7582870f08561f482fa6c51a0b347fdcc066bd88bda5575a2052f"],
8
+ "brand-orqx.md": ["a4afdbd9dc8fd118939be2e07377967a86748310b89c7f59b14818d84fbb10ad"],
9
+ "cloning-orqx.md": ["134d10224635b9898469d7bb3992f636ecdba375272e3ad1c24eb0215c1a85c0"],
10
+ "commercial-orqx.md": ["170288942d7c85c28fece5eef0256101eee0699a76a8f28b3e7ec5878d2f0b79"],
11
+ "content-orqx.md": ["f93bf397c8da352d10b884600ae339979a0c6c5190fb3b21daa1de6d3e7a2e45"],
12
+ "copy-orqx.md": ["86f70db676af7475d13face6468da78ee05e5c257b506fe484b62c0d1ca9439c"],
13
+ "council-orqx.md": ["d2ab7fcd71e71677d6fd15ae1565c35fb241e4b9558e79da0e030fc70a81efe4"],
14
+ "courses-orqx.md": ["236e69533c0155a42dfc762f563455244b71f127ae6459b70ffabad8ba081654"],
15
+ "cyber-orqx.md": ["8efff68f6e1441df8719b3d743dc27f9e6d0ce6d9f6e7128284a144d5c74ac63"],
16
+ "data-engineer.md": ["9c9faba56ad0bb8816e76117e0392e0727d8f4aba46413fa053c97d5aaf086bf"],
17
+ "design-orqx.md": ["e3b10fb943a64916826a11d13e0e57f61cc5e27e8966ea15924cae4e055aad64"],
18
+ "developer.md": ["b996b501f752b4f049367182d0c2c5a99019fddeab343c840a7da2f8ca1d70a4"],
19
+ "devops.md": ["18354e8a0e8b39786d48a5d9f8a7e532698e6fe49a65f3243be6a96eb6c73c42"],
20
+ "finance-orqx.md": ["f4ebeb70b7a0890c491608a00feedd31b9b4a9b872ed02d489b39a99fa01c537"],
21
+ "growth-orqx.md": ["b0f74d98f40fe384c81a22ab705007e4e40ab9f82bc6c23d2282a05aed17cbdf"],
22
+ "paidmedia-orqx.md": ["b68b91bc647fa6290c9109d621d98f6567b28967acb9e474cbf399442711c530"],
23
+ "product-lead.md": ["a62d2f6079b7deabe22d24cd66943375e82fbe9ea389e23bf9a7a9ea2fa55e08"],
24
+ "product-orqx.md": ["5df3d89096efdc1cabdcfaaf82b14571689d258a495981520ae0a0ea8fa04a92"],
25
+ "project-lead.md": ["0dd16d04f28c7614c025dadb557aa4b3eeb56c96fda8874afec40b8044db6e2b"],
26
+ "quality-gate.md": ["ed735180b28c51df2803933009f9b9bc6f5f497ac2b528919a4eed83e0ba1682"],
27
+ "research-orqx.md": ["9cf3c189b670ee4565329abec1a2c7d208a3013a9dba695208cbe617b4b0984b"],
28
+ "sinapse-orqx.md": ["36e704406c594a72cbf72eac52bb9ce22a636965a8c86a45c6560e4cc4fb67d6"],
29
+ "snps-orqx.md": ["0452d5291fd27239c0e6f79064d214c9755234e444daabbcea7cdd5e0f5a544d"],
30
+ "sprint-lead.md": ["3abfcccefb0e15765549cc54fabb8e398de135532c76f9ef8b16a57220dcac69"],
31
+ "squad-creator.md": ["1a92aeabebee66782dd906a75844451d3cb0fc0af59f7df266ab4106026ef657"],
32
+ "storytelling-orqx.md": ["45889421752caaecaff3ccc92d22e333235d675f65710f435b3b7224bbc0e022"],
33
+ "swarm-orqx.md": ["9eb519069dc7b9a10fd2ca677f4dfbed9ad3f966d0d65b2a73daf67b000b81c0"],
34
+ "ux-design-expert.md": ["f26f6935b95bd3c5d051a0b0a70394571f47fb1992e26ad17b25033b04f81048"]
35
+ }
36
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "algorithm": "sha256",
4
+ "files": {
5
+ ".agents/skills/sinapse-agent/SKILL.md": ["122ddfefe2fc23579c54513953f2f8cb7e872708c3c2078aac1a72a5e8a55959"],
6
+ ".agents/skills/sinapse-analyst/SKILL.md": ["3ff8b42481e23e847ebac3c255ff4437b984c715046e16fb61d0605fa1a5b677"],
7
+ ".agents/skills/sinapse-animations/SKILL.md": ["78666bb951ea083f1df13d9960ed7cecaef304024cea4392b2fc3022251fdab5"],
8
+ ".agents/skills/sinapse-architect/SKILL.md": ["05bbbaf7d493918cf4856feddd611b8f2e1f88924e7bba0f6ab9ce83ab7b6718"],
9
+ ".agents/skills/sinapse-brand/SKILL.md": ["bfbdebaa0e3c9e2668025af01df88841646ea450e4b1e6cb5008ca02258206db"],
10
+ ".agents/skills/sinapse-claude/SKILL.md": ["85f25d07838fa259cf48338ab526eeb6391c47ddbf10dba44aa6cc35421a90bf"],
11
+ ".agents/skills/sinapse-cloning/SKILL.md": ["b9e6d9e6c87cb79f261c46b6f6e83fc1d10506bcb920ece4012b07020334dc2a"],
12
+ ".agents/skills/sinapse-commercial/SKILL.md": ["b26cb18661293ffee078bd555677b7fa201718f1cdedf859dd358ae33b901e1e"],
13
+ ".agents/skills/sinapse-content/SKILL.md": ["61f4cbb9b9a5de4dde5fbf7b3027898be20fb127a287b19d5955e34a832b3ba3"],
14
+ ".agents/skills/sinapse-copy/SKILL.md": ["dddf3da31fe8807eb60450ad0dcdd73288ba32470b142d4d1c994277282dd1a2"],
15
+ ".agents/skills/sinapse-council/SKILL.md": ["6692021c2578524513a5462b7271aa7eda7335961cea5d9d015f7b75f3dfd2a1"],
16
+ ".agents/skills/sinapse-courses/SKILL.md": ["653a21c66dd78904b53b8924601778f1682db8b879ab5d50320d06452236687d"],
17
+ ".agents/skills/sinapse-cyber/SKILL.md": ["55c12f9a6f35970b3bf0d6d6ab8208b6348a9390040df1d8d01df108a369b429"],
18
+ ".agents/skills/sinapse-data-engineer/SKILL.md": ["5c8e31f9cba46f89fffbb5c83fe831725d951f75275b085b51ea48beab0291fa"],
19
+ ".agents/skills/sinapse-design/SKILL.md": ["3c35efa3e905d636352f5f046d494665ebf9e33f4c896fddcfa112103877a00d"],
20
+ ".agents/skills/sinapse-dev/SKILL.md": ["a2ff86c33e61efb1085e9388c8f1f6a58389b9dc8b74ecbe83c096b920f114db"],
21
+ ".agents/skills/sinapse-devops/SKILL.md": ["9cf5ef12999568e292f4e5d302bd61831872a783d6ed9caaca7a1ac247c63c01"],
22
+ ".agents/skills/sinapse-finance/SKILL.md": ["4b596578bbb950e8513eb96ea62200880c9184989bdfc91c7ecd1370f47b07ab"],
23
+ ".agents/skills/sinapse-growth/SKILL.md": ["53d32adb439098528925425d4ff86458e8fb2efb3c8fd6b62e8c42de4f06873c"],
24
+ ".agents/skills/sinapse-loop/SKILL.md": ["e5a33f9235c66e554e67d7668ac5e3255e1b6f9ed026f7bf3be666c169bf396a"],
25
+ ".agents/skills/sinapse-orqx/SKILL.md": ["b522b867768f4f6cf9151978734611b1482ec6f35cbe8552e9e324c4bb16de50"],
26
+ ".agents/skills/sinapse-paidmedia/SKILL.md": ["81ae19dae135dcd001703446131989ac51da1a299d8fab59ddc21596b1d462e2"],
27
+ ".agents/skills/sinapse-pm/SKILL.md": ["ecf406f3a94c48defff97c9ef55410cdf82cfa445b3d1244df0c6dd66543b910"],
28
+ ".agents/skills/sinapse-po/SKILL.md": ["56693d11a13cf51eb600883d26f232adfb6c0275015a1dfa0fd467387700667f"],
29
+ ".agents/skills/sinapse-product/SKILL.md": ["50bae9a40dca95287b864a873fe578953b5f361239a3e6c2b70ae2cd38197eb7"],
30
+ ".agents/skills/sinapse-qa/SKILL.md": ["54ac75619ae2cd28fa820e224abc5fc82163198652ce200d0706aa0734e98509"],
31
+ ".agents/skills/sinapse-research/SKILL.md": ["ac98bc465c1c975d95d93ffc6a665c41ab679e1885882ab7a91c86501a34d181"],
32
+ ".agents/skills/sinapse-sm/SKILL.md": ["408556e7cff08d5b84c724e55a3d35e7624e155984edfe697c7a9fe61e60a72f"],
33
+ ".agents/skills/sinapse-spec-driven/SKILL.md": ["5216b2f651f88263323a07cabec78259036bb4ec23689d312c770cef216b26a1"],
34
+ ".agents/skills/sinapse-squad-creator/SKILL.md": ["779a93754fb121d8f405667f2d3852faeb8945302129aec74c0d1f6b51f62ed3"],
35
+ ".agents/skills/sinapse-storytelling/SKILL.md": ["65e2d8719eee6d0221bcfe758144c24575cf00c0aa988b785f98c69fc4982025"],
36
+ ".agents/skills/sinapse-swarm/SKILL.md": ["8c98e9b1da2cb279ef7fa10ca4e81cb0a230ecdbfda4f890b0b94f1604626f00"],
37
+ ".agents/skills/sinapse-ux-design-expert/SKILL.md": ["5a1c747c030113b50d98a431bfffc4418e38355cae72f7e5a088ee5bee8bc530"],
38
+ ".agents/skills/sinapse/SKILL.md": ["fe017c091d1308eb937762ffe25f83518f778a266303cc3f028d0f468df0747f"],
39
+ ".agents/skills/snps-orqx/SKILL.md": ["24f66470a5721fc086a52eca9907958d091d68c2de0a0b56a42e07e83fe00342"],
40
+ ".agents/skills/snps/SKILL.md": ["9f9a0972a184a07234948f4e5dd8f7032835be3283608a00c8cbf8bbf68064a3"]
41
+ }
42
+ }
@@ -26,6 +26,24 @@ const { PostInstallValidator, formatReport: formatValidationReport } = require('
26
26
  const { _testing: { installGlobalAgents } } = require('../wizard/index');
27
27
  const { installSinapseCore } = require('../installer/sinapse-ai-installer');
28
28
 
29
+ function detectInstalledProviders(projectRoot, recordedProviders = []) {
30
+ if (recordedProviders.length > 0) {
31
+ return {
32
+ includeClaude: recordedProviders.includes('claude-code'),
33
+ includeCodex: recordedProviders.includes('codex'),
34
+ };
35
+ }
36
+ const includeClaude = fs.existsSync(path.join(projectRoot, '.claude', 'skill-manifest.json'));
37
+ const hasCodexCatalog = fs.existsSync(path.join(projectRoot, '.codex', 'catalog.json'));
38
+ const hasNativeActivator = fs.existsSync(
39
+ path.join(projectRoot, '.agents', 'skills', 'sinapse-agent', 'SKILL.md'),
40
+ );
41
+ const hasLegacyActivator = fs.existsSync(
42
+ path.join(projectRoot, '.codex', 'skills', 'sinapse-agent', 'SKILL.md'),
43
+ );
44
+ return { includeClaude, includeCodex: hasCodexCatalog && (hasNativeActivator || hasLegacyActivator) };
45
+ }
46
+
29
47
  /**
30
48
  * Update status types
31
49
  * @enum {string}
@@ -614,13 +632,10 @@ class SINAPSEUpdater {
614
632
  const recordedProviders = Array.isArray(this.versionInfo?.providers)
615
633
  ? this.versionInfo.providers
616
634
  : [];
617
- const includeClaude = recordedProviders.length > 0
618
- ? recordedProviders.includes('claude-code')
619
- : fs.existsSync(path.join(this.projectRoot, '.claude', 'skill-manifest.json'));
620
- const includeCodex = recordedProviders.length > 0
621
- ? recordedProviders.includes('codex')
622
- : fs.existsSync(path.join(this.projectRoot, '.codex', 'catalog.json'))
623
- && fs.existsSync(path.join(this.projectRoot, '.agents', 'skills', 'sinapse-agent', 'SKILL.md'));
635
+ const { includeClaude, includeCodex } = detectInstalledProviders(
636
+ this.projectRoot,
637
+ recordedProviders,
638
+ );
624
639
  const delivery = await installSinapseCore({
625
640
  targetDir: this.projectRoot,
626
641
  includeClaude,
@@ -631,6 +646,7 @@ class SINAPSEUpdater {
631
646
 
632
647
  result.success = true;
633
648
  result.filesUpdated = 1 + delivery.installedFiles.length;
649
+ result.codexLegacySkillReconciliation = delivery.codexLegacySkillReconciliation || null;
634
650
 
635
651
  return result;
636
652
  } catch (error) {
@@ -942,4 +958,5 @@ module.exports = {
942
958
  formatCheckResult,
943
959
  formatUpdateResult,
944
960
  sanitizeLogText,
961
+ detectInstalledProviders,
945
962
  };
@@ -18,6 +18,7 @@ const crossSpawn = require('cross-spawn');
18
18
  const { getIDEConfig } = require('../config/ide-configs');
19
19
  const { validateProjectName } = require('./validators');
20
20
  const { getMergeStrategy, hasMergeStrategy } = require('../merger/index.js');
21
+ const { reconcileLegacyClaudeAgentCommands } = require('../installer/sinapse-ai-installer');
21
22
 
22
23
  /**
23
24
  * Render template with variables
@@ -373,6 +374,9 @@ async function generateIDEConfigs(selectedIDEs, wizardState, options = {}) {
373
374
 
374
375
  // Copy agent files to IDE-specific agent folder
375
376
  if (ide.agentFolder) {
377
+ if (ideKey === 'claude-code') {
378
+ await reconcileLegacyClaudeAgentCommands(projectRoot);
379
+ }
376
380
  spinner.start(`Copying agents to ${ide.agentFolder}...`);
377
381
  const agentFiles = await copyAgentFiles(projectRoot, ide.agentFolder, ide);
378
382
  createdFiles.push(...agentFiles);
@@ -536,6 +536,10 @@ async function runWizard(options = {}) {
536
536
  const installed = pillars.filter(p => sinapseCoreResult.installedFolders.includes(p));
537
537
  const aux = sinapseCoreResult.installedFolders.filter(f => !pillars.includes(f));
538
538
  console.log(`✅ SINAPSE core: ${sinapseCoreResult.installedFiles.length} files (${installed.length} pillars, ${aux.length} modules)`);
539
+ const cleanup = sinapseCoreResult.codexLegacySkillReconciliation;
540
+ if (cleanup && (cleanup.removed || cleanup.migrated || cleanup.quarantined || cleanup.ambiguous.length)) {
541
+ console.log(`SINAPSE Codex skill cleanup: ${cleanup.removed} removed, ${cleanup.migrated} migrated, ${cleanup.quarantined} quarantined, ${cleanup.ambiguous.length} ambiguous`);
542
+ }
539
543
  }
540
544
  answers.sinapseCoreInstalled = true;
541
545
  answers.sinapseCoreResult = sinapseCoreResult;
@@ -806,7 +810,7 @@ async function runWizard(options = {}) {
806
810
  }
807
811
 
808
812
  // Codex local-first sync: the ide-sync above drives claude-code only now,
809
- // so .codex/agents + .codex/skills are regenerated here from the canonical
813
+ // so .codex/agents + .agents/skills are regenerated here from the canonical
810
814
  // source (the static .codex payload — scripts/tasks/JSONs — was already
811
815
  // copied by installSinapseCore when Codex was selected). Codex installs only.
812
816
  if (shouldConfigureCodex(selectedLLM)) {
@@ -822,7 +826,7 @@ async function runWizard(options = {}) {
822
826
  : { total: 0 };
823
827
  answers.codexSyncStatus = codexResult.ok ? 'synced' : 'failed';
824
828
  const m = codexResult.metrics || {};
825
- console.log(`✅ Codex sync: ${codexResult.ok ? 'synced' : 'issues'} (${m.codexAgents || 0} adapters, ${nativeResult.total || 0} native agents, ${m.codexSkills || 0} compatibility skills)`);
829
+ console.log(`✅ Codex sync: ${codexResult.ok ? 'synced' : 'issues'} (${m.codexAgents || 0} adapters, ${nativeResult.total || 0} native agents, ${m.codexSkills || 0} native skills)`);
826
830
  } catch (codexErr) {
827
831
  console.warn(`⚠️ Codex sync failed: ${codexErr.message} — run 'npm run sync:ide:codex' post-install`);
828
832
  answers.codexSyncStatus = 'failed';
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const legacy = require('../.sinapse-ai/infrastructure/scripts/validate-codex-command-registry');
8
+
9
+ function resolveRegistryPath(projectRoot, declaredPath) {
10
+ if (typeof declaredPath !== 'string' || !declaredPath.trim() || path.isAbsolute(declaredPath)) return null;
11
+ const resolved = path.resolve(projectRoot, declaredPath);
12
+ const relative = path.relative(path.resolve(projectRoot), resolved);
13
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return null;
14
+ return resolved;
15
+ }
16
+
17
+ function validateCodexCommandRegistry(options = {}) {
18
+ const projectRoot = options.projectRoot || process.cwd();
19
+ const { registryPath, registry, error } = legacy.loadRegistry(projectRoot);
20
+ const errors = [];
21
+ const requiredCoverage = options.requiredCoverage === false
22
+ ? null
23
+ : options.requiredCoverage || legacy.REQUIRED_COMMAND_COVERAGE;
24
+
25
+ if (error) return { ok: false, errors: [error], warnings: [], metrics: { agents: 0, commands: 0 } };
26
+
27
+ let commandCount = 0;
28
+ const seenAgentAliases = new Map();
29
+ for (const [agentId, agentSpec] of Object.entries(registry.agents || {})) {
30
+ const skillPath = path.join(projectRoot, '.agents', 'skills', agentSpec.skillId || agentId, 'SKILL.md');
31
+ if (!fs.existsSync(skillPath)) {
32
+ errors.push(`${agentId}: missing skill file ${path.relative(projectRoot, skillPath)}`);
33
+ }
34
+
35
+ const sourceOfTruth = resolveRegistryPath(projectRoot, agentSpec.sourceOfTruth);
36
+ if (!sourceOfTruth) {
37
+ errors.push(`${agentId}: invalid or missing source of truth`);
38
+ } else if (!fs.existsSync(sourceOfTruth)) {
39
+ errors.push(`${agentId}: missing source of truth ${path.relative(projectRoot, sourceOfTruth)}`);
40
+ }
41
+
42
+ for (const alias of legacy.collectAgentAliases(agentId, agentSpec)) {
43
+ const owner = seenAgentAliases.get(alias);
44
+ if (owner && owner !== agentId) errors.push(`duplicate agent alias "${alias}" claimed by ${owner} and ${agentId}`);
45
+ else seenAgentAliases.set(alias, agentId);
46
+ }
47
+
48
+ const seenCommandAliases = new Map();
49
+ for (const [commandId, commandSpec] of Object.entries(agentSpec.commands || {})) {
50
+ commandCount += 1;
51
+ const targetPath = resolveRegistryPath(projectRoot, commandSpec.target);
52
+ if (!targetPath) {
53
+ errors.push(`${agentId}.${commandId}: invalid or missing target`);
54
+ } else if (!fs.existsSync(targetPath)) {
55
+ errors.push(`${agentId}.${commandId}: missing target ${path.relative(projectRoot, targetPath)}`);
56
+ }
57
+ for (const resource of commandSpec.resources || []) {
58
+ const resourcePath = resolveRegistryPath(projectRoot, resource);
59
+ if (!resourcePath) {
60
+ errors.push(`${agentId}.${commandId}: invalid or missing resource`);
61
+ } else if (!fs.existsSync(resourcePath)) {
62
+ errors.push(`${agentId}.${commandId}: missing resource ${path.relative(projectRoot, resourcePath)}`);
63
+ }
64
+ }
65
+ for (const alias of legacy.collectCommandAliases(commandId, commandSpec)) {
66
+ const owner = seenCommandAliases.get(alias);
67
+ if (owner && owner !== commandId) errors.push(`duplicate command alias "${alias}" claimed by ${owner} and ${commandId}`);
68
+ else seenCommandAliases.set(alias, commandId);
69
+ }
70
+ }
71
+ }
72
+
73
+ legacy.validateRequiredCoverage(registry, requiredCoverage, errors);
74
+ return {
75
+ ok: errors.length === 0,
76
+ errors,
77
+ warnings: [],
78
+ metrics: {
79
+ agents: Object.keys(registry.agents || {}).length,
80
+ commands: commandCount,
81
+ registryPath: path.relative(projectRoot, registryPath),
82
+ },
83
+ };
84
+ }
85
+
86
+ function main() {
87
+ const args = legacy.parseArgs();
88
+ const result = validateCodexCommandRegistry(args);
89
+ if (!args.quiet) {
90
+ console.log(args.json ? JSON.stringify(result, null, 2) : legacy.formatHumanReport(result));
91
+ }
92
+ if (!result.ok) process.exitCode = 1;
93
+ }
94
+
95
+ if (require.main === module) main();
96
+
97
+ module.exports = {
98
+ ...legacy,
99
+ resolveRegistryPath,
100
+ validateCodexCommandRegistry,
101
+ };
@@ -3,7 +3,9 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const crypto = require('crypto');
6
7
  const yaml = require('js-yaml');
8
+ const legacyClaudeCommandHashes = require('../packages/installer/src/migrations/legacy-claude-agent-command-hashes.json');
7
9
 
8
10
  const { validateNativeCodex } = require('../.codex/scripts/validate-codex-native');
9
11
 
@@ -17,8 +19,103 @@ function parseFrontmatter(content) {
17
19
  return metadata;
18
20
  }
19
21
 
22
+ const REQUIRED_CLAUDE_HOOKS = [
23
+ { event: 'PreToolUse', matcher: 'Write|Edit', filename: 'doc-first-gate.cjs' },
24
+ { event: 'PreToolUse', matcher: 'Write|Edit|Bash', filename: 'enforce-delegation.cjs' },
25
+ { event: 'PreToolUse', matcher: 'Write|Edit', filename: 'enforce-framework-boundary.cjs' },
26
+ { event: 'PreToolUse', matcher: 'Bash', filename: 'enforce-git-push-authority.sh' },
27
+ { event: 'PreToolUse', matcher: 'Bash', filename: 'verify-packages.cjs' },
28
+ ];
29
+
30
+ function validateClaudeHookSettings(projectRoot) {
31
+ const errors = [];
32
+ const settingsCandidates = [
33
+ path.join(projectRoot, '.claude', 'settings.json'),
34
+ path.join(projectRoot, '.claude', 'settings.local.json'),
35
+ ];
36
+ const settingsPaths = settingsCandidates.filter((candidate) => fs.existsSync(candidate));
37
+ if (settingsPaths.length === 0) {
38
+ return ['Claude hook settings are missing: expected .claude/settings.json or .claude/settings.local.json'];
39
+ }
40
+ const commands = [];
41
+ for (const settingsPath of settingsPaths) {
42
+ try {
43
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
44
+ for (const [event, groups] of Object.entries(settings.hooks || {})) {
45
+ for (const group of groups || []) {
46
+ commands.push(...(group.hooks || []).map((hook) => ({
47
+ command: String(hook.command || ''),
48
+ event,
49
+ matcher: group.matcher ?? null,
50
+ source: path.relative(projectRoot, settingsPath),
51
+ })));
52
+ }
53
+ }
54
+ } catch (error) {
55
+ errors.push(`Claude hook settings (${path.relative(projectRoot, settingsPath)}): ${error.message}`);
56
+ }
57
+ }
58
+ const registered = new Set();
59
+ for (const entry of commands) {
60
+ const match = entry.command.replace(/\\/g, '/').match(/\.claude\/hooks\/([^"'\s]+)/);
61
+ if (!match) continue;
62
+ const registrationKey = JSON.stringify([entry.event, entry.matcher, match[1]]);
63
+ if (registered.has(registrationKey)) {
64
+ errors.push(`Claude hook is registered more than once for the same event and matcher: ${match[1]} (${entry.source})`);
65
+ continue;
66
+ }
67
+ registered.add(registrationKey);
68
+ if (!fs.existsSync(path.join(projectRoot, '.claude', 'hooks', match[1]))) {
69
+ errors.push(`Claude settings points to missing hook: .claude/hooks/${match[1]}`);
70
+ }
71
+ }
72
+ for (const hook of REQUIRED_CLAUDE_HOOKS) {
73
+ const requiredKey = JSON.stringify([hook.event, hook.matcher, hook.filename]);
74
+ if (!registered.has(requiredKey)) {
75
+ errors.push(`Claude governance hook is not registered at ${hook.event}/${hook.matcher}: ${hook.filename}`);
76
+ }
77
+ }
78
+ return errors;
79
+ }
80
+
81
+ function validateClaudeAliasTargets(projectRoot) {
82
+ const errors = [];
83
+ const aliases = {
84
+ sinapse: ['# SINAPSE Claude Activation: sinapse-orqx', 'development/agents/snps-orqx.md'],
85
+ 'sinapse-orqx': ['# SINAPSE Claude Activation: sinapse-orqx', 'development/agents/snps-orqx.md'],
86
+ snps: ['# SINAPSE Claude Activation: sinapse-orqx', 'development/agents/snps-orqx.md'],
87
+ 'snps-orqx': ['# SINAPSE Claude Activation: sinapse-orqx', 'development/agents/snps-orqx.md'],
88
+ 'sinapse-agent': ['# SINAPSE Parametric Agent Activator for Claude Code', '.claude/agents/sinapse-'],
89
+ };
90
+ for (const [alias, [heading, canonicalTarget]] of Object.entries(aliases)) {
91
+ const relativePath = path.join('.claude', 'skills', alias, 'SKILL.md');
92
+ try {
93
+ const content = fs.readFileSync(path.join(projectRoot, relativePath), 'utf8');
94
+ if (!content.includes(heading) || !content.includes(canonicalTarget)) {
95
+ errors.push(`${relativePath} does not resolve to its canonical target`);
96
+ }
97
+ } catch (error) {
98
+ errors.push(`${relativePath}: ${error.message}`);
99
+ }
100
+ }
101
+ return errors;
102
+ }
103
+
20
104
  function validateClaudeNative(projectRoot = PROJECT_ROOT) {
21
105
  const errors = [];
106
+ const legacyCommandsDir = path.join(projectRoot, '.claude', 'commands', 'SINAPSE', 'agents');
107
+ if (fs.existsSync(legacyCommandsDir)) {
108
+ const legacyCommands = fs.readdirSync(legacyCommandsDir).filter((file) => file.endsWith('.md'));
109
+ const managedLegacyCommands = legacyCommands.filter((file) => {
110
+ const content = fs.readFileSync(path.join(legacyCommandsDir, file));
111
+ const digest = crypto.createHash('sha256').update(content).digest('hex');
112
+ return (legacyClaudeCommandHashes.files[file] || []).includes(digest)
113
+ || content.includes(Buffer.from('SINAPSE-MANAGED:claude-command'));
114
+ });
115
+ if (managedLegacyCommands.length > 0) {
116
+ errors.push(`Legacy Claude agent command surface contains ${managedLegacyCommands.length} managed duplicates`);
117
+ }
118
+ }
22
119
  const agentsDir = path.join(projectRoot, '.claude', 'agents');
23
120
  const agentFiles = fs.existsSync(agentsDir)
24
121
  ? fs.readdirSync(agentsDir).filter((file) => /^sinapse-.+\.md$/.test(file)).sort()
@@ -29,6 +126,9 @@ function validateClaudeNative(projectRoot = PROJECT_ROOT) {
29
126
  const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
30
127
  expectedSkills = (manifest.skillIds || []).map((skillId) => ({ skillId }));
31
128
  if (expectedSkills.length !== 36) errors.push(`Claude skill manifest must contain 36 IDs, found ${expectedSkills.length}`);
129
+ for (const alias of ['sinapse', 'sinapse-orqx', 'snps', 'snps-orqx', 'sinapse-agent']) {
130
+ if (!manifest.skillIds.includes(alias)) errors.push(`Claude skill manifest is missing public alias ${alias}`);
131
+ }
32
132
  } catch (error) {
33
133
  errors.push(`Claude skill manifest: ${error.message}`);
34
134
  }
@@ -77,6 +177,15 @@ function validateClaudeNative(projectRoot = PROJECT_ROOT) {
77
177
  if (!claudeNames.has(name)) errors.push(`Claude native inventory is missing ${name}`);
78
178
  }
79
179
 
180
+ const hooksDir = path.join(projectRoot, '.claude', 'hooks');
181
+ for (const hook of REQUIRED_CLAUDE_HOOKS) {
182
+ if (!fs.existsSync(path.join(hooksDir, hook.filename))) {
183
+ errors.push(`Missing Claude governance hook: .claude/hooks/${hook.filename}`);
184
+ }
185
+ }
186
+ errors.push(...validateClaudeHookSettings(projectRoot));
187
+ errors.push(...validateClaudeAliasTargets(projectRoot));
188
+
80
189
  return {
81
190
  ok: errors.length === 0,
82
191
  errors,
@@ -99,6 +208,7 @@ function validateProviderAdapters(projectRoot = PROJECT_ROOT) {
99
208
  for (const entry of ['.claude/agents/', '.claude/skills/', '.agents/skills/', '.codex/agents/']) {
100
209
  if (!pkg.files.includes(entry)) errors.push(`package.json files[] is missing ${entry}`);
101
210
  }
211
+ if (pkg.files.includes('.codex/skills/')) errors.push('package.json must not publish the legacy .codex/skills root');
102
212
  return {
103
213
  ok: errors.length === 0,
104
214
  errors,
@@ -124,4 +234,10 @@ function main() {
124
234
 
125
235
  if (require.main === module) main();
126
236
 
127
- module.exports = { parseFrontmatter, validateClaudeNative, validateProviderAdapters };
237
+ module.exports = {
238
+ parseFrontmatter,
239
+ validateClaudeAliasTargets,
240
+ validateClaudeHookSettings,
241
+ validateClaudeNative,
242
+ validateProviderAdapters,
243
+ };
@@ -484,7 +484,7 @@ dependencies:
484
484
 
485
485
  sinapse_mapping: |
486
486
  SINAPSE tasks (.sinapse-ai/development/tasks/) map to Claude Code skills (.claude/skills/)
487
- SINAPSE agents (.claude/commands/SINAPSE/agents/) map to Claude Code subagents (.claude/agents/)
487
+ SINAPSE agents (.claude/agents/) map to Claude Code subagents (.claude/agents/)
488
488
  SINAPSE workflows map to Claude Code command sequences
489
489
  SINAPSE checklists map to skill validation steps
490
490
  SINAPSE templates map to skill supporting files (templates/)
@@ -1119,7 +1119,7 @@ Type `*help` to see all commands, or `*guide` for detailed usage.
1119
1119
  | SINAPSE Concept | Claude Code Equivalent |
1120
1120
  |-------------|----------------------|
1121
1121
  | Task (`.sinapse-ai/development/tasks/`) | Skill (`.claude/skills/`) |
1122
- | Agent (`.claude/commands/SINAPSE/agents/`) | Subagent (`.claude/agents/`) |
1122
+ | Agent (`.claude/agents/`) | Subagent (`.claude/agents/`) |
1123
1123
  | Workflow | Command sequence / Skill chain |
1124
1124
  | Checklist | Skill validation steps |
1125
1125
  | Template | Skill supporting file |
@@ -353,7 +353,7 @@ Example: `sinapse-claude:claude-code-audit`
353
353
  | Platform | Support | Notes |
354
354
  |----------|---------|-------|
355
355
  | Claude Code | Full | Native marketplace |
356
- | Codex | Full | `.codex/skills/` directory |
356
+ | Codex | Full | `.agents/skills/` directory |
357
357
  | Gemini CLI | Full | Standard SKILL.md |
358
358
  | Cursor | Full | Marketplace |
359
359
  | VS Code | Full | Extension integration |
@@ -30,7 +30,7 @@ You are an autonomous SINAPSE UX Design Expert agent spawned to execute a specif
30
30
 
31
31
  ## 1. Persona Loading
32
32
 
33
- Read `.claude/commands/SINAPSE/agents/ux-design-expert.md` and adopt the persona of **Uma**.
33
+ Read `.claude/agents/ux-design-expert.md` and adopt the persona of **Uma**.
34
34
  - SKIP the greeting flow entirely — go straight to work
35
35
 
36
36
  ## 2. Context Loading (mandatory)
@@ -1,10 +0,0 @@
1
- ---
2
- name: sinapse
3
- description: Activate the SINAPSE supreme orchestrator in Codex.
4
- ---
5
-
6
- # SINAPSE Supreme Orchestrator Alias
7
-
8
- This is a public alias for `$sinapse-orqx`. Follow the authoritative skill at
9
- `.agents/skills/sinapse-orqx/SKILL.md` exactly. Resolve all aliases to the
10
- canonical `snps-orqx` source and preserve its delegation-only authority.
@@ -1,22 +0,0 @@
1
- ---
2
- name: sinapse-agent
3
- description: Resolve and activate any canonical SINAPSE agent by ID in Codex.
4
- ---
5
-
6
- # SINAPSE Parametric Agent Activator
7
-
8
- Use the agent ID supplied after `$sinapse-agent`.
9
- Canonical agent files under `.sinapse-ai/development/agents/` are the source of truth;
10
- squad specialists resolve to their catalog-declared source. A persona greeting may use
11
- `.codex/scripts/generate-codex-greeting.js` after successful resolution.
12
-
13
- 1. Resolve it with `node .codex/scripts/resolve-codex-agent.js <agent-id> --json`.
14
- 2. If resolution fails, stop and return the resolver error without guessing an ID.
15
- 3. Read the returned `sourceOfTruth` and `pointer` files.
16
- 4. Adopt the canonical persona, authority boundaries and activation protocol.
17
- 5. Resolve starred commands with
18
- `node .codex/scripts/resolve-codex-command.js <agent-id> <command> --json`.
19
- 6. Use native Codex collaboration for delegation; never start nested Codex or Claude.
20
-
21
- The 172 TOML adapters in `.codex/agents` remain the native subagent layer. This
22
- skill is the discoverable `$` entrypoint and must not copy or redefine personas.
@@ -1,29 +0,0 @@
1
- ---
2
- name: sinapse-analyst
3
- description: Business Analyst (Scope). Use for market research, competitive analysis, user research, brainstorming session facilitation, structured ideation workshops, feasibility studies, i...
4
- ---
5
-
6
- # SINAPSE Business Analyst Activator
7
-
8
- ## When To Use
9
- Use for market research, competitive analysis, user research, brainstorming session facilitation, structured ideation workshops, feasibility studies, industry trends analysis, project discovery (brownfield documentati...
10
-
11
- ## Activation Protocol
12
- 1. Load `.sinapse-ai/development/agents/analyst.md` as source of truth (fallback: `.codex/agents/analyst.md`).
13
- 2. Generate greeting via `node .sinapse-ai/development/scripts/generate-greeting.js analyst` and show it first.
14
- 3. Adopt this agent persona and command system.
15
- 4. If a starred command is invoked in Codex, resolve it via `node .codex/scripts/resolve-codex-command.js sinapse-analyst <command>` when a registry mapping exists.
16
- 5. Stay in this persona until the user asks to switch or exit.
17
-
18
- ## Starter Commands
19
- - `*help` - Show all available commands with descriptions
20
- - `*create-project-brief` - Create project brief document
21
- - `*perform-market-research` - Create market research analysis
22
- - `*create-competitor-analysis` - Create competitive analysis
23
- - `*brainstorm` - Facilitate structured brainstorming
24
- - `*guide` - Show comprehensive usage guide for this agent
25
-
26
- ## Non-Negotiables
27
- - Follow `.sinapse-ai/constitution.md`.
28
- - Execute workflows/tasks only from declared dependencies.
29
- - Do not invent requirements outside the project artifacts.
@@ -1,28 +0,0 @@
1
- ---
2
- name: sinapse-animations
3
- description: Motion design, CSS animations, particulas, 3D, micro-interacoes
4
- ---
5
-
6
- # SINAPSE Animations Squad Activator
7
-
8
- ## When To Use
9
- Motion design, CSS animations, particulas, 3D, micro-interacoes
10
-
11
- ## Activation Protocol
12
- 1. Load `squads/squad-animations/agents/animations-orqx.md` as source of truth (fallback: `.codex/agents/animations-orqx.md`).
13
- 2. Load squad knowledge base from `squads/squad-animations/knowledge-base/`.
14
- 3. Adopt the orqx persona and command system.
15
- 4. Stay in this persona until the user asks to switch or exit.
16
-
17
- ## Starter Commands
18
- - `*motion` - Animacao CSS/JS com performance
19
- - `*particulas` - Sistema de particulas interativo
20
- - `*transicao` - Animacao de transicao
21
- - `*3d` - Efeito 3D com CSS/WebGL
22
- - `*micro-interacao` - Micro-interacoes de feedback
23
-
24
- ## Non-Negotiables
25
- - Follow `.sinapse-ai/constitution.md`.
26
- - Load squad KB before executing any task.
27
- - Execute tasks only from `squads/squad-animations/tasks/`.
28
- - Output quality: 5.0/5.0 minimum.
@@ -1,31 +0,0 @@
1
- ---
2
- name: sinapse-architect
3
- description: Architect (Stratum). Use for system architecture (fullstack, backend, frontend, infrastructure), technology stack selection (technical evaluation), API design (REST/GraphQL/tRPC...
4
- ---
5
-
6
- # SINAPSE Architect Activator
7
-
8
- ## When To Use
9
- Use for system architecture (fullstack, backend, frontend, infrastructure), technology stack selection (technical evaluation), API design (REST/GraphQL/tRPC/WebSocket), security architecture, performance optimization,...
10
-
11
- ## Activation Protocol
12
- 1. Load `.sinapse-ai/development/agents/architect.md` as source of truth (fallback: `.codex/agents/architect.md`).
13
- 2. Generate greeting via `node .sinapse-ai/development/scripts/generate-greeting.js architect` and show it first.
14
- 3. Adopt this agent persona and command system.
15
- 4. If a starred command is invoked in Codex, resolve it via `node .codex/scripts/resolve-codex-command.js sinapse-architect <command>` when a registry mapping exists.
16
- 5. Stay in this persona until the user asks to switch or exit.
17
-
18
- ## Starter Commands
19
- - `*help` - Show all available commands with descriptions
20
- - `*create-full-stack-architecture` - Complete system architecture
21
- - `*create-backend-architecture` - Backend architecture design
22
- - `*create-front-end-architecture` - Frontend architecture design
23
- - `*document-project` - Generate project documentation
24
- - `*research` - Generate deep research prompt
25
- - `*analyze-project-structure` - Analyze project for new feature implementation (WIS-15)
26
- - `*guide` - Show comprehensive usage guide for this agent
27
-
28
- ## Non-Negotiables
29
- - Follow `.sinapse-ai/constitution.md`.
30
- - Execute workflows/tasks only from declared dependencies.
31
- - Do not invent requirements outside the project artifacts.
@@ -1,28 +0,0 @@
1
- ---
2
- name: sinapse-brand
3
- description: Estrategia de marca, identidade visual, brandbook, auditoria de marca
4
- ---
5
-
6
- # SINAPSE Brand Squad Activator
7
-
8
- ## When To Use
9
- Estrategia de marca, identidade visual, brandbook, auditoria de marca
10
-
11
- ## Activation Protocol
12
- 1. Load `squads/squad-brand/agents/brand-orqx.md` as source of truth (fallback: `.codex/agents/brand-orqx.md`).
13
- 2. Load squad knowledge base from `squads/squad-brand/knowledge-base/`.
14
- 3. Adopt the orqx persona and command system.
15
- 4. Stay in this persona until the user asks to switch or exit.
16
-
17
- ## Starter Commands
18
- - `*criar-marca` - Ciclo completo de branding (zero-to-brand)
19
- - `*auditar-marca` - Analisa coerencia e gaps de marca existente
20
- - `*brandbook` - Compila brandbook final
21
- - `*identidade` - Cria identidade visual
22
- - `*posicionamento` - Define posicionamento estrategico
23
-
24
- ## Non-Negotiables
25
- - Follow `.sinapse-ai/constitution.md`.
26
- - Load squad KB before executing any task.
27
- - Execute tasks only from `squads/squad-brand/tasks/`.
28
- - Output quality: 5.0/5.0 minimum.