@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/lib/common.sh CHANGED
@@ -140,11 +140,9 @@ ensure_dir() {
140
140
  # ── IDE Config Directory Helpers (cross-platform) ─────────────────────────────
141
141
 
142
142
  get_claude_config_dir() {
143
- if is_windows && [[ -n "${APPDATA:-}" ]]; then
144
- cygpath -u "$APPDATA/claude" 2>/dev/null || echo "$HOME/.claude"
145
- else
146
- echo "$HOME/.claude"
147
- fi
143
+ # Claude Code CLI always uses ~/.claude/ on all platforms
144
+ # (APPDATA/claude is the Claude Desktop app, not the CLI)
145
+ echo "$HOME/.claude"
148
146
  }
149
147
 
150
148
  get_cursor_config_dir() {
@@ -861,15 +859,20 @@ scan_skills_catalog() {
861
859
  ;;
862
860
  pdf|docx|pptx|xlsx)
863
861
  office="${office}- ${skill_name}: ${desc}
862
+ "
863
+ ;;
864
+ # Core initialization
865
+ atool-init)
866
+ planning="${planning}- ${skill_name}: ${desc}
864
867
  "
865
868
  ;;
866
869
  # Architecture & Design
867
- software-architecture|ai-project-architecture|ui-ux-pro)
870
+ software-architecture|ai-project-architecture|ui-ux-pro|architecture-guard)
868
871
  arch_design="${arch_design}- ${skill_name}: ${desc}
869
872
  "
870
873
  ;;
871
874
  # Quality & Review
872
- code-review|project-analyze|project-query|verification-before-completion)
875
+ code-review|project-analyze|project-query|verification-before-completion|agent-audit)
873
876
  quality_review="${quality_review}- ${skill_name}: ${desc}
874
877
  "
875
878
  ;;
@@ -879,8 +882,13 @@ scan_skills_catalog() {
879
882
  "
880
883
  ;;
881
884
  # Planning & Dispatch
882
- writing-plans|smart-dispatch|brainstorming|clarify-before-build)
885
+ writing-plans|smart-dispatch|brainstorming|clarify-before-build|using-git-worktrees|find-skills)
883
886
  planning="${planning}- ${skill_name}: ${desc}
887
+ "
888
+ ;;
889
+ # Testing & Automation
890
+ webapp-testing|ci-feedback)
891
+ quality_review="${quality_review}- ${skill_name}: ${desc}
884
892
  "
885
893
  ;;
886
894
  *)
