@joshski/dust 0.1.45 → 0.1.46

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.
@@ -39,8 +39,4 @@ export interface EventMessage {
39
39
  * is forwarded as a claude-event.
40
40
  */
41
41
  export declare function rawEventToAgentEvent(rawEvent: Record<string, unknown>): AgentSessionEvent;
42
- /**
43
- * Format an AgentSessionEvent for console output.
44
- * Returns null for events that should not be displayed.
45
- */
46
42
  export declare function formatAgentEvent(event: AgentSessionEvent): string | null;
@@ -22,6 +22,7 @@ export interface FileSystem {
22
22
  readdir: (path: string) => Promise<string[]>;
23
23
  chmod: (path: string, mode: number) => Promise<void>;
24
24
  isDirectory: (path: string) => boolean;
25
+ getFileCreationTime: (path: string) => number;
25
26
  }
26
27
  export interface GlobScanner {
27
28
  scan: (dir: string) => AsyncIterable<string>;
package/dist/dust.js CHANGED
@@ -577,6 +577,10 @@ function agentDeveloperExperience() {
577
577
  4. **Debugging tools** - Can agents diagnose issues without trial and error?
578
578
  5. **Structured logging** - Is system behavior observable through logs?
579
579
 
580
+ ## Goals
581
+
582
+ (none)
583
+
580
584
  ## Blocked By
581
585
 
582
586
  (none)
@@ -606,6 +610,10 @@ function deadCode() {
606
610
  4. **Unused dependencies** - Packages in package.json not used in code
607
611
  5. **Commented-out code** - Old code left in comments
608
612
 
613
+ ## Goals
614
+
615
+ (none)
616
+
609
617
  ## Blocked By
610
618
 
611
619
  (none)
@@ -636,6 +644,10 @@ function factsVerification() {
636
644
  3. **Staleness** - Have facts become outdated due to recent changes?
637
645
  4. **Relevance** - Are all facts still useful for understanding the project?
638
646
 
647
+ ## Goals
648
+
649
+ (none)
650
+
639
651
  ## Blocked By
640
652
 
641
653
  (none)
@@ -665,6 +677,10 @@ function ideasFromCommits() {
665
677
  3. **Pattern opportunities** - Can recent changes be generalized?
666
678
  4. **Test gaps** - Do recent changes have adequate test coverage?
667
679
 
680
+ ## Goals
681
+
682
+ (none)
683
+
668
684
  ## Blocked By
669
685
 
670
686
  (none)
@@ -693,6 +709,10 @@ function ideasFromGoals() {
693
709
  3. **New opportunities** - What work would better achieve each goal?
694
710
  4. **Goal alignment** - Are current tasks aligned with stated goals?
695
711
 
712
+ ## Goals
713
+
714
+ (none)
715
+
696
716
  ## Blocked By
697
717
 
698
718
  (none)
@@ -721,6 +741,10 @@ function performanceReview() {
721
741
  4. **Build performance** - How fast is the build process?
722
742
  5. **Test speed** - Are tests running efficiently?
723
743
 
744
+ ## Goals
745
+
746
+ (none)
747
+
724
748
  ## Blocked By
725
749
 
726
750
  (none)
@@ -750,6 +774,10 @@ function securityReview() {
750
774
  4. **Sensitive data exposure** - Logging sensitive data, insecure storage
751
775
  5. **Dependency vulnerabilities** - Known CVEs in dependencies
752
776
 
777
+ ## Goals
778
+
779
+ (none)
780
+
753
781
  ## Blocked By
754
782
 
755
783
  (none)
@@ -780,6 +808,10 @@ function staleIdeas() {
780
808
  3. **Actionability** - Can the idea be converted to a task?
781
809
  4. **Duplication** - Are there overlapping or redundant ideas?
782
810
 
811
+ ## Goals
812
+
813
+ (none)
814
+
783
815
  ## Blocked By
784
816
 
785
817
  (none)
@@ -809,6 +841,10 @@ function testCoverage() {
809
841
  4. **User-facing features** - UI components, form validation
810
842
  5. **Recent changes** - Code modified in the last few commits
811
843
 
844
+ ## Goals
845
+
846
+ (none)
847
+
812
848
  ## Blocked By
813
849
 
814
850
  (none)
@@ -1593,12 +1629,19 @@ function rawEventToAgentEvent(rawEvent) {
1593
1629
  }
1594
1630
  return { type: "claude-event", rawEvent };
1595
1631
  }
1632
+ function agentDisplayName(agentType) {
1633
+ if (agentType === "codex")
1634
+ return "Codex";
1635
+ return "Claude";
1636
+ }
1596
1637
  function formatAgentEvent(event) {
1597
1638
  switch (event.type) {
1598
- case "agent-session-started":
1599
- return `\uD83E\uDD16 Starting Claude: ${event.title}`;
1639
+ case "agent-session-started": {
1640
+ const name = agentDisplayName(event.agentType);
1641
+ return `\uD83E\uDD16 Starting ${name}: ${event.title}`;
1642
+ }
1600
1643
  case "agent-session-ended":
1601
- return event.success ? "\uD83E\uDD16 Claude session ended (success)" : `\uD83E\uDD16 Claude session ended (error: ${event.error})`;
1644
+ return event.success ? "\uD83E\uDD16 Agent session ended (success)" : `\uD83E\uDD16 Agent session ended (error: ${event.error})`;
1602
1645
  case "agent-session-activity":
1603
1646
  case "claude-event":
1604
1647
  return null;
@@ -1699,7 +1742,11 @@ async function findUnblockedTasks(cwd, fileSystem) {
1699
1742
  return { tasks: [] };
1700
1743
  }
1701
1744
  const files = await fileSystem.readdir(tasksPath);
1702
- const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
1745
+ const mdFiles = files.filter((f) => f.endsWith(".md")).sort((a, b) => {
1746
+ const aTime = fileSystem.getFileCreationTime(`${tasksPath}/${a}`);
1747
+ const bTime = fileSystem.getFileCreationTime(`${tasksPath}/${b}`);
1748
+ return aTime - bTime;
1749
+ });
1703
1750
  if (mdFiles.length === 0) {
1704
1751
  return { tasks: [] };
1705
1752
  }
@@ -1777,8 +1824,10 @@ function formatLoopEvent(event) {
1777
1824
  switch (event.type) {
1778
1825
  case "loop.warning":
1779
1826
  return "⚠️ WARNING: This command skips all permission checks. Only use in a sandbox environment!";
1780
- case "loop.started":
1781
- return `\uD83D\uDD04 Starting dust loop claude (max ${event.maxIterations} iterations)...`;
1827
+ case "loop.started": {
1828
+ const agent2 = event.agentType ?? "claude";
1829
+ return `\uD83D\uDD04 Starting dust loop ${agent2} (max ${event.maxIterations} iterations)...`;
1830
+ }
1782
1831
  case "loop.syncing":
1783
1832
  return "\uD83C\uDF0D Syncing with remote";
1784
1833
  case "loop.sync_skipped":
@@ -1864,6 +1913,7 @@ async function findAvailableTasks(dependencies) {
1864
1913
  async function runOneIteration(dependencies, loopDependencies, onLoopEvent, onAgentEvent, options = {}) {
1865
1914
  const { context } = dependencies;
1866
1915
  const { spawn, run: run2 } = loopDependencies;
1916
+ const agentName = loopDependencies.agentType === "codex" ? "Codex" : "Claude";
1867
1917
  const { onRawEvent } = options;
1868
1918
  onLoopEvent({ type: "loop.syncing" });
1869
1919
  const pullResult = await gitPull(context.cwd, spawn);
@@ -1888,7 +1938,7 @@ Make sure the repository is in a clean state and synced with remote before finis
1888
1938
  type: "agent-session-started",
1889
1939
  title: "Resolving git conflict",
1890
1940
  prompt: prompt2,
1891
- agentType: "claude",
1941
+ agentType: loopDependencies.agentType ?? "claude",
1892
1942
  purpose: "git-conflict",
1893
1943
  ...getEnvironmentContext(context.cwd)
1894
1944
  });
@@ -1905,7 +1955,7 @@ Make sure the repository is in a clean state and synced with remote before finis
1905
1955
  return "resolved_pull_conflict";
1906
1956
  } catch (error) {
1907
1957
  const errorMessage = error instanceof Error ? error.message : String(error);
1908
- context.stderr(`Claude failed to resolve git pull conflict: ${errorMessage}`);
1958
+ context.stderr(`${agentName} failed to resolve git pull conflict: ${errorMessage}`);
1909
1959
  onAgentEvent?.({
1910
1960
  type: "agent-session-ended",
1911
1961
  success: false,
@@ -1941,7 +1991,7 @@ ${instructions}`;
1941
1991
  type: "agent-session-started",
1942
1992
  title: task.title ?? task.path,
1943
1993
  prompt,
1944
- agentType: "claude",
1994
+ agentType: loopDependencies.agentType ?? "claude",
1945
1995
  purpose: "task",
1946
1996
  ...getEnvironmentContext(context.cwd)
1947
1997
  });
@@ -1958,7 +2008,7 @@ ${instructions}`;
1958
2008
  return "ran_claude";
1959
2009
  } catch (error) {
1960
2010
  const errorMessage = error instanceof Error ? error.message : String(error);
1961
- context.stderr(`Claude exited with error: ${errorMessage}`);
2011
+ context.stderr(`${agentName} exited with error: ${errorMessage}`);
1962
2012
  onAgentEvent?.({
1963
2013
  type: "agent-session-ended",
1964
2014
  success: false,
@@ -2002,7 +2052,11 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
2002
2052
  sendWireEvent(event);
2003
2053
  };
2004
2054
  onLoopEvent({ type: "loop.warning" });
2005
- onLoopEvent({ type: "loop.started", maxIterations });
2055
+ onLoopEvent({
2056
+ type: "loop.started",
2057
+ maxIterations,
2058
+ agentType: loopDependencies.agentType
2059
+ });
2006
2060
  context.stdout(" Press Ctrl+C to stop");
2007
2061
  context.stdout("");
2008
2062
  let completedIterations = 0;
@@ -2751,6 +2805,7 @@ function createDefaultBucketDependencies() {
2751
2805
  return false;
2752
2806
  }
2753
2807
  },
2808
+ getFileCreationTime: (path) => statSync(path).birthtimeMs,
2754
2809
  readFile: (path) => readFile(path, "utf8"),
2755
2810
  writeFile: (path, content) => writeFile(path, content, "utf8"),
2756
2811
  mkdir: (path, options) => mkdir(path, options).then(() => {}),
@@ -4281,6 +4336,125 @@ async function list(dependencies) {
4281
4336
  return { exitCode: 0 };
4282
4337
  }
4283
4338
 
4339
+ // lib/codex/spawn-codex.ts
4340
+ import { spawn as nodeSpawn4 } from "node:child_process";
4341
+ import { createInterface as nodeCreateInterface2 } from "node:readline";
4342
+ var defaultDependencies2 = {
4343
+ spawn: nodeSpawn4,
4344
+ createInterface: nodeCreateInterface2
4345
+ };
4346
+ async function* spawnCodex(prompt, options = {}, dependencies = defaultDependencies2) {
4347
+ const { cwd, env } = options;
4348
+ const codexArguments = ["exec", prompt, "--json", "--yolo"];
4349
+ if (cwd) {
4350
+ codexArguments.push("--cd", cwd);
4351
+ }
4352
+ const proc = dependencies.spawn("codex", codexArguments, {
4353
+ stdio: ["ignore", "pipe", "pipe"],
4354
+ env: { ...process.env, ...env }
4355
+ });
4356
+ if (!proc.stdout) {
4357
+ throw new Error("Failed to get stdout from codex process");
4358
+ }
4359
+ const rl = dependencies.createInterface({ input: proc.stdout });
4360
+ for await (const line of rl) {
4361
+ if (!line.trim())
4362
+ continue;
4363
+ try {
4364
+ yield JSON.parse(line);
4365
+ } catch {}
4366
+ }
4367
+ let stderrOutput = "";
4368
+ proc.stderr?.on("data", (data) => {
4369
+ stderrOutput += data.toString();
4370
+ });
4371
+ await new Promise((resolve2, reject) => {
4372
+ proc.on("close", (code) => {
4373
+ if (code === 0 || code === null)
4374
+ resolve2();
4375
+ else {
4376
+ const errMsg = stderrOutput.trim() ? `codex exited with code ${code}: ${stderrOutput.trim()}` : `codex exited with code ${code}`;
4377
+ reject(new Error(errMsg));
4378
+ }
4379
+ });
4380
+ proc.on("error", reject);
4381
+ });
4382
+ }
4383
+
4384
+ // lib/codex/event-parser.ts
4385
+ function* parseCodexRawEvent(raw) {
4386
+ if (raw.type !== "item.completed")
4387
+ return;
4388
+ const item = raw.item;
4389
+ if (!item)
4390
+ return;
4391
+ if (item.type === "agent_message" && typeof item.text === "string") {
4392
+ yield { type: "text_delta", text: `${item.text}
4393
+ ` };
4394
+ } else if (item.type === "command_execution" && typeof item.command === "string") {
4395
+ yield {
4396
+ type: "tool_use",
4397
+ id: typeof item.id === "string" ? item.id : "",
4398
+ name: "command_execution",
4399
+ input: { command: item.command }
4400
+ };
4401
+ if (typeof item.aggregated_output === "string") {
4402
+ yield {
4403
+ type: "tool_result",
4404
+ toolUseId: typeof item.id === "string" ? item.id : "",
4405
+ content: item.aggregated_output || `(exit code: ${item.exit_code ?? "unknown"})`
4406
+ };
4407
+ }
4408
+ }
4409
+ }
4410
+
4411
+ // lib/codex/streamer.ts
4412
+ async function streamCodexEvents(events, sink, onRawEvent) {
4413
+ let hadTextOutput = false;
4414
+ for await (const raw of events) {
4415
+ onRawEvent?.(raw);
4416
+ for (const event of parseCodexRawEvent(raw)) {
4417
+ processEvent(event, sink, { hadTextOutput });
4418
+ if (event.type === "text_delta") {
4419
+ hadTextOutput = true;
4420
+ } else if (event.type === "tool_use") {
4421
+ hadTextOutput = false;
4422
+ }
4423
+ }
4424
+ }
4425
+ }
4426
+
4427
+ // lib/codex/run.ts
4428
+ var defaultRunnerDependencies2 = {
4429
+ spawnCodex,
4430
+ createStdoutSink,
4431
+ streamCodexEvents
4432
+ };
4433
+ async function run2(prompt, options = {}, dependencies = defaultRunnerDependencies2) {
4434
+ const isRunOptions = (opt) => ("spawnOptions" in opt) || ("onRawEvent" in opt);
4435
+ const spawnOptions = isRunOptions(options) ? options.spawnOptions ?? {} : options;
4436
+ const onRawEvent = isRunOptions(options) ? options.onRawEvent : undefined;
4437
+ const events = dependencies.spawnCodex(prompt, spawnOptions);
4438
+ const sink = dependencies.createStdoutSink();
4439
+ await dependencies.streamCodexEvents(events, sink, onRawEvent);
4440
+ }
4441
+
4442
+ // lib/cli/commands/loop-codex.ts
4443
+ function createCodexDependencies(overrides = {}) {
4444
+ return {
4445
+ ...createDefaultDependencies(),
4446
+ run: run2,
4447
+ ...overrides,
4448
+ agentType: "codex"
4449
+ };
4450
+ }
4451
+ async function loopCodex(dependencies, loopDependencies = createCodexDependencies()) {
4452
+ return loopClaude(dependencies, {
4453
+ ...loopDependencies,
4454
+ agentType: "codex"
4455
+ });
4456
+ }
4457
+
4284
4458
  // lib/cli/commands/new-goal.ts
4285
4459
  function newGoalInstructions(vars) {
4286
4460
  const intro = vars.isClaudeCodeWeb ? "Follow these steps. Use a todo list to track your progress." : "Follow these steps:";
@@ -4611,6 +4785,7 @@ var commandRegistry = {
4611
4785
  "implement task": implementTask,
4612
4786
  "pick task": pickTask,
4613
4787
  "loop claude": loopClaude,
4788
+ "loop codex": loopCodex,
4614
4789
  "pre push": prePush,
4615
4790
  help
4616
4791
  };
@@ -4677,6 +4852,7 @@ function createFileSystem(primitives) {
4677
4852
  mkdir: async (path, options) => {
4678
4853
  await primitives.mkdir(path, options);
4679
4854
  },
4855
+ getFileCreationTime: (path) => primitives.statSync(path).birthtimeMs,
4680
4856
  readdir: (path) => primitives.readdir(path),
4681
4857
  chmod: (path, mode) => primitives.chmod(path, mode)
4682
4858
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {