siesa-agents 2.1.90 → 2.1.92
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/bmad/_config/bmad-help.csv +45 -0
- package/bmad/_config/files-manifest.csv +272 -0
- package/bmad/_config/manifest.yaml +23 -0
- package/bmad/_config/skill-manifest.csv +43 -0
- package/bmad/bmm/module-help.csv +33 -0
- package/bmad/config.toml +69 -0
- package/bmad/config.user.toml +17 -0
- package/bmad/core/config.yaml +9 -0
- package/bmad/core/module-help.csv +13 -0
- package/bmad/custom/config.toml +7 -0
- package/bmad/scripts/resolve_config.py +176 -0
- package/bmad/scripts/resolve_customization.py +230 -0
- package/claude/skills/sa-wds-visual-proposals/SKILL.md +6 -0
- package/package.json +1 -1
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/data/component-mapping.csv +47 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-01-init.md +148 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-01b-continue.md +79 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-02-bridge.md +114 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-02b-kit-warmup.md +96 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-03-scenarios.md +102 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-04-specs.md +97 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-05-prototype.md +102 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/steps/step-06-handoff.md +96 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/templates/product-brief-bridge.md +68 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/templates/proposal-index.md +46 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/templates/trigger-map-bridge.md +54 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/workflow.md +82 -0
- package/siesa-agents/bmm/workflows/3-solutioning/wds-visual-proposals/workflow_ext.md +195 -0
- package/siesa-agents/scripts/phases/phase1.js +42 -4
- package/siesa-agents/scripts/phases/phase2.js +42 -4
- package/siesa-agents/scripts/phases/phase3.js +42 -4
- package/siesa-agents/wds/config.yaml +20 -0
- package/siesa-agents/wds/data/agent-contracts.md +72 -0
- package/siesa-agents/wds/data/agent-guides/freya/agentic-development.md +223 -0
- package/siesa-agents/wds/data/agent-guides/freya/content-creation.md +270 -0
- package/siesa-agents/wds/data/agent-guides/freya/design-system.md +333 -0
- package/siesa-agents/wds/data/agent-guides/freya/meta-content-guide.md +495 -0
- package/siesa-agents/wds/data/agent-guides/freya/specification-quality.md +262 -0
- package/siesa-agents/wds/data/agent-guides/freya/strategic-design.md +116 -0
- package/siesa-agents/wds/data/agent-guides/saga/content-structure-principles.md +190 -0
- package/siesa-agents/wds/data/agent-guides/saga/conversational-followups.md +372 -0
- package/siesa-agents/wds/data/agent-guides/saga/discovery-conversation.md +265 -0
- package/siesa-agents/wds/data/agent-guides/saga/dream-up-approach.md +1034 -0
- package/siesa-agents/wds/data/agent-guides/saga/inspiration-analysis.md +215 -0
- package/siesa-agents/wds/data/agent-guides/saga/resources/project-brief.template.md +187 -0
- package/siesa-agents/wds/data/agent-guides/saga/seo-strategy-guide.md +391 -0
- package/siesa-agents/wds/data/agent-guides/saga/strategic-documentation.md +454 -0
- package/siesa-agents/wds/data/agent-guides/saga/trigger-mapping.md +653 -0
- package/siesa-agents/wds/data/agent-guides/saga/working-with-existing-materials.md +172 -0
- package/siesa-agents/wds/data/design-system/component-boundaries.md +318 -0
- package/siesa-agents/wds/data/design-system/figma-component-structure.md +697 -0
- package/siesa-agents/wds/data/design-system/naming-conventions.md +200 -0
- package/siesa-agents/wds/data/design-system/state-management.md +93 -0
- package/siesa-agents/wds/data/design-system/token-architecture.md +474 -0
- package/siesa-agents/wds/data/design-system/validation-patterns.md +74 -0
- package/siesa-agents/wds/data/presentations/freya-how-i-help.md +63 -0
- package/siesa-agents/wds/data/presentations/freya-intro.md +269 -0
- package/siesa-agents/wds/data/presentations/freya-presentation.md +77 -0
- package/siesa-agents/wds/data/presentations/freya-workflows-guide.md +51 -0
- package/siesa-agents/wds/data/presentations/mimir-agents-overview.md +66 -0
- package/siesa-agents/wds/data/presentations/mimir-tone-setting.md +48 -0
- package/siesa-agents/wds/data/presentations/saga-how-i-help.md +62 -0
- package/siesa-agents/wds/data/presentations/saga-intro.md +285 -0
- package/siesa-agents/wds/data/presentations/saga-presentation.md +74 -0
- package/siesa-agents/wds/data/presentations/saga-workflows-guide.md +48 -0
- package/siesa-agents/wds/data/shared-activation.md +49 -0
- package/siesa-agents/wds/data/wds-glossary.md +98 -0
- package/siesa-agents/wds/module-help.csv +19 -0
- package/siesa-agents/wds/scripts/README.md +155 -0
- package/siesa-agents/wds/scripts/wds-add-object.js +207 -0
- package/siesa-agents/wds/scripts/wds-add-spacing.js +158 -0
- package/siesa-agents/wds/scripts/wds-init-page.js +234 -0
- package/siesa-agents/wds/scripts/wds-init-scenario.js +125 -0
- package/siesa-agents/wds/scripts/wds-nav.js +206 -0
- package/siesa-agents/wds/scripts/wds-validate.js +306 -0
- package/siesa-agents/wds/skills/freya.activation.md +204 -0
- package/siesa-agents/wds/skills/handoff.md +91 -0
- package/siesa-agents/wds/skills/saga.activation.md +169 -0
- package/siesa-agents/wds/skills/shared/git.md +55 -0
- package/siesa-agents/wds/skills/start.md +99 -0
- package/siesa-agents/wds/skills/wrap.md +198 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# WDS Scaffold Scripts
|
|
2
|
+
|
|
3
|
+
Node.js scripts that enforce deterministic output from AI agents. Agents provide content via CLI flags; scripts produce structure.
|
|
4
|
+
|
|
5
|
+
All scripts use only Node.js stdlib (no external dependencies). Run from the project root.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Scripts
|
|
10
|
+
|
|
11
|
+
### `wds-init-scenario.js` — Initialize a scenario
|
|
12
|
+
|
|
13
|
+
Creates the scenario folder and a README index file.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node src/scripts/wds-init-scenario.js \
|
|
17
|
+
--scenario "01 New User Onboarding" \
|
|
18
|
+
--description "New user first visit to account creation"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Output: `C-UX-Scenarios/01-new-user-onboarding/README.md`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### `wds-init-page.js` — Initialize a page spec
|
|
26
|
+
|
|
27
|
+
Creates a new page spec file with all required sections pre-filled with placeholders.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node src/scripts/wds-init-page.js \
|
|
31
|
+
--page "01 Start" \
|
|
32
|
+
--scenario "01 New User Onboarding" \
|
|
33
|
+
--platform "Mobile web" \
|
|
34
|
+
--visibility "Public"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Output:
|
|
38
|
+
- `C-UX-Scenarios/01-new-user-onboarding/01-start/01-start.md`
|
|
39
|
+
- `C-UX-Scenarios/01-new-user-onboarding/01-start/sketches/`
|
|
40
|
+
|
|
41
|
+
After creating all pages in a scenario, run `wds-nav.js` to wire up navigation links.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### `wds-nav.js` — Update navigation links
|
|
46
|
+
|
|
47
|
+
Scans pages in a scenario (sorted by name) and writes correct prev/next navigation rows into each page spec.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# One scenario
|
|
51
|
+
node src/scripts/wds-nav.js --scenario "01 New User Onboarding"
|
|
52
|
+
|
|
53
|
+
# All scenarios
|
|
54
|
+
node src/scripts/wds-nav.js --all
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Run this after adding or removing pages, or after reordering page numbers.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### `wds-add-object.js` — Append an object spec
|
|
62
|
+
|
|
63
|
+
Appends a structured object spec block to a page spec under a named section.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node src/scripts/wds-add-object.js \
|
|
67
|
+
--page "C-UX-Scenarios/01-new-user-onboarding/01-start/01-start.md" \
|
|
68
|
+
--section "Hero" \
|
|
69
|
+
--object "Primary Headline" \
|
|
70
|
+
--component "H1 heading" \
|
|
71
|
+
--se "Välkommen" \
|
|
72
|
+
--en "Welcome" \
|
|
73
|
+
--behavior "Static display"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Object ID is auto-derived: `start-hero-primary-headline`
|
|
77
|
+
|
|
78
|
+
The section heading (`### Section: Hero`) is created if it doesn't already exist.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### `wds-add-spacing.js` — Append a spacing object
|
|
83
|
+
|
|
84
|
+
Appends a spacing notation entry to the `## Spacing` section of a page spec.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
node src/scripts/wds-add-spacing.js \
|
|
88
|
+
--page "C-UX-Scenarios/01-new-user-onboarding/01-start/01-start.md" \
|
|
89
|
+
--direction v \
|
|
90
|
+
--type space \
|
|
91
|
+
--size xl \
|
|
92
|
+
--reason "major section boundary between hero and features"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Valid directions: `v` (vertical), `h` (horizontal)
|
|
96
|
+
Valid types: `space`, `separator`, `line`
|
|
97
|
+
Valid sizes: `zero`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `flex`
|
|
98
|
+
|
|
99
|
+
Spacing ID is auto-derived: `start-v-space-xl`
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### `wds-validate.js` — Validate page specs
|
|
104
|
+
|
|
105
|
+
Checks page spec files for structural correctness.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Single page
|
|
109
|
+
node src/scripts/wds-validate.js \
|
|
110
|
+
--page "C-UX-Scenarios/01-new-user-onboarding/01-start/01-start.md"
|
|
111
|
+
|
|
112
|
+
# All pages in a scenario
|
|
113
|
+
node src/scripts/wds-validate.js --scenario "01 New User Onboarding"
|
|
114
|
+
|
|
115
|
+
# All scenarios
|
|
116
|
+
node src/scripts/wds-validate.js --all
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Validates:
|
|
120
|
+
- Required sections present
|
|
121
|
+
- Object IDs are kebab-case with correct page prefix
|
|
122
|
+
- No duplicate Object IDs
|
|
123
|
+
- Navigation rows (3 expected)
|
|
124
|
+
- Metadata table has all required properties
|
|
125
|
+
- Sketches folder exists
|
|
126
|
+
- SE + EN content present for each object
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## How agents use these scripts
|
|
131
|
+
|
|
132
|
+
1. Agent calls `wds-init-scenario.js` with scenario name and description
|
|
133
|
+
2. Agent calls `wds-init-page.js` for each page in the scenario
|
|
134
|
+
3. Agent calls `wds-nav.js` to wire navigation after all pages exist
|
|
135
|
+
4. Agent calls `wds-add-object.js` for each UI object, providing Swedish and English content
|
|
136
|
+
5. Agent calls `wds-add-spacing.js` for each spacing decision
|
|
137
|
+
6. Agent calls `wds-validate.js` to confirm the spec is structurally correct before handoff
|
|
138
|
+
|
|
139
|
+
The agent never writes raw markdown — it only supplies content as flag values. The scripts own all structural decisions.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## File location convention
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
C-UX-Scenarios/
|
|
147
|
+
{scenario-slug}/
|
|
148
|
+
README.md
|
|
149
|
+
{page-slug}/
|
|
150
|
+
{page-slug}.md
|
|
151
|
+
sketches/
|
|
152
|
+
{page-slug}-concept.jpg
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Example: `C-UX-Scenarios/01-new-user-onboarding/02-signup/02-signup.md`
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// wds-add-object.js — WDS scaffold: append an object spec block to a page spec file
|
|
2
|
+
// Usage: node src/scripts/wds-add-object.js --page "C-UX-Scenarios/01-onboarding/01-start/01-start.md" \
|
|
3
|
+
// --section "Hero" --object "Primary Headline" --component "H1 heading" \
|
|
4
|
+
// --se "Välkommen" --en "Welcome"
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const args = {};
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
if (argv[i].startsWith('--')) {
|
|
15
|
+
const key = argv[i].slice(2);
|
|
16
|
+
const value = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : true;
|
|
17
|
+
args[key] = value;
|
|
18
|
+
if (value !== true) i++;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return args;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toSlug(str) {
|
|
25
|
+
return str
|
|
26
|
+
.normalize('NFD')
|
|
27
|
+
.replace(/\p{Diacritic}/gu, '') // strip accents: á→a, ó→o, ñ→n, ü→u
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replaceAll(/[^a-z0-9]+/g, '-') // any run of non-alphanumeric → single dash
|
|
30
|
+
.replace(/^-+|-+$/g, ''); // trim leading/trailing dashes
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function printUsage() {
|
|
34
|
+
process.stdout.write(
|
|
35
|
+
[
|
|
36
|
+
'Usage: node src/scripts/wds-add-object.js --page <path> --section <name> --object <name> [options]',
|
|
37
|
+
'',
|
|
38
|
+
'Required:',
|
|
39
|
+
' --page Path to the page spec .md file',
|
|
40
|
+
' --section Section name (e.g. "Hero")',
|
|
41
|
+
' --object Object name (e.g. "Primary Headline")',
|
|
42
|
+
'',
|
|
43
|
+
'Optional:',
|
|
44
|
+
' --component Component name (default: "—")',
|
|
45
|
+
' --translation Translation key (auto-derived if omitted)',
|
|
46
|
+
' --se Swedish text content',
|
|
47
|
+
' --en English text content',
|
|
48
|
+
' --behavior Behavior description (e.g. "onClick: submit form")',
|
|
49
|
+
' --component-path Path to component file (default: "—")',
|
|
50
|
+
'',
|
|
51
|
+
].join('\n'),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Derive page slug from file path: "01-start/01-start.md" -> "01-start"
|
|
56
|
+
function pageSlugFromPath(filePath) {
|
|
57
|
+
const base = path.basename(filePath, '.md');
|
|
58
|
+
return base;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Derive Object ID: pageSlug + sectionSlug + objectSlug
|
|
62
|
+
// e.g. page=01-start, section=Hero, object=Primary Headline -> 01-start-hero-primary-headline
|
|
63
|
+
function deriveObjectId(pageSlug, sectionName, objectName) {
|
|
64
|
+
// Strip leading page number from pageSlug for ID prefix
|
|
65
|
+
// "01-start" -> "start", "02-signup" -> "signup"
|
|
66
|
+
const slugParts = pageSlug.split('-');
|
|
67
|
+
const pagePrefix = slugParts.length > 1 ? slugParts.slice(1).join('-') : pageSlug;
|
|
68
|
+
const sectionSlug = toSlug(sectionName);
|
|
69
|
+
const objectSlug = toSlug(objectName);
|
|
70
|
+
return `${pagePrefix}-${sectionSlug}-${objectSlug}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildObjectBlock({ objectName, objectId, component, componentPath, translationKey, se, en, behavior }) {
|
|
74
|
+
const compDisplay = componentPath && componentPath !== '—' ? `[${component}](${componentPath})` : component || '—';
|
|
75
|
+
|
|
76
|
+
const lines = [
|
|
77
|
+
`#### ${objectName}`,
|
|
78
|
+
'',
|
|
79
|
+
`**OBJECT ID:** \`${objectId}\``,
|
|
80
|
+
'',
|
|
81
|
+
'| Property | Value |',
|
|
82
|
+
'|----------|-------|',
|
|
83
|
+
`| Component | ${compDisplay} |`,
|
|
84
|
+
`| Translation Key | \`${translationKey}\` |`,
|
|
85
|
+
`| SE | "${se || '—'}" |`,
|
|
86
|
+
`| EN | "${en || '—'}" |`,
|
|
87
|
+
`| Behavior | ${behavior || '—'} |`,
|
|
88
|
+
'',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Insert content after a section heading. Creates the section heading if it doesn't exist.
|
|
95
|
+
function insertUnderSection(content, sectionHeading, objectBlock) {
|
|
96
|
+
const lines = content.split('\n');
|
|
97
|
+
const headingLine = `### Section: ${sectionHeading}`;
|
|
98
|
+
const headingIdx = lines.findIndex((l) => l.trim() === headingLine);
|
|
99
|
+
|
|
100
|
+
if (headingIdx === -1) {
|
|
101
|
+
// Section doesn't exist — append it before the next ## heading after ## Page Sections
|
|
102
|
+
const pageSectionsIdx = lines.findIndex((l) => l.trim() === '## Page Sections');
|
|
103
|
+
if (pageSectionsIdx === -1) {
|
|
104
|
+
// Just append at end before last nav row
|
|
105
|
+
return content + `\n${headingLine}\n\n${objectBlock}\n`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Find end of ## Page Sections block
|
|
109
|
+
let insertIdx = pageSectionsIdx + 1;
|
|
110
|
+
while (insertIdx < lines.length) {
|
|
111
|
+
const t = lines[insertIdx].trim();
|
|
112
|
+
if (t.startsWith('## ') || t === '---') break;
|
|
113
|
+
insertIdx++;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const before = lines.slice(0, insertIdx);
|
|
117
|
+
const after = lines.slice(insertIdx);
|
|
118
|
+
return [...before, '', headingLine, '', objectBlock, ...after].join('\n');
|
|
119
|
+
} else {
|
|
120
|
+
// Find the end of this section (next ### or ## or end of file)
|
|
121
|
+
let insertIdx = headingIdx + 1;
|
|
122
|
+
// Skip blank lines after heading
|
|
123
|
+
while (insertIdx < lines.length && lines[insertIdx].trim() === '') insertIdx++;
|
|
124
|
+
// Skip comment lines
|
|
125
|
+
while (insertIdx < lines.length && lines[insertIdx].trim().startsWith('<!--')) insertIdx++;
|
|
126
|
+
|
|
127
|
+
// Find end of section
|
|
128
|
+
let endIdx = insertIdx;
|
|
129
|
+
while (endIdx < lines.length) {
|
|
130
|
+
const t = lines[endIdx].trim();
|
|
131
|
+
if (t.startsWith('### ') || t.startsWith('## ') || t === '---') break;
|
|
132
|
+
endIdx++;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Insert object block before end of section
|
|
136
|
+
const before = lines.slice(0, endIdx);
|
|
137
|
+
const after = lines.slice(endIdx);
|
|
138
|
+
return [...before, '', objectBlock, ...after].join('\n');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function main() {
|
|
143
|
+
const args = parseArgs(process.argv.slice(2));
|
|
144
|
+
|
|
145
|
+
if (args.help) {
|
|
146
|
+
printUsage();
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!args.page || !args.section || !args.object) {
|
|
151
|
+
process.stderr.write('Error: --page, --section, and --object are required.\n\n');
|
|
152
|
+
printUsage();
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const filePath = path.resolve(args.page);
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(filePath)) {
|
|
159
|
+
process.stderr.write(`Error: File not found: ${filePath}\n`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const pageSlug = pageSlugFromPath(filePath);
|
|
164
|
+
const objectId = deriveObjectId(pageSlug, args.section, args.object);
|
|
165
|
+
|
|
166
|
+
// Auto-derive translation key from objectId
|
|
167
|
+
const translationKey = args.translation || objectId.replaceAll('-', '.');
|
|
168
|
+
|
|
169
|
+
const objectBlock = buildObjectBlock({
|
|
170
|
+
objectName: args.object,
|
|
171
|
+
objectId,
|
|
172
|
+
component: args.component || '—',
|
|
173
|
+
componentPath: args['component-path'] || '—',
|
|
174
|
+
translationKey,
|
|
175
|
+
se: args.se || '',
|
|
176
|
+
en: args.en || '',
|
|
177
|
+
behavior: args.behavior || '—',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
let content;
|
|
181
|
+
try {
|
|
182
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
183
|
+
} catch (error) {
|
|
184
|
+
process.stderr.write(`Error reading file: ${error.message}\n`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for duplicate object ID
|
|
189
|
+
if (content.includes(`\`${objectId}\``)) {
|
|
190
|
+
process.stderr.write(`Error: Object ID already exists in file: ${objectId}\n`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const updated = insertUnderSection(content, args.section, objectBlock);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
fs.writeFileSync(filePath, updated, 'utf8');
|
|
198
|
+
} catch (error) {
|
|
199
|
+
process.stderr.write(`Error writing file: ${error.message}\n`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
process.stdout.write(`✓ Added object ${objectId}\n`);
|
|
204
|
+
process.stdout.write(` File: ${filePath}\n`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
main();
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// wds-add-spacing.js — WDS scaffold: append a spacing object to a page spec file
|
|
2
|
+
// Usage: node src/scripts/wds-add-spacing.js --page "C-UX-Scenarios/01-onboarding/01-start/01-start.md" \
|
|
3
|
+
// --direction v --type space --size xl --reason "major section boundary between hero and features"
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const args = {};
|
|
12
|
+
for (let i = 0; i < argv.length; i++) {
|
|
13
|
+
if (argv[i].startsWith('--')) {
|
|
14
|
+
const key = argv[i].slice(2);
|
|
15
|
+
const value = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : true;
|
|
16
|
+
args[key] = value;
|
|
17
|
+
if (value !== true) i++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return args;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function printUsage() {
|
|
24
|
+
process.stdout.write(
|
|
25
|
+
[
|
|
26
|
+
'Usage: node src/scripts/wds-add-spacing.js --page <path> --direction <v|h> --type <type> --size <size> [options]',
|
|
27
|
+
'',
|
|
28
|
+
'Required:',
|
|
29
|
+
' --page Path to the page spec .md file',
|
|
30
|
+
' --direction v (vertical) or h (horizontal)',
|
|
31
|
+
' --type space | separator | line',
|
|
32
|
+
' --size zero | sm | md | lg | xl | 2xl | 3xl | flex',
|
|
33
|
+
'',
|
|
34
|
+
'Optional:',
|
|
35
|
+
' --reason Why this spacing exists',
|
|
36
|
+
'',
|
|
37
|
+
'Valid directions: v, h',
|
|
38
|
+
'Valid types: space, separator, line',
|
|
39
|
+
'Valid sizes: zero, sm, md, lg, xl, 2xl, 3xl, flex',
|
|
40
|
+
'',
|
|
41
|
+
].join('\n'),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const VALID_DIRECTIONS = ['v', 'h'];
|
|
46
|
+
const VALID_TYPES = ['space', 'separator', 'line'];
|
|
47
|
+
const VALID_SIZES = ['zero', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 'flex'];
|
|
48
|
+
|
|
49
|
+
// Derive page prefix from slug: "01-start" -> "start"
|
|
50
|
+
function pagePrefix(slug) {
|
|
51
|
+
const parts = slug.split('-');
|
|
52
|
+
return parts.length > 1 ? parts.slice(1).join('-') : slug;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pageSlugFromPath(filePath) {
|
|
56
|
+
return path.basename(filePath, '.md');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildSpacingBlock(spacingId, reason) {
|
|
60
|
+
const icon = '↕';
|
|
61
|
+
const reasonText = reason ? ` — ${reason}` : '';
|
|
62
|
+
return `#### ${icon} \`${spacingId}\`${reasonText}\n`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function appendToSpacingSection(content, spacingBlock) {
|
|
66
|
+
const lines = content.split('\n');
|
|
67
|
+
const spacingIdx = lines.findIndex((l) => l.trim() === '## Spacing');
|
|
68
|
+
|
|
69
|
+
if (spacingIdx === -1) {
|
|
70
|
+
// No spacing section — append before first ## after metadata
|
|
71
|
+
return content + `\n## Spacing\n\n${spacingBlock}\n`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Find end of spacing section (next ## or ---)
|
|
75
|
+
let endIdx = spacingIdx + 1;
|
|
76
|
+
while (endIdx < lines.length) {
|
|
77
|
+
const t = lines[endIdx].trim();
|
|
78
|
+
if ((t.startsWith('## ') && t !== '## Spacing') || t === '---') break;
|
|
79
|
+
endIdx++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Insert just before the end marker
|
|
83
|
+
const before = lines.slice(0, endIdx);
|
|
84
|
+
const after = lines.slice(endIdx);
|
|
85
|
+
return [...before, spacingBlock, ...after].join('\n');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function main() {
|
|
89
|
+
const args = parseArgs(process.argv.slice(2));
|
|
90
|
+
|
|
91
|
+
if (args.help) {
|
|
92
|
+
printUsage();
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!args.page || !args.direction || !args.type || args.size === 0) {
|
|
97
|
+
process.stderr.write('Error: --page, --direction, --type, and --size are required.\n\n');
|
|
98
|
+
printUsage();
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!VALID_DIRECTIONS.includes(args.direction)) {
|
|
103
|
+
process.stderr.write(`Error: Invalid direction "${args.direction}". Must be: ${VALID_DIRECTIONS.join(', ')}\n`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!VALID_TYPES.includes(args.type)) {
|
|
108
|
+
process.stderr.write(`Error: Invalid type "${args.type}". Must be: ${VALID_TYPES.join(', ')}\n`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!VALID_SIZES.includes(args.size)) {
|
|
113
|
+
process.stderr.write(`Error: Invalid size "${args.size}". Must be: ${VALID_SIZES.join(', ')}\n`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const filePath = path.resolve(args.page);
|
|
118
|
+
|
|
119
|
+
if (!fs.existsSync(filePath)) {
|
|
120
|
+
process.stderr.write(`Error: File not found: ${filePath}\n`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const slug = pageSlugFromPath(filePath);
|
|
125
|
+
const prefix = pagePrefix(slug);
|
|
126
|
+
const spacingId = `${prefix}-${args.direction}-${args.type}-${args.size}`;
|
|
127
|
+
const reason = args.reason || '';
|
|
128
|
+
|
|
129
|
+
let content;
|
|
130
|
+
try {
|
|
131
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
132
|
+
} catch (error) {
|
|
133
|
+
process.stderr.write(`Error reading file: ${error.message}\n`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check for duplicate spacing ID
|
|
138
|
+
if (content.includes(`\`${spacingId}\``)) {
|
|
139
|
+
process.stderr.write(`Error: Spacing ID already exists in file: ${spacingId}\n`);
|
|
140
|
+
process.stderr.write('Use a different combination of direction/type/size or manually edit the file.\n');
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const spacingBlock = buildSpacingBlock(spacingId, reason);
|
|
145
|
+
const updated = appendToSpacingSection(content, spacingBlock);
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
fs.writeFileSync(filePath, updated, 'utf8');
|
|
149
|
+
} catch (error) {
|
|
150
|
+
process.stderr.write(`Error writing file: ${error.message}\n`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
process.stdout.write(`✓ Added spacing object ${spacingId}\n`);
|
|
155
|
+
process.stdout.write(` File: ${filePath}\n`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
main();
|