@joshski/dust 0.1.63 → 0.1.65

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,58 @@
1
+ language js
2
+
3
+ or {
4
+ `ctx` where {
5
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'ctx'. Use 'context' instead.")
6
+ },
7
+ `deps` where {
8
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'deps'. Use 'dependencies' instead.")
9
+ },
10
+ `fs` where {
11
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'fs'. Use 'fileSystem' instead.")
12
+ },
13
+ `args` where {
14
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'args'. Use 'arguments' instead.")
15
+ },
16
+ `req` where {
17
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'req'. Use 'request' instead.")
18
+ },
19
+ `res` where {
20
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'res'. Use 'response' instead.")
21
+ },
22
+ `err` where {
23
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'err'. Use 'error' instead.")
24
+ },
25
+ `cb` where {
26
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'cb'. Use 'callback' instead.")
27
+ },
28
+ `fn` where {
29
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'fn'. Use a descriptive name instead.")
30
+ },
31
+ `opts` where {
32
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'opts'. Use 'options' instead.")
33
+ },
34
+ `params` where {
35
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'params'. Use 'parameters' instead.")
36
+ },
37
+ `obj` where {
38
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'obj'. Use a descriptive name instead.")
39
+ },
40
+ `val` where {
41
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'val'. Use 'value' instead.")
42
+ },
43
+ `idx` where {
44
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'idx'. Use 'index' instead.")
45
+ },
46
+ `len` where {
47
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'len'. Use 'length' instead.")
48
+ },
49
+ `tmp` where {
50
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'tmp'. Use a descriptive name instead.")
51
+ },
52
+ `str` where {
53
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'str'. Use 'string' or a descriptive name instead.")
54
+ },
55
+ `num` where {
56
+ register_diagnostic(span=$match, message="Avoid abbreviated name 'num'. Use 'number' or a descriptive name instead.")
57
+ }
58
+ }
package/dist/artifacts.js CHANGED
@@ -353,10 +353,6 @@ ${renderResolvedQuestions(options.resolvedQuestions)}
353
353
 
354
354
  ${openingSentence}
355
355
  ${descriptionParagraph}${resolvedSection}
356
- ## Principles
357
-
358
- (none)
359
-
360
356
  ## Blocked By
361
357
 
362
358
  (none)
@@ -418,10 +414,6 @@ Research this idea thoroughly, then create one or more narrowly-scoped task file
418
414
 
419
415
  ${description}
420
416
 
421
- ## Principles
422
-
423
- (none)
424
-
425
417
  ## Blocked By
426
418
 
427
419
  (none)
@@ -438,28 +430,22 @@ ${description}
438
430
  const taskTitle = `${CAPTURE_IDEA_PREFIX}${title}`;
439
431
  const filename = titleToFilename(taskTitle);
440
432
  const filePath = `${dustPath}/tasks/${filename}`;
441
- const ideaFilename = titleToFilename(title);
442
- const ideaPath = `.dust/ideas/${ideaFilename}`;
443
433
  const content = `# ${taskTitle}
444
434
 
445
- Research this idea thoroughly, then create an idea file at \`${ideaPath}\`. Read the codebase for relevant context, flesh out the description, and identify any ambiguity. Where aspects are unclear or could go multiple ways, add open questions to the idea file. If you add open questions, use \`## Open Questions\` with \`### Question?\` headings and one or more \`#### Option\` headings beneath each question, and only add questions that are meaningful decisions worth asking. Review \`.dust/principles/\` and \`.dust/facts/\` for relevant context.
435
+ Research this idea thoroughly, then create one or more idea files in \`.dust/ideas/\`. Read the codebase for relevant context, flesh out the description, and identify any ambiguity. Where aspects are unclear or could go multiple ways, add open questions to the idea file. If you add open questions, use \`## Open Questions\` with \`### Question?\` headings and one or more \`#### Option\` headings beneath each question, and only add questions that are meaningful decisions worth asking. Review \`.dust/principles/\` and \`.dust/facts/\` for relevant context.
446
436
 
447
437
  ## Idea Description
448
438
 
449
439
  ${description}
450
440
 
451
- ## Principles
452
-
453
- (none)
454
-
455
441
  ## Blocked By
456
442
 
457
443
  (none)
458
444
 
459
445
  ## Definition of Done
460
446
 
461
- - [ ] Idea file exists at ${ideaPath}
462
- - [ ] Idea file has an H1 title matching "${title}"
447
+ - [ ] One or more idea files are created in \`.dust/ideas/\`
448
+ - [ ] Each idea file has an H1 title matching its content
463
449
  - [ ] Idea includes relevant context from codebase exploration
464
450
  - [ ] Open questions are added for any ambiguous or underspecified aspects
465
451
  - [ ] Open questions follow the required heading format and focus on high-value decisions
package/dist/audits.js CHANGED
@@ -63,11 +63,50 @@ function dedent(strings, ...values) {
63
63
  }
64
64
 
65
65
  // lib/audits/stock-audits.ts
66
+ var ideasHint = "Review existing ideas in `./.dust/ideas/` to understand what has been proposed or considered historically, then create new idea files in `./.dust/ideas/` for any issues you identify, avoiding duplication.";
67
+ function componentReuse() {
68
+ return dedent`
69
+ # Component Reuse
70
+
71
+ Find repeated patterns and code that could be extracted into reusable components.
72
+
73
+ ${ideasHint}
74
+
75
+ ## Scope
76
+
77
+ Focus on these areas:
78
+
79
+ 1. **Repeated patterns** - Similar code blocks that appear multiple times
80
+ 2. **Copy-pasted code** - Near-identical logic across different files
81
+ 3. **Parallel structures** - Code that handles similar cases with minor variations
82
+ 4. **Extraction opportunities** - Logic that could be unified without forcing unrelated concepts together
83
+
84
+ ## Principles
85
+
86
+ - [Reasonably DRY](../principles/reasonably-dry.md)
87
+ - [Decoupled Code](../principles/decoupled-code.md)
88
+ - [Maintainable Codebase](../principles/maintainable-codebase.md)
89
+
90
+ ## Blocked By
91
+
92
+ (none)
93
+
94
+ ## Definition of Done
95
+
96
+ - [ ] Searched for repeated patterns across the codebase
97
+ - [ ] Identified copy-pasted or near-duplicate code
98
+ - [ ] Evaluated each case for whether extraction would be beneficial
99
+ - [ ] Considered whether similar code serves different purposes that may evolve independently
100
+ - [ ] Proposed ideas only for extractions where duplication is truly about the same concept
101
+ `;
102
+ }
66
103
  function agentDeveloperExperience() {
67
104
  return dedent`
