@joshski/dust 0.1.62 → 0.1.64

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.
@@ -1,4 +1,4 @@
1
- import type { ReadableFileSystem } from '../cli/types';
1
+ import type { ReadableFileSystem } from '../filesystem/types';
2
2
  export interface Fact {
3
3
  slug: string;
4
4
  title: string;
@@ -1,4 +1,4 @@
1
- import type { ReadableFileSystem } from '../cli/types';
1
+ import type { ReadableFileSystem } from '../filesystem/types';
2
2
  export interface IdeaOption {
3
3
  name: string;
4
4
  description: string;
@@ -1,4 +1,4 @@
1
- import type { FileSystem, ReadableFileSystem } from '../cli/types';
1
+ import type { FileSystem, ReadableFileSystem } from '../filesystem/types';
2
2
  import { type Fact } from './facts';
3
3
  import { type Idea, type IdeaOpenQuestion, type IdeaOption, parseOpenQuestions } from './ideas';
4
4
  import { type Principle } from './principles';
@@ -1,4 +1,4 @@
1
- import type { ReadableFileSystem } from '../cli/types';
1
+ import type { ReadableFileSystem } from '../filesystem/types';
2
2
  export interface Principle {
3
3
  slug: string;
4
4
  title: string;
@@ -1,4 +1,4 @@
1
- import type { ReadableFileSystem } from '../cli/types';
1
+ import type { ReadableFileSystem } from '../filesystem/types';
2
2
  export interface Task {
3
3
  slug: string;
4
4
  title: string;
@@ -1,4 +1,4 @@
1
- import type { FileSystem, ReadableFileSystem } from '../cli/types';
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
4
  export declare const BUILD_IDEA_PREFIX = "Build Idea: ";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Audits repository - programmatic access to audit templates.
3
+ *
4
+ * Audits are canned tasks that help maintain project health.
5
+ * Sources:
6
+ * 1. User-configured audits in .dust/config/audits/*.md (takes precedence)
7
+ * 2. Stock audits from lib/audits/stock-audits.ts
8
+ */
9
+ import type { FileSystem } from '../filesystem/types';
10
+ export { loadStockAudits } from './stock-audits';
11
+ export interface Audit {
12
+ name: string;
13
+ title: string;
14
+ description: string;
15
+ template: string;
16
+ source: 'stock' | string;
17
+ }
18
+ export interface CreateAuditTaskResult {
19
+ filePath: string;
20
+ relativePath: string;
21
+ }
22
+ export interface AuditsRepository {
23
+ listAudits(): Promise<Audit[]>;
24
+ parseAudit(options: {
25
+ name: string;
26
+ }): Promise<Audit>;
27
+ createAuditTask(options: {
28
+ name: string;
29
+ }): Promise<CreateAuditTaskResult>;
30
+ }
31
+ /**
32
+ * Transforms audit template content for the task file.
33
+ * Changes the title from "# Original Title" to "# Audit: Original Title"
34
+ */
35
+ export declare function transformAuditContent(content: string): string;
36
+ export declare function buildAuditsRepository(fileSystem: FileSystem, dustPath: string): AuditsRepository;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Stock audit templates as type-safe functions.
3
+ *
4
+ * Users can override any of these by placing a file with the same name
5
+ * in .dust/config/audits/.
6
+ */
7
+ export interface StockAudit {
8
+ name: string;
9
+ description: string;
10
+ template: string;
11
+ }
12
+ export declare function loadStockAudits(): StockAudit[];
package/dist/audits.js ADDED
@@ -0,0 +1,459 @@
1
+ // lib/audits/index.ts
2
+ import { basename } from "node:path";
3
+
4
+ // lib/markdown/markdown-utilities.ts
5
+ function extractTitle(content) {
6
+ const match = content.match(/^#\s+(.+)$/m);
7
+ return match ? match[1].trim() : null;
8
+ }
9
+ function extractOpeningSentence(content) {
10
+ const lines = content.split(`
11
+ `);
12
+ let h1Index = -1;
13
+ for (let i = 0;i < lines.length; i++) {
14
+ if (lines[i].match(/^#\s+.+$/)) {
15
+ h1Index = i;
16
+ break;
17
+ }
18
+ }
19
+ if (h1Index === -1) {
20
+ return null;
21
+ }
22
+ let paragraphStart = -1;
23
+ for (let i = h1Index + 1;i < lines.length; i++) {
24
+ const line = lines[i].trim();
25
+ if (line !== "") {
26
+ paragraphStart = i;
27
+ break;
28
+ }
29
+ }
30
+ if (paragraphStart === -1) {
31
+ return null;
32
+ }
33
+ const firstLine = lines[paragraphStart];
34
+ const trimmedFirstLine = firstLine.trim();
35
+ if (trimmedFirstLine.startsWith("#") || trimmedFirstLine.startsWith("-") || trimmedFirstLine.startsWith("*") || trimmedFirstLine.startsWith("+") || trimmedFirstLine.match(/^\d+\./) || trimmedFirstLine.startsWith("```") || trimmedFirstLine.startsWith(">")) {
36
+ return null;
37
+ }
38
+ let paragraph = "";
39
+ for (let i = paragraphStart;i < lines.length; i++) {
40
+ const line = lines[i].trim();
41
+ if (line === "")
42
+ break;
43
+ if (line.startsWith("#") || line.startsWith("```") || line.startsWith(">")) {
44
+ break;
45
+ }
46
+ paragraph += (paragraph ? " " : "") + line;
47
+ }
48
+ const sentenceMatch = paragraph.match(/^(.+?[.?!])(?:\s|$)/);
49
+ if (!sentenceMatch) {
50
+ return null;
51
+ }
52
+ return sentenceMatch[1];
53
+ }
54
+
55
+ // lib/cli/dedent.ts
56
+ function dedent(strings, ...values) {
57
+ const result = strings.reduce((acc, part, index) => acc + part + (values[index] ?? ""), "");
58
+ const lines = result.split(`
59
+ `);
60
+ const indent = lines.filter((line) => line.trim()).reduce((min, line) => Math.min(min, line.match(/^\s*/)[0].length), Number.POSITIVE_INFINITY);
61
+ return lines.map((line) => line.slice(indent)).join(`
62
+ `).trim();
63
+ }
64
+
65
+ // lib/audits/stock-audits.ts
66
+ function agentDeveloperExperience() {
67
+ return dedent`
68
+ # Agent Developer Experience
69
+
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.
71
+
72
+ ## Scope
73
+
74
+ Focus on these areas:
75
+
76
+ 1. **Context window efficiency** - Are files small and well-organized?
77
+ 2. **Test coverage** - Can agents verify correctness through tests?
78
+ 3. **Feedback loop speed** - How fast are checks and tests?
79
+ 4. **Debugging tools** - Can agents diagnose issues without trial and error?
80
+ 5. **Structured logging** - Is system behavior observable through logs?
81
+
82
+ ## Principles
83
+
84
+ (none)
85
+
86
+ ## Blocked By
87
+
88
+ (none)
89
+
90
+ ## Definition of Done
91
+
92
+ - [ ] Reviewed file sizes and organization for context window fit
93
+ - [ ] Verified test coverage is sufficient for agent verification
94
+ - [ ] Measured feedback loop speed (time from change to check result)
95
+ - [ ] Confirmed debugging tools and structured logging are in place
96
+ - [ ] Proposed ideas for any improvements identified
97
+ `;
98
+ }
99
+ function deadCode() {
100
+ return dedent`
101
+ # Dead Code
102
+
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.
104
+
105
+ ## Scope
106
+
107
+ Focus on these areas:
108
+
109
+ 1. **Unused exports** - Functions, classes, constants that are never imported
110
+ 2. **Unreachable code** - Code after return statements, impossible conditions
111
+ 3. **Orphaned files** - Files that are not imported anywhere
112
+ 4. **Unused dependencies** - Packages in package.json not used in code
113
+ 5. **Commented-out code** - Old code left in comments
114
+
115
+ ## Principles
116
+
117
+ (none)
118
+
119
+ ## Blocked By
120
+
121
+ (none)
122
+
123
+ ## Definition of Done
124
+
125
+ - [ ] Ran static analysis tools to find unused exports
126
+ - [ ] Identified files with no incoming imports
127
+ - [ ] Listed unused dependencies
128
+ - [ ] Reviewed commented-out code blocks
129
+ - [ ] Created list of code safe to remove
130
+ - [ ] Verified removal won't break dynamic imports or reflection
131
+ - [ ] Proposed ideas for any dead code worth removing
132
+ `;
133
+ }
134
+ function factsVerification() {
135
+ return dedent`
136
+ # Facts Verification
137
+
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.
139
+
140
+ ## Scope
141
+
142
+ Focus on these areas:
143
+
144
+ 1. **Accuracy** - Do documented facts reflect the current codebase?
145
+ 2. **Completeness** - Are important implementation details documented?
146
+ 3. **Staleness** - Have facts become outdated due to recent changes?
147
+ 4. **Relevance** - Are all facts still useful for understanding the project?
148
+
149
+ ## Principles
150
+
151
+ (none)
152
+
153
+ ## Blocked By
154
+
155
+ (none)
156
+
157
+ ## Definition of Done
158
+
159
+ - [ ] Read each fact file in \`.dust/facts/\`
160
+ - [ ] Verified each fact against current codebase
161
+ - [ ] Identified outdated or inaccurate facts
162
+ - [ ] Listed missing facts that would help agents
163
+ - [ ] Updated or removed stale facts
164
+ - [ ] Proposed ideas for any facts improvements needed
165
+ `;
166
+ }
167
+ function ideasFromCommits() {
168
+ return dedent`
169
+ # Ideas from Commits
170
+
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.
172
+
173
+ ## Scope
174
+
175
+ Focus on these areas:
176
+
177
+ 1. **Technical debt** - Did recent work introduce shortcuts?
178
+ 2. **Incomplete work** - Are there TODO comments or partial implementations?
179
+ 3. **Pattern opportunities** - Can recent changes be generalized?
180
+ 4. **Test gaps** - Do recent changes have adequate test coverage?
181
+
182
+ ## Principles
183
+
184
+ (none)
185
+
186
+ ## Blocked By
187
+
188
+ (none)
189
+
190
+ ## Definition of Done
191
+
192
+ - [ ] Reviewed commits from the last 20 commits
193
+ - [ ] Identified patterns or shortcuts worth addressing
194
+ - [ ] Listed TODO comments added in recent commits
195
+ - [ ] Noted areas where changes could be generalized
196
+ - [ ] Proposed follow-up ideas for any issues identified
197
+ `;
198
+ }
199
+ function ideasFromPrinciples() {
200
+ return dedent`
201
+ # Ideas from Principles
202
+
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.
204
+
205
+ ## Scope
206
+
207
+ Focus on these areas:
208
+
209
+ 1. **Unmet principles** - Which principles lack supporting work?
210
+ 2. **Gap analysis** - Where does the codebase fall short of principles?
211
+ 3. **New opportunities** - What work would better achieve each principle?
212
+ 4. **Principle alignment** - Are current tasks aligned with stated principles?
213
+
214
+ ## Principles
215
+
216
+ (none)
217
+
218
+ ## Blocked By
219
+
220
+ (none)
221
+
222
+ ## Definition of Done
223
+
224
+ - [ ] Read each principle file in \`.dust/principles/\`
225
+ - [ ] Analyzed codebase for alignment with each principle
226
+ - [ ] Listed gaps between current state and principle intent
227
+ - [ ] Proposed new ideas for unmet or underserved principles
228
+ `;
229
+ }
230
+ function performanceReview() {
231
+ return dedent`
232
+ # Performance Review
233
+
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.
235
+
236
+ ## Scope
237
+
238
+ Focus on these areas:
239
+
240
+ 1. **Startup time** - How fast does the application start?
241
+ 2. **Command latency** - How responsive are CLI commands?
242
+ 3. **Memory usage** - Is memory being used efficiently?
243
+ 4. **Build performance** - How fast is the build process?
244
+ 5. **Test speed** - Are tests running efficiently?
245
+
246
+ ## Principles
247
+
248
+ (none)
249
+
250
+ ## Blocked By
251
+
252
+ (none)
253
+
254
+ ## Definition of Done
255
+
256
+ - [ ] Measured startup time for common commands
257
+ - [ ] Profiled memory usage during typical operations
258
+ - [ ] Identified slow commands or operations
259
+ - [ ] Listed optimization opportunities by impact
260
+ - [ ] Proposed ideas for any performance improvements identified
261
+ `;
262
+ }
263
+ function securityReview() {
264
+ return dedent`
265
+ # Security Review
266
+
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.
268
+
269
+ ## Scope
270
+
271
+ Focus on these areas:
272
+
273
+ 1. **Hardcoded secrets** - API keys, passwords, tokens in source code
274
+ 2. **Injection vulnerabilities** - SQL injection, command injection, XSS
275
+ 3. **Authentication issues** - Weak password handling, missing auth checks
276
+ 4. **Sensitive data exposure** - Logging sensitive data, insecure storage
277
+ 5. **Dependency vulnerabilities** - Known CVEs in dependencies
278
+
279
+ ## Principles
280
+
281
+ (none)
282
+
283
+ ## Blocked By
284
+
285
+ (none)
286
+
287
+ ## Definition of Done
288
+
289
+ - [ ] Searched for hardcoded secrets (API keys, passwords, tokens)
290
+ - [ ] Reviewed input validation and sanitization
291
+ - [ ] Checked authentication and authorization logic
292
+ - [ ] Verified sensitive data is not logged or exposed
293
+ - [ ] Ran dependency audit for known vulnerabilities
294
+ - [ ] Documented any findings with severity ratings
295
+ - [ ] Proposed ideas for any security issues found
296
+ `;
297
+ }
298
+ function staleIdeas() {
299
+ return dedent`
300
+ # Stale Ideas
301
+
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.
303
+
304
+ ## Scope
305
+
306
+ Focus on these areas:
307
+
308
+ 1. **Age** - Ideas unchanged for many commits may need attention
309
+ 2. **Relevance** - Has the project evolved past the idea?
310
+ 3. **Actionability** - Can the idea be converted to a task?
311
+ 4. **Duplication** - Are there overlapping or redundant ideas?
312
+
313
+ ## Principles
314
+
315
+ (none)
316
+
317
+ ## Blocked By
318
+
319
+ (none)
320
+
321
+ ## Definition of Done
322
+
323
+ - [ ] Listed all ideas with their last modification date
324
+ - [ ] Identified ideas unchanged for 50+ commits
325
+ - [ ] Reviewed each stale idea for current relevance
326
+ - [ ] Promoted actionable ideas to tasks
327
+ - [ ] Deleted ideas that are no longer relevant
328
+ `;
329
+ }
330
+ function testCoverage() {
331
+ return dedent`
332
+ # Test Coverage
333
+
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.
335
+
336
+ ## Scope
337
+
338
+ Focus on these areas:
339
+
340
+ 1. **Core business logic** - Functions that handle critical operations
341
+ 2. **Edge cases** - Boundary conditions, error handling paths
342
+ 3. **Integration points** - API endpoints, database operations
343
+ 4. **User-facing features** - UI components, form validation
344
+ 5. **Recent changes** - Code modified in the last few commits
345
+
346
+ ## Principles
347
+
348
+ (none)
349
+
350
+ ## Blocked By
351
+
352
+ (none)
353
+
354
+ ## Definition of Done
355
+
356
+ - [ ] Identified modules with low or no test coverage
357
+ - [ ] Listed critical paths that lack tests
358
+ - [ ] Prioritized areas by risk and importance
359
+ - [ ] Proposed ideas for any test coverage gaps identified
360
+ `;
361
+ }
362
+ var stockAuditFunctions = {
363
+ "agent-developer-experience": agentDeveloperExperience,
364
+ "dead-code": deadCode,
365
+ "facts-verification": factsVerification,
366
+ "ideas-from-commits": ideasFromCommits,
367
+ "ideas-from-principles": ideasFromPrinciples,
368
+ "performance-review": performanceReview,
369
+ "security-review": securityReview,
370
+ "stale-ideas": staleIdeas,
371
+ "test-coverage": testCoverage
372
+ };
373
+ function loadStockAudits() {
374
+ return Object.entries(stockAuditFunctions).sort(([a], [b]) => a.localeCompare(b)).map(([name, render]) => {
375
+ const template = render();
376
+ const description = extractOpeningSentence(template);
377
+ return { name, description, template };
378
+ });
379
+ }
380
+
381
+ // lib/audits/index.ts
382
+ function transformAuditContent(content) {
383
+ const titleMatch = content.match(/^#\s+(.+)$/m);
384
+ if (!titleMatch) {
385
+ return content;
386
+ }
387
+ const originalTitle = titleMatch[1];
388
+ return content.replace(/^#\s+.+$/m, `# Audit: ${originalTitle}`);
389
+ }
390
+ function buildAuditsRepository(fileSystem, dustPath) {
391
+ const userAuditsPath = `${dustPath}/config/audits`;
392
+ const tasksPath = `${dustPath}/tasks`;
393
+ async function loadAllAudits() {
394
+ const audits = new Map;
395
+ for (const stockAudit of loadStockAudits()) {
396
+ audits.set(stockAudit.name, {
397
+ name: stockAudit.name,
398
+ title: extractTitle(stockAudit.template),
399
+ description: stockAudit.description,
400
+ template: stockAudit.template,
401
+ source: "stock"
402
+ });
403
+ }
404
+ if (fileSystem.exists(userAuditsPath)) {
405
+ const files = await fileSystem.readdir(userAuditsPath);
406
+ const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
407
+ for (const file of mdFiles) {
408
+ const name = basename(file, ".md");
409
+ const filePath = `${userAuditsPath}/${file}`;
410
+ const content = await fileSystem.readFile(filePath);
411
+ const title = extractTitle(content) || name;
412
+ const description = extractOpeningSentence(content) || "";
413
+ const relativePath = `.dust/config/audits/${file}`;
414
+ audits.set(name, {
415
+ name,
416
+ title,
417
+ description,
418
+ template: content,
419
+ source: relativePath
420
+ });
421
+ }
422
+ }
423
+ return audits;
424
+ }
425
+ return {
426
+ async listAudits() {
427
+ const auditsMap = await loadAllAudits();
428
+ return Array.from(auditsMap.values()).sort((a, b) => a.name.localeCompare(b.name));
429
+ },
430
+ async parseAudit(options) {
431
+ const auditsMap = await loadAllAudits();
432
+ const audit = auditsMap.get(options.name);
433
+ if (!audit) {
434
+ throw new Error(`Audit not found: "${options.name}"`);
435
+ }
436
+ return audit;
437
+ },
438
+ async createAuditTask(options) {
439
+ const audit = await this.parseAudit(options);
440
+ const taskFilePath = `${tasksPath}/audit-${options.name}.md`;
441
+ const relativeTaskPath = `.dust/tasks/audit-${options.name}.md`;
442
+ if (fileSystem.exists(taskFilePath)) {
443
+ throw new Error(`Audit task already exists at ${relativeTaskPath}`);
444
+ }
445
+ const transformedContent = transformAuditContent(audit.template);
446
+ await fileSystem.mkdir(tasksPath, { recursive: true });
447
+ await fileSystem.writeFile(taskFilePath, transformedContent);
448
+ return {
449
+ filePath: taskFilePath,
450
+ relativePath: relativeTaskPath
451
+ };
452
+ }
453
+ };
454
+ }
455
+ export {
456
+ transformAuditContent,
457
+ loadStockAudits,
458
+ buildAuditsRepository
459
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Dedent tagged template literal helper
3
+ *
4
+ * Strips common leading whitespace from multi-line template literals,
5
+ * making it possible to write properly indented code while producing
6
+ * clean output.
7
+ */
8
+ export declare function dedent(strings: TemplateStringsArray, ...values: unknown[]): string;
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Common types for CLI commands
3
3
  */
4
+ import type { FileSystem, GlobScanner } from '../filesystem/types';
5
+ export type { FileSystem, GlobScanner, ReadableFileSystem, WriteOptions, } from '../filesystem/types';
4
6
  export interface CommandContext {
5
7
  cwd: string;
6
8
  stdout: (message: string) => void;
@@ -10,27 +12,6 @@ export interface CommandContext {
10
12
  export interface CommandResult {
11
13
  exitCode: number;
12
14
  }
13
- export interface WriteOptions {
14
- flag?: 'w' | 'wx';
15
- }
16
- export interface ReadableFileSystem {
17
- exists: (path: string) => boolean;
18
- readFile: (path: string) => Promise<string>;
19
- readdir: (path: string) => Promise<string[]>;
20
- isDirectory: (path: string) => boolean;
21
- }
22
- export interface FileSystem extends ReadableFileSystem {
23
- writeFile: (path: string, content: string, options?: WriteOptions) => Promise<void>;
24
- mkdir: (path: string, options?: {
25
- recursive?: boolean;
26
- }) => Promise<void>;
27
- chmod: (path: string, mode: number) => Promise<void>;
28
- getFileCreationTime: (path: string) => number;
29
- rename: (oldPath: string, newPath: string) => Promise<void>;
30
- }
31
- export interface GlobScanner {
32
- scan: (dir: string) => AsyncIterable<string>;
33
- }
34
15
  export interface CheckConfig {
35
16
  name: string;
36
17
  command: string;