@joshski/dust 0.1.10 → 0.1.12

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 CHANGED
@@ -1,129 +1,51 @@
1
1
  # Dust
2
2
 
3
- A lightweight workflow tool for humans working with AI agents.
3
+ A workflow tool for keeping AI coding agents on track.
4
4
 
5
5
  [![CI](https://github.com/joshski/dust/actions/workflows/ci.yml/badge.svg)](https://github.com/joshski/dust/actions/workflows/ci.yml)
6
6
 
7
- ## Why Would I Use This?
7
+ ## Why Use This?
8
8
 
9
- Use this to plan a series of tasks that coding agents can perform autonomously.
9
+ AI coding agents work best with clear tasks and fast feedback. Dust gives them both:
10
10
 
11
- ## Getting Started
11
+ - **Tasks** — a queue of work, each with clear requirements and definition of done
12
+ - **Checks** — quality gates (tests, lint, build) that run before and after changes
12
13
 
13
- Install dust using your package manager of choice (`npm` and `bun` officially supported for now):
14
+ Agents pick tasks, implement them, verify checks pass, and move on. You add tasks, review commits, and steer direction.
14
15
 
15
- ```bash
16
- npm install @joshski/dust
17
- ```
18
-
19
- Initialize dust in your repository:
16
+ ## Quick Start
20
17
 
21
18
  ```bash
19
+ npm install @joshski/dust
22
20
  npx dust init
23
21
  ```
24
22
 
25
- ## How It Works
26
-
27
- The [.dust](./.dust) directory in your repository contains 4 sets of markdown files:
28
-
29
- ```
30
- .dust/
31
- ├── goals/ # Mission statements explaining why the project exists
32
- ├── ideas/ # Brief notes about future tasks (intentionally vague)
33
- ├── tasks/ # Detailed work plans with dependencies and definition of done
34
- └── facts/ # Current state: design, architecture, rules, invariants
35
- ```
36
-
37
- These files are used to facilitate exploration and management of AI agent workflow.
38
-
39
- ## Workflow
40
-
41
- Progress is tracked via changes to markdown files in the `.dust/` directory. The four directories together (`goals/`, `ideas/`, `tasks/`, `facts/`) act as a kanban system for managing work.
23
+ This creates a [.dust](./.dust/facts/dust-directory-structure.md) directory and adds an [instruction](./.dust/facts/agents-md-instruction.md) to your `AGENTS.md` file.
42
24
 
43
- The `tasks/` directory acts as a work queue. When a task is completed, the commit typically includes both the code changes and the deletion of the task file—removing work from the queue for subsequent agents.
44
-
45
- Humans primarily interact with `dust` using their AI coding agent of choice, e.g.
46
-
47
- ```
48
- > claude "implement the next task"
49
- > claude "get to work!"
50
- > codex "add task: coverage reporting"
51
- > codex "goal: cross-platform compatibility"
52
- ```
25
+ ## Running Agents
53
26
 
54
- Agents discover everything they need to know to interpret human prompts using the `dust` CLI.
55
-
56
- ## Agent CLI
57
-
58
- The `dust` CLI is intended to be used primarily by agents, not by humans (except for the `init` command, as mentioned above).
59
-
60
- Don't let that stop you:
27
+ Start an agent on a single task:
61
28
 
62
29
  ```bash
63
- npx dust help
64
- ```
65
-
66
- ## AGENTS.md
67
-
68
- Add a line like this to your `AGENTS.md` or `CLAUDE.md` file:
69
-
70
- ```markdown
71
- Always immediately run `npx dust agent` when you start working in this repository.
30
+ claude "implement the next task"
72
31
  ```
73
32
 
74
- In fact, you might find it effective to use that as the _only_ line in those files. Essential documentation should be discoverable by agents exploring your `./.dust` directory.
75
-
76
- ## Configuration
33
+ Or let dust run agents continuously [in a sandbox](./.dust/facts/autonomous-agents-need-sandboxes.md) with the [loop](./.dust/facts/loop-command.md) command:
77
34
 
78
- Configure dust in `./.dust/config/settings.json` e.g.
79
-
80
- ```json
81
- {
82
- "dustCommand": "bunx dust",
83
- "checks": [
84
- { "name": "lint", "command": "bunx biome check ." },
85
- { "name": "build", "command": "bun run build" },
86
- { "name": "tests", "command": "bun run test:coverage" },
87
- { "name": "typecheck", "command": "bunx tsc --noEmit lib/**/*.ts" }
88
- ]
89
- }
90
- ```
91
-
92
- The `dust check` command will run all of the configured checks in parallel and produce a very terse (context window-friendly) output unless one or more of the checks fail.
93
-
94
- ### Check Failure Hints
95
-
96
- Add optional `hints` to help agents recover from check failures:
97
-
98
- ```json
99
- {
100
- "checks": [
101
- {
102
- "name": "build",
103
- "command": "npm run build",
104
- "hints": [
105
- "Run `npm install` if this is a fresh checkout",
106
- "Check for TypeScript errors in the files you modified"
107
- ]
108
- }
109
- ]
110
- }
35
+ ```bash
36
+ npx dust loop claude
111
37
  ```
112
38
 
113
- When a check fails, hints are displayed after the error output:
39
+ This runs Claude Code in a [ralph loop](https://ghuntley.com/loop/), picking up tasks until the iteration limit is reached (default: 10). You can specify a custom limit:
114
40
 
41
+ ```bash
42
+ npx dust loop claude 5
115
43
  ```
116
- ✓ validate
117
- ✗ build
118
-
119
- > npm run build
120
- error TS2307: Cannot find module 'lodash'...
121
44
 
122
- Hints for fixing 'build':
123
- - Run `npm install` if this is a fresh checkout
124
- - Check for TypeScript errors in the files you modified
45
+ ## Learn More
125
46
 
126
- 1/2 checks passed
127
- ```
47
+ Details live in the [.dust/facts](./.dust/facts) directory:
128
48
 
129
- Agents are instructed to run `dust check` before and after any changes, as a way of keeping them on track. It's more important that these commands are comprehensive, than they are fast.
49
+ - [Directory Structure](./.dust/facts/dust-directory-structure.md) how `.dust/` is organized
50
+ - [Configuration](./.dust/facts/configuration-system.md) — settings and quality checks
51
+ - [CLI Commands](./.dust/facts/unified-cli.md) — full command reference
package/dist/dust.js CHANGED
@@ -254,7 +254,7 @@ async function agentUnderstandGoals(dependencies) {
254
254
  // lib/cli/commands/check.ts
255
255
  import { spawn } from "node:child_process";
256
256
 
257
- // lib/cli/commands/validate.ts
257
+ // lib/cli/commands/lint-markdown.ts
258
258
  import { dirname as dirname2, resolve } from "node:path";
259
259
 
260
260
  // lib/cli/markdown-utilities.ts
@@ -263,8 +263,53 @@ function extractTitle(content) {
263
263
  return match ? match[1].trim() : null;
264
264
  }
265
265
  var MARKDOWN_LINK_PATTERN = /\[([^\]]+)\]\(([^)]+)\)/;
266
+ function extractOpeningSentence(content) {
267
+ const lines = content.split(`
268
+ `);
269
+ let h1Index = -1;
270
+ for (let i = 0;i < lines.length; i++) {
271
+ if (lines[i].match(/^#\s+.+$/)) {
272
+ h1Index = i;
273
+ break;
274
+ }
275
+ }
276
+ if (h1Index === -1) {
277
+ return null;
278
+ }
279
+ let paragraphStart = -1;
280
+ for (let i = h1Index + 1;i < lines.length; i++) {
281
+ const line = lines[i].trim();
282
+ if (line !== "") {
283
+ paragraphStart = i;
284
+ break;
285
+ }
286
+ }
287
+ if (paragraphStart === -1) {
288
+ return null;
289
+ }
290
+ const firstLine = lines[paragraphStart];
291
+ const trimmedFirstLine = firstLine.trim();
292
+ if (trimmedFirstLine.startsWith("#") || trimmedFirstLine.startsWith("-") || trimmedFirstLine.startsWith("*") || trimmedFirstLine.startsWith("+") || trimmedFirstLine.match(/^\d+\./) || trimmedFirstLine.startsWith("```") || trimmedFirstLine.startsWith(">")) {
293
+ return null;
294
+ }
295
+ let paragraph = "";
296
+ for (let i = paragraphStart;i < lines.length; i++) {
297
+ const line = lines[i].trim();
298
+ if (line === "")
299
+ break;
300
+ if (line.startsWith("#") || line.startsWith("```") || line.startsWith(">")) {
301
+ break;
302
+ }
303
+ paragraph += (paragraph ? " " : "") + line;
304
+ }
305
+ const sentenceMatch = paragraph.match(/^(.+?[.?!])(?:\s|$)/);
306
+ if (!sentenceMatch) {
307
+ return null;
308
+ }
309
+ return sentenceMatch[1];
310
+ }
266
311
 
267
- // lib/cli/commands/validate.ts
312
+ // lib/cli/commands/lint-markdown.ts
268
313
  var REQUIRED_HEADINGS = ["## Goals", "## Blocked by", "## Definition of done"];
269
314
  var SLUG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*\.md$/;
270
315
  function validateFilename(filePath) {
@@ -278,6 +323,16 @@ function validateFilename(filePath) {
278
323
  }
279
324
  return null;
280
325
  }
326
+ function validateOpeningSentence(filePath, content) {
327
+ const openingSentence = extractOpeningSentence(content);
328
+ if (!openingSentence) {
329
+ return {
330
+ file: filePath,
331
+ message: "Missing or malformed opening sentence after H1 heading"
332
+ };
333
+ }
334
+ return null;
335
+ }
281
336
  function validateTaskHeadings(filePath, content) {
282
337
  const violations = [];
283
338
  for (const heading of REQUIRED_HEADINGS) {
@@ -380,7 +435,7 @@ function validateSemanticLinks(filePath, content) {
380
435
  }
381
436
  return violations;
382
437
  }
383
- async function validate(dependencies) {
438
+ async function lintMarkdown(dependencies) {
384
439
  const { context, fileSystem, globScanner: glob } = dependencies;
385
440
  const dustPath = `${context.cwd}/.dust`;
386
441
  if (!fileSystem.exists(dustPath)) {
@@ -397,6 +452,23 @@ async function validate(dependencies) {
397
452
  const content = await fileSystem.readFile(filePath);
398
453
  violations.push(...validateLinks(filePath, content, fileSystem));
399
454
  }
455
+ const contentDirs = ["goals", "facts", "ideas", "tasks"];
456
+ context.stdout("Validating opening sentences...");
457
+ for (const dir of contentDirs) {
458
+ const dirPath = `${dustPath}/${dir}`;
459
+ if (!fileSystem.exists(dirPath))
460
+ continue;
461
+ for await (const file of glob.scan(dirPath)) {
462
+ if (!file.endsWith(".md"))
463
+ continue;
464
+ const filePath = `${dirPath}/${file}`;
465
+ const content = await fileSystem.readFile(filePath);
466
+ const openingSentenceViolation = validateOpeningSentence(filePath, content);
467
+ if (openingSentenceViolation) {
468
+ violations.push(openingSentenceViolation);
469
+ }
470
+ }
471
+ }
400
472
  const tasksPath = `${dustPath}/tasks`;
401
473
  if (fileSystem.exists(tasksPath)) {
402
474
  context.stdout("Validating task files in .dust/tasks/...");
@@ -471,14 +543,14 @@ async function runValidationCheck(dependencies) {
471
543
  stdout: (msg) => outputLines.push(msg),
472
544
  stderr: (msg) => outputLines.push(msg)
473
545
  };
474
- const result = await validate({
546
+ const result = await lintMarkdown({
475
547
  ...dependencies,
476
548
  context: bufferedContext,
477
549
  arguments: []
478
550
  });
479
551
  return {
480
- name: "validate",
481
- command: "dust validate",
552
+ name: "lint markdown",
553
+ command: "dust lint markdown",
482
554
  exitCode: result.exitCode,
483
555
  output: outputLines.join(`
484
556
  `),
@@ -695,6 +767,24 @@ async function init(dependencies) {
695
767
 
696
768
  // lib/cli/commands/list.ts
697
769
  var VALID_TYPES = ["tasks", "ideas", "goals", "facts"];
770
+ var SECTION_HEADERS = {
771
+ tasks: "\uD83D\uDCCB Tasks",
772
+ ideas: "\uD83D\uDCA1 Ideas",
773
+ goals: "\uD83C\uDFAF Goals",
774
+ facts: "\uD83D\uDCC4 Facts"
775
+ };
776
+ var TYPE_EXPLANATIONS = {
777
+ tasks: "Tasks are detailed work plans with dependencies and completion criteria. Each task describes a specific piece of work to be done.",
778
+ ideas: "Ideas are future feature notes and proposals. Ideas capture possibilities that haven't yet been refined into actionable tasks.",
779
+ goals: "Goals are mission statements and guiding principles. Goals describe desired outcomes and values that inform decision-making.",
780
+ facts: "Facts are current state documentation. Facts capture how things work today, providing context for agents and contributors."
781
+ };
782
+ var colors = {
783
+ reset: "\x1B[0m",
784
+ bold: "\x1B[1m",
785
+ dim: "\x1B[2m",
786
+ cyan: "\x1B[36m"
787
+ };
698
788
  async function list(dependencies) {
699
789
  const { arguments: commandArguments, context, fileSystem } = dependencies;
700
790
  const dustPath = `${context.cwd}/.dust`;
@@ -709,29 +799,44 @@ async function list(dependencies) {
709
799
  context.stderr(`Valid types: ${VALID_TYPES.join(", ")}`);
710
800
  return { exitCode: 1 };
711
801
  }
802
+ const specificTypeRequested = commandArguments.length > 0;
712
803
  for (const type of typesToList) {
713
804
  const dirPath = `${dustPath}/${type}`;
714
- if (!fileSystem.exists(dirPath)) {
715
- continue;
716
- }
717
- const files = await fileSystem.readdir(dirPath);
805
+ const dirExists = fileSystem.exists(dirPath);
806
+ const files = dirExists ? await fileSystem.readdir(dirPath) : [];
718
807
  const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
719
808
  if (mdFiles.length === 0) {
809
+ if (specificTypeRequested) {
810
+ context.stdout(SECTION_HEADERS[type]);
811
+ context.stdout("");
812
+ context.stdout(TYPE_EXPLANATIONS[type]);
813
+ context.stdout("");
814
+ context.stdout(`No ${type} found.`);
815
+ context.stdout("");
816
+ }
720
817
  continue;
721
818
  }
722
- context.stdout(`${type}:`);
819
+ context.stdout(SECTION_HEADERS[type]);
820
+ context.stdout("");
821
+ context.stdout(TYPE_EXPLANATIONS[type]);
822
+ context.stdout("");
723
823
  for (const file of mdFiles) {
724
824
  const filePath = `${dirPath}/${file}`;
725
825
  const content = await fileSystem.readFile(filePath);
726
826
  const title = extractTitle(content);
727
- const name = file.replace(/\.md$/, "");
827
+ const openingSentence = extractOpeningSentence(content);
828
+ const relativePath = `.dust/${type}/${file}`;
728
829
  if (title) {
729
- context.stdout(` ${name} - ${title}`);
830
+ context.stdout(`${colors.bold}# ${title}${colors.reset}`);
730
831
  } else {
731
- context.stdout(` ${name}`);
832
+ context.stdout(`${colors.bold}# ${file.replace(".md", "")}${colors.reset}`);
833
+ }
834
+ if (openingSentence) {
835
+ context.stdout(`${colors.dim}${openingSentence}${colors.reset}`);
732
836
  }
837
+ context.stdout(`${colors.cyan}→ ${relativePath}${colors.reset}`);
838
+ context.stdout("");
733
839
  }
734
- context.stdout("");
735
840
  }
736
841
  return { exitCode: 0 };
737
842
  }
@@ -915,6 +1020,12 @@ async function run(prompt, options = {}, dependencies = defaultRunnerDependencie
915
1020
  }
916
1021
 
917
1022
  // lib/cli/commands/next.ts
1023
+ var colors2 = {
1024
+ reset: "\x1B[0m",
1025
+ bold: "\x1B[1m",
1026
+ dim: "\x1B[2m",
1027
+ cyan: "\x1B[36m"
1028
+ };
918
1029
  function extractBlockedBy(content) {
919
1030
  const blockedByMatch = content.match(/^## Blocked by\s*\n([\s\S]*?)(?=\n## |\n*$)/m);
920
1031
  if (!blockedByMatch) {
@@ -959,20 +1070,24 @@ async function next(dependencies) {
959
1070
  const hasIncompleteBlocker = blockers.some((blocker) => existingTasks.has(blocker));
960
1071
  if (!hasIncompleteBlocker) {
961
1072
  const title = extractTitle(content);
1073
+ const openingSentence = extractOpeningSentence(content);
962
1074
  const relativePath = `.dust/tasks/${file}`;
963
- unblockedTasks.push({ path: relativePath, title });
1075
+ unblockedTasks.push({ path: relativePath, title, openingSentence });
964
1076
  }
965
1077
  }
966
1078
  if (unblockedTasks.length === 0) {
967
1079
  return { exitCode: 0 };
968
1080
  }
969
- context.stdout("Next tasks:");
1081
+ context.stdout("\uD83D\uDCCB Next tasks");
1082
+ context.stdout("");
970
1083
  for (const task of unblockedTasks) {
971
- if (task.title) {
972
- context.stdout(` ${task.path} - ${task.title}`);
973
- } else {
974
- context.stdout(` ${task.path}`);
1084
+ const displayTitle = task.title || task.path.split("/").pop().replace(".md", "");
1085
+ context.stdout(`${colors2.bold}# ${displayTitle}${colors2.reset}`);
1086
+ if (task.openingSentence) {
1087
+ context.stdout(`${colors2.dim}${task.openingSentence}${colors2.reset}`);
975
1088
  }
1089
+ context.stdout(`${colors2.cyan}→ ${task.path}${colors2.reset}`);
1090
+ context.stdout("");
976
1091
  }
977
1092
  return { exitCode: 0 };
978
1093
  }
@@ -986,6 +1101,7 @@ function createDefaultDependencies() {
986
1101
  };
987
1102
  }
988
1103
  var SLEEP_INTERVAL_MS = 30000;
1104
+ var DEFAULT_MAX_ITERATIONS = 10;
989
1105
  async function gitPull(cwd, spawn2) {
990
1106
  return new Promise((resolve2) => {
991
1107
  const proc = spawn2("git", ["pull"], {
@@ -1022,48 +1138,66 @@ async function hasAvailableTasks(dependencies) {
1022
1138
  async function runOneIteration(dependencies, loopDependencies) {
1023
1139
  const { context } = dependencies;
1024
1140
  const { spawn: spawn2, run: run2 } = loopDependencies;
1025
- context.stdout("Syncing with remote...");
1141
+ context.stdout("\uD83D\uDD04 Syncing with remote...");
1026
1142
  const pullResult = await gitPull(context.cwd, spawn2);
1027
1143
  if (!pullResult.success) {
1028
1144
  context.stdout(`Note: git pull skipped (${pullResult.message})`);
1029
1145
  }
1030
- context.stdout("Checking for available tasks...");
1146
+ context.stdout("\uD83D\uDD0D Checking for available tasks...");
1031
1147
  const hasTasks = await hasAvailableTasks(dependencies);
1032
1148
  if (!hasTasks) {
1033
- context.stdout("No tasks available. Sleeping...");
1149
+ context.stdout("\uD83D\uDCA4 No tasks available. Sleeping...");
1034
1150
  context.stdout("");
1035
1151
  return "no_tasks";
1036
1152
  }
1037
- context.stdout("Found task(s). Starting Claude...");
1153
+ context.stdout("Found task(s). \uD83E\uDD16 Starting Claude...");
1038
1154
  context.stdout("");
1039
1155
  try {
1040
1156
  await run2("go", { cwd: context.cwd, dangerouslySkipPermissions: true });
1041
1157
  context.stdout("");
1042
- context.stdout("Claude session complete. Continuing loop...");
1158
+ context.stdout("Claude session complete. Continuing loop...");
1043
1159
  context.stdout("");
1044
1160
  return "ran_claude";
1045
1161
  } catch (error) {
1046
1162
  const message = error instanceof Error ? error.message : String(error);
1047
1163
  context.stderr(`Claude exited with error: ${message}`);
1048
1164
  context.stdout("");
1049
- context.stdout("Claude session complete. Continuing loop...");
1165
+ context.stdout("Claude session complete. Continuing loop...");
1050
1166
  context.stdout("");
1051
1167
  return "claude_error";
1052
1168
  }
1053
1169
  }
1054
- async function loop(dependencies, loopDependencies = createDefaultDependencies()) {
1170
+ function parseMaxIterations(commandArguments) {
1171
+ if (commandArguments.length === 0) {
1172
+ return DEFAULT_MAX_ITERATIONS;
1173
+ }
1174
+ const parsed = Number.parseInt(commandArguments[0], 10);
1175
+ if (Number.isNaN(parsed) || parsed <= 0) {
1176
+ return DEFAULT_MAX_ITERATIONS;
1177
+ }
1178
+ return parsed;
1179
+ }
1180
+ async function loopClaude(dependencies, loopDependencies = createDefaultDependencies()) {
1055
1181
  const { context } = dependencies;
1056
- context.stdout("WARNING: This command skips all permission checks. Only use in a sandbox environment!");
1182
+ const maxIterations = parseMaxIterations(dependencies.arguments);
1183
+ context.stdout("⚠️ WARNING: This command skips all permission checks. Only use in a sandbox environment!");
1057
1184
  context.stdout("");
1058
- context.stdout("Starting dust loop...");
1059
- context.stdout("Press Ctrl+C to stop");
1185
+ context.stdout(`\uD83D\uDD04 Starting dust loop claude (max ${maxIterations} iterations)...`);
1186
+ context.stdout(" Press Ctrl+C to stop");
1060
1187
  context.stdout("");
1061
- while (true) {
1188
+ let completedIterations = 0;
1189
+ while (completedIterations < maxIterations) {
1062
1190
  const result = await runOneIteration(dependencies, loopDependencies);
1063
1191
  if (result === "no_tasks") {
1064
1192
  await loopDependencies.sleep(SLEEP_INTERVAL_MS);
1193
+ } else {
1194
+ completedIterations++;
1195
+ context.stdout(`\uD83D\uDCCB Completed iteration ${completedIterations}/${maxIterations}`);
1196
+ context.stdout("");
1065
1197
  }
1066
1198
  }
1199
+ context.stdout(`\uD83C\uDFC1 Reached max iterations (${maxIterations}). Exiting.`);
1200
+ return { exitCode: 0 };
1067
1201
  }
1068
1202
 
1069
1203
  // lib/cli/commands/pre-push.ts
@@ -1074,7 +1208,7 @@ async function prePush(dependencies) {
1074
1208
  // lib/cli/main.ts
1075
1209
  var commandRegistry = {
1076
1210
  init,
1077
- validate,
1211
+ "lint-markdown": lintMarkdown,
1078
1212
  list,
1079
1213
  next,
1080
1214
  check,
@@ -1086,7 +1220,7 @@ var commandRegistry = {
1086
1220
  "agent-implement-task": agentImplementTask,
1087
1221
  "agent-pick-task": agentPickTask,
1088
1222
  "agent-understand-goals": agentUnderstandGoals,
1089
- loop,
1223
+ "loop-claude": loopClaude,
1090
1224
  "pre-push": prePush,
1091
1225
  help
1092
1226
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A lightweight planning system for human-AI collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,8 @@
26
26
  "scripts": {
27
27
  "build": "bun build lib/cli/entry.ts --target node --outfile dist/dust.js && printf '%s\\n%s' '#!/usr/bin/env node' \"$(cat dist/dust.js)\" > dist/dust.js && cp -r lib/templates templates",
28
28
  "test": "vitest run",
29
- "test:coverage": "vitest run --coverage"
29
+ "test:coverage": "vitest run --coverage",
30
+ "eval": "bun run ./evals/run.ts"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@biomejs/biome": "^2.3.13",
@@ -11,7 +11,7 @@ Dust is a lightweight planning system. The `.dust/` directory contains:
11
11
  - `{{bin}} check` - Run quality gates (do this before and after work)
12
12
  - `{{bin}} next` - Show tasks ready to work on
13
13
  - `{{bin}} list [type]` - List artifacts (tasks, ideas, goals, facts)
14
- - `{{bin}} validate` - Check .dust/ files for errors
14
+ - `{{bin}} lint markdown` - Check .dust/ files for errors
15
15
 
16
16
  **Workflow:** Pick a task, implement it, delete the task file, commit atomically.
17
17
 
@@ -11,7 +11,7 @@ Follow these steps:
11
11
  - What this goal means in practice
12
12
  - Why it matters for the project
13
13
  - How to evaluate whether work supports this goal
14
- 5. Run `{{bin}} validate` to catch any formatting issues
14
+ 5. Run `{{bin}} lint markdown` to catch any formatting issues
15
15
  6. Create a single atomic commit with a message in the format "Add goal: <title>"
16
16
  7. Push your commit to the remote repository
17
17
 
@@ -6,6 +6,6 @@ Follow these steps:
6
6
  2. Create a new markdown file in `.dust/ideas/` with a descriptive kebab-case name (e.g., `improve-error-messages.md`)
7
7
  3. Add a title as the first line using an H1 heading (e.g., `# Improve error messages`)
8
8
  4. Write a brief description of the potential change or improvement
9
- 5. Run `{{bin}} validate` to catch any issues with the idea file format
9
+ 5. Run `{{bin}} lint markdown` to catch any issues with the idea file format
10
10
  6. Create a single atomic commit with a message in the format "Add idea: <title>"
11
11
  7. Push your commit to the remote repository
@@ -12,7 +12,7 @@ Follow these steps:
12
12
  6. Add a `## Goals` section with links to relevant goals this task supports (e.g., `- [Goal Name](../goals/goal-name.md)`)
13
13
  7. Add a `## Blocked by` section listing any tasks that must complete first, or `(none)` if there are no blockers
14
14
  8. Add a `## Definition of done` section with a checklist of completion criteria using `- [ ]` for each item
15
- 9. Run `{{bin}} validate` to catch any issues with the task format
15
+ 9. Run `{{bin}} lint markdown` to catch any issues with the task format
16
16
  10. Create a single atomic commit with a message in the format "Add task: <title>" that includes:
17
17
  - The new task file
18
18
  - Deletion of any ideas that were fully realized
@@ -1,24 +1,27 @@
1
- dust - A lightweight planning system for human-AI collaboration
1
+ 💨 dust - A workflow tool for keeping AI coding agents on track.
2
2
 
3
3
  Usage: {{bin}} <command> [options]
4
4
 
5
5
  Commands:
6
- init Initialize a new Dust repository
7
- validate Run validation checks on .dust/ files
8
- list [type] List items (tasks, ideas, goals, facts)
9
- next Show tasks ready to work on (not blocked)
10
- check Run project-defined quality gate hook
11
- agent [cmd] Agent-specific guidance (work, tasks, goals, ideas, help)
12
- loop Run continuous Claude iteration on tasks
13
- help Show this help message
6
+ init Initialize a new Dust repository
7
+ lint markdown Run lint checks on .dust/ files
8
+ list List all items (tasks, ideas, goals, facts)
9
+ list tasks List tasks (actionable work with definitions of done)
10
+ list ideas List ideas (vague proposals, convert to tasks when ready)
11
+ list goals List goals (guiding principles, stable, rarely change)
12
+ list facts List facts (documentation of current system state)
13
+ next Show tasks ready to work on (not blocked)
14
+ check Run project-defined quality gate hook
15
+ agent Agent greeting and routing instructions
16
+ agent help Show agent guidance
17
+ agent pick task Pick the next task to work on
18
+ agent implement task Implement a task
19
+ agent new task Create a new task
20
+ agent new goal Create a new goal
21
+ agent new idea Working with ideas
22
+ agent understand goals Understanding goals
23
+ loop Run continuous Claude iteration on tasks
24
+ pre-push Git pre-push hook validation
25
+ help Show this help message
14
26
 
15
- Examples:
16
- {{bin}} init
17
- {{bin}} validate
18
- {{bin}} list tasks
19
- {{bin}} list
20
- {{bin}} next
21
- {{bin}} check
22
- {{bin}} agent work
23
-
24
- If you are an AI agent, run `{{bin}} agent` now!
27
+ 🤖 If you are an AI agent, run `{{bin}} agent` to get started!
@@ -0,0 +1,14 @@
1
+ Hello Agent, welcome to dust!
2
+
3
+ CRITICAL: You MUST run exactly ONE of the commands below before doing anything else.
4
+
5
+ Based on what the user asked, run the matching command NOW:
6
+
7
+ 1. "work", "go", "pick a task", "implement a task" → `{{bin}} agent pick task`
8
+ 2. "implement [task name]" → `{{bin}} agent implement task`
9
+ 3. "task: ..." or "add a task ..." → `{{bin}} agent new task`
10
+ 4. "goal: ..." or "add a goal ..." → `{{bin}} agent new goal`
11
+ 5. A vague idea about a potential change → `{{bin}} agent new idea`
12
+ 6. Anything else → `{{bin}} agent help`
13
+
14
+ Do NOT proceed without running one of these commands.
@@ -0,0 +1,35 @@
1
+ ## Dust Agent Guide
2
+
3
+ Dust is a lightweight planning system. The `.dust/` directory contains:
4
+
5
+ - **goals/** - Guiding principles (stable, rarely change)
6
+ - **ideas/** - Vague proposals (convert to tasks when ready)
7
+ - **tasks/** - Actionable work with definitions of done
8
+ - **facts/** - Documentation of current system state
9
+
10
+ **Key commands:**
11
+ - `{{bin}} check` - Run quality gates (do this before and after work)
12
+ - `{{bin}} next` - Show tasks ready to work on
13
+ - `{{bin}} list [type]` - List artifacts (tasks, ideas, goals, facts)
14
+ - `{{bin}} lint markdown` - Check .dust/ files for errors
15
+
16
+ **Workflow:** Pick a task, implement it, delete the task file, commit atomically.
17
+
18
+ For focused guidance, run:
19
+ - `{{bin}} agent pick task` - Pick the next task to work on
20
+ - `{{bin}} agent implement task` - Implement a task
21
+ - `{{bin}} agent new task` - Create a new task
22
+ - `{{bin}} agent new goal` - Create a new goal
23
+ - `{{bin}} agent new idea` - Working with ideas
24
+ - `{{bin}} agent understand goals` - Understanding goals
25
+
26
+ ### Configuring Agent Files
27
+
28
+ Projects using dust should add a minimal pointer to their agent configuration files (CLAUDE.md, AGENTS.md, etc.):
29
+
30
+ ```markdown
31
+ This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
32
+ Always run `dust agent` when you start working in this repository.
33
+ ```
34
+
35
+ This keeps agent instructions minimal and ensures agents get current documentation.
@@ -0,0 +1,24 @@
1
+ ## Implement a Task
2
+
3
+ Follow these steps:
4
+
5
+ 1. {{installDependenciesHint}} (unless already installed)
6
+ 2. Run `{{bin}} check` to verify the project is in a good state
7
+ 3. Implement the task
8
+ {{#unless hooksInstalled}}4. Run `{{bin}} check` before committing
9
+ 5.{{/unless}}{{#if hooksInstalled}}4.{{/if}} Create a single atomic commit that includes:
10
+ - All implementation changes
11
+ - Deletion of the completed task file
12
+ - Updates to any facts that changed
13
+ - Deletion of any ideas that were fully realized
14
+
15
+ 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.
16
+
17
+ Example: If the task title is "Add validation for user input", the commit message should be:
18
+ ```
19
+ Add validation for user input
20
+ ```
21
+
22
+ {{#unless hooksInstalled}}6.{{/unless}}{{#if hooksInstalled}}5.{{/if}} Push your commit to the remote repository
23
+
24
+ Keep your change small and focused. One task, one commit.
@@ -0,0 +1,21 @@
1
+ ## Adding a New Goal
2
+
3
+ Goals are guiding principles that persist across tasks. They define the "why" behind the work.
4
+
5
+ Follow these steps:
6
+
7
+ 1. Run `{{bin}} list goals` to see existing goals and avoid duplication
8
+ 2. Create a new markdown file in `.dust/goals/` with a descriptive kebab-case name (e.g., `cross-platform-support.md`)
9
+ 3. Add a title as the first line using an H1 heading (e.g., `# Cross-platform support`)
10
+ 4. Write a clear description explaining:
11
+ - What this goal means in practice
12
+ - Why it matters for the project
13
+ - How to evaluate whether work supports this goal
14
+ 5. Run `{{bin}} lint markdown` to catch any formatting issues
15
+ 6. Create a single atomic commit with a message in the format "Add goal: <title>"
16
+ 7. Push your commit to the remote repository
17
+
18
+ Goals should be:
19
+ - **Stable** - They rarely change once established
20
+ - **Actionable** - Tasks can be linked to them
21
+ - **Clear** - Anyone reading should understand what it means
@@ -0,0 +1,11 @@
1
+ ## Adding a New Idea
2
+
3
+ Follow these steps:
4
+
5
+ 1. Run `{{bin}} list ideas` to see all existing ideas and avoid duplicates
6
+ 2. Create a new markdown file in `.dust/ideas/` with a descriptive kebab-case name (e.g., `improve-error-messages.md`)
7
+ 3. Add a title as the first line using an H1 heading (e.g., `# Improve error messages`)
8
+ 4. Write a brief description of the potential change or improvement
9
+ 5. Run `{{bin}} lint markdown` to catch any issues with the idea file format
10
+ 6. Create a single atomic commit with a message in the format "Add idea: <title>"
11
+ 7. Push your commit to the remote repository
@@ -0,0 +1,20 @@
1
+ ## Adding a New Task
2
+
3
+ Follow these steps:
4
+
5
+ 1. Run `{{bin}} list ideas` to see all existing ideas
6
+ 2. Determine which ideas (if any) should be:
7
+ - **Deleted** - if the new task fully covers the idea
8
+ - **Updated** - if the idea's scope changes as a result of the task
9
+ 3. Create a new markdown file in `.dust/tasks/` with a descriptive kebab-case name (e.g., `add-user-authentication.md`)
10
+ 4. Add a title as the first line using an H1 heading (e.g., `# Add user authentication`)
11
+ 5. Write a comprehensive description of what needs to be done with technical details and references to relevant files
12
+ 6. Add a `## Goals` section with links to relevant goals this task supports (e.g., `- [Goal Name](../goals/goal-name.md)`)
13
+ 7. Add a `## Blocked by` section listing any tasks that must complete first, or `(none)` if there are no blockers
14
+ 8. Add a `## Definition of done` section with a checklist of completion criteria using `- [ ]` for each item
15
+ 9. Run `{{bin}} lint markdown` to catch any issues with the task format
16
+ 10. Create a single atomic commit with a message in the format "Add task: <title>" that includes:
17
+ - The new task file
18
+ - Deletion of any ideas that were fully realized
19
+ - Updates to any ideas whose scope changed
20
+ 11. Push your commit to the remote repository
@@ -0,0 +1,7 @@
1
+ ## Pick a Task
2
+
3
+ Follow these steps:
4
+
5
+ 1. Run `{{bin}} next` to see available tasks
6
+ 2. Pick ONE task and read its file to understand the requirements
7
+ 3. Run `{{bin}} agent implement task` for instructions about how to implement
@@ -0,0 +1,9 @@
1
+ ## Understanding Goals
2
+
3
+ **List goals:** `{{bin}} list goals`
4
+
5
+ Goals live in `.dust/goals/` as markdown files. They define the project's guiding principles and priorities.
6
+
7
+ Goals are linked from tasks to show which principles each task supports. When working on a task, you can read its linked goals for context on why the work matters.
8
+
9
+ Goals are stable—they rarely change. Tasks come and go, but goals persist.
@@ -0,0 +1,5 @@
1
+ # Agent Instructions
2
+
3
+ This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
4
+
5
+ Always immediately run `{{dustCommand}} agent` when you start working in this repository.
@@ -0,0 +1,5 @@
1
+ # Claude Code Instructions
2
+
3
+ This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
4
+
5
+ Always immediately run `{{dustCommand}} agent` when you start working in this repository.
@@ -0,0 +1,27 @@
1
+ 💨 dust - A workflow tool for keeping AI coding agents on track.
2
+
3
+ Usage: {{bin}} <command> [options]
4
+
5
+ Commands:
6
+ init Initialize a new Dust repository
7
+ lint markdown Run lint checks on .dust/ files
8
+ list List all items (tasks, ideas, goals, facts)
9
+ list tasks List tasks (actionable work with definitions of done)
10
+ list ideas List ideas (vague proposals, convert to tasks when ready)
11
+ list goals List goals (guiding principles, stable, rarely change)
12
+ list facts List facts (documentation of current system state)
13
+ next Show tasks ready to work on (not blocked)
14
+ check Run project-defined quality gate hook
15
+ agent Agent greeting and routing instructions
16
+ agent help Show agent guidance
17
+ agent pick task Pick the next task to work on
18
+ agent implement task Implement a task
19
+ agent new task Create a new task
20
+ agent new goal Create a new goal
21
+ agent new idea Working with ideas
22
+ agent understand goals Understanding goals
23
+ loop Run continuous Claude iteration on tasks
24
+ pre-push Git pre-push hook validation
25
+ help Show this help message
26
+
27
+ 🤖 If you are an AI agent, run `{{bin}} agent` to get started!