@joshski/dust 0.1.102 → 0.1.103

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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Audit file validation for .dust/config/audits/ markdown files.
3
+ *
4
+ * Custom audits must follow the same structure as stock audits:
5
+ * - Opening description after H1 title
6
+ * - ## Scope section
7
+ * - ## Blocked By section
8
+ * - ## Definition of Done section
9
+ */
10
+ import type { ParsedArtifact } from '../../artifacts/parsed-artifact';
11
+ import type { Violation } from './types';
12
+ /**
13
+ * Validates that an audit file contains all required section headings.
14
+ */
15
+ export declare function validateAuditHeadings(artifact: ParsedArtifact): Violation[];
@@ -39,6 +39,8 @@ export interface IterationOptions {
39
39
  proxyPort?: number;
40
40
  /** Branch name when working on a non-default branch */
41
41
  branch?: string;
42
+ /** Trace ID for correlating events across processes */
43
+ traceId?: string;
42
44
  }
43
45
  export declare function findAvailableTasks(dependencies: CommandDependencies): Promise<{
44
46
  tasks: UnblockedTask[];
package/dist/patch.js CHANGED
@@ -225,6 +225,22 @@ function createOverlayFileSystem(base, patchFiles, deletedPaths = new Set) {
225
225
  };
226
226
  }
227
227
 
228
+ // lib/lint/validators/audit-validator.ts
229
+ var REQUIRED_AUDIT_HEADINGS = ["Scope", "Blocked By", "Definition of Done"];
230
+ function validateAuditHeadings(artifact) {
231
+ const violations = [];
232
+ const sectionHeadings = new Set(artifact.sections.map((s) => s.heading));
233
+ for (const heading of REQUIRED_AUDIT_HEADINGS) {
234
+ if (!sectionHeadings.has(heading)) {
235
+ violations.push({
236
+ file: artifact.filePath,
237
+ message: `Missing required heading: "## ${heading}"`
238
+ });
239
+ }
240
+ }
241
+ return violations;
242
+ }
243
+
228
244
  // lib/lint/validators/content-validator.ts
229
245
  var REQUIRED_TASK_HEADINGS = ["Blocked By", "Definition of Done"];
230
246
  var MAX_OPENING_SENTENCE_LENGTH = 150;
@@ -519,6 +535,12 @@ function validateWorkflowTaskBodySection(artifact, ideasPath, fileSystem) {
519
535
  }
520
536
  if (!matchedPrefix)
521
537
  return violations;
538
+ if (matchedPrefix === "Expedite Idea: ") {
539
+ const hasIdeaDescriptionSection = artifact.sections.some((s) => s.heading === "Idea Description" && s.level === 2);
540
+ if (hasIdeaDescriptionSection) {
541
+ return violations;
542
+ }
543
+ }
522
544
  const expectedHeading = WORKFLOW_PREFIX_TO_SECTION[matchedPrefix];
523
545
  const section = artifact.sections.find((s) => s.heading === expectedHeading && s.level === 2);
