@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.
- package/biome/dust-no-abbreviated-names.grit +58 -0
- package/dist/artifacts.js +3 -17
- package/dist/audits.js +65 -9
- package/dist/biome/index.d.ts +16 -0
- package/dist/biome.js +8 -0
- package/dist/dust.js +368 -100
- package/package.json +8 -3
|
@@ -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
|
|
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
|
-
- [ ]
|
|
462
|
-
- [ ]
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
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
|
|
6
|
+
import { existsSync, statSync as statSync3 } from "node:fs";
|
|
7
7
|
import {
|
|
8
|
-
chmod as
|
|
9
|
-
mkdir as
|
|
10
|
-
readdir as
|
|
11
|
-
readFile as
|
|
8
|
+
chmod as chmod3,
|
|
9
|
+
mkdir as mkdir3,
|
|
10
|
+
readdir as readdir3,
|
|
11
|
+
readFile as readFile3,
|
|
12
12
|
rename,
|
|
13
|
-
writeFile as
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
1308
|
+
import { spawn as nodeSpawn2 } from "node:child_process";
|
|
1220
1309
|
import { createInterface as nodeCreateInterface } from "node:readline";
|
|
1221
1310
|
var defaultDependencies = {
|
|
1222
|
-
spawn:
|
|
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
|
|
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
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
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:
|
|
2114
|
+
spawn: nodeSpawn3,
|
|
2024
2115
|
run,
|
|
2025
2116
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
2026
|
-
postEvent:
|
|
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 {
|
|
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:
|
|
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:
|
|
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) =>
|
|
2296
|
-
stderr: (msg) =>
|
|
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
|
-
|
|
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
|
-
|
|
2491
|
+
sendEvent(buildEventMessage({
|
|
2355
2492
|
sequence,
|
|
2356
|
-
timestamp: new Date().toISOString(),
|
|
2357
2493
|
sessionId,
|
|
2358
2494
|
repository: repoName,
|
|
2359
|
-
event
|
|
2360
|
-
|
|
2361
|
-
|
|
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:
|
|
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:
|
|
3102
|
-
openBrowser
|
|
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
|
|
4980
|
+
import { spawn as nodeSpawn5 } from "node:child_process";
|
|
4714
4981
|
import { createInterface as nodeCreateInterface2 } from "node:readline";
|
|
4715
4982
|
var defaultDependencies2 = {
|
|
4716
|
-
spawn:
|
|
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(
|
|
5591
|
+
function createGlobScanner(readdir3) {
|
|
5324
5592
|
return {
|
|
5325
5593
|
scan: async function* (dir) {
|
|
5326
|
-
for (const entry of await
|
|
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:
|
|
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.
|
|
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",
|