clckernel 1.2.19 → 2.0.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 (78) hide show
  1. package/.agents/skills/clckernel_cli/SKILL.md +23 -6
  2. package/.agents/skills/clckernel_cli/reference/audit.md +13 -3
  3. package/.agents/skills/clckernel_cli/reference/commit.md +1 -1
  4. package/.agents/skills/clckernel_cli/reference/doctor.md +5 -3
  5. package/.agents/skills/clckernel_cli/reference/feature.md +3 -3
  6. package/.agents/skills/clckernel_cli/reference/guard.md +48 -12
  7. package/.agents/skills/clckernel_cli/reference/phase.md +6 -3
  8. package/.agents/skills/clckernel_cli/reference/start.md +14 -4
  9. package/.agents/skills/clckernel_cli/reference/test.md +1 -1
  10. package/.agents/skills/clckernel_cli/reference/update.md +19 -19
  11. package/CHANGELOG.md +250 -0
  12. package/LICENSE +21 -0
  13. package/README.es.md +280 -51
  14. package/README.md +274 -50
  15. package/bin/deprecated.js +13 -0
  16. package/package.json +14 -4
  17. package/src/adapters/agent-os.js +161 -84
  18. package/src/adapters/astro.js +8 -6
  19. package/src/adapters/base.js +23 -38
  20. package/src/adapters/django.js +14 -13
  21. package/src/adapters/fastapi.js +16 -15
  22. package/src/adapters/go.js +11 -9
  23. package/src/adapters/laravel.js +12 -10
  24. package/src/adapters/nextjs.js +7 -5
  25. package/src/adapters/rails.js +13 -11
  26. package/src/adapters/rust.js +11 -9
  27. package/src/catalog.js +58 -49
  28. package/src/config.js +38 -240
  29. package/src/detector.js +243 -44
  30. package/src/doctor.js +18 -9
  31. package/src/generator.js +734 -302
  32. package/src/gitignore.js +60 -14
  33. package/src/index.js +257 -18
  34. package/src/io.js +409 -0
  35. package/src/known-hashes.json +309 -0
  36. package/src/onboarding.js +15 -0
  37. package/src/technologies/detector.js +1 -1
  38. package/src/technologies/guards/_shared.js +1 -1
  39. package/src/ui.js +233 -40
  40. package/src/updater.js +271 -38
  41. package/tools/_changes.js +279 -0
  42. package/tools/_changes.py +233 -0
  43. package/tools/_config.js +294 -0
  44. package/tools/_config.py +271 -0
  45. package/tools/_yaml.py +489 -0
  46. package/tools/audit.js +511 -111
  47. package/tools/audit.py +506 -128
  48. package/tools/celery_guard.py +13 -35
  49. package/tools/check_a11y.js +8 -28
  50. package/tools/check_api_contracts.js +13 -39
  51. package/tools/check_architecture.js +35 -44
  52. package/tools/check_architecture.py +172 -105
  53. package/tools/check_custom.js +108 -61
  54. package/tools/check_custom.py +108 -271
  55. package/tools/check_data_files.js +88 -0
  56. package/tools/check_db_efficiency.py +80 -65
  57. package/tools/check_hitl.js +112 -69
  58. package/tools/check_links.js +138 -0
  59. package/tools/check_migrations.py +143 -94
  60. package/tools/check_performance.js +35 -44
  61. package/tools/check_placeholders.js +104 -0
  62. package/tools/check_responsive.js +8 -28
  63. package/tools/check_scope.py +40 -31
  64. package/tools/check_seo.js +8 -28
  65. package/tools/check_skills.js +158 -0
  66. package/tools/check_storybook.js +8 -21
  67. package/tools/check_symlinks.js +40 -22
  68. package/tools/check_ui_reuse.js +17 -29
  69. package/tools/docker_guard.py +19 -41
  70. package/tools/postgres_guard.py +15 -36
  71. package/tools/redis_guard.py +13 -35
  72. package/tools/scan_secrets.js +134 -67
  73. package/tools/scan_secrets.py +163 -72
  74. package/tools/verify_tdd.js +867 -0
  75. package/tools/verify_tdd.py +407 -165
  76. package/tools/yaml.js +404 -132
  77. package/tools/check_domain_data.js +0 -100
  78. package/tools/check_test_calendar_sync.js +0 -111
@@ -1,77 +1,126 @@
1
1
  /**
2
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.
3
+ * Supports agent operating systems: repositories of skills, playbooks, rules,
4
+ * prompts, templates and data that AI agents run to carry out tasks.
5
+ * The kernel holds no business-domain rules: the guards below hold for any
6
+ * Agent-OS, and domain checks live in the project (tools/guards/), which the
7
+ * pre-commit hook and the audit run like the kernel's own guards.
5
8
  */
6
9
 
7
10
  const fs = require('fs');
8
11
  const path = require('path');
9
12
  const BaseAdapter = require('./base');
