@aikdna/kdna-cli 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +73 -0
  4. package/package.json +58 -0
  5. package/skills/kdna-loader/SKILL.md +257 -0
  6. package/src/agent.js +434 -0
  7. package/src/cli.js +260 -0
  8. package/src/cluster.js +235 -0
  9. package/src/cmds/_common.js +100 -0
  10. package/src/cmds/cluster.js +235 -0
  11. package/src/cmds/domain.js +638 -0
  12. package/src/cmds/identity.js +31 -0
  13. package/src/cmds/legacy.js +83 -0
  14. package/src/cmds/quality.js +87 -0
  15. package/src/cmds/registry.js +114 -0
  16. package/src/cmds/setup.js +8 -0
  17. package/src/compare.js +324 -0
  18. package/src/diff.js +288 -0
  19. package/src/identity.js +211 -0
  20. package/src/init.js +168 -0
  21. package/src/install.js +849 -0
  22. package/src/loader.js +70 -0
  23. package/src/publish.js +600 -0
  24. package/src/registry.js +258 -0
  25. package/src/search.js +73 -0
  26. package/src/setup.js +197 -0
  27. package/src/verify.js +423 -0
  28. package/src/version.js +112 -0
  29. package/templates/cluster/KDNA_Cluster.json +25 -0
  30. package/templates/cluster/README.md +32 -0
  31. package/templates/minimal-domain/KDNA_Core.json +54 -0
  32. package/templates/minimal-domain/KDNA_Patterns.json +37 -0
  33. package/templates/minimal-domain/kdna.json +31 -0
  34. package/templates/minimal-domain/tests/before-after.json +16 -0
  35. package/templates/standard-domain/KDNA_Core.json +76 -0
  36. package/templates/standard-domain/KDNA_Patterns.json +44 -0
  37. package/templates/standard-domain/README.md +74 -0
  38. package/templates/standard-domain/USAGE.md +59 -0
  39. package/templates/standard-domain/evals/1_excluded_case.json +16 -0
  40. package/templates/standard-domain/evals/3_boundary_cases.json +38 -0
  41. package/templates/standard-domain/evals/3_core_cases.json +35 -0
  42. package/templates/standard-domain/evals/3_failure_cases.json +35 -0
  43. package/templates/standard-domain/evals/scoring.json +60 -0
  44. package/templates/standard-domain/kdna.json +28 -0
  45. package/validators/kdna-lint.js +53 -0
  46. package/validators/kdna-validate.js +92 -0
