@theglitchking/persistent-planning 3.0.1 → 3.1.0
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.documentation/INDEX.md +216 -0
- package/.documentation/README.md +90 -0
- package/.documentation/REGISTRY.md +80 -0
- package/.documentation/agents/INDEX.md +43 -0
- package/.documentation/agents/REGISTRY.md +38 -0
- package/.documentation/api/INDEX.md +43 -0
- package/.documentation/api/REGISTRY.md +38 -0
- package/.documentation/architecture/INDEX.md +48 -0
- package/.documentation/architecture/REGISTRY.md +53 -0
- package/{docs/reference.md → .documentation/architecture/context-engineering-principles.md} +20 -1
- package/{docs → .documentation/architecture}/lg-mode.md +17 -8
- package/.documentation/archive/README.md +46 -0
- package/.documentation/backups/INDEX.md +43 -0
- package/.documentation/backups/REGISTRY.md +38 -0
- package/.documentation/database/INDEX.md +43 -0
- package/.documentation/database/REGISTRY.md +38 -0
- package/.documentation/devops/INDEX.md +43 -0
- package/.documentation/devops/REGISTRY.md +38 -0
- package/.documentation/drafts/.gitkeep +1 -0
- package/.documentation/features/INDEX.md +43 -0
- package/.documentation/features/REGISTRY.md +38 -0
- package/.documentation/plans/INDEX.md +43 -0
- package/.documentation/plans/REGISTRY.md +38 -0
- package/.documentation/procedures/INDEX.md +42 -0
- package/.documentation/procedures/REGISTRY.md +51 -0
- package/.documentation/procedures/plan-completion-and-archive.md +151 -0
- package/.documentation/quickstart/INDEX.md +42 -0
- package/.documentation/quickstart/REGISTRY.md +51 -0
- package/{docs → .documentation/quickstart}/examples.md +20 -0
- package/.documentation/reference/INDEX.md +42 -0
- package/.documentation/reference/REGISTRY.md +51 -0
- package/{docs → .documentation/reference}/workspace-json.md +11 -5
- package/.documentation/security/INDEX.md +43 -0
- package/.documentation/security/REGISTRY.md +38 -0
- package/.documentation/standards/INDEX.md +43 -0
- package/.documentation/standards/REGISTRY.md +53 -0
- package/{docs → .documentation/standards}/atom-granularity.md +12 -5
- package/.documentation/standards/mandatory-closing-phases.md +125 -0
- package/.documentation/testing/INDEX.md +42 -0
- package/.documentation/testing/REGISTRY.md +51 -0
- package/.documentation/testing/test-suite.md +88 -0
- package/.documentation/troubleshooting/INDEX.md +43 -0
- package/.documentation/troubleshooting/REGISTRY.md +38 -0
- package/.documentation/workflows/INDEX.md +43 -0
- package/.documentation/workflows/REGISTRY.md +38 -0
- package/CHANGELOG.md +49 -0
- package/README.md +67 -7
- package/commands/archive-plan.md +66 -0
- package/commands/plan-status.md +62 -0
- package/commands/start-atom.md +3 -3
- package/commands/start-planning.md +2 -2
- package/commands/start-task.md +2 -2
- package/hooks/session-start.js +79 -8
- package/package.json +6 -3
- package/scripts/archive-plan.sh +146 -0
- package/scripts/init-planning.sh +23 -0
- package/scripts/plan-status.sh +121 -0
- package/skills/persistent-planning/SKILL.md +34 -9
- package/templates/lg/phase.md +18 -2
- package/templates/lg/task.md +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: archive-plan
|
|
3
|
+
description: "Retire a completed plan into the gitignored .planning/.archive/ directory. Refuses to archive a plan that still has unchecked boxes unless --force is passed. Works in both sm and lg mode."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /archive-plan
|
|
7
|
+
|
|
8
|
+
Move a finished plan out of the active tree so `.planning/` only ever shows work
|
|
9
|
+
that is still live. Nothing is deleted — archiving is a move plus a status stamp,
|
|
10
|
+
and is reversed by moving the directory back.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
/archive-plan <plan-slug>
|
|
18
|
+
/archive-plan --all-complete
|
|
19
|
+
/archive-plan <plan-slug> --force # archive a plan that is not finished
|
|
20
|
+
/archive-plan <plan-slug> --dry-run
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What To Do
|
|
24
|
+
|
|
25
|
+
When the user runs `/archive-plan`:
|
|
26
|
+
|
|
27
|
+
1. Run the script:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")/../scripts"
|
|
31
|
+
bash "${SCRIPT_DIR}/archive-plan.sh" "<plan-slug>"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If the script isn't found at that path, try in order:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bash scripts/archive-plan.sh "<plan-slug>"
|
|
38
|
+
bash ~/.claude/skills/persistent-planning/scripts/archive-plan.sh "<plan-slug>"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. If the script refuses because the plan is incomplete, **do not reach for
|
|
42
|
+
`--force` on your own.** Run `/plan-status`, tell the user what is still
|
|
43
|
+
unchecked, and let them decide.
|
|
44
|
+
|
|
45
|
+
## What it does
|
|
46
|
+
|
|
47
|
+
1. Verifies the plan is complete (every checkbox checked, or `status: done`).
|
|
48
|
+
2. Ensures `.planning/.archive/` exists and is listed in `.gitignore` — completed
|
|
49
|
+
plans are local history, not something every clone carries.
|
|
50
|
+
3. Moves `.planning/<slug>/` to `.planning/.archive/<slug>/`, using `git mv` when
|
|
51
|
+
the plan is tracked so history follows it.
|
|
52
|
+
4. Stamps the plan's top-level artifact: `status: archived` plus `archived_on`
|
|
53
|
+
(frontmatter), or a dated footer for sm plans that have no frontmatter.
|
|
54
|
+
|
|
55
|
+
## Restoring
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
mv .planning/.archive/<slug> .planning/<slug>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then set `status:` back to `active` and drop the `archived_on` stamp.
|
|
62
|
+
|
|
63
|
+
## See also
|
|
64
|
+
|
|
65
|
+
- `/plan-status` — see which plans are complete
|
|
66
|
+
- `.documentation/procedures/plan-completion-and-archive.md` — the full completion protocol
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-status
|
|
3
|
+
description: "Show completion status for every plan under .planning/ — which are in progress, blocked, or complete and ready to archive. Works in both sm and lg mode."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /plan-status
|
|
7
|
+
|
|
8
|
+
Report where every plan stands. Completion is derived from the plan artifacts
|
|
9
|
+
themselves (checkbox state + frontmatter `status:`), so it cannot drift away from
|
|
10
|
+
what the plan actually says.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
/plan-status
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## What To Do
|
|
21
|
+
|
|
22
|
+
Run the status script:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")/../scripts"
|
|
26
|
+
bash "${SCRIPT_DIR}/plan-status.sh"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If the script isn't found at that path, try in order:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bash scripts/plan-status.sh
|
|
33
|
+
bash ~/.claude/skills/persistent-planning/scripts/plan-status.sh
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then read the table back to the user. If any plan is listed `COMPLETE`, offer to
|
|
37
|
+
archive it with `/archive-plan <slug>`.
|
|
38
|
+
|
|
39
|
+
## Verdicts
|
|
40
|
+
|
|
41
|
+
| Verdict | Meaning |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `in progress` | Unchecked boxes remain |
|
|
44
|
+
| `blocked` | An atom in the plan carries `status: blocked` |
|
|
45
|
+
| `COMPLETE` | Every box checked, or the top-level artifact says `status: done` — ready to archive |
|
|
46
|
+
| `empty` | Plan artifact exists but has no checkboxes yet |
|
|
47
|
+
| `archived` | Already retired (only visible if left in the active tree) |
|
|
48
|
+
|
|
49
|
+
## Machine-readable forms
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
bash scripts/plan-status.sh --complete # one complete plan slug per line
|
|
53
|
+
bash scripts/plan-status.sh --nudge # one-line notice, or silence
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`--nudge` is what the SessionStart hook uses, so a finished plan gets mentioned
|
|
57
|
+
at the top of the next session rather than being rediscovered later.
|
|
58
|
+
|
|
59
|
+
## See also
|
|
60
|
+
|
|
61
|
+
- `/archive-plan` — retire a completed plan into `.planning/.archive/`
|
|
62
|
+
- `.documentation/procedures/plan-completion-and-archive.md` — the full completion protocol
|
package/commands/start-atom.md
CHANGED
|
@@ -69,7 +69,7 @@ Not every step needs a standalone atom file. The rule of thumb:
|
|
|
69
69
|
- **Inline checkbox in `task.md`**: simple, self-contained step that doesn't need its own context file. The user (or the active agent) handles it directly.
|
|
70
70
|
- **Standalone atom file via `/start-atom`**: a step that will be **handed off to a subagent**. Atoms get their own files because subagents need explicit inputs / outputs / acceptance criteria they can read structured rather than parse from prose.
|
|
71
71
|
|
|
72
|
-
See
|
|
72
|
+
See `.documentation/standards/atom-granularity.md` for full anti-pattern guidance with examples.
|
|
73
73
|
|
|
74
74
|
## Sequence semantics
|
|
75
75
|
|
|
@@ -89,5 +89,5 @@ This command refuses to run if the project is in sm mode. To switch:
|
|
|
89
89
|
|
|
90
90
|
- `/start-planning` — initialize the planning structure (phase in lg)
|
|
91
91
|
- `/start-task` — add a task under a phase
|
|
92
|
-
-
|
|
93
|
-
-
|
|
92
|
+
- `.documentation/architecture/lg-mode.md` — full lg-mode layer model
|
|
93
|
+
- `.documentation/standards/atom-granularity.md` — when to spawn an atom file vs. inline checkbox
|
|
@@ -111,5 +111,5 @@ Names are converted to URL-friendly slugs:
|
|
|
111
111
|
|
|
112
112
|
- `/start-task` — add a task under a phase (lg mode only)
|
|
113
113
|
- `/start-atom` — add an atom (subagent hand-off unit) under a task (lg mode only)
|
|
114
|
-
-
|
|
115
|
-
-
|
|
114
|
+
- `.documentation/reference/workspace-json.md` — workspace.json schema reference
|
|
115
|
+
- `.documentation/architecture/lg-mode.md` — full lg-mode guide (phase / task / atom / notes layer model)
|
package/commands/start-task.md
CHANGED
|
@@ -79,5 +79,5 @@ That writes `.planning/.meta/workspace.json` with `mode: "lg"` and creates the f
|
|
|
79
79
|
|
|
80
80
|
- `/start-planning` — initialize the planning structure (phase in lg, task in sm)
|
|
81
81
|
- `/start-atom` — add an atom under a task
|
|
82
|
-
-
|
|
83
|
-
-
|
|
82
|
+
- `.documentation/architecture/lg-mode.md` — full lg-mode layer model
|
|
83
|
+
- `.documentation/standards/atom-granularity.md` — when to spawn an atom file vs. add an inline checkbox
|
package/hooks/session-start.js
CHANGED
|
@@ -1,13 +1,84 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// persistent-planning SessionStart hook.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// check
|
|
3
|
+
//
|
|
4
|
+
// Two jobs:
|
|
5
|
+
// 1. The update check, delegated entirely to @theglitchking/claude-plugin-runtime.
|
|
6
|
+
// 2. A completion nudge: if any plan under .planning/ has every box checked
|
|
7
|
+
// but is still sitting in the active tree, say so, so the agent archives it
|
|
8
|
+
// instead of rediscovering a finished plan next session.
|
|
6
9
|
|
|
7
10
|
import { runSessionStart } from "@theglitchking/claude-plugin-runtime";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
17
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
18
|
+
|
|
19
|
+
function planNudge() {
|
|
20
|
+
try {
|
|
21
|
+
if (!existsSync(join(projectRoot, ".planning"))) return "";
|
|
22
|
+
const script = join(packageRoot, "scripts", "plan-status.sh");
|
|
23
|
+
if (!existsSync(script)) return "";
|
|
24
|
+
const r = spawnSync("bash", [script, "--nudge"], {
|
|
25
|
+
cwd: projectRoot,
|
|
26
|
+
env: { ...process.env, CLAUDE_PROJECT_DIR: projectRoot },
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
timeout: 5000,
|
|
29
|
+
});
|
|
30
|
+
return (r.stdout || "").trim();
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const notice = planNudge();
|
|
37
|
+
|
|
38
|
+
if (!notice) {
|
|
39
|
+
await runSessionStart({
|
|
40
|
+
packageName: "@theglitchking/persistent-planning",
|
|
41
|
+
pluginName: "persistent-planning",
|
|
42
|
+
configFile: "persistent-planning.json",
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
// ponytail: the runtime owns the one SessionStart JSON response and exposes no
|
|
46
|
+
// hook for appending to it, and a second response line on stdout is invalid.
|
|
47
|
+
// So intercept its single write and merge the nudge into that same payload.
|
|
48
|
+
// Fails open — anything unexpected is written through untouched.
|
|
49
|
+
const realWrite = process.stdout.write.bind(process.stdout);
|
|
50
|
+
let merged = false;
|
|
51
|
+
process.stdout.write = (chunk, ...rest) => {
|
|
52
|
+
if (!merged) {
|
|
53
|
+
try {
|
|
54
|
+
const payload = JSON.parse(String(chunk));
|
|
55
|
+
const out = payload?.hookSpecificOutput;
|
|
56
|
+
if (out && out.hookEventName === "SessionStart") {
|
|
57
|
+
out.additionalContext = out.additionalContext
|
|
58
|
+
? `${out.additionalContext}\n${notice}`
|
|
59
|
+
: notice;
|
|
60
|
+
merged = true;
|
|
61
|
+
return realWrite(JSON.stringify(payload) + "\n", ...rest);
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Not the response payload — fall through and write it as-is.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return realWrite(chunk, ...rest);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
await runSessionStart({
|
|
71
|
+
packageName: "@theglitchking/persistent-planning",
|
|
72
|
+
pluginName: "persistent-planning",
|
|
73
|
+
configFile: "persistent-planning.json",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// The runtime can return without emitting (e.g. it chdir-failed). Say our piece.
|
|
77
|
+
if (!merged) {
|
|
78
|
+
realWrite(
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: notice },
|
|
81
|
+
}) + "\n"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theglitchking/persistent-planning",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Persistent markdown-based planning with task directories, progress tracking, and context engineering for multi-step Claude Code workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"persistent-planning": "bin/persistent-planning.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"postinstall": "node scripts/link-skills.js"
|
|
10
|
+
"postinstall": "node scripts/link-skills.js",
|
|
11
|
+
"test": "bash tests/run.sh"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"bin/",
|
|
@@ -16,7 +17,8 @@
|
|
|
16
17
|
"scripts/",
|
|
17
18
|
"templates/",
|
|
18
19
|
"skills/",
|
|
19
|
-
"
|
|
20
|
+
".documentation/",
|
|
21
|
+
"!.documentation/reports",
|
|
20
22
|
".claude-plugin/",
|
|
21
23
|
"README.md",
|
|
22
24
|
"LICENSE",
|
|
@@ -57,6 +59,7 @@
|
|
|
57
59
|
"commander": "^14.0.0"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
62
|
+
"@theglitchking/hit-em-with-the-docs": "^2.9.0",
|
|
60
63
|
"@theglitchking/semantic-pages": "^0.10.0"
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
###############################################################################
|
|
3
|
+
# archive-plan.sh - Retire a completed plan into .planning/.archive/
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash scripts/archive-plan.sh <plan-slug>
|
|
7
|
+
# bash scripts/archive-plan.sh --all-complete
|
|
8
|
+
# bash scripts/archive-plan.sh <plan-slug> --force # archive an unfinished plan
|
|
9
|
+
# bash scripts/archive-plan.sh <plan-slug> --dry-run
|
|
10
|
+
#
|
|
11
|
+
# What it does:
|
|
12
|
+
# 1. Refuses unless the plan is complete (every checkbox checked, or the
|
|
13
|
+
# top-level artifact declares `status: done`) -- override with --force.
|
|
14
|
+
# 2. Ensures .planning/.archive/ exists and is gitignored.
|
|
15
|
+
# 3. Moves .planning/<slug>/ to .planning/.archive/<slug>/ (git mv when the
|
|
16
|
+
# plan is tracked, so history follows it).
|
|
17
|
+
# 4. Stamps the plan's top-level artifact with status: archived + archived_on.
|
|
18
|
+
#
|
|
19
|
+
# Nothing is ever deleted. Restore by moving the directory back out.
|
|
20
|
+
###############################################################################
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
24
|
+
source "${SCRIPT_DIR}/lib/planning.sh"
|
|
25
|
+
|
|
26
|
+
SLUG=""
|
|
27
|
+
ALL_COMPLETE=0
|
|
28
|
+
FORCE=0
|
|
29
|
+
DRY_RUN=0
|
|
30
|
+
|
|
31
|
+
while [[ $# -gt 0 ]]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--all-complete) ALL_COMPLETE=1; shift ;;
|
|
34
|
+
--force) FORCE=1; shift ;;
|
|
35
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
36
|
+
-h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
37
|
+
-*) planning_err "Unknown option: $1"; exit 1 ;;
|
|
38
|
+
*) [[ -z "$SLUG" ]] && SLUG="$1"; shift ;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [[ -z "$SLUG" && $ALL_COMPLETE -eq 0 ]]; then
|
|
43
|
+
planning_err "A plan slug is required (or --all-complete)."
|
|
44
|
+
echo "Usage: bash scripts/archive-plan.sh <plan-slug> [--force] [--dry-run]"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
ROOT=$(planning_root)
|
|
49
|
+
PLANNING="$(planning_dir)"
|
|
50
|
+
ARCHIVE="${PLANNING}/.archive"
|
|
51
|
+
TODAY=$(planning_today)
|
|
52
|
+
|
|
53
|
+
# .planning/.archive/ must be gitignored: completed plans are local history, not
|
|
54
|
+
# something every clone should carry. Idempotent.
|
|
55
|
+
ensure_gitignored() {
|
|
56
|
+
local gitignore="${ROOT}/.gitignore"
|
|
57
|
+
grep -qE '^\.planning/?$' "$gitignore" 2>/dev/null && return 0 # whole tree already ignored
|
|
58
|
+
grep -qE '^\.planning/\.archive/?$' "$gitignore" 2>/dev/null && return 0
|
|
59
|
+
printf '\n# Completed plans retired by scripts/archive-plan.sh (local history)\n.planning/.archive/\n' >> "$gitignore"
|
|
60
|
+
planning_ok "Added .planning/.archive/ to .gitignore"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
plan_root_artifact() {
|
|
64
|
+
for f in "$1/phase.md" "$1/task_plan.md" "$1/task.md"; do
|
|
65
|
+
[[ -f "$f" ]] && { echo "$f"; return; }
|
|
66
|
+
done
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Stamp the plan's top-level artifact so an archived plan reads as archived even
|
|
70
|
+
# when someone opens the file directly, with no directory context.
|
|
71
|
+
stamp_archived() {
|
|
72
|
+
local file="$1"
|
|
73
|
+
[[ -f "$file" ]] || return 0
|
|
74
|
+
if head -1 "$file" | grep -q '^---$'; then
|
|
75
|
+
# Frontmatter: flip status and add archived_on before the closing delimiter.
|
|
76
|
+
awk -v today="$TODAY" '
|
|
77
|
+
NR == 1 { print; next }
|
|
78
|
+
!done_fm && /^status:[[:space:]]*/ { print "status: archived"; seen_status = 1; next }
|
|
79
|
+
!done_fm && /^---$/ {
|
|
80
|
+
if (!seen_status) print "status: archived"
|
|
81
|
+
print "archived_on: \"" today "\""
|
|
82
|
+
print; done_fm = 1; next
|
|
83
|
+
}
|
|
84
|
+
{ print }
|
|
85
|
+
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
|
|
86
|
+
else
|
|
87
|
+
printf '\n---\n\n**Archived %s** — retired by `scripts/archive-plan.sh`.\n' "$TODAY" >> "$file"
|
|
88
|
+
fi
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
archive_one() {
|
|
92
|
+
local slug="$1"
|
|
93
|
+
local src="${PLANNING}/${slug}"
|
|
94
|
+
local dest="${ARCHIVE}/${slug}"
|
|
95
|
+
|
|
96
|
+
if [[ ! -d "$src" ]]; then
|
|
97
|
+
planning_err "No such plan: .planning/${slug}/"
|
|
98
|
+
return 1
|
|
99
|
+
fi
|
|
100
|
+
if [[ -e "$dest" ]]; then
|
|
101
|
+
planning_err "Already archived: .planning/.archive/${slug}/ exists. Move or rename it first."
|
|
102
|
+
return 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [[ $FORCE -eq 0 ]]; then
|
|
106
|
+
local complete
|
|
107
|
+
complete=$(bash "${SCRIPT_DIR}/plan-status.sh" --complete | grep -Fx "$slug" || true)
|
|
108
|
+
if [[ -z "$complete" ]]; then
|
|
109
|
+
planning_err "Plan '${slug}' is not complete — refusing to archive."
|
|
110
|
+
echo " Check what is outstanding with: bash scripts/plan-status.sh"
|
|
111
|
+
echo " Archive anyway with: bash scripts/archive-plan.sh ${slug} --force"
|
|
112
|
+
return 1
|
|
113
|
+
fi
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if [[ $DRY_RUN -eq 1 ]]; then
|
|
117
|
+
planning_log "[dry-run] would archive .planning/${slug}/ -> .planning/.archive/${slug}/"
|
|
118
|
+
return 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
ensure_gitignored
|
|
122
|
+
mkdir -p "$ARCHIVE"
|
|
123
|
+
|
|
124
|
+
if git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1 \
|
|
125
|
+
&& [[ -n "$(git -C "$ROOT" ls-files ".planning/${slug}" 2>/dev/null)" ]]; then
|
|
126
|
+
git -C "$ROOT" mv ".planning/${slug}" ".planning/.archive/${slug}"
|
|
127
|
+
else
|
|
128
|
+
mv "$src" "$dest"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
stamp_archived "$(plan_root_artifact "$dest")"
|
|
132
|
+
planning_ok "Archived .planning/${slug}/ -> .planning/.archive/${slug}/"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
RC=0
|
|
136
|
+
if [[ $ALL_COMPLETE -eq 1 ]]; then
|
|
137
|
+
mapfile -t SLUGS < <(bash "${SCRIPT_DIR}/plan-status.sh" --complete)
|
|
138
|
+
if [[ ${#SLUGS[@]} -eq 0 || -z "${SLUGS[0]}" ]]; then
|
|
139
|
+
planning_log "No complete plans to archive."
|
|
140
|
+
exit 0
|
|
141
|
+
fi
|
|
142
|
+
for s in "${SLUGS[@]}"; do archive_one "$s" || RC=1; done
|
|
143
|
+
else
|
|
144
|
+
archive_one "$SLUG" || RC=1
|
|
145
|
+
fi
|
|
146
|
+
exit $RC
|
package/scripts/init-planning.sh
CHANGED
|
@@ -105,6 +105,16 @@ if [ ! -f "$TASK_PLAN_FILE" ]; then
|
|
|
105
105
|
- [ ] Phase 2: Research/gather information
|
|
106
106
|
- [ ] Phase 3: Execute/build
|
|
107
107
|
- [ ] Phase 4: Review and deliver
|
|
108
|
+
- [ ] Phase 5: Validate success through comprehensive testing (MANDATORY)
|
|
109
|
+
- [ ] Phase 6: Documentation pass -- create/update/deprecate docs (MANDATORY)
|
|
110
|
+
|
|
111
|
+
> Phases 5 and 6 are mandatory and MUST stay the last two phases of this plan.
|
|
112
|
+
> Insert new work above them; never after. Renumber them if you add phases.
|
|
113
|
+
>
|
|
114
|
+
> - **Validate success**: prove the work with tests that fail if the change breaks.
|
|
115
|
+
> - **Documentation pass**: create/update/deprecate every doc this change touches --
|
|
116
|
+
> what it is, where it lives, how to fix it, how to operate it, why it matters.
|
|
117
|
+
> If hit-em-with-the-docs is installed, use it for this pass.
|
|
108
118
|
|
|
109
119
|
## Key Questions
|
|
110
120
|
1. [Question to answer]
|
|
@@ -126,9 +136,22 @@ if [ ! -f "$TASK_PLAN_FILE" ]; then
|
|
|
126
136
|
- Update status after each phase
|
|
127
137
|
- Mark completed phases with [x]
|
|
128
138
|
- Save research to notes.md instead of stuffing context
|
|
139
|
+
|
|
140
|
+
## On Completion
|
|
141
|
+
This plan is complete when every checkbox above is checked. That IS the
|
|
142
|
+
completion signal -- there is no separate status to remember to flip.
|
|
143
|
+
|
|
144
|
+
When complete, archive it instead of deleting it:
|
|
145
|
+
|
|
146
|
+
/plan-status # confirm this plan reads COMPLETE
|
|
147
|
+
/archive-plan TASK_SLUG_PLACEHOLDER # -> .planning/.archive/TASK_SLUG_PLACEHOLDER/
|
|
148
|
+
|
|
149
|
+
Archiving moves this directory to the gitignored .planning/.archive/ and stamps
|
|
150
|
+
it archived. Nothing is destroyed.
|
|
129
151
|
EOF
|
|
130
152
|
|
|
131
153
|
sed -i "s/TASK_NAME_PLACEHOLDER/${TASK_NAME}/g" "$TASK_PLAN_FILE"
|
|
154
|
+
sed -i "s/TASK_SLUG_PLACEHOLDER/${TASK_SLUG}/g" "$TASK_PLAN_FILE"
|
|
132
155
|
echo -e "${GREEN}+${NC} Created task_plan.md"
|
|
133
156
|
else
|
|
134
157
|
echo -e "${YELLOW}*${NC} task_plan.md already exists (not overwritten)"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
###############################################################################
|
|
3
|
+
# plan-status.sh - Report completion status for every plan under .planning/
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash scripts/plan-status.sh # table of every active plan
|
|
7
|
+
# bash scripts/plan-status.sh --nudge # one line if plans are archivable, else silence
|
|
8
|
+
# bash scripts/plan-status.sh --complete # print the slug of each complete plan, one per line
|
|
9
|
+
#
|
|
10
|
+
# A plan is COMPLETE when every checkbox in it is checked, or when its
|
|
11
|
+
# top-level artifact declares `status: done` in frontmatter. Completion is
|
|
12
|
+
# derived from the artifacts themselves so no separate state file can drift
|
|
13
|
+
# away from what the plan actually says.
|
|
14
|
+
#
|
|
15
|
+
# This is the single implementation of the scan: the SessionStart hook and the
|
|
16
|
+
# /plan-status slash command both shell out to it.
|
|
17
|
+
###############################################################################
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
source "${SCRIPT_DIR}/lib/planning.sh"
|
|
22
|
+
|
|
23
|
+
MODE="table"
|
|
24
|
+
case "${1:-}" in
|
|
25
|
+
--nudge) MODE="nudge" ;;
|
|
26
|
+
--complete) MODE="complete" ;;
|
|
27
|
+
-h|--help)
|
|
28
|
+
sed -n '2,18p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
"") ;;
|
|
32
|
+
*)
|
|
33
|
+
planning_err "Unknown option: $1 (try --help)"
|
|
34
|
+
exit 1
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
PLANNING="$(planning_dir)"
|
|
39
|
+
[[ -d "$PLANNING" ]] || { [[ "$MODE" == "table" ]] && planning_warn "No .planning/ directory here."; exit 0; }
|
|
40
|
+
|
|
41
|
+
# plan_root_artifact <plan-dir> -> path of the plan's top-level file, or empty
|
|
42
|
+
plan_root_artifact() {
|
|
43
|
+
for f in "$1/phase.md" "$1/task_plan.md" "$1/task.md"; do
|
|
44
|
+
[[ -f "$f" ]] && { echo "$f"; return; }
|
|
45
|
+
done
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# frontmatter_status <file> -> value of `status:` inside the leading --- block
|
|
49
|
+
frontmatter_status() {
|
|
50
|
+
[[ -f "$1" ]] || return
|
|
51
|
+
awk 'NR==1 && $0 != "---" { exit }
|
|
52
|
+
NR>1 && $0 == "---" { exit }
|
|
53
|
+
/^status:[[:space:]]*/ { sub(/^status:[[:space:]]*/, ""); gsub(/["\047]/, ""); print; exit }' "$1"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Emits "<total> <checked>" for every markdown file under a plan directory.
|
|
57
|
+
count_boxes() {
|
|
58
|
+
local total checked
|
|
59
|
+
total=$(grep -rhoE '^[[:space:]]*- \[[ xX]\] ' --include='*.md' "$1" 2>/dev/null | wc -l | tr -d ' ')
|
|
60
|
+
checked=$(grep -rhoE '^[[:space:]]*- \[[xX]\] ' --include='*.md' "$1" 2>/dev/null | wc -l | tr -d ' ')
|
|
61
|
+
echo "$total $checked"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
COMPLETE=()
|
|
65
|
+
ROWS=()
|
|
66
|
+
|
|
67
|
+
for plan_path in "$PLANNING"/*/; do
|
|
68
|
+
[[ -d "$plan_path" ]] || continue
|
|
69
|
+
slug=$(basename "$plan_path")
|
|
70
|
+
case "$slug" in .meta|.archive|archive) continue ;; esac
|
|
71
|
+
|
|
72
|
+
root=$(plan_root_artifact "${plan_path%/}")
|
|
73
|
+
[[ -n "$root" ]] || continue
|
|
74
|
+
|
|
75
|
+
read -r total checked <<<"$(count_boxes "${plan_path%/}")"
|
|
76
|
+
status=$(frontmatter_status "$root")
|
|
77
|
+
blocked=$(grep -rl '^status: blocked' --include='*.md' "${plan_path%/}" 2>/dev/null | wc -l | tr -d ' ')
|
|
78
|
+
|
|
79
|
+
if [[ "$status" == "archived" ]]; then
|
|
80
|
+
verdict="archived"
|
|
81
|
+
elif [[ "$status" == "done" ]] || { [[ "$total" -gt 0 ]] && [[ "$checked" -eq "$total" ]]; }; then
|
|
82
|
+
verdict="COMPLETE"
|
|
83
|
+
COMPLETE+=("$slug")
|
|
84
|
+
elif [[ "$blocked" -gt 0 ]]; then
|
|
85
|
+
verdict="blocked"
|
|
86
|
+
elif [[ "$total" -eq 0 ]]; then
|
|
87
|
+
verdict="empty"
|
|
88
|
+
else
|
|
89
|
+
verdict="in progress"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
ROWS+=("$(printf '%-38s %-12s %s/%s boxes' "$slug" "$verdict" "$checked" "$total")")
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
case "$MODE" in
|
|
96
|
+
complete)
|
|
97
|
+
printf '%s\n' "${COMPLETE[@]+"${COMPLETE[@]}"}"
|
|
98
|
+
;;
|
|
99
|
+
nudge)
|
|
100
|
+
if [[ ${#COMPLETE[@]} -gt 0 ]]; then
|
|
101
|
+
printf 'persistent-planning: %d plan(s) complete but not archived — %s. Archive with: /archive-plan <slug>\n' \
|
|
102
|
+
"${#COMPLETE[@]}" "$(IFS=', '; echo "${COMPLETE[*]}")"
|
|
103
|
+
fi
|
|
104
|
+
;;
|
|
105
|
+
table)
|
|
106
|
+
if [[ ${#ROWS[@]} -eq 0 ]]; then
|
|
107
|
+
planning_warn "No plans found under .planning/."
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
planning_log "Plans under .planning/"
|
|
111
|
+
echo ""
|
|
112
|
+
printf ' %-38s %-12s %s\n' "PLAN" "STATUS" "PROGRESS"
|
|
113
|
+
printf ' %s\n' "$(printf '%.0s-' {1..70})"
|
|
114
|
+
printf ' %s\n' "${ROWS[@]}"
|
|
115
|
+
echo ""
|
|
116
|
+
if [[ ${#COMPLETE[@]} -gt 0 ]]; then
|
|
117
|
+
planning_ok "${#COMPLETE[@]} plan(s) ready to archive:"
|
|
118
|
+
for s in "${COMPLETE[@]}"; do echo " bash scripts/archive-plan.sh $s"; done
|
|
119
|
+
fi
|
|
120
|
+
;;
|
|
121
|
+
esac
|
|
@@ -160,6 +160,16 @@ Create this file FIRST for any complex task:
|
|
|
160
160
|
- [ ] Phase 2: Research/gather information
|
|
161
161
|
- [ ] Phase 3: Execute/build
|
|
162
162
|
- [ ] Phase 4: Review and deliver
|
|
163
|
+
- [ ] Phase 5: Validate success through comprehensive testing (MANDATORY)
|
|
164
|
+
- [ ] Phase 6: Documentation pass -- create/update/deprecate docs (MANDATORY)
|
|
165
|
+
|
|
166
|
+
> Phases 5 and 6 are mandatory and MUST stay the last two phases of this plan.
|
|
167
|
+
> Insert new work above them; never after. Renumber them if you add phases.
|
|
168
|
+
>
|
|
169
|
+
> - **Validate success**: prove the work with tests that fail if the change breaks.
|
|
170
|
+
> - **Documentation pass**: create/update/deprecate every doc this change touches --
|
|
171
|
+
> what it is, where it lives, how to fix it, how to operate it, why it matters.
|
|
172
|
+
> If hit-em-with-the-docs is installed, use it for this pass.
|
|
163
173
|
|
|
164
174
|
## Key Questions
|
|
165
175
|
1. [Question to answer]
|
|
@@ -217,17 +227,29 @@ Large outputs go to files, not context. Keep only paths in working memory.
|
|
|
217
227
|
### 5. Log All Errors
|
|
218
228
|
Every error goes in the "Errors Encountered" section. This builds knowledge for future tasks.
|
|
219
229
|
|
|
220
|
-
### 6.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
230
|
+
### 6. Archive on Completion, Don't Delete
|
|
231
|
+
A plan is **complete** when every checkbox in it is checked (or its top-level
|
|
232
|
+
artifact declares `status: done`). Completion is derived from the artifacts, so
|
|
233
|
+
checking the last box IS the completion signal -- there is no separate state to
|
|
234
|
+
update and no way for it to drift.
|
|
225
235
|
|
|
226
|
-
|
|
236
|
+
When a plan is complete, archive it rather than deleting it:
|
|
227
237
|
```bash
|
|
228
|
-
|
|
238
|
+
/plan-status # which plans are COMPLETE?
|
|
239
|
+
/archive-plan [task-name] # -> .planning/.archive/[task-name]/
|
|
229
240
|
```
|
|
230
241
|
|
|
242
|
+
Archiving moves the plan to the gitignored `.planning/.archive/` and stamps it
|
|
243
|
+
`status: archived` + `archived_on`. The active `.planning/` tree then shows only
|
|
244
|
+
live work. Nothing is destroyed -- restore with
|
|
245
|
+
`mv .planning/.archive/[task-name] .planning/[task-name]`.
|
|
246
|
+
|
|
247
|
+
The SessionStart hook checks for complete-but-unarchived plans and says so at the
|
|
248
|
+
top of the session, so a finished plan gets retired instead of rediscovered.
|
|
249
|
+
|
|
250
|
+
Deleting is still available (`rm -rf .planning/[task-name]/`) but throws away the
|
|
251
|
+
record of what was done and why. Prefer the archive.
|
|
252
|
+
|
|
231
253
|
## When to Use This Pattern
|
|
232
254
|
|
|
233
255
|
**Use 3-file pattern for:**
|
|
@@ -254,14 +276,17 @@ rm -rf .planning/
|
|
|
254
276
|
| Overwrite previous plans | Use `/start-planning` (auto-creates unique task dir) |
|
|
255
277
|
| Start executing immediately | Run `/start-planning "task name"` FIRST |
|
|
256
278
|
| Switch tasks without saving | Update task_plan.md before switching |
|
|
279
|
+
| `rm -rf` a finished plan | `/archive-plan <slug>` -- keeps the record, clears the tree |
|
|
280
|
+
| Leave finished plans in `.planning/` | Archive them; the active tree should show only live work |
|
|
281
|
+
| Track completion in your head or in chat | Check the boxes -- the artifact is the status |
|
|
257
282
|
|
|
258
283
|
## Advanced Patterns
|
|
259
284
|
|
|
260
|
-
See [
|
|
285
|
+
See [Manus Context Engineering Principles](../../.documentation/architecture/context-engineering-principles.md) for:
|
|
261
286
|
- Attention manipulation techniques
|
|
262
287
|
- Error recovery patterns
|
|
263
288
|
- Context optimization principles
|
|
264
289
|
|
|
265
|
-
See [
|
|
290
|
+
See [Worked Examples](../../.documentation/quickstart/examples.md) for:
|
|
266
291
|
- Real task examples
|
|
267
292
|
- Complex workflow patterns
|