bmad-method 6.3.1-next.2 → 6.3.1-next.21
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/package.json +3 -3
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +51 -36
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/customize.toml +90 -0
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +50 -33
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/customize.toml +81 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +57 -1
- package/src/bmm-skills/1-analysis/bmad-document-project/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-instructions.md +1 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +48 -9
- package/src/bmm-skills/1-analysis/bmad-prfaq/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/bmad-prfaq/references/verdict.md +4 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +44 -9
- package/src/bmm-skills/1-analysis/bmad-product-brief/customize.toml +47 -0
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/contextual-discovery.md +8 -7
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/draft-and-review.md +6 -5
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/finalize.md +4 -1
- package/src/bmm-skills/1-analysis/bmad-product-brief/prompts/guided-elicitation.md +3 -2
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-market-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +6 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +91 -1
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/customize.toml +41 -0
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +50 -35
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/customize.toml +85 -0
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +50 -31
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/customize.toml +60 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-08-scoping.md +70 -23
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-11-polish.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/steps-c/step-12-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +70 -1
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/customize.toml +41 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +6 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md +97 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +2 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md +99 -1
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/customize.toml +42 -0
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/steps-v/step-v-13-report-complete.md +1 -0
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +50 -30
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/customize.toml +65 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +86 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +69 -1
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/steps/step-08-complete.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +88 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/steps/step-04-final-validation.md +6 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +76 -1
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/customize.toml +41 -0
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/steps/step-03-complete.md +6 -0
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +48 -43
- package/src/bmm-skills/4-implementation/bmad-agent-dev/customize.toml +90 -0
- package/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +296 -1
- package/src/bmm-skills/4-implementation/bmad-correct-course/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +412 -1
- package/src/bmm-skills/4-implementation/bmad-create-story/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +171 -1
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/customize.toml +41 -0
- package/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +1507 -1
- package/src/bmm-skills/4-implementation/bmad-retrospective/customize.toml +41 -0
- package/src/bmm-skills/module.yaml +49 -0
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +7 -1
- package/src/core-skills/bmad-customize/SKILL.md +111 -0
- package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
- package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
- package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
- package/src/core-skills/bmad-party-mode/SKILL.md +13 -10
- package/src/core-skills/module-help.csv +1 -0
- package/src/core-skills/module.yaml +3 -0
- package/src/scripts/resolve_config.py +176 -0
- package/src/scripts/resolve_customization.py +230 -0
- package/tools/installer/cli-utils.js +0 -137
- package/tools/installer/commands/install.js +13 -0
- package/tools/installer/commands/status.js +1 -1
- package/tools/installer/commands/uninstall.js +1 -1
- package/tools/installer/core/config.js +4 -1
- package/tools/installer/core/existing-install.js +1 -1
- package/tools/installer/core/install-paths.js +12 -6
- package/tools/installer/core/installer.js +182 -95
- package/tools/installer/core/manifest-generator.js +347 -190
- package/tools/installer/core/manifest.js +49 -642
- package/tools/installer/file-ops.js +1 -1
- package/tools/installer/fs-native.js +116 -0
- package/tools/installer/ide/_config-driven.js +1 -1
- package/tools/installer/ide/platform-codes.js +1 -1
- package/tools/installer/ide/shared/path-utils.js +0 -145
- package/tools/installer/ide/shared/skill-manifest.js +1 -1
- package/tools/installer/message-loader.js +1 -1
- package/tools/installer/modules/channel-plan.js +203 -0
- package/tools/installer/modules/channel-resolver.js +241 -0
- package/tools/installer/modules/community-manager.js +131 -24
- package/tools/installer/modules/custom-module-manager.js +161 -47
- package/tools/installer/modules/external-manager.js +236 -73
- package/tools/installer/modules/official-modules.js +61 -63
- package/tools/installer/modules/plugin-resolver.js +1 -1
- package/tools/installer/modules/registry-client.js +133 -12
- package/tools/installer/modules/registry-fallback.yaml +8 -0
- package/tools/installer/modules/version-resolver.js +336 -0
- package/tools/installer/project-root.js +55 -1
- package/tools/installer/prompts.js +0 -106
- package/tools/installer/ui.js +457 -51
- package/tools/migrate-custom-module-paths.js +1 -1
- package/src/bmm-skills/1-analysis/bmad-agent-analyst/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-agent-tech-writer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/1-analysis/bmad-document-project/workflow.md +0 -25
- package/src/bmm-skills/1-analysis/research/bmad-domain-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-market-research/workflow.md +0 -51
- package/src/bmm-skills/1-analysis/research/bmad-technical-research/workflow.md +0 -52
- package/src/bmm-skills/2-plan-workflows/bmad-agent-pm/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/2-plan-workflows/bmad-create-prd/workflow.md +0 -61
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/workflow.md +0 -35
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/workflow.md +0 -62
- package/src/bmm-skills/2-plan-workflows/bmad-validate-prd/workflow.md +0 -61
- package/src/bmm-skills/3-solutioning/bmad-agent-architect/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +0 -47
- package/src/bmm-skills/3-solutioning/bmad-create-architecture/workflow.md +0 -32
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +0 -51
- package/src/bmm-skills/3-solutioning/bmad-generate-project-context/workflow.md +0 -39
- package/src/bmm-skills/4-implementation/bmad-agent-dev/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +0 -267
- package/src/bmm-skills/4-implementation/bmad-create-story/workflow.md +0 -380
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/workflow.md +0 -136
- package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +0 -1479
- package/tools/installer/ide/shared/agent-command-generator.js +0 -180
- package/tools/installer/ide/shared/bmad-artifacts.js +0 -208
- package/tools/installer/ide/shared/module-injections.js +0 -136
- package/tools/installer/ide/templates/agent-command-template.md +0 -14
- package/tools/installer/ide/templates/combined/antigravity.md +0 -8
- package/tools/installer/ide/templates/combined/default-agent.md +0 -15
- package/tools/installer/ide/templates/combined/default-task.md +0 -10
- package/tools/installer/ide/templates/combined/default-tool.md +0 -10
- package/tools/installer/ide/templates/combined/default-workflow.md +0 -6
- package/tools/installer/ide/templates/combined/gemini-agent.toml +0 -14
- package/tools/installer/ide/templates/combined/gemini-task.toml +0 -11
- package/tools/installer/ide/templates/combined/gemini-tool.toml +0 -11
- package/tools/installer/ide/templates/combined/gemini-workflow-yaml.toml +0 -16
- package/tools/installer/ide/templates/combined/gemini-workflow.toml +0 -14
- package/tools/installer/ide/templates/combined/kiro-agent.md +0 -16
- package/tools/installer/ide/templates/combined/kiro-task.md +0 -9
- package/tools/installer/ide/templates/combined/kiro-tool.md +0 -9
- package/tools/installer/ide/templates/combined/kiro-workflow.md +0 -7
- package/tools/installer/ide/templates/combined/opencode-agent.md +0 -15
- package/tools/installer/ide/templates/combined/opencode-task.md +0 -13
- package/tools/installer/ide/templates/combined/opencode-tool.md +0 -13
- package/tools/installer/ide/templates/combined/opencode-workflow-yaml.md +0 -16
- package/tools/installer/ide/templates/combined/opencode-workflow.md +0 -16
- package/tools/installer/ide/templates/combined/rovodev.md +0 -9
- package/tools/installer/ide/templates/combined/trae.md +0 -9
- package/tools/installer/ide/templates/combined/windsurf-workflow.md +0 -10
- package/tools/installer/ide/templates/split/.gitkeep +0 -0
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
const fs = require('fs-
|
|
1
|
+
const fs = require('../fs-native');
|
|
2
2
|
const os = require('node:os');
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { execSync } = require('node:child_process');
|
|
5
5
|
const prompts = require('../prompts');
|
|
6
6
|
|
|
7
|
+
function quoteCustomRef(ref) {
|
|
8
|
+
if (typeof ref !== 'string' || !/^[\w.\-+/]+$/.test(ref)) {
|
|
9
|
+
throw new Error(`Unsafe ref name: ${JSON.stringify(ref)}`);
|
|
10
|
+
}
|
|
11
|
+
return `"${ref}"`;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
/**
|
|
8
15
|
* Manages custom modules installed from user-provided sources.
|
|
9
16
|
* Supports any Git host (GitHub, GitLab, Bitbucket, self-hosted) and local file paths.
|
|
@@ -38,8 +45,8 @@ class CustomModuleManager {
|
|
|
38
45
|
};
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
48
|
+
const trimmedRaw = input.trim();
|
|
49
|
+
if (!trimmedRaw) {
|
|
43
50
|
return {
|
|
44
51
|
type: null,
|
|
45
52
|
cloneUrl: null,
|
|
@@ -52,8 +59,53 @@ class CustomModuleManager {
|
|
|
52
59
|
};
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
// Extract optional @<tag-or-branch> suffix from the end of the input.
|
|
63
|
+
// Semver-valid characters: letters, digits, dot, hyphen, underscore, plus, slash.
|
|
64
|
+
// Raw commit SHAs are NOT supported here — `git clone --branch` can't take
|
|
65
|
+
// them; use --pin at the module level or check out the SHA manually.
|
|
66
|
+
// Only strip when the tail looks like a ref, so we don't disturb
|
|
67
|
+
// URLs without a version spec or the SSH protocol's `git@host:...` prefix.
|
|
68
|
+
let trimmed = trimmedRaw;
|
|
69
|
+
let versionSuffix = null;
|
|
70
|
+
const lastAt = trimmedRaw.lastIndexOf('@');
|
|
71
|
+
// Skip if @ is part of git@github.com:... (first char cannot be stripped as version)
|
|
72
|
+
// and skip if @ appears before the path rather than after a ref-shaped tail.
|
|
73
|
+
if (lastAt > 0) {
|
|
74
|
+
const candidate = trimmedRaw.slice(lastAt + 1);
|
|
75
|
+
const before = trimmedRaw.slice(0, lastAt);
|
|
76
|
+
// candidate must be ref-shaped and must not itself look like a URL / SSH host
|
|
77
|
+
if (/^[\w.\-+/]+$/.test(candidate) && !candidate.includes(':')) {
|
|
78
|
+
// Avoid consuming the @ in `git@host:owner/repo` — `before` wouldn't end with a path separator
|
|
79
|
+
// in that case. Require that the @ comes after the host/path, not inside the auth segment.
|
|
80
|
+
// Rule: the @ is a version suffix only if `before` looks like a complete URL or local path.
|
|
81
|
+
const beforeLooksLikeRepo =
|
|
82
|
+
before.startsWith('/') ||
|
|
83
|
+
before.startsWith('./') ||
|
|
84
|
+
before.startsWith('../') ||
|
|
85
|
+
before.startsWith('~') ||
|
|
86
|
+
/^https?:\/\//i.test(before) ||
|
|
87
|
+
/^git@[^:]+:.+/.test(before);
|
|
88
|
+
if (beforeLooksLikeRepo) {
|
|
89
|
+
versionSuffix = candidate;
|
|
90
|
+
trimmed = before;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
55
95
|
// Local path detection: starts with /, ./, ../, or ~
|
|
56
96
|
if (trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../') || trimmed.startsWith('~')) {
|
|
97
|
+
if (versionSuffix) {
|
|
98
|
+
return {
|
|
99
|
+
type: 'local',
|
|
100
|
+
cloneUrl: null,
|
|
101
|
+
subdir: null,
|
|
102
|
+
localPath: null,
|
|
103
|
+
cacheKey: null,
|
|
104
|
+
displayName: null,
|
|
105
|
+
isValid: false,
|
|
106
|
+
error: 'Local paths do not support @version suffixes',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
57
109
|
return this._parseLocalPath(trimmed);
|
|
58
110
|
}
|
|
59
111
|
|
|
@@ -66,6 +118,8 @@ class CustomModuleManager {
|
|
|
66
118
|
cloneUrl: trimmed,
|
|
67
119
|
subdir: null,
|
|
68
120
|
localPath: null,
|
|
121
|
+
version: versionSuffix || null,
|
|
122
|
+
rawInput: trimmedRaw,
|
|
69
123
|
cacheKey: `${host}/${owner}/${repo}`,
|
|
70
124
|
displayName: `${owner}/${repo}`,
|
|
71
125
|
isValid: true,
|
|
@@ -79,29 +133,47 @@ class CustomModuleManager {
|
|
|
79
133
|
const [, host, owner, repo, remainder] = httpsMatch;
|
|
80
134
|
const cloneUrl = `https://${host}/${owner}/${repo}`;
|
|
81
135
|
let subdir = null;
|
|
136
|
+
let urlRef = null; // branch/tag extracted from /tree/<ref>/subdir
|
|
82
137
|
|
|
83
138
|
if (remainder) {
|
|
84
139
|
// Extract subdir from deep path patterns used by various Git hosts
|
|
85
140
|
const deepPathPatterns = [
|
|
86
|
-
/^\/(?:-\/)?tree\/[^/]
|
|
87
|
-
/^\/(?:-\/)?blob\/[^/]
|
|
88
|
-
/^\/src\/[^/]
|
|
141
|
+
{ regex: /^\/(?:-\/)?tree\/([^/]+)\/(.+)$/, refIdx: 1, pathIdx: 2 }, // GitHub, GitLab
|
|
142
|
+
{ regex: /^\/(?:-\/)?blob\/([^/]+)\/(.+)$/, refIdx: 1, pathIdx: 2 },
|
|
143
|
+
{ regex: /^\/src\/([^/]+)\/(.+)$/, refIdx: 1, pathIdx: 2 }, // Gitea/Forgejo
|
|
89
144
|
];
|
|
145
|
+
// Also match `/tree/<ref>` with no subdir
|
|
146
|
+
const refOnlyPatterns = [/^\/(?:-\/)?tree\/([^/]+?)\/?$/, /^\/(?:-\/)?blob\/([^/]+?)\/?$/, /^\/src\/([^/]+?)\/?$/];
|
|
90
147
|
|
|
91
|
-
for (const
|
|
92
|
-
const match = remainder.match(
|
|
148
|
+
for (const p of deepPathPatterns) {
|
|
149
|
+
const match = remainder.match(p.regex);
|
|
93
150
|
if (match) {
|
|
94
|
-
|
|
151
|
+
urlRef = match[p.refIdx];
|
|
152
|
+
subdir = match[p.pathIdx].replace(/\/$/, '');
|
|
95
153
|
break;
|
|
96
154
|
}
|
|
97
155
|
}
|
|
156
|
+
if (!subdir) {
|
|
157
|
+
for (const r of refOnlyPatterns) {
|
|
158
|
+
const match = remainder.match(r);
|
|
159
|
+
if (match) {
|
|
160
|
+
urlRef = match[1];
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
98
165
|
}
|
|
99
166
|
|
|
167
|
+
// Precedence: explicit @version suffix > URL /tree/<ref> path segment.
|
|
168
|
+
const version = versionSuffix || urlRef || null;
|
|
169
|
+
|
|
100
170
|
return {
|
|
101
171
|
type: 'url',
|
|
102
172
|
cloneUrl,
|
|
103
173
|
subdir,
|
|
104
174
|
localPath: null,
|
|
175
|
+
version,
|
|
176
|
+
rawInput: trimmedRaw,
|
|
105
177
|
cacheKey: `${host}/${owner}/${repo}`,
|
|
106
178
|
displayName: `${owner}/${repo}`,
|
|
107
179
|
isValid: true,
|
|
@@ -155,33 +227,6 @@ class CustomModuleManager {
|
|
|
155
227
|
};
|
|
156
228
|
}
|
|
157
229
|
|
|
158
|
-
/**
|
|
159
|
-
* @deprecated Use parseSource() instead. Kept for backward compatibility.
|
|
160
|
-
* Parse and validate a GitHub repository URL.
|
|
161
|
-
* @param {string} url - GitHub URL to validate
|
|
162
|
-
* @returns {Object} { owner, repo, isValid, error }
|
|
163
|
-
*/
|
|
164
|
-
validateGitHubUrl(url) {
|
|
165
|
-
if (!url || typeof url !== 'string') {
|
|
166
|
-
return { owner: null, repo: null, isValid: false, error: 'URL is required' };
|
|
167
|
-
}
|
|
168
|
-
const trimmed = url.trim();
|
|
169
|
-
|
|
170
|
-
// HTTPS format: https://github.com/owner/repo[.git] (strict, no trailing path)
|
|
171
|
-
const httpsMatch = trimmed.match(/^https?:\/\/github\.com\/([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
172
|
-
if (httpsMatch) {
|
|
173
|
-
return { owner: httpsMatch[1], repo: httpsMatch[2], isValid: true, error: null };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// SSH format: git@github.com:owner/repo[.git]
|
|
177
|
-
const sshMatch = trimmed.match(/^git@github\.com:([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
178
|
-
if (sshMatch) {
|
|
179
|
-
return { owner: sshMatch[1], repo: sshMatch[2], isValid: true, error: null };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return { owner: null, repo: null, isValid: false, error: 'Not a valid GitHub URL (expected https://github.com/owner/repo)' };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
230
|
// ─── Marketplace JSON ─────────────────────────────────────────────────────
|
|
186
231
|
|
|
187
232
|
/**
|
|
@@ -282,6 +327,10 @@ class CustomModuleManager {
|
|
|
282
327
|
const silent = options.silent || false;
|
|
283
328
|
const displayName = parsed.displayName;
|
|
284
329
|
|
|
330
|
+
// Pin override: --pin CODE=TAG resolved at module-selection time overrides
|
|
331
|
+
// any @version suffix present in the URL.
|
|
332
|
+
const effectiveVersion = options.pinOverride || parsed.version || null;
|
|
333
|
+
|
|
285
334
|
await fs.ensureDir(path.dirname(repoCacheDir));
|
|
286
335
|
|
|
287
336
|
const createSpinner = async () => {
|
|
@@ -291,8 +340,23 @@ class CustomModuleManager {
|
|
|
291
340
|
return await prompts.spinner();
|
|
292
341
|
};
|
|
293
342
|
|
|
343
|
+
// If an existing cache exists but was cloned at a different version, re-clone.
|
|
344
|
+
// Tracked via .bmad-source.json's recorded version.
|
|
294
345
|
if (await fs.pathExists(repoCacheDir)) {
|
|
295
|
-
|
|
346
|
+
let cachedVersion = null;
|
|
347
|
+
try {
|
|
348
|
+
const existing = await fs.readJson(path.join(repoCacheDir, '.bmad-source.json'));
|
|
349
|
+
cachedVersion = existing?.version || null;
|
|
350
|
+
} catch {
|
|
351
|
+
// no metadata; treat as mismatched to be safe if a version was requested
|
|
352
|
+
}
|
|
353
|
+
if ((effectiveVersion || null) !== (cachedVersion || null)) {
|
|
354
|
+
await fs.remove(repoCacheDir);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (await fs.pathExists(repoCacheDir)) {
|
|
359
|
+
// Update existing clone (same version as before)
|
|
296
360
|
const fetchSpinner = await createSpinner();
|
|
297
361
|
fetchSpinner.start(`Updating ${displayName}...`);
|
|
298
362
|
try {
|
|
@@ -301,10 +365,25 @@ class CustomModuleManager {
|
|
|
301
365
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
302
366
|
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
303
367
|
});
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
368
|
+
if (effectiveVersion) {
|
|
369
|
+
// Fetch the ref as either a tag or a branch — `origin <ref>` works
|
|
370
|
+
// for both, whereas `origin tag <ref>` fails for branch refs parsed
|
|
371
|
+
// out of /tree/<branch>/... URLs.
|
|
372
|
+
execSync(`git fetch --depth 1 origin ${quoteCustomRef(effectiveVersion)} --no-tags`, {
|
|
373
|
+
cwd: repoCacheDir,
|
|
374
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
375
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
376
|
+
});
|
|
377
|
+
execSync(`git checkout --quiet FETCH_HEAD`, {
|
|
378
|
+
cwd: repoCacheDir,
|
|
379
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
execSync('git reset --hard origin/HEAD', {
|
|
383
|
+
cwd: repoCacheDir,
|
|
384
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
385
|
+
});
|
|
386
|
+
}
|
|
308
387
|
fetchSpinner.stop(`Updated ${displayName}`);
|
|
309
388
|
} catch {
|
|
310
389
|
fetchSpinner.error(`Update failed, re-downloading ${displayName}`);
|
|
@@ -314,25 +393,44 @@ class CustomModuleManager {
|
|
|
314
393
|
|
|
315
394
|
if (!(await fs.pathExists(repoCacheDir))) {
|
|
316
395
|
const fetchSpinner = await createSpinner();
|
|
317
|
-
fetchSpinner.start(`Cloning ${displayName}...`);
|
|
396
|
+
fetchSpinner.start(`Cloning ${displayName}${effectiveVersion ? ` @ ${effectiveVersion}` : ''}...`);
|
|
318
397
|
try {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
398
|
+
if (effectiveVersion) {
|
|
399
|
+
execSync(`git clone --depth 1 --branch ${quoteCustomRef(effectiveVersion)} "${parsed.cloneUrl}" "${repoCacheDir}"`, {
|
|
400
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
401
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
execSync(`git clone --depth 1 "${parsed.cloneUrl}" "${repoCacheDir}"`, {
|
|
405
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
406
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
407
|
+
});
|
|
408
|
+
}
|
|
323
409
|
fetchSpinner.stop(`Cloned ${displayName}`);
|
|
324
410
|
} catch (error_) {
|
|
325
411
|
fetchSpinner.error(`Failed to clone ${displayName}`);
|
|
326
|
-
|
|
412
|
+
const refSuffix = effectiveVersion ? `@${effectiveVersion}` : '';
|
|
413
|
+
throw new Error(`Failed to clone ${parsed.cloneUrl}${refSuffix}: ${error_.message}`);
|
|
327
414
|
}
|
|
328
415
|
}
|
|
329
416
|
|
|
417
|
+
// Record the resolved SHA for the manifest writer.
|
|
418
|
+
let resolvedSha = null;
|
|
419
|
+
try {
|
|
420
|
+
resolvedSha = execSync('git rev-parse HEAD', { cwd: repoCacheDir, stdio: 'pipe' }).toString().trim();
|
|
421
|
+
} catch {
|
|
422
|
+
// swallow — a non-git repo (local path) wouldn't reach here anyway
|
|
423
|
+
}
|
|
424
|
+
|
|
330
425
|
// Write source metadata for later URL reconstruction
|
|
331
426
|
const metadataPath = path.join(repoCacheDir, '.bmad-source.json');
|
|
332
427
|
await fs.writeJson(metadataPath, {
|
|
333
428
|
cloneUrl: parsed.cloneUrl,
|
|
334
429
|
cacheKey: parsed.cacheKey,
|
|
335
430
|
displayName: parsed.displayName,
|
|
431
|
+
version: effectiveVersion || null,
|
|
432
|
+
rawInput: parsed.rawInput || sourceInput,
|
|
433
|
+
sha: resolvedSha,
|
|
336
434
|
clonedAt: new Date().toISOString(),
|
|
337
435
|
});
|
|
338
436
|
|
|
@@ -373,10 +471,26 @@ class CustomModuleManager {
|
|
|
373
471
|
const resolver = new PluginResolver();
|
|
374
472
|
const resolved = await resolver.resolve(repoPath, plugin);
|
|
375
473
|
|
|
474
|
+
// Read clone metadata (written by cloneRepo) so we can pick up the
|
|
475
|
+
// resolved git ref + SHA for manifest recording.
|
|
476
|
+
let cloneMetadata = null;
|
|
477
|
+
if (sourceUrl) {
|
|
478
|
+
try {
|
|
479
|
+
cloneMetadata = await fs.readJson(path.join(repoPath, '.bmad-source.json'));
|
|
480
|
+
} catch {
|
|
481
|
+
// no metadata — local-source or legacy cache
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
376
485
|
// Stamp source info onto each resolved module for manifest tracking
|
|
377
486
|
for (const mod of resolved) {
|
|
378
487
|
if (sourceUrl) mod.repoUrl = sourceUrl;
|
|
379
488
|
if (localPath) mod.localPath = localPath;
|
|
489
|
+
if (cloneMetadata) {
|
|
490
|
+
mod.cloneRef = cloneMetadata.version || null;
|
|
491
|
+
mod.cloneSha = cloneMetadata.sha || null;
|
|
492
|
+
mod.rawInput = cloneMetadata.rawInput || null;
|
|
493
|
+
}
|
|
380
494
|
CustomModuleManager._resolutionCache.set(mod.code, mod);
|
|
381
495
|
}
|
|
382
496
|
|