@cregis-dev/cckit 0.6.5 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +221 -221
  2. package/package.json +1 -1
  3. package/registry.json +145 -128
  4. package/src/cli.js +79 -79
  5. package/src/commands/init.js +174 -161
  6. package/src/commands/status.js +125 -85
  7. package/src/commands/update.js +192 -151
  8. package/src/core/config.js +82 -74
  9. package/src/core/orchestrator.js +79 -79
  10. package/src/core/registry.js +60 -60
  11. package/src/steps/add-plugin.js +148 -0
  12. package/src/steps/configure-user.js +181 -181
  13. package/src/steps/enable-plugins.js +97 -97
  14. package/src/steps/install-bmad.js +85 -85
  15. package/src/steps/install-mcp.js +70 -70
  16. package/src/steps/install-rules.js +69 -69
  17. package/src/steps/install-skills.js +56 -56
  18. package/src/utils/compare-versions.js +106 -0
  19. package/src/utils/fs.js +33 -33
  20. package/src/utils/manifest.js +101 -99
  21. package/src/utils/prompt.js +41 -41
  22. package/templates/mcp/claude-code/.mcp.json +40 -40
  23. package/templates/rules/README.md +103 -103
  24. package/templates/rules/common/agents.md +49 -49
  25. package/templates/rules/common/coding-style.md +48 -48
  26. package/templates/rules/common/development-workflow.md +37 -37
  27. package/templates/rules/common/git-workflow.md +24 -24
  28. package/templates/rules/common/hooks.md +30 -30
  29. package/templates/rules/common/patterns.md +31 -31
  30. package/templates/rules/common/performance.md +55 -55
  31. package/templates/rules/common/security.md +29 -29
  32. package/templates/rules/common/testing.md +29 -29
  33. package/templates/rules/golang/coding-style.md +32 -32
  34. package/templates/rules/golang/hooks.md +17 -17
  35. package/templates/rules/golang/patterns.md +45 -45
  36. package/templates/rules/golang/security.md +34 -34
  37. package/templates/rules/golang/testing.md +31 -31
  38. package/templates/rules/python/coding-style.md +42 -42
  39. package/templates/rules/python/hooks.md +19 -19
  40. package/templates/rules/python/patterns.md +39 -39
  41. package/templates/rules/python/security.md +30 -30
  42. package/templates/rules/python/testing.md +38 -38
  43. package/templates/rules/swift/coding-style.md +47 -47
  44. package/templates/rules/swift/hooks.md +20 -20
  45. package/templates/rules/swift/patterns.md +66 -66
  46. package/templates/rules/swift/security.md +33 -33
  47. package/templates/rules/swift/testing.md +45 -45
  48. package/templates/rules/typescript/coding-style.md +65 -65
  49. package/templates/rules/typescript/hooks.md +22 -22
  50. package/templates/rules/typescript/patterns.md +52 -52
  51. package/templates/rules/typescript/security.md +28 -28
  52. package/templates/rules/typescript/testing.md +18 -18
package/src/utils/fs.js CHANGED
@@ -1,33 +1,33 @@
1
- import fse from 'fs-extra'
2
- import path from 'node:path'
3
- import crypto from 'node:crypto'
4
- import { glob } from 'glob'
5
-
6
- /**
7
- * Compute SHA256 hash of a file.
8
- *
9
- * @param {string} filePath - Absolute path to file
10
- * @returns {Promise<string>} Hash in format "sha256:<hex>"
11
- */
12
- export async function computeFileHash(filePath) {
13
- const content = await fse.readFile(filePath)
14
- const hash = crypto.createHash('sha256').update(content).digest('hex')
15
- return `sha256:${hash}`
16
- }
17
-
18
- /**
19
- * List all files in a directory recursively.
20
- *
21
- * @param {string} dir - Directory to scan
22
- * @returns {Promise<string[]>} Relative file paths
23
- */
24
- export async function listFiles(dir) {
25
- const pattern = '**/*'
26
- const files = await glob(pattern, {
27
- cwd: dir,
28
- nodir: true,
29
- dot: true,
30
- ignore: ['**/.git/**'],
31
- })
32
- return files
33
- }
1
+ import fse from 'fs-extra'
2
+ import path from 'node:path'
3
+ import crypto from 'node:crypto'
4
+ import { glob } from 'glob'
5
+
6
+ /**
7
+ * Compute SHA256 hash of a file.
8
+ *
9
+ * @param {string} filePath - Absolute path to file
10
+ * @returns {Promise<string>} Hash in format "sha256:<hex>"
11
+ */
12
+ export async function computeFileHash(filePath) {
13
+ const content = await fse.readFile(filePath)
14
+ const hash = crypto.createHash('sha256').update(content).digest('hex')
15
+ return `sha256:${hash}`
16
+ }
17
+
18
+ /**
19
+ * List all files in a directory recursively.
20
+ *
21
+ * @param {string} dir - Directory to scan
22
+ * @returns {Promise<string[]>} Relative file paths
23
+ */
24
+ export async function listFiles(dir) {
25
+ const pattern = '**/*'
26
+ const files = await glob(pattern, {
27
+ cwd: dir,
28
+ nodir: true,
29
+ dot: true,
30
+ ignore: ['**/.git/**'],
31
+ })
32
+ return files
33
+ }
@@ -1,99 +1,101 @@
1
- /**
2
- * Manifest utilities — read/write the cckit installation manifest.
3
- *
4
- * Manifest lives at `_bmad/_config/manifest.yaml` and tracks:
5
- * - Installation config (user preferences)
6
- * - Step execution results
7
- * - File registry (SHA256 hashes for tracked files)
8
- */
9
-
10
- import path from 'node:path'
11
- import fse from 'fs-extra'
12
- import YAML from 'yaml'
13
-
14
- export const MANIFEST_REL = path.join('_bmad', '_config', 'manifest.yaml')
15
-
16
- /**
17
- * Read and parse the manifest from a project directory.
18
- *
19
- * @param {string} dir - Project root directory
20
- * @returns {Promise<object>} Parsed manifest object
21
- * @throws {Error} If manifest does not exist
22
- */
23
- export async function readManifest(dir) {
24
- const manifestPath = path.join(path.resolve(dir), MANIFEST_REL)
25
- try {
26
- const content = await fse.readFile(manifestPath, 'utf8')
27
- return YAML.parse(content)
28
- } catch (err) {
29
- if (err.code === 'ENOENT') {
30
- throw new Error('cckit is not installed. Run `cckit init` first.')
31
- }
32
- throw err
33
- }
34
- }
35
-
36
- /**
37
- * Write manifest to a project directory.
38
- *
39
- * @param {string} dir - Project root directory
40
- * @param {object} manifest - Manifest data to write
41
- * @returns {Promise<void>}
42
- */
43
- export async function writeManifest(dir, manifest) {
44
- const manifestPath = path.join(path.resolve(dir), MANIFEST_REL)
45
- await fse.ensureDir(path.dirname(manifestPath))
46
- await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
47
- }
48
-
49
- /**
50
- * Serialize step results for manifest storage.
51
- *
52
- * @param {Array<import('../core/orchestrator.js').StepResult>} steps
53
- * @returns {Array<object>}
54
- */
55
- export function serializeSteps(steps) {
56
- return steps.map(s => ({
57
- stepId: s.stepId,
58
- success: s.success,
59
- skipped: s.skipped || false,
60
- ...(s.details ? { details: s.details } : {}),
61
- ...(s.error ? { error: s.error } : {}),
62
- }))
63
- }
64
-
65
- /**
66
- * Build a new manifest from orchestration results.
67
- *
68
- * @param {object} params
69
- * @param {object} params.config - Merged config
70
- * @param {import('../core/orchestrator.js').RunReport} params.report - Orchestration report
71
- * @param {string} params.version - cckit version
72
- * @returns {object} Manifest object
73
- */
74
- export function buildManifest({ config, report, version }) {
75
- return {
76
- cckit: {
77
- version,
78
- installedAt: new Date().toISOString(),
79
- config: {
80
- user_name: config.user_name,
81
- communication_language: config.communication_language,
82
- document_output_language: config.document_output_language,
83
- languages: config.languages,
84
- },
85
- steps: serializeSteps(report.steps),
86
- fileRegistry: report.fileRegistry,
87
- }
88
- }
89
- }
90
-
91
- /**
92
- * Check if a manifest uses the legacy v1 format (modules-based).
93
- *
94
- * @param {object} manifest - Parsed manifest
95
- * @returns {boolean}
96
- */
97
- export function isLegacyManifest(manifest) {
98
- return !!(manifest.cckit?.modules) && !manifest.cckit?.steps
99
- }
1
+ /**
2
+ * Manifest utilities — read/write the cckit installation manifest.
3
+ *
4
+ * Manifest lives at `_bmad/_config/manifest.yaml` and tracks:
5
+ * - Installation config (user preferences)
6
+ * - Step execution results
7
+ * - File registry (SHA256 hashes for tracked files)
8
+ */
9
+
10
+ import path from 'node:path'
11
+ import fse from 'fs-extra'
12
+ import YAML from 'yaml'
13
+
14
+ export const MANIFEST_REL = path.join('_bmad', '_config', 'manifest.yaml')
15
+
16
+ /**
17
+ * Read and parse the manifest from a project directory.
18
+ *
19
+ * @param {string} dir - Project root directory
20
+ * @returns {Promise<object>} Parsed manifest object
21
+ * @throws {Error} If manifest does not exist
22
+ */
23
+ export async function readManifest(dir) {
24
+ const manifestPath = path.join(path.resolve(dir), MANIFEST_REL)
25
+ try {
26
+ const content = await fse.readFile(manifestPath, 'utf8')
27
+ return YAML.parse(content)
28
+ } catch (err) {
29
+ if (err.code === 'ENOENT') {
30
+ throw new Error('cckit is not installed. Run `cckit init` first.')
31
+ }
32
+ throw err
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Write manifest to a project directory.
38
+ *
39
+ * @param {string} dir - Project root directory
40
+ * @param {object} manifest - Manifest data to write
41
+ * @returns {Promise<void>}
42
+ */
43
+ export async function writeManifest(dir, manifest) {
44
+ const manifestPath = path.join(path.resolve(dir), MANIFEST_REL)
45
+ await fse.ensureDir(path.dirname(manifestPath))
46
+ await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
47
+ }
48
+
49
+ /**
50
+ * Serialize step results for manifest storage.
51
+ *
52
+ * @param {Array<import('../core/orchestrator.js').StepResult>} steps
53
+ * @returns {Array<object>}
54
+ */
55
+ export function serializeSteps(steps) {
56
+ return steps.map(s => ({
57
+ stepId: s.stepId,
58
+ success: s.success,
59
+ skipped: s.skipped || false,
60
+ ...(s.details ? { details: s.details } : {}),
61
+ ...(s.error ? { error: s.error } : {}),
62
+ }))
63
+ }
64
+
65
+ /**
66
+ * Build a new manifest from orchestration results.
67
+ *
68
+ * @param {object} params
69
+ * @param {object} params.config - Merged config
70
+ * @param {import('../core/orchestrator.js').RunReport} params.report - Orchestration report
71
+ * @param {string} params.version - cckit version
72
+ * @param {object} [params.versions] - Component versions from registry
73
+ * @returns {object} Manifest object
74
+ */
75
+ export function buildManifest({ config, report, version, versions = {} }) {
76
+ return {
77
+ cckit: {
78
+ version,
79
+ installedAt: new Date().toISOString(),
80
+ config: {
81
+ user_name: config.user_name,
82
+ communication_language: config.communication_language,
83
+ document_output_language: config.document_output_language,
84
+ languages: config.languages,
85
+ versions,
86
+ },
87
+ steps: serializeSteps(report.steps),
88
+ fileRegistry: report.fileRegistry,
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Check if a manifest uses the legacy v1 format (modules-based).
95
+ *
96
+ * @param {object} manifest - Parsed manifest
97
+ * @returns {boolean}
98
+ */
99
+ export function isLegacyManifest(manifest) {
100
+ return !!(manifest.cckit?.modules) && !manifest.cckit?.steps
101
+ }
@@ -1,41 +1,41 @@
1
- /**
2
- * Readline utility for interactive prompts.
3
- */
4
-
5
- import { createInterface } from 'node:readline'
6
-
7
- /**
8
- * Prompt user for input via readline.
9
- * Returns empty string if user presses Enter without typing.
10
- *
11
- * @param {string} question - Prompt text
12
- * @param {object} [_deps] - Injectable dependencies for testing
13
- * @param {object} [_deps.rl] - Mock readline interface
14
- * @returns {Promise<string>} User input (trimmed)
15
- */
16
- export function askQuestion(question, _deps = {}) {
17
- const { rl: mockRl } = _deps
18
-
19
- // If mock is provided, use it directly
20
- if (mockRl) {
21
- return new Promise((resolve) => {
22
- mockRl.question(question, (answer) => {
23
- mockRl.close()
24
- resolve((answer == null ? '' : String(answer)).trim())
25
- })
26
- })
27
- }
28
-
29
- // Default: use real readline
30
- const rl = createInterface({
31
- input: process.stdin,
32
- output: process.stdout,
33
- })
34
-
35
- return new Promise((resolve) => {
36
- rl.question(question, (answer) => {
37
- rl.close()
38
- resolve((answer == null ? '' : String(answer)).trim())
39
- })
40
- })
41
- }
1
+ /**
2
+ * Readline utility for interactive prompts.
3
+ */
4
+
5
+ import { createInterface } from 'node:readline'
6
+
7
+ /**
8
+ * Prompt user for input via readline.
9
+ * Returns empty string if user presses Enter without typing.
10
+ *
11
+ * @param {string} question - Prompt text
12
+ * @param {object} [_deps] - Injectable dependencies for testing
13
+ * @param {object} [_deps.rl] - Mock readline interface
14
+ * @returns {Promise<string>} User input (trimmed)
15
+ */
16
+ export function askQuestion(question, _deps = {}) {
17
+ const { rl: mockRl } = _deps
18
+
19
+ // If mock is provided, use it directly
20
+ if (mockRl) {
21
+ return new Promise((resolve) => {
22
+ mockRl.question(question, (answer) => {
23
+ mockRl.close()
24
+ resolve((answer == null ? '' : String(answer)).trim())
25
+ })
26
+ })
27
+ }
28
+
29
+ // Default: use real readline
30
+ const rl = createInterface({
31
+ input: process.stdin,
32
+ output: process.stdout,
33
+ })
34
+
35
+ return new Promise((resolve) => {
36
+ rl.question(question, (answer) => {
37
+ rl.close()
38
+ resolve((answer == null ? '' : String(answer)).trim())
39
+ })
40
+ })
41
+ }
@@ -1,40 +1,40 @@
1
- {
2
- "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/.mcp.schema.json",
3
- "mcpServers": {
4
- "cf-bindings": {
5
- "command": "npx",
6
- "args": ["-y", "mcp-remote", "https://bindings.mcp.cloudflare.com/mcp"],
7
- "description": "Workers Bindings: D1, R2, KV, Queues 资源管理",
8
- "disabled": true
9
- },
10
- "cf-observability": {
11
- "command": "npx",
12
- "args": ["-y", "mcp-remote", "https://observability.mcp.cloudflare.com/mcp"],
13
- "description": "Workers 日志分析与调试",
14
- "disabled": true
15
- },
16
- "cf-builds": {
17
- "command": "npx",
18
- "args": ["-y", "mcp-remote", "https://builds.mcp.cloudflare.com/mcp"],
19
- "description": "Workers 构建状态监控",
20
- "disabled": true
21
- },
22
- "cf-docs": {
23
- "command": "npx",
24
- "args": ["-y", "mcp-remote", "https://docs.mcp.cloudflare.com/sse"],
25
- "description": "Cloudflare 官方文档查询",
26
- "disabled": true
27
- },
28
- "context7": {
29
- "command": "npx",
30
- "args": ["-y", "@upstash/context7-mcp@latest"],
31
- "description": "库文档实时查询(Hono, Zod, React Query, Zustand, Radix UI 等)"
32
- },
33
- "playwright": {
34
- "command": "npx",
35
- "args": ["-y", "@anthropic-ai/mcp-playwright@latest"],
36
- "description": "Playwright 浏览器自动化,用于 E2E 测试和 UI 验证",
37
- "disabled": true
38
- }
39
- }
40
- }
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/.mcp.schema.json",
3
+ "mcpServers": {
4
+ "cf-bindings": {
5
+ "command": "npx",
6
+ "args": ["-y", "mcp-remote", "https://bindings.mcp.cloudflare.com/mcp"],
7
+ "description": "Workers Bindings: D1, R2, KV, Queues 资源管理",
8
+ "disabled": true
9
+ },
10
+ "cf-observability": {
11
+ "command": "npx",
12
+ "args": ["-y", "mcp-remote", "https://observability.mcp.cloudflare.com/mcp"],
13
+ "description": "Workers 日志分析与调试",
14
+ "disabled": true
15
+ },
16
+ "cf-builds": {
17
+ "command": "npx",
18
+ "args": ["-y", "mcp-remote", "https://builds.mcp.cloudflare.com/mcp"],
19
+ "description": "Workers 构建状态监控",
20
+ "disabled": true
21
+ },
22
+ "cf-docs": {
23
+ "command": "npx",
24
+ "args": ["-y", "mcp-remote", "https://docs.mcp.cloudflare.com/sse"],
25
+ "description": "Cloudflare 官方文档查询",
26
+ "disabled": true
27
+ },
28
+ "context7": {
29
+ "command": "npx",
30
+ "args": ["-y", "@upstash/context7-mcp@latest"],
31
+ "description": "库文档实时查询(Hono, Zod, React Query, Zustand, Radix UI 等)"
32
+ },
33
+ "playwright": {
34
+ "command": "npx",
35
+ "args": ["-y", "@anthropic-ai/mcp-playwright@latest"],
36
+ "description": "Playwright 浏览器自动化,用于 E2E 测试和 UI 验证",
37
+ "disabled": true
38
+ }
39
+ }
40
+ }
@@ -1,103 +1,103 @@
1
- # Rules
2
- ## Structure
3
-
4
- Rules are organized into a **common** layer plus **language-specific** directories:
5
-
6
- ```
7
- rules/
8
- ├── common/ # Language-agnostic principles (always install)
9
- │ ├── coding-style.md
10
- │ ├── git-workflow.md
11
- │ ├── testing.md
12
- │ ├── performance.md
13
- │ ├── patterns.md
14
- │ ├── hooks.md
15
- │ ├── agents.md
16
- │ └── security.md
17
- ├── typescript/ # TypeScript/JavaScript specific
18
- ├── python/ # Python specific
19
- ├── golang/ # Go specific
20
- └── swift/ # Swift specific
21
- ```
22
-
23
- - **common/** contains universal principles — no language-specific code examples.
24
- - **Language directories** extend the common rules with framework-specific patterns, tools, and code examples. Each file references its common counterpart.
25
-
26
- ## Installation
27
-
28
- ### Option 1: Install Script (Recommended)
29
-
30
- ```bash
31
- # Install common + one or more language-specific rule sets
32
- ./install.sh typescript
33
- ./install.sh python
34
- ./install.sh golang
35
- ./install.sh swift
36
-
37
- # Install multiple languages at once
38
- ./install.sh typescript python
39
- ```
40
-
41
- ### Option 2: Manual Installation
42
-
43
- > **Important:** Copy entire directories — do NOT flatten with `/*`.
44
- > Common and language-specific directories contain files with the same names.
45
- > Flattening them into one directory causes language-specific files to overwrite
46
- > common rules, and breaks the relative `../common/` references used by
47
- > language-specific files.
48
-
49
- ```bash
50
- # Install common rules (required for all projects)
51
- cp -r rules/common ~/.claude/rules/common
52
-
53
- # Install language-specific rules based on your project's tech stack
54
- cp -r rules/typescript ~/.claude/rules/typescript
55
- cp -r rules/python ~/.claude/rules/python
56
- cp -r rules/golang ~/.claude/rules/golang
57
- cp -r rules/swift ~/.claude/rules/swift
58
-
59
- # Attention ! ! ! Configure according to your actual project requirements; the configuration here is for reference only.
60
- ```
61
-
62
- ## Rules vs Skills
63
-
64
- - **Rules** define standards, conventions, and checklists that apply broadly (e.g., "80% test coverage", "no hardcoded secrets").
65
- - **Skills** (`skills/` directory) provide deep, actionable reference material for specific tasks (e.g., `python-patterns`, `golang-testing`).
66
-
67
- Language-specific rule files reference relevant skills where appropriate. Rules tell you *what* to do; skills tell you *how* to do it.
68
-
69
- ## Adding a New Language
70
-
71
- To add support for a new language (e.g., `rust/`):
72
-
73
- 1. Create a `rules/rust/` directory
74
- 2. Add files that extend the common rules:
75
- - `coding-style.md` — formatting tools, idioms, error handling patterns
76
- - `testing.md` — test framework, coverage tools, test organization
77
- - `patterns.md` — language-specific design patterns
78
- - `hooks.md` — PostToolUse hooks for formatters, linters, type checkers
79
- - `security.md` — secret management, security scanning tools
80
- 3. Each file should start with:
81
- ```
82
- > This file extends [common/xxx.md](../common/xxx.md) with <Language> specific content.
83
- ```
84
- 4. Reference existing skills if available, or create new ones under `skills/`.
85
-
86
- ## Rule Priority
87
-
88
- When language-specific rules and common rules conflict, **language-specific rules take precedence** (specific overrides general). This follows the standard layered configuration pattern (similar to CSS specificity or `.gitignore` precedence).
89
-
90
- - `rules/common/` defines universal defaults applicable to all projects.
91
- - `rules/golang/`, `rules/python/`, `rules/typescript/`, etc. override those defaults where language idioms differ.
92
-
93
- ### Example
94
-
95
- `common/coding-style.md` recommends immutability as a default principle. A language-specific `golang/coding-style.md` can override this:
96
-
97
- > Idiomatic Go uses pointer receivers for struct mutation — see [common/coding-style.md](../common/coding-style.md) for the general principle, but Go-idiomatic mutation is preferred here.
98
-
99
- ### Common rules with override notes
100
-
101
- Rules in `rules/common/` that may be overridden by language-specific files are marked with:
102
-
103
- > **Language note**: This rule may be overridden by language-specific rules for languages where this pattern is not idiomatic.
1
+ # Rules
2
+ ## Structure
3
+
4
+ Rules are organized into a **common** layer plus **language-specific** directories:
5
+
6
+ ```
7
+ rules/
8
+ ├── common/ # Language-agnostic principles (always install)
9
+ │ ├── coding-style.md
10
+ │ ├── git-workflow.md
11
+ │ ├── testing.md
12
+ │ ├── performance.md
13
+ │ ├── patterns.md
14
+ │ ├── hooks.md
15
+ │ ├── agents.md
16
+ │ └── security.md
17
+ ├── typescript/ # TypeScript/JavaScript specific
18
+ ├── python/ # Python specific
19
+ ├── golang/ # Go specific
20
+ └── swift/ # Swift specific
21
+ ```
22
+
23
+ - **common/** contains universal principles — no language-specific code examples.
24
+ - **Language directories** extend the common rules with framework-specific patterns, tools, and code examples. Each file references its common counterpart.
25
+
26
+ ## Installation
27
+
28
+ ### Option 1: Install Script (Recommended)
29
+
30
+ ```bash
31
+ # Install common + one or more language-specific rule sets
32
+ ./install.sh typescript
33
+ ./install.sh python
34
+ ./install.sh golang
35
+ ./install.sh swift
36
+
37
+ # Install multiple languages at once
38
+ ./install.sh typescript python
39
+ ```
40
+
41
+ ### Option 2: Manual Installation
42
+
43
+ > **Important:** Copy entire directories — do NOT flatten with `/*`.
44
+ > Common and language-specific directories contain files with the same names.
45
+ > Flattening them into one directory causes language-specific files to overwrite
46
+ > common rules, and breaks the relative `../common/` references used by
47
+ > language-specific files.
48
+
49
+ ```bash
50
+ # Install common rules (required for all projects)
51
+ cp -r rules/common ~/.claude/rules/common
52
+
53
+ # Install language-specific rules based on your project's tech stack
54
+ cp -r rules/typescript ~/.claude/rules/typescript
55
+ cp -r rules/python ~/.claude/rules/python
56
+ cp -r rules/golang ~/.claude/rules/golang
57
+ cp -r rules/swift ~/.claude/rules/swift
58
+
59
+ # Attention ! ! ! Configure according to your actual project requirements; the configuration here is for reference only.
60
+ ```
61
+
62
+ ## Rules vs Skills
63
+
64
+ - **Rules** define standards, conventions, and checklists that apply broadly (e.g., "80% test coverage", "no hardcoded secrets").
65
+ - **Skills** (`skills/` directory) provide deep, actionable reference material for specific tasks (e.g., `python-patterns`, `golang-testing`).
66
+
67
+ Language-specific rule files reference relevant skills where appropriate. Rules tell you *what* to do; skills tell you *how* to do it.
68
+
69
+ ## Adding a New Language
70
+
71
+ To add support for a new language (e.g., `rust/`):
72
+
73
+ 1. Create a `rules/rust/` directory
74
+ 2. Add files that extend the common rules:
75
+ - `coding-style.md` — formatting tools, idioms, error handling patterns
76
+ - `testing.md` — test framework, coverage tools, test organization
77
+ - `patterns.md` — language-specific design patterns
78
+ - `hooks.md` — PostToolUse hooks for formatters, linters, type checkers
79
+ - `security.md` — secret management, security scanning tools
80
+ 3. Each file should start with:
81
+ ```
82
+ > This file extends [common/xxx.md](../common/xxx.md) with <Language> specific content.
83
+ ```
84
+ 4. Reference existing skills if available, or create new ones under `skills/`.
85
+
86
+ ## Rule Priority
87
+
88
+ When language-specific rules and common rules conflict, **language-specific rules take precedence** (specific overrides general). This follows the standard layered configuration pattern (similar to CSS specificity or `.gitignore` precedence).
89
+
90
+ - `rules/common/` defines universal defaults applicable to all projects.
91
+ - `rules/golang/`, `rules/python/`, `rules/typescript/`, etc. override those defaults where language idioms differ.
92
+
93
+ ### Example
94
+
95
+ `common/coding-style.md` recommends immutability as a default principle. A language-specific `golang/coding-style.md` can override this:
96
+
97
+ > Idiomatic Go uses pointer receivers for struct mutation — see [common/coding-style.md](../common/coding-style.md) for the general principle, but Go-idiomatic mutation is preferred here.
98
+
99
+ ### Common rules with override notes
100
+
101
+ Rules in `rules/common/` that may be overridden by language-specific files are marked with:
102
+
103
+ > **Language note**: This rule may be overridden by language-specific rules for languages where this pattern is not idiomatic.