@@ -890,26 +898,31 @@ scan_skills_catalog() {
890
898
  esac
891
899
  done
892
900
 
893
- # Check for Superpowers skills
901
+ # Check for Superpowers skills (only if Superpowers directory exists and is separate from skills_dir)
894
902
  local sp_dir="$skills_dir"
895
903
  if [[ -d "$skills_dir/_superpowers/skills" ]]; then
896
904
  sp_dir="$skills_dir/_superpowers/skills"
897
905
  fi
898
- for skill_dir in "$sp_dir"/*/; do
899
- [[ ! -d "$skill_dir" ]] && continue
900
- local skill_name
901
- skill_name=$(basename "$skill_dir")
902
- [[ -L "$skill_dir" ]] && continue
903
- local sp_md="$skill_dir/SKILL.md"
904
- [[ ! -f "$sp_md" ]] && continue
905
- local desc=""
906
- desc=$(head -10 "$sp_md" | sed -n '/^---$/,/^---$/p' | grep '^description:' | head -1 | sed 's/^description:[[:space:]]*//')
907
- if [[ -z "$desc" ]]; then
908
- desc=$(head -10 "$sp_md" | grep -i '^#' | head -1 | sed 's/^#*\s*//' || echo "$skill_name")
909
- fi
910
- superpowers_list="${superpowers_list}- ${skill_name}: ${desc}
906
+
907
+ # Only scan Superpowers directory if it exists and is NOT the same as skills_dir
908
+ # (prevents double-scanning skills already categorized in first loop)
909
+ if [[ "$sp_dir" != "$skills_dir" ]] && [[ -d "$sp_dir" ]]; then
910
+ for skill_dir in "$sp_dir"/*/; do
911
+ [[ ! -d "$skill_dir" ]] && continue
912
+ local skill_name
913
+ skill_name=$(basename "$skill_dir")
914
+ [[ -L "$skill_dir" ]] && continue
915
+ local sp_md="$skill_dir/SKILL.md"
916
+ [[ ! -f "$sp_md" ]] && continue
917
+ local desc=""
918
+ desc=$(head -10 "$sp_md" | sed -n '/^---$/,/^---$/p' | grep '^description:' | head -1 | sed 's/^description:[[:space:]]*//')
919
+ if [[ -z "$desc" ]]; then
920
+ desc=$(head -10 "$sp_md" | grep -i '^#' | head -1 | sed 's/^#*\s*//' || echo "$skill_name")
921
+ fi
922
+ superpowers_list="${superpowers_list}- ${skill_name}: ${desc}
911
923
  "
912
- done
924
+ done
925
+ fi
913
926
 
914
927
  # Output catalog
915
928
  if [[ -n "$conventions" ]]; then
@@ -591,6 +591,79 @@ compute_importance() {
591
591
  rm -rf "$tmpdir"
592
592
  }
593
593
 
594
+ # === Batch Processing (for pre-scan manifest.json) ===
595
+
596
+ # Update manifest.json modules array with importance scores
597
+ # Args: MANIFEST_PATH - path to manifest.json
598
+ # PROJECT_ROOT - project root directory (parent of .atool-docs)
599
+ # Updates: manifest.json modules[].importance field in-place
600
+ compute_importance_batch() {
601
+ local manifest_path="${1:-.atool-docs/pre-scan/manifest.json}"
602
+ local project_root="${2:-.}"
603
+
604
+ if [[ ! -f "$manifest_path" ]]; then
605
+ log_error "Manifest not found: $manifest_path"
606
+ return 1
607
+ fi
608
+
609
+ log_info "Computing importance scores for modules in manifest..."
610
+
611
+ # Get inventory directory from project_root
612
+ local inventory_dir="${project_root}/.atool-docs/inventory"
613
+
614
+ # Read modules from manifest
615
+ local modules_json
616
+ modules_json=$(jq -r '.modules[] | .slug' "$manifest_path" 2>/dev/null)
617
+
618
+ if [[ -z "$modules_json" ]]; then
619
+ log_warn "No modules found in manifest"
620
+ return 0
621
+ fi
622
+
623
+ # Build module path map: slug -> importance_score
624
+ local tmpdir
625
+ tmpdir=$(mktemp -d)
626
+
627
+ # Discover all modules in project and compute importance
628
+ local all_modules
629
+ all_modules=$(discover_modules "$project_root")
630
+
631
+ if [[ -z "$all_modules" ]]; then
632
+ log_warn "No source modules found in project"
633
+ echo "{}" > "$tmpdir/importance_map"
634
+ else
635
+ # Compute importance for all discovered modules
636
+ log_info "Computing importance factors for discovered modules..."
637
+ local importance_output
638
+ importance_output=$(compute_importance "$project_root" --inventory-dir "$inventory_dir" 2>/dev/null || true)
639
+
640
+ # Build slug-to-importance map from output (module_path<tab>score<tab>tier)
641
+ # Convert module path to slug: extract last path component
642
+ {
643
+ while IFS=$'\t' read -r module_path score tier; do
644
+ [[ -z "$module_path" ]] && continue
645
+ local slug
646
+ slug=$(basename "$module_path")
647
+ printf '%s\t%s\n' "$slug" "$score"
648
+ done <<< "$importance_output"
649
+ } > "$tmpdir/importance_map"
650
+ fi
651
+
652
+ # Update manifest.json with importance scores
653
+ log_info "Updating manifest with importance scores..."
654
+ jq --slurpfile importance_data <(cat "$tmpdir/importance_map" | jq -R 'split("\t") | {key: .[0], value: (.[1] | tonumber)}' | jq -s 'from_entries') \
655
+ '.modules |= map(.importance = ($importance_data[0][.slug] // 0))' \
656
+ "$manifest_path" > "$tmpdir/manifest_updated.json"
657
+
658
+ # Atomically replace manifest
659
+ mv "$tmpdir/manifest_updated.json" "$manifest_path"
660
+
661
+ # Cleanup
662
+ rm -rf "$tmpdir"
663
+
664
+ log_success "Importance scores computed and manifest updated"
665
+ }
666
+
594
667
  # === Standalone Execution ===
595
668
  # When run directly (not sourced), execute compute_importance with CLI args
596
669
  if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
@@ -79,8 +79,8 @@ should_always_apply() {
79
79
  project-analyze) return 0 ;;
80
80
  # Workflow skill with no good glob match - must be always-on
81
81
  using-git-worktrees) return 0 ;;
82
- # Conventions skills - always relevant for their file types (via globs too)
83
- *-conventions) return 0 ;;
82
+ # Conventions skills - globs handle triggering by file type (no need for alwaysApply)
83
+ *-conventions) return 1 ;;
84
84
  # All other skills: use globs or on-demand (reduces context token consumption)
85
85
  *) return 1 ;;
86
86
  esac
