@joshski/dust 0.1.75 → 0.1.77

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.
@@ -30,6 +30,7 @@ export interface EventMessage {
30
30
  timestamp: string;
31
31
  sessionId: string;
32
32
  repository: string;
33
+ repoId?: number;
33
34
  agentSessionId?: string;
34
35
  event: AgentSessionEvent;
35
36
  }
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
@@ -772,6 +831,75 @@ function ubiquitousLanguage() {
772
831
  - [ ] Proposed ideas for standardizing inconsistent terminology
773
832
  `;
774
833
  }
834
+ function uxAudit() {
835
+ return dedent`
836
+ # UX Audit
837
+
838
+ Review the end user experience by capturing visual or interactive evidence at key scenarios.
839
+
840
+ ${ideasHint}
841
+
842
+ ## Scope
843
+
844
+ 1. **Identify key scenarios** - What are the main user journeys? (e.g., signup, login, checkout, onboarding, core workflows)
845
+ 2. **Capture evidence** - For each scenario:
846
+ - Web apps: Take screenshots at each step using browser automation (Playwright, Puppeteer, Cypress, or similar)
847
+ - Terminal apps: Capture command output and interactive sessions
848
+ 3. **Review captured evidence** for UX issues:
849
+ - Confusing or unclear states
850
+ - Missing feedback or loading indicators
851
+ - Error messages that don't guide recovery
852
+ - Inconsistent styling or layout
853
+ 4. **Document findings** with screenshots/output and specific recommendations
854
+
855
+ ## Applicability
856
+
857
+ Determine the application type and available tooling:
858
+ - If browser tests exist (Playwright, Puppeteer, Cypress), extend them to capture screenshots
859
+ - If no browser tests exist, write a standalone script for key scenarios
860
+ - For terminal apps, capture representative sessions using command output or terminal recording
861
+
862
+ If the project has no user-facing interface, document that finding and skip the detailed analysis.
863
+
864
+ ## Analysis Steps
865
+
866
+ 1. Identify the application type (web, terminal, hybrid, no UI)
867
+ 2. List the key user scenarios from documentation, tests, or code analysis
868
+ 3. Capture screenshots or output at each stage of each scenario
869
+ 4. Store artifacts in a temporary directory for review during this audit
870
+ 5. Review each artifact for UX issues
871
+ 6. Document findings with evidence and specific recommendations
872
+
873
+ ## Output
874
+
875
+ For each UX issue identified, provide:
876
+ - **Location** - Which scenario and step
877
+ - **Evidence** - Screenshot filename or captured output
878
+ - **Problem** - What's wrong from the user's perspective
879
+ - **Impact** - How it affects the user's ability to complete their goal
880
+ - **Recommendation** - Specific fix
881
+ - **Verification** - How to verify the fix (e.g., "Screenshot at step 3 should show success message instead of spinner")
882
+
883
+ ## Principles
884
+
885
+ - [Actionable Errors](../principles/actionable-errors.md) - Error messages should tell users what to do next
886
+ - [Unsurprising UX](../principles/unsurprising-ux.md) - The interface should be as guessable as possible
887
+
888
+ ## Blocked By
889
+
890
+ (none)
891
+
892
+ ## Definition of Done
893
+
894
+ - [ ] Identified the application type (web, terminal, hybrid, or no UI)
895
+ - [ ] Listed key user scenarios
896
+ - [ ] Captured screenshots or output at each stage of key scenarios
897
+ - [ ] Reviewed evidence for UX issues
898
+ - [ ] Documented findings with evidence and recommendations
899
+ - [ ] Included verification criteria for each issue
900
+ - [ ] Created ideas for any UX improvements needed
901
+ `;
902
+ }
775
903
  var stockAuditFunctions = {
776
904
  "agent-developer-experience": agentDeveloperExperience,
777
905
  "component-reuse": componentReuse,
@@ -787,9 +915,11 @@ var stockAuditFunctions = {
787
915
  "refactoring-opportunities": refactoringOpportunities,
788
916
  "repository-context": repositoryContext,
789
917
  "security-review": securityReview,
918
+ "slow-tests": slowTests,
790
919
  "stale-ideas": staleIdeas,
791
920
  "test-coverage": testCoverage,
792
- "ubiquitous-language": ubiquitousLanguage
921
+ "ubiquitous-language": ubiquitousLanguage,
922
+ "ux-audit": uxAudit
793
923
  };
794
924
  function loadStockAudits() {
795
925
  return Object.entries(stockAuditFunctions).sort(([a], [b]) => a.localeCompare(b)).map(([name, render]) => {
@@ -30,6 +30,7 @@ export declare function buildEventMessage(parameters: {
30
30
  sequence: number;
31
31
  sessionId: string;
32
32
  repository: string;
33
+ repoId?: number;
33
34
  event: AgentSessionEvent;
34
35
  agentSessionId?: string;
35
36
  }): EventMessage;
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.77";
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
@@ -1319,6 +1378,75 @@ function ubiquitousLanguage() {
1319
1378
  - [ ] Proposed ideas for standardizing inconsistent terminology
1320
1379
  `;
1321
1380
  }
1381
+ function uxAudit() {
1382
+ return dedent`
1383
+ # UX Audit
1384
+
1385
+ Review the end user experience by capturing visual or interactive evidence at key scenarios.
1386
+
1387
+ ${ideasHint}
1388
+
1389
+ ## Scope
1390
+
1391
+ 1. **Identify key scenarios** - What are the main user journeys? (e.g., signup, login, checkout, onboarding, core workflows)
1392
+ 2. **Capture evidence** - For each scenario:
1393
+ - Web apps: Take screenshots at each step using browser automation (Playwright, Puppeteer, Cypress, or similar)
1394
+ - Terminal apps: Capture command output and interactive sessions
1395
+ 3. **Review captured evidence** for UX issues:
1396
+ - Confusing or unclear states
1397
+ - Missing feedback or loading indicators
1398
+ - Error messages that don't guide recovery
1399
+ - Inconsistent styling or layout
1400
+ 4. **Document findings** with screenshots/output and specific recommendations
1401
+
1402
+ ## Applicability
1403
+
1404
+ Determine the application type and available tooling:
1405
+ - If browser tests exist (Playwright, Puppeteer, Cypress), extend them to capture screenshots
1406
+ - If no browser tests exist, write a standalone script for key scenarios
1407
+ - For terminal apps, capture representative sessions using command output or terminal recording
1408
+
1409
+ If the project has no user-facing interface, document that finding and skip the detailed analysis.
1410
+
1411
+ ## Analysis Steps
1412
+
1413
+ 1. Identify the application type (web, terminal, hybrid, no UI)
1414
+ 2. List the key user scenarios from documentation, tests, or code analysis
1415
+ 3. Capture screenshots or output at each stage of each scenario
1416
+ 4. Store artifacts in a temporary directory for review during this audit
1417
+ 5. Review each artifact for UX issues
1418
+ 6. Document findings with evidence and specific recommendations
1419
+
1420
+ ## Output
1421
+
1422
+ For each UX issue identified, provide:
1423
+ - **Location** - Which scenario and step
1424
+ - **Evidence** - Screenshot filename or captured output
1425
+ - **Problem** - What's wrong from the user's perspective
1426
+ - **Impact** - How it affects the user's ability to complete their goal
1427
+ - **Recommendation** - Specific fix
1428
+ - **Verification** - How to verify the fix (e.g., "Screenshot at step 3 should show success message instead of spinner")
1429
+
1430
+ ## Principles
1431
+
1432
+ - [Actionable Errors](../principles/actionable-errors.md) - Error messages should tell users what to do next
1433
+ - [Unsurprising UX](../principles/unsurprising-ux.md) - The interface should be as guessable as possible
1434
+
1435
+ ## Blocked By
1436
+
1437
+ (none)
1438
+
1439
+ ## Definition of Done
1440
+
1441
+ - [ ] Identified the application type (web, terminal, hybrid, or no UI)
1442
+ - [ ] Listed key user scenarios
1443
+ - [ ] Captured screenshots or output at each stage of key scenarios
1444
+ - [ ] Reviewed evidence for UX issues
1445
+ - [ ] Documented findings with evidence and recommendations
1446
+ - [ ] Included verification criteria for each issue
1447
+ - [ ] Created ideas for any UX improvements needed
1448
+ `;
1449
+ }
1322
1450
  var stockAuditFunctions = {
1323
1451
  "agent-developer-experience": agentDeveloperExperience,
1324
1452
  "component-reuse": componentReuse,
@@ -1334,9 +1462,11 @@ var stockAuditFunctions = {
1334
1462
  "refactoring-opportunities": refactoringOpportunities,
1335
1463
  "repository-context": repositoryContext,
1336
1464
  "security-review": securityReview,
1465
+ "slow-tests": slowTests,
1337
1466
  "stale-ideas": staleIdeas,
1338
1467
  "test-coverage": testCoverage,
1339
- "ubiquitous-language": ubiquitousLanguage
1468
+ "ubiquitous-language": ubiquitousLanguage,
1469
+ "ux-audit": uxAudit
1340
1470
  };
1341
1471
  function loadStockAudits() {
1342
1472
  return Object.entries(stockAuditFunctions).sort(([a], [b]) => a.localeCompare(b)).map(([name, render]) => {
@@ -2264,7 +2394,8 @@ function getRepoPath(repoName, reposDir) {
2264
2394
  async function cloneRepository(repository, targetPath, spawn, context) {
2265
2395
  return new Promise((resolve) => {
2266
2396
  const proc = spawn("git", ["clone", repository.gitUrl, targetPath], {
2267
- stdio: ["ignore", "pipe", "pipe"]
2397
+ stdio: ["ignore", "pipe", "pipe"],
2398
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
2268
2399
  });
2269
2400
  let stderr = "";
2270
2401
  proc.stderr?.on("data", (data) => {
@@ -2488,26 +2619,26 @@ function getEnvironmentContext(cwd) {
2488
2619
  function formatLoopEvent(event) {
2489
2620
  switch (event.type) {
2490
2621
  case "loop.warning":
2491
- return "⚠️ WARNING: This command skips all permission checks. Only use in a sandbox environment!";
2622
+ return "WARNING: This command skips all permission checks. Only use in a sandbox environment!";
2492
2623
  case "loop.started": {
2493
2624
  const agent2 = event.agentType ?? "claude";
2494
- return `\uD83D\uDD04 Starting dust loop ${agent2} (max ${event.maxIterations} iterations)...`;
2625
+ return `Starting dust loop ${agent2} (max ${event.maxIterations} iterations)...`;
2495
2626
  }
2496
2627
  case "loop.syncing":
2497
- return "\uD83C\uDF0D Syncing with remote";
2628
+ return "Syncing with remote";
2498
2629
  case "loop.sync_skipped":
2499
2630
  return `Note: git pull skipped (${event.reason})`;
2500
2631
  case "loop.checking_tasks":
2501
2632
  return null;
2502
2633
  case "loop.no_tasks":
2503
- return "\uD83D\uDE34 No tasks available. Sleeping...";
2634
+ return "No tasks available. Sleeping...";
2504
2635
  case "loop.tasks_found":
2505
- return `✨ Found a task. Going to work!
2636
+ return `Found a task. Going to work!
2506
2637
  `;
2507
2638
  case "loop.iteration_complete":
2508
- return `\uD83D\uDCCB Completed iteration ${event.iteration}/${event.maxIterations}`;
2639
+ return `Completed iteration ${event.iteration}/${event.maxIterations}`;
2509
2640
  case "loop.ended":
2510
- return `\uD83C\uDFC1 Reached max iterations (${event.maxIterations}). Exiting.`;
2641
+ return `Reached max iterations (${event.maxIterations}). Exiting.`;
2511
2642
  }
2512
2643
  }
2513
2644
  function createPostEvent(fetchFn) {
@@ -2565,7 +2696,8 @@ async function gitPull(cwd, spawn) {
2565
2696
  return new Promise((resolve) => {
2566
2697
  const proc = spawn("git", ["pull"], {
2567
2698
  cwd,
2568
- stdio: ["ignore", "pipe", "pipe"]
2699
+ stdio: ["ignore", "pipe", "pipe"],
2700
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
2569
2701
  });
2570
2702
  let stderr = "";
2571
2703
  proc.stderr?.on("data", (data) => {
@@ -2815,6 +2947,9 @@ function buildEventMessage(parameters) {
2815
2947
  repository: parameters.repository,
2816
2948
  event: parameters.event
2817
2949
  };
2950
+ if (parameters.repoId !== undefined) {
2951
+ msg.repoId = parameters.repoId;
2952
+ }
2818
2953
  if (parameters.agentSessionId) {
2819
2954
  msg.agentSessionId = parameters.agentSessionId;
2820
2955
  }
@@ -2900,6 +3035,7 @@ async function runRepositoryLoop(repoState, repoDeps, sendEvent, sessionId) {
2900
3035
  sequence,
2901
3036
  sessionId,
2902
3037
  repository: repoName,
3038
+ repoId: repoState.repository.id,
2903
3039
  event,
2904
3040
  agentSessionId
2905
3041
  }));
@@ -3115,6 +3251,15 @@ function parseServerMessage(data) {
3115
3251
  }
3116
3252
 
3117
3253
  // lib/bucket/terminal-ui.ts
3254
+ var CHARS = {
3255
+ dot: "*",
3256
+ sparkle: "",
3257
+ ellipsis: "...",
3258
+ hline: "-",
3259
+ arrows_lr: "<->",
3260
+ arrows_ud: "up/dn",
3261
+ scroll_down: "v"
3262
+ };
3118
3263
  var ANSI = {
3119
3264
  HIDE_CURSOR: "\x1B[?25l",
3120
3265
  SHOW_CURSOR: "\x1B[?25h",
@@ -3169,8 +3314,8 @@ function truncateLine(text, maxWidth) {
3169
3314
  for (let match = ansiRegex.exec(text);match !== null; match = ansiRegex.exec(text)) {
3170
3315
  const textBefore = text.slice(lastIndex, match.index);
3171
3316
  for (const char of textBefore) {
3172
- if (visibleCount >= maxWidth - 1) {
3173
- result += "…";
3317
+ if (visibleCount >= maxWidth - CHARS.ellipsis.length) {
3318
+ result += CHARS.ellipsis;
3174
3319
  return result + ANSI.RESET;
3175
3320
  }
3176
3321
  result += char;
@@ -3181,8 +3326,8 @@ function truncateLine(text, maxWidth) {
3181
3326
  }
3182
3327
  const remaining = text.slice(lastIndex);
3183
3328
  for (const char of remaining) {
3184
- if (visibleCount >= maxWidth - 1) {
3185
- result += "…";
3329
+ if (visibleCount >= maxWidth - CHARS.ellipsis.length) {
3330
+ result += CHARS.ellipsis;
3186
3331
  return result + ANSI.RESET;
3187
3332
  }
3188
3333
  result += char;
@@ -3282,7 +3427,7 @@ function getTabRowCount(state) {
3282
3427
  return 1;
3283
3428
  const tabWidths = [5];
3284
3429
  for (const name of state.repositories) {
3285
- tabWidths.push(name.length + 4);
3430
+ tabWidths.push(name.length + 2 + CHARS.dot.length + 1);
3286
3431
  }
3287
3432
  let rows = 1;
3288
3433
  let currentRowWidth = 0;
@@ -3352,8 +3497,8 @@ function renderTabs(state) {
3352
3497
  const color = getRepoColor(name, i);
3353
3498
  const agentStatus = state.agentStatuses.get(name) ?? "idle";
3354
3499
  const dotColor = agentStatus === "busy" ? ANSI.FG_GREEN : ANSI.DIM;
3355
- const dot = `${dotColor}●${ANSI.RESET}`;
3356
- const width = name.length + 4;
3500
+ const dot = `${dotColor}${CHARS.dot}${ANSI.RESET}`;
3501
+ const width = name.length + 2 + CHARS.dot.length + 1;
3357
3502
  if (i === state.selectedIndex) {
3358
3503
  tabs.push({
3359
3504
  text: ` ${dot}${color} ${ANSI.INVERSE}${name}${ANSI.RESET} `,
@@ -3378,10 +3523,10 @@ function renderTabs(state) {
3378
3523
  return rows.map((row) => row.map((t) => t.text).join("|"));
3379
3524
  }
3380
3525
  function renderHelpLine() {
3381
- return `${ANSI.DIM}[←→] select [↑↓] scroll [PgUp/PgDn] page [g/G] top/bottom [o] open [q] quit${ANSI.RESET}`;
3526
+ 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
3527
  }
3383
3528
  function renderSeparator(width) {
3384
- return "─".repeat(width);
3529
+ return CHARS.hline.repeat(Math.max(0, width));
3385
3530
  }
3386
3531
  function formatLogLine(line, prefixAlign, maxWidth) {
3387
3532
  let prefix = "";
@@ -3413,7 +3558,7 @@ function formatLogLine(line, prefixAlign, maxWidth) {
3413
3558
  function renderFrame(state) {
3414
3559
  const lines = [];
3415
3560
  const hostLabel = state.connectedHost ? ` ${ANSI.DIM}[connected to ${state.connectedHost}]${ANSI.RESET}` : "";
3416
- lines.push(`${ANSI.BOLD}dust bucket${ANSI.RESET}${hostLabel}`);
3561
+ lines.push(`${ANSI.BOLD}${CHARS.sparkle}dust bucket${ANSI.RESET}${hostLabel}`);
3417
3562
  const tabRows = renderTabs(state);
3418
3563
  for (const tabRow of tabRows) {
3419
3564
  lines.push(tabRow);
@@ -3436,7 +3581,7 @@ function renderFrame(state) {
3436
3581
  lines.push("");
3437
3582
  }
3438
3583
  if (state.scrollOffset > 0) {
3439
- const indicator = `${ANSI.DIM} ${state.scrollOffset} more${ANSI.RESET}`;
3584
+ const indicator = `${ANSI.DIM}${CHARS.scroll_down} ${state.scrollOffset} more${ANSI.RESET}`;
3440
3585
  lines[lines.length - 1] = indicator;
3441
3586
  }
3442
3587
  const output = [];
@@ -3584,8 +3729,8 @@ function defaultSetupResize(onResize) {
3584
3729
  }
3585
3730
  function defaultGetTerminalSize() {
3586
3731
  return {
3587
- width: process.stdout.columns ?? 80,
3588
- height: process.stdout.rows ?? 24
3732
+ width: process.stdout.columns || 80,
3733
+ height: process.stdout.rows || 24
3589
3734
  };
3590
3735
  }
3591
3736
  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.77",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {