knowzcode 0.3.6 → 0.4.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 (79) hide show
  1. package/.claude-plugin/marketplace.json +61 -61
  2. package/.claude-plugin/plugin.json +8 -8
  3. package/LICENSE +121 -121
  4. package/README.md +354 -320
  5. package/agents/analyst.md +114 -114
  6. package/agents/architect.md +200 -200
  7. package/agents/builder.md +104 -104
  8. package/agents/closer.md +177 -95
  9. package/agents/context-scout.md +54 -54
  10. package/agents/knowledge-migrator.md +349 -349
  11. package/agents/knowz-scout.md +83 -83
  12. package/agents/knowz-scribe.md +180 -180
  13. package/agents/microfix-specialist.md +135 -135
  14. package/agents/project-advisor.md +111 -111
  15. package/agents/reviewer.md +172 -172
  16. package/agents/security-officer.md +194 -194
  17. package/agents/test-advisor.md +162 -162
  18. package/agents/update-coordinator.md +394 -394
  19. package/bin/knowzcode.mjs +1199 -956
  20. package/commands/audit.md +328 -328
  21. package/commands/connect-mcp.md +549 -549
  22. package/commands/fix.md +107 -107
  23. package/commands/init.md +500 -439
  24. package/commands/learn.md +332 -332
  25. package/commands/plan.md +272 -272
  26. package/commands/register.md +733 -733
  27. package/commands/status.md +309 -309
  28. package/commands/telemetry-setup.md +368 -368
  29. package/commands/telemetry.md +188 -188
  30. package/commands/work.md +1204 -1170
  31. package/knowzcode/automation_manifest.md +59 -59
  32. package/knowzcode/claude_code_execution.md +431 -420
  33. package/knowzcode/copilot_execution.md +231 -231
  34. package/knowzcode/enterprise/compliance_manifest.md +137 -137
  35. package/knowzcode/enterprise/compliance_status.md +30 -30
  36. package/knowzcode/enterprise/guidelines/code-quality.md +67 -67
  37. package/knowzcode/enterprise/guidelines/security.md +355 -355
  38. package/knowzcode/enterprise/templates/guideline-template.md +55 -55
  39. package/knowzcode/gitignore.template +13 -13
  40. package/knowzcode/knowzcode_architecture.md +51 -51
  41. package/knowzcode/knowzcode_log.md +142 -142
  42. package/knowzcode/knowzcode_loop.md +596 -593
  43. package/knowzcode/knowzcode_orchestration.md +66 -66
  44. package/knowzcode/knowzcode_project.md +48 -48
  45. package/knowzcode/knowzcode_tracker.md +40 -40
  46. package/knowzcode/knowzcode_vaults.md +257 -257
  47. package/knowzcode/mcp_config.md +191 -191
  48. package/knowzcode/planning/Readme.md +6 -6
  49. package/knowzcode/platform_adapters.md +1260 -1047
  50. package/knowzcode/prompts/Execute_Micro_Fix.md +57 -57
  51. package/knowzcode/prompts/Investigate_Codebase.md +227 -227
  52. package/knowzcode/prompts/Migrate_Knowledge.md +301 -301
  53. package/knowzcode/prompts/Refactor_Node.md +72 -72
  54. package/knowzcode/prompts/Spec_Verification_Checkpoint.md +59 -59
  55. package/knowzcode/prompts/[LOOP_1A]__Propose_Change_Set.md +52 -52
  56. package/knowzcode/prompts/[LOOP_1B]__Draft_Specs.md +75 -75
  57. package/knowzcode/prompts/[LOOP_2A]__Implement_Change_Set.md +55 -55
  58. package/knowzcode/prompts/[LOOP_2B]__Verify_Implementation.md +72 -72
  59. package/knowzcode/prompts/[LOOP_3]__Finalize_And_Commit.md +67 -67
  60. package/knowzcode/specs/Readme.md +10 -10
  61. package/knowzcode/telemetry_config.md +89 -89
  62. package/knowzcode/user_preferences.md +120 -120
  63. package/package.json +53 -53
  64. package/skills/alias-resolver.json +15 -15
  65. package/skills/architecture-diff.json +12 -12
  66. package/skills/check-installation-status.json +14 -14
  67. package/skills/continue.md +126 -126
  68. package/skills/environment-guard.json +12 -12
  69. package/skills/generate-workgroup-id.json +25 -25
  70. package/skills/install-knowzcode.json +21 -21
  71. package/skills/load-core-context.json +18 -18
  72. package/skills/log-entry-builder.json +15 -15
  73. package/skills/spec-quality-check.json +14 -14
  74. package/skills/spec-template.json +15 -15
  75. package/skills/spec-validator.json +25 -25
  76. package/skills/start-work.md +224 -224
  77. package/skills/tracker-scan.json +12 -12
  78. package/skills/tracker-update.json +28 -28
  79. package/skills/validate-installation.json +14 -14
