@cregis-dev/cckit 0.6.6 → 0.6.8

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 (54) hide show
  1. package/LICENSE +21 -21
  2. package/bin/cckit.js +3 -3
  3. package/package.json +53 -53
  4. package/registry.json +145 -145
  5. package/src/cli.js +79 -79
  6. package/src/commands/init.js +174 -174
  7. package/src/commands/status.js +125 -125
  8. package/src/commands/update.js +192 -192
  9. package/src/core/config.js +82 -75
  10. package/src/core/orchestrator.js +79 -79
  11. package/src/core/registry.js +60 -60
  12. package/src/steps/add-plugin.js +148 -116
  13. package/src/steps/configure-user.js +181 -181
  14. package/src/steps/enable-plugins.js +97 -97
  15. package/src/steps/install-bmad.js +85 -85
  16. package/src/steps/install-mcp.js +70 -70
  17. package/src/steps/install-rules.js +69 -69
  18. package/src/steps/install-skills.js +56 -56
  19. package/src/utils/compare-versions.js +106 -106
  20. package/src/utils/fs.js +33 -33
  21. package/src/utils/logger.js +16 -16
  22. package/src/utils/manifest.js +101 -101
  23. package/src/utils/prompt.js +41 -41
  24. package/templates/mcp/claude-code/.mcp.json +40 -40
  25. package/templates/rules/README.md +103 -103
  26. package/templates/rules/common/agents.md +49 -49
  27. package/templates/rules/common/coding-style.md +48 -48
  28. package/templates/rules/common/development-workflow.md +37 -37
  29. package/templates/rules/common/git-workflow.md +24 -24
  30. package/templates/rules/common/hooks.md +30 -30
  31. package/templates/rules/common/patterns.md +31 -31
  32. package/templates/rules/common/performance.md +55 -55
  33. package/templates/rules/common/security.md +29 -29
  34. package/templates/rules/common/testing.md +29 -29
  35. package/templates/rules/golang/coding-style.md +32 -32
  36. package/templates/rules/golang/hooks.md +17 -17
  37. package/templates/rules/golang/patterns.md +45 -45
  38. package/templates/rules/golang/security.md +34 -34
  39. package/templates/rules/golang/testing.md +31 -31
  40. package/templates/rules/python/coding-style.md +42 -42
  41. package/templates/rules/python/hooks.md +19 -19
  42. package/templates/rules/python/patterns.md +39 -39
  43. package/templates/rules/python/security.md +30 -30
  44. package/templates/rules/python/testing.md +38 -38
  45. package/templates/rules/swift/coding-style.md +47 -47
  46. package/templates/rules/swift/hooks.md +20 -20
  47. package/templates/rules/swift/patterns.md +66 -66
  48. package/templates/rules/swift/security.md +33 -33
  49. package/templates/rules/swift/testing.md +45 -45
  50. package/templates/rules/typescript/coding-style.md +65 -65
  51. package/templates/rules/typescript/hooks.md +22 -22
  52. package/templates/rules/typescript/patterns.md +52 -52
  53. package/templates/rules/typescript/security.md +28 -28
  54. package/templates/rules/typescript/testing.md +18 -18
