codeforge-dev 1.4.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 (131) hide show
  1. package/.devcontainer/.env +22 -0
  2. package/.devcontainer/CHANGELOG.md +197 -0
  3. package/.devcontainer/CLAUDE.md +117 -0
  4. package/.devcontainer/README.md +222 -0
  5. package/.devcontainer/config/main-system-prompt.md +502 -0
  6. package/.devcontainer/config/settings.json +47 -0
  7. package/.devcontainer/devcontainer.json +94 -0
  8. package/.devcontainer/features/README.md +113 -0
  9. package/.devcontainer/features/agent-browser/README.md +65 -0
  10. package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
  11. package/.devcontainer/features/agent-browser/install.sh +79 -0
  12. package/.devcontainer/features/ast-grep/README.md +24 -0
  13. package/.devcontainer/features/ast-grep/devcontainer-feature.json +24 -0
  14. package/.devcontainer/features/ast-grep/install.sh +51 -0
  15. package/.devcontainer/features/ccstatusline/README.md +296 -0
  16. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +19 -0
  17. package/.devcontainer/features/ccstatusline/install.sh +290 -0
  18. package/.devcontainer/features/ccusage/README.md +205 -0
  19. package/.devcontainer/features/ccusage/devcontainer-feature.json +38 -0
  20. package/.devcontainer/features/ccusage/install.sh +132 -0
  21. package/.devcontainer/features/claude-code/README.md +498 -0
  22. package/.devcontainer/features/claude-code/config/settings.json +36 -0
  23. package/.devcontainer/features/claude-code/config/system-prompt.md +118 -0
  24. package/.devcontainer/features/claude-code/config/world-building-sp.md +1432 -0
  25. package/.devcontainer/features/claude-code/devcontainer-feature.json +42 -0
  26. package/.devcontainer/features/claude-code/install.sh +466 -0
  27. package/.devcontainer/features/claude-monitor/README.md +74 -0
  28. package/.devcontainer/features/claude-monitor/devcontainer-feature.json +38 -0
  29. package/.devcontainer/features/claude-monitor/install.sh +99 -0
  30. package/.devcontainer/features/lsp-servers/README.md +85 -0
  31. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +40 -0
  32. package/.devcontainer/features/lsp-servers/install.sh +116 -0
  33. package/.devcontainer/features/mcp-qdrant/CHANGES.md +399 -0
  34. package/.devcontainer/features/mcp-qdrant/README.md +474 -0
  35. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +57 -0
  36. package/.devcontainer/features/mcp-qdrant/install.sh +295 -0
  37. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +129 -0
  38. package/.devcontainer/features/mcp-reasoner/README.md +177 -0
  39. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +20 -0
  40. package/.devcontainer/features/mcp-reasoner/install.sh +177 -0
  41. package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +67 -0
  42. package/.devcontainer/features/notify-hook/README.md +86 -0
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
  44. package/.devcontainer/features/notify-hook/install.sh +38 -0
  45. package/.devcontainer/features/splitrail/README.md +140 -0
  46. package/.devcontainer/features/splitrail/devcontainer-feature.json +34 -0
  47. package/.devcontainer/features/splitrail/install.sh +129 -0
  48. package/.devcontainer/features/tree-sitter/README.md +138 -0
  49. package/.devcontainer/features/tree-sitter/devcontainer-feature.json +52 -0
  50. package/.devcontainer/features/tree-sitter/install.sh +173 -0
  51. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +106 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
  56. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
  57. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/.claude-plugin/plugin.json +8 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/SKILL.md +387 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/cli-flags-and-output.md +312 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/sdk-and-mcp.md +569 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/SKILL.md +309 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/compose-services.md +438 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/dockerfile-patterns.md +340 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/SKILL.md +412 -0
  66. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/container-lifecycle.md +388 -0
  67. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/resources-and-security.md +444 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/SKILL.md +344 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/middleware-and-lifespan.md +254 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/pydantic-models.md +245 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/routing-and-dependencies.md +255 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/sse-and-streaming.md +318 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/SKILL.md +345 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/agents-and-tools.md +271 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/models-and-streaming.md +422 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/SKILL.md +220 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/cross-vendor-principles.md +139 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/patterns-and-antipatterns.md +376 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/skill-authoring-patterns.md +356 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/SKILL.md +329 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/advanced-queries.md +314 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/javascript-patterns.md +323 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/python-patterns.md +354 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/schema-and-pragmas.md +326 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/SKILL.md +356 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/ai-sdk-svelte.md +128 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/component-patterns.md +332 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/layercake.md +203 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/migration-guide.md +350 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/runes-and-reactivity.md +328 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/spa-and-routing.md +262 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/svelte-dnd-action.md +181 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/SKILL.md +414 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/fastapi-testing.md +411 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/svelte-testing.md +538 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +7 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
  101. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +17 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
  106. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
  107. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272create-pr.md +337 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272new.md +166 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272review-commit.md +290 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272work.md +257 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +6 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +14 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +989 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +33 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +71 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +68 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +120 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +133 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +253 -0
  123. package/.devcontainer/scripts/setup-aliases.sh +80 -0
  124. package/.devcontainer/scripts/setup-config.sh +28 -0
  125. package/.devcontainer/scripts/setup-irie-claude.sh +32 -0
  126. package/.devcontainer/scripts/setup-plugins.sh +80 -0
  127. package/.devcontainer/scripts/setup.sh +58 -0
  128. package/LICENSE.txt +674 -0
  129. package/README.md +267 -0
  130. package/package.json +44 -0
  131. package/setup.js +83 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "description": "Enhance plan generation and task management with custom post-processing",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "EnterPlanMode",
7
+ "hooks": [{
8
+ "type": "command",
9
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/enhance-planning.py",
10
+ "timeout": 5
11
+ }]
12
+ }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "Write",
17
+ "hooks": [{
18
+ "type": "command",
19
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/post-enhance-plan.py",
20
+ "timeout": 30
21
+ }]
22
+ },
23
+ {
24
+ "matcher": "TaskCreate|TaskUpdate",
25
+ "hooks": [{
26
+ "type": "command",
27
+ "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/post-enhance-task.py",
28
+ "timeout": 30
29
+ }]
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Plan Enhancer PreToolUse Hook Script
4
+
5
+ Injects custom planning instructions when Claude enters plan mode.
6
+ """
7
+
8
+ import json
9
+ import sys
10
+ from pathlib import Path
11
+
12
+
13
+ def get_plugin_root() -> Path:
14
+ """Get the plugin root directory."""
15
+ # Script is at ${CLAUDE_PLUGIN_ROOT}/scripts/enhance-planning.py
16
+ return Path(__file__).parent.parent
17
+
18
+
19
+ def load_planning_instructions() -> str:
20
+ """Load custom planning instructions from config file."""
21
+ config_path = get_plugin_root() / "config" / "planning-instructions.md"
22
+
23
+ if not config_path.exists():
24
+ return ""
25
+
26
+ try:
27
+ return config_path.read_text().strip()
28
+ except Exception as e:
29
+ print(f"Warning: Could not read planning instructions: {e}", file=sys.stderr)
30
+ return ""
31
+
32
+
33
+ def handle_enter_plan_mode(tool_input: dict) -> dict:
34
+ """Handle EnterPlanMode tool - inject custom instructions."""
35
+ instructions = load_planning_instructions()
36
+
37
+ if not instructions:
38
+ # No instructions configured, allow without modification
39
+ return {"decision": "allow"}
40
+
41
+ return {
42
+ "decision": "allow",
43
+ "additionalContext": f"\n\n--- Custom Planning Instructions ---\n{instructions}\n--- End Custom Instructions ---\n",
44
+ }
45
+
46
+
47
+ def main():
48
+ """Main entry point."""
49
+ try:
50
+ # Read hook input from stdin
51
+ input_data = json.load(sys.stdin)
52
+ except json.JSONDecodeError as e:
53
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
54
+ sys.exit(1)
55
+
56
+ tool_name = input_data.get("tool_name", "")
57
+ tool_input = input_data.get("tool_input", {})
58
+
59
+ # Route to appropriate handler
60
+ if tool_name == "EnterPlanMode":
61
+ result = handle_enter_plan_mode(tool_input)
62
+ else:
63
+ # Unknown tool, allow by default
64
+ result = {"decision": "allow"}
65
+
66
+ # Output result as JSON
67
+ print(json.dumps(result))
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
@@ -0,0 +1,68 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Plan Enhancement Script
4
+ # =============================================================================
5
+ #
6
+ # This script is called after Claude writes a plan file.
7
+ # Customize it to enhance plans with your own logic.
8
+ #
9
+ # Usage: enhance-plan.sh <plan-file-path>
10
+ #
11
+ # The plan file content is already written. You can:
12
+ # - Read it, modify it, write it back in-place
13
+ # - Call external APIs (LLM critique, linters, validators)
14
+ # - Add sections, headers, or metadata
15
+ # - Validate structure and add missing elements
16
+ #
17
+ # Exit codes:
18
+ # 0 - Success (stdout is shown to Claude as enhancement output)
19
+ # Non-zero - Failure (stderr is shown as a warning)
20
+ #
21
+ # =============================================================================
22
+
23
+ PLAN_FILE="$1"
24
+
25
+ if [[ -z "$PLAN_FILE" ]]; then
26
+ echo "Error: No plan file path provided" >&2
27
+ exit 1
28
+ fi
29
+
30
+ if [[ ! -f "$PLAN_FILE" ]]; then
31
+ echo "Error: Plan file not found: $PLAN_FILE" >&2
32
+ exit 1
33
+ fi
34
+
35
+ # -----------------------------------------------------------------------------
36
+ # Example Enhancements (uncomment to enable)
37
+ # -----------------------------------------------------------------------------
38
+
39
+ # Example 1: Add timestamp header
40
+ # sed -i "1i<!-- Enhanced: $(date -Iseconds) -->" "$PLAN_FILE"
41
+
42
+ # Example 2: Add enhancement marker at end
43
+ # echo -e "\n---\n_Plan reviewed by enhancement script at $(date)_" >> "$PLAN_FILE"
44
+
45
+ # Example 3: Run through another LLM for critique
46
+ # CRITIQUE=$(cat "$PLAN_FILE" | llm "Briefly critique this plan in 2-3 bullet points")
47
+ # echo -e "\n## AI Critique\n$CRITIQUE" >> "$PLAN_FILE"
48
+
49
+ # Example 4: Validate required sections exist
50
+ # required_sections=("## Summary" "## Files to Modify" "## Testing Strategy")
51
+ # for section in "${required_sections[@]}"; do
52
+ # if ! grep -q "$section" "$PLAN_FILE"; then
53
+ # echo "Warning: Missing section: $section" >&2
54
+ # fi
55
+ # done
56
+
57
+ # Example 5: Add word/line count metadata
58
+ # LINES=$(wc -l < "$PLAN_FILE")
59
+ # WORDS=$(wc -w < "$PLAN_FILE")
60
+ # sed -i "1i<!-- Lines: $LINES | Words: $WORDS -->" "$PLAN_FILE"
61
+
62
+ # -----------------------------------------------------------------------------
63
+ # Your custom enhancement logic goes here
64
+ # -----------------------------------------------------------------------------
65
+
66
+ # Default: No enhancement, just pass through
67
+ echo "No custom enhancements configured"
68
+ exit 0
@@ -0,0 +1,120 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # Task Enhancement Script
4
+ # =============================================================================
5
+ #
6
+ # This script is called after Claude creates or updates a task.
7
+ # Customize it to enhance tasks with your own logic.
8
+ #
9
+ # Usage: enhance-task.sh <task-json-file> <task-subject> [plan-path]
10
+ #
11
+ # Arguments:
12
+ # $1 - Path to the task JSON file
13
+ # $2 - Subject of the task being created/updated
14
+ # $3 - (Optional) Path to the session's plan file (if one exists)
15
+ #
16
+ # Environment variables:
17
+ # TASK_TOOL - Either "TaskCreate" or "TaskUpdate"
18
+ # PLAN_PATH - Path to the session's plan file (same as $3, for convenience)
19
+ #
20
+ # The task JSON file contains all tasks for this session as a JSON array.
21
+ # Each task object has: subject, description, status, activeForm, etc.
22
+ #
23
+ # Plan-Task Correlation:
24
+ # When a plan exists for this session, you can use $PLAN_PATH to read
25
+ # the enhanced plan content and use it to inform task enhancement.
26
+ # This allows tasks to reference or validate against the approved plan.
27
+ #
28
+ # You can:
29
+ # - Read/modify the JSON file in-place
30
+ # - Call external APIs (LLM critique, validators)
31
+ # - Add metadata or annotations to tasks
32
+ # - Validate task structure
33
+ # - Cross-reference tasks with the plan file
34
+ #
35
+ # Exit codes:
36
+ # 0 - Success (stdout is shown to Claude as enhancement output)
37
+ # Non-zero - Failure (stderr is shown as a warning)
38
+ #
39
+ # =============================================================================
40
+
41
+ TASK_FILE="$1"
42
+ TASK_SUBJECT="$2"
43
+ PLAN_PATH="${3:-${PLAN_PATH:-}}" # Use arg $3 or env var PLAN_PATH
44
+ TOOL="${TASK_TOOL:-unknown}"
45
+
46
+ if [[ -z "$TASK_FILE" ]]; then
47
+ echo "Error: No task file path provided" >&2
48
+ exit 1
49
+ fi
50
+
51
+ if [[ ! -f "$TASK_FILE" ]]; then
52
+ echo "Error: Task file not found: $TASK_FILE" >&2
53
+ exit 1
54
+ fi
55
+
56
+ # -----------------------------------------------------------------------------
57
+ # Example Enhancements (uncomment to enable)
58
+ # -----------------------------------------------------------------------------
59
+
60
+ # Example 1: Add timestamp to the most recent task's description
61
+ # if command -v jq &> /dev/null; then
62
+ # TIMESTAMP=$(date -Iseconds)
63
+ # jq --arg ts "$TIMESTAMP" --arg subj "$TASK_SUBJECT" \
64
+ # '(.[] | select(.subject == $subj) | .description) += " [Enhanced: \($ts)]"' \
65
+ # "$TASK_FILE" > "${TASK_FILE}.tmp" && mv "${TASK_FILE}.tmp" "$TASK_FILE"
66
+ # fi
67
+
68
+ # Example 2: Log task creation/updates
69
+ # echo "[$(date -Iseconds)] $TOOL: $TASK_SUBJECT" >> /tmp/task-log.txt
70
+
71
+ # Example 3: Validate task has description
72
+ # if command -v jq &> /dev/null; then
73
+ # EMPTY_DESC=$(jq -r '.[] | select(.description == "" or .description == null) | .subject' "$TASK_FILE")
74
+ # if [[ -n "$EMPTY_DESC" ]]; then
75
+ # echo "Warning: Tasks with empty descriptions found" >&2
76
+ # fi
77
+ # fi
78
+
79
+ # Example 4: Add priority field based on keywords
80
+ # if command -v jq &> /dev/null; then
81
+ # jq '
82
+ # .[] |= (
83
+ # if (.subject | test("urgent|critical|ASAP"; "i")) then
84
+ # .priority = "high"
85
+ # elif (.subject | test("minor|low"; "i")) then
86
+ # .priority = "low"
87
+ # else
88
+ # .priority = "normal"
89
+ # end
90
+ # )
91
+ # ' "$TASK_FILE" > "${TASK_FILE}.tmp" && mv "${TASK_FILE}.tmp" "$TASK_FILE"
92
+ # fi
93
+
94
+ # Example 5: Export tasks to external system
95
+ # curl -X POST -H "Content-Type: application/json" \
96
+ # -d @"$TASK_FILE" \
97
+ # "https://your-api.example.com/tasks"
98
+
99
+ # Example 6: Use plan context for task enhancement (Plan-Task Correlation)
100
+ # If a plan file exists for this session, use it to enhance tasks
101
+ # if [[ -n "$PLAN_PATH" && -f "$PLAN_PATH" ]]; then
102
+ # # Extract plan title from the file
103
+ # PLAN_TITLE=$(head -1 "$PLAN_PATH" | sed 's/^# //')
104
+ # echo "Task created under plan: $PLAN_TITLE"
105
+ #
106
+ # # Example: Add plan reference to task metadata
107
+ # # if command -v jq &> /dev/null; then
108
+ # # jq --arg plan "$PLAN_PATH" --arg subj "$TASK_SUBJECT" \
109
+ # # '(.[] | select(.subject == $subj) | .metadata.planFile) = $plan' \
110
+ # # "$TASK_FILE" > "${TASK_FILE}.tmp" && mv "${TASK_FILE}.tmp" "$TASK_FILE"
111
+ # # fi
112
+ # fi
113
+
114
+ # -----------------------------------------------------------------------------
115
+ # Your custom enhancement logic goes here
116
+ # -----------------------------------------------------------------------------
117
+
118
+ # Default: No enhancement, just pass through
119
+ echo "No custom task enhancements configured"
120
+ exit 0
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Plan Enhancer PostToolUse Hook Script
4
+
5
+ Captures plan files after they're written, runs a user-customizable
6
+ enhancement script, and notifies Claude of the enhancement.
7
+ """
8
+
9
+ import json
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+
14
+
15
+ def get_plugin_root() -> Path:
16
+ """Get the plugin root directory."""
17
+ # Script is at ${CLAUDE_PLUGIN_ROOT}/scripts/post-enhance-plan.py
18
+ return Path(__file__).parent.parent
19
+
20
+
21
+ def is_plan_file(file_path: str) -> bool:
22
+ """Check if a file is a plan file based on path patterns."""
23
+ return "/plans/" in file_path or file_path.endswith(".plan.md")
24
+
25
+
26
+ def get_enhancement_script() -> Path | None:
27
+ """Get the user's enhancement script if it exists and is executable."""
28
+ script_path = get_plugin_root() / "scripts" / "enhancers" / "enhance-plan.sh"
29
+
30
+ if script_path.exists() and script_path.is_file():
31
+ return script_path
32
+
33
+ return None
34
+
35
+
36
+ def run_enhancement_script(script_path: Path, plan_file_path: str) -> tuple[bool, str]:
37
+ """
38
+ Run the enhancement script on the plan file.
39
+
40
+ Returns:
41
+ tuple: (success: bool, message: str)
42
+ """
43
+ try:
44
+ result = subprocess.run(
45
+ [str(script_path), plan_file_path],
46
+ capture_output=True,
47
+ text=True,
48
+ timeout=25, # Leave buffer for hook timeout
49
+ cwd=get_plugin_root(),
50
+ )
51
+
52
+ if result.returncode == 0:
53
+ output = result.stdout.strip()
54
+ return True, output if output else "Enhancement completed successfully"
55
+ else:
56
+ stderr = result.stderr.strip()
57
+ return (
58
+ False,
59
+ f"Enhancement script failed (exit {result.returncode}): {stderr}",
60
+ )
61
+
62
+ except subprocess.TimeoutExpired:
63
+ return False, "Enhancement script timed out"
64
+ except Exception as e:
65
+ return False, f"Error running enhancement script: {e}"
66
+
67
+
68
+ def handle_write(tool_input: dict, tool_response: dict) -> dict:
69
+ """Handle Write tool PostToolUse - enhance plan files."""
70
+ file_path = tool_input.get("file_path", "")
71
+
72
+ # Only process plan files
73
+ if not is_plan_file(file_path):
74
+ return {} # Silent pass-through for non-plan files
75
+
76
+ # Check if enhancement script exists
77
+ script_path = get_enhancement_script()
78
+ if script_path is None:
79
+ return {} # No enhancement script configured
80
+
81
+ # Verify the file was written successfully
82
+ if not Path(file_path).exists():
83
+ return {} # File doesn't exist, skip enhancement
84
+
85
+ # Run the enhancement script
86
+ success, message = run_enhancement_script(script_path, file_path)
87
+
88
+ if success:
89
+ return {
90
+ "additionalContext": (
91
+ f"\n--- Plan Enhancement Applied ---\n"
92
+ f"The plan file at `{file_path}` was enhanced by a custom script.\n"
93
+ f"Output: {message}\n"
94
+ f"The file content has been modified. Re-read if you need the updated content.\n"
95
+ f"--- End Enhancement Notice ---\n"
96
+ )
97
+ }
98
+ else:
99
+ return {
100
+ "additionalContext": (
101
+ f"\n--- Plan Enhancement Warning ---\n"
102
+ f"Enhancement script ran but reported an issue:\n{message}\n"
103
+ f"--- End Enhancement Notice ---\n"
104
+ )
105
+ }
106
+
107
+
108
+ def main():
109
+ """Main entry point."""
110
+ try:
111
+ # Read hook input from stdin
112
+ input_data = json.load(sys.stdin)
113
+ except json.JSONDecodeError as e:
114
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
115
+ sys.exit(1)
116
+
117
+ tool_name = input_data.get("tool_name", "")
118
+ tool_input = input_data.get("tool_input", {})
119
+ tool_response = input_data.get("tool_response", {})
120
+
121
+ # Route to appropriate handler
122
+ if tool_name == "Write":
123
+ result = handle_write(tool_input, tool_response)
124
+ else:
125
+ # Unknown tool, pass through silently
126
+ result = {}
127
+
128
+ # Output result as JSON
129
+ print(json.dumps(result))
130
+
131
+
132
+ if __name__ == "__main__":
133
+ main()
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Task Enhancer PostToolUse Hook Script
4
+
5
+ Captures tasks after they're created/updated, runs a user-customizable
6
+ enhancement script, and notifies Claude of the enhancement.
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+
15
+
16
+ def get_plugin_root() -> Path:
17
+ """Get the plugin root directory."""
18
+ # Script is at ${CLAUDE_PLUGIN_ROOT}/scripts/post-enhance-task.py
19
+ return Path(__file__).parent.parent
20
+
21
+
22
+ def find_session_plan(transcript_path: str, cwd: str) -> Path | None:
23
+ """
24
+ Find the most recent plan file created in this session.
25
+
26
+ Uses two methods:
27
+ 1. Read transcript for Write calls to /plans/ directory
28
+ 2. Fallback: scan plans directory for most recently modified file
29
+ """
30
+ # Method 1: Read transcript for Write calls to /plans/
31
+ if transcript_path:
32
+ transcript = Path(transcript_path)
33
+ if transcript.exists():
34
+ try:
35
+ with open(transcript) as f:
36
+ for line in f:
37
+ try:
38
+ entry = json.loads(line)
39
+ # Look for tool_use entries with Write tool
40
+ if (
41
+ entry.get("type") == "tool_use"
42
+ and entry.get("name") == "Write"
43
+ ):
44
+ file_path = entry.get("input", {}).get("file_path", "")
45
+ if "/plans/" in file_path and Path(file_path).exists():
46
+ return Path(file_path)
47
+ except json.JSONDecodeError:
48
+ continue
49
+ except (OSError, IOError):
50
+ pass # Fall through to Method 2
51
+
52
+ # Method 2: Fallback - scan plans directory for most recent
53
+ plans_dir = Path(cwd) / ".claude" / "plans"
54
+ if plans_dir.exists():
55
+ plan_files = sorted(
56
+ plans_dir.glob("*.md"), key=lambda p: p.stat().st_mtime, reverse=True
57
+ )
58
+ if plan_files:
59
+ return plan_files[0]
60
+
61
+ return None
62
+
63
+
64
+ def find_task_file(session_id: str) -> Path | None:
65
+ """
66
+ Find the task JSON file for the given session.
67
+
68
+ Task files are stored at:
69
+ - /workspaces/.claude/todos/{session_id}-agent-{session_id}.json
70
+ - ~/.claude/todos/{session_id}-agent-{session_id}.json
71
+ """
72
+ if not session_id:
73
+ return None
74
+
75
+ # Build the expected filename pattern
76
+ filename = f"{session_id}-agent-{session_id}.json"
77
+
78
+ # Check possible locations
79
+ locations = [
80
+ Path("/workspaces/.claude/todos") / filename,
81
+ Path.home() / ".claude" / "todos" / filename,
82
+ ]
83
+
84
+ for path in locations:
85
+ if path.exists():
86
+ return path
87
+
88
+ # Also try scanning the todos directories for any matching session
89
+ todos_dirs = [
90
+ Path("/workspaces/.claude/todos"),
91
+ Path.home() / ".claude" / "todos",
92
+ ]
93
+
94
+ for todos_dir in todos_dirs:
95
+ if todos_dir.exists():
96
+ # Look for files starting with the session_id
97
+ for f in todos_dir.glob(f"{session_id}*.json"):
98
+ return f
99
+
100
+ return None
101
+
102
+
103
+ def get_enhancement_script() -> Path | None:
104
+ """Get the user's enhancement script if it exists."""
105
+ script_path = get_plugin_root() / "scripts" / "enhancers" / "enhance-task.sh"
106
+
107
+ if script_path.exists() and script_path.is_file():
108
+ return script_path
109
+
110
+ return None
111
+
112
+
113
+ def run_enhancement_script(
114
+ script_path: Path,
115
+ task_file_path: str,
116
+ task_subject: str,
117
+ tool_name: str,
118
+ plan_path: str | None = None,
119
+ ) -> tuple[bool, str]:
120
+ """
121
+ Run the enhancement script on the task file.
122
+
123
+ Args:
124
+ script_path: Path to the enhancement script
125
+ task_file_path: Path to the task JSON file
126
+ task_subject: Subject of the task being created/updated
127
+ tool_name: Either "TaskCreate" or "TaskUpdate"
128
+ plan_path: Optional path to the session's plan file
129
+
130
+ Returns:
131
+ tuple: (success: bool, message: str)
132
+ """
133
+ try:
134
+ env = os.environ.copy()
135
+ env["TASK_TOOL"] = tool_name
136
+ if plan_path:
137
+ env["PLAN_PATH"] = plan_path
138
+
139
+ # Build arguments: script, task_file, subject, [plan_path]
140
+ args = [str(script_path), task_file_path, task_subject]
141
+ if plan_path:
142
+ args.append(plan_path)
143
+
144
+ result = subprocess.run(
145
+ args,
146
+ capture_output=True,
147
+ text=True,
148
+ timeout=25, # Leave buffer for hook timeout
149
+ cwd=get_plugin_root(),
150
+ env=env,
151
+ )
152
+
153
+ if result.returncode == 0:
154
+ output = result.stdout.strip()
155
+ return True, output if output else "Enhancement completed successfully"
156
+ else:
157
+ stderr = result.stderr.strip()
158
+ return (
159
+ False,
160
+ f"Enhancement script failed (exit {result.returncode}): {stderr}",
161
+ )
162
+
163
+ except subprocess.TimeoutExpired:
164
+ return False, "Enhancement script timed out"
165
+ except Exception as e:
166
+ return False, f"Error running enhancement script: {e}"
167
+
168
+
169
+ def handle_task_tool(
170
+ tool_name: str,
171
+ tool_input: dict,
172
+ tool_response: dict,
173
+ session_id: str,
174
+ transcript_path: str = "",
175
+ cwd: str = "",
176
+ ) -> dict:
177
+ """Handle TaskCreate/TaskUpdate PostToolUse - enhance tasks."""
178
+ task_subject = tool_input.get("subject", "")
179
+
180
+ # For TaskUpdate, we might not have a subject in the input
181
+ if not task_subject and tool_name == "TaskUpdate":
182
+ task_subject = tool_input.get("taskId", "unknown")
183
+
184
+ # Check if enhancement script exists
185
+ script_path = get_enhancement_script()
186
+ if script_path is None:
187
+ return {} # No enhancement script configured
188
+
189
+ # Find the task file
190
+ task_file = find_task_file(session_id)
191
+ if task_file is None:
192
+ return {} # Task file not found, skip silently
193
+
194
+ # Find the session's plan file (if any)
195
+ plan_file = find_session_plan(transcript_path, cwd)
196
+ plan_path = str(plan_file) if plan_file else None
197
+
198
+ # Run the enhancement script
199
+ success, message = run_enhancement_script(
200
+ script_path, str(task_file), task_subject, tool_name, plan_path
201
+ )
202
+
203
+ if success:
204
+ return {
205
+ "additionalContext": (
206
+ f"\n--- Task Enhancement Applied ---\n"
207
+ f"The task file at `{task_file}` was enhanced by a custom script.\n"
208
+ f"Tool: {tool_name} | Subject: {task_subject}\n"
209
+ f"Output: {message}\n"
210
+ f"--- End Enhancement Notice ---\n"
211
+ )
212
+ }
213
+ else:
214
+ return {
215
+ "additionalContext": (
216
+ f"\n--- Task Enhancement Warning ---\n"
217
+ f"Enhancement script ran but reported an issue:\n{message}\n"
218
+ f"--- End Enhancement Notice ---\n"
219
+ )
220
+ }
221
+
222
+
223
+ def main():
224
+ """Main entry point."""
225
+ try:
226
+ # Read hook input from stdin
227
+ input_data = json.load(sys.stdin)
228
+ except json.JSONDecodeError as e:
229
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
230
+ sys.exit(1)
231
+
232
+ tool_name = input_data.get("tool_name", "")
233
+ tool_input = input_data.get("tool_input", {})
234
+ tool_response = input_data.get("tool_response", {})
235
+ session_id = input_data.get("session_id", "")
236
+ transcript_path = input_data.get("transcript_path", "")
237
+ cwd = input_data.get("cwd", "")
238
+
239
+ # Route to handler for task tools
240
+ if tool_name in ("TaskCreate", "TaskUpdate"):
241
+ result = handle_task_tool(
242
+ tool_name, tool_input, tool_response, session_id, transcript_path, cwd
243
+ )
244
+ else:
245
+ # Unknown tool, pass through silently
246
+ result = {}
247
+
248
+ # Output result as JSON
249
+ print(json.dumps(result))
250
+
251
+
252
+ if __name__ == "__main__":
253
+ main()