@joshski/dust 0.1.31 → 0.1.33
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/README.md +1 -1
- package/dist/dust.js +45 -20
- package/dist/workflow-tasks.d.ts +10 -4
- package/dist/workflow-tasks.js +55 -15
- package/package.json +1 -1
- package/templates/agent-greeting.txt +6 -6
- package/templates/templates/agent-greeting.txt +6 -6
package/README.md
CHANGED
package/dist/dust.js
CHANGED
|
@@ -435,7 +435,7 @@ function extractOpeningSentence(content) {
|
|
|
435
435
|
// lib/workflow-tasks.ts
|
|
436
436
|
var IDEA_TRANSITION_PREFIXES = [
|
|
437
437
|
"Refine Idea: ",
|
|
438
|
-
"
|
|
438
|
+
"Decompose Idea: ",
|
|
439
439
|
"Shelve Idea: "
|
|
440
440
|
];
|
|
441
441
|
function titleToFilename(title) {
|
|
@@ -1050,25 +1050,33 @@ async function lintMarkdown(dependencies) {
|
|
|
1050
1050
|
|
|
1051
1051
|
// lib/cli/commands/check.ts
|
|
1052
1052
|
var DEFAULT_CHECK_TIMEOUT_MS = 13000;
|
|
1053
|
+
async function runSingleCheck(check, cwd, runner) {
|
|
1054
|
+
const timeoutMs = check.timeoutMilliseconds ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
1055
|
+
const startTime = Date.now();
|
|
1056
|
+
const result = await runner.run(check.command, cwd, timeoutMs);
|
|
1057
|
+
const durationMs = Date.now() - startTime;
|
|
1058
|
+
return {
|
|
1059
|
+
name: check.name,
|
|
1060
|
+
command: check.command,
|
|
1061
|
+
exitCode: result.exitCode,
|
|
1062
|
+
output: result.output,
|
|
1063
|
+
hints: check.hints,
|
|
1064
|
+
durationMs,
|
|
1065
|
+
timedOut: result.timedOut,
|
|
1066
|
+
timeoutSeconds: timeoutMs / 1000
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1053
1069
|
async function runConfiguredChecks(checks, cwd, runner) {
|
|
1054
|
-
const promises = checks.map(
|
|
1055
|
-
const timeoutMs = check.timeoutMilliseconds ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
1056
|
-
const startTime = Date.now();
|
|
1057
|
-
const result = await runner.run(check.command, cwd, timeoutMs);
|
|
1058
|
-
const durationMs = Date.now() - startTime;
|
|
1059
|
-
return {
|
|
1060
|
-
name: check.name,
|
|
1061
|
-
command: check.command,
|
|
1062
|
-
exitCode: result.exitCode,
|
|
1063
|
-
output: result.output,
|
|
1064
|
-
hints: check.hints,
|
|
1065
|
-
durationMs,
|
|
1066
|
-
timedOut: result.timedOut,
|
|
1067
|
-
timeoutSeconds: timeoutMs / 1000
|
|
1068
|
-
};
|
|
1069
|
-
});
|
|
1070
|
+
const promises = checks.map((check) => runSingleCheck(check, cwd, runner));
|
|
1070
1071
|
return Promise.all(promises);
|
|
1071
1072
|
}
|
|
1073
|
+
async function runConfiguredChecksSerially(checks, cwd, runner) {
|
|
1074
|
+
const results = [];
|
|
1075
|
+
for (const check of checks) {
|
|
1076
|
+
results.push(await runSingleCheck(check, cwd, runner));
|
|
1077
|
+
}
|
|
1078
|
+
return results;
|
|
1079
|
+
}
|
|
1072
1080
|
async function runValidationCheck(dependencies) {
|
|
1073
1081
|
const outputLines = [];
|
|
1074
1082
|
const bufferedContext = {
|
|
@@ -1131,7 +1139,13 @@ function displayResults(results, context) {
|
|
|
1131
1139
|
return failed.length > 0 ? 1 : 0;
|
|
1132
1140
|
}
|
|
1133
1141
|
async function check(dependencies, shellRunner = defaultShellRunner) {
|
|
1134
|
-
const {
|
|
1142
|
+
const {
|
|
1143
|
+
arguments: commandArguments,
|
|
1144
|
+
context,
|
|
1145
|
+
fileSystem,
|
|
1146
|
+
settings
|
|
1147
|
+
} = dependencies;
|
|
1148
|
+
const serial = commandArguments.includes("--serial");
|
|
1135
1149
|
if (!settings.checks || settings.checks.length === 0) {
|
|
1136
1150
|
context.stderr("Error: No checks configured in .dust/config/settings.json");
|
|
1137
1151
|
context.stderr("");
|
|
@@ -1144,9 +1158,20 @@ async function check(dependencies, shellRunner = defaultShellRunner) {
|
|
|
1144
1158
|
context.stderr(" }");
|
|
1145
1159
|
return { exitCode: 1 };
|
|
1146
1160
|
}
|
|
1147
|
-
const checkPromises = [];
|
|
1148
1161
|
const dustPath = `${context.cwd}/.dust`;
|
|
1149
|
-
|
|
1162
|
+
const hasDustDir = fileSystem.exists(dustPath);
|
|
1163
|
+
if (serial) {
|
|
1164
|
+
const results2 = [];
|
|
1165
|
+
if (hasDustDir) {
|
|
1166
|
+
results2.push(await runValidationCheck(dependencies));
|
|
1167
|
+
}
|
|
1168
|
+
const configuredResults = await runConfiguredChecksSerially(settings.checks, context.cwd, shellRunner);
|
|
1169
|
+
results2.push(...configuredResults);
|
|
1170
|
+
const exitCode2 = displayResults(results2, context);
|
|
1171
|
+
return { exitCode: exitCode2 };
|
|
1172
|
+
}
|
|
1173
|
+
const checkPromises = [];
|
|
1174
|
+
if (hasDustDir) {
|
|
1150
1175
|
checkPromises.push(runValidationCheck(dependencies));
|
|
1151
1176
|
}
|
|
1152
1177
|
checkPromises.push(runConfiguredChecks(settings.checks, context.cwd, shellRunner));
|
package/dist/workflow-tasks.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface IdeaInProgress {
|
|
|
5
5
|
taskSlug: string;
|
|
6
6
|
ideaTitle: string;
|
|
7
7
|
}
|
|
8
|
+
export interface ParsedCaptureIdeaTask {
|
|
9
|
+
ideaTitle: string;
|
|
10
|
+
ideaDescription: string;
|
|
11
|
+
}
|
|
8
12
|
export declare function findAllCaptureIdeaTasks(fileSystem: FileSystem, dustPath: string): Promise<IdeaInProgress[]>;
|
|
9
13
|
/**
|
|
10
14
|
* Converts a markdown title to the expected filename using deterministic rules:
|
|
@@ -16,12 +20,13 @@ export declare function findAllCaptureIdeaTasks(fileSystem: FileSystem, dustPath
|
|
|
16
20
|
* 6. Add .md extension
|
|
17
21
|
*/
|
|
18
22
|
export declare function titleToFilename(title: string): string;
|
|
19
|
-
export type WorkflowTaskType = 'refine' | '
|
|
23
|
+
export type WorkflowTaskType = 'refine' | 'decompose-idea' | 'shelve';
|
|
20
24
|
export interface WorkflowTaskMatch {
|
|
21
25
|
type: WorkflowTaskType;
|
|
26
|
+
ideaSlug: string;
|
|
22
27
|
taskSlug: string;
|
|
23
28
|
}
|
|
24
|
-
export declare function
|
|
29
|
+
export declare function findWorkflowTaskForIdea(fileSystem: FileSystem, dustPath: string, ideaSlug: string): Promise<WorkflowTaskMatch | null>;
|
|
25
30
|
export interface CreateIdeaTransitionTaskResult {
|
|
26
31
|
filePath: string;
|
|
27
32
|
}
|
|
@@ -29,12 +34,13 @@ export interface OpenQuestionResponse {
|
|
|
29
34
|
question: string;
|
|
30
35
|
chosenOption: string;
|
|
31
36
|
}
|
|
32
|
-
export interface
|
|
37
|
+
export interface DecomposeIdeaOptions {
|
|
33
38
|
ideaSlug: string;
|
|
34
39
|
description?: string;
|
|
35
40
|
openQuestionResponses?: OpenQuestionResponse[];
|
|
36
41
|
}
|
|
37
42
|
export declare function createRefineIdeaTask(fileSystem: FileSystem, dustPath: string, ideaSlug: string, description?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
38
|
-
export declare function
|
|
43
|
+
export declare function decomposeIdea(fileSystem: FileSystem, dustPath: string, options: DecomposeIdeaOptions): Promise<CreateIdeaTransitionTaskResult>;
|
|
39
44
|
export declare function createShelveIdeaTask(fileSystem: FileSystem, dustPath: string, ideaSlug: string, description?: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
40
45
|
export declare function createCaptureIdeaTask(fileSystem: FileSystem, dustPath: string, title: string, description: string): Promise<CreateIdeaTransitionTaskResult>;
|
|
46
|
+
export declare function parseCaptureIdeaTask(fileSystem: FileSystem, dustPath: string, taskSlug: string): Promise<ParsedCaptureIdeaTask | null>;
|
package/dist/workflow-tasks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// lib/workflow-tasks.ts
|
|
2
2
|
var IDEA_TRANSITION_PREFIXES = [
|
|
3
3
|
"Refine Idea: ",
|
|
4
|
-
"
|
|
4
|
+
"Decompose Idea: ",
|
|
5
5
|
"Shelve Idea: "
|
|
6
6
|
];
|
|
7
7
|
var CAPTURE_IDEA_PREFIX = "Add Idea: ";
|
|
@@ -31,17 +31,17 @@ function titleToFilename(title) {
|
|
|
31
31
|
}
|
|
32
32
|
var WORKFLOW_TASK_TYPES = [
|
|
33
33
|
{ type: "refine", prefix: "Refine Idea: " },
|
|
34
|
-
{ type: "
|
|
34
|
+
{ type: "decompose-idea", prefix: "Decompose Idea: " },
|
|
35
35
|
{ type: "shelve", prefix: "Shelve Idea: " }
|
|
36
36
|
];
|
|
37
|
-
async function
|
|
37
|
+
async function findWorkflowTaskForIdea(fileSystem, dustPath, ideaSlug) {
|
|
38
38
|
const ideaTitle = await readIdeaTitle(fileSystem, dustPath, ideaSlug);
|
|
39
39
|
for (const { type, prefix } of WORKFLOW_TASK_TYPES) {
|
|
40
40
|
const filename = titleToFilename(`${prefix}${ideaTitle}`);
|
|
41
41
|
const filePath = `${dustPath}/tasks/${filename}`;
|
|
42
42
|
if (fileSystem.exists(filePath)) {
|
|
43
43
|
const taskSlug = filename.replace(/\.md$/, "");
|
|
44
|
-
return { type, taskSlug };
|
|
44
|
+
return { type, ideaSlug, taskSlug };
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
return null;
|
|
@@ -111,9 +111,9 @@ async function createRefineIdeaTask(fileSystem, dustPath, ideaSlug, description)
|
|
|
111
111
|
"Idea file is updated with findings"
|
|
112
112
|
], { description });
|
|
113
113
|
}
|
|
114
|
-
async function
|
|
115
|
-
return createIdeaTask(fileSystem, dustPath, "
|
|
116
|
-
"
|
|
114
|
+
async function decomposeIdea(fileSystem, dustPath, options) {
|
|
115
|
+
return createIdeaTask(fileSystem, dustPath, "Decompose Idea: ", options.ideaSlug, (ideaTitle) => `Create one or more well-defined tasks from this idea. Prefer smaller, narrowly scoped tasks -- split the idea into multiple tasks if it covers more than one logical change. Review \`.dust/goals/\` to link relevant goals and \`.dust/facts/\` for design decisions that should inform the task. See [${ideaTitle}](../ideas/${options.ideaSlug}.md).`, [
|
|
116
|
+
"One or more new tasks are created in .dust/tasks/",
|
|
117
117
|
"Task's Goals section links to relevant goals from .dust/goals/",
|
|
118
118
|
"The original idea is deleted or updated to reflect remaining scope"
|
|
119
119
|
], {
|
|
@@ -136,20 +136,60 @@ async function createCaptureIdeaTask(fileSystem, dustPath, title, description) {
|
|
|
136
136
|
const filePath = `${dustPath}/tasks/${filename}`;
|
|
137
137
|
const ideaFilename = titleToFilename(title);
|
|
138
138
|
const ideaPath = `.dust/ideas/${ideaFilename}`;
|
|
139
|
-
const content =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
const content = `# ${taskTitle}
|
|
140
|
+
|
|
141
|
+
Research this idea thoroughly, then create an idea file at \`${ideaPath}\`. Read the codebase for relevant context, flesh out the description, and identify any ambiguity. Where aspects are unclear or could go multiple ways, add open questions to the idea file. Review \`.dust/goals/\` and \`.dust/facts/\` for relevant context.
|
|
142
|
+
|
|
143
|
+
## Idea Description
|
|
144
|
+
|
|
145
|
+
${description}
|
|
146
|
+
|
|
147
|
+
## Goals
|
|
148
|
+
|
|
149
|
+
(none)
|
|
150
|
+
|
|
151
|
+
## Blocked By
|
|
152
|
+
|
|
153
|
+
(none)
|
|
154
|
+
|
|
155
|
+
## Definition of Done
|
|
156
|
+
|
|
157
|
+
- [ ] Idea file exists at ${ideaPath}
|
|
158
|
+
- [ ] Idea file has an H1 title matching "${title}"
|
|
159
|
+
- [ ] Idea includes relevant context from codebase exploration
|
|
160
|
+
- [ ] Open questions are added for any ambiguous or underspecified aspects
|
|
161
|
+
`;
|
|
145
162
|
await fileSystem.writeFile(filePath, content);
|
|
146
163
|
return { filePath };
|
|
147
164
|
}
|
|
165
|
+
async function parseCaptureIdeaTask(fileSystem, dustPath, taskSlug) {
|
|
166
|
+
const filePath = `${dustPath}/tasks/${taskSlug}.md`;
|
|
167
|
+
if (!fileSystem.exists(filePath)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const content = await fileSystem.readFile(filePath);
|
|
171
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
172
|
+
if (!titleMatch) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const title = titleMatch[1].trim();
|
|
176
|
+
if (!title.startsWith(CAPTURE_IDEA_PREFIX)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const ideaTitle = title.slice(CAPTURE_IDEA_PREFIX.length);
|
|
180
|
+
const descriptionMatch = content.match(/^## Idea Description\n\n([\s\S]*?)\n\n## /m);
|
|
181
|
+
if (!descriptionMatch) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const ideaDescription = descriptionMatch[1];
|
|
185
|
+
return { ideaTitle, ideaDescription };
|
|
186
|
+
}
|
|
148
187
|
export {
|
|
149
188
|
titleToFilename,
|
|
150
|
-
|
|
189
|
+
parseCaptureIdeaTask,
|
|
190
|
+
findWorkflowTaskForIdea,
|
|
151
191
|
findAllCaptureIdeaTasks,
|
|
152
|
-
|
|
192
|
+
decomposeIdea,
|
|
153
193
|
createShelveIdeaTask,
|
|
154
194
|
createRefineIdeaTask,
|
|
155
195
|
createCaptureIdeaTask,
|
package/package.json
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
🤖 Hello {{agentName}}, welcome to dust!
|
|
2
|
-
{{#if agentInstructions}}
|
|
3
|
-
|
|
4
|
-
## Project Instructions
|
|
5
|
-
|
|
6
|
-
{{agentInstructions}}
|
|
7
|
-
{{/if}}
|
|
8
2
|
|
|
9
3
|
CRITICAL: You MUST run exactly ONE of the commands below before doing anything else.
|
|
10
4
|
|
|
@@ -29,3 +23,9 @@ Determine the user's intent and run the matching command NOW:
|
|
|
29
23
|
If none of the above clearly apply, run this to see all available commands.
|
|
30
24
|
|
|
31
25
|
Do NOT proceed without running one of these commands.
|
|
26
|
+
{{#if agentInstructions}}
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
{{agentInstructions}}
|
|
31
|
+
{{/if}}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
🤖 Hello {{agentName}}, welcome to dust!
|
|
2
|
-
{{#if agentInstructions}}
|
|
3
|
-
|
|
4
|
-
## Project Instructions
|
|
5
|
-
|
|
6
|
-
{{agentInstructions}}
|
|
7
|
-
{{/if}}
|
|
8
2
|
|
|
9
3
|
CRITICAL: You MUST run exactly ONE of the commands below before doing anything else.
|
|
10
4
|
|
|
@@ -29,3 +23,9 @@ Determine the user's intent and run the matching command NOW:
|
|
|
29
23
|
If none of the above clearly apply, run this to see all available commands.
|
|
30
24
|
|
|
31
25
|
Do NOT proceed without running one of these commands.
|
|
26
|
+
{{#if agentInstructions}}
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
{{agentInstructions}}
|
|
31
|
+
{{/if}}
|