@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.
- package/VERSION +1 -1
- package/hooks/doc-sync-reminder +4 -4
- package/hooks/hooks-cursor.json +20 -0
- package/hooks/hooks.json +21 -1
- package/hooks/pre-commit +191 -0
- package/hooks/prompt-guard +84 -35
- package/hooks/session-start +34 -12
- package/hooks/task-state-tracker +145 -0
- package/lib/common.sh +36 -23
- package/lib/compute-importance.sh +73 -0
- package/lib/install-cursor.sh +2 -2
- package/lib/install-hooks.sh +64 -0
- package/lib/install-skills.sh +19 -0
- package/lib/knowledge-graph.sh +483 -81
- package/lib/pre-scan.sh +81 -6
- package/package.json +1 -1
- package/skills/agent-audit/SKILL.md +180 -0
- package/skills/architecture-guard/SKILL.md +164 -0
- package/skills/architecture-guard/rules/violation-detection.md +90 -0
- package/skills/ci-feedback/SKILL.md +165 -0
- package/skills/project-analyze/SKILL.md +131 -23
- package/skills/project-analyze/phases/phase1-setup.md +15 -1
- package/skills/project-analyze/phases/phase2-understand.md +17 -2
- package/skills/project-analyze/phases/phase2.5-refine.md +293 -0
- package/skills/project-analyze/phases/phase3-graph.md +7 -1
- package/skills/project-analyze/phases/phase4-synthesize.md +117 -120
- package/skills/project-analyze/phases/phase5-export.md +117 -33
- package/skills/project-analyze/prompts/understand-agent.md +17 -0
- package/skills/project-analyze/rules/android.md +61 -260
- package/skills/project-analyze/rules/devops.md +61 -421
- package/skills/project-analyze/rules/generic.md +53 -221
- package/skills/project-analyze/rules/go.md +60 -275
- package/skills/project-analyze/rules/harmony.md +64 -237
- package/skills/project-analyze/rules/java.md +47 -485
- package/skills/project-analyze/rules/mobile-flutter.md +57 -292
- package/skills/project-analyze/rules/mobile-react-native.md +65 -262
- package/skills/project-analyze/rules/mobile-swift.md +58 -303
- package/skills/project-analyze/rules/python.md +50 -296
- package/skills/project-analyze/rules/rust-tauri.md +51 -217
- package/skills/project-analyze/rules/rust.md +50 -274
- package/skills/project-analyze/rules/web-nextjs.md +61 -335
- package/skills/project-analyze/rules/web-react.md +50 -272
- package/skills/project-analyze/rules/web-vue.md +58 -352
- package/skills/project-analyze/rules/web.md +55 -347
- package/skills/project-query/SKILL.md +681 -120
- package/skills/requirements-writer/SKILL.md +48 -1
- 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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
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
|
package/lib/install-cursor.sh
CHANGED
|
@@ -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 -
|
|
83
|
-
*-conventions) return
|
|
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
|
package/lib/install-hooks.sh
CHANGED
|
@@ -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"
|
package/lib/install-skills.sh
CHANGED
|
@@ -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
|