@joshski/dust 0.1.5 → 0.1.7

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
@@ -4,104 +4,38 @@ A lightweight planning system and work tracker optimised for humans working with
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
- ## Usage
7
+ ## Getting Started
8
8
 
9
- Document your system and any future plans in a [.dust](./.dust) directory in your repository:
9
+ Install dust:
10
10
 
11
+ ```bash
12
+ npm install -g @joshski/dust
11
13
  ```
12
- .dust/
13
- ├── goals/ # Mission statements explaining why the project exists
14
- ├── ideas/ # Brief notes about future tasks (intentionally vague)
15
- ├── tasks/ # Detailed work plans with dependencies and definition of done
16
- ├── facts/ # Current state: design, architecture, rules, invariants
17
- └── hooks/ # Executable scripts for CLI integration (e.g., quality gates)
18
- ```
19
-
20
- The `goals`, `ideas`, `tasks`, and `facts` directories should be flat (no subdirectories) and contain only markdown files with slug-style names (alphanumeric and hyphens only).
21
-
22
- The `hooks` directory contains executable scripts that integrate with the `dust` CLI. For example, the `check` hook is run by `dust check` to execute project-defined quality gates.
23
-
24
- ## CLI Commands
25
14
 
26
- The `dust` CLI provides commands for managing your planning repository:
27
-
28
- | Command | Description |
29
- |---------|-------------|
30
- | `dust init` | Initialize a new Dust repository with the standard directory structure |
31
- | `dust prompt <name>` | Output a prompt by name from the `prompts/` directory |
32
- | `dust validate` | Run validation checks on `.dust/` files (links, task structure, naming) |
33
- | `dust list [type]` | List items by type (tasks, ideas, goals, facts) or all if no type specified |
34
- | `dust next` | Show tasks ready to work on (not blocked by other incomplete tasks) |
35
- | `dust check` | Run `validate` then execute the project's quality gate hook at `.dust/hooks/check` |
36
- | `dust help` | Show help message with all available commands |
37
-
38
- ### Examples
15
+ Initialize dust in your repository:
39
16
 
40
17
  ```bash
41
- # Initialize a new project
42
- dust init
43
-
44
- # List all tasks
45
- dust list tasks
46
-
47
- # List everything (tasks, ideas, goals, facts)
48
- dust list
49
-
50
- # Show tasks that are ready to start
51
- dust next
52
-
53
- # Validate all markdown files under ./.dust
54
- dust validate
55
-
56
- # Run quality checks (validation + custom hook)
57
- dust check
58
-
59
- # Output a prompt by name
60
- dust prompt work
18
+ npx dust init
19
+ # or
20
+ bunx dust init
61
21
  ```
62
22
 
63
- ## Workflow
64
-
65
- Dust is designed for successive cycles of human planning (AI-assisted, of course) followed by agent autonomy, followed by human planning, and so on.
66
-
67
- In order for work to begin, there must be a task. A worker (an AI agent or human) chooses any task to work on. In a team environment, the worker must “claim” the task i.e. let the team know they are working on it. The team can use a version control system (like git) claim the task by making a branch with the same name as the task. If any attempt to claim fails (e.g. a branch with that name already exists) then the agent must choose an alternative task.
68
-
69
- When the worker completes their task, they make a single commit that includes the work, but also deletes the task, and removes any references to the task. The commit should often update one or more facts as well.
70
-
71
- Tasks should be small units of work that can be completed quickly and result in a single commit that leaves the system in a reasonable state (e.g. no broken or half-implemented features exposed to end users). When in doubt, workers are encouraged to split the task into smaller sub-tasks, and abandon the attempt to finish any ambitious work in one go.
72
-
73
- Over time, new ideas emerge, and ideas become more detailed plans in the form of "tasks". This should be deferred until the last responsible moment. Since humans like control over plans, ideas become plans in the "human-in-the-loop" phase at the start of a sprint.
23
+ ## How It Works
74
24
 
75
- ## Tasks
76
-
77
- Tasks are the only markdown files that have a strict structure. Tasks must have each of the following subheadings:
25
+ The [.dust](./.dust) directory in your repository contains 4 sets of markdown files:
78
26
 
79
27
  ```
