substrate-ai 0.1.33 → 0.2.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.
@@ -4,9 +4,9 @@
4
4
  This project uses Substrate for automated implementation pipelines.
5
5
 
6
6
  ### Quick Start
7
- - Run `substrate auto --help-agent` to get full pipeline interaction instructions
8
- - Run `substrate auto run --events` to execute the pipeline with structured event output
9
- - Run `substrate auto run --events --stories 7-1,7-2` to run specific stories
7
+ - Run `substrate run --help-agent` to get full pipeline interaction instructions
8
+ - Run `substrate run --events` to execute the pipeline with structured event output
9
+ - Run `substrate run --events --stories 7-1,7-2` to run specific stories
10
10
 
11
11
  ### Agent Behavior
12
12
  - On story escalation: read the flagged files and issues, propose a fix, ask the user before applying
@@ -0,0 +1,336 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/persistence/schemas/decisions.ts
4
+ const PhaseEnum = z.enum([
5
+ "analysis",
6
+ "planning",
7
+ "solutioning",
8
+ "implementation"
9
+ ]);
10
+ const RequirementPriorityEnum = z.enum([
11
+ "must",
12
+ "should",
13
+ "could",
14
+ "wont"
15
+ ]);
16
+ const RequirementTypeEnum = z.enum([
17
+ "functional",
18
+ "non_functional",
19
+ "constraint"
20
+ ]);
21
+ const PipelineRunStatusEnum = z.enum([
22
+ "running",
23
+ "paused",
24
+ "completed",
25
+ "failed",
26
+ "stopped"
27
+ ]);
28
+ const DecisionSchema = z.object({
29
+ id: z.string().uuid(),
30
+ pipeline_run_id: z.string().nullable().optional(),
31
+ phase: z.string().min(1),
32
+ category: z.string().min(1),
33
+ key: z.string().min(1),
34
+ value: z.string().min(1),
35
+ rationale: z.string().nullable().optional(),
36
+ superseded_by: z.string().nullable().optional(),
37
+ created_at: z.string().optional(),
38
+ updated_at: z.string().optional()
39
+ });
40
+ const CreateDecisionInputSchema = z.object({
41
+ pipeline_run_id: z.string().nullable().optional(),
42
+ phase: z.string().min(1),
43
+ category: z.string().min(1),
44
+ key: z.string().min(1),
45
+ value: z.string().min(1),
46
+ rationale: z.string().nullable().optional()
47
+ });
48
+ const RequirementSchema = z.object({
49
+ id: z.string().uuid(),
50
+ pipeline_run_id: z.string().nullable().optional(),
51
+ source: z.string().min(1),
52
+ type: RequirementTypeEnum,
53
+ description: z.string().min(1),
54
+ priority: RequirementPriorityEnum,
55
+ status: z.string().default("active"),
56
+ created_at: z.string().optional()
57
+ });
58
+ const CreateRequirementInputSchema = z.object({
59
+ pipeline_run_id: z.string().nullable().optional(),
60
+ source: z.string().min(1),
61
+ type: RequirementTypeEnum,
62
+ description: z.string().min(1),
63
+ priority: RequirementPriorityEnum
64
+ });
65
+ const ConstraintSchema = z.object({
66
+ id: z.string().uuid(),
67
+ pipeline_run_id: z.string().nullable().optional(),
68
+ category: z.string().min(1),
69
+ description: z.string().min(1),
70
+ source: z.string().min(1),
71
+ created_at: z.string().optional()
72
+ });
73
+ const CreateConstraintInputSchema = z.object({
74
+ pipeline_run_id: z.string().nullable().optional(),
75
+ category: z.string().min(1),
76
+ description: z.string().min(1),
77
+ source: z.string().min(1)
78
+ });
79
+ const ArtifactSchema = z.object({
80
+ id: z.string().uuid(),
81
+ pipeline_run_id: z.string().nullable().optional(),
82
+ phase: z.string().min(1),
83
+ type: z.string().min(1),
84
+ path: z.string().min(1),
85
+ content_hash: z.string().nullable().optional(),
86
+ summary: z.string().nullable().optional(),
87
+ created_at: z.string().optional()
88
+ });
89
+ const RegisterArtifactInputSchema = z.object({
90
+ pipeline_run_id: z.string().nullable().optional(),
91
+ phase: z.string().min(1),
92
+ type: z.string().min(1),
93
+ path: z.string().min(1),
94
+ content_hash: z.string().nullable().optional(),
95
+ summary: z.string().nullable().optional()
96
+ });
97
+ const PipelineRunSchema = z.object({
98
+ id: z.string().uuid(),
99
+ methodology: z.string().min(1),
100
+ current_phase: z.string().nullable().optional(),
101
+ status: PipelineRunStatusEnum,
102
+ config_json: z.string().nullable().optional(),
103
+ token_usage_json: z.string().nullable().optional(),
104
+ parent_run_id: z.string().nullable().optional(),
105
+ created_at: z.string().optional(),
106
+ updated_at: z.string().optional()
107
+ });
108
+ const CreatePipelineRunInputSchema = z.object({
109
+ methodology: z.string().min(1),
110
+ start_phase: z.string().nullable().optional(),
111
+ config_json: z.string().nullable().optional()
112
+ });
113
+ const TokenUsageSchema = z.object({
114
+ id: z.number().int().optional(),
115
+ pipeline_run_id: z.string().nullable().optional(),
116
+ phase: z.string().min(1),
117
+ agent: z.string().min(1),
118
+ input_tokens: z.number().int().min(0).default(0),
119
+ output_tokens: z.number().int().min(0).default(0),
120
+ cost_usd: z.number().min(0).default(0),
121
+ metadata: z.string().nullable().optional(),
122
+ created_at: z.string().optional()
123
+ });
124
+ const AddTokenUsageInputSchema = z.object({
125
+ phase: z.string().min(1),
126
+ agent: z.string().min(1),
127
+ input_tokens: z.number().int().min(0).default(0),
128
+ output_tokens: z.number().int().min(0).default(0),
129
+ cost_usd: z.number().min(0).default(0),
130
+ metadata: z.string().nullable().optional()
131
+ });
132
+
133
+ //#endregion
134
+ //#region src/persistence/queries/decisions.ts
135
+ /**
136
+ * Insert a new decision record with a generated UUID.
137
+ */
138
+ function createDecision(db, input) {
139
+ const validated = CreateDecisionInputSchema.parse(input);
140
+ const id = crypto.randomUUID();
141
+ const stmt = db.prepare(`
142
+ INSERT INTO decisions (id, pipeline_run_id, phase, category, key, value, rationale)
143
+ VALUES (?, ?, ?, ?, ?, ?, ?)
144
+ `);
145
+ stmt.run(id, validated.pipeline_run_id ?? null, validated.phase, validated.category, validated.key, validated.value, validated.rationale ?? null);
146
+ const row = db.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
147
+ return row;
148
+ }
149
+ /**
150
+ * Insert or update a decision record.
151
+ * If a decision with the same pipeline_run_id, category, and key already exists,
152
+ * update its value and rationale. Otherwise, insert a new record.
153
+ */
154
+ function upsertDecision(db, input) {
155
+ const validated = CreateDecisionInputSchema.parse(input);
156
+ const existing = db.prepare("SELECT * FROM decisions WHERE pipeline_run_id = ? AND category = ? AND key = ? LIMIT 1").get(validated.pipeline_run_id ?? null, validated.category, validated.key);
157
+ if (existing) {
158
+ updateDecision(db, existing.id, {
159
+ value: validated.value,
160
+ rationale: validated.rationale ?? void 0
161
+ });
162
+ return db.prepare("SELECT * FROM decisions WHERE id = ?").get(existing.id);
163
+ }
164
+ return createDecision(db, input);
165
+ }
166
+ /**
167
+ * Get all decisions for a given phase, ordered by created_at ascending.
168
+ */
169
+ function getDecisionsByPhase(db, phase) {
170
+ const stmt = db.prepare("SELECT * FROM decisions WHERE phase = ? ORDER BY created_at ASC");
171
+ return stmt.all(phase);
172
+ }
173
+ /**
174
+ * Get all decisions for a given phase scoped to a specific pipeline run,
175
+ * ordered by created_at ascending.
176
+ */
177
+ function getDecisionsByPhaseForRun(db, runId, phase) {
178
+ const stmt = db.prepare("SELECT * FROM decisions WHERE pipeline_run_id = ? AND phase = ? ORDER BY created_at ASC");
179
+ return stmt.all(runId, phase);
180
+ }
181
+ /**
182
+ * Update a decision's value and/or rationale and set updated_at.
183
+ */
184
+ function updateDecision(db, id, updates) {
185
+ const setClauses = [];
186
+ const values = [];
187
+ if (updates.value !== void 0) {
188
+ setClauses.push("value = ?");
189
+ values.push(updates.value);
190
+ }
191
+ if (updates.rationale !== void 0) {
192
+ setClauses.push("rationale = ?");
193
+ values.push(updates.rationale);
194
+ }
195
+ if (setClauses.length === 0) return;
196
+ setClauses.push("updated_at = datetime('now')");
197
+ values.push(id);
198
+ const stmt = db.prepare(`UPDATE decisions SET ${setClauses.join(", ")} WHERE id = ?`);
199
+ stmt.run(...values);
200
+ }
201
+ /**
202
+ * Insert a new requirement with status = 'active'.
203
+ */
204
+ function createRequirement(db, input) {
205
+ const validated = CreateRequirementInputSchema.parse(input);
206
+ const id = crypto.randomUUID();
207
+ const stmt = db.prepare(`
208
+ INSERT INTO requirements (id, pipeline_run_id, source, type, description, priority, status)
209
+ VALUES (?, ?, ?, ?, ?, ?, 'active')
210
+ `);
211
+ stmt.run(id, validated.pipeline_run_id ?? null, validated.source, validated.type, validated.description, validated.priority);
212
+ const row = db.prepare("SELECT * FROM requirements WHERE id = ?").get(id);
213
+ return row;
214
+ }
215
+ /**
216
+ * Register a new artifact record.
217
+ */
218
+ function registerArtifact(db, input) {
219
+ const validated = RegisterArtifactInputSchema.parse(input);
220
+ const id = crypto.randomUUID();
221
+ const stmt = db.prepare(`
222
+ INSERT INTO artifacts (id, pipeline_run_id, phase, type, path, content_hash, summary)
223
+ VALUES (?, ?, ?, ?, ?, ?, ?)
224
+ `);
225
+ stmt.run(id, validated.pipeline_run_id ?? null, validated.phase, validated.type, validated.path, validated.content_hash ?? null, validated.summary ?? null);
226
+ const row = db.prepare("SELECT * FROM artifacts WHERE id = ?").get(id);
227
+ return row;
228
+ }
229
+ /**
230
+ * Get the latest artifact of a given type for a specific pipeline run.
231
+ * Filters by pipeline_run_id, phase, and type.
232
+ * Returns undefined if none found.
233
+ */
234
+ function getArtifactByTypeForRun(db, runId, phase, type) {
235
+ const stmt = db.prepare("SELECT * FROM artifacts WHERE pipeline_run_id = ? AND phase = ? AND type = ? ORDER BY created_at DESC, rowid DESC LIMIT 1");
236
+ return stmt.get(runId, phase, type);
237
+ }
238
+ /**
239
+ * Get all artifacts registered for a specific pipeline run, ordered by created_at ascending.
240
+ */
241
+ function getArtifactsByRun(db, runId) {
242
+ const stmt = db.prepare("SELECT * FROM artifacts WHERE pipeline_run_id = ? ORDER BY created_at ASC");
243
+ return stmt.all(runId);
244
+ }
245
+ /**
246
+ * Get a pipeline run by its ID. Returns undefined if not found.
247
+ */
248
+ function getPipelineRunById(db, id) {
249
+ const stmt = db.prepare("SELECT * FROM pipeline_runs WHERE id = ?");
250
+ return stmt.get(id);
251
+ }
252
+ /**
253
+ * Update a pipeline run's config_json field.
254
+ */
255
+ function updatePipelineRunConfig(db, id, configJson) {
256
+ const stmt = db.prepare("UPDATE pipeline_runs SET config_json = ?, updated_at = datetime('now') WHERE id = ?");
257
+ stmt.run(configJson, id);
258
+ }
259
+ /**
260
+ * Create a new pipeline run with status = 'running'.
261
+ */
262
+ function createPipelineRun(db, input) {
263
+ const validated = CreatePipelineRunInputSchema.parse(input);
264
+ const id = crypto.randomUUID();
265
+ const stmt = db.prepare(`
266
+ INSERT INTO pipeline_runs (id, methodology, current_phase, status, config_json)
267
+ VALUES (?, ?, ?, 'running', ?)
268
+ `);
269
+ stmt.run(id, validated.methodology, validated.start_phase ?? null, validated.config_json ?? null);
270
+ const row = db.prepare("SELECT * FROM pipeline_runs WHERE id = ?").get(id);
271
+ return row;
272
+ }
273
+ /**
274
+ * Update a pipeline run's current_phase, status, and/or token_usage_json.
275
+ */
276
+ function updatePipelineRun(db, id, updates) {
277
+ const setClauses = [];
278
+ const values = [];
279
+ if (updates.current_phase !== void 0) {
280
+ setClauses.push("current_phase = ?");
281
+ values.push(updates.current_phase);
282
+ }
283
+ if (updates.status !== void 0) {
284
+ setClauses.push("status = ?");
285
+ values.push(updates.status);
286
+ }
287
+ if (updates.token_usage_json !== void 0) {
288
+ setClauses.push("token_usage_json = ?");
289
+ values.push(updates.token_usage_json);
290
+ }
291
+ if (setClauses.length === 0) return;
292
+ setClauses.push("updated_at = datetime('now')");
293
+ values.push(id);
294
+ const stmt = db.prepare(`UPDATE pipeline_runs SET ${setClauses.join(", ")} WHERE id = ?`);
295
+ stmt.run(...values);
296
+ }
297
+ /**
298
+ * Get the most recently created pipeline run. Returns undefined if none found.
299
+ */
300
+ function getLatestRun(db) {
301
+ const stmt = db.prepare("SELECT * FROM pipeline_runs ORDER BY created_at DESC, rowid DESC LIMIT 1");
302
+ return stmt.get();
303
+ }
304
+ /**
305
+ * Append a token usage record for a pipeline run.
306
+ */
307
+ function addTokenUsage(db, runId, usage) {
308
+ const validated = AddTokenUsageInputSchema.parse(usage);
309
+ const stmt = db.prepare(`
310
+ INSERT INTO token_usage (pipeline_run_id, phase, agent, input_tokens, output_tokens, cost_usd, metadata)
311
+ VALUES (?, ?, ?, ?, ?, ?, ?)
312
+ `);
313
+ stmt.run(runId, validated.phase, validated.agent, validated.input_tokens, validated.output_tokens, validated.cost_usd, validated.metadata ?? null);
314
+ }
315
+ /**
316
+ * Aggregate token usage by phase and agent for a given pipeline run.
317
+ */
318
+ function getTokenUsageSummary(db, runId) {
319
+ const stmt = db.prepare(`
320
+ SELECT
321
+ phase,
322
+ agent,
323
+ SUM(input_tokens) AS total_input_tokens,
324
+ SUM(output_tokens) AS total_output_tokens,
325
+ SUM(cost_usd) AS total_cost_usd
326
+ FROM token_usage
327
+ WHERE pipeline_run_id = ?
328
+ GROUP BY phase, agent
329
+ ORDER BY phase ASC, agent ASC
330
+ `);
331
+ return stmt.all(runId);
332
+ }
333
+
334
+ //#endregion
335
+ export { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision };
336
+ //# sourceMappingURL=decisions-BBLMsN_c.js.map
@@ -0,0 +1,3 @@
1
+ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-BBLMsN_c.js";
2
+
3
+ export { getLatestRun };
@@ -0,0 +1,111 @@
1
+ //#region src/core/errors.ts
2
+ /**
3
+ * Error definitions for Substrate
4
+ * Provides structured error hierarchy for all toolkit operations
5
+ */
6
+ /** Base error class for all Substrate errors */
7
+ var AdtError = class AdtError extends Error {
8
+ code;
9
+ context;
10
+ constructor(message, code, context = {}) {
11
+ super(message);
12
+ this.name = "AdtError";
13
+ this.code = code;
14
+ this.context = context;
15
+ if (Error.captureStackTrace) Error.captureStackTrace(this, AdtError);
16
+ }
17
+ toJSON() {
18
+ return {
19
+ name: this.name,
20
+ message: this.message,
21
+ code: this.code,
22
+ context: this.context,
23
+ stack: this.stack
24
+ };
25
+ }
26
+ };
27
+ /** Error thrown when task configuration is invalid */
28
+ var TaskConfigError = class extends AdtError {
29
+ constructor(message, context = {}) {
30
+ super(message, "TASK_CONFIG_ERROR", context);
31
+ this.name = "TaskConfigError";
32
+ }
33
+ };
34
+ /** Error thrown when a worker/agent operation fails */
35
+ var WorkerError = class extends AdtError {
36
+ constructor(message, context = {}) {
37
+ super(message, "WORKER_ERROR", context);
38
+ this.name = "WorkerError";
39
+ }
40
+ };
41
+ /** Error thrown when a worker/agent cannot be found */
42
+ var WorkerNotFoundError = class extends AdtError {
43
+ constructor(agentId) {
44
+ super(`Worker agent not found: ${agentId}`, "WORKER_NOT_FOUND", { agentId });
45
+ this.name = "WorkerNotFoundError";
46
+ }
47
+ };
48
+ /** Error thrown when a task graph is invalid */
49
+ var TaskGraphError = class extends AdtError {
50
+ constructor(message, context = {}) {
51
+ super(message, "TASK_GRAPH_ERROR", context);
52
+ this.name = "TaskGraphError";
53
+ }
54
+ };
55
+ /** Error thrown when a task graph has cycles (deadlock) */
56
+ var TaskGraphCycleError = class extends TaskGraphError {
57
+ constructor(cycle) {
58
+ super(`Circular dependency detected in task graph: ${cycle.join(" -> ")}`, { cycle });
59
+ this.name = "TaskGraphCycleError";
60
+ }
61
+ };
62
+ /** Error thrown when a budget limit is exceeded */
63
+ var BudgetExceededError = class extends AdtError {
64
+ constructor(limit, current, context = {}) {
65
+ super(`Budget cap exceeded: current=${String(current)}, limit=${String(limit)}`, "BUDGET_EXCEEDED", {
66
+ limit,
67
+ current,
68
+ ...context
69
+ });
70
+ this.name = "BudgetExceededError";
71
+ }
72
+ };
73
+ /** Error thrown when git operations fail */
74
+ var GitError = class extends AdtError {
75
+ constructor(message, context = {}) {
76
+ super(message, "GIT_ERROR", context);
77
+ this.name = "GitError";
78
+ }
79
+ };
80
+ /** Error thrown when configuration is invalid or missing */
81
+ var ConfigError = class extends AdtError {
82
+ constructor(message, context = {}) {
83
+ super(message, "CONFIG_ERROR", context);
84
+ this.name = "ConfigError";
85
+ }
86
+ };
87
+ /** Error thrown when state recovery fails */
88
+ var RecoveryError = class extends AdtError {
89
+ constructor(message, context = {}) {
90
+ super(message, "RECOVERY_ERROR", context);
91
+ this.name = "RecoveryError";
92
+ }
93
+ };
94
+ /** Error thrown when a config file uses an incompatible format version */
95
+ var ConfigIncompatibleFormatError = class extends AdtError {
96
+ constructor(message, context = {}) {
97
+ super(message, "CONFIG_INCOMPATIBLE_FORMAT", context);
98
+ this.name = "ConfigIncompatibleFormatError";
99
+ }
100
+ };
101
+ /** Error thrown when a task graph file uses an incompatible format version */
102
+ var TaskGraphIncompatibleFormatError = class extends AdtError {
103
+ constructor(message, context = {}) {
104
+ super(message, "TASK_GRAPH_INCOMPATIBLE_FORMAT", context);
105
+ this.name = "TaskGraphIncompatibleFormatError";
106
+ }
107
+ };
108
+
109
+ //#endregion
110
+ export { AdtError, BudgetExceededError, ConfigError, ConfigIncompatibleFormatError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError };
111
+ //# sourceMappingURL=errors-BPqtzQ4U.js.map