wdi-method 0.6.18 → 0.6.24
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/CHANGELOG.md +105 -0
- package/LICENSE +21 -21
- package/NOTICE +28 -0
- package/README.id.md +190 -0
- package/README.ja.md +188 -0
- package/README.md +190 -530
- package/README.zh.md +188 -0
- package/bin/wdi-method.js +2199 -2112
- package/kit/.constitution/method/README.md +1 -0
- package/kit/.constitution/method/branch-guide.md +87 -0
- package/kit/.constitution/method/ci-guide.md +169 -0
- package/kit/.constitution/method/constitution.md +1 -0
- package/kit/.constitution/method/scripts/lifecycle.py +416 -0
- package/kit/.constitution/method/scripts/validate.py +126 -9
- package/kit/skills/wdi-autopilot/SKILL.md +461 -384
- package/kit/skills/wdi-build/SKILL.md +404 -393
- package/kit/skills/wdi-daily-autopilot/SKILL.md +138 -0
- package/kit/skills/wdi-daily-what-to-build/SKILL.md +155 -0
- package/kit/skills/wdi-daily-what-to-test/SKILL.md +127 -0
- package/kit/skills/wdi-explain-to-me/SKILL.md +1 -1
- package/kit/skills/wdi-help/SKILL.md +21 -7
- package/kit/skills/wdi-init/SKILL.md +16 -0
- package/kit/skills/wdi-prune-or-archive/SKILL.md +76 -0
- package/kit/skills/wdi-review/SKILL.md +4 -1
- package/kit-overlay/AGENTS.md +37 -4
- package/kit-overlay/README.md +1 -0
- package/kit-overlay/constitution.md +1 -0
- package/lib/identity.mjs +246 -117
- package/package.json +8 -4
- package/scaffold/.control/custom-dispatch.yaml.example +65 -0
- package/scaffold/.control/registry/index.yaml +9 -0
- package/scaffold/.control/test-targets/desktop.md +15 -0
- package/scaffold/.control/test-targets/mobile.md +6 -0
- package/scaffold/.control/test-targets/web.md +6 -0
package/kit-overlay/AGENTS.md
CHANGED
|
@@ -27,7 +27,7 @@ npx wdi-method update --yes
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
BMad: https://github.com/bmad-code-org/BMAD-METHOD
|
|
30
|
-
WDI Method: https://github.com/
|
|
30
|
+
WDI Method: https://github.com/wiradeltaid/wdi-method
|
|
31
31
|
|
|
32
32
|
A method file MUST NOT be invented here. If a rule is wrong, fix it in the WDI
|
|
33
33
|
Method package, then update.
|
|
@@ -66,6 +66,28 @@ under `.constitution/`. They are agent instructions, and they travel to every re
|
|
|
66
66
|
**A corpus written before these settings existed MUST NOT be migrated for them.** The readers accept more
|
|
67
67
|
than one language, so existing documents keep working and only new writing follows the setting.
|
|
68
68
|
|
|
69
|
+
## Branches
|
|
70
|
+
|
|
71
|
+
**Two branch settings live in `.control/registry/index.yaml` under `policy:`, both defaulting to `main`:**
|
|
72
|
+
|
|
73
|
+
| Setting | Governs | Default |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `primary_branch` | Production / release trunk | `main` |
|
|
76
|
+
| `development_branch` | Active development branch for specs, worktrees, and PR targets | `main` |
|
|
77
|
+
|
|
78
|
+
- **Immunity:** Agents MUST NOT delete, overwrite, force-push, or rename `primary_branch` or `development_branch`.
|
|
79
|
+
- **Branch protection:** Agents MUST NOT commit application code directly to `primary_branch` or `development_branch`. Code changes MUST arrive via isolated task branches/worktrees and pull requests. In single-branch mode, direct commits to the shared branch are permitted strictly for Phase 1 & 2 planning documents in `.scratch/` and `.control/registry/specs.yaml`.
|
|
80
|
+
- **Target branch:** All worktrees and PRs MUST target `development_branch`. Agents MUST verify the branch exists locally or on remote (`refs/heads/<branch>` or `refs/remotes/origin/<branch>`) and MUST NOT guess or silently fall back to `main`.
|
|
81
|
+
- `.constitution/method/branch-guide.md` binds branch rules.
|
|
82
|
+
|
|
83
|
+
## Delivery flow
|
|
84
|
+
|
|
85
|
+
Applies to all feature specification, ticket creation, and code delivery in this repo:
|
|
86
|
+
|
|
87
|
+
1. Turning notes, a task, or test results into a spec and tickets MUST go through `wdi-build`'s engines (`to-spec` / `to-tickets`, `mattpocock/skills`) — agents MUST NOT invent a bespoke spec or ticket format.
|
|
88
|
+
2. Standing exception to `wdi-build`'s isolated worktree precondition: the spec authoring step (opening the spec, writing `SPEC.md` and ticket files in `.scratch/`, with no application code changes) happens directly on the active development branch (`policy.development_branch`, default `main`). The worktree requirement binds ticket implementation onward and `wdi-autopilot`.
|
|
89
|
+
3. During or after spec and ticket authoring, an agent MUST NOT auto-continue into implementing code, committing, pushing, launching `wdi-autopilot`, merging a PR, cleaning up a worktree, or deploying without explicit confirmation from the maintainer. When explicitly commanded to build a ticket (`wdi-build`), committing and pushing to the task branch are permitted, but auto-merging PRs or auto-deploying remain strictly prohibited.
|
|
90
|
+
|
|
69
91
|
## The thing in your hand → its folder
|
|
70
92
|
|
|
71
93
|
Read this instead of reasoning about what `.what/` and `.how/` mean.
|
|
@@ -157,6 +179,11 @@ verifies the result, and lands the memlog.
|
|
|
157
179
|
- `CONTEXT.md`, `CONTEXT-MAP.md`, and `docs/adr/` MUST NOT be created. `docs/agents/domain.md` is the
|
|
158
180
|
engines' config and says so too: the vocabulary is `.control/product-glossary.md`, domain knowledge is
|
|
159
181
|
`.what/`, design is `.how/`, and a decision is a `DEC-` — never an ADR. Article 3 owns the rule.
|
|
182
|
+
- `.control/registry/specs.yaml` MUST NOT be read in full (1000+ lines) into context to check project
|
|
183
|
+
status or open work — invoke skill `wdi-help` or read the generated status projection in
|
|
184
|
+
`.control/generated/` (`status.yaml`) instead.
|
|
185
|
+
- `.scratch/` MUST NOT be searched with broad or recursive wildcard patterns (`*` or `**`) to discover
|
|
186
|
+
specs — inspect only the candidate spec's folder using `spec_folder:` from `status.yaml` or `specs.yaml`.
|
|
160
187
|
|
|
161
188
|
## Routing — load a guide when the task matches
|
|
162
189
|
|
|
@@ -171,7 +198,7 @@ verifies the result, and lands the memlog.
|
|
|
171
198
|
| Unsure about a domain term | `.control/product-glossary.md` |
|
|
172
199
|
| Looking for a non-technical fact — a domain, an account, a legal entity, a locked date | `.control/project-non-technical-log.md` |
|
|
173
200
|
| Naming anything — a code identifier, a file, a database column | `.constitution/method/language-guide.md` |
|
|
174
|
-
| Asking "which gate now, what next" |
|
|
201
|
+
| Asking "which gate now, what next", project status, or open/pending specs | skill `wdi-help` (MUST invoke `wdi-help` or read generated `status.yaml`) |
|
|
175
202
|
| Having to decide something, and wanting the reading done first | skill `wdi-explain-to-me` — it briefs, and changes nothing |
|
|
176
203
|
| Wanting every `FR` delivered without being asked in between | skill `wdi-autopilot` — preflight, one mandate, then the loop |
|
|
177
204
|
| Setting or changing `mode` or `risk_accepted` | `.constitution/method/document/delivery-flow-guide.md` · skill `wdi-init` |
|
|
@@ -184,6 +211,8 @@ verifies the result, and lands the memlog.
|
|
|
184
211
|
| Writing the spine, an `AD-N`, C4, or one of the three inventories | `.constitution/method/document/architecture-guide.md` |
|
|
185
212
|
| Opening, accepting, or applying a `DEC-` | `.constitution/method/document/decision-guide.md` |
|
|
186
213
|
| Writing or reading a structure map | `.constitution/method/structure-guide.md` |
|
|
214
|
+
| Writing or changing a CI workflow, or deciding whether a push may start a cloud run | `.constitution/method/ci-guide.md` |
|
|
215
|
+
| Branching rules, protected branches, or PR target policy | `.constitution/method/branch-guide.md` |
|
|
187
216
|
| Looking for where code lives, or placing new code | `.control/structure-codebase.md` |
|
|
188
217
|
| Looking for where a document lives | `.control/structure-document.md` |
|
|
189
218
|
| Writing or reviewing code | `.constitution/project/codebase-stack-guide.md` · `.constitution/project/codebase-conventions-guide.md` · `.constitution/project/codebase-brownfield-guide.md` |
|
|
@@ -214,9 +243,13 @@ MUST anything in `.constitution/method/why/`; `status: Reference` forbids it. A
|
|
|
214
243
|
|
|
215
244
|
- A skill MUST NOT be invoked automatically. Name the one that fits and wait for the owner's
|
|
216
245
|
go-ahead — this holds even when the skill's own description says it must be used. Reading a
|
|
217
|
-
skill as reference is fine. The
|
|
246
|
+
skill as reference is fine. The exceptions are: (1) `wdi-autopilot` under a `DEC-` of `type: mandate`
|
|
218
247
|
at `status: accepted` that has not expired: the mandate **is** the go-ahead, for every skill it
|
|
219
|
-
needs, until it lapses
|
|
248
|
+
needs, until it lapses; (2) when the owner explicitly invokes an autonomous daily tier skill
|
|
249
|
+
(`/wdi-daily-*`), the owner's invocation authorizes the bounded orchestration steps specified in that skill;
|
|
250
|
+
(3) read-only status, open specs, and routing inquiries (`wdi-help`): the model MUST invoke
|
|
251
|
+
`wdi-help` (or read the generated status projection in `.control/generated/`) automatically to
|
|
252
|
+
inspect current gate progress, open work, or determine the next skill without modifying project state.
|
|
220
253
|
- `.work/` is not production code. It MUST NOT be imported by the application, and MUST be
|
|
221
254
|
excluded when searching for code.
|
|
222
255
|
<!-- END:wdi-method -->
|
package/kit-overlay/README.md
CHANGED
|
@@ -43,6 +43,7 @@ Never a rule. When it disagrees with a guide, the guide wins and the disagreemen
|
|
|
43
43
|
| [`language-guide.md`](language-guide.md) | Naming anything — a code identifier, a code file, a document file |
|
|
44
44
|
| [`method-glossary.md`](method-glossary.md) | Unsure what a method term means — layer, wave, Product Component, ID code |
|
|
45
45
|
| [`structure-guide.md`](structure-guide.md) | Writing or checking the two structure maps in `.control/` |
|
|
46
|
+
| [`ci-guide.md`](ci-guide.md) | Writing or changing a CI workflow; when a push may start a cloud run, and what MUST NOT |
|
|
46
47
|
|
|
47
48
|
## `document/` — document rules
|
|
48
49
|
|
|
@@ -26,6 +26,7 @@ The repo layout is governed by `corpus-guide.md` and mapped by
|
|
|
26
26
|
| `.what-rendered/` · `.how-rendered/` | The two above, assembled for a human to read — one page per gate, at the mirror path. Regenerated by `validate.py`, never edited, never read by a skill |
|
|
27
27
|
| `_bmad-output/` | Run workspace; MUST be in git, not curated |
|
|
28
28
|
| `.scratch/` | One directory per effort: a spec's `SPEC.md` and its ticket files, and ad hoc work that has no `FR` yet. MUST be in git — the corpus cites into it by path |
|
|
29
|
+
| `.archive/` | Archived records — closed specs and historical provenance moved from `.scratch/`. MUST be in git |
|
|
29
30
|
| `.work/` | Scratch; MUST be in git, emptied when a task closes |
|
|
30
31
|
| *(application roots)* | Application code — named and mapped in `.control/structure-codebase.md` |
|
|
31
32
|
|
package/lib/identity.mjs
CHANGED
|
@@ -1,117 +1,246 @@
|
|
|
1
|
-
// index.yaml editing. Every regex here is anchored on `\n`, and a Windows checkout hands us `\r\n` —
|
|
2
|
-
// so every function normalises before matching and restores the file's own ending before writing.
|
|
3
|
-
//
|
|
4
|
-
// Skipping that is not a cosmetic bug. It silently returns "" for a name that IS set, which makes
|
|
5
|
-
// `identityIsPlaceholder` true, which makes `setProductIdentity` return early: the installer reports
|
|
6
|
-
// nothing and writes nothing, on every CRLF repo. Found 2026-08-18 on the first real install into a
|
|
7
|
-
// Windows product repo, where `policy.doc_language` printed empty while the file plainly said `id`.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export function
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
1
|
+
// index.yaml editing. Every regex here is anchored on `\n`, and a Windows checkout hands us `\r\n` —
|
|
2
|
+
// so every function normalises before matching and restores the file's own ending before writing.
|
|
3
|
+
//
|
|
4
|
+
// Skipping that is not a cosmetic bug. It silently returns "" for a name that IS set, which makes
|
|
5
|
+
// `identityIsPlaceholder` true, which makes `setProductIdentity` return early: the installer reports
|
|
6
|
+
// nothing and writes nothing, on every CRLF repo. Found 2026-08-18 on the first real install into a
|
|
7
|
+
// Windows product repo, where `policy.doc_language` printed empty while the file plainly said `id`.
|
|
8
|
+
|
|
9
|
+
// FREE TEXT, not an enum. The consumer is a model, and a model does not need a list: "English",
|
|
10
|
+
// "Bahasa Indonesia", "id", "Indonesia" all read the same to it. Fencing this into two values would
|
|
11
|
+
// force the owner to translate their intent into the installer's vocabulary first, and nothing is
|
|
12
|
+
// bought with that.
|
|
13
|
+
export const DEFAULT_DOC_LANGUAGE = "English";
|
|
14
|
+
|
|
15
|
+
/** LF view of the text, plus how to put the original endings back. */
|
|
16
|
+
function lf(text) {
|
|
17
|
+
const crlf = text.includes("\r\n");
|
|
18
|
+
return { body: crlf ? text.replaceAll("\r\n", "\n") : text, crlf };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function restore(body, crlf) {
|
|
22
|
+
return crlf ? body.replaceAll("\n", "\r\n") : body;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function yamlQuote(value) {
|
|
26
|
+
return `"${String(value).replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generic block-level key upsert that preserves comments, indentations, and existing sibling keys.
|
|
31
|
+
*/
|
|
32
|
+
export function upsertBlockKeys(body, blockName, pairs) {
|
|
33
|
+
const entries = Array.isArray(pairs) ? pairs : Object.entries(pairs);
|
|
34
|
+
const validEntries = entries.filter(([_, v]) => v !== undefined);
|
|
35
|
+
if (validEntries.length === 0) {
|
|
36
|
+
return body;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const lines = body.split("\n");
|
|
40
|
+
// Header must start at column 0 to prevent matching nested keys
|
|
41
|
+
const headerRegex = new RegExp(`^${blockName}:[ \\t]*(?:#.*)?$`);
|
|
42
|
+
const headerIndex = lines.findIndex((l) => headerRegex.test(l));
|
|
43
|
+
|
|
44
|
+
if (headerIndex === -1) {
|
|
45
|
+
const blockContent = validEntries.map(([k, v]) => ` ${k}: ${v}`).join("\n");
|
|
46
|
+
const newBlockLines = [`${blockName}:`, ...validEntries.map(([k, v]) => ` ${k}: ${v}`)];
|
|
47
|
+
|
|
48
|
+
// If adding policy and product exists, place policy right after product block
|
|
49
|
+
if (blockName === "policy") {
|
|
50
|
+
const productHeaderRegex = /^product:[ \t]*(?:#.*)?$/;
|
|
51
|
+
const productHeaderIndex = lines.findIndex((l) => productHeaderRegex.test(l));
|
|
52
|
+
if (productHeaderIndex !== -1) {
|
|
53
|
+
let nextIndex = lines.length;
|
|
54
|
+
for (let i = productHeaderIndex + 1; i < lines.length; i++) {
|
|
55
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
56
|
+
nextIndex = i;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let lastIndented = productHeaderIndex;
|
|
61
|
+
for (let i = nextIndex - 1; i > productHeaderIndex; i--) {
|
|
62
|
+
if (/^\s/.test(lines[i])) {
|
|
63
|
+
lastIndented = i;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (lines[lastIndented + 1] === "") {
|
|
68
|
+
const insertLines = nextIndex < lines.length ? [...newBlockLines, ""] : newBlockLines;
|
|
69
|
+
lines.splice(lastIndented + 2, 0, ...insertLines);
|
|
70
|
+
} else {
|
|
71
|
+
const insertLines = nextIndex < lines.length ? ["", ...newBlockLines, ""] : ["", ...newBlockLines];
|
|
72
|
+
lines.splice(lastIndented + 1, 0, ...insertLines);
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const cleanBody = body.replace(/^\uFEFF/, "");
|
|
79
|
+
return cleanBody ? `${blockName}:\n${blockContent}\n\n${cleanBody}` : `${blockName}:\n${blockContent}\n`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// End of block is reached at the next top-level mapping key or document separator
|
|
83
|
+
let nextBlockIndex = lines.length;
|
|
84
|
+
for (let i = headerIndex + 1; i < lines.length; i++) {
|
|
85
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
86
|
+
nextBlockIndex = i;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let lastIndentedIndex = headerIndex;
|
|
92
|
+
for (let i = nextBlockIndex - 1; i > headerIndex; i--) {
|
|
93
|
+
if (/^\s/.test(lines[i])) {
|
|
94
|
+
lastIndentedIndex = i;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const toAppend = [];
|
|
100
|
+
for (const [key, value] of validEntries) {
|
|
101
|
+
// Only match direct children at 2 spaces indentation
|
|
102
|
+
const keyRegex = new RegExp(`^ ${key}:[ \\t]*`);
|
|
103
|
+
let foundIndex = -1;
|
|
104
|
+
for (let i = headerIndex + 1; i < nextBlockIndex; i++) {
|
|
105
|
+
if (keyRegex.test(lines[i])) {
|
|
106
|
+
foundIndex = i;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (foundIndex !== -1) {
|
|
112
|
+
// Preserve any trailing inline comment
|
|
113
|
+
const commentMatch = lines[foundIndex].match(/(?:[ \t]+#.*)$/);
|
|
114
|
+
const inlineComment = commentMatch ? commentMatch[0] : "";
|
|
115
|
+
lines[foundIndex] = ` ${key}: ${value}${inlineComment}`;
|
|
116
|
+
} else {
|
|
117
|
+
toAppend.push(` ${key}: ${value}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (toAppend.length > 0) {
|
|
122
|
+
lines.splice(lastIndentedIndex + 1, 0, ...toAppend);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function readBlockValue(body, blockName, key) {
|
|
129
|
+
const lines = body.split("\n");
|
|
130
|
+
const headerRegex = new RegExp(`^${blockName}:[ \\t]*(?:#.*)?$`);
|
|
131
|
+
const headerIndex = lines.findIndex((l) => headerRegex.test(l));
|
|
132
|
+
if (headerIndex === -1) return "";
|
|
133
|
+
|
|
134
|
+
let nextBlockIndex = lines.length;
|
|
135
|
+
for (let i = headerIndex + 1; i < lines.length; i++) {
|
|
136
|
+
if (/^[a-zA-Z0-9_-]+:|^---/.test(lines[i])) {
|
|
137
|
+
nextBlockIndex = i;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const doubleQuotedRegex = new RegExp(`^ ${key}:[ \\t]*"((?:[^"\\\\]|\\\\.)*)"`);
|
|
143
|
+
const singleQuotedRegex = new RegExp(`^ ${key}:[ \\t]*'((?:[^'\\\\]|\\\\.)*)'`);
|
|
144
|
+
const bareRegex = new RegExp(`^ ${key}:[ \\t]*([^\\n#]*)`);
|
|
145
|
+
|
|
146
|
+
for (let i = headerIndex + 1; i < nextBlockIndex; i++) {
|
|
147
|
+
const dQuoted = lines[i].match(doubleQuotedRegex);
|
|
148
|
+
if (dQuoted) {
|
|
149
|
+
return dQuoted[1].replaceAll('\\"', '"').replaceAll('\\\\', '\\').trim();
|
|
150
|
+
}
|
|
151
|
+
const sQuoted = lines[i].match(singleQuotedRegex);
|
|
152
|
+
if (sQuoted) {
|
|
153
|
+
return sQuoted[1].replaceAll("''", "'").trim();
|
|
154
|
+
}
|
|
155
|
+
const bare = lines[i].match(bareRegex);
|
|
156
|
+
if (bare) return bare[1].trim();
|
|
157
|
+
}
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function readProductIdentity(text) {
|
|
162
|
+
const { body } = lf(text);
|
|
163
|
+
return {
|
|
164
|
+
name: readBlockValue(body, "product", "name"),
|
|
165
|
+
client: readBlockValue(body, "product", "client"),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function writeProductIdentity(text, { name, client }) {
|
|
170
|
+
const { body, crlf } = lf(text);
|
|
171
|
+
const pairs = [
|
|
172
|
+
["name", yamlQuote(name)],
|
|
173
|
+
["client", yamlQuote(client ?? "")],
|
|
174
|
+
];
|
|
175
|
+
const nextBody = upsertBlockKeys(body, "product", pairs);
|
|
176
|
+
return restore(nextBody, crlf);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function identityIsPlaceholder(name) {
|
|
180
|
+
return !name || name === "{product}";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------- policy (language & branches)
|
|
184
|
+
//
|
|
185
|
+
// Language settings:
|
|
186
|
+
// doc_language the PROSE of working documents in .what/ .how/ .control/
|
|
187
|
+
// doc_filename_language the SLUG part of a document filename
|
|
188
|
+
//
|
|
189
|
+
// Branch settings:
|
|
190
|
+
// primary_branch trunk / production branch (defaults to "main")
|
|
191
|
+
// development_branch active development branch (defaults to "main")
|
|
192
|
+
|
|
193
|
+
function readPolicyValue(body, key) {
|
|
194
|
+
return readBlockValue(body, "policy", key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function readLanguagePolicy(text) {
|
|
198
|
+
const { body } = lf(text);
|
|
199
|
+
return {
|
|
200
|
+
docLanguage: readPolicyValue(body, "doc_language"),
|
|
201
|
+
docFilenameLanguage: readPolicyValue(body, "doc_filename_language"),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function writeLanguagePolicy(text, { docLanguage, docFilenameLanguage }) {
|
|
206
|
+
const { body, crlf } = lf(text);
|
|
207
|
+
const pairs = [];
|
|
208
|
+
if (docLanguage !== undefined) pairs.push(["doc_language", yamlQuote(docLanguage)]);
|
|
209
|
+
if (docFilenameLanguage !== undefined) pairs.push(["doc_filename_language", yamlQuote(docFilenameLanguage)]);
|
|
210
|
+
const nextBody = upsertBlockKeys(body, "policy", pairs);
|
|
211
|
+
return restore(nextBody, crlf);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function readBranchPolicy(text) {
|
|
215
|
+
const { body } = lf(text);
|
|
216
|
+
const primary = readPolicyValue(body, "primary_branch");
|
|
217
|
+
const development = readPolicyValue(body, "development_branch");
|
|
218
|
+
return {
|
|
219
|
+
primaryBranch: primary || "main",
|
|
220
|
+
developmentBranch: development || "main",
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function writeBranchPolicy(text, { primaryBranch, developmentBranch }) {
|
|
225
|
+
const { body, crlf } = lf(text);
|
|
226
|
+
const pairs = [];
|
|
227
|
+
if (primaryBranch !== undefined) pairs.push(["primary_branch", yamlQuote(primaryBranch)]);
|
|
228
|
+
if (developmentBranch !== undefined) pairs.push(["development_branch", yamlQuote(developmentBranch)]);
|
|
229
|
+
const nextBody = upsertBlockKeys(body, "policy", pairs);
|
|
230
|
+
return restore(nextBody, crlf);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// A first install has nowhere to read a product name from, so the folder is the best guess available —
|
|
234
|
+
// and a guess the owner can accept with Enter beats a field they must type. `acme-billing-portal`
|
|
235
|
+
// becomes `Acme Billing Portal`; camelCase splits too. It is a SUGGESTION: G1 confirms the real name.
|
|
236
|
+
export function humaniseFolderName(name) {
|
|
237
|
+
return String(name || "")
|
|
238
|
+
.replace(/[._-]+/g, " ")
|
|
239
|
+
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
|
|
240
|
+
.replace(/\s+/g, " ")
|
|
241
|
+
.trim()
|
|
242
|
+
.split(" ")
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
.map((w) => (w === w.toUpperCase() && w.length <= 4 ? w : w[0].toUpperCase() + w.slice(1)))
|
|
245
|
+
.join(" ");
|
|
246
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wdi-method",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.24",
|
|
4
4
|
"description": "WDI Method — software delivery method that wraps BMad",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
"kit-overlay/",
|
|
14
14
|
"scaffold/",
|
|
15
15
|
"README.md",
|
|
16
|
+
"README.id.md",
|
|
17
|
+
"README.ja.md",
|
|
18
|
+
"README.zh.md",
|
|
16
19
|
"CHANGELOG.md",
|
|
17
20
|
"LICENSE",
|
|
21
|
+
"NOTICE",
|
|
18
22
|
"!**/__pycache__",
|
|
19
23
|
"!**/*.pyc"
|
|
20
24
|
],
|
|
@@ -35,10 +39,10 @@
|
|
|
35
39
|
"license": "MIT",
|
|
36
40
|
"repository": {
|
|
37
41
|
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/
|
|
42
|
+
"url": "git+https://github.com/wiradeltaid/wdi-method.git"
|
|
39
43
|
},
|
|
40
44
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
45
|
+
"url": "https://github.com/wiradeltaid/wdi-method/issues"
|
|
42
46
|
},
|
|
43
|
-
"homepage": "https://github.com/
|
|
47
|
+
"homepage": "https://github.com/wiradeltaid/wdi-method#readme"
|
|
44
48
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# .control/custom-dispatch.yaml.example
|
|
2
|
+
# Optional machine-local runner and dispatch configuration for autonomous daily tier skills.
|
|
3
|
+
# When seeded to `.control/custom-dispatch.yaml`, it remains local and gitignored.
|
|
4
|
+
schema: 1
|
|
5
|
+
|
|
6
|
+
runners: {}
|
|
7
|
+
# Uncomment and customize runners on this machine:
|
|
8
|
+
#
|
|
9
|
+
# Auto runners (dispatches in-session if model is reachable from active session;
|
|
10
|
+
# otherwise executes shell-out command):
|
|
11
|
+
# sonnet:
|
|
12
|
+
# type: auto
|
|
13
|
+
# model: sonnet
|
|
14
|
+
# command: 'claude --model claude-sonnet-5 --effort high --dangerously-skip-permissions -p'
|
|
15
|
+
#
|
|
16
|
+
# opus:
|
|
17
|
+
# type: auto
|
|
18
|
+
# model: opus
|
|
19
|
+
# command: 'claude --model claude-opus-5 --effort high --dangerously-skip-permissions -p'
|
|
20
|
+
#
|
|
21
|
+
# External CLI runners (always shell-out):
|
|
22
|
+
# NOTE: For roles.reviewer, runner commands MUST enforce read-only execution where supported
|
|
23
|
+
# (e.g. `--trust-tools=fs_read` for kiro-cli, `--mode plan` for cursor-agent).
|
|
24
|
+
#
|
|
25
|
+
# terra:
|
|
26
|
+
# type: shell-out
|
|
27
|
+
# command: 'kiro-cli chat --model gpt-5.6-terra --effort high --trust-tools=fs_read --no-interactive'
|
|
28
|
+
# timeout_s: 600 # Hard wall-clock timeout in seconds for backgrounded dispatch (default: 600s)
|
|
29
|
+
#
|
|
30
|
+
# composer:
|
|
31
|
+
# type: shell-out
|
|
32
|
+
# command: 'cursor-agent --mode plan --model "composer-2.5"'
|
|
33
|
+
# timeout_s: 600
|
|
34
|
+
#
|
|
35
|
+
# sol:
|
|
36
|
+
# type: shell-out
|
|
37
|
+
# command: 'cursor-agent --mode plan --model "gpt-5.6-sol-high"'
|
|
38
|
+
# timeout_s: 600
|
|
39
|
+
|
|
40
|
+
roles:
|
|
41
|
+
# Builder configuration:
|
|
42
|
+
# builder is fixed to 'coordinator' (mandatory). The coordinating session implements code
|
|
43
|
+
# directly in the active run worktree (tight TDD cycles, direct verification). Coding delegation
|
|
44
|
+
# (in-session subagent or external builder runner) is prohibited in the daily routine to eliminate
|
|
45
|
+
# handoff latency and delegation hallucinations.
|
|
46
|
+
# Guardrail: builder: coordinator MUST NOT be paired with reviewer: none unless all touched
|
|
47
|
+
# components have risk_accepted: low and the decision is recorded. A builder cannot be its own sole judge.
|
|
48
|
+
builder: coordinator
|
|
49
|
+
|
|
50
|
+
# Reviewer options:
|
|
51
|
+
# - default: Resolves peer review via caller's active session / global agent rules
|
|
52
|
+
# - <runner-id>: Dispatches peer review to the named runner
|
|
53
|
+
# - none: Disables peer review pass (coordinator self-review only)
|
|
54
|
+
# NOTE: Reviewer runners in daily triage (wdi-daily-what-to-build) operate strictly as advisory /
|
|
55
|
+
# read-only critics. The coordinator alone applies edits to the spec/tickets and writes registry traces.
|
|
56
|
+
reviewer: default
|
|
57
|
+
|
|
58
|
+
# Deep analyst options (for architecture spine, complex debugging, or hard decisions):
|
|
59
|
+
# - default: Uses reviewer runner or coordinator for deep analysis
|
|
60
|
+
# - <runner-id>: Dispatches deep analysis to a dedicated high-reasoning runner
|
|
61
|
+
# - none: Disables separate deep analyst dispatch (handled by reviewer/coordinator)
|
|
62
|
+
deep_analyst: default
|
|
63
|
+
|
|
64
|
+
review_policy:
|
|
65
|
+
peer_review: true # Set to false (or set reviewer: none) to bypass peer review pass
|
|
@@ -9,6 +9,15 @@ product:
|
|
|
9
9
|
name: "{product}"
|
|
10
10
|
client: ""
|
|
11
11
|
|
|
12
|
+
# POLICY SETTINGS. Language and branching rules for the repository.
|
|
13
|
+
# Branch settings default to `main` if omitted. Set `development_branch` to match
|
|
14
|
+
# `primary_branch` for single-branch workflows.
|
|
15
|
+
policy:
|
|
16
|
+
doc_language: "English"
|
|
17
|
+
doc_filename_language: "English"
|
|
18
|
+
# primary_branch: main
|
|
19
|
+
# development_branch: development # Set equal to primary_branch for single-branch workflow
|
|
20
|
+
|
|
12
21
|
# GLOBAL DEPTH. The only document-depth knob, and it MUST NOT also govern review intensity —
|
|
13
22
|
# that is `risk_accepted` per component in components.yaml.
|
|
14
23
|
# catalog UC list + three inventories + C4; zero extra files per component. G4 is skipped
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Desktop Smoke Test Target
|
|
2
|
+
|
|
3
|
+
## Target Configuration
|
|
4
|
+
# Optional build and process settings for this repository:
|
|
5
|
+
# build_command: <build command from codebase-stack-guide.md>
|
|
6
|
+
# profile: debug | release (default: release for visual/performance, debug for fast logic loops)
|
|
7
|
+
# artifact_path: <path to compiled binary artifact>
|
|
8
|
+
# process_name: <application process name>
|
|
9
|
+
|
|
10
|
+
## Verification Checklist Template
|
|
11
|
+
|
|
12
|
+
- [ ] Application launches cleanly without crashes or uncaught runtime panics
|
|
13
|
+
- [ ] Primary window displays expected initial view and layout
|
|
14
|
+
- [ ] User input, navigation, and window controls respond as expected
|
|
15
|
+
- [ ] Application logs are free from unexpected warnings or errors
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Mobile Smoke Test Checklist Template
|
|
2
|
+
|
|
3
|
+
- [ ] Application installs and launches on target device or emulator
|
|
4
|
+
- [ ] Initial view displays cleanly without crashing
|
|
5
|
+
- [ ] Touch gestures and primary navigation respond as expected
|
|
6
|
+
- [ ] Device runtime log displays no unhandled exceptions
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Web Smoke Test Checklist Template
|
|
2
|
+
|
|
3
|
+
- [ ] Application server starts and responds to HTTP requests
|
|
4
|
+
- [ ] Initial route renders without 404 or 500 status codes
|
|
5
|
+
- [ ] Interactive UI controls respond to user interaction
|
|
6
|
+
- [ ] Browser console log shows no uncaught exceptions
|