Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/aidd-dev/skills/01-plan/assets/phase-template.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
type: phase
status: pending
---

Expand Down
1 change: 1 addition & 0 deletions plugins/aidd-dev/skills/01-plan/assets/plan-template.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
type: plan
objective: "{What must be true when done. One sentence.}"
status: pending
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The tracking file at `aidd_docs/tasks/<task-name>.md`, marked created or resumed
6. **Pre-flight.** For each step, list tools, secrets, API access, data, and permissions. Mark `[✓]` already satisfied, `[~]` soft (the agent self-serves), `[!]` hard (only the user can provide it). Collect every `[!]` now; when any stays unresolved, stop before the next step.
7. **Map.** Project the whole path as an ASCII map of steps, dependencies, tools, and blockers. Ask the user to confirm and iterate until they do.
8. **Scaffold.** Load [plan-template.md](../assets/plan-template.md), creating `aidd_docs/tasks/` when missing.
9. **Create.** Write `aidd_docs/tasks/<task-name>.md` from the template. Fill the frontmatter (`objective`, `success_condition`, `iteration: 0`, `status: pending`), the phases with their tasks and acceptance criteria, and the journey map.
9. **Create.** Write `aidd_docs/tasks/<task-name>.md` from the template. Fill the frontmatter (`type: plan`, `objective`, `success_condition`, `iteration: 0`, `status: pending`), the phases with their tasks and acceptance criteria, and the journey map.
10. **Spawn.** Read the orchestrator recipe from [03-autonomous-loop.md](./03-autonomous-loop.md) and hand it to the Agent tool with `<task-name>` filled in.

## Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
type: plan
objective: "{What must be true when done. One sentence.}"
success_condition: "{Runnable command that proves done. Example: 'npm test exits 0 AND coverage > 80%'}"
iteration: 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const test = require("node:test");

const root = path.resolve(__dirname, "../..");

// A template that stamps `status:` feeds the kanban board, and the board's type badge and
// --type filter read `type:` from the same frontmatter. aidd-dev shipped three templates
// without it and every plan and phase landed as `type: unknown` - 49 of 49 parents on this
// repository's own board. This ratchet keeps the next status-bearing template from
// repeating that silently.

/** The leading frontmatter block's lines, CRLF tolerated; undefined without one. */
function frontmatterLines(content) {
const lines = content.split(/\r?\n/u);
if (lines[0] !== "---") return undefined;

const closing = lines.indexOf("---", 1);
if (closing === -1) return undefined;

return lines.slice(1, closing);
}

function templateFiles(directory) {
const files = [];
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
const entryPath = path.join(directory, entry.name);
if (entry.isDirectory()) {
files.push(...templateFiles(entryPath));
} else if (entry.name.endsWith(".md")) {
files.push(entryPath);
}
}
return files;
}

function assetDirectories() {
const directories = [];
const pluginsPath = path.join(root, "plugins");

for (const plugin of fs.readdirSync(pluginsPath)) {
const skillsPath = path.join(pluginsPath, plugin, "skills");
if (!fs.existsSync(skillsPath)) continue;

for (const skill of fs.readdirSync(skillsPath)) {
const assetsPath = path.join(skillsPath, skill, "assets");
if (fs.existsSync(assetsPath)) directories.push(assetsPath);
}
}

return directories;
}

test("every plugin template that stamps a status also names its type", () => {
const untyped = [];
let statusBearing = 0;

for (const assetsPath of assetDirectories()) {
for (const filePath of templateFiles(assetsPath)) {
const lines = frontmatterLines(fs.readFileSync(filePath, "utf-8"));
if (lines === undefined || !lines.some((line) => /^status:/u.test(line))) continue;

statusBearing += 1;
if (!lines.some((line) => /^type:/u.test(line))) {
untyped.push(path.relative(root, filePath));
}
}
}

assert.ok(statusBearing > 0, "no status-bearing template found; the walk is broken");
assert.deepEqual(untyped, []);
});
Loading