clckernel 1.2.6 → 1.2.8

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 (45) hide show
  1. package/README.es.md +51 -118
  2. package/README.md +48 -116
  3. package/package.json +14 -2
  4. package/src/adapters/agent-os.js +183 -0
  5. package/src/catalog.js +16 -0
  6. package/src/detector.js +2 -0
  7. package/src/index.js +11 -0
  8. package/src/ui.js +79 -35
  9. package/tools/audit.js +0 -0
  10. package/tools/audit.py +0 -0
  11. package/tools/check_custom.js +0 -0
  12. package/tools/check_custom.py +0 -0
  13. package/tools/check_db_efficiency.py +0 -0
  14. package/tools/check_domain_data.js +100 -0
  15. package/tools/check_hitl.js +93 -0
  16. package/tools/check_responsive.js +0 -0
  17. package/tools/check_seo.js +1 -1
  18. package/tools/check_symlinks.js +76 -0
  19. package/tools/scan_secrets.js +12 -1
  20. package/tools/scan_secrets.py +0 -0
  21. package/AGENTS.md +0 -124
  22. package/docs/Journal/001-adr-clckernel-governance.md +0 -18
  23. package/test/adapters/astro.test.js +0 -102
  24. package/test/adapters/base.test.js +0 -86
  25. package/test/adapters/django.test.js +0 -95
  26. package/test/adapters/fastapi.test.js +0 -88
  27. package/test/adapters/go.test.js +0 -81
  28. package/test/adapters/laravel.test.js +0 -106
  29. package/test/adapters/nextjs.test.js +0 -113
  30. package/test/adapters/rails.test.js +0 -108
  31. package/test/adapters/rust.test.js +0 -83
  32. package/test/catalog.test.js +0 -170
  33. package/test/config.test.js +0 -512
  34. package/test/detector.test.js +0 -345
  35. package/test/doctor.test.js +0 -302
  36. package/test/generator.test.js +0 -419
  37. package/test/guards_new.test.js +0 -95
  38. package/test/helpers.js +0 -36
  39. package/test/integration/cli-flow.test.js +0 -270
  40. package/test/technologies/celery_guard.test.js +0 -185
  41. package/test/technologies/detector.test.js +0 -342
  42. package/test/technologies/docker_guard.test.js +0 -187
  43. package/test/technologies/integration.test.js +0 -123
  44. package/test/technologies/postgres_guard.test.js +0 -87
  45. package/test/technologies/redis_guard.test.js +0 -128
