@joshski/dust 0.1.29 → 0.1.30
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/dust.js
CHANGED
|
@@ -572,8 +572,15 @@ function validateIdeaOpenQuestions(filePath, content) {
|
|
|
572
572
|
`);
|
|
573
573
|
let inOpenQuestions = false;
|
|
574
574
|
let currentQuestionLine = null;
|
|
575
|
+
let inCodeBlock = false;
|
|
575
576
|
for (let i = 0;i < lines.length; i++) {
|
|
576
577
|
const line = lines[i];
|
|
578
|
+
if (line.startsWith("```")) {
|
|
579
|
+
inCodeBlock = !inCodeBlock;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (inCodeBlock)
|
|
583
|
+
continue;
|
|
577
584
|
if (line.startsWith("## ")) {
|
|
578
585
|
if (inOpenQuestions && currentQuestionLine !== null) {
|
|
579
586
|
violations.push({
|
|
@@ -588,6 +595,14 @@ function validateIdeaOpenQuestions(filePath, content) {
|
|
|
588
595
|
}
|
|
589
596
|
if (!inOpenQuestions)
|
|
590
597
|
continue;
|
|
598
|
+
if (/^[-*] /.test(line.trimStart())) {
|
|
599
|
+
violations.push({
|
|
600
|
+
file: filePath,
|
|
601
|
+
message: "Open Questions must use ### headings for questions and #### headings for options, not bullet points. Run `dust new idea` to see the expected format.",
|
|
602
|
+
line: i + 1
|
|
603
|
+
});
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
591
606
|
if (line.startsWith("### ")) {
|
|
592
607
|
if (currentQuestionLine !== null) {
|
|
593
608
|
violations.push({
|
|
@@ -1142,14 +1157,34 @@ async function check(dependencies, shellRunner = defaultShellRunner) {
|
|
|
1142
1157
|
|
|
1143
1158
|
// lib/cli/commands/focus.ts
|
|
1144
1159
|
async function focus(dependencies) {
|
|
1145
|
-
const { context } = dependencies;
|
|
1160
|
+
const { context, settings } = dependencies;
|
|
1146
1161
|
const objective = dependencies.arguments.join(" ").trim();
|
|
1147
1162
|
if (!objective) {
|
|
1148
1163
|
context.stderr("Error: No objective provided");
|
|
1149
1164
|
context.stderr('Usage: dust focus "your objective here"');
|
|
1150
1165
|
return { exitCode: 1 };
|
|
1151
1166
|
}
|
|
1167
|
+
const hooksInstalled = await manageGitHooks(dependencies);
|
|
1168
|
+
const vars = templateVariables(settings, hooksInstalled);
|
|
1152
1169
|
context.stdout(`\uD83C\uDFAF Focus: ${objective}`);
|
|
1170
|
+
context.stdout("");
|
|
1171
|
+
const steps = [];
|
|
1172
|
+
let step = 1;
|
|
1173
|
+
steps.push(`${step}. Run \`${vars.bin} check\` to verify the project is in a good state`);
|
|
1174
|
+
step++;
|
|
1175
|
+
steps.push(`${step}. Implement the task`);
|
|
1176
|
+
step++;
|
|
1177
|
+
if (!hooksInstalled) {
|
|
1178
|
+
steps.push(`${step}. Run \`${vars.bin} check\` before committing`);
|
|
1179
|
+
step++;
|
|
1180
|
+
}
|
|
1181
|
+
steps.push(`${step}. Create a single atomic commit that includes:`, " - All implementation changes", " - Deletion of the completed task file", " - Updates to any facts that changed", " - Deletion of any ideas that were fully realized", "", ' Use the task title as the commit message. Task titles are written in imperative form, which is the recommended style for git commit messages. Do not add prefixes like "Complete task:" - use the title directly.', "", ' Example: If the task title is "Add validation for user input", the commit message should be:', " ```", " Add validation for user input", " ```", "");
|
|
1182
|
+
step++;
|
|
1183
|
+
steps.push(`${step}. Push your commit to the remote repository`);
|
|
1184
|
+
steps.push("");
|
|
1185
|
+
steps.push("Keep your change small and focused. One task, one commit.");
|
|
1186
|
+
context.stdout(steps.join(`
|
|
1187
|
+
`));
|
|
1153
1188
|
return { exitCode: 0 };
|
|
1154
1189
|
}
|
|
1155
1190
|
|
|
@@ -1162,17 +1197,14 @@ async function help(dependencies) {
|
|
|
1162
1197
|
return { exitCode: 0 };
|
|
1163
1198
|
}
|
|
1164
1199
|
|
|
1165
|
-
// lib/cli/
|
|
1166
|
-
|
|
1200
|
+
// lib/cli/commands/implement-task.ts
|
|
1201
|
+
async function implementTask(dependencies) {
|
|
1167
1202
|
const { context, settings } = dependencies;
|
|
1168
1203
|
const hooksInstalled = await manageGitHooks(dependencies);
|
|
1169
1204
|
const vars = templateVariables(settings, hooksInstalled);
|
|
1170
|
-
context.stdout(
|
|
1205
|
+
context.stdout(`Run \`${vars.bin} focus "<task name>"\` to set your focus and see implementation instructions.`);
|
|
1171
1206
|
return { exitCode: 0 };
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
// lib/cli/commands/implement-task.ts
|
|
1175
|
-
var implementTask = createTemplateCommand("agent-implement-task");
|
|
1207
|
+
}
|
|
1176
1208
|
|
|
1177
1209
|
// lib/cli/colors.ts
|
|
1178
1210
|
var ANSI_COLORS = {
|
|
@@ -1835,26 +1867,22 @@ function extractBlockedBy(content) {
|
|
|
1835
1867
|
}
|
|
1836
1868
|
return blockers;
|
|
1837
1869
|
}
|
|
1838
|
-
async function
|
|
1839
|
-
const
|
|
1840
|
-
const dustPath = `${context.cwd}/.dust`;
|
|
1841
|
-
const colors = getColors();
|
|
1870
|
+
async function findUnblockedTasks(cwd, fileSystem) {
|
|
1871
|
+
const dustPath = `${cwd}/.dust`;
|
|
1842
1872
|
if (!fileSystem.exists(dustPath)) {
|
|
1843
|
-
|
|
1844
|
-
context.stderr("Run 'dust init' to initialize a Dust repository");
|
|
1845
|
-
return { exitCode: 1 };
|
|
1873
|
+
return { error: ".dust directory not found", tasks: [] };
|
|
1846
1874
|
}
|
|
1847
1875
|
const tasksPath = `${dustPath}/tasks`;
|
|
1848
1876
|
if (!fileSystem.exists(tasksPath)) {
|
|
1849
|
-
return {
|
|
1877
|
+
return { tasks: [] };
|
|
1850
1878
|
}
|
|
1851
1879
|
const files = await fileSystem.readdir(tasksPath);
|
|
1852
1880
|
const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
|
|
1853
1881
|
if (mdFiles.length === 0) {
|
|
1854
|
-
return {
|
|
1882
|
+
return { tasks: [] };
|
|
1855
1883
|
}
|
|
1856
1884
|
const existingTasks = new Set(mdFiles);
|
|
1857
|
-
const
|
|
1885
|
+
const tasks = [];
|
|
1858
1886
|
for (const file of mdFiles) {
|
|
1859
1887
|
const filePath = `${tasksPath}/${file}`;
|
|
1860
1888
|
const content = await fileSystem.readFile(filePath);
|
|
@@ -1864,15 +1892,16 @@ async function next(dependencies) {
|
|
|
1864
1892
|
const title = extractTitle(content);
|
|
1865
1893
|
const openingSentence = extractOpeningSentence(content);
|
|
1866
1894
|
const relativePath = `.dust/tasks/${file}`;
|
|
1867
|
-
|
|
1895
|
+
tasks.push({ path: relativePath, title, openingSentence });
|
|
1868
1896
|
}
|
|
1869
1897
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1898
|
+
return { tasks };
|
|
1899
|
+
}
|
|
1900
|
+
function printTaskList(context, tasks) {
|
|
1901
|
+
const colors = getColors();
|
|
1873
1902
|
context.stdout("\uD83D\uDCCB Next tasks");
|
|
1874
1903
|
context.stdout("");
|
|
1875
|
-
for (const task of
|
|
1904
|
+
for (const task of tasks) {
|
|
1876
1905
|
const parts = task.path.split("/");
|
|
1877
1906
|
const displayTitle = task.title || parts[parts.length - 1].replace(".md", "");
|
|
1878
1907
|
context.stdout(`${colors.bold}# ${displayTitle}${colors.reset}`);
|
|
@@ -1882,6 +1911,19 @@ async function next(dependencies) {
|
|
|
1882
1911
|
context.stdout(`${colors.cyan}→ ${task.path}${colors.reset}`);
|
|
1883
1912
|
context.stdout("");
|
|
1884
1913
|
}
|
|
1914
|
+
}
|
|
1915
|
+
async function next(dependencies) {
|
|
1916
|
+
const { context, fileSystem } = dependencies;
|
|
1917
|
+
const result = await findUnblockedTasks(context.cwd, fileSystem);
|
|
1918
|
+
if (result.error) {
|
|
1919
|
+
context.stderr(`Error: ${result.error}`);
|
|
1920
|
+
context.stderr("Run 'dust init' to initialize a Dust repository");
|
|
1921
|
+
return { exitCode: 1 };
|
|
1922
|
+
}
|
|
1923
|
+
if (result.tasks.length === 0) {
|
|
1924
|
+
return { exitCode: 0 };
|
|
1925
|
+
}
|
|
1926
|
+
printTaskList(context, result.tasks);
|
|
1885
1927
|
return { exitCode: 0 };
|
|
1886
1928
|
}
|
|
1887
1929
|
|
|
@@ -2113,6 +2155,15 @@ async function loopClaude(dependencies, loopDependencies = createDefaultDependen
|
|
|
2113
2155
|
return { exitCode: 0 };
|
|
2114
2156
|
}
|
|
2115
2157
|
|
|
2158
|
+
// lib/cli/template-command.ts
|
|
2159
|
+
var createTemplateCommand = (templateName) => async (dependencies) => {
|
|
2160
|
+
const { context, settings } = dependencies;
|
|
2161
|
+
const hooksInstalled = await manageGitHooks(dependencies);
|
|
2162
|
+
const vars = templateVariables(settings, hooksInstalled);
|
|
2163
|
+
context.stdout(loadTemplate(templateName, vars));
|
|
2164
|
+
return { exitCode: 0 };
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2116
2167
|
// lib/cli/commands/new-goal.ts
|
|
2117
2168
|
var newGoal = createTemplateCommand("agent-new-goal");
|
|
2118
2169
|
|
|
@@ -2123,7 +2174,26 @@ var newIdea = createTemplateCommand("agent-new-idea");
|
|
|
2123
2174
|
var newTask = createTemplateCommand("agent-new-task");
|
|
2124
2175
|
|
|
2125
2176
|
// lib/cli/commands/pick-task.ts
|
|
2126
|
-
|
|
2177
|
+
async function pickTask(dependencies) {
|
|
2178
|
+
const { context, fileSystem, settings } = dependencies;
|
|
2179
|
+
await manageGitHooks(dependencies);
|
|
2180
|
+
const result = await findUnblockedTasks(context.cwd, fileSystem);
|
|
2181
|
+
if (result.error) {
|
|
2182
|
+
context.stderr(`Error: ${result.error}`);
|
|
2183
|
+
context.stderr("Run 'dust init' to initialize a Dust repository");
|
|
2184
|
+
return { exitCode: 1 };
|
|
2185
|
+
}
|
|
2186
|
+
if (result.tasks.length === 0) {
|
|
2187
|
+
context.stdout("No unblocked tasks found.");
|
|
2188
|
+
return { exitCode: 0 };
|
|
2189
|
+
}
|
|
2190
|
+
context.stdout("## Pick a Task");
|
|
2191
|
+
context.stdout("");
|
|
2192
|
+
printTaskList(context, result.tasks);
|
|
2193
|
+
const vars = templateVariables(settings, false);
|
|
2194
|
+
context.stdout(`Pick ONE task, read its file to understand the requirements, then run \`${vars.bin} focus "<task name>"\`.`);
|
|
2195
|
+
return { exitCode: 0 };
|
|
2196
|
+
}
|
|
2127
2197
|
|
|
2128
2198
|
// lib/cli/commands/pre-push.ts
|
|
2129
2199
|
function parseGitDiffNameStatus(output) {
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ Determine the user's intent and run the matching command NOW:
|
|
|
13
13
|
1. **Pick up work from the backlog** → `{{bin}} pick task`
|
|
14
14
|
User wants to start working. Examples: "work", "go", "pick a task", "what's next?"
|
|
15
15
|
|
|
16
|
-
2. **Implement a specific task** → `{{bin}}
|
|
16
|
+
2. **Implement a specific task** → `{{bin}} focus "<task name>"`
|
|
17
17
|
User mentions a particular task by name. Examples: "implement the auth task", "work on caching"
|
|
18
18
|
|
|
19
19
|
3. **Capture a new task** → `{{bin}} new task`
|
|
@@ -13,7 +13,7 @@ Determine the user's intent and run the matching command NOW:
|
|
|
13
13
|
1. **Pick up work from the backlog** → `{{bin}} pick task`
|
|
14
14
|
User wants to start working. Examples: "work", "go", "pick a task", "what's next?"
|
|
15
15
|
|
|
16
|
-
2. **Implement a specific task** → `{{bin}}
|
|
16
|
+
2. **Implement a specific task** → `{{bin}} focus "<task name>"`
|
|
17
17
|
User mentions a particular task by name. Examples: "implement the auth task", "work on caching"
|
|
18
18
|
|
|
19
19
|
3. **Capture a new task** → `{{bin}} new task`
|