@blockspool/core 0.3.2 → 0.3.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.
Files changed (93) hide show
  1. package/dist/db/adapter.d.ts +191 -0
  2. package/dist/db/adapter.d.ts.map +1 -0
  3. package/dist/db/adapter.js +40 -0
  4. package/dist/db/adapter.js.map +1 -0
  5. package/dist/db/contract.d.ts +47 -0
  6. package/dist/db/contract.d.ts.map +1 -0
  7. package/dist/db/contract.js +258 -0
  8. package/dist/db/contract.js.map +1 -0
  9. package/dist/db/index.d.ts +6 -0
  10. package/dist/db/index.d.ts.map +1 -0
  11. package/dist/db/index.js +7 -0
  12. package/dist/db/index.js.map +1 -0
  13. package/dist/exec/index.d.ts +5 -0
  14. package/dist/exec/index.d.ts.map +1 -0
  15. package/dist/exec/index.js +5 -0
  16. package/dist/exec/index.js.map +1 -0
  17. package/dist/exec/types.d.ts +64 -0
  18. package/dist/exec/types.d.ts.map +1 -0
  19. package/dist/exec/types.js +8 -0
  20. package/dist/exec/types.js.map +1 -0
  21. package/dist/index.d.ts +23 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +27 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/repos/index.d.ts +15 -0
  26. package/dist/repos/index.d.ts.map +1 -0
  27. package/dist/repos/index.js +11 -0
  28. package/dist/repos/index.js.map +1 -0
  29. package/dist/repos/projects.d.ts +41 -0
  30. package/dist/repos/projects.d.ts.map +1 -0
  31. package/dist/repos/projects.js +74 -0
  32. package/dist/repos/projects.js.map +1 -0
  33. package/dist/repos/run_steps.d.ts +152 -0
  34. package/dist/repos/run_steps.d.ts.map +1 -0
  35. package/dist/repos/run_steps.js +321 -0
  36. package/dist/repos/run_steps.js.map +1 -0
  37. package/dist/repos/runs.d.ts +92 -0
  38. package/dist/repos/runs.d.ts.map +1 -0
  39. package/dist/repos/runs.js +177 -0
  40. package/dist/repos/runs.js.map +1 -0
  41. package/dist/repos/tickets.d.ts +71 -0
  42. package/dist/repos/tickets.d.ts.map +1 -0
  43. package/dist/repos/tickets.js +128 -0
  44. package/dist/repos/tickets.js.map +1 -0
  45. package/dist/scout/index.d.ts +15 -0
  46. package/dist/scout/index.d.ts.map +1 -0
  47. package/dist/scout/index.js +383 -0
  48. package/dist/scout/index.js.map +1 -0
  49. package/dist/scout/prompt.d.ts +32 -0
  50. package/dist/scout/prompt.d.ts.map +1 -0
  51. package/dist/scout/prompt.js +104 -0
  52. package/dist/scout/prompt.js.map +1 -0
  53. package/dist/scout/runner.d.ts +68 -0
  54. package/dist/scout/runner.d.ts.map +1 -0
  55. package/dist/scout/runner.js +270 -0
  56. package/dist/scout/runner.js.map +1 -0
  57. package/dist/scout/scanner.d.ts +35 -0
  58. package/dist/scout/scanner.d.ts.map +1 -0
  59. package/dist/scout/scanner.js +164 -0
  60. package/dist/scout/scanner.js.map +1 -0
  61. package/dist/scout/types.d.ts +179 -0
  62. package/dist/scout/types.d.ts.map +1 -0
  63. package/dist/scout/types.js +44 -0
  64. package/dist/scout/types.js.map +1 -0
  65. package/dist/services/index.d.ts +10 -0
  66. package/dist/services/index.d.ts.map +1 -0
  67. package/dist/services/index.js +9 -0
  68. package/dist/services/index.js.map +1 -0
  69. package/dist/services/qa.d.ts +76 -0
  70. package/dist/services/qa.d.ts.map +1 -0
  71. package/dist/services/qa.js +239 -0
  72. package/dist/services/qa.js.map +1 -0
  73. package/dist/services/scout.d.ts +120 -0
  74. package/dist/services/scout.d.ts.map +1 -0
  75. package/dist/services/scout.js +205 -0
  76. package/dist/services/scout.js.map +1 -0
  77. package/dist/utils/id.d.ts +12 -0
  78. package/dist/utils/id.d.ts.map +1 -0
  79. package/dist/utils/id.js +24 -0
  80. package/dist/utils/id.js.map +1 -0
  81. package/dist/utils/id.test.d.ts +5 -0
  82. package/dist/utils/id.test.d.ts.map +1 -0
  83. package/dist/utils/id.test.js +173 -0
  84. package/dist/utils/id.test.js.map +1 -0
  85. package/dist/utils/index.d.ts +6 -0
  86. package/dist/utils/index.d.ts.map +1 -0
  87. package/dist/utils/index.js +6 -0
  88. package/dist/utils/index.js.map +1 -0
  89. package/dist/utils/json.d.ts +9 -0
  90. package/dist/utils/json.d.ts.map +1 -0
  91. package/dist/utils/json.js +19 -0
  92. package/dist/utils/json.js.map +1 -0
  93. package/package.json +1 -1