@@ -1,126 +1,126 @@
1
- ---
2
- name: continue
3
- description: Detect continuation intent and resume active WorkGroup workflow
4
- trigger: User says "continue", "keep going", "resume", or similar continuation intent
5
- ---
6
-
7
- # Continue Skill
8
-
9
- **Purpose**: Detect when user wants to continue work and resume the active WorkGroup with proper context restoration.
10
-
11
- ## Trigger Patterns
12
-
13
- Activate when user message matches ANY of these patterns:
14
- - "continue"
15
- - "keep going"
16
- - "resume"
17
- - "carry on"
18
- - "next"
19
- - "continue with this"
20
- - "let's continue"
21
- - "keep working"
22
-
23
- **Context Requirements**:
24
- - Must be in a KnowzCode-initialized project (knowzcode/ directory exists)
25
- - Should NOT trigger if user is clearly giving new instructions
26
- - Should NOT trigger during explicit command execution
27
-
28
- ## When NOT to Trigger
29
-
30
- - User is giving specific new instructions
31
- - User is asking a question
32
- - Already executing a /kc:* command
33
- - knowzcode/ directory doesn't exist
34
-
35
- ## Skill Behavior
36
-
37
- When triggered:
38
-
39
- ### Step 1: Find Active WorkGroup
40
-
41
- Search `knowzcode/knowzcode_tracker.md` for `[WIP]` entries.
42
-
43
- - **One active WorkGroup**: Use it automatically
44
- - **Multiple active**: Present options to user
45
- - **None active**: Inform user and suggest `/kc:work`
46
-
47
- ### Step 2: Load WorkGroup Context
48
-
49
- Read `knowzcode/workgroups/{WorkGroupID}.md` to determine:
50
- - Current phase
51
- - Primary goal
52
- - Change Set
53
- - Outstanding todos
54
- - **Autonomous Mode**: If the WorkGroup file contains `**Autonomous Mode**: Active`, restore `AUTONOMOUS_MODE = true` and announce: `> **Autonomous Mode: RESTORED** — continuing with auto-approved gates.`
55
- - **Orchestration Config**: If `knowzcode/knowzcode_orchestration.md` exists, parse and restore `MAX_BUILDERS`, `SCOUT_MODE`, `MCP_AGENTS_ENABLED`, `DEFAULT_SPECIALISTS` (same logic as work.md Step 2.4). Defaults apply if file is absent.
56
-
57
- ### Step 3: Resume at Current Phase
58
-
59
- Read `knowzcode/knowzcode_loop.md` and resume the workflow at the detected phase.
60
-
61
- #### Parallel Mode Detection
62
-
63
- If the WorkGroup file contains a `## Current Stage` section (instead of `Current Phase`):
64
- - This is a **parallel-mode WorkGroup**
65
- - Read the per-NodeID phase table to determine what's in progress
66
- - Resume by recreating the team and spawning agents appropriate for the current stage:
67
- - **Stage 0/1**: Spawn analyst + architect. If `SCOUT_MODE != "none"` and context is stale, re-spawn scout(s) per SCOUT_MODE.
68
- - **Stage 2**: Spawn builder(s) per the dependency map + reviewer if any NodeIDs are past implementation
69
- - **Stage 3**: Spawn closer
70
- - Builders and reviewer persist through gap loops (don't respawn per iteration)
71
- - Announce: `**Resuming Parallel Teams** — Stage {N}: {description}`
72
-
73
- If resuming mid-Stage-2 (e.g., builder was implementing, reviewer had started auditing):
74
- - Read the per-NodeID status table to determine which NodeIDs need builders and which need reviewer
75
- - Carry forward existing context by reading the WorkGroup file
76
-
77
- #### Sequential Mode Detection
78
-
79
- If the WorkGroup file contains `Current Phase:` (standard format):
80
- - This is a **sequential-mode WorkGroup**
81
- - Create tasks only for the **remaining** phases (not completed ones):
82
-
83
- | Detected Phase | Remaining Work |
84
- |----------------|----------------|
85
- | 1A | All phases (1A → 1B → 2A → 2B → 3) |
86
- | 1B | Specs + implementation + audit + finalization |
87
- | 2A | Implementation + audit + finalization |
88
- | 2B | Audit + finalization |
89
- | 3 | Finalization only |
90
-
91
- **Set up execution mode** — check `~/.claude/settings.json` and `.claude/settings.json` for `"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"` in the `env` block. If found, Agent Teams is available — create a team named `kc-{wgid}` and activate delegate mode (you coordinate only, never write code directly). Read `knowzcode/claude_code_execution.md` for team conventions. For each remaining phase, spawn one teammate with the spawn prompt from the corresponding phase section of `/kc:work`, create a task, wait for completion, present quality gate, shut down teammate. Shut down all teammates when done or on cancel.
92
-
93
- If Agent Teams is not available, announce `**Execution Mode: Subagent Delegation** — Agent Teams not available (add "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" to the env block in settings.json, then restart Claude Code)` and use `Task()` calls to delegate each remaining phase to the named agent.
94
-
95
- Follow the same phase delegation patterns (spawn prompts, quality gates, gap loop) as `/kc:work`.
96
-
97
- ### Step 4: Present Status
98
-
99
- ```markdown
100
- ## Resuming WorkGroup: {wgid}
101
-
102
- **Goal**: {primary goal}
103
- **Phase**: {current phase}
104
- **NodeIDs**: {list}
105
-
106
- **Outstanding Todos**:
107
- {list from WorkGroup file}
108
-
109
- Continuing from where we left off...
110
- ```
111
-
112
- Then proceed with the appropriate phase using the same agents as `/kc:work`.
113
-
114
- ## Logging
115
-
116
- ```markdown
117
- ---
118
- **Type:** SkillActivation
119
- **Timestamp:** [timestamp]
120
- **Skill:** continue
121
- **Trigger:** User said "{user_message}"
122
- **WorkGroup:** {wgid}
123
- **Phase:** {current phase}
124
- **Logged By:** AI-Agent
125
- ---
126
- ```
1
+ ---
2
+ name: continue
3
+ description: Detect continuation intent and resume active WorkGroup workflow
4
+ trigger: User says "continue", "keep going", "resume", or similar continuation intent
5
+ ---
6
+
7
+ # Continue Skill
8
+
9
+ **Purpose**: Detect when user wants to continue work and resume the active WorkGroup with proper context restoration.
10
+
11
+ ## Trigger Patterns
12
+
13
+ Activate when user message matches ANY of these patterns:
14
+ - "continue"
15
+ - "keep going"
16
+ - "resume"
17
+ - "carry on"
18
+ - "next"
19
+ - "continue with this"
20
+ - "let's continue"
21
+ - "keep working"
22
+
23
+ **Context Requirements**:
24
+ - Must be in a KnowzCode-initialized project (knowzcode/ directory exists)
25
+ - Should NOT trigger if user is clearly giving new instructions
26
+ - Should NOT trigger during explicit command execution
27
+
28
+ ## When NOT to Trigger
29
+
30
+ - User is giving specific new instructions
31
+ - User is asking a question
32
+ - Already executing a /kc:* command
33
+ - knowzcode/ directory doesn't exist
34
+
35
+ ## Skill Behavior
36
+
37
+ When triggered:
38
+
39
+ ### Step 1: Find Active WorkGroup
40
+
41
+ Search `knowzcode/knowzcode_tracker.md` for `[WIP]` entries.
42
+
43
+ - **One active WorkGroup**: Use it automatically
44
+ - **Multiple active**: Present options to user
45
+ - **None active**: Inform user and suggest `/kc:work`
46
+
47
+ ### Step 2: Load WorkGroup Context
48
+
49
+ Read `knowzcode/workgroups/{WorkGroupID}.md` to determine:
50
+ - Current phase
51
+ - Primary goal
52
+ - Change Set
53
+ - Outstanding todos
54
+ - **Autonomous Mode**: If the WorkGroup file contains `**Autonomous Mode**: Active`, restore `AUTONOMOUS_MODE = true` and announce: `> **Autonomous Mode: RESTORED** — continuing with auto-approved gates.`
55
+ - **Orchestration Config**: If `knowzcode/knowzcode_orchestration.md` exists, parse and restore `MAX_BUILDERS`, `SCOUT_MODE`, `MCP_AGENTS_ENABLED`, `DEFAULT_SPECIALISTS` (same logic as work.md Step 2.4). Defaults apply if file is absent.
56
+
57
+ ### Step 3: Resume at Current Phase
58
+
59
+ Read `knowzcode/knowzcode_loop.md` and resume the workflow at the detected phase.
60
+
61
+ #### Parallel Mode Detection
62
+
63
+ If the WorkGroup file contains a `## Current Stage` section (instead of `Current Phase`):
64
+ - This is a **parallel-mode WorkGroup**
65
+ - Read the per-NodeID phase table to determine what's in progress
66
+ - Resume by recreating the team and spawning agents appropriate for the current stage:
67
+ - **Stage 0/1**: Spawn analyst + architect. If `SCOUT_MODE != "none"` and context is stale, re-spawn scout(s) per SCOUT_MODE.
68
+ - **Stage 2**: Spawn builder(s) per the dependency map + reviewer if any NodeIDs are past implementation
69
+ - **Stage 3**: Spawn closer
70
+ - Builders and reviewer persist through gap loops (don't respawn per iteration)
71
+ - Announce: `**Resuming Parallel Teams** — Stage {N}: {description}`
72
+
73
+ If resuming mid-Stage-2 (e.g., builder was implementing, reviewer had started auditing):
74
+ - Read the per-NodeID status table to determine which NodeIDs need builders and which need reviewer
75
+ - Carry forward existing context by reading the WorkGroup file
76
+
77
+ #### Sequential Mode Detection
78
+
79
+ If the WorkGroup file contains `Current Phase:` (standard format):
80
+ - This is a **sequential-mode WorkGroup**
81
+ - Create tasks only for the **remaining** phases (not completed ones):
82
+
83
+ | Detected Phase | Remaining Work |
84
+ |----------------|----------------|
85
+ | 1A | All phases (1A → 1B → 2A → 2B → 3) |
86
+ | 1B | Specs + implementation + audit + finalization |
87
+ | 2A | Implementation + audit + finalization |
88
+ | 2B | Audit + finalization |
89
+ | 3 | Finalization only |
90
+
91
+ **Set up execution mode** — check `~/.claude/settings.json` and `.claude/settings.json` for `"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"` in the `env` block. If found, Agent Teams is available — create a team named `kc-{wgid}` and activate delegate mode (you coordinate only, never write code directly). Read `knowzcode/claude_code_execution.md` for team conventions. For each remaining phase, spawn one teammate with the spawn prompt from the corresponding phase section of `/kc:work`, create a task, wait for completion, present quality gate, shut down teammate. Shut down all teammates when done or on cancel.
92
+
93
+ If Agent Teams is not available, announce `**Execution Mode: Subagent Delegation** — Agent Teams not available (add "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" to the env block in settings.json, then restart Claude Code)` and use `Task()` calls to delegate each remaining phase to the named agent.
94
+
95
+ Follow the same phase delegation patterns (spawn prompts, quality gates, gap loop) as `/kc:work`.
96
+
97
+ ### Step 4: Present Status
98
+
99
+ ```markdown
100
+ ## Resuming WorkGroup: {wgid}
101
+
102
+ **Goal**: {primary goal}
103
+ **Phase**: {current phase}
104
+ **NodeIDs**: {list}
105
+
106
+ **Outstanding Todos**:
107
+ {list from WorkGroup file}
108
+
109
+ Continuing from where we left off...
110
+ ```
111
+
112
+ Then proceed with the appropriate phase using the same agents as `/kc:work`.
113
+
114
+ ## Logging
115
+
116
+ ```markdown
117
+ ---
118
+ **Type:** SkillActivation
119
+ **Timestamp:** [timestamp]
120
+ **Skill:** continue
121
+ **Trigger:** User said "{user_message}"
122
+ **WorkGroup:** {wgid}
123
+ **Phase:** {current phase}
124
+ **Logged By:** AI-Agent
125
+ ---
126
+ ```
@@ -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.4.0",
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.4.0",
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.4.0",
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.4.0",
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.4.0",
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.4.0",
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.4.0",
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.4.0",
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
+ }