clawstrap 1.2.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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +374 -0
  3. package/dist/index.cjs +1222 -0
  4. package/package.json +57 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,1222 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/index.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/init.ts
30
+ var import_node_fs2 = __toESM(require("fs"), 1);
31
+ var import_node_path3 = __toESM(require("path"), 1);
32
+ var import_prompts2 = require("@inquirer/prompts");
33
+
34
+ // src/schema.ts
35
+ var import_zod = require("zod");
36
+ var AI_SYSTEMS = ["claude-code"];
37
+ var WORKLOAD_TYPES = [
38
+ "research",
39
+ "content",
40
+ "data-processing",
41
+ "custom"
42
+ ];
43
+ var PARALLEL_AGENTS = ["single", "small-team", "production"];
44
+ var QUALITY_LEVELS = ["solo", "team", "production"];
45
+ var LastExportSchema = import_zod.z.object({
46
+ format: import_zod.z.string(),
47
+ exportedAt: import_zod.z.string(),
48
+ outputDir: import_zod.z.string()
49
+ }).optional();
50
+ var ClawstrapConfigSchema = import_zod.z.object({
51
+ version: import_zod.z.string().default("1.0.0"),
52
+ createdAt: import_zod.z.string(),
53
+ workspaceName: import_zod.z.string().min(1),
54
+ targetDirectory: import_zod.z.string().min(1).default("."),
55
+ aiSystem: import_zod.z.enum(AI_SYSTEMS).default("claude-code"),
56
+ workloadType: import_zod.z.enum(WORKLOAD_TYPES),
57
+ parallelAgents: import_zod.z.enum(PARALLEL_AGENTS),
58
+ qualityLevel: import_zod.z.enum(QUALITY_LEVELS),
59
+ sessionHandoff: import_zod.z.boolean(),
60
+ lastExport: LastExportSchema
61
+ });
62
+
63
+ // src/derive-vars.ts
64
+ var SYSTEM_DIR = {
65
+ "claude-code": ".claude"
66
+ };
67
+ var GOVERNANCE_FILE = {
68
+ "claude-code": "CLAUDE.md"
69
+ };
70
+ var WORKLOAD_LABELS = {
71
+ research: "Research & Analysis",
72
+ content: "Content & Writing",
73
+ "data-processing": "Data Processing",
74
+ custom: "General Purpose"
75
+ };
76
+ var FLUSH_CADENCES = {
77
+ research: "every 3 findings",
78
+ content: "every draft iteration",
79
+ "data-processing": "every 5 batch items",
80
+ custom: "every 5 operations"
81
+ };
82
+ function deriveTemplateVars(config) {
83
+ return {
84
+ // Metadata
85
+ workspaceName: config.workspaceName,
86
+ generatedDate: config.createdAt.split("T")[0],
87
+ clawstrapVersion: config.version,
88
+ // AI system (v1: always claude-code, v2: user-selectable)
89
+ systemDir: SYSTEM_DIR[config.aiSystem],
90
+ governanceFile: GOVERNANCE_FILE[config.aiSystem],
91
+ // Direct values
92
+ workloadType: config.workloadType,
93
+ workloadLabel: WORKLOAD_LABELS[config.workloadType],
94
+ flushCadence: FLUSH_CADENCES[config.workloadType],
95
+ // Workload booleans
96
+ isResearch: config.workloadType === "research",
97
+ isContent: config.workloadType === "content",
98
+ isDataProcessing: config.workloadType === "data-processing",
99
+ isCustom: config.workloadType === "custom",
100
+ // Agent booleans
101
+ hasSubagents: config.parallelAgents !== "single",
102
+ isProductionAgents: config.parallelAgents === "production",
103
+ // Quality booleans
104
+ hasQualityGates: config.qualityLevel !== "solo",
105
+ isProductionQuality: config.qualityLevel === "production",
106
+ // Session handoff
107
+ sessionHandoff: config.sessionHandoff
108
+ };
109
+ }
110
+
111
+ // src/writer.ts
112
+ var import_node_fs = __toESM(require("fs"), 1);
113
+ var import_node_path = __toESM(require("path"), 1);
114
+
115
+ // src/template-engine.ts
116
+ var IF_BLOCK = /\{%#if\s+(\w+)%\}([\s\S]*?)\{%\/if%\}/g;
117
+ var UNLESS_BLOCK = /\{%#unless\s+(\w+)%\}([\s\S]*?)\{%\/unless%\}/g;
118
+ var NESTED_IF = /\{%#if\s+\w+%\}(?:(?!\{%\/if%\})[\s\S])*?\{%#if\s+/;
119
+ var VARIABLE = /\{%(\w+)%\}/g;
120
+ var TRIPLE_BLANK = /\n{3,}/g;
121
+ function render(template, vars) {
122
+ let result = template;
123
+ if (NESTED_IF.test(result)) {
124
+ throw new Error(
125
+ "Nested {%#if%} blocks are not supported. Use {%#unless%} inside {%#if%} instead."
126
+ );
127
+ }
128
+ result = result.replace(IF_BLOCK, (_, key, content) => {
129
+ return vars[key] ? content : "";
130
+ });
131
+ result = result.replace(UNLESS_BLOCK, (_, key, content) => {
132
+ return vars[key] ? "" : content;
133
+ });
134
+ result = result.replace(VARIABLE, (match, key) => {
135
+ const value = vars[key];
136
+ if (value === void 0) {
137
+ throw new Error(`Template variable not found: ${key}`);
138
+ }
139
+ return String(value);
140
+ });
141
+ result = result.replace(TRIPLE_BLANK, "\n\n");
142
+ return result;
143
+ }
144
+
145
+ // src/manifest.ts
146
+ var OUTPUT_MANIFEST = [
147
+ // Always generated
148
+ {
149
+ templateKey: "governanceFile",
150
+ outputPath: "{%governanceFile%}"
151
+ },
152
+ {
153
+ templateKey: "gettingStarted",
154
+ outputPath: "GETTING_STARTED.md"
155
+ },
156
+ {
157
+ templateKey: "gitignore",
158
+ outputPath: ".gitignore"
159
+ },
160
+ {
161
+ templateKey: "contextDiscipline",
162
+ outputPath: "{%systemDir%}/rules/context-discipline.md"
163
+ },
164
+ {
165
+ templateKey: "approvalFirst",
166
+ outputPath: "{%systemDir%}/rules/approval-first.md"
167
+ },
168
+ {
169
+ templateKey: "skillRegistry",
170
+ outputPath: "{%systemDir%}/skills/SKILL_REGISTRY.md"
171
+ },
172
+ {
173
+ templateKey: "gotchaLog",
174
+ outputPath: "{%systemDir%}/gotcha-log.md"
175
+ },
176
+ {
177
+ templateKey: "futureConsiderations",
178
+ outputPath: "{%systemDir%}/future-considerations.md"
179
+ },
180
+ {
181
+ templateKey: "projectProcess",
182
+ outputPath: "projects/_template/process.md"
183
+ },
184
+ {
185
+ templateKey: "projectReadme",
186
+ outputPath: "projects/_template/README.md"
187
+ },
188
+ // Conditional
189
+ {
190
+ templateKey: "qualityGates",
191
+ outputPath: "{%systemDir%}/rules/quality-gates.md",
192
+ condition: "hasQualityGates"
193
+ },
194
+ {
195
+ templateKey: "primaryAgent",
196
+ outputPath: "{%systemDir%}/agents/primary-agent.md",
197
+ condition: "hasSubagents"
198
+ },
199
+ {
200
+ templateKey: "agentTemplate",
201
+ outputPath: "{%systemDir%}/agents/_template.md",
202
+ condition: "hasSubagents"
203
+ },
204
+ {
205
+ templateKey: "subagentBootstrap",
206
+ outputPath: "{%systemDir%}/subagent-bootstrap.md",
207
+ condition: "hasSubagents"
208
+ },
209
+ {
210
+ templateKey: "memoryIndex",
211
+ outputPath: "{%systemDir%}/memory/MEMORY.md",
212
+ condition: "sessionHandoff"
213
+ }
214
+ ];
215
+ var EMPTY_DIRS = ["tmp", "research", "context", "artifacts"];
216
+
217
+ // src/templates/governance-file.md.tmpl
218
+ var governance_file_md_default = "# {%governanceFile%} \u2014 Master Governance Rules\n> **Workspace**: {%workspaceName%} | **Generated**: {%generatedDate%} | **Status**: active\n> Loaded every session. Keep lean. Move details to skills or rules files.\n\n---\n\n## Workflow Rules\n\n**Approval-first, always.**\nPlan work and get explicit approval before executing. No speculative actions.\nIf scope changes mid-task, pause and re-confirm.\n\n**If it's not on disk, it didn't happen.**\nSave findings immediately, not at session end. Flush {%flushCadence%}.\nWrite corrections to durable locations before applying them.\n{%#if hasSubagents%}Subagents return file paths, not raw data.{%/if%}\n\n**Quality > context cleanliness > speed > token cost.**\nQuality failures require full rework (100% waste). Never trade quality for tokens.\n{%#if isResearch%}\n**Research-specific:**\n- Cite sources for every claim \u2014 no unsourced assertions\n- Write findings to file immediately, not at session end\n- Separate facts from interpretation in all output\n{%/if%}\n{%#if isContent%}\n**Content-specific:**\n- Every piece of content follows the draft \u2192 review \u2192 approve cycle\n- Never publish or finalize without explicit approval\n- Track all revision feedback in a dedicated file\n{%/if%}\n{%#if isDataProcessing%}\n**Data processing-specific:**\n- Define batch size before starting \u2014 never process unbounded sets\n- Validate schema on every write\n- Log every transformation step for auditability\n{%/if%}\n\n---\n\n## Persistence Hierarchy\n\nFrom most ephemeral to most durable:\n\n| Layer | Location | Loaded When |\n|-------|----------|-------------|\n| Conversation | (in-context) | Always \u2014 volatile |\n| Temp files | `tmp/` | Per-task, gitignored |\n{%#if sessionHandoff%}| Memory | `{%systemDir%}/memory/` | On demand |\n{%/if%}| Skills | `{%systemDir%}/skills/*/SKILL.md` | When triggered |\n| Rules | `{%systemDir%}/rules/*.md` | Every session |\n| **{%governanceFile%}** | `./{%governanceFile%}` | **Every session** |\n\n---\n\n## Context Discipline\n\n- Flush working state to file {%flushCadence%}\n{%#if hasSubagents%}- Subagents write output to `tmp/{task}/{name}.json`; return one-line receipt\n- Main session reads subagent files ONLY if needed for the next step\n- Never hold raw batch results in conversation \u2014 write to disk first\n{%/if%}- Before batch work: write execution plan to file (survives context loss)\n\n---\n{%#if sessionHandoff%}\n## Session Handoff Checklist\n\nRun this at every session end (mandatory, not optional):\n\n1. Save all work to SSOT files\n2. Sync derived files (rebuild from SSOTs)\n3. SSOT integrity check (no duplicates, no stale data)\n4. Update progress tracker\n5. Write next-session plan (with pre-execution hooks listed)\n6. Launch QC on work done this session\n\n---\n{%/if%}\n\n## Security Rules\n\n- Never read `.env` files or echo credentials\n- Never install third-party MCP servers/plugins without explicit approval\n- Never write outside this workspace root \u2014 use project-local `tmp/`, not system `/tmp/`\n- Approved tools only \u2014 ask before using new tools/APIs\n\n---\n\n## Quality Rules\n{%#if isProductionQuality%}\n- QC is a structural gate, not an optional post-step\n- Run spot-checks after every 5 items in batch work (Ralph Loop)\n- Combined agents (extract + classify) beat split agents on quality\n- Disagreements between agents reveal ambiguity \u2014 escalate, don't suppress\n{%/if%}\n{%#if hasQualityGates%}\n{%#unless isProductionQuality%}- QC is a structural gate, not an optional post-step\n- Run QC checkpoints at regular intervals during batch work\n- All results must be reviewed before being marked complete\n{%/unless%}\n{%/if%}\n{%#unless hasQualityGates%}- Run a quick check before finishing any task\n- Review outputs before marking work complete\n{%/unless%}\n\n---\n\n## Pointers to Other Layers\n\n- Rules: `{%systemDir%}/rules/` \u2014 domain-specific rules loaded every session\n- Skills: `{%systemDir%}/skills/SKILL_REGISTRY.md` \u2014 index of all skills\n{%#if hasSubagents%}- Agents: `{%systemDir%}/agents/` \u2014 subagent definitions with governance baked in\n{%/if%}- Gotchas: `{%systemDir%}/gotcha-log.md` \u2014 incident log (why rules exist)\n- Future: `{%systemDir%}/future-considerations.md` \u2014 deferred ideas\n";
219
+
220
+ // src/templates/getting-started.md.tmpl
221
+ var getting_started_md_default = "# Getting Started \u2014 {%workspaceName%}\n> Generated by Clawstrap v{%clawstrapVersion%} on {%generatedDate%}\n\nWelcome to your AI agent workspace. This workspace is configured for\n**{%workloadLabel%}** workflows.\n\n---\n\n## Quick Start\n\n1. **Read `{%governanceFile%}`** \u2014 this is your master governance file. It loads\n automatically every session and defines the rules your AI assistant follows.\n\n2. **Check `{%systemDir%}/rules/`** \u2014 these rule files also load every session.\n They contain detailed procedures for context discipline, approval workflows,\n and quality gates.\n\n3. **Start working** \u2014 open a Claude Code session in this directory. The\n governance files will load automatically.\n\n---\n\n## What's in This Workspace\n\n```\n{%governanceFile%} \u2190 Master governance (always loaded)\nGETTING_STARTED.md \u2190 You are here\n.clawstrap.json \u2190 Your workspace configuration\n\n{%systemDir%}/\n rules/ \u2190 Auto-loaded rules (every session)\n skills/ \u2190 Skill definitions (loaded when triggered)\n{%#if hasSubagents%} agents/ \u2190 Agent definitions\n{%/if%} gotcha-log.md \u2190 Incident log\n future-considerations.md \u2190 Deferred ideas\n{%#if sessionHandoff%} memory/ \u2190 Persistent memory across sessions\n{%/if%}\nprojects/\n _template/ \u2190 Template for new projects\n\ntmp/ \u2190 Temporary files (gitignored)\nresearch/ \u2190 Reference material\ncontext/ \u2190 Session checkpoints\nartifacts/ \u2190 Durable output\n```\n\n---\n\n## Key Principles\n\nThis workspace enforces five principles:\n\n1. **If it's not on disk, it didn't happen.** Context windows compress. Sessions\n end. The only thing that persists is files. Flush work {%flushCadence%}.\n\n2. **Approval-first, always.** Plan work, get approval, then execute. No\n speculative actions.\n\n3. **Quality is a structural gate.** Quality checks happen during work, not after.\n{%#if hasQualityGates%} See `{%systemDir%}/rules/quality-gates.md` for the full procedure.{%/if%}\n\n4. **Disagreements reveal ambiguity.** When agents disagree, the rules are\n ambiguous. Escalate and clarify \u2014 don't suppress.\n\n5. **One decision at a time.** Binary decisions made sequentially compound into\n reliable outcomes.\n{%#if hasSubagents%}\n\n---\n\n## Working with Multiple Agents\n\nYour workspace is configured for multi-agent workflows. Key rules:\n\n- **Subagents write to `tmp/`** \u2014 they return file paths and one-line summaries,\n never raw data in conversation.\n- **The main session is an orchestrator** \u2014 it tracks progress, launches agents,\n and checks output files. It does not hold subagent data in memory.\n- **Agent definitions live in `{%systemDir%}/agents/`** \u2014 each file becomes the\n system prompt for a subagent. Governance is baked into the definition.\n{%/if%}\n{%#if sessionHandoff%}\n\n---\n\n## Session Handoff\n\nThis workspace uses session handoff checklists. At the end of every session:\n\n1. Save all work to SSOT files\n2. Sync derived files\n3. Check SSOT integrity\n4. Update progress tracker\n5. Write next-session plan\n6. Run QC on session work\n\nThis ensures no context is lost between sessions.\n{%/if%}\n{%#if isResearch%}\n\n---\n\n## Research Tips\n\n- Write findings to disk immediately \u2014 don't accumulate in conversation\n- Cite sources for every claim\n- Separate facts from interpretation\n- Use `research/` for reference material, `artifacts/` for synthesis output\n{%/if%}\n{%#if isContent%}\n\n---\n\n## Content Workflow Tips\n\n- Every piece follows: draft \u2192 review \u2192 approve\n- Track revision feedback in dedicated files\n- Never finalize without explicit approval\n- Use `artifacts/` for final deliverables\n{%/if%}\n{%#if isDataProcessing%}\n\n---\n\n## Data Processing Tips\n\n- Define batch size before starting any processing run\n- Validate schema on every write\n- Checkpoint every 5 batch items\n- Log all transformations for auditability\n{%/if%}\n\n---\n\n*This workspace was generated by [Clawstrap](https://github.com/clawstrap/clawstrap).\nEdit any file to fit your needs \u2014 there's no lock-in.*\n";
222
+
223
+ // src/templates/gitignore.tmpl
224
+ var gitignore_default = "# Dependencies\nnode_modules/\n\n# Temporary files (subagent output, session data)\ntmp/\n\n# Secrets\n.env\n.env.*\ncredentials.json\n*.pem\n*.key\n\n# OS\n.DS_Store\nThumbs.db\n\n# Editor\n*.swp\n*.swo\n*~\n";
225
+
226
+ // src/templates/rules/context-discipline.md.tmpl
227
+ var context_discipline_md_default = "# Rule: Context Discipline\n> **Scope**: All sessions | **Generated**: {%generatedDate%}\n\n## Flush Cadence\n\nFlush working state to file {%flushCadence%}:\n- Write current state to a context checkpoint file\n- Include: what's done, what's next, accumulated results\n- Path: `context/checkpoint-{date}-{task}.md`\n{%#if hasSubagents%}\n\n## Subagent Output Rules\n\nSubagents MUST:\n1. Write all output to `tmp/{task}/{name}.json` (or `.md`)\n2. Return a one-line receipt: `\"Done. N items. File: {path}. Summary: {1 line}.\"`\n3. NOT dump raw results into the conversation\n\nMain session MUST:\n- Only read the subagent file if needed for the NEXT step\n- Never hold subagent output in conversation memory\n\n## Thin Orchestrator Principle\n\nDuring batch work, the main session is an orchestrator only:\n\n**DO**:\n- Track queue / done / next\n- Launch agents with correct prompts\n- Check output files for completeness\n- Update SSOT data files\n- Run QC gates\n\n**DO NOT**:\n- Read raw extraction data into main context\n- Classify results (agents do that)\n- Hold full subagent output in conversation\n{%/if%}\n\n## Before Batch Work\n\nAlways write an execution plan to `tmp/{task}/plan.md` before starting.\nThis file must survive context loss and be readable by any future session.\n\n## On User Correction\n\n1. FIRST write the correction to its durable home (memory/rule/skill file)\n2. THEN apply the correction to current work\n\nThis ensures the learning persists even if the session ends unexpectedly.\n";
228
+
229
+ // src/templates/rules/approval-first.md.tmpl
230
+ var approval_first_md_default = `# Rule: Approval-First Workflow
231
+ > **Scope**: All sessions | **Generated**: {%generatedDate%}
232
+
233
+ ## Core Principle
234
+
235
+ Plan work and surface it before executing. Always.
236
+
237
+ ## Rules
238
+
239
+ 1. **Plan before executing** \u2014 present your approach and get explicit approval
240
+ before making changes. No speculative actions.
241
+
242
+ 2. **No shortcuts** \u2014 never skip a step because it seems tedious or obvious.
243
+ Every step exists for a reason.
244
+
245
+ 3. **Scope changes require re-approval** \u2014 if the task changes mid-execution,
246
+ pause and re-confirm the new scope before continuing.
247
+
248
+ 4. **State verification** \u2014 before modifying any file, read its current state.
249
+ Don't assume you know what's in a file from earlier in the conversation.
250
+
251
+ 5. **Destructive actions require explicit confirmation** \u2014 deleting files,
252
+ overwriting data, or resetting state always needs a direct "yes" from the user.
253
+ `;
254
+
255
+ // src/templates/rules/quality-gates.md.tmpl
256
+ var quality_gates_md_default = `# Rule: Quality Gates
257
+ > **Scope**: All sessions | **Generated**: {%generatedDate%}
258
+
259
+ ## Core Principle
260
+
261
+ Quality is a structural gate in the execution loop, not a phase run at the end.
262
+
263
+ ## Gates
264
+
265
+ ### Write-Time Validation
266
+ - Validate schema/structure on every file write
267
+ - Malformed output is caught immediately, not discovered later
268
+
269
+ ### Checkpoint Reviews (Ralph Loop)
270
+ - Every 5 outputs: stop and review the most complex item in the batch
271
+ - If quality grade is below B: fix the issue and rerun before proceeding
272
+ - This is mandatory \u2014 not optional, not skippable
273
+ {%#if isProductionQuality%}
274
+
275
+ ### Multi-Agent Agreement
276
+ - When multiple agents classify the same item, compare results
277
+ - Disagreements are escalated \u2014 they reveal ambiguity in the rules, not agent failure
278
+ - Resolution: clarify the rule, don't add more rules
279
+
280
+ ### Combined Agent Pattern
281
+ - Combined agents (extract + classify in one pass) beat split agents on quality
282
+ - Only split into separate agents when the task genuinely requires different tools
283
+ {%/if%}
284
+
285
+ ### Human Review
286
+ - Human review is the final gate \u2014 all results surface to the user
287
+ - No output is marked "complete" without human confirmation
288
+ - Low-confidence results must be flagged, never silently passed through
289
+ `;
290
+
291
+ // src/templates/skills/SKILL_REGISTRY.md.tmpl
292
+ var SKILL_REGISTRY_md_default = "# Skill Registry \u2014 {%workspaceName%}\n> Index of all skills in this workspace. Update as skills are added.\n\n## What Are Skills?\n\nSkills are specialized procedures stored in `{%systemDir%}/skills/*/SKILL.md`.\nThey provide domain-specific instructions that load on demand \u2014 only when\nthe relevant task is triggered.\n\n## Registered Skills\n\n| Skill | Path | Trigger |\n|-------|------|---------|\n| *(none yet)* | \u2014 | \u2014 |\n\n## Adding a Skill\n\n1. Create a directory: `{%systemDir%}/skills/{skill-name}/`\n2. Add a `SKILL.md` file with frontmatter and procedures\n3. Register it in this file\n4. Reference the trigger phrase in `{%governanceFile%}` if needed\n";
293
+
294
+ // src/templates/agents/primary-agent.md.tmpl
295
+ var primary_agent_md_default = "# Agent: Primary Orchestrator\n> **Workspace**: {%workspaceName%} | **Generated**: {%generatedDate%}\n\n## Identity\n\nYou are the primary orchestrator agent for the {%workspaceName%} workspace,\nconfigured for {%workloadLabel%} workflows.\n\n## Role\n\n- Coordinate work across subagents\n- Track progress and maintain execution plans\n- Ensure all output meets quality standards\n- Escalate ambiguity \u2014 never suppress disagreements\n\n## Rules\n\n1. **You are an orchestrator, not a worker.** Launch subagents for extraction,\n classification, and generation tasks. Do not hold their raw output.\n\n2. **Subagent output goes to files.** Every subagent writes to\n `tmp/{task}/{name}.json` and returns a one-line receipt.\n\n3. **Check before proceeding.** After every subagent completes, verify the\n output file exists and has the expected structure.\n\n4. **QC is your responsibility.** Run the Ralph Loop every 5 items:\n pick the most complex item, review it, fix issues before continuing.\n\n## Output Format\n\nWhen reporting to the user:\n```\nStatus: {done}/{total} items complete\nQC: last check at item #{n}, grade: {A/B/C}\nNext: {what happens next}\nIssues: {any blockers or flags}\n```\n\n## Approved Tools\n\n- Read / Write / Edit files\n- Launch subagents (using agent definitions in `{%systemDir%}/agents/`)\n- Web search and fetch (with domain restrictions)\n\n**Not approved without explicit permission:**\n- Installing packages or plugins\n- Modifying files outside this workspace\n- Running destructive commands\n";
296
+
297
+ // src/templates/agents/agent-template.md.tmpl
298
+ var agent_template_md_default = '# Agent: [Name]\n> **Purpose**: [one-line description]\n> **Workspace**: {%workspaceName%}\n\n## Identity\n\nYou are a subagent in the {%workspaceName%} workspace. You have a focused task\nand strict output requirements.\n\n## Task\n\n*(Describe the specific task this agent performs)*\n\n## Input\n\n*(What this agent receives \u2014 file paths, structured data, etc.)*\n\n## Output\n\nWrite results to: `tmp/{task}/{name}.json`\n\nReturn to orchestrator:\n```\nDone. {N} items. File: {path}. Summary: {1-line summary}.\n```\n\n## Output Schema\n\n```json\n{\n "items": [\n {\n "id": "",\n "result": "",\n "confidence": "high | medium | low",\n "source": ""\n }\n ],\n "metadata": {\n "total": 0,\n "processed": 0,\n "errors": 0\n }\n}\n```\n\n## Rules\n\n1. Write ALL output to the specified file \u2014 never return raw data in conversation\n2. Include confidence scores for every item\n3. Flag low-confidence items explicitly\n4. If you encounter ambiguity, stop and report it \u2014 do not guess\n\n## Approved Tools\n\n- Read files (input only)\n- Write to `tmp/` (output only)\n- *(Add task-specific tools here)*\n';
299
+
300
+ // src/templates/subagent-bootstrap.md.tmpl
301
+ var subagent_bootstrap_md_default = '# Subagent Bootstrap \u2014 Lightweight Governance\n> For ad-hoc subagents that don\'t have a dedicated agent definition file.\n\nWhen spawning a quick subagent without a full definition, include these rules\nin the prompt:\n\n```\nYou are a subagent in the {%workspaceName%} workspace.\n\nRules:\n1. Write ALL output to tmp/{task}/{name}.json\n2. Return a one-line receipt: "Done. N items. File: {path}. Summary: {1 line}."\n3. Do NOT dump raw results into the conversation\n4. Include confidence scores where applicable\n5. Flag ambiguity \u2014 do not guess\n6. Stay within your assigned task scope\n```\n\nThis ensures governance even for agents created on the fly.\n';
302
+
303
+ // src/templates/gotcha-log.md.tmpl
304
+ var gotcha_log_md_default = "# Gotcha Log \u2014 {%workspaceName%}\n> Incident log tracking why rules exist. Every entry = a real failure that happened.\n\n## Format\n\n```\n### G001 \u2014 [Short title]\n**Date**: YYYY-MM-DD\n**What happened**: [1-2 sentences]\n**Root cause**: [Why it happened]\n**Fix**: [What rule/process was added]\n**Rule ref**: [Link to the rule that prevents recurrence]\n```\n\n## Entries\n\n*(None yet \u2014 add entries as issues arise)*\n";
305
+
306
+ // src/templates/future-considerations.md.tmpl
307
+ var future_considerations_md_default = "# Future Considerations \u2014 {%workspaceName%}\n> Ideas and improvements deferred from current work. Not a backlog \u2014 a parking lot.\n\n## Format\n\n```\n### F001 \u2014 [Short title]\n**Added**: YYYY-MM-DD\n**Context**: [Why this came up]\n**Idea**: [What could be done]\n**Priority**: low | medium | high\n```\n\n## Entries\n\n*(None yet \u2014 add entries as ideas arise)*\n";
308
+
309
+ // src/templates/memory/MEMORY.md.tmpl
310
+ var MEMORY_md_default = "# Memory Index \u2014 {%workspaceName%}\n> Persistent memory across sessions. Each entry links to a memory file.\n> Keep this index concise \u2014 one line per entry, under 150 characters.\n\n## Entries\n\n*(None yet \u2014 memories are added as sessions progress)*\n";
311
+
312
+ // src/templates/projects/project-readme.md.tmpl
313
+ var project_readme_md_default = '# Project: [Name]\n> **Status**: active | paused | complete | archived\n> **Started**: YYYY-MM-DD | **Last session**: YYYY-MM-DD\n\n---\n\n## What This Project Is\n\n*(2-3 sentences. What\'s the goal? What does "done" look like?)*\n\n---\n\n## Key Files\n\n| File | Role |\n|------|------|\n| `process.md` | Workflow, hooks, session checklist |\n\n---\n\n## SSOT Map\n\n*(Every piece of data has exactly one authoritative location. List them here.)*\n\n| Data | SSOT File | Derived Files |\n|------|-----------|---------------|\n| *(none yet)* | \u2014 | \u2014 |\n\n---\n\n## Current Status\n\n**Last done**: \u2014\n**Next**: \u2014\n';
314
+
315
+ // src/templates/projects/project-process.md.tmpl
316
+ var project_process_md_default = '# Process: [Project Name]\n> Workflow, hooks, and session checklist for this project.\n> Update this file as the workflow evolves.\n\n---\n{%#if sessionHandoff%}\n## Session Start Checklist (Pre-Hooks)\n\nRun these at the start of every session on this project:\n\n1. [ ] Read `README.md` \u2014 confirm current status and next step\n2. [ ] Read `{%governanceFile%}` (auto-loaded, but confirm it\'s current)\n3. [ ] Check `tmp/{task}/plan.md` if a batch is in progress\n4. [ ] Confirm SSOT files are in expected state (no stale data)\n\n---\n\n## Session End Checklist (Post-Hooks)\n\nRun these at the end of every session (mandatory \u2014 not optional):\n\n1. [ ] Save all work to SSOT files\n2. [ ] Sync derived files (regenerate from SSOTs)\n3. [ ] SSOT integrity check (no duplicates, no stale data)\n4. [ ] Update `README.md` \u2192 "Last done" and "Next" fields\n5. [ ] Write next-session plan to `tmp/{task}/plan.md`\n6. [ ] Run QC on work done this session\n\n---\n{%/if%}\n\n## Execution Workflow\n\n*(Describe the main execution loop for this project\'s primary task)*\n{%#if isResearch%}\n```\n1. Define research question \u2192 get approval\n2. Gather sources \u2192 write findings to file immediately\n3. Synthesize \u2192 cite all sources\n4. QC gate: {%flushCadence%}\n5. Output: artifacts/{topic}/synthesis.md\n```\n{%/if%}\n{%#if isContent%}\n```\n1. Brief \u2192 get approval on scope and outline\n2. Draft \u2192 write to artifacts/\n3. Review \u2192 collect feedback in dedicated file\n4. Revise \u2192 approval required before finalizing\n5. QC gate: {%flushCadence%}\n```\n{%/if%}\n{%#if isDataProcessing%}\n```\n1. Plan \u2192 get approval \u2192 define batch size\n2. Process batch \u2192 validate schema on every write\n3. QC gate: {%flushCadence%} (Ralph Loop)\n4. Output: tmp/{task}/{name}.json\n5. Human review \u2192 finalize\n```\n{%/if%}\n{%#if isCustom%}\n```\n1. Plan \u2192 get approval \u2192 execute\n2. Batch size: [N items per batch]\n3. QC gate: {%flushCadence%}\n4. Output: tmp/{task}/{name}.json\n```\n{%/if%}\n\n---\n{%#if hasQualityGates%}\n## Active Batch Tracking\n\n| Batch | Status | QC Grade | Notes |\n|-------|--------|----------|-------|\n| *(none yet)* | \u2014 | \u2014 | \u2014 |\n\n---\n{%/if%}\n\n## Known Gotchas (Project-Specific)\n\n*(1-2 line summaries of project-specific failure modes)*\n\n*(None yet \u2014 add entries as issues arise)*\n';
317
+
318
+ // src/templates/agents/new-agent.md.tmpl
319
+ var new_agent_md_default = "# Agent: {%agentName%}\n> **Purpose**: {%agentDescription%}\n> **Workspace**: {%workspaceName%}\n\n## Identity\n\nYou are {%agentName%}, a {%agentRole%} agent in the {%workspaceName%} workspace.\n\n## Task\n\n*(Define this agent's specific task)*\n\n## Input\n\n*(What this agent receives \u2014 file paths, structured data, etc.)*\n\n## Output\n\nWrite results to: `tmp/{task}/{%agentName%}.json`\n\nReturn to orchestrator:\n```\nDone. {N} items. File: {path}. Summary: {1-line summary}.\n```\n\n## Rules\n\n1. Write ALL output to the specified file \u2014 never return raw data in conversation\n2. Include confidence scores for every item\n3. Flag low-confidence items explicitly\n4. If you encounter ambiguity, stop and report it \u2014 do not guess\n5. Stay within your assigned task scope\n\n## Approved Tools\n\n- Read files (input only)\n- Write to `tmp/` (output only)\n- *(Add task-specific tools here)*\n";
320
+
321
+ // src/templates/skills/new-skill.md.tmpl
322
+ var new_skill_md_default = "# Skill: {%skillName%}\n> {%skillDescription%}\n> **Triggers**: {%skillTriggers%}\n\n## When to Use\n\n*(Describe when this skill should be activated)*\n\n## Procedure\n\n*(Step-by-step instructions for executing this skill)*\n\n1. Step one\n2. Step two\n3. Step three\n\n## Quality Checks\n\n*(How to verify this skill's output is correct)*\n\n- [ ] Output matches expected format\n- [ ] No data loss or corruption\n- [ ] Results reviewed before marking complete\n";
323
+
324
+ // src/templates/projects/add-project-readme.md.tmpl
325
+ var add_project_readme_md_default = "# Project: {%projectName%}\n> **Status**: active\n> **Started**: {%generatedDate%} | **Last session**: {%generatedDate%}\n\n---\n\n## What This Project Is\n\n{%projectDescription%}\n\n---\n\n## Key Files\n\n| File | Role |\n|------|------|\n| `process.md` | Workflow, hooks, session checklist |\n\n---\n\n## SSOT Map\n\n*(Every piece of data has exactly one authoritative location. List them here.)*\n\n| Data | SSOT File | Derived Files |\n|------|-----------|---------------|\n| *(none yet)* | \u2014 | \u2014 |\n\n---\n\n## Current Status\n\n**Last done**: Project created.\n**Next**: \u2014\n";
326
+
327
+ // src/templates/projects/add-project-process.md.tmpl
328
+ var add_project_process_md_default = '# Process: {%projectName%}\n> Workflow, hooks, and session checklist for this project.\n> Update this file as the workflow evolves.\n\n---\n{%#if sessionHandoff%}\n## Session Start Checklist (Pre-Hooks)\n\nRun these at the start of every session on this project:\n\n1. [ ] Read `README.md` \u2014 confirm current status and next step\n2. [ ] Read `{%governanceFile%}` (auto-loaded, but confirm it\'s current)\n3. [ ] Check `tmp/{task}/plan.md` if a batch is in progress\n4. [ ] Confirm SSOT files are in expected state (no stale data)\n\n---\n\n## Session End Checklist (Post-Hooks)\n\nRun these at the end of every session (mandatory \u2014 not optional):\n\n1. [ ] Save all work to SSOT files\n2. [ ] Sync derived files (regenerate from SSOTs)\n3. [ ] SSOT integrity check (no duplicates, no stale data)\n4. [ ] Update `README.md` \u2192 "Last done" and "Next" fields\n5. [ ] Write next-session plan to `tmp/{task}/plan.md`\n6. [ ] Run QC on work done this session\n\n---\n{%/if%}\n\n## Execution Workflow\n\n*(Describe the main execution loop for this project\'s primary task)*\n{%#if isResearch%}\n```\n1. Define research question \u2192 get approval\n2. Gather sources \u2192 write findings to file immediately\n3. Synthesize \u2192 cite all sources\n4. QC gate: {%flushCadence%}\n5. Output: artifacts/{topic}/synthesis.md\n```\n{%/if%}\n{%#if isContent%}\n```\n1. Brief \u2192 get approval on scope and outline\n2. Draft \u2192 write to artifacts/\n3. Review \u2192 collect feedback in dedicated file\n4. Revise \u2192 approval required before finalizing\n5. QC gate: {%flushCadence%}\n```\n{%/if%}\n{%#if isDataProcessing%}\n```\n1. Plan \u2192 get approval \u2192 define batch size\n2. Process batch \u2192 validate schema on every write\n3. QC gate: {%flushCadence%} (Ralph Loop)\n4. Output: tmp/{task}/{name}.json\n5. Human review \u2192 finalize\n```\n{%/if%}\n{%#if isCustom%}\n```\n1. Plan \u2192 get approval \u2192 execute\n2. Batch size: [N items per batch]\n3. QC gate: {%flushCadence%}\n4. Output: tmp/{task}/{name}.json\n```\n{%/if%}\n\n---\n{%#if hasQualityGates%}\n## Active Batch Tracking\n\n| Batch | Status | QC Grade | Notes |\n|-------|--------|----------|-------|\n| *(none yet)* | \u2014 | \u2014 | \u2014 |\n\n---\n{%/if%}\n\n## Known Gotchas (Project-Specific)\n\n*(1-2 line summaries of project-specific failure modes)*\n\n*(None yet \u2014 add entries as issues arise)*\n';
329
+
330
+ // src/templates/index.ts
331
+ var templates = {
332
+ governanceFile: governance_file_md_default,
333
+ gettingStarted: getting_started_md_default,
334
+ gitignore: gitignore_default,
335
+ contextDiscipline: context_discipline_md_default,
336
+ approvalFirst: approval_first_md_default,
337
+ qualityGates: quality_gates_md_default,
338
+ skillRegistry: SKILL_REGISTRY_md_default,
339
+ primaryAgent: primary_agent_md_default,
340
+ agentTemplate: agent_template_md_default,
341
+ subagentBootstrap: subagent_bootstrap_md_default,
342
+ gotchaLog: gotcha_log_md_default,
343
+ futureConsiderations: future_considerations_md_default,
344
+ memoryIndex: MEMORY_md_default,
345
+ projectReadme: project_readme_md_default,
346
+ projectProcess: project_process_md_default,
347
+ newAgent: new_agent_md_default,
348
+ newSkill: new_skill_md_default,
349
+ addProjectReadme: add_project_readme_md_default,
350
+ addProjectProcess: add_project_process_md_default
351
+ };
352
+
353
+ // src/writer.ts
354
+ function writeWorkspace(targetDir, vars, config) {
355
+ const filesWritten = [];
356
+ const dirsCreated = [];
357
+ try {
358
+ for (const entry of OUTPUT_MANIFEST) {
359
+ if (entry.condition && !vars[entry.condition]) {
360
+ continue;
361
+ }
362
+ const template = templates[entry.templateKey];
363
+ if (!template) {
364
+ throw new Error(`Template not found: ${entry.templateKey}`);
365
+ }
366
+ const resolvedPath = render(entry.outputPath, vars);
367
+ const fullPath = import_node_path.default.join(targetDir, resolvedPath);
368
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(fullPath), { recursive: true });
369
+ const content = render(template, vars);
370
+ import_node_fs.default.writeFileSync(fullPath, content, "utf-8");
371
+ filesWritten.push(resolvedPath);
372
+ }
373
+ for (const dir of EMPTY_DIRS) {
374
+ const fullDir = import_node_path.default.join(targetDir, dir);
375
+ import_node_fs.default.mkdirSync(fullDir, { recursive: true });
376
+ dirsCreated.push(dir);
377
+ if (dir === "tmp") {
378
+ import_node_fs.default.writeFileSync(
379
+ import_node_path.default.join(fullDir, ".gitignore"),
380
+ "*\n!.gitignore\n",
381
+ "utf-8"
382
+ );
383
+ } else {
384
+ import_node_fs.default.writeFileSync(import_node_path.default.join(fullDir, ".gitkeep"), "", "utf-8");
385
+ }
386
+ }
387
+ const configPath = import_node_path.default.join(targetDir, ".clawstrap.json");
388
+ import_node_fs.default.writeFileSync(
389
+ configPath,
390
+ JSON.stringify(config, null, 2) + "\n",
391
+ "utf-8"
392
+ );
393
+ filesWritten.push(".clawstrap.json");
394
+ } catch (error) {
395
+ if (filesWritten.length > 0) {
396
+ console.error(
397
+ `
398
+ Error during workspace generation. ${filesWritten.length} file(s) were written before failure:`
399
+ );
400
+ for (const f of filesWritten) {
401
+ console.error(` - ${f}`);
402
+ }
403
+ }
404
+ throw error;
405
+ }
406
+ return { filesWritten, dirsCreated };
407
+ }
408
+
409
+ // src/prompts.ts
410
+ var import_prompts = require("@inquirer/prompts");
411
+ var import_node_path2 = __toESM(require("path"), 1);
412
+ async function runPrompts() {
413
+ console.log("\nWelcome to Clawstrap \u{1F528}");
414
+ console.log("Scaffold a production-ready AI agent workspace.\n");
415
+ const workspaceName = await (0, import_prompts.input)({
416
+ message: "Workspace name:",
417
+ default: import_node_path2.default.basename(process.cwd()),
418
+ validate: (v) => v.length > 0 || "Name is required"
419
+ });
420
+ const workloadType = await (0, import_prompts.select)({
421
+ message: "What kind of work will this workspace handle?",
422
+ choices: [
423
+ {
424
+ name: "Research & analysis pipeline",
425
+ value: "research"
426
+ },
427
+ {
428
+ name: "Content & writing system",
429
+ value: "content"
430
+ },
431
+ {
432
+ name: "Data processing workflow",
433
+ value: "data-processing"
434
+ },
435
+ {
436
+ name: "Custom / general purpose",
437
+ value: "custom"
438
+ }
439
+ ]
440
+ });
441
+ const parallelAgents = await (0, import_prompts.select)({
442
+ message: "How many AI agents will work in parallel?",
443
+ choices: [
444
+ {
445
+ name: "Just me \u2014 single agent",
446
+ value: "single"
447
+ },
448
+ {
449
+ name: "Small team \u2014 2 to 5 agents",
450
+ value: "small-team"
451
+ },
452
+ {
453
+ name: "Production \u2014 5+ agents, orchestrator pattern",
454
+ value: "production"
455
+ }
456
+ ]
457
+ });
458
+ const qualityLevel = await (0, import_prompts.select)({
459
+ message: "What quality controls do you need?",
460
+ choices: [
461
+ {
462
+ name: "Solo \u2014 lightweight checks, fast iteration",
463
+ value: "solo"
464
+ },
465
+ {
466
+ name: "Team \u2014 structured QC gates, review steps",
467
+ value: "team"
468
+ },
469
+ {
470
+ name: "Production \u2014 full QC pipeline, batch monitoring",
471
+ value: "production"
472
+ }
473
+ ]
474
+ });
475
+ const sessionHandoff = await (0, import_prompts.confirm)({
476
+ message: "Enable session handoff checklists? (for multi-session work)",
477
+ default: true
478
+ });
479
+ return {
480
+ workspaceName,
481
+ workloadType,
482
+ parallelAgents,
483
+ qualityLevel,
484
+ sessionHandoff
485
+ };
486
+ }
487
+ function getDefaults(targetDir) {
488
+ const name = targetDir ? import_node_path2.default.basename(import_node_path2.default.resolve(targetDir)) : import_node_path2.default.basename(process.cwd());
489
+ return {
490
+ workspaceName: name,
491
+ workloadType: "custom",
492
+ parallelAgents: "single",
493
+ qualityLevel: "solo",
494
+ sessionHandoff: true
495
+ };
496
+ }
497
+
498
+ // src/init.ts
499
+ var WORKLOAD_LABELS2 = {
500
+ research: "Research & Analysis",
501
+ content: "Content & Writing",
502
+ "data-processing": "Data Processing",
503
+ custom: "General Purpose"
504
+ };
505
+ async function init(directory, options) {
506
+ const targetDir = import_node_path3.default.resolve(directory);
507
+ if (import_node_fs2.default.existsSync(targetDir)) {
508
+ const hasClawstrap = import_node_fs2.default.existsSync(
509
+ import_node_path3.default.join(targetDir, ".clawstrap.json")
510
+ );
511
+ if (hasClawstrap) {
512
+ if (options.yes) {
513
+ console.error(
514
+ "\nError: directory already has .clawstrap.json. Use interactive mode to confirm overwrite.\n"
515
+ );
516
+ process.exit(1);
517
+ }
518
+ const proceed = await (0, import_prompts2.confirm)({
519
+ message: "This directory already has a .clawstrap.json. Overwrite generated files?",
520
+ default: false
521
+ });
522
+ if (!proceed) {
523
+ console.log("Aborted.\n");
524
+ return;
525
+ }
526
+ }
527
+ } else {
528
+ import_node_fs2.default.mkdirSync(targetDir, { recursive: true });
529
+ }
530
+ const answers = options.yes ? getDefaults(directory) : await runPrompts();
531
+ const config = ClawstrapConfigSchema.parse({
532
+ version: "1.0.0",
533
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
534
+ workspaceName: answers.workspaceName,
535
+ targetDirectory: directory,
536
+ aiSystem: "claude-code",
537
+ workloadType: answers.workloadType,
538
+ parallelAgents: answers.parallelAgents,
539
+ qualityLevel: answers.qualityLevel,
540
+ sessionHandoff: answers.sessionHandoff
541
+ });
542
+ console.log("\nConfiguration:");
543
+ console.log(` Workspace: ${config.workspaceName}`);
544
+ console.log(` Workload: ${WORKLOAD_LABELS2[config.workloadType]}`);
545
+ console.log(` Parallel agents: ${config.parallelAgents}`);
546
+ console.log(` Quality level: ${config.qualityLevel}`);
547
+ console.log(
548
+ ` Session handoff: ${config.sessionHandoff ? "yes" : "no"}`
549
+ );
550
+ const vars = deriveTemplateVars(config);
551
+ console.log("\nGenerating your workspace...\n");
552
+ const result = writeWorkspace(targetDir, vars, config);
553
+ for (const file of result.filesWritten) {
554
+ console.log(` \u2713 ${file}`);
555
+ }
556
+ for (const dir of result.dirsCreated) {
557
+ console.log(` \u2713 ${dir}/`);
558
+ }
559
+ console.log(`
560
+ Done. Open GETTING_STARTED.md to begin.
561
+ `);
562
+ }
563
+
564
+ // src/add-agent.ts
565
+ var import_node_fs4 = __toESM(require("fs"), 1);
566
+ var import_node_path5 = __toESM(require("path"), 1);
567
+ var import_prompts4 = require("@inquirer/prompts");
568
+
569
+ // src/load-workspace.ts
570
+ var import_node_fs3 = __toESM(require("fs"), 1);
571
+ var import_node_path4 = __toESM(require("path"), 1);
572
+ var import_zod2 = require("zod");
573
+ function loadWorkspace(fromDir) {
574
+ const dir = fromDir ? import_node_path4.default.resolve(fromDir) : process.cwd();
575
+ const configPath = import_node_path4.default.join(dir, ".clawstrap.json");
576
+ if (!import_node_fs3.default.existsSync(configPath)) {
577
+ console.error(
578
+ "\nError: .clawstrap.json not found. Run `clawstrap init` first.\n"
579
+ );
580
+ process.exit(1);
581
+ }
582
+ let raw;
583
+ try {
584
+ raw = JSON.parse(import_node_fs3.default.readFileSync(configPath, "utf-8"));
585
+ } catch {
586
+ console.error(
587
+ "\nError: .clawstrap.json contains invalid JSON. Fix or delete it and run `clawstrap init`.\n"
588
+ );
589
+ process.exit(1);
590
+ }
591
+ let config;
592
+ try {
593
+ config = ClawstrapConfigSchema.parse(raw);
594
+ } catch (err) {
595
+ console.error("\nError: .clawstrap.json has an invalid schema.");
596
+ if (err instanceof import_zod2.ZodError) {
597
+ for (const issue of err.issues) {
598
+ console.error(` - ${issue.path.join(".")}: ${issue.message}`);
599
+ }
600
+ }
601
+ console.error("\nFix the file or delete it and run `clawstrap init`.\n");
602
+ process.exit(1);
603
+ }
604
+ const vars = deriveTemplateVars(config);
605
+ return { config, vars, rootDir: dir };
606
+ }
607
+
608
+ // src/add-agent.ts
609
+ async function addAgent(name) {
610
+ const { vars, rootDir } = loadWorkspace();
611
+ const systemDir = String(vars.systemDir);
612
+ const outPath = import_node_path5.default.join(rootDir, systemDir, "agents", `${name}.md`);
613
+ if (import_node_fs4.default.existsSync(outPath)) {
614
+ console.error(`
615
+ Error: agent "${name}" already exists at ${systemDir}/agents/${name}.md
616
+ `);
617
+ process.exit(1);
618
+ }
619
+ const description = await (0, import_prompts4.input)({
620
+ message: "Agent description (one line):",
621
+ validate: (v) => v.length > 0 || "Description is required"
622
+ });
623
+ const role = await (0, import_prompts4.select)({
624
+ message: "Agent role:",
625
+ choices: [
626
+ { name: "Worker \u2014 performs a specific task", value: "worker" },
627
+ { name: "Orchestrator \u2014 coordinates other agents", value: "orchestrator" },
628
+ { name: "Reviewer \u2014 validates and QCs output", value: "reviewer" }
629
+ ]
630
+ });
631
+ const agentVars = {
632
+ ...vars,
633
+ agentName: name,
634
+ agentDescription: description,
635
+ agentRole: role
636
+ };
637
+ const content = render(templates.newAgent, agentVars);
638
+ import_node_fs4.default.mkdirSync(import_node_path5.default.dirname(outPath), { recursive: true });
639
+ import_node_fs4.default.writeFileSync(outPath, content, "utf-8");
640
+ console.log(`
641
+ \u2713 ${systemDir}/agents/${name}.md
642
+ `);
643
+ }
644
+
645
+ // src/add-skill.ts
646
+ var import_node_fs5 = __toESM(require("fs"), 1);
647
+ var import_node_path6 = __toESM(require("path"), 1);
648
+ var import_prompts5 = require("@inquirer/prompts");
649
+ async function addSkill(name) {
650
+ const { vars, rootDir } = loadWorkspace();
651
+ const systemDir = String(vars.systemDir);
652
+ const skillDir = import_node_path6.default.join(rootDir, systemDir, "skills", name);
653
+ if (import_node_fs5.default.existsSync(skillDir)) {
654
+ console.error(`
655
+ Error: skill "${name}" already exists at ${systemDir}/skills/${name}/
656
+ `);
657
+ process.exit(1);
658
+ }
659
+ const description = await (0, import_prompts5.input)({
660
+ message: "Skill description (one line):",
661
+ validate: (v) => v.length > 0 || "Description is required"
662
+ });
663
+ const triggers = await (0, import_prompts5.input)({
664
+ message: "Trigger phrases (comma-separated):",
665
+ validate: (v) => v.length > 0 || "At least one trigger is required"
666
+ });
667
+ const skillVars = {
668
+ ...vars,
669
+ skillName: name,
670
+ skillDescription: description,
671
+ skillTriggers: triggers
672
+ };
673
+ import_node_fs5.default.mkdirSync(skillDir, { recursive: true });
674
+ const content = render(templates.newSkill, skillVars);
675
+ import_node_fs5.default.writeFileSync(import_node_path6.default.join(skillDir, "SKILL.md"), content, "utf-8");
676
+ const registryPath = import_node_path6.default.join(rootDir, systemDir, "skills", "SKILL_REGISTRY.md");
677
+ if (import_node_fs5.default.existsSync(registryPath)) {
678
+ let registry = import_node_fs5.default.readFileSync(registryPath, "utf-8");
679
+ const newRow = `| ${name} | \`${systemDir}/skills/${name}/SKILL.md\` | ${triggers} |`;
680
+ if (registry.includes("*(none yet)*")) {
681
+ registry = registry.replace(
682
+ /\| \*\(none yet\)\* \| — \| — \|/,
683
+ newRow
684
+ );
685
+ } else {
686
+ const headingIdx = registry.indexOf("## Registered Skills");
687
+ if (headingIdx === -1) return;
688
+ const tableStart = registry.indexOf("|", headingIdx);
689
+ const afterTable = registry.substring(tableStart);
690
+ const blankLine = afterTable.search(/\n\n/);
691
+ const insertPos = blankLine !== -1 ? tableStart + blankLine + 1 : registry.length;
692
+ registry = registry.slice(0, insertPos) + newRow + "\n" + registry.slice(insertPos);
693
+ }
694
+ import_node_fs5.default.writeFileSync(registryPath, registry, "utf-8");
695
+ }
696
+ console.log(`
697
+ \u2713 ${systemDir}/skills/${name}/SKILL.md`);
698
+ console.log(` \u2713 ${systemDir}/skills/SKILL_REGISTRY.md (updated)
699
+ `);
700
+ }
701
+
702
+ // src/add-project.ts
703
+ var import_node_fs6 = __toESM(require("fs"), 1);
704
+ var import_node_path7 = __toESM(require("path"), 1);
705
+ var import_prompts6 = require("@inquirer/prompts");
706
+ async function addProject(name) {
707
+ const { vars, rootDir } = loadWorkspace();
708
+ const projectDir = import_node_path7.default.join(rootDir, "projects", name);
709
+ if (import_node_fs6.default.existsSync(projectDir)) {
710
+ console.error(`
711
+ Error: project "${name}" already exists at projects/${name}/
712
+ `);
713
+ process.exit(1);
714
+ }
715
+ const description = await (0, import_prompts6.input)({
716
+ message: "Project description (1-2 sentences):",
717
+ validate: (v) => v.length > 0 || "Description is required"
718
+ });
719
+ const projectVars = {
720
+ ...vars,
721
+ projectName: name,
722
+ projectDescription: description
723
+ };
724
+ import_node_fs6.default.mkdirSync(projectDir, { recursive: true });
725
+ const readme = render(templates.addProjectReadme, projectVars);
726
+ import_node_fs6.default.writeFileSync(import_node_path7.default.join(projectDir, "README.md"), readme, "utf-8");
727
+ const process_ = render(templates.addProjectProcess, projectVars);
728
+ import_node_fs6.default.writeFileSync(import_node_path7.default.join(projectDir, "process.md"), process_, "utf-8");
729
+ console.log(`
730
+ \u2713 projects/${name}/README.md`);
731
+ console.log(` \u2713 projects/${name}/process.md
732
+ `);
733
+ }
734
+
735
+ // src/status.ts
736
+ var import_node_fs7 = __toESM(require("fs"), 1);
737
+ var import_node_path8 = __toESM(require("path"), 1);
738
+ var WORKLOAD_LABELS3 = {
739
+ research: "Research & Analysis",
740
+ content: "Content & Writing",
741
+ "data-processing": "Data Processing",
742
+ custom: "General Purpose"
743
+ };
744
+ function countEntries(dir, exclude = []) {
745
+ if (!import_node_fs7.default.existsSync(dir)) return 0;
746
+ return import_node_fs7.default.readdirSync(dir, { withFileTypes: true }).filter((e) => !exclude.includes(e.name) && !e.name.startsWith(".")).length;
747
+ }
748
+ function countSkills(skillsDir) {
749
+ if (!import_node_fs7.default.existsSync(skillsDir)) return 0;
750
+ return import_node_fs7.default.readdirSync(skillsDir, { withFileTypes: true }).filter(
751
+ (e) => e.isDirectory() && import_node_fs7.default.existsSync(import_node_path8.default.join(skillsDir, e.name, "SKILL.md"))
752
+ ).length;
753
+ }
754
+ async function showStatus() {
755
+ const { config, vars, rootDir } = loadWorkspace();
756
+ const systemDir = String(vars.systemDir);
757
+ const agentsDir = import_node_path8.default.join(rootDir, systemDir, "agents");
758
+ const skillsDir = import_node_path8.default.join(rootDir, systemDir, "skills");
759
+ const rulesDir = import_node_path8.default.join(rootDir, systemDir, "rules");
760
+ const projectsDir = import_node_path8.default.join(rootDir, "projects");
761
+ const agentCount = countEntries(agentsDir, ["_template.md"]);
762
+ const skillCount = countSkills(skillsDir);
763
+ const projectCount = countEntries(projectsDir, ["_template"]);
764
+ const ruleCount = countEntries(rulesDir);
765
+ const date = config.createdAt.split("T")[0];
766
+ console.log(`
767
+ Clawstrap Workspace: ${config.workspaceName}`);
768
+ console.log(`Created: ${date} | Version: ${config.version}`);
769
+ console.log(`
770
+ Configuration:`);
771
+ console.log(
772
+ ` Workload: ${WORKLOAD_LABELS3[config.workloadType] ?? config.workloadType}`
773
+ );
774
+ console.log(` Parallel agents: ${config.parallelAgents}`);
775
+ console.log(` Quality level: ${config.qualityLevel}`);
776
+ console.log(
777
+ ` Session handoff: ${config.sessionHandoff ? "yes" : "no"}`
778
+ );
779
+ console.log(`
780
+ Structure:`);
781
+ console.log(` Agents: ${agentCount} (${systemDir}/agents/)`);
782
+ console.log(` Skills: ${skillCount} (${systemDir}/skills/)`);
783
+ console.log(` Projects: ${projectCount} (projects/)`);
784
+ console.log(` Rules: ${ruleCount} (${systemDir}/rules/)`);
785
+ if (config.lastExport) {
786
+ const exportDate = config.lastExport.exportedAt.split("T")[0];
787
+ console.log(`
788
+ Last Export:`);
789
+ console.log(` Format: ${config.lastExport.format}`);
790
+ console.log(` Date: ${exportDate}`);
791
+ console.log(` Output: ${config.lastExport.outputDir}`);
792
+ }
793
+ console.log();
794
+ }
795
+
796
+ // src/export-paperclip.ts
797
+ var import_node_fs12 = __toESM(require("fs"), 1);
798
+ var import_node_path13 = __toESM(require("path"), 1);
799
+ var import_prompts7 = require("@inquirer/prompts");
800
+
801
+ // src/export-paperclip/translate-agents.ts
802
+ var import_node_fs8 = __toESM(require("fs"), 1);
803
+ var import_node_path9 = __toESM(require("path"), 1);
804
+ function slugToName(slug) {
805
+ return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
806
+ }
807
+ function translateAgents(rootDir, systemDir, workspaceName, skillSlugs) {
808
+ const agentsDir = import_node_path9.default.join(rootDir, systemDir, "agents");
809
+ const agents = [];
810
+ const workerNames = [];
811
+ const workerAgents = [];
812
+ if (import_node_fs8.default.existsSync(agentsDir)) {
813
+ for (const entry of import_node_fs8.default.readdirSync(agentsDir)) {
814
+ if (entry === "primary-agent.md" || entry === "_template.md" || entry.startsWith(".") || !entry.endsWith(".md")) {
815
+ continue;
816
+ }
817
+ const slug = entry.replace(/\.md$/, "");
818
+ const rawBody = import_node_fs8.default.readFileSync(import_node_path9.default.join(agentsDir, entry), "utf-8");
819
+ const roleMatch = rawBody.match(/^>\s*\*\*Purpose\*\*:\s*(.+)/m);
820
+ const description = roleMatch ? roleMatch[1].trim() : "";
821
+ const name = slugToName(slug);
822
+ const title = description || `${name} Agent`;
823
+ const combined = (rawBody + " " + slug).toLowerCase();
824
+ const isReviewer = combined.includes("review") || combined.includes("qa") || combined.includes("qc") || combined.includes("quality");
825
+ const roleType = isReviewer ? "reviewer" : "worker";
826
+ workerAgents.push({
827
+ slug,
828
+ name,
829
+ title,
830
+ reportsTo: "ceo",
831
+ skills: skillSlugs,
832
+ body: buildWorkerBody(name, title, workspaceName, roleType)
833
+ });
834
+ workerNames.push(`**${name}**`);
835
+ }
836
+ }
837
+ agents.push({
838
+ slug: "ceo",
839
+ name: "CEO",
840
+ title: "Chief Executive Officer",
841
+ reportsTo: null,
842
+ skills: [],
843
+ body: buildCeoBody(workspaceName, workerNames)
844
+ });
845
+ agents.push(...workerAgents);
846
+ return agents;
847
+ }
848
+ function buildCeoBody(workspaceName, workerNames) {
849
+ const handoff = workerNames.length > 0 ? `Route work to the appropriate team member: ${workerNames.join(", ")}.` : "The appropriate specialist for the task.";
850
+ return `You are the CEO of ${workspaceName}. You set direction, scope work, and ensure every task starts with clear requirements before engineering begins.
851
+
852
+ ## What triggers you
853
+
854
+ You are activated when new work arrives \u2014 a feature request, a bug report, a project that needs scoping, or a decision that needs product-level thinking.
855
+
856
+ ## What you do
857
+
858
+ Your job is to turn incoming requests into clear, actionable plans. You decide what to build, why it matters, and who should build it.
859
+
860
+ You operate in three modes:
861
+ - **Scope and plan** \u2014 break down work into clear tasks with owners and acceptance criteria
862
+ - **Review and approve** \u2014 check completed work meets quality standards before sign-off
863
+ - **Prioritize** \u2014 when multiple tasks compete, decide what ships first and what waits
864
+
865
+ You enforce the approval-first principle: no work starts without a plan, no plan ships without review.
866
+
867
+ ## What you produce
868
+
869
+ A scoped plan with task assignments, priorities, and acceptance criteria. Not implementation details \u2014 that's the team's job.
870
+
871
+ ## Who you hand off to
872
+
873
+ ${handoff}`;
874
+ }
875
+ function buildWorkerBody(name, title, workspaceName, roleType) {
876
+ if (roleType === "reviewer") {
877
+ return `You are the ${name} at ${workspaceName}. You operate in quality gate mode.
878
+
879
+ ## What triggers you
880
+
881
+ You are activated when work is ready for review \u2014 a PR needs checking, output needs validation, or quality needs to be verified before shipping.
882
+
883
+ ## What you do
884
+
885
+ You review work for correctness, security, and adherence to standards. You are not a rubber stamp \u2014 you catch the issues that would hurt in production.
886
+
887
+ You look for:
888
+ - Logic errors and edge cases
889
+ - Security vulnerabilities
890
+ - Style and convention violations
891
+ - Missing tests or insufficient coverage
892
+ - Assumptions that should be explicit
893
+
894
+ ## What you produce
895
+
896
+ A review verdict: approved, or a specific list of issues that must be fixed. Every issue includes what's wrong and how to fix it.
897
+
898
+ ## Who you hand off to
899
+
900
+ If approved, hand back to the **CEO** for final sign-off. If issues found, send back to the original author with specific fixes needed.`;
901
+ }
902
+ return `You are the ${name} at ${workspaceName}. Your specialty: ${title.toLowerCase()}.
903
+
904
+ ## What triggers you
905
+
906
+ You are activated when the CEO assigns you a task that matches your expertise, or when a project needs your specific skills.
907
+
908
+ ## What you do
909
+
910
+ You take scoped, assigned tasks and execute them. You write code, build features, and solve problems within your domain. You follow the plan \u2014 if the plan is unclear, you escalate to the CEO rather than guessing.
911
+
912
+ You work with discipline:
913
+ - Read the task requirements fully before starting
914
+ - Break complex tasks into smaller steps
915
+ - Test your work before marking it complete
916
+ - Document decisions that aren't obvious from the code
917
+
918
+ ## What you produce
919
+
920
+ Working code, tested and ready for review. You hand off a clean branch or deliverable, not a work in progress.
921
+
922
+ ## Who you hand off to
923
+
924
+ When your work is complete, hand off to the **CEO** for review routing. If a reviewer is available, the CEO will route it there.`;
925
+ }
926
+
927
+ // src/export-paperclip/translate-governance.ts
928
+ var import_node_fs9 = __toESM(require("fs"), 1);
929
+ var import_node_path10 = __toESM(require("path"), 1);
930
+ var GOVERNANCE_TIERS = {
931
+ solo: {
932
+ tier: "light",
933
+ requiresApproval: false,
934
+ approvalGates: [],
935
+ qualityCheckInterval: 10,
936
+ minQualityGrade: "C"
937
+ },
938
+ team: {
939
+ tier: "standard",
940
+ requiresApproval: true,
941
+ approvalGates: ["strategy", "budget-change"],
942
+ qualityCheckInterval: 5,
943
+ minQualityGrade: "B"
944
+ },
945
+ production: {
946
+ tier: "strict",
947
+ requiresApproval: true,
948
+ approvalGates: [
949
+ "strategy",
950
+ "agent-hire",
951
+ "budget-change",
952
+ "task-transition"
953
+ ],
954
+ qualityCheckInterval: 5,
955
+ minQualityGrade: "A"
956
+ }
957
+ };
958
+ function getGovernanceConfig(qualityLevel) {
959
+ return GOVERNANCE_TIERS[qualityLevel];
960
+ }
961
+
962
+ // src/export-paperclip/translate-skills.ts
963
+ var import_node_fs10 = __toESM(require("fs"), 1);
964
+ var import_node_path11 = __toESM(require("path"), 1);
965
+ function translateSkills(rootDir, systemDir) {
966
+ const skillsDir = import_node_path11.default.join(rootDir, systemDir, "skills");
967
+ const skills = [];
968
+ if (!import_node_fs10.default.existsSync(skillsDir)) return skills;
969
+ for (const entry of import_node_fs10.default.readdirSync(skillsDir, { withFileTypes: true })) {
970
+ if (!entry.isDirectory()) continue;
971
+ const skillMdPath = import_node_path11.default.join(skillsDir, entry.name, "SKILL.md");
972
+ if (!import_node_fs10.default.existsSync(skillMdPath)) continue;
973
+ skills.push({
974
+ name: entry.name,
975
+ sourcePath: `${systemDir}/skills/${entry.name}/SKILL.md`,
976
+ content: import_node_fs10.default.readFileSync(skillMdPath, "utf-8")
977
+ });
978
+ }
979
+ return skills;
980
+ }
981
+
982
+ // src/export-paperclip/translate-goals.ts
983
+ var import_node_fs11 = __toESM(require("fs"), 1);
984
+ var import_node_path12 = __toESM(require("path"), 1);
985
+ function translateGoals(rootDir) {
986
+ const projectsDir = import_node_path12.default.join(rootDir, "projects");
987
+ const goals = [];
988
+ if (!import_node_fs11.default.existsSync(projectsDir)) return goals;
989
+ for (const entry of import_node_fs11.default.readdirSync(projectsDir, { withFileTypes: true })) {
990
+ if (!entry.isDirectory() || entry.name === "_template" || entry.name.startsWith(".")) {
991
+ continue;
992
+ }
993
+ const readmePath = import_node_path12.default.join(projectsDir, entry.name, "README.md");
994
+ if (!import_node_fs11.default.existsSync(readmePath)) continue;
995
+ const content = import_node_fs11.default.readFileSync(readmePath, "utf-8");
996
+ const descMatch = content.match(
997
+ /## What This Project Is\s*\n+([\s\S]*?)(?=\n---|\n##|$)/
998
+ );
999
+ const description = descMatch ? descMatch[1].trim() : `Project: ${entry.name}`;
1000
+ goals.push({ name: entry.name, description });
1001
+ }
1002
+ return goals;
1003
+ }
1004
+
1005
+ // src/export-paperclip.ts
1006
+ var CLI_VERSION = "1.2.0";
1007
+ function toSlug(name) {
1008
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1009
+ }
1010
+ async function exportPaperclip(options) {
1011
+ const { config, vars, rootDir } = loadWorkspace();
1012
+ const systemDir = String(vars.systemDir);
1013
+ const validateOnly = options.validate ?? false;
1014
+ const companyName = options.name ?? await (0, import_prompts7.input)({
1015
+ message: "Company name:",
1016
+ default: config.workspaceName
1017
+ });
1018
+ const mission = options.mission ?? await (0, import_prompts7.input)({
1019
+ message: "Company mission (one line):",
1020
+ default: `Governed AI workspace for ${String(vars.workloadLabel).toLowerCase()}`
1021
+ });
1022
+ const companySlug = toSlug(companyName);
1023
+ const outDir = import_node_path13.default.resolve(
1024
+ options.out ?? `${config.workspaceName}-paperclip`
1025
+ );
1026
+ const skills = translateSkills(rootDir, systemDir);
1027
+ const skillSlugs = skills.map((s) => s.name);
1028
+ const agents = translateAgents(rootDir, systemDir, companyName, skillSlugs);
1029
+ const govConfig = getGovernanceConfig(config.qualityLevel);
1030
+ const goals = translateGoals(rootDir);
1031
+ const nonCeoAgents = agents.filter((a) => a.slug !== "ceo");
1032
+ if (validateOnly) {
1033
+ console.log("\nValidating Paperclip export...\n");
1034
+ console.log(" \u2713 COMPANY.md valid");
1035
+ console.log(" \u2713 .paperclip.yaml valid");
1036
+ for (const agent of agents) {
1037
+ console.log(` \u2713 agents/${agent.slug}/AGENTS.md valid`);
1038
+ }
1039
+ if (nonCeoAgents.length > 0) {
1040
+ console.log(" \u2713 teams/engineering/TEAM.md valid");
1041
+ }
1042
+ for (const skill of skills) {
1043
+ console.log(` \u2713 skills/${skill.name}/SKILL.md valid`);
1044
+ }
1045
+ console.log(
1046
+ `
1047
+ ${agents.length} agent(s), ${skills.length} skill(s), ${goals.length} goal(s)`
1048
+ );
1049
+ console.log(`Governance tier: ${govConfig.tier}`);
1050
+ console.log("\nValidation passed. Run without --validate to export.\n");
1051
+ return;
1052
+ }
1053
+ if (import_node_fs12.default.existsSync(outDir)) {
1054
+ const proceed = await (0, import_prompts7.confirm)({
1055
+ message: `Output directory already exists at ${outDir}. Overwrite?`,
1056
+ default: false
1057
+ });
1058
+ if (!proceed) {
1059
+ console.log("Aborted.\n");
1060
+ return;
1061
+ }
1062
+ import_node_fs12.default.rmSync(outDir, { recursive: true, force: true });
1063
+ }
1064
+ console.log("\nExporting to Paperclip format (agentcompanies/v1)...\n");
1065
+ import_node_fs12.default.mkdirSync(outDir, { recursive: true });
1066
+ const goalsYaml = goals.length > 0 ? goals.map((g) => ` - ${g.description.split("\n")[0]}`).join("\n") : ` - ${mission}`;
1067
+ const pipelineLines = agents.map((a, i) => {
1068
+ return `${i + 1}. **${a.name}** ${a.title.toLowerCase()}`;
1069
+ });
1070
+ const companyMd = [
1071
+ "---",
1072
+ `name: ${companyName}`,
1073
+ `description: ${mission}`,
1074
+ `slug: ${companySlug}`,
1075
+ `schema: agentcompanies/v1`,
1076
+ `version: 1.0.0`,
1077
+ `license: MIT`,
1078
+ `authors:`,
1079
+ ` - name: Clawstrap Export`,
1080
+ `goals:`,
1081
+ goalsYaml,
1082
+ "---",
1083
+ "",
1084
+ `${companyName} is a governed AI company with built-in quality gates, approval workflows, and file-first persistence. Every agent operates under structural governance \u2014 no unsupervised work, no lost context between sessions.`,
1085
+ "",
1086
+ ...pipelineLines,
1087
+ "",
1088
+ `The philosophy: plan before building, review before shipping, persist everything to disk. Governance tier: **${govConfig.tier}** \u2014 quality checks every ${govConfig.qualityCheckInterval} tasks, minimum grade ${govConfig.minQualityGrade}.`,
1089
+ "",
1090
+ "---",
1091
+ "",
1092
+ `Generated with [Clawstrap](https://github.com/peppinho89/clawstrap) v${CLI_VERSION}`,
1093
+ ""
1094
+ ].join("\n");
1095
+ import_node_fs12.default.writeFileSync(import_node_path13.default.join(outDir, "COMPANY.md"), companyMd, "utf-8");
1096
+ console.log(" \u2713 COMPANY.md");
1097
+ import_node_fs12.default.writeFileSync(
1098
+ import_node_path13.default.join(outDir, ".paperclip.yaml"),
1099
+ "schema: paperclip/v1\n",
1100
+ "utf-8"
1101
+ );
1102
+ console.log(" \u2713 .paperclip.yaml");
1103
+ for (const agent of agents) {
1104
+ const agentDir = import_node_path13.default.join(outDir, "agents", agent.slug);
1105
+ import_node_fs12.default.mkdirSync(agentDir, { recursive: true });
1106
+ const frontmatterLines = [
1107
+ "---",
1108
+ `name: ${agent.name}`,
1109
+ `title: ${agent.title}`,
1110
+ `reportsTo: ${agent.reportsTo ?? "null"}`
1111
+ ];
1112
+ if (agent.skills.length > 0) {
1113
+ frontmatterLines.push("skills:");
1114
+ for (const s of agent.skills) {
1115
+ frontmatterLines.push(` - ${s}`);
1116
+ }
1117
+ }
1118
+ frontmatterLines.push("---");
1119
+ const agentMd = frontmatterLines.join("\n") + "\n\n" + agent.body + "\n";
1120
+ import_node_fs12.default.writeFileSync(import_node_path13.default.join(agentDir, "AGENTS.md"), agentMd, "utf-8");
1121
+ console.log(` \u2713 agents/${agent.slug}/AGENTS.md`);
1122
+ }
1123
+ if (nonCeoAgents.length > 0) {
1124
+ const teamDir = import_node_path13.default.join(outDir, "teams", "engineering");
1125
+ import_node_fs12.default.mkdirSync(teamDir, { recursive: true });
1126
+ const includesList = nonCeoAgents.map((a) => ` - ../../agents/${a.slug}/AGENTS.md`).join("\n");
1127
+ const teamMd = [
1128
+ "---",
1129
+ `name: Engineering`,
1130
+ `description: ${companyName} engineering team`,
1131
+ `slug: engineering`,
1132
+ `manager: ../../agents/ceo/AGENTS.md`,
1133
+ `includes:`,
1134
+ includesList,
1135
+ `tags:`,
1136
+ ` - engineering`,
1137
+ "---",
1138
+ "",
1139
+ `The engineering team at ${companyName}. Led by the CEO, who scopes and delegates work to specialists.`,
1140
+ ""
1141
+ ].join("\n");
1142
+ import_node_fs12.default.writeFileSync(import_node_path13.default.join(teamDir, "TEAM.md"), teamMd, "utf-8");
1143
+ console.log(" \u2713 teams/engineering/TEAM.md");
1144
+ }
1145
+ for (const skill of skills) {
1146
+ const skillDir = import_node_path13.default.join(outDir, "skills", skill.name);
1147
+ import_node_fs12.default.mkdirSync(skillDir, { recursive: true });
1148
+ import_node_fs12.default.writeFileSync(import_node_path13.default.join(skillDir, "SKILL.md"), skill.content, "utf-8");
1149
+ console.log(` \u2713 skills/${skill.name}/SKILL.md`);
1150
+ }
1151
+ const importScript = [
1152
+ "#!/bin/bash",
1153
+ "# Import this Clawstrap company into Paperclip",
1154
+ "# Requires: Paperclip running at localhost:3100 (default) or PAPERCLIP_URL env var",
1155
+ "",
1156
+ "PAPERCLIP_URL=${PAPERCLIP_URL:-http://localhost:3100}",
1157
+ "",
1158
+ 'echo "Importing into Paperclip..."',
1159
+ 'npx paperclipai company import . --paperclip-url "$PAPERCLIP_URL" --yes',
1160
+ "",
1161
+ 'echo "Done. Open your Paperclip dashboard to review."',
1162
+ ""
1163
+ ].join("\n");
1164
+ const importPath = import_node_path13.default.join(outDir, "import.sh");
1165
+ import_node_fs12.default.writeFileSync(importPath, importScript, "utf-8");
1166
+ import_node_fs12.default.chmodSync(importPath, 493);
1167
+ console.log(" \u2713 import.sh");
1168
+ const updatedConfig = {
1169
+ ...config,
1170
+ lastExport: {
1171
+ format: "paperclip",
1172
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
1173
+ outputDir: import_node_path13.default.relative(rootDir, outDir) || outDir
1174
+ }
1175
+ };
1176
+ import_node_fs12.default.writeFileSync(
1177
+ import_node_path13.default.join(rootDir, ".clawstrap.json"),
1178
+ JSON.stringify(updatedConfig, null, 2) + "\n",
1179
+ "utf-8"
1180
+ );
1181
+ console.log(
1182
+ `
1183
+ Exported to ${import_node_path13.default.relative(process.cwd(), outDir) || outDir}`
1184
+ );
1185
+ console.log(
1186
+ `${agents.length} agent(s), ${skills.length} skill(s), ${goals.length} goal(s)`
1187
+ );
1188
+ console.log(`Governance tier: ${govConfig.tier}
1189
+ `);
1190
+ }
1191
+
1192
+ // src/index.ts
1193
+ var program = new import_commander.Command();
1194
+ program.name("clawstrap").description("Scaffold a production-ready AI agent workspace").version("1.2.0");
1195
+ program.command("init").description("Create a new AI workspace in the current directory").argument("[directory]", "Target directory", ".").option("-y, --yes", "Use defaults, skip prompts").action(async (directory, options) => {
1196
+ await init(directory, options);
1197
+ });
1198
+ var add = program.command("add").description("Add components to the workspace");
1199
+ add.command("agent").description("Add a new agent definition").argument("<name>", "Agent name").action(async (name) => {
1200
+ await addAgent(name);
1201
+ });
1202
+ add.command("skill").description("Add a new skill").argument("<name>", "Skill name").action(async (name) => {
1203
+ await addSkill(name);
1204
+ });
1205
+ add.command("project").description("Add a new project").argument("<name>", "Project name").action(async (name) => {
1206
+ await addProject(name);
1207
+ });
1208
+ program.command("status").description("Show workspace status and configuration").action(async () => {
1209
+ await showStatus();
1210
+ });
1211
+ program.command("export").description("Export workspace to another format").requiredOption("-f, --format <format>", "Export format (paperclip)").option("-o, --out <dir>", "Output directory").option("-n, --name <name>", "Company name").option("-m, --mission <mission>", "Company mission").option("-a, --adapter <type>", "Agent adapter type (default: claude_local)").option("--validate", "Validate export without writing").action(async (options) => {
1212
+ if (options.format !== "paperclip") {
1213
+ console.error(
1214
+ `
1215
+ Unknown format: ${options.format}. Supported: paperclip
1216
+ `
1217
+ );
1218
+ process.exit(1);
1219
+ }
1220
+ await exportPaperclip(options);
1221
+ });
1222
+ program.parse();