@joshski/dust 0.1.75 → 0.1.76

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 CHANGED
@@ -719,6 +719,65 @@ function repositoryContext() {
719
719
  - [ ] A new agent reading only this document could make sensible high-level suggestions
720
720
  `;
721
721
  }
722
+ function slowTests() {
723
+ return dedent`
724
+ # Slow Tests
725
+
726
+ Identify slow-running tests that impact feedback loop speed.
727
+
728
+ ${ideasHint}
729
+
730
+ ## Scope
731
+
732
+ Focus on these areas:
733
+
734
+ 1. **Test execution times** - Identify tests that exceed a reasonable duration threshold (e.g., 100ms for unit tests, 1s for integration tests)
735
+ 2. **I/O-bound tests** - Tests that perform actual file system, network, or database operations
736
+ 3. **Sleep/delay usage** - Tests using \`setTimeout\`, \`sleep\`, or similar timing functions
737
+ 4. **Missing mocks** - Tests that make real HTTP requests or database calls instead of using stubs
738
+ 5. **Setup overhead** - Expensive \`beforeEach\`/\`beforeAll\` setup that could be optimized or shared
739
+ 6. **Serial test execution** - Tests that could run in parallel but are forced to run serially
740
+
741
+ ## Analysis Steps
742
+
743
+ 1. Run the test suite with timing output: \`npm test -- --reporter=verbose\` or equivalent
744
+ 2. Identify tests taking longer than the threshold (100ms+ for unit, 1s+ for integration)
745
+ 3. Search for \`setTimeout\`, \`sleep\`, \`delay\`, and similar timing patterns in test files
746
+ 4. Look for real I/O: \`fetch\`, \`axios\`, database clients, file system operations without mocks
747
+ 5. Review \`beforeEach\`/\`beforeAll\` blocks for expensive operations
748
+ 6. Check test configuration for parallelization settings
749
+
750
+ ## Output
751
+
752
+ For each slow test identified, provide:
753
+ - **Test name** - The describe/it block name
754
+ - **File path** - Location of the test
755
+ - **Duration** - How long the test takes (if measurable)
756
+ - **Cause** - Why the test is slow (I/O, sleep, setup, etc.)
757
+ - **Suggestion** - Specific optimization (mock the API, use fake timers, share setup, etc.)
758
+
759
+ ## Principles
760
+
761
+ - [Fast Feedback Loops](../principles/fast-feedback-loops.md) - Tests should run quickly for tight iteration cycles
762
+ - [Fast Feedback](../principles/fast-feedback.md) - Slow tests discourage frequent validation
763
+ - [Keep Unit Tests Pure](../principles/keep-unit-tests-pure.md) - Pure tests are faster and more reliable
764
+ - [Stubs Over Mocks](../principles/stubs-over-mocks.md) - Use stubs to avoid slow real dependencies
765
+
766
+ ## Blocked By
767
+
768
+ (none)
769
+
770
+ ## Definition of Done
771
+
772
+ - [ ] Ran test suite with timing information
773
+ - [ ] Listed tests exceeding duration thresholds (100ms unit, 1s integration)
774
+ - [ ] Identified tests using sleep/setTimeout/delay patterns
775
+ - [ ] Found tests with unmocked I/O (network, database, file system)
776
+ - [ ] Reviewed beforeEach/beforeAll for optimization opportunities
777
+ - [ ] Checked test parallelization configuration
778
+ - [ ] Proposed ideas for optimizing the slowest tests
779
+ `;
780
+ }
722
781
  function ubiquitousLanguage() {
723
782
  return dedent`
724
783
  # Ubiquitous Language
@@ -787,6 +846,7 @@ var stockAuditFunctions = {
787
846
  "refactoring-opportunities": refactoringOpportunities,
788
847
  "repository-context": repositoryContext,
789
848
  "security-review": securityReview,
849
+ "slow-tests": slowTests,
790
850
  "stale-ideas": staleIdeas,
791
851
  "test-coverage": testCoverage,
792
852
  "ubiquitous-language": ubiquitousLanguage
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.75";
278
+ var DUST_VERSION = "0.1.76";
279
279
 
280
280
  // lib/session.ts
281
281
  var DUST_UNATTENDED = "DUST_UNATTENDED";
@@ -1266,6 +1266,65 @@ function repositoryContext() {
1266
1266
  - [ ] A new agent reading only this document could make sensible high-level suggestions
1267
1267
  `;
1268
1268
  }