@@ -0,0 +1,239 @@
1
+ /**
2
+ * QA Service - Run local QA commands and record results
3
+ *
4
+ * This is pure orchestration: no FS writes, no raw SQL.
5
+ * Uses repos + ExecRunner for all operations.
6
+ */
7
+ import * as path from 'node:path';
8
+ import * as runs from '../repos/runs.js';
9
+ import * as runSteps from '../repos/run_steps.js';
10
+ function resolveCwd(repoRootAbs, cwd) {
11
+ if (!cwd || cwd === '.')
12
+ return repoRootAbs;
13
+ return path.isAbsolute(cwd) ? cwd : path.join(repoRootAbs, cwd);
14
+ }
15
+ function execStatusToStepStatus(exec) {
16
+ if (exec.status === 'success')
17
+ return 'success';
18
+ if (exec.status === 'canceled')
19
+ return 'canceled';
20
+ return 'failed';
21
+ }
22
+ /**
23
+ * Extract flat fields from ExecOutput for repo methods
24
+ */
25
+ function flattenOutput(output, prefix) {
26
+ return {
27
+ [`${prefix}Path`]: output.path,
28
+ [`${prefix}Bytes`]: output.bytes,
29
+ [`${prefix}Truncated`]: output.truncated,
30
+ [`${prefix}Tail`]: output.tail,
31
+ };
32
+ }
33
+ /**
34
+ * Run QA commands and record results
35
+ */
36
+ export async function runQa(deps, opts) {
37
+ const { db, exec, logger } = deps;
38
+ const { projectId, repoRoot, config, signal } = opts;
39
+ if (!config.commands?.length) {
40
+ throw new Error('QA config has no commands. Add qa.commands to .blockspool/config.json');
41
+ }
42
+ // Validate unique step names
43
+ const names = new Set();
44
+ for (const c of config.commands) {
45
+ if (!c.name?.trim())
46
+ throw new Error('QA command is missing a name');
47
+ if (!c.cmd?.trim())
48
+ throw new Error(`QA command "${c.name}" is missing cmd`);
49
+ if (names.has(c.name))
50
+ throw new Error(`Duplicate QA command name: "${c.name}"`);
51
+ names.add(c.name);
52
+ }
53
+ const startedAtMs = Date.now();
54
+ // Determine max attempts
55
+ const maxAttempts = opts.maxAttemptsOverride ??
56
+ (config.retry.enabled ? config.retry.maxAttempts : 1);
57
+ // Create the run
58
+ const run = await runs.create(db, {
59
+ projectId,
60
+ type: 'qa',
61
+ maxIterations: maxAttempts,
62
+ metadata: {
63
+ maxAttempts,
64
+ commandCount: config.commands.length,
65
+ },
66
+ });
67
+ logger.info(`QA run started: ${run.id}`);
68
+ let latestAttempt = 1;
69
+ let finalStatus = 'failed';
70
+ let failedAt;
71
+ // Check for early cancellation
72
+ if (signal?.aborted) {
73
+ await runs.markFailure(db, run.id, 'Canceled before start', { attempts: 0 });
74
+ return {
75
+ runId: run.id,
76
+ projectId,
77
+ status: 'canceled',
78
+ attempts: 0,
79
+ latestAttempt: 0,
80
+ startedAtMs,
81
+ endedAtMs: Date.now(),
82
+ durationMs: Date.now() - startedAtMs,
83
+ };
84
+ }
85
+ // Attempt loop
86
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
87
+ latestAttempt = attempt;
88
+ logger.info(`Attempt ${attempt}/${maxAttempts}`);
89
+ // Pre-create steps as queued
90
+ const createdSteps = await runSteps.createMany(db, run.id, config.commands.map((c) => ({
91
+ name: c.name,
92
+ cmd: c.cmd,
93
+ cwd: c.cwd ?? '.',
94
+ timeoutMs: c.timeoutMs,
95
+ })), attempt);
96
+ let attemptFailed = false;
97
+ for (const step of createdSteps) {
98
+ // Check for cancellation before each step
99
+ if (signal?.aborted) {
100
+ await runSteps.markCanceled(db, step.id, 'Canceled by user');
101
+ // Cancel remaining steps
102
+ for (const remaining of createdSteps) {
103
+ if (remaining.ordinal > step.ordinal) {
104
+ await runSteps.markCanceled(db, remaining.id, 'Canceled by user');
105
+ }
106
+ }
107
+ finalStatus = 'canceled';
108
+ failedAt = { attempt, stepName: step.name };
109
+ attemptFailed = true;
110
+ break;
111
+ }
112
+ await runSteps.markStarted(db, step.id);
113
+ const cmdCfg = config.commands[step.ordinal];
114
+ const cwdAbs = resolveCwd(repoRoot, cmdCfg.cwd ?? step.cwd ?? '.');
115
+ const execRes = await exec.run({
116
+ cmd: cmdCfg.cmd,
117
+ cwd: cwdAbs,
118
+ env: cmdCfg.env,
119
+ timeoutMs: cmdCfg.timeoutMs ?? step.timeoutMs ?? undefined,
120
+ signal,
121
+ repoRoot,
122
+ artifactsDir: config.artifacts.dir,
123
+ runId: run.id,
124
+ attempt,
125
+ stepName: step.name,
126
+ ordinal: step.ordinal,
127
+ maxLogBytes: config.artifacts.maxLogBytes,
128
+ tailBytes: config.artifacts.tailBytes,
129
+ });
130
+ const stepStatus = execStatusToStepStatus(execRes);
131
+ if (stepStatus === 'success') {
132
+ await runSteps.markSuccess(db, step.id, {
133
+ exitCode: execRes.exitCode ?? 0,
134
+ stdoutPath: execRes.stdout.path,
135
+ stderrPath: execRes.stderr.path,
136
+ stdoutBytes: execRes.stdout.bytes,
137
+ stderrBytes: execRes.stderr.bytes,
138
+ stdoutTruncated: execRes.stdout.truncated,
139
+ stderrTruncated: execRes.stderr.truncated,
140
+ stdoutTail: execRes.stdout.tail,
141
+ stderrTail: execRes.stderr.tail,
142
+ metadata: { execStatus: execRes.status },
143
+ });
144
+ logger.info(` ✓ ${step.name}`);
145
+ continue;
146
+ }
147
+ // Failed or canceled
148
+ attemptFailed = true;
149
+ const errorMessage = execRes.errorMessage ??
150
+ (execRes.status === 'timeout'
151
+ ? `Timed out after ${cmdCfg.timeoutMs ?? step.timeoutMs ?? 'unknown'}ms`
152
+ : execRes.status === 'canceled'
153
+ ? 'Canceled'
154
+ : `Exited with code ${execRes.exitCode ?? 'unknown'}`);
155
+ if (stepStatus === 'canceled') {
156
+ await runSteps.markCanceled(db, step.id, errorMessage);
157
+ finalStatus = 'canceled';
158
+ }
159
+ else {
160
+ await runSteps.markFailed(db, step.id, {
161
+ exitCode: execRes.exitCode ?? undefined,
162
+ signal: execRes.signal ?? undefined,
163
+ errorMessage,
164
+ stdoutPath: execRes.stdout.path,
165
+ stderrPath: execRes.stderr.path,
166
+ stdoutBytes: execRes.stdout.bytes,
167
+ stderrBytes: execRes.stderr.bytes,
168
+ stdoutTruncated: execRes.stdout.truncated,
169
+ stderrTruncated: execRes.stderr.truncated,
170
+ stdoutTail: execRes.stdout.tail,
171
+ stderrTail: execRes.stderr.tail,
172
+ metadata: { execStatus: execRes.status },
173
+ });
174
+ finalStatus = 'failed';
175
+ }
176
+ logger.error(` ✗ ${step.name}: ${errorMessage}`);
177
+ failedAt = { attempt, stepName: step.name };
178
+ // Cancel remaining queued steps in this attempt
179
+ for (const remaining of createdSteps) {
180
+ if (remaining.ordinal <= step.ordinal)
181
+ continue;
182
+ await runSteps.markSkipped(db, remaining.id, `Skipped (previous step "${step.name}" ${stepStatus})`);
183
+ }
184
+ break;
185
+ }
186
+ if (!attemptFailed) {
187
+ finalStatus = 'success';
188
+ failedAt = undefined;
189
+ break;
190
+ }
191
+ // If canceled, don't retry
192
+ if (finalStatus === 'canceled') {
193
+ break;
194
+ }
195
+ }
196
+ const endedAtMs = Date.now();
197
+ // Finalize run
198
+ if (finalStatus === 'success') {
199
+ await runs.markSuccess(db, run.id, {
200
+ attempts: latestAttempt,
201
+ durationMs: endedAtMs - startedAtMs,
202
+ });
203
+ }
204
+ else {
205
+ const errorMsg = failedAt
206
+ ? `Failed at ${failedAt.stepName} (attempt ${failedAt.attempt})`
207
+ : finalStatus === 'canceled'
208
+ ? 'QA canceled'
209
+ : 'QA failed';
210
+ await runs.markFailure(db, run.id, errorMsg, {
211
+ attempts: latestAttempt,
212
+ failedAt,
213
+ durationMs: endedAtMs - startedAtMs,
214
+ });
215
+ }
216
+ return {
217
+ runId: run.id,
218
+ projectId,
219
+ status: finalStatus,
220
+ attempts: latestAttempt,
221
+ latestAttempt,
222
+ failedAt,
223
+ startedAtMs,
224
+ endedAtMs,
225
+ durationMs: endedAtMs - startedAtMs,
226
+ };
227
+ }
228
+ /**
229
+ * Get QA run details with step information
230
+ */
231
+ export async function getQaRunDetails(db, runId) {
232
+ const run = await runs.getById(db, runId);
233
+ if (!run)
234
+ return null;
235
+ const steps = await runSteps.listByRun(db, runId);
236
+ const summary = await runSteps.getSummary(db, runId);
237
+ return { run, steps, summary };
238
+ }
239
+ //# sourceMappingURL=qa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa.js","sourceRoot":"","sources":["../../src/services/qa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Scout Service - Orchestrates codebase scanning and ticket creation
3
+ *
4
+ * This is pure orchestration - it coordinates between:
5
+ * - File scanning (scanner.ts)
6
+ * - LLM analysis (runner.ts)
7
+ * - Data persistence (repos/*)
8
+ *
9
+ * It accepts dependencies via ScoutDeps, making it testable and adapter-agnostic.
10
+ */
11
+ import type { DatabaseAdapter } from '../db/adapter.js';
12
+ import * as projects from '../repos/projects.js';
13
+ import * as tickets from '../repos/tickets.js';
14
+ import * as runs from '../repos/runs.js';
15
+ import { type TicketProposal, type ScoutBackend } from '../scout/index.js';
16
+ /**
17
+ * Dependencies for the scout service
18
+ */
19
+ export interface ScoutDeps {
20
+ /** Database adapter */
21
+ db: DatabaseAdapter;
22
+ /** Git operations */
23
+ git: GitService;
24
+ /** Logger */
25
+ logger?: Logger;
26
+ /** Clock for timestamps (useful for testing) */
27
+ clock?: () => Date;
28
+ }
29
+ /**
30
+ * Git service interface
31
+ */
32
+ export interface GitService {
33
+ /** Find repository root from a path */
34
+ findRepoRoot(path: string): Promise<string | null>;
35
+ /** Get remote URL */
36
+ getRemoteUrl(repoRoot: string): Promise<string | null>;
37
+ /** Generate deterministic project ID from repo */
38
+ getProjectId(repoRoot: string, remoteUrl: string | null): string;
39
+ }
40
+ /**
41
+ * Logger interface
42
+ */
43
+ export interface Logger {
44
+ debug(msg: string, data?: Record<string, unknown>): void;
45
+ info(msg: string, data?: Record<string, unknown>): void;
46
+ warn(msg: string, data?: Record<string, unknown>): void;
47
+ error(msg: string, data?: Record<string, unknown>): void;
48
+ }
49
+ /**
50
+ * Scout options
51
+ */
52
+ export interface ScoutRepoOptions {
53
+ /** Path to scan (defaults to repo root) */
54
+ path?: string;
55
+ /** Glob pattern for files */
56
+ scope?: string;
57
+ /** Filter to categories */
58
+ types?: Array<'refactor' | 'docs' | 'test' | 'perf' | 'security'>;
59
+ /** Maximum proposals */
60
+ maxProposals?: number;
61
+ /** Minimum confidence threshold */
62
+ minConfidence?: number;
63
+ /** Model to use */
64
+ model?: 'haiku' | 'sonnet' | 'opus';
65
+ /** Custom prompt from formula — tells the AI what to focus on */
66
+ customPrompt?: string;
67
+ /** Timeout per batch */
68
+ timeoutMs?: number;
69
+ /** Abort signal */
70
+ signal?: AbortSignal;
71
+ /** Progress callback */
72
+ onProgress?: (progress: ScoutProgress) => void;
73
+ /** Auto-create tickets from proposals */
74
+ autoApprove?: boolean;
75
+ /** Scout backend override (default: ClaudeScoutBackend) */
76
+ backend?: ScoutBackend;
77
+ /** Files the scout can read but must NOT propose changes to */
78
+ protectedFiles?: string[];
79
+ }
80
+ /**
81
+ * Scout progress
82
+ */
83
+ export interface ScoutProgress {
84
+ phase: 'init' | 'scanning' | 'analyzing' | 'storing' | 'complete';
85
+ filesScanned?: number;
86
+ totalFiles?: number;
87
+ proposalsFound?: number;
88
+ ticketsCreated?: number;
89
+ message?: string;
90
+ }
91
+ /**
92
+ * Scout result
93
+ */
94
+ export interface ScoutRepoResult {
95
+ success: boolean;
96
+ project: projects.Project;
97
+ run: runs.Run;
98
+ proposals: TicketProposal[];
99
+ tickets: tickets.Ticket[];
100
+ errors: string[];
101
+ scannedFiles: number;
102
+ durationMs: number;
103
+ }
104
+ /**
105
+ * Scout a repository for improvement opportunities
106
+ *
107
+ * This is the main entry point for the scout service. It:
108
+ * 1. Resolves the repository root
109
+ * 2. Ensures the project exists in the database
110
+ * 3. Creates a scout run
111
+ * 4. Scans files and generates proposals
112
+ * 5. Optionally creates tickets from proposals
113
+ * 6. Records the run outcome
114
+ */
115
+ export declare function scoutRepo(deps: ScoutDeps, opts?: ScoutRepoOptions): Promise<ScoutRepoResult>;
116
+ /**
117
+ * Approve specific proposals and create tickets
118
+ */
119
+ export declare function approveProposals(deps: ScoutDeps, projectId: string, proposals: TicketProposal[]): Promise<tickets.Ticket[]>;
120
+ //# sourceMappingURL=scout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scout.d.ts","sourceRoot":"","sources":["../../src/services/scout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Scout Service - Orchestrates codebase scanning and ticket creation
3
+ *
4
+ * This is pure orchestration - it coordinates between:
5
+ * - File scanning (scanner.ts)
6
+ * - LLM analysis (runner.ts)
7
+ * - Data persistence (repos/*)
8
+ *
9
+ * It accepts dependencies via ScoutDeps, making it testable and adapter-agnostic.
10
+ */
11
+ import * as projects from '../repos/projects.js';
12
+ import * as tickets from '../repos/tickets.js';
13
+ import * as runs from '../repos/runs.js';
14
+ import { scout as scanAndPropose, } from '../scout/index.js';
15
+ /**
16
+ * Scout a repository for improvement opportunities
17
+ *
18
+ * This is the main entry point for the scout service. It:
19
+ * 1. Resolves the repository root
20
+ * 2. Ensures the project exists in the database
21
+ * 3. Creates a scout run
22
+ * 4. Scans files and generates proposals
23
+ * 5. Optionally creates tickets from proposals
24
+ * 6. Records the run outcome
25
+ */
26
+ export async function scoutRepo(deps, opts = {}) {
27
+ const { db, git, logger } = deps;
28
+ const startTime = Date.now();
29
+ const errors = [];
30
+ const report = (progress) => {
31
+ opts.onProgress?.({
32
+ phase: 'init',
33
+ ...progress,
34
+ });
35
+ };
36
+ // Phase 1: Resolve repository
37
+ report({ phase: 'init', message: 'Resolving repository...' });
38
+ const targetPath = opts.path ?? process.cwd();
39
+ const repoRoot = await git.findRepoRoot(targetPath);
40
+ if (!repoRoot) {
41
+ throw new Error(`Not a git repository: ${targetPath}`);
42
+ }
43
+ const remoteUrl = await git.getRemoteUrl(repoRoot);
44
+ const projectId = git.getProjectId(repoRoot, remoteUrl);
45
+ logger?.debug('Resolved repository', { repoRoot, remoteUrl, projectId });
46
+ // Phase 2: Ensure project exists
47
+ const project = await projects.ensureForRepo(db, {
48
+ id: projectId,
49
+ name: repoRoot.split('/').pop() || 'unknown',
50
+ repoUrl: remoteUrl,
51
+ rootPath: repoRoot,
52
+ });
53
+ logger?.info('Project ensured', { projectId: project.id, name: project.name });
54
+ // Phase 3: Create scout run
55
+ const run = await runs.create(db, {
56
+ projectId: project.id,
57
+ type: 'scout',
58
+ metadata: {
59
+ scope: opts.scope ?? 'src/**',
60
+ maxProposals: opts.maxProposals ?? 10,
61
+ model: opts.model ?? 'opus',
62
+ },
63
+ });
64
+ logger?.debug('Scout run created', { runId: run.id });
65
+ try {
66
+ // Phase 4: Get dedup context
67
+ const recentTickets = await tickets.getRecentlyCompleted(db, project.id, 20);
68
+ const recentTitles = recentTickets.map(t => t.title);
69
+ // Phase 5: Scan and generate proposals
70
+ report({ phase: 'scanning', message: 'Scanning files...' });
71
+ const scanResult = await scanAndPropose({
72
+ scope: opts.scope ?? 'src/**',
73
+ types: opts.types,
74
+ maxProposals: opts.maxProposals ?? 10,
75
+ minConfidence: opts.minConfidence ?? 50,
76
+ projectPath: repoRoot,
77
+ model: opts.model ?? 'opus',
78
+ timeoutMs: opts.timeoutMs ?? 120000,
79
+ signal: opts.signal,
80
+ recentlyCompletedTitles: recentTitles,
81
+ customPrompt: opts.customPrompt,
82
+ backend: opts.backend,
83
+ protectedFiles: opts.protectedFiles,
84
+ onProgress: (p) => {
85
+ report({
86
+ phase: 'analyzing',
87
+ filesScanned: p.filesScanned,
88
+ totalFiles: p.totalFiles,
89
+ proposalsFound: p.proposalsFound,
90
+ message: `Analyzing batch ${p.currentBatch}/${p.totalBatches}`,
91
+ });
92
+ },
93
+ });
94
+ if (!scanResult.success) {
95
+ errors.push(...scanResult.errors);
96
+ }
97
+ logger?.info('Scan complete', {
98
+ files: scanResult.scannedFiles,
99
+ proposals: scanResult.proposals.length,
100
+ });
101
+ // Phase 6: Create tickets if auto-approve
102
+ const createdTickets = [];
103
+ if (opts.autoApprove && scanResult.proposals.length > 0) {
104
+ report({
105
+ phase: 'storing',
106
+ proposalsFound: scanResult.proposals.length,
107
+ message: 'Creating tickets...',
108
+ });
109
+ const ticketInputs = scanResult.proposals.map(proposal => ({
110
+ projectId: project.id,
111
+ title: proposal.title,
112
+ description: formatProposalDescription(proposal),
113
+ status: 'ready',
114
+ priority: proposal.confidence,
115
+ category: proposal.category,
116
+ allowedPaths: proposal.allowed_paths,
117
+ verificationCommands: proposal.verification_commands,
118
+ }));
119
+ const created = await tickets.createMany(db, ticketInputs);
120
+ createdTickets.push(...created);
121
+ logger?.info('Tickets created', { count: created.length });
122
+ }
123
+ // Phase 7: Mark run success
124
+ await runs.markSuccess(db, run.id, {
125
+ scannedFiles: scanResult.scannedFiles,
126
+ proposalCount: scanResult.proposals.length,
127
+ ticketCount: createdTickets.length,
128
+ durationMs: Date.now() - startTime,
129
+ });
130
+ report({
131
+ phase: 'complete',
132
+ filesScanned: scanResult.scannedFiles,
133
+ proposalsFound: scanResult.proposals.length,
134
+ ticketsCreated: createdTickets.length,
135
+ });
136
+ return {
137
+ success: true,
138
+ project,
139
+ run: (await runs.getById(db, run.id)),
140
+ proposals: scanResult.proposals,
141
+ tickets: createdTickets,
142
+ errors: scanResult.errors,
143
+ scannedFiles: scanResult.scannedFiles,
144
+ durationMs: Date.now() - startTime,
145
+ };
146
+ }
147
+ catch (error) {
148
+ // Mark run as failed
149
+ await runs.markFailure(db, run.id, error, {
150
+ durationMs: Date.now() - startTime,
151
+ });
152
+ logger?.error('Scout failed', { error: error.message });
153
+ return {
154
+ success: false,
155
+ project,
156
+ run: (await runs.getById(db, run.id)),
157
+ proposals: [],
158
+ tickets: [],
159
+ errors: [...errors, error.message],
160
+ scannedFiles: 0,
161
+ durationMs: Date.now() - startTime,
162
+ };
163
+ }
164
+ }
165
+ /**
166
+ * Format a proposal into ticket description markdown
167
+ */
168
+ function formatProposalDescription(proposal) {
169
+ const parts = [
170
+ proposal.description,
171
+ '',
172
+ '## Acceptance Criteria',
173
+ ...proposal.acceptance_criteria.map(c => `- ${c}`),
174
+ '',
175
+ '## Rationale',
176
+ proposal.rationale,
177
+ '',
178
+ '## Files',
179
+ ...proposal.files.map(f => `- \`${f}\``),
180
+ '',
181
+ `**Complexity:** ${proposal.estimated_complexity}`,
182
+ `**Confidence:** ${proposal.confidence}%`,
183
+ ];
184
+ return parts.join('\n');
185
+ }
186
+ /**
187
+ * Approve specific proposals and create tickets
188
+ */
189
+ export async function approveProposals(deps, projectId, proposals) {
190
+ const { db, logger } = deps;
191
+ const ticketInputs = proposals.map(proposal => ({
192
+ projectId,
193
+ title: proposal.title,
194
+ description: formatProposalDescription(proposal),
195
+ status: 'ready',
196
+ priority: proposal.confidence,
197
+ category: proposal.category,
198
+ allowedPaths: proposal.allowed_paths,
199
+ verificationCommands: proposal.verification_commands,
200
+ }));
201
+ const created = await tickets.createMany(db, ticketInputs);
202
+ logger?.info('Proposals approved', { count: created.length });
203
+ return created;
204
+ }
205
+ //# sourceMappingURL=scout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scout.js","sourceRoot":"","sources":["../../src/services/scout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ID generation utilities
3
+ */
4
+ /**
5
+ * Generate a random ID
6
+ */
7
+ export declare function nanoid(size?: number): string;
8
+ /**
9
+ * Generate a prefixed ID
10
+ */
11
+ export declare function prefixedId(prefix: string, size?: number): string;
12
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,wBAAgB,MAAM,CAAC,IAAI,GAAE,MAAW,GAAG,MAAM,CAWhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAW,GAAG,MAAM,CAEpE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ID generation utilities
3
+ */
4
+ const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
5
+ /**
6
+ * Generate a random ID
7
+ */
8
+ export function nanoid(size = 21) {
9
+ let id = '';
10
+ const bytes = new Uint8Array(size);
11
+ // eslint-disable-next-line no-undef
12
+ crypto.getRandomValues(bytes);
13
+ for (let i = 0; i < size; i++) {
14
+ id += ALPHABET[bytes[i] % ALPHABET.length];
15
+ }
16
+ return id;
17
+ }
18
+ /**
19
+ * Generate a prefixed ID
20
+ */
21
+ export function prefixedId(prefix, size = 12) {
22
+ return `${prefix}_${nanoid(size)}`;
23
+ }
24
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,QAAQ,GAAG,sCAAsC,CAAC;AAExD;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,OAAe,EAAE;IACtC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,oCAAoC;IACpC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAe,EAAE;IAC1D,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;AACrC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for ID generation utilities
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=id.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.test.d.ts","sourceRoot":"","sources":["../../src/utils/id.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}