@joshski/dust 0.1.85 → 0.1.87
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/artifacts.js +21 -5
- package/dist/bucket/repository-loop.d.ts +11 -0
- package/dist/bucket/repository.d.ts +3 -0
- package/dist/bucket/server-messages.d.ts +38 -0
- package/dist/bucket/tool-prompt.d.ts +9 -0
- package/dist/claude/spawn-claude-code.d.ts +5 -1
- package/dist/claude/types.d.ts +4 -2
- package/dist/cli/commands/loop.d.ts +2 -0
- package/dist/docker/docker-agent.d.ts +35 -0
- package/dist/dust.js +920 -342
- package/dist/proxy/claude-api-proxy.d.ts +59 -0
- package/dist/proxy/git-credential-proxy.d.ts +68 -0
- package/package.json +1 -1
package/dist/artifacts.js
CHANGED
|
@@ -276,6 +276,18 @@ var EXPEDITE_IDEA_PREFIX = "Expedite Idea: ";
|
|
|
276
276
|
function titleToFilename(title) {
|
|
277
277
|
return `${title.toLowerCase().replace(/\./g, "-").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
278
278
|
}
|
|
279
|
+
var WORKFLOW_HINT_PATHS = {
|
|
280
|
+
refine: "config/workflow-hints/refine.md",
|
|
281
|
+
"decompose-idea": "config/workflow-hints/decompose-idea.md",
|
|
282
|
+
shelve: "config/workflow-hints/shelve.md"
|
|
283
|
+
};
|
|
284
|
+
async function readWorkflowHint(fileSystem, dustPath, workflowType) {
|
|
285
|
+
const hintPath = `${dustPath}/${WORKFLOW_HINT_PATHS[workflowType]}`;
|
|
286
|
+
if (!fileSystem.exists(hintPath)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
return fileSystem.readFile(hintPath);
|
|
290
|
+
}
|
|
279
291
|
var WORKFLOW_SECTION_HEADINGS = [
|
|
280
292
|
{ type: "refine", heading: "Refines Idea" },
|
|
281
293
|
{ type: "decompose-idea", heading: "Decomposes Idea" },
|
|
@@ -418,12 +430,16 @@ ${definitionOfDone.map((item) => `- [ ] ${item}`).join(`
|
|
|
418
430
|
`)}
|
|
419
431
|
`;
|
|
420
432
|
}
|
|
421
|
-
async function createIdeaTransitionTask(fileSystem, dustPath, prefix, ideaSlug, openingSentenceTemplate, definitionOfDone, ideaSectionHeading, taskOptions) {
|
|
433
|
+
async function createIdeaTransitionTask(fileSystem, dustPath, workflowType, prefix, ideaSlug, openingSentenceTemplate, definitionOfDone, ideaSectionHeading, taskOptions) {
|
|
422
434
|
const ideaTitle = await readIdeaTitle(fileSystem, dustPath, ideaSlug);
|
|
423
435
|
const taskTitle = `${prefix}${ideaTitle}`;
|
|
424
436
|
const filename = titleToFilename(taskTitle);
|
|
425
437
|
const filePath = `${dustPath}/tasks/${filename}`;
|
|
426
|
-
const
|
|
438
|
+
const baseOpeningSentence = openingSentenceTemplate(ideaTitle);
|
|
439
|
+
const hint = await readWorkflowHint(fileSystem, dustPath, workflowType);
|
|
440
|
+
const openingSentence = hint ? `${baseOpeningSentence}
|
|
441
|
+
|
|
442
|
+
${hint}` : baseOpeningSentence;
|
|
427
443
|
const ideaSection = { heading: ideaSectionHeading, ideaTitle, ideaSlug };
|
|
428
444
|
const content = renderTask(taskTitle, openingSentence, definitionOfDone, ideaSection, {
|
|
429
445
|
description: taskOptions?.description,
|
|
@@ -434,7 +450,7 @@ async function createIdeaTransitionTask(fileSystem, dustPath, prefix, ideaSlug,
|
|
|
434
450
|
}
|
|
435
451
|
async function createRefineIdeaTask(fileSystem, dustPath, ideaSlug, description, dustCommand) {
|
|
436
452
|
const cmd = dustCommand ?? "dust";
|
|
437
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "Refine Idea: ", ideaSlug, (ideaTitle) => `Thoroughly research this idea and refine it into a well-defined proposal. Read the idea file, explore the codebase for relevant context, and identify any ambiguity. Where aspects are unclear or could go multiple ways, add open questions to the idea file. Run \`${cmd} principles\` for alignment and \`${cmd} facts\` for relevant design decisions. See [${ideaTitle}](../ideas/${ideaSlug}.md). If you add open questions, use \`## Open Questions\` with \`### Question?\` headings and one or more \`#### Option\` headings beneath each question, and only add questions that are meaningful decisions worth asking.`, [
|
|
453
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "refine", "Refine Idea: ", ideaSlug, (ideaTitle) => `Thoroughly research this idea and refine it into a well-defined proposal. Read the idea file, explore the codebase for relevant context, and identify any ambiguity. Where aspects are unclear or could go multiple ways, add open questions to the idea file. Run \`${cmd} principles\` for alignment and \`${cmd} facts\` for relevant design decisions. See [${ideaTitle}](../ideas/${ideaSlug}.md). If you add open questions, use \`## Open Questions\` with \`### Question?\` headings and one or more \`#### Option\` headings beneath each question, and only add questions that are meaningful decisions worth asking.`, [
|
|
438
454
|
"Idea is thoroughly researched with relevant codebase context",
|
|
439
455
|
"Open questions are added for any ambiguous or underspecified aspects",
|
|
440
456
|
"Open questions follow the required heading format and focus on high-value decisions",
|
|
@@ -443,7 +459,7 @@ async function createRefineIdeaTask(fileSystem, dustPath, ideaSlug, description,
|
|
|
443
459
|
}
|
|
444
460
|
async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
445
461
|
const cmd = dustCommand ?? "dust";
|
|
446
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "Decompose Idea: ", options.ideaSlug, (ideaTitle) => `Create one or more well-defined tasks from this idea. Prefer smaller, narrowly scoped tasks that each deliver a thin but complete vertical slice of working software -- a path through the system that can be tested end-to-end -- rather than component-oriented tasks (like "add schema" or "build endpoint") that only work once all tasks are done. Split the idea into multiple tasks if it covers more than one logical change. Run \`${cmd} principles\` to link relevant principles and \`${cmd} facts\` for design decisions that should inform the task. See [${ideaTitle}](../ideas/${options.ideaSlug}.md).`, [
|
|
462
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "decompose-idea", "Decompose Idea: ", options.ideaSlug, (ideaTitle) => `Create one or more well-defined tasks from this idea. Prefer smaller, narrowly scoped tasks that each deliver a thin but complete vertical slice of working software -- a path through the system that can be tested end-to-end -- rather than component-oriented tasks (like "add schema" or "build endpoint") that only work once all tasks are done. Split the idea into multiple tasks if it covers more than one logical change. Run \`${cmd} principles\` to link relevant principles and \`${cmd} facts\` for design decisions that should inform the task. See [${ideaTitle}](../ideas/${options.ideaSlug}.md).`, [
|
|
447
463
|
"One or more new tasks are created in .dust/tasks/",
|
|
448
464
|
"Task's Principles section links to relevant principles from .dust/principles/",
|
|
449
465
|
"The original idea is deleted or updated to reflect remaining scope"
|
|
@@ -453,7 +469,7 @@ async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
|
453
469
|
});
|
|
454
470
|
}
|
|
455
471
|
async function createShelveIdeaTask(fileSystem, dustPath, ideaSlug, description, _dustCommand) {
|
|
456
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "Shelve Idea: ", ideaSlug, (ideaTitle) => `Archive this idea and remove it from the active backlog. See [${ideaTitle}](../ideas/${ideaSlug}.md).`, ["Idea file is deleted", "Rationale is recorded in the commit message"], "Shelves Idea", { description });
|
|
472
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "shelve", "Shelve Idea: ", ideaSlug, (ideaTitle) => `Archive this idea and remove it from the active backlog. See [${ideaTitle}](../ideas/${ideaSlug}.md).`, ["Idea file is deleted", "Rationale is recorded in the commit message"], "Shelves Idea", { description });
|
|
457
473
|
}
|
|
458
474
|
async function createIdeaTask(fileSystem, dustPath, options) {
|
|
459
475
|
const { title, description, expedite, dustCommand } = options;
|
|
@@ -8,6 +8,7 @@ import type { AgentSessionEvent, EventMessage } from '../agent-events';
|
|
|
8
8
|
import { type run as claudeRun, type RunnerDependencies } from '../claude/run';
|
|
9
9
|
import type { OutputSink } from '../claude/types';
|
|
10
10
|
import { type LoopEmitFn, type SendAgentEventFn } from '../cli/commands/loop';
|
|
11
|
+
import { type RunnerDependencies as CodexRunnerDependencies, run as codexRun } from '../codex/run';
|
|
11
12
|
import type { SendEventFn } from './events';
|
|
12
13
|
import { type LogBuffer } from './log-buffer';
|
|
13
14
|
import type { RepositoryDependencies, RepositoryState } from './repository';
|
|
@@ -53,10 +54,20 @@ export interface LoopState {
|
|
|
53
54
|
* Create an OutputSink that buffers stdout and logs complete lines.
|
|
54
55
|
*/
|
|
55
56
|
export declare function createBufferStdoutSink(loopState: LoopState, logBuffer: LogBuffer): OutputSink;
|
|
57
|
+
/**
|
|
58
|
+
* Create a factory function that produces OutputSinks for agent runners.
|
|
59
|
+
* This factory captures loopState and logBuffer, returning a function
|
|
60
|
+
* that can be passed to RunnerDependencies.createStdoutSink.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createStdoutSinkFactory(loopState: LoopState, logBuffer: LogBuffer): () => OutputSink;
|
|
56
63
|
/**
|
|
57
64
|
* Create a run function that redirects Claude output to a log buffer.
|
|
58
65
|
*/
|
|
59
66
|
export declare function createBufferRun(run: RepositoryDependencies['run'], bufferSinkDeps: RunnerDependencies): typeof claudeRun;
|
|
67
|
+
/**
|
|
68
|
+
* Create a run function that redirects Codex output to a log buffer.
|
|
69
|
+
*/
|
|
70
|
+
export declare function createCodexBufferRun(run: typeof codexRun, codexBufferSinkDeps: CodexRunnerDependencies): typeof claudeRun;
|
|
60
71
|
/** No-op postEvent for LoopDependencies. */
|
|
61
72
|
export declare function noOpPostEvent(): Promise<void>;
|
|
62
73
|
/**
|
|
@@ -10,6 +10,7 @@ import type { CommandDependencies, FileSystem } from '../cli/types';
|
|
|
10
10
|
import type { DockerDependencies } from '../docker/docker-agent';
|
|
11
11
|
import { type BucketEmitFn, type SendEventFn } from './events';
|
|
12
12
|
import { type LogBuffer } from './log-buffer';
|
|
13
|
+
import type { ToolDefinition } from './server-messages';
|
|
13
14
|
export { cloneRepository, getRepoPath, removeRepository, } from './repository-git';
|
|
14
15
|
export { runRepositoryLoop } from './repository-loop';
|
|
15
16
|
export interface Repository {
|
|
@@ -50,6 +51,8 @@ export interface RepositoryDependencies {
|
|
|
50
51
|
getReposDir: () => string;
|
|
51
52
|
/** Optional overrides for Docker dependency functions (for testing) */
|
|
52
53
|
dockerDeps?: Partial<DockerDependencies>;
|
|
54
|
+
/** Function to get current tool definitions */
|
|
55
|
+
getTools?: () => ToolDefinition[];
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
55
58
|
* Start (or restart) the per-repository loop and keep loopPromise state accurate.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed server-to-client WebSocket messages for the bucket protocol.
|
|
3
|
+
*/
|
|
4
|
+
import type { Repository } from './repository';
|
|
5
|
+
export interface RepositoryListMessage {
|
|
6
|
+
type: 'repository-list';
|
|
7
|
+
repositories: RepositoryListItem[];
|
|
8
|
+
}
|
|
9
|
+
export interface RepositoryListItem extends Repository {
|
|
10
|
+
hasTask: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface TaskAvailableMessage {
|
|
13
|
+
type: 'task-available';
|
|
14
|
+
repository: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ToolParameter {
|
|
17
|
+
name: string;
|
|
18
|
+
type: 'string' | 'file' | 'number' | 'boolean';
|
|
19
|
+
required: boolean;
|
|
20
|
+
description: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ToolDefinition {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
endpoint: string;
|
|
26
|
+
method: 'GET' | 'POST';
|
|
27
|
+
parameters: ToolParameter[];
|
|
28
|
+
}
|
|
29
|
+
export interface ToolDefinitionsMessage {
|
|
30
|
+
type: 'tool-definitions';
|
|
31
|
+
tools: ToolDefinition[];
|
|
32
|
+
}
|
|
33
|
+
export type ServerMessage = RepositoryListMessage | TaskAvailableMessage | ToolDefinitionsMessage;
|
|
34
|
+
/**
|
|
35
|
+
* Parse and validate a server message from raw JSON data.
|
|
36
|
+
* Returns the typed message if valid, or null if invalid.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseServerMessage(data: unknown): ServerMessage | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats tool definitions for injection into agent prompts.
|
|
3
|
+
*/
|
|
4
|
+
import type { ToolDefinition } from './server-messages';
|
|
5
|
+
/**
|
|
6
|
+
* Format tool definitions into a markdown section for agent prompts.
|
|
7
|
+
* Returns an empty string if no tools are defined.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatToolsSection(tools: ToolDefinition[]): string;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
2
2
|
import { createInterface as nodeCreateInterface } from 'node:readline';
|
|
3
|
-
import type { RawEvent, SpawnOptions } from './types';
|
|
3
|
+
import type { DockerSpawnConfig, RawEvent, SpawnOptions } from './types';
|
|
4
4
|
export interface EventSourceDependencies {
|
|
5
5
|
spawn: typeof nodeSpawn;
|
|
6
6
|
createInterface: typeof nodeCreateInterface;
|
|
7
7
|
}
|
|
8
8
|
export declare const defaultDependencies: EventSourceDependencies;
|
|
9
|
+
/**
|
|
10
|
+
* Build docker run arguments for spawning claude in a container.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildDockerRunArguments(docker: DockerSpawnConfig, claudeArguments: string[], env: Record<string, string>): string[];
|
|
9
13
|
export declare function spawnClaudeCode(prompt: string, options?: SpawnOptions, dependencies?: EventSourceDependencies): AsyncGenerator<RawEvent>;
|
package/dist/claude/types.d.ts
CHANGED
|
@@ -54,8 +54,10 @@ export interface DockerSpawnConfig {
|
|
|
54
54
|
repoPath: string;
|
|
55
55
|
/** Home directory for credential mounts */
|
|
56
56
|
homeDir: string;
|
|
57
|
-
/**
|
|
58
|
-
|
|
57
|
+
/** Git credential proxy URL (e.g., http://host.docker.internal:3001) */
|
|
58
|
+
gitProxyUrl?: string;
|
|
59
|
+
/** Claude API proxy URL (e.g., http://host.docker.internal:3002) */
|
|
60
|
+
claudeApiProxyUrl?: string;
|
|
59
61
|
}
|
|
60
62
|
export interface SpawnOptions {
|
|
61
63
|
cwd?: string;
|
|
@@ -107,6 +107,8 @@ interface IterationOptions {
|
|
|
107
107
|
repositoryId?: string;
|
|
108
108
|
/** Docker spawn config when running in Docker mode */
|
|
109
109
|
docker?: DockerSpawnConfig;
|
|
110
|
+
/** Pre-formatted tools section to inject into the prompt */
|
|
111
|
+
toolsSection?: string;
|
|
110
112
|
}
|
|
111
113
|
export declare function runOneIteration(dependencies: CommandDependencies, loopDependencies: LoopDependencies, onLoopEvent: LoopEmitFn, onAgentEvent?: SendAgentEventFn, options?: IterationOptions): Promise<IterationResult>;
|
|
112
114
|
export declare function parseMaxIterations(commandArguments: string[]): number;
|
|
@@ -41,4 +41,39 @@ export declare function buildDockerImage(config: DockerConfig, dependencies: Doc
|
|
|
41
41
|
* Check if a Dockerfile exists at .dust/Dockerfile in the repository.
|
|
42
42
|
*/
|
|
43
43
|
export declare function hasDockerfile(repoPath: string, dependencies: DockerDependencies): boolean;
|
|
44
|
+
type DockerPrepareEvent = {
|
|
45
|
+
type: 'loop.docker_detected';
|
|
46
|
+
imageTag: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'loop.docker_building';
|
|
49
|
+
imageTag: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'loop.docker_built';
|
|
52
|
+
imageTag: string;
|
|
53
|
+
} | {
|
|
54
|
+
type: 'loop.docker_error';
|
|
55
|
+
error: string;
|
|
56
|
+
};
|
|
57
|
+
interface DockerSpawnConfig {
|
|
58
|
+
imageTag: string;
|
|
59
|
+
repoPath: string;
|
|
60
|
+
homeDir: string;
|
|
61
|
+
}
|
|
62
|
+
type PrepareDockerConfigResult = {
|
|
63
|
+
config: DockerSpawnConfig;
|
|
64
|
+
} | {
|
|
65
|
+
error: string;
|
|
66
|
+
} | Record<string, never>;
|
|
67
|
+
/**
|
|
68
|
+
* Prepare Docker configuration for agent execution.
|
|
69
|
+
*
|
|
70
|
+
* Checks for a .dust/Dockerfile, verifies Docker availability, builds the image,
|
|
71
|
+
* and returns the spawn configuration. Emits events throughout the process.
|
|
72
|
+
*
|
|
73
|
+
* Returns:
|
|
74
|
+
* - `{ config: DockerSpawnConfig }` on success
|
|
75
|
+
* - `{ error: string }` on failure (Docker not available or build failed)
|
|
76
|
+
* - `{}` if no Dockerfile exists
|
|
77
|
+
*/
|
|
78
|
+
export declare function prepareDockerConfig(repoPath: string, dependencies: DockerDependencies, onEvent: (event: DockerPrepareEvent) => void): Promise<PrepareDockerConfigResult>;
|
|
44
79
|
export {};
|