@joshski/dust 0.1.69 → 0.1.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts/index.d.ts +3 -3
- package/dist/artifacts/workflow-tasks.d.ts +6 -1
- package/dist/artifacts.js +42 -2
- package/dist/audits.js +57 -0
- package/dist/dust.js +72 -10
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@ import { type Fact } from './facts';
|
|
|
3
3
|
import { type Idea, type IdeaOpenQuestion, type IdeaOption, parseOpenQuestions } from './ideas';
|
|
4
4
|
import { type Principle } from './principles';
|
|
5
5
|
import { type Task } from './tasks';
|
|
6
|
-
import { CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllCaptureIdeaTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, type WorkflowTaskMatch } from './workflow-tasks';
|
|
7
|
-
export type { CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedCaptureIdeaTask, Principle, Task, WorkflowTaskMatch, };
|
|
8
|
-
export { CAPTURE_IDEA_PREFIX, findAllCaptureIdeaTasks, parseOpenQuestions };
|
|
6
|
+
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllCaptureIdeaTasks, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, type WorkflowTaskMatch } from './workflow-tasks';
|
|
7
|
+
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedCaptureIdeaTask, Principle, Task, WorkflowTaskMatch, };
|
|
8
|
+
export { CAPTURE_IDEA_PREFIX, findAllCaptureIdeaTasks, findAllWorkflowTasks, parseOpenQuestions, };
|
|
9
9
|
export type { IdeaInProgress };
|
|
10
10
|
export interface ArtifactsRepository {
|
|
11
11
|
parseIdea(options: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FileSystem, ReadableFileSystem } from '../filesystem/types';
|
|
2
2
|
export declare const IDEA_TRANSITION_PREFIXES: string[];
|
|
3
3
|
export declare const CAPTURE_IDEA_PREFIX = "Add Idea: ";
|
|
4
|
-
export declare const BUILD_IDEA_PREFIX = "
|
|
4
|
+
export declare const BUILD_IDEA_PREFIX = "Decompose Idea: ";
|
|
5
5
|
export interface IdeaInProgress {
|
|
6
6
|
taskSlug: string;
|
|
7
7
|
ideaTitle: string;
|
|
@@ -28,6 +28,11 @@ export interface WorkflowTaskMatch {
|
|
|
28
28
|
ideaSlug: string;
|
|
29
29
|
taskSlug: string;
|
|
30
30
|
}
|
|
31
|
+
export interface AllWorkflowTasks {
|
|
32
|
+
captureIdeaTasks: IdeaInProgress[];
|
|
33
|
+
workflowTasksByIdeaSlug: Map<string, WorkflowTaskMatch>;
|
|
34
|
+
}
|
|
35
|
+
export declare function findAllWorkflowTasks(fileSystem: ReadableFileSystem, dustPath: string): Promise<AllWorkflowTasks>;
|
|
31
36
|
export declare function findWorkflowTaskForIdea(fileSystem: ReadableFileSystem, dustPath: string, ideaSlug: string): Promise<WorkflowTaskMatch | null>;
|
|
32
37
|
export interface CreateIdeaTransitionTaskResult {
|
|
33
38
|
filePath: string;
|
package/dist/artifacts.js
CHANGED
|
@@ -272,7 +272,7 @@ async function parseTask(fileSystem, dustPath, slug) {
|
|
|
272
272
|
|
|
273
273
|
// lib/artifacts/workflow-tasks.ts
|
|
274
274
|
var CAPTURE_IDEA_PREFIX = "Add Idea: ";
|
|
275
|
-
var BUILD_IDEA_PREFIX = "
|
|
275
|
+
var BUILD_IDEA_PREFIX = "Decompose Idea: ";
|
|
276
276
|
async function findAllCaptureIdeaTasks(fileSystem, dustPath) {
|
|
277
277
|
const tasksPath = `${dustPath}/tasks`;
|
|
278
278
|
if (!fileSystem.exists(tasksPath))
|
|
@@ -290,7 +290,7 @@ async function findAllCaptureIdeaTasks(fileSystem, dustPath) {
|
|
|
290
290
|
taskSlug: file.replace(/\.md$/, ""),
|
|
291
291
|
ideaTitle: title.slice(CAPTURE_IDEA_PREFIX.length)
|
|
292
292
|
});
|
|
293
|
-
} else if (title.startsWith(BUILD_IDEA_PREFIX)) {
|
|
293
|
+
} else if (title.startsWith(BUILD_IDEA_PREFIX) && !content.includes("## Decomposes Idea")) {
|
|
294
294
|
results.push({
|
|
295
295
|
taskSlug: file.replace(/\.md$/, ""),
|
|
296
296
|
ideaTitle: title.slice(BUILD_IDEA_PREFIX.length)
|
|
@@ -331,6 +331,45 @@ function extractIdeaSlugFromSection(content, sectionHeading) {
|
|
|
331
331
|
}
|
|
332
332
|
return null;
|
|
333
333
|
}
|
|
334
|
+
async function findAllWorkflowTasks(fileSystem, dustPath) {
|
|
335
|
+
const tasksPath = `${dustPath}/tasks`;
|
|
336
|
+
const captureIdeaTasks = [];
|
|
337
|
+
const workflowTasksByIdeaSlug = new Map;
|
|
338
|
+
if (!fileSystem.exists(tasksPath)) {
|
|
339
|
+
return { captureIdeaTasks, workflowTasksByIdeaSlug };
|
|
340
|
+
}
|
|
341
|
+
const files = await fileSystem.readdir(tasksPath);
|
|
342
|
+
for (const file of files.filter((f) => f.endsWith(".md")).sort()) {
|
|
343
|
+
const content = await fileSystem.readFile(`${tasksPath}/${file}`);
|
|
344
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
345
|
+
if (!titleMatch)
|
|
346
|
+
continue;
|
|
347
|
+
const title = titleMatch[1].trim();
|
|
348
|
+
const taskSlug = file.replace(/\.md$/, "");
|
|
349
|
+
if (title.startsWith(CAPTURE_IDEA_PREFIX)) {
|
|
350
|
+
captureIdeaTasks.push({
|
|
351
|
+
taskSlug,
|
|
352
|
+
ideaTitle: title.slice(CAPTURE_IDEA_PREFIX.length)
|
|
353
|
+
});
|
|
354
|
+
} else if (title.startsWith(BUILD_IDEA_PREFIX) && !content.includes("## Decomposes Idea")) {
|
|
355
|
+
captureIdeaTasks.push({
|
|
356
|
+
taskSlug,
|
|
357
|
+
ideaTitle: title.slice(BUILD_IDEA_PREFIX.length)
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
for (const { type, heading } of WORKFLOW_SECTION_HEADINGS) {
|
|
361
|
+
const linkedSlug = extractIdeaSlugFromSection(content, heading);
|
|
362
|
+
if (linkedSlug) {
|
|
363
|
+
workflowTasksByIdeaSlug.set(linkedSlug, {
|
|
364
|
+
type,
|
|
365
|
+
ideaSlug: linkedSlug,
|
|
366
|
+
taskSlug
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return { captureIdeaTasks, workflowTasksByIdeaSlug };
|
|
372
|
+
}
|
|
334
373
|
async function findWorkflowTaskForIdea(fileSystem, dustPath, ideaSlug) {
|
|
335
374
|
const ideaPath = `${dustPath}/ideas/${ideaSlug}.md`;
|
|
336
375
|
if (!fileSystem.exists(ideaPath)) {
|
|
@@ -652,6 +691,7 @@ function buildReadOnlyArtifactsRepository(fileSystem, dustPath) {
|
|
|
652
691
|
}
|
|
653
692
|
export {
|
|
654
693
|
parseOpenQuestions,
|
|
694
|
+
findAllWorkflowTasks,
|
|
655
695
|
findAllCaptureIdeaTasks,
|
|
656
696
|
buildReadOnlyArtifactsRepository,
|
|
657
697
|
buildArtifactsRepository,
|
package/dist/audits.js
CHANGED
|
@@ -614,6 +614,62 @@ function errorHandling() {
|
|
|
614
614
|
- [ ] Proposed ideas for any error handling improvements identified
|
|
615
615
|
`;
|
|
616
616
|
}
|
|
617
|
+
function globalState() {
|
|
618
|
+
return dedent`
|
|
619
|
+
# Global State
|
|
620
|
+
|
|
621
|
+
Find global state and singletons that introduce hidden coupling and hurt testability.
|
|
622
|
+
|
|
623
|
+
${ideasHint}
|
|
624
|
+
|
|
625
|
+
## Scope
|
|
626
|
+
|
|
627
|
+
Focus on these patterns:
|
|
628
|
+
|
|
629
|
+
1. **Module-level mutable variables** - Variables declared outside functions that can be modified
|
|
630
|
+
2. **Singleton patterns** - Classes or objects that enforce a single instance (getInstance, static instance fields)
|
|
631
|
+
3. **Global registries** - Maps, arrays, or sets that accumulate state across module loads
|
|
632
|
+
4. **Implicit dependencies** - Functions that read from or write to module-level state instead of using parameters
|
|
633
|
+
5. **Shared configuration objects** - Mutable config objects imported and modified by multiple modules
|
|
634
|
+
6. **Lazy initialization with caching** - Values computed once and stored at module level
|
|
635
|
+
|
|
636
|
+
## Analysis Steps
|
|
637
|
+
|
|
638
|
+
1. Search for \`let\` and \`var\` declarations at module level (outside functions/classes)
|
|
639
|
+
2. Look for singleton patterns: \`getInstance\`, \`static instance\`, \`export const instance\`
|
|
640
|
+
3. Find module-level \`Map\`, \`Set\`, \`Array\`, or \`Object\` that gets modified
|
|
641
|
+
4. Identify functions that reference module-level variables not passed as parameters
|
|
642
|
+
5. Check for \`process.env\` access scattered throughout the codebase instead of centralized
|
|
643
|
+
|
|
644
|
+
## Output
|
|
645
|
+
|
|
646
|
+
For each global state instance identified, provide:
|
|
647
|
+
- **Location** - File path and line number
|
|
648
|
+
- **Pattern** - Which category (mutable variable, singleton, registry, etc.)
|
|
649
|
+
- **Impact** - How this affects testability or coupling (e.g., "Tests must reset this state", "Cannot run tests in parallel")
|
|
650
|
+
- **Suggestion** - How to refactor (e.g., "Pass as dependency", "Use factory function", "Move to function scope")
|
|
651
|
+
|
|
652
|
+
## Principles
|
|
653
|
+
|
|
654
|
+
- [Dependency Injection](../principles/dependency-injection.md) - Dependencies should be passed in, not accessed globally
|
|
655
|
+
- [Decoupled Code](../principles/decoupled-code.md) - Code should be organized into independent units
|
|
656
|
+
- [Test Isolation](../principles/test-isolation.md) - Tests should not affect each other
|
|
657
|
+
|
|
658
|
+
## Blocked By
|
|
659
|
+
|
|
660
|
+
(none)
|
|
661
|
+
|
|
662
|
+
## Definition of Done
|
|
663
|
+
|
|
664
|
+
- [ ] Searched for module-level mutable variables (let/var outside functions)
|
|
665
|
+
- [ ] Identified singleton patterns and getInstance methods
|
|
666
|
+
- [ ] Found global registries (Maps, Sets, Arrays modified at module level)
|
|
667
|
+
- [ ] Located functions with implicit dependencies on module-level state
|
|
668
|
+
- [ ] Checked for scattered process.env access
|
|
669
|
+
- [ ] Documented impact of each global state instance on testing
|
|
670
|
+
- [ ] Proposed ideas for refactoring global state to explicit dependencies
|
|
671
|
+
`;
|
|
672
|
+
}
|
|
617
673
|
function ubiquitousLanguage() {
|
|
618
674
|
return dedent`
|
|
619
675
|
# Ubiquitous Language
|
|
@@ -675,6 +731,7 @@ var stockAuditFunctions = {
|
|
|
675
731
|
"dead-code": deadCode,
|
|
676
732
|
"error-handling": errorHandling,
|
|
677
733
|
"facts-verification": factsVerification,
|
|
734
|
+
"global-state": globalState,
|
|
678
735
|
"ideas-from-commits": ideasFromCommits,
|
|
679
736
|
"ideas-from-principles": ideasFromPrinciples,
|
|
680
737
|
"performance-review": performanceReview,
|
package/dist/dust.js
CHANGED
|
@@ -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.70";
|
|
279
279
|
|
|
280
280
|
// lib/session.ts
|
|
281
281
|
var DUST_UNATTENDED = "DUST_UNATTENDED";
|
|
@@ -537,6 +537,8 @@ ${vars.agentInstructions}` : "";
|
|
|
537
537
|
6. **Unclear** → \`${vars.bin} help\`
|
|
538
538
|
If none of the above clearly apply, run this to see all available commands.
|
|
539
539
|
|
|
540
|
+
Note: "tasks" here refers to dust task files in \`.dust/tasks/\`, not internal task tracking tools.
|
|
541
|
+
|
|
540
542
|
Do NOT proceed without running one of these commands.${instructions}
|
|
541
543
|
`;
|
|
542
544
|
}
|
|
@@ -1159,6 +1161,62 @@ function errorHandling() {
|
|
|
1159
1161
|
- [ ] Proposed ideas for any error handling improvements identified
|
|
1160
1162
|
`;
|
|
1161
1163
|
}
|
|
1164
|
+
function globalState() {
|
|
1165
|
+
return dedent`
|
|
1166
|
+
# Global State
|
|
1167
|
+
|
|
1168
|
+
Find global state and singletons that introduce hidden coupling and hurt testability.
|
|
1169
|
+
|
|
1170
|
+
${ideasHint}
|
|
1171
|
+
|
|
1172
|
+
## Scope
|
|
1173
|
+
|
|
1174
|
+
Focus on these patterns:
|
|
1175
|
+
|
|
1176
|
+
1. **Module-level mutable variables** - Variables declared outside functions that can be modified
|
|
1177
|
+
2. **Singleton patterns** - Classes or objects that enforce a single instance (getInstance, static instance fields)
|
|
1178
|
+
3. **Global registries** - Maps, arrays, or sets that accumulate state across module loads
|
|
1179
|
+
4. **Implicit dependencies** - Functions that read from or write to module-level state instead of using parameters
|
|
1180
|
+
5. **Shared configuration objects** - Mutable config objects imported and modified by multiple modules
|
|
1181
|
+
6. **Lazy initialization with caching** - Values computed once and stored at module level
|
|
1182
|
+
|
|
1183
|
+
## Analysis Steps
|
|
1184
|
+
|
|
1185
|
+
1. Search for \`let\` and \`var\` declarations at module level (outside functions/classes)
|
|
1186
|
+
2. Look for singleton patterns: \`getInstance\`, \`static instance\`, \`export const instance\`
|
|
1187
|
+
3. Find module-level \`Map\`, \`Set\`, \`Array\`, or \`Object\` that gets modified
|
|
1188
|
+
4. Identify functions that reference module-level variables not passed as parameters
|
|
1189
|
+
5. Check for \`process.env\` access scattered throughout the codebase instead of centralized
|
|
1190
|
+
|
|
1191
|
+
## Output
|
|
1192
|
+
|
|
1193
|
+
For each global state instance identified, provide:
|
|
1194
|
+
- **Location** - File path and line number
|
|
1195
|
+
- **Pattern** - Which category (mutable variable, singleton, registry, etc.)
|
|
1196
|
+
- **Impact** - How this affects testability or coupling (e.g., "Tests must reset this state", "Cannot run tests in parallel")
|
|
1197
|
+
- **Suggestion** - How to refactor (e.g., "Pass as dependency", "Use factory function", "Move to function scope")
|
|
1198
|
+
|
|
1199
|
+
## Principles
|
|
1200
|
+
|
|
1201
|
+
- [Dependency Injection](../principles/dependency-injection.md) - Dependencies should be passed in, not accessed globally
|
|
1202
|
+
- [Decoupled Code](../principles/decoupled-code.md) - Code should be organized into independent units
|
|
1203
|
+
- [Test Isolation](../principles/test-isolation.md) - Tests should not affect each other
|
|
1204
|
+
|
|
1205
|
+
## Blocked By
|
|
1206
|
+
|
|
1207
|
+
(none)
|
|
1208
|
+
|
|
1209
|
+
## Definition of Done
|
|
1210
|
+
|
|
1211
|
+
- [ ] Searched for module-level mutable variables (let/var outside functions)
|
|
1212
|
+
- [ ] Identified singleton patterns and getInstance methods
|
|
1213
|
+
- [ ] Found global registries (Maps, Sets, Arrays modified at module level)
|
|
1214
|
+
- [ ] Located functions with implicit dependencies on module-level state
|
|
1215
|
+
- [ ] Checked for scattered process.env access
|
|
1216
|
+
- [ ] Documented impact of each global state instance on testing
|
|
1217
|
+
- [ ] Proposed ideas for refactoring global state to explicit dependencies
|
|
1218
|
+
`;
|
|
1219
|
+
}
|
|
1162
1220
|
function ubiquitousLanguage() {
|
|
1163
1221
|
return dedent`
|
|
1164
1222
|
# Ubiquitous Language
|
|
@@ -1220,6 +1278,7 @@ var stockAuditFunctions = {
|
|
|
1220
1278
|
"dead-code": deadCode,
|
|
1221
1279
|
"error-handling": errorHandling,
|
|
1222
1280
|
"facts-verification": factsVerification,
|
|
1281
|
+
"global-state": globalState,
|
|
1223
1282
|
"ideas-from-commits": ideasFromCommits,
|
|
1224
1283
|
"ideas-from-principles": ideasFromPrinciples,
|
|
1225
1284
|
"performance-review": performanceReview,
|
|
@@ -2226,7 +2285,7 @@ var IDEA_TRANSITION_PREFIXES = [
|
|
|
2226
2285
|
"Decompose Idea: ",
|
|
2227
2286
|
"Shelve Idea: "
|
|
2228
2287
|
];
|
|
2229
|
-
var BUILD_IDEA_PREFIX = "
|
|
2288
|
+
var BUILD_IDEA_PREFIX = "Decompose Idea: ";
|
|
2230
2289
|
function titleToFilename(title) {
|
|
2231
2290
|
return `${title.toLowerCase().replace(/\./g, "-").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
2232
2291
|
}
|
|
@@ -3979,14 +4038,15 @@ function createDefaultUploadDependencies() {
|
|
|
3979
4038
|
const file = Bun.file(path);
|
|
3980
4039
|
return file.exists();
|
|
3981
4040
|
},
|
|
3982
|
-
uploadFile: async (url, token, fileBytes, contentType) => {
|
|
4041
|
+
uploadFile: async (url, token, fileBytes, contentType, fileName) => {
|
|
4042
|
+
const formData = new FormData;
|
|
4043
|
+
formData.append("file", new Blob([fileBytes.buffer], { type: contentType }), fileName);
|
|
3983
4044
|
const response = await fetch(url, {
|
|
3984
4045
|
method: "POST",
|
|
3985
4046
|
headers: {
|
|
3986
|
-
Authorization: `Bearer ${token}
|
|
3987
|
-
"Content-Type": contentType
|
|
4047
|
+
Authorization: `Bearer ${token}`
|
|
3988
4048
|
},
|
|
3989
|
-
body:
|
|
4049
|
+
body: formData
|
|
3990
4050
|
});
|
|
3991
4051
|
if (!response.ok) {
|
|
3992
4052
|
const text = await response.text();
|
|
@@ -4071,9 +4131,10 @@ async function bucketAssetUpload(dependencies, uploadDeps = createDefaultUploadD
|
|
|
4071
4131
|
}
|
|
4072
4132
|
const fileBytes = await uploadDeps.readFileBytes(filePath);
|
|
4073
4133
|
const contentType = getContentType(filePath);
|
|
4134
|
+
const fileName = filePath.split("/").pop();
|
|
4074
4135
|
const uploadUrl = `${getDustbucketHost()}/api/assets?repositoryId=${encodeURIComponent(repositoryId)}`;
|
|
4075
4136
|
try {
|
|
4076
|
-
const result = await uploadDeps.uploadFile(uploadUrl, token, fileBytes, contentType);
|
|
4137
|
+
const result = await uploadDeps.uploadFile(uploadUrl, token, fileBytes, contentType, fileName);
|
|
4077
4138
|
context.stdout(result.url);
|
|
4078
4139
|
return { exitCode: 0 };
|
|
4079
4140
|
} catch (error) {
|
|
@@ -5018,7 +5079,7 @@ async function runSingleCheck(check, cwd, runner) {
|
|
|
5018
5079
|
output: result.output,
|
|
5019
5080
|
hints: check.hints,
|
|
5020
5081
|
durationMs,
|
|
5021
|
-
timedOut: result.timedOut,
|
|
5082
|
+
timedOut: result.timedOut ?? false,
|
|
5022
5083
|
timeoutSeconds: timeoutMs / 1000
|
|
5023
5084
|
};
|
|
5024
5085
|
}
|
|
@@ -5057,7 +5118,8 @@ async function runValidationCheck(dependencies) {
|
|
|
5057
5118
|
output: outputLines.join(`
|
|
5058
5119
|
`),
|
|
5059
5120
|
isBuiltIn: true,
|
|
5060
|
-
durationMs
|
|
5121
|
+
durationMs,
|
|
5122
|
+
timedOut: false
|
|
5061
5123
|
};
|
|
5062
5124
|
}
|
|
5063
5125
|
function displayResults(results, context) {
|
|
@@ -5067,7 +5129,7 @@ function displayResults(results, context) {
|
|
|
5067
5129
|
if (result.timedOut) {
|
|
5068
5130
|
context.stdout(`✗ ${result.name} [timed out after ${result.timeoutSeconds}s]`);
|
|
5069
5131
|
} else {
|
|
5070
|
-
const timing = result.durationMs
|
|
5132
|
+
const timing = result.durationMs >= 1000 ? ` [${(result.durationMs / 1000).toFixed(1)}s]` : "";
|
|
5071
5133
|
if (result.exitCode === 0) {
|
|
5072
5134
|
context.stdout(`✓ ${result.name}${timing}`);
|
|
5073
5135
|
} else {
|