@@ -132,6 +132,58 @@ install_hooks() {
132
132
  fi
133
133
  fi
134
134
 
135
+ # Install pre-commit hook (PreToolUse - checks before git commit)
136
+ local pre_commit_src="$atool_root/hooks/pre-commit"
137
+ if [[ -f "$pre_commit_src" ]]; then
138
+ local pre_commit_name="atool-pre-commit"
139
+ local dest_pre_commit="$hooks_dir/$pre_commit_name"
140
+
141
+ if [[ -f "$dest_pre_commit" ]] && [[ "${ATOOL_FORCE:-0}" != "1" ]]; then
142
+ local pc_existing pc_source
143
+ pc_existing=$(file_checksum "$dest_pre_commit")
144
+ pc_source=$(file_checksum "$pre_commit_src")
145
+ if [[ "$pc_existing" == "$pc_source" ]]; then
146
+ log_info "Hook '$pre_commit_name' already up to date, skipping"
147
+ else
148
+ log_info "Hook '$pre_commit_name' exists but differs - backing up"
149
+ backup_file "$dest_pre_commit"
150
+ run_cmd cp "$pre_commit_src" "$dest_pre_commit"
151
+ run_cmd chmod +x "$dest_pre_commit"
152
+ log_success "Hook installed: $dest_pre_commit"
153
+ fi
154
+ else
155
+ run_cmd cp "$pre_commit_src" "$dest_pre_commit"
156
+ run_cmd chmod +x "$dest_pre_commit"
157
+ log_success "Hook installed: $dest_pre_commit"
158
+ fi
159
+ fi
160
+
161
+ # Install task-state-tracker hook (PostToolUse - tracks session state)
162
+ local task_state_src="$atool_root/hooks/task-state-tracker"
163
+ if [[ -f "$task_state_src" ]]; then
164
+ local task_state_name="atool-task-state-tracker"
165
+ local dest_task_state="$hooks_dir/$task_state_name"
166
+
167
+ if [[ -f "$dest_task_state" ]] && [[ "${ATOOL_FORCE:-0}" != "1" ]]; then
168
+ local ts_existing ts_source
169
+ ts_existing=$(file_checksum "$dest_task_state")
170
+ ts_source=$(file_checksum "$task_state_src")
171
+ if [[ "$ts_existing" == "$ts_source" ]]; then
172
+ log_info "Hook '$task_state_name' already up to date, skipping"
173
+ else
174
+ log_info "Hook '$task_state_name' exists but differs - backing up"
175
+ backup_file "$dest_task_state"
176
+ run_cmd cp "$task_state_src" "$dest_task_state"
177
+ run_cmd chmod +x "$dest_task_state"
178
+ log_success "Hook installed: $dest_task_state"
179
+ fi
180
+ else
181
+ run_cmd cp "$task_state_src" "$dest_task_state"
182
+ run_cmd chmod +x "$dest_task_state"
183
+ log_success "Hook installed: $dest_task_state"
184
+ fi
185
+ fi
186
+
135
187
  # Install hooks config JSON
136
188
  local config_file
137
189
  case "$ide_type" in
@@ -224,6 +276,18 @@ install_hooks_global() {
224
276
  run_cmd chmod +x "$claude_hooks_dir/atool-doc-sync-reminder"
225
277
  fi
226
278
 
279
+ # Copy pre-commit hook (PreToolUse)
280
+ if [[ -f "$atool_root/hooks/pre-commit" ]]; then
281
+ run_cmd cp "$atool_root/hooks/pre-commit" "$claude_hooks_dir/atool-pre-commit"
282
+ run_cmd chmod +x "$claude_hooks_dir/atool-pre-commit"
283
+ fi
284
+
285
+ # Copy task-state-tracker hook (PostToolUse)
286
+ if [[ -f "$atool_root/hooks/task-state-tracker" ]]; then
287
+ run_cmd cp "$atool_root/hooks/task-state-tracker" "$claude_hooks_dir/atool-task-state-tracker"
288
+ run_cmd chmod +x "$claude_hooks_dir/atool-task-state-tracker"
289
+ fi
290
+
227
291
  # 2. Register hooks config in settings.json
228
292
  # Claude Code reads hooks from settings.json, NOT from files on disk
229
293
  local settings_file="$claude_config_dir/settings.json"
@@ -118,6 +118,25 @@ install_skills() {
118
118
  run_cmd cp -r "$skill_dir" "$target_skill"
119
119
  write_skill_version_marker "$target_skill" "$source_ver"
120
120
 
121
+ # Copy referenced lib/ scripts into skill's lib/ subdirectory
122
+ # Skills reference scripts via "source lib/X.sh" relative to the skill dir
123
+ local skill_lib_dir="$target_skill/lib"
124
+ local referenced_libs
125
+ local _find_paths=()
126
+ [[ -d "$target_skill/phases" ]] && _find_paths+=("$target_skill/phases")
127
+ _find_paths+=("$target_skill")
128
+ referenced_libs=$(find "${_find_paths[@]}" -maxdepth 2 -name '*.md' -exec grep -roh 'source lib/[^ ]*\.sh' {} + 2>/dev/null \
129
+ | sed 's/source lib\///' | sort -u || true)
130
+ if [[ -n "$referenced_libs" ]]; then
131
+ run_cmd mkdir -p "$skill_lib_dir"
132
+ for lib_name in $referenced_libs; do
133
+ local lib_src="$atool_root/lib/$lib_name"
134
+ if [[ -f "$lib_src" ]]; then
135
+ run_cmd cp "$lib_src" "$skill_lib_dir/$lib_name"
136
+ fi
137
+ done
138
+ fi
139
+
121
140
  if [[ "$installed_ver" != "none" ]] && [[ "$installed_ver" != "$source_ver" ]]; then
122
141
  log_info "Updated skill: $skill_name"
123
142
  else