@dtt_siye/atool 1.3.0 → 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 (47) hide show
  1. package/VERSION +1 -1
  2. package/hooks/doc-sync-reminder +4 -4
  3. package/hooks/hooks-cursor.json +20 -0
  4. package/hooks/hooks.json +21 -1
  5. package/hooks/pre-commit +191 -0
  6. package/hooks/prompt-guard +84 -35
  7. package/hooks/session-start +34 -12
  8. package/hooks/task-state-tracker +145 -0
  9. package/lib/common.sh +36 -23
  10. package/lib/compute-importance.sh +73 -0
  11. package/lib/install-cursor.sh +2 -2
  12. package/lib/install-hooks.sh +64 -0
  13. package/lib/install-skills.sh +19 -0
  14. package/lib/knowledge-graph.sh +483 -81
  15. package/lib/pre-scan.sh +81 -6
  16. package/package.json +1 -1
  17. package/skills/agent-audit/SKILL.md +180 -0
  18. package/skills/architecture-guard/SKILL.md +164 -0
  19. package/skills/architecture-guard/rules/violation-detection.md +90 -0
  20. package/skills/ci-feedback/SKILL.md +165 -0
  21. package/skills/project-analyze/SKILL.md +131 -23
  22. package/skills/project-analyze/phases/phase1-setup.md +15 -1
  23. package/skills/project-analyze/phases/phase2-understand.md +17 -2
  24. package/skills/project-analyze/phases/phase2.5-refine.md +293 -0
  25. package/skills/project-analyze/phases/phase3-graph.md +7 -1
  26. package/skills/project-analyze/phases/phase4-synthesize.md +117 -120
  27. package/skills/project-analyze/phases/phase5-export.md +117 -33
  28. package/skills/project-analyze/prompts/understand-agent.md +17 -0
  29. package/skills/project-analyze/rules/android.md +61 -260
  30. package/skills/project-analyze/rules/devops.md +61 -421
  31. package/skills/project-analyze/rules/generic.md +53 -221
  32. package/skills/project-analyze/rules/go.md +60 -275
  33. package/skills/project-analyze/rules/harmony.md +64 -237
  34. package/skills/project-analyze/rules/java.md +47 -485
  35. package/skills/project-analyze/rules/mobile-flutter.md +57 -292
  36. package/skills/project-analyze/rules/mobile-react-native.md +65 -262
  37. package/skills/project-analyze/rules/mobile-swift.md +58 -303
  38. package/skills/project-analyze/rules/python.md +50 -296
  39. package/skills/project-analyze/rules/rust-tauri.md +51 -217
  40. package/skills/project-analyze/rules/rust.md +50 -274
  41. package/skills/project-analyze/rules/web-nextjs.md +61 -335
  42. package/skills/project-analyze/rules/web-react.md +50 -272
  43. package/skills/project-analyze/rules/web-vue.md +58 -352
  44. package/skills/project-analyze/rules/web.md +55 -347
  45. package/skills/project-query/SKILL.md +681 -120
  46. package/skills/requirements-writer/SKILL.md +48 -1
  47. package/skills/software-architecture/SKILL.md +73 -3
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -36,8 +36,8 @@ main() {
36
36
  FILE_PATH=$(printf '%s' "$INPUT" | grep -oE '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"//;s/"$//' || echo "")
37
37
  fi
38
38
 
39
- # Only respond to Write/Edit
40
- if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
39
+ # Only respond to Write/Edit/MultiEdit
40
+ if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "MultiEdit" ]]; then
41
41
  exit 0
42
42
  fi
43
43
 
@@ -120,9 +120,9 @@ main() {
120
120
  fi
121
121
  fi
122
122
 
123
- # If no stale doc detection available, always remind (conservative)
123
+ # If no stale doc detection available, exit silently (consistent with task-state-tracker fallback)
124
124
  if ! $_LIB_FOUND; then
125
- STALE=true
125
+ exit 0
126
126
  fi
127
127
 
128
128
  if ! $STALE; then
@@ -22,6 +22,17 @@
22
22
  ]
23
23
  }
24
24
  ],
25
+ "PreToolUse": [
26
+ {
27
+ "matcher": "Bash",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "bash ~/.cursor/hooks/atool-pre-commit"
32
+ }
33
+ ]
34
+ }
35
+ ],
25
36
  "PostToolUse": [
26
37
  {
27
38
  "matcher": "Write|Edit",
@@ -31,6 +42,15 @@
31
42
  "command": "bash ~/.cursor/hooks/atool-doc-sync-reminder"
32
43
  }
33
44
  ]
45
+ },
46
+ {
47
+ "matcher": "Write|Edit",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "bash ~/.cursor/hooks/atool-task-state-tracker"
52
+ }
53
+ ]
34
54
  }
35
55
  ]
36
56
  }
package/hooks/hooks.json CHANGED
@@ -22,15 +22,35 @@
22
22
  ]
23
23
  }
24
24
  ],
25
+ "PreToolUse": [
26
+ {
27
+ "matcher": "Bash",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "bash ~/.claude/hooks/atool-pre-commit"
32
+ }
33
+ ]
34
+ }
35
+ ],
25
36
  "PostToolUse": [
26
37
  {
27
- "matcher": "Write|Edit",
38
+ "matcher": "Write|Edit|MultiEdit",
28
39
  "hooks": [
29
40
  {
30
41
  "type": "command",
31
42
  "command": "bash ~/.claude/hooks/atool-doc-sync-reminder"
32
43
  }
33
44
  ]
45
+ },
46
+ {
47
+ "matcher": "Write|Edit|MultiEdit",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "bash ~/.claude/hooks/atool-task-state-tracker"
52
+ }
53
+ ]
34
54
  }
35
55
  ]
36
56
  }
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env bash
2
+ # aTool - hooks/pre-commit
3
+ # PreToolUse hook: enforces quality standards before git commit
4
+ # Checks: TODO/FIXME leftovers, sensitive files, conventional commits, large files
5
+
6
+ set -euo pipefail
7
+
8
+ # Detect which IDE is running this hook
9
+ HOOK_IDE="claude"
10
+ if [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
11
+ HOOK_IDE="cursor"
12
+ fi
13
+
14
+ # Escape string for JSON embedding
15
+ escape_for_json() {
16
+ local s="$1"
17
+ s="${s//\\/\\\\}"
18
+ s="${s//\"/\\\"}"
19
+ s="${s//$'\n'/\\n}"
20
+ s="${s//$'\r'/\\r}"
21
+ s="${s//$'\t'/\\t}"
22
+ printf '%s' "$s"
23
+ }
24
+
25
+ # Read JSON from stdin (Claude Code provides tool input on stdin)
26
+ INPUT=""
27
+ if [[ ! -t 0 ]]; then
28
+ INPUT=$(cat)
29
+ fi
30
+
31
+ # Extract tool_name and command from tool input
32
+ TOOL_NAME=""
33
+ TOOL_INPUT=""
34
+ if command -v jq &>/dev/null && [[ -n "$INPUT" ]]; then
35
+ TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
36
+ TOOL_INPUT=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || echo "")
37
+ fi
38
+
39
+ # Only intercept git commit commands
40
+ if [[ "$TOOL_NAME" != "Bash" ]]; then
41
+ exit 0
42
+ fi
43
+ if [[ -z "$TOOL_INPUT" ]]; then
44
+ exit 0
45
+ fi
46
+
47
+ # Check if this is a git commit command
48
+ TOOL_INPUT_LOWER=$(printf '%s' "$TOOL_INPUT" | tr '[:upper:]' '[:lower:]')
49
+ IS_GIT_COMMIT=false
50
+ if printf '%s' "$TOOL_INPUT_LOWER" | grep -qE 'git\s+commit'; then
51
+ IS_GIT_COMMIT=true
52
+ fi
53
+
54
+ if ! $IS_GIT_COMMIT; then
55
+ exit 0
56
+ fi
57
+
58
+ # ── Pre-commit checks ─────────────────────────────────────────────────────
59
+
60
+ WARNINGS=""
61
+ CHECKS_FAILED=0
62
+ PROJECT_DIR="${PWD:-}"
63
+
64
+ # 1. Check for TODO/FIXME in staged files (AI "lazy" patterns)
65
+ TODO_COUNT=0
66
+ if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null; then
67
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || echo "")
68
+ if [[ -n "$STAGED_FILES" ]]; then
69
+ for file in $STAGED_FILES; do
70
+ if [[ -f "$PROJECT_DIR/$file" ]]; then
71
+ local_count=$(grep -cE '(TODO|FIXME|HACK|XXX)' "$PROJECT_DIR/$file" 2>/dev/null || echo "0")
72
+ if [[ "$local_count" -gt 0 ]]; then
73
+ TODO_COUNT=$((TODO_COUNT + local_count))
74
+ fi
75
+ fi
76
+ done
77
+ fi
78
+ fi
79
+
80
+ if [[ "$TODO_COUNT" -gt 0 ]]; then
81
+ WARNINGS+="- Found ${TODO_COUNT} TODO/FIXME/HACK/XXX marker(s) in staged files. Consider resolving before commit.\n"
82
+ CHECKS_FAILED=$((CHECKS_FAILED + 1))
83
+ fi
84
+
85
+ # 2. Check for sensitive files in staged changes
86
+ SENSITIVE_PATTERNS="(\.env$|\.env\.|credentials|\.pem$|\.key$|secret|\.p12$|\.pfx$|id_rsa|id_ed25519|\.npmrc$|\.pypirc$)"
87
+ SENSITIVE_FOUND=""
88
+ if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null; then
89
+ if [[ -n "${STAGED_FILES:-}" ]]; then
90
+ for file in $STAGED_FILES; do
91
+ if printf '%s' "$file" | grep -qE "$SENSITIVE_PATTERNS" 2>/dev/null; then
92
+ if [[ -z "$SENSITIVE_FOUND" ]]; then
93
+ SENSITIVE_FOUND="$file"
94
+ else
95
+ SENSITIVE_FOUND="$SENSITIVE_FOUND, $file"
96
+ fi
97
+ fi
98
+ done
99
+ fi
100
+ fi
101
+
102
+ if [[ -n "$SENSITIVE_FOUND" ]]; then
103
+ WARNINGS+="- BLOCKED: Sensitive file(s) detected in commit: ${SENSITIVE_FOUND}. Do NOT commit secrets.\n"
104
+ CHECKS_FAILED=$((CHECKS_FAILED + 10)) # High severity
105
+ fi
106
+
107
+ # 3. Check conventional commit message format
108
+ # Note: PreToolUse hook runs BEFORE commit happens, so we can only extract from command line.
109
+ # However, commit message extraction from bash command is unreliable for heredoc format.
110
+ # This check is best-effort only. Use PostToolUse hook for reliable validation after commit.
111
+ COMMIT_MSG=""
112
+ if printf '%s' "$TOOL_INPUT" | grep -qE '\-m'; then
113
+ # Try to extract message after -m flag (only works for simple quoted messages)
114
+ COMMIT_MSG=$(printf '%s' "$TOOL_INPUT" | sed -n 's/.*-m[[:space:]]*\(['"'"'"][^'"'"'"]*['"'"'"]\|"[^"]*"\|\S\+\).*/\1/p' 2>/dev/null | head -1 || echo "")
115
+ # Strip surrounding quotes
116
+ COMMIT_MSG="${COMMIT_MSG#\"}"
117
+ COMMIT_MSG="${COMMIT_MSG%\"}"
118
+ COMMIT_MSG="${COMMIT_MSG#\'}"
119
+ COMMIT_MSG="${COMMIT_MSG%\'}"
120
+ fi
121
+
122
+ # Only warn if message extraction succeeded AND it doesn't match pattern
123
+ # (If extraction fails, skip validation — will be checked post-commit by PostToolUse hook)
124
+ if [[ -n "$COMMIT_MSG" ]] && [[ ! "$COMMIT_MSG" =~ \$\(cat ]]; then
125
+ CONVENTIONAL_PATTERN='^(feat|fix|docs|test|refactor|chore|style|perf|build|ci|revert|release)(\(.+\))?:'
126
+ if ! printf '%s' "$COMMIT_MSG" | grep -qE "$CONVENTIONAL_PATTERN" 2>/dev/null; then
127
+ WARNINGS+="- Commit message does not follow Conventional Commits: '${COMMIT_MSG}'\n"
128
+ WARNINGS+=" Expected: feat|fix|docs|test|refactor|chore|style|perf|build|ci|revert|release[(scope)]: description\n"
129
+ # This is a warning, not a hard block
130
+ fi
131
+ else
132
+ # If commit message extraction failed or detected heredoc, add note about post-validation
133
+ if printf '%s' "$TOOL_INPUT" | grep -qE '\-m.*\$\(cat'; then
134
+ WARNINGS+="- INFO: Using heredoc commit message format. Conventional Commits validation will be performed post-commit.\n"
135
+ fi
136
+ fi
137
+
138
+ # 4. Check for large files (> 1MB) in staged changes
139
+ LARGE_FILES=""
140
+ if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null; then
141
+ if [[ -n "${STAGED_FILES:-}" ]]; then
142
+ for file in $STAGED_FILES; do
143
+ if [[ -f "$PROJECT_DIR/$file" ]]; then
144
+ file_size=$(wc -c < "$PROJECT_DIR/$file" 2>/dev/null || echo "0")
145
+ # 1MB = 1048576 bytes
146
+ if [[ "$file_size" -gt 1048576 ]]; then
147
+ size_mb=$((file_size / 1048576))
148
+ if [[ -z "$LARGE_FILES" ]]; then
149
+ LARGE_FILES="$file (${size_mb}MB)"
150
+ else
151
+ LARGE_FILES="$LARGE_FILES, $file (${size_mb}MB)"
152
+ fi
153
+ fi
154
+ fi
155
+ done
156
+ fi
157
+ fi
158
+
159
+ if [[ -n "$LARGE_FILES" ]]; then
160
+ WARNINGS+="- WARNING: Large file(s) in commit: ${LARGE_FILES}. Consider using .gitattributes or Git LFS.\n"
161
+ fi
162
+
163
+ # ── Output ────────────────────────────────────────────────────────────────
164
+
165
+ if [[ "$CHECKS_FAILED" -gt 0 ]] || [[ -n "$WARNINGS" ]]; then
166
+ SEVERITY="WARNING"
167
+ if [[ "$CHECKS_FAILED" -ge 10 ]]; then
168
+ SEVERITY="BLOCKED"
169
+ fi
170
+
171
+ _MSG="<ATOOL-PRE-COMMIT-CHECK>\n"
172
+ _MSG+="${SEVERITY}: Pre-commit quality checks found issues:\n\n"
173
+ _MSG+="$WARNINGS"
174
+ _MSG+="\n"
175
+
176
+ if [[ "$SEVERITY" == "BLOCKED" ]]; then
177
+ _MSG+="**This commit is BLOCKED.** Fix the critical issues above before proceeding.\n"
178
+ else
179
+ _MSG+="Please review the warnings above. You may proceed if they are acceptable, but consider addressing them.\n"
180
+ fi
181
+ _MSG+="</ATOOL-PRE-COMMIT-CHECK>"
182
+
183
+ if [[ "$HOOK_IDE" == "cursor" ]]; then
184
+ _ESCAPED=$(escape_for_json "$_MSG")
185
+ printf '{\n "hookSpecificOutput": {\n "hookEventName": "PreToolUse",\n "additionalContext": "%s"\n }\n}\n' "$_ESCAPED"
186
+ else
187
+ printf '%b' "$_MSG"
188
+ fi
189
+ fi
190
+
191
+ exit 0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # aTool - hooks/prompt-guard
3
- # Lightweight UserPromptSubmit hook: reminds AI to clarify before implementing
4
- # Skips short/trivial prompts, triggers on substantial implementation requests
3
+ # UserPromptSubmit hook: reminds AI to clarify before implementing
4
+ # v2: Tightened skip conditions, structured template injection for Tier 2+
5
5
 
6
6
  set -euo pipefail
7
7
 
@@ -57,45 +57,41 @@ if printf '%s' "$PROMPT" | LC_ALL=C grep -q '[一-龥]' 2>/dev/null; then
57
57
  HAS_CJK=true
58
58
  fi
59
59
 
60
- # 1. Short messages
61
- if $HAS_CJK && [[ "$PROMPT_LEN" -lt 8 ]]; then
60
+ # 1. Short messages — TIGHTENED: lower thresholds
61
+ # Old: CJK < 8, Latin < 20
62
+ # New: CJK < 6, Latin < 15 (pure short replies like "ok", "好")
63
+ if $HAS_CJK && [[ "$PROMPT_LEN" -lt 6 ]]; then
62
64
  exit 0
63
- elif ! $HAS_CJK && [[ "$PROMPT_LEN" -lt 20 ]]; then
65
+ elif ! $HAS_CJK && [[ "$PROMPT_LEN" -lt 15 ]]; then
64
66
  exit 0
65
67
  fi
66
68
 
67
- # 2. Follow-up replies (affirmative, no new intent)
68
- FOLLOW_UP_PATTERNS="^(yes|ok|okay|好的|继续|继续做|go ahead|sure|yep|yeah|pls|please|go on|继续吧|对|嗯|是的|行|可以)$"
69
- if printf '%s' "$PROMPT_LOWER" | grep -qE "$FOLLOW_UP_PATTERNS" 2>/dev/null; then
69
+ # 2. Pure confirmations only TIGHTENED: strict single-word matches
70
+ # Old: included multi-word phrases like "go ahead", "pls", "please"
71
+ # New: only exact single-word affirmative replies
72
+ PURE_CONFIRMATIONS="^(yes|ok|okay|好的|继续|对|嗯|是的|行|可以|sure|yep|yeah|done|go|next)$"
73
+ if printf '%s' "$PROMPT_LOWER" | grep -qE "$PURE_CONFIRMATIONS" 2>/dev/null; then
70
74
  exit 0
71
75
  fi
72
76
 
73
- # 3. User asking a question (ends with ? or ?)
74
- if printf '%s' "$PROMPT" | grep -qE '[??]$' 2>/dev/null; then
75
- exit 0
76
- fi
77
-
78
- # 4. Meta-commands (non-implementation requests)
79
- # Note: Ambiguous words like "build", "test", "deploy" are NOT listed here
80
- # because they could be feature requests (e.g., "build a dashboard")
81
- META_COMMANDS="^(commit|git |run |npm |yarn |pnpm |bun |cargo |go run|python |pytest|jest|vitest|explain|diff |show |list |check |lint |format |log |tail |grep |find |cat |head )"
82
- if printf '%s' "$PROMPT_LOWER" | grep -qE "$META_COMMANDS" 2>/dev/null; then
83
- exit 0
84
- fi
85
-
86
- # 5. Exit signals (user wants to skip clarification)
77
+ # 3. Exit signals (user explicitly wants to skip clarification) — KEPT as-is
87
78
  EXIT_SIGNALS="(别问了|不要问|别再问|直接做|直接实现|直接开始|不用确认|跳过提问|不要确认|just do it|stop asking|no questions|don.t ask|skip asking|go ahead and implement)"
88
79
  if printf '%s' "$PROMPT_LOWER" | grep -qE "$EXIT_SIGNALS" 2>/dev/null; then
89
80
  exit 0
90
81
  fi
91
82
 
92
- # ── Trigger conditions ───────────────────────────────────────────────────
83
+ # ── REMOVED: Old skip conditions that were too broad ───────────────────────
84
+ # OLD: "Questions ending with ?" → REMOVED (questions like "能帮我实现一个搜索功能吗?" should trigger)
85
+ # OLD: "Meta-commands (commit, run, npm, explain...)" → REMOVED
86
+ # ("build a dashboard" starts with "build" but is a feature request)
87
+ # ("run the migration to add new user fields" is an implementation request)
88
+
89
+ # ── Classify task complexity ──────────────────────────────────────────────
93
90
 
94
91
  # Count words: for CJK text (no spaces), count CJK chars as words;
95
92
  # for Latin text, count whitespace-separated tokens
96
93
  WORD_COUNT=0
97
94
  if $HAS_CJK; then
98
- # CJK: count CJK characters (use LC_ALL=C for locale safety)
99
95
  CJK_COUNT=$(printf '%s' "$PROMPT" | LC_ALL=C grep -o '[一-龥]' 2>/dev/null | wc -l | tr -d '[:space:]')
100
96
  WORD_COUNT=$((CJK_COUNT))
101
97
  else
@@ -104,25 +100,78 @@ else
104
100
  done
105
101
  fi
106
102
 
107
- # Trigger: sufficient length + enough words/CJK chars + implementation keyword
108
- IMPLEMENT_KEYWORDS="(开发|创建|新增|实现|添加|加个|做一个|写一个|build|create|add|implement|make|develop|design|feature|refactor|extract|integrate|support|handle)"
103
+ # Detect implementation intent keywords
104
+ # Extended with common natural language patterns (CJK + English)
105
+ IMPLEMENT_KEYWORDS="(开发|创建|新增|实现|添加|加个|做一个|写一个|重构|优化|修改|调整|修复|集成|迁移|设计|拆分|改造|搭建|编写|扩展|帮我|需要|能不能|可以|请|麻烦|给我|加上|看看|想要|build|create|add|implement|make|develop|design|feature|refactor|extract|integrate|support|handle|fix|migrate|modify|update|change|enhance|improve|help.me|show.me|please|create.a|build.a|write.a|let.me|could.you|can.you)"
106
+
107
+ HAS_IMPLEMENT=false
109
108
  LENGTH_OK=false
110
- if $HAS_CJK && [[ "$PROMPT_LEN" -ge 8 ]]; then
109
+
110
+ if $HAS_CJK && [[ "$PROMPT_LEN" -ge 6 ]]; then
111
111
  LENGTH_OK=true
112
- elif ! $HAS_CJK && [[ "$PROMPT_LEN" -ge 20 ]]; then
112
+ elif ! $HAS_CJK && [[ "$PROMPT_LEN" -ge 15 ]]; then
113
113
  LENGTH_OK=true
114
114
  fi
115
115
 
116
- if $LENGTH_OK && [[ "$WORD_COUNT" -gt 5 ]] && \
117
- printf '%s' "$PROMPT_LOWER" | grep -qE "$IMPLEMENT_KEYWORDS" 2>/dev/null; then
118
- _REMINDER='<system-reminder>
116
+ if printf '%s' "$PROMPT_LOWER" | grep -qE "$IMPLEMENT_KEYWORDS" 2>/dev/null; then
117
+ HAS_IMPLEMENT=true
118
+ fi
119
+
120
+ # ── Determine tier and output ─────────────────────────────────────────────
121
+
122
+ if $LENGTH_OK && $HAS_IMPLEMENT && [[ "$WORD_COUNT" -gt 3 ]]; then
123
+ # Determine complexity tier
124
+ TIER=0
125
+
126
+ # Tier 3 indicators: architecture-level, multi-module, system-wide
127
+ TIER3_KEYWORDS="(架构|系统|整体|全面|重新设计|从零|微服务|拆分|architecture|system.redesign|from scratch|microservice|monolith|break down|overhaul)"
128
+ # Tier 2 indicators: new feature, multi-step, unclear scope
129
+ TIER2_KEYWORDS="(功能|模块|页面|组件|接口|服务|支持|feature|module|page|component|endpoint|service|support|workflow|pipeline|dashboard|integration)"
130
+
131
+ if printf '%s' "$PROMPT_LOWER" | grep -qE "$TIER3_KEYWORDS" 2>/dev/null; then
132
+ TIER=3
133
+ elif printf '%s' "$PROMPT_LOWER" | grep -qE "$TIER2_KEYWORDS" 2>/dev/null; then
134
+ TIER=2
135
+ else
136
+ TIER=1
137
+ fi
138
+
139
+ # Fallback tier escalation: if message has pronouns + concrete request context, bump tier
140
+ # This catches natural language requests without explicit keywords
141
+ if [[ "$TIER" -eq 1 ]] && [[ "$PROMPT_LEN" -gt 30 ]]; then
142
+ if printf '%s' "$PROMPT_LOWER" | grep -qE "(我|my|我们|our|帮|help|想|want|需要|need|能|can|可以|able)" 2>/dev/null; then
143
+ # Check if there are specific code/file references (suggests implementation request)
144
+ if printf '%s' "$PROMPT" | grep -qE "(\\.\\w+|/\\w+|\\{\\w+\\}|function|class|method|endpoint)" 2>/dev/null; then
145
+ TIER=2
146
+ fi
147
+ fi
148
+ fi
149
+
150
+ # Build tier-appropriate reminder
151
+ if [[ "$TIER" -ge 2 ]]; then
152
+ # Tier 2+: Structured template — require analysis before implementation
153
+ _REMINDER="<system-reminder>
154
+ **TIER ${TIER} TASK DETECTED** — This is a ${TIER}-level task. Before writing ANY code:
155
+
156
+ 1. **Analyze** — Read relevant source files first (CLAUDE.md, package.json, existing patterns)
157
+ 2. **Classify** — Confirm complexity: Tier ${TIER} = $([ "$TIER" -eq 2 ] && echo "3-5 clarification questions" || echo "delegate to /brainstorming")
158
+ 3. **Clarify** — Ask context-aware questions about: $([ "$TIER" -eq 2 ] && echo "scope, edge cases, existing patterns, acceptance criteria" || echo "architecture, constraints, trade-offs, migration strategy")
159
+ 4. **Plan** — Write implementation plan BEFORE touching code
160
+
161
+ Do NOT jump to implementation. Read first, ask second, plan third, code last.
162
+ See /clarify-before-build for full methodology.
163
+ </system-reminder>"
164
+ else
165
+ # Tier 1: Lightweight reminder
166
+ _REMINDER='<system-reminder>
119
167
  Before implementing, classify task complexity and clarify requirements if needed:
120
- - Tier 0 (direct): single-value change just do it
121
- - Tier 1 (1-2 questions): small change with ambiguity ask key questions
122
- - Tier 2 (3-5 questions): new feature read project files first, then ask context-aware questions
123
- - Tier 3: architecture-level delegate to /brainstorming
168
+ - Tier 0 (direct): single-value change, typo, format fix -> just do it
169
+ - Tier 1 (1-2 questions): small change with ambiguity -> ask key questions
170
+ - Tier 2 (3-5 questions): new feature, medium complexity -> structured clarification
171
+ - Tier 3: architecture-level, vague requirements -> delegate to /brainstorming
124
172
  See /clarify-before-build for full methodology.
125
173
  </system-reminder>'
174
+ fi
126
175
 
127
176
  if [[ "$HOOK_IDE" == "cursor" ]]; then
128
177
  _ESCAPED=$(escape_for_json "$_REMINDER")
@@ -245,26 +245,48 @@ if [[ -n "$DOC_SYNC" ]]; then
245
245
  INJECTION+=$'\n\n'
246
246
  fi
247
247
 
248
- INJECTION+="<ATOOL-CLARIFY-RULE>"
249
- INJECTION+=$'\n'
250
- INJECTION+="Before implementing ANY feature, first classify task complexity:"
251
- INJECTION+=$'\n'
252
- INJECTION+="Tier 0 (direct): single-value change, typo, format fix -> just do it"
248
+ # Only inject clarify rule if in a code project (detected by presence of common project files)
249
+ if [[ -f "package.json" || -f "pom.xml" || -f "go.mod" || -f "Cargo.toml" || -f "requirements.txt" || -f "build.gradle" || -f ".gradle" || -f "setup.py" || -f "Gemfile" ]]; then
250
+ INJECTION+="<ATOOL-CLARIFY-RULE>"
251
+ INJECTION+=$'\n'
252
+ INJECTION+="Before implementing ANY feature, first classify task complexity:"
253
+ INJECTION+=$'\n'
254
+ INJECTION+="Tier 0 (direct): single-value change, typo, format fix -> just do it"
255
+ INJECTION+=$'\n'
256
+ INJECTION+="Tier 1 (1-2 questions): small change with ambiguity -> ask key questions"
257
+ INJECTION+=$'\n'
258
+ INJECTION+="Tier 2 (3-5 questions): new feature, medium complexity -> structured clarification"
259
+ INJECTION+=$'\n'
260
+ INJECTION+="Tier 3: architecture-level, vague requirements -> delegate to /brainstorming"
261
+ INJECTION+=$'\n'
262
+ INJECTION+="Rules: Read project files BEFORE asking (CLAUDE.md, package.json, source code). Never assume installed libs or existing patterns. Ask context-aware questions, not obvious ones."
263
+ INJECTION+=$'\n'
264
+ INJECTION+="Exit: If user says '别问了'/'直接做'/'just do it'/'stop asking' -> skip all questions, Tier 0."
265
+ INJECTION+=$'\n'
266
+ INJECTION+="See /clarify-before-build for full methodology."
267
+ INJECTION+=$'\n'
268
+ INJECTION+="</ATOOL-CLARIFY-RULE>"
269
+ INJECTION+=$'\n'
270
+ fi
271
+
272
+ # Inject language rule (always, for all projects)
273
+ INJECTION+="<ATOOL-LANGUAGE-RULE>"
253
274
  INJECTION+=$'\n'
254
- INJECTION+="Tier 1 (1-2 questions): small change with ambiguity -> ask key questions"
275
+ INJECTION+="语言要求 (Language Requirement):"
255
276
  INJECTION+=$'\n'
256
- INJECTION+="Tier 2 (3-5 questions): new feature, medium complexity -> structured clarification"
277
+ INJECTION+="- 项目文档 (docs/, README, 架构指南): 必须中文"
257
278
  INJECTION+=$'\n'
258
- INJECTION+="Tier 3: architecture-level, vague requirements -> delegate to /brainstorming"
279
+ INJECTION+="- SKILL.md: 保持英文(AI 指令文件)"
259
280
  INJECTION+=$'\n'
260
- INJECTION+="Rules: Read project files BEFORE asking (CLAUDE.md, package.json, source code). Never assume installed libs or existing patterns. Ask context-aware questions, not obvious ones."
281
+ INJECTION+="- Commit message: 推荐中文"
261
282
  INJECTION+=$'\n'
262
- INJECTION+="Exit: If user says '别问了'/'直接做'/'just do it'/'stop asking' -> skip all questions, Tier 0."
283
+ INJECTION+="- 代码注释: 可中文或英文(跟随代码风格)"
263
284
  INJECTION+=$'\n'
264
- INJECTION+="See /clarify-before-build for full methodology."
285
+ INJECTION+="如果误用英文写项目文档,请用 /doc-coauthoring 重写为中文。"
265
286
  INJECTION+=$'\n'
266
- INJECTION+="</ATOOL-CLARIFY-RULE>"
287
+ INJECTION+="</ATOOL-LANGUAGE-RULE>"
267
288
  INJECTION+=$'\n'
289
+
268
290
  INJECTION+="</ATOOL-SESSION>"
269
291
  INJECTION+=$'\n'
270
292
  INJECTION+="</EXTREMELY_IMPORTANT>"
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env bash
2
+ # aTool - hooks/task-state-tracker
3
+ # PostToolUse hook: tracks session-level task state for feedback loop
4
+ # Records: modified files, doc staleness, verification status, pending actions
5
+ # State file: .claude/task-state.json (session-scoped, gitignored)
6
+
7
+ set -euo pipefail
8
+
9
+ # Escape string for JSON embedding
10
+ escape_for_json() {
11
+ local s="$1"
12
+ s="${s//\\/\\\\}"
13
+ s="${s//\"/\\\"}"
14
+ s="${s//$'\n'/\\n}"
15
+ s="${s//$'\r'/\\r}"
16
+ s="${s//$'\t'/\\t}"
17
+ printf '%s' "$s"
18
+ }
19
+
20
+ # ── Read tool input ───────────────────────────────────────────────────────
21
+
22
+ INPUT=""
23
+ if [[ ! -t 0 ]]; then
24
+ INPUT=$(cat)
25
+ fi
26
+
27
+ TOOL_NAME=""
28
+ FILE_PATH=""
29
+ if command -v jq &>/dev/null && [[ -n "$INPUT" ]]; then
30
+ TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
31
+ FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
32
+ fi
33
+
34
+ # Only respond to Write/Edit
35
+ if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
36
+ exit 0
37
+ fi
38
+ if [[ -z "$FILE_PATH" ]]; then
39
+ exit 0
40
+ fi
41
+
42
+ # ── Source file detection ────────────────────────────────────────────────
43
+
44
+ FILE_EXT="${FILE_PATH##*.}"
45
+ FILE_BASE=$(basename "$FILE_PATH")
46
+
47
+ # Skip non-source files (same logic as doc-sync-reminder)
48
+ case "$FILE_EXT" in
49
+ md|markdown|txt|rst) exit 0 ;;
50
+ json|yaml|yml|toml|xml) exit 0 ;;
51
+ css|scss|less|sass|styl) exit 0 ;;
52
+ lock|map|log) exit 0 ;;
53
+ svg|png|jpg|jpeg|gif|ico|webp|ttf|woff|woff2|eot) exit 0 ;;
54
+ esac
55
+ case "$FILE_BASE" in
56
+ .*|*.config.*) exit 0 ;;
57
+ esac
58
+ case "$FILE_EXT" in
59
+ ts|tsx|js|jsx|vue|svelte|html|rs|py|go|java|kt|kts|swift|dart|ets|sh|bash) ;;
60
+ *) exit 0 ;;
61
+ esac
62
+
63
+ # ── State management ──────────────────────────────────────────────────────
64
+
65
+ # Require jq
66
+ if ! command -v jq &>/dev/null; then
67
+ exit 0
68
+ fi
69
+
70
+ NOW=$(date +%s 2>/dev/null || echo "0")
71
+ STATE_FILE=".claude/task-state.json"
72
+ MAX_TRACKED_FILES=50
73
+
74
+ # Initialize state file if needed
75
+ if [[ ! -f "$STATE_FILE" ]]; then
76
+ mkdir -p ".claude" 2>/dev/null || true
77
+ printf '{"session_id":"","modified_files":[],"docs_stale":false,"verification_passed":false,"pending_actions":[],"architecture_violations":0,"created_at":%s,"updated_at":%s}\n' "$NOW" "$NOW" > "$STATE_FILE"
78
+ fi
79
+
80
+ # Read current state
81
+ STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "{}")
82
+
83
+ # Update: add file, set docs_stale=true, reset verification, update timestamp
84
+ NEW_STATE=$(printf '%s' "$STATE" | jq \
85
+ --arg fp "$FILE_PATH" \
86
+ --argjson now "$NOW" \
87
+ '
88
+ # Add file if not already tracked
89
+ if (.modified_files | map(select(.path == $fp)) | length) == 0 then
90
+ .modified_files += [{"path": $fp, "modified_at": $now}]
91
+ else
92
+ .modified_files = [.modified_files[] | if .path == $fp then .modified_at = $now else . end]
93
+ end
94
+ # Cap modified files to 50
95
+ | .modified_files = (.modified_files | sort_by(-.modified_at) | .[0:50])
96
+ # Mark docs as stale
97
+ | .docs_stale = true
98
+ # Reset verification since we made changes
99
+ | .verification_passed = false
100
+ # Ensure pending_actions includes doc update
101
+ | if (.pending_actions | index("update_docs") | not) then
102
+ .pending_actions += ["update_docs"]
103
+ else . end
104
+ | .updated_at = $now
105
+ ')
106
+
107
+ printf '%s' "$NEW_STATE" | jq '.' > "$STATE_FILE" 2>/dev/null || true
108
+
109
+ # ── Output feedback at milestones ─────────────────────────────────────────
110
+
111
+ FILE_COUNT=$(jq '.modified_files | length' "$STATE_FILE" 2>/dev/null || echo "0")
112
+
113
+ _MILESTONE=""
114
+ # Milestone check: for every 5 files >= 10, show progress update
115
+ if (( FILE_COUNT >= 10 && FILE_COUNT % 5 == 0 )); then
116
+ if (( FILE_COUNT == 10 )); then
117
+ _MILESTONE="<ATOOL-TASK-PROGRESS>
118
+ Progress check: You have modified ${FILE_COUNT} source files. This is substantial.
119
+ - Consider breaking your work into smaller, verifiable units
120
+ - Docs are STALE and need updating
121
+ - Run /verification-before-completion before claiming done
122
+ </ATOOL-TASK-PROGRESS>"
123
+ else
124
+ _MILESTONE="<ATOOL-TASK-PROGRESS>
125
+ Progress check: You have modified ${FILE_COUNT} source files.
126
+ - This is a large refactoring. Consider intermediate commits/verification
127
+ - Docs are STALE and need updating
128
+ - Run /verification-before-completion to validate progress
129
+ </ATOOL-TASK-PROGRESS>"
130
+ fi
131
+ elif (( FILE_COUNT == 5 )); then
132
+ _MILESTONE="<ATOOL-TASK-PROGRESS>
133
+ Progress check: You have modified ${FILE_COUNT} source files.
134
+ - Docs are STALE and need updating
135
+ - Verification has NOT been run since last changes
136
+ Remember: Run /verification-before-completion before claiming done.
137
+ </ATOOL-TASK-PROGRESS>"
138
+ fi
139
+
140
+ if [[ -n "$_MILESTONE" ]]; then
141
+ _ESCAPED=$(escape_for_json "$_MILESTONE")
142
+ printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$_ESCAPED"
143
+ fi
144
+
145
+ exit 0