@joshski/dust 0.1.93 → 0.1.95
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/artifacts/index.d.ts +18 -11
- package/dist/artifacts/workflow-tasks.d.ts +4 -1
- package/dist/artifacts.js +114 -104
- package/dist/audits/checks-audit.d.ts +55 -0
- package/dist/audits.js +1641 -132
- package/dist/bucket/command-events-proxy.d.ts +1 -0
- package/dist/bucket/events.d.ts +7 -9
- package/dist/bucket/paths.d.ts +4 -12
- package/dist/bucket/repository-loop.d.ts +2 -1
- package/dist/bucket/repository.d.ts +8 -0
- package/dist/bucket/server-messages.d.ts +1 -0
- package/dist/bucket/tool-prompt.d.ts +10 -1
- package/dist/cli/commands/next.d.ts +6 -0
- package/dist/cli/types.d.ts +2 -0
- package/dist/config/settings.d.ts +4 -3
- package/dist/dust.js +4429 -2489
- package/dist/env-config.d.ts +103 -0
- package/dist/logging/index.d.ts +20 -1
- package/dist/logging.js +19 -10
- package/dist/loop/events.d.ts +52 -0
- package/dist/loop/git-pull.d.ts +9 -0
- package/dist/loop/iteration.d.ts +42 -0
- package/dist/loop/wire-events.d.ts +5 -0
- package/dist/proxy/claude-api-proxy.d.ts +28 -0
- package/dist/session.d.ts +4 -2
- package/dist/validation/index.d.ts +4 -1
- package/dist/validation.js +61 -33
- package/lib/istanbul/minimal-reporter.cjs +1 -1
- package/package.json +2 -2
- package/dist/cli/commands/loop.d.ts +0 -118
- /package/dist/cli/{commands → shared}/agent-shared.d.ts +0 -0
|
@@ -3,7 +3,7 @@ import { type Fact } from './facts';
|
|
|
3
3
|
import { type Idea, type IdeaOpenQuestion, type IdeaOption, parseOpenQuestions } from './ideas';
|
|
4
4
|
import { type Principle } from './principles';
|
|
5
5
|
import { type Task } from './tasks';
|
|
6
|
-
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, type WorkflowTaskMatch, type WorkflowTaskType } from './workflow-tasks';
|
|
6
|
+
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, parseResolvedQuestions, type WorkflowTaskMatch, type WorkflowTaskType } from './workflow-tasks';
|
|
7
7
|
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedCaptureIdeaTask, Principle, Task, WorkflowTaskMatch, WorkflowTaskType, };
|
|
8
8
|
export interface TaskGraphNode {
|
|
9
9
|
task: Task;
|
|
@@ -16,10 +16,10 @@ export interface TaskGraph {
|
|
|
16
16
|
to: string;
|
|
17
17
|
}>;
|
|
18
18
|
}
|
|
19
|
-
export { CAPTURE_IDEA_PREFIX, findAllWorkflowTasks, parseOpenQuestions };
|
|
19
|
+
export { CAPTURE_IDEA_PREFIX, findAllWorkflowTasks, parseOpenQuestions, parseResolvedQuestions, };
|
|
20
20
|
export type { IdeaInProgress };
|
|
21
21
|
export type ArtifactType = 'ideas' | 'tasks' | 'principles' | 'facts';
|
|
22
|
-
export interface
|
|
22
|
+
export interface ReadOnlyArtifactsRepository {
|
|
23
23
|
artifactPath(type: ArtifactType, slug: string): string;
|
|
24
24
|
parseIdea(options: {
|
|
25
25
|
slug: string;
|
|
@@ -37,6 +37,15 @@ export interface ArtifactsRepository {
|
|
|
37
37
|
slug: string;
|
|
38
38
|
}): Promise<Task>;
|
|
39
39
|
listTasks(): Promise<string[]>;
|
|
40
|
+
findWorkflowTaskForIdea(options: {
|
|
41
|
+
ideaSlug: string;
|
|
42
|
+
}): Promise<WorkflowTaskMatch | null>;
|
|
43
|
+
parseCaptureIdeaTask(options: {
|
|
44
|
+
taskSlug: string;
|
|
45
|
+
}): Promise<ParsedCaptureIdeaTask | null>;
|
|
46
|
+
buildTaskGraph(): Promise<TaskGraph>;
|
|
47
|
+
}
|
|
48
|
+
export interface ArtifactsRepository extends ReadOnlyArtifactsRepository {
|
|
40
49
|
createRefineIdeaTask(options: {
|
|
41
50
|
ideaSlug: string;
|
|
42
51
|
description?: string;
|
|
@@ -51,19 +60,17 @@ export interface ArtifactsRepository {
|
|
|
51
60
|
description?: string;
|
|
52
61
|
dustCommand?: string;
|
|
53
62
|
}): Promise<CreateIdeaTransitionTaskResult>;
|
|
63
|
+
createExpediteIdeaTask(options: {
|
|
64
|
+
ideaSlug: string;
|
|
65
|
+
description?: string;
|
|
66
|
+
dustCommand?: string;
|
|
67
|
+
}): Promise<CreateIdeaTransitionTaskResult>;
|
|
54
68
|
createIdeaTask(options: {
|
|
55
69
|
title: string;
|
|
56
70
|
description: string;
|
|
57
71
|
expedite?: boolean;
|
|
58
72
|
dustCommand?: string;
|
|
59
73
|
}): Promise<CreateIdeaTransitionTaskResult>;
|
|
60
|
-
findWorkflowTaskForIdea(options: {
|
|
61
|
-
ideaSlug: string;
|
|
62
|
-
}): Promise<WorkflowTaskMatch | null>;
|
|
63
|
-
parseCaptureIdeaTask(options: {
|
|
64
|
-
taskSlug: string;
|
|
65
|
-
}): Promise<ParsedCaptureIdeaTask | null>;
|
|
66
|
-
buildTaskGraph(): Promise<TaskGraph>;
|
|
67
74
|
}
|
|
68
75
|
export declare function buildArtifactsRepository(fileSystem: FileSystem, dustPath: string): ArtifactsRepository;
|
|
69
|
-
export declare function buildReadOnlyArtifactsRepository(fileSystem: ReadableFileSystem, dustPath: string):
|
|
76
|
+
export declare function buildReadOnlyArtifactsRepository(fileSystem: ReadableFileSystem, dustPath: string): ReadOnlyArtifactsRepository;
|
|
@@ -21,11 +21,12 @@ export interface ParsedCaptureIdeaTask {
|
|
|
21
21
|
* 6. Add .md extension
|
|
22
22
|
*/
|
|
23
23
|
export declare function titleToFilename(title: string): string;
|
|
24
|
-
export type WorkflowTaskType = 'refine-idea' | 'decompose-idea' | 'shelve-idea';
|
|
24
|
+
export type WorkflowTaskType = 'refine-idea' | 'decompose-idea' | 'shelve-idea' | 'expedite-idea';
|
|
25
25
|
export interface WorkflowTaskMatch {
|
|
26
26
|
type: WorkflowTaskType;
|
|
27
27
|
ideaSlug: string;
|
|
28
28
|
taskSlug: string;
|
|
29
|
+
resolvedQuestions: OpenQuestionResponse[];
|
|
29
30
|
}
|
|
30
31
|
export interface AllWorkflowTasks {
|
|
31
32
|
captureIdeaTasks: IdeaInProgress[];
|
|
@@ -45,9 +46,11 @@ export interface DecomposeIdeaOptions {
|
|
|
45
46
|
description?: string;
|
|
46
47
|
openQuestionResponses?: OpenQuestionResponse[];
|
|
47
48
|
}
|
|
49
|
+
export declare function parseResolvedQuestions(content: string): OpenQuestionResponse[];
|
|
48
50
|
export declare function createRefineIdeaTask(fileSystem: FileSystem, dustPath: string, ideaSlug: string, description?: string, openQuestionResponses?: OpenQuestionResponse[], dustCommand?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
49
51
|
export declare function decomposeIdea(fileSystem: FileSystem, dustPath: string, options: DecomposeIdeaOptions, dustCommand?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
50
52
|
export declare function createShelveIdeaTask(fileSystem: FileSystem, dustPath: string, ideaSlug: string, description?: string, _dustCommand?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
53
|
+
export declare function createExpediteIdeaTask(fileSystem: FileSystem, dustPath: string, ideaSlug: string, description?: string, dustCommand?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
51
54
|
export declare function createIdeaTask(fileSystem: FileSystem, dustPath: string, options: {
|
|
52
55
|
title: string;
|
|
53
56
|
description: string;
|
package/dist/artifacts.js
CHANGED
|
@@ -92,7 +92,21 @@ function parseOpenQuestions(content) {
|
|
|
92
92
|
currentQuestion = null;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
let inCodeFence = false;
|
|
95
96
|
for (const line of lines) {
|
|
97
|
+
if (line.startsWith("```")) {
|
|
98
|
+
inCodeFence = !inCodeFence;
|
|
99
|
+
if (currentOption) {
|
|
100
|
+
descriptionLines.push(line);
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (inCodeFence) {
|
|
105
|
+
if (currentOption) {
|
|
106
|
+
descriptionLines.push(line);
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
96
110
|
if (line.startsWith("## ")) {
|
|
97
111
|
if (inOpenQuestions) {
|
|
98
112
|
flushQuestion();
|
|
@@ -240,9 +254,9 @@ function extractDefinitionOfDone(content) {
|
|
|
240
254
|
continue;
|
|
241
255
|
if (line.startsWith("# "))
|
|
242
256
|
break;
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
items.push(
|
|
257
|
+
const listMatch = line.match(/^-\s+(.+)$/);
|
|
258
|
+
if (listMatch) {
|
|
259
|
+
items.push(listMatch[1].trim());
|
|
246
260
|
}
|
|
247
261
|
}
|
|
248
262
|
return items;
|
|
@@ -293,13 +307,21 @@ async function readWorkflowHint(fileSystem, dustPath, workflowType) {
|
|
|
293
307
|
var WORKFLOW_SECTION_HEADINGS = [
|
|
294
308
|
{ type: "refine-idea", heading: "Refines Idea" },
|
|
295
309
|
{ type: "decompose-idea", heading: "Decomposes Idea" },
|
|
296
|
-
{ type: "shelve-idea", heading: "Shelves Idea" }
|
|
310
|
+
{ type: "shelve-idea", heading: "Shelves Idea" },
|
|
311
|
+
{ type: "expedite-idea", heading: "Expedites Idea" }
|
|
297
312
|
];
|
|
298
313
|
function extractIdeaSlugFromSection(content, sectionHeading) {
|
|
299
314
|
const lines = content.split(`
|
|
300
315
|
`);
|
|
301
316
|
let inSection = false;
|
|
317
|
+
let inCodeFence = false;
|
|
302
318
|
for (const line of lines) {
|
|
319
|
+
if (line.startsWith("```")) {
|
|
320
|
+
inCodeFence = !inCodeFence;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (inCodeFence)
|
|
324
|
+
continue;
|
|
303
325
|
if (line.startsWith("## ")) {
|
|
304
326
|
inSection = line.trimEnd() === `## ${sectionHeading}`;
|
|
305
327
|
continue;
|
|
@@ -327,7 +349,7 @@ async function findAllWorkflowTasks(fileSystem, dustPath) {
|
|
|
327
349
|
return { captureIdeaTasks, workflowTasksByIdeaSlug };
|
|
328
350
|
}
|
|
329
351
|
const files = await fileSystem.readdir(tasksPath);
|
|
330
|
-
for (const file of files.filter((f) => f.endsWith(".md")).
|
|
352
|
+
for (const file of files.filter((f) => f.endsWith(".md")).toSorted()) {
|
|
331
353
|
const content = await fileSystem.readFile(`${tasksPath}/${file}`);
|
|
332
354
|
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
333
355
|
if (!titleMatch)
|
|
@@ -351,7 +373,8 @@ async function findAllWorkflowTasks(fileSystem, dustPath) {
|
|
|
351
373
|
workflowTasksByIdeaSlug.set(linkedSlug, {
|
|
352
374
|
type,
|
|
353
375
|
ideaSlug: linkedSlug,
|
|
354
|
-
taskSlug
|
|
376
|
+
taskSlug,
|
|
377
|
+
resolvedQuestions: parseResolvedQuestions(content)
|
|
355
378
|
});
|
|
356
379
|
}
|
|
357
380
|
}
|
|
@@ -368,13 +391,18 @@ async function findWorkflowTaskForIdea(fileSystem, dustPath, ideaSlug) {
|
|
|
368
391
|
return null;
|
|
369
392
|
}
|
|
370
393
|
const files = await fileSystem.readdir(tasksPath);
|
|
371
|
-
for (const file of files.filter((f) => f.endsWith(".md")).
|
|
394
|
+
for (const file of files.filter((f) => f.endsWith(".md")).toSorted()) {
|
|
372
395
|
const content = await fileSystem.readFile(`${tasksPath}/${file}`);
|
|
373
396
|
for (const { type, heading } of WORKFLOW_SECTION_HEADINGS) {
|
|
374
397
|
const linkedSlug = extractIdeaSlugFromSection(content, heading);
|
|
375
398
|
if (linkedSlug === ideaSlug) {
|
|
376
399
|
const taskSlug = file.replace(/\.md$/, "");
|
|
377
|
-
return {
|
|
400
|
+
return {
|
|
401
|
+
type,
|
|
402
|
+
ideaSlug,
|
|
403
|
+
taskSlug,
|
|
404
|
+
resolvedQuestions: parseResolvedQuestions(content)
|
|
405
|
+
};
|
|
378
406
|
}
|
|
379
407
|
}
|
|
380
408
|
}
|
|
@@ -403,6 +431,46 @@ ${sections.join(`
|
|
|
403
431
|
`)}
|
|
404
432
|
`;
|
|
405
433
|
}
|
|
434
|
+
function parseResolvedQuestions(content) {
|
|
435
|
+
const lines = content.split(`
|
|
436
|
+
`);
|
|
437
|
+
const results = [];
|
|
438
|
+
let inSection = false;
|
|
439
|
+
let currentQuestion = null;
|
|
440
|
+
let inCodeFence = false;
|
|
441
|
+
for (const line of lines) {
|
|
442
|
+
if (line.startsWith("```")) {
|
|
443
|
+
inCodeFence = !inCodeFence;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (inCodeFence)
|
|
447
|
+
continue;
|
|
448
|
+
if (line.startsWith("## ")) {
|
|
449
|
+
inSection = line.trimEnd() === "## Resolved Questions";
|
|
450
|
+
currentQuestion = null;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (!inSection)
|
|
454
|
+
continue;
|
|
455
|
+
if (line.startsWith("# "))
|
|
456
|
+
break;
|
|
457
|
+
if (line.startsWith("### ")) {
|
|
458
|
+
currentQuestion = line.slice(4).trimEnd();
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (currentQuestion !== null) {
|
|
462
|
+
const decisionMatch = line.match(/^\*\*Decision:\*\*\s*(.+)$/);
|
|
463
|
+
if (decisionMatch) {
|
|
464
|
+
results.push({
|
|
465
|
+
question: currentQuestion,
|
|
466
|
+
chosenOption: decisionMatch[1].trimEnd()
|
|
467
|
+
});
|
|
468
|
+
currentQuestion = null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return results;
|
|
473
|
+
}
|
|
406
474
|
function renderIdeaSection(ideaSection) {
|
|
407
475
|
return `## ${ideaSection.heading}
|
|
408
476
|
|
|
@@ -440,7 +508,7 @@ ${repositoryHintsSection}
|
|
|
440
508
|
|
|
441
509
|
## Definition of Done
|
|
442
510
|
|
|
443
|
-
${definitionOfDone.map((item) => `-
|
|
511
|
+
${definitionOfDone.map((item) => `- ${item}`).join(`
|
|
444
512
|
`)}
|
|
445
513
|
`;
|
|
446
514
|
}
|
|
@@ -486,6 +554,14 @@ async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
|
486
554
|
async function createShelveIdeaTask(fileSystem, dustPath, ideaSlug, description, _dustCommand) {
|
|
487
555
|
return createIdeaTransitionTask(fileSystem, dustPath, "shelve-idea", "Shelve Idea: ", ideaSlug, (ideaTitle) => `Archive this idea and remove it from the active backlog. See [${ideaTitle}](../ideas/${ideaSlug}.md).`, ["Idea file is deleted", "Rationale is recorded in the commit message"], "Shelves Idea", { description });
|
|
488
556
|
}
|
|
557
|
+
async function createExpediteIdeaTask(fileSystem, dustPath, ideaSlug, description, dustCommand) {
|
|
558
|
+
const cmd = dustCommand ?? "dust";
|
|
559
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "expedite-idea", "Expedite Idea: ", ideaSlug, (ideaTitle) => `Research this idea briefly. If confident the implementation is straightforward (clear scope, minimal risk, no open questions), implement directly and commit. Otherwise, create one or more narrowly-scoped task files in \`.dust/tasks/\`. Run \`${cmd} principles\` and \`${cmd} facts\` for relevant context. See [${ideaTitle}](../ideas/${ideaSlug}.md).`, [
|
|
560
|
+
"Idea is implemented directly OR one or more new tasks are created in `.dust/tasks/`",
|
|
561
|
+
"If tasks were created, they link to relevant principles from `.dust/principles/`",
|
|
562
|
+
"Changes are committed with a clear commit message"
|
|
563
|
+
], "Expedites Idea", { description });
|
|
564
|
+
}
|
|
489
565
|
async function createIdeaTask(fileSystem, dustPath, options) {
|
|
490
566
|
const { title, description, expedite, dustCommand } = options;
|
|
491
567
|
const cmd = dustCommand ?? "dust";
|
|
@@ -517,9 +593,9 @@ ${repositoryHintsSection2}
|
|
|
517
593
|
|
|
518
594
|
## Definition of Done
|
|
519
595
|
|
|
520
|
-
-
|
|
521
|
-
-
|
|
522
|
-
-
|
|
596
|
+
- Idea is implemented directly OR one or more new tasks are created in \`.dust/tasks/\`
|
|
597
|
+
- If tasks were created, they link to relevant principles from \`.dust/principles/\`
|
|
598
|
+
- Changes are committed with a clear commit message
|
|
523
599
|
`;
|
|
524
600
|
await fileSystem.writeFile(filePath2, content2);
|
|
525
601
|
return { filePath: filePath2 };
|
|
@@ -545,11 +621,11 @@ ${repositoryHintsSection}
|
|
|
545
621
|
|
|
546
622
|
## Definition of Done
|
|
547
623
|
|
|
548
|
-
-
|
|
549
|
-
-
|
|
550
|
-
-
|
|
551
|
-
-
|
|
552
|
-
-
|
|
624
|
+
- One or more idea files are created in \`.dust/ideas/\`
|
|
625
|
+
- Each idea file has an H1 title matching its content
|
|
626
|
+
- Idea includes relevant context from codebase exploration
|
|
627
|
+
- Open questions are added for any ambiguous or underspecified aspects
|
|
628
|
+
- Open questions follow the required heading format and focus on high-value decisions
|
|
553
629
|
`;
|
|
554
630
|
await fileSystem.writeFile(filePath, content);
|
|
555
631
|
return { filePath };
|
|
@@ -585,7 +661,7 @@ async function parseCaptureIdeaTask(fileSystem, dustPath, taskSlug) {
|
|
|
585
661
|
}
|
|
586
662
|
|
|
587
663
|
// lib/artifacts/index.ts
|
|
588
|
-
function
|
|
664
|
+
function buildReadOperations(fileSystem, dustPath) {
|
|
589
665
|
return {
|
|
590
666
|
artifactPath(type, slug) {
|
|
591
667
|
return `${dustPath}/${type}/${slug}.md`;
|
|
@@ -599,7 +675,7 @@ function buildArtifactsRepository(fileSystem, dustPath) {
|
|
|
599
675
|
return [];
|
|
600
676
|
}
|
|
601
677
|
const files = await fileSystem.readdir(ideasPath);
|
|
602
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).
|
|
678
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).toSorted();
|
|
603
679
|
},
|
|
604
680
|
async parsePrinciple(options) {
|
|
605
681
|
return parsePrinciple(fileSystem, dustPath, options.slug);
|
|
@@ -610,7 +686,7 @@ function buildArtifactsRepository(fileSystem, dustPath) {
|
|
|
610
686
|
return [];
|
|
611
687
|
}
|
|
612
688
|
const files = await fileSystem.readdir(principlesPath);
|
|
613
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).
|
|
689
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).toSorted();
|
|
614
690
|
},
|
|
615
691
|
async parseFact(options) {
|
|
616
692
|
return parseFact(fileSystem, dustPath, options.slug);
|
|
@@ -621,7 +697,7 @@ function buildArtifactsRepository(fileSystem, dustPath) {
|
|
|
621
697
|
return [];
|
|
622
698
|
}
|
|
623
699
|
const files = await fileSystem.readdir(factsPath);
|
|
624
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).
|
|
700
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).toSorted();
|
|
625
701
|
},
|
|
626
702
|
async parseTask(options) {
|
|
627
703
|
return parseTask(fileSystem, dustPath, options.slug);
|
|
@@ -632,19 +708,7 @@ function buildArtifactsRepository(fileSystem, dustPath) {
|
|
|
632
708
|
return [];
|
|
633
709
|
}
|
|
634
710
|
const files = await fileSystem.readdir(tasksPath);
|
|
635
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).
|
|
636
|
-
},
|
|
637
|
-
async createRefineIdeaTask(options) {
|
|
638
|
-
return createRefineIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.openQuestionResponses, options.dustCommand);
|
|
639
|
-
},
|
|
640
|
-
async createDecomposeIdeaTask(options) {
|
|
641
|
-
return decomposeIdea(fileSystem, dustPath, options, options.dustCommand);
|
|
642
|
-
},
|
|
643
|
-
async createShelveIdeaTask(options) {
|
|
644
|
-
return createShelveIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.dustCommand);
|
|
645
|
-
},
|
|
646
|
-
async createIdeaTask(options) {
|
|
647
|
-
return createIdeaTask(fileSystem, dustPath, options);
|
|
711
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).toSorted();
|
|
648
712
|
},
|
|
649
713
|
async findWorkflowTaskForIdea(options) {
|
|
650
714
|
return findWorkflowTaskForIdea(fileSystem, dustPath, options.ideaSlug);
|
|
@@ -675,85 +739,31 @@ function buildArtifactsRepository(fileSystem, dustPath) {
|
|
|
675
739
|
}
|
|
676
740
|
};
|
|
677
741
|
}
|
|
678
|
-
function
|
|
742
|
+
function buildArtifactsRepository(fileSystem, dustPath) {
|
|
679
743
|
return {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
async parseIdea(options) {
|
|
684
|
-
return parseIdea(fileSystem, dustPath, options.slug);
|
|
685
|
-
},
|
|
686
|
-
async listIdeas() {
|
|
687
|
-
const ideasPath = `${dustPath}/ideas`;
|
|
688
|
-
if (!fileSystem.exists(ideasPath)) {
|
|
689
|
-
return [];
|
|
690
|
-
}
|
|
691
|
-
const files = await fileSystem.readdir(ideasPath);
|
|
692
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).sort();
|
|
693
|
-
},
|
|
694
|
-
async parsePrinciple(options) {
|
|
695
|
-
return parsePrinciple(fileSystem, dustPath, options.slug);
|
|
696
|
-
},
|
|
697
|
-
async listPrinciples() {
|
|
698
|
-
const principlesPath = `${dustPath}/principles`;
|
|
699
|
-
if (!fileSystem.exists(principlesPath)) {
|
|
700
|
-
return [];
|
|
701
|
-
}
|
|
702
|
-
const files = await fileSystem.readdir(principlesPath);
|
|
703
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).sort();
|
|
704
|
-
},
|
|
705
|
-
async parseFact(options) {
|
|
706
|
-
return parseFact(fileSystem, dustPath, options.slug);
|
|
707
|
-
},
|
|
708
|
-
async listFacts() {
|
|
709
|
-
const factsPath = `${dustPath}/facts`;
|
|
710
|
-
if (!fileSystem.exists(factsPath)) {
|
|
711
|
-
return [];
|
|
712
|
-
}
|
|
713
|
-
const files = await fileSystem.readdir(factsPath);
|
|
714
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).sort();
|
|
715
|
-
},
|
|
716
|
-
async parseTask(options) {
|
|
717
|
-
return parseTask(fileSystem, dustPath, options.slug);
|
|
744
|
+
...buildReadOperations(fileSystem, dustPath),
|
|
745
|
+
async createRefineIdeaTask(options) {
|
|
746
|
+
return createRefineIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.openQuestionResponses, options.dustCommand);
|
|
718
747
|
},
|
|
719
|
-
async
|
|
720
|
-
|
|
721
|
-
if (!fileSystem.exists(tasksPath)) {
|
|
722
|
-
return [];
|
|
723
|
-
}
|
|
724
|
-
const files = await fileSystem.readdir(tasksPath);
|
|
725
|
-
return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")).sort();
|
|
748
|
+
async createDecomposeIdeaTask(options) {
|
|
749
|
+
return decomposeIdea(fileSystem, dustPath, options, options.dustCommand);
|
|
726
750
|
},
|
|
727
|
-
async
|
|
728
|
-
return
|
|
751
|
+
async createShelveIdeaTask(options) {
|
|
752
|
+
return createShelveIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.dustCommand);
|
|
729
753
|
},
|
|
730
|
-
async
|
|
731
|
-
return
|
|
754
|
+
async createExpediteIdeaTask(options) {
|
|
755
|
+
return createExpediteIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.dustCommand);
|
|
732
756
|
},
|
|
733
|
-
async
|
|
734
|
-
|
|
735
|
-
const allWorkflowTasks = await findAllWorkflowTasks(fileSystem, dustPath);
|
|
736
|
-
const workflowTypeByTaskSlug = new Map;
|
|
737
|
-
for (const match of allWorkflowTasks.workflowTasksByIdeaSlug.values()) {
|
|
738
|
-
workflowTypeByTaskSlug.set(match.taskSlug, match.type);
|
|
739
|
-
}
|
|
740
|
-
const nodes = [];
|
|
741
|
-
const edges = [];
|
|
742
|
-
for (const slug of taskSlugs) {
|
|
743
|
-
const task = await this.parseTask({ slug });
|
|
744
|
-
nodes.push({
|
|
745
|
-
task,
|
|
746
|
-
workflowType: workflowTypeByTaskSlug.get(slug) ?? null
|
|
747
|
-
});
|
|
748
|
-
for (const blockerSlug of task.blockedBy) {
|
|
749
|
-
edges.push({ from: blockerSlug, to: slug });
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return { nodes, edges };
|
|
757
|
+
async createIdeaTask(options) {
|
|
758
|
+
return createIdeaTask(fileSystem, dustPath, options);
|
|
753
759
|
}
|
|
754
760
|
};
|
|
755
761
|
}
|
|
762
|
+
function buildReadOnlyArtifactsRepository(fileSystem, dustPath) {
|
|
763
|
+
return buildReadOperations(fileSystem, dustPath);
|
|
764
|
+
}
|
|
756
765
|
export {
|
|
766
|
+
parseResolvedQuestions,
|
|
757
767
|
parseOpenQuestions,
|
|
758
768
|
findAllWorkflowTasks,
|
|
759
769
|
buildReadOnlyArtifactsRepository,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks Audit - Pure functions for detecting tech stacks and suggesting checks.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the functional core for the checks-audit stock audit.
|
|
5
|
+
* It detects the project's technology ecosystem, identifies configured checks,
|
|
6
|
+
* parses CI configuration files, and suggests missing check categories.
|
|
7
|
+
*/
|
|
8
|
+
import type { DustSettings } from '../cli/types';
|
|
9
|
+
export type Ecosystem = 'javascript' | 'python' | 'go' | 'rust' | 'ruby' | 'php' | 'elixir';
|
|
10
|
+
export interface TechStackDetection {
|
|
11
|
+
ecosystem: Ecosystem;
|
|
12
|
+
indicators: string[];
|
|
13
|
+
packageManager?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CheckOption {
|
|
16
|
+
name: string;
|
|
17
|
+
command: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface CheckSuggestion {
|
|
21
|
+
category: string;
|
|
22
|
+
ecosystem: Ecosystem;
|
|
23
|
+
reason: string;
|
|
24
|
+
suggestedCheck: CheckOption;
|
|
25
|
+
alternatives: CheckOption[];
|
|
26
|
+
detectedIndicators: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface CIFileContent {
|
|
29
|
+
path: string;
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detects tech stacks based on project files.
|
|
34
|
+
*/
|
|
35
|
+
export declare function detectTechStack(projectFiles: string[]): TechStackDetection[];
|
|
36
|
+
/**
|
|
37
|
+
* Extracts check categories from existing dust settings.
|
|
38
|
+
*/
|
|
39
|
+
export declare function detectConfiguredChecks(settings: DustSettings): Set<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Parses CI configuration files to detect what checks run in CI.
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectCIChecks(ciFiles: CIFileContent[]): Set<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Suggests missing checks based on detected tech stack and configured checks.
|
|
46
|
+
*/
|
|
47
|
+
export declare function suggestChecks(techStack: TechStackDetection[], projectFiles: string[], configuredChecks: Set<string>, ciChecks: Set<string>): CheckSuggestion[];
|
|
48
|
+
/**
|
|
49
|
+
* Renders a check suggestion as an idea file content.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderCheckIdea(suggestion: CheckSuggestion, packageManager?: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the checks-audit stock audit template.
|
|
54
|
+
*/
|
|
55
|
+
export declare function checksAuditTemplate(): string;
|