@bastani/atomic 0.5.25-0 → 0.5.26-0
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/.agents/skills/ado-commit/SKILL.md +92 -0
- package/.agents/skills/ado-create-pr/SKILL.md +209 -0
- package/.claude/settings.json +1 -0
- package/.mcp.json +5 -0
- package/.opencode/opencode.json +7 -1
- package/README.md +150 -116
- package/assets/settings.schema.json +2 -2
- package/dist/sdk/runtime/executor.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts +46 -0
- package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts +34 -0
- package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts +72 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts +46 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts +32 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts +33 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts +106 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts +50 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts +12 -0
- package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts.map +1 -0
- package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts +36 -0
- package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts.map +1 -0
- package/dist/services/config/atomic-config.d.ts +6 -0
- package/dist/services/config/atomic-config.d.ts.map +1 -1
- package/dist/services/config/scm-sync.d.ts +37 -0
- package/dist/services/config/scm-sync.d.ts.map +1 -0
- package/package.json +7 -7
- package/src/cli.ts +2 -2
- package/src/commands/cli/chat/index.ts +8 -1
- package/src/commands/cli/config.ts +34 -10
- package/src/commands/cli/init/index.ts +9 -0
- package/src/sdk/runtime/executor.ts +13 -0
- package/src/sdk/workflows/builtin/open-claude-design/claude/index.ts +499 -0
- package/src/sdk/workflows/builtin/open-claude-design/copilot/index.ts +507 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/constants.ts +159 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/design-system.ts +88 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/export.ts +193 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/import.ts +52 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/prompts.ts +1110 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/scan.ts +117 -0
- package/src/sdk/workflows/builtin/open-claude-design/helpers/validation.ts +38 -0
- package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +570 -0
- package/src/services/config/atomic-config.ts +12 -0
- package/src/services/config/scm-sync.ts +174 -0
- package/src/services/config/settings.ts +19 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design.md persistence — loading, saving, and reading design system data.
|
|
3
|
+
*
|
|
4
|
+
* The Design.md file is a structured markdown document containing design
|
|
5
|
+
* tokens (colors, typography, spacing, components) extracted from the
|
|
6
|
+
* codebase and approved by the user via HIL. It serves as the single
|
|
7
|
+
* source of truth for all generation and refinement stages.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { DESIGN_SYSTEM_FILENAME, IMPECCABLE_FILENAME } from "./constants.ts";
|
|
13
|
+
|
|
14
|
+
/** Structured representation of the design system data. */
|
|
15
|
+
export interface DesignSystemData {
|
|
16
|
+
/** Raw markdown content of Design.md. */
|
|
17
|
+
raw: string;
|
|
18
|
+
/** Absolute path to the Design.md file. */
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Load an existing Design.md from the given path.
|
|
24
|
+
* Used when the user provides `--design-system=<path>`.
|
|
25
|
+
*/
|
|
26
|
+
export async function loadDesignSystem(
|
|
27
|
+
designSystemPath: string,
|
|
28
|
+
): Promise<DesignSystemData> {
|
|
29
|
+
const resolved = path.resolve(designSystemPath);
|
|
30
|
+
const raw = await readFile(resolved, "utf-8");
|
|
31
|
+
return { raw, path: resolved };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Persist the design system builder's output as Design.md in the project root.
|
|
36
|
+
*
|
|
37
|
+
* The builder stage produces a visible session where the agent constructs
|
|
38
|
+
* the Design.md content interactively with the user. We read the transcript
|
|
39
|
+
* and extract the final Design.md content that the agent wrote.
|
|
40
|
+
*
|
|
41
|
+
* For simplicity, the agent in the builder stage is instructed to write
|
|
42
|
+
* Design.md directly to disk. This function reads it back to populate
|
|
43
|
+
* the DesignSystemData for downstream stages.
|
|
44
|
+
*/
|
|
45
|
+
export async function persistDesignSystem(
|
|
46
|
+
root: string,
|
|
47
|
+
): Promise<DesignSystemData> {
|
|
48
|
+
const designPath = path.join(root, DESIGN_SYSTEM_FILENAME);
|
|
49
|
+
const raw = await readFile(designPath, "utf-8");
|
|
50
|
+
return { raw, path: designPath };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read the existing .impeccable.md brand context file, if present.
|
|
55
|
+
* Returns empty string if the file does not exist.
|
|
56
|
+
*/
|
|
57
|
+
export async function readImpeccableMd(root: string): Promise<string> {
|
|
58
|
+
try {
|
|
59
|
+
return await readFile(path.join(root, IMPECCABLE_FILENAME), "utf-8");
|
|
60
|
+
} catch {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Derive a URL-safe slug from the user's prompt for directory naming.
|
|
67
|
+
*/
|
|
68
|
+
export function slugifyPrompt(prompt: string): string {
|
|
69
|
+
const slug = prompt
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
72
|
+
.split(/\s+/)
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.slice(0, 6)
|
|
75
|
+
.join("-")
|
|
76
|
+
.substring(0, 60);
|
|
77
|
+
return slug || "design";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Ensure the scratch directory exists for intermediate workflow outputs.
|
|
82
|
+
*/
|
|
83
|
+
export async function ensureScratchDir(root: string): Promise<string> {
|
|
84
|
+
const { mkdir } = await import("node:fs/promises");
|
|
85
|
+
const scratchDir = path.join(root, "research", "designs", ".scratch");
|
|
86
|
+
await mkdir(scratchDir, { recursive: true });
|
|
87
|
+
return scratchDir;
|
|
88
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export engine — handoff bundle packaging.
|
|
3
|
+
*
|
|
4
|
+
* Writes the deterministic parts of the handoff bundle (no LLM call):
|
|
5
|
+
* - handoff-prompt.md — ready-to-use prompt for Claude Code
|
|
6
|
+
* - README.md — how to use the bundle
|
|
7
|
+
*
|
|
8
|
+
* The exporter stage (LLM) handles copying design files, writing
|
|
9
|
+
* design-intent.md and component-specs.md since those require
|
|
10
|
+
* understanding the generated design content.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { writeFile, mkdir, copyFile } from "node:fs/promises";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import type { DesignSystemData } from "./design-system.ts";
|
|
16
|
+
|
|
17
|
+
export interface HandoffBundleOptions {
|
|
18
|
+
designSystem: DesignSystemData;
|
|
19
|
+
prompt: string;
|
|
20
|
+
outputType: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Write the deterministic handoff bundle files.
|
|
25
|
+
*
|
|
26
|
+
* The exporter stage is responsible for:
|
|
27
|
+
* - Copying design files (index.html, styles.css, script.js) to design/
|
|
28
|
+
* - Writing design-intent.md from transcript analysis
|
|
29
|
+
* - Writing component-specs.md from design analysis
|
|
30
|
+
*
|
|
31
|
+
* This function writes the remaining deterministic files:
|
|
32
|
+
* - Design.md (copy of the design system)
|
|
33
|
+
* - handoff-prompt.md (ready-to-use implementation prompt)
|
|
34
|
+
* - README.md (bundle documentation)
|
|
35
|
+
*/
|
|
36
|
+
export async function writeHandoffBundle(
|
|
37
|
+
finalDesignDir: string,
|
|
38
|
+
opts: HandoffBundleOptions,
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
await mkdir(finalDesignDir, { recursive: true });
|
|
41
|
+
await mkdir(path.join(finalDesignDir, "design"), { recursive: true });
|
|
42
|
+
|
|
43
|
+
// Copy Design.md
|
|
44
|
+
await copyFile(
|
|
45
|
+
opts.designSystem.path,
|
|
46
|
+
path.join(finalDesignDir, "Design.md"),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Write handoff-prompt.md
|
|
50
|
+
const handoffPrompt = buildHandoffPrompt(opts);
|
|
51
|
+
await writeFile(
|
|
52
|
+
path.join(finalDesignDir, "handoff-prompt.md"),
|
|
53
|
+
handoffPrompt,
|
|
54
|
+
"utf-8",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Write README.md
|
|
58
|
+
const readme = buildReadme(opts);
|
|
59
|
+
await writeFile(
|
|
60
|
+
path.join(finalDesignDir, "README.md"),
|
|
61
|
+
readme,
|
|
62
|
+
"utf-8",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildHandoffPrompt(opts: HandoffBundleOptions): string {
|
|
67
|
+
return `# Design Handoff — Implementation Prompt
|
|
68
|
+
|
|
69
|
+
Use this prompt with Claude Code to implement the design in this bundle.
|
|
70
|
+
|
|
71
|
+
## Original Design Request
|
|
72
|
+
|
|
73
|
+
${opts.prompt}
|
|
74
|
+
|
|
75
|
+
## Output Type
|
|
76
|
+
|
|
77
|
+
${opts.outputType}
|
|
78
|
+
|
|
79
|
+
## Instructions
|
|
80
|
+
|
|
81
|
+
1. Read \`Design.md\` for the complete design system (colors, typography, spacing, components).
|
|
82
|
+
2. Open \`design/index.html\` in a browser to see the reference design.
|
|
83
|
+
3. Read \`design-intent.md\` for the design rationale and key decisions.
|
|
84
|
+
4. Read \`component-specs.md\` for individual component specifications.
|
|
85
|
+
5. Implement the design using the design system tokens from Design.md.
|
|
86
|
+
6. Match the reference design in \`design/\` as closely as possible.
|
|
87
|
+
7. Use semantic HTML, CSS custom properties for tokens, and progressive enhancement.
|
|
88
|
+
|
|
89
|
+
## Design System Tokens
|
|
90
|
+
|
|
91
|
+
The design tokens are defined in \`Design.md\`. Use CSS custom properties:
|
|
92
|
+
|
|
93
|
+
\`\`\`css
|
|
94
|
+
:root {
|
|
95
|
+
/* Import all tokens from Design.md as custom properties */
|
|
96
|
+
}
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
## Quality Checklist
|
|
100
|
+
|
|
101
|
+
- [ ] All colors match Design.md tokens
|
|
102
|
+
- [ ] Typography follows the defined scale
|
|
103
|
+
- [ ] Spacing uses the defined base unit
|
|
104
|
+
- [ ] Components match the identified patterns
|
|
105
|
+
- [ ] No AI slop anti-patterns (gradient text, side-stripe borders, reflex fonts)
|
|
106
|
+
- [ ] Responsive at common breakpoints
|
|
107
|
+
- [ ] Accessible (WCAG 2.1 AA)
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildReadme(opts: HandoffBundleOptions): string {
|
|
112
|
+
const extraFileRows = buildExtraFileRows(opts.outputType);
|
|
113
|
+
const previewSection = buildPreviewSection(opts.outputType);
|
|
114
|
+
|
|
115
|
+
return `# Design Handoff Bundle
|
|
116
|
+
|
|
117
|
+
Generated by the \`open-claude-design\` workflow.
|
|
118
|
+
|
|
119
|
+
Output type: **${opts.outputType}**
|
|
120
|
+
|
|
121
|
+
## Contents
|
|
122
|
+
|
|
123
|
+
| File | Description |
|
|
124
|
+
|------|-------------|
|
|
125
|
+
| \`design/index.html\` | Generated HTML |
|
|
126
|
+
| \`design/styles.css\` | Generated CSS styles |
|
|
127
|
+
| \`design/script.js\` | Generated JavaScript |${extraFileRows}
|
|
128
|
+
| \`Design.md\` | Design system tokens (colors, typography, spacing) |
|
|
129
|
+
| \`design-intent.md\` | Design rationale and key decisions |
|
|
130
|
+
| \`component-specs.md\` | Component / page / wireframe / component-anatomy specs |
|
|
131
|
+
| \`handoff-prompt.md\` | Ready-to-use prompt for Claude Code |
|
|
132
|
+
|
|
133
|
+
## Usage
|
|
134
|
+
|
|
135
|
+
${previewSection}
|
|
136
|
+
|
|
137
|
+
### Implement with Claude Code
|
|
138
|
+
|
|
139
|
+
\`\`\`bash
|
|
140
|
+
# From the project root, run:
|
|
141
|
+
claude "$(cat ${opts.outputType === "component" ? "component-specs.md" : "handoff-prompt.md"})"
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
### Design System Reference
|
|
145
|
+
|
|
146
|
+
All design tokens are in \`Design.md\`. Use these tokens when implementing
|
|
147
|
+
to ensure visual consistency with the generated design.
|
|
148
|
+
|
|
149
|
+
## Original Request
|
|
150
|
+
|
|
151
|
+
${opts.prompt}
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildExtraFileRows(outputType: string): string {
|
|
156
|
+
if (outputType === "prototype") {
|
|
157
|
+
return `
|
|
158
|
+
| \`design/server.ts\` | Zero-dependency Bun static-file server (port 4173) |
|
|
159
|
+
| \`design/package.json\` | Defines the \`start\` script |`;
|
|
160
|
+
}
|
|
161
|
+
if (outputType === "component") {
|
|
162
|
+
return `
|
|
163
|
+
| \`design/snippet.html\` | Isolated component markup for copy-paste into a host app |`;
|
|
164
|
+
}
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildPreviewSection(outputType: string): string {
|
|
169
|
+
if (outputType === "prototype") {
|
|
170
|
+
return `### Preview the Prototype
|
|
171
|
+
|
|
172
|
+
The prototype ships with a runnable Bun server.
|
|
173
|
+
|
|
174
|
+
\`\`\`bash
|
|
175
|
+
cd design
|
|
176
|
+
bun run start
|
|
177
|
+
# → Prototype running at http://localhost:4173
|
|
178
|
+
\`\`\`
|
|
179
|
+
|
|
180
|
+
Override the port with \`PORT=3000 bun run start\` if 4173 is taken.`;
|
|
181
|
+
}
|
|
182
|
+
if (outputType === "component") {
|
|
183
|
+
return `### Preview the Component
|
|
184
|
+
|
|
185
|
+
Open \`design/index.html\` in a browser to view the component showcase
|
|
186
|
+
(all variants and states). Use \`design/snippet.html\` as a minimal
|
|
187
|
+
copy-paste target for dropping the component into a host application.`;
|
|
188
|
+
}
|
|
189
|
+
// page, wireframe, and any other future single-view output types
|
|
190
|
+
return `### Preview the Design
|
|
191
|
+
|
|
192
|
+
Open \`design/index.html\` directly in a browser — no server required.`;
|
|
193
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import handler — input type detection and aggregation.
|
|
3
|
+
*
|
|
4
|
+
* Classifies the user's `--reference` input as a URL, file path, or
|
|
5
|
+
* neither, and aggregates all import sources into a structured context
|
|
6
|
+
* object that the generator consumes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Aggregated import context passed to the generator stage. */
|
|
10
|
+
export interface ImportContext {
|
|
11
|
+
/** The user's free-form design prompt. */
|
|
12
|
+
prompt: string;
|
|
13
|
+
/** Raw reference string (URL, file path, or empty). */
|
|
14
|
+
reference: string;
|
|
15
|
+
/** Extracted content from a web URL capture, or null. */
|
|
16
|
+
webCapture: string | null;
|
|
17
|
+
/** Extracted content from a parsed file, or null. */
|
|
18
|
+
fileParse: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Check whether the reference string looks like a URL. */
|
|
22
|
+
export function isUrl(reference: string): boolean {
|
|
23
|
+
if (!reference.trim()) return false;
|
|
24
|
+
return /^https?:\/\//i.test(reference.trim());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Check whether the reference string looks like a file path. */
|
|
28
|
+
export function isFilePath(reference: string): boolean {
|
|
29
|
+
const trimmed = reference.trim();
|
|
30
|
+
if (!trimmed) return false;
|
|
31
|
+
if (isUrl(trimmed)) return false;
|
|
32
|
+
// Looks like a path: starts with /, ./, ~/, or contains file extension
|
|
33
|
+
return /^[./~]/.test(trimmed) || /\.\w{1,6}$/.test(trimmed);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Aggregate all import results into a single context object.
|
|
38
|
+
* Pure deterministic function — no LLM call.
|
|
39
|
+
*/
|
|
40
|
+
export function aggregateImportResults(opts: {
|
|
41
|
+
prompt: string;
|
|
42
|
+
reference: string;
|
|
43
|
+
webCapture: string | null;
|
|
44
|
+
fileParse: string | null;
|
|
45
|
+
}): ImportContext {
|
|
46
|
+
return {
|
|
47
|
+
prompt: opts.prompt,
|
|
48
|
+
reference: opts.reference,
|
|
49
|
+
webCapture: opts.webCapture,
|
|
50
|
+
fileParse: opts.fileParse,
|
|
51
|
+
};
|
|
52
|
+
}
|