@wrongstack/sdd 1.0.19 → 1.0.24
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 +33 -22
- package/dist/intake-kickoff.d.ts +3 -3
- package/dist/sdd-interview-driver.d.ts +1 -1
- package/dist/spec-builder.d.ts +3 -2
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -739,9 +739,9 @@ var SpecDrivenDev = class {
|
|
|
739
739
|
};
|
|
740
740
|
|
|
741
741
|
// src/spec-store.ts
|
|
742
|
+
import { randomUUID } from "node:crypto";
|
|
742
743
|
import * as fsp from "node:fs/promises";
|
|
743
744
|
import * as path from "node:path";
|
|
744
|
-
import { randomUUID } from "node:crypto";
|
|
745
745
|
import { atomicWrite, ensureDir } from "@wrongstack/core/utils";
|
|
746
746
|
var SpecStore = class {
|
|
747
747
|
baseDir;
|
|
@@ -833,7 +833,7 @@ var SpecStore = class {
|
|
|
833
833
|
const dir = path.resolve(this.baseDir);
|
|
834
834
|
const resolved = path.resolve(dir, `${id}.json`);
|
|
835
835
|
const rel = path.relative(dir, resolved);
|
|
836
|
-
if (rel.startsWith(
|
|
836
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel) || rel.includes(path.sep)) {
|
|
837
837
|
throw new Error(`Invalid spec id: ${JSON.stringify(id)}`);
|
|
838
838
|
}
|
|
839
839
|
return resolved;
|
|
@@ -970,7 +970,7 @@ var TaskGraphStore = class {
|
|
|
970
970
|
const dir = path2.resolve(this.baseDir);
|
|
971
971
|
const resolved = path2.resolve(dir, `${id}.json`);
|
|
972
972
|
const rel = path2.relative(dir, resolved);
|
|
973
|
-
if (rel.startsWith(
|
|
973
|
+
if (rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel) || rel.includes(path2.sep)) {
|
|
974
974
|
throw new Error(`Invalid task-graph id: ${JSON.stringify(id)}`);
|
|
975
975
|
}
|
|
976
976
|
return resolved;
|
|
@@ -1358,14 +1358,16 @@ var SddBoardStore = class {
|
|
|
1358
1358
|
});
|
|
1359
1359
|
}
|
|
1360
1360
|
async removeFromIndex(runId) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1361
|
+
await withFileLock(this.indexPath, async () => {
|
|
1362
|
+
const current = await this.readIndex();
|
|
1363
|
+
const index = {
|
|
1364
|
+
version: 1,
|
|
1365
|
+
entries: current.entries.filter((entry) => entry.runId !== runId).map((entry) => ({ ...entry }))
|
|
1366
|
+
};
|
|
1367
|
+
await atomicWrite3(this.indexPath, JSON.stringify(index, null, 2), { mode: 384 });
|
|
1368
|
+
this.cachedIndex = index;
|
|
1369
|
+
this.cachedIndexSignature = await this.indexSignature();
|
|
1370
|
+
});
|
|
1369
1371
|
}
|
|
1370
1372
|
async indexSignature() {
|
|
1371
1373
|
try {
|
|
@@ -2011,6 +2013,7 @@ var AISpecBuilder = class {
|
|
|
2011
2013
|
store;
|
|
2012
2014
|
minQuestions;
|
|
2013
2015
|
maxQuestions;
|
|
2016
|
+
projectContext;
|
|
2014
2017
|
sessionPath;
|
|
2015
2018
|
sessionPersistence;
|
|
2016
2019
|
pendingSessionWrite = Promise.resolve();
|
|
@@ -2018,6 +2021,7 @@ var AISpecBuilder = class {
|
|
|
2018
2021
|
this.store = opts.store;
|
|
2019
2022
|
this.minQuestions = opts.minQuestions ?? 2;
|
|
2020
2023
|
this.maxQuestions = opts.maxQuestions ?? 10;
|
|
2024
|
+
this.projectContext = opts.projectContext ?? "";
|
|
2021
2025
|
this.sessionPath = opts.sessionPath;
|
|
2022
2026
|
this.sessionPersistence = opts.sessionPersistence;
|
|
2023
2027
|
this.session = {
|
|
@@ -2025,7 +2029,7 @@ var AISpecBuilder = class {
|
|
|
2025
2029
|
phase: "questioning",
|
|
2026
2030
|
title: "",
|
|
2027
2031
|
userIntent: "",
|
|
2028
|
-
projectContext:
|
|
2032
|
+
projectContext: this.projectContext,
|
|
2029
2033
|
answers: [],
|
|
2030
2034
|
questionCount: 0,
|
|
2031
2035
|
approved: false,
|
|
@@ -2105,10 +2109,11 @@ var AISpecBuilder = class {
|
|
|
2105
2109
|
void this.saveSession();
|
|
2106
2110
|
}
|
|
2107
2111
|
// ── Session Lifecycle ─────────────────────────────────────────────────────
|
|
2108
|
-
/** Start a new session with a title and
|
|
2109
|
-
startSession(title, intent) {
|
|
2112
|
+
/** Start a new session with a title, optional intent, and per-session context. */
|
|
2113
|
+
startSession(title, intent, projectContext) {
|
|
2110
2114
|
this.session.title = title;
|
|
2111
2115
|
this.session.userIntent = intent ?? "";
|
|
2116
|
+
this.session.projectContext = projectContext ?? this.projectContext;
|
|
2112
2117
|
this.session.phase = "questioning";
|
|
2113
2118
|
this.session.updatedAt = Date.now();
|
|
2114
2119
|
this.autoSave();
|
|
@@ -2308,6 +2313,7 @@ var AISpecBuilder = class {
|
|
|
2308
2313
|
this.session.phase = "questioning";
|
|
2309
2314
|
this.session.title = "";
|
|
2310
2315
|
this.session.userIntent = "";
|
|
2316
|
+
this.session.projectContext = this.projectContext;
|
|
2311
2317
|
this.session.answers = [];
|
|
2312
2318
|
this.session.questionCount = 0;
|
|
2313
2319
|
this.session.spec = void 0;
|
|
@@ -2500,9 +2506,9 @@ var SddInterviewDriver = class {
|
|
|
2500
2506
|
});
|
|
2501
2507
|
}
|
|
2502
2508
|
/** Begin a fresh interview. Returns the first AI prompt (a question kickoff). */
|
|
2503
|
-
start(title, intent) {
|
|
2509
|
+
start(title, intent, projectContext) {
|
|
2504
2510
|
this.builder.resetForNewInterview();
|
|
2505
|
-
this.builder.startSession(title, intent);
|
|
2511
|
+
this.builder.startSession(title, intent, projectContext);
|
|
2506
2512
|
this.tracker = null;
|
|
2507
2513
|
this.graph = null;
|
|
2508
2514
|
this.resumedFromDisk = false;
|
|
@@ -4493,10 +4499,13 @@ function createKanbanSddSessionPersistence(projectRoot, legacySessionPath) {
|
|
|
4493
4499
|
await pending;
|
|
4494
4500
|
},
|
|
4495
4501
|
async delete() {
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4502
|
+
const pending = writeChain.then(async () => {
|
|
4503
|
+
await deleteKanbanWorkflowState2(projectRoot, SDD_SESSION_WORKFLOW_ID);
|
|
4504
|
+
revision = 0;
|
|
4505
|
+
if (legacySessionPath) await fsp5.unlink(legacySessionPath).catch(() => void 0);
|
|
4506
|
+
});
|
|
4507
|
+
writeChain = pending.catch(() => void 0);
|
|
4508
|
+
await pending;
|
|
4500
4509
|
}
|
|
4501
4510
|
};
|
|
4502
4511
|
}
|
|
@@ -4581,7 +4590,7 @@ function intakeToInterviewKickoff(record) {
|
|
|
4581
4590
|
}
|
|
4582
4591
|
function startInterviewFromIntake(driver, record) {
|
|
4583
4592
|
const kickoff = intakeToInterviewKickoff(record);
|
|
4584
|
-
return driver.start(kickoff.title, kickoff.intent);
|
|
4593
|
+
return driver.start(kickoff.title, kickoff.intent, kickoff.projectContext);
|
|
4585
4594
|
}
|
|
4586
4595
|
|
|
4587
4596
|
// src/spec-templates.ts
|
|
@@ -5051,7 +5060,9 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
5051
5060
|
}
|
|
5052
5061
|
function computeParallelGroups(graph, blockedByMap) {
|
|
5053
5062
|
const groups = [];
|
|
5054
|
-
const assigned =
|
|
5063
|
+
const assigned = new Set(
|
|
5064
|
+
Array.from(graph.nodes.values()).filter((n) => n.status === "completed").map((n) => n.id)
|
|
5065
|
+
);
|
|
5055
5066
|
const nodes = Array.from(graph.nodes.values()).filter((n) => n.status !== "completed");
|
|
5056
5067
|
const remaining = new Set(nodes.map((n) => n.id));
|
|
5057
5068
|
while (remaining.size > 0) {
|
package/dist/intake-kickoff.d.ts
CHANGED
|
@@ -25,9 +25,9 @@ export interface IntakeInterviewKickoff {
|
|
|
25
25
|
export declare function intakeToInterviewKickoff(record: RequirementIntakeRecord): IntakeInterviewKickoff;
|
|
26
26
|
/**
|
|
27
27
|
* Start a spec-builder interview from an intake record. Returns the first AI
|
|
28
|
-
* prompt (a question kickoff), ready to feed the agent loop.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* prompt (a question kickoff), ready to feed the agent loop. Collected intake
|
|
29
|
+
* facts are passed as this interview's project context so the questioning
|
|
30
|
+
* prompt already knows them.
|
|
31
31
|
*/
|
|
32
32
|
export declare function startInterviewFromIntake(driver: Pick<SddInterviewDriver, 'start'>, record: RequirementIntakeRecord): string;
|
|
33
33
|
//# sourceMappingURL=intake-kickoff.d.ts.map
|
|
@@ -78,7 +78,7 @@ export declare class SddInterviewDriver {
|
|
|
78
78
|
private resumedFromDisk;
|
|
79
79
|
constructor(opts: SddInterviewDriverOptions);
|
|
80
80
|
/** Begin a fresh interview. Returns the first AI prompt (a question kickoff). */
|
|
81
|
-
start(title: string, intent?: string): string;
|
|
81
|
+
start(title: string, intent?: string, projectContext?: string): string;
|
|
82
82
|
/**
|
|
83
83
|
* Resume a previously-persisted interview. Re-hydrates the task
|
|
84
84
|
* graph too when one was already produced. Returns true if a session loaded.
|
package/dist/spec-builder.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare class AISpecBuilder {
|
|
|
25
25
|
private readonly store;
|
|
26
26
|
private readonly minQuestions;
|
|
27
27
|
private readonly maxQuestions;
|
|
28
|
+
private readonly projectContext;
|
|
28
29
|
private readonly sessionPath?;
|
|
29
30
|
private readonly sessionPersistence?;
|
|
30
31
|
private pendingSessionWrite;
|
|
@@ -37,8 +38,8 @@ export declare class AISpecBuilder {
|
|
|
37
38
|
deleteSession(): Promise<void>;
|
|
38
39
|
/** Auto-save helper. saveSession() already handles best-effort persistence. */
|
|
39
40
|
private autoSave;
|
|
40
|
-
/** Start a new session with a title and
|
|
41
|
-
startSession(title: string, intent?: string): void;
|
|
41
|
+
/** Start a new session with a title, optional intent, and per-session context. */
|
|
42
|
+
startSession(title: string, intent?: string, projectContext?: string): void;
|
|
42
43
|
/** Get current session state (readonly). */
|
|
43
44
|
getSession(): Readonly<AISpecSession>;
|
|
44
45
|
/** Get the current phase. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sdd",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack Spec-Driven Development engine — standalone package extracted from @wrongstack/core. Task graph generation, tracking, execution, lifecycle management, and AI-driven spec building for SDD workflows.",
|
|
6
6
|
"repository": {
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"!dist/**/*.map"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/core": "1.0.
|
|
31
|
-
"@wrongstack/kanban": "1.0.
|
|
32
|
-
"@wrongstack/primitives": "1.0.
|
|
33
|
-
"@wrongstack/requirement-intake": "1.0.
|
|
30
|
+
"@wrongstack/core": "1.0.24",
|
|
31
|
+
"@wrongstack/kanban": "1.0.24",
|
|
32
|
+
"@wrongstack/primitives": "1.0.24",
|
|
33
|
+
"@wrongstack/requirement-intake": "1.0.24"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.5.1",
|