@@ -0,0 +1,183 @@
1
+ /**
2
+ * CLC Kernel — Agent-OS & Domain Workflows Stack Adapter
3
+ * Supports AI Agent Operating Systems (Marketing OS, Content Ops, Research OS)
4
+ * featuring Dual-Layer Governance and Domain Integrity Safeguards.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const BaseAdapter = require('./base');
10
+
11
+ class AgentOSAdapter extends BaseAdapter {
12
+ constructor() {
13
+ super('Agent-OS (Domain & AI Workflows)', 'agent-os', 'Playbook / Contract Test');
14
+ }
15
+
16
+ detect(targetDir) {
17
+ // 1. package.json check
18
+ const pkgPath = path.join(targetDir, 'package.json');
19
+ if (fs.existsSync(pkgPath)) {
20
+ try {
21
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
22
+ const name = (pkg.name || '').toLowerCase();
23
+ const desc = (pkg.description || '').toLowerCase();
24
+ const keywords = Array.isArray(pkg.keywords) ? pkg.keywords.map(k => k.toLowerCase()) : [];
25
+ if (
26
+ name.includes('agent-os') || name.includes('marketing-os') || name.includes('laila-mos') ||
27
+ desc.includes('agent-os') || desc.includes('marketing operating system') ||
28
+ keywords.includes('agent-os') || keywords.includes('marketing-os') || keywords.includes('ai-agents')
29
+ ) {
30
+ return true;
31
+ }
32
+ } catch {}
33
+ }
34
+
35
+ // 2. skills/ or .agents/skills/ directory check
36
+ const skillsDir = path.join(targetDir, 'skills');
37
+ const agentSkillsDir = path.join(targetDir, '.agents', 'skills');
38
+ if (fs.existsSync(skillsDir) || fs.existsSync(agentSkillsDir)) {
39
+ const dirToCheck = fs.existsSync(skillsDir) ? skillsDir : agentSkillsDir;
40
+ try {
41
+ const files = fs.readdirSync(dirToCheck);
42
+ if (files.some(f => f.endsWith('.md'))) {
43
+ return true;
44
+ }
45
+ } catch {}
46
+ }
47
+
48
+ // 3. brand/ or templates/ check alongside README referencing agents/workflows
49
+ const brandPath = path.join(targetDir, 'brand');
50
+ const templatesPath = path.join(targetDir, 'templates');
51
+ const readmePath = path.join(targetDir, 'README.md');
52
+ if ((fs.existsSync(brandPath) || fs.existsSync(templatesPath)) && fs.existsSync(readmePath)) {
53
+ try {
54
+ const readme = fs.readFileSync(readmePath, 'utf-8').toLowerCase();
55
+ if (readme.includes('subagent') || readme.includes('operating system') || readme.includes('agent')) {
56
+ return true;
57
+ }
58
+ } catch {}
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ getSafeguards() {
65
+ return [
66
+ 'Dual-Layer Architecture: Layer 1 (Engineering) & Layer 2 (Domain Runtime)',
67
+ 'Mandatory Human-in-the-Loop (HITL) Checkpoints',
68
+ 'Symlink Interoperability (.cursorrules, CLAUDE.md, GEMINI.md, .windsurfrules)',
69
+ 'Domain Data Integrity & Clean Creation State (No Hallucinated Metrics)',
70
+ 'Single Source of Truth (SSOT) Memory Primacy',
71
+ 'Local-only Spec Driven Development (sdds/)'
72
+ ];
73
+ }
74
+
75
+ getTools() {
76
+ return [
77
+ {
78
+ name: 'check_symlinks',
79
+ language: 'node',
80
+ path: 'tools/check_symlinks.js',
81
+ command: 'node tools/check_symlinks.js',
82
+ description: 'AI IDE symlinks and rule mirror verification'
83
+ },
84
+ {
85
+ name: 'check_hitl',
86
+ language: 'node',
87
+ path: 'tools/check_hitl.js',
88
+ command: 'node tools/check_hitl.js',
89
+ description: 'Human-in-the-Loop review checkpoint validator'
90
+ },
91
+ {
92
+ name: 'check_domain_data',
93
+ language: 'node',
94
+ path: 'tools/check_domain_data.js',
95
+ command: 'node tools/check_domain_data.js',
96
+ description: 'Domain deliverables schema and clean state integrity'
97
+ },
98
+ {
99
+ name: 'scan_secrets',
100
+ language: 'node',
101
+ path: 'tools/scan_secrets.js',
102
+ command: 'node tools/scan_secrets.js',
103
+ description: 'Security & API key leak scanner'
104
+ }
105
+ ];
106
+ }
107
+
108
+ provision(targetDir, config) {
109
+ // 1. Create docs/, sdds/, and tools/
110
+ super.provision(targetDir, config);
111
+
112
+ // 2. Write Dual-Layer AGENTS.md
113
+ const dualLayerAgents = `# Standard Operating Procedure (SOP) — AGENTS
114
+ Governed by **CLC Kernel: The Universal AI Agent Governance Engine**.
115
+ Target System: **${this.name}**
116
+
117
+ ---
118
+
119
+ ## 🛠️ Layer 1: Development & Maintenance Governance (Engineering)
120
+ All AI Agents (Antigravity, OpenCode, Claude Code, Cursor, Windsurf, Copilot) developing or maintaining this codebase MUST adhere to strict engineering standards:
121
+
122
+ > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (UNBYPASSABLE)**
123
+ > Even when the user issues a direct or urgent fix request ("fix this bug", "fix this error", "quick fix"):
124
+ > YOU ARE STRICTLY FORBIDDEN from modifying source code or workflows directly without completing the full quality harness:
125
+ > 1. **Research & Root Cause Analysis:** Investigate tracebacks and affected files before editing.
126
+ > 2. **SDD Specification:** Document non-trivial changes in \`sdds/{change-name}/01-spec.md\` (gitignored).
127
+ > 3. **Verification (Red Phase):** Write a failing test or contract validation first.
128
+ > 4. **Clean Implementation (Green Phase):** Make tests pass maintaining strict layer isolation.
129
+ > 5. **Mandatory Audit Gate:** Execute \`node tools/audit.js\` and output summary to stdout.
130
+
131
+ ### Engineering Rules
132
+ 1. **Conventional Commits Only**: Commits must use Conventional Commits (e.g. \`feat(...): ...\`, \`fix(...): ...\`). NEVER add \`Co-Authored-By\` or AI attribution.
133
+ 2. **Mirror Links Interoperability**: \`CLAUDE.md\`, \`GEMINI.md\`, \`.cursorrules\`, and \`.windsurfrules\` must remain valid symlinks to \`AGENTS.md\`.
134
+ 3. **No Superficial Symptom Patches**: Always address root causes.
135
+
136
+ ---
137
+
138
+ ## 🚀 Layer 2: Domain Runtime Directives (Agent Workflows & Operations)
139
+ When operating as domain agents for end-users and non-technical operators:
140
+
141
+ 1. **Non-Technical Operator Friendly**: Interact naturally with the operator. Guide step-by-step through conversational prompts without technical jargon.
142
+ 2. **Mandatory Human-in-the-Loop (HITL) Checkpoints**: Always pause phase-by-phase during multi-step pipelines. Present draft output and WAIT for explicit user confirmation before executing irreversible steps.
143
+ 3. **SSOT Memory Primacy**: Always read from and persist to designated single-source-of-truth memory files (e.g. \`brand/client-profile.md\` or \`docs/\`).
144
+ 4. **Clean Creation State (No Hallucinated Data)**: When new records, calendars, or deliverables are drafted, metric fields must initialize as \`null\` / empty. Never invent fictional stats.
145
+ 5. **Deterministic Output Standard**: Outputs must strictly follow valid Markdown, JSON schema, or domain templates.
146
+
147
+ ---
148
+
149
+ ## 🛡️ Core AI Safeguards
150
+ ${this.getSafeguards().map(s => `- ${s}`).join('\n')}
151
+ `;
152
+
153
+ fs.writeFileSync(path.join(targetDir, 'AGENTS.md'), dualLayerAgents, 'utf-8');
154
+
155
+ // 3. Create AI IDE mirror symlinks
156
+ const mirrors = ['CLAUDE.md', 'GEMINI.md', '.cursorrules', '.windsurfrules'];
157
+ for (const mirror of mirrors) {
158
+ const mirrorPath = path.join(targetDir, mirror);
159
+ try {
160
+ if (!fs.existsSync(mirrorPath)) {
161
+ fs.symlinkSync('AGENTS.md', mirrorPath);
162
+ }
163
+ } catch {}
164
+ }
165
+
166
+ // 4. Copy domain guard tools into tools/
167
+ const srcToolsDir = path.join(__dirname, '..', '..', 'tools');
168
+ const targetToolsDir = path.join(targetDir, 'tools');
169
+ const toolsToCopy = ['check_symlinks.js', 'check_hitl.js', 'check_domain_data.js', 'scan_secrets.js', 'audit.js'];
170
+
171
+ for (const toolFile of toolsToCopy) {
172
+ const srcFile = path.join(srcToolsDir, toolFile);
173
+ const destFile = path.join(targetToolsDir, toolFile);
174
+ if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) {
175
+ try {
176
+ fs.copyFileSync(srcFile, destFile);
177
+ } catch {}
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ module.exports = AgentOSAdapter;
package/src/catalog.js CHANGED
@@ -110,6 +110,21 @@ const SAFEGUARD_CATALOG = [
110
110
  applies_when: 'docker', severity: 'error', gate_mode: 'hard', config_keys: ['scope', 'exclude_paths'],
111
111
  phase: 'audit', file: 'docker_guard.py', manifest: true, planned: false },
112
112
 
113
+ { name: 'check_symlinks', labels: ['check_symlinks', 'check_symlinks.js'],
114
+ checks: 'AI IDE symlinks and rule mirror verification', verify: ['verify_symlinks', 'verify_rule_mirrors'],
115
+ applies_when: 'agent-os', severity: 'error', gate_mode: 'hard', config_keys: ['scope'],
116
+ phase: 'audit', file: 'check_symlinks.js', manifest: true, planned: false },
117
+
118
+ { name: 'check_hitl', labels: ['check_hitl', 'check_hitl.js'],
119
+ checks: 'Human-in-the-Loop review checkpoint validator in subagent skills', verify: ['verify_hitl', 'verify_human_review_checkpoints'],
120
+ applies_when: 'agent-os', severity: 'error', gate_mode: 'hard', config_keys: ['scope'],
121
+ phase: 'audit', file: 'check_hitl.js', manifest: true, planned: false },
122
+
123
+ { name: 'check_domain_data', labels: ['check_domain_data', 'check_domain_data.js'],
124
+ checks: 'Domain deliverables schema and clean state integrity', verify: ['verify_domain_data', 'verify_clean_state_metrics'],
125
+ applies_when: 'agent-os', severity: 'error', gate_mode: 'hard', config_keys: ['scope'],
126
+ phase: 'audit', file: 'check_domain_data.js', manifest: true, planned: false },
127
+
113
128
  { name: 'ruff', labels: ['ruff'],
114
129
  checks: 'Python AST linter (external CLI)', verify: ['verify_python_lint'],
115
130
  applies_when: 'backend', severity: 'warning', gate_mode: 'advisory', config_keys: [],
@@ -172,6 +187,7 @@ const VALID_PHASES = ['audit', 'test', 'review', 'handover'];
172
187
  const VALID_APPLIES_WHEN = [
173
188
  'always', 'frontend', 'backend', 'fastapi', 'django', 'django|sqlalchemy',
174
189
  'celery', 'redis', 'postgresql', 'docker', 'frontend,backend', 'backend,fastapi,django',
190
+ 'agent-os',
175
191
  ];
176
192
 
177
193
  /** Normalize an input label for catalog lookup. */
