@phuc1403/musketeer 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -28,14 +28,17 @@ musketeers are in this project; promote = upgrade the binary._
|
|
|
28
28
|
|
|
29
29
|
## Musketeers (v1)
|
|
30
30
|
|
|
31
|
-
| Musketeer | Default | What it adds |
|
|
32
|
-
|
|
33
|
-
| **core** | always (locked) | research, handoff, skill-creator · statusline, usage-context, format-json hooks |
|
|
34
|
-
| **architecture** |
|
|
35
|
-
| **hallmark** |
|
|
36
|
-
| **code-review** |
|
|
37
|
-
| **dotnet** |
|
|
38
|
-
| **design-docs** |
|
|
31
|
+
| Musketeer | Default in muster | What it adds |
|
|
32
|
+
|-----------|-------------------|--------------|
|
|
33
|
+
| **core** | always on (locked, hidden) | research, handoff, skill-creator · statusline, usage-context, format-json hooks |
|
|
34
|
+
| **architecture** | off | adr-writer, architecture-characteristic-writer, context-map · CML validation hook |
|
|
35
|
+
| **hallmark** | off | hallmark, hallmark-explore, hallmark-loop · auditor/explorer agents |
|
|
36
|
+
| **code-review** | off | code-review skill + code-reviewer agent |
|
|
37
|
+
| **dotnet** | off | tdd, knowledge-crunching · EF migration guard hook |
|
|
38
|
+
| **design-docs** | off | inject-design-docs SessionStart hook |
|
|
39
|
+
|
|
40
|
+
The muster starts every musketeer **unselected**, except ones you already installed (pre-checked from
|
|
41
|
+
`.musketeer.json`). **core** is always included and isn't shown as a toggle — it can't be removed.
|
|
39
42
|
|
|
40
43
|
Vocabulary: **musketeer** = a selectable unit · **muster** = the picker · **company** = your recruited set.
|
|
41
44
|
|
package/manifest.json
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
"label": "core",
|
|
6
6
|
"description": "Always-on foundation: research, handoff, skill-creator + statusline, usage-context & format-json hooks.",
|
|
7
7
|
"locked": true,
|
|
8
|
-
"defaultChecked": true,
|
|
9
8
|
"deps": [],
|
|
10
9
|
"files": [
|
|
11
10
|
"skills/research/**",
|
|
@@ -50,7 +49,6 @@
|
|
|
50
49
|
"label": "architecture",
|
|
51
50
|
"description": "DDD architecture authoring: adr-writer, architecture-characteristic-writer, context-map (CML) + validation hook.",
|
|
52
51
|
"locked": false,
|
|
53
|
-
"defaultChecked": true,
|
|
54
52
|
"deps": [],
|
|
55
53
|
"files": [
|
|
56
54
|
"skills/adr-writer/**",
|
|
@@ -72,7 +70,6 @@
|
|
|
72
70
|
"label": "hallmark",
|
|
73
71
|
"description": "Hallmark design system: hallmark rubric, hallmark-explore, hallmark-loop + auditor/explorer agents.",
|
|
74
72
|
"locked": false,
|
|
75
|
-
"defaultChecked": true,
|
|
76
73
|
"deps": [],
|
|
77
74
|
"files": [
|
|
78
75
|
"skills/hallmark/**",
|
|
@@ -88,7 +85,6 @@
|
|
|
88
85
|
"label": "code-review",
|
|
89
86
|
"description": "Adversarial code review skill + code-reviewer agent (commit / pending / PR modes).",
|
|
90
87
|
"locked": false,
|
|
91
|
-
"defaultChecked": true,
|
|
92
88
|
"deps": [],
|
|
93
89
|
"files": [
|
|
94
90
|
"skills/code-review/**",
|
|
@@ -101,7 +97,6 @@
|
|
|
101
97
|
"label": "dotnet",
|
|
102
98
|
"description": ".NET extras: tdd, knowledge-crunching + EF migration-guard hook.",
|
|
103
99
|
"locked": false,
|
|
104
|
-
"defaultChecked": true,
|
|
105
100
|
"deps": [],
|
|
106
101
|
"files": [
|
|
107
102
|
"skills/tdd/**",
|
|
@@ -123,7 +118,6 @@
|
|
|
123
118
|
"label": "design-docs",
|
|
124
119
|
"description": "SessionStart hook that injects architecture/context-map/adr docs/ outputs into context.",
|
|
125
120
|
"locked": false,
|
|
126
|
-
"defaultChecked": true,
|
|
127
121
|
"deps": [],
|
|
128
122
|
"files": [
|
|
129
123
|
"hooks/inject-design-docs.cjs"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phuc1403/musketeer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Distributable custom Claude Code harness — one declarative command scaffolds a curated company of musketeers (skills/agents/hooks) into any project's .claude/.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
package/src/muster.js
CHANGED
|
@@ -1,30 +1,63 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// The muster — the interactive picker. Uses @clack/prompts (ESM-only), loaded
|
|
4
|
-
// via dynamic import() for CommonJS interop.
|
|
5
|
-
//
|
|
4
|
+
// via dynamic import() for CommonJS interop. The selection rules live in the pure
|
|
5
|
+
// helpers below (planSelection / withLocked) so they can be unit-tested without a TTY.
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
+
* Compute the muster's option list and initial selection. Pure — no I/O.
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* * Locked musketeers (core) are NOT selectable — always-on, hidden from the
|
|
12
|
+
* toggle list so they can't be unchecked. They're force-added by withLocked().
|
|
13
|
+
* * Every other musketeer defaults to UNSELECTED. Only musketeers already
|
|
14
|
+
* installed (present in the lockfile) start pre-checked, so re-running shows
|
|
15
|
+
* the current company and confirming unchanged is a no-op.
|
|
16
|
+
*
|
|
8
17
|
* @param {object} manifest
|
|
9
18
|
* @param {{musketeers:string[]}} prev current lockfile company
|
|
10
|
-
* @returns {
|
|
19
|
+
* @returns {{lockedIds:string[], options:Array<{value:string,label:string,hint:string}>, initialValues:string[]}}
|
|
11
20
|
*/
|
|
12
|
-
|
|
13
|
-
const clack = await import('@clack/prompts');
|
|
21
|
+
function planSelection(manifest, prev) {
|
|
14
22
|
const entries = Object.entries(manifest.musketeers);
|
|
15
|
-
const installed = new Set(prev.musketeers);
|
|
16
|
-
const hasLock = prev.musketeers.length > 0;
|
|
23
|
+
const installed = new Set((prev && prev.musketeers) || []);
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
.filter(([id, m]) => m.locked || (hasLock ? installed.has(id) : m.defaultChecked))
|
|
21
|
-
.map(([id]) => id);
|
|
25
|
+
const lockedIds = entries.filter(([, m]) => m.locked).map(([id]) => id);
|
|
26
|
+
const selectable = entries.filter(([, m]) => !m.locked);
|
|
22
27
|
|
|
23
|
-
const options =
|
|
28
|
+
const options = selectable.map(([id, m]) => ({
|
|
24
29
|
value: id,
|
|
25
|
-
label: m.
|
|
30
|
+
label: m.label,
|
|
26
31
|
hint: m.description,
|
|
27
32
|
}));
|
|
33
|
+
const initialValues = selectable
|
|
34
|
+
.filter(([id]) => installed.has(id))
|
|
35
|
+
.map(([id]) => id);
|
|
36
|
+
|
|
37
|
+
return { lockedIds, options, initialValues };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Finalize a raw multiselect result: dedupe and force every locked musketeer on
|
|
42
|
+
* (enforces locked-on — "core can't be removed").
|
|
43
|
+
* @param {string[]} selectedIds
|
|
44
|
+
* @param {string[]} lockedIds
|
|
45
|
+
* @returns {string[]}
|
|
46
|
+
*/
|
|
47
|
+
function withLocked(selectedIds, lockedIds) {
|
|
48
|
+
const chosen = new Set(Array.isArray(selectedIds) ? selectedIds : []);
|
|
49
|
+
for (const id of lockedIds || []) chosen.add(id);
|
|
50
|
+
return [...chosen];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {object} manifest
|
|
55
|
+
* @param {{musketeers:string[]}} prev current lockfile company
|
|
56
|
+
* @returns {Promise<?string[]>} chosen ids (incl. locked), or null if cancelled
|
|
57
|
+
*/
|
|
58
|
+
async function pick(manifest, prev) {
|
|
59
|
+
const clack = await import('@clack/prompts');
|
|
60
|
+
const { lockedIds, options, initialValues } = planSelection(manifest, prev);
|
|
28
61
|
|
|
29
62
|
clack.intro('musketeer — muster');
|
|
30
63
|
const selected = await clack.multiselect({
|
|
@@ -39,10 +72,9 @@ async function pick(manifest, prev) {
|
|
|
39
72
|
return null;
|
|
40
73
|
}
|
|
41
74
|
|
|
42
|
-
const chosen =
|
|
43
|
-
chosen.
|
|
44
|
-
|
|
45
|
-
return [...chosen];
|
|
75
|
+
const chosen = withLocked(selected, lockedIds);
|
|
76
|
+
clack.outro(`Company: ${chosen.join(', ')}`);
|
|
77
|
+
return chosen;
|
|
46
78
|
}
|
|
47
79
|
|
|
48
|
-
module.exports = { pick };
|
|
80
|
+
module.exports = { pick, planSelection, withLocked };
|
package/src/schema.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* "<id>": {
|
|
11
11
|
* label: string,
|
|
12
12
|
* description: string,
|
|
13
|
-
* locked: boolean,
|
|
14
|
-
* defaultChecked: boolean,
|
|
13
|
+
* locked: boolean, // always-on; hidden from the muster toggle list
|
|
15
14
|
* files: string[], // globs relative to template/.claude/
|
|
16
15
|
* deps: string[], // other musketeer ids (intra-package edges)
|
|
17
16
|
* settings: SettingFragment[],
|
|
@@ -80,7 +79,6 @@ function validateMusketeer(id, m) {
|
|
|
80
79
|
if (typeof m.label !== 'string') fail(`${ctx}.label must be a string`);
|
|
81
80
|
if (typeof m.description !== 'string') fail(`${ctx}.description must be a string`);
|
|
82
81
|
if (typeof m.locked !== 'boolean') fail(`${ctx}.locked must be a boolean`);
|
|
83
|
-
if (typeof m.defaultChecked !== 'boolean') fail(`${ctx}.defaultChecked must be a boolean`);
|
|
84
82
|
if (!isStringArray(m.files)) fail(`${ctx}.files must be a string[]`);
|
|
85
83
|
if (!isStringArray(m.deps)) fail(`${ctx}.deps must be a string[]`);
|
|
86
84
|
if (!isStringArray(m.prereqs)) fail(`${ctx}.prereqs must be a string[]`);
|
|
@@ -42,7 +42,7 @@ it's out of scope and offer to hand off afterward.
|
|
|
42
42
|
|
|
43
43
|
### Step 1 — Open the session and name the flow
|
|
44
44
|
|
|
45
|
-
**First read any prior crunching output for that context** —
|
|
45
|
+
**First read any prior crunching output for that context** — the root-level `CONTEXT.md` and read the domain's code; in DDD, document = code. While reading the code, **note the language, idioms, and test framework already in use** — you'll model in those. If the module is a bare scaffold with no language chosen yet, ask the user which language and test framework the context should be modeled in before proposing any code.
|
|
46
46
|
|
|
47
47
|
### Step 2 — Find the first model elements (the "glimmer")
|
|
48
48
|
Listen past the user's solution framing for the **domain nouns and verbs that keep recurring** — Name 2–4 candidate
|
|
@@ -105,7 +105,7 @@ that embodies it), then `_Avoid_:` rejected synonyms — grouped under a `###` s
|
|
|
105
105
|
`CONTEXT.md` identical: rename in code, rename here in the same turn. Distilled-out concepts go under
|
|
106
106
|
`## Deferred`, unresolved ones under `## Flagged ambiguities`.
|
|
107
107
|
|
|
108
|
-
Edit the context's existing `
|
|
108
|
+
Edit the context's existing root-level `CONTEXT.md` directly — it is canonical, and is
|
|
109
109
|
also your seed (Step 1). If the module is new and has no `CONTEXT.md` yet, create one from
|
|
110
110
|
`assets/context.template.md`.
|
|
111
111
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<One line: the slice of the domain this context covers — the flow you crunched, in the expert's words.>
|
|
4
4
|
|
|
5
|
-
> Starter for a context that has **no `CONTEXT.md` yet**. It lives at
|
|
6
|
-
> `
|
|
5
|
+
> Starter for a context that has **no `CONTEXT.md` yet**. It lives at the
|
|
6
|
+
> root folder as `CONTEXT.md`. If the context already has one, edit that — never a second.
|
|
7
7
|
|
|
8
8
|
## Language
|
|
9
9
|
|