dotclaudemd 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dotclaudemd contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # dotclaudemd
2
+
3
+ CLAUDE.md Template Registry CLI — scaffold, lint, and health-check your CLAUDE.md files.
4
+
5
+ Think "github/gitignore but for CLAUDE.md."
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx dotclaudemd init
11
+ ```
12
+
13
+ This auto-detects your project stack and generates a CLAUDE.md from the best matching template.
14
+
15
+ ## Why
16
+
17
+ There is no standard starting point for writing CLAUDE.md files. Developers write them from scratch, often missing best practices or including anti-patterns. `dotclaudemd` provides a searchable, community-driven registry of templates with a CLI for scaffolding, linting, and health-checking.
18
+
19
+ ## Commands
20
+
21
+ ### `dotclaudemd init`
22
+
23
+ Scaffold a CLAUDE.md from a template with auto-detection.
24
+
25
+ ```bash
26
+ dotclaudemd init # Auto-detect stack, interactive prompts
27
+ dotclaudemd init --stack mern-stack # Use a specific template
28
+ dotclaudemd init --global # Write to ~/.claude/CLAUDE.md
29
+ dotclaudemd init --no-interactive # Use defaults without prompting
30
+ ```
31
+
32
+ ### `dotclaudemd lint [file]`
33
+
34
+ Lint a CLAUDE.md for common anti-patterns.
35
+
36
+ ```bash
37
+ dotclaudemd lint # Lint CLAUDE.md in current project
38
+ dotclaudemd lint path/to/CLAUDE.md # Lint a specific file
39
+ dotclaudemd lint --json # Output as JSON
40
+ ```
41
+
42
+ **Lint Rules:**
43
+
44
+ | Rule | Severity | Trigger |
45
+ |------|----------|---------|
46
+ | `line-count` | warn/error | >80 lines (warn), >150 lines (error) |
47
+ | `has-commands` | warn | Missing build/test/dev commands |
48
+ | `no-personality` | warn | "Be a senior engineer", persona instructions |
49
+ | `no-at-file-refs` | warn | `@docs/...` patterns that embed entire files |
50
+ | `no-negative-only` | warn | "Never use X" without "prefer Y instead" |
51
+ | `stale-file-refs` | warn | Referenced paths that don't exist |
52
+ | `no-unicode-bullets` | info | Unicode bullets instead of markdown lists |
53
+ | `no-placeholder-vars` | error | Unreplaced `{{variable}}` placeholders |
54
+
55
+ ### `dotclaudemd doctor`
56
+
57
+ Check CLAUDE.md freshness against actual project state.
58
+
59
+ ```bash
60
+ dotclaudemd doctor # Run health checks
61
+ dotclaudemd doctor --json # Output as JSON
62
+ ```
63
+
64
+ **Health Checks:**
65
+
66
+ | Check | Description |
67
+ |-------|-------------|
68
+ | `scripts-exist` | Commands in CLAUDE.md exist in package.json scripts |
69
+ | `deps-mentioned` | Major dependencies are mentioned |
70
+ | `file-refs-valid` | File paths mentioned actually exist |
71
+ | `node-version-match` | Stated Node version matches .nvmrc |
72
+ | `test-framework-match` | Mentioned test framework matches devDeps |
73
+ | `package-manager-match` | Stated package manager matches lockfile |
74
+
75
+ ### `dotclaudemd browse`
76
+
77
+ Browse and preview available templates.
78
+
79
+ ```bash
80
+ dotclaudemd browse # Interactive template browser
81
+ dotclaudemd browse --list # Non-interactive list
82
+ dotclaudemd browse --category python # Filter by category
83
+ ```
84
+
85
+ ## Available Templates
86
+
87
+ | Template | Description |
88
+ |----------|-------------|
89
+ | `default` | Generic template for any project |
90
+ | `nextjs-typescript` | Next.js App Router with TypeScript |
91
+ | `nextjs-prisma-tailwind` | Full-stack Next.js with Prisma + Tailwind |
92
+ | `express-mongodb` | Express.js REST API with MongoDB |
93
+ | `mern-stack` | MERN full-stack application |
94
+ | `react-vite` | React SPA with Vite |
95
+ | `node-cli-tool` | Node.js CLI tool with TypeScript |
96
+ | `fastapi-sqlalchemy` | FastAPI with SQLAlchemy ORM |
97
+ | `django-rest` | Django REST Framework |
98
+ | `flask-basic` | Flask web application |
99
+ | `cargo-workspace` | Rust Cargo workspace |
100
+ | `go-api` | Go REST API |
101
+
102
+ ## Contributing
103
+
104
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add templates and contribute.
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ filterTemplates,
4
+ listTemplates,
5
+ renderTemplate
6
+ } from "./chunk-3ERFQLAD.js";
7
+ import "./chunk-3WTPUEHL.js";
8
+ import {
9
+ defaultFsDeps
10
+ } from "./chunk-3R6PUA3E.js";
11
+ import {
12
+ warn
13
+ } from "./chunk-YHVOBZLV.js";
14
+
15
+ // src/commands/browse.ts
16
+ import chalk from "chalk";
17
+ import { select, confirm } from "@inquirer/prompts";
18
+ async function browseCommand(options = {}, deps = defaultFsDeps) {
19
+ const allTemplates = await listTemplates();
20
+ if (options.list) {
21
+ printTemplateList(allTemplates, options.category);
22
+ return;
23
+ }
24
+ let category = options.category;
25
+ if (!category) {
26
+ const categories = [
27
+ ...new Set(allTemplates.map((t) => t.frontmatter.category))
28
+ ];
29
+ category = await select({
30
+ message: "Choose a category:",
31
+ choices: [
32
+ { name: "All", value: "" },
33
+ ...categories.map((c) => ({ name: c, value: c }))
34
+ ]
35
+ });
36
+ }
37
+ const filtered = category ? await filterTemplates({ category }) : allTemplates;
38
+ if (filtered.length === 0) {
39
+ warn("No templates found for this category.");
40
+ return;
41
+ }
42
+ const templateName = await select({
43
+ message: "Choose a template to preview:",
44
+ choices: filtered.map((t) => ({
45
+ name: `${t.frontmatter.displayName} \u2014 ${t.frontmatter.description}`,
46
+ value: t.frontmatter.name
47
+ }))
48
+ });
49
+ const template = filtered.find(
50
+ (t) => t.frontmatter.name === templateName
51
+ );
52
+ const defaults = {};
53
+ for (const v of template.frontmatter.variables) {
54
+ if (v.default) defaults[v.name] = v.default;
55
+ }
56
+ console.log();
57
+ console.log(chalk.bold(`Preview: ${template.frontmatter.displayName}`));
58
+ console.log(chalk.dim("\u2500".repeat(60)));
59
+ console.log(renderTemplate(template, defaults));
60
+ console.log(chalk.dim("\u2500".repeat(60)));
61
+ console.log();
62
+ const useIt = await confirm({
63
+ message: "Use this template?",
64
+ default: true
65
+ });
66
+ if (useIt) {
67
+ const { initCommand } = await import("./init-GLWLFVHN.js");
68
+ await initCommand({ stack: template.frontmatter.name }, deps);
69
+ }
70
+ }
71
+ function printTemplateList(templates, categoryFilter) {
72
+ const filtered = categoryFilter ? templates.filter((t) => t.frontmatter.category === categoryFilter) : templates;
73
+ console.log();
74
+ console.log(chalk.bold("Available Templates"));
75
+ console.log();
76
+ const byCategory = /* @__PURE__ */ new Map();
77
+ for (const t of filtered) {
78
+ const cat = t.frontmatter.category;
79
+ if (!byCategory.has(cat)) byCategory.set(cat, []);
80
+ byCategory.get(cat).push(t);
81
+ }
82
+ for (const [category, templates2] of byCategory) {
83
+ console.log(chalk.bold.underline(category));
84
+ for (const t of templates2) {
85
+ console.log(
86
+ ` ${chalk.cyan(t.frontmatter.name.padEnd(30))} ${t.frontmatter.description}`
87
+ );
88
+ }
89
+ console.log();
90
+ }
91
+ console.log(chalk.dim(`${filtered.length} template(s) available`));
92
+ }
93
+ export {
94
+ browseCommand
95
+ };
96
+ //# sourceMappingURL=browse-7V4CRGTH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/browse.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { select, confirm } from \"@inquirer/prompts\";\nimport {\n listTemplates,\n filterTemplates,\n} from \"../core/template-registry.js\";\nimport { renderTemplate } from \"../core/template-engine.js\";\nimport type { Template, FsDeps } from \"../types.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\nimport * as logger from \"../utils/logger.js\";\n\nexport interface BrowseOptions {\n category?: string;\n list?: boolean;\n}\n\nexport async function browseCommand(\n options: BrowseOptions = {},\n deps: FsDeps = defaultFsDeps,\n): Promise<void> {\n const allTemplates = await listTemplates();\n\n if (options.list) {\n printTemplateList(allTemplates, options.category);\n return;\n }\n\n // Category selection\n let category = options.category;\n if (!category) {\n const categories = [\n ...new Set(allTemplates.map((t) => t.frontmatter.category)),\n ];\n category = await select({\n message: \"Choose a category:\",\n choices: [\n { name: \"All\", value: \"\" },\n ...categories.map((c) => ({ name: c, value: c })),\n ],\n });\n }\n\n // Filter templates\n const filtered = category\n ? await filterTemplates({ category })\n : allTemplates;\n\n if (filtered.length === 0) {\n logger.warn(\"No templates found for this category.\");\n return;\n }\n\n // Template selection\n const templateName = await select({\n message: \"Choose a template to preview:\",\n choices: filtered.map((t) => ({\n name: `${t.frontmatter.displayName} — ${t.frontmatter.description}`,\n value: t.frontmatter.name,\n })),\n });\n\n const template = filtered.find(\n (t) => t.frontmatter.name === templateName,\n )!;\n\n // Preview with defaults\n const defaults: Record<string, string> = {};\n for (const v of template.frontmatter.variables) {\n if (v.default) defaults[v.name] = v.default;\n }\n\n console.log();\n console.log(chalk.bold(`Preview: ${template.frontmatter.displayName}`));\n console.log(chalk.dim(\"─\".repeat(60)));\n console.log(renderTemplate(template, defaults));\n console.log(chalk.dim(\"─\".repeat(60)));\n console.log();\n\n // Offer to use\n const useIt = await confirm({\n message: \"Use this template?\",\n default: true,\n });\n\n if (useIt) {\n // Dynamically import to avoid circular deps\n const { initCommand } = await import(\"./init.js\");\n await initCommand({ stack: template.frontmatter.name }, deps);\n }\n}\n\nfunction printTemplateList(\n templates: Template[],\n categoryFilter?: string,\n): void {\n const filtered = categoryFilter\n ? templates.filter((t) => t.frontmatter.category === categoryFilter)\n : templates;\n\n console.log();\n console.log(chalk.bold(\"Available Templates\"));\n console.log();\n\n // Group by category\n const byCategory = new Map<string, Template[]>();\n for (const t of filtered) {\n const cat = t.frontmatter.category;\n if (!byCategory.has(cat)) byCategory.set(cat, []);\n byCategory.get(cat)!.push(t);\n }\n\n for (const [category, templates] of byCategory) {\n console.log(chalk.bold.underline(category));\n for (const t of templates) {\n console.log(\n ` ${chalk.cyan(t.frontmatter.name.padEnd(30))} ${t.frontmatter.description}`,\n );\n }\n console.log();\n }\n\n console.log(chalk.dim(`${filtered.length} template(s) available`));\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA,OAAO,WAAW;AAClB,SAAS,QAAQ,eAAe;AAehC,eAAsB,cACpB,UAAyB,CAAC,GAC1B,OAAe,eACA;AACf,QAAM,eAAe,MAAM,cAAc;AAEzC,MAAI,QAAQ,MAAM;AAChB,sBAAkB,cAAc,QAAQ,QAAQ;AAChD;AAAA,EACF;AAGA,MAAI,WAAW,QAAQ;AACvB,MAAI,CAAC,UAAU;AACb,UAAM,aAAa;AAAA,MACjB,GAAG,IAAI,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,YAAY,QAAQ,CAAC;AAAA,IAC5D;AACA,eAAW,MAAM,OAAO;AAAA,MACtB,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,MAAM,OAAO,OAAO,GAAG;AAAA,QACzB,GAAG,WAAW,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,EAAE;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,WAAW,WACb,MAAM,gBAAgB,EAAE,SAAS,CAAC,IAClC;AAEJ,MAAI,SAAS,WAAW,GAAG;AACzB,IAAO,KAAK,uCAAuC;AACnD;AAAA,EACF;AAGA,QAAM,eAAe,MAAM,OAAO;AAAA,IAChC,SAAS;AAAA,IACT,SAAS,SAAS,IAAI,CAAC,OAAO;AAAA,MAC5B,MAAM,GAAG,EAAE,YAAY,WAAW,WAAM,EAAE,YAAY,WAAW;AAAA,MACjE,OAAO,EAAE,YAAY;AAAA,IACvB,EAAE;AAAA,EACJ,CAAC;AAED,QAAM,WAAW,SAAS;AAAA,IACxB,CAAC,MAAM,EAAE,YAAY,SAAS;AAAA,EAChC;AAGA,QAAM,WAAmC,CAAC;AAC1C,aAAW,KAAK,SAAS,YAAY,WAAW;AAC9C,QAAI,EAAE,QAAS,UAAS,EAAE,IAAI,IAAI,EAAE;AAAA,EACtC;AAEA,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,KAAK,YAAY,SAAS,YAAY,WAAW,EAAE,CAAC;AACtE,UAAQ,IAAI,MAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AACrC,UAAQ,IAAI,eAAe,UAAU,QAAQ,CAAC;AAC9C,UAAQ,IAAI,MAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AACrC,UAAQ,IAAI;AAGZ,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAED,MAAI,OAAO;AAET,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAW;AAChD,UAAM,YAAY,EAAE,OAAO,SAAS,YAAY,KAAK,GAAG,IAAI;AAAA,EAC9D;AACF;AAEA,SAAS,kBACP,WACA,gBACM;AACN,QAAM,WAAW,iBACb,UAAU,OAAO,CAAC,MAAM,EAAE,YAAY,aAAa,cAAc,IACjE;AAEJ,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,KAAK,qBAAqB,CAAC;AAC7C,UAAQ,IAAI;AAGZ,QAAM,aAAa,oBAAI,IAAwB;AAC/C,aAAW,KAAK,UAAU;AACxB,UAAM,MAAM,EAAE,YAAY;AAC1B,QAAI,CAAC,WAAW,IAAI,GAAG,EAAG,YAAW,IAAI,KAAK,CAAC,CAAC;AAChD,eAAW,IAAI,GAAG,EAAG,KAAK,CAAC;AAAA,EAC7B;AAEA,aAAW,CAAC,UAAUA,UAAS,KAAK,YAAY;AAC9C,YAAQ,IAAI,MAAM,KAAK,UAAU,QAAQ,CAAC;AAC1C,eAAW,KAAKA,YAAW;AACzB,cAAQ;AAAA,QACN,KAAK,MAAM,KAAK,EAAE,YAAY,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,WAAW;AAAA,MAC7E;AAAA,IACF;AACA,YAAQ,IAAI;AAAA,EACd;AAEA,UAAQ,IAAI,MAAM,IAAI,GAAG,SAAS,MAAM,wBAAwB,CAAC;AACnE;","names":["templates"]}
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/ui.ts
4
+ import ora from "ora";
5
+ var isCI = process.env.CI === "true";
6
+ var isTest = process.env.NODE_ENV === "test";
7
+ function createSpinner(text) {
8
+ return ora({
9
+ text,
10
+ isSilent: isCI || isTest
11
+ });
12
+ }
13
+
14
+ export {
15
+ createSpinner
16
+ };
17
+ //# sourceMappingURL=chunk-2D66CC54.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/ui.ts"],"sourcesContent":["import ora, { type Ora } from \"ora\";\n\nconst isCI = process.env.CI === \"true\";\nconst isTest = process.env.NODE_ENV === \"test\";\n\nexport function createSpinner(text: string): Ora {\n return ora({\n text,\n isSilent: isCI || isTest,\n });\n}\n"],"mappings":";;;AAAA,OAAO,SAAuB;AAE9B,IAAM,OAAO,QAAQ,IAAI,OAAO;AAChC,IAAM,SAAS,QAAQ,IAAI,aAAa;AAEjC,SAAS,cAAc,MAAmB;AAC/C,SAAO,IAAI;AAAA,IACT;AAAA,IACA,UAAU,QAAQ;AAAA,EACpB,CAAC;AACH;","names":[]}
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ValidationError
4
+ } from "./chunk-3WTPUEHL.js";
5
+ import {
6
+ defaultFsDeps,
7
+ getTemplatesDir
8
+ } from "./chunk-3R6PUA3E.js";
9
+
10
+ // src/core/template-engine.ts
11
+ import matter from "gray-matter";
12
+ async function parseTemplate(filePath, deps = defaultFsDeps) {
13
+ const raw = await deps.readFile(filePath);
14
+ const { data, content } = matter(raw);
15
+ validateFrontmatter(data, filePath);
16
+ return {
17
+ filePath,
18
+ frontmatter: {
19
+ name: data.name,
20
+ displayName: data.displayName,
21
+ description: data.description,
22
+ category: data.category,
23
+ tags: data.tags ?? [],
24
+ variables: data.variables ?? [],
25
+ detects: data.detects,
26
+ priority: data.priority ?? 0
27
+ },
28
+ content: content.trim()
29
+ };
30
+ }
31
+ function validateFrontmatter(data, filePath) {
32
+ const required = ["name", "displayName", "description", "category"];
33
+ for (const field of required) {
34
+ if (!data[field]) {
35
+ throw new ValidationError(
36
+ `Template "${filePath}" is missing required frontmatter field: ${field}`
37
+ );
38
+ }
39
+ }
40
+ }
41
+ function renderTemplate(template, variables) {
42
+ let result = template.content;
43
+ const placeholders = /* @__PURE__ */ new Set();
44
+ const regex = /\{\{(\w+)\}\}/g;
45
+ let match;
46
+ while ((match = regex.exec(result)) !== null) {
47
+ placeholders.add(match[1]);
48
+ }
49
+ for (const varDef of template.frontmatter.variables) {
50
+ if (placeholders.has(varDef.name) && !variables[varDef.name]) {
51
+ if (varDef.default) {
52
+ variables[varDef.name] = varDef.default;
53
+ } else {
54
+ throw new ValidationError(
55
+ `Missing required variable: ${varDef.name}`
56
+ );
57
+ }
58
+ }
59
+ }
60
+ result = result.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
61
+ return variables[name] ?? _match;
62
+ });
63
+ return result;
64
+ }
65
+
66
+ // src/core/template-registry.ts
67
+ import { join } from "path";
68
+ var cachedTemplates = null;
69
+ async function listTemplates(templatesDir, deps = defaultFsDeps) {
70
+ if (cachedTemplates) return cachedTemplates;
71
+ const dir = templatesDir ?? getTemplatesDir();
72
+ const templates = [];
73
+ const categories = await deps.readDir(dir);
74
+ for (const category of categories) {
75
+ if (category.startsWith(".")) continue;
76
+ const categoryPath = join(dir, category);
77
+ let files;
78
+ try {
79
+ files = await deps.readDir(categoryPath);
80
+ } catch {
81
+ continue;
82
+ }
83
+ for (const file of files) {
84
+ if (!file.endsWith(".md")) continue;
85
+ try {
86
+ const template = await parseTemplate(join(categoryPath, file), deps);
87
+ templates.push(template);
88
+ } catch {
89
+ }
90
+ }
91
+ }
92
+ cachedTemplates = templates;
93
+ return templates;
94
+ }
95
+ async function filterTemplates(options = {}, templatesDir, deps = defaultFsDeps) {
96
+ const templates = await listTemplates(templatesDir, deps);
97
+ return templates.filter((t) => {
98
+ if (options.category && t.frontmatter.category !== options.category) {
99
+ return false;
100
+ }
101
+ if (options.tags && options.tags.length > 0) {
102
+ return options.tags.some((tag) => t.frontmatter.tags.includes(tag));
103
+ }
104
+ return true;
105
+ });
106
+ }
107
+ async function suggestTemplates(stack, templatesDir, deps = defaultFsDeps) {
108
+ const templates = await listTemplates(templatesDir, deps);
109
+ const scored = templates.map((template) => {
110
+ let score = 0;
111
+ const detects = template.frontmatter.detects;
112
+ if (!detects) return { template, score };
113
+ if (detects.files) {
114
+ }
115
+ if (detects.dependencies) {
116
+ for (const dep of detects.dependencies) {
117
+ if (stack.dependencies.includes(dep)) score += 2;
118
+ }
119
+ }
120
+ if (detects.devDependencies) {
121
+ for (const dep of detects.devDependencies) {
122
+ if (stack.devDependencies.includes(dep)) score += 1;
123
+ }
124
+ }
125
+ if (template.frontmatter.category === stack.language || template.frontmatter.tags.includes(stack.language)) {
126
+ score += 1;
127
+ }
128
+ if (stack.framework && template.frontmatter.tags.includes(stack.framework.toLowerCase())) {
129
+ score += 3;
130
+ }
131
+ return { template, score };
132
+ }).filter((s) => s.score > 0).sort((a, b) => {
133
+ if (b.score !== a.score) return b.score - a.score;
134
+ return b.template.frontmatter.priority - a.template.frontmatter.priority;
135
+ });
136
+ return scored.map((s) => s.template);
137
+ }
138
+
139
+ export {
140
+ renderTemplate,
141
+ listTemplates,
142
+ filterTemplates,
143
+ suggestTemplates
144
+ };
145
+ //# sourceMappingURL=chunk-3ERFQLAD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/template-engine.ts","../src/core/template-registry.ts"],"sourcesContent":["import matter from \"gray-matter\";\nimport type { Template, TemplateFrontmatter, FsDeps } from \"../types.js\";\nimport { ValidationError } from \"../utils/errors.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\n\nexport async function parseTemplate(\n filePath: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<Template> {\n const raw = await deps.readFile(filePath);\n const { data, content } = matter(raw);\n\n validateFrontmatter(data, filePath);\n\n return {\n filePath,\n frontmatter: {\n name: data.name,\n displayName: data.displayName,\n description: data.description,\n category: data.category,\n tags: data.tags ?? [],\n variables: data.variables ?? [],\n detects: data.detects,\n priority: data.priority ?? 0,\n },\n content: content.trim(),\n };\n}\n\nfunction validateFrontmatter(\n data: Record<string, unknown>,\n filePath: string,\n): void {\n const required = [\"name\", \"displayName\", \"description\", \"category\"];\n for (const field of required) {\n if (!data[field]) {\n throw new ValidationError(\n `Template \"${filePath}\" is missing required frontmatter field: ${field}`,\n );\n }\n }\n}\n\nexport function renderTemplate(\n template: Template,\n variables: Record<string, string>,\n): string {\n let result = template.content;\n\n // Collect all variable placeholders in the template\n const placeholders = new Set<string>();\n const regex = /\\{\\{(\\w+)\\}\\}/g;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(result)) !== null) {\n placeholders.add(match[1]);\n }\n\n // Check for missing required variables\n for (const varDef of template.frontmatter.variables) {\n if (placeholders.has(varDef.name) && !variables[varDef.name]) {\n if (varDef.default) {\n variables[varDef.name] = varDef.default;\n } else {\n throw new ValidationError(\n `Missing required variable: ${varDef.name}`,\n );\n }\n }\n }\n\n // Substitute all variables\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, name: string) => {\n return variables[name] ?? _match;\n });\n\n return result;\n}\n","import { join } from \"node:path\";\nimport { parseTemplate } from \"./template-engine.js\";\nimport type { Template, DetectedStack, FsDeps } from \"../types.js\";\nimport { TemplateNotFoundError } from \"../utils/errors.js\";\nimport { getTemplatesDir } from \"../utils/paths.js\";\nimport { defaultFsDeps } from \"../utils/fs.js\";\n\nlet cachedTemplates: Template[] | null = null;\n\nexport function clearCache(): void {\n cachedTemplates = null;\n}\n\nexport async function listTemplates(\n templatesDir?: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<Template[]> {\n if (cachedTemplates) return cachedTemplates;\n\n const dir = templatesDir ?? getTemplatesDir();\n const templates: Template[] = [];\n\n const categories = await deps.readDir(dir);\n for (const category of categories) {\n if (category.startsWith(\".\")) continue;\n const categoryPath = join(dir, category);\n let files: string[];\n try {\n files = await deps.readDir(categoryPath);\n } catch {\n continue; // Skip non-directory entries\n }\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n try {\n const template = await parseTemplate(join(categoryPath, file), deps);\n templates.push(template);\n } catch {\n // Skip invalid templates\n }\n }\n }\n\n cachedTemplates = templates;\n return templates;\n}\n\nexport async function getTemplate(\n name: string,\n templatesDir?: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<Template> {\n const templates = await listTemplates(templatesDir, deps);\n const template = templates.find((t) => t.frontmatter.name === name);\n if (!template) {\n throw new TemplateNotFoundError(name);\n }\n return template;\n}\n\nexport async function filterTemplates(\n options: { category?: string; tags?: string[] } = {},\n templatesDir?: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<Template[]> {\n const templates = await listTemplates(templatesDir, deps);\n return templates.filter((t) => {\n if (options.category && t.frontmatter.category !== options.category) {\n return false;\n }\n if (options.tags && options.tags.length > 0) {\n return options.tags.some((tag) => t.frontmatter.tags.includes(tag));\n }\n return true;\n });\n}\n\nexport async function suggestTemplates(\n stack: DetectedStack,\n templatesDir?: string,\n deps: FsDeps = defaultFsDeps,\n): Promise<Template[]> {\n const templates = await listTemplates(templatesDir, deps);\n\n const scored = templates\n .map((template) => {\n let score = 0;\n const detects = template.frontmatter.detects;\n if (!detects) return { template, score };\n\n // Check file-based detection\n if (detects.files) {\n // File detection is handled by the project detector\n // Here we just match on deps\n }\n\n // Check dependency matches\n if (detects.dependencies) {\n for (const dep of detects.dependencies) {\n if (stack.dependencies.includes(dep)) score += 2;\n }\n }\n\n if (detects.devDependencies) {\n for (const dep of detects.devDependencies) {\n if (stack.devDependencies.includes(dep)) score += 1;\n }\n }\n\n // Language match\n if (\n template.frontmatter.category === stack.language ||\n template.frontmatter.tags.includes(stack.language)\n ) {\n score += 1;\n }\n\n // Framework match\n if (\n stack.framework &&\n template.frontmatter.tags.includes(stack.framework.toLowerCase())\n ) {\n score += 3;\n }\n\n return { template, score };\n })\n .filter((s) => s.score > 0)\n .sort((a, b) => {\n // Sort by score descending, then by priority descending\n if (b.score !== a.score) return b.score - a.score;\n return b.template.frontmatter.priority - a.template.frontmatter.priority;\n });\n\n return scored.map((s) => s.template);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/fs.ts
4
+ import { readFile as nodeReadFile, writeFile as nodeWriteFile } from "fs/promises";
5
+ import { existsSync } from "fs";
6
+ import { readdir } from "fs/promises";
7
+ var defaultFsDeps = {
8
+ async readFile(path) {
9
+ return nodeReadFile(path, "utf-8");
10
+ },
11
+ async writeFile(path, content) {
12
+ await nodeWriteFile(path, content, "utf-8");
13
+ },
14
+ async fileExists(path) {
15
+ return existsSync(path);
16
+ },
17
+ async readDir(path) {
18
+ return readdir(path);
19
+ }
20
+ };
21
+
22
+ // src/utils/paths.ts
23
+ import { dirname, join, resolve } from "path";
24
+ import { fileURLToPath } from "url";
25
+ import { existsSync as existsSync2 } from "fs";
26
+ function getTemplatesDir() {
27
+ const __filename = fileURLToPath(import.meta.url);
28
+ const __dirname = dirname(__filename);
29
+ let dir = __dirname;
30
+ for (let i = 0; i < 5; i++) {
31
+ const candidate = join(dir, "templates");
32
+ if (existsSync2(candidate)) {
33
+ return candidate;
34
+ }
35
+ dir = dirname(dir);
36
+ }
37
+ return join(dirname(dirname(__dirname)), "templates");
38
+ }
39
+ function findProjectRoot(startDir) {
40
+ let dir = startDir ? resolve(startDir) : process.cwd();
41
+ while (true) {
42
+ const indicators = [
43
+ "package.json",
44
+ "pyproject.toml",
45
+ "Cargo.toml",
46
+ "go.mod",
47
+ ".git"
48
+ ];
49
+ for (const indicator of indicators) {
50
+ if (existsSync2(join(dir, indicator))) {
51
+ return dir;
52
+ }
53
+ }
54
+ const parent = dirname(dir);
55
+ if (parent === dir) break;
56
+ dir = parent;
57
+ }
58
+ return startDir ? resolve(startDir) : process.cwd();
59
+ }
60
+ function findClaudeMd(projectRoot) {
61
+ const candidates = [
62
+ join(projectRoot, "CLAUDE.md"),
63
+ join(projectRoot, ".claude", "CLAUDE.md")
64
+ ];
65
+ for (const candidate of candidates) {
66
+ if (existsSync2(candidate)) {
67
+ return candidate;
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+
73
+ export {
74
+ defaultFsDeps,
75
+ getTemplatesDir,
76
+ findProjectRoot,
77
+ findClaudeMd
78
+ };
79
+ //# sourceMappingURL=chunk-3R6PUA3E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/fs.ts","../src/utils/paths.ts"],"sourcesContent":["import { readFile as nodeReadFile, writeFile as nodeWriteFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport type { FsDeps } from \"../types.js\";\n\nexport const defaultFsDeps: FsDeps = {\n async readFile(path: string): Promise<string> {\n return nodeReadFile(path, \"utf-8\");\n },\n\n async writeFile(path: string, content: string): Promise<void> {\n await nodeWriteFile(path, content, \"utf-8\");\n },\n\n async fileExists(path: string): Promise<boolean> {\n return existsSync(path);\n },\n\n async readDir(path: string): Promise<string[]> {\n return readdir(path);\n },\n};\n","import { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { existsSync } from \"node:fs\";\n\nexport function getTemplatesDir(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n // From dist/cli.mjs or src/utils/paths.ts, go up to package root\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, \"templates\");\n if (existsSync(candidate)) {\n return candidate;\n }\n dir = dirname(dir);\n }\n // Fallback: relative to package root\n return join(dirname(dirname(__dirname)), \"templates\");\n}\n\nexport function findProjectRoot(startDir?: string): string {\n let dir = startDir ? resolve(startDir) : process.cwd();\n while (true) {\n // Check for common project root indicators\n const indicators = [\n \"package.json\",\n \"pyproject.toml\",\n \"Cargo.toml\",\n \"go.mod\",\n \".git\",\n ];\n for (const indicator of indicators) {\n if (existsSync(join(dir, indicator))) {\n return dir;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return startDir ? resolve(startDir) : process.cwd();\n}\n\nexport function findClaudeMd(projectRoot: string): string | null {\n // Check common CLAUDE.md locations\n const candidates = [\n join(projectRoot, \"CLAUDE.md\"),\n join(projectRoot, \".claude\", \"CLAUDE.md\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n return null;\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,aAAa,qBAAqB;AACrE,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AAGjB,IAAM,gBAAwB;AAAA,EACnC,MAAM,SAAS,MAA+B;AAC5C,WAAO,aAAa,MAAM,OAAO;AAAA,EACnC;AAAA,EAEA,MAAM,UAAU,MAAc,SAAgC;AAC5D,UAAM,cAAc,MAAM,SAAS,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAM,WAAW,MAAgC;AAC/C,WAAO,WAAW,IAAI;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAiC;AAC7C,WAAO,QAAQ,IAAI;AAAA,EACrB;AACF;;;ACrBA,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAC9B,SAAS,cAAAA,mBAAkB;AAEpB,SAAS,kBAA0B;AACxC,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAY,QAAQ,UAAU;AAEpC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,YAAY,KAAK,KAAK,WAAW;AACvC,QAAIA,YAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,GAAG;AAAA,EACnB;AAEA,SAAO,KAAK,QAAQ,QAAQ,SAAS,CAAC,GAAG,WAAW;AACtD;AAEO,SAAS,gBAAgB,UAA2B;AACzD,MAAI,MAAM,WAAW,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AACrD,SAAO,MAAM;AAEX,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,aAAa,YAAY;AAClC,UAAIA,YAAW,KAAK,KAAK,SAAS,CAAC,GAAG;AACpC,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO,WAAW,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AACpD;AAEO,SAAS,aAAa,aAAoC;AAE/D,QAAM,aAAa;AAAA,IACjB,KAAK,aAAa,WAAW;AAAA,IAC7B,KAAK,aAAa,WAAW,WAAW;AAAA,EAC1C;AACA,aAAW,aAAa,YAAY;AAClC,QAAIA,YAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;","names":["existsSync"]}
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/errors.ts
4
+ var TemplateNotFoundError = class extends Error {
5
+ constructor(name) {
6
+ super(`Template not found: "${name}"`);
7
+ this.name = "TemplateNotFoundError";
8
+ }
9
+ };
10
+ var ValidationError = class extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "ValidationError";
14
+ }
15
+ };
16
+ var FileExistsError = class extends Error {
17
+ constructor(path) {
18
+ super(`File already exists: ${path}`);
19
+ this.name = "FileExistsError";
20
+ }
21
+ };
22
+ function formatError(error) {
23
+ if (error instanceof TemplateNotFoundError) {
24
+ return `Error: ${error.message}`;
25
+ }
26
+ if (error instanceof ValidationError) {
27
+ return `Validation Error: ${error.message}`;
28
+ }
29
+ if (error instanceof FileExistsError) {
30
+ return `Error: ${error.message}`;
31
+ }
32
+ if (error instanceof Error) {
33
+ return `Error: ${error.message}`;
34
+ }
35
+ return `Error: ${String(error)}`;
36
+ }
37
+
38
+ export {
39
+ ValidationError,
40
+ formatError
41
+ };
42
+ //# sourceMappingURL=chunk-3WTPUEHL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/errors.ts"],"sourcesContent":["export class TemplateNotFoundError extends Error {\n constructor(name: string) {\n super(`Template not found: \"${name}\"`);\n this.name = \"TemplateNotFoundError\";\n }\n}\n\nexport class ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class FileExistsError extends Error {\n constructor(path: string) {\n super(`File already exists: ${path}`);\n this.name = \"FileExistsError\";\n }\n}\n\nexport function formatError(error: unknown): string {\n if (error instanceof TemplateNotFoundError) {\n return `Error: ${error.message}`;\n }\n if (error instanceof ValidationError) {\n return `Validation Error: ${error.message}`;\n }\n if (error instanceof FileExistsError) {\n return `Error: ${error.message}`;\n }\n if (error instanceof Error) {\n return `Error: ${error.message}`;\n }\n return `Error: ${String(error)}`;\n}\n"],"mappings":";;;AAAO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YAAY,MAAc;AACxB,UAAM,wBAAwB,IAAI,GAAG;AACrC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YAAY,MAAc;AACxB,UAAM,wBAAwB,IAAI,EAAE;AACpC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YAAY,OAAwB;AAClD,MAAI,iBAAiB,uBAAuB;AAC1C,WAAO,UAAU,MAAM,OAAO;AAAA,EAChC;AACA,MAAI,iBAAiB,iBAAiB;AACpC,WAAO,qBAAqB,MAAM,OAAO;AAAA,EAC3C;AACA,MAAI,iBAAiB,iBAAiB;AACpC,WAAO,UAAU,MAAM,OAAO;AAAA,EAChC;AACA,MAAI,iBAAiB,OAAO;AAC1B,WAAO,UAAU,MAAM,OAAO;AAAA,EAChC;AACA,SAAO,UAAU,OAAO,KAAK,CAAC;AAChC;","names":[]}
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/logger.ts
4
+ import chalk from "chalk";
5
+ function success(message) {
6
+ console.log(chalk.green(`\u2713 ${message}`));
7
+ }
8
+ function error(message) {
9
+ console.error(chalk.red(`\u2717 ${message}`));
10
+ }
11
+ function warn(message) {
12
+ console.log(chalk.yellow(`\u26A0 ${message}`));
13
+ }
14
+ function info(message) {
15
+ console.log(chalk.blue(`\u2139 ${message}`));
16
+ }
17
+ function dim(message) {
18
+ console.log(chalk.dim(message));
19
+ }
20
+
21
+ export {
22
+ success,
23
+ error,
24
+ warn,
25
+ info,
26
+ dim
27
+ };
28
+ //# sourceMappingURL=chunk-YHVOBZLV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nexport function success(message: string): void {\n console.log(chalk.green(`✓ ${message}`));\n}\n\nexport function error(message: string): void {\n console.error(chalk.red(`✗ ${message}`));\n}\n\nexport function warn(message: string): void {\n console.log(chalk.yellow(`⚠ ${message}`));\n}\n\nexport function info(message: string): void {\n console.log(chalk.blue(`ℹ ${message}`));\n}\n\nexport function dim(message: string): void {\n console.log(chalk.dim(message));\n}\n"],"mappings":";;;AAAA,OAAO,WAAW;AAEX,SAAS,QAAQ,SAAuB;AAC7C,UAAQ,IAAI,MAAM,MAAM,UAAK,OAAO,EAAE,CAAC;AACzC;AAEO,SAAS,MAAM,SAAuB;AAC3C,UAAQ,MAAM,MAAM,IAAI,UAAK,OAAO,EAAE,CAAC;AACzC;AAEO,SAAS,KAAK,SAAuB;AAC1C,UAAQ,IAAI,MAAM,OAAO,UAAK,OAAO,EAAE,CAAC;AAC1C;AAEO,SAAS,KAAK,SAAuB;AAC1C,UAAQ,IAAI,MAAM,KAAK,UAAK,OAAO,EAAE,CAAC;AACxC;AAEO,SAAS,IAAI,SAAuB;AACzC,UAAQ,IAAI,MAAM,IAAI,OAAO,CAAC;AAChC;","names":[]}
package/dist/cli.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ formatError
4
+ } from "./chunk-3WTPUEHL.js";
5
+ import {
6
+ error
7
+ } from "./chunk-YHVOBZLV.js";
8
+
9
+ // src/cli.ts
10
+ import { Command } from "commander";
11
+ var program = new Command();
12
+ program.name("dotclaudemd").description(
13
+ "CLAUDE.md Template Registry CLI \u2014 scaffold, lint, and health-check your CLAUDE.md files"
14
+ ).version("0.1.1");
15
+ program.command("init").description("Scaffold a CLAUDE.md from a template").option("--stack <name>", "Use a specific template by name (skip detection)").option("--global", "Write to ~/.claude/CLAUDE.md").option("--no-interactive", "Use defaults without prompting").option("--force", "Overwrite existing CLAUDE.md without prompting").action(async (options) => {
16
+ const { initCommand } = await import("./init-GLWLFVHN.js");
17
+ await initCommand({
18
+ stack: options.stack,
19
+ global: options.global,
20
+ noInteractive: !options.interactive,
21
+ force: options.force
22
+ });
23
+ });
24
+ program.command("lint [file]").description("Lint a CLAUDE.md for anti-patterns").option("--json", "Output results as JSON").action(async (file, options) => {
25
+ const { lintCommand } = await import("./lint-W7ZIDPL7.js");
26
+ await lintCommand(file, { json: options.json });
27
+ });
28
+ program.command("doctor").description("Check CLAUDE.md freshness against project state").option("--json", "Output results as JSON").action(async (options) => {
29
+ const { doctorCommand } = await import("./doctor-4B7J2EH3.js");
30
+ await doctorCommand({ json: options.json });
31
+ });
32
+ program.command("browse").description("Browse and preview available templates").option("--category <cat>", "Filter by category").option("--list", "Non-interactive list mode").action(async (options) => {
33
+ const { browseCommand } = await import("./browse-7V4CRGTH.js");
34
+ await browseCommand({ category: options.category, list: options.list });
35
+ });
36
+ program.parseAsync(process.argv).catch((err) => {
37
+ error(formatError(err));
38
+ process.exitCode = 1;
39
+ });
40
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { formatError } from \"./utils/errors.js\";\nimport * as logger from \"./utils/logger.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"dotclaudemd\")\n .description(\n \"CLAUDE.md Template Registry CLI — scaffold, lint, and health-check your CLAUDE.md files\",\n )\n .version(\"0.1.1\");\n\nprogram\n .command(\"init\")\n .description(\"Scaffold a CLAUDE.md from a template\")\n .option(\"--stack <name>\", \"Use a specific template by name (skip detection)\")\n .option(\"--global\", \"Write to ~/.claude/CLAUDE.md\")\n .option(\"--no-interactive\", \"Use defaults without prompting\")\n .option(\"--force\", \"Overwrite existing CLAUDE.md without prompting\")\n .action(async (options) => {\n const { initCommand } = await import(\"./commands/init.js\");\n await initCommand({\n stack: options.stack,\n global: options.global,\n noInteractive: !options.interactive,\n force: options.force,\n });\n });\n\nprogram\n .command(\"lint [file]\")\n .description(\"Lint a CLAUDE.md for anti-patterns\")\n .option(\"--json\", \"Output results as JSON\")\n .action(async (file, options) => {\n const { lintCommand } = await import(\"./commands/lint.js\");\n await lintCommand(file, { json: options.json });\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Check CLAUDE.md freshness against project state\")\n .option(\"--json\", \"Output results as JSON\")\n .action(async (options) => {\n const { doctorCommand } = await import(\"./commands/doctor.js\");\n await doctorCommand({ json: options.json });\n });\n\nprogram\n .command(\"browse\")\n .description(\"Browse and preview available templates\")\n .option(\"--category <cat>\", \"Filter by category\")\n .option(\"--list\", \"Non-interactive list mode\")\n .action(async (options) => {\n const { browseCommand } = await import(\"./commands/browse.js\");\n await browseCommand({ category: options.category, list: options.list });\n });\n\nprogram.parseAsync(process.argv).catch((err) => {\n logger.error(formatError(err));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;;AAAA,SAAS,eAAe;AAIxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,aAAa,EAClB;AAAA,EACC;AACF,EACC,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,sCAAsC,EAClD,OAAO,kBAAkB,kDAAkD,EAC3E,OAAO,YAAY,8BAA8B,EACjD,OAAO,oBAAoB,gCAAgC,EAC3D,OAAO,WAAW,gDAAgD,EAClE,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAoB;AACzD,QAAM,YAAY;AAAA,IAChB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,eAAe,CAAC,QAAQ;AAAA,IACxB,OAAO,QAAQ;AAAA,EACjB,CAAC;AACH,CAAC;AAEH,QACG,QAAQ,aAAa,EACrB,YAAY,oCAAoC,EAChD,OAAO,UAAU,wBAAwB,EACzC,OAAO,OAAO,MAAM,YAAY;AAC/B,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAoB;AACzD,QAAM,YAAY,MAAM,EAAE,MAAM,QAAQ,KAAK,CAAC;AAChD,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,iDAAiD,EAC7D,OAAO,UAAU,wBAAwB,EACzC,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,QAAM,cAAc,EAAE,MAAM,QAAQ,KAAK,CAAC;AAC5C,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,wCAAwC,EACpD,OAAO,oBAAoB,oBAAoB,EAC/C,OAAO,UAAU,2BAA2B,EAC5C,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,QAAM,cAAc,EAAE,UAAU,QAAQ,UAAU,MAAM,QAAQ,KAAK,CAAC;AACxE,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAQ;AAC9C,EAAO,MAAM,YAAY,GAAG,CAAC;AAC7B,UAAQ,WAAW;AACrB,CAAC;","names":[]}