@theglitchking/persistent-planning 3.0.0 → 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.
Files changed (69) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.documentation/INDEX.md +216 -0
  4. package/.documentation/README.md +90 -0
  5. package/.documentation/REGISTRY.md +80 -0
  6. package/.documentation/agents/INDEX.md +43 -0
  7. package/.documentation/agents/REGISTRY.md +38 -0
  8. package/.documentation/api/INDEX.md +43 -0
  9. package/.documentation/api/REGISTRY.md +38 -0
  10. package/.documentation/architecture/INDEX.md +48 -0
  11. package/.documentation/architecture/REGISTRY.md +53 -0
  12. package/{docs/reference.md → .documentation/architecture/context-engineering-principles.md} +20 -1
  13. package/{docs → .documentation/architecture}/lg-mode.md +17 -8
  14. package/.documentation/archive/README.md +46 -0
  15. package/.documentation/backups/INDEX.md +43 -0
  16. package/.documentation/backups/REGISTRY.md +38 -0
  17. package/.documentation/database/INDEX.md +43 -0
  18. package/.documentation/database/REGISTRY.md +38 -0
  19. package/.documentation/devops/INDEX.md +43 -0
  20. package/.documentation/devops/REGISTRY.md +38 -0
  21. package/.documentation/drafts/.gitkeep +1 -0
  22. package/.documentation/features/INDEX.md +43 -0
  23. package/.documentation/features/REGISTRY.md +38 -0
  24. package/.documentation/plans/INDEX.md +43 -0
  25. package/.documentation/plans/REGISTRY.md +38 -0
  26. package/.documentation/procedures/INDEX.md +42 -0
  27. package/.documentation/procedures/REGISTRY.md +51 -0
  28. package/.documentation/procedures/plan-completion-and-archive.md +151 -0
  29. package/.documentation/quickstart/INDEX.md +42 -0
  30. package/.documentation/quickstart/REGISTRY.md +51 -0
  31. package/{docs → .documentation/quickstart}/examples.md +20 -0
  32. package/.documentation/reference/INDEX.md +42 -0
  33. package/.documentation/reference/REGISTRY.md +51 -0
  34. package/{docs → .documentation/reference}/workspace-json.md +11 -5
  35. package/.documentation/security/INDEX.md +43 -0
  36. package/.documentation/security/REGISTRY.md +38 -0
  37. package/.documentation/standards/INDEX.md +43 -0
  38. package/.documentation/standards/REGISTRY.md +53 -0
  39. package/{docs → .documentation/standards}/atom-granularity.md +12 -5
  40. package/.documentation/standards/mandatory-closing-phases.md +125 -0
  41. package/.documentation/testing/INDEX.md +42 -0
  42. package/.documentation/testing/REGISTRY.md +51 -0
  43. package/.documentation/testing/test-suite.md +88 -0
  44. package/.documentation/troubleshooting/INDEX.md +43 -0
  45. package/.documentation/troubleshooting/REGISTRY.md +38 -0
  46. package/.documentation/workflows/INDEX.md +43 -0
  47. package/.documentation/workflows/REGISTRY.md +38 -0
  48. package/CHANGELOG.md +59 -0
  49. package/README.md +67 -7
  50. package/commands/archive-plan.md +66 -0
  51. package/commands/plan-status.md +62 -0
  52. package/commands/start-atom.md +3 -3
  53. package/commands/start-planning.md +2 -2
  54. package/commands/start-task.md +2 -2
  55. package/hooks/session-start.js +79 -8
  56. package/package.json +8 -4
  57. package/scripts/archive-plan.sh +146 -0
  58. package/scripts/detect-mode.sh +67 -0
  59. package/scripts/init-atom.sh +139 -0
  60. package/scripts/init-phase.sh +141 -0
  61. package/scripts/init-planning.sh +256 -0
  62. package/scripts/init-task.sh +118 -0
  63. package/scripts/lib/planning.sh +161 -0
  64. package/scripts/plan-status.sh +121 -0
  65. package/skills/persistent-planning/SKILL.md +34 -9
  66. package/templates/lg/atom.md +45 -0
  67. package/templates/lg/notes.md +40 -0
  68. package/templates/lg/phase.md +58 -0
  69. package/templates/lg/task.md +54 -0