@@ -0,0 +1,31 @@
1
+ {
2
+ "kdna_spec": "1.0-rc",
3
+ "name": "@aikdna/example_domain",
4
+ "version": "0.1.0",
5
+ "language": "en",
6
+ "languages": ["en"],
7
+ "created": "YYYY-MM-DD",
8
+ "updated": "YYYY-MM-DD",
9
+ "description": "[TODO: describe what judgment this domain improves in one sentence]",
10
+ "core_insight": "[TODO: one-sentence insight that captures the domain's core judgment principle]",
11
+ "keywords": ["[TODO: add relevant keywords]"],
12
+ "access": "open",
13
+ "status": "experimental",
14
+ "author": {
15
+ "name": "Your Name",
16
+ "id": "your-id"
17
+ },
18
+ "license": {
19
+ "type": "CC-BY-4.0",
20
+ "commercial": false,
21
+ "allow_agent_use": true,
22
+ "allow_redistribution": true,
23
+ "allow_training": true
24
+ },
25
+ "registry": {
26
+ "repo": "https://github.com/your-org/your-repo"
27
+ },
28
+ "file_count": 3,
29
+ "kdna_file_count": 3,
30
+ "files": ["KDNA_Core.json", "KDNA_Patterns.json", "tests/before-after.json"]
31
+ }
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "input": "A task related to this domain that needs expert judgment",
4
+ "without_kdna": {
5
+ "expected_approach": "How a generic AI would typically respond",
6
+ "common_mistake": "What the generic response gets wrong"
7
+ },
8
+ "with_kdna": {
9
+ "expected_approach": "How a domain-aware response should differ",
10
+ "signal_reading": "Which domain signal this input triggers",
11
+ "diagnosis_path": "The expert reasoning chain"
12
+ },
13
+ "domain": "example_domain",
14
+ "trigger": "keyword or pattern that activates this domain"
15
+ }
16
+ ]
@@ -0,0 +1,76 @@
1
+ {
2
+ "meta": {
3
+ "version": "0.1.0",
4
+ "domain": "your_domain_id",
5
+ "created": "YYYY-MM-DD",
6
+ "purpose": "One-sentence statement of what this KDNA helps an AI agent judge in this domain.",
7
+ "load_condition": "Load when the user asks for <specific task type that triggers this domain>."
8
+ },
9
+ "axioms": [
10
+ {
11
+ "id": "axiom_one",
12
+ "one_sentence": "A specific, contestable principle this domain claims about judgment.",
13
+ "full_statement": "Expand into 2-3 sentences. Be specific. Avoid 'X is important' style. Two experts in this field should be able to agree on whether this axiom applies to a given situation.",
14
+ "why": "What would the agent get wrong without this axiom? (Be specific.)",
15
+ "confidence": "high",
16
+ "evidence_type": ["practice_pattern", "case_observation"],
17
+ "applies_when": [
18
+ "describe situation 1 where this axiom guides judgment correctly",
19
+ "describe situation 2"
20
+ ],
21
+ "does_not_apply_when": [
22
+ "describe situation where applying this axiom would mislead the agent",
23
+ "describe another case where the axiom should step back"
24
+ ],
25
+ "failure_risk": "What bad judgment results if the agent applies this axiom OUTSIDE its scope? (Be specific.)"
26
+ },
27
+ {
28
+ "id": "axiom_two",
29
+ "one_sentence": "(see writing the axioms guide)",
30
+ "full_statement": "...",
31
+ "why": "...",
32
+ "confidence": "medium",
33
+ "evidence_type": ["practice_pattern"],
34
+ "applies_when": ["..."],
35
+ "does_not_apply_when": ["..."],
36
+ "failure_risk": "..."
37
+ }
38
+ ],
39
+ "ontology": [
40
+ {
41
+ "id": "concept_one",
42
+ "one_sentence": "One-sentence description of a domain-specific concept.",
43
+ "essence": "What this concept fundamentally is. Operational, not dictionary.",
44
+ "boundary": "What this concept is often confused with. Name the confusable, not just 'not X'.",
45
+ "trigger_signal": "Observable signal in user input that this concept is relevant.",
46
+ "applies_when": ["..."],
47
+ "does_not_apply_when": ["..."]
48
+ }
49
+ ],
50
+ "frameworks": [
51
+ {
52
+ "id": "frame_diagnose_before_fix",
53
+ "name": "Diagnose Before Fix",
54
+ "when_to_use": "Use when asked to evaluate or improve in this domain.",
55
+ "steps": [
56
+ "Step 1: identify whether ...",
57
+ "Step 2: identify whether ...",
58
+ "Step 3: name the root before suggesting any fix."
59
+ ]
60
+ }
61
+ ],
62
+ "core_structure": [
63
+ {
64
+ "from": "user_symptom_or_complaint",
65
+ "to": "root_cause_in_this_domain",
66
+ "via": "the specific check or framing this domain provides"
67
+ }
68
+ ],
69
+ "stances": [
70
+ {
71
+ "stance": "A prescriptive position that biases agent behavior in this domain.",
72
+ "applies_when": ["..."],
73
+ "does_not_apply_when": ["..."]
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "meta": {
3
+ "version": "0.1.0",
4
+ "domain": "your_domain_id",
5
+ "created": "YYYY-MM-DD",
6
+ "purpose": "Common misdiagnoses and self-checks for <domain>.",
7
+ "load_condition": "Always load with KDNA_Core.json."
8
+ },
9
+ "terminology": {
10
+ "standard_terms": [
11
+ {
12
+ "term": "domain_specific_concept",
13
+ "definition": "Operational definition. Not a dictionary definition. What the agent must do, see, or check when this term applies."
14
+ }
15
+ ],
16
+ "banned_terms": [
17
+ {
18
+ "term": "vague_word_agents_default_to",
19
+ "why": "Why using this term in this domain misleads the agent or the user.",
20
+ "replace_with": "the specific, operational alternative",
21
+ "applies_when": ["situation where the substitution matters"]
22
+ }
23
+ ]
24
+ },
25
+ "misunderstandings": [
26
+ {
27
+ "id": "misread_one",
28
+ "wrong": "A specific belief a real agent in this domain might hold. NOT a straw-man.",
29
+ "correct": "What the agent should believe instead. Be operational, not slogan.",
30
+ "key_distinction": "Name the conceptual boundary, not just the negation.",
31
+ "why": "What bad output results when an agent holds the wrong belief.",
32
+ "confidence": "high",
33
+ "evidence_type": ["practice_pattern"],
34
+ "applies_when": ["describe situations where this misunderstanding is likely"],
35
+ "does_not_apply_when": ["describe situations where this distinction would be a red herring"],
36
+ "failure_risk": "What harm if the agent over-applies this distinction outside its scope."
37
+ }
38
+ ],
39
+ "self_check": [
40
+ "Domain-specific yes/no question 1 the agent should answer about its own output?",
41
+ "Domain-specific yes/no question 2?",
42
+ "Domain-specific yes/no question 3?"
43
+ ]
44
+ }
@@ -0,0 +1,74 @@
1
+ > 🧬 [aikdna.com](https://aikdna.com) — Official website
2
+
3
+ # [Your domain name]
4
+
5
+ [![KDNA Spec](https://img.shields.io/badge/KDNA-v1.0-4c1)](https://github.com/knowledge-dna/KDNA)
6
+
7
+ **[Domain Title]** — [one-sentence description, same as kdna.json.description]
8
+
9
+ ## Core Insight
10
+
11
+ [one-sentence core insight, same as kdna.json.core_insight]
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ kdna install @yourscope/your_domain_id
17
+ kdna verify @yourscope/your_domain_id # structure + trust + judgment
18
+ ```
19
+
20
+ ## The Four Questions (v2.1 governance)
21
+
22
+ Every KDNA must answer four questions. Here are this domain's answers.
23
+
24
+ ### 1. Where does it come from?
25
+
26
+ - **Authored by**: [Your name / team]
27
+ - **Evidence type**: [practice patterns / case observations / research findings — be specific]
28
+ - **Signed by**: `@yourscope` trust key (fingerprint `[your-fingerprint]`)
29
+
30
+ ### 2. Where does it apply?
31
+
32
+ This KDNA helps agents [specific judgment] in:
33
+
34
+ - [situation 1]
35
+ - [situation 2]
36
+ - [situation 3]
37
+
38
+ ### 3. How is it verified?
39
+
40
+ - `kdna verify @yourscope/your_domain_id --judgment` — checks v2.1 governance fields
41
+ - `kdna compare @yourscope/your_domain_id --input "<task>"` — runs with/without KDNA on a real LLM and shows the reasoning-trajectory diff
42
+ - `evals/` directory contains 10 cases: 3 core + 3 boundary + 3 failure + 1 excluded
43
+ - `demo/` directory holds rendered before/after comparisons. Regenerate with `ANTHROPIC_API_KEY=... node scripts/build-demo.js`
44
+
45
+ ### 4. When does it NOT apply?
46
+
47
+ Loading this domain on the wrong task is itself a risk. **Do not load** when:
48
+
49
+ - [explicit case 1 from your axioms' does_not_apply_when]
50
+ - [explicit case 2]
51
+ - [explicit case 3]
52
+
53
+ If any of the above is true, the agent should decline to load this domain.
54
+
55
+ ## Known Failure Risks
56
+
57
+ | Risk | When it shows up |
58
+ |------|---|
59
+ | [risk 1 from axiom_one.failure_risk] | [trigger] |
60
+ | [risk 2 from axiom_two.failure_risk] | [trigger] |
61
+ | [risk 3 from misread_one.failure_risk] | [trigger] |
62
+
63
+ ## Files
64
+
65
+ | File | Purpose |
66
+ |------|---------|
67
+ | `KDNA_Core.json` | Axioms (with v2.1 boundaries), ontology, frameworks, causal structure, stances |
68
+ | `KDNA_Patterns.json` | Terminology, banned terms, misunderstandings (with v2.1 boundaries), self-checks |
69
+ | `evals/` | Test cases for `kdna compare` and quality scoring |
70
+ | `kdna.json` | Domain manifest (name, version, judgment_version, signature) |
71
+
72
+ ## License
73
+
74
+ [Your license — CC-BY-4.0 / MIT / etc.]
@@ -0,0 +1,59 @@
1
+ # standard-domain template
2
+
3
+ This is the v2.1 reference template for a new KDNA domain. Copy this folder, fill in the bracketed placeholders, and publish.
4
+
5
+ ## What this template includes
6
+
7
+ ```
8
+ standard-domain/
9
+ ├── README.md # Four Questions + Failure Risks + Files
10
+ ├── kdna.json # Manifest with judgment_version (v2.1)
11
+ ├── KDNA_Core.json # Axioms with applies_when / does_not_apply_when / failure_risk
12
+ ├── KDNA_Patterns.json # Misunderstandings with same governance fields
13
+ └── evals/
14
+ ├── 3_core_cases.json # Domain MUST handle correctly
15
+ ├── 3_boundary_cases.json # Edge of scope
16
+ ├── 3_failure_cases.json # Known failure modes (cite failure_risk)
17
+ ├── 1_excluded_case.json # Wrong domain entirely
18
+ └── scoring.json # D1-D8 dimensions
19
+ ```
20
+
21
+ ## How to use
22
+
23
+ ```bash
24
+ # 1. Copy to your new domain folder
25
+ cp -r templates/standard-domain ./my_domain
26
+ cd my_domain
27
+
28
+ # 2. Generate your scope identity (once per scope)
29
+ kdna identity init
30
+
31
+ # 3. Edit kdna.json:
32
+ # - name: "@yourscope/your_domain_id"
33
+ # - judgment_version: "YYYY.MM"
34
+ # - description, core_insight, keywords, author
35
+
36
+ # 4. Edit KDNA_Core.json and KDNA_Patterns.json:
37
+ # - Replace placeholder axioms with your own
38
+ # - Every axiom MUST have applies_when, does_not_apply_when, failure_risk
39
+ # - Every misunderstanding MUST have applies_when, failure_risk
40
+
41
+ # 5. Fill out evals/ — 10 real cases
42
+
43
+ # 6. Verify quality
44
+ kdna verify ./.
45
+
46
+ # 7. Publish
47
+ KDNA_IDENTITY_DIR=~/.kdna/identity-official \
48
+ kdna publish ./. \
49
+ --release-tag v0.1.0 \
50
+ --repo yourname/kdna-<your_domain_id>
51
+
52
+ # 8. Open PR to kdna-registry with the patch JSON the command printed
53
+ ```
54
+
55
+ ## Standard vs minimal-domain
56
+
57
+ `minimal-domain/` is the **bare-minimum** template — 2 files, no v2.1 fields, no evals. Use it only for fast experimentation or learning.
58
+
59
+ `standard-domain/` is the **registry-ready** template. Any domain meant to be published to the registry should start here and aim for `kdna verify --judgment` ≥ 80% before publishing.
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://aikdna.com/schemas/eval-cases-v1.json",
3
+ "domain": "@yourscope/your_domain_id",
4
+ "category": "excluded",
5
+ "purpose": "Case where your KDNA should NOT be loaded at all. The loader skill should detect the mismatch.",
6
+ "cases": [
7
+ {
8
+ "id": "excluded-1-wrong-domain-entirely",
9
+ "title": "Task that belongs to a different KDNA (or no KDNA)",
10
+ "input": "...",
11
+ "should_load_this_domain": false,
12
+ "why_not": "Explain which feature of the input signals 'wrong domain' (format, audience, task type, etc.).",
13
+ "what_to_load_instead": "@yourscope/other_domain (if known) or no KDNA"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://aikdna.com/schemas/eval-cases-v1.json",
3
+ "domain": "@yourscope/your_domain_id",
4
+ "category": "boundary",
5
+ "purpose": "Cases at the edge of the domain's scope. The agent should detect the boundary and either decline OR be careful about which axioms still apply.",
6
+ "cases": [
7
+ {
8
+ "id": "boundary-1-adjacent-domain",
9
+ "title": "Task that looks like your domain but isn't",
10
+ "input": "...",
11
+ "expected_without_kdna": "...",
12
+ "expected_with_kdna": "Agent recognizes the boundary, names which axiom's does_not_apply_when triggers, and does NOT over-apply your domain's frame.",
13
+ "must_contain": ["..."],
14
+ "must_not_contain": ["the over-applied frame you want to avoid"],
15
+ "boundary_signal": "[which axiom's does_not_apply_when should trigger]"
16
+ },
17
+ {
18
+ "id": "boundary-2-format-mismatch",
19
+ "title": "Right topic, wrong format",
20
+ "input": "...",
21
+ "expected_without_kdna": "...",
22
+ "expected_with_kdna": "...",
23
+ "must_contain": ["..."],
24
+ "must_not_contain": ["..."],
25
+ "boundary_signal": "..."
26
+ },
27
+ {
28
+ "id": "boundary-3-audience-mismatch",
29
+ "title": "Right task, wrong audience for the domain's stance",
30
+ "input": "...",
31
+ "expected_without_kdna": "...",
32
+ "expected_with_kdna": "...",
33
+ "must_contain": ["..."],
34
+ "must_not_contain": ["..."],
35
+ "boundary_signal": "..."
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "https://aikdna.com/schemas/eval-cases-v1.json",
3
+ "domain": "@yourscope/your_domain_id",
4
+ "category": "core",
5
+ "purpose": "Cases the domain MUST handle correctly. Loading should clearly change the agent's response from generic to domain-specific.",
6
+ "cases": [
7
+ {
8
+ "id": "core-1-typical-task",
9
+ "title": "Most common task in your domain",
10
+ "input": "[paste the user prompt that triggers your domain's typical task]",
11
+ "expected_without_kdna": "[what a baseline agent would say — usually surface-level]",
12
+ "expected_with_kdna": "[what your domain produces — root cause / specific terminology / boundary awareness]",
13
+ "must_contain": ["domain-specific term 1", "expected diagnosis word"],
14
+ "must_not_contain": ["generic advice phrase you want to avoid"]
15
+ },
16
+ {
17
+ "id": "core-2-edge-of-core",
18
+ "title": "Less-common but still inside scope",
19
+ "input": "...",
20
+ "expected_without_kdna": "...",
21
+ "expected_with_kdna": "...",
22
+ "must_contain": ["..."],
23
+ "must_not_contain": ["..."]
24
+ },
25
+ {
26
+ "id": "core-3-trickier-pattern",
27
+ "title": "A case requiring the agent to apply an axiom non-obviously",
28
+ "input": "...",
29
+ "expected_without_kdna": "...",
30
+ "expected_with_kdna": "...",
31
+ "must_contain": ["..."],
32
+ "must_not_contain": ["..."]
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "https://aikdna.com/schemas/eval-cases-v1.json",
3
+ "domain": "@yourscope/your_domain_id",
4
+ "category": "failure",
5
+ "purpose": "Cases where loading your KDNA produces a WORSE response than not loading it. Each case should cite the failure_risk declared by your domain.",
6
+ "cases": [
7
+ {
8
+ "id": "failure-1-known-risk-one",
9
+ "title": "Failure mode named in axiom_one.failure_risk",
10
+ "input": "...",
11
+ "expected_failure_with_kdna": "Agent over-applies axiom_one, fights the user's stated goal.",
12
+ "correct_behavior": "Agent recognizes the boundary, honors the user's actual request, surfaces the trade-off.",
13
+ "detection_signal": "...",
14
+ "failure_risk_named_in_domain": "axiom_one.failure_risk: '...'"
15
+ },
16
+ {
17
+ "id": "failure-2-known-risk-two",
18
+ "title": "Failure mode named in axiom_two.failure_risk",
19
+ "input": "...",
20
+ "expected_failure_with_kdna": "...",
21
+ "correct_behavior": "...",
22
+ "detection_signal": "...",
23
+ "failure_risk_named_in_domain": "axiom_two.failure_risk: '...'"
24
+ },
25
+ {
26
+ "id": "failure-3-misunderstanding-risk",
27
+ "title": "Failure mode named in a misunderstanding's failure_risk",
28
+ "input": "...",
29
+ "expected_failure_with_kdna": "...",
30
+ "correct_behavior": "...",
31
+ "detection_signal": "...",
32
+ "failure_risk_named_in_domain": "misread_one.failure_risk: '...'"
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "https://aikdna.com/schemas/eval-scoring-v1.json",
3
+ "domain": "@yourscope/your_domain_id",
4
+ "scoring_framework": "D1-D8",
5
+ "purpose": "How to score whether loading your KDNA actually changes the agent's reasoning trajectory.",
6
+ "dimensions": [
7
+ {
8
+ "id": "D1",
9
+ "name": "Diagnostic depth",
10
+ "question": "Root cause vs surface symptoms?",
11
+ "scale": "0-5"
12
+ },
13
+ {
14
+ "id": "D2",
15
+ "name": "Terminology consistency",
16
+ "question": "Used domain-specific terms?",
17
+ "scale": "0-5"
18
+ },
19
+ {
20
+ "id": "D3",
21
+ "name": "Misunderstanding detection",
22
+ "question": "Avoided named misdiagnoses?",
23
+ "scale": "0-5"
24
+ },
25
+ {
26
+ "id": "D4",
27
+ "name": "Axiom alignment",
28
+ "question": "Response grounded in axioms?",
29
+ "scale": "0-5"
30
+ },
31
+ {
32
+ "id": "D5",
33
+ "name": "Scenario classification",
34
+ "question": "Correctly identified case category?",
35
+ "scale": "0-5"
36
+ },
37
+ {
38
+ "id": "D6",
39
+ "name": "Actionable specificity",
40
+ "question": "Suggestions specific enough to act on?",
41
+ "scale": "0-5"
42
+ },
43
+ {
44
+ "id": "D7",
45
+ "name": "Boundary awareness",
46
+ "question": "Recognized when task is outside scope?",
47
+ "scale": "0-5"
48
+ },
49
+ {
50
+ "id": "D8",
51
+ "name": "Self-check pass rate",
52
+ "question": "Of N self-checks, how many can the response answer 'yes'?",
53
+ "scale": "0/N to N/N"
54
+ }
55
+ ],
56
+ "minimum_threshold_for_kdna_value": {
57
+ "description": "Loading your KDNA must improve average score by at least +2 vs no-KDNA baseline, averaged across all 10 cases.",
58
+ "rationale": "If the lift is <+2, the domain is not adding value."
59
+ }
60
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "kdna_spec": "1.0",
3
+ "name": "@yourscope/your_domain_id",
4
+ "version": "0.1.0",
5
+ "judgment_version": "YYYY.MM",
6
+ "status": "experimental",
7
+ "access": "open",
8
+ "language": "en",
9
+ "created": "YYYY-MM-DD",
10
+ "updated": "YYYY-MM-DD",
11
+ "description": "[one-sentence description; appears on registry and in install output]",
12
+ "core_insight": "[one-sentence core insight that distinguishes this domain]",
13
+ "keywords": ["[add 3-6 keywords for kdna search]"],
14
+ "author": {
15
+ "name": "Your Name",
16
+ "id": "your-id",
17
+ "pubkey": "[set automatically by kdna publish — must match your scope trust_pubkey]"
18
+ },
19
+ "license": {
20
+ "type": "CC-BY-4.0",
21
+ "commercial": false,
22
+ "allow_agent_use": true,
23
+ "allow_redistribution": true,
24
+ "allow_training": true
25
+ },
26
+ "file_count": 2,
27
+ "files": ["KDNA_Core.json", "KDNA_Patterns.json"]
28
+ }
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * kdna-lint — Structural and content validation for KDNA domains.
4
+ *
5
+ * Uses @aikdna/kdna-core lintDomain for pure validation logic,
6
+ * handles file I/O here.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { lintDomain } = require('@aikdna/kdna-core');
12
+
13
+ const domainDir = process.argv[2];
14
+ if (!domainDir || domainDir === '--help' || domainDir === '-h') {
15
+ console.log(`kdna-lint — Structural and content validation for KDNA domains.
16
+
17
+ Usage: kdna-lint <domain-folder>
18
+
19
+ Runs lint checks on KDNA_*.json files for structure, content, and consistency.
20
+
21
+ Options:
22
+ -h, --help Show this help message`);
23
+ process.exit(domainDir ? 0 : 2);
24
+ }
25
+
26
+ if (!fs.existsSync(domainDir) || !fs.statSync(domainDir).isDirectory()) {
27
+ console.error(`Not a directory: ${domainDir}`);
28
+ process.exit(2);
29
+ }
30
+
31
+ // Read all KDNA JSON files in the domain directory
32
+ const files = fs.readdirSync(domainDir).filter((f) => f.endsWith('.json') && f !== 'kdna.json');
33
+ const dataMap = {};
34
+ for (const f of files) {
35
+ try {
36
+ dataMap[f] = JSON.parse(fs.readFileSync(path.join(domainDir, f), 'utf8'));
37
+ } catch (e) {
38
+ // lintDomain will report missing required files; skip unparseable here
39
+ }
40
+ }
41
+
42
+ const result = lintDomain(dataMap);
43
+
44
+ if (result.warnings.length) {
45
+ console.log('Warnings:');
46
+ result.warnings.forEach((w) => console.log(` - ${w}`));
47
+ }
48
+ if (result.errors.length) {
49
+ console.error('Errors:');
50
+ result.errors.forEach((e) => console.error(` - ${e}`));
51
+ process.exit(1);
52
+ }
53
+ console.log(`✓ KDNA domain valid: ${domainDir}`);
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * kdna-validate — Enhanced KDNA domain validator using JSON Schema.
4
+ *
5
+ * Uses @aikdna/kdna-core validateDomainSchema and validateCrossFile for
6
+ * pure validation logic, handles file I/O here.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { validateDomainSchema, validateCrossFile } = require('@aikdna/kdna-core');
12
+
13
+ const domainDir = process.argv[2];
14
+ if (!domainDir || domainDir === '--help' || domainDir === '-h') {
15
+ console.log(`kdna-validate — Validate a KDNA domain against JSON Schema.
16
+
17
+ Usage: kdna-validate <domain-folder>
18
+
19
+ Checks each KDNA_*.json file against its schema, then runs cross-file validation.
20
+
21
+ Options:
22
+ -h, --help Show this help message`);
23
+ process.exit(domainDir ? 0 : 2);
24
+ }
25
+
26
+ if (!fs.existsSync(domainDir) || !fs.statSync(domainDir).isDirectory()) {
27
+ console.error(`Not a directory: ${domainDir}`);
28
+ process.exit(2);
29
+ }
30
+
31
+ const SCHEMA_DIR = path.join(
32
+ path.dirname(require.resolve('@aikdna/kdna-core/package.json')),
33
+ 'schema',
34
+ );
35
+
36
+ const FILE_MAP = {
37
+ 'KDNA_Core.json': 'KDNA_Core.schema.json',
38
+ 'KDNA_Patterns.json': 'KDNA_Patterns.schema.json',
39
+ 'KDNA_Scenarios.json': 'KDNA_Scenarios.schema.json',
40
+ 'KDNA_Cases.json': 'KDNA_Cases.schema.json',
41
+ 'KDNA_Reasoning.json': 'KDNA_Reasoning.schema.json',
42
+ 'KDNA_Evolution.json': 'KDNA_Evolution.schema.json',
43
+ };
44
+
45
+ // Read all KDNA JSON files
46
+ const dataMap = {};
47
+ for (const [file] of Object.entries(FILE_MAP)) {
48
+ const filePath = path.join(domainDir, file);
49
+ if (fs.existsSync(filePath)) {
50
+ try {
51
+ dataMap[file] = JSON.parse(fs.readFileSync(filePath, 'utf8'));
52
+ } catch {
53
+ /* skip unparseable files */
54
+ }
55
+ }
56
+ }
57
+
58
+ // Read schemas
59
+ const schemaMap = {};
60
+ for (const [file, schemaFile] of Object.entries(FILE_MAP)) {
61
+ const schemaPath = path.join(SCHEMA_DIR, schemaFile);
62
+ if (fs.existsSync(schemaPath)) {
63
+ try {
64
+ schemaMap[schemaFile] = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
65
+ } catch {
66
+ /* skip unparseable schemas */
67
+ }
68
+ }
69
+ }
70
+
71
+ // Schema validation
72
+ const schemaResult = validateDomainSchema(dataMap, schemaMap);
73
+
74
+ // Cross-file validation
75
+ const crossResult = validateCrossFile(dataMap);
76
+
77
+ // Combine results
78
+ const errors = [...schemaResult.errors, ...crossResult.errors];
79
+ const warnings = [...schemaResult.warnings, ...crossResult.warnings];
80
+
81
+ if (warnings.length) {
82
+ console.log('Warnings:');
83
+ warnings.forEach((w) => console.log(` - ${w}`));
84
+ }
85
+ if (errors.length) {
86
+ console.error('Errors:');
87
+ errors.forEach((e) => console.error(` - ${e}`));
88
+ process.exit(1);
89
+ }
90
+
91
+ const validCount = Object.keys(dataMap).length;
92
+ console.log(`✓ KDNA domain valid (schema): ${domainDir} (${validCount} files passed)`);