68
105
  # Agent Developer Experience
69
106
 
70
- Review the codebase to ensure agents have everything they need to operate effectively. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
107
+ Review the codebase to ensure agents have everything they need to operate effectively.
108
+
109
+ ${ideasHint}
71
110
 
72
111
  ## Scope
73
112
 
@@ -100,7 +139,9 @@ function deadCode() {
100
139
  return dedent`
101
140
  # Dead Code
102
141
 
103
- Find and remove unused code to improve maintainability and reduce bundle size. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
142
+ Find and remove unused code to improve maintainability and reduce bundle size.
143
+
144
+ ${ideasHint}
104
145
 
105
146
  ## Scope
106
147
 
@@ -135,7 +176,9 @@ function factsVerification() {
135
176
  return dedent`
136
177
  # Facts Verification
137
178
 
138
- Review \`.dust/facts/\` to ensure documented facts match current reality. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
179
+ Review \`.dust/facts/\` to ensure documented facts match current reality.
180
+
181
+ ${ideasHint}
139
182
 
140
183
  ## Scope
141
184
 
@@ -168,7 +211,9 @@ function ideasFromCommits() {
168
211
  return dedent`
169
212
  # Ideas from Commits
170
213
 
171
- Review recent commit history to identify follow-up improvement ideas. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues or opportunities you identify, avoiding duplication.
214
+ Review recent commit history to identify follow-up improvement ideas.
215
+
216
+ ${ideasHint}
172
217
 
173
218
  ## Scope
174
219
 
@@ -200,7 +245,9 @@ function ideasFromPrinciples() {
200
245
  return dedent`
201
246
  # Ideas from Principles
202
247
 
203
- Review \`.dust/principles/\` to generate new improvement ideas. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues or opportunities you identify, avoiding duplication.
248
+ Review \`.dust/principles/\` to generate new improvement ideas.
249
+
250
+ ${ideasHint}
204
251
 
205
252
  ## Scope
206
253
 
@@ -231,7 +278,9 @@ function performanceReview() {
231
278
  return dedent`
232
279
  # Performance Review
233
280
 
234
- Review the application for performance issues and optimization opportunities. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
281
+ Review the application for performance issues and optimization opportunities.
282
+
283
+ ${ideasHint}
235
284
 
236
285
  ## Scope
237
286
 
@@ -264,7 +313,9 @@ function securityReview() {
264
313
  return dedent`
265
314
  # Security Review
266
315
 
267
- Review the codebase for common security vulnerabilities and misconfigurations. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
316
+ Review the codebase for common security vulnerabilities and misconfigurations.
317
+
318
+ ${ideasHint}
268
319
 
269
320
  ## Scope
270
321
 
@@ -299,7 +350,9 @@ function staleIdeas() {
299
350
  return dedent`
300
351
  # Stale Ideas
301
352
 
302
- Review \`.dust/ideas/\` to identify ideas that have become stale or irrelevant. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
353
+ Review \`.dust/ideas/\` to identify ideas that have become stale or irrelevant.
354
+
355
+ ${ideasHint}
303
356
 
304
357
  ## Scope
305
358
 
@@ -331,7 +384,9 @@ function testCoverage() {
331
384
  return dedent`
332
385
  # Test Coverage
333
386
 
334
- Identify untested code paths and areas that need additional test coverage. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
387
+ Identify untested code paths and areas that need additional test coverage.
388
+
389
+ ${ideasHint}
335
390
 
336
391
  ## Scope
337
392
 
@@ -361,6 +416,7 @@ function testCoverage() {
361
416
  }
362
417
  var stockAuditFunctions = {
363
418
  "agent-developer-experience": agentDeveloperExperience,
419
+ "component-reuse": componentReuse,
364
420
  "dead-code": deadCode,
365
421
  "facts-verification": factsVerification,
366
422
  "ideas-from-commits": ideasFromCommits,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Biome path export
3
+ *
4
+ * Provides the path to the biome directory containing custom GritQL lint rules.
5
+ * Downstream users can reference this path in their biome.json plugins array.
6
+ */
7
+ /**
8
+ * Absolute path to the biome directory containing custom GritQL lint rules.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { biomePath } from "@joshski/dust/biome";
13
+ * // Returns: "/path/to/node_modules/@joshski/dust/biome"
14
+ * ```
15
+ */
16
+ export declare const biomePath: string;
package/dist/biome.js ADDED
@@ -0,0 +1,8 @@
1
+ // lib/biome/index.ts
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ var currentDir = dirname(fileURLToPath(import.meta.url));
5
+ var biomePath = join(currentDir, "..", "biome");
6
+ export {
7
+ biomePath
8
+ };
package/dist/dust.js CHANGED
@@ -3,14 +3,14 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // lib/cli/run.ts
6
- import { existsSync, statSync as statSync2 } from "node:fs";
6
+ import { existsSync, statSync as statSync3 } from "node:fs";
7
7
  import {
8
- chmod as chmod2,
9
- mkdir as mkdir2,
10
- readdir as readdir2,
11
- readFile as readFile2,
8
+ chmod as chmod3,
9
+ mkdir as mkdir3,
10
+ readdir as readdir3,
11
+ readFile as readFile3,
12
12
  rename,
13
- writeFile as writeFile2
13
+ writeFile as writeFile3
14
14
  } from "node:fs/promises";
15
15
 
16
16
  // lib/git/file-sorter.ts
@@ -275,7 +275,7 @@ async function loadSettings(cwd, fileSystem) {
275
275
  }
276
276
 
277
277
  // lib/version.ts
278
- var DUST_VERSION = "0.1.63";
278
+ var DUST_VERSION = "0.1.65";
279
279
 
280
280
  // lib/cli/dedent.ts
281
281
  function dedent(strings, ...values) {
@@ -590,11 +590,50 @@ function extractOpeningSentence(content) {
590
590
  }
591
591
 
592
592
  // lib/audits/stock-audits.ts
593
+ var ideasHint = "Review existing ideas in `./.dust/ideas/` to understand what has been proposed or considered historically, then create new idea files in `./.dust/ideas/` for any issues you identify, avoiding duplication.";
594
+ function componentReuse() {
595
+ return dedent`
596
+ # Component Reuse
597
+
598
+ Find repeated patterns and code that could be extracted into reusable components.
599
+
600
+ ${ideasHint}
601
+
602
+ ## Scope
603
+
604
+ Focus on these areas:
605
+
606
+ 1. **Repeated patterns** - Similar code blocks that appear multiple times
607
+ 2. **Copy-pasted code** - Near-identical logic across different files
608
+ 3. **Parallel structures** - Code that handles similar cases with minor variations
609
+ 4. **Extraction opportunities** - Logic that could be unified without forcing unrelated concepts together
610
+
611
+ ## Principles
612
+
613
+ - [Reasonably DRY](../principles/reasonably-dry.md)
614
+ - [Decoupled Code](../principles/decoupled-code.md)
615
+ - [Maintainable Codebase](../principles/maintainable-codebase.md)
616
+
617
+ ## Blocked By
618
+
619
+ (none)
620
+
621
+ ## Definition of Done
622
+
623
+ - [ ] Searched for repeated patterns across the codebase
624
+ - [ ] Identified copy-pasted or near-duplicate code
625
+ - [ ] Evaluated each case for whether extraction would be beneficial
626
+ - [ ] Considered whether similar code serves different purposes that may evolve independently
627
+ - [ ] Proposed ideas only for extractions where duplication is truly about the same concept
628
+ `;
629
+ }
593
630
  function agentDeveloperExperience() {
594
631
  return dedent`
595
632
  # Agent Developer Experience
596
633
 
597
- Review the codebase to ensure agents have everything they need to operate effectively. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
634
+ Review the codebase to ensure agents have everything they need to operate effectively.
635
+
636
+ ${ideasHint}
598
637
 
599
638
  ## Scope
600
639
 
@@ -627,7 +666,9 @@ function deadCode() {
627
666
  return dedent`
628
667
  # Dead Code
629
668
 
630
- Find and remove unused code to improve maintainability and reduce bundle size. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
669
+ Find and remove unused code to improve maintainability and reduce bundle size.
670
+
671
+ ${ideasHint}
631
672
 
632
673
  ## Scope
633
674
 
@@ -662,7 +703,9 @@ function factsVerification() {
662
703
  return dedent`
663
704
  # Facts Verification
664
705
 
665
- Review \`.dust/facts/\` to ensure documented facts match current reality. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
706
+ Review \`.dust/facts/\` to ensure documented facts match current reality.
707
+
708
+ ${ideasHint}
666
709
 
667
710
  ## Scope
668
711
 
@@ -695,7 +738,9 @@ function ideasFromCommits() {
695
738
  return dedent`
696
739
  # Ideas from Commits
697
740
 
698
- Review recent commit history to identify follow-up improvement ideas. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues or opportunities you identify, avoiding duplication.
741
+ Review recent commit history to identify follow-up improvement ideas.
742
+
743
+ ${ideasHint}
699
744
 
700
745
  ## Scope
701
746
 
@@ -727,7 +772,9 @@ function ideasFromPrinciples() {
727
772
  return dedent`
728
773
  # Ideas from Principles
729
774
 
730
- Review \`.dust/principles/\` to generate new improvement ideas. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues or opportunities you identify, avoiding duplication.
775
+ Review \`.dust/principles/\` to generate new improvement ideas.
776
+
777
+ ${ideasHint}
731
778
 
732
779
  ## Scope
733
780
 
@@ -758,7 +805,9 @@ function performanceReview() {
758
805
  return dedent`
759
806
  # Performance Review
760
807
 
761
- Review the application for performance issues and optimization opportunities. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
808
+ Review the application for performance issues and optimization opportunities.
809
+
810
+ ${ideasHint}
762
811
 
763
812
  ## Scope
764
813
 
@@ -791,7 +840,9 @@ function securityReview() {
791
840
  return dedent`
792
841
  # Security Review
793
842
 
794
- Review the codebase for common security vulnerabilities and misconfigurations. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
843
+ Review the codebase for common security vulnerabilities and misconfigurations.
844
+
845
+ ${ideasHint}
795
846
 
796
847
  ## Scope
797
848
 
@@ -826,7 +877,9 @@ function staleIdeas() {
826
877
  return dedent`
827
878
  # Stale Ideas
828
879
 
829
- Review \`.dust/ideas/\` to identify ideas that have become stale or irrelevant. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
880
+ Review \`.dust/ideas/\` to identify ideas that have become stale or irrelevant.
881
+
882
+ ${ideasHint}
830
883
 
831
884
  ## Scope
832
885
 
@@ -858,7 +911,9 @@ function testCoverage() {
858
911
  return dedent`
859
912
  # Test Coverage
860
913
 
861
- Identify untested code paths and areas that need additional test coverage. Review existing ideas in \`./.ideas/\` and the recent history of \`./.dust/ideas\` to understand what has been proposed or considered historically, then create new idea files in \`./.ideas/\` for any issues you identify, avoiding duplication.
914
+ Identify untested code paths and areas that need additional test coverage.
915
+
916
+ ${ideasHint}
862
917
 
863
918
  ## Scope
864
919
 
@@ -888,6 +943,7 @@ function testCoverage() {
888
943
  }
889
944
  var stockAuditFunctions = {
890
945
  "agent-developer-experience": agentDeveloperExperience,
946
+ "component-reuse": componentReuse,
891
947
  "dead-code": deadCode,
892
948
  "facts-verification": factsVerification,
893
949
  "ideas-from-commits": ideasFromCommits,
@@ -1034,10 +1090,9 @@ async function audit(dependencies) {
1034
1090
  }
1035
1091
 
1036
1092
  // lib/cli/commands/bucket.ts
1037
- import { spawn as nodeSpawn3 } from "node:child_process";
1093
+ import { spawn as nodeSpawn4 } from "node:child_process";
1038
1094
  import { accessSync, statSync } from "node:fs";
1039
1095
  import { chmod, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
1040
- import { createServer as httpCreateServer } from "node:http";
1041
1096
  import { homedir } from "node:os";
1042
1097
 
1043
1098
  // lib/bucket/auth.ts
@@ -1156,6 +1211,40 @@ async function authenticate(authDeps) {
1156
1211
  });
1157
1212
  }
1158
1213
 
1214
+ // lib/bucket/auth-server.ts
1215
+ import { spawn as nodeSpawn } from "node:child_process";
1216
+ import { createServer as httpCreateServer } from "node:http";
1217
+ function createLocalServer(handler) {
1218
+ let resolvedPort = 0;
1219
+ const server = httpCreateServer(async (nodeRequest, nodeResponse) => {
1220
+ const url = new URL(nodeRequest.url ?? "/", `http://localhost:${resolvedPort}`);
1221
+ const request = new Request(url.toString(), {
1222
+ method: nodeRequest.method ?? "GET"
1223
+ });
1224
+ const response = handler(request);
1225
+ const body = await response.text();
1226
+ nodeResponse.writeHead(response.status, {
1227
+ "Content-Type": response.headers.get("content-type") ?? "text/plain"
1228
+ });
1229
+ nodeResponse.end(body);
1230
+ });
1231
+ server.listen(0, () => {
1232
+ const addr2 = server.address();
1233
+ if (addr2 && typeof addr2 === "object") {
1234
+ resolvedPort = addr2.port;
1235
+ }
1236
+ });
1237
+ const addr = server.address();
1238
+ if (addr && typeof addr === "object") {
1239
+ resolvedPort = addr.port;
1240
+ }
1241
+ return { port: resolvedPort, stop: () => server.close() };
1242
+ }
1243
+ function openBrowser(url) {
1244
+ const cmd = process.platform === "darwin" ? "open" : "xdg-open";
1245
+ nodeSpawn(cmd, [url], { stdio: "ignore", detached: true }).unref();
1246
+ }
1247
+
1159
1248
  // lib/bucket/events.ts
1160
1249
  var WS_OPEN = 1;
1161
1250
  function formatBucketEvent(event) {
@@ -1216,10 +1305,10 @@ function getReposDir(env, homeDir) {
1216
1305
  import { dirname as dirname2 } from "node:path";
1217
1306
 
1218
1307
  // lib/claude/spawn-claude-code.ts
1219
- import { spawn as nodeSpawn } from "node:child_process";
1308
+ import { spawn as nodeSpawn2 } from "node:child_process";
1220
1309
  import { createInterface as nodeCreateInterface } from "node:readline";
1221
1310
  var defaultDependencies = {
1222
- spawn: nodeSpawn,
1311
+ spawn: nodeSpawn2,
1223
1312
  createInterface: nodeCreateInterface
1224
1313
  };
1225
1314
  async function* spawnClaudeCode(prompt, options = {}, dependencies = defaultDependencies) {
@@ -1827,7 +1916,7 @@ function formatAgentEvent(event) {
1827
1916
  }
1828
1917
 
1829
1918
  // lib/cli/commands/loop.ts
1830
- import { spawn as nodeSpawn2 } from "node:child_process";
1919
+ import { spawn as nodeSpawn3 } from "node:child_process";
1831
1920
  import os from "node:os";
1832
1921
 
1833
1922
  // lib/artifacts/workflow-tasks.ts
@@ -2011,19 +2100,21 @@ function formatLoopEvent(event) {
2011
2100
  return `\uD83C\uDFC1 Reached max iterations (${event.maxIterations}). Exiting.`;
2012
2101
  }
2013
2102
  }
2014
- async function defaultPostEvent(url, payload) {
2015
- await fetch(url, {
2016
- method: "POST",
2017
- headers: { "Content-Type": "application/json" },
2018
- body: JSON.stringify(payload)
2019
- });
2103
+ function createPostEvent(fetchFn) {
2104
+ return async (url, payload) => {
2105
+ await fetchFn(url, {
2106
+ method: "POST",
2107
+ headers: { "Content-Type": "application/json" },
2108
+ body: JSON.stringify(payload)
2109
+ });
2110
+ };
2020
2111
  }
2021
2112
  function createDefaultDependencies() {
2022
2113
  return {
2023
- spawn: nodeSpawn2,
2114
+ spawn: nodeSpawn3,
2024
2115
  run,
2025
2116
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
2026
- postEvent: defaultPostEvent
2117
+ postEvent: createPostEvent(fetch)
2027
2118
  };
2028
2119
  }
2029
2120
  function createWireEventSender(eventsUrl, sessionId, postEvent, onError, getAgentSessionId, repository = "") {
@@ -2091,7 +2182,20 @@ async function runOneIteration(dependencies, loopDependencies, onLoopEvent, onAg
2091
2182
  const { context } = dependencies;
2092
2183
  const { spawn, run: run2 } = loopDependencies;
2093
2184
  const agentName = loopDependencies.agentType === "codex" ? "Codex" : "Claude";
2094
- const { onRawEvent, hooksInstalled = false, signal, logger = log } = options;
2185
+ const {
2186
+ onRawEvent,
2187
+ hooksInstalled = false,
2188
+ signal,
2189
+ logger = log,
2190
+ repositoryId
2191
+ } = options;
2192
+ const baseEnv = {
2193
+ DUST_UNATTENDED: "1",
2194
+ DUST_SKIP_AGENT: "1"
2195
+ };
2196
+ if (repositoryId) {
2197
+ baseEnv.DUST_REPOSITORY_ID = repositoryId;
2198
+ }
2095
2199
  log("syncing with remote");
2096
2200
  onLoopEvent({ type: "loop.syncing" });
2097
2201
  const pullResult = await gitPull(context.cwd, spawn);
@@ -2126,7 +2230,7 @@ Make sure the repository is in a clean state and synced with remote before finis
2126
2230
  spawnOptions: {
2127
2231
  cwd: context.cwd,
2128
2232
  dangerouslySkipPermissions: true,
2129
- env: { DUST_UNATTENDED: "1", DUST_SKIP_AGENT: "1" },
2233
+ env: baseEnv,
2130
2234
  signal
2131
2235
  },
2132
2236
  onRawEvent
@@ -2182,7 +2286,7 @@ ${instructions}`;
2182
2286
  spawnOptions: {
2183
2287
  cwd: context.cwd,
2184
2288
  dangerouslySkipPermissions: true,
2185
- env: { DUST_UNATTENDED: "1", DUST_SKIP_AGENT: "1" },
2289
+ env: baseEnv,
2186
2290
  signal
2187
2291
  },
2188
2292
  onRawEvent
@@ -2279,6 +2383,45 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
2279
2383
  // lib/bucket/repository-loop.ts
2280
2384
  var log2 = createLogger("dust:bucket:repository-loop");
2281
2385
  var FALLBACK_TIMEOUT_MS = 300000;
2386
+ function createLogCallbacks(logBuffer) {
2387
+ return {
2388
+ stdout: (msg) => appendLogLine(logBuffer, createLogLine(msg, "stdout")),
2389
+ stderr: (msg) => appendLogLine(logBuffer, createLogLine(msg, "stderr"))
2390
+ };
2391
+ }
2392
+ function flushAndLogMultiLine(partialLine, text, logBuffer) {
2393
+ if (partialLine) {
2394
+ appendLogLine(logBuffer, createLogLine(partialLine, "stdout"));
2395
+ }
2396
+ for (const segment of text.split(`
2397
+ `)) {
2398
+ appendLogLine(logBuffer, createLogLine(segment, "stdout"));
2399
+ }
2400
+ return "";
2401
+ }
2402
+ function buildEventMessage(parameters) {
2403
+ const msg = {
2404
+ sequence: parameters.sequence,
2405
+ timestamp: new Date().toISOString(),
2406
+ sessionId: parameters.sessionId,
2407
+ repository: parameters.repository,
2408
+ event: parameters.event
2409
+ };
2410
+ if (parameters.agentSessionId) {
2411
+ msg.agentSessionId = parameters.agentSessionId;
2412
+ }
2413
+ return msg;
2414
+ }
2415
+ function createWakeUpHandler(repoState, resolve) {
2416
+ const handler = () => {
2417
+ if (repoState.wakeUp !== handler) {
2418
+ return;
2419
+ }
2420
+ repoState.wakeUp = undefined;
2421
+ resolve();
2422
+ };
2423
+ return handler;
2424
+ }
2282
2425
  function createNoOpGlobScanner() {
2283
2426
  return {
2284
2427
  scan: async function* () {}
@@ -2288,12 +2431,13 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2288
2431
  const { spawn, run: run2, fileSystem, sleep } = repoDeps;
2289
2432
  const repoName = repoState.repository.name;
2290
2433
  const settings = await loadSettings(repoState.path, fileSystem);
2434
+ const logCallbacks = createLogCallbacks(repoState.logBuffer);
2291
2435
  const commandDeps = {
2292
2436
  arguments: [],
2293
2437
  context: {
2294
2438
  cwd: repoState.path,
2295
- stdout: (msg) => appendLogLine(repoState.logBuffer, createLogLine(msg, "stdout")),
2296
- stderr: (msg) => appendLogLine(repoState.logBuffer, createLogLine(msg, "stderr"))
2439
+ stdout: (msg) => logCallbacks.stdout(msg),
2440
+ stderr: (msg) => logCallbacks.stderr(msg)
2297
2441
  },
2298
2442
  fileSystem,
2299
2443
  globScanner: createNoOpGlobScanner(),
@@ -2313,14 +2457,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2313
2457
  partialLine = lines[lines.length - 1];
2314
2458
  },
2315
2459
  line: (text) => {
2316
- if (partialLine) {
2317
- appendLogLine(repoState.logBuffer, createLogLine(partialLine, "stdout"));
2318
- partialLine = "";
2319
- }
2320
- for (const segment of text.split(`
2321
- `)) {
2322
- appendLogLine(repoState.logBuffer, createLogLine(segment, "stdout"));
2323
- }
2460
+ partialLine = flushAndLogMultiLine(partialLine, text, repoState.logBuffer);
2324
2461
  }
2325
2462
  })
2326
2463
  };
@@ -2351,17 +2488,13 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2351
2488
  }
2352
2489
  if (sendEvent && sessionId) {
2353
2490
  sequence++;
2354
- const msg = {
2491
+ sendEvent(buildEventMessage({
2355
2492
  sequence,
2356
- timestamp: new Date().toISOString(),
2357
2493
  sessionId,
2358
2494
  repository: repoName,
2359
- event
2360
- };
2361
- if (agentSessionId) {
2362
- msg.agentSessionId = agentSessionId;
2363
- }
2364
- sendEvent(msg);
2495
+ event,
2496
+ agentSessionId
2497
+ }));
2365
2498
  }
2366
2499
  };
2367
2500
  const hooksInstalled = await manageGitHooks(commandDeps);
@@ -2379,6 +2512,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2379
2512
  result = await runOneIteration(commandDeps, loopDeps, onLoopEvent, onAgentEvent, {
2380
2513
  hooksInstalled,
2381
2514
  signal: abortController.signal,
2515
+ repositoryId: repoState.repository.id,
2382
2516
  onRawEvent: (rawEvent) => {
2383
2517
  onAgentEvent(rawEventToAgentEvent(rawEvent));
2384
2518
  }
@@ -2404,13 +2538,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2404
2538
  log2(`${repoName}: no tasks available, waiting`);
2405
2539
  logLine("Waiting for tasks...");
2406
2540
  await new Promise((resolve) => {
2407
- const wakeUpForThisWait = () => {
2408
- if (repoState.wakeUp !== wakeUpForThisWait) {
2409
- return;
2410
- }
2411
- repoState.wakeUp = undefined;
2412
- resolve();
2413
- };
2541
+ const wakeUpForThisWait = createWakeUpHandler(repoState, resolve);
2414
2542
  repoState.wakeUp = wakeUpForThisWait;
2415
2543
  sleep(FALLBACK_TIMEOUT_MS).then(() => {
2416
2544
  if (repoState.wakeUp === wakeUpForThisWait) {
@@ -2456,6 +2584,9 @@ function parseRepository(data) {
2456
2584
  if (typeof repositoryData.url === "string") {
2457
2585
  repo.url = repositoryData.url;
2458
2586
  }
2587
+ if (typeof repositoryData.id === "string") {
2588
+ repo.id = repositoryData.id;
2589
+ }
2459
2590
  return repo;
2460
2591
  }
2461
2592
  }
@@ -3019,36 +3150,6 @@ function defaultGetTerminalSize() {
3019
3150
  function defaultWriteStdout(data) {
3020
3151
  process.stdout.write(data);
3021
3152
  }
3022
- function defaultCreateServer(handler) {
3023
- let resolvedPort = 0;
3024
- const server = httpCreateServer(async (nodeRequest, nodeResponse) => {
3025
- const url = new URL(nodeRequest.url ?? "/", `http://localhost:${resolvedPort}`);
3026
- const request = new Request(url.toString(), {
3027
- method: nodeRequest.method ?? "GET"
3028
- });
3029
- const response = handler(request);
3030
- const body = await response.text();
3031
- nodeResponse.writeHead(response.status, {
3032
- "Content-Type": response.headers.get("content-type") ?? "text/plain"
3033
- });
3034
- nodeResponse.end(body);
3035
- });
3036
- server.listen(0, () => {
3037
- const addr2 = server.address();
3038
- if (addr2 && typeof addr2 === "object") {
3039
- resolvedPort = addr2.port;
3040
- }
3041
- });
3042
- const addr = server.address();
3043
- if (addr && typeof addr === "object") {
3044
- resolvedPort = addr.port;
3045
- }
3046
- return { port: resolvedPort, stop: () => server.close() };
3047
- }
3048
- function defaultOpenBrowser(url) {
3049
- const cmd = process.platform === "darwin" ? "open" : "xdg-open";
3050
- nodeSpawn3(cmd, [url], { stdio: "ignore", detached: true }).unref();
3051
- }
3052
3153
  function createAuthFileSystem(dependencies) {
3053
3154
  return {
3054
3155
  exists: (path) => {
@@ -3087,7 +3188,7 @@ function createDefaultBucketDependencies() {
3087
3188
  rename: (oldPath, newPath) => import("node:fs/promises").then((mod) => mod.rename(oldPath, newPath))
3088
3189
  });
3089
3190
  return {
3090
- spawn: nodeSpawn3,
3191
+ spawn: nodeSpawn4,
3091
3192
  createWebSocket: defaultCreateWebSocket,
3092
3193
  setupKeypress: defaultSetupKeypress,
3093
3194
  setupSignals: defaultSetupSignals,
@@ -3098,8 +3199,8 @@ function createDefaultBucketDependencies() {
3098
3199
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
3099
3200
  getReposDir: () => getReposDir(process.env, homedir()),
3100
3201
  auth: {
3101
- createServer: defaultCreateServer,
3102
- openBrowser: defaultOpenBrowser,
3202
+ createServer: createLocalServer,
3203
+ openBrowser,
3103
3204
  getHomeDir: () => homedir(),
3104
3205
  fileSystem: authFileSystem
3105
3206
  }
@@ -3464,6 +3565,176 @@ async function bucket(dependencies, bucketDeps = createDefaultBucketDependencies
3464
3565
  return { exitCode: 0 };
3465
3566
  }
3466
3567
 
3568
+ // lib/cli/commands/bucket-asset-upload.ts
3569
+ import { accessSync as accessSync2, statSync as statSync2 } from "node:fs";
3570
+ import { chmod as chmod2, mkdir as mkdir2, readdir as readdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
3571
+ import { homedir as homedir2 } from "node:os";
3572
+ import { extname } from "node:path";
3573
+ var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
3574
+ var ALLOWED_EXTENSIONS = new Set([
3575
+ ".png",
3576
+ ".jpg",
3577
+ ".jpeg",
3578
+ ".gif",
3579
+ ".webp",
3580
+ ".svg",
3581
+ ".pdf",
3582
+ ".txt",
3583
+ ".json",
3584
+ ".csv",
3585
+ ".md",
3586
+ ".html",
3587
+ ".xml"
3588
+ ]);
3589
+ var MIME_TYPES = {
3590
+ ".png": "image/png",
3591
+ ".jpg": "image/jpeg",
3592
+ ".jpeg": "image/jpeg",
3593
+ ".gif": "image/gif",
3594
+ ".webp": "image/webp",
3595
+ ".svg": "image/svg+xml",
3596
+ ".pdf": "application/pdf",
3597
+ ".txt": "text/plain",
3598
+ ".json": "application/json",
3599
+ ".csv": "text/csv",
3600
+ ".md": "text/markdown",
3601
+ ".html": "text/html",
3602
+ ".xml": "application/xml"
3603
+ };
3604
+ function createDefaultUploadDependencies() {
3605
+ const authFileSystemDeps = {
3606
+ accessSync: accessSync2,
3607
+ statSync: statSync2,
3608
+ readFile: readFile2,
3609
+ writeFile: writeFile2,
3610
+ mkdir: mkdir2,
3611
+ readdir: readdir2,
3612
+ chmod: chmod2,
3613
+ rename: (oldPath, newPath) => import("node:fs/promises").then((mod) => mod.rename(oldPath, newPath))
3614
+ };
3615
+ const authFileSystem = createAuthFileSystem(authFileSystemDeps);
3616
+ return {
3617
+ auth: {
3618
+ createServer: createLocalServer,
3619
+ openBrowser,
3620
+ getHomeDir: () => homedir2(),
3621
+ fileSystem: authFileSystem
3622
+ },
3623
+ readFileBytes: async (path) => {
3624
+ const buffer = await Bun.file(path).arrayBuffer();
3625
+ return new Uint8Array(buffer);
3626
+ },
3627
+ getFileSize: async (path) => {
3628
+ const file = Bun.file(path);
3629
+ return file.size;
3630
+ },
3631
+ fileExists: async (path) => {
3632
+ const file = Bun.file(path);
3633
+ return file.exists();
3634
+ },
3635
+ uploadFile: async (url, token, fileBytes, contentType) => {
3636
+ const response = await fetch(url, {
3637
+ method: "POST",
3638
+ headers: {
3639
+ Authorization: `Bearer ${token}`,
3640
+ "Content-Type": contentType
3641
+ },
3642
+ body: new Blob([fileBytes])
3643
+ });
3644
+ if (!response.ok) {
3645
+ const text = await response.text();
3646
+ throw new Error(`Upload failed (${response.status}): ${text || response.statusText}`);
3647
+ }
3648
+ const body = await response.json();
3649
+ if (typeof body.url !== "string") {
3650
+ throw new Error("Server response missing URL");
3651
+ }
3652
+ return { url: body.url };
3653
+ }
3654
+ };
3655
+ }
3656
+ async function resolveToken2(authDeps, context) {
3657
+ const envToken = process.env.DUST_BUCKET_TOKEN;
3658
+ if (envToken) {
3659
+ return envToken;
3660
+ }
3661
+ const stored = await loadStoredToken(authDeps.fileSystem, authDeps.getHomeDir());
3662
+ if (stored) {
3663
+ return stored;
3664
+ }
3665
+ context.stdout("Opening browser to authenticate with dustbucket...");
3666
+ try {
3667
+ const token = await authenticate(authDeps);
3668
+ await storeToken(authDeps.fileSystem, authDeps.getHomeDir(), token);
3669
+ context.stdout("Authenticated successfully");
3670
+ return token;
3671
+ } catch (error) {
3672
+ context.stderr(`Authentication failed: ${error.message}`);
3673
+ return null;
3674
+ }
3675
+ }
3676
+ function getContentType(filePath) {
3677
+ const ext = extname(filePath).toLowerCase();
3678
+ return MIME_TYPES[ext] || "application/octet-stream";
3679
+ }
3680
+ function isAllowedExtension(filePath) {
3681
+ const ext = extname(filePath).toLowerCase();
3682
+ return ALLOWED_EXTENSIONS.has(ext);
3683
+ }
3684
+ function formatFileSize(bytes) {
3685
+ if (bytes < 1024)
3686
+ return `${bytes} bytes`;
3687
+ if (bytes < 1048576)
3688
+ return `${(bytes / 1024).toFixed(1)} KB`;
3689
+ return `${(bytes / 1048576).toFixed(1)} MB`;
3690
+ }
3691
+ async function bucketAssetUpload(dependencies, uploadDeps = createDefaultUploadDependencies(), env = process.env) {
3692
+ const { context } = dependencies;
3693
+ const filePath = dependencies.arguments[0];
3694
+ if (!filePath) {
3695
+ context.stderr("Usage: dust bucket asset upload <file-path>");
3696
+ return { exitCode: 1 };
3697
+ }
3698
+ const repositoryId = env.DUST_REPOSITORY_ID;
3699
+ if (!repositoryId) {
3700
+ context.stderr("Error: DUST_REPOSITORY_ID environment variable is not set.");
3701
+ context.stderr("This command must be run within a repository context (via `dust bucket`).");
3702
+ return { exitCode: 1 };
3703
+ }
3704
+ const exists = await uploadDeps.fileExists(filePath);
3705
+ if (!exists) {
3706
+ context.stderr(`File not found: ${filePath}`);
3707
+ return { exitCode: 1 };
3708
+ }
3709
+ if (!isAllowedExtension(filePath)) {
3710
+ const ext = extname(filePath).toLowerCase() || "(no extension)";
3711
+ const allowed = Array.from(ALLOWED_EXTENSIONS).join(", ");
3712
+ context.stderr(`Unsupported file type: ${ext}`);
3713
+ context.stderr(`Allowed types: ${allowed}`);
3714
+ return { exitCode: 1 };
3715
+ }
3716
+ const fileSize = await uploadDeps.getFileSize(filePath);
3717
+ if (fileSize > MAX_FILE_SIZE_BYTES) {
3718
+ context.stderr(`File too large: ${formatFileSize(fileSize)} (max ${formatFileSize(MAX_FILE_SIZE_BYTES)})`);
3719
+ return { exitCode: 1 };
3720
+ }
3721
+ const token = await resolveToken2(uploadDeps.auth, context);
3722
+ if (!token) {
3723
+ return { exitCode: 1 };
3724
+ }
3725
+ const fileBytes = await uploadDeps.readFileBytes(filePath);
3726
+ const contentType = getContentType(filePath);
3727
+ const uploadUrl = `${getDustbucketHost()}/api/assets?repositoryId=${encodeURIComponent(repositoryId)}`;
3728
+ try {
3729
+ const result = await uploadDeps.uploadFile(uploadUrl, token, fileBytes, contentType);
3730
+ context.stdout(result.url);
3731
+ return { exitCode: 0 };
3732
+ } catch (error) {
3733
+ context.stderr(`Upload failed: ${error.message}`);
3734
+ return { exitCode: 1 };
3735
+ }
3736
+ }
3737
+
3467
3738
  // lib/cli/process-runner.ts
3468
3739
  import { spawn } from "node:child_process";
3469
3740
  function createShellRunner(spawnFn) {
@@ -3525,11 +3796,7 @@ function runBufferedProcess(spawnFn, command, commandArguments, cwd, shell, time
3525
3796
  import { join as join8 } from "node:path";
3526
3797
 
3527
3798
  // lib/lint/validators/content-validator.ts
3528
- var REQUIRED_HEADINGS = [
3529
- "## Principles",
3530
- "## Blocked By",
3531
- "## Definition of Done"
3532
- ];
3799
+ var REQUIRED_HEADINGS = ["## Blocked By", "## Definition of Done"];
3533
3800
  var MAX_OPENING_SENTENCE_LENGTH = 150;
3534
3801
  var NON_IMPERATIVE_STARTERS = new Set([
3535
3802
  "the",
@@ -4710,10 +4977,10 @@ async function list(dependencies) {
4710
4977
  }
4711
4978
 
4712
4979
  // lib/codex/spawn-codex.ts
4713
- import { spawn as nodeSpawn4 } from "node:child_process";
4980
+ import { spawn as nodeSpawn5 } from "node:child_process";
4714
4981
  import { createInterface as nodeCreateInterface2 } from "node:readline";
4715
4982
  var defaultDependencies2 = {
4716
- spawn: nodeSpawn4,
4983
+ spawn: nodeSpawn5,
4717
4984
  createInterface: nodeCreateInterface2
4718
4985
  };
4719
4986
  async function* spawnCodex(prompt, options = {}, dependencies = defaultDependencies2) {
@@ -5232,6 +5499,7 @@ var commandRegistry = {
5232
5499
  agent,
5233
5500
  audit,
5234
5501
  bucket,
5502
+ "bucket asset upload": bucketAssetUpload,
5235
5503
  focus,
5236
5504
  "new task": newTask,
5237
5505
  "new principle": newPrinciple,
@@ -5320,10 +5588,10 @@ function createFileSystem(primitives) {
5320
5588
  rename: (oldPath, newPath) => primitives.rename(oldPath, newPath)
5321
5589
  };
5322
5590
  }
5323
- function createGlobScanner(readdir2) {
5591
+ function createGlobScanner(readdir3) {
5324
5592
  return {
5325
5593
  scan: async function* (dir) {
5326
- for (const entry of await readdir2(dir, { recursive: true })) {
5594
+ for (const entry of await readdir3(dir, { recursive: true })) {
5327
5595
  if (entry.endsWith(".md"))
5328
5596
  yield entry;
5329
5597
  }
@@ -5350,7 +5618,7 @@ async function wireEntry(fsPrimitives, processPrimitives, consolePrimitives) {
5350
5618
  }
5351
5619
 
5352
5620
  // lib/cli/run.ts
5353
- await wireEntry({ existsSync, statSync: statSync2, readFile: readFile2, writeFile: writeFile2, mkdir: mkdir2, readdir: readdir2, chmod: chmod2, rename }, {
5621
+ await wireEntry({ existsSync, statSync: statSync3, readFile: readFile3, writeFile: writeFile3, mkdir: mkdir3, readdir: readdir3, chmod: chmod3, rename }, {
5354
5622
  argv: process.argv,
5355
5623
  cwd: () => process.cwd(),
5356
5624
  exit: (code) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.63",
3
+ "version": "0.1.65",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,12 +29,17 @@
29
29
  "./filesystem": {
30
30
  "types": "./dist/filesystem/types.d.ts"
31
31
  },
32
- "./istanbul/minimal-reporter": "./lib/istanbul/minimal-reporter.cjs"
32
+ "./istanbul/minimal-reporter": "./lib/istanbul/minimal-reporter.cjs",
33
+ "./biome": {
34
+ "import": "./dist/biome.js",
35
+ "types": "./dist/biome/index.d.ts"
36
+ }
33
37
  },
34
38
  "files": [
35
39
  "dist",
36
40
  "bin",
37
- "lib/istanbul/minimal-reporter.cjs"
41
+ "lib/istanbul/minimal-reporter.cjs",
42
+ "biome"
38
43
  ],
39
44
  "repository": {
40
45
  "type": "git",