Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
1f74034
Backup: Before implementing CCPM CLI tool
jeremymanning Aug 22, 2025
1b64979
Implement CCPM CLI tool with automatic GitHub CLI installation
jeremymanning Aug 22, 2025
48f1fb6
Complete CCPM CLI implementation with tests and documentation
jeremymanning Aug 22, 2025
b456918
Fix recursion error in test_setup_without_gh_continues
jeremymanning Aug 22, 2025
d955c86
Fix Windows Unicode encoding error in setup.py
jeremymanning Aug 22, 2025
7b5ccc2
Fix Windows emoji encoding issues in console output
jeremymanning Aug 22, 2025
80f7892
Fix indentation error in github.py
jeremymanning Aug 22, 2025
48a2bac
Fix uninstall command output for no installation case
jeremymanning Aug 22, 2025
e3a713a
Fix all remaining emoji characters for Windows compatibility
jeremymanning Aug 22, 2025
1f9ce0f
Comprehensive Windows emoji handling system
jeremymanning Aug 22, 2025
8028fcc
Fix Windows bash script execution
jeremymanning Aug 22, 2025
14117e1
Skip shell script tests on Windows
jeremymanning Aug 22, 2025
4c7615e
Update repository URLs to point to upstream fork
jeremymanning Aug 23, 2025
454d90c
Update CCPM CLI to invoke Claude Code directly
jeremymanning Aug 23, 2025
81fc09a
Bundle .claude template with package and fix installer
jeremymanning Aug 23, 2025
21f9ca5
Fix Claude Code CLI detection for local environments
jeremymanning Aug 23, 2025
6895636
Increase Claude command timeouts to 30 minutes
jeremymanning Aug 23, 2025
e524557
Fix test_sync_requires_github to handle Claude Code not installed
jeremymanning Aug 23, 2025
dff681d
Reorganize README for improved clarity - Closes #2
jeremymanning Aug 23, 2025
52f6880
Fix GitHub Actions tests - skip interactive auth in CI
jeremymanning Aug 23, 2025
a7b90bc
Skip Claude-dependent integration tests in CI
jeremymanning Aug 23, 2025
aa683c4
Make all Claude-dependent commands CI-aware
jeremymanning Aug 23, 2025
65a3e02
Remove .pyc files and improve .gitignore
jeremymanning Aug 23, 2025
d85032f
Fix security permissions and build artifacts issues
jeremymanning Aug 23, 2025
6bc4efe
Improve CI/CD coverage reporting
jeremymanning Aug 23, 2025
d6802bf
Fix linting issues (black, isort, flake8)
jeremymanning Aug 23, 2025
25a7c62
Fix test assertions and final formatting
jeremymanning Aug 23, 2025
5465e2f
Fix badge URLs and Codecov configuration
jeremymanning Aug 24, 2025
c946f70
Merge badge fixes from feature branch
jeremymanning Aug 24, 2025
5d0a18e
Address cross-platform shell script compatibility (issue #4)
jeremymanning Aug 25, 2025
9014f2b
Add comprehensive shell utility test coverage
jeremymanning Aug 25, 2025
fdb1d06
Fix linting issues in shell utility tests
jeremymanning Aug 25, 2025
cd2d056
Fix final linting issues before push
jeremymanning Aug 25, 2025
5360474
Merge branch 'feature/cli-and-readme-improvements'
jeremymanning Aug 25, 2025
0f8161d
Fix shell utility tests for Windows and Python 3.8 compatibility
jeremymanning Aug 25, 2025
50ae6f2
Simplify robust_parse test to fix CI failures
jeremymanning Aug 25, 2025
6899e78
Fix Windows Unicode encoding issues in shell utility tests
jeremymanning Aug 25, 2025
9c7cb49
Skip remaining Windows shell tests and fix all encoding issues
jeremymanning Aug 25, 2025
d654e7e
Fix final Windows emoji encoding issues
jeremymanning Aug 25, 2025
f59f632
Fix test assertion to match updated status.sh output format
jeremymanning Aug 25, 2025
f757b29
Improve CLI help and output visibility with Windows emoji compatibility
jeremymanning Aug 25, 2025
c7e1d3a
Fix test assertion for improved CLI list command output
jeremymanning Aug 25, 2025
c05c970
Add git merge permission to local settings
jeremymanning Aug 25, 2025
d96f641
Merge main branch CLI improvements into feature branch
jeremymanning Aug 25, 2025
fb57fd5
Complete template documentation cleanup - closes #7
jeremymanning Aug 25, 2025
539b722
Remove backup files from template cleanup
jeremymanning Aug 25, 2025
600827f
Backup before implementing Issue #8: Build artifacts and .gitignore i…
jeremymanning Aug 25, 2025
203d20a
Implement comprehensive packaging and .gitignore tests for Issue #8
jeremymanning Aug 25, 2025
8cffda9
Backup before implementing Issue #9: Shell script hardening
jeremymanning Aug 26, 2025
107e658
Complete Issue #9: Shell script hardening and ShellCheck warnings
jeremymanning Aug 26, 2025
dd1b8ca
Implement comprehensive security hardening for Issue #10
jeremymanning Aug 26, 2025
28f3bdf
Implement comprehensive code quality improvements for Issue #11
jeremymanning Aug 26, 2025
7fe37a8
Complete Issue #11: Code quality improvements implementation
jeremymanning Aug 26, 2025
76adb43
Complete Phase 4-5: Comprehensive documentation quality testing
jeremymanning Aug 27, 2025
5944f9b
Apply black formatting to test_documentation_quality.py
jeremymanning Aug 27, 2025
e29fc94
Fix CI: Install markdownlint and yamllint for documentation quality t…
jeremymanning Aug 27, 2025
c8b1f4c
Fix gitignore and test issues
jeremymanning Aug 27, 2025
b724bd0
Fix packaging test failures
jeremymanning Aug 27, 2025
69835d5
Fix permission compliance and PM shell script tests
jeremymanning Aug 27, 2025
c4a83a7
Fix security validation test failures
jeremymanning Aug 27, 2025
032faaf
Fix PM shell script test YAML formatting
jeremymanning Aug 27, 2025
f5477c3
Complete Issue #12: Fix documentation linting and implement comprehen…
jeremymanning Aug 28, 2025
f69cdd8
Fix CI test failures - preserve user context and add missing dependen…
jeremymanning Aug 28, 2025
8973fba
Fix CI: Add missing linting tools to all test jobs
jeremymanning Aug 28, 2025
893b5b4
Merge branch 'feature/cli-and-readme-improvements'
jeremymanning Aug 28, 2025
2ea4543
Fix missing ruff tool in Integration Tests job
jeremymanning Aug 28, 2025
41254b6
Fix ccpm clean command hanging on empty epics directory
jeremymanning Aug 28, 2025
637b162
Fix uninstall to properly remove context directory in clean installs
jeremymanning Aug 28, 2025
38a51ff
Complete Phase 1: Fix local test execution
jeremymanning Aug 28, 2025
27231e3
Fix global ccpm installation corruption during test execution
jeremymanning Aug 28, 2025
05fd0a6
Complete Phase 2: Fix all Python linting and formatting issues
jeremymanning Aug 28, 2025
a069150
Fix GitHub Actions workflow dependencies and configuration
jeremymanning Aug 28, 2025
fd97641
Fix PM shell script execution in CI environments
jeremymanning Aug 28, 2025
7cac4a2
Fix link validation test syntax error
jeremymanning Aug 28, 2025
fc0f14e
Fix security validation test for mypy command scoping
jeremymanning Aug 28, 2025
ada7887
Add mypy permission and PM test command to Claude settings
jeremymanning Aug 28, 2025
7415f77
Fix bash arithmetic compatibility in all PM shell scripts
jeremymanning Aug 29, 2025
f2c07ea
Merge branch 'main' into feature/cli-and-readme-improvements
jeremymanning Aug 30, 2025
6477d6a
Fix cross-platform compatibility for macOS and Windows
jeremymanning Aug 29, 2025
7c51dd9
Fix flaky network test by excluding problematic Twitter/X badge URL
jeremymanning Aug 29, 2025
c099dc5
Fix Windows test failures due to Unicode encoding issues
jeremymanning Aug 29, 2025
3f175d7
Fix duplicate permission in settings.local.json
jeremymanning Aug 29, 2025
2e30c85
Fix remaining Windows test failures - markdownlint detection and subp…
jeremymanning Aug 29, 2025
90e35df
Fix final 3 Windows-specific test failures
jeremymanning Aug 29, 2025
8b138db
Fix Windows path case sensitivity and directory cleanup issues
jeremymanning Aug 29, 2025
7beb98e
Fix remaining 3 Windows test failures properly
jeremymanning Aug 29, 2025
7df6c4a
Fix final Python 3.8 and 3.11 Windows test failures
jeremymanning Aug 29, 2025
760899e
Fix shutil scoping issue in Windows pip cleanup
jeremymanning Aug 29, 2025
acaada9
Implement comprehensive Python 3.8 Windows cleanup for 100% success
jeremymanning Aug 29, 2025
3e44946
Complete improved .claude directory backup and merge system
jeremymanning Aug 30, 2025
bf090fa
Update local settings with additional permissions
jeremymanning Aug 30, 2025
3da771c
Fix GitHub Actions test failures
jeremymanning Aug 30, 2025
733c706
Update local settings for additional bash permissions
jeremymanning Aug 30, 2025
02e2ade
Fix chmod failure in init.sh when no .sh files exist
jeremymanning Aug 30, 2025
6a9d42d
Fix markdownlint error: add missing trailing newline to README.md
jeremymanning Aug 30, 2025
b5d06e6
Comprehensive security fixes and code improvements
jeremymanning Aug 30, 2025
3ac2d24
Fix Windows compatibility in search.sh find commands
jeremymanning Aug 30, 2025
bf28827
Fix Windows compatibility in search.sh by avoiding find -exec
jeremymanning Aug 30, 2025
506d856
Rewrite search.sh with Windows-compatible file iteration
jeremymanning Aug 30, 2025
e11a5a2
Simplify search.sh with direct grep approach for Windows compatibility
jeremymanning Aug 30, 2025
9abfc58
Fix Windows search script with temporary file approach
jeremymanning Aug 30, 2025
225427e
Fix Windows search script compatibility by removing -F flag
jeremymanning Aug 30, 2025
b103e50
Fix shell script inclusion in Python package distribution
jeremymanning Sep 4, 2025
db43928
Fix remaining test failures for 100% test success
jeremymanning Sep 4, 2025
134a318
Fix Windows GitHub Actions by switching to winget installer
jeremymanning Sep 4, 2025
40a32cf
Improve Windows installation with direct download approach
jeremymanning Sep 4, 2025
db8bac1
Add robust Windows CLI installation with timeout and fallback
jeremymanning Sep 4, 2025
2475cee
Update local settings with additional permissions
jeremymanning Sep 4, 2025
84f2c08
Fix GitHub label creation errors by ensuring labels exist before use
jeremymanning Sep 4, 2025
f4ce3c6
Fix security validation tests and duplicate permissions
jeremymanning Sep 4, 2025
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
11 changes: 7 additions & 4 deletions .claude/commands/pm/epic-refresh.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,19 @@ if [ ! -z "$epic_issue" ]; then
gh issue view $epic_issue --json body -q .body > /tmp/epic-body.md

# For each task, check its status and update checkbox
# Source cross-platform utilities
source .claude/scripts/utils.sh

for task_file in .claude/epics/$ARGUMENTS/[0-9]*.md; do
task_issue=$(grep 'github:' $task_file | grep -oE '[0-9]+$')
task_status=$(grep 'status:' $task_file | cut -d: -f2 | tr -d ' ')

if [ "$task_status" = "closed" ]; then
# Mark as checked
sed -i "s/- \[ \] #$task_issue/- [x] #$task_issue/" /tmp/epic-body.md
# Mark as checked - cross-platform approach
cross_platform_sed "s/- \[ \] #$task_issue/- [x] #$task_issue/" /tmp/epic-body.md
else
# Ensure unchecked (in case manually checked)
sed -i "s/- \[x\] #$task_issue/- [ ] #$task_issue/" /tmp/epic-body.md
# Ensure unchecked (in case manually checked) - cross-platform approach
cross_platform_sed "s/- \[x\] #$task_issue/- [ ] #$task_issue/" /tmp/epic-body.md
fi
done

Expand Down
32 changes: 24 additions & 8 deletions .claude/commands/pm/epic-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ else
epic_type="feature"
fi

# Ensure labels exist
gh label create "epic" --force 2>/dev/null || true
gh label create "epic:$ARGUMENTS" --force 2>/dev/null || true
gh label create "$epic_type" --force 2>/dev/null || true

# Create epic issue with labels
epic_number=$(gh issue create \
--title "Epic: $ARGUMENTS" \
Expand Down Expand Up @@ -147,6 +152,10 @@ if [ "$task_count" -lt 5 ]; then
# Strip frontmatter from task content
sed '1,/^---$/d; 1,/^---$/d' "$task_file" > /tmp/task-body.md

# Ensure labels exist
gh label create "task" --force 2>/dev/null || true
gh label create "epic:$ARGUMENTS" --force 2>/dev/null || true

# Create sub-issue with labels
if [ "$use_subissues" = true ]; then
task_number=$(gh sub-issue create \
Expand Down Expand Up @@ -208,9 +217,13 @@ Task:
2. Strip frontmatter using: sed '1,/^---$/d; 1,/^---$/d'
3. Create sub-issue using:
- If gh-sub-issue available:
gh label create "task" --force 2>/dev/null || true
gh label create "epic:$ARGUMENTS" --force 2>/dev/null || true
gh sub-issue create --parent $epic_number --title "$task_name" \
--body-file /tmp/task-body.md --label "task,epic:$ARGUMENTS"
- Otherwise:
gh label create "task" --force 2>/dev/null || true
gh label create "epic:$ARGUMENTS" --force 2>/dev/null || true
gh issue create --title "$task_name" --body-file /tmp/task-body.md \
--label "task,epic:$ARGUMENTS"
4. Record: task_file:issue_number
Expand Down Expand Up @@ -272,11 +285,13 @@ while IFS=: read -r task_file task_number; do

# Update frontmatter with GitHub URL and current timestamp
current_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Use cross-platform sed to update the github and updated fields
source .claude/scripts/utils.sh
cross_platform_sed_backup "/^github:/c\github: $github_url" "$new_name"
cross_platform_sed_backup "/^updated:/c\updated: $current_date" "$new_name"
# Backup files are automatically managed by cross_platform_sed_backup

Comment on lines +288 to 294
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Guard sourcing utils.sh to avoid hard failures; provide fallback

If .claude/scripts/utils.sh is missing, the script will fail. Add a safe guard and fallback to sed -i.bak.

-# Use cross-platform sed to update the github and updated fields
-source .claude/scripts/utils.sh
-cross_platform_sed_backup "/^github:/c\github: $github_url" "$new_name"
-cross_platform_sed_backup "/^updated:/c\updated: $current_date" "$new_name"
-# Backup files are automatically managed by cross_platform_sed_backup
+# Use cross-platform sed to update the github and updated fields
+if [ -f ".claude/scripts/utils.sh" ]; then
+  # shellcheck disable=SC1091
+  source .claude/scripts/utils.sh
+  cross_platform_sed_backup "/^github:/c\github: $github_url" "$new_name"
+  cross_platform_sed_backup "/^updated:/c\updated: $current_date" "$new_name"
+else
+  # Fallback
+  sed -i.bak -E "s|^github:.*$|github: $github_url|" "$new_name"
+  sed -i.bak -E "s|^updated:.*$|updated: $current_date|" "$new_name"
+fi
+# Backup files may be created with *.bak in fallback mode
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Use cross-platform sed to update the github and updated fields
source .claude/scripts/utils.sh
cross_platform_sed_backup "/^github:/c\github: $github_url" "$new_name"
cross_platform_sed_backup "/^updated:/c\updated: $current_date" "$new_name"
# Backup files are automatically managed by cross_platform_sed_backup
# Use cross-platform sed to update the github and updated fields
if [ -f ".claude/scripts/utils.sh" ]; then
# shellcheck disable=SC1091
source .claude/scripts/utils.sh
cross_platform_sed_backup "/^github:/c\github: $github_url" "$new_name"
cross_platform_sed_backup "/^updated:/c\updated: $current_date" "$new_name"
else
# Fallback
sed -i.bak -E "s|^github:.*$|github: $github_url|" "$new_name"
sed -i.bak -E "s|^updated:.*$|updated: $current_date|" "$new_name"
fi
# Backup files may be created with *.bak in fallback mode
πŸ€– Prompt for AI Agents
In .claude/commands/pm/epic-sync.md around lines 288 to 294, the script
unconditionally sources .claude/scripts/utils.sh which will hard-fail if the
file is missing; add a guard that checks for the file before sourcing and, if
absent, define a minimal fallback function (e.g., cross_platform_sed_backup)
that uses sed -i.bak to perform the same replacement semantics, then continue
using cross_platform_sed_backup as before so the script works on systems without
utils.sh.

# Use sed to update the github and updated fields
sed -i.bak "/^github:/c\github: $github_url" "$new_name"
sed -i.bak "/^updated:/c\updated: $current_date" "$new_name"
rm "${new_name}.bak"
done < /tmp/task-mapping.txt
```

Expand Down Expand Up @@ -316,10 +331,11 @@ repo=$(gh repo view --json nameWithOwner -q .nameWithOwner)
epic_url="https://github.com/$repo/issues/$epic_number"
current_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Update epic frontmatter
sed -i.bak "/^github:/c\github: $epic_url" .claude/epics/$ARGUMENTS/epic.md
sed -i.bak "/^updated:/c\updated: $current_date" .claude/epics/$ARGUMENTS/epic.md
rm .claude/epics/$ARGUMENTS/epic.md.bak
# Update epic frontmatter - cross-platform approach
source .claude/scripts/utils.sh
cross_platform_sed_backup "/^github:/c\github: $epic_url" .claude/epics/$ARGUMENTS/epic.md
cross_platform_sed_backup "/^updated:/c\updated: $current_date" .claude/epics/$ARGUMENTS/epic.md
# Backup files are automatically managed by cross_platform_sed_backup
```

#### 5b. Update Tasks Created Section
Expand Down
5 changes: 3 additions & 2 deletions .claude/commands/pm/issue-close.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ if [ ! -z "$epic_issue" ]; then
# Get current epic body
gh issue view $epic_issue --json body -q .body > /tmp/epic-body.md

# Check off this task
sed -i "s/- \[ \] #$ARGUMENTS/- [x] #$ARGUMENTS/" /tmp/epic-body.md
# Check off this task - use cross-platform approach
source .claude/scripts/utils.sh
cross_platform_sed "s/- \[ \] #$ARGUMENTS/- [x] #$ARGUMENTS/" /tmp/epic-body.md

# Update epic issue
gh issue edit $epic_issue --body-file /tmp/epic-body.md
Expand Down
4 changes: 4 additions & 0 deletions .claude/commands/pm/issue-edit.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ gh issue edit $ARGUMENTS --body-file {updated_task_file}

If labels changed:
```bash
# Ensure labels exist
for label in {new_labels}; do
gh label create "$label" --force 2>/dev/null || true
done
Comment on lines +58 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Handle multi-label values and spaces safely when creating labels

{new_labels} is likely a comma-separated string; the current loop will split on IFS (spaces), breaking labels containing spaces and not splitting on commas. Parse by comma and trim whitespace.

-# Ensure labels exist
-for label in {new_labels}; do
-  gh label create "$label" --force 2>/dev/null || true
-done
+# Ensure labels exist (comma-separated), robust to spaces in labels
+IFS=, read -ra __labels__ <<< "{new_labels}"
+for raw in "${__labels__[@]}"; do
+  label="$(echo "$raw" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
+  [ -n "$label" ] || continue
+  gh label create "$label" --force 2>/dev/null || true
+done
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Ensure labels exist
for label in {new_labels}; do
gh label create "$label" --force 2>/dev/null || true
done
# Ensure labels exist (comma-separated), robust to spaces in labels
IFS=, read -ra __labels__ <<< "{new_labels}"
for raw in "${__labels__[@]}"; do
label="$(echo "$raw" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
[ -n "$label" ] || continue
gh label create "$label" --force 2>/dev/null || true
done
πŸ€– Prompt for AI Agents
In .claude/commands/pm/issue-edit.md around lines 58 to 61, the label creation
loop splits on spaces and will break labels that contain spaces and won't split
a comma-separated list correctly; change the logic to split the input by commas,
trim leading/trailing whitespace from each resulting token, skip empty tokens,
and then call gh label create for each cleaned label (still suppress errors as
before). Ensure you handle labels with spaces safely by iterating over the
comma-separated, trimmed list rather than relying on word-splitting.

gh issue edit $ARGUMENTS --add-label "{new_labels}"
gh issue edit $ARGUMENTS --remove-label "{removed_labels}"
```
Expand Down
2 changes: 2 additions & 0 deletions .claude/commands/pm/issue-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ Task:

```bash
# Assign to self and mark in-progress
# Ensure label exists
gh label create "in-progress" --force 2>/dev/null || true
gh issue edit $ARGUMENTS --add-assignee @me --add-label "in-progress"
```

Expand Down
6 changes: 6 additions & 0 deletions .claude/rules/github-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,18 @@ gh issue view {number} --json state,title,labels,body
### Create Issue
```bash
# ALWAYS check remote origin first!
# Ensure labels exist (force create if needed)
for label in {labels}; do
gh label create "$label" --force 2>/dev/null || true
done
Comment on lines +58 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Create-all-labels loop should split by comma and trim whitespace

As written, labels with spaces break and comma lists aren’t split. Use a comma-aware parse and trim each label before creation.

-# Ensure labels exist (force create if needed)
-for label in {labels}; do
-  gh label create "$label" --force 2>/dev/null || true
-done
+# Ensure labels exist (force create if needed)
+IFS=, read -ra __labels__ <<< "{labels}"
+for raw in "${__labels__[@]}"; do
+  label="$(echo "$raw" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
+  [ -n "$label" ] || continue
+  gh label create "$label" --force 2>/dev/null || true
+done
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Ensure labels exist (force create if needed)
for label in {labels}; do
gh label create "$label" --force 2>/dev/null || true
done
# Ensure labels exist (force create if needed)
IFS=, read -ra __labels__ <<< "{labels}"
for raw in "${__labels__[@]}"; do
label="$(echo "$raw" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
[ -n "$label" ] || continue
gh label create "$label" --force 2>/dev/null || true
done
πŸ€– Prompt for AI Agents
In .claude/rules/github-operations.md around lines 58-61, the current loop
treats the entire labels string as single token so comma-separated lists and
labels containing spaces break; update the parsing to split the input on commas
and trim surrounding whitespace for each label before calling gh label create.
Specifically, iterate over the comma-separated items (not space-splitting), trim
leading/trailing whitespace from each item, skip empty entries, and then call gh
label create "$label" --force redirecting errors as before.

gh issue create --title "{title}" --body-file {file} --label "{labels}"
```

### Update Issue
```bash
# ALWAYS check remote origin first!
# Ensure label exists (force create if needed)
gh label create "{label}" --force 2>/dev/null || true
gh issue edit {number} --add-label "{label}" --add-assignee @me
```

Expand Down
11 changes: 6 additions & 5 deletions .claude/scripts/pm/blocked.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/bin/bash
set -euo pipefail
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

⚠️ Potential issue

set -euo pipefail introduces hard failures via grep|sed pipelines (empty matches exit non‑zero). Fix parsing to be non-fatal.

With set -e + pipefail, grep returning 1 (no match) aborts the script. Tasks missing status, depends_on, or name will cause premature exit. Switch to awk/sed -n forms that succeed when there’s no match, and simplify the deps check accordingly.

Apply:

-set -euo pipefail
+set -euo pipefail
-    status=$(grep "^status:" "$task_file" | head -1 | sed 's/^status: *//')
+    # Extract first status value; no-fail if absent
+    status=$(awk -F': *' '/^status:/ {print $2; exit}' "$task_file")
-    deps=$(grep "^depends_on:" "$task_file" | head -1 | sed 's/^depends_on: *\[//' | sed 's/\]//' | sed 's/,/ /g')
+    # Extract deps; normalize delimiters to spaces; no-fail if absent
+    deps=$(awk -F': *' '/^depends_on:/ {gsub(/\[|\]|,/, " "); print $2; exit}' "$task_file")
-    if [ -n "$deps" ] && [ "$deps" != "depends_on:" ]; then
+    if [ -n "$deps" ]; then
-      task_name=$(grep "^name:" "$task_file" | head -1 | sed 's/^name: *//')
+      task_name=$(awk -F': *' '/^name:/ {print $2; exit}' "$task_file")
-          dep_status=$(grep "^status:" "$dep_file" | head -1 | sed 's/^status: *//')
+          dep_status=$(awk -F': *' '/^status:/ {print $2; exit}' "$dep_file")

Also applies to: 21-26, 28-28, 36-42

πŸ€– Prompt for AI Agents
In .claude/scripts/pm/blocked.sh around line 2 (and also adjust occurrences at
21-26, 28, and 36-42): the top-level use of set -euo pipefail makes grep/sed
pipelines fatal when they return no matches, causing the script to abort on
missing fields; replace those grep pipelines with non-failing parses (use awk or
sed -n forms that safely produce empty output instead of failing), or explicitly
handle grep non-zero exits (e.g., capture output into a variable with command ||
true). Also simplify the dependencies check to test whether the parsed deps
string/array is empty rather than relying on the pipeline exit status. Ensure
all parsing commands return success even when no match is found so the script
does not terminate prematurely.

echo "Getting tasks..."
echo ""
echo ""

echo "🚫 Blocked Tasks"
echo "[BLOCKED] Blocked Tasks"
echo "================"
echo ""

Expand All @@ -27,7 +28,7 @@ for epic_dir in .claude/epics/*/; do
task_name=$(grep "^name:" "$task_file" | head -1 | sed 's/^name: *//')
task_num=$(basename "$task_file" .md)

echo "⏸️ Task #$task_num - $task_name"
echo "[PAUSED] Task #$task_num - $task_name"
echo " Epic: $epic_name"
echo " Blocked by: [$deps]"

Expand All @@ -43,17 +44,17 @@ for epic_dir in .claude/epics/*/; do

[ -n "$open_deps" ] && echo " Waiting for:$open_deps"
echo ""
((found++))
found=$((found + 1))
fi
done
done

if [ $found -eq 0 ]; then
echo "No blocked tasks found!"
echo ""
echo "πŸ’‘ All tasks with dependencies are either completed or in progress."
echo "TIP All tasks with dependencies are either completed or in progress."
else
echo "πŸ“Š Total blocked: $found tasks"
echo "STATUS Total blocked: $found tasks"
fi

exit 0
35 changes: 18 additions & 17 deletions .claude/scripts/pm/epic-list.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/bin/bash
set -euo pipefail
echo "Getting epics..."
echo ""
echo ""

[ ! -d ".claude/epics" ] && echo "πŸ“ No epics directory found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
[ -z "$(ls -d .claude/epics/*/ 2>/dev/null)" ] && echo "πŸ“ No epics found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
[ ! -d ".claude/epics" ] && echo "No epics directory found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
[ -z "$(ls -d .claude/epics/*/ 2>/dev/null)" ] && echo "No epics found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid ls in command substitutions under set -euo pipefail.

ls + globs can fail and abort the script. Use compgen or find.

-[ ! -d ".claude/epics" ] && echo "No epics directory found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
-[ -z "$(ls -d .claude/epics/*/ 2>/dev/null)" ] && echo "No epics found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
+[ ! -d ".claude/epics" ] && echo "No epics directory found. Create your first epic with: /pm:prd-parse <feature-name>" && exit 0
+if ! compgen -G ".claude/epics/*/" > /dev/null; then
+  echo "No epics found. Create your first epic with: /pm:prd-parse <feature-name>"
+  exit 0
+fi
πŸ€– Prompt for AI Agents
In .claude/scripts/pm/epic-list.sh around lines 7 to 8, the command
substitutions use ls with globs which can fail under set -euo pipefail; replace
these checks with safe alternatives such as compgen -G ".claude/epics/*/" or a
find command to test for existence without invoking ls. Specifically, for the
first check test [ ! -d ".claude/epics" ] keep as-is, and for the second replace
the ls substitution with a compgen or find-based existence test (e.g., use
compgen -G ".claude/epics/*/" >/dev/null || ...) so the script won’t abort on
glob expansion failures.


echo "πŸ“š Project Epics"
echo "EPIC Project Epics"
echo "================"
echo ""

Expand All @@ -21,24 +22,24 @@ for dir in .claude/epics/*/; do
[ -f "$dir/epic.md" ] || continue

# Extract metadata
n=$(grep "^name:" "$dir/epic.md" | head -1 | sed 's/^name: *//')
s=$(grep "^status:" "$dir/epic.md" | head -1 | sed 's/^status: *//' | tr '[:upper:]' '[:lower:]')
p=$(grep "^progress:" "$dir/epic.md" | head -1 | sed 's/^progress: *//')
g=$(grep "^github:" "$dir/epic.md" | head -1 | sed 's/^github: *//')
n=$(grep "^name:" "$dir/epic.md" | head -1 | sed 's/^name: *//' || true)
s=$(grep "^status:" "$dir/epic.md" | head -1 | sed 's/^status: *//' | tr '[:upper:]' '[:lower:]' || true)
p=$(grep "^progress:" "$dir/epic.md" | head -1 | sed 's/^progress: *//' || true)
g=$(grep "^github:" "$dir/epic.md" | head -1 | sed 's/^github: *//' || true)

# Defaults
[ -z "$n" ] && n=$(basename "$dir")
[ -z "$p" ] && p="0%"

# Count tasks
t=$(ls "$dir"[0-9]*.md 2>/dev/null | wc -l)
t=$(ls "$dir"[0-9]*.md 2>/dev/null | wc -l || echo "0")

Comment on lines +35 to 36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Robust task counting and portability.

Avoid ls in pipelines; use find with -type f.

-  t=$(ls "$dir"[0-9]*.md 2>/dev/null | wc -l || echo "0")
+  t=$(find "$dir" -maxdepth 1 -type f -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
t=$(ls "$dir"[0-9]*.md 2>/dev/null | wc -l || echo "0")
t=$(find "$dir" -maxdepth 1 -type f -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
πŸ€– Prompt for AI Agents
In .claude/scripts/pm/epic-list.sh around lines 35–36, the current task count
uses "ls" in a pipeline which is fragile and non-portable; replace it with a
find-based count such as using find "$dir" -maxdepth 1 -type f -name '[0-9]*.md'
and pipe that to wc -l (or use find ... -print0 + tr -cd '\0' | wc -c for
NUL-safe counting) to reliably count matching files; ensure you handle the case
where $dir may be empty or contain spaces by quoting it and assign the result to
t via command substitution.

# Format output with GitHub issue number if available
if [ -n "$g" ]; then
i=$(echo "$g" | grep -o '/[0-9]*$' | tr -d '/')
entry=" πŸ“‹ ${dir}epic.md (#$i) - $p complete ($t tasks)"
i=$(echo "$g" | grep -o '/[0-9]*$' | tr -d '/' || true)
entry=" TASK ${dir}epic.md (#$i) - $p complete ($t tasks)"
else
entry=" πŸ“‹ ${dir}epic.md - $p complete ($t tasks)"
entry=" TASK ${dir}epic.md - $p complete ($t tasks)"
fi

# Categorize by status (handle various status values)
Expand All @@ -60,23 +61,23 @@ for dir in .claude/epics/*/; do
done

# Display categorized epics
echo "πŸ“ Planning:"
echo "NOTE Planning:"
if [ -n "$planning_epics" ]; then
echo -e "$planning_epics" | sed '/^$/d'
else
echo " (none)"
fi

echo ""
echo "πŸš€ In Progress:"
echo "LAUNCH In Progress:"
if [ -n "$in_progress_epics" ]; then
echo -e "$in_progress_epics" | sed '/^$/d'
else
echo " (none)"
fi

echo ""
echo "βœ… Completed:"
echo "OK Completed:"
if [ -n "$completed_epics" ]; then
echo -e "$completed_epics" | sed '/^$/d'
else
Expand All @@ -85,9 +86,9 @@ fi

# Summary
echo ""
echo "πŸ“Š Summary"
total=$(ls -d .claude/epics/*/ 2>/dev/null | wc -l)
tasks=$(find .claude/epics -name "[0-9]*.md" 2>/dev/null | wc -l)
echo "STATUS Summary"
total=$(ls -d .claude/epics/*/ 2>/dev/null | wc -l || echo "0")
tasks=$(find .claude/epics -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
echo " Total epics: $total"
echo " Total tasks: $tasks"
Comment on lines +90 to 93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Summary counters should avoid ls and include -type filters.

More reliable directory/file counts.

-total=$(ls -d .claude/epics/*/ 2>/dev/null | wc -l || echo "0")
-tasks=$(find .claude/epics -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
+total=$(find .claude/epics -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l || echo "0")
+tasks=$(find .claude/epics -type f -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
total=$(ls -d .claude/epics/*/ 2>/dev/null | wc -l || echo "0")
tasks=$(find .claude/epics -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
echo " Total epics: $total"
echo " Total tasks: $tasks"
total=$(find .claude/epics -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l || echo "0")
tasks=$(find .claude/epics -type f -name "[0-9]*.md" 2>/dev/null | wc -l || echo "0")
echo " Total epics: $total"
echo " Total tasks: $tasks"


Expand Down
39 changes: 21 additions & 18 deletions .claude/scripts/pm/epic-show.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/bin/bash
set -euo pipefail

epic_name="$1"
epic_name="${1:-}"

if [ -z "$epic_name" ]; then
echo "❌ Please provide an epic name"
echo "ERROR Please provide an epic name"
echo "Usage: /pm:epic-show <epic-name>"
exit 1
fi
Expand All @@ -16,17 +17,17 @@ epic_dir=".claude/epics/$epic_name"
epic_file="$epic_dir/epic.md"

if [ ! -f "$epic_file" ]; then
echo "❌ Epic not found: $epic_name"
echo "ERROR Epic not found: $epic_name"
echo ""
echo "Available epics:"
for dir in .claude/epics/*/; do
[ -d "$dir" ] && echo " β€’ $(basename "$dir")"
[ -d "$dir" ] && echo " * $(basename "$dir")"
done
exit 1
fi

# Display epic details
echo "πŸ“š Epic: $epic_name"
echo "EPIC Epic: $epic_name"
echo "================================"
echo ""

Expand All @@ -36,15 +37,15 @@ progress=$(grep "^progress:" "$epic_file" | head -1 | sed 's/^progress: *//')
github=$(grep "^github:" "$epic_file" | head -1 | sed 's/^github: *//')
created=$(grep "^created:" "$epic_file" | head -1 | sed 's/^created: *//')

echo "πŸ“Š Metadata:"
echo "STATUS Metadata:"
echo " Status: ${status:-planning}"
echo " Progress: ${progress:-0%}"
[ -n "$github" ] && echo " GitHub: $github"
echo " Created: ${created:-unknown}"
echo ""

# Show tasks
echo "πŸ“ Tasks:"
echo "NOTE Tasks:"
task_count=0
open_count=0
closed_count=0
Expand All @@ -58,15 +59,17 @@ for task_file in "$epic_dir"/[0-9]*.md; do
parallel=$(grep "^parallel:" "$task_file" | head -1 | sed 's/^parallel: *//')

if [ "$task_status" = "closed" ] || [ "$task_status" = "completed" ]; then
echo " βœ… #$task_num - $task_name"
((closed_count++))
echo " OK #$task_num - $task_name"
closed_count=$((closed_count + 1))
else
echo " ⬜ #$task_num - $task_name"
[ "$parallel" = "true" ] && echo -n " (parallel)"
((open_count++))
# Construct full output string to avoid dangling output
output_line=" OPEN #$task_num - $task_name"
[ "$parallel" = "true" ] && output_line="$output_line (parallel)"
echo "$output_line"
open_count=$((open_count + 1))
fi

((task_count++))
task_count=$((task_count + 1))
done

if [ $task_count -eq 0 ]; then
Expand All @@ -75,17 +78,17 @@ if [ $task_count -eq 0 ]; then
fi

echo ""
echo "πŸ“ˆ Statistics:"
echo "STATISTICS:"
echo " Total tasks: $task_count"
echo " Open: $open_count"
echo " Closed: $closed_count"
[ $task_count -gt 0 ] && echo " Completion: $((closed_count * 100 / task_count))%"

# Next actions
echo ""
echo "πŸ’‘ Actions:"
[ $task_count -eq 0 ] && echo " β€’ Decompose into tasks: /pm:epic-decompose $epic_name"
[ -z "$github" ] && [ $task_count -gt 0 ] && echo " β€’ Sync to GitHub: /pm:epic-sync $epic_name"
[ -n "$github" ] && [ "$status" != "completed" ] && echo " β€’ Start work: /pm:epic-start $epic_name"
echo "TIP Actions:"
[ $task_count -eq 0 ] && echo " * Decompose into tasks: /pm:epic-decompose $epic_name"
[ -z "$github" ] && [ $task_count -gt 0 ] && echo " * Sync to GitHub: /pm:epic-sync $epic_name"
[ -n "$github" ] && [ "$status" != "completed" ] && echo " * Start work: /pm:epic-start $epic_name"

exit 0
Loading