1269
+ function slowTests() {
1270
+ return dedent`
1271
+ # Slow Tests
1272
+
1273
+ Identify slow-running tests that impact feedback loop speed.
1274
+
1275
+ ${ideasHint}
1276
+
1277
+ ## Scope
1278
+
1279
+ Focus on these areas:
1280
+
1281
+ 1. **Test execution times** - Identify tests that exceed a reasonable duration threshold (e.g., 100ms for unit tests, 1s for integration tests)
1282
+ 2. **I/O-bound tests** - Tests that perform actual file system, network, or database operations
1283
+ 3. **Sleep/delay usage** - Tests using \`setTimeout\`, \`sleep\`, or similar timing functions
1284
+ 4. **Missing mocks** - Tests that make real HTTP requests or database calls instead of using stubs
1285
+ 5. **Setup overhead** - Expensive \`beforeEach\`/\`beforeAll\` setup that could be optimized or shared
1286
+ 6. **Serial test execution** - Tests that could run in parallel but are forced to run serially
1287
+
1288
+ ## Analysis Steps
1289
+
1290
+ 1. Run the test suite with timing output: \`npm test -- --reporter=verbose\` or equivalent
1291
+ 2. Identify tests taking longer than the threshold (100ms+ for unit, 1s+ for integration)
1292
+ 3. Search for \`setTimeout\`, \`sleep\`, \`delay\`, and similar timing patterns in test files
1293
+ 4. Look for real I/O: \`fetch\`, \`axios\`, database clients, file system operations without mocks
1294
+ 5. Review \`beforeEach\`/\`beforeAll\` blocks for expensive operations
1295
+ 6. Check test configuration for parallelization settings
1296
+
1297
+ ## Output
1298
+
1299
+ For each slow test identified, provide:
1300
+ - **Test name** - The describe/it block name
1301
+ - **File path** - Location of the test
1302
+ - **Duration** - How long the test takes (if measurable)
1303
+ - **Cause** - Why the test is slow (I/O, sleep, setup, etc.)
1304
+ - **Suggestion** - Specific optimization (mock the API, use fake timers, share setup, etc.)
1305
+
1306
+ ## Principles
1307
+
1308
+ - [Fast Feedback Loops](../principles/fast-feedback-loops.md) - Tests should run quickly for tight iteration cycles
1309
+ - [Fast Feedback](../principles/fast-feedback.md) - Slow tests discourage frequent validation
1310
+ - [Keep Unit Tests Pure](../principles/keep-unit-tests-pure.md) - Pure tests are faster and more reliable
1311
+ - [Stubs Over Mocks](../principles/stubs-over-mocks.md) - Use stubs to avoid slow real dependencies
1312
+
1313
+ ## Blocked By
1314
+
1315
+ (none)
1316
+
1317
+ ## Definition of Done
1318
+
1319
+ - [ ] Ran test suite with timing information
1320
+ - [ ] Listed tests exceeding duration thresholds (100ms unit, 1s integration)
1321
+ - [ ] Identified tests using sleep/setTimeout/delay patterns
1322
+ - [ ] Found tests with unmocked I/O (network, database, file system)
1323
+ - [ ] Reviewed beforeEach/beforeAll for optimization opportunities
1324
+ - [ ] Checked test parallelization configuration
1325
+ - [ ] Proposed ideas for optimizing the slowest tests
1326
+ `;
1327
+ }
1269
1328
  function ubiquitousLanguage() {
1270
1329
  return dedent`
1271
1330
  # Ubiquitous Language
@@ -1334,6 +1393,7 @@ var stockAuditFunctions = {
1334
1393
  "refactoring-opportunities": refactoringOpportunities,
1335
1394
  "repository-context": repositoryContext,
1336
1395
  "security-review": securityReview,
1396
+ "slow-tests": slowTests,
1337
1397
  "stale-ideas": staleIdeas,
1338
1398
  "test-coverage": testCoverage,
1339
1399
  "ubiquitous-language": ubiquitousLanguage
@@ -2264,7 +2324,8 @@ function getRepoPath(repoName, reposDir) {
2264
2324
  async function cloneRepository(repository, targetPath, spawn, context) {
2265
2325
  return new Promise((resolve) => {
2266
2326
  const proc = spawn("git", ["clone", repository.gitUrl, targetPath], {
2267
- stdio: ["ignore", "pipe", "pipe"]
2327
+ stdio: ["ignore", "pipe", "pipe"],
2328
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
2268
2329
  });
2269
2330
  let stderr = "";
2270
2331
  proc.stderr?.on("data", (data) => {
@@ -2488,26 +2549,26 @@ function getEnvironmentContext(cwd) {
2488
2549
  function formatLoopEvent(event) {
2489
2550
  switch (event.type) {
2490
2551
  case "loop.warning":
2491
- return "⚠️ WARNING: This command skips all permission checks. Only use in a sandbox environment!";
2552
+ return "WARNING: This command skips all permission checks. Only use in a sandbox environment!";
2492
2553
  case "loop.started": {
2493
2554
  const agent2 = event.agentType ?? "claude";
2494
- return `\uD83D\uDD04 Starting dust loop ${agent2} (max ${event.maxIterations} iterations)...`;
2555
+ return `Starting dust loop ${agent2} (max ${event.maxIterations} iterations)...`;
2495
2556
  }
2496
2557
  case "loop.syncing":
2497
- return "\uD83C\uDF0D Syncing with remote";
2558
+ return "Syncing with remote";
2498
2559
  case "loop.sync_skipped":
2499
2560
  return `Note: git pull skipped (${event.reason})`;
2500
2561
  case "loop.checking_tasks":
2501
2562
  return null;
2502
2563
  case "loop.no_tasks":
2503
- return "\uD83D\uDE34 No tasks available. Sleeping...";
2564
+ return "No tasks available. Sleeping...";
2504
2565
  case "loop.tasks_found":
2505
- return `✨ Found a task. Going to work!
2566
+ return `Found a task. Going to work!
2506
2567
  `;
2507
2568
  case "loop.iteration_complete":
2508
- return `\uD83D\uDCCB Completed iteration ${event.iteration}/${event.maxIterations}`;
2569
+ return `Completed iteration ${event.iteration}/${event.maxIterations}`;
2509
2570
  case "loop.ended":
2510
- return `\uD83C\uDFC1 Reached max iterations (${event.maxIterations}). Exiting.`;
2571
+ return `Reached max iterations (${event.maxIterations}). Exiting.`;
2511
2572
  }