13
+ const { currentIO, registeredPaths } = require('../io');
14
+ const { FIRST_SESSION_SECTION } = require('../onboarding');
15
+
16
+ /** Skills the harness installs itself: never a detection signal. */
17
+ const HARNESS_SKILLS = new Set(['clckernel_cli', 'clc_forge_cli']);
18
+ const SKILL_ROOTS = ['skills', '.agents/skills', '.claude/skills'];
19
+ const MARKDOWN_SEARCH_DEPTH = 3;
20
+
21
+ /** Folders that make up an Agent-OS, described in AGENTS.md when present. */
22
+ const LAYER_FOLDERS = [
23
+ { folder: 'skills', skills: true, text: 'one folder per skill with a `SKILL.md` whose frontmatter holds a unique lowercase `name` and a `description`; the agent loads a skill when its description matches the task.' },
24
+ { folder: '.agents/skills', skills: true, text: 'skills in the shared agent layout (same `SKILL.md` contract as above).' },
25
+ { folder: '.claude/skills', skills: true, text: 'skills in the Claude Code layout (same `SKILL.md` contract as above).' },
26
+ { folder: 'playbooks', text: 'step-by-step procedures the agent runs for an operator; each declares its human checkpoint.' },
27
+ { folder: 'rules', text: 'standing constraints (style, tone, policy) every run must respect.' },
28
+ { folder: 'prompts', text: 'reusable prompt texts the skills and playbooks reference.' },
29
+ { folder: 'templates', text: 'output skeletons; double-brace slots are allowed only here.' },
30
+ ];
31
+
32
+ /** Paths listed in the harness registry (working copy, either format); empty when absent or malformed. */
33
+ function managedPaths(targetDir) {
34
+ return new Set(registeredPaths(targetDir));
35
+ }
36
+
37
+ /** True when root/<name>/SKILL.md exists for a skill the harness did not install. */
38
+ function hasUserSkill(targetDir, root, managed = managedPaths(targetDir)) {
39
+ let entries;
40
+ try {
41
+ entries = fs.readdirSync(path.join(targetDir, root), { withFileTypes: true });
42
+ } catch {
43
+ return false;
44
+ }
45
+ return entries.some(entry => {
46
+ if (!entry.isDirectory() || HARNESS_SKILLS.has(entry.name)) return false;
47
+ const rel = `${root}/${entry.name}/SKILL.md`;
48
+ return !managed.has(rel) && fs.existsSync(path.join(targetDir, rel));
49
+ });
50
+ }
51
+
52
+ /** True when dir holds a Markdown file within `depth` levels. */
53
+ function hasMarkdown(dir, depth = MARKDOWN_SEARCH_DEPTH) {
54
+ let entries;
55
+ try {
56
+ entries = fs.readdirSync(dir, { withFileTypes: true });
57
+ } catch {
58
+ return false;
59
+ }
60
+ return entries.some(entry => (entry.isFile() && entry.name.endsWith('.md'))
61
+ || (entry.isDirectory() && depth > 1 && hasMarkdown(path.join(dir, entry.name), depth - 1)));
62
+ }
63
+
64
+ /** Layer folders present in targetDir, in LAYER_FOLDERS order. */
65
+ function presentLayers(targetDir) {
66
+ const managed = managedPaths(targetDir);
67
+ return LAYER_FOLDERS.filter(layer => (layer.skills
68
+ ? hasUserSkill(targetDir, layer.folder, managed)
69
+ : fs.existsSync(path.join(targetDir, layer.folder))));
70
+ }
10
71
 
11
72
  class AgentOSAdapter extends BaseAdapter {
12
73
  constructor() {
13
74
  super('Agent-OS (Domain & AI Workflows)', 'agent-os', 'Playbook / Contract Test');
14
75
  }
15
76
 
77
+ /**
78
+ * An agent operating system: a SKILL.md in skills/<name>/, .agents/skills/<name>/
79
+ * or .claude/skills/<name>/ (skills the harness installs excluded), a
80
+ * playbooks/ or rules/ folder with Markdown, or a generic package.json
81
+ * signal (`agent-os` in the name, description or keywords, or the
82
+ * `ai-agents` keyword). A code stack that also matches wins (see detector).
83
+ */
16
84
  detect(targetDir) {
17
- // 1. package.json check
18
85
  const pkgPath = path.join(targetDir, 'package.json');
19
86
  if (fs.existsSync(pkgPath)) {
20
87
  try {
21
88
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
22
89
  const name = (pkg.name || '').toLowerCase();
23
90
  const desc = (pkg.description || '').toLowerCase();
24
- const keywords = Array.isArray(pkg.keywords) ? pkg.keywords.map(k => k.toLowerCase()) : [];
91
+ const keywords = Array.isArray(pkg.keywords) ? pkg.keywords.map(k => String(k).toLowerCase()) : [];
25
92
  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')
93
+ name.includes('agent-os') || desc.includes('agent-os') ||
94
+ keywords.includes('agent-os') || keywords.includes('ai-agents')
29
95
  ) {
30
96
  return true;
31
97
  }
32
98
  } catch {}
33
99
  }
34
100
 
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;
101
+ const managed = managedPaths(targetDir);
102
+ if (SKILL_ROOTS.some(root => hasUserSkill(targetDir, root, managed))) return true;
103
+ return ['playbooks', 'rules'].some(folder => hasMarkdown(path.join(targetDir, folder)));
62
104
  }
63
105
 
64
106
  getSafeguards() {
65
107
  return [
66
108
  '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/)'
109
+ 'Human-in-the-Loop (HITL) checkpoints: every changed skill and playbook declares a `HITL:` line or a `## Human checkpoint` section (check_hitl)',
110
+ 'Skill frontmatter: unique lowercase `name` and a `description` of at most 1024 characters in every changed SKILL.md (check_skills)',
111
+ 'Relative Markdown links resolve to files in the commit (check_links)',
112
+ 'No forgotten draft placeholders in changed Markdown, YAML and JSON (check_placeholders)',
113
+ 'Changed JSON data files parse (check_data_files)',
114
+ 'Rule mirrors (CLAUDE.md, GEMINI.md, .cursorrules, .windsurfrules) point to AGENTS.md (check_symlinks)',
115
+ 'Project rules from .clckernel.yaml (check_custom)',
116
+ 'Secret Leak Guard (scan_secrets)',
117
+ 'Single Source of Truth (SSOT) memory protocol (agent-enforced; not checked by a guard)',
118
+ 'Local-only Spec Driven Development (sdds/)',
119
+ 'Red→Green TDD Verifier (runs changed tests with vitest / jest / node:test)'
72
120
  ];
73
121
  }
74
122
 
123
+ /** Guards for any Agent-OS; domain checks belong in the project's tools/guards/. */
75
124
  getTools() {
76
125
  return [
77
126
  {
@@ -79,28 +128,42 @@ class AgentOSAdapter extends BaseAdapter {
79
128
  language: 'js',
80
129
  path: 'check_symlinks.js',
81
130
  command: 'node tools/check_symlinks.js',
82
- description: 'AI IDE symlinks and rule mirror verification'
131
+ description: 'AI IDE rule mirrors point to AGENTS.md (symlink or identical copy)'
83
132
  },
84
133
  {
85
134
  name: 'check_hitl',
86
135
  language: 'js',
87
136
  path: 'check_hitl.js',
88
137
  command: 'node tools/check_hitl.js',
89
- description: 'Human-in-the-Loop review checkpoint validator'
138
+ description: 'Explicit Human-in-the-Loop checkpoint marker in changed skills and playbooks'
139
+ },
140
+ {
141
+ name: 'check_skills',
142
+ language: 'js',
143
+ path: 'check_skills.js',
144
+ command: 'node tools/check_skills.js',
145
+ description: 'SKILL.md frontmatter: name and description present, well-formed and unique'
146
+ },
147
+ {
148
+ name: 'check_links',
149
+ language: 'js',
150
+ path: 'check_links.js',
151
+ command: 'node tools/check_links.js',
152
+ description: 'Relative links in changed Markdown point to files in the commit'
90
153
  },
91
154
  {
92
- name: 'check_domain_data',
155
+ name: 'check_placeholders',
93
156
  language: 'js',
94
- path: 'check_domain_data.js',
95
- command: 'node tools/check_domain_data.js',
96
- description: 'Domain deliverables schema and clean state integrity'
157
+ path: 'check_placeholders.js',
158
+ command: 'node tools/check_placeholders.js',
159
+ description: 'Forgotten draft placeholders in changed Markdown, YAML and JSON (templates/ may keep slots)'
97
160
  },
98
161
  {
99
- name: 'check_test_calendar_sync',
162
+ name: 'check_data_files',
100
163
  language: 'js',
101
- path: 'check_test_calendar_sync.js',
102
- command: 'node tools/check_test_calendar_sync.js',
103
- description: 'Test calendar harness synchronization and parity check'
164
+ path: 'check_data_files.js',
165
+ command: 'node tools/check_data_files.js',
166
+ description: 'Every changed .json file parses (snapshot content, any folder)'
104
167
  },
105
168
  {
106
169
  name: 'scan_secrets',
@@ -108,6 +171,20 @@ class AgentOSAdapter extends BaseAdapter {
108
171
  path: 'scan_secrets.js',
109
172
  command: 'node tools/scan_secrets.js',
110
173
  description: 'Security & API key leak scanner'
174
+ },
175
+ {
176
+ name: 'verify_tdd',
177
+ language: 'js',
178
+ path: 'verify_tdd.js',
179
+ command: 'node tools/verify_tdd.js',
180
+ description: 'Runs changed tests (vitest / jest / node:test) against BASE (must fail) and the staged code (must pass)'
181
+ },
182
+ {
183
+ name: 'check_custom',
184
+ language: 'js',
185
+ path: 'check_custom.js',
186
+ command: 'node tools/check_custom.js',
187
+ description: 'Project rules from .clckernel.yaml (e.g. sections every playbook must have)'
111
188
  }
112
189
  ];
113
190
  }
@@ -119,7 +196,11 @@ class AgentOSAdapter extends BaseAdapter {
119
196
  // would make the guard below skip the dual-layer render entirely.
120
197
  super.provision(targetDir, { ...(config || {}), skipAgentsMd: true });
121
198
 
122
- // 2. Write Dual-Layer AGENTS.md
199
+ // 2. Write Dual-Layer AGENTS.md, describing only the folders that exist.
200
+ const layers = presentLayers(targetDir);
201
+ const layerMap = layers.length > 0
202
+ ? layers.map(layer => `- \`${layer.folder}/\` — ${layer.text}`).join('\n')
203
+ : '- No agent folders yet. Add skills, playbooks, rules, prompts or templates folders and describe them here.';
123
204
  const dualLayerAgents = `# Standard Operating Procedure (SOP) — AGENTS
124
205
  Governed by **CLC Kernel: The Universal AI Agent Governance Engine**.
125
206
  Target System: **${this.name}**
@@ -129,7 +210,7 @@ Target System: **${this.name}**
129
210
  ## 🛠️ Layer 1: Development & Maintenance Governance (Engineering)
130
211
  All AI Agents (Antigravity, OpenCode, Claude Code, Cursor, Windsurf, Copilot) developing or maintaining this codebase MUST adhere to strict engineering standards:
131
212
 
132
- > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (UNBYPASSABLE)**
213
+ > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (NO EXCEPTIONS)**
133
214
  > Even when the user issues a direct or urgent fix request ("fix this bug", "fix this error", "quick fix"):
134
215
  > YOU ARE STRICTLY FORBIDDEN from modifying source code or workflows directly without completing the full quality harness:
135
216
  > 1. **Research & Root Cause Analysis:** Investigate tracebacks and affected files before editing.
@@ -140,7 +221,7 @@ All AI Agents (Antigravity, OpenCode, Claude Code, Cursor, Windsurf, Copilot) de
140
221
 
141
222
  ### Engineering Rules
142
223
  1. **Conventional Commits Only**: Commits must use Conventional Commits (e.g. \`feat(...): ...\`, \`fix(...): ...\`). NEVER add \`Co-Authored-By\` or AI attribution.
143
- 2. **Mirror Links Interoperability**: \`CLAUDE.md\`, \`GEMINI.md\`, \`.cursorrules\`, and \`.windsurfrules\` must remain valid symlinks to \`AGENTS.md\`.
224
+ 2. **Mirror Links Interoperability**: \`CLAUDE.md\`, \`GEMINI.md\`, \`.cursorrules\`, and \`.windsurfrules\` must remain symlinks to (or identical copies of) \`AGENTS.md\` (checked by \`tools/check_symlinks.js\`).
144
225
  3. **No Superficial Symptom Patches**: Always address root causes.
145
226
 
146
227
  ---
@@ -149,10 +230,14 @@ All AI Agents (Antigravity, OpenCode, Claude Code, Cursor, Windsurf, Copilot) de
149
230
  When operating as domain agents for end-users and non-technical operators:
150
231
 
151
232
  1. **Non-Technical Operator Friendly**: Interact naturally with the operator. Guide step-by-step through conversational prompts without technical jargon.
152
- 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.
153
- 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/\`).
154
- 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.
155
- 5. **Deterministic Output Standard**: Outputs must strictly follow valid Markdown, JSON schema, or domain templates.
233
+ 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. Every skill and playbook declares its checkpoint with a \`HITL:\` line or a \`## Human checkpoint\` section (checked by \`tools/check_hitl.js\`).
234
+ 3. **Skill Contract**: Every \`SKILL.md\` opens with a frontmatter holding a unique lowercase \`name\` and a \`description\` that says when to use the skill (checked by \`tools/check_skills.js\`).
235
+ 4. **SSOT Memory Primacy**: Read from and persist to the files this repository designates as its single source of truth; never keep operator data only in a conversation.
236
+ 5. **Clean Creation State (No Invented Data)**: Never invent data. A field without a real value stays empty or \`null\` until real data exists.
237
+ 6. **Finished Documents**: Deliverables follow the repository's templates and formats, keep no draft placeholders and link only to files that exist (checked by \`tools/check_placeholders.js\` and \`tools/check_links.js\`).
238
+
239
+ ### Repository Map
240
+ ${layerMap}
156
241
 
157
242
  ---
158
243
 
@@ -161,27 +246,31 @@ When given a command matching \`clckernel <action>\` or \`/clckernel <action>\`,
161
246
 
162
247
  | Command | Lifecycle Phase | Mandatory Execution Protocol |
163
248
  |---|---|---|
164
- | \`clckernel feature <name>\` | New Feature | 1. Research affected layers.<br>2. Author local spec in \`sdds/{name}/spec.md\`.<br>3. Present HIT review to user and wait for approval.<br>4. Write failing test (Red Phase).<br>5. Implement clean code (Green Phase).<br>6. Run \`node tools/audit.js\` (or \`audit.py\`). |
249
+ | \`clckernel feature <name>\` | New Feature | 1. Research affected layers.<br>2. Author local spec in \`sdds/{name}/spec.md\`.<br>3. Present HIT review to user and wait for approval.<br>4. Write failing test (Red Phase).<br>5. Implement clean code (Green Phase).<br>6. Run \`node tools/audit.js --change {name}\` (or \`audit.py --change {name}\`). |
165
250
  | \`clckernel fix <issue>\` | Bug Fix | 1. Investigate root cause (no code edits).<br>2. Reproduce with failing test (Red Phase).<br>3. Minimal surgical fix respecting Clean Arch (Green Phase).<br>4. Run audit gate without skipping. |
166
251
  | \`clckernel test\` | Verification | Run test suite and verify clean pass (exit code 0). |
167
- | \`clckernel audit\` | Verification | Run \`node tools/audit.js\` or \`python tools/audit.py\` and output the Educational Summary. |
168
- | \`clckernel doctor\` | Diagnostics | Check git pre-commit hooks, IDE symlinks, and AST engine integrity. |
252
+ | \`clckernel audit\` | Verification | Run \`npx clckernel audit [dir] [--change <name>]\` (every installed orchestrator: \`tools/audit.js\` and/or \`tools/audit.py\`) and output the Educational Summary; only \`--change\` writes the report to \`sdds/<name>/\`. |
253
+ | \`clckernel doctor\` | Diagnostics | Run \`npx clckernel doctor [dir]\`: checks AGENTS.md, sdds/, tools/, the pre-commit hook and that the guard scripts load. |
169
254
  | \`clckernel commit <message>\` | Version Control | Run full audit, verify tests pass, ensure no changes outside SDD scope, and commit with conventional commits (never add AI attribution). |
170
- | \`clckernel guard create <name>\` | Safeguards | Scaffold a new AST guard in the stack's native language in \`tools/guards/\`. |
171
- | \`clckernel update\` | Maintenance | Non-destructive update: refresh core tools, playbooks & symlinks (preserves SDDs & custom guards). |
172
- | \`clckernel start\` | Setup | Scan codebase, propose governance plan, and materialize \`.clckernel.yaml\`. |
255
+ | \`clckernel guard create <name>\` | Safeguards | Scaffold a new guard script in the stack's native language in \`tools/guards/\`. |
256
+ | \`clckernel update\` | Maintenance | Non-destructive update: run \`npx clckernel update [dir]\` (\`--dry-run\` previews, \`--add-guards <a,b\\|all>\` adds guards); refreshes kernel tools, hook, playbooks & symlinks (preserves SDDs, project guards, edited files & \`.clckernel.yaml\`). |
257
+ | \`clckernel start\` | Setup | Scan codebase and propose a governance plan; if the harness is missing, install it with \`npx clckernel . --yes\`. \`.clckernel.yaml\` is optional: write it only for settings that differ from the defaults. |
173
258
 
174
259
  ---
175
260
 
176
261
  ## 🛡️ Core AI Safeguards
177
262
  ${this.getSafeguards().map(s => `- ${s}`).join('\n')}
178
- `;
179
263
 
180
- // Guard: never overwrite a project-customized AGENTS.md (e.g. a Laila MOS
181
- // law file carrying domain invariants the template cannot know about).
264
+ ---
265
+
266
+ ${FIRST_SESSION_SECTION}`;
267
+
268
+ // Guard: never overwrite a project-customized AGENTS.md (e.g. a law file
269
+ // carrying domain invariants the template cannot know about).
270
+ const io = currentIO(targetDir);
182
271
  const dualAgentsPath = path.join(targetDir, 'AGENTS.md');
183
- if (!fs.existsSync(dualAgentsPath)) {
184
- fs.writeFileSync(dualAgentsPath, dualLayerAgents, 'utf-8');
272
+ if (!io.exists(dualAgentsPath)) {
273
+ io.writeFile(dualAgentsPath, dualLayerAgents);
185
274
  }
186
275
 
187
276
  // 3. Create AI IDE mirror symlinks
@@ -189,26 +278,14 @@ ${this.getSafeguards().map(s => `- ${s}`).join('\n')}
189
278
  for (const mirror of mirrors) {
190
279
  const mirrorPath = path.join(targetDir, mirror);
191
280
  try {
192
- if (!fs.existsSync(mirrorPath)) {
193
- fs.symlinkSync('AGENTS.md', mirrorPath);
281
+ // An update never creates a mirror the project deleted (sdds/update-no-additions).
282
+ if (!io.exists(mirrorPath)) {
283
+ io.mirror('AGENTS.md', mirrorPath);
194
284
  }
195
285
  } catch {}
196
286
  }
197
-
198
- // 4. Copy domain guard tools into tools/
199
- const srcToolsDir = path.join(__dirname, '..', '..', 'tools');
200
- const targetToolsDir = path.join(targetDir, 'tools');
201
- const toolsToCopy = ['check_symlinks.js', 'check_hitl.js', 'check_domain_data.js', 'check_test_calendar_sync.js', 'scan_secrets.js', 'audit.js'];
202
-
203
- for (const toolFile of toolsToCopy) {
204
- const srcFile = path.join(srcToolsDir, toolFile);
205
- const destFile = path.join(targetToolsDir, toolFile);
206
- if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) {
207
- try {
208
- fs.copyFileSync(srcFile, destFile);
209
- } catch {}
210
- }
211
- }
287
+ // Guards are copied by the generator from getTools(targetDir), so the
288
+ // registry and the hook list exactly what is installed.
212
289
  }
213
290
  }
214
291
 
@@ -34,9 +34,9 @@ class AstroAdapter extends BaseAdapter {
34
34
  'SEO, GEO & Web Performance Guard (metadata, OpenGraph, Core Web Vitals)',
35
35
  'Secret & API Key Leak Scanner',
36
36
  'Content Collection Schema Validation Guard',
37
- 'Vitest / Playwright Red-to-Green TDD Validator',
37
+ 'Red→Green TDD Verifier (runs changed tests with vitest / jest / node:test)',
38
38
  'Educational Audit Gate (05-audit-report.md)',
39
- 'Memory Guard (Engram / Graphify ADR Sync)'
39
+ 'Memory protocol (agent-enforced; Engram / Graphify optional, not checked by a guard)'
40
40
  ];
41
41
  }
42
42
 
@@ -51,11 +51,13 @@ class AstroAdapter extends BaseAdapter {
51
51
  { name: 'check_a11y', language: 'js', path: 'check_a11y.js', command: 'node tools/check_a11y.js', description: 'Validates ARIA and accessibility rules' },
52
52
  { name: 'check_responsive', language: 'js', path: 'check_responsive.js', command: 'node tools/check_responsive.js', description: 'Audits mobile-first responsiveness and fixed-width overflows' },
53
53
  { name: 'check_seo', language: 'js', path: 'check_seo.js', command: 'node tools/check_seo.js', description: 'Validates SEO metadata, OpenGraph, GEO tags, and LCP priority' },
54
- { name: 'check_custom', language: 'js', path: 'check_custom.js', command: 'node tools/check_custom.js', description: 'Runs user-defined custom rules from .clc-forge.yml' },
55
- { name: 'astro_check', language: 'external', path: '', command: 'npx astro check', description: 'Astro type checking', extCLI: 'astro' },
56
- { name: 'vitest', language: 'external', path: '', command: 'npx vitest run', description: 'Test runner', extCLI: 'vitest' },
54
+ { name: 'verify_tdd', language: 'js', path: 'verify_tdd.js', command: 'node tools/verify_tdd.js', description: 'Runs changed tests (vitest / jest / node:test) against BASE (must fail) and the staged code (must pass)' },
55
+ { name: 'check_custom', language: 'js', path: 'check_custom.js', command: 'node tools/check_custom.js', description: 'Runs user-defined custom rules from .clckernel.yaml' },
56
+ { name: 'astro_check', language: 'external', path: '', command: 'npx astro check', description: 'Astro type checking', extCLI: 'astro', availableIf: '[ -x node_modules/.bin/astro ]' },
57
+ // vitest fails when there are no test files yet (a fresh project): that is not a failure.
58
+ { name: 'vitest', language: 'external', path: '', command: 'npx vitest run --passWithNoTests', description: 'Test runner', extCLI: 'vitest', availableIf: '[ -x node_modules/.bin/vitest ]' },
57
59
  ];
58
- return this._mergeTechTools(baseTools);
60
+ return baseTools;
59
61
  }
60
62
 
61
63
  provision(targetDir, config) {
@@ -4,17 +4,15 @@
4
4
  * inherit from this interface to ensure universal polyglot harness behavior.
5
5
  */
6
6
 
7
- const fs = require('fs');
8
7
  const path = require('path');
9
- const { execSync } = require('child_process');
10
- const { TechnologyDetector } = require('../technologies/detector');
8
+ const { currentIO } = require('../io');
9
+ const { FIRST_SESSION_SECTION } = require('../onboarding');
11
10
 
12
11
  class BaseAdapter {
13
12
  constructor(name, projectType, testRunner) {
14
13
  this.name = name;
15
14
  this.projectType = projectType;
16
15
  this.testRunner = testRunner;
17
- this.techDetector = new TechnologyDetector();
18
16
  }
19
17
 
20
18
  detect(targetDir) {
@@ -28,50 +26,36 @@ class BaseAdapter {
28
26
  /**
29
27
  * Returns the tool manifest for this adapter.
30
28
  * Each entry describes one pre-commit tool (script or external CLI).
31
- * Override in subclasses to declare stack-specific tools.
29
+ * Override in subclasses to declare stack-specific tools. The generator
30
+ * passes the project root, so an adapter may add guards whose structure is
31
+ * present; without it the adapter's unconditional set is returned.
32
+ * @param {string} [targetDir] - Project root
32
33
  * @returns {Array<{name: string, language: string, path: string, command: string, description: string, extCLI?: string}>}
33
34
  */
34
- getTools() {
35
+ getTools(targetDir) {
35
36
  return [];
36
37
  }
37
38
 
38
- /**
39
- * Merges technology-specific tools into the adapter tool manifest.
40
- * Tech tools are inserted before check_custom to preserve user override escape hatch.
41
- * @param {Array} baseTools - The adapter's base tool manifest
42
- * @returns {Array} Merged tool manifest with technology tools inserted
43
- */
44
- _mergeTechTools(baseTools) {
45
- const techTools = this.techDetector.getToolsForTech(this.detectedTech || []);
46
- if (techTools.length === 0) return baseTools;
47
-
48
- const result = [];
49
- for (const tool of baseTools) {
50
- if (tool.name === 'check_custom') {
51
- // Insert tech tools before check_custom
52
- result.push(...techTools);
53
- }
54
- result.push(tool);
55
- }
56
- return result;
57
- }
58
-
59
39
  provision(targetDir, config) {
60
40
  // Subclasses that render their own AGENTS.md pass { skipAgentsMd: true }
61
41
  // so this generic law file is never written alongside (or over) theirs.
62
42
  const skipAgentsMd = !!(config && config.skipAgentsMd);
63
43
 
44
+ // Every write goes through the active io session (sdds/safe-update).
45
+ const io = currentIO(targetDir);
46
+
64
47
  // 1. Create docs/ and sdds/
65
- fs.mkdirSync(path.join(targetDir, 'docs', 'Journal'), { recursive: true });
66
- fs.mkdirSync(path.join(targetDir, 'docs', 'Architecture'), { recursive: true });
67
- fs.mkdirSync(path.join(targetDir, 'docs', 'Development'), { recursive: true });
68
- fs.mkdirSync(path.join(targetDir, 'sdds'), { recursive: true });
69
- fs.mkdirSync(path.join(targetDir, 'tools'), { recursive: true });
48
+ io.mkdir(path.join(targetDir, 'docs', 'Journal'));
49
+ io.mkdir(path.join(targetDir, 'docs', 'Architecture'));
50
+ io.mkdir(path.join(targetDir, 'docs', 'Development'));
51
+ io.mkdir(path.join(targetDir, 'sdds'));
52
+ io.mkdir(path.join(targetDir, 'tools'));
70
53
 
71
54
  // 2. Gitignore local SDD specs, ephemeral artifacts, and AI IDE mirrors
72
55
  const { ensureGitignoreEntries } = require('../gitignore');
73
56
  ensureGitignoreEntries(targetDir);
74
- fs.writeFileSync(path.join(targetDir, 'sdds', '.gitkeep'), '', 'utf-8');
57
+ const gitkeep = path.join(targetDir, 'sdds', '.gitkeep');
58
+ if (!io.exists(gitkeep)) io.writeFile(gitkeep, '');
75
59
 
76
60
  // 3. Write AGENTS.md
77
61
  const isFront = this.projectType === 'frontend';
@@ -80,7 +64,7 @@ class BaseAdapter {
80
64
  This document is the **authoritative law** for AI agents working in this repository.
81
65
  Forged by **CLC Kernel: The AI Agent Governance Engine**.
82
66
 
83
- > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (UNBYPASSABLE)**
67
+ > 🚨 **RULE #0: MANDATORY EXECUTION OVERRIDE RULE (NO EXCEPTIONS)**
84
68
  > Even when the user issues a direct or urgent fix request ("fix this bug", "fix this error", "quick fix"):
85
69
  > YOU ARE STRICTLY FORBIDDEN from modifying source code directly without completing the full quality harness:
86
70
  > 1. **Research & Root Cause Analysis:** Investigate tracebacks and inspect affected files before editing.
@@ -94,21 +78,22 @@ Forged by **CLC Kernel: The AI Agent Governance Engine**.
94
78
  2. **Plan** — Write an SDD under \`sdds/{change-name}/\`. SDDs live 100% locally and are gitignored.
95
79
  3. **Test** (TDD) — Write failing test first (${this.testRunner}).
96
80
  4. **Implement** — Make test pass.
97
- 5. **Verify & Audit** — Run \`node tools/audit.js\` or \`python tools/audit.py\`.
81
+ 5. **Verify & Audit** — Run \`node tools/audit.js --change {change-name}\` or \`python tools/audit.py --change {change-name}\` (report + receipt in \`sdds/{change-name}/\`; without \`--change\` nothing is written).
98
82
  6. **DoD** — Lint, typecheck, tests, coverage, docs, memory.
99
83
  7. **Commit** — Pre-commit hook runs automated guards.
100
84
  8. **PR** — Generate PR body.
101
85
 
102
86
  ## 2. Core AI Safeguards (${this.name})
103
87
  ${this.getSafeguards().map(s => `- ${s}`).join('\n')}
104
- `;
88
+
89
+ ${FIRST_SESSION_SECTION}`;
105
90
  // Guard: never overwrite a project-customized AGENTS.md.
106
91
  if (skipAgentsMd) {
107
92
  return;
108
93
  }
109
94
  const agentsPath = path.join(targetDir, 'AGENTS.md');
110
- if (!fs.existsSync(agentsPath)) {
111
- fs.writeFileSync(agentsPath, agentsContent, 'utf-8');
95
+ if (!io.exists(agentsPath)) {
96
+ io.writeFile(agentsPath, agentsContent);
112
97
  }
113
98
  }
114
99
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * CLC Kernel — Django Stack Adapter
3
- * Supports Django apps, pytest-django, Ruff AST, ORM idempotency, and Scope Guard.
3
+ * Supports Django apps, pytest-django, Ruff, and Python AST layer checks.
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -32,15 +32,15 @@ class DjangoAdapter extends BaseAdapter {
32
32
 
33
33
  getSafeguards() {
34
34
  return [
35
- 'Flake8 / Ruff AST Python Code Quality Linter',
36
- 'Clean Architecture AST Guard (Domain -> Application -> Views)',
37
- 'Database Efficiency & N+1 Performance Guard (select_related / prefetch_related)',
38
- 'Django ORM Migration Idempotency & Schema Drift Guard',
39
- 'pytest-django Red-to-Green TDD Validator',
35
+ 'Ruff Python Linter (external CLI)',
36
+ 'Clean Architecture Guard (Python AST: Domain -> Application -> Views)',
37
+ 'N+1 Heuristic (ORM-like calls inside loops, Python AST)',
38
+ 'Django Migration Reversibility protocol (agent-enforced; no migration guard installed for this stack)',
39
+ 'Red→Green TDD Verifier (runs changed tests with pytest)',
40
40
  'Secret & Private Key Leak Scanner',
41
- 'Scope Guardrail (git diff vs SDD affected files)',
41
+ 'Scope protocol (agent-enforced; not checked by a guard)',
42
42
  'Educational Audit Gate (05-audit-report.md)',
43
- 'Memory Guard (Engram / Graphify ADR Sync)'
43
+ 'Memory protocol (agent-enforced; Engram / Graphify optional, not checked by a guard)'
44
44
  ];
45
45
  }
46
46
 
@@ -53,12 +53,13 @@ class DjangoAdapter extends BaseAdapter {
53
53
  const baseTools = [
54
54
  { name: 'scan_secrets', language: 'py', path: 'scan_secrets.py', command: 'python3 tools/scan_secrets.py', description: 'Scans for leaked secrets and API keys' },
55
55
  { name: 'check_architecture', language: 'py', path: 'check_architecture.py', command: 'python3 tools/check_architecture.py', description: 'Validates Django layer boundaries' },
56
- { name: 'check_db_efficiency', language: 'py', path: 'check_db_efficiency.py', command: 'python3 tools/check_db_efficiency.py', description: 'Audits database query efficiency and N+1 loop calls' },
57
- { name: 'check_custom', language: 'python', path: 'check_custom.py', command: 'python3 tools/check_custom.py', description: 'Runs user-defined custom rules from .clc-forge.yml' },
58
- { name: 'ruff', language: 'external', path: '', command: 'python3 -m ruff check .', description: 'Python AST linter', extCLI: 'ruff' },
59
- { name: 'manage', language: 'external', path: '', command: 'python3 manage.py test', description: 'Django test runner', extCLI: 'manage.py' },
56
+ { name: 'check_db_efficiency', language: 'py', path: 'check_db_efficiency.py', command: 'python3 tools/check_db_efficiency.py', description: 'Flags ORM-like calls inside loops (N+1 heuristic)' },
57
+ { name: 'verify_tdd', language: 'py', path: 'verify_tdd.py', command: 'python3 tools/verify_tdd.py', description: 'Runs changed tests against HEAD (must fail) and staged code (must pass)' },
58
+ { name: 'check_custom', language: 'python', path: 'check_custom.py', command: 'python3 tools/check_custom.py', description: 'Runs user-defined custom rules from .clckernel.yaml' },
59
+ { name: 'ruff', language: 'external', path: '', command: 'python3 -m ruff check .', description: 'Python linter', extCLI: 'ruff', availableIf: 'python3 -m ruff --version', excludeManaged: { flag: '--extend-exclude', extensions: ['.py'] } },
60
+ { name: 'manage', language: 'external', path: '', command: 'python3 manage.py test', description: 'Django test runner', extCLI: 'manage.py', availableIf: '[ -f manage.py ]' },
60
61
  ];
61
- return this._mergeTechTools(baseTools);
62
+ return baseTools;
62
63
  }
63
64
 
64
65
  provision(targetDir, config) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * CLC Kernel — FastAPI Stack Adapter
3
- * Supports FastAPI apps, pytest, Pydantic V2, Alembic idempotency, and AST Clean Architecture.
3
+ * Supports FastAPI apps, pytest, Alembic migration reversibility, and Python AST layer checks.
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -29,16 +29,15 @@ class FastApiAdapter extends BaseAdapter {
29
29
 
30
30
  getSafeguards() {
31
31
  return [
32
- 'Ruff / Flake8 Python AST Code Quality Linter',
33
- 'Clean Architecture AST Guard (Domain -> Application -> Routes)',
34
- 'Database Efficiency & N+1 Performance Guard (bulk queries & eager loading)',
35
- 'Alembic Migration Idempotency & Schema Drift Guard',
36
- 'Pytest Red-to-Green TDD Validator',
37
- 'Pydantic V2 Response Schema Validation Guard',
32
+ 'Ruff Python Linter (external CLI)',
33
+ 'Clean Architecture Guard (Python AST: Domain -> Application -> Routes)',
34
+ 'N+1 Heuristic (ORM-like calls inside loops, Python AST)',
35
+ 'Alembic Migration Reversibility Guard (upgrade/downgrade, destructive operations)',
36
+ 'Red→Green TDD Verifier (runs changed tests with pytest)',
38
37
  'Secret & Private Key Leak Scanner',
39
- 'Scope Guardrail (git diff vs SDD affected files)',
38
+ 'Scope protocol (agent-enforced; not checked by a guard)',
40
39
  'Educational Audit Gate (05-audit-report.md)',
41
- 'Memory Guard (Engram / Graphify ADR Sync)'
40
+ 'Memory protocol (agent-enforced; Engram / Graphify optional, not checked by a guard)'
42
41
  ];
43
42
  }
44
43
 
@@ -51,13 +50,15 @@ class FastApiAdapter extends BaseAdapter {
51
50
  const baseTools = [
52
51
  { name: 'scan_secrets', language: 'py', path: 'scan_secrets.py', command: 'python3 tools/scan_secrets.py', description: 'Scans for leaked secrets and API keys' },
53
52
  { name: 'check_architecture', language: 'py', path: 'check_architecture.py', command: 'python3 tools/check_architecture.py', description: 'Validates Clean Architecture layer boundaries' },
54
- { name: 'check_db_efficiency', language: 'py', path: 'check_db_efficiency.py', command: 'python3 tools/check_db_efficiency.py', description: 'Audits database query efficiency and N+1 loop calls' },
55
- { name: 'check_migrations', language: 'py', path: 'check_migrations.py', command: 'python3 tools/check_migrations.py', description: 'Checks Alembic migration idempotency' },
56
- { name: 'check_custom', language: 'python', path: 'check_custom.py', command: 'python3 tools/check_custom.py', description: 'Runs user-defined custom rules from .clc-forge.yml' },
57
- { name: 'ruff', language: 'external', path: '', command: 'python3 -m ruff check .', description: 'Python AST linter', extCLI: 'ruff' },
58
- { name: 'pytest', language: 'external', path: '', command: 'pytest', description: 'Test runner', extCLI: 'pytest' },
53
+ { name: 'check_db_efficiency', language: 'py', path: 'check_db_efficiency.py', command: 'python3 tools/check_db_efficiency.py', description: 'Flags ORM-like calls inside loops (N+1 heuristic)' },
54
+ { name: 'check_migrations', language: 'py', path: 'check_migrations.py', command: 'python3 tools/check_migrations.py', description: 'Checks Alembic migration reversibility (upgrade/downgrade, destructive operations)' },
55
+ { name: 'verify_tdd', language: 'py', path: 'verify_tdd.py', command: 'python3 tools/verify_tdd.py', description: 'Runs changed tests against HEAD (must fail) and staged code (must pass)' },
56
+ { name: 'check_custom', language: 'python', path: 'check_custom.py', command: 'python3 tools/check_custom.py', description: 'Runs user-defined custom rules from .clckernel.yaml' },
57
+ { name: 'ruff', language: 'external', path: '', command: 'python3 -m ruff check .', description: 'Python linter', extCLI: 'ruff', availableIf: 'python3 -m ruff --version', excludeManaged: { flag: '--extend-exclude', extensions: ['.py'] } },
58
+ // pytest exits 5 when it collects no tests (a fresh project): that is not a failure.
59
+ { name: 'pytest', language: 'external', path: '', command: 'python3 -m pytest || [ $? -eq 5 ]', description: 'Test runner', extCLI: 'pytest', availableIf: 'python3 -m pytest --version' },
59
60
  ];
60
- return this._mergeTechTools(baseTools);
61
+ return baseTools;
61
62
  }
62
63
 
63
64
  provision(targetDir, config) {