80
- ## Goals
81
- ## Blocked by
82
- ## Definition of done
28
+ .dust/
29
+ ├── goals/ # Mission statements explaining why the project exists
30
+ ├── ideas/ # Brief notes about future tasks (intentionally vague)
31
+ ├── tasks/ # Detailed work plans with dependencies and definition of done
32
+ └── facts/ # Current state: design, architecture, rules, invariants
83
33
  ```
84
34
 
85
- * Goals - a list of relative links to other markdown files, always under ./.dust/goals
86
- * Blocked by - a list relative links to other markdown files, each of which nominates a task that must be implemented before this task can be started.
87
- * Definition of done - A short description of how the implementor of the task can decide when the task has been completed successfully
88
-
89
- These special headings and sections are required, but the remainder of the document is free form.
90
-
91
- ## The single commit
92
-
93
- Each task should be a small unit of work. If it was underestimated, the agent implementing the task should commit any progress that does not have a negative impact on end users, and create another "follow up" task to complete the remainder of the work.
94
-
95
- ## Links between documents
35
+ These files are used to facilitate exploration and management of AI agent workflow.
96
36
 
97
- Documents should include links to all relevant related documents, regardless of the type. These should be relative links in markdown format. The link text should typically match the title of the target document.
98
-
99
- ## Change history
100
-
101
- Commits delete tasks, but commit history can be traversed to retrieve the thinking behind any changes. Tools can be implemented to make this easier or build indexes. The current working copy is kept intentionally free of this detail, to keep commits clean and reduce noise in the current repository state.
102
-
103
- ## Hygiene
37
+ ## Workflow
104
38
 
105
- A linter can be used for static analysis of task files, and to ensure there are no broken relative links as the result of any changes.
39
+ 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.
106
40
 
107
- Regular semantic and logic checks are expected to be carried out to ensure ideas have not drifted from reality. This would typically happen after one or more commits, e.g. at the end of a sprint.
41
+ 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.
package/dist/dust.js CHANGED
@@ -27,22 +27,22 @@ var AGENT_SUBCOMMANDS = [
27
27
  "help"
28
28
  ];
29
29
  function generateAgentGreeting(settings) {
30
- return loadTemplate("agent-greeting", { bin: settings.binaryPath });
30
+ return loadTemplate("agent-greeting", { bin: settings.dustCommand });
31
31
  }
32
32
  function generateWorkInstructions(settings) {
33
- return loadTemplate("agent-work", { bin: settings.binaryPath });
33
+ return loadTemplate("agent-work", { bin: settings.dustCommand });
34
34
  }
35
35
  function generateTasksInstructions(settings) {
36
- return loadTemplate("agent-tasks", { bin: settings.binaryPath });
36
+ return loadTemplate("agent-tasks", { bin: settings.dustCommand });
37
37
  }
38
38
  function generateGoalsInstructions(settings) {
39
- return loadTemplate("agent-goals", { bin: settings.binaryPath });
39
+ return loadTemplate("agent-goals", { bin: settings.dustCommand });
40
40
  }
41
41
  function generateIdeasInstructions(settings) {
42
- return loadTemplate("agent-ideas", { bin: settings.binaryPath });
42
+ return loadTemplate("agent-ideas", { bin: settings.dustCommand });
43
43
  }
44
44
  function generateAgentHelp(settings) {
45
- return loadTemplate("agent-help", { bin: settings.binaryPath });
45
+ return loadTemplate("agent-help", { bin: settings.dustCommand });
46
46
  }
47
47
  async function agent(ctx, args, settings) {
48
48
  const subcommand = args[0];
@@ -76,6 +76,54 @@ async function agent(ctx, args, settings) {
76
76
  // lib/cli/check.ts
77
77
  import { spawn } from "node:child_process";
78
78
 
79
+ // lib/cli/settings.ts
80
+ import { join as join2 } from "node:path";
81
+ var DEFAULT_SETTINGS = {
82
+ dustCommand: "npx dust"
83
+ };
84
+ function detectDustCommand(cwd, fs) {
85
+ if (fs.exists(join2(cwd, "bun.lockb"))) {
86
+ return "bunx dust";
87
+ }
88
+ if (fs.exists(join2(cwd, "pnpm-lock.yaml"))) {
89
+ return "pnpx dust";
90
+ }
91
+ if (fs.exists(join2(cwd, "package-lock.json"))) {
92
+ return "npx dust";
93
+ }
94
+ if (process.env.BUN_INSTALL) {
95
+ return "bunx dust";
96
+ }
97
+ return "npx dust";
98
+ }
99
+ async function loadSettings(cwd, fs) {
100
+ const settingsPath = join2(cwd, ".dust", "config", "settings.json");
101
+ if (!fs.exists(settingsPath)) {
102
+ return {
103
+ dustCommand: detectDustCommand(cwd, fs)
104
+ };
105
+ }
106
+ try {
107
+ const content = await fs.readFile(settingsPath);
108
+ const parsed = JSON.parse(content);
109
+ if (!parsed.dustCommand) {
110
+ return {
111
+ ...DEFAULT_SETTINGS,
112
+ ...parsed,
113
+ dustCommand: detectDustCommand(cwd, fs)
114
+ };
115
+ }
116
+ return {
117
+ ...DEFAULT_SETTINGS,
118
+ ...parsed
119
+ };
120
+ } catch {
121
+ return {
122
+ dustCommand: detectDustCommand(cwd, fs)
123
+ };
124
+ }
125
+ }
126
+
79
127
  // lib/cli/validate.ts
80
128
  import { dirname as dirname2, resolve } from "node:path";
81
129
  var REQUIRED_HEADINGS = ["## Goals", "## Blocked by", "## Definition of done"];
@@ -240,66 +288,121 @@ async function validate(ctx, fs, _args, glob) {
240
288
  }
241
289
 
242
290
  // lib/cli/check.ts
243
- function createProcessRunner(spawnFn) {
291
+ function createBufferedRunner(spawnFn) {
244
292
  return {
245
- spawn: (command, args, options) => {
293
+ run: (command, cwd) => {
246
294
  return new Promise((resolve2) => {
247
- const proc = spawnFn(command, args, options);
248
- proc.on("close", (code) => resolve2(code ?? 1));
249
- proc.on("error", () => resolve2(1));
295
+ const proc = spawnFn(command, [], { cwd, shell: true });
296
+ const chunks = [];
297
+ proc.stdout?.on("data", (data) => {
298
+ chunks.push(data.toString());
299
+ });
300
+ proc.stderr?.on("data", (data) => {
301
+ chunks.push(data.toString());
302
+ });
303
+ proc.on("close", (code) => {
304
+ resolve2({ exitCode: code ?? 1, output: chunks.join("") });
305
+ });
306
+ proc.on("error", (err) => {
307
+ resolve2({ exitCode: 1, output: err.message });
308
+ });
250
309
  });
251
310
  }
252
311
  };
253
312
  }
254
- var defaultProcessRunner = createProcessRunner(spawn);
255
- async function check(ctx, fs, _args, runner = defaultProcessRunner, glob) {
256
- if (glob) {
257
- const validationResult = await validate(ctx, fs, [], glob);
258
- if (validationResult.exitCode !== 0) {
259
- return validationResult;
313
+ var defaultBufferedRunner = createBufferedRunner(spawn);
314
+ async function runConfiguredChecks(checks, cwd, runner) {
315
+ const promises = checks.map(async (check) => {
316
+ const { exitCode, output } = await runner.run(check.command, cwd);
317
+ return {
318
+ name: check.name,
319
+ command: check.command,
320
+ exitCode,
321
+ output
322
+ };
323
+ });
324
+ return Promise.all(promises);
325
+ }
326
+ async function runValidationCheck(ctx, fs, glob) {
327
+ const outputLines = [];
328
+ const bufferedCtx = {
329
+ cwd: ctx.cwd,
330
+ stdout: (msg) => outputLines.push(msg),
331
+ stderr: (msg) => outputLines.push(msg)
332
+ };
333
+ const result = await validate(bufferedCtx, fs, [], glob);
334
+ return {
335
+ name: "validate",
336
+ command: "dust validate",
337
+ exitCode: result.exitCode,
338
+ output: outputLines.join(`
339
+ `),
340
+ isBuiltIn: true
341
+ };
342
+ }
343
+ function displayResults(results, ctx) {
344
+ const passed = results.filter((r) => r.exitCode === 0);
345
+ const failed = results.filter((r) => r.exitCode !== 0);
346
+ for (const result of results) {
347
+ if (result.exitCode === 0) {
348
+ ctx.stdout(`✓ ${result.name}`);
349
+ } else {
350
+ ctx.stdout(`✗ ${result.name}`);
260
351
  }
352
+ }
353
+ for (const result of failed) {
261
354
  ctx.stdout("");
355
+ ctx.stdout(`> ${result.command}`);
356
+ if (result.output.trim()) {
357
+ ctx.stdout(result.output.trimEnd());
358
+ }
262
359
  }
263
- const hookPath = `${ctx.cwd}/.dust/hooks/check`;
264
- if (!fs.exists(hookPath)) {
265
- ctx.stderr("Error: No check hook found at .dust/hooks/check");
360
+ ctx.stdout("");
361
+ ctx.stdout(`${passed.length}/${results.length} checks passed`);
362
+ return failed.length > 0 ? 1 : 0;
363
+ }
364
+ async function check(ctx, fs, _args, glob, bufferedRunner = defaultBufferedRunner) {
365
+ const settings = await loadSettings(ctx.cwd, fs);
366
+ if (!settings.checks || settings.checks.length === 0) {
367
+ ctx.stderr("Error: No checks configured in .dust/config/settings.json");
266
368
  ctx.stderr("");
267
- ctx.stderr("To create a check hook:");
268
- ctx.stderr(" 1. Create the hooks directory: mkdir -p .dust/hooks");
269
- ctx.stderr(" 2. Create the check script: touch .dust/hooks/check");
270
- ctx.stderr(" 3. Make it executable: chmod +x .dust/hooks/check");
271
- ctx.stderr(" 4. Add your quality checks (tests, linting, etc.)");
369
+ ctx.stderr("Add checks to your settings.json:");
370
+ ctx.stderr(" {");
371
+ ctx.stderr(' "checks": [');
372
+ ctx.stderr(' { "name": "lint", "command": "npm run lint" },');
373
+ ctx.stderr(' { "name": "test", "command": "npm test" }');
374
+ ctx.stderr(" ]");
375
+ ctx.stderr(" }");
272
376
  return { exitCode: 1 };
273
377
  }
274
- const exitCode = await runner.spawn(hookPath, [], {
275
- cwd: ctx.cwd,
276
- stdio: "inherit"
277
- });
378
+ const checkPromises = [];
379
+ if (glob) {
380
+ checkPromises.push(runValidationCheck(ctx, fs, glob));
381
+ }
382
+ checkPromises.push(runConfiguredChecks(settings.checks, ctx.cwd, bufferedRunner));
383
+ const promiseResults = await Promise.all(checkPromises);
384
+ const results = [];
385
+ for (const result of promiseResults) {
386
+ if (Array.isArray(result)) {
387
+ results.push(...result);
388
+ } else {
389
+ results.push(result);
390
+ }
391
+ }
392
+ const exitCode = displayResults(results, ctx);
278
393
  return { exitCode };
279
394
  }
280
395
 
281
396
  // lib/cli/init.ts
282
397
  var DUST_DIRECTORIES = ["goals", "ideas", "tasks", "facts"];
283
- var DEFAULT_GOAL = `# Project Goal
398
+ var USE_DUST_FACT = `# Use dust for planning
284
399
 
285
- Describe the high-level mission of this project.
400
+ This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
286
401
  `;