2512
2573
  }
2513
2574
  function createPostEvent(fetchFn) {
@@ -2565,7 +2626,8 @@ async function gitPull(cwd, spawn) {
2565
2626
  return new Promise((resolve) => {
2566
2627
  const proc = spawn("git", ["pull"], {
2567
2628
  cwd,
2568
- stdio: ["ignore", "pipe", "pipe"]
2629
+ stdio: ["ignore", "pipe", "pipe"],
2630
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
2569
2631
  });
2570
2632
  let stderr = "";
2571
2633
  proc.stderr?.on("data", (data) => {
@@ -3115,6 +3177,15 @@ function parseServerMessage(data) {
3115
3177
  }
3116
3178
 
3117
3179
  // lib/bucket/terminal-ui.ts
3180
+ var CHARS = {
3181
+ dot: "*",
3182
+ sparkle: "",
3183
+ ellipsis: "...",
3184
+ hline: "-",
3185
+ arrows_lr: "<->",
3186
+ arrows_ud: "up/dn",
3187
+ scroll_down: "v"
3188
+ };
3118
3189
  var ANSI = {
3119
3190
  HIDE_CURSOR: "\x1B[?25l",
3120
3191
  SHOW_CURSOR: "\x1B[?25h",
@@ -3169,8 +3240,8 @@ function truncateLine(text, maxWidth) {
3169
3240
  for (let match = ansiRegex.exec(text);match !== null; match = ansiRegex.exec(text)) {
3170
3241
  const textBefore = text.slice(lastIndex, match.index);
3171
3242
  for (const char of textBefore) {
3172
- if (visibleCount >= maxWidth - 1) {
3173
- result += "…";
3243
+ if (visibleCount >= maxWidth - CHARS.ellipsis.length) {
3244
+ result += CHARS.ellipsis;
3174
3245
  return result + ANSI.RESET;
3175
3246
  }
3176
3247
  result += char;
@@ -3181,8 +3252,8 @@ function truncateLine(text, maxWidth) {
3181
3252
  }
3182
3253
  const remaining = text.slice(lastIndex);
3183
3254
  for (const char of remaining) {
3184
- if (visibleCount >= maxWidth - 1) {
3185
- result += "…";
3255
+ if (visibleCount >= maxWidth - CHARS.ellipsis.length) {
3256
+ result += CHARS.ellipsis;
3186
3257
  return result + ANSI.RESET;
3187
3258
  }
3188
3259
  result += char;
@@ -3282,7 +3353,7 @@ function getTabRowCount(state) {
3282
3353
  return 1;
3283
3354
  const tabWidths = [5];
3284
3355
  for (const name of state.repositories) {
3285
- tabWidths.push(name.length + 4);
3356
+ tabWidths.push(name.length + 2 + CHARS.dot.length + 1);
3286
3357
  }
3287
3358
  let rows = 1;
3288
3359
  let currentRowWidth = 0;
@@ -3352,8 +3423,8 @@ function renderTabs(state) {
3352
3423
  const color = getRepoColor(name, i);
3353
3424
  const agentStatus = state.agentStatuses.get(name) ?? "idle";
3354
3425
  const dotColor = agentStatus === "busy" ? ANSI.FG_GREEN : ANSI.DIM;
3355
- const dot = `${dotColor}●${ANSI.RESET}`;
3356
- const width = name.length + 4;
3426
+ const dot = `${dotColor}${CHARS.dot}${ANSI.RESET}`;
3427
+ const width = name.length + 2 + CHARS.dot.length + 1;
3357
3428
  if (i === state.selectedIndex) {
3358
3429
  tabs.push({
3359
3430
  text: ` ${dot}${color} ${ANSI.INVERSE}${name}${ANSI.RESET} `,
@@ -3378,10 +3449,10 @@ function renderTabs(state) {
3378
3449
  return rows.map((row) => row.map((t) => t.text).join("|"));
3379
3450
  }
3380
3451
  function renderHelpLine() {
3381
- return `${ANSI.DIM}[←→] select [↑↓] scroll [PgUp/PgDn] page [g/G] top/bottom [o] open [q] quit${ANSI.RESET}`;
3452
+ return `${ANSI.DIM}[${CHARS.arrows_lr}] select [${CHARS.arrows_ud}] scroll [PgUp/PgDn] page [g/G] top/bottom [o] open [q] quit${ANSI.RESET}`;
3382
3453
  }
3383
3454
  function renderSeparator(width) {
3384
- return "─".repeat(width);
3455
+ return CHARS.hline.repeat(Math.max(0, width));
3385
3456
  }
3386
3457
  function formatLogLine(line, prefixAlign, maxWidth) {
3387
3458
  let prefix = "";
@@ -3413,7 +3484,7 @@ function formatLogLine(line, prefixAlign, maxWidth) {
3413
3484
  function renderFrame(state) {
3414
3485
  const lines = [];
3415
3486
  const hostLabel = state.connectedHost ? ` ${ANSI.DIM}[connected to ${state.connectedHost}]${ANSI.RESET}` : "";
3416
- lines.push(`${ANSI.BOLD}dust bucket${ANSI.RESET}${hostLabel}`);
3487
+ lines.push(`${ANSI.BOLD}${CHARS.sparkle}dust bucket${ANSI.RESET}${hostLabel}`);
3417
3488
  const tabRows = renderTabs(state);
3418
3489
  for (const tabRow of tabRows) {
3419
3490
  lines.push(tabRow);
@@ -3436,7 +3507,7 @@ function renderFrame(state) {
3436
3507
  lines.push("");
3437
3508
  }
3438
3509
  if (state.scrollOffset > 0) {
3439
- const indicator = `${ANSI.DIM} ${state.scrollOffset} more${ANSI.RESET}`;
3510
+ const indicator = `${ANSI.DIM}${CHARS.scroll_down} ${state.scrollOffset} more${ANSI.RESET}`;
3440
3511
  lines[lines.length - 1] = indicator;
3441
3512
  }
3442
3513
  const output = [];
@@ -3584,8 +3655,8 @@ function defaultSetupResize(onResize) {
3584
3655
  }
3585
3656
  function defaultGetTerminalSize() {
3586
3657
  return {
3587
- width: process.stdout.columns ?? 80,
3588
- height: process.stdout.rows ?? 24
3658
+ width: process.stdout.columns || 80,
3659
+ height: process.stdout.rows || 24
3589
3660
  };
3590
3661
  }
3591
3662
  function defaultWriteStdout(data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.75",
3
+ "version": "0.1.76",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {