@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.
@@ -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 ArtifactsRepository {
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): Pick<ArtifactsRepository, 'artifactPath' | 'parseIdea' | 'listIdeas' | 'parsePrinciple' | 'listPrinciples' | 'parseFact' | 'listFacts' | 'parseTask' | 'listTasks' | 'findWorkflowTaskForIdea' | 'parseCaptureIdeaTask' | 'buildTaskGraph'>;
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 checklistMatch = line.match(/^-\s+\[[x\s]\]\s+(.+)$/i);
244
- if (checklistMatch) {
245
- items.push(checklistMatch[1].trim());
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")).sort()) {
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")).sort()) {
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 { type, ideaSlug, taskSlug };
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) => `- [ ] ${item}`).join(`
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
- - [ ] Idea is implemented directly OR one or more new tasks are created in \`.dust/tasks/\`
521
- - [ ] If tasks were created, they link to relevant principles from \`.dust/principles/\`
522
- - [ ] Changes are committed with a clear commit message
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
- - [ ] One or more idea files are created in \`.dust/ideas/\`
549
- - [ ] Each idea file has an H1 title matching its content
550
- - [ ] Idea includes relevant context from codebase exploration
551
- - [ ] Open questions are added for any ambiguous or underspecified aspects
552
- - [ ] Open questions follow the required heading format and focus on high-value decisions
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 buildArtifactsRepository(fileSystem, dustPath) {
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$/, "")).sort();
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$/, "")).sort();
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$/, "")).sort();
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$/, "")).sort();
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 buildReadOnlyArtifactsRepository(fileSystem, dustPath) {
742
+ function buildArtifactsRepository(fileSystem, dustPath) {
679
743
  return {
680
- artifactPath(type, slug) {
681
- return `${dustPath}/${type}/${slug}.md`;
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 listTasks() {
720
- const tasksPath = `${dustPath}/tasks`;
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 findWorkflowTaskForIdea(options) {
728
- return findWorkflowTaskForIdea(fileSystem, dustPath, options.ideaSlug);
751
+ async createShelveIdeaTask(options) {
752
+ return createShelveIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.dustCommand);
729
753
  },
730
- async parseCaptureIdeaTask(options) {
731
- return parseCaptureIdeaTask(fileSystem, dustPath, options.taskSlug);
754
+ async createExpediteIdeaTask(options) {
755
+ return createExpediteIdeaTask(fileSystem, dustPath, options.ideaSlug, options.description, options.dustCommand);
732
756
  },
733
- async buildTaskGraph() {
734
- const taskSlugs = await this.listTasks();
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;