@structured-vibe-coding/core 0.5.2 → 0.5.3

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/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ AGENT_INTEGRATIONS,
2
3
  AGENT_TARGET_OPTIONS,
3
4
  ALL_AGENT_TARGETS,
4
5
  ArtifactDefSchema,
@@ -14,29 +15,26 @@ import {
14
15
  WorkflowSchema,
15
16
  activePointerPath,
16
17
  activeStages,
17
- advanceStage,
18
- approveGate,
19
18
  archiveDir,
20
19
  buildAgentPrompt,
21
20
  buildContext,
22
- canLeaveStage,
23
21
  changeDirName,
24
22
  changeMetaPath,
25
23
  changePath,
26
24
  changesDir,
27
25
  clearActiveIf,
28
- completeChange,
29
26
  configPath,
30
27
  copyDir,
31
- createChange,
32
28
  defaultConfig,
33
29
  domainsDir,
34
30
  ensureDir,
35
31
  firstActiveStageId,
36
32
  formatStatus,
37
33
  getActiveChangeId,
34
+ getIntegration,
38
35
  getStage,
39
36
  inferComplexity,
37
+ installAgentIntegration,
40
38
  installAgentIntegrations,
41
39
  integrationKeyFor,
42
40
  isInitialized,
@@ -68,32 +66,367 @@ import {
68
66
  sddRoot,
69
67
  setActiveChange,
70
68
  shouldAutoSkip,
71
- skipStage,
72
69
  slugify,
73
- switchWorkflow,
74
70
  templatesDir,
75
71
  todayIsoDate,
76
72
  uniqueChangeDirName,
77
73
  workflowsDir,
78
74
  writeText,
79
75
  writeYaml
80
- } from "./chunk-IO6AS6JI.js";
76
+ } from "./chunk-CHDYZIOH.js";
81
77
 
82
- // src/init.ts
78
+ // src/change.ts
83
79
  import { join as join2 } from "pathe";
84
80
 
