@structured-vibe-coding/core 0.9.0 → 0.10.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/README.md CHANGED
@@ -2,10 +2,7 @@
2
2
 
3
3
  Core engine for **Structured Vibe Coding** — local-first Spec-Driven Development.
4
4
 
5
- Used by:
6
-
7
- - `@structured-vibe-coding/cli` (`sdd` command)
8
- - VS Code / Cursor extension (`packages/vscode`)
5
+ Used by `@structured-vibe-coding/cli` (`sdd` command).
9
6
 
10
7
  ## Install
11
8
 
@@ -19,7 +19,10 @@ import {
19
19
  removeOtherAgentHosts,
20
20
  renderThinAgent,
21
21
  writeAgentHandoff
22
- } from "./chunk-M245MC6E.js";
22
+ } from "./chunk-UAOFNSLQ.js";
23
+ import "./chunk-KZHNYGD6.js";
24
+ import "./chunk-7MJYPJMR.js";
25
+ import "./chunk-EPFURATC.js";
23
26
  export {
24
27
  AGENT_INTEGRATIONS,
25
28
  AGENT_TARGET_OPTIONS,
@@ -0,0 +1,51 @@
1
+ // src/paths.ts
2
+ import { join } from "pathe";
3
+ var SDD_DIR = ".sdd";
4
+ function sddRoot(projectRoot) {
5
+ return join(projectRoot, SDD_DIR);
6
+ }
7
+ function configPath(projectRoot) {
8
+ return join(sddRoot(projectRoot), "config.yaml");
9
+ }
10
+ function workflowsDir(projectRoot) {
11
+ return join(sddRoot(projectRoot), "workflows");
12
+ }
13
+ function templatesDir(projectRoot) {
14
+ return join(sddRoot(projectRoot), "templates");
15
+ }
16
+ function memoryDir(projectRoot, config) {
17
+ return join(projectRoot, config.memory_path);
18
+ }
19
+ function changesDir(projectRoot, config) {
20
+ return join(projectRoot, config.changes_path);
21
+ }
22
+ function archiveDir(projectRoot, config) {
23
+ return join(projectRoot, config.archive_path);
24
+ }
25
+ function domainsDir(projectRoot, config) {
26
+ return join(projectRoot, config.domains_path);
27
+ }
28
+ function changePath(projectRoot, config, changeId) {
29
+ return join(changesDir(projectRoot, config), changeId);
30
+ }
31
+ function changeMetaPath(projectRoot, config, changeId) {
32
+ return join(changePath(projectRoot, config, changeId), "meta.yaml");
33
+ }
34
+ function activePointerPath(projectRoot, config) {
35
+ return join(changesDir(projectRoot, config), ".active");
36
+ }
37
+
38
+ export {
39
+ SDD_DIR,
40
+ sddRoot,
41
+ configPath,
42
+ workflowsDir,
43
+ templatesDir,
44
+ memoryDir,
45
+ changesDir,
46
+ archiveDir,
47
+ domainsDir,
48
+ changePath,
49
+ changeMetaPath,
50
+ activePointerPath
51
+ };
@@ -0,0 +1,96 @@
1
+ // src/fs.ts
2
+ import { promises as fs } from "fs";
3
+ import { dirname } from "pathe";
4
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
5
+ async function pathExists(path) {
6
+ try {
7
+ await fs.access(path);
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ async function ensureDir(path) {
14
+ await fs.mkdir(path, { recursive: true });
15
+ }
16
+ async function readText(path) {
17
+ return fs.readFile(path, "utf8");
18
+ }
19
+ async function writeText(path, content) {
20
+ await ensureDir(dirname(path));
21
+ await fs.writeFile(path, content, "utf8");
22
+ }
23
+ async function readYaml(path) {
24
+ const raw = await readText(path);
25
+ return parseYaml(raw);
26
+ }
27
+ async function writeYaml(path, data) {
28
+ const content = stringifyYaml(data, {
29
+ lineWidth: 100,
30
+ defaultStringType: "PLAIN",
31
+ defaultKeyType: "PLAIN"
32
+ });
33
+ await writeText(path, content.endsWith("\n") ? content : `${content}
34
+ `);
35
+ }
36
+ async function listDirs(path) {
37
+ if (!await pathExists(path)) return [];
38
+ const entries = await fs.readdir(path, { withFileTypes: true });
39
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
40
+ }
41
+ async function listFiles(path) {
42
+ if (!await pathExists(path)) return [];
43
+ const entries = await fs.readdir(path, { withFileTypes: true });
44
+ return entries.filter((e) => e.isFile()).map((e) => e.name);
45
+ }
46
+ async function copyDir(src, dest) {
47
+ await ensureDir(dest);
48
+ const entries = await fs.readdir(src, { withFileTypes: true });
49
+ for (const entry of entries) {
50
+ const from = `${src}/${entry.name}`;
51
+ const to = `${dest}/${entry.name}`;
52
+ if (entry.isDirectory()) {
53
+ await copyDir(from, to);
54
+ } else {
55
+ await ensureDir(dirname(to));
56
+ await fs.copyFile(from, to);
57
+ }
58
+ }
59
+ }
60
+ async function copyDirSkipExisting(src, dest) {
61
+ await ensureDir(dest);
62
+ const entries = await fs.readdir(src, { withFileTypes: true });
63
+ for (const entry of entries) {
64
+ const from = `${src}/${entry.name}`;
65
+ const to = `${dest}/${entry.name}`;
66
+ if (entry.isDirectory()) {
67
+ await copyDirSkipExisting(from, to);
68
+ } else if (!await pathExists(to)) {
69
+ await ensureDir(dirname(to));
70
+ await fs.copyFile(from, to);
71
+ }
72
+ }
73
+ }
74
+ async function writeTextIfMissing(path, content) {
75
+ if (await pathExists(path)) return false;
76
+ await writeText(path, content);
77
+ return true;
78
+ }
79
+ async function removePath(path) {
80
+ await fs.rm(path, { recursive: true, force: true });
81
+ }
82
+
83
+ export {
84
+ pathExists,
85
+ ensureDir,
86
+ readText,
87
+ writeText,
88
+ readYaml,
89
+ writeYaml,
90
+ listDirs,
91
+ listFiles,
92
+ copyDir,
93
+ copyDirSkipExisting,
94
+ writeTextIfMissing,
95
+ removePath
96
+ };
@@ -0,0 +1,187 @@
1
+ import {
2
+ configPath,
3
+ sddRoot
4
+ } from "./chunk-7MJYPJMR.js";
5
+ import {
6
+ ensureDir,
7
+ pathExists,
8
+ readYaml,
9
+ writeYaml
10
+ } from "./chunk-EPFURATC.js";
11
+
12
+ // src/schemas.ts
13
+ import { z } from "zod";
14
+ var GateSchema = z.object({
15
+ type: z.enum(["none", "soft", "hard"]).default("soft"),
16
+ mode: z.enum(["none", "human_approval", "checklist"]).default("checklist"),
17
+ checklist: z.array(z.string()).default([]),
18
+ overridable: z.boolean().default(true)
19
+ });
20
+ var ArtifactDefSchema = z.object({
21
+ id: z.string(),
22
+ path: z.string(),
23
+ template: z.string().optional(),
24
+ required: z.boolean().default(true)
25
+ });
26
+ var VerifyCommandSchema = z.object({
27
+ name: z.string(),
28
+ run: z.string(),
29
+ required: z.boolean().default(false)
30
+ });
31
+ var StageSchema = z.object({
32
+ id: z.string(),
33
+ title: z.string().optional(),
34
+ summary: z.string().optional(),
35
+ /**
36
+ * standard — normal work stage
37
+ * brainstorm — optional multi-approach exploration (before design)
38
+ * clarify — optional Q&A before leaving a major stage
39
+ */
40
+ kind: z.enum(["standard", "brainstorm", "clarify"]).default("standard"),
41
+ /** When true, stage is optional (skippable); agents may skip if not needed */
42
+ optional: z.boolean().default(false),
43
+ artifacts: z.array(ArtifactDefSchema).default([]),
44
+ gate: GateSchema.default({}),
45
+ skippable: z.boolean().default(true),
46
+ skip_when: z.object({
47
+ flags: z.array(z.string()).optional()
48
+ }).optional(),
49
+ verify: z.object({
50
+ commands: z.array(VerifyCommandSchema).default([]),
51
+ evidence_dir: z.string().optional()
52
+ }).optional(),
53
+ agent_context: z.object({
54
+ include: z.array(z.string()).default([]),
55
+ instructions: z.string().optional()
56
+ }).optional()
57
+ });
58
+ var WorkflowSchema = z.object({
59
+ name: z.string(),
60
+ description: z.string().optional(),
61
+ version: z.number().default(1),
62
+ stages: z.array(StageSchema).min(1),
63
+ recommendation: z.object({
64
+ when: z.object({
65
+ labels: z.array(z.string()).optional(),
66
+ keywords: z.array(z.string()).optional(),
67
+ max_complexity: z.enum(["low", "medium", "high"]).optional(),
68
+ min_complexity: z.enum(["low", "medium", "high"]).optional()
69
+ }).optional(),
70
+ priority: z.number().default(0)
71
+ }).optional(),
72
+ on_complete: z.object({
73
+ archive: z.boolean().default(false),
74
+ domain_sync: z.enum(["never", "recommend", "require"]).default("never")
75
+ }).optional()
76
+ });
77
+ var ConfigSchema = z.object({
78
+ version: z.number().default(1),
79
+ memory_path: z.string().default("memory"),
80
+ changes_path: z.string().default("changes"),
81
+ archive_path: z.string().default("archive"),
82
+ domains_path: z.string().default("domains"),
83
+ default_workflow: z.string().default("recommend"),
84
+ allowed_workflows: z.array(z.string()).optional(),
85
+ per_change: z.object({
86
+ allow_skip: z.boolean().default(true),
87
+ allow_gate_override: z.enum(["true", "false", "soft_only"]).default("soft_only"),
88
+ allow_custom_stages: z.boolean().default(true),
89
+ require_reason_on_skip: z.boolean().default(true)
90
+ }).default({}),
91
+ persistence: z.object({
92
+ default: z.enum(["change_only", "domain_sync"]).default("change_only"),
93
+ archive_on_complete: z.boolean().default(false),
94
+ domain_sync: z.object({
95
+ mode: z.enum(["never", "recommend", "require"]).default("never"),
96
+ anchored_domains: z.array(z.string()).default([])
97
+ }).default({})
98
+ }).default({}),
99
+ policy: z.object({
100
+ gates: z.enum(["soft", "hard"]).default("soft")
101
+ }).default({})
102
+ });
103
+ var ChangeMetaSchema = z.object({
104
+ id: z.string(),
105
+ title: z.string(),
106
+ workflow: z.string(),
107
+ created: z.string(),
108
+ updated: z.string().optional(),
109
+ status: z.enum(["in_progress", "blocked", "completed", "abandoned"]).default("in_progress"),
110
+ stage: z.string(),
111
+ branch: z.string().optional(),
112
+ domain: z.string().optional(),
113
+ flags: z.record(z.union([z.boolean(), z.string(), z.number()])).default({}),
114
+ overrides: z.object({
115
+ skip_stages: z.array(z.string()).default([]),
116
+ gates: z.record(z.enum(["none", "soft", "hard"])).default({}),
117
+ extra_stages: z.array(StageSchema).default([])
118
+ }).default({}),
119
+ skipped: z.array(
120
+ z.object({
121
+ stage: z.string(),
122
+ reason: z.string(),
123
+ at: z.string()
124
+ })
125
+ ).default([]),
126
+ gates: z.record(
127
+ z.object({
128
+ status: z.enum(["pending", "approved", "failed", "waived"]),
129
+ note: z.string().optional(),
130
+ at: z.string().optional()
131
+ })
132
+ ).default({}),
133
+ /** Results from `sdd verify` keyed by stage id */
134
+ verify_results: z.record(
135
+ z.object({
136
+ ok: z.boolean(),
137
+ at: z.string(),
138
+ results: z.array(
139
+ z.object({
140
+ name: z.string(),
141
+ exitCode: z.number().nullable()
142
+ })
143
+ ).default([])
144
+ })
145
+ ).default({}),
146
+ pr: z.object({
147
+ number: z.number().optional(),
148
+ url: z.string().optional()
149
+ }).optional(),
150
+ completed_at: z.string().optional()
151
+ });
152
+
153
+ // src/config.ts
154
+ async function isInitialized(projectRoot) {
155
+ return pathExists(configPath(projectRoot));
156
+ }
157
+ async function loadConfig(projectRoot) {
158
+ const path = configPath(projectRoot);
159
+ if (!await pathExists(path)) {
160
+ throw new Error(
161
+ `SDD is not initialized in ${projectRoot}. Run \`sdd init\` first.`
162
+ );
163
+ }
164
+ const raw = await readYaml(path);
165
+ return ConfigSchema.parse(raw);
166
+ }
167
+ async function saveConfig(projectRoot, config) {
168
+ await ensureDir(sddRoot(projectRoot));
169
+ await writeYaml(configPath(projectRoot), ConfigSchema.parse(config));
170
+ }
171
+ function defaultConfig() {
172
+ return ConfigSchema.parse({});
173
+ }
174
+
175
+ export {
176
+ GateSchema,
177
+ ArtifactDefSchema,
178
+ VerifyCommandSchema,
179
+ StageSchema,
180
+ WorkflowSchema,
181
+ ConfigSchema,
182
+ ChangeMetaSchema,
183
+ isInitialized,
184
+ loadConfig,
185
+ saveConfig,
186
+ defaultConfig
187
+ };