@joshski/dust 0.1.108 → 0.1.110
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/audits.js +666 -28
- package/dist/dust.js +710 -56
- package/dist/filesystem/error-codes.d.ts +2 -0
- package/dist/loop/iteration.d.ts +2 -2
- package/dist/patch.js +23 -14
- package/dist/validation.js +20 -11
- package/package.json +1 -1
package/dist/dust.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
7
7
|
var require_package = __commonJS((exports, module) => {
|
|
8
8
|
module.exports = {
|
|
9
9
|
name: "@joshski/dust",
|
|
10
|
-
version: "0.1.
|
|
10
|
+
version: "0.1.110",
|
|
11
11
|
description: "Flow state for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -409,6 +409,16 @@ function createGitDirectoryFileSorter(gitRunner) {
|
|
|
409
409
|
|
|
410
410
|
// lib/config/settings.ts
|
|
411
411
|
import { join as join2 } from "node:path";
|
|
412
|
+
|
|
413
|
+
// lib/filesystem/error-codes.ts
|
|
414
|
+
function isErrnoException(error) {
|
|
415
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string";
|
|
416
|
+
}
|
|
417
|
+
function isErrorCode(error, code) {
|
|
418
|
+
return isErrnoException(error) && error.code === code;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// lib/config/settings.ts
|
|
412
422
|
var KNOWN_SETTINGS_KEYS = new Set([
|
|
413
423
|
"dustCommand",
|
|
414
424
|
"checks",
|
|
@@ -693,7 +703,7 @@ async function loadSettings(cwd, fileSystem, runtime) {
|
|
|
693
703
|
}
|
|
694
704
|
return result;
|
|
695
705
|
} catch (error) {
|
|
696
|
-
if (error
|
|
706
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
697
707
|
const result = {
|
|
698
708
|
dustCommand: detectDustCommand(cwd, fileSystem, runtime)
|
|
699
709
|
};
|
|
@@ -711,7 +721,7 @@ async function loadSettings(cwd, fileSystem, runtime) {
|
|
|
711
721
|
}
|
|
712
722
|
|
|
713
723
|
// lib/version.ts
|
|
714
|
-
var DUST_VERSION = "0.1.
|
|
724
|
+
var DUST_VERSION = "0.1.110";
|
|
715
725
|
|
|
716
726
|
// lib/cli/middleware.ts
|
|
717
727
|
function applyMiddleware(middlewares, execute) {
|
|
@@ -834,7 +844,7 @@ function createHooksManager(cwd, fileSystem, settings) {
|
|
|
834
844
|
const content = await fileSystem.readFile(prePushPath);
|
|
835
845
|
return content.includes(DUST_HOOK_START);
|
|
836
846
|
} catch (error) {
|
|
837
|
-
if (error
|
|
847
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
838
848
|
return false;
|
|
839
849
|
}
|
|
840
850
|
throw error;
|
|
@@ -862,7 +872,7 @@ ${hookContent}
|
|
|
862
872
|
`;
|
|
863
873
|
}
|
|
864
874
|
} catch (error) {
|
|
865
|
-
if (error
|
|
875
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
866
876
|
finalContent = `#!/bin/sh
|
|
867
877
|
|
|
868
878
|
${hookContent}
|
|
@@ -884,7 +894,7 @@ ${hookContent}
|
|
|
884
894
|
const match = dustSection.match(/^(.+) pre push$/m);
|
|
885
895
|
return match ? match[1] : null;
|
|
886
896
|
} catch (error) {
|
|
887
|
-
if (error
|
|
897
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
888
898
|
return null;
|
|
889
899
|
}
|
|
890
900
|
throw error;
|
|
@@ -895,7 +905,7 @@ ${hookContent}
|
|
|
895
905
|
try {
|
|
896
906
|
content = await fileSystem.readFile(prePushPath);
|
|
897
907
|
} catch (error) {
|
|
898
|
-
if (error
|
|
908
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
899
909
|
return;
|
|
900
910
|
}
|
|
901
911
|
throw error;
|
|
@@ -929,7 +939,7 @@ async function loadAgentInstructions(cwd, fileSystem, agentType) {
|
|
|
929
939
|
const content = await fileSystem.readFile(instructionsPath);
|
|
930
940
|
return content.trim();
|
|
931
941
|
} catch (error) {
|
|
932
|
-
if (error
|
|
942
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
933
943
|
return "";
|
|
934
944
|
}
|
|
935
945
|
throw error;
|
|
@@ -1530,6 +1540,72 @@ function deadCode() {
|
|
|
1530
1540
|
- No changes to files outside \`.dust/\`
|
|
1531
1541
|
`;
|
|
1532
1542
|
}
|
|
1543
|
+
function directoryHierarchy() {
|
|
1544
|
+
return dedent`
|
|
1545
|
+
# Directory Hierarchy
|
|
1546
|
+
|
|
1547
|
+
Review directory structure and create improvement ideas.
|
|
1548
|
+
|
|
1549
|
+
${ideasHint}
|
|
1550
|
+
|
|
1551
|
+
## Scope
|
|
1552
|
+
|
|
1553
|
+
Analyze the project's directory organization for these issues:
|
|
1554
|
+
|
|
1555
|
+
1. **Concern mixing** - Directories containing files that serve different purposes
|
|
1556
|
+
2. **Missing grouping** - Related files scattered across multiple locations
|
|
1557
|
+
3. **Depth inconsistency** - Similar directories at inconsistent depths
|
|
1558
|
+
4. **Naming inconsistency** - Directory names that don't follow established patterns
|
|
1559
|
+
5. **Singleton directories** - Directories with only a single file or subdirectory
|
|
1560
|
+
6. **Orphaned files** - Files at inappropriate directory levels
|
|
1561
|
+
|
|
1562
|
+
## Analysis Steps
|
|
1563
|
+
|
|
1564
|
+
1. **Explore the directory tree** - Walk the project's file system recursively, excluding \`node_modules\`, \`.git\`, \`dist\`, \`build\`, \`coverage\`, and other common build artifact directories
|
|
1565
|
+
2. **Identify issues** - For each of the issue types listed above, look for concrete examples in the directory structure
|
|
1566
|
+
3. **Create ideas** - For each issue found, create an idea file in \`.dust/ideas/\` with:
|
|
1567
|
+
- Descriptive filename based on the issue type and affected paths
|
|
1568
|
+
- The specific paths affected
|
|
1569
|
+
- Why the current structure is problematic
|
|
1570
|
+
- A proposed reorganization
|
|
1571
|
+
- Migration complexity estimate (low/medium/high)
|
|
1572
|
+
|
|
1573
|
+
## Output Format
|
|
1574
|
+
|
|
1575
|
+
Each idea file should follow this structure:
|
|
1576
|
+
|
|
1577
|
+
\`\`\`markdown
|
|
1578
|
+
# [Issue Type]: [Brief Description]
|
|
1579
|
+
|
|
1580
|
+
## Current Structure
|
|
1581
|
+
|
|
1582
|
+
[List specific paths from affectedPaths]
|
|
1583
|
+
|
|
1584
|
+
## Problem
|
|
1585
|
+
|
|
1586
|
+
[Description of why this is problematic]
|
|
1587
|
+
|
|
1588
|
+
## Proposed Solution
|
|
1589
|
+
|
|
1590
|
+
[Suggested reorganization]
|
|
1591
|
+
|
|
1592
|
+
## Migration Complexity
|
|
1593
|
+
|
|
1594
|
+
[Low/Medium/High with brief rationale]
|
|
1595
|
+
\`\`\`
|
|
1596
|
+
|
|
1597
|
+
## Blocked By
|
|
1598
|
+
|
|
1599
|
+
(none)
|
|
1600
|
+
|
|
1601
|
+
## Definition of Done
|
|
1602
|
+
|
|
1603
|
+
- Explored the directory tree excluding standard build/tool directories
|
|
1604
|
+
- Created idea files for all findings in \`.dust/ideas/\`
|
|
1605
|
+
- Each idea includes specific paths, problem description, proposed solution, and complexity
|
|
1606
|
+
- No changes to files outside \`.dust/\`
|
|
1607
|
+
`;
|
|
1608
|
+
}
|
|
1533
1609
|
function documentationDrift() {
|
|
1534
1610
|
return dedent`
|
|
1535
1611
|
# Documentation Drift
|
|
@@ -1680,6 +1756,110 @@ function factsVerification() {
|
|
|
1680
1756
|
- No changes to files outside \`.dust/\`
|
|
1681
1757
|
`;
|
|
1682
1758
|
}
|
|
1759
|
+
function factsExpansion() {
|
|
1760
|
+
return dedent`
|
|
1761
|
+
# Facts Expansion
|
|
1762
|
+
|
|
1763
|
+
Review the codebase for significant facts that should be documented in \`.dust/facts/\`.
|
|
1764
|
+
|
|
1765
|
+
${ideasHint}
|
|
1766
|
+
|
|
1767
|
+
## Context
|
|
1768
|
+
|
|
1769
|
+
Facts capture how things work today, providing context for agents and contributors. However, not all significant aspects of the codebase are currently documented as facts. This creates gaps where agents working in specific areas may lack important context that isn't obvious from scanning code or having prior framework knowledge.
|
|
1770
|
+
|
|
1771
|
+
## Applicability
|
|
1772
|
+
|
|
1773
|
+
This audit applies to all codebases. If \`.dust/facts/\` does not exist, the audit will identify initial facts to document.
|
|
1774
|
+
|
|
1775
|
+
## Scope
|
|
1776
|
+
|
|
1777
|
+
Analyze the codebase for undocumented facts across these areas:
|
|
1778
|
+
|
|
1779
|
+
### Architectural Decisions
|
|
1780
|
+
- Separation of concerns patterns not enforced by directory structure
|
|
1781
|
+
- Dependency flow rules (e.g., what can depend on what)
|
|
1782
|
+
- Layer boundaries and their purposes
|
|
1783
|
+
- Module initialization order requirements
|
|
1784
|
+
- Plugin or extension mechanisms
|
|
1785
|
+
|
|
1786
|
+
### Implementation Conventions
|
|
1787
|
+
- Naming patterns for specific types of code (factories, builders, validators)
|
|
1788
|
+
- Error handling conventions (when to throw vs return errors)
|
|
1789
|
+
- Async/await patterns and Promise handling
|
|
1790
|
+
- Resource cleanup patterns
|
|
1791
|
+
- State management approaches
|
|
1792
|
+
|
|
1793
|
+
### External Integration Points
|
|
1794
|
+
- CLI command structure and parsing approach
|
|
1795
|
+
- Event emission patterns
|
|
1796
|
+
- File system conventions
|
|
1797
|
+
- Process spawning patterns
|
|
1798
|
+
- Network communication protocols
|
|
1799
|
+
|
|
1800
|
+
### Performance Characteristics
|
|
1801
|
+
- Known performance bottlenecks
|
|
1802
|
+
- Caching strategies
|
|
1803
|
+
- Lazy loading patterns
|
|
1804
|
+
- Resource pooling approaches
|
|
1805
|
+
- Optimization trade-offs
|
|
1806
|
+
|
|
1807
|
+
### Historical Context
|
|
1808
|
+
- Migration paths from previous approaches
|
|
1809
|
+
- Deprecated patterns still present in legacy code
|
|
1810
|
+
- Trade-offs made in past decisions
|
|
1811
|
+
- Features that were removed and why
|
|
1812
|
+
|
|
1813
|
+
## Analysis Approach
|
|
1814
|
+
|
|
1815
|
+
1. **Scan for patterns** - Look for repeated implementation patterns across multiple files
|
|
1816
|
+
2. **Identify conventions** - Find coding conventions that aren't enforced by linters
|
|
1817
|
+
3. **Review configuration** - Document configuration systems and their purposes
|
|
1818
|
+
4. **Trace data flows** - Identify how data moves through the system
|
|
1819
|
+
5. **Check existing facts** - Compare findings against what's already documented in \`.dust/facts/\`
|
|
1820
|
+
6. **Filter for significance** - Only suggest facts that would genuinely help future agents (facts that aren't obvious from code inspection or general framework knowledge)
|
|
1821
|
+
|
|
1822
|
+
## Significance Criteria
|
|
1823
|
+
|
|
1824
|
+
A fact is worth documenting if:
|
|
1825
|
+
- It's not obvious from reading the code in isolation
|
|
1826
|
+
- It represents a project-specific decision or convention
|
|
1827
|
+
- Future agents would benefit from knowing it before making changes
|
|
1828
|
+
- It documents framework patterns actually used in this project
|
|
1829
|
+
|
|
1830
|
+
## Output Format
|
|
1831
|
+
|
|
1832
|
+
For each suggested fact, create an idea file in \`.dust/ideas/\` that includes:
|
|
1833
|
+
|
|
1834
|
+
### Fact Title
|
|
1835
|
+
A clear, concise title for the proposed fact.
|
|
1836
|
+
|
|
1837
|
+
### Why This Matters
|
|
1838
|
+
Explanation of why this fact would be valuable to document (what gaps it fills, what problems it prevents).
|
|
1839
|
+
|
|
1840
|
+
### What to Document
|
|
1841
|
+
Specific aspects to cover in the fact file.
|
|
1842
|
+
|
|
1843
|
+
### Where to Look
|
|
1844
|
+
File paths or code locations that demonstrate this fact.
|
|
1845
|
+
|
|
1846
|
+
### Example Content
|
|
1847
|
+
A sketch of what the fact file might contain (2-3 sentences showing the style and key points).
|
|
1848
|
+
|
|
1849
|
+
## Blocked By
|
|
1850
|
+
|
|
1851
|
+
(none)
|
|
1852
|
+
|
|
1853
|
+
## Definition of Done
|
|
1854
|
+
|
|
1855
|
+
- Analyzed codebase for undocumented patterns across all specified areas
|
|
1856
|
+
- Compared findings against existing facts in \`.dust/facts/\`
|
|
1857
|
+
- Applied significance criteria to filter suggestions
|
|
1858
|
+
- Created idea files for each suggested fact with complete metadata
|
|
1859
|
+
- Each idea includes: fact title, why it matters, what to document, where to look, example content
|
|
1860
|
+
- No changes to files outside \`.dust/\`
|
|
1861
|
+
`;
|
|
1862
|
+
}
|
|
1683
1863
|
function feedbackLoopSpeed() {
|
|
1684
1864
|
return dedent`
|
|
1685
1865
|
# Feedback Loop Speed
|
|
@@ -1690,7 +1870,7 @@ function feedbackLoopSpeed() {
|
|
|
1690
1870
|
|
|
1691
1871
|
## Context
|
|
1692
1872
|
|
|
1693
|
-
The
|
|
1873
|
+
The primary feedback loop—write code, run checks, see results—should be as fast as possible. Agents especially benefit because they operate in tight loops of change-and-verify; slow feedback wastes tokens and context window space on waiting rather than working.
|
|
1694
1874
|
|
|
1695
1875
|
This audit focuses specifically on measuring the development feedback loop speed to help identify which checks consume the most time.
|
|
1696
1876
|
|
|
@@ -2136,6 +2316,223 @@ function ideasFromPrinciples() {
|
|
|
2136
2316
|
- No changes to files outside \`.dust/\`
|
|
2137
2317
|
`;
|
|
2138
2318
|
}
|
|
2319
|
+
function incidentalTestDetails() {
|
|
2320
|
+
return dedent`
|
|
2321
|
+
# Incidental Test Details
|
|
2322
|
+
|
|
2323
|
+
Identify tests with overly specific data and other incidental details that obscure test intent.
|
|
2324
|
+
|
|
2325
|
+
${ideasHint}
|
|
2326
|
+
|
|
2327
|
+
## Context
|
|
2328
|
+
|
|
2329
|
+
Test clarity suffers when tests include incidental complexity — details that aren't relevant to what's being tested. Overly specific data, unused properties, magic numbers, excessive mocking, and complex nested structures all make tests harder to understand and maintain. This audit identifies these patterns as candidates for simplification.
|
|
2330
|
+
|
|
2331
|
+
The audit flags patterns for review without making judgments about whether they're necessary in specific cases. Some tests legitimately need complex setup to verify specific behaviors; the goal is to surface candidates so agents can evaluate each case and simplify where appropriate.
|
|
2332
|
+
|
|
2333
|
+
## Guidance
|
|
2334
|
+
|
|
2335
|
+
### Readable Test Data
|
|
2336
|
+
|
|
2337
|
+
Test data setup should use natural structures that mirror what they represent.
|
|
2338
|
+
|
|
2339
|
+
When test data is easy to read, tests become self-documenting. A file system hierarchy expressed as a nested object immediately conveys structure, while a flat Map with path strings requires mental parsing to understand the relationships.
|
|
2340
|
+
|
|
2341
|
+
Prefer literal structures that visually match the domain:
|
|
2342
|
+
|
|
2343
|
+
\`\`\`javascript
|
|
2344
|
+
// Avoid: flat paths that obscure hierarchy
|
|
2345
|
+
const fs = createFileSystemEmulator({
|
|
2346
|
+
files: new Map([['/project/.dust/principles/my-goal.md', '# My Goal']]),
|
|
2347
|
+
existingPaths: new Set(['/project/.dust/ideas']),
|
|
2348
|
+
})
|
|
2349
|
+
|
|
2350
|
+
// Prefer: nested object that mirrors file system structure
|
|
2351
|
+
const fs = createFileSystemEmulator({
|
|
2352
|
+
project: {
|
|
2353
|
+
'.dust': {
|
|
2354
|
+
principles: {
|
|
2355
|
+
'my-goal.md': '# My Goal'
|
|
2356
|
+
},
|
|
2357
|
+
ideas: {}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
})
|
|
2361
|
+
\`\`\`
|
|
2362
|
+
|
|
2363
|
+
The nested form:
|
|
2364
|
+
- Shows parent-child relationships through indentation
|
|
2365
|
+
- Makes empty directories explicit with empty objects
|
|
2366
|
+
- Requires no mental path concatenation to understand structure
|
|
2367
|
+
|
|
2368
|
+
### Comprehensive Assertions
|
|
2369
|
+
|
|
2370
|
+
Assert the whole, not the parts.
|
|
2371
|
+
|
|
2372
|
+
When you break a complex object into many small assertions, a failure tells you *one thing that's wrong*. When you assert against the whole expected value, the diff tells you *what actually happened versus what you expected* — the full picture, in one glance.
|
|
2373
|
+
|
|
2374
|
+
Small assertions are like yes/no questions to a witness. A whole-object assertion is like asking "tell me what you saw."
|
|
2375
|
+
|
|
2376
|
+
Collapse multiple partial assertions into one comprehensive assertion:
|
|
2377
|
+
|
|
2378
|
+
\`\`\`javascript
|
|
2379
|
+
// Fragmented — each failure is a narrow keyhole
|
|
2380
|
+
expect(result.name).toBe("Alice");
|
|
2381
|
+
expect(result.age).toBe(30);
|
|
2382
|
+
expect(result.role).toBe("admin");
|
|
2383
|
+
|
|
2384
|
+
// Whole — a failure diff tells the full story
|
|
2385
|
+
expect(result).toEqual({
|
|
2386
|
+
name: "Alice",
|
|
2387
|
+
age: 30,
|
|
2388
|
+
role: "admin",
|
|
2389
|
+
});
|
|
2390
|
+
\`\`\`
|
|
2391
|
+
|
|
2392
|
+
If \`role\` is \`"user"\` and \`age\` is \`29\`, the fragmented version stops at the first failure. The whole-object assertion shows both discrepancies at once, in context.
|
|
2393
|
+
|
|
2394
|
+
### Self-Diagnosing Tests
|
|
2395
|
+
|
|
2396
|
+
When a big test fails, it should be self-evident how to diagnose and fix the failure.
|
|
2397
|
+
|
|
2398
|
+
The more moving parts a test has — end-to-end, system, integration — the more critical this becomes. A test that fails with \`expected true, received false\` forces the developer (or agent) to re-run, add logging, and guess. A test that fails with a rich diff showing the actual state versus the expected state turns diagnosis into reading.
|
|
2399
|
+
|
|
2400
|
+
Anti-patterns:
|
|
2401
|
+
|
|
2402
|
+
**Boolean flattening** — collapsing a rich value into true/false before asserting:
|
|
2403
|
+
\`\`\`javascript
|
|
2404
|
+
// Bad: "expected true, received false" — what events arrived?
|
|
2405
|
+
expect(events.some(e => e.type === 'check-passed')).toBe(true)
|
|
2406
|
+
|
|
2407
|
+
// Good: shows the actual event types on failure
|
|
2408
|
+
expect(events.map(e => e.type)).toContain('check-passed')
|
|
2409
|
+
\`\`\`
|
|
2410
|
+
|
|
2411
|
+
**Length-only assertions** — checking count without showing contents:
|
|
2412
|
+
\`\`\`javascript
|
|
2413
|
+
// Bad: "expected 2, received 0" — what requests were captured?
|
|
2414
|
+
expect(requests.length).toBe(2)
|
|
2415
|
+
|
|
2416
|
+
// Good: shows the actual requests on failure
|
|
2417
|
+
expect(requests).toHaveLength(2) // vitest shows the array
|
|
2418
|
+
\`\`\`
|
|
2419
|
+
|
|
2420
|
+
**Silent guards** — using \`if\` where an assertion belongs:
|
|
2421
|
+
\`\`\`javascript
|
|
2422
|
+
// Bad: silently passes when settings is undefined
|
|
2423
|
+
if (settings) {
|
|
2424
|
+
expect(JSON.parse(settings).key).toBeDefined()
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
// Good: fails explicitly if settings is missing
|
|
2428
|
+
expect(settings).toBeDefined()
|
|
2429
|
+
const parsed = JSON.parse(settings!)
|
|
2430
|
+
expect(parsed.key).toBeDefined()
|
|
2431
|
+
\`\`\`
|
|
2432
|
+
|
|
2433
|
+
### Functional Core, Imperative Shell
|
|
2434
|
+
|
|
2435
|
+
Separate code into a pure "functional core" and a thin "imperative shell." The core takes values in and returns values out, with no side effects. The shell handles I/O and wires things together.
|
|
2436
|
+
|
|
2437
|
+
Purely functional code makes some things easier to understand: because values don't change, you can call functions and know that only their return value matters—they don't change anything outside themselves.
|
|
2438
|
+
|
|
2439
|
+
The functional core contains business logic as pure functions that take values and return values. The imperative shell sits at the boundary, reading input, calling into the core, and performing side effects with the results. This keeps the majority of code easy to test (no mocks or stubs needed for pure functions) and makes the I/O surface area small and explicit.
|
|
2440
|
+
|
|
2441
|
+
## Scope
|
|
2442
|
+
|
|
2443
|
+
Search for test files and analyze them for clarity issues:
|
|
2444
|
+
|
|
2445
|
+
1. **Test files** - Files matching \`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`
|
|
2446
|
+
- Include unit, integration, and system tests
|
|
2447
|
+
- Exclude exploratory test files
|
|
2448
|
+
|
|
2449
|
+
2. **Patterns to identify**:
|
|
2450
|
+
- Object literals with unused properties in test setup
|
|
2451
|
+
- Magic numbers without semantic meaning
|
|
2452
|
+
- Excessive mock/stub setup
|
|
2453
|
+
- Complex nested structures where simpler ones would suffice
|
|
2454
|
+
- Brittle string assertions coupled to formatting
|
|
2455
|
+
- Boolean flattening (testing \`.toBe(true)\` instead of showing actual values)
|
|
2456
|
+
- Length-only assertions (testing \`.length\` instead of \`.toHaveLength()\`)
|
|
2457
|
+
- Silent guards (using \`if\` where assertions belong)
|
|
2458
|
+
|
|
2459
|
+
## Analysis Steps
|
|
2460
|
+
|
|
2461
|
+
1. **Find test files**
|
|
2462
|
+
- Search for \`**/*.test.ts\`, \`**/*.test.js\`, \`**/*.spec.ts\`, \`**/*.spec.js\`
|
|
2463
|
+
- Filter out exploratory tests
|
|
2464
|
+
|
|
2465
|
+
2. **Analyze each test file**
|
|
2466
|
+
- Look for object literals in test setup with properties that aren't used in assertions
|
|
2467
|
+
- Identify numeric literals that lack semantic meaning (e.g., \`42\`, \`123\` without explaining what they represent)
|
|
2468
|
+
- Count mock/stub setup lines relative to actual test logic
|
|
2469
|
+
- Check for deeply nested test data structures (3+ levels)
|
|
2470
|
+
- Find string assertions that compare exact formatting (spaces, newlines, etc.) rather than semantic content
|
|
2471
|
+
- Detect boolean flattening patterns (\`.some()\`, \`.every()\`, \`.includes()\` followed by \`.toBe(true/false)\`)
|
|
2472
|
+
- Find length checks using \`.length\` property instead of \`.toHaveLength()\`
|
|
2473
|
+
- Locate conditional logic in tests (\`if\` statements) that should be assertions
|
|
2474
|
+
|
|
2475
|
+
3. **Create ideas for issues found**
|
|
2476
|
+
- Group issues by test file
|
|
2477
|
+
- For each file with issues, create an idea file documenting:
|
|
2478
|
+
- Test file path
|
|
2479
|
+
- List of patterns found with line numbers
|
|
2480
|
+
- Pattern categories
|
|
2481
|
+
- Current problematic patterns
|
|
2482
|
+
- Recommended refactoring approaches
|
|
2483
|
+
|
|
2484
|
+
## Output Format
|
|
2485
|
+
|
|
2486
|
+
For each test file with clarity issues, create an idea file with:
|
|
2487
|
+
|
|
2488
|
+
### Title
|
|
2489
|
+
"Simplify test data in [filename]"
|
|
2490
|
+
|
|
2491
|
+
### Content Structure
|
|
2492
|
+
\`\`\`markdown
|
|
2493
|
+
# Simplify test data in [filename]
|
|
2494
|
+
|
|
2495
|
+
The test file \`[path]\` contains incidental details that obscure test intent.
|
|
2496
|
+
|
|
2497
|
+
## Issues Found
|
|
2498
|
+
|
|
2499
|
+
### [Pattern Name] (line X)
|
|
2500
|
+
- **Current**: \`[code snippet]\`
|
|
2501
|
+
- **Issue**: [explanation of how this obscures intent]
|
|
2502
|
+
- **Recommendation**: [specific simplification guidance]
|
|
2503
|
+
|
|
2504
|
+
[Repeat for each issue]
|
|
2505
|
+
\`\`\`
|
|
2506
|
+
|
|
2507
|
+
## Applicability
|
|
2508
|
+
|
|
2509
|
+
This audit applies to codebases with test files. If the codebase has no test files (\`*.test.ts\`, \`*.spec.js\`, etc.), document that finding and skip the detailed analysis.
|
|
2510
|
+
|
|
2511
|
+
## Focus
|
|
2512
|
+
|
|
2513
|
+
This audit focuses purely on test clarity — whether tests clearly communicate intent. It does not evaluate test performance or execution speed.
|
|
2514
|
+
|
|
2515
|
+
## Blocked By
|
|
2516
|
+
|
|
2517
|
+
(none)
|
|
2518
|
+
|
|
2519
|
+
## Definition of Done
|
|
2520
|
+
|
|
2521
|
+
- Searched for all test files in the codebase
|
|
2522
|
+
- Analyzed test files for incidental complexity patterns
|
|
2523
|
+
- Identified tests with unused properties in setup data
|
|
2524
|
+
- Found magic numbers lacking semantic meaning
|
|
2525
|
+
- Flagged excessive mock/stub setup
|
|
2526
|
+
- Located complex nested structures
|
|
2527
|
+
- Detected brittle string assertions
|
|
2528
|
+
- Found boolean flattening patterns
|
|
2529
|
+
- Located length-only assertions
|
|
2530
|
+
- Identified silent guards (if statements in tests)
|
|
2531
|
+
- Created idea files for each test file with findings
|
|
2532
|
+
- Each idea includes: file path, issues with line numbers, pattern categories, current patterns, recommendations
|
|
2533
|
+
- No changes to files outside \`.dust/\`
|
|
2534
|
+
`;
|
|
2535
|
+
}
|
|
2139
2536
|
function commitReview() {
|
|
2140
2537
|
return dedent`
|
|
2141
2538
|
# Commit Review
|
|
@@ -2146,7 +2543,13 @@ function commitReview() {
|
|
|
2146
2543
|
|
|
2147
2544
|
## Scope
|
|
2148
2545
|
|
|
2149
|
-
|
|
2546
|
+
Determine which commits to analyze:
|
|
2547
|
+
|
|
2548
|
+
1. Check VCS history for a prior commit-review run: \`git log --grep="Audit: Commit Review" -1 --format=%H\`
|
|
2549
|
+
2. If found, analyze commits since that commit
|
|
2550
|
+
3. If not found, analyze the last 20 commits as a fallback
|
|
2551
|
+
|
|
2552
|
+
Focus on these signals:
|
|
2150
2553
|
|
|
2151
2554
|
1. **File churn** - Files modified frequently across multiple commits may have unclear responsibilities or be accumulating technical debt
|
|
2152
2555
|
2. **Size growth** - Files that have grown significantly may benefit from decomposition
|
|
@@ -2492,6 +2895,168 @@ function slowTests() {
|
|
|
2492
2895
|
- No changes to files outside \`.dust/\`
|
|
2493
2896
|
`;
|
|
2494
2897
|
}
|
|
2898
|
+
function overAbstraction() {
|
|
2899
|
+
return dedent`
|
|
2900
|
+
# Over-Abstraction
|
|
2901
|
+
|
|
2902
|
+
Identify violations of the "reasonably-dry" principle where code has been over-engineered with excessive abstraction.
|
|
2903
|
+
|
|
2904
|
+
${ideasHint}
|
|
2905
|
+
|
|
2906
|
+
## Scope
|
|
2907
|
+
|
|
2908
|
+
Detect these over-abstraction patterns:
|
|
2909
|
+
|
|
2910
|
+
1. **Single-use abstractions** - Interfaces, base classes, or utility functions used in only one place
|
|
2911
|
+
2. **Deep inheritance hierarchies** - Classes extending more than 2 levels deep
|
|
2912
|
+
3. **Premature generalization** - Parameters always used with the same value, unused options/flags
|
|
2913
|
+
4. **Excessive indirection** - Multiple layers of wrappers adding no value
|
|
2914
|
+
|
|
2915
|
+
## Analysis Steps
|
|
2916
|
+
|
|
2917
|
+
### 1. Find Single-Use Abstractions
|
|
2918
|
+
|
|
2919
|
+
Search for abstractions that are only used once:
|
|
2920
|
+
|
|
2921
|
+
1. **Interfaces with one implementation**
|
|
2922
|
+
- Search for \`interface\` declarations
|
|
2923
|
+
- Check if each interface has only one implementing class
|
|
2924
|
+
- Flag interfaces that exist solely for testing (can be replaced with the concrete type)
|
|
2925
|
+
|
|
2926
|
+
2. **Base classes with one subclass**
|
|
2927
|
+
- Search for \`abstract class\` or classes used as base classes
|
|
2928
|
+
- Count implementations extending each base class
|
|
2929
|
+
- Flag base classes with only one subclass
|
|
2930
|
+
|
|
2931
|
+
3. **Utility functions called once**
|
|
2932
|
+
- Search for exported utility functions
|
|
2933
|
+
- Check call sites - if only called from one location, it's over-abstraction
|
|
2934
|
+
- Consider inlining single-use utilities
|
|
2935
|
+
|
|
2936
|
+
4. **Generic types with one concrete usage**
|
|
2937
|
+
- Find generic type parameters: \`<T>\`, \`<TData>\`, etc.
|
|
2938
|
+
- Check if T is always the same type at all call sites
|
|
2939
|
+
- Flag generics that could be concrete types
|
|
2940
|
+
|
|
2941
|
+
### 2. Detect Deep Inheritance Hierarchies
|
|
2942
|
+
|
|
2943
|
+
Find inheritance chains longer than 2 levels:
|
|
2944
|
+
|
|
2945
|
+
1. Search for \`extends\` keywords in class declarations
|
|
2946
|
+
2. Build inheritance tree for each class
|
|
2947
|
+
3. Flag chains deeper than 2 (A extends B extends C extends D...)
|
|
2948
|
+
4. Respect framework conventions (don't flag React.Component, etc.)
|
|
2949
|
+
|
|
2950
|
+
### 3. Identify Premature Generalization
|
|
2951
|
+
|
|
2952
|
+
Look for flexibility that's never used:
|
|
2953
|
+
|
|
2954
|
+
1. **Always-same parameter values**
|
|
2955
|
+
- Find function parameters
|
|
2956
|
+
- Check all call sites - if always the same value, it's not needed
|
|
2957
|
+
- Flag parameters that could be constants or removed
|
|
2958
|
+
|
|
2959
|
+
2. **Unused configuration options**
|
|
2960
|
+
- Search for configuration objects/interfaces
|
|
2961
|
+
- Check which options are actually used
|
|
2962
|
+
- Flag options that are never set or always default
|
|
2963
|
+
|
|
2964
|
+
3. **Unused function parameters**
|
|
2965
|
+
- Find parameters that aren't referenced in function bodies
|
|
2966
|
+
- Flag as candidates for removal
|
|
2967
|
+
|
|
2968
|
+
### 4. Find Excessive Indirection
|
|
2969
|
+
|
|
2970
|
+
Detect wrapper chains that add no value:
|
|
2971
|
+
|
|
2972
|
+
1. **Delegation chains**
|
|
2973
|
+
- Search for functions that only call another function
|
|
2974
|
+
- Flag wrappers that don't add logic, just forward calls
|
|
2975
|
+
- Example: \`function foo(x) { return bar(x) }\`
|
|
2976
|
+
|
|
2977
|
+
2. **Proxy patterns without behavior**
|
|
2978
|
+
- Find classes that wrap another class
|
|
2979
|
+
- Check if wrapper adds any logic beyond forwarding
|
|
2980
|
+
- Flag pure proxies
|
|
2981
|
+
|
|
2982
|
+
3. **Middleware without transformation**
|
|
2983
|
+
- Look for middleware/interceptor patterns
|
|
2984
|
+
- Check if they modify data or just pass through
|
|
2985
|
+
- Flag pass-through middleware
|
|
2986
|
+
|
|
2987
|
+
## Output Format
|
|
2988
|
+
|
|
2989
|
+
For each over-abstraction found, create an idea file in \`.dust/ideas/\` with:
|
|
2990
|
+
|
|
2991
|
+
\`\`\`markdown
|
|
2992
|
+
# Over-Abstraction: [Type] in [Location]
|
|
2993
|
+
|
|
2994
|
+
## Type
|
|
2995
|
+
|
|
2996
|
+
[Single-use | Deep hierarchy | Premature generalization | Excessive indirection]
|
|
2997
|
+
|
|
2998
|
+
## Location
|
|
2999
|
+
|
|
3000
|
+
\`\`\`
|
|
3001
|
+
[file path]:[line number]
|
|
3002
|
+
\`\`\`
|
|
3003
|
+
|
|
3004
|
+
## Description
|
|
3005
|
+
|
|
3006
|
+
[What the abstraction is]
|
|
3007
|
+
|
|
3008
|
+
## Problem
|
|
3009
|
+
|
|
3010
|
+
[Why this is over-abstraction - complexity without benefit]
|
|
3011
|
+
|
|
3012
|
+
## Usage Analysis
|
|
3013
|
+
|
|
3014
|
+
- **Times used**: [count]
|
|
3015
|
+
- **Variation in usage**: [how different are the use cases]
|
|
3016
|
+
- **Complexity cost**: [lines of code, indirection levels, etc.]
|
|
3017
|
+
|
|
3018
|
+
## Suggested Simplification
|
|
3019
|
+
|
|
3020
|
+
[How to remove or reduce this abstraction]
|
|
3021
|
+
|
|
3022
|
+
## Impact
|
|
3023
|
+
|
|
3024
|
+
[Lines of code saved, reduced complexity, improved clarity]
|
|
3025
|
+
\`\`\`
|
|
3026
|
+
|
|
3027
|
+
## Special Considerations
|
|
3028
|
+
|
|
3029
|
+
1. **Framework conventions** - Don't flag patterns mandated by frameworks:
|
|
3030
|
+
- React: Component base classes, hooks patterns
|
|
3031
|
+
- Express: Middleware signatures
|
|
3032
|
+
- Testing: Test base classes, fixture patterns
|
|
3033
|
+
|
|
3034
|
+
2. **Library boundaries** - Public API abstractions may be justified even if internal usage is simple
|
|
3035
|
+
|
|
3036
|
+
3. **Test code** - Apply the same standards to test code as production code
|
|
3037
|
+
|
|
3038
|
+
4. **Context depth thresholds**:
|
|
3039
|
+
- Deep hierarchies (>2 levels) make understanding difficult
|
|
3040
|
+
- Wrapper chains (>2 levels) obscure actual behavior
|
|
3041
|
+
- Generic parameters should have multiple concrete usages
|
|
3042
|
+
|
|
3043
|
+
## Blocked By
|
|
3044
|
+
|
|
3045
|
+
(none)
|
|
3046
|
+
|
|
3047
|
+
## Definition of Done
|
|
3048
|
+
|
|
3049
|
+
- Searched for single-use interfaces, base classes, and utility functions
|
|
3050
|
+
- Identified deep inheritance hierarchies (>2 levels)
|
|
3051
|
+
- Found parameters always used with the same value
|
|
3052
|
+
- Detected unused configuration options
|
|
3053
|
+
- Located excessive wrapper chains and delegation
|
|
3054
|
+
- Respected framework conventions (didn't flag framework-mandated patterns)
|
|
3055
|
+
- Created idea files for each over-abstraction found
|
|
3056
|
+
- Each idea includes usage analysis and simplification suggestions
|
|
3057
|
+
- No changes to files outside \`.dust/\`
|
|
3058
|
+
`;
|
|
3059
|
+
}
|
|
2495
3060
|
function primitiveObsession() {
|
|
2496
3061
|
return dedent`
|
|
2497
3062
|
# Primitive Obsession
|
|
@@ -2763,7 +3328,7 @@ function testAssertions() {
|
|
|
2763
3328
|
|
|
2764
3329
|
## Background
|
|
2765
3330
|
|
|
2766
|
-
|
|
3331
|
+
Comprehensive assertions (asserting the whole, not the parts) provide richer failure diagnostics. Self-diagnosing tests ensure that failures reveal enough context to guide a fix without re-running. This audit addresses complementary assertion quality signals not covered by those principles.
|
|
2767
3332
|
|
|
2768
3333
|
## Scope
|
|
2769
3334
|
|
|
@@ -2822,7 +3387,7 @@ function testAssertions() {
|
|
|
2822
3387
|
- Require test updates for unrelated changes
|
|
2823
3388
|
- Obscure what the test is actually verifying
|
|
2824
3389
|
|
|
2825
|
-
This works in tension with
|
|
3390
|
+
This works in tension with comprehensive assertions (asserting the whole, not the parts). Let context determine the balance:
|
|
2826
3391
|
- Public API contracts → comprehensive assertions
|
|
2827
3392
|
- Internal implementation tests → precise assertions
|
|
2828
3393
|
- Snapshot tests → consider \`toMatchSnapshot()\` with care
|
|
@@ -2849,7 +3414,7 @@ function testAssertions() {
|
|
|
2849
3414
|
|
|
2850
3415
|
Tests should ideally verify one behavior or scenario. When a test has multiple unrelated assertions, a failure in the first masks all subsequent ones.
|
|
2851
3416
|
|
|
2852
|
-
This does not mean "one \`expect\` call per test". A single logical assertion may require multiple \`expect\` calls to express (especially for complex state).
|
|
3417
|
+
This does not mean "one \`expect\` call per test". A single logical assertion may require multiple \`expect\` calls to express (especially for complex state). Comprehensive assertions (asserting the whole, not the parts) often allow collapsing multiple calls into one whole-object assertion.
|
|
2853
3418
|
|
|
2854
3419
|
The anti-pattern to avoid:
|
|
2855
3420
|
\`\`\`javascript
|
|
@@ -3012,6 +3577,95 @@ function loggingAndTraceability() {
|
|
|
3012
3577
|
- No changes to files outside \`.dust/\`
|
|
3013
3578
|
`;
|
|
3014
3579
|
}
|
|
3580
|
+
function testDeterminism() {
|
|
3581
|
+
return dedent`
|
|
3582
|
+
# Test Determinism
|
|
3583
|
+
|
|
3584
|
+
Audit unit tests for non-deterministic patterns that cause tests to produce inconsistent results across different environments or executions.
|
|
3585
|
+
|
|
3586
|
+
${ideasHint}
|
|
3587
|
+
|
|
3588
|
+
## Context
|
|
3589
|
+
|
|
3590
|
+
Tests must produce the same result regardless of where they run. Non-deterministic tests undermine confidence in CI, make debugging harder, and waste developer time chasing phantom failures. This audit identifies patterns that introduce non-determinism: time dependencies, randomness, environment variable access, filesystem operations, real timers, and platform-specific behavior.
|
|
3591
|
+
|
|
3592
|
+
## Scope
|
|
3593
|
+
|
|
3594
|
+
Search for unit test files and analyze them for determinism issues:
|
|
3595
|
+
|
|
3596
|
+
1. **Unit test files** - Files matching \`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`
|
|
3597
|
+
- Exclude system test files (files containing 'system-test' or in 'system-tests/' directories)
|
|
3598
|
+
- Exclude exploratory test files
|
|
3599
|
+
|
|
3600
|
+
2. **Issue categories to detect**:
|
|
3601
|
+
- Time dependencies (\`Date.now()\`, \`new Date()\`) — should use dependency injection or stubbed time
|
|
3602
|
+
- Randomness (\`Math.random()\`, \`crypto.randomBytes()\`, \`randomUUID()\`) — should use seeded random or injection
|
|
3603
|
+
- Environment variables (\`process.env.VARIABLE\` without \`stubEnv\`) — should use \`stubEnv()\` or pass env as a parameter
|
|
3604
|
+
- Filesystem operations (file reads/writes in unit tests) — should use in-memory filesystem or ensure cleanup
|
|
3605
|
+
- Real timers (\`setTimeout\`, \`setInterval\` without fake timers) — should use \`vi.useFakeTimers()\`
|
|
3606
|
+
- Platform-specific code (\`process.platform\`, \`__dirname\`, \`os.EOL\`) — should use dependency injection or normalize paths
|
|
3607
|
+
|
|
3608
|
+
## Analysis Steps
|
|
3609
|
+
|
|
3610
|
+
1. **Find unit test files**
|
|
3611
|
+
- Search for \`**/*.test.ts\`, \`**/*.test.js\`, \`**/*.spec.ts\`, \`**/*.spec.js\`
|
|
3612
|
+
- Filter out system test files and exploratory tests
|
|
3613
|
+
|
|
3614
|
+
2. **Analyze each test file**
|
|
3615
|
+
- Read the file content
|
|
3616
|
+
- Look for the patterns listed above
|
|
3617
|
+
- Note: patterns used inside stub/mock setups (\`vi.fn()\`, \`vi.mock()\`, \`vi.spyOn()\`), function parameter type annotations, or \`stubEnv()\` calls are not issues — they represent proper test practices
|
|
3618
|
+
|
|
3619
|
+
3. **Create ideas for issues found**
|
|
3620
|
+
- Group issues by test file
|
|
3621
|
+
- For each file with issues, create an idea file documenting:
|
|
3622
|
+
- Test file path
|
|
3623
|
+
- List of issues with line numbers
|
|
3624
|
+
- Issue categories
|
|
3625
|
+
- Current problematic patterns
|
|
3626
|
+
- Recommended refactoring approaches
|
|
3627
|
+
|
|
3628
|
+
## Output Format
|
|
3629
|
+
|
|
3630
|
+
For each test file with determinism issues, create an idea file with:
|
|
3631
|
+
|
|
3632
|
+
### Title
|
|
3633
|
+
"Refactor [filename] for test determinism"
|
|
3634
|
+
|
|
3635
|
+
### Content Structure
|
|
3636
|
+
\`\`\`markdown
|
|
3637
|
+
# Refactor [filename] for test determinism
|
|
3638
|
+
|
|
3639
|
+
The test file \`[path]\` contains non-deterministic patterns that should be refactored.
|
|
3640
|
+
|
|
3641
|
+
## Issues Found
|
|
3642
|
+
|
|
3643
|
+
### [Category Name] (line X)
|
|
3644
|
+
- **Pattern**: \`[code snippet]\`
|
|
3645
|
+
- **Issue**: [explanation of why this is non-deterministic]
|
|
3646
|
+
- **Recommendation**: [specific refactoring guidance]
|
|
3647
|
+
|
|
3648
|
+
[Repeat for each issue]
|
|
3649
|
+
\`\`\`
|
|
3650
|
+
|
|
3651
|
+
## Applicability
|
|
3652
|
+
|
|
3653
|
+
This audit applies to codebases with unit tests. If the codebase has no unit test files (\`*.test.ts\`, \`*.spec.js\`, etc.), document that finding and skip the detailed analysis.
|
|
3654
|
+
|
|
3655
|
+
## Blocked By
|
|
3656
|
+
|
|
3657
|
+
(none)
|
|
3658
|
+
|
|
3659
|
+
## Definition of Done
|
|
3660
|
+
|
|
3661
|
+
- Searched for unit test files (\`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`)
|
|
3662
|
+
- Excluded system test and exploratory test files
|
|
3663
|
+
- Analyzed each unit test file for determinism issues
|
|
3664
|
+
- Created idea files for test files containing determinism issues
|
|
3665
|
+
- Each idea includes specific line numbers, patterns, and refactoring guidance
|
|
3666
|
+
- No changes to files outside \`.dust/\`
|
|
3667
|
+
`;
|
|
3668
|
+
}
|
|
3015
3669
|
function testPyramid() {
|
|
3016
3670
|
return dedent`
|
|
3017
3671
|
# Test Pyramid
|
|
@@ -3380,7 +4034,7 @@ function ciDevelopmentParity() {
|
|
|
3380
4034
|
2. **Wasted cycles** - Developers push code that passes locally only to have CI fail
|
|
3381
4035
|
3. **Agent confusion** - AI agents rely on consistent feedback; discrepancies trigger incorrect debugging paths
|
|
3382
4036
|
|
|
3383
|
-
|
|
4037
|
+
Every check must produce the same result regardless of who runs it, when, or on what machine.
|
|
3384
4038
|
|
|
3385
4039
|
## Scope
|
|
3386
4040
|
|
|
@@ -3470,7 +4124,7 @@ function ciDevelopmentParity() {
|
|
|
3470
4124
|
|
|
3471
4125
|
- Developers may push code that passes locally but fails CI on other checks
|
|
3472
4126
|
- CI provides no coverage for [check category]
|
|
3473
|
-
-
|
|
4127
|
+
- Problems aren't caught before merge—any worker should halt and fix a problem the moment they detect it
|
|
3474
4128
|
|
|
3475
4129
|
## Suggested Fix
|
|
3476
4130
|
|
|
@@ -3494,7 +4148,7 @@ function ciDevelopmentParity() {
|
|
|
3494
4148
|
## Impact
|
|
3495
4149
|
|
|
3496
4150
|
- Developers don't get [check category] feedback until CI runs
|
|
3497
|
-
-
|
|
4151
|
+
- Fast feedback loops are broken—local checks give incomplete picture
|
|
3498
4152
|
- Agents may make changes that pass local checks but fail CI
|
|
3499
4153
|
|
|
3500
4154
|
## Suggested Fix
|
|
@@ -3532,7 +4186,7 @@ function commitMessageQuality() {
|
|
|
3532
4186
|
|
|
3533
4187
|
## Context
|
|
3534
4188
|
|
|
3535
|
-
|
|
4189
|
+
Commit history should explain why changes were made, not just what changed. Good commit messages help agents understand project history and make better decisions. This audit evaluates commit message quality itself, not the code changes.
|
|
3536
4190
|
|
|
3537
4191
|
## Scope
|
|
3538
4192
|
|
|
@@ -3673,20 +4327,14 @@ function commitMessageQuality() {
|
|
|
3673
4327
|
`;
|
|
3674
4328
|
}
|
|
3675
4329
|
function suggestAudits() {
|
|
3676
|
-
|
|
3677
|
-
const template = render();
|
|
3678
|
-
const description = extractOpeningSentence(template);
|
|
3679
|
-
return `- **${name}**: ${description}`;
|
|
3680
|
-
}).join(`
|
|
3681
|
-
`);
|
|
3682
|
-
let content = dedent`
|
|
4330
|
+
return dedent`
|
|
3683
4331
|
# Suggest Audits
|
|
3684
4332
|
|
|
3685
4333
|
Analyze recent commits and create tasks for relevant audits to run.
|
|
3686
4334
|
|
|
3687
4335
|
## Context
|
|
3688
4336
|
|
|
3689
|
-
This audit examines recent commit history and suggests which
|
|
4337
|
+
This audit examines recent commit history and suggests which audits would be valuable based on what changed. Rather than manually selecting audits, this provides an automated way to maintain codebase health by matching recent work to appropriate audits.
|
|
3690
4338
|
|
|
3691
4339
|
## Commit Range
|
|
3692
4340
|
|
|
@@ -3698,21 +4346,17 @@ function suggestAudits() {
|
|
|
3698
4346
|
|
|
3699
4347
|
## Available Audits
|
|
3700
4348
|
|
|
3701
|
-
|
|
3702
|
-
content += `
|
|
3703
|
-
|
|
3704
|
-
` + auditList + `
|
|
3705
|
-
`;
|
|
3706
|
-
content += dedent`
|
|
4349
|
+
Run \`dust audit\` to list all available audits (including both stock audits and any repository-specific audits configured in \`.dust/config/audits/\`). This will show the audit name and description for each available audit.
|
|
3707
4350
|
|
|
3708
4351
|
## Analysis Steps
|
|
3709
4352
|
|
|
3710
|
-
1. **
|
|
3711
|
-
2. **
|
|
3712
|
-
3. **
|
|
4353
|
+
1. **List audits** - Run \`dust audit\` to get the complete list of available audits with descriptions
|
|
4354
|
+
2. **Gather commits** - Get the list of commits in the determined range with their messages and changed files
|
|
4355
|
+
3. **Categorize changes** - Group commits by the type of work (features, fixes, refactoring, tests, docs, config)
|
|
4356
|
+
4. **Match to audits** - For each relevant audit, explain why recent changes make it valuable:
|
|
3713
4357
|
- What specific commits or file changes triggered the suggestion?
|
|
3714
4358
|
- What might the audit uncover given this context?
|
|
3715
|
-
|
|
4359
|
+
5. **Create tasks** - For each suggested audit, create a task file in \`.dust/tasks/\`
|
|
3716
4360
|
|
|
3717
4361
|
## Output
|
|
3718
4362
|
|
|
@@ -3761,7 +4405,6 @@ function suggestAudits() {
|
|
|
3761
4405
|
- Each task explains why the audit is valuable given recent changes
|
|
3762
4406
|
- No changes to files outside \`.dust/\`
|
|
3763
4407
|
`;
|
|
3764
|
-
return content;
|
|
3765
4408
|
}
|
|
3766
4409
|
var stockAuditFunctions = {
|
|
3767
4410
|
"agent-developer-experience": agentDeveloperExperience,
|
|
@@ -3777,7 +4420,9 @@ var stockAuditFunctions = {
|
|
|
3777
4420
|
"data-access-review": dataAccessReview,
|
|
3778
4421
|
"dead-code": deadCode,
|
|
3779
4422
|
"design-patterns": designPatterns,
|
|
4423
|
+
"directory-hierarchy": directoryHierarchy,
|
|
3780
4424
|
"error-handling": errorHandling,
|
|
4425
|
+
"facts-expansion": factsExpansion,
|
|
3781
4426
|
"facts-verification": factsVerification,
|
|
3782
4427
|
"feedback-loop-speed": feedbackLoopSpeed,
|
|
3783
4428
|
"flaky-tests": flakyTests,
|
|
@@ -3785,7 +4430,9 @@ var stockAuditFunctions = {
|
|
|
3785
4430
|
"commit-review": commitReview,
|
|
3786
4431
|
"ideas-from-principles": ideasFromPrinciples,
|
|
3787
4432
|
"idiomatic-style": idiomaticStyle,
|
|
4433
|
+
"incidental-test-details": incidentalTestDetails,
|
|
3788
4434
|
"logging-and-traceability": loggingAndTraceability,
|
|
4435
|
+
"over-abstraction": overAbstraction,
|
|
3789
4436
|
"primitive-obsession": primitiveObsession,
|
|
3790
4437
|
"repository-context": repositoryContext,
|
|
3791
4438
|
"security-review": securityReview,
|
|
@@ -3794,6 +4441,7 @@ var stockAuditFunctions = {
|
|
|
3794
4441
|
"stale-ideas": staleIdeas,
|
|
3795
4442
|
"suggest-audits": suggestAudits,
|
|
3796
4443
|
"test-assertions": testAssertions,
|
|
4444
|
+
"test-determinism": testDeterminism,
|
|
3797
4445
|
"test-pyramid": testPyramid,
|
|
3798
4446
|
"ubiquitous-language": ubiquitousLanguage,
|
|
3799
4447
|
"ux-audit": uxAudit
|
|
@@ -4022,7 +4670,7 @@ async function loadStoredToken(fileSystem, homeDir) {
|
|
|
4022
4670
|
const data = JSON.parse(content);
|
|
4023
4671
|
return typeof data.token === "string" ? data.token : null;
|
|
4024
4672
|
} catch (error) {
|
|
4025
|
-
if (error
|
|
4673
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
4026
4674
|
return null;
|
|
4027
4675
|
}
|
|
4028
4676
|
throw error;
|
|
@@ -4038,7 +4686,7 @@ async function clearToken(fileSystem, homeDir) {
|
|
|
4038
4686
|
try {
|
|
4039
4687
|
await fileSystem.writeFile(path, "{}");
|
|
4040
4688
|
} catch (error) {
|
|
4041
|
-
if (error
|
|
4689
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
4042
4690
|
return;
|
|
4043
4691
|
}
|
|
4044
4692
|
throw error;
|
|
@@ -5269,6 +5917,8 @@ function buildImplementationInstructions(bin, hooksInstalled, taskTitle, taskPat
|
|
|
5269
5917
|
steps.push(`${step}. Run \`${bin} check\` to verify the project is in a good state`);
|
|
5270
5918
|
step++;
|
|
5271
5919
|
}
|
|
5920
|
+
steps.push(`${step}. If the task file contains Principles and Guidance sections, read and follow them before implementing changes`);
|
|
5921
|
+
step++;
|
|
5272
5922
|
steps.push(`${step}. Implement the task`);
|
|
5273
5923
|
step++;
|
|
5274
5924
|
if (!hooksInstalled) {
|
|
@@ -5934,7 +6584,10 @@ function createWireEventSender(eventsUrl, sessionId, postEvent, onError, getAgen
|
|
|
5934
6584
|
|
|
5935
6585
|
// lib/loop/iteration.ts
|
|
5936
6586
|
var log2 = createLogger("dust:loop:iteration");
|
|
5937
|
-
|
|
6587
|
+
function DUST_QUICK_REFERENCE(dustCommand) {
|
|
6588
|
+
return `## Dust Quick Reference
|
|
6589
|
+
|
|
6590
|
+
Dust is a CLI tool for managing development workflows through markdown artifacts. In this environment, run dust commands using: \`${dustCommand}\` (this might be \`dust\`, \`bunx dust\`, \`npx dust\`, or another prefix depending on how dust is installed).
|
|
5938
6591
|
|
|
5939
6592
|
Dust stores project context in \`.dust/\` as markdown artifacts. Use these commands to explore:
|
|
5940
6593
|
|
|
@@ -5944,6 +6597,7 @@ Dust stores project context in \`.dust/\` as markdown artifacts. Use these comma
|
|
|
5944
6597
|
- \`dust help\` — see all available commands
|
|
5945
6598
|
|
|
5946
6599
|
Use dust commands instead of manually searching \`.dust/\` directories.`;
|
|
6600
|
+
}
|
|
5947
6601
|
function getEnvironmentContext(cwd) {
|
|
5948
6602
|
return {
|
|
5949
6603
|
machineName: os.hostname(),
|
|
@@ -6158,7 +6812,7 @@ async function runOneIteration(dependencies, loopDependencies, onLoopEvent, onAg
|
|
|
6158
6812
|
}
|
|
6159
6813
|
const taskContent = await fileSystem.readFile(`${context.cwd}/${task.path}`);
|
|
6160
6814
|
const instructions = buildImplementationInstructions(settings.dustCommand, hooksInstalled, task.title ?? undefined, task.path, settings.installCommand, true);
|
|
6161
|
-
const taskPrompt = buildTaskPrompt(task.path, taskContent, instructions, toolsSection, options.branch);
|
|
6815
|
+
const taskPrompt = buildTaskPrompt(task.path, taskContent, instructions, toolsSection, settings.dustCommand, options.branch);
|
|
6162
6816
|
let originalRemoteUrl;
|
|
6163
6817
|
if (docker?.gitProxyUrl) {
|
|
6164
6818
|
try {
|
|
@@ -6220,7 +6874,7 @@ Please resolve this issue. Common approaches:
|
|
|
6220
6874
|
|
|
6221
6875
|
Make sure the repository is in a clean state and synced with remote before finishing.`;
|
|
6222
6876
|
}
|
|
6223
|
-
function buildTaskPrompt(taskPath, taskContent, instructions, toolsSection, branch) {
|
|
6877
|
+
function buildTaskPrompt(taskPath, taskContent, instructions, toolsSection, dustCommand, branch) {
|
|
6224
6878
|
const suffix = toolsSection ? `
|
|
6225
6879
|
${toolsSection}` : "";
|
|
6226
6880
|
const branchContext = branch ? `You are working on the \`${branch}\` branch.
|
|
@@ -6232,7 +6886,7 @@ ${toolsSection}` : "";
|
|
|
6232
6886
|
${taskContent}
|
|
6233
6887
|
----------
|
|
6234
6888
|
|
|
6235
|
-
${DUST_QUICK_REFERENCE}
|
|
6889
|
+
${DUST_QUICK_REFERENCE(dustCommand)}
|
|
6236
6890
|
|
|
6237
6891
|
## How to implement the task
|
|
6238
6892
|
|
|
@@ -10097,7 +10751,7 @@ async function validateContentDirectoryFiles(dirPath, fileSystem) {
|
|
|
10097
10751
|
try {
|
|
10098
10752
|
entries = await fileSystem.readdir(dirPath);
|
|
10099
10753
|
} catch (error) {
|
|
10100
|
-
if (error
|
|
10754
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10101
10755
|
return [];
|
|
10102
10756
|
}
|
|
10103
10757
|
throw error;
|
|
@@ -10133,7 +10787,7 @@ async function validateDirectoryStructure(dustPath, fileSystem) {
|
|
|
10133
10787
|
try {
|
|
10134
10788
|
entries = await fileSystem.readdir(dustPath);
|
|
10135
10789
|
} catch (error) {
|
|
10136
|
-
if (error
|
|
10790
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10137
10791
|
return [];
|
|
10138
10792
|
}
|
|
10139
10793
|
throw error;
|
|
@@ -10179,7 +10833,7 @@ async function validateDirectoryStructure(dustPath, fileSystem) {
|
|
|
10179
10833
|
try {
|
|
10180
10834
|
configEntries = await fileSystem.readdir(configPath);
|
|
10181
10835
|
} catch (error) {
|
|
10182
|
-
if (error
|
|
10836
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10183
10837
|
return violations;
|
|
10184
10838
|
}
|
|
10185
10839
|
throw error;
|
|
@@ -10617,7 +11271,7 @@ async function parseArtifacts(fileSystem, dustPath) {
|
|
|
10617
11271
|
try {
|
|
10618
11272
|
rootEntries = await fileSystem.readdir(dustPath);
|
|
10619
11273
|
} catch (error) {
|
|
10620
|
-
if (error
|
|
11274
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10621
11275
|
rootEntries = [];
|
|
10622
11276
|
} else {
|
|
10623
11277
|
throw error;
|
|
@@ -10631,7 +11285,7 @@ async function parseArtifacts(fileSystem, dustPath) {
|
|
|
10631
11285
|
try {
|
|
10632
11286
|
content = await fileSystem.readFile(filePath);
|
|
10633
11287
|
} catch (error) {
|
|
10634
|
-
if (error
|
|
11288
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10635
11289
|
continue;
|
|
10636
11290
|
}
|
|
10637
11291
|
throw error;
|
|
@@ -10647,7 +11301,7 @@ async function parseArtifacts(fileSystem, dustPath) {
|
|
|
10647
11301
|
try {
|
|
10648
11302
|
entries = await fileSystem.readdir(dirPath);
|
|
10649
11303
|
} catch (error) {
|
|
10650
|
-
if (error
|
|
11304
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10651
11305
|
continue;
|
|
10652
11306
|
}
|
|
10653
11307
|
throw error;
|
|
@@ -10675,7 +11329,7 @@ async function parseArtifacts(fileSystem, dustPath) {
|
|
|
10675
11329
|
try {
|
|
10676
11330
|
auditEntries = await fileSystem.readdir(auditsPath);
|
|
10677
11331
|
} catch (error) {
|
|
10678
|
-
if (error
|
|
11332
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10679
11333
|
auditEntries = [];
|
|
10680
11334
|
} else {
|
|
10681
11335
|
throw error;
|
|
@@ -10689,7 +11343,7 @@ async function parseArtifacts(fileSystem, dustPath) {
|
|
|
10689
11343
|
try {
|
|
10690
11344
|
content = await fileSystem.readFile(filePath);
|
|
10691
11345
|
} catch (error) {
|
|
10692
|
-
if (error
|
|
11346
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10693
11347
|
continue;
|
|
10694
11348
|
}
|
|
10695
11349
|
throw error;
|
|
@@ -10774,7 +11428,7 @@ async function safeReadFile(fileSystem, filePath) {
|
|
|
10774
11428
|
try {
|
|
10775
11429
|
return await fileSystem.readFile(filePath);
|
|
10776
11430
|
} catch (error) {
|
|
10777
|
-
if (error
|
|
11431
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
10778
11432
|
return null;
|
|
10779
11433
|
}
|
|
10780
11434
|
throw error;
|
|
@@ -12662,7 +13316,7 @@ async function init(dependencies) {
|
|
|
12662
13316
|
await fileSystem.writeFile(`${dustPath}/facts/use-dust-for-planning.md`, USE_DUST_FACT, { flag: "wx" });
|
|
12663
13317
|
dustDirCreated = true;
|
|
12664
13318
|
} catch (error) {
|
|
12665
|
-
if (error
|
|
13319
|
+
if (!isErrorCode(error, "EEXIST")) {
|
|
12666
13320
|
throw error;
|
|
12667
13321
|
}
|
|
12668
13322
|
}
|
|
@@ -12671,7 +13325,7 @@ async function init(dependencies) {
|
|
|
12671
13325
|
await fileSystem.writeFile(`${dustPath}/config/settings.json`, `${JSON.stringify(settings, null, 2)}
|
|
12672
13326
|
`, { flag: "wx" });
|
|
12673
13327
|
} catch (error) {
|
|
12674
|
-
if (error
|
|
13328
|
+
if (!isErrorCode(error, "EEXIST")) {
|
|
12675
13329
|
throw error;
|
|
12676
13330
|
}
|
|
12677
13331
|
}
|
|
@@ -12690,7 +13344,7 @@ async function init(dependencies) {
|
|
|
12690
13344
|
});
|
|
12691
13345
|
context.stdout(`${colors.green}\uD83D\uDCC4 Created${colors.reset} ${colors.cyan}CLAUDE.md${colors.reset} with agent instructions`);
|
|
12692
13346
|
} catch (error) {
|
|
12693
|
-
if (error
|
|
13347
|
+
if (isErrorCode(error, "EEXIST")) {
|
|
12694
13348
|
context.stdout(`${colors.yellow}⚠️ Warning:${colors.reset} ${colors.cyan}CLAUDE.md${colors.reset} already exists. Consider adding: ${colors.dim}"${agentInstruction}"${colors.reset}`);
|
|
12695
13349
|
} else {
|
|
12696
13350
|
throw error;
|
|
@@ -12703,7 +13357,7 @@ async function init(dependencies) {
|
|
|
12703
13357
|
});
|
|
12704
13358
|
context.stdout(`${colors.green}\uD83D\uDCC4 Created${colors.reset} ${colors.cyan}AGENTS.md${colors.reset} with agent instructions`);
|
|
12705
13359
|
} catch (error) {
|
|
12706
|
-
if (error
|
|
13360
|
+
if (isErrorCode(error, "EEXIST")) {
|
|
12707
13361
|
context.stdout(`${colors.yellow}⚠️ Warning:${colors.reset} ${colors.cyan}AGENTS.md${colors.reset} already exists. Consider adding: ${colors.dim}"${agentInstruction}"${colors.reset}`);
|
|
12708
13362
|
} else {
|
|
12709
13363
|
throw error;
|
|
@@ -13331,7 +13985,7 @@ async function scanMarkdownFiles(glob, dirPath) {
|
|
|
13331
13985
|
}
|
|
13332
13986
|
return files;
|
|
13333
13987
|
} catch (error) {
|
|
13334
|
-
if (error
|
|
13988
|
+
if (isErrorCode(error, "ENOENT")) {
|
|
13335
13989
|
return [];
|
|
13336
13990
|
}
|
|
13337
13991
|
throw error;
|