@joshski/dust 0.1.103 → 0.1.105
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/agents/detection.d.ts +1 -1
- package/dist/agents.js +1 -1
- package/dist/artifacts/facts.d.ts +2 -2
- package/dist/artifacts/ideas.d.ts +2 -2
- package/dist/artifacts/index.d.ts +3 -3
- package/dist/artifacts/principles.d.ts +2 -2
- package/dist/artifacts/tasks.d.ts +2 -2
- package/dist/artifacts/workflow-tasks.d.ts +8 -2
- package/dist/artifacts.js +128 -36
- package/dist/audits/index.d.ts +1 -0
- package/dist/audits.js +24 -1
- package/dist/bucket/repository-loop.d.ts +4 -4
- package/dist/bucket/repository.d.ts +10 -0
- package/dist/claude/run.d.ts +3 -9
- package/dist/claude/spawn-claude-code.d.ts +1 -1
- package/dist/claude/types.d.ts +9 -0
- package/dist/cli/commands/focus.d.ts +2 -1
- package/dist/cli/shared/agent-shared.d.ts +4 -4
- package/dist/cli/types.d.ts +1 -1
- package/dist/codex/run.d.ts +3 -9
- package/dist/codex/spawn-codex.d.ts +1 -1
- package/dist/config/settings.d.ts +5 -5
- package/dist/core-principles.js +5 -16
- package/dist/docker/docker-agent.d.ts +0 -4
- package/dist/dust.js +374 -426
- package/dist/filesystem/types.d.ts +5 -1
- package/dist/lint/validators/content-validator.d.ts +1 -0
- package/dist/lint/validators/directory-validator.d.ts +3 -3
- package/dist/lint/validators/idea-validator.d.ts +3 -3
- package/dist/lint/validators/link-validator.d.ts +2 -2
- package/dist/loop/iteration.d.ts +2 -3
- package/dist/patch/index.d.ts +13 -0
- package/dist/patch/task.d.ts +2 -1
- package/dist/patch.js +64 -133
- package/dist/proxy/helper-token.d.ts +2 -2
- package/dist/types.d.ts +1 -1
- package/dist/validation.js +39 -115
- package/package.json +2 -2
package/dist/agents.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileReader } from '../filesystem/types';
|
|
2
2
|
export interface Fact {
|
|
3
3
|
slug: string;
|
|
4
4
|
title: string;
|
|
@@ -7,4 +7,4 @@ export interface Fact {
|
|
|
7
7
|
/**
|
|
8
8
|
* Parses a fact markdown file into a structured Fact object.
|
|
9
9
|
*/
|
|
10
|
-
export declare function parseFact(fileSystem:
|
|
10
|
+
export declare function parseFact(fileSystem: FileReader, dustPath: string, slug: string): Promise<Fact>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileReader } from '../filesystem/types';
|
|
2
2
|
export interface IdeaOption {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
@@ -27,7 +27,7 @@ export declare function parseOpenQuestions(content: string): IdeaOpenQuestion[];
|
|
|
27
27
|
/**
|
|
28
28
|
* Parses an idea markdown file into a structured Idea object.
|
|
29
29
|
*/
|
|
30
|
-
export declare function parseIdea(fileSystem:
|
|
30
|
+
export declare function parseIdea(fileSystem: FileReader, dustPath: string, slug: string): Promise<Idea>;
|
|
31
31
|
/**
|
|
32
32
|
* Parses idea markdown into a structured object that can be bound to a UI
|
|
33
33
|
* and serialized back to markdown.
|
|
@@ -5,11 +5,11 @@ import { extractTitle } from '../markdown/markdown-utilities';
|
|
|
5
5
|
import { type Principle } from './principles';
|
|
6
6
|
import { type Task } from './tasks';
|
|
7
7
|
import { type ParsedArtifact, type ParsedMarkdownLink, type ParsedSection, parseArtifact } from './parsed-artifact';
|
|
8
|
-
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, parseResolvedQuestions, type
|
|
9
|
-
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedArtifact, ParsedCaptureIdeaTask, ParsedIdeaContent, ParsedMarkdownLink, ParsedSection, Principle, Task,
|
|
8
|
+
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, parseResolvedQuestions, type TaskType, type WorkflowTaskMatch } from './workflow-tasks';
|
|
9
|
+
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedArtifact, ParsedCaptureIdeaTask, ParsedIdeaContent, ParsedMarkdownLink, ParsedSection, Principle, Task, TaskType, WorkflowTaskMatch, };
|
|
10
10
|
export interface TaskGraphNode {
|
|
11
11
|
task: Task;
|
|
12
|
-
workflowType:
|
|
12
|
+
workflowType: TaskType | null;
|
|
13
13
|
}
|
|
14
14
|
export interface TaskGraph {
|
|
15
15
|
nodes: TaskGraphNode[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileReader } from '../filesystem/types';
|
|
2
2
|
export interface Principle {
|
|
3
3
|
slug: string;
|
|
4
4
|
title: string;
|
|
@@ -9,4 +9,4 @@ export interface Principle {
|
|
|
9
9
|
/**
|
|
10
10
|
* Parses a principle markdown file into a structured Principle object.
|
|
11
11
|
*/
|
|
12
|
-
export declare function parsePrinciple(fileSystem:
|
|
12
|
+
export declare function parsePrinciple(fileSystem: FileReader, dustPath: string, slug: string): Promise<Principle>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileReader } from '../filesystem/types';
|
|
2
2
|
export interface Task {
|
|
3
3
|
slug: string;
|
|
4
4
|
title: string;
|
|
@@ -10,4 +10,4 @@ export interface Task {
|
|
|
10
10
|
/**
|
|
11
11
|
* Parses a task markdown file into a structured Task object.
|
|
12
12
|
*/
|
|
13
|
-
export declare function parseTask(fileSystem:
|
|
13
|
+
export declare function parseTask(fileSystem: FileReader, dustPath: string, slug: string): Promise<Task>;
|
|
@@ -21,9 +21,15 @@ export interface ParsedCaptureIdeaTask {
|
|
|
21
21
|
* 6. Add .md extension
|
|
22
22
|
*/
|
|
23
23
|
export declare function titleToFilename(title: string): string;
|
|
24
|
-
export
|
|
24
|
+
export declare const VALID_TASK_TYPES: readonly ["implement", "capture", "refine", "decompose", "shelve"];
|
|
25
|
+
export type TaskType = (typeof VALID_TASK_TYPES)[number];
|
|
26
|
+
/**
|
|
27
|
+
* Extracts and validates the task type from the ## Task Type section.
|
|
28
|
+
* Returns the task type if found and valid, null otherwise.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseTaskType(content: string): TaskType | null;
|
|
25
31
|
export interface WorkflowTaskMatch {
|
|
26
|
-
type:
|
|
32
|
+
type: TaskType;
|
|
27
33
|
ideaSlug: string;
|
|
28
34
|
taskSlug: string;
|
|
29
35
|
resolvedQuestions: OpenQuestionResponse[];
|
package/dist/artifacts.js
CHANGED
|
@@ -522,25 +522,59 @@ var EXPEDITE_IDEA_PREFIX = "Expedite Idea: ";
|
|
|
522
522
|
function titleToFilename(title) {
|
|
523
523
|
return `${title.toLowerCase().replace(/\./g, "-").replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")}.md`;
|
|
524
524
|
}
|
|
525
|
+
var VALID_TASK_TYPES = [
|
|
526
|
+
"implement",
|
|
527
|
+
"capture",
|
|
528
|
+
"refine",
|
|
529
|
+
"decompose",
|
|
530
|
+
"shelve"
|
|
531
|
+
];
|
|
532
|
+
function parseTaskType(content) {
|
|
533
|
+
const lines = content.split(`
|
|
534
|
+
`);
|
|
535
|
+
let inSection = false;
|
|
536
|
+
let inCodeFence = false;
|
|
537
|
+
for (const line of lines) {
|
|
538
|
+
if (line.startsWith("```")) {
|
|
539
|
+
inCodeFence = !inCodeFence;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (inCodeFence)
|
|
543
|
+
continue;
|
|
544
|
+
if (line.startsWith("## ")) {
|
|
545
|
+
inSection = line.trimEnd() === "## Task Type";
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
if (!inSection)
|
|
549
|
+
continue;
|
|
550
|
+
if (line.startsWith("# "))
|
|
551
|
+
break;
|
|
552
|
+
const trimmed = line.trim();
|
|
553
|
+
if (trimmed && VALID_TASK_TYPES.includes(trimmed)) {
|
|
554
|
+
return trimmed;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
525
559
|
var WORKFLOW_HINT_PATHS = {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
560
|
+
refine: "config/hints/refine-idea.md",
|
|
561
|
+
decompose: "config/hints/decompose-idea.md",
|
|
562
|
+
shelve: "config/hints/shelve-idea.md",
|
|
563
|
+
capture: "config/hints/add-idea.md",
|
|
564
|
+
implement: "config/hints/expedite-idea.md"
|
|
531
565
|
};
|
|
532
|
-
async function readWorkflowHint(fileSystem, dustPath,
|
|
533
|
-
const hintPath = `${dustPath}/${WORKFLOW_HINT_PATHS[
|
|
566
|
+
async function readWorkflowHint(fileSystem, dustPath, taskType) {
|
|
567
|
+
const hintPath = `${dustPath}/${WORKFLOW_HINT_PATHS[taskType]}`;
|
|
534
568
|
if (!fileSystem.exists(hintPath)) {
|
|
535
569
|
return null;
|
|
536
570
|
}
|
|
537
571
|
return fileSystem.readFile(hintPath);
|
|
538
572
|
}
|
|
539
573
|
var WORKFLOW_SECTION_HEADINGS = [
|
|
540
|
-
{ type: "refine
|
|
541
|
-
{ type: "decompose
|
|
542
|
-
{ type: "shelve
|
|
543
|
-
{ type: "
|
|
574
|
+
{ type: "refine", heading: "Refines Idea" },
|
|
575
|
+
{ type: "decompose", heading: "Decomposes Idea" },
|
|
576
|
+
{ type: "shelve", heading: "Shelves Idea" },
|
|
577
|
+
{ type: "implement", heading: "Expedites Idea" }
|
|
544
578
|
];
|
|
545
579
|
function extractIdeaSlugFromSection(content, sectionHeading) {
|
|
546
580
|
const lines = content.split(`
|
|
@@ -588,16 +622,32 @@ async function findAllWorkflowTasks(fileSystem, dustPath) {
|
|
|
588
622
|
continue;
|
|
589
623
|
const title = titleMatch[1].trim();
|
|
590
624
|
const taskSlug = file.replace(/\.md$/, "");
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
625
|
+
const taskType = parseTaskType(content);
|
|
626
|
+
if (taskType === "capture" || taskType === "implement") {
|
|
627
|
+
let ideaTitle = null;
|
|
628
|
+
if (title.startsWith(CAPTURE_IDEA_PREFIX)) {
|
|
629
|
+
ideaTitle = title.slice(CAPTURE_IDEA_PREFIX.length);
|
|
630
|
+
} else if (title.startsWith(EXPEDITE_IDEA_PREFIX)) {
|
|
631
|
+
ideaTitle = title.slice(EXPEDITE_IDEA_PREFIX.length);
|
|
632
|
+
}
|
|
633
|
+
if (ideaTitle) {
|
|
634
|
+
captureIdeaTasks.push({
|
|
635
|
+
taskSlug,
|
|
636
|
+
ideaTitle
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
} else if (!taskType) {
|
|
640
|
+
if (title.startsWith(CAPTURE_IDEA_PREFIX)) {
|
|
641
|
+
captureIdeaTasks.push({
|
|
642
|
+
taskSlug,
|
|
643
|
+
ideaTitle: title.slice(CAPTURE_IDEA_PREFIX.length)
|
|
644
|
+
});
|
|
645
|
+
} else if (title.startsWith(EXPEDITE_IDEA_PREFIX)) {
|
|
646
|
+
captureIdeaTasks.push({
|
|
647
|
+
taskSlug,
|
|
648
|
+
ideaTitle: title.slice(EXPEDITE_IDEA_PREFIX.length)
|
|
649
|
+
});
|
|
650
|
+
}
|
|
601
651
|
}
|
|
602
652
|
for (const { type, heading } of WORKFLOW_SECTION_HEADINGS) {
|
|
603
653
|
const linkedSlug = extractIdeaSlugFromSection(content, heading);
|
|
@@ -625,12 +675,23 @@ async function findWorkflowTaskForIdea(fileSystem, dustPath, ideaSlug) {
|
|
|
625
675
|
const files = await fileSystem.readdir(tasksPath);
|
|
626
676
|
for (const file of files.filter((f) => f.endsWith(".md")).toSorted()) {
|
|
627
677
|
const content = await fileSystem.readFile(`${tasksPath}/${file}`);
|
|
628
|
-
|
|
678
|
+
const taskSlug = file.replace(/\.md$/, "");
|
|
679
|
+
const match = findWorkflowMatch(content, ideaSlug, taskSlug);
|
|
680
|
+
if (match) {
|
|
681
|
+
return match;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
function findWorkflowMatch(content, ideaSlug, taskSlug) {
|
|
687
|
+
const taskType = parseTaskType(content);
|
|
688
|
+
if (taskType) {
|
|
689
|
+
const heading = WORKFLOW_SECTION_HEADINGS.find((h) => h.type === taskType)?.heading;
|
|
690
|
+
if (heading) {
|
|
629
691
|
const linkedSlug = extractIdeaSlugFromSection(content, heading);
|
|
630
692
|
if (linkedSlug === ideaSlug) {
|
|
631
|
-
const taskSlug = file.replace(/\.md$/, "");
|
|
632
693
|
return {
|
|
633
|
-
type,
|
|
694
|
+
type: taskType,
|
|
634
695
|
ideaSlug,
|
|
635
696
|
taskSlug,
|
|
636
697
|
resolvedQuestions: parseResolvedQuestions(content)
|
|
@@ -638,6 +699,17 @@ async function findWorkflowTaskForIdea(fileSystem, dustPath, ideaSlug) {
|
|
|
638
699
|
}
|
|
639
700
|
}
|
|
640
701
|
}
|
|
702
|
+
for (const { type, heading } of WORKFLOW_SECTION_HEADINGS) {
|
|
703
|
+
const linkedSlug = extractIdeaSlugFromSection(content, heading);
|
|
704
|
+
if (linkedSlug === ideaSlug) {
|
|
705
|
+
return {
|
|
706
|
+
type,
|
|
707
|
+
ideaSlug,
|
|
708
|
+
taskSlug,
|
|
709
|
+
resolvedQuestions: parseResolvedQuestions(content)
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
}
|
|
641
713
|
return null;
|
|
642
714
|
}
|
|
643
715
|
async function readIdeaTitle(fileSystem, dustPath, ideaSlug) {
|
|
@@ -719,7 +791,7 @@ function renderRepositoryHintsSection(repositoryHint) {
|
|
|
719
791
|
${repositoryHint}
|
|
720
792
|
`;
|
|
721
793
|
}
|
|
722
|
-
function renderTask(title, openingSentence, definitionOfDone, ideaSection, options) {
|
|
794
|
+
function renderTask(title, openingSentence, definitionOfDone, ideaSection, taskType, options) {
|
|
723
795
|
const descriptionParagraph = options?.description !== undefined ? `
|
|
724
796
|
${options.description}
|
|
725
797
|
` : "";
|
|
@@ -733,7 +805,12 @@ ${renderIdeaSection(ideaSection)}
|
|
|
733
805
|
return `# ${title}
|
|
734
806
|
|
|
735
807
|
${openingSentence}
|
|
736
|
-
${descriptionParagraph}${resolvedSection}${ideaSectionContent}
|
|
808
|
+
${descriptionParagraph}${resolvedSection}${ideaSectionContent}
|
|
809
|
+
## Task Type
|
|
810
|
+
|
|
811
|
+
${taskType}
|
|
812
|
+
|
|
813
|
+
## Blocked By
|
|
737
814
|
|
|
738
815
|
(none)
|
|
739
816
|
${repositoryHintsSection}
|
|
@@ -744,15 +821,15 @@ ${definitionOfDone.map((item) => `- ${item}`).join(`
|
|
|
744
821
|
`)}
|
|
745
822
|
`;
|
|
746
823
|
}
|
|
747
|
-
async function createIdeaTransitionTask(fileSystem, dustPath,
|
|
824
|
+
async function createIdeaTransitionTask(fileSystem, dustPath, taskType, prefix, ideaSlug, openingSentenceTemplate, definitionOfDone, ideaSectionHeading, taskOptions) {
|
|
748
825
|
const ideaTitle = await readIdeaTitle(fileSystem, dustPath, ideaSlug);
|
|
749
826
|
const taskTitle = `${prefix}${ideaTitle}`;
|
|
750
827
|
const filename = titleToFilename(taskTitle);
|
|
751
828
|
const filePath = `${dustPath}/tasks/${filename}`;
|
|
752
829
|
const baseOpeningSentence = openingSentenceTemplate(ideaTitle);
|
|
753
|
-
const hint = await readWorkflowHint(fileSystem, dustPath,
|
|
830
|
+
const hint = await readWorkflowHint(fileSystem, dustPath, taskType);
|
|
754
831
|
const ideaSection = { heading: ideaSectionHeading, ideaTitle, ideaSlug };
|
|
755
|
-
const content = renderTask(taskTitle, baseOpeningSentence, definitionOfDone, ideaSection, {
|
|
832
|
+
const content = renderTask(taskTitle, baseOpeningSentence, definitionOfDone, ideaSection, taskType, {
|
|
756
833
|
description: taskOptions?.description,
|
|
757
834
|
resolvedQuestions: taskOptions?.resolvedQuestions,
|
|
758
835
|
repositoryHint: hint ?? undefined
|
|
@@ -762,7 +839,7 @@ async function createIdeaTransitionTask(fileSystem, dustPath, workflowType, pref
|
|
|
762
839
|
}
|
|
763
840
|
async function createRefineIdeaTask(fileSystem, dustPath, ideaSlug, description, openQuestionResponses, dustCommand) {
|
|
764
841
|
const cmd = dustCommand ?? "dust";
|
|
765
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "refine
|
|
842
|
+
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.`, [
|
|
766
843
|
"Idea is thoroughly researched with relevant codebase context",
|
|
767
844
|
"Open questions are added for any ambiguous or underspecified aspects",
|
|
768
845
|
"Open questions follow the required heading format and focus on high-value decisions",
|
|
@@ -774,7 +851,7 @@ async function createRefineIdeaTask(fileSystem, dustPath, ideaSlug, description,
|
|
|
774
851
|
}
|
|
775
852
|
async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
776
853
|
const cmd = dustCommand ?? "dust";
|
|
777
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "decompose
|
|
854
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "decompose", "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).`, [
|
|
778
855
|
"One or more new tasks are created in .dust/tasks/",
|
|
779
856
|
"Task's Principles section links to relevant principles from .dust/principles/",
|
|
780
857
|
"The original idea is deleted or updated to reflect remaining scope"
|
|
@@ -784,11 +861,11 @@ async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
|
784
861
|
});
|
|
785
862
|
}
|
|
786
863
|
async function createShelveIdeaTask(fileSystem, dustPath, ideaSlug, description, _dustCommand) {
|
|
787
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "shelve
|
|
864
|
+
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 });
|
|
788
865
|
}
|
|
789
866
|
async function createExpediteIdeaTask(fileSystem, dustPath, ideaSlug, description, dustCommand) {
|
|
790
867
|
const cmd = dustCommand ?? "dust";
|
|
791
|
-
return createIdeaTransitionTask(fileSystem, dustPath, "
|
|
868
|
+
return createIdeaTransitionTask(fileSystem, dustPath, "implement", "Expedite Idea: ", ideaSlug, (ideaTitle) => `Research this idea briefly. If confident the implementation is straightforward (clear scope, minimal risk, no open questions), implement directly and commit. Otherwise, create one or more narrowly-scoped task files in \`.dust/tasks/\`. Run \`${cmd} principles\` and \`${cmd} facts\` for relevant context. See [${ideaTitle}](../ideas/${ideaSlug}.md).`, [
|
|
792
869
|
"Idea is implemented directly OR one or more new tasks are created in `.dust/tasks/`",
|
|
793
870
|
"If tasks were created, they link to relevant principles from `.dust/principles/`",
|
|
794
871
|
"Changes are committed with a clear commit message"
|
|
@@ -808,7 +885,7 @@ async function createIdeaTask(fileSystem, dustPath, options) {
|
|
|
808
885
|
const filename2 = titleToFilename(taskTitle2);
|
|
809
886
|
const filePath2 = `${dustPath}/tasks/${filename2}`;
|
|
810
887
|
const baseOpeningSentence2 = `Research this idea briefly. If confident the implementation is straightforward (clear scope, minimal risk, no open questions), implement directly and commit. Otherwise, create one or more narrowly-scoped task files in \`.dust/tasks/\`. Run \`${cmd} principles\` and \`${cmd} facts\` for relevant context.`;
|
|
811
|
-
const hint2 = await readWorkflowHint(fileSystem, dustPath, "
|
|
888
|
+
const hint2 = await readWorkflowHint(fileSystem, dustPath, "implement");
|
|
812
889
|
const repositoryHintsSection2 = renderRepositoryHintsSection(hint2 ?? undefined);
|
|
813
890
|
const content2 = `# ${taskTitle2}
|
|
814
891
|
|
|
@@ -818,6 +895,10 @@ ${baseOpeningSentence2}
|
|
|
818
895
|
|
|
819
896
|
${description}
|
|
820
897
|
|
|
898
|
+
## Task Type
|
|
899
|
+
|
|
900
|
+
implement
|
|
901
|
+
|
|
821
902
|
## Blocked By
|
|
822
903
|
|
|
823
904
|
(none)
|
|
@@ -836,7 +917,7 @@ ${repositoryHintsSection2}
|
|
|
836
917
|
const filename = titleToFilename(taskTitle);
|
|
837
918
|
const filePath = `${dustPath}/tasks/${filename}`;
|
|
838
919
|
const baseOpeningSentence = `Research this idea thoroughly, then create one or more idea files in \`.dust/ideas/\`. 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. 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. Run \`${cmd} principles\` and \`${cmd} facts\` for relevant context.`;
|
|
839
|
-
const hint = await readWorkflowHint(fileSystem, dustPath, "
|
|
920
|
+
const hint = await readWorkflowHint(fileSystem, dustPath, "capture");
|
|
840
921
|
const repositoryHintsSection = renderRepositoryHintsSection(hint ?? undefined);
|
|
841
922
|
const content = `# ${taskTitle}
|
|
842
923
|
|
|
@@ -846,6 +927,10 @@ ${baseOpeningSentence}
|
|
|
846
927
|
|
|
847
928
|
${description}
|
|
848
929
|
|
|
930
|
+
## Task Type
|
|
931
|
+
|
|
932
|
+
capture
|
|
933
|
+
|
|
849
934
|
## Blocked By
|
|
850
935
|
|
|
851
936
|
(none)
|
|
@@ -873,9 +958,16 @@ async function parseCaptureIdeaTask(fileSystem, dustPath, taskSlug) {
|
|
|
873
958
|
return null;
|
|
874
959
|
}
|
|
875
960
|
const title = titleMatch[1].trim();
|
|
961
|
+
const taskType = parseTaskType(content);
|
|
876
962
|
let ideaTitle;
|
|
877
963
|
let expedite;
|
|
878
|
-
if (
|
|
964
|
+
if (taskType === "implement") {
|
|
965
|
+
expedite = true;
|
|
966
|
+
ideaTitle = title.startsWith(EXPEDITE_IDEA_PREFIX) ? title.slice(EXPEDITE_IDEA_PREFIX.length) : title;
|
|
967
|
+
} else if (taskType === "capture") {
|
|
968
|
+
expedite = false;
|
|
969
|
+
ideaTitle = title.startsWith(CAPTURE_IDEA_PREFIX) ? title.slice(CAPTURE_IDEA_PREFIX.length) : title;
|
|
970
|
+
} else if (title.startsWith(EXPEDITE_IDEA_PREFIX)) {
|
|
879
971
|
ideaTitle = title.slice(EXPEDITE_IDEA_PREFIX.length);
|
|
880
972
|
expedite = true;
|
|
881
973
|
} else if (title.startsWith(CAPTURE_IDEA_PREFIX)) {
|
package/dist/audits/index.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface AuditsRepository {
|
|
|
32
32
|
/**
|
|
33
33
|
* Transforms audit template content for the task file.
|
|
34
34
|
* Changes the title from "# Original Title" to "# Audit: Original Title"
|
|
35
|
+
* and adds the Task Type section.
|
|
35
36
|
*/
|
|
36
37
|
export declare function transformAuditContent(content: string): string;
|
|
37
38
|
/**
|
package/dist/audits.js
CHANGED
|
@@ -2497,7 +2497,30 @@ function transformAuditContent(content) {
|
|
|
2497
2497
|
return content;
|
|
2498
2498
|
}
|
|
2499
2499
|
const originalTitle = titleMatch[1];
|
|
2500
|
-
|
|
2500
|
+
let transformed = content.replace(/^#\s+.+$/m, `# Audit: ${originalTitle}`);
|
|
2501
|
+
const lines = transformed.split(`
|
|
2502
|
+
`);
|
|
2503
|
+
let inCodeFence = false;
|
|
2504
|
+
let blockedByIndex = -1;
|
|
2505
|
+
for (let i = 0;i < lines.length; i++) {
|
|
2506
|
+
const line = lines[i];
|
|
2507
|
+
if (line.startsWith("```")) {
|
|
2508
|
+
inCodeFence = !inCodeFence;
|
|
2509
|
+
continue;
|
|
2510
|
+
}
|
|
2511
|
+
if (inCodeFence)
|
|
2512
|
+
continue;
|
|
2513
|
+
if (line === "## Blocked By") {
|
|
2514
|
+
blockedByIndex = i;
|
|
2515
|
+
break;
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
if (blockedByIndex !== -1) {
|
|
2519
|
+
lines.splice(blockedByIndex, 0, "## Task Type", "", "implement", "");
|
|
2520
|
+
transformed = lines.join(`
|
|
2521
|
+
`);
|
|
2522
|
+
}
|
|
2523
|
+
return transformed;
|
|
2501
2524
|
}
|
|
2502
2525
|
function injectComment(content, comment) {
|
|
2503
2526
|
const scopeMatch = content.match(/\n## Scope\n/);
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* for a single repository.
|
|
6
6
|
*/
|
|
7
7
|
import type { EventMessage } from '../agent-events';
|
|
8
|
-
import { type
|
|
9
|
-
import type { OutputSink } from '../claude/types';
|
|
8
|
+
import { type RunnerDependencies } from '../claude/run';
|
|
9
|
+
import type { BoundRunFn, OutputSink } from '../claude/types';
|
|
10
10
|
import { type LoopEmitFn } from '../loop/events';
|
|
11
11
|
import type { SendAgentEventFn } from '../loop/wire-events';
|
|
12
12
|
import { type RunnerDependencies as CodexRunnerDependencies, run as codexRun } from '../codex/run';
|
|
@@ -64,11 +64,11 @@ export declare function createStdoutSinkFactory(loopState: LoopState, logBuffer:
|
|
|
64
64
|
/**
|
|
65
65
|
* Create a run function that redirects Claude output to a log buffer.
|
|
66
66
|
*/
|
|
67
|
-
export declare function createBufferRun(run: RepositoryDependencies['run'], bufferSinkDeps: RunnerDependencies):
|
|
67
|
+
export declare function createBufferRun(run: RepositoryDependencies['run'], bufferSinkDeps: RunnerDependencies): BoundRunFn;
|
|
68
68
|
/**
|
|
69
69
|
* Create a run function that redirects Codex output to a log buffer.
|
|
70
70
|
*/
|
|
71
|
-
export declare function createCodexBufferRun(run: typeof codexRun, codexBufferSinkDeps: CodexRunnerDependencies):
|
|
71
|
+
export declare function createCodexBufferRun(run: typeof codexRun, codexBufferSinkDeps: CodexRunnerDependencies): BoundRunFn;
|
|
72
72
|
/** No-op postEvent for LoopDependencies. */
|
|
73
73
|
export declare function noOpPostEvent(): Promise<void>;
|
|
74
74
|
/**
|
|
@@ -72,6 +72,16 @@ export interface RepositoryDependencies {
|
|
|
72
72
|
/** Force Apple Container mode using bundled default Dockerfile */
|
|
73
73
|
forceAppleContainer?: boolean;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Handle loop completion: transition lifecycle and reset agent status.
|
|
77
|
+
* Extracted as a named function for testability.
|
|
78
|
+
*/
|
|
79
|
+
export declare function handleLoopFinished(repoState: RepositoryState): void;
|
|
80
|
+
/**
|
|
81
|
+
* Create a cancel function for a running repository loop.
|
|
82
|
+
* Extracted as a named function for testability.
|
|
83
|
+
*/
|
|
84
|
+
export declare function createLoopCancel(repoState: RepositoryState): () => void;
|
|
75
85
|
/**
|
|
76
86
|
* Start (or restart) the per-repository loop and keep lifecycle state accurate.
|
|
77
87
|
*/
|
package/dist/claude/run.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { spawnClaudeCode as defaultSpawnClaudeCode } from './spawn-claude-code';
|
|
2
1
|
import { createStdoutSink as defaultCreateStdoutSink, streamEvents as defaultStreamEvents } from './streamer';
|
|
3
|
-
import type {
|
|
4
|
-
interface RunOptions {
|
|
5
|
-
spawnOptions?: SpawnOptions;
|
|
6
|
-
onRawEvent?: RawEventCallback;
|
|
7
|
-
}
|
|
2
|
+
import type { RawEvent, RunOptions, SpawnOptions } from './types';
|
|
8
3
|
export interface RunnerDependencies {
|
|
9
|
-
spawnClaudeCode:
|
|
4
|
+
spawnClaudeCode: (prompt: string, options: SpawnOptions) => AsyncGenerator<RawEvent>;
|
|
10
5
|
createStdoutSink: typeof defaultCreateStdoutSink;
|
|
11
6
|
streamEvents: typeof defaultStreamEvents;
|
|
12
7
|
}
|
|
13
8
|
export declare const defaultRunnerDependencies: RunnerDependencies;
|
|
14
|
-
export declare function run(prompt: string, options
|
|
15
|
-
export {};
|
|
9
|
+
export declare function run(prompt: string, options: SpawnOptions | RunOptions, dependencies: RunnerDependencies): Promise<void>;
|
|
@@ -21,4 +21,4 @@ export declare const defaultDependencies: EventSourceDependencies;
|
|
|
21
21
|
* Build docker run arguments for spawning claude in a container.
|
|
22
22
|
*/
|
|
23
23
|
export declare function buildDockerRunArguments(docker: DockerSpawnConfig, claudeArguments: string[], env: Record<string, string>): string[];
|
|
24
|
-
export declare function spawnClaudeCode(prompt: string, options
|
|
24
|
+
export declare function spawnClaudeCode(prompt: string, options: SpawnOptions, dependencies: EventSourceDependencies): AsyncGenerator<RawEvent>;
|
package/dist/claude/types.d.ts
CHANGED
|
@@ -79,3 +79,12 @@ export interface SpawnOptions {
|
|
|
79
79
|
docker?: DockerSpawnConfig;
|
|
80
80
|
}
|
|
81
81
|
export type RawEventCallback = (event: RawEvent) => void;
|
|
82
|
+
export interface RunOptions {
|
|
83
|
+
spawnOptions?: SpawnOptions;
|
|
84
|
+
onRawEvent?: RawEventCallback;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A run function with its runner dependencies already bound.
|
|
88
|
+
* Used in LoopDependencies, AgentRunParams, etc.
|
|
89
|
+
*/
|
|
90
|
+
export type BoundRunFn = (prompt: string, options: SpawnOptions | RunOptions) => Promise<void>;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Usage: dust focus "add login box"
|
|
8
8
|
*/
|
|
9
|
+
import type { TaskType } from '../../artifacts/workflow-tasks';
|
|
9
10
|
import type { CommandDependencies, CommandResult } from '../types';
|
|
10
|
-
export declare function buildImplementationInstructions(bin: string, hooksInstalled: boolean, taskTitle?: string, taskPath?: string, installCommand?: string, skipPreflightSteps?: boolean): string;
|
|
11
|
+
export declare function buildImplementationInstructions(bin: string, hooksInstalled: boolean, taskTitle?: string, taskPath?: string, installCommand?: string, skipPreflightSteps?: boolean, taskType?: TaskType): string;
|
|
11
12
|
export declare function focus(dependencies: CommandDependencies): Promise<CommandResult>;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides common functionality used across all agent-* command files.
|
|
5
5
|
*/
|
|
6
6
|
import { type AgentType } from '../../agents/detection';
|
|
7
|
-
import type { CommandDependencies, DustSettings,
|
|
7
|
+
import type { CommandDependencies, DustSettings, FileReader } from '../types';
|
|
8
8
|
/**
|
|
9
9
|
* Type-safe template variables for agent commands.
|
|
10
10
|
* Uses real booleans instead of string-encoded booleans.
|
|
@@ -23,14 +23,14 @@ export interface TemplateVarsWithInstructions extends TemplateVars {
|
|
|
23
23
|
* Loads agent-specific instructions from .dust/config/agents/{agent-type}.md
|
|
24
24
|
* Returns empty string if file doesn't exist.
|
|
25
25
|
*/
|
|
26
|
-
export declare function loadAgentInstructions(cwd: string, fileSystem:
|
|
27
|
-
export declare function templateVariables(settings: DustSettings, hooksInstalled: boolean, env
|
|
26
|
+
export declare function loadAgentInstructions(cwd: string, fileSystem: FileReader, agentType: AgentType): Promise<string>;
|
|
27
|
+
export declare function templateVariables(settings: DustSettings, hooksInstalled: boolean, env: NodeJS.ProcessEnv, options?: {
|
|
28
28
|
hasIdeaFile?: boolean;
|
|
29
29
|
}): TemplateVars;
|
|
30
30
|
/**
|
|
31
31
|
* Creates template variables with agent-specific instructions loaded.
|
|
32
32
|
*/
|
|
33
|
-
export declare function templateVariablesWithInstructions(cwd: string, fileSystem:
|
|
33
|
+
export declare function templateVariablesWithInstructions(cwd: string, fileSystem: FileReader, settings: DustSettings, hooksInstalled: boolean, env: NodeJS.ProcessEnv, options?: {
|
|
34
34
|
hasIdeaFile?: boolean;
|
|
35
35
|
}): Promise<TemplateVarsWithInstructions>;
|
|
36
36
|
/**
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { CommandEvent } from '../command-events';
|
|
5
5
|
import type { RuntimeConfig } from '../env-config';
|
|
6
6
|
import type { FileSystem, GlobScanner } from '../filesystem/types';
|
|
7
|
-
export type { FileSystem, GlobScanner, ReadableFileSystem, } from '../filesystem/types';
|
|
7
|
+
export type { FileReader, FileSystem, GlobScanner, ReadableFileSystem, } from '../filesystem/types';
|
|
8
8
|
export interface CommandContext {
|
|
9
9
|
cwd: string;
|
|
10
10
|
stdout: (message: string) => void;
|
package/dist/codex/run.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { createStdoutSink as defaultCreateStdoutSink } from '../claude/streamer';
|
|
2
|
-
import type {
|
|
3
|
-
import { spawnCodex as defaultSpawnCodex } from './spawn-codex';
|
|
2
|
+
import type { RawEvent, RunOptions, SpawnOptions } from '../claude/types';
|
|
4
3
|
import { streamCodexEvents as defaultStreamCodexEvents } from './streamer';
|
|
5
|
-
interface RunOptions {
|
|
6
|
-
spawnOptions?: SpawnOptions;
|
|
7
|
-
onRawEvent?: RawEventCallback;
|
|
8
|
-
}
|
|
9
4
|
export interface RunnerDependencies {
|
|
10
|
-
spawnCodex:
|
|
5
|
+
spawnCodex: (prompt: string, options: SpawnOptions) => AsyncGenerator<RawEvent>;
|
|
11
6
|
createStdoutSink: typeof defaultCreateStdoutSink;
|
|
12
7
|
streamCodexEvents: typeof defaultStreamCodexEvents;
|
|
13
8
|
}
|
|
14
9
|
export declare const defaultRunnerDependencies: RunnerDependencies;
|
|
15
|
-
export declare function run(prompt: string, options
|
|
16
|
-
export {};
|
|
10
|
+
export declare function run(prompt: string, options: SpawnOptions | RunOptions, dependencies: RunnerDependencies): Promise<void>;
|
|
@@ -9,4 +9,4 @@ export declare const defaultDependencies: EventSourceDependencies;
|
|
|
9
9
|
* Build docker run arguments for spawning codex in a container.
|
|
10
10
|
*/
|
|
11
11
|
export declare function buildDockerRunArguments(docker: DockerSpawnConfig, codexArguments: string[], env: Record<string, string>): string[];
|
|
12
|
-
export declare function spawnCodex(prompt: string, options
|
|
12
|
+
export declare function spawnCodex(prompt: string, options: SpawnOptions, dependencies: EventSourceDependencies): AsyncGenerator<RawEvent>;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Reads optional configuration from .dust/config/settings.json
|
|
5
5
|
*/
|
|
6
|
-
import type { CheckConfig, DustSettings,
|
|
6
|
+
import type { CheckConfig, DustSettings, FileReader } from '../cli/types';
|
|
7
7
|
import type { RuntimeConfig } from '../env-config';
|
|
8
8
|
export type { CheckConfig, DustSettings };
|
|
9
9
|
interface SettingsViolation {
|
|
@@ -19,7 +19,7 @@ export declare function validateSettingsJson(content: string): SettingsViolation
|
|
|
19
19
|
* 4. No lockfile + BUN_INSTALL env var set → bunx dust
|
|
20
20
|
* 5. Default → npx dust
|
|
21
21
|
*/
|
|
22
|
-
export declare function detectDustCommand(cwd: string, fileSystem:
|
|
22
|
+
export declare function detectDustCommand(cwd: string, fileSystem: FileReader, runtime: RuntimeConfig): string;
|
|
23
23
|
/**
|
|
24
24
|
* Detects the appropriate install command based on lockfiles.
|
|
25
25
|
* Returns null when:
|
|
@@ -28,7 +28,7 @@ export declare function detectDustCommand(cwd: string, fileSystem: ReadableFileS
|
|
|
28
28
|
*
|
|
29
29
|
* Priority within each ecosystem follows the order in LOCKFILE_COMMANDS.
|
|
30
30
|
*/
|
|
31
|
-
export declare function detectInstallCommand(cwd: string, fileSystem:
|
|
31
|
+
export declare function detectInstallCommand(cwd: string, fileSystem: FileReader): string | null;
|
|
32
32
|
/**
|
|
33
33
|
* Detects the appropriate test command based on lockfiles and environment.
|
|
34
34
|
* Priority:
|
|
@@ -40,5 +40,5 @@ export declare function detectInstallCommand(cwd: string, fileSystem: ReadableFi
|
|
|
40
40
|
* 6. package.json exists → npm test
|
|
41
41
|
* 7. Default → null (no test command)
|
|
42
42
|
*/
|
|
43
|
-
export declare function detectTestCommand(cwd: string, fileSystem:
|
|
44
|
-
export declare function loadSettings(cwd: string, fileSystem:
|
|
43
|
+
export declare function detectTestCommand(cwd: string, fileSystem: FileReader, runtime: RuntimeConfig): string | null;
|
|
44
|
+
export declare function loadSettings(cwd: string, fileSystem: FileReader, runtime: RuntimeConfig): Promise<DustSettings>;
|