@dittowords/spec-cli 0.0.1-alpha.0 → 0.0.1-alpha.1

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
@@ -92,6 +92,16 @@ First-time setup. Creates `dittospec.config.json` and `workspace.ditto.md` in th
92
92
 
93
93
  Use `--agent` to also write agent configuration directly: appends a Ditto Content Specs section to `CLAUDE.md` or `.cursorrules` (creates the file if absent).
94
94
 
95
+ ### `ditto-spec scaffold <ComponentName>`
96
+
97
+ Creates a new `index.ditto.md` for a component with the correct YAML structure and empty managed keys.
98
+
99
+ ```
100
+ ditto-spec scaffold DialogueModal --path src/components/DialogueModal
101
+ ```
102
+
103
+ Use `--path <dir>` to specify where the file is created (defaults to the current directory). After scaffolding, add surfaces for the component's text-bearing props and run `ditto-spec pull` to populate rules.
104
+
95
105
  ### `ditto-spec pull`
96
106
 
97
107
  Syncs rules from the platform into spec files.
@@ -143,6 +153,26 @@ When writing or editing text props for a component:
143
153
 
144
154
  ### Creating specs
145
155
 
146
- When scaffolding a new design-system component with text-bearing props, create an `index.ditto.md` alongside it. Declare a surface for each prop that holds user-facing copy. Tag surfaces with relevant content categories (e.g., `heading`, `body`, `button`, `cta`) so platform rules propagate on the next `pull`.
156
+ When creating a new component with text-bearing props, scaffold a spec file:
157
+
158
+ ```
159
+ npx ditto-spec scaffold <ComponentName> --path <dir>
160
+ ```
161
+
162
+ Then edit the generated `index.ditto.md` to add surfaces — one entry per text-bearing prop:
163
+
164
+ ```yaml
165
+ surfaces:
166
+ title:
167
+ tags: [heading]
168
+ maxLength: 60
169
+ $children:
170
+ tags: [button, cta]
171
+ maxLength: 30
172
+ ```
173
+
174
+ - Use `$children` for text via children. Use dot notation for nested props (`primaryAction.label`).
175
+ - Choose `tags` from content categories like `heading`, `body`, `button`, `cta`, `dialog-title`, `call-to-action`.
176
+ - **Never write `rules` or `examples` by hand.** Run `ditto-spec pull` after adding surfaces to populate rules from the platform.
147
177
 
148
178
  If a component lacks a spec and you'd have found one useful, propose creating one. If a spec lacks a rule you'd have wanted, propose adding the rule to the platform style guide with appropriate tags.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dittowords/spec-cli",
3
- "version": "0.0.1-alpha.0",
3
+ "version": "0.0.1-alpha.1",
4
4
  "description": "CLI for syncing .ditto.md content specs with the Ditto platform.",
5
5
  "main": "src/cli.ts",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -2,12 +2,23 @@ import { check } from "./commands/check";
2
2
  import { init } from "./commands/init";
3
3
  import { list } from "./commands/list";
4
4
  import { pull } from "./commands/pull";
5
+ import { scaffold } from "./commands/scaffold";
5
6
 
6
7
  const COMMANDS: Record<string, (args: string[]) => Promise<void>> = {
7
8
  init: async (args) => init({ writeAgent: args.includes("--agent") }),
8
9
  pull: async (args) => pull({ dryRun: args.includes("--dry-run") }),
9
10
  check: async () => check(),
10
11
  list: async () => list(),
12
+ scaffold: async (args) => {
13
+ const name = args.find((a) => !a.startsWith("--"));
14
+ if (!name) {
15
+ process.stderr.write("Usage: ditto-spec scaffold <ComponentName> [--path <dir>]\n");
16
+ process.exit(2);
17
+ }
18
+ const pathIdx = args.indexOf("--path");
19
+ const targetDir = pathIdx !== -1 && args[pathIdx + 1] ? args[pathIdx + 1] : process.cwd();
20
+ return scaffold({ componentName: name, targetDir });
21
+ },
11
22
  };
12
23
 
13
24
  const HELP = `ditto-spec — sync .ditto.md content specs with the Ditto platform.
@@ -18,6 +29,8 @@ Usage:
18
29
  Commands:
19
30
  init Set up ditto specs: creates config, workspace spec, and prints agent setup.
20
31
  init --agent Also writes agent configuration (CLAUDE.md, .cursorrules, etc.).
32
+ scaffold <Name> Create a new index.ditto.md for a component.
33
+ scaffold <Name> --path <dir> Create the spec in a specific directory.
21
34
  pull Pull rules from the platform into the managed keys of each spec.
22
35
  pull --dry-run Show which files would change without writing.
23
36
  check Parse every spec file; exit non-zero on any malformed file.
@@ -26,7 +26,9 @@ rules: []
26
26
  const CLAUDE_MD_BLOCK = `
27
27
  ## Ditto Content Specs
28
28
 