81
+ // src/stage-gates.ts
82
+ import { join } from "pathe";
83
+ async function canLeaveStage(ctx, config) {
84
+ const stage = getStage(ctx.workflow, ctx.meta, ctx.meta.stage);
85
+ const errors = [];
86
+ const warnings = [];
87
+ if (!stage) {
88
+ return { ok: false, errors: [`Unknown stage: ${ctx.meta.stage}`], warnings };
89
+ }
90
+ for (const artifact of stage.artifacts) {
91
+ if (!artifact.required) continue;
92
+ const target = join(ctx.path, artifact.path);
93
+ if (!await pathExists(target)) {
94
+ errors.push(`Missing required artifact: ${artifact.path}`);
95
+ }
96
+ }
97
+ const declared = stage.gate?.type ?? "soft";
98
+ let effectiveGate = declared;
99
+ if (config.policy.gates === "hard" && declared === "soft") {
100
+ effectiveGate = "hard";
101
+ }
102
+ const gateState = ctx.meta.gates?.[stage.id];
103
+ if (effectiveGate === "hard") {
104
+ if (!gateState || gateState.status !== "approved" && gateState.status !== "waived") {
105
+ errors.push(
106
+ `Hard gate on stage "${stage.id}" is not approved. Run \`sdd gate approve\` or complete the checklist.`
107
+ );
108
+ }
109
+ } else if (effectiveGate === "soft" && stage.gate?.checklist?.length) {
110
+ if (!gateState || gateState.status === "pending") {
111
+ warnings.push(
112
+ `Soft gate checklist not signed off on "${stage.id}" (proceeding allowed).`
113
+ );
114
+ }
115
+ }
116
+ const requiredCmds = stage.verify?.commands?.filter((c) => c.required) ?? [];
117
+ if (requiredCmds.length) {
118
+ const verifyOk = ctx.meta.verify_results?.[stage.id]?.ok === true;
119
+ const humanOverride = gateState?.status === "waived" || gateState?.status === "approved";
120
+ if (!verifyOk && !humanOverride) {
121
+ errors.push(
122
+ `Required local verify commands have not passed for "${stage.id}". Run \`sdd verify\` (or \`sdd gate approve/waive\` with a note).`
123
+ );
124
+ } else if (!verifyOk && humanOverride) {
125
+ warnings.push(
126
+ `Stage "${stage.id}" gate signed off without a successful required verify run.`
127
+ );
128
+ }
129
+ }
130
+ return { ok: errors.length === 0, errors, warnings };
131
+ }
132
+ async function approveGate(projectRoot, config, changeId, stageId, note, status = "approved") {
133
+ const ctx = await buildContext(projectRoot, config, changeId);
134
+ const id = stageId ?? ctx.meta.stage;
135
+ ctx.meta.gates = {
136
+ ...ctx.meta.gates,
137
+ [id]: { status, note, at: nowIso() }
138
+ };
139
+ await saveChangeMeta(projectRoot, config, ctx.meta);
140
+ return buildContext(projectRoot, config, changeId);
141
+ }
142
+
143
+ // src/change.ts
144
+ async function existingChangeIds(projectRoot, config) {
145
+ const open = await listDirs(changesDir(projectRoot, config));
146
+ const archived = await listDirs(archiveDir(projectRoot, config));
147
+ return /* @__PURE__ */ new Set([...open, ...archived]);
148
+ }
149
+ async function createChange(input) {
150
+ const { projectRoot, config, title, workflowName } = input;
151
+ const workflow = await loadWorkflow(projectRoot, workflowName);
152
+ if (!workflow.stages.length) {
153
+ throw new Error(`Workflow ${workflowName} has no stages`);
154
+ }
155
+ const baseId = changeDirName(title);
156
+ const id = uniqueChangeDirName(baseId, await existingChangeIds(projectRoot, config));
157
+ const dir = changePath(projectRoot, config, id);
158
+ await ensureDir(dir);
159
+ let meta = ChangeMetaSchema.parse({
160
+ id,
161
+ title,
162
+ workflow: workflowName,
163
+ created: nowIso(),
164
+ updated: nowIso(),
165
+ status: "in_progress",
166
+ stage: workflow.stages[0].id,
167
+ domain: input.domain,
168
+ branch: input.branch,
169
+ flags: input.flags ?? {},
170
+ overrides: {
171
+ skip_stages: [],
172
+ gates: {},
173
+ extra_stages: []
174
+ },
175
+ skipped: [],
176
+ gates: {}
177
+ });
178
+ const startId = firstActiveStageId(workflow, meta) ?? workflow.stages[0].id;
179
+ meta = { ...meta, stage: startId };
180
+ const firstStage = getStage(workflow, meta, startId);
181
+ if (!firstStage) {
182
+ throw new Error(`Workflow ${workflowName} has no active stages`);
183
+ }
184
+ await writeYaml(changeMetaPath(projectRoot, config, id), meta);
185
+ await materializeStageArtifacts(projectRoot, config, id, workflow, meta, firstStage);
186
+ await setActiveChange(projectRoot, config, id);
187
+ try {
188
+ const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-5YI2CWAB.js");
189
+ await refreshActiveAgentContext2(projectRoot);
190
+ } catch {
191
+ }
192
+ return buildContext(projectRoot, config, id);
193
+ }
194
+ async function materializeStageArtifacts(projectRoot, config, changeId, workflow, meta, stage) {
195
+ const created = [];
196
+ const dir = changePath(projectRoot, config, changeId);
197
+ const tdir = templatesDir(projectRoot);
198
+ for (const artifact of stage.artifacts) {
199
+ const target = join2(dir, artifact.path);
200
+ if (await pathExists(target)) continue;
201
+ if (artifact.path.endsWith("/")) {
202
+ await ensureDir(target);
203
+ created.push(artifact.path);
204
+ continue;
205
+ }
206
+ let content = defaultArtifactContent(artifact.id, meta, stage, workflow);
207
+ if (artifact.template) {
208
+ const templatePath = join2(tdir, artifact.template.replace(/^templates\//, ""));
209
+ const candidates = [
210
+ join2(tdir, artifact.template),
211
+ templatePath,
212
+ join2(tdir, `${artifact.id}.md`)
213
+ ];
214
+ for (const c of candidates) {
215
+ if (await pathExists(c)) {
216
+ content = interpolate(await readText(c), meta, stage, workflow);
217
+ break;
218
+ }
219
+ }
220
+ } else {
221
+ const fallback = join2(tdir, `${artifact.id}.md`);
222
+ if (await pathExists(fallback)) {
223
+ content = interpolate(await readText(fallback), meta, stage, workflow);
224
+ }
225
+ }
226
+ await writeText(target, content);
227
+ created.push(artifact.path);
228
+ }
229
+ return created;
230
+ }
231
+ function interpolate(template, meta, stage, workflow) {
232
+ return template.replaceAll("{{title}}", meta.title).replaceAll("{{id}}", meta.id).replaceAll("{{workflow}}", workflow.name).replaceAll("{{stage}}", stage.id).replaceAll("{{stage_title}}", stage.title ?? stage.id).replaceAll("{{date}}", meta.created.slice(0, 10));
233
+ }
234
+ function defaultArtifactContent(artifactId, meta, stage, workflow) {
235
+ return `# ${artifactId}
236
+
237
+ > Change: **${meta.title}** (\`${meta.id}\`)
238
+ > Workflow: \`${workflow.name}\` \xB7 Stage: \`${stage.id}\`
239
+
240
+ ## Summary
241
+
242
+ <!-- Write the intent / content for this artifact -->
243
+
244
+ ## Notes
245
+
246
+ -
247
+ `;
248
+ }
249
+ async function advanceStage(projectRoot, config, changeId, opts) {
250
+ const ctx = await buildContext(projectRoot, config, changeId);
251
+ const warnings = [];
252
+ if (ctx.meta.status === "completed") {
253
+ throw new Error("Change is already completed.");
254
+ }
255
+ const check = await canLeaveStage(ctx, config);
256
+ warnings.push(...check.warnings);
257
+ if (!check.ok && !opts?.force) {
258
+ throw new Error(check.errors.join("\n"));
259
+ }
260
+ const from = ctx.meta.stage;
261
+ const next = nextStageId(ctx.workflow, ctx.meta);
262
+ if (!next) {
263
+ return {
264
+ ctx,
265
+ from,
266
+ to: null,
267
+ completed: false,
268
+ artifactsCreated: [],
269
+ warnings: [...warnings, "No further stages. Run `sdd complete` when ready."]
270
+ };
271
+ }
272
+ ctx.meta.stage = next;
273
+ await saveChangeMeta(projectRoot, config, ctx.meta);
274
+ const stage = getStage(ctx.workflow, ctx.meta, next);
275
+ const artifactsCreated = await materializeStageArtifacts(
276
+ projectRoot,
277
+ config,
278
+ changeId,
279
+ ctx.workflow,
280
+ ctx.meta,
281
+ stage
282
+ );
283
+ try {
284
+ const { refreshActiveAgentContext: refreshActiveAgentContext2 } = await import("./agents-5YI2CWAB.js");
285
+ await refreshActiveAgentContext2(projectRoot);
286
+ } catch {
287
+ }
288
+ const updated = await buildContext(projectRoot, config, changeId);
289
+ return {
290
+ ctx: updated,
291
+ from,
292
+ to: next,
293
+ completed: false,
294
+ artifactsCreated,
295
+ warnings
296
+ };
297
+ }
298
+ async function skipStage(projectRoot, config, changeId, stageId, reason) {
299
+ if (!config.per_change.allow_skip) {
300
+ throw new Error("Skipping stages is disabled in .sdd/config.yaml (per_change.allow_skip).");
301
+ }
302
+ if (config.per_change.require_reason_on_skip && !reason.trim()) {
303
+ throw new Error("A reason is required to skip a stage.");
304
+ }
305
+ const ctx = await buildContext(projectRoot, config, changeId);
306
+ const stage = getStage(ctx.workflow, ctx.meta, stageId);
307
+ if (!stage) {
308
+ throw new Error(`Stage "${stageId}" not found in workflow ${ctx.meta.workflow}`);
309
+ }
310
+ if (!stage.skippable) {
311
+ throw new Error(`Stage "${stageId}" is not skippable.`);
312
+ }
313
+ const skip_stages = new Set(ctx.meta.overrides.skip_stages ?? []);
314
+ skip_stages.add(stageId);
315
+ ctx.meta.overrides.skip_stages = [...skip_stages];
316
+ ctx.meta.skipped = [
317
+ ...ctx.meta.skipped ?? [],
318
+ { stage: stageId, reason, at: nowIso() }
319
+ ];
320
+ if (ctx.meta.stage === stageId) {
321
+ const next = nextStageId(ctx.workflow, ctx.meta);
322
+ if (next) {
323
+ ctx.meta.stage = next;
324
+ const nextStage = getStage(ctx.workflow, ctx.meta, next);
325
+ if (nextStage) {
326
+ await materializeStageArtifacts(
327
+ projectRoot,
328
+ config,
329
+ changeId,
330
+ ctx.workflow,
331
+ ctx.meta,
332
+ nextStage
333
+ );
334
+ }
335
+ }
336
+ }
337
+ await saveChangeMeta(projectRoot, config, ctx.meta);
338
+ return buildContext(projectRoot, config, changeId);
339
+ }
340
+ async function switchWorkflow(projectRoot, config, changeId, workflowName, reason) {
341
+ const ctx = await buildContext(projectRoot, config, changeId);
342
+ const workflow = await loadWorkflow(projectRoot, workflowName);
343
+ if (config.allowed_workflows?.length && !config.allowed_workflows.includes(workflowName)) {
344
+ throw new Error(
345
+ `Workflow "${workflowName}" is not in allowed_workflows: ${config.allowed_workflows.join(", ")}`
346
+ );
347
+ }
348
+ ctx.meta.workflow = workflowName;
349
+ const hasStage = workflow.stages.some((s) => s.id === ctx.meta.stage);
350
+ if (!hasStage) {
351
+ ctx.meta.stage = workflow.stages[0].id;
352
+ }
353
+ if (reason) {
354
+ ctx.meta.flags = {
355
+ ...ctx.meta.flags,
356
+ workflow_switch_reason: reason
357
+ };
358
+ }
359
+ await saveChangeMeta(projectRoot, config, ctx.meta);
360
+ const stage = getStage(workflow, ctx.meta, ctx.meta.stage);
361
+ if (stage) {
362
+ await materializeStageArtifacts(projectRoot, config, changeId, workflow, ctx.meta, stage);
363
+ }
364
+ return buildContext(projectRoot, config, changeId);
365
+ }
366
+ async function completeChange(projectRoot, config, changeId) {
367
+ const ctx = await buildContext(projectRoot, config, changeId);
368
+ const next = nextStageId(ctx.workflow, ctx.meta);
369
+ if (next) {
370
+ const active = activeStages(ctx.workflow, ctx.meta);
371
+ const last = active[active.length - 1];
372
+ if (last && ctx.meta.stage !== last.id) {
373
+ throw new Error(
374
+ `Cannot complete: still on stage "${ctx.meta.stage}". Finish remaining stages or skip them. Next: ${next}`
375
+ );
376
+ }
377
+ }
378
+ const check = await canLeaveStage(ctx, config);
379
+ if (!check.ok) {
380
+ throw new Error(check.errors.join("\n"));
381
+ }
382
+ ctx.meta.status = "completed";
383
+ ctx.meta.completed_at = nowIso();
384
+ await saveChangeMeta(projectRoot, config, ctx.meta);
385
+ await clearActiveIf(projectRoot, config, changeId);
386
+ let archivedTo = null;
387
+ const shouldArchive = config.persistence.archive_on_complete && ctx.workflow.on_complete?.archive !== false;
388
+ if (shouldArchive) {
389
+ const dest = join2(archiveDir(projectRoot, config), changeId);
390
+ if (await pathExists(dest)) {
391
+ throw new Error(`Archive already exists for change: ${changeId}`);
392
+ }
393
+ await ensureDir(archiveDir(projectRoot, config));
394
+ await copyDir(ctx.path, dest);
395
+ await removePath(ctx.path);
396
+ archivedTo = dest;
397
+ }
398
+ if (archivedTo) {
399
+ const meta = ChangeMetaSchema.parse(await readYaml(join2(archivedTo, "meta.yaml")));
400
+ return {
401
+ archivedTo,
402
+ ctx: {
403
+ id: changeId,
404
+ path: archivedTo,
405
+ meta,
406
+ workflow: ctx.workflow,
407
+ stages: ctx.stages,
408
+ active: ctx.active
409
+ }
410
+ };
411
+ }
412
+ return { archivedTo, ctx: await buildContext(projectRoot, config, changeId) };
413
+ }
414
+
415
+ // src/init.ts
416
+ import { join as join4 } from "pathe";
417
+
85
418
  // src/defaults.ts
86
419
  import { existsSync } from "fs";
87
420
  import { fileURLToPath } from "url";
88
- import { dirname, join } from "pathe";
421
+ import { dirname, join as join3 } from "pathe";
89
422
  function defaultsRoot() {
90
423
  const here = dirname(fileURLToPath(import.meta.url));
91
424
  const candidates = [
92
- join(here, "..", "defaults"),
425
+ join3(here, "..", "defaults"),
93
426
  // npm package: dist/../defaults
94
- join(here, "defaults"),
427
+ join3(here, "defaults"),
95
428
  // extension bundle: dist/defaults
96
- join(here, "..", "..", "defaults")
429
+ join3(here, "..", "..", "defaults")
97
430
  // src/ during some test runners
98
431
  ];
99
432
  for (const c of candidates) {
@@ -102,13 +435,13 @@ function defaultsRoot() {
102
435
  return candidates[0];
103
436
  }
104
437
  function defaultWorkflowsDir() {
105
- return join(defaultsRoot(), "workflows");
438
+ return join3(defaultsRoot(), "workflows");
106
439
  }
107
440
  function defaultTemplatesDir() {
108
- return join(defaultsRoot(), "templates");
441
+ return join3(defaultsRoot(), "templates");
109
442
  }
110
443
  function defaultMemoryDir() {
111
- return join(defaultsRoot(), "memory");
444
+ return join3(defaultsRoot(), "memory");
112
445
  }
113
446
 
114
447
  // src/init.ts
@@ -116,15 +449,15 @@ async function initProject(opts) {
116
449
  const { projectRoot, force = false } = opts;
117
450
  const created = [];
118
451
  const root = sddRoot(projectRoot);
119
- if (await pathExists(join2(root, "config.yaml")) && !force) {
452
+ if (await pathExists(join4(root, "config.yaml")) && !force) {
120
453
  throw new Error(
121
- `Already initialized (${join2(root, "config.yaml")}). Use --force to re-copy defaults.`
454
+ `Already initialized (${join4(root, "config.yaml")}). Use --force to re-copy defaults.`
122
455
  );
123
456
  }
124
457
  const config = defaultConfig();
125
458
  await ensureDir(root);
126
459
  await saveConfig(projectRoot, config);
127
- created.push(join2(root, "config.yaml"));
460
+ created.push(join4(root, "config.yaml"));
128
461
  const wfDest = workflowsDir(projectRoot);
129
462
  await ensureDir(wfDest);
130
463
  const wfSrc = defaultWorkflowsDir();
@@ -146,15 +479,15 @@ async function initProject(opts) {
146
479
  await copyDir(mSrc, mDest);
147
480
  } else {
148
481
  await writeText(
149
- join2(mDest, "product.md"),
482
+ join4(mDest, "product.md"),
150
483
  "# Product\n\n<!-- What are you building? -->\n"
151
484
  );
152
485
  await writeText(
153
- join2(mDest, "architecture.md"),
486
+ join4(mDest, "architecture.md"),
154
487
  "# Architecture\n\n<!-- High-level system shape -->\n"
155
488
  );
156
489
  await writeText(
157
- join2(mDest, "conventions.md"),
490
+ join4(mDest, "conventions.md"),
158
491
  "# Conventions\n\n<!-- Coding and process conventions -->\n"
159
492
  );
160
493
  }
@@ -165,10 +498,10 @@ async function initProject(opts) {
165
498
  created.push(changesDir(projectRoot, config));
166
499
  created.push(archiveDir(projectRoot, config));
167
500
  created.push(domainsDir(projectRoot, config));
168
- await writeText(join2(changesDir(projectRoot, config), ".gitkeep"), "");
169
- await writeText(join2(archiveDir(projectRoot, config), ".gitkeep"), "");
170
- await writeText(join2(domainsDir(projectRoot, config), ".gitkeep"), "");
171
- const sddReadme = join2(root, "README.md");
501
+ await writeText(join4(changesDir(projectRoot, config), ".gitkeep"), "");
502
+ await writeText(join4(archiveDir(projectRoot, config), ".gitkeep"), "");
503
+ await writeText(join4(domainsDir(projectRoot, config), ".gitkeep"), "");
504
+ const sddReadme = join4(root, "README.md");
172
505
  if (!await pathExists(sddReadme) || force) {
173
506
  await writeText(
174
507
  sddReadme,
@@ -196,10 +529,10 @@ sdd complete
196
529
  created.push(sddReadme);
197
530
  }
198
531
  let agentsResult;
199
- if (Array.isArray(opts.agents) && opts.agents.length > 0) {
200
- const ag = await installAgentIntegrations({
532
+ if (opts.agents) {
533
+ const ag = await installAgentIntegration({
201
534
  projectRoot,
202
- targets: opts.agents,
535
+ target: opts.agents,
203
536
  force
204
537
  });
205
538
  created.push(...ag.created);
@@ -210,7 +543,7 @@ sdd complete
210
543
 
211
544
  // src/verify.ts
212
545
  import { spawn } from "child_process";
213
- import { join as join3 } from "pathe";
546
+ import { join as join5 } from "pathe";
214
547
  function runCommand(command, cwd) {
215
548
  return new Promise((resolve) => {
216
549
  const child = spawn(command, {
@@ -239,7 +572,7 @@ async function runLocalVerify(projectRoot, config, changeId, opts) {
239
572
  if (!stage) {
240
573
  throw new Error(`Unknown stage: ${ctx.meta.stage}`);
241
574
  }
242
- const evidenceDir = stage.verify?.evidence_dir != null ? join3(ctx.path, stage.verify.evidence_dir) : join3(ctx.path, "evidence", "local");
575
+ const evidenceDir = stage.verify?.evidence_dir != null ? join5(ctx.path, stage.verify.evidence_dir) : join5(ctx.path, "evidence", "local");
243
576
  await ensureDir(evidenceDir);
244
577
  const results = [];
245
578
  const runCommands = opts?.runCommands !== false;
@@ -253,7 +586,7 @@ async function runLocalVerify(projectRoot, config, changeId, opts) {
253
586
  exitCode,
254
587
  output: output.slice(0, 5e4)
255
588
  });
256
- const logPath = join3(evidenceDir, `${cmd.name.replace(/\s+/g, "-")}.log`);
589
+ const logPath = join5(evidenceDir, `${cmd.name.replace(/\s+/g, "-")}.log`);
257
590
  await writeText(
258
591
  logPath,
259
592
  `# ${cmd.name}
@@ -279,7 +612,7 @@ ${output}`
279
612
  ok = results.every((r) => r.exitCode === 0);
280
613
  }
281
614
  const checklist = stage.gate?.checklist ?? [];
282
- const summaryPath = join3(ctx.path, "local-test-results.md");
615
+ const summaryPath = join5(ctx.path, "local-test-results.md");
283
616
  const lines = [
284
617
  `# Local verify results`,
285
618
  ``,
@@ -315,7 +648,7 @@ ${output}`
315
648
  if (await pathExists(summaryPath)) {
316
649
  const stamp = nowIso();
317
650
  await writeText(
318
- join3(evidenceDir, `run-${stamp.replace(/[:.]/g, "-")}.md`),
651
+ join5(evidenceDir, `run-${stamp.replace(/[:.]/g, "-")}.md`),
319
652
  lines.join("\n")
320
653
  );
321
654
  } else {
@@ -339,6 +672,7 @@ ${output}`
339
672
  };
340
673
  }
341
674
  export {
675
+ AGENT_INTEGRATIONS,
342
676
  AGENT_TARGET_OPTIONS,
343
677
  ALL_AGENT_TARGETS,
344
678
  ArtifactDefSchema,
@@ -379,9 +713,11 @@ export {
379
713
  firstActiveStageId,
380
714
  formatStatus,
381
715
  getActiveChangeId,
716
+ getIntegration,
382
717
  getStage,
383
718
  inferComplexity,
384
719
  initProject,
720
+ installAgentIntegration,
385
721
  installAgentIntegrations,
386
722
  integrationKeyFor,
387
723
  isInitialized,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@structured-vibe-coding/core",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Core engine for structured vibe coding (local-first Spec-Driven Development)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",