knowzcode 0.3.6 → 0.3.7

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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Official KnowzCode plugin marketplace - Platform-agnostic AI development methodology",
9
- "version": "0.3.6"
9
+ "version": "0.3.7"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "kc",
14
14
  "source": "./",
15
15
  "description": "KnowzCode - Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
16
- "version": "0.3.6",
16
+ "version": "0.3.7",
17
17
  "author": {
18
18
  "name": "Alex Headscarf"
19
19
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kc",
3
3
  "description": "KnowzCode - Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
4
- "version": "0.3.6",
4
+ "version": "0.3.7",
5
5
  "author": {
6
6
  "name": "Alex Headscarf"
7
7
  }
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![License: MIT + Commons Clause](https://img.shields.io/badge/License-MIT_+_Commons_Clause-yellow.svg)](LICENSE)
8
8
  [![Claude Code Plugin](https://img.shields.io/badge/Claude_Code-Plugin-purple)](https://github.com/knowz-io/knowzcode)
9
- [![Version](https://img.shields.io/badge/version-0.3.6-blue)](https://github.com/knowz-io/knowzcode/releases)
9
+ [![Version](https://img.shields.io/badge/version-0.3.7-blue)](https://github.com/knowz-io/knowzcode/releases)
10
10
 
11
11
  [Installation](#installation) · [Quick Start](#quick-start) · [When to Use It](#when-to-use-knowzcode) · [How It Works](#how-it-works) · [Commands](#commands) · [Docs](#documentation)
12
12
 
package/agents/closer.md CHANGED
@@ -4,7 +4,7 @@ description: "KnowzCode: Finalization — specs, tracker, log, architecture, lea
4
4
  tools: Read, Write, Edit, Glob, Grep
5
5
  model: opus
6
6
  permissionMode: acceptEdits
7
- maxTurns: 20
7
+ maxTurns: 25
8
8
  ---
9
9
 
10
10
  # Closer
@@ -16,6 +16,16 @@ Your expertise: Finalization of specs, tracker, log, architecture docs, and lear
16
16
 
17
17
  Execute the finalization phase after implementation is verified. Update all KnowzCode artifacts to reflect the completed work, then create a final commit.
18
18
 
19
+ ## Startup MCP Verification
20
+
21
+ On spawn, verify MCP connectivity before beginning finalization:
22
+
23
+ 1. Read `knowzcode/knowzcode_vaults.md` — check for configured vaults (non-empty ID)
24
+ 2. If no configured vaults → skip vault writes (nothing to write to)
25
+ 3. If configured vaults exist → call `list_vaults()` to verify MCP connectivity
26
+ - If succeeds → proceed with vault writes during Learning Capture
27
+ - If fails → queue all captures to `knowzcode/pending_captures.md` during Learning Capture
28
+
19
29
  ## Finalization Protocol
20
30
 
21
31
  Follow the steps in `knowzcode_loop.md` section 3.5:
@@ -56,7 +66,7 @@ During finalization:
56
66
 
57
67
  ## Learning Capture
58
68
 
59
- Scan the WorkGroup for insight-worthy patterns using the signal types from `knowzcode_loop.md` section 7 (Pattern, Decision, Workaround, Performance, Security, Convention).
69
+ Scan the WorkGroup for insight-worthy patterns using the signal types from `knowzcode_loop.md` section 7 (Pattern, Decision, Workaround, Performance, Security, Convention, Integration, Scope).
60
70
 
61
71
  ### Knowz-Scribe Delegation (Parallel Teams)
62
72
 
@@ -68,21 +78,93 @@ If knowz-scribe is active (Parallel Teams mode with MCP connected):
68
78
 
69
79
  ### Direct Write Fallback (Sequential/Subagent)
70
80
 
71
- If knowz-scribe is NOT active but MCP is configured:
81
+ If knowz-scribe is NOT active but MCP is available (verified at startup):
82
+
83
+ > **Content Detail Principle**: Vault entries are retrieved via semantic search — write detailed, self-contained content with full reasoning, technology names, and code examples. See `knowzcode/knowzcode_vaults.md` (canonical source for content filters).
84
+
85
+ #### Step 1: Read Context
86
+
87
+ 1. Read `knowzcode/knowzcode_vaults.md` to discover configured vaults, their IDs, write conditions, and content filters
88
+ 2. Skip vault entries with empty ID fields — these haven't been created on the server yet
89
+ 3. Treat backwards-compat aliases identically: `research`/`domain`/`platform` = `ecosystem`, `sessions` = `finalizations`
90
+ 4. If a single vault is configured (regardless of type), route everything there
91
+
92
+ #### Step 2: Determine Target Vaults
93
+
94
+ Use the **Learning Category Routing** table to map each detected learning to the correct vault type:
95
+
96
+ | Learning Category | Target Vault Type | Title Prefix |
97
+ |-------------------|-------------------|--------------|
98
+ | Pattern | `code` | `Pattern:` |
99
+ | Workaround | `code` | `Workaround:` |
100
+ | Performance | `code` | `Performance:` |
101
+ | Decision | `ecosystem` | `Decision:` |
102
+ | Convention | `ecosystem` | `Convention:` |
103
+ | Security | `ecosystem` | `Security:` |
104
+ | Integration | `ecosystem` | `Integration:` |
105
+ | Scope | `ecosystem` | `Scope:` |
106
+ | Completion record | `finalizations` | `Completion:` |
107
+ | Audit trail | user's enterprise vault (if configured) | `Audit:` |
108
+
109
+ Only write if the targeted vault is configured — skip gracefully if not.
110
+
111
+ #### Step 3: Format Content
112
+
113
+ For each target vault, apply its **Content Filter** as defined in `knowzcode/knowzcode_vaults.md`:
114
+
115
+ - `code` vault: `[CONTEXT]` / `[PATTERN]` / `[EXAMPLE]` / `[TAGS]`
116
+ - `ecosystem` vault: `[CONTEXT]` / `[INSIGHT]` / `[RATIONALE]` / `[TAGS]`
117
+ - `finalizations` vault: `[GOAL]` / `[OUTCOME]` / `[NODES]` / `[DURATION]` / `[SUMMARY]` / `[TAGS]`
118
+
119
+ Follow the Content Detail Principle: write self-contained entries with full reasoning, specific technology names, code examples, and file paths. Every entry must be useful without any other context — it will be found via semantic search months later.
120
+
121
+ - **Title**: Use the prefix from the routing table + descriptive summary with technology names
122
+ - **Tags**: learning category, `phase-3`, domain tags, technology names
123
+ - **Source**: `KnowzCode WorkGroup {wgid}`
124
+
125
+ #### Step 4: Dedup Check
126
+
127
+ Before each write, call `search_knowledge(title, vaultId, 3)` on the target vault. If a result with a substantially similar title AND content already exists, skip the write. Log dedup catches in the WorkGroup file.
128
+
129
+ #### Step 5: Write
130
+
131
+ Call `create_knowledge` with the formatted payload for each target vault.
132
+
133
+ #### Phase 3 Extraction Guide
134
+
135
+ When scanning the WorkGroup for learnings, extract:
136
+ - **Architectural learnings**: Structural discoveries, component relationships that were not obvious, integration patterns that emerged during implementation
137
+ - **Convention patterns established**: New team conventions with full rationale and examples
138
+ - **Consolidation decisions**: What was merged or refactored during finalization and why
139
+ - **Implementation patterns**: Any Pattern/Workaround/Performance insights captured in the WorkGroup during Phase 2A that were not already written by a scribe
140
+ - **Scope decisions**: What was included/excluded and the rationale (from Phase 1A)
141
+ - **Security findings**: From Phase 2B audit, with severity and remediation
142
+
143
+ #### Enterprise Audit Trail
144
+
145
+ If `knowzcode/enterprise/compliance_manifest.md` exists and `mcp_compliance_enabled: true`:
146
+ 1. Find vault matching type "enterprise" in `knowzcode/knowzcode_vaults.md`
147
+ 2. Push completion record with goal, NodeIDs, audit score, and decisions
148
+ 3. Push architecture drift findings if any detected during finalization
149
+
150
+ ### MCP Graceful Degradation
72
151
 
73
- > **Content Detail Principle**: Vault entries are retrieved via semantic search write detailed, self-contained content with full reasoning, technology names, and code examples. See `knowzcode/knowzcode_vaults.md`.
152
+ If MCP calls fail during vault writes (or MCP was unavailable at startup):
74
153
 
75
- 1. Read `knowzcode/knowzcode_vaults.md` to discover configured vaults
76
- 2. For each learning type, find the appropriate vault by type:
77
- - Pattern/Workaround/Performance vault with type `code`
78
- - Decision/Convention/Security/Integration vault with type `ecosystem`
79
- - Completion records → vault with type `finalizations`
80
- - If a single vault covers all types (or only a `research`/`domain`/`platform` alias vault exists), write everything there
81
- 3. Only write if the targeted vault is configured — skip gracefully if not
82
- 4. **Title prefixes**: `Pattern:`, `Decision:`, `Workaround:`, `Performance:`, `Security:`, `Convention:`
83
- 5. For enterprise audit trail: find vault matching type "enterprise", push completion record if configured and compliance is enabled
154
+ 1. **Queue locally**: Append each capture to `knowzcode/pending_captures.md`:
155
+ ```markdown
156
+ ### {timestamp} {title}
157
+ - **Intent**: Phase 3 capture
158
+ - **Category**: {Pattern|Decision|Workaround|Performance|Security|Convention|Integration|Scope|Completion}
159
+ - **Target Vault Type**: {code|ecosystem|enterprise|finalizations}
160
+ - **Source**: closer / WorkGroup {wgid}
161
+ - **Content**: {full formatted content that would have been written to the vault}
162
+ ```
163
+ 2. Log the MCP failure in the WorkGroup file: `"KnowzCode: MCP unavailable — queued {N} capture(s) to pending_captures.md"`
164
+ 3. Note in the finalization report that captures were queued locally
165
+ 4. The pending file can be flushed later via `/kc:learn --flush` or by a future scribe instance
84
166
 
85
- If MCP is not available, skip learning capture and audit trail all other finalization works normally.
167
+ **Never drop knowledge.** If MCP is down, queue it. All other finalization steps (specs, tracker, log, architecture, commit) proceed normally regardless of MCP status.
86
168
 
87
169
  ## Exit Expectations
88
170
 
package/commands/work.md CHANGED
@@ -313,7 +313,10 @@ When Parallel Teams mode is active, follow these 4 stages instead of spawning on
313
313
  3. **MCP Probe** — determine vault availability BEFORE spawning:
314
314
  a. Read `knowzcode/knowzcode_vaults.md` — partition entries into CONFIGURED (non-empty ID) and UNCREATED (empty ID)
315
315
  b. Call `list_vaults(includeStats=true)` **always** — regardless of whether any IDs exist in the file
316
- c. If `list_vaults()` fails → set `MCP_ACTIVE = false`, announce `**MCP Status: Not connected**`, skip to Step 4
316
+ c. If `list_vaults()` fails:
317
+ - Check if `knowzcode/knowzcode_vaults.md` has any CONFIGURED entries (non-empty ID)
318
+ - **If CONFIGURED entries exist**: Set `MCP_ACTIVE = true`, `VAULTS_CONFIGURED = true` — vault agents will verify connectivity independently via their Startup Verification. Announce `**MCP Status: Lead probe failed — delegating verification to vault agents**`. Proceed to Step 4.
319
+ - **If no CONFIGURED entries** (all empty IDs or no file): Set `MCP_ACTIVE = false`, announce `**MCP Status: Not connected**`, skip Group B spawn
317
320
  d. If `list_vaults()` succeeds AND UNCREATED list is non-empty → present the **Vault Creation Prompt**:
318
321
 
319
322
  ```markdown
@@ -610,6 +613,20 @@ When creating tasks, model the dependency chain with `addBlockedBy` and pre-assi
610
613
 
611
614
  When using Sequential Teams (`--sequential`) or Subagent Delegation, follow the traditional one-agent-per-phase flow. For each phase below: spawn the agent, create a task, wait for completion, present quality gate, shut down agent, proceed to next phase.
612
615
 
616
+ ### MCP Probe (Sequential/Subagent)
617
+
618
+ Determine vault availability before Phase 1A:
619
+
620
+ 1. Read `knowzcode/knowzcode_vaults.md` — check for CONFIGURED entries (non-empty ID)
621
+ 2. Attempt `list_vaults(includeStats=true)`
622
+ 3. If succeeds AND UNCREATED entries exist → present the **Vault Creation Prompt** (same as Parallel Teams Step 3d). Handle selection identically.
623
+ 4. After resolution, announce MCP status to the user:
624
+ - `list_vaults()` succeeded: `**MCP Status: Connected — N vault(s) available**`
625
+ - `list_vaults()` failed but configured vaults exist: `**MCP Status: Lead probe failed — closer will verify at Phase 3**`
626
+ - No configured vaults: `**MCP Status: Not configured**`
627
+
628
+ The closer agent independently verifies MCP at Phase 3 regardless of this result (see `agents/closer.md` — Startup MCP Verification). This probe is for the user announcement and vault creation opportunity only.
629
+
613
630
  ---
614
631
 
615
632
  ## Phase Prompt Reference
@@ -752,7 +769,7 @@ Two temporary agents that scan the codebase in parallel with the analyst, broadc
752
769
  **Dispatch**:
753
770
  - *Parallel Teams*: **Group B** — spawned at Stage 0 if `VAULTS_CONFIGURED = true`, no blockedBy. Persistent — stays alive through the entire workflow. Shut down after Phase 3 capture is complete.
754
771
  - *Sequential Teams*: Not applicable (knowz-scribe is Parallel Teams only).
755
- - *Subagent*: Not applicable — vault writes in subagent mode are handled inline by the lead.
772
+ - *Subagent*: Not applicable — vault writes in subagent mode are handled by the closer during Phase 3 finalization (see Direct Write Fallback in `agents/closer.md`).
756
773
 
757
774
  ---
758
775
 
@@ -1089,7 +1106,7 @@ If MCP is configured and knowz-scribe is active, after audit approval:
1089
1106
 
1090
1107
  **Agent**: `closer` | **Loop.md**: Section 3.5
1091
1108
 
1092
- **Spawn prompt**:
1109
+ **Spawn prompt (Parallel Teams)**:
1093
1110
  > You are the **closer** for WorkGroup `{wgid}`.
1094
1111
  > Read `agents/closer.md` for your full role definition.
1095
1112
  >
@@ -1101,13 +1118,28 @@ If MCP is configured and knowz-scribe is active, after audit approval:
1101
1118
  >
1102
1119
  > **Your Task**: #{task-id} — claim immediately (`TaskUpdate(status: "in_progress")`). Mark completed with summary when done.
1103
1120
  > **Conventions**: Update WorkGroup file with results (prefix entries with `KnowzCode:`). If blocked, report blocker and notify lead.
1104
- > **Vault writes**: If knowz-scribe is active, create a capture task (`TaskCreate("Scribe: Capture Phase 3")` → `TaskUpdate(owner: "knowz-scribe")`), then send `"Capture Phase 3: {wgid}. Your task: #{task-id}"` to delegate learning capture and audit trail writes. Do NOT call `create_knowledge` directly.
1121
+ > **Vault writes**: Knowz-scribe is active. Create a capture task (`TaskCreate("Scribe: Capture Phase 3")` → `TaskUpdate(owner: "knowz-scribe")`), then send `"Capture Phase 3: {wgid}. Your task: #{task-id}"` to delegate learning capture and audit trail writes. Do NOT call `create_knowledge` directly.
1105
1122
  > **Deliverable**: Atomic finalization — update specs to FINAL, update tracker, write log entry, update architecture if needed, delegate learning capture to knowz-scribe, and create final commit.
1106
1123
 
1124
+ **Spawn prompt (Sequential Teams / Subagent)**:
1125
+ > You are the **closer** for WorkGroup `{wgid}`.
1126
+ > Read `agents/closer.md` for your full role definition.
1127
+ >
1128
+ > **Goal**: {goal}
1129
+ > **Change Set**: {NodeIDs}
1130
+ > **Specs**: {list of spec file paths}
1131
+ > **Context files**: Read sections 1-2, 3.5, 6, and 7 of `knowzcode/knowzcode_loop.md` (skip other phases), `knowzcode/knowzcode_tracker.md`, `knowzcode/knowzcode_project.md`, `knowzcode/knowzcode_architecture.md`, `knowzcode/knowzcode_log.md`
1132
+ > **WorkGroup file**: `knowzcode/workgroups/{wgid}.md`
1133
+ >
1134
+ > **Your Task**: #{task-id} — claim immediately (`TaskUpdate(status: "in_progress")`). Mark completed with summary when done.
1135
+ > **Conventions**: Update WorkGroup file with results (prefix entries with `KnowzCode:`). If blocked, report blocker and notify lead.
1136
+ > **Vault writes**: No knowz-scribe — you own all vault writes. Follow the Direct Write Fallback in `agents/closer.md`.
1137
+ > **Deliverable**: Atomic finalization — update specs to FINAL, update tracker, write log entry, update architecture if needed, write learnings to vaults, and create final commit.
1138
+
1107
1139
  **Dispatch**:
1108
- - *Parallel Teams*: Spawned at Stage 3 (`addBlockedBy`: last audit/re-audit task). All other agents shut down before closer starts, except knowz-scout and knowz-scribe (stay alive for Phase 3 capture).
1109
- - *Sequential Teams*: Spawn teammate `closer`, create task `Phase 3: Finalize WorkGroup {wgid}`, wait for completion.
1110
- - *Subagent*: `Task(subagent_type="closer", description="Phase 3 finalization", prompt=<above>)`
1140
+ - *Parallel Teams*: Spawned at Stage 3 (`addBlockedBy`: last audit/re-audit task). Use the **Parallel Teams** spawn prompt. All other agents shut down before closer starts, except knowz-scout and knowz-scribe (stay alive for Phase 3 capture).
1141
+ - *Sequential Teams*: Spawn teammate `closer`, create task `Phase 3: Finalize WorkGroup {wgid}`, wait for completion. Use the **Sequential Teams / Subagent** spawn prompt.
1142
+ - *Subagent*: `Task(subagent_type="closer", description="Phase 3 finalization", prompt=<Sequential/Subagent spawn prompt above>)`
1111
1143
 
1112
1144
  ### Phase 3 Output
1113
1145
 
@@ -338,6 +338,9 @@ During finalization, scan the WorkGroup for insight-worthy patterns:
338
338
  | Workaround | "workaround", "limitation", "can't do X so" |
339
339
  | Performance | "optimized", "reduced from X to Y", "cache" |
340
340
  | Security | "vulnerability", "sanitize", "authentication fix" |
341
+ | Convention | "established convention", "team standard", "naming pattern", "agreed to always" |
342
+ | Integration | "API integration", "upstream API changed", "service dependency", "webhook" |
343
+ | Scope | "included because", "excluded because", "out of scope", "deferred to" |
341
344
 
342
345
  ### Auto-Capture Triggers
343
346
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knowzcode",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Platform-agnostic AI development methodology with TDD, quality gates, and structured workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,15 +1,15 @@
1
- {
2
- "name": "alias-resolver",
3
- "version": "0.3.6",
4
- "description": "Resolves friendly natural-language aliases to KnowzCode canonical values (phase, audit, plan, workgroup_type).",
5
- "parameters": [
6
- {"name": "domain", "type": "string", "required": true},
7
- {"name": "text", "type": "string", "required": false}
8
- ],
9
- "actions": [
10
- {
11
- "type": "python",
12
- "code": "domain = (inputs.get('domain') or '').strip().lower()\ntext = (inputs.get('text') or '').strip().lower()\nresult = {'value': None}\nphase_map = {\n '1a': {'agent': 'analyst', 'prompt': 'knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md'},\n '1b': {'agent': 'architect', 'prompt': 'knowzcode/prompts/[LOOP_1B]__Draft_Specs.md'},\n '2a': {'agent': 'builder', 'prompt': 'knowzcode/prompts/[LOOP_2A]__Implement_Change_Set.md'},\n '2b': {'agent': 'reviewer', 'prompt': 'knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md'},\n '3': {'agent': 'closer', 'prompt': 'knowzcode/prompts/[LOOP_3]__Finalize_And_Commit.md'}\n}\nphase_synonyms = {\n '1a': ['1a','loop 1a','analyze','analysis','impact','scope','change set','proposal'],\n '1b': ['1b','loop 1b','spec','specs','draft','design','blueprint'],\n '2a': ['2a','loop 2a','implement','implementation','build','code','develop'],\n '2b': ['2b','loop 2b','verify','verification','audit','test','qa','review'],\n '3': ['3','loop 3','final','finalize','finalization','commit','wrap up','close']\n}\naudit_map = {\n 'spec': 'knowzcode/prompts/Spec_Verification_Checkpoint.md',\n 'implementation': 'knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md',\n 'architecture': None,\n 'security': None,\n 'integration': None\n}\naudit_synonyms = {\n 'spec': ['spec','specs','specification','checkpoint'],\n 'implementation': ['implementation','code','verify implementation','loop 2b'],\n 'architecture': ['architecture','arch','diagram','flowchart','structure'],\n 'security': ['security','sec','owasp','vulnerability'],\n 'integration': ['integration','holistic','system','end to end','e2e']\n}\nworkgroup_map = {'feat': 'feat','feature': 'feat','fix': 'fix','bug': 'fix','bugfix': 'fix','refactor': 'refactor','cleanup': 'refactor','issue': 'issue','incident': 'issue'}\nif domain == 'phase':\n if not text:\n key = '1a'\n else:\n key = None\n for canonical, synonyms in phase_synonyms.items():\n if text == canonical or text in synonyms or any(s in text for s in synonyms):\n key = canonical\n break\n if key is None:\n for canonical in phase_map.keys():\n if text.replace(' ','') == canonical:\n key = canonical\n break\n if key is None:\n key = '1a'\n data = phase_map[key]\n result['value'] = key.upper()\n result['prompt'] = data['prompt']\n result['agent'] = data['agent']\n result['requires_workgroup'] = data['agent'] != 'analyst'\nelif domain == 'audit':\n if not text:\n key = 'spec'\n else:\n key = None\n for canonical, synonyms in audit_synonyms.items():\n if text == canonical or text in synonyms or any(s in text for s in synonyms):\n key = canonical\n break\n if key is None:\n key = 'spec'\n result['value'] = key\n result['prompt'] = audit_map.get(key)\n result['agent'] = 'reviewer'\nelif domain == 'plan':\n result['value'] = text or ''\n result['prompt'] = None\nelif domain == 'workgroup_type':\n if not text:\n value = 'feat'\n else:\n value = None\n for key, mapped in workgroup_map.items():\n if text == key or key in text:\n value = mapped\n break\n if value is None:\n value = workgroup_map.get(text, 'feat')\n result['value'] = value\nelse:\n result['value'] = text or ''\noutputs.update(result)\n"
13
- }
14
- ]
15
- }
1
+ {
2
+ "name": "alias-resolver",
3
+ "version": "0.3.7",
4
+ "description": "Resolves friendly natural-language aliases to KnowzCode canonical values (phase, audit, plan, workgroup_type).",
5
+ "parameters": [
6
+ {"name": "domain", "type": "string", "required": true},
7
+ {"name": "text", "type": "string", "required": false}
8
+ ],
9
+ "actions": [
10
+ {
11
+ "type": "python",
12
+ "code": "domain = (inputs.get('domain') or '').strip().lower()\ntext = (inputs.get('text') or '').strip().lower()\nresult = {'value': None}\nphase_map = {\n '1a': {'agent': 'analyst', 'prompt': 'knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md'},\n '1b': {'agent': 'architect', 'prompt': 'knowzcode/prompts/[LOOP_1B]__Draft_Specs.md'},\n '2a': {'agent': 'builder', 'prompt': 'knowzcode/prompts/[LOOP_2A]__Implement_Change_Set.md'},\n '2b': {'agent': 'reviewer', 'prompt': 'knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md'},\n '3': {'agent': 'closer', 'prompt': 'knowzcode/prompts/[LOOP_3]__Finalize_And_Commit.md'}\n}\nphase_synonyms = {\n '1a': ['1a','loop 1a','analyze','analysis','impact','scope','change set','proposal'],\n '1b': ['1b','loop 1b','spec','specs','draft','design','blueprint'],\n '2a': ['2a','loop 2a','implement','implementation','build','code','develop'],\n '2b': ['2b','loop 2b','verify','verification','audit','test','qa','review'],\n '3': ['3','loop 3','final','finalize','finalization','commit','wrap up','close']\n}\naudit_map = {\n 'spec': 'knowzcode/prompts/Spec_Verification_Checkpoint.md',\n 'implementation': 'knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md',\n 'architecture': None,\n 'security': None,\n 'integration': None\n}\naudit_synonyms = {\n 'spec': ['spec','specs','specification','checkpoint'],\n 'implementation': ['implementation','code','verify implementation','loop 2b'],\n 'architecture': ['architecture','arch','diagram','flowchart','structure'],\n 'security': ['security','sec','owasp','vulnerability'],\n 'integration': ['integration','holistic','system','end to end','e2e']\n}\nworkgroup_map = {'feat': 'feat','feature': 'feat','fix': 'fix','bug': 'fix','bugfix': 'fix','refactor': 'refactor','cleanup': 'refactor','issue': 'issue','incident': 'issue'}\nif domain == 'phase':\n if not text:\n key = '1a'\n else:\n key = None\n for canonical, synonyms in phase_synonyms.items():\n if text == canonical or text in synonyms or any(s in text for s in synonyms):\n key = canonical\n break\n if key is None:\n for canonical in phase_map.keys():\n if text.replace(' ','') == canonical:\n key = canonical\n break\n if key is None:\n key = '1a'\n data = phase_map[key]\n result['value'] = key.upper()\n result['prompt'] = data['prompt']\n result['agent'] = data['agent']\n result['requires_workgroup'] = data['agent'] != 'analyst'\nelif domain == 'audit':\n if not text:\n key = 'spec'\n else:\n key = None\n for canonical, synonyms in audit_synonyms.items():\n if text == canonical or text in synonyms or any(s in text for s in synonyms):\n key = canonical\n break\n if key is None:\n key = 'spec'\n result['value'] = key\n result['prompt'] = audit_map.get(key)\n result['agent'] = 'reviewer'\nelif domain == 'plan':\n result['value'] = text or ''\n result['prompt'] = None\nelif domain == 'workgroup_type':\n if not text:\n value = 'feat'\n else:\n value = None\n for key, mapped in workgroup_map.items():\n if text == key or key in text:\n value = mapped\n break\n if value is None:\n value = workgroup_map.get(text, 'feat')\n result['value'] = value\nelse:\n result['value'] = text or ''\noutputs.update(result)\n"
13
+ }
14
+ ]
15
+ }
@@ -1,12 +1,12 @@
1
- {
2
- "name": "architecture-diff",
3
- "version": "0.3.6",
4
- "description": "Highlights differences between specs and the Mermaid flowchart in knowzcode_architecture.md.",
5
- "parameters": [],
6
- "actions": [
7
- {
8
- "type": "python",
9
- "code": "from pathlib import Path\nimport re\narch = Path('knowzcode/knowzcode_architecture.md').read_text()\nspec_paths = list(Path('knowzcode/specs').glob('*.md'))\nnode_ids = {p.stem for p in spec_paths}\nmermaid_nodes = set(re.findall(r'([A-Z]{2,}_[A-Za-z0-9]+)', arch))\nmissing_in_arch = sorted(node_ids - mermaid_nodes)\nmissing_in_specs = sorted(mermaid_nodes - node_ids)\noutputs['missing_in_architecture'] = missing_in_arch\noutputs['missing_specs'] = missing_in_specs\n"
10
- }
11
- ]
12
- }
1
+ {
2
+ "name": "architecture-diff",
3
+ "version": "0.3.7",
4
+ "description": "Highlights differences between specs and the Mermaid flowchart in knowzcode_architecture.md.",
5
+ "parameters": [],
6
+ "actions": [
7
+ {
8
+ "type": "python",
9
+ "code": "from pathlib import Path\nimport re\narch = Path('knowzcode/knowzcode_architecture.md').read_text()\nspec_paths = list(Path('knowzcode/specs').glob('*.md'))\nnode_ids = {p.stem for p in spec_paths}\nmermaid_nodes = set(re.findall(r'([A-Z]{2,}_[A-Za-z0-9]+)', arch))\nmissing_in_arch = sorted(node_ids - mermaid_nodes)\nmissing_in_specs = sorted(mermaid_nodes - node_ids)\noutputs['missing_in_architecture'] = missing_in_arch\noutputs['missing_specs'] = missing_in_specs\n"
10
+ }
11
+ ]
12
+ }
@@ -1,14 +1,14 @@
1
- {
2
- "name": "check-installation-status",
3
- "version": "0.3.6",
4
- "description": "Checks if KnowzCode is initialized in the current project and reports current status",
5
-
6
- "parameters": [],
7
-
8
- "actions": [
9
- {
10
- "type": "python",
11
- "code": "from pathlib import Path\n\nknowzcode_dir = Path('knowzcode')\n\nif not knowzcode_dir.exists():\n outputs['initialized'] = False\n outputs['commands_count'] = 0\n outputs['skills_count'] = 0\n outputs['agents_count'] = 0\n outputs['has_required_files'] = False\n exit()\n\n# Check required files exist\nrequired_files = [\n 'knowzcode_loop.md',\n 'knowzcode_tracker.md',\n 'knowzcode_project.md',\n 'knowzcode_architecture.md'\n]\n\nmissing_files = [f for f in required_files if not (knowzcode_dir / f).exists()]\nhas_required_files = len(missing_files) == 0\n\n# Count components from various possible locations\n\n# Check .claude directory\nclaude_dir = Path('.claude')\ncommands_count = 0\nskills_count = 0\nagents_count = 0\n\nif claude_dir.exists():\n if (claude_dir / 'commands').exists():\n commands_count = len(list((claude_dir / 'commands').glob('*.md')))\n if (claude_dir / 'skills').exists():\n skills_count = len(list((claude_dir / 'skills').glob('*.json')))\n if (claude_dir / 'agents').exists():\n agents_count = len(list((claude_dir / 'agents').glob('*.md')))\n if (claude_dir / 'subagents').exists():\n agents_count += len(list((claude_dir / 'subagents').glob('*.yaml')))\n\n# Also check top-level directories (plugin installation)\nif Path('commands').exists():\n commands_count = max(commands_count, len(list(Path('commands').glob('*.md'))))\nif Path('skills').exists():\n skills_count = max(skills_count, len(list(Path('skills').glob('*.json'))))\nif Path('agents').exists():\n agents_count = max(agents_count, len(list(Path('agents').glob('*.md'))))\n\noutputs['initialized'] = has_required_files\noutputs['commands_count'] = commands_count\noutputs['skills_count'] = skills_count\noutputs['agents_count'] = agents_count\noutputs['has_required_files'] = has_required_files\noutputs['missing_files'] = missing_files if missing_files else None"
12
- }
13
- ]
14
- }
1
+ {
2
+ "name": "check-installation-status",
3
+ "version": "0.3.7",
4
+ "description": "Checks if KnowzCode is initialized in the current project and reports current status",
5
+
6
+ "parameters": [],
7
+
8
+ "actions": [
9
+ {
10
+ "type": "python",
11
+ "code": "from pathlib import Path\n\nknowzcode_dir = Path('knowzcode')\n\nif not knowzcode_dir.exists():\n outputs['initialized'] = False\n outputs['commands_count'] = 0\n outputs['skills_count'] = 0\n outputs['agents_count'] = 0\n outputs['has_required_files'] = False\n exit()\n\n# Check required files exist\nrequired_files = [\n 'knowzcode_loop.md',\n 'knowzcode_tracker.md',\n 'knowzcode_project.md',\n 'knowzcode_architecture.md'\n]\n\nmissing_files = [f for f in required_files if not (knowzcode_dir / f).exists()]\nhas_required_files = len(missing_files) == 0\n\n# Count components from various possible locations\n\n# Check .claude directory\nclaude_dir = Path('.claude')\ncommands_count = 0\nskills_count = 0\nagents_count = 0\n\nif claude_dir.exists():\n if (claude_dir / 'commands').exists():\n commands_count = len(list((claude_dir / 'commands').glob('*.md')))\n if (claude_dir / 'skills').exists():\n skills_count = len(list((claude_dir / 'skills').glob('*.json')))\n if (claude_dir / 'agents').exists():\n agents_count = len(list((claude_dir / 'agents').glob('*.md')))\n if (claude_dir / 'subagents').exists():\n agents_count += len(list((claude_dir / 'subagents').glob('*.yaml')))\n\n# Also check top-level directories (plugin installation)\nif Path('commands').exists():\n commands_count = max(commands_count, len(list(Path('commands').glob('*.md'))))\nif Path('skills').exists():\n skills_count = max(skills_count, len(list(Path('skills').glob('*.json'))))\nif Path('agents').exists():\n agents_count = max(agents_count, len(list(Path('agents').glob('*.md'))))\n\noutputs['initialized'] = has_required_files\noutputs['commands_count'] = commands_count\noutputs['skills_count'] = skills_count\noutputs['agents_count'] = agents_count\noutputs['has_required_files'] = has_required_files\noutputs['missing_files'] = missing_files if missing_files else None"
12
+ }
13
+ ]
14
+ }
@@ -1,12 +1,12 @@
1
- {
2
- "name": "environment-guard",
3
- "version": "0.3.6",
4
- "description": "Verifies that knowzcode/environment_context.md no longer contains placeholder brackets.",
5
- "parameters": [],
6
- "actions": [
7
- {
8
- "type": "python",
9
- "code": "from pathlib import Path\ntext = Path('knowzcode/environment_context.md').read_text()\nplaceholders = [line.strip() for line in text.splitlines() if '[' in line and ']' in line]\nif placeholders:\n raise ValueError('environment_context.md still contains placeholders: ' + ', '.join(placeholders[:5]))\n"
10
- }
11
- ]
12
- }
1
+ {
2
+ "name": "environment-guard",
3
+ "version": "0.3.7",
4
+ "description": "Verifies that knowzcode/environment_context.md no longer contains placeholder brackets.",
5
+ "parameters": [],
6
+ "actions": [
7
+ {
8
+ "type": "python",
9
+ "code": "from pathlib import Path\ntext = Path('knowzcode/environment_context.md').read_text()\nplaceholders = [line.strip() for line in text.splitlines() if '[' in line and ']' in line]\nif placeholders:\n raise ValueError('environment_context.md still contains placeholders: ' + ', '.join(placeholders[:5]))\n"
10
+ }
11
+ ]
12
+ }
@@ -1,25 +1,25 @@
1
- {
2
- "name": "generate-workgroup-id",
3
- "version": "0.3.6",
4
- "description": "Generates a WorkGroupID following the KnowzCode convention [type]-[slug]-YYYYMMDD-HHMMSS. The slug is a 2-4 word descriptor extracted from the goal.",
5
- "parameters": [
6
- {
7
- "name": "type",
8
- "type": "string",
9
- "required": true,
10
- "description": "One of feat, fix, refactor, issue"
11
- },
12
- {
13
- "name": "slug",
14
- "type": "string",
15
- "required": false,
16
- "description": "A 2-4 word kebab-case descriptor (e.g., 'auth-jwt', 'dark-mode'). If not provided, generates ID without slug."
17
- }
18
- ],
19
- "actions": [
20
- {
21
- "type": "python",
22
- "code": "import datetime\nimport re\nallowed = {'feat', 'fix', 'refactor', 'issue'}\nwg_type = inputs['type']\nif wg_type not in allowed:\n raise ValueError(f'Invalid type {wg_type!r}, expected one of {sorted(allowed)}')\ntimestamp = datetime.datetime.utcnow().strftime('%Y%m%d-%H%M%S')\nslug = inputs.get('slug', '')\nif slug:\n # Sanitize slug: lowercase, replace spaces with hyphens, remove non-alphanumeric\n slug = re.sub(r'[^a-z0-9-]', '', slug.lower().replace(' ', '-'))\n slug = re.sub(r'-+', '-', slug).strip('-')[:25] # Max 25 chars\n outputs['workgroup_id'] = f'{wg_type}-{slug}-{timestamp}'\nelse:\n outputs['workgroup_id'] = f'{wg_type}-{timestamp}'"
23
- }
24
- ]
25
- }
1
+ {
2
+ "name": "generate-workgroup-id",
3
+ "version": "0.3.7",
4
+ "description": "Generates a WorkGroupID following the KnowzCode convention [type]-[slug]-YYYYMMDD-HHMMSS. The slug is a 2-4 word descriptor extracted from the goal.",
5
+ "parameters": [
6
+ {
7
+ "name": "type",
8
+ "type": "string",
9
+ "required": true,
10
+ "description": "One of feat, fix, refactor, issue"
11
+ },
12
+ {
13
+ "name": "slug",
14
+ "type": "string",
15
+ "required": false,
16
+ "description": "A 2-4 word kebab-case descriptor (e.g., 'auth-jwt', 'dark-mode'). If not provided, generates ID without slug."
17
+ }
18
+ ],
19
+ "actions": [
20
+ {
21
+ "type": "python",
22
+ "code": "import datetime\nimport re\nallowed = {'feat', 'fix', 'refactor', 'issue'}\nwg_type = inputs['type']\nif wg_type not in allowed:\n raise ValueError(f'Invalid type {wg_type!r}, expected one of {sorted(allowed)}')\ntimestamp = datetime.datetime.utcnow().strftime('%Y%m%d-%H%M%S')\nslug = inputs.get('slug', '')\nif slug:\n # Sanitize slug: lowercase, replace spaces with hyphens, remove non-alphanumeric\n slug = re.sub(r'[^a-z0-9-]', '', slug.lower().replace(' ', '-'))\n slug = re.sub(r'-+', '-', slug).strip('-')[:25] # Max 25 chars\n outputs['workgroup_id'] = f'{wg_type}-{slug}-{timestamp}'\nelse:\n outputs['workgroup_id'] = f'{wg_type}-{timestamp}'"
23
+ }
24
+ ]
25
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "name": "install-knowzcode",
3
- "version": "0.3.6",
4
- "description": "Initializes KnowzCode directory structure and required files in the current project",
5
-
6
- "parameters": [
7
- {
8
- "name": "force",
9
- "type": "boolean",
10
- "required": false,
11
- "description": "Force reinstall even if knowzcode/ exists"
12
- }
13
- ],
14
-
15
- "actions": [
16
- {
17
- "type": "python",
18
- "code": "import os\nimport json\nfrom pathlib import Path\nfrom datetime import datetime\n\n# Configuration\ntarget_dir = Path('knowzcode')\nforce = inputs.get('force', False)\n\n# Check if target exists\nif target_dir.exists() and not force:\n outputs['success'] = False\n outputs['error'] = 'knowzcode/ already exists. Use force=true to reinitialize'\n outputs['already_installed'] = True\n exit()\n\n# Create directory structure\ntarget_dir.mkdir(exist_ok=True)\n(target_dir / 'specs').mkdir(exist_ok=True)\n(target_dir / 'workgroups').mkdir(exist_ok=True)\n(target_dir / 'prompts').mkdir(exist_ok=True)\n(target_dir / 'planning').mkdir(exist_ok=True)\n\n# Create .gitignore if it doesn't exist\ngitignore_path = target_dir / '.gitignore'\nif not gitignore_path.exists():\n gitignore_content = '''# KnowzCode gitignore - protects environment-specific files\n\n# Environment-specific (contains local paths, commands)\nenvironment_context.md\n\n# Session-specific work (can be regenerated)\nworkgroups/\n\n# Personal notes and scratch files\n*.local.md\n.scratch/\n'''\n with open(gitignore_path, 'w') as f:\n f.write(gitignore_content)\n\n# Note: The actual template files (knowzcode_loop.md, etc.) should be copied\n# from the plugin template directory or created by the /kc:init command.\n# This skill creates the structure; the command copies the content.\n\noutputs['success'] = True\noutputs['target_dir'] = str(target_dir)\noutputs['directories_created'] = ['specs', 'workgroups', 'prompts', 'planning']\noutputs['gitignore_created'] = not gitignore_path.exists()\noutputs['message'] = f'KnowzCode directory structure created at {target_dir}/'"
19
- }
20
- ]
21
- }
1
+ {
2
+ "name": "install-knowzcode",
3
+ "version": "0.3.7",
4
+ "description": "Initializes KnowzCode directory structure and required files in the current project",
5
+
6
+ "parameters": [
7
+ {
8
+ "name": "force",
9
+ "type": "boolean",
10
+ "required": false,
11
+ "description": "Force reinstall even if knowzcode/ exists"
12
+ }
13
+ ],
14
+
15
+ "actions": [
16
+ {
17
+ "type": "python",
18
+ "code": "import os\nimport json\nfrom pathlib import Path\nfrom datetime import datetime\n\n# Configuration\ntarget_dir = Path('knowzcode')\nforce = inputs.get('force', False)\n\n# Check if target exists\nif target_dir.exists() and not force:\n outputs['success'] = False\n outputs['error'] = 'knowzcode/ already exists. Use force=true to reinitialize'\n outputs['already_installed'] = True\n exit()\n\n# Create directory structure\ntarget_dir.mkdir(exist_ok=True)\n(target_dir / 'specs').mkdir(exist_ok=True)\n(target_dir / 'workgroups').mkdir(exist_ok=True)\n(target_dir / 'prompts').mkdir(exist_ok=True)\n(target_dir / 'planning').mkdir(exist_ok=True)\n\n# Create .gitignore if it doesn't exist\ngitignore_path = target_dir / '.gitignore'\nif not gitignore_path.exists():\n gitignore_content = '''# KnowzCode gitignore - protects environment-specific files\n\n# Environment-specific (contains local paths, commands)\nenvironment_context.md\n\n# Session-specific work (can be regenerated)\nworkgroups/\n\n# Personal notes and scratch files\n*.local.md\n.scratch/\n'''\n with open(gitignore_path, 'w') as f:\n f.write(gitignore_content)\n\n# Note: The actual template files (knowzcode_loop.md, etc.) should be copied\n# from the plugin template directory or created by the /kc:init command.\n# This skill creates the structure; the command copies the content.\n\noutputs['success'] = True\noutputs['target_dir'] = str(target_dir)\noutputs['directories_created'] = ['specs', 'workgroups', 'prompts', 'planning']\noutputs['gitignore_created'] = not gitignore_path.exists()\noutputs['message'] = f'KnowzCode directory structure created at {target_dir}/'"
19
+ }
20
+ ]
21
+ }
@@ -1,18 +1,18 @@
1
- {
2
- "name": "load-core-context",
3
- "version": "0.3.6",
4
- "description": "Loads the KnowzCode project overview, architecture, tracker, log header, and automation manifest for downstream steps.",
5
- "parameters": [],
6
- "actions": [
7
- {
8
- "type": "read_file",
9
- "paths": [
10
- "knowzcode/knowzcode_project.md",
11
- "knowzcode/knowzcode_architecture.md",
12
- "knowzcode/knowzcode_tracker.md",
13
- "knowzcode/knowzcode_log.md",
14
- "knowzcode/automation_manifest.md"
15
- ]
16
- }
17
- ]
18
- }
1
+ {
2
+ "name": "load-core-context",
3
+ "version": "0.3.7",
4
+ "description": "Loads the KnowzCode project overview, architecture, tracker, log header, and automation manifest for downstream steps.",
5
+ "parameters": [],
6
+ "actions": [
7
+ {
8
+ "type": "read_file",
9
+ "paths": [
10
+ "knowzcode/knowzcode_project.md",
11
+ "knowzcode/knowzcode_architecture.md",
12
+ "knowzcode/knowzcode_tracker.md",
13
+ "knowzcode/knowzcode_log.md",
14
+ "knowzcode/automation_manifest.md"
15
+ ]
16
+ }
17
+ ]
18
+ }
@@ -1,15 +1,15 @@
1
- {
2
- "name": "log-entry-builder",
3
- "version": "0.3.6",
4
- "description": "Appends structured entries to knowzcode/knowzcode_log.md with KnowzCode formatting.",
5
- "parameters": [
6
- {"name": "entry_type", "type": "string", "required": true},
7
- {"name": "content", "type": "string", "required": true}
8
- ],
9
- "actions": [
10
- {
11
- "type": "python",
12
- "code": "from pathlib import Path\nentry_type = inputs['entry_type']\ncontent = inputs['content'].strip()\npath = Path('knowzcode/knowzcode_log.md')\ntext = path.read_text()\nmarker = '---\n**[NEWEST ENTRIES APPEAR HERE - DO NOT REMOVE THIS MARKER]**'\nidx = text.index(marker) + len(marker)\nnew_entry = f"\n---\n**Type:** {entry_type}\n{content}\n"\nupdated = text[:idx] + new_entry + text[idx:]\npath.write_text(updated)"
13
- }
14
- ]
15
- }
1
+ {
2
+ "name": "log-entry-builder",
3
+ "version": "0.3.7",
4
+ "description": "Appends structured entries to knowzcode/knowzcode_log.md with KnowzCode formatting.",
5
+ "parameters": [
6
+ {"name": "entry_type", "type": "string", "required": true},
7
+ {"name": "content", "type": "string", "required": true}
8
+ ],
9
+ "actions": [
10
+ {
11
+ "type": "python",
12
+ "code": "from pathlib import Path\nentry_type = inputs['entry_type']\ncontent = inputs['content'].strip()\npath = Path('knowzcode/knowzcode_log.md')\ntext = path.read_text()\nmarker = '---\n**[NEWEST ENTRIES APPEAR HERE - DO NOT REMOVE THIS MARKER]**'\nidx = text.index(marker) + len(marker)\nnew_entry = f"\n---\n**Type:** {entry_type}\n{content}\n"\nupdated = text[:idx] + new_entry + text[idx:]\npath.write_text(updated)"
13
+ }
14
+ ]
15
+ }
@@ -1,14 +1,14 @@
1
- {
2
- "name": "spec-quality-check",
3
- "version": "0.3.6",
4
- "description": "Validates KnowzCode spec files for mandatory sections. Supports new 4-section format and legacy numbered format.",
5
- "parameters": [
6
- {"name": "node_id", "type": "string", "required": true}
7
- ],
8
- "actions": [
9
- {
10
- "type": "python",
11
- "code": "from pathlib import Path\nimport re\nnode_id = inputs['node_id']\npath = Path('knowzcode/specs') / f'{node_id}.md'\nif not path.exists():\n raise FileNotFoundError(f'Spec for {node_id} not found')\ntext = path.read_text()\n\n# Check new format first\nnew_sections = ['## Rules & Decisions', '## Interfaces', '## Verification Criteria', '## Debt & Gaps']\nlegacy_sections = ['## 1. Purpose', '## 2. Dependencies & Triggers', '## 3. Interfaces', '## 4. Core Logic & Processing Steps', '## 5. Data Structures', '## 6. ARC Verification Criteria', '## 7. Technical Debt']\n\nnew_missing = [s for s in new_sections if s not in text]\nlegacy_missing = [s for s in legacy_sections if s not in text]\n\n# Use whichever format has fewer missing sections\nif len(new_missing) <= len(legacy_missing):\n outputs['format'] = 'v2'\n outputs['missing_sections'] = new_missing\nelse:\n outputs['format'] = 'legacy'\n outputs['missing_sections'] = legacy_missing\n outputs['deprecation_warning'] = 'Spec uses old numbered format. New specs should use: Rules & Decisions, Interfaces, Verification Criteria, Debt & Gaps'\n"
12
- }
13
- ]
14
- }
1
+ {
2
+ "name": "spec-quality-check",
3
+ "version": "0.3.7",
4
+ "description": "Validates KnowzCode spec files for mandatory sections. Supports new 4-section format and legacy numbered format.",
5
+ "parameters": [
6
+ {"name": "node_id", "type": "string", "required": true}
7
+ ],
8
+ "actions": [
9
+ {
10
+ "type": "python",
11
+ "code": "from pathlib import Path\nimport re\nnode_id = inputs['node_id']\npath = Path('knowzcode/specs') / f'{node_id}.md'\nif not path.exists():\n raise FileNotFoundError(f'Spec for {node_id} not found')\ntext = path.read_text()\n\n# Check new format first\nnew_sections = ['## Rules & Decisions', '## Interfaces', '## Verification Criteria', '## Debt & Gaps']\nlegacy_sections = ['## 1. Purpose', '## 2. Dependencies & Triggers', '## 3. Interfaces', '## 4. Core Logic & Processing Steps', '## 5. Data Structures', '## 6. ARC Verification Criteria', '## 7. Technical Debt']\n\nnew_missing = [s for s in new_sections if s not in text]\nlegacy_missing = [s for s in legacy_sections if s not in text]\n\n# Use whichever format has fewer missing sections\nif len(new_missing) <= len(legacy_missing):\n outputs['format'] = 'v2'\n outputs['missing_sections'] = new_missing\nelse:\n outputs['format'] = 'legacy'\n outputs['missing_sections'] = legacy_missing\n outputs['deprecation_warning'] = 'Spec uses old numbered format. New specs should use: Rules & Decisions, Interfaces, Verification Criteria, Debt & Gaps'\n"
12
+ }
13
+ ]
14
+ }
@@ -1,15 +1,15 @@
1
- {
2
- "name": "spec-template",
3
- "version": "0.3.6",
4
- "description": "Seeds or repairs KnowzCode spec files with the lean 4-section template.",
5
- "parameters": [
6
- {"name": "node_id", "type": "string", "required": true},
7
- {"name": "label", "type": "string", "required": true}
8
- ],
9
- "actions": [
10
- {
11
- "type": "python",
12
- "code": "from pathlib import Path\nfrom datetime import datetime\nnode_id = inputs['node_id']\nlabel = inputs['label']\npath = Path('knowzcode/specs') / f'{node_id}.md'\nnow = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%SZ')\ntemplate_lines = [\n f'# {node_id}: {label}',\n '',\n f'**Updated:** {now}',\n '**Status:** Draft',\n '',\n '## Rules & Decisions',\n '- [Key architectural decisions, business rules, constraints, and purpose]',\n '',\n '## Interfaces',\n '- [Public contracts: inputs, outputs, API signatures, dependencies, events]',\n '',\n '## Verification Criteria',\n '- VERIFY: [testable assertion 1]',\n '- VERIFY: [testable assertion 2]',\n '',\n '## Debt & Gaps',\n '- [Known limitations and future work, or \"None\"]',\n ''\n]\ntemplate = '\\n'.join(template_lines)\nif not path.exists():\n path.write_text(template + '\\n')\n outputs['created'] = True\nelse:\n text = path.read_text()\n new_sections = ['## Rules & Decisions', '## Interfaces', '## Verification Criteria', '## Debt & Gaps']\n if all(section in text for section in new_sections):\n outputs['created'] = False\n else:\n merged = text.strip() + '\\n\\n<!-- Template sections restored -->\\n' + template + '\\n'\n path.write_text(merged)\n outputs['created'] = False"
13
- }
14
- ]
15
- }
1
+ {
2
+ "name": "spec-template",
3
+ "version": "0.3.7",
4
+ "description": "Seeds or repairs KnowzCode spec files with the lean 4-section template.",
5
+ "parameters": [
6
+ {"name": "node_id", "type": "string", "required": true},
7
+ {"name": "label", "type": "string", "required": true}
8
+ ],
9
+ "actions": [
10
+ {
11
+ "type": "python",
12
+ "code": "from pathlib import Path\nfrom datetime import datetime\nnode_id = inputs['node_id']\nlabel = inputs['label']\npath = Path('knowzcode/specs') / f'{node_id}.md'\nnow = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%SZ')\ntemplate_lines = [\n f'# {node_id}: {label}',\n '',\n f'**Updated:** {now}',\n '**Status:** Draft',\n '',\n '## Rules & Decisions',\n '- [Key architectural decisions, business rules, constraints, and purpose]',\n '',\n '## Interfaces',\n '- [Public contracts: inputs, outputs, API signatures, dependencies, events]',\n '',\n '## Verification Criteria',\n '- VERIFY: [testable assertion 1]',\n '- VERIFY: [testable assertion 2]',\n '',\n '## Debt & Gaps',\n '- [Known limitations and future work, or \"None\"]',\n ''\n]\ntemplate = '\\n'.join(template_lines)\nif not path.exists():\n path.write_text(template + '\\n')\n outputs['created'] = True\nelse:\n text = path.read_text()\n new_sections = ['## Rules & Decisions', '## Interfaces', '## Verification Criteria', '## Debt & Gaps']\n if all(section in text for section in new_sections):\n outputs['created'] = False\n else:\n merged = text.strip() + '\\n\\n<!-- Template sections restored -->\\n' + template + '\\n'\n path.write_text(merged)\n outputs['created'] = False"
13
+ }
14
+ ]
15
+ }
@@ -1,25 +1,25 @@
1
- {
2
- "name": "spec-validator",
3
- "version": "0.3.6",
4
- "description": "Validates NodeID specification completeness and quality. Supports new 4-section format (preferred) and legacy numbered sections (deprecated).",
5
- "parameters": [
6
- {
7
- "name": "spec_path",
8
- "type": "string",
9
- "required": true,
10
- "description": "Absolute path to the spec file to validate"
11
- },
12
- {
13
- "name": "node_id",
14
- "type": "string",
15
- "required": true,
16
- "description": "NodeID being validated (for error reporting)"
17
- }
18
- ],
19
- "actions": [
20
- {
21
- "type": "python",
22
- "code": "from pathlib import Path\nimport re\n\nspec_path = Path(inputs['spec_path'])\nnode_id = inputs['node_id']\n\nif not spec_path.exists():\n outputs['valid'] = False\n outputs['errors'] = [f'Spec file does not exist: {spec_path}']\n outputs['warnings'] = []\n outputs['score'] = 0\n outputs['node_id'] = node_id\n outputs['format'] = 'missing'\n exit()\n\ncontent = spec_path.read_text()\n\nerrors = []\nwarnings = []\nscore = 100\n\n# Detect format: new 4-section or legacy numbered\nnew_sections = {\n 'rules': r'##\\s*Rules\\s*(&|and)\\s*Decisions',\n 'interfaces': r'##\\s*Interfaces',\n 'verification': r'##\\s*Verification\\s*Criteria',\n 'debt': r'##\\s*Debt\\s*(&|and)\\s*Gaps'\n}\nlegacy_sections = {\n 'purpose': r'##\\s*1\\.\\s*Purpose',\n 'deps': r'##\\s*2\\.\\s*Dependencies',\n 'interfaces': r'##\\s*3\\.\\s*Interfaces',\n 'logic': r'##\\s*4\\.\\s*Core\\s*Logic',\n 'arc': r'##\\s*(6\\.\\s*Error\\s*Handling|7\\.\\s*ARC\\s*Verification)'\n}\n\nnew_count = sum(1 for p in new_sections.values() if re.search(p, content, re.IGNORECASE))\nlegacy_count = sum(1 for p in legacy_sections.values() if re.search(p, content, re.IGNORECASE))\n\nif new_count >= legacy_count:\n spec_format = 'v2'\n # Validate new 4-section format\n required = [\n (new_sections['rules'], 'Missing \"Rules & Decisions\" section'),\n (new_sections['interfaces'], 'Missing \"Interfaces\" section'),\n (new_sections['verification'], 'Missing \"Verification Criteria\" section'),\n (new_sections['debt'], 'Missing \"Debt & Gaps\" section')\n ]\n for pattern, error_msg in required:\n if not re.search(pattern, content, re.IGNORECASE):\n errors.append(error_msg)\n score -= 15\n \n # Check VERIFY: statements (minimum 2)\n verify_section = re.search(r'##\\s*Verification\\s*Criteria(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if verify_section:\n verify_content = verify_section.group(1)\n verify_count = len(re.findall(r'VERIFY:', verify_content))\n if verify_count < 2:\n errors.append(f'Only {verify_count} VERIFY: statements found (minimum 2 required)')\n score -= 15\n else:\n errors.append('Verification Criteria section is empty or malformed')\n score -= 20\n \n # Check Rules has content\n rules_section = re.search(r'##\\s*Rules\\s*(&|and)\\s*Decisions(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if rules_section:\n rules_content = rules_section.group(2).strip()\n if len(rules_content) < 20 or re.search(r'^\\s*-\\s*\\[', rules_content):\n warnings.append('Rules & Decisions section appears to have only placeholder content')\n score -= 10\n \n # Check Interfaces has content\n iface_section = re.search(r'##\\s*Interfaces(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if iface_section:\n iface_content = iface_section.group(1).strip()\n if len(iface_content) < 20 or re.search(r'^\\s*-\\s*\\[', iface_content):\n warnings.append('Interfaces section appears to have only placeholder content')\n score -= 10\nelse:\n spec_format = 'legacy'\n warnings.append('DEPRECATED: Spec uses old numbered format. Will be migrated when next touched during finalization.')\n \n # Validate legacy format\n required = [\n (legacy_sections['purpose'], 'Missing \"1. Purpose\" section'),\n (legacy_sections['deps'], 'Missing \"2. Dependencies\" section'),\n (legacy_sections['interfaces'], 'Missing \"3. Interfaces\" section'),\n (legacy_sections['logic'], 'Missing \"4. Core Logic\" section'),\n (legacy_sections['arc'], 'Missing ARC/Error Handling section')\n ]\n for pattern, error_msg in required:\n if not re.search(pattern, content, re.IGNORECASE):\n errors.append(error_msg)\n score -= 15\n \n # Check ARC criteria (legacy format)\n arc_section = re.search(r'##\\s*7\\.\\s*ARC\\s*Verification\\s*Criteria(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if arc_section:\n arc_content = arc_section.group(1)\n criteria_count = len(re.findall(r'ARC_[A-Z]+_\\d+:', arc_content))\n if criteria_count < 3:\n errors.append(f'Only {criteria_count} testable ARC criteria found (minimum 3 required)')\n score -= 15\n else:\n errors.append('ARC Verification Criteria section is empty or malformed')\n score -= 20\n\n# Check for placeholder text (both formats)\nplaceholder_patterns = [\n (r'\\[Agent:', 'Contains [Agent: placeholder'),\n (r'\\[TODO', 'Contains [TODO] placeholder'),\n (r'\\[FILL', 'Contains [FILL] placeholder'),\n (r'\\[TBD\\]', 'Contains [TBD] placeholder'),\n (r'\\[INSERT', 'Contains [INSERT] placeholder')\n]\nfor pattern, msg in placeholder_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n errors.append(msg)\n score -= 20\n break\n\n# Calculate final validation\noutputs['valid'] = (score >= 70 and len(errors) == 0)\noutputs['score'] = max(0, score)\noutputs['errors'] = errors\noutputs['warnings'] = warnings\noutputs['node_id'] = node_id\noutputs['spec_path'] = str(spec_path)\noutputs['format'] = spec_format"
23
- }
24
- ]
25
- }
1
+ {
2
+ "name": "spec-validator",
3
+ "version": "0.3.7",
4
+ "description": "Validates NodeID specification completeness and quality. Supports new 4-section format (preferred) and legacy numbered sections (deprecated).",
5
+ "parameters": [
6
+ {
7
+ "name": "spec_path",
8
+ "type": "string",
9
+ "required": true,
10
+ "description": "Absolute path to the spec file to validate"
11
+ },
12
+ {
13
+ "name": "node_id",
14
+ "type": "string",
15
+ "required": true,
16
+ "description": "NodeID being validated (for error reporting)"
17
+ }
18
+ ],
19
+ "actions": [
20
+ {
21
+ "type": "python",
22
+ "code": "from pathlib import Path\nimport re\n\nspec_path = Path(inputs['spec_path'])\nnode_id = inputs['node_id']\n\nif not spec_path.exists():\n outputs['valid'] = False\n outputs['errors'] = [f'Spec file does not exist: {spec_path}']\n outputs['warnings'] = []\n outputs['score'] = 0\n outputs['node_id'] = node_id\n outputs['format'] = 'missing'\n exit()\n\ncontent = spec_path.read_text()\n\nerrors = []\nwarnings = []\nscore = 100\n\n# Detect format: new 4-section or legacy numbered\nnew_sections = {\n 'rules': r'##\\s*Rules\\s*(&|and)\\s*Decisions',\n 'interfaces': r'##\\s*Interfaces',\n 'verification': r'##\\s*Verification\\s*Criteria',\n 'debt': r'##\\s*Debt\\s*(&|and)\\s*Gaps'\n}\nlegacy_sections = {\n 'purpose': r'##\\s*1\\.\\s*Purpose',\n 'deps': r'##\\s*2\\.\\s*Dependencies',\n 'interfaces': r'##\\s*3\\.\\s*Interfaces',\n 'logic': r'##\\s*4\\.\\s*Core\\s*Logic',\n 'arc': r'##\\s*(6\\.\\s*Error\\s*Handling|7\\.\\s*ARC\\s*Verification)'\n}\n\nnew_count = sum(1 for p in new_sections.values() if re.search(p, content, re.IGNORECASE))\nlegacy_count = sum(1 for p in legacy_sections.values() if re.search(p, content, re.IGNORECASE))\n\nif new_count >= legacy_count:\n spec_format = 'v2'\n # Validate new 4-section format\n required = [\n (new_sections['rules'], 'Missing \"Rules & Decisions\" section'),\n (new_sections['interfaces'], 'Missing \"Interfaces\" section'),\n (new_sections['verification'], 'Missing \"Verification Criteria\" section'),\n (new_sections['debt'], 'Missing \"Debt & Gaps\" section')\n ]\n for pattern, error_msg in required:\n if not re.search(pattern, content, re.IGNORECASE):\n errors.append(error_msg)\n score -= 15\n \n # Check VERIFY: statements (minimum 2)\n verify_section = re.search(r'##\\s*Verification\\s*Criteria(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if verify_section:\n verify_content = verify_section.group(1)\n verify_count = len(re.findall(r'VERIFY:', verify_content))\n if verify_count < 2:\n errors.append(f'Only {verify_count} VERIFY: statements found (minimum 2 required)')\n score -= 15\n else:\n errors.append('Verification Criteria section is empty or malformed')\n score -= 20\n \n # Check Rules has content\n rules_section = re.search(r'##\\s*Rules\\s*(&|and)\\s*Decisions(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if rules_section:\n rules_content = rules_section.group(2).strip()\n if len(rules_content) < 20 or re.search(r'^\\s*-\\s*\\[', rules_content):\n warnings.append('Rules & Decisions section appears to have only placeholder content')\n score -= 10\n \n # Check Interfaces has content\n iface_section = re.search(r'##\\s*Interfaces(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if iface_section:\n iface_content = iface_section.group(1).strip()\n if len(iface_content) < 20 or re.search(r'^\\s*-\\s*\\[', iface_content):\n warnings.append('Interfaces section appears to have only placeholder content')\n score -= 10\nelse:\n spec_format = 'legacy'\n warnings.append('DEPRECATED: Spec uses old numbered format. Will be migrated when next touched during finalization.')\n \n # Validate legacy format\n required = [\n (legacy_sections['purpose'], 'Missing \"1. Purpose\" section'),\n (legacy_sections['deps'], 'Missing \"2. Dependencies\" section'),\n (legacy_sections['interfaces'], 'Missing \"3. Interfaces\" section'),\n (legacy_sections['logic'], 'Missing \"4. Core Logic\" section'),\n (legacy_sections['arc'], 'Missing ARC/Error Handling section')\n ]\n for pattern, error_msg in required:\n if not re.search(pattern, content, re.IGNORECASE):\n errors.append(error_msg)\n score -= 15\n \n # Check ARC criteria (legacy format)\n arc_section = re.search(r'##\\s*7\\.\\s*ARC\\s*Verification\\s*Criteria(.+?)(?=##|$)', content, re.DOTALL | re.IGNORECASE)\n if arc_section:\n arc_content = arc_section.group(1)\n criteria_count = len(re.findall(r'ARC_[A-Z]+_\\d+:', arc_content))\n if criteria_count < 3:\n errors.append(f'Only {criteria_count} testable ARC criteria found (minimum 3 required)')\n score -= 15\n else:\n errors.append('ARC Verification Criteria section is empty or malformed')\n score -= 20\n\n# Check for placeholder text (both formats)\nplaceholder_patterns = [\n (r'\\[Agent:', 'Contains [Agent: placeholder'),\n (r'\\[TODO', 'Contains [TODO] placeholder'),\n (r'\\[FILL', 'Contains [FILL] placeholder'),\n (r'\\[TBD\\]', 'Contains [TBD] placeholder'),\n (r'\\[INSERT', 'Contains [INSERT] placeholder')\n]\nfor pattern, msg in placeholder_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n errors.append(msg)\n score -= 20\n break\n\n# Calculate final validation\noutputs['valid'] = (score >= 70 and len(errors) == 0)\noutputs['score'] = max(0, score)\noutputs['errors'] = errors\noutputs['warnings'] = warnings\noutputs['node_id'] = node_id\noutputs['spec_path'] = str(spec_path)\noutputs['format'] = spec_format"
23
+ }
24
+ ]
25
+ }
@@ -1,12 +1,12 @@
1
- {
2
- "name": "tracker-scan",
3
- "version": "0.3.6",
4
- "description": "Extracts NodeID statuses and WorkGroup assignments from the KnowzCode tracker.",
5
- "parameters": [],
6
- "actions": [
7
- {
8
- "type": "python",
9
- "code": "from pathlib import Path\ntext = Path('knowzcode/knowzcode_tracker.md').read_text()\nrows = []\nfor line in text.splitlines():\n if line.startswith('|') and '`' in line:\n cols = [c.strip() for c in line.strip('|').split('|')]\n if len(cols) >= 9 and cols[2] != 'Node ID':\n rows.append({'status': cols[0], 'workgroup_id': cols[1], 'node_id': cols[2].strip('`'), 'notes': cols[8]})\noutputs['rows'] = rows\n"
10
- }
11
- ]
12
- }
1
+ {
2
+ "name": "tracker-scan",
3
+ "version": "0.3.7",
4
+ "description": "Extracts NodeID statuses and WorkGroup assignments from the KnowzCode tracker.",
5
+ "parameters": [],
6
+ "actions": [
7
+ {
8
+ "type": "python",
9
+ "code": "from pathlib import Path\ntext = Path('knowzcode/knowzcode_tracker.md').read_text()\nrows = []\nfor line in text.splitlines():\n if line.startswith('|') and '`' in line:\n cols = [c.strip() for c in line.strip('|').split('|')]\n if len(cols) >= 9 and cols[2] != 'Node ID':\n rows.append({'status': cols[0], 'workgroup_id': cols[1], 'node_id': cols[2].strip('`'), 'notes': cols[8]})\noutputs['rows'] = rows\n"
10
+ }
11
+ ]
12
+ }
@@ -1,28 +1,28 @@
1
- {
2
- "name": "tracker-update",
3
- "version": "0.3.6",
4
- "description": "Applies validated updates to knowzcode/knowzcode_tracker.md while preserving table structure.",
5
- "parameters": [
6
- {
7
- "name": "updates",
8
- "type": "array",
9
- "items": {
10
- "type": "object",
11
- "properties": {
12
- "node_id": {"type": "string"},
13
- "status": {"type": "string"},
14
- "workgroup_id": {"type": "string"},
15
- "notes": {"type": "string"}
16
- },
17
- "required": ["node_id"]
18
- },
19
- "required": true
20
- }
21
- ],
22
- "actions": [
23
- {
24
- "type": "python",
25
- "code": "from pathlib import Path\npath = Path('knowzcode/knowzcode_tracker.md')\ntext = path.read_text()\nlines = text.splitlines()\nheader_idx = next(i for i, line in enumerate(lines) if line.startswith('| Status |'))\ntable = lines[header_idx:]\nrows = [line for line in table if line.startswith('|')]\nupdates = {u['node_id']: u for u in inputs['updates']}\nnew_rows = []\nfor row in rows:\n parts = [p.strip() for p in row.strip('|').split('|')]\n if not parts or parts[2] == 'Node ID':\n new_rows.append(row)\n continue\n node_id = parts[2].strip('`')\n if node_id in updates:\n upd = updates[node_id]\n status = upd.get('status', parts[0].strip())\n workgroup = upd.get('workgroup_id', parts[1].strip())\n notes = upd.get('notes', parts[8].strip())\n new_row = f\"| {status} | {workgroup} | `{node_id}` | {parts[3]} | {parts[4]} | {parts[5]} | {parts[6]} | {parts[7]} | {notes} |\"\n new_rows.append(new_row)\n updates.pop(node_id)\n else:\n new_rows.append(row)\nif updates:\n missing = ', '.join(sorted(updates.keys()))\n raise ValueError(f\"NodeIDs {missing} not found in tracker table\")\nlines[header_idx:] = new_rows + lines[header_idx + len(rows):]\npath.write_text('\\n'.join(lines) + '\\n')"
26
- }
27
- ]
28
- }
1
+ {
2
+ "name": "tracker-update",
3
+ "version": "0.3.7",
4
+ "description": "Applies validated updates to knowzcode/knowzcode_tracker.md while preserving table structure.",
5
+ "parameters": [
6
+ {
7
+ "name": "updates",
8
+ "type": "array",
9
+ "items": {
10
+ "type": "object",
11
+ "properties": {
12
+ "node_id": {"type": "string"},
13
+ "status": {"type": "string"},
14
+ "workgroup_id": {"type": "string"},
15
+ "notes": {"type": "string"}
16
+ },
17
+ "required": ["node_id"]
18
+ },
19
+ "required": true
20
+ }
21
+ ],
22
+ "actions": [
23
+ {
24
+ "type": "python",
25
+ "code": "from pathlib import Path\npath = Path('knowzcode/knowzcode_tracker.md')\ntext = path.read_text()\nlines = text.splitlines()\nheader_idx = next(i for i, line in enumerate(lines) if line.startswith('| Status |'))\ntable = lines[header_idx:]\nrows = [line for line in table if line.startswith('|')]\nupdates = {u['node_id']: u for u in inputs['updates']}\nnew_rows = []\nfor row in rows:\n parts = [p.strip() for p in row.strip('|').split('|')]\n if not parts or parts[2] == 'Node ID':\n new_rows.append(row)\n continue\n node_id = parts[2].strip('`')\n if node_id in updates:\n upd = updates[node_id]\n status = upd.get('status', parts[0].strip())\n workgroup = upd.get('workgroup_id', parts[1].strip())\n notes = upd.get('notes', parts[8].strip())\n new_row = f\"| {status} | {workgroup} | `{node_id}` | {parts[3]} | {parts[4]} | {parts[5]} | {parts[6]} | {parts[7]} | {notes} |\"\n new_rows.append(new_row)\n updates.pop(node_id)\n else:\n new_rows.append(row)\nif updates:\n missing = ', '.join(sorted(updates.keys()))\n raise ValueError(f\"NodeIDs {missing} not found in tracker table\")\nlines[header_idx:] = new_rows + lines[header_idx + len(rows):]\npath.write_text('\\n'.join(lines) + '\\n')"
26
+ }
27
+ ]
28
+ }
@@ -1,14 +1,14 @@
1
- {
2
- "name": "validate-installation",
3
- "version": "0.3.6",
4
- "description": "Validates that KnowzCode installation completed successfully with required directories and files",
5
-
6
- "parameters": [],
7
-
8
- "actions": [
9
- {
10
- "type": "python",
11
- "code": "from pathlib import Path\nimport json\n\n# Check for knowzcode directory (the primary installation target)\nknowzcode_dir = Path('knowzcode')\nerrors = []\nwarnings = []\n\n# Check directory exists\nif not knowzcode_dir.exists():\n errors.append('knowzcode/ directory not found')\n outputs['valid'] = False\n outputs['errors'] = errors\n outputs['warnings'] = warnings\n outputs['commands_count'] = 0\n outputs['skills_count'] = 0\n outputs['agents_count'] = 0\n exit()\n\n# Check required knowzcode files\nrequired_files = [\n 'knowzcode_loop.md',\n 'knowzcode_tracker.md',\n 'knowzcode_project.md',\n 'knowzcode_architecture.md'\n]\n\nfor required_file in required_files:\n if not (knowzcode_dir / required_file).exists():\n errors.append(f'Missing required file: knowzcode/{required_file}')\n\n# Check required subdirectories\nrequired_dirs = [\n 'specs',\n 'workgroups',\n 'prompts'\n]\n\nfor subdir in required_dirs:\n if not (knowzcode_dir / subdir).exists():\n warnings.append(f'Missing directory: knowzcode/{subdir}')\n\n# Check .claude directory for commands/agents (plugin or local installation)\nclaude_dir = Path('.claude')\ncommands_count = 0\nskills_count = 0\nagents_count = 0\n\nif claude_dir.exists():\n if (claude_dir / 'commands').exists():\n commands_count = len(list((claude_dir / 'commands').glob('*.md')))\n if (claude_dir / 'skills').exists():\n skills_count = len(list((claude_dir / 'skills').glob('*.json')))\n if (claude_dir / 'agents').exists():\n agents_count = len(list((claude_dir / 'agents').glob('*.md')))\n if (claude_dir / 'subagents').exists():\n agents_count += len(list((claude_dir / 'subagents').glob('*.yaml')))\n\n# Also check top-level commands/skills/agents (plugin installation)\nif Path('commands').exists():\n commands_count = max(commands_count, len(list(Path('commands').glob('*.md'))))\nif Path('skills').exists():\n skills_count = max(skills_count, len(list(Path('skills').glob('*.json'))))\nif Path('agents').exists():\n agents_count = max(agents_count, len(list(Path('agents').glob('*.md'))))\n\n# Expected minimums\nif commands_count < 5:\n warnings.append(f'Expected at least 5 commands, found {commands_count}')\nif agents_count < 10:\n warnings.append(f'Expected at least 10 agents, found {agents_count}')\n\noutputs['valid'] = len(errors) == 0\noutputs['errors'] = errors\noutputs['warnings'] = warnings\noutputs['commands_count'] = commands_count\noutputs['skills_count'] = skills_count\noutputs['agents_count'] = agents_count"
12
- }
13
- ]
14
- }
1
+ {
2
+ "name": "validate-installation",
3
+ "version": "0.3.7",
4
+ "description": "Validates that KnowzCode installation completed successfully with required directories and files",
5
+
6
+ "parameters": [],
7
+
8
+ "actions": [
9
+ {
10
+ "type": "python",
11
+ "code": "from pathlib import Path\nimport json\n\n# Check for knowzcode directory (the primary installation target)\nknowzcode_dir = Path('knowzcode')\nerrors = []\nwarnings = []\n\n# Check directory exists\nif not knowzcode_dir.exists():\n errors.append('knowzcode/ directory not found')\n outputs['valid'] = False\n outputs['errors'] = errors\n outputs['warnings'] = warnings\n outputs['commands_count'] = 0\n outputs['skills_count'] = 0\n outputs['agents_count'] = 0\n exit()\n\n# Check required knowzcode files\nrequired_files = [\n 'knowzcode_loop.md',\n 'knowzcode_tracker.md',\n 'knowzcode_project.md',\n 'knowzcode_architecture.md'\n]\n\nfor required_file in required_files:\n if not (knowzcode_dir / required_file).exists():\n errors.append(f'Missing required file: knowzcode/{required_file}')\n\n# Check required subdirectories\nrequired_dirs = [\n 'specs',\n 'workgroups',\n 'prompts'\n]\n\nfor subdir in required_dirs:\n if not (knowzcode_dir / subdir).exists():\n warnings.append(f'Missing directory: knowzcode/{subdir}')\n\n# Check .claude directory for commands/agents (plugin or local installation)\nclaude_dir = Path('.claude')\ncommands_count = 0\nskills_count = 0\nagents_count = 0\n\nif claude_dir.exists():\n if (claude_dir / 'commands').exists():\n commands_count = len(list((claude_dir / 'commands').glob('*.md')))\n if (claude_dir / 'skills').exists():\n skills_count = len(list((claude_dir / 'skills').glob('*.json')))\n if (claude_dir / 'agents').exists():\n agents_count = len(list((claude_dir / 'agents').glob('*.md')))\n if (claude_dir / 'subagents').exists():\n agents_count += len(list((claude_dir / 'subagents').glob('*.yaml')))\n\n# Also check top-level commands/skills/agents (plugin installation)\nif Path('commands').exists():\n commands_count = max(commands_count, len(list(Path('commands').glob('*.md'))))\nif Path('skills').exists():\n skills_count = max(skills_count, len(list(Path('skills').glob('*.json'))))\nif Path('agents').exists():\n agents_count = max(agents_count, len(list(Path('agents').glob('*.md'))))\n\n# Expected minimums\nif commands_count < 5:\n warnings.append(f'Expected at least 5 commands, found {commands_count}')\nif agents_count < 10:\n warnings.append(f'Expected at least 10 agents, found {agents_count}')\n\noutputs['valid'] = len(errors) == 0\noutputs['errors'] = errors\noutputs['warnings'] = warnings\noutputs['commands_count'] = commands_count\noutputs['skills_count'] = skills_count\noutputs['agents_count'] = agents_count"
12
+ }
13
+ ]
14
+ }