@@ -0,0 +1,141 @@
1
+ #!/bin/bash
2
+ ###############################################################################
3
+ # init-phase.sh - Create a new phase in lg-mode persistent-planning
4
+ #
5
+ # Usage:
6
+ # bash scripts/init-phase.sh "Phase Name"
7
+ #
8
+ # Creates:
9
+ # .planning/<phase-slug>/phase.md (HEWTD-frontmattered phase template)
10
+ # .planning/<phase-slug>/notes.md (cross-cutting notes for the phase)
11
+ # .planning/.meta/workspace.json (mode = lg, on first phase init)
12
+ #
13
+ # Refuses to run unless mode is lg. Run init-planning.sh for sm-mode tasks.
14
+ ###############################################################################
15
+ set -e
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ source "${SCRIPT_DIR}/lib/planning.sh"
19
+
20
+ case "${1:-}" in
21
+ -h|--help)
22
+ cat <<EOF
23
+ Usage: bash scripts/init-phase.sh "Phase Name"
24
+
25
+ Creates:
26
+ .planning/<phase-slug>/phase.md
27
+ .planning/<phase-slug>/notes.md
28
+ .planning/.meta/workspace.json (on first phase init, with mode=lg)
29
+
30
+ Refuses to run unless mode is lg. Run init-planning.sh for sm-mode tasks.
31
+
32
+ Examples:
33
+ bash scripts/init-phase.sh "Foundation"
34
+ bash scripts/init-phase.sh "Multi-corpus refactor"
35
+ EOF
36
+ exit 0
37
+ ;;
38
+ "")
39
+ planning_err "Phase name required."
40
+ echo ""
41
+ echo "Usage: bash scripts/init-phase.sh \"Phase Name\""
42
+ echo " bash scripts/init-phase.sh --help"
43
+ exit 1
44
+ ;;
45
+ esac
46
+
47
+ PHASE_NAME="$1"
48
+ PHASE_SLUG=$(planning_slugify "$PHASE_NAME")
49
+
50
+ if [[ -z "$PHASE_SLUG" ]]; then
51
+ planning_err "Phase name must contain at least one alphanumeric character."
52
+ exit 1
53
+ fi
54
+
55
+ # Refuse to run in sm mode
56
+ MODE=$(planning_mode)
57
+ if [[ "$MODE" != "lg" ]]; then
58
+ planning_err "Current mode is '$MODE' but init-phase.sh requires lg mode."
59
+ echo ""
60
+ echo "Switch to lg mode by running:"
61
+ echo " /start-planning --mode lg \"<phase name>\""
62
+ echo "or by editing .planning/.meta/workspace.json directly."
63
+ exit 1
64
+ fi
65
+
66
+ ROOT=$(planning_root)
67
+ PLANNING="$(planning_dir)"
68
+ META="$(planning_meta_dir)"
69
+ WORKSPACE_JSON="$(planning_workspace_json)"
70
+ TODAY=$(planning_today)
71
+ PHASE_DIR="${PLANNING}/${PHASE_SLUG}"
72
+ TEMPLATE_DIR="${ROOT}/templates/lg"
73
+
74
+ # Fallback: when run from the installed plugin location, templates live in the
75
+ # plugin's own directory, not the project root.
76
+ if [[ ! -d "$TEMPLATE_DIR" ]]; then
77
+ TEMPLATE_DIR="${SCRIPT_DIR}/../templates/lg"
78
+ fi
79
+ if [[ ! -d "$TEMPLATE_DIR" ]]; then
80
+ planning_err "Cannot locate templates/lg directory. Tried:"
81
+ echo " ${ROOT}/templates/lg"
82
+ echo " ${SCRIPT_DIR}/../templates/lg"
83
+ exit 1
84
+ fi
85
+
86
+ planning_log "Initializing phase: ${PHASE_NAME}"
87
+
88
+ # Bootstrap .planning/.meta/workspace.json if missing
89
+ mkdir -p "$META"
90
+ if [[ ! -f "$WORKSPACE_JSON" ]]; then
91
+ CONTRIB=0
92
+ if command -v git >/dev/null 2>&1 && git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
93
+ CONTRIB=$(git -C "$ROOT" log --since="90 days ago" --format='%ae' 2>/dev/null | sort -u | wc -l | tr -d ' ')
94
+ fi
95
+ cat > "$WORKSPACE_JSON" <<EOF
96
+ {
97
+ "schema_version": "1.0",
98
+ "mode": "lg",
99
+ "auto_detected": true,
100
+ "detected_contributors": ${CONTRIB},
101
+ "created_at": "$(date -Iseconds)"
102
+ }
103
+ EOF
104
+ planning_ok "Created .planning/.meta/workspace.json (mode=lg, contributors=${CONTRIB})"
105
+ fi
106
+
107
+ # Create phase directory
108
+ mkdir -p "$PHASE_DIR"
109
+
110
+ # Render phase.md
111
+ planning_render_and_log \
112
+ "${TEMPLATE_DIR}/phase.md" \
113
+ "${PHASE_DIR}/phase.md" \
114
+ "phase.md at ${PHASE_DIR}/phase.md" \
115
+ "PHASE_TITLE_PLACEHOLDER=${PHASE_NAME}" \
116
+ "PHASE_SLUG_PLACEHOLDER=${PHASE_SLUG}" \
117
+ "PHASE_DATE_PLACEHOLDER=${TODAY}"
118
+
119
+ # Render notes.md (scoped to the phase)
120
+ planning_render_and_log \
121
+ "${TEMPLATE_DIR}/notes.md" \
122
+ "${PHASE_DIR}/notes.md" \
123
+ "notes.md at ${PHASE_DIR}/notes.md" \
124
+ "NOTES_TITLE_PLACEHOLDER=${PHASE_NAME} — Notes" \
125
+ "NOTES_SCOPE_PLACEHOLDER=${PHASE_SLUG}" \
126
+ "NOTES_DATE_PLACEHOLDER=${TODAY}"
127
+
128
+ # Add .planning/ to .gitignore if missing (preserve existing behavior)
129
+ GITIGNORE="${ROOT}/.gitignore"
130
+ if [[ -f "$GITIGNORE" ]] && ! grep -q "^\.planning/" "$GITIGNORE"; then
131
+ echo ".planning/" >> "$GITIGNORE"
132
+ planning_ok "Added .planning/ to .gitignore"
133
+ fi
134
+
135
+ echo ""
136
+ planning_ok "Phase '${PHASE_NAME}' ready at .planning/${PHASE_SLUG}/"
137
+ echo ""
138
+ echo "Next steps:"
139
+ echo " 1. Edit .planning/${PHASE_SLUG}/phase.md to describe the phase goal"
140
+ echo " 2. Add tasks with: /start-task \"<task name>\" --parent ${PHASE_SLUG}"
141
+ echo " 3. Add cross-cutting context to .planning/${PHASE_SLUG}/notes.md"
@@ -0,0 +1,256 @@
1
+ #!/bin/bash
2
+
3
+ ###############################################################################
4
+ # init-planning.sh - Persistent Planning Directory Setup
5
+ #
6
+ # Part of the persistent-planning plugin by TheGlitchKing
7
+ # https://github.com/TheGlitchKing/persistent-planning
8
+ #
9
+ # Usage:
10
+ # bash scripts/init-planning.sh [task-name]
11
+ # bash scripts/init-planning.sh "Refactor database layer"
12
+ #
13
+ # What it does:
14
+ # 1. Creates .planning/[task-slug]/ directory (separate per task)
15
+ # 2. Creates task_plan.md with templates
16
+ # 3. Creates notes.md with templates
17
+ # 4. Adds .planning/ to .gitignore (optional)
18
+ # 5. Confirms creation and provides next steps
19
+ #
20
+ # Multiple tasks example:
21
+ # /start-planning "Refactor auth" -> .planning/refactor-auth/
22
+ # /start-planning "Debug memory leak" -> .planning/debug-memory-leak/
23
+ # Both plans coexist without conflicts!
24
+ ###############################################################################
25
+
26
+ set -e
27
+
28
+ # Validate that task name was provided
29
+ if [ -z "$1" ]; then
30
+ echo "Error: Task name required"
31
+ echo ""
32
+ echo "Usage: bash scripts/init-planning.sh \"Your Task Name\""
33
+ echo ""
34
+ echo "Examples:"
35
+ echo " bash scripts/init-planning.sh \"Refactor authentication\""
36
+ echo " bash scripts/init-planning.sh \"Debug API performance\""
37
+ exit 1
38
+ fi
39
+
40
+ TASK_NAME="$1"
41
+
42
+ # Convert task name to slug
43
+ # "Refactor Authentication" -> "refactor-authentication"
44
+ task_name_to_slug() {
45
+ local name="$1"
46
+ name=$(echo "$name" | tr '[:upper:]' '[:lower:]')
47
+ name=$(echo "$name" | sed 's/[[:space:]_]\+/-/g')
48
+ name=$(echo "$name" | sed 's/[^a-z0-9-]//g')
49
+ name=$(echo "$name" | sed 's/^-\+\|-\+$//g')
50
+ name=$(echo "$name" | sed 's/-\+/-/g')
51
+ echo "$name"
52
+ }
53
+
54
+ TASK_SLUG=$(task_name_to_slug "$TASK_NAME")
55
+
56
+ # Validate slug
57
+ if [ -z "$TASK_SLUG" ]; then
58
+ echo "Error: Task name must contain at least one alphanumeric character"
59
+ echo "Provided: $TASK_NAME"
60
+ exit 1
61
+ fi
62
+
63
+ # Determine project root
64
+ PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
65
+ PLANNING_DIR="${PROJECT_ROOT}/.planning"
66
+ TASK_DIR="${PLANNING_DIR}/${TASK_SLUG}"
67
+
68
+ # Color codes
69
+ GREEN='\033[0;32m'
70
+ BLUE='\033[0;34m'
71
+ YELLOW='\033[1;33m'
72
+ CYAN='\033[0;36m'
73
+ NC='\033[0m'
74
+
75
+ echo -e "${BLUE}Initializing Planning Structure${NC}"
76
+ echo ""
77
+
78
+ # Step 1: Create .planning directory
79
+ if [ ! -d "$PLANNING_DIR" ]; then
80
+ mkdir -p "$PLANNING_DIR"
81
+ echo -e "${GREEN}+${NC} Created .planning/ directory"
82
+ else
83
+ echo -e "${YELLOW}*${NC} .planning/ directory exists"
84
+ fi
85
+
86
+ # Step 2: Create task directory
87
+ if [ ! -d "$TASK_DIR" ]; then
88
+ mkdir -p "$TASK_DIR"
89
+ echo -e "${GREEN}+${NC} Created task directory: ${CYAN}.planning/${TASK_SLUG}/${NC}"
90
+ else
91
+ echo -e "${YELLOW}*${NC} Task directory already exists: ${CYAN}.planning/${TASK_SLUG}/${NC}"
92
+ fi
93
+
94
+ # Step 3: Create task_plan.md
95
+ TASK_PLAN_FILE="${TASK_DIR}/task_plan.md"
96
+ if [ ! -f "$TASK_PLAN_FILE" ]; then
97
+ cat > "$TASK_PLAN_FILE" << 'EOF'
98
+ # Task Plan: TASK_NAME_PLACEHOLDER
99
+
100
+ ## Goal
101
+ [One sentence describing the end state]
102
+
103
+ ## Phases
104
+ - [ ] Phase 1: Plan and setup
105
+ - [ ] Phase 2: Research/gather information
106
+ - [ ] Phase 3: Execute/build
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.
118
+
119
+ ## Key Questions
120
+ 1. [Question to answer]
121
+ 2. [Question to answer]
122
+
123
+ ## Decisions Made
124
+ - [Decision]: [Rationale]
125
+
126
+ ## Errors Encountered
127
+ - [Error]: [Resolution]
128
+
129
+ ## Status
130
+ **Currently in Phase 1** - Setting up planning structure
131
+
132
+ ---
133
+
134
+ ## Notes
135
+ - This file persists across sessions
136
+ - Update status after each phase
137
+ - Mark completed phases with [x]
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.
151
+ EOF
152
+
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"
155
+ echo -e "${GREEN}+${NC} Created task_plan.md"
156
+ else
157
+ echo -e "${YELLOW}*${NC} task_plan.md already exists (not overwritten)"
158
+ fi
159
+
160
+ # Step 4: Create notes.md
161
+ NOTES_FILE="${TASK_DIR}/notes.md"
162
+ if [ ! -f "$NOTES_FILE" ]; then
163
+ cat > "$NOTES_FILE" << 'EOF'
164
+ # Notes: TASK_NAME_PLACEHOLDER
165
+
166
+ ## Key Findings
167
+ - [Finding 1]
168
+ - [Finding 2]
169
+
170
+ ## Research Sources
171
+ - [Source 1]: [URL or reference]
172
+ - [Source 2]: [URL or reference]
173
+
174
+ ## Synthesized Findings
175
+
176
+ ### [Category/Topic 1]
177
+ - [Finding]
178
+ - [Finding]
179
+
180
+ ### [Category/Topic 2]
181
+ - [Finding]
182
+ - [Finding]
183
+
184
+ ## Decisions Made
185
+ - [Decision]: [Rationale]
186
+ - [Decision]: [Rationale]
187
+
188
+ ## Errors & Solutions
189
+ - [Error]: [Resolution]
190
+ - [Error]: [Resolution]
191
+
192
+ ---
193
+
194
+ ## Append-Only Log
195
+ Store findings here as you discover them. This builds a searchable history.
196
+
197
+ ### [Date/Time]
198
+ - [What I found/did]
199
+ EOF
200
+
201
+ sed -i "s/TASK_NAME_PLACEHOLDER/${TASK_NAME}/g" "$NOTES_FILE"
202
+ echo -e "${GREEN}+${NC} Created notes.md"
203
+ else
204
+ echo -e "${YELLOW}*${NC} notes.md already exists (not overwritten)"
205
+ fi
206
+
207
+ # Step 5: Update .gitignore
208
+ if [ -f "${PROJECT_ROOT}/.gitignore" ]; then
209
+ if ! grep -q "^\.planning/" "${PROJECT_ROOT}/.gitignore"; then
210
+ echo ".planning/" >> "${PROJECT_ROOT}/.gitignore"
211
+ echo -e "${GREEN}+${NC} Added .planning/ to .gitignore"
212
+ else
213
+ echo -e "${YELLOW}*${NC} .planning/ already in .gitignore"
214
+ fi
215
+ else
216
+ echo -e "${YELLOW}*${NC} No .gitignore found (not added)"
217
+ fi
218
+
219
+ # Step 6: List other active tasks
220
+ OTHER_TASKS=$(find "$PLANNING_DIR" -maxdepth 1 -type d -not -name ".planning" -not -path "$TASK_DIR" 2>/dev/null | wc -l)
221
+ if [ "$OTHER_TASKS" -gt 0 ]; then
222
+ echo ""
223
+ echo -e "${CYAN}Other active tasks:${NC}"
224
+ find "$PLANNING_DIR" -maxdepth 1 -type d -not -name ".planning" -not -path "$TASK_DIR" | sort | while read dir; do
225
+ task_slug=$(basename "$dir")
226
+ echo " .planning/${task_slug}/"
227
+ done
228
+ fi
229
+
230
+ # Step 7: Confirmation
231
+ echo ""
232
+ echo -e "${GREEN}=====================================${NC}"
233
+ echo -e "${GREEN}Task Plan Ready${NC}"
234
+ echo -e "${GREEN}=====================================${NC}"
235
+ echo ""
236
+
237
+ echo -e "${BLUE}Task:${NC} ${TASK_NAME}"
238
+ echo -e "${BLUE}Location:${NC} .planning/${TASK_SLUG}/"
239
+ echo ""
240
+
241
+ echo -e "${BLUE}Files:${NC}"
242
+ echo " task_plan.md (your task plan)"
243
+ echo " notes.md (research & findings)"
244
+ echo ""
245
+
246
+ echo -e "${BLUE}Next steps:${NC}"
247
+ echo " 1. Edit .planning/${TASK_SLUG}/task_plan.md to define your phases"
248
+ echo " 2. As you work, update the Status section"
249
+ echo " 3. Save findings to .planning/${TASK_SLUG}/notes.md"
250
+ echo " 4. Re-read task_plan.md before major decisions"
251
+ echo ""
252
+
253
+ echo -e "${BLUE}Cleanup:${NC}"
254
+ echo " rm -rf .planning/${TASK_SLUG}/ # Remove this task"
255
+ echo " rm -rf .planning/ # Remove all tasks"
256
+ echo ""
@@ -0,0 +1,118 @@
1
+ #!/bin/bash
2
+ ###############################################################################
3
+ # init-task.sh - Create a new task under a phase in lg-mode persistent-planning
4
+ #
5
+ # Usage:
6
+ # bash scripts/init-task.sh "Task Name" --parent <phase-slug>
7
+ #
8
+ # Creates:
9
+ # .planning/<phase-slug>/<task-slug>/task.md
10
+ # .planning/<phase-slug>/<task-slug>/notes.md
11
+ # .planning/<phase-slug>/<task-slug>/atoms/ (empty dir for future atoms)
12
+ #
13
+ # Refuses if mode is not lg, or if --parent doesn't reference an existing phase.
14
+ ###############################################################################
15
+ set -e
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ source "${SCRIPT_DIR}/lib/planning.sh"
19
+
20
+ # Parse arguments
21
+ TASK_NAME=""
22
+ PARENT_PHASE=""
23
+ while [[ $# -gt 0 ]]; do
24
+ case "$1" in
25
+ --parent)
26
+ PARENT_PHASE="$2"
27
+ shift 2
28
+ ;;
29
+ -h|--help)
30
+ cat <<EOF
31
+ Usage: bash scripts/init-task.sh "Task Name" --parent <phase-slug>
32
+
33
+ Examples:
34
+ bash scripts/init-task.sh "HEWTD schema extension" --parent foundation
35
+ bash scripts/init-task.sh "Multi-corpus refactor" --parent semantic-memory-core
36
+ EOF
37
+ exit 0
38
+ ;;
39
+ *)
40
+ if [[ -z "$TASK_NAME" ]]; then
41
+ TASK_NAME="$1"
42
+ fi
43
+ shift
44
+ ;;
45
+ esac
46
+ done
47
+
48
+ if [[ -z "$TASK_NAME" || -z "$PARENT_PHASE" ]]; then
49
+ planning_err "Both task name and --parent <phase-slug> are required."
50
+ echo "Usage: bash scripts/init-task.sh \"Task Name\" --parent <phase-slug>"
51
+ exit 1
52
+ fi
53
+
54
+ TASK_SLUG=$(planning_slugify "$TASK_NAME")
55
+
56
+ if [[ -z "$TASK_SLUG" ]]; then
57
+ planning_err "Task name must contain at least one alphanumeric character."
58
+ exit 1
59
+ fi
60
+
61
+ # Refuse non-lg
62
+ MODE=$(planning_mode)
63
+ if [[ "$MODE" != "lg" ]]; then
64
+ planning_err "Current mode is '$MODE' but init-task.sh requires lg mode."
65
+ exit 1
66
+ fi
67
+
68
+ ROOT=$(planning_root)
69
+ PLANNING="$(planning_dir)"
70
+ TODAY=$(planning_today)
71
+ PHASE_DIR="${PLANNING}/${PARENT_PHASE}"
72
+ TASK_DIR="${PHASE_DIR}/${TASK_SLUG}"
73
+ TEMPLATE_DIR="${ROOT}/templates/lg"
74
+
75
+ if [[ ! -d "$TEMPLATE_DIR" ]]; then
76
+ TEMPLATE_DIR="${SCRIPT_DIR}/../templates/lg"
77
+ fi
78
+ if [[ ! -d "$TEMPLATE_DIR" ]]; then
79
+ planning_err "Cannot locate templates/lg directory."
80
+ exit 1
81
+ fi
82
+
83
+ # Validate parent phase exists
84
+ if [[ ! -f "${PHASE_DIR}/phase.md" ]]; then
85
+ planning_err "Parent phase not found: .planning/${PARENT_PHASE}/phase.md"
86
+ echo "Create the phase first with:"
87
+ echo " /start-planning \"<phase name>\" (which runs init-phase.sh in lg mode)"
88
+ exit 1
89
+ fi
90
+
91
+ planning_log "Initializing task: ${TASK_NAME} (parent: ${PARENT_PHASE})"
92
+
93
+ mkdir -p "${TASK_DIR}/atoms"
94
+
95
+ planning_render_and_log \
96
+ "${TEMPLATE_DIR}/task.md" \
97
+ "${TASK_DIR}/task.md" \
98
+ "task.md at ${TASK_DIR}/task.md" \
99
+ "TASK_TITLE_PLACEHOLDER=${TASK_NAME}" \
100
+ "TASK_SLUG_PLACEHOLDER=${TASK_SLUG}" \
101
+ "PHASE_SLUG_PLACEHOLDER=${PARENT_PHASE}" \
102
+ "TASK_DATE_PLACEHOLDER=${TODAY}"
103
+
104
+ planning_render_and_log \
105
+ "${TEMPLATE_DIR}/notes.md" \
106
+ "${TASK_DIR}/notes.md" \
107
+ "notes.md at ${TASK_DIR}/notes.md" \
108
+ "NOTES_TITLE_PLACEHOLDER=${TASK_NAME} — Notes" \
109
+ "NOTES_SCOPE_PLACEHOLDER=${PARENT_PHASE}/${TASK_SLUG}" \
110
+ "NOTES_DATE_PLACEHOLDER=${TODAY}"
111
+
112
+ echo ""
113
+ planning_ok "Task '${TASK_NAME}' ready at .planning/${PARENT_PHASE}/${TASK_SLUG}/"
114
+ echo ""
115
+ echo "Next steps:"
116
+ echo " 1. Edit .planning/${PARENT_PHASE}/${TASK_SLUG}/task.md to describe the goal"
117
+ echo " 2. Add atoms with: /start-atom \"<atom name>\" --parent ${TASK_SLUG}"
118
+ echo " (or add inline atom checkboxes in task.md for simple atoms)"
@@ -0,0 +1,161 @@
1
+ #!/bin/bash
2
+ ###############################################################################
3
+ # scripts/lib/planning.sh - Shared helpers for persistent-planning init scripts
4
+ #
5
+ # Source this file from init-phase.sh, init-task.sh, init-atom.sh, and any
6
+ # other planning-related script that needs slug, root, mode, or template
7
+ # rendering helpers.
8
+ #
9
+ # Usage:
10
+ # #!/bin/bash
11
+ # set -e
12
+ # SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ # source "${SCRIPT_DIR}/lib/planning.sh"
14
+ #
15
+ # Provides:
16
+ # - planning_slugify "Some Name" → "some-name" (echoed)
17
+ # - planning_root → prints project root (CLAUDE_PROJECT_DIR or pwd)
18
+ # - planning_dir → prints "<root>/.planning"
19
+ # - planning_meta_dir → prints "<root>/.planning/.meta"
20
+ # - planning_workspace_json → prints "<meta>/workspace.json"
21
+ # - planning_mode → prints active mode (sm | lg) via detect-mode.sh
22
+ # - planning_today → prints today's date as YYYY-MM-DD
23
+ # - planning_render_template "<src>" "<dest>" "PLACEHOLDER1=value1" "PLACEHOLDER2=value2" ...
24
+ # Renders a template file by sed-substituting PLACEHOLDER tokens.
25
+ # Refuses to overwrite existing dest unless PLANNING_FORCE=1 in env.
26
+ # - planning_color_<name> → ANSI color codes (green, blue, yellow, cyan, red, reset)
27
+ # (only set when stdout is a tty; empty otherwise)
28
+ # - Color-coded log helpers: planning_log, planning_ok, planning_warn, planning_err
29
+ ###############################################################################
30
+
31
+ # Color codes (only when stdout is a tty)
32
+ if [[ -t 1 ]]; then
33
+ planning_color_green='\033[0;32m'
34
+ planning_color_blue='\033[0;34m'
35
+ planning_color_yellow='\033[1;33m'
36
+ planning_color_cyan='\033[0;36m'
37
+ planning_color_red='\033[0;31m'
38
+ planning_color_reset='\033[0m'
39
+ else
40
+ planning_color_green=''
41
+ planning_color_blue=''
42
+ planning_color_yellow=''
43
+ planning_color_cyan=''
44
+ planning_color_red=''
45
+ planning_color_reset=''
46
+ fi
47
+
48
+ planning_log() { printf "${planning_color_blue}[planning]${planning_color_reset} %s\n" "$*"; }
49
+ planning_ok() { printf "${planning_color_green}[planning]${planning_color_reset} %s\n" "$*"; }
50
+ planning_warn() { printf "${planning_color_yellow}[planning]${planning_color_reset} %s\n" "$*"; }
51
+ planning_err() { printf "${planning_color_red}[planning]${planning_color_reset} %s\n" "$*" >&2; }
52
+
53
+ planning_slugify() {
54
+ local name="$1"
55
+ name=$(echo "$name" | tr '[:upper:]' '[:lower:]')
56
+ name=$(echo "$name" | sed 's/[[:space:]_]\+/-/g')
57
+ name=$(echo "$name" | sed 's/[^a-z0-9-]//g')
58
+ name=$(echo "$name" | sed 's/^-\+\|-\+$//g')
59
+ name=$(echo "$name" | sed 's/-\+/-/g')
60
+ echo "$name"
61
+ }
62
+
63
+ planning_root() {
64
+ echo "${CLAUDE_PROJECT_DIR:-$(pwd)}"
65
+ }
66
+
67
+ planning_dir() {
68
+ echo "$(planning_root)/.planning"
69
+ }
70
+
71
+ planning_meta_dir() {
72
+ echo "$(planning_dir)/.meta"
73
+ }
74
+
75
+ planning_workspace_json() {
76
+ echo "$(planning_meta_dir)/workspace.json"
77
+ }
78
+
79
+ planning_today() {
80
+ date '+%Y-%m-%d'
81
+ }
82
+
83
+ planning_mode() {
84
+ # Resolve via detect-mode.sh (in the same scripts/ dir as the caller's library).
85
+ local lib_dir
86
+ lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
87
+ local detect="${lib_dir}/../detect-mode.sh"
88
+ if [[ -x "$detect" ]]; then
89
+ bash "$detect"
90
+ else
91
+ echo "sm"
92
+ fi
93
+ }
94
+
95
+ # planning_render_template <template_src> <dest_path> <PLACEHOLDER1=value1> ...
96
+ # Renders a template file by sed-substituting placeholders.
97
+ #
98
+ # Return codes:
99
+ # 0 → rendered fresh
100
+ # 2 → skipped (dest already exists; pass PLANNING_FORCE=1 to overwrite)
101
+ # 1 → error (template not found, etc.)
102
+ #
103
+ # Callers can branch on the return code to log accurately.
104
+ planning_render_template() {
105
+ local src="$1"
106
+ local dest="$2"
107
+ shift 2
108
+
109
+ if [[ ! -f "$src" ]]; then
110
+ planning_err "Template not found: $src"
111
+ return 1
112
+ fi
113
+
114
+ if [[ -f "$dest" && "${PLANNING_FORCE:-0}" != "1" ]]; then
115
+ planning_warn "Destination already exists, skipping: $dest"
116
+ planning_warn " (re-run with PLANNING_FORCE=1 to overwrite)"
117
+ return 2
118
+ fi
119
+
120
+ # Copy template to dest
121
+ cp "$src" "$dest"
122
+
123
+ # Apply substitutions
124
+ for kv in "$@"; do
125
+ local key="${kv%%=*}"
126
+ local value="${kv#*=}"
127
+ # Use a safe delimiter (~) since values may contain /
128
+ # Escape & and ~ in value
129
+ local escaped_value
130
+ escaped_value=$(printf '%s\n' "$value" | sed -e 's/[\&~]/\\&/g')
131
+ sed -i "s~${key}~${escaped_value}~g" "$dest"
132
+ done
133
+ return 0
134
+ }
135
+
136
+ # planning_render_and_log <template_src> <dest_path> <relative_label> <PLACEHOLDER=val> ...
137
+ # Wraps planning_render_template with accurate logging:
138
+ # - Prints "Created <relative_label>" only when the template was actually rendered
139
+ # - Prints nothing extra when skipped (the warn from render_template already covered it)
140
+ # - Lets the script continue (set -e tolerant) regardless of skip vs render
141
+ planning_render_and_log() {
142
+ local src="$1"
143
+ local dest="$2"
144
+ local label="$3"
145
+ shift 3
146
+
147
+ local rc=0
148
+ planning_render_template "$src" "$dest" "$@" || rc=$?
149
+ case $rc in
150
+ 0)
151
+ planning_ok "Created ${label}"
152
+ ;;
153
+ 2)
154
+ # Already covered by the warn inside planning_render_template
155
+ ;;
156
+ *)
157
+ planning_err "Failed to render ${label} (rc=$rc)"
158
+ return $rc
159
+ ;;
160
+ esac
161
+ }