524
546
  if (!section) {
@@ -816,6 +838,7 @@ async function parseArtifacts(fileSystem, dustPath) {
816
838
  tasks: []
817
839
  };
818
840
  const rootFiles = [];
841
+ const customAudits = [];
819
842
  const violations = [];
820
843
  let rootEntries;
821
844
  try {
@@ -874,11 +897,40 @@ async function parseArtifacts(fileSystem, dustPath) {
874
897
  byType[dir].push(artifact);
875
898
  }
876
899
  }
900
+ const auditsPath = `${dustPath}/config/audits`;
901
+ let auditEntries;
902
+ try {
903
+ auditEntries = await fileSystem.readdir(auditsPath);
904
+ } catch (error) {
905
+ if (error.code === "ENOENT") {
906
+ auditEntries = [];
907
+ } else {
908
+ throw error;
909
+ }
910
+ }
911
+ for (const entry of auditEntries) {
912
+ if (!entry.endsWith(".md"))
913
+ continue;
914
+ const filePath = `${auditsPath}/${entry}`;
915
+ let content;
916
+ try {
917
+ content = await fileSystem.readFile(filePath);
918
+ } catch (error) {
919
+ if (error.code === "ENOENT") {
920
+ continue;
921
+ }
922
+ throw error;
923
+ }
924
+ const artifact = parseArtifact(filePath, content);
925
+ artifacts.set(filePath, artifact);
926
+ customAudits.push(artifact);
927
+ }
877
928
  return {
878
929
  context: {
879
930
  artifacts,
880
931
  byType,
881
932
  rootFiles,
933
+ customAudits,
882
934
  dustPath,
883
935
  fileSystem
884
936
  },
@@ -887,7 +939,7 @@ async function parseArtifacts(fileSystem, dustPath) {
887
939
  }
888
940
  function validateArtifacts(context) {
889
941
  const violations = [];
890
- const { byType, rootFiles, dustPath, fileSystem } = context;
942
+ const { byType, rootFiles, customAudits, dustPath, fileSystem } = context;
891
943
  const ideasPath = `${dustPath}/ideas`;
892
944
  for (const artifact of rootFiles) {
893
945
  violations.push(...validateLinks(artifact, fileSystem));
@@ -931,6 +983,18 @@ function validateArtifacts(context) {
931
983
  }
932
984
  violations.push(...validateBidirectionalLinks(allPrincipleRelationships));
933
985
  violations.push(...validateNoCycles(allPrincipleRelationships));
986
+ for (const artifact of customAudits) {
987
+ const filenameViolation = validateFilename(artifact.filePath);
988
+ if (filenameViolation)
989
+ violations.push(filenameViolation);
990
+ const openingSentenceViolation = validateOpeningSentence(artifact);
991
+ if (openingSentenceViolation)
992
+ violations.push(openingSentenceViolation);
993
+ const lengthViolation = validateOpeningSentenceLength(artifact);
994
+ if (lengthViolation)
995
+ violations.push(lengthViolation);
996
+ violations.push(...validateAuditHeadings(artifact));
997
+ }
934
998
  return violations;
935
999
  }
936
1000
 
package/dist/session.d.ts CHANGED
@@ -3,9 +3,11 @@ export declare const DUST_UNATTENDED = "DUST_UNATTENDED";
3
3
  export declare const DUST_SKIP_AGENT = "DUST_SKIP_AGENT";
4
4
  export declare const DUST_REPOSITORY_ID = "DUST_REPOSITORY_ID";
5
5
  export declare const DUST_PROXY_PORT = "DUST_PROXY_PORT";
6
+ export declare const DUST_TRACE_ID = "DUST_TRACE_ID";
6
7
  export declare function isUnattended(session: SessionConfig): boolean;
7
8
  export declare function buildUnattendedEnv(options: {
8
9
  repositoryId?: string;
9
10
  proxyPort?: number;
11
+ traceId?: string;
10
12
  session: SessionConfig;
11
13
  }): Record<string, string>;
@@ -19,6 +19,7 @@ interface ValidationContext {
19
19
  tasks: ParsedArtifact[];
20
20
  };
21
21
  rootFiles: ParsedArtifact[];
22
+ customAudits: ParsedArtifact[];
22
23
  dustPath: string;
23
24
  fileSystem: ReadableFileSystem;
24
25
  }
@@ -222,6 +222,22 @@ var ARTIFACT_TYPES = [
222
222
  "tasks"
223
223
  ];
224
224
 
225
+ // lib/lint/validators/audit-validator.ts
226
+ var REQUIRED_AUDIT_HEADINGS = ["Scope", "Blocked By", "Definition of Done"];
227
+ function validateAuditHeadings(artifact) {
228
+ const violations = [];
229
+ const sectionHeadings = new Set(artifact.sections.map((s) => s.heading));
230
+ for (const heading of REQUIRED_AUDIT_HEADINGS) {
231
+ if (!sectionHeadings.has(heading)) {
232
+ violations.push({
233
+ file: artifact.filePath,
234
+ message: `Missing required heading: "## ${heading}"`
235
+ });
236
+ }
237
+ }
238
+ return violations;
239
+ }
240
+
225
241
  // lib/lint/validators/content-validator.ts
226
242
  var REQUIRED_TASK_HEADINGS = ["Blocked By", "Definition of Done"];
227
243
  var MAX_OPENING_SENTENCE_LENGTH = 150;
@@ -516,6 +532,12 @@ function validateWorkflowTaskBodySection(artifact, ideasPath, fileSystem) {
516
532
  }
517
533
  if (!matchedPrefix)
518
534
  return violations;
535
+ if (matchedPrefix === "Expedite Idea: ") {
536
+ const hasIdeaDescriptionSection = artifact.sections.some((s) => s.heading === "Idea Description" && s.level === 2);
537
+ if (hasIdeaDescriptionSection) {
538
+ return violations;
539
+ }
540
+ }
519
541
  const expectedHeading = WORKFLOW_PREFIX_TO_SECTION[matchedPrefix];
520
542
  const section = artifact.sections.find((s) => s.heading === expectedHeading && s.level === 2);
521
543
  if (!section) {
@@ -813,6 +835,7 @@ async function parseArtifacts(fileSystem, dustPath) {
813
835
  tasks: []
814
836
  };
815
837
  const rootFiles = [];
838
+ const customAudits = [];
816
839
  const violations = [];
817
840
  let rootEntries;
818
841
  try {
@@ -871,11 +894,40 @@ async function parseArtifacts(fileSystem, dustPath) {
871
894
  byType[dir].push(artifact);
872
895
  }
873
896
  }
897
+ const auditsPath = `${dustPath}/config/audits`;
898
+ let auditEntries;
899
+ try {
900
+ auditEntries = await fileSystem.readdir(auditsPath);
901
+ } catch (error) {
902
+ if (error.code === "ENOENT") {
903
+ auditEntries = [];
904
+ } else {
905
+ throw error;
906
+ }
907
+ }
908
+ for (const entry of auditEntries) {
909
+ if (!entry.endsWith(".md"))
910
+ continue;
911
+ const filePath = `${auditsPath}/${entry}`;
912
+ let content;
913
+ try {
914
+ content = await fileSystem.readFile(filePath);
915
+ } catch (error) {
916
+ if (error.code === "ENOENT") {
917
+ continue;
918
+ }
919
+ throw error;
920
+ }
921
+ const artifact = parseArtifact(filePath, content);
922
+ artifacts.set(filePath, artifact);
923
+ customAudits.push(artifact);
924
+ }
874
925
  return {
875
926
  context: {
876
927
  artifacts,
877
928
  byType,
878
929
  rootFiles,
930
+ customAudits,
879
931
  dustPath,
880
932
  fileSystem
881
933
  },
@@ -884,7 +936,7 @@ async function parseArtifacts(fileSystem, dustPath) {
884
936
  }
885
937
  function validateArtifacts(context) {
886
938
  const violations = [];
887
- const { byType, rootFiles, dustPath, fileSystem } = context;
939
+ const { byType, rootFiles, customAudits, dustPath, fileSystem } = context;
888
940
  const ideasPath = `${dustPath}/ideas`;
889
941
  for (const artifact of rootFiles) {
890
942
  violations.push(...validateLinks(artifact, fileSystem));
@@ -928,6 +980,18 @@ function validateArtifacts(context) {
928
980
  }
929
981
  violations.push(...validateBidirectionalLinks(allPrincipleRelationships));
930
982
  violations.push(...validateNoCycles(allPrincipleRelationships));
983
+ for (const artifact of customAudits) {
984
+ const filenameViolation = validateFilename(artifact.filePath);
985
+ if (filenameViolation)
986
+ violations.push(filenameViolation);
987
+ const openingSentenceViolation = validateOpeningSentence(artifact);
988
+ if (openingSentenceViolation)
989
+ violations.push(openingSentenceViolation);
990
+ const lengthViolation = validateOpeningSentenceLength(artifact);
991
+ if (lengthViolation)
992
+ violations.push(lengthViolation);
993
+ violations.push(...validateAuditHeadings(artifact));
994
+ }
931
995
  return violations;
932
996
  }
933
997
 
@@ -0,0 +1,8 @@
1
+ FROM oven/bun:1
2
+ RUN apt-get update && apt-get install -y git nodejs npm curl && rm -rf /var/lib/apt/lists/*
3
+ RUN npm install -g @anthropic-ai/claude-code @openai/codex
4
+ # Install system libraries required by Playwright browsers (Chromium, Firefox, WebKit)
5
+ RUN npx -y playwright install-deps
6
+ RUN useradd -m -s /bin/bash user
7
+ USER user
8
+ WORKDIR /workspace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,6 +55,7 @@
55
55
  "dist",
56
56
  "bin",
57
57
  "lib/istanbul/minimal-reporter.cjs",
58
+ "lib/docker/default.Dockerfile",
58
59
  "biome",
59
60
  ".dust/principles"
60
61
  ],