@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.
- package/LICENSE +21 -21
- package/bin/cckit.js +3 -3
- package/package.json +53 -53
- package/registry.json +145 -145
- package/src/cli.js +79 -79
- package/src/commands/init.js +174 -174
- package/src/commands/status.js +125 -125
- package/src/commands/update.js +192 -192
- package/src/core/config.js +82 -75
- package/src/core/orchestrator.js +79 -79
- package/src/core/registry.js +60 -60
- package/src/steps/add-plugin.js +148 -116
- 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 -106
- package/src/utils/fs.js +33 -33
- package/src/utils/logger.js +16 -16
- package/src/utils/manifest.js +101 -101
- 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
|
@@ -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
|
+
}
|
package/src/utils/logger.js
CHANGED
|
@@ -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
|
+
}
|
package/src/utils/manifest.js
CHANGED
|
@@ -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
|
+
}
|
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
|
+
}
|