@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.
- package/dist/agent-events.d.ts +0 -4
- package/dist/cli/types.d.ts +1 -0
- package/dist/dust.js +187 -11
- package/package.json +1 -1
package/dist/agent-events.d.ts
CHANGED
|
@@ -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;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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({
|
|
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
|
};
|