287
- function detectPackageRunner(cwd, fs) {
288
- if (process.versions.bun) {
289
- return "bunx";
290
- }
291
- if (fs.exists(`${cwd}/bun.lockb`)) {
292
- return "bunx";
293
- }
294
- if (fs.exists(`${cwd}/pnpm-lock.yaml`)) {
295
- return "pnpx";
296
- }
297
- return "npx";
298
- }
299
402
  async function init(ctx, fs, _args) {
300
403
  const dustPath = `${ctx.cwd}/.dust`;
301
- const runner = detectPackageRunner(ctx.cwd, fs);
302
- const agentInstruction = `Always immediately run \`${runner} dust agent\` when you start working in this repository.`;
404
+ const dustCommand = detectDustCommand(ctx.cwd, fs);
405
+ const agentInstruction = `Always immediately run \`${dustCommand} agent\` when you start working in this repository.`;
303
406
  if (fs.exists(dustPath)) {
304
407
  ctx.stdout("Note: .dust directory already exists, skipping creation");
305
408
  } else {
@@ -307,16 +410,16 @@ async function init(ctx, fs, _args) {
307
410
  for (const dir of DUST_DIRECTORIES) {
308
411
  await fs.mkdir(`${dustPath}/${dir}`, { recursive: true });
309
412
  }
310
- await fs.writeFile(`${dustPath}/goals/project-goal.md`, DEFAULT_GOAL);
413
+ await fs.writeFile(`${dustPath}/facts/use-dust-for-planning.md`, USE_DUST_FACT);
311
414
  ctx.stdout("Initialized Dust repository in .dust/");
312
415
  ctx.stdout(`Created directories: ${DUST_DIRECTORIES.join(", ")}`);
313
- ctx.stdout("Created initial goal: .dust/goals/project-goal.md");
416
+ ctx.stdout("Created initial fact: .dust/facts/use-dust-for-planning.md");
314
417
  }
315
418
  const claudeMdPath = `${ctx.cwd}/CLAUDE.md`;
316
419
  if (fs.exists(claudeMdPath)) {
317
420
  ctx.stdout(`Warning: CLAUDE.md already exists. Consider adding: "${agentInstruction}"`);
318
421
  } else {
319
- const claudeContent = loadTemplate("claude-md", { runner });
422
+ const claudeContent = loadTemplate("claude-md", { dustCommand });
320
423
  await fs.writeFile(claudeMdPath, claudeContent);
321
424
  ctx.stdout("Created CLAUDE.md with agent instructions");
322
425
  }
@@ -324,7 +427,7 @@ async function init(ctx, fs, _args) {
324
427
  if (fs.exists(agentsMdPath)) {
325
428
  ctx.stdout(`Warning: AGENTS.md already exists. Consider adding: "${agentInstruction}"`);
326
429
  } else {
327
- const agentsContent = loadTemplate("agents-md", { runner });
430
+ const agentsContent = loadTemplate("agents-md", { dustCommand });
328
431
  await fs.writeFile(agentsMdPath, agentsContent);
329
432
  ctx.stdout("Created AGENTS.md with agent instructions");
330
433
  }
@@ -443,61 +546,9 @@ async function next(ctx, fs, _args) {
443
546
  return { exitCode: 0 };
444
547
  }
445
548
 
446
- // lib/cli/prompt.ts
447
- async function prompt(ctx, fs, args) {
448
- const promptsDir = `${ctx.cwd}/prompts`;
449
- if (args.length === 0) {
450
- ctx.stderr("Usage: dust prompt <name>");
451
- ctx.stderr("Example: dust prompt work");
452
- return { exitCode: 1 };
453
- }
454
- const promptName = args[0];
455
- const promptFile = `${promptsDir}/${promptName}.md`;
456
- if (!fs.exists(promptFile)) {
457
- ctx.stderr(`Error: Prompt '${promptName}' not found`);
458
- ctx.stderr("Available prompts:");
459
- try {
460
- const files = await fs.readdir(promptsDir);
461
- const prompts = files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, ""));
462
- for (const p of prompts) {
463
- ctx.stderr(` ${p}`);
464
- }
465
- } catch {
466
- ctx.stderr(" (no prompts directory found)");
467
- }
468
- return { exitCode: 1 };
469
- }
470
- const content = await fs.readFile(promptFile);
471
- ctx.stdout(content);
472
- return { exitCode: 0 };
473
- }
474
-
475
- // lib/cli/settings.ts
476
- import { join as join2 } from "node:path";
477
- var DEFAULT_SETTINGS = {
478
- binaryPath: "dust"
479
- };
480
- async function loadSettings(cwd, fs) {
481
- const settingsPath = join2(cwd, ".dust", "config", "settings.json");
482
- if (!fs.exists(settingsPath)) {
483
- return DEFAULT_SETTINGS;
484
- }
485
- try {
486
- const content = await fs.readFile(settingsPath);
487
- const parsed = JSON.parse(content);
488
- return {
489
- ...DEFAULT_SETTINGS,
490
- ...parsed
491
- };
492
- } catch {
493
- return DEFAULT_SETTINGS;
494
- }
495
- }
496
-
497
549
  // lib/cli/main.ts
498
550
  var COMMANDS = [
499
551
  "init",
500
- "prompt",
501
552
  "validate",
502
553
  "list",
503
554
  "next",
@@ -506,9 +557,9 @@ var COMMANDS = [
506
557
  "help"
507
558
  ];
508
559
  function generateHelpText(settings) {
509
- return loadTemplate("help", { bin: settings.binaryPath });
560
+ return loadTemplate("help", { bin: settings.dustCommand });
510
561
  }
511
- var HELP_TEXT = generateHelpText({ binaryPath: "dust" });
562
+ var HELP_TEXT = generateHelpText({ dustCommand: "dust" });
512
563
  function isHelpRequest(command) {
513
564
  return !command || command === "help" || command === "--help" || command === "-h";
514
565
  }
@@ -519,8 +570,6 @@ async function runCommand(command, commandArgs, ctx, fs, glob, settings) {
519
570
  switch (command) {
520
571
  case "init":
521
572
  return init(ctx, fs, commandArgs);
522
- case "prompt":
523
- return prompt(ctx, fs, commandArgs);
524
573
  case "validate":
525
574
  return validate(ctx, fs, commandArgs, glob);
526
575
  case "list":
@@ -528,7 +577,7 @@ async function runCommand(command, commandArgs, ctx, fs, glob, settings) {
528
577
  case "next":
529
578
  return next(ctx, fs, commandArgs);
530
579
  case "check":
531
- return check(ctx, fs, commandArgs, defaultProcessRunner, glob);
580
+ return check(ctx, fs, commandArgs, glob);
532
581
  case "agent":
533
582
  return agent(ctx, commandArgs, settings);
534
583
  case "help":
@@ -548,7 +597,7 @@ async function main(options) {
548
597
  }
549
598
  if (!isValidCommand(command)) {
550
599
  ctx.stderr(`Unknown command: ${command}`);
551
- ctx.stderr(`Run '${settings.binaryPath} help' for available commands`);
600
+ ctx.stderr(`Run '${settings.dustCommand} help' for available commands`);
552
601
  return { exitCode: 1 };
553
602
  }
554
603
  return runCommand(command, commandArgs, ctx, fs, glob, settings);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A lightweight planning system for human-AI collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,10 +2,43 @@ Hello Claude, welcome to dust!
2
2
 
3
3
  Your goal today is to make ONE SMALL CHANGE and then commit and push your changes.
4
4
 
5
- Based on what the user asked you to do, run the appropriate command:
5
+ The user might be prompting you to do one of many things. Depending on their intention, run
6
+ one of the following commands:
6
7
 
7
- - If the user mentioned "work" run `{{bin}} agent work`
8
- - If the user mentioned "task" or "tasks" → run `{{bin}} agent tasks`
9
- - If the user mentioned "goal" or "goals" → run `{{bin}} agent goals`
10
- - If the user mentioned "idea" or "ideas" → run `{{bin}} agent ideas`
11
- - For anything else → run `{{bin}} agent help`
8
+ If your prompt is something like one of these:
9
+
10
+ * "work"
11
+ * "get to work"
12
+ * "go"
13
+ * "pick a task"
14
+
15
+ ...then run:
16
+
17
+ `{{bin}} agent work`
18
+
19
+ If your prompt is something like one of these:
20
+
21
+ * "add a task ..."
22
+ * "task: ..."
23
+
24
+ ...then you should create a new task. To find out how, you should immediately run:
25
+
26
+ `{{bin}} agent tasks`
27
+
28
+ If your prompt is something like one of these:
29
+
30
+ * "add a goal ..."
31
+ * "goal: ..."
32
+
33
+ ...then you should create a new goal. To find out how, you should immediately run:
34
+
35
+ `{{bin}} agent goals`
36
+
37
+ If it sounds like the prompt is a vague idea about some potential change to the system,
38
+ then you should create an idea. To find out how, you should immediately run:
39
+
40
+ `{{bin}} agent ideas`
41
+
42
+ If it's still not clear, run:
43
+
44
+ `{{bin}} agent help`
@@ -21,3 +21,14 @@ For focused guidance, run:
21
21
  - `{{bin}} agent tasks` - Task management
22
22
  - `{{bin}} agent goals` - Understanding goals
23
23
  - `{{bin}} agent ideas` - Working with ideas
24
+
25
+ ### Configuring Agent Files
26
+
27
+ Projects using dust should add a minimal pointer to their agent configuration files (CLAUDE.md, AGENTS.md, etc.):
28
+
29
+ ```markdown
30
+ This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
31
+ Always run `dust agent` when you start working in this repository.
32
+ ```
33
+
34
+ This keeps agent instructions minimal and ensures agents get current documentation.
@@ -7,7 +7,6 @@ Ideas live in `.dust/ideas/` as markdown files. They are intentionally vague pro
7
7
  **Converting an idea to tasks:**
8
8
  1. Read the idea file to understand the proposal
9
9
  2. Break it down into concrete, actionable tasks
10
- 3. Create task files in `.dust/tasks/` with clear definitions of done
11
- 4. Delete the idea file once it's fully captured in tasks
10
+ 3. Run `{{bin}} agent tasks` to see the workflow to apply to each individual task.
12
11
 
13
12
  Ideas are cheap to create and easy to discard. Not every idea becomes a task.
@@ -1,15 +1,48 @@
1
- ## Task Management
1
+ # Task Management
2
2
 
3
- **List tasks:** `{{bin}} list tasks`
3
+ **List all tasks:** `{{bin}} list tasks`
4
4
  **Find ready tasks:** `{{bin}} next`
5
5
 
6
- Tasks live in `.dust/tasks/` as markdown files. Each task has:
7
- - `## Goals` - Links to goals this task supports
8
- - `## Blocked by` - Tasks that must complete first
9
- - `## Definition of done` - Checklist of completion criteria
6
+ ## Implementing a Task
10
7
 
11
- A task is ready when "Blocked by" is empty or says "(none)".
8
+ When implementing a task, follow these steps:
12
9
 
13
- **Creating tasks:** Write a new markdown file in `.dust/tasks/` following the format above.
10
+ 1. Install dependencies first (e.g., `npm install`, `bundle install`)
11
+ 2. Run `{{bin}} check` before implementing anything to verify the project is in a good state
12
+ 3. Read the task file and understand all requirements in "Definition of done"
13
+ 4. Implement the changes, checking off items as you complete them
14
+ 5. Run `{{bin}} check` after implementing all changes to verify nothing is broken
15
+ 6. Create a single atomic commit that includes:
16
+ - All implementation changes
17
+ - Deletion of the completed task file
18
+ - Updates to any facts that changed
19
+ - Deletion of any ideas that were fully realized
14
20
 
15
- **Completing tasks:** Delete the task file in your commit after implementation.
21
+ ## Adding a Task from scratch
22
+
23
+ To add a new task, follow these steps:
24
+
25
+ 1. Create a new markdown file in `.dust/tasks/` with a descriptive kebab-case name (e.g., `add-user-authentication.md`)
26
+ 2. Add a title as the first line using an H1 heading (e.g., `# Add user authentication`)
27
+ 3. Write a comprehensive description of what needs to be done with technical details and references to relevant files
28
+ 4. Add a `## Goals` section with links to relevant goals this task supports (e.g., `- [Goal Name](../goals/goal-name.md)`)
29
+ 5. Add a `## Blocked by` section listing any tasks that must complete first, or `(none)` if there are no blockers
30
+ 6. Add a `## Definition of done` section with a checklist of completion criteria using `- [ ]` for each item
31
+ 7. Run `{{bin}} validate` to catch any issues with the task format
32
+ 8. Create a single atomic commit with a message in the format "Add task: <title>"
33
+
34
+ ## Converting an Idea into a Task (fleshing out an idea)
35
+
36
+ To add a new task, follow these steps:
37
+
38
+ 1. Create a new markdown file in `.dust/tasks/` with a descriptive kebab-case name (e.g., `add-user-authentication.md`)
39
+ 2. Add a title as the first line using an H1 heading (e.g., `# Add user authentication`)
40
+ 3. Write a comprehensive description of what needs to be done with technical details and references to relevant files
41
+ 4. Add a `## Goals` section with links to relevant goals this task supports (e.g., `- [Goal Name](../goals/goal-name.md)`)
42
+ 5. Add a `## Blocked by` section listing any tasks that must complete first, or `(none)` if there are no blockers
43
+ 6. Add a `## Definition of done` section with a checklist of completion criteria using `- [ ]` for each item
44
+ 7. Run `{{bin}} validate` to catch any issues with the task format or regressions
45
+ 8. Create a single atomic commit with the message "Add task: <title>" that includes:
46
+ - All implementation changes
47
+ - Deletion of the idea file
48
+ - Updates to any facts that changed
@@ -2,4 +2,4 @@
2
2
 
3
3
  This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
4
4
 
5
- Always immediately run `{{runner}} dust agent` when you start working in this repository.
5
+ Always immediately run `{{dustCommand}} agent` when you start working in this repository.
@@ -2,4 +2,4 @@
2
2
 
3
3
  This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
4
4
 
5
- Always immediately run `{{runner}} dust agent` when you start working in this repository.
5
+ Always immediately run `{{dustCommand}} agent` when you start working in this repository.
@@ -4,7 +4,6 @@ Usage: {{bin}} <command> [options]
4
4
 
5
5
  Commands:
6
6
  init Initialize a new Dust repository
7
- prompt <name> Output a prompt by name (e.g., {{bin}} prompt work)
8
7
  validate Run validation checks on .dust/ files
9
8
  list [type] List items (tasks, ideas, goals, facts)
10
9
  next Show tasks ready to work on (not blocked)
@@ -14,7 +13,6 @@ Commands:
14
13
 
15
14
  Examples:
16
15
  {{bin}} init
17
- {{bin}} prompt work
18
16
  {{bin}} validate
19
17
  {{bin}} list tasks
20
18
  {{bin}} list
@@ -22,61 +20,4 @@ Examples:
22
20
  {{bin}} check
23
21
  {{bin}} agent work
24
22
 
25
- ---
26
-
27
- ## Agent Guide
28
-
29
- This section provides comprehensive guidance for AI agents working with dust.
30
-
31
- ### Directory Structure
32
-
33
- The `.dust/` directory contains all planning artifacts:
34
-
35
- - **`.dust/goals/`** - Mission statements and guiding principles
36
- - **`.dust/ideas/`** - Future feature notes and proposals (intentionally vague)
37
- - **`.dust/tasks/`** - Detailed work plans with dependencies and definitions of done
38
- - **`.dust/facts/`** - Documentation of current system state and architecture
39
- - **`.dust/hooks/`** - Executable scripts for quality gates (e.g., `check` hook)
40
-
41
- All files are markdown with slug-style names (lowercase, hyphens, no spaces).
42
-
43
- ### Working on Tasks
44
-
45
- **Run `{{bin}} check` before starting work** to verify the project is in a good state before making changes.
46
-
47
- Run `{{bin}} next` to find tasks ready to work on. Each task file contains:
48
-
49
- - `## Goals` - Links to goals this task supports
50
- - `## Blocked by` - Tasks that must complete first (empty or "(none)" means ready)
51
- - `## Definition of done` - Criteria for completion
52
-
53
- A task is **unblocked** when its "Blocked by" section is empty, says "(none)", or all referenced task files have been deleted.
54
-
55
- ### Completing a Task
56
-
57
- **Run `{{bin}} check` before committing** to ensure all quality gates pass.
58
-
59
- When finishing a task, create a single atomic commit that includes:
60
-
61
- 1. All implementation changes
62
- 2. Deletion of the completed task file
63
- 3. Updates to any facts that changed
64
- 4. Deletion of any ideas that were fully realized
65
- 5. Updates to any tasks that referenced this one in their "Blocked by" sections
66
-
67
- ### Common Workflows
68
-
69
- - **"Work on the next task"** - Run `{{bin}} next`, pick a task, implement it
70
- - **"Work on task X"** - Implement `.dust/tasks/X.md` directly
71
- - **"Convert idea Y to tasks"** - Break down `.dust/ideas/Y.md` into tasks
72
- - **"Validate facts"** - Check `.dust/facts/` for accuracy against the codebase
73
-
74
- ### Configuring Agent Files
75
-
76
- Projects using dust should add a minimal pointer to their agent configuration files (CLAUDE.md, AGENTS.md, etc.):
77
-
78
- ```markdown
79
- This project uses [dust](https://github.com/joshski/dust) for planning and documentation.\nAlways run `dust help` when you start working in this repository.
80
- ```
81
-
82
- This approach keeps agent instructions minimal, ensures agents get current documentation, and reduces maintenance burden.
23
+ For AI agent guidance, run `{{bin}} agent`.