29
- This project uses \`.ditto.md\` files for component content governance.
29
+ This project uses \`.ditto.md\` files to declare text surfaces on components. Rules are synced from the Ditto platform — never written by hand.
30
+
31
+ ### Reading specs
30
32
 
31
33
  When writing or editing text props for a component:
32
34
 
@@ -34,15 +36,36 @@ When writing or editing text props for a component:
34
36
  2. Check for \`index.ditto.md\` next to the component for surface-specific rules and constraints.
35
37
  3. Respect \`maxLength\` — it's a layout constraint, not a suggestion.
36
38
  4. Follow all rules in the \`rules\` key. Rules without a \`surface\` field apply to every surface; rules with \`surface\` apply only to that surface.
37
- 5. When creating a new component with text-bearing props, scaffold an \`index.ditto.md\` alongside it.
38
39
 
39
- Run \`ditto-spec list\` to see all specs, or read the [spec format docs](./packages/ditto-spec-cli/README.md).
40
+ ### Creating specs
41
+
42
+ When creating a new component with text-bearing props, scaffold a spec file:
43
+
44
+ npx ditto-spec scaffold <ComponentName> --path <dir>
45
+
46
+ Then edit the generated \`index.ditto.md\` to add surfaces — one entry per text-bearing prop:
47
+
48
+ surfaces:
49
+ title:
50
+ tags: [heading]
51
+ maxLength: 60
52
+ $children:
53
+ tags: [button, cta]
54
+ maxLength: 30
55
+
56
+ - Use \`$children\` for text via children. Use dot notation for nested props (\`primaryAction.label\`).
57
+ - Choose \`tags\` from content categories like \`heading\`, \`body\`, \`button\`, \`cta\`, \`dialog-title\`, \`call-to-action\`.
58
+ - **Never write \`rules\` or \`examples\` by hand.** Run \`npx ditto-spec pull\` after adding surfaces to populate rules from the platform.
59
+
60
+ Run \`npx ditto-spec list\` to see all existing specs.
40
61
  `;
41
62
 
42
63
  const CURSOR_RULES_BLOCK = `
43
64
  # Ditto Content Specs
44
65
 
45
- This project uses .ditto.md files for component content governance.
66
+ This project uses .ditto.md files to declare text surfaces on components. Rules are synced from the Ditto platform — never written by hand.
67
+
68
+ ## Reading specs
46
69
 
47
70
  When writing or editing text props for a component:
48
71
 
@@ -50,7 +73,28 @@ When writing or editing text props for a component:
50
73
  2. Check for index.ditto.md next to the component for surface-specific rules and constraints.
51
74
  3. Respect maxLength — it's a layout constraint, not a suggestion.
52
75
  4. Follow all rules in the rules key. Rules without a surface field apply to every surface; rules with surface apply only to that surface.
53
- 5. When creating a new component with text-bearing props, scaffold an index.ditto.md alongside it.
76
+
77
+ ## Creating specs
78
+
79
+ When creating a new component with text-bearing props, scaffold a spec file:
80
+
81
+ npx ditto-spec scaffold <ComponentName> --path <dir>
82
+
83
+ Then edit the generated index.ditto.md to add surfaces — one entry per text-bearing prop:
84
+
85
+ surfaces:
86
+ title:
87
+ tags: [heading]
88
+ maxLength: 60
89
+ $children:
90
+ tags: [button, cta]
91
+ maxLength: 30
92
+
93
+ - Use $children for text via children. Use dot notation for nested props (primaryAction.label).
94
+ - Choose tags from content categories like heading, body, button, cta, dialog-title, call-to-action.
95
+ - NEVER write rules or examples by hand. Run npx ditto-spec pull after adding surfaces to populate rules from the platform.
96
+
97
+ Run npx ditto-spec list to see all existing specs.
54
98
  `;
55
99
 
56
100
  const AGENTS_MD_ROW =
@@ -0,0 +1,40 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ const SPEC_FILENAME = "index.ditto.md";
5
+
6
+ interface ScaffoldOptions {
7
+ componentName: string;
8
+ targetDir: string;
9
+ }
10
+
11
+ export async function scaffold(opts: ScaffoldOptions): Promise<void> {
12
+ const dest = path.join(opts.targetDir, SPEC_FILENAME);
13
+
14
+ if (fs.existsSync(dest)) {
15
+ console.log(`${SPEC_FILENAME} already exists at ${opts.targetDir}. Nothing to do.`);
16
+ return;
17
+ }
18
+
19
+ const content = `---
20
+ component: ${opts.componentName}
21
+ description: >
22
+ TODO: describe this component.
23
+ tags: []
24
+ surfaces: {}
25
+ # Managed by Ditto — do not edit below
26
+ rules: []
27
+ examples: []
28
+ ---
29
+ `;
30
+
31
+ fs.mkdirSync(opts.targetDir, { recursive: true });
32
+ fs.writeFileSync(dest, content);
33
+ console.log(`✓ Created ${path.relative(process.cwd(), dest)}`);
34
+
35
+ console.log(`
36
+ Next steps:
37
+ 1. Add text-bearing props as surface keys under 'surfaces'
38
+ 2. Tag each surface with content categories (heading, body, button, cta, etc.)
39
+ 3. Run \`ditto-spec pull\` to populate rules from the platform`);
40
+ }