@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.
- package/README.md +221 -221
- package/package.json +1 -1
- package/registry.json +145 -128
- package/src/cli.js +79 -79
- package/src/commands/init.js +174 -161
- package/src/commands/status.js +125 -85
- package/src/commands/update.js +192 -151
- package/src/core/config.js +82 -74
- package/src/core/orchestrator.js +79 -79
- package/src/core/registry.js +60 -60
- package/src/steps/add-plugin.js +148 -0
- package/src/steps/configure-user.js +181 -181
- package/src/steps/enable-plugins.js +97 -97
- package/src/steps/install-bmad.js +85 -85
- package/src/steps/install-mcp.js +70 -70
- package/src/steps/install-rules.js +69 -69
- package/src/steps/install-skills.js +56 -56
- package/src/utils/compare-versions.js +106 -0
- package/src/utils/fs.js +33 -33
- package/src/utils/manifest.js +101 -99
- package/src/utils/prompt.js +41 -41
- package/templates/mcp/claude-code/.mcp.json +40 -40
- package/templates/rules/README.md +103 -103
- package/templates/rules/common/agents.md +49 -49
- package/templates/rules/common/coding-style.md +48 -48
- package/templates/rules/common/development-workflow.md +37 -37
- package/templates/rules/common/git-workflow.md +24 -24
- package/templates/rules/common/hooks.md +30 -30
- package/templates/rules/common/patterns.md +31 -31
- package/templates/rules/common/performance.md +55 -55
- package/templates/rules/common/security.md +29 -29
- package/templates/rules/common/testing.md +29 -29
- package/templates/rules/golang/coding-style.md +32 -32
- package/templates/rules/golang/hooks.md +17 -17
- package/templates/rules/golang/patterns.md +45 -45
- package/templates/rules/golang/security.md +34 -34
- package/templates/rules/golang/testing.md +31 -31
- package/templates/rules/python/coding-style.md +42 -42
- package/templates/rules/python/hooks.md +19 -19
- package/templates/rules/python/patterns.md +39 -39
- package/templates/rules/python/security.md +30 -30
- package/templates/rules/python/testing.md +38 -38
- package/templates/rules/swift/coding-style.md +47 -47
- package/templates/rules/swift/hooks.md +20 -20
- package/templates/rules/swift/patterns.md +66 -66
- package/templates/rules/swift/security.md +33 -33
- package/templates/rules/swift/testing.md +45 -45
- package/templates/rules/typescript/coding-style.md +65 -65
- package/templates/rules/typescript/hooks.md +22 -22
- package/templates/rules/typescript/patterns.md +52 -52
- package/templates/rules/typescript/security.md +28 -28
- 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
|
+
}
|
package/src/utils/manifest.js
CHANGED
|
@@ -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
|
-
* @
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
}
|
package/src/utils/prompt.js
CHANGED
|
@@ -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.
|