bmad-method 6.11.1-next.13 → 6.11.1-next.15
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 +1 -1
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-build/step-01-clarify-and-route.md +12 -7
- package/src/bmm-skills/ship/bmad-code-review/steps/step-01-gather-context.md +4 -1
- package/src/bmm-skills/ship/bmad-correct-course/SKILL.md +11 -3
- package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +21 -22
- package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
- package/tools/installer/core/installer.js +33 -1
- package/tools/installer/core/shim-policy.js +75 -7
- package/tools/installer/prompts.js +15 -2
- package/tools/installer/ui.js +28 -8
package/package.json
CHANGED
package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -30,10 +30,13 @@ Before listing artifacts or prompting the user, check whether you already know t
|
|
|
30
30
|
Use the same routing as above.
|
|
31
31
|
|
|
32
32
|
3. Otherwise — scan artifacts and ask
|
|
33
|
-
- Active specs (`draft`, `ready-for-dev`, `in-progress`, `in-review`) in `{{.implementation_artifacts}}`? → List them and HALT.
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
|
|
33
|
+
- Active specs (`draft`, `ready-for-dev`, `in-progress`, `in-review`) in `{{.implementation_artifacts}}`? → List them and HALT. Give the user a choice:
|
|
34
|
+
- Resume one of the listed specs
|
|
35
|
+
- **New** — start new work
|
|
36
|
+
If `draft` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `[[bmad-snapshot:step-02-plan.md]]` (resume planning from the draft)
|
|
37
|
+
If `ready-for-dev` or `in-progress` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `[[bmad-snapshot:step-03-implement.md]]`
|
|
38
|
+
If `in-review` selected: Set `spec_file`. Run **Story-key resolution** (below). **EARLY EXIT** → `[[bmad-snapshot:step-04-review.md]]`
|
|
39
|
+
If the user chooses **New**: proceed to INSTRUCTIONS
|
|
37
40
|
- Unformatted spec or intent file lacking `status` frontmatter? → Suggest treating its contents as the starting intent. Do NOT attempt to infer a state and resume it.
|
|
38
41
|
|
|
39
42
|
Never ask extra questions if you already understand what the user intends.
|
|
@@ -82,14 +85,16 @@ If the spec is an epic story and `{{.implementation_artifacts}}/sprint-status.ya
|
|
|
82
85
|
4. Multi-goal check (see SCOPE STANDARD). If the intent fails the single-goal criteria:
|
|
83
86
|
- Present detected distinct goals as a bullet list.
|
|
84
87
|
- Explain briefly (2–4 sentences): why each goal qualifies as independently shippable, any coupling risks if split, and which goal you recommend tackling first.
|
|
85
|
-
- HALT and
|
|
86
|
-
|
|
88
|
+
- HALT and give the user a choice:
|
|
89
|
+
- **Split** — pick first goal, defer the rest.
|
|
90
|
+
- **Keep all goals** — accept the risks.
|
|
91
|
+
- If the user chooses **Split**: For each deferred goal, append one new entry to `{{.implementation_artifacts}}/deferred-work.md` using this format. Do not modify existing entries or look for duplicates. Narrow scope to the first-mentioned goal. Continue routing.
|
|
87
92
|
```markdown
|
|
88
93
|
- source_spec: none
|
|
89
94
|
summary: <one sentence naming the deferred goal>
|
|
90
95
|
evidence: <why this was split from the current intent>
|
|
91
96
|
```
|
|
92
|
-
-
|
|
97
|
+
- If the user chooses **Keep all goals**: Proceed as-is.
|
|
93
98
|
5. Route — choose exactly one:
|
|
94
99
|
|
|
95
100
|
If the explicit spec-folder-plus-story-id pair had no matching story file, keep the colocated `spec_file` selected above. Otherwise, derive a valid kebab-case slug from the clarified intent. If the intent references a tracking identifier (story number, issue number, ticket ID), lead the slug with it (e.g. `3-2-digest-delivery`, `gh-47-fix-auth`). If `{{.implementation_artifacts}}/spec-{slug}.md` already exists: if its status is `draft`, treat it as the same work and resume it (set `spec_file` to that path, **EARLY EXIT** → `[[bmad-snapshot:step-02-plan.md]]`); otherwise append `-2`, `-3`, etc. Set `spec_file` = `{{.implementation_artifacts}}/spec-{slug}.md`.
|
|
@@ -35,7 +35,10 @@ story_key: '' # set at runtime when discovered from sprint status
|
|
|
35
35
|
|
|
36
36
|
**Tier 3 — Sprint tracking.**
|
|
37
37
|
Look for a sprint status file (`*sprint-status*`) in `{implementation_artifacts}` or `{planning_artifacts}`. If found, scan for stories with status `review`:
|
|
38
|
-
- **Exactly one `review` story:** Set `{story_key}` to the story's key (e.g., `1-2-user-auth`).
|
|
38
|
+
- **Exactly one `review` story:** Set `{story_key}` to the story's key (e.g., `1-2-user-auth`). HALT and give the user a choice:
|
|
39
|
+
- **Review this story** — review the detected story `<story-id>` (status `review`).
|
|
40
|
+
- **Choose another target** — pick a different review target.
|
|
41
|
+
If the user chooses **Review this story**, use the story context to determine the diff source (branch name derived from story slug, or uncommitted changes). If they choose **Choose another target**, clear `{story_key}` and fall through.
|
|
39
42
|
- **Multiple `review` stories:** Present them as numbered options alongside a manual choice option. Wait for user selection. If a story is selected, set `{story_key}` and use its context to determine the diff source. If manual choice is selected, clear `{story_key}` and fall through.
|
|
40
43
|
- **None:** Fall through.
|
|
41
44
|
|
|
@@ -182,8 +182,12 @@ Activation is complete. If `activation_steps_prepend` or `activation_steps_appen
|
|
|
182
182
|
|
|
183
183
|
<check if="mode is Incremental">
|
|
184
184
|
<action>Present each edit proposal individually</action>
|
|
185
|
-
<
|
|
186
|
-
|
|
185
|
+
<action>HALT and give the user a choice:
|
|
186
|
+
- **Approve** — accept this proposal
|
|
187
|
+
- **Edit** — refine this proposal
|
|
188
|
+
- **Skip** — drop this proposal
|
|
189
|
+
</action>
|
|
190
|
+
<action>If the user chooses **Approve**, keep the proposal. If they choose **Edit**, refine it with them. If they choose **Skip**, drop it. Continue to the next proposal.</action>
|
|
187
191
|
</check>
|
|
188
192
|
|
|
189
193
|
<action if="mode is Batch">Collect all edit proposals and present together at end of step</action>
|
|
@@ -232,7 +236,11 @@ Activation is complete. If `activation_steps_prepend` or `activation_steps_appen
|
|
|
232
236
|
|
|
233
237
|
<action>Present complete Sprint Change Proposal to user</action>
|
|
234
238
|
<action>Write Sprint Change Proposal document to {default_output_file}</action>
|
|
235
|
-
<
|
|
239
|
+
<action>HALT and give the user a choice:
|
|
240
|
+
- **Continue** — proceed to approval
|
|
241
|
+
- **Edit** — revise the proposal first
|
|
242
|
+
</action>
|
|
243
|
+
<action>If the user chooses **Edit**, revise the proposal with them and write the updated document before continuing.</action>
|
|
236
244
|
</step>
|
|
237
245
|
|
|
238
246
|
<step n="5" goal="Finalize and Route for Implementation">
|
package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -19,7 +19,7 @@ You are BMad's shared refinement checkpoint: other skills invoke you at natural
|
|
|
19
19
|
|
|
20
20
|
## Serving the Catalog
|
|
21
21
|
|
|
22
|
-
`scripts/pick_methods.py` serves the method catalog (num, category, method_name, description, output_pattern) so it never enters context whole — the one exception is
|
|
22
|
+
`scripts/pick_methods.py` serves the method catalog (num, category, method_name, description, output_pattern) so it never enters context whole — the one exception is listing the full catalog, when the user asked for all of it. Invoke as:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
uv run {skill-root}/scripts/pick_methods.py --file {workflow.methods_file} <command>
|
|
@@ -28,7 +28,7 @@ uv run {skill-root}/scripts/pick_methods.py --file {workflow.methods_file} <comm
|
|
|
28
28
|
If `{workflow.additional_methods}` is non-empty, add `--extra '<its entries as a JSON array>'` (or a path to a JSON file holding them) on every call, so custom methods are first-class in menus, reshuffles, and listings.
|
|
29
29
|
|
|
30
30
|
- `categories` — category names + counts, the cheap map.
|
|
31
|
-
- `list --category <cat> [--category <cat>]` — the index for chosen categories; `--all` dumps the whole catalog, only
|
|
31
|
+
- `list --category <cat> [--category <cat>]` — the index for chosen categories; `--all` dumps the whole catalog, only when listing all.
|
|
32
32
|
- `show <name-or-num> [...]` — full rows by name or num.
|
|
33
33
|
- `random -n 5 --spread [--exclude <name>]...` — a category-diverse random draw.
|
|
34
34
|
|
|
@@ -36,30 +36,29 @@ If `{workflow.additional_methods}` is non-empty, add `--extra '<its entries as a
|
|
|
36
36
|
|
|
37
37
|
## The Menu
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
3. [Method Name]
|
|
46
|
-
4. [Method Name]
|
|
47
|
-
5. [Method Name]
|
|
48
|
-
r. Reshuffle the list with 5 new options
|
|
49
|
-
a. List all methods with descriptions
|
|
50
|
-
x. Proceed / No Further Actions
|
|
51
|
-
```
|
|
39
|
+
HALT and give the user a choice:
|
|
40
|
+
|
|
41
|
+
- The five offered methods, listed by name. The user may pick one or several.
|
|
42
|
+
- **Reshuffle** — replace the list with five new options.
|
|
43
|
+
- **List all** — show the full catalog with descriptions.
|
|
44
|
+
- **Proceed** — no further elicitation.
|
|
52
45
|
|
|
53
|
-
This menu is the interface other skills and their users rely on — keep its options and behavior stable. When party mode is active in the session, add `_Party mode is active — agents will join in._` under the heading.
|
|
46
|
+
This menu is the interface other skills and their users rely on — keep its options and behavior stable. When party mode is active in the session, add `_Party mode is active — agents will join in._` under the heading.
|
|
54
47
|
|
|
55
|
-
-
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
59
|
-
-
|
|
48
|
+
- If the user picks methods: run them (several: in sequence), then offer the menu again.
|
|
49
|
+
- If the user chooses **Reshuffle**: reshuffle as above and offer the menu again.
|
|
50
|
+
- If the user chooses **List all**: show the full catalog (`list --all`) as a compact table; a pick by name or number runs like a method choice.
|
|
51
|
+
- If the user chooses **Proceed**: done. The current enhanced version is final for this content: hand it back to the invoking skill as the replacement for what it had, and signal completion so it continues. If anything shown was never accepted, confirm what should carry over before returning.
|
|
52
|
+
- Any other reply is direction: apply it to the target and offer the menu again.
|
|
60
53
|
|
|
61
54
|
## Running a Method
|
|
62
55
|
|
|
63
|
-
Use the method's description as its intent and its output_pattern as a flexible flow guide; scale depth to the target — a paragraph gets a light pass, an architecture decision gets the full treatment. Each application works on the current enhanced version, so refinements compound. Show what the method revealed and the changes it proposes, then
|
|
56
|
+
Use the method's description as its intent and its output_pattern as a flexible flow guide; scale depth to the target — a paragraph gets a light pass, an architecture decision gets the full treatment. Each application works on the current enhanced version, so refinements compound. Show what the method revealed and the changes it proposes, then HALT and give the user a choice:
|
|
57
|
+
|
|
58
|
+
- **Apply** — accept the proposed changes.
|
|
59
|
+
- **Reject** — drop the proposal entirely.
|
|
60
|
+
- Or give different direction.
|
|
61
|
+
|
|
62
|
+
Never change the work unless the user accepts the proposal. If they reject it, drop the proposal entirely. Any other reply is instruction to follow.
|
|
64
63
|
|
|
65
64
|
When a method casts personas (round tables, panels, debates), reuse party members already in the session if party mode is active; otherwise resolve installed agents on demand via `uv run {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root} --key agents` (a four-layer merge of `_bmad/config.toml`, `config.user.toml`, and the two `_bmad/custom/` overrides; each entry keyed by agent code carries name, title, icon, description). If neither yields a fit, invent named viewpoints suited to the content.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -13,7 +13,14 @@ const { InstallPaths } = require('./install-paths');
|
|
|
13
13
|
const { ExternalModuleManager } = require('../modules/external-manager');
|
|
14
14
|
const { resolveModuleVersion } = require('../modules/version-resolver');
|
|
15
15
|
const { MODULE_HELP_CSV_HEADER } = require('../modules/module-help-schema');
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
formatRemovedShimNotice,
|
|
18
|
+
formatRetainedShimNotice,
|
|
19
|
+
inferShimPreference,
|
|
20
|
+
readInstalledShims,
|
|
21
|
+
readInstalledSkillIds,
|
|
22
|
+
selectShimOutcome,
|
|
23
|
+
} = require('./shim-policy');
|
|
17
24
|
|
|
18
25
|
const { ExistingInstall } = require('./existing-install');
|
|
19
26
|
const { warnPreNativeSkillsLegacy } = require('./legacy-warnings');
|
|
@@ -59,6 +66,24 @@ class Installer {
|
|
|
59
66
|
}),
|
|
60
67
|
};
|
|
61
68
|
|
|
69
|
+
const installedShims = existingInstall.installed ? await readInstalledShims(paths.bmadDir) : [];
|
|
70
|
+
const { retained: retainedShims, removed: removedShims } = selectShimOutcome({
|
|
71
|
+
installedShims,
|
|
72
|
+
availableShims,
|
|
73
|
+
install: shimPolicy.install,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Reported here, not at the prompt, so --yes/--shims/scripted runs get it too.
|
|
77
|
+
if (retainedShims.length > 0) {
|
|
78
|
+
await prompts.note(formatRetainedShimNotice(retainedShims), 'Deprecated shim skills retained');
|
|
79
|
+
}
|
|
80
|
+
if (removedShims.length > 0) {
|
|
81
|
+
await prompts.note(formatRemovedShimNotice(removedShims, { canReinstall: shimPolicy.available }), 'Deprecated shim skills removed');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The notices above scroll away on a long install; repeat them in the summary.
|
|
85
|
+
const shimStatus = { retained: retainedShims.length, removed: removedShims.length };
|
|
86
|
+
|
|
62
87
|
try {
|
|
63
88
|
await warnPreNativeSkillsLegacy({
|
|
64
89
|
projectRoot: paths.projectRoot,
|
|
@@ -132,6 +157,7 @@ class Installer {
|
|
|
132
157
|
customFiles: restoreResult.customFiles.length > 0 ? restoreResult.customFiles : undefined,
|
|
133
158
|
modifiedFiles: restoreResult.modifiedFiles.length > 0 ? restoreResult.modifiedFiles : undefined,
|
|
134
159
|
preInstallVersions,
|
|
160
|
+
shimStatus,
|
|
135
161
|
});
|
|
136
162
|
|
|
137
163
|
return {
|
|
@@ -1261,6 +1287,12 @@ class Installer {
|
|
|
1261
1287
|
if (context.modifiedFiles && context.modifiedFiles.length > 0) {
|
|
1262
1288
|
lines.push(` ${color.yellow(`Modified files backed up (.bak): ${context.modifiedFiles.length}`)}`);
|
|
1263
1289
|
}
|
|
1290
|
+
if (context.shimStatus?.retained > 0) {
|
|
1291
|
+
lines.push(` ${color.yellow(`Deprecated shim skills retained: ${context.shimStatus.retained}`)} (re-run to remove them)`);
|
|
1292
|
+
}
|
|
1293
|
+
if (context.shimStatus?.removed > 0) {
|
|
1294
|
+
lines.push(` ${color.green(`Deprecated shim skills removed: ${context.shimStatus.removed}`)}`);
|
|
1295
|
+
}
|
|
1264
1296
|
|
|
1265
1297
|
// Next steps
|
|
1266
1298
|
lines.push(
|
|
@@ -37,6 +37,7 @@ async function discoverShims(modulePath) {
|
|
|
37
37
|
if (isShimSkill(metadata)) {
|
|
38
38
|
shims.push({
|
|
39
39
|
id: metadata.name || path.basename(dir),
|
|
40
|
+
description: typeof metadata.description === 'string' ? metadata.description : '',
|
|
40
41
|
directory: dir,
|
|
41
42
|
relativeDirectory: path.relative(modulePath, dir),
|
|
42
43
|
});
|
|
@@ -54,25 +55,40 @@ async function discoverShims(modulePath) {
|
|
|
54
55
|
return shims;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
async function
|
|
58
|
-
const ids = new Set();
|
|
58
|
+
async function readSkillManifest(bmadDir) {
|
|
59
59
|
const manifestPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
|
|
60
|
-
if (!(await fs.pathExists(manifestPath))) return
|
|
60
|
+
if (!(await fs.pathExists(manifestPath))) return [];
|
|
61
61
|
|
|
62
62
|
try {
|
|
63
63
|
const content = await fs.readFile(manifestPath, 'utf8');
|
|
64
|
-
|
|
65
|
-
for (const record of records) {
|
|
66
|
-
if (record.canonicalId) ids.add(record.canonicalId);
|
|
67
|
-
}
|
|
64
|
+
return csv.parse(content, { columns: true, skip_empty_lines: true });
|
|
68
65
|
} catch {
|
|
69
66
|
// A missing or unreadable legacy manifest means there is no reliable
|
|
70
67
|
// evidence that compatibility shims were installed.
|
|
68
|
+
return [];
|
|
71
69
|
}
|
|
70
|
+
}
|
|
72
71
|
|
|
72
|
+
async function readInstalledSkillIds(bmadDir) {
|
|
73
|
+
const ids = new Set();
|
|
74
|
+
for (const record of await readSkillManifest(bmadDir)) {
|
|
75
|
+
if (record.canonicalId) ids.add(record.canonicalId);
|
|
76
|
+
}
|
|
73
77
|
return ids;
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
// The installed manifest carries no lifecycle column, so the description
|
|
81
|
+
// prefix every shim ships with is the only record of what was a shim. This
|
|
82
|
+
// is the same signal validate-skills.js uses to exempt them.
|
|
83
|
+
async function readInstalledShims(bmadDir) {
|
|
84
|
+
const shims = [];
|
|
85
|
+
for (const record of await readSkillManifest(bmadDir)) {
|
|
86
|
+
if (!record.canonicalId || !/^\s*deprecated\b/i.test(record.description || '')) continue;
|
|
87
|
+
shims.push({ id: record.canonicalId, description: record.description || '', module: record.module || '' });
|
|
88
|
+
}
|
|
89
|
+
return shims;
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
function inferShimPreference({ requested, persisted, availableShims = [], installedSkillIds = new Set(), existing = false }) {
|
|
77
93
|
if (availableShims.length === 0) return false;
|
|
78
94
|
if (typeof requested === 'boolean') return requested;
|
|
@@ -82,10 +98,62 @@ function inferShimPreference({ requested, persisted, availableShims = [], instal
|
|
|
82
98
|
return availableShims.some((shim) => installedSkillIds.has(shim.id));
|
|
83
99
|
}
|
|
84
100
|
|
|
101
|
+
// Removal is driven by what is installed, not by what this release ships: a
|
|
102
|
+
// shim retired from source is still deleted by the update cleanup.
|
|
103
|
+
function selectShimOutcome({ installedShims = [], availableShims = [], install = false }) {
|
|
104
|
+
const availableShimIds = new Set(availableShims.map((shim) => shim.id));
|
|
105
|
+
return {
|
|
106
|
+
retained: install ? availableShims : [],
|
|
107
|
+
removed: installedShims.filter((shim) => !(install && availableShimIds.has(shim.id))),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Shim descriptions all open with "Deprecated — "; the notice heading says it once.
|
|
112
|
+
function describeShim(shim) {
|
|
113
|
+
const cleaned = (shim.description || '').replace(/^\s*deprecated\s*[-–—:]*\s*/i, '').trim();
|
|
114
|
+
const source = shim.module ? ` (${shim.module})` : '';
|
|
115
|
+
return cleaned ? ` ${shim.id}${source}: ${cleaned}` : ` ${shim.id}${source}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function formatRetainedShimNotice(availableShims = []) {
|
|
119
|
+
const lines = availableShims.map((shim) => describeShim(shim)).sort();
|
|
120
|
+
|
|
121
|
+
return [
|
|
122
|
+
`${availableShims.length} deprecated shim skill(s) are still installed. Each one only forwards to the skill that replaced it:`,
|
|
123
|
+
'',
|
|
124
|
+
...lines,
|
|
125
|
+
'',
|
|
126
|
+
'Shims will be removed with v7, and anything still calling the old name stops working then.',
|
|
127
|
+
'Only keep a shim if you customized it and still need to move that customization to the replacement.',
|
|
128
|
+
'Once you have, re-run Quick Update and answer No to this question so the shims come off.',
|
|
129
|
+
].join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function formatRemovedShimNotice(removedShims = [], { canReinstall = true } = {}) {
|
|
133
|
+
const lines = removedShims.map((shim) => describeShim(shim)).sort();
|
|
134
|
+
const recovery = canReinstall
|
|
135
|
+
? 'these shims, move it to the replacement, or re-run with --shims to put the shims back.'
|
|
136
|
+
: 'these shims, move it to the replacement. This release no longer ships them, so --shims cannot bring them back.';
|
|
137
|
+
|
|
138
|
+
return [
|
|
139
|
+
`${removedShims.length} deprecated shim skill(s) are being removed. Invoking these names will no longer work:`,
|
|
140
|
+
'',
|
|
141
|
+
...lines,
|
|
142
|
+
'',
|
|
143
|
+
'Each replacement named above is installed and ready. If you still had a customization on one of',
|
|
144
|
+
recovery,
|
|
145
|
+
].join('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
85
148
|
module.exports = {
|
|
149
|
+
describeShim,
|
|
150
|
+
formatRemovedShimNotice,
|
|
86
151
|
discoverShims,
|
|
152
|
+
formatRetainedShimNotice,
|
|
87
153
|
inferShimPreference,
|
|
88
154
|
isShimSkill,
|
|
89
155
|
parseSkillMetadata,
|
|
156
|
+
readInstalledShims,
|
|
90
157
|
readInstalledSkillIds,
|
|
158
|
+
selectShimOutcome,
|
|
91
159
|
};
|
|
@@ -316,7 +316,9 @@ async function autocompleteMultiselect(options) {
|
|
|
316
316
|
|
|
317
317
|
switch (this.state) {
|
|
318
318
|
case 'submit': {
|
|
319
|
-
|
|
319
|
+
const count = this.selectedValues.length;
|
|
320
|
+
const emptyHint = count === 0 && options.emptyLabel ? ` (${options.emptyLabel})` : '';
|
|
321
|
+
return `${title}${color.gray(clack.S_BAR)} ${color.dim(`${count} item${count === 1 ? '' : 's'} selected${emptyHint}`)}`;
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
case 'cancel': {
|
|
@@ -331,7 +333,18 @@ async function autocompleteMultiselect(options) {
|
|
|
331
333
|
|
|
332
334
|
const errorLine = this.state === 'error' ? [`${bar} ${color.yellow(this.error)}`] : [];
|
|
333
335
|
|
|
334
|
-
const
|
|
336
|
+
const emptyLine =
|
|
337
|
+
this.selectedValues.length === 0 && options.emptyLabel
|
|
338
|
+
? [`${bar} ${color.dim(`Nothing selected: installs ${options.emptyLabel}`)}`]
|
|
339
|
+
: [];
|
|
340
|
+
|
|
341
|
+
const headerLines = [
|
|
342
|
+
...`${title}${bar}`.split('\n'),
|
|
343
|
+
`${bar} ${searchDisplay}${matchCount}`,
|
|
344
|
+
...noMatchesLine,
|
|
345
|
+
...errorLine,
|
|
346
|
+
...emptyLine,
|
|
347
|
+
];
|
|
335
348
|
|
|
336
349
|
const footerLines = [`${bar} ${color.dim(hints.join(' • '))}`, `${barEnd}`];
|
|
337
350
|
|
package/tools/installer/ui.js
CHANGED
|
@@ -111,7 +111,7 @@ async function getModuleVersion(moduleCode, { repoUrl = null, registryDefault =
|
|
|
111
111
|
* UI utilities for the installer
|
|
112
112
|
*/
|
|
113
113
|
class UI {
|
|
114
|
-
async _selectShimPreference({ selectedModules, bmadDir, existing, options, channelOptions }) {
|
|
114
|
+
async _selectShimPreference({ selectedModules, bmadDir, existing, options, channelOptions, quickUpdate = false }) {
|
|
115
115
|
const { OfficialModules } = require('./modules/official-modules');
|
|
116
116
|
const officialModules = new OfficialModules({ channelOptions });
|
|
117
117
|
const availableShims = await officialModules.discoverShims(selectedModules, { channelOptions });
|
|
@@ -132,10 +132,20 @@ class UI {
|
|
|
132
132
|
|
|
133
133
|
if (typeof options.shims === 'boolean' || options.yes) return currentValue;
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
// clack's confirm never resolves without a TTY: a scripted run would exit mid-install.
|
|
136
|
+
if (!process.stdin.isTTY) return currentValue;
|
|
137
|
+
|
|
138
|
+
// Nothing to give up, so nothing to ask on every single update.
|
|
139
|
+
if (quickUpdate && !currentValue) return currentValue;
|
|
140
|
+
|
|
141
|
+
const verb = currentValue ? 'Keep' : 'Install';
|
|
142
|
+
const message =
|
|
143
|
+
`${verb} ${availableShims.length} deprecated compatibility shim skill(s)? Recommended: No. ` +
|
|
144
|
+
`If you say yes, the deprecated skills will exist as a skill that forwards to its replacement skill. ` +
|
|
145
|
+
`Shims will be removed with v7. You should only retain if you customized a shimmed skill and need to ` +
|
|
146
|
+
`still transition it to the replacement.`;
|
|
147
|
+
|
|
148
|
+
return prompts.confirm({ message, default: currentValue });
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
/**
|
|
@@ -346,11 +356,21 @@ class UI {
|
|
|
346
356
|
// Quick update never shows the module picker, so this is the only
|
|
347
357
|
// place an existing install of a deprecated module hears about it.
|
|
348
358
|
await this._warnDeprecatedModules(existingInstall.moduleIds || []);
|
|
359
|
+
|
|
360
|
+
const installShims = await this._selectShimPreference({
|
|
361
|
+
selectedModules: existingInstall.moduleIds || [],
|
|
362
|
+
bmadDir,
|
|
363
|
+
existing: true,
|
|
364
|
+
options,
|
|
365
|
+
channelOptions,
|
|
366
|
+
quickUpdate: true,
|
|
367
|
+
});
|
|
368
|
+
|
|
349
369
|
return {
|
|
350
370
|
actionType: 'quick-update',
|
|
351
371
|
directory: confirmedDirectory,
|
|
352
372
|
skipPrompts: options.yes || false,
|
|
353
|
-
installShims: options.shims,
|
|
373
|
+
installShims: installShims === undefined ? options.shims : installShims,
|
|
354
374
|
};
|
|
355
375
|
}
|
|
356
376
|
|
|
@@ -1116,9 +1136,9 @@ class UI {
|
|
|
1116
1136
|
message: 'Select official modules to install:',
|
|
1117
1137
|
options: allOptions,
|
|
1118
1138
|
initialValues: initialValues.length > 0 ? initialValues : undefined,
|
|
1119
|
-
//
|
|
1120
|
-
// legitimate "core only" install rather than a mistake to block on.
|
|
1139
|
+
// Core installs either way and is not a row here, so empty is a valid core-only install.
|
|
1121
1140
|
required: false,
|
|
1141
|
+
emptyLabel: 'core only',
|
|
1122
1142
|
maxItems: allOptions.length,
|
|
1123
1143
|
});
|
|
1124
1144
|
|