package/src/detector.js CHANGED
@@ -15,9 +15,11 @@ const RailsAdapter = require('./adapters/rails');
15
15
  const GoAdapter = require('./adapters/go');
16
16
  const RustAdapter = require('./adapters/rust');
17
17
  const LaravelAdapter = require('./adapters/laravel');
18
+ const AgentOSAdapter = require('./adapters/agent-os');
18
19
  const { TechnologyDetector } = require('./technologies/detector');
19
20
 
20
21
  const adapters = [
22
+ new AgentOSAdapter(),
21
23
  new NextjsAdapter(),
22
24
  new FastApiAdapter(),
23
25
  new DjangoAdapter(),
package/src/index.js CHANGED
@@ -43,8 +43,19 @@ async function runCli() {
43
43
  const finalConfig = await promptOptions(detected);
44
44
 
45
45
  // 5. Generate Harness & Safeguards
46
+ let s;
47
+ if (Boolean(process.stdin.isTTY) && !process.env.CI) {
48
+ const p = require('@clack/prompts');
49
+ s = p.spinner();
50
+ s.start('Configurando salvaguardas y motor de gobernanza...');
51
+ }
52
+
46
53
  generateHarness(targetDir, finalConfig);
47
54
 
55
+ if (s) {
56
+ s.stop('Salvaguardas y motor de gobernanza configurados.');
57
+ }
58
+
48
59
  // 6. Render Pro Success Card
49
60
  printSuccess(targetDir, finalConfig.projectType === 'frontend', finalConfig);
50
61
  }
package/src/ui.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Brand Identity: The AI Agent Governance & Quality Engineering Engine
4
4
  */
5
5
 
6
- const readline = require('readline');
6
+ const p = require('@clack/prompts');
7
7
 
8
8
  // ANSI Color Palette
9
9
  const colors = {
@@ -33,7 +33,7 @@ function printBanner() {
33
33
  ╚██████╗███████╗╚██████╗ ██║ ╚██╗███████╗██║ ██║██║ ╚████║███████╗███████╗
34
34
  ╚═════╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝
35
35
  ${colors.reset}`);
36
- console.log(` ${colors.bgCyan} CLC KERNEL ${colors.reset} ${colors.bold}The AI Agent Governance & Quality Engineering Engine${colors.reset} ${colors.gray}v1.2.4${colors.reset}\n`);
36
+ console.log(` ${colors.bgCyan} CLC KERNEL ${colors.reset} ${colors.bold}The AI Agent Governance & Quality Engineering Engine${colors.reset} ${colors.gray}v1.2.6${colors.reset}\n`);
37
37
  }
38
38
 
39
39
  function printBox(title, items) {
@@ -52,44 +52,88 @@ function printBox(title, items) {
52
52
  console.log(`${colors.dim}└${'─'.repeat(width)}┘${colors.reset}\n`);
53
53
  }
54
54
 
55
- function promptOptions(detected) {
56
- return new Promise((resolve) => {
57
- const rl = readline.createInterface({
58
- input: process.stdin,
59
- output: process.stdout
60
- });
61
-
62
- console.log(`${colors.bold} \x1b[32m[1]\x1b[0m Confirmar e Instalar CLC Kernel ${colors.dim}(Recomendado)${colors.reset}`);
63
- console.log(` \x1b[33m[2]\x1b[0m Forzar modo FRONTEND`);
64
- console.log(` \x1b[33m[3]\x1b[0m Forzar modo BACKEND`);
65
- console.log(` \x1b[90m[4] Cancelar${colors.reset}\n`);
66
-
67
- rl.question(`${colors.cyan}${colors.bold}➔ Seleccioná una opción [1]: ${colors.reset}`, (answer) => {
68
- const choice = answer.trim() || '1';
69
- rl.close();
70
-
71
- if (choice === '1') {
72
- resolve(detected);
73
- } else if (choice === '2') {
74
- detected.projectType = 'frontend';
75
- resolve(detected);
76
- } else if (choice === '3') {
77
- detected.projectType = 'backend';
78
- resolve(detected);
79
- } else {
80
- console.log(`\n${colors.red}❌ Instalación de CLC Kernel cancelada.${colors.reset}`);
81
- process.exit(0);
55
+ async function promptOptions(detected) {
56
+ const isInteractive = Boolean(process.stdin.isTTY) &&
57
+ !process.env.CI &&
58
+ !process.argv.includes('--yes') &&
59
+ !process.argv.includes('-y');
60
+
61
+ if (!isInteractive) {
62
+ return detected;
63
+ }
64
+
65
+ p.intro(`${colors.cyan}${colors.bold}CLC Kernel Setup${colors.reset}`);
66
+
67
+ const choice = await p.select({
68
+ message: '¿Cómo deseas inicializar CLC Kernel en este proyecto?',
69
+ initialValue: 'confirm',
70
+ options: [
71
+ {
72
+ value: 'confirm',
73
+ label: 'Confirmar e Instalar CLC Kernel',
74
+ hint: `Recomendado (${detected.projectType.toUpperCase()} - ${detected.framework})`
75
+ },
76
+ {
77
+ value: 'frontend',
78
+ label: 'Forzar modo FRONTEND',
79
+ hint: 'Salvaguardas A11y, Zod, Next/Image, UI Reuse'
80
+ },
81
+ {
82
+ value: 'backend',
83
+ label: 'Forzar modo BACKEND',
84
+ hint: 'Salvaguardas TDD, Clean Arch, N+1, Secrets'
85
+ },
86
+ {
87
+ value: 'agent-os',
88
+ label: 'Forzar modo AGENT-OS',
89
+ hint: 'Salvaguardas HITL, Symlinks, Schemas, Memoria SSOT'
90
+ },
91
+ {
92
+ value: 'cancel',
93
+ label: 'Cancelar',
94
+ hint: 'Salir sin realizar cambios'
82
95
  }
83
- });
96
+ ]
84
97
  });
98
+
99
+ if (p.isCancel(choice) || choice === 'cancel') {
100
+ p.cancel('Instalación de CLC Kernel cancelada.');
101
+ process.exit(0);
102
+ }
103
+
104
+ if (choice === 'frontend') {
105
+ detected.projectType = 'frontend';
106
+ } else if (choice === 'backend') {
107
+ detected.projectType = 'backend';
108
+ } else if (choice === 'agent-os') {
109
+ detected.projectType = 'agent-os';
110
+ }
111
+
112
+ return detected;
85
113
  }
86
114
 
87
115
  function printSuccess(targetDir, isFront, config) {
88
- console.log(`\n${colors.bgEmerald} SUCCESS ${colors.reset} ${colors.bold}${colors.emerald}¡CLC Kernel instalado exitosamente!${colors.reset}\n`);
89
- console.log(` 🔨 ${colors.bold}Proyecto Inicializado:${colors.reset} ${colors.cyan}${targetDir}${colors.reset}`);
90
- console.log(` 🛡️ ${colors.bold}Salvaguardas:${colors.reset} ${isFront ? `${colors.emerald}10 Guardias Frontend (A11y, Zod, Next/Image, UI Reuse, Storybook)` : `${colors.violet}7 Guardias Backend (TDD, Architecture, Scope, Secret Scan)`}${colors.reset}`);
91
- console.log(` 📝 ${colors.bold}Leyes & SDD:${colors.reset} AGENTS.md, sdds/, docs/Journal/ precargados`);
92
- console.log(` 🔒 ${colors.bold}Git Hooks:${colors.reset} Configurados en ${colors.amber}${config.gitHooks === 'husky' ? '.husky/pre-commit' : '.githooks/pre-commit'}${colors.reset}\n`);
116
+ const isAgentOS = config.projectType === 'agent-os';
117
+ const guardsDesc = isAgentOS
118
+ ? `${colors.cyan}6 Guardias Agent-OS (HITL, Symlinks, Schemas, Memoria, Secrets)${colors.reset}`
119
+ : isFront
120
+ ? `${colors.emerald}10 Guardias Frontend (A11y, Zod, Image, UI Reuse, Storybook)${colors.reset}`
121
+ : `${colors.violet}7 Guardias Backend (TDD, Architecture, Scope, Secret Scan)${colors.reset}`;
122
+
123
+ const summary = [
124
+ `🔨 ${colors.bold}Proyecto Inicializado:${colors.reset} ${colors.cyan}${targetDir}${colors.reset}`,
125
+ `🛡️ ${colors.bold}Salvaguardas:${colors.reset} ${guardsDesc}`,
126
+ `📝 ${colors.bold}Leyes & SDD:${colors.reset} AGENTS.md, sdds/, docs/Journal/ precargados`,
127
+ `🔒 ${colors.bold}Git Hooks:${colors.reset} Configurados en ${colors.amber}${config.gitHooks === 'husky' ? '.husky/pre-commit' : '.githooks/pre-commit'}${colors.reset}`
128
+ ].join('\n');
129
+
130
+ if (p.note && p.outro) {
131
+ p.note(summary, '🚀 Resumen de Instalación');
132
+ p.outro(`${colors.emerald}${colors.bold}¡CLC Kernel instalado exitosamente!${colors.reset}`);
133
+ } else {
134
+ console.log(`\n${colors.bgEmerald} SUCCESS ${colors.reset} ${colors.bold}${colors.emerald}¡CLC Kernel instalado exitosamente!${colors.reset}\n`);
135
+ console.log(summary + '\n');
136
+ }
93
137
  }
94
138
 
95
139
  module.exports = {
package/tools/audit.js CHANGED
File without changes
package/tools/audit.py CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Domain Data Integrity & Clean State Guard
4
+ * Validates domain deliverables (content.json, calendar.json, voc.json)
5
+ * to ensure valid JSON structure and clean creation state (no fake hallucinated metrics on pending posts).
6
+ * Self-contained: uses only Node.js built-ins.
7
+ *
8
+ * @param {string} targetDir - Directory to scan (defaults to cwd)
9
+ * @returns {{ exitCode: number, skipped: boolean, name: string, data: object }}
10
+ */
11
+ const check = async function checkDomainData(targetDir) {
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const dir = targetDir || process.cwd();
16
+ const searchDirs = [
17
+ dir,
18
+ path.join(dir, 'output'),
19
+ path.join(dir, 'content-calendar'),
20
+ path.join(dir, 'data')
21
+ ];
22
+
23
+ const jsonFiles = [];
24
+ for (const sDir of searchDirs) {
25
+ if (fs.existsSync(sDir)) {
26
+ try {
27
+ const files = fs.readdirSync(sDir);
28
+ for (const file of files) {
29
+ if (file.endsWith('.json') && !file.includes('package') && !file.includes('tsconfig')) {
30
+ jsonFiles.push(path.join(sDir, file));
31
+ }
32
+ }
33
+ } catch {}
34
+ }
35
+ }
36
+
37
+ if (jsonFiles.length === 0) {
38
+ return {
39
+ exitCode: 0,
40
+ skipped: true,
41
+ name: 'check_domain_data',
42
+ data: { violations: [], message: 'No domain data JSON files to validate' }
43
+ };
44
+ }
45
+
46
+ const violations = [];
47
+ for (const file of jsonFiles) {
48
+ try {
49
+ const raw = fs.readFileSync(file, 'utf-8');
50
+ const data = JSON.parse(raw);
51
+
52
+ // Clean Creation State Check: If array of posts/calendar entries
53
+ const entries = Array.isArray(data) ? data : (data.posts || data.items || data.calendar || []);
54
+ if (Array.isArray(entries)) {
55
+ for (let i = 0; i < entries.length; i++) {
56
+ const item = entries[i];
57
+ if (item && (item.status === 'pending' || item.status === 'draft')) {
58
+ // Check if metrics are falsely pre-filled with non-null numbers
59
+ if (item.metrics && typeof item.metrics === 'object') {
60
+ const fakeMetrics = Object.entries(item.metrics).filter(([_, val]) => typeof val === 'number' && val > 0);
61
+ if (fakeMetrics.length > 0) {
62
+ violations.push({
63
+ file: path.relative(dir, file),
64
+ message: `Entry #${i + 1} (${item.title || item.id || 'draft'}) has pre-filled fake metrics on pending/draft post`
65
+ });
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ } catch (err) {
72
+ violations.push({
73
+ file: path.relative(dir, file),
74
+ message: `JSON syntax or structural error: ${err.message}`
75
+ });
76
+ }
77
+ }
78
+
79
+ const exitCode = violations.length > 0 ? 1 : 0;
80
+ return {
81
+ exitCode,
82
+ skipped: false,
83
+ name: 'check_domain_data',
84
+ data: {
85
+ violations,
86
+ message: violations.length > 0
87
+ ? `${violations.length} domain data integrity violation(s) found`
88
+ : 'All domain deliverables & state data verified'
89
+ }
90
+ };
91
+ };
92
+
93
+ module.exports = check;
94
+
95
+ if (require.main === module) {
96
+ check(process.argv[2]).then(result => {
97
+ console.log(JSON.stringify(result, null, 2));
98
+ process.exit(result.exitCode);
99
+ });
100
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Human-in-the-Loop (HITL) Checkpoint Guard
4
+ * Ensures subagent playbooks and workflows in skills/ or .agents/skills/
5
+ * define mandatory user review or approval checkpoints before final output.
6
+ * Self-contained: uses only Node.js built-ins.
7
+ *
8
+ * @param {string} targetDir - Directory to scan (defaults to cwd)
9
+ * @returns {{ exitCode: number, skipped: boolean, name: string, data: object }}
10
+ */
11
+ const check = async function checkHitl(targetDir) {
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const dir = targetDir || process.cwd();
16
+ const searchDirs = [
17
+ path.join(dir, 'skills'),
18
+ path.join(dir, '.agents', 'skills')
19
+ ];
20
+
21
+ const skillFiles = [];
22
+ for (const sDir of searchDirs) {
23
+ if (fs.existsSync(sDir)) {
24
+ try {
25
+ const files = fs.readdirSync(sDir);
26
+ for (const file of files) {
27
+ if (file.endsWith('.md')) {
28
+ skillFiles.push(path.join(sDir, file));
29
+ }
30
+ }
31
+ } catch {}
32
+ }
33
+ }
34
+
35
+ if (skillFiles.length === 0) {
36
+ return {
37
+ exitCode: 0,
38
+ skipped: true,
39
+ name: 'check_hitl',
40
+ data: { violations: [], message: 'No skills/ or subagent playbook files found' }
41
+ };
42
+ }
43
+
44
+ const hitlKeywords = [
45
+ /human[- ]in[- ]the[- ]loop/i,
46
+ /hitl/i,
47
+ /checkpoint/i,
48
+ /approval/i,
49
+ /confirm/i,
50
+ /review/i,
51
+ /pausa/i,
52
+ /aprobaci[oó]n/i,
53
+ /esperar/i,
54
+ /validaci[oó]n/i
55
+ ];
56
+
57
+ const violations = [];
58
+ for (const file of skillFiles) {
59
+ try {
60
+ const content = fs.readFileSync(file, 'utf-8');
61
+ const hasHitl = hitlKeywords.some(kw => kw.test(content));
62
+
63
+ if (!hasHitl) {
64
+ violations.push({
65
+ file: path.relative(dir, file),
66
+ message: 'Skill/Playbook missing mandatory Human-in-the-Loop (HITL) review checkpoint'
67
+ });
68
+ }
69
+ } catch {}
70
+ }
71
+
72
+ const exitCode = violations.length > 0 ? 1 : 0;
73
+ return {
74
+ exitCode,
75
+ skipped: false,
76
+ name: 'check_hitl',
77
+ data: {
78
+ violations,
79
+ message: violations.length > 0
80
+ ? `${violations.length} skill(s) missing mandatory Human-in-the-Loop checkpoints`
81
+ : 'All subagent skills include mandatory Human-in-the-Loop (HITL) checkpoints'
82
+ }
83
+ };
84
+ };
85
+
86
+ module.exports = check;
87
+
88
+ if (require.main === module) {
89
+ check(process.argv[2]).then(result => {
90
+ console.log(JSON.stringify(result, null, 2));
91
+ process.exit(result.exitCode);
92
+ });
93
+ }
File without changes
@@ -90,7 +90,7 @@ const check = async function checkSeo(targetDir) {
90
90
  }
91
91
 
92
92
  // Check for Hero / Banner images without priority (Core Web Vitals / LCP)
93
- if (/(?:hero|banner|cover|hero-image|heroImage|HeroBanner)/i.test(line) && /<(?:img|Image)\b/i.test(line)) {
93
+ if (/\b(?:hero|banner|hero-image|heroImage|heroBanner)\b/i.test(line) && /<(?:img|Image)\b/i.test(line)) {
94
94
  if (!/priority|fetchpriority|loading\s*=\s*['"]eager['"]/i.test(line)) {
95
95
  fileViolations.push({
96
96
  lineNo: i + 1,