@@ -1,106 +1,106 @@
1
- /**
2
- * Version comparison utilities for detecting configuration changes.
3
- *
4
- * Compares installed versions (from manifest) with registry versions
5
- * to determine which components need updating.
6
- */
7
-
8
- import { loadRegistry } from '../core/registry.js'
9
-
10
- /**
11
- * Component map: stepId -> version key in registry.versions
12
- */
13
- const COMPONENT_MAP = {
14
- 'configure-user': 'userSettings',
15
- 'enable-plugins': 'plugins',
16
- 'install-rules': 'rules',
17
- 'install-skills': 'skills',
18
- 'install-bmad': 'bmad',
19
- 'install-mcp': 'mcp',
20
- }
21
-
22
- /**
23
- * Compare installed versions against registry versions.
24
- *
25
- * @param {object} manifest - Installed manifest with config.versions
26
- * @param {object} registry - Current registry with versions
27
- * @returns {object} - { needsUpdate: boolean, components: { [stepId]: { current, latest, needsUpdate } } }
28
- */
29
- export function compareVersions(manifest, registry) {
30
- const installedVersions = manifest.cckit?.config?.versions || {}
31
- const latestVersions = registry.versions || {}
32
-
33
- const components = {}
34
- let needsUpdate = false
35
-
36
- // Compare each component
37
- for (const [stepId, versionKey] of Object.entries(COMPONENT_MAP)) {
38
- const current = installedVersions[versionKey]
39
- const latest = latestVersions[versionKey]
40
-
41
- // If either version is missing, consider it needs update
42
- const componentNeedsUpdate = !current || !latest || current !== latest
43
-
44
- components[stepId] = {
45
- versionKey,
46
- current: current || 'none',
47
- latest: latest || 'none',
48
- needsUpdate: componentNeedsUpdate,
49
- }
50
-
51
- if (componentNeedsUpdate) {
52
- needsUpdate = true
53
- }
54
- }
55
-
56
- return { needsUpdate, components }
57
- }
58
-
59
- /**
60
- * Check if specific component needs update.
61
- *
62
- * @param {object} manifest - Installed manifest
63
- * @param {object} registry - Current registry
64
- * @param {string} stepId - Step ID to check
65
- * @returns {boolean}
66
- */
67
- export function needsComponentUpdate(manifest, registry, stepId) {
68
- const versionKey = COMPONENT_MAP[stepId]
69
- if (!versionKey) return false
70
-
71
- const current = manifest.cckit?.config?.versions?.[versionKey]
72
- const latest = registry.versions?.[versionKey]
73
-
74
- return !current || !latest || current !== latest
75
- }
76
-
77
- /**
78
- * Get version info for a specific component.
79
- *
80
- * @param {object} registry - Current registry
81
- * @returns {object} - { plugins, rules, skills, bmad, mcp, userSettings }
82
- */
83
- export function getRegistryVersions(registry) {
84
- return registry.versions || {}
85
- }
86
-
87
- /**
88
- * Load registry and compare versions (async).
89
- *
90
- * @param {string} targetDir - Project directory
91
- * @returns {Promise<{ needsUpdate: boolean, components: object }>}
92
- */
93
- export async function checkForUpdates(targetDir) {
94
- const { readManifest } = await import('../utils/manifest.js')
95
- const registry = await loadRegistry()
96
-
97
- let manifest
98
- try {
99
- manifest = await readManifest(targetDir)
100
- } catch {
101
- // Not installed yet
102
- return { needsUpdate: true, components: {}, notInstalled: true }
103
- }
104
-
105
- return compareVersions(manifest, registry)
106
- }
1
+ /**
2
+ * Version comparison utilities for detecting configuration changes.
3
+ *
4
+ * Compares installed versions (from manifest) with registry versions
5
+ * to determine which components need updating.
6
+ */
7
+
8
+ import { loadRegistry } from '../core/registry.js'
9
+
10
+ /**
11
+ * Component map: stepId -> version key in registry.versions
12
+ */
13
+ const COMPONENT_MAP = {
14
+ 'configure-user': 'userSettings',
15
+ 'enable-plugins': 'plugins',
16
+ 'install-rules': 'rules',
17
+ 'install-skills': 'skills',
18
+ 'install-bmad': 'bmad',
19
+ 'install-mcp': 'mcp',
20
+ }
21
+
22
+ /**
23
+ * Compare installed versions against registry versions.
24
+ *
25
+ * @param {object} manifest - Installed manifest with config.versions
26
+ * @param {object} registry - Current registry with versions
27
+ * @returns {object} - { needsUpdate: boolean, components: { [stepId]: { current, latest, needsUpdate } } }
28
+ */
29
+ export function compareVersions(manifest, registry) {
30
+ const installedVersions = manifest.cckit?.config?.versions || {}
31
+ const latestVersions = registry.versions || {}
32
+
33
+ const components = {}
34
+ let needsUpdate = false
35
+
36
+ // Compare each component
37
+ for (const [stepId, versionKey] of Object.entries(COMPONENT_MAP)) {
38
+ const current = installedVersions[versionKey]
39
+ const latest = latestVersions[versionKey]
40
+
41
+ // If either version is missing, consider it needs update
42
+ const componentNeedsUpdate = !current || !latest || current !== latest
43
+
44
+ components[stepId] = {
45
+ versionKey,
46
+ current: current || 'none',
47
+ latest: latest || 'none',
48
+ needsUpdate: componentNeedsUpdate,
49
+ }
50
+
51
+ if (componentNeedsUpdate) {
52
+ needsUpdate = true
53
+ }
54
+ }
55
+
56
+ return { needsUpdate, components }
57
+ }
58
+
59
+ /**
60
+ * Check if specific component needs update.
61
+ *
62
+ * @param {object} manifest - Installed manifest
63
+ * @param {object} registry - Current registry
64
+ * @param {string} stepId - Step ID to check
65
+ * @returns {boolean}
66
+ */
67
+ export function needsComponentUpdate(manifest, registry, stepId) {
68
+ const versionKey = COMPONENT_MAP[stepId]
69
+ if (!versionKey) return false
70
+
71
+ const current = manifest.cckit?.config?.versions?.[versionKey]
72
+ const latest = registry.versions?.[versionKey]
73
+
74
+ return !current || !latest || current !== latest
75
+ }
76
+
77
+ /**
78
+ * Get version info for a specific component.
79
+ *
80
+ * @param {object} registry - Current registry
81
+ * @returns {object} - { plugins, rules, skills, bmad, mcp, userSettings }
82
+ */
83
+ export function getRegistryVersions(registry) {
84
+ return registry.versions || {}
85
+ }
86
+
87
+ /**
88
+ * Load registry and compare versions (async).
89
+ *
90
+ * @param {string} targetDir - Project directory
91
+ * @returns {Promise<{ needsUpdate: boolean, components: object }>}
92
+ */
93
+ export async function checkForUpdates(targetDir) {
94
+ const { readManifest } = await import('../utils/manifest.js')
95
+ const registry = await loadRegistry()
96
+
97
+ let manifest
98
+ try {
99
+ manifest = await readManifest(targetDir)
100
+ } catch {
101
+ // Not installed yet
102
+ return { needsUpdate: true, components: {}, notInstalled: true }
103
+ }
104
+
105
+ return compareVersions(manifest, registry)
106
+ }
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,16 +1,16 @@
1
- import chalk from 'chalk'
2
-
3
- export function createLogger(options = {}) {
4
- const { write = (msg) => console.log(msg), debug: debugEnabled = false } = options
5
-
6
- return {
7
- info: (msg) => write(msg),
8
- success: (msg) => write(chalk.green(` \u2713 ${msg}`)),
9
- error: (msg) => write(chalk.red(` \u2717 ${msg}`)),
10
- warn: (msg) => write(chalk.yellow(` \u26A0 ${msg}`)),
11
- debug: (msg) => { if (debugEnabled) write(chalk.gray(` [debug] ${msg}`)) },
12
- step: (current, total, msg) => write(chalk.blue(` [${current}/${total}] ${msg}`)),
13
- banner: (msg) => write(chalk.bold.green(`\n${msg}\n`)),
14
- newline: () => write('')
15
- }
16
- }
1
+ import chalk from 'chalk'
2
+
3
+ export function createLogger(options = {}) {
4
+ const { write = (msg) => console.log(msg), debug: debugEnabled = false } = options
5
+
6
+ return {
7
+ info: (msg) => write(msg),
8
+ success: (msg) => write(chalk.green(` \u2713 ${msg}`)),
9
+ error: (msg) => write(chalk.red(` \u2717 ${msg}`)),
10
+ warn: (msg) => write(chalk.yellow(` \u26A0 ${msg}`)),
11
+ debug: (msg) => { if (debugEnabled) write(chalk.gray(` [debug] ${msg}`)) },
12
+ step: (current, total, msg) => write(chalk.blue(` [${current}/${total}] ${msg}`)),
13
+ banner: (msg) => write(chalk.bold.green(`\n${msg}\n`)),
14
+ newline: () => write('')
15
+ }
16
+ }
@@ -1,101 +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
- * @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
+ /**
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
+ }