create-snowline-agents 1.0.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/install.sh ADDED
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+
3
+ echo "Installing Snowline Agent Ecosystem (Project-Level)..."
4
+
5
+ PROJECT_ROOT=$(pwd)
6
+ AGENTS_DIR="$PROJECT_ROOT/.agents"
7
+ SKILLS_DIR="$AGENTS_DIR/skills"
8
+ KNOWLEDGE_DIR="$AGENTS_DIR/knowledge"
9
+ REPO_URL="https://github.com/UsmanAzizz/snowline-agent-tools.git"
10
+ IS_NEW_INSTALL=false
11
+
12
+ # Ensure .agents and .agents/knowledge exist
13
+ mkdir -p "$AGENTS_DIR"
14
+ mkdir -p "$KNOWLEDGE_DIR"
15
+
16
+ # Scaffold skills
17
+ if [ -d "$SKILLS_DIR" ]; then
18
+ echo "Found existing skills directory at $SKILLS_DIR"
19
+ if [ -d "$SKILLS_DIR/.git" ]; then
20
+ echo "Pulling latest updates..."
21
+ (cd "$SKILLS_DIR" && git pull origin main) || echo "Failed to update repository."
22
+ else
23
+ echo "Existing skills directory is not a git repository. Skipping git pull."
24
+ fi
25
+ else
26
+ IS_NEW_INSTALL=true
27
+ echo "Downloading Snowline Agent skills..."
28
+ if ! git clone "$REPO_URL" "$SKILLS_DIR"; then
29
+ echo "Failed to clone repository. Make sure git is installed."
30
+ exit 1
31
+ fi
32
+ fi
33
+
34
+ # Copy AGENTS_TEMPLATE.md to AGENTS.md
35
+ TEMPLATE_PATH="$SKILLS_DIR/AGENTS_TEMPLATE.md"
36
+ LOCAL_AGENTS_PATH="$AGENTS_DIR/AGENTS.md"
37
+
38
+ if [ -f "$TEMPLATE_PATH" ]; then
39
+ if [ ! -f "$LOCAL_AGENTS_PATH" ]; then
40
+ echo "Creating Project AGENTS.md..."
41
+ cp "$TEMPLATE_PATH" "$LOCAL_AGENTS_PATH"
42
+ echo "Project AGENTS.md created successfully."
43
+ else
44
+ echo "Project AGENTS.md already exists. Skipping overwrite."
45
+ fi
46
+ fi
47
+
48
+ # Scaffold PLAN.md
49
+ PLAN_PATH="$PROJECT_ROOT/PLAN.md"
50
+ if [ ! -f "$PLAN_PATH" ]; then
51
+ echo "Creating PLAN.md..."
52
+ echo -e "# Project Plan / Task Tracker\n\n- [ ] Initial task" > "$PLAN_PATH"
53
+ fi
54
+
55
+ if [ "$IS_NEW_INSTALL" = true ]; then
56
+ echo -e "\nInstallation Complete!"
57
+ echo "This project is now powered by the Snowline Agent Ecosystem."
58
+ fi
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "create-snowline-agents",
3
+ "version": "1.0.0",
4
+ "description": "One-Liner installer for the Snowline Agent AI Agents ecosystem (Global Rules & Python Skills)",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "create-snowline-agents": "bin/install.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/UsmanAzizz/snowline-agent-tools.git"
15
+ },
16
+ "keywords": [
17
+ "ai",
18
+ "agents",
19
+ "gemini",
20
+ "tools",
21
+ "scaffolding",
22
+ "Snowline Agent"
23
+ ],
24
+ "author": "UsmanAzizz",
25
+ "license": "MIT",
26
+ "bugs": {
27
+ "url": "https://github.com/UsmanAzizz/snowline-agent-tools/issues"
28
+ },
29
+ "homepage": "https://github.com/UsmanAzizz/snowline-agent-tools#readme",
30
+ "dependencies": {}
31
+ }
@@ -0,0 +1,20 @@
1
+ # Task: [Nama Task Singkat]
2
+ Status: [not-started | in-progress | menunggu-konfirmasi | selesai]
3
+ Skill/tool dipakai: [Sebutkan alat yang dominan dipakai, misal: smart_replace, clean_sweeper, atau view_file]
4
+
5
+ ## Konteks
6
+ [Satu atau dua kalimat tentang alasan task ini dikerjakan dan apa tujuannya]
7
+
8
+ ## Log Langkah
9
+ - [Waktu] [Deskripsi singkat aksi yang dilakukan] -> [Status/Hasil]
10
+ - [Waktu] [Deskripsi singkat aksi yang dilakukan] -> [Status/Hasil]
11
+
12
+ ## Keputusan & Asumsi
13
+ - [Catat jika ada keputusan desain, trade-off, atau asumsi yang diambil saat coding]
14
+
15
+ ## Menunggu Konfirmasi Saya
16
+ [BAGIAN KRITIKAL: Jika butuh persetujuan user (misal sebelum hapus file atau sebelum eksekusi replace masal), tulis pertanyaannya di sini, DILARANG MELANJUTKAN sebelum dijawab]
17
+ - [Tulis pertanyaan/permintaan konfirmasi di sini...]
18
+
19
+ ## Anomali / Peringatan
20
+ - [Catat jika ada pesan error, behavior aneh, atau sesuatu yang butuh perhatian nanti]
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: Project Guardian (Security & Health Auditor)
3
+ description: Use this skill when the user asks you to audit the project for security leaks, secret credentials, vulnerable dependencies, or environment file (.gitignore) issues.
4
+ ---
5
+
6
+ ## Instructions for AI Agent
7
+
8
+ When the user asks you to "audit project", "cek keamanan", or "jalankan project guardian", use this tool to scan the repository.
9
+
10
+ **Command to run:**
11
+ ```powershell
12
+ python .agents/skills/project_guardian/guardian.py
13
+ ```
14
+ *(Make sure your current working directory `Cwd` is set to the root of the project).*
15
+
16
+ **Expected Output:**
17
+ The script will output a report directly to standard output. It will highlight specific modules:
18
+ 1. **Secret Scanner**: Identifies hardcoded API keys and passwords.
19
+ 2. **Env & Gitignore Verifier**: Ensures `.env` files are not exposed.
20
+ 3. **Physical Import Checker**: Checks if relative imports exist.
21
+ 4. **Dependencies**: Reports unused packages and high/critical npm audit vulnerabilities.
22
+
23
+ ### 🛑 CRITICAL BEHAVIORAL RULE
24
+ 1. Present the findings clearly to the user.
25
+ 2. Group the findings into **[FAIL]** (Must fix immediately, like exposed `.env`) and **[WARN]** (Should be reviewed).
26
+ 3. Do NOT automatically fix or modify files. Ask the user which items they want you to fix first.
@@ -0,0 +1,206 @@
1
+ import os
2
+ import re
3
+ import sys
4
+ import json
5
+ import subprocess
6
+ import argparse
7
+
8
+ if sys.stdout.encoding != 'utf-8':
9
+ sys.stdout.reconfigure(encoding='utf-8')
10
+
11
+ exclude_dirs = {'.git', 'node_modules', 'vendor', 'dist', 'build', 'quarantine', '.backup_replace', '.agents', '.history'}
12
+ js_py_exts = {'.js', '.jsx', '.ts', '.tsx', '.py'}
13
+ MAX_FILE_SIZE = 500 * 1024
14
+
15
+ target_dir = os.getcwd()
16
+
17
+ total_fails = 0
18
+ total_warns = 0
19
+
20
+ def print_fail(msg, summary_mode):
21
+ global total_fails
22
+ total_fails += 1
23
+ if not summary_mode:
24
+ print(f"[FAIL] {msg}")
25
+
26
+ def print_warn(msg, summary_mode):
27
+ global total_warns
28
+ total_warns += 1
29
+ if not summary_mode:
30
+ print(f"[WARN] {msg}")
31
+
32
+ def print_info(msg, summary_mode):
33
+ if not summary_mode:
34
+ print(f"[INFO] {msg}")
35
+
36
+ def scan_secrets(summary_mode):
37
+ if not summary_mode: print("\n--- MODULE 1: SECRET SCANNER ---")
38
+ secret_patterns = [
39
+ r'(?i)(password\s*[:=]\s*[\'"].+[\'"])',
40
+ r'(?i)(api_key\s*[:=]\s*[\'"].+[\'"])',
41
+ r'(?i)(secret\s*[:=]\s*[\'"].+[\'"])',
42
+ r'(mongodb\+srv://.+)',
43
+ r'(mysql://.+)',
44
+ r'(Bearer\s+[A-Za-z0-9\-\._~+/]+=*)'
45
+ ]
46
+ compiled_patterns = [re.compile(p) for p in secret_patterns]
47
+
48
+ for root, dirs, files in os.walk(target_dir):
49
+ dirs[:] = [d for d in dirs if d not in exclude_dirs]
50
+ for file in files:
51
+ if file.startswith('.env'): continue
52
+ filepath = os.path.join(root, file)
53
+ if os.path.getsize(filepath) > MAX_FILE_SIZE: continue
54
+
55
+ try:
56
+ with open(filepath, 'r', encoding='utf-8') as f:
57
+ for line_num, line in enumerate(f, 1):
58
+ for pattern in compiled_patterns:
59
+ if pattern.search(line):
60
+ rel_path = os.path.relpath(filepath, target_dir)
61
+ print_fail(f"Potential credential leak in {rel_path} line {line_num}", summary_mode)
62
+ except UnicodeDecodeError:
63
+ pass
64
+
65
+ def check_env_gitignore(summary_mode):
66
+ if not summary_mode: print("\n--- MODULE 2: ENV & GITIGNORE VERIFIER ---")
67
+ gitignore_path = os.path.join(target_dir, '.gitignore')
68
+ ignored_lines = set()
69
+ if os.path.exists(gitignore_path):
70
+ with open(gitignore_path, 'r', encoding='utf-8') as f:
71
+ ignored_lines = set(line.strip() for line in f if line.strip() and not line.startswith('#'))
72
+
73
+ env_files = [f for f in os.listdir(target_dir) if f.startswith('.env') and os.path.isfile(os.path.join(target_dir, f))]
74
+ for env_file in env_files:
75
+ if env_file == '.env.example': continue
76
+ if env_file not in ignored_lines and f"/{env_file}" not in ignored_lines and "*.env" not in ignored_lines and ".env*" not in ignored_lines:
77
+ print_fail(f"File {env_file} is missing from .gitignore!", summary_mode)
78
+
79
+ env_example_path = os.path.join(target_dir, '.env.example')
80
+ example_keys = set()
81
+ if os.path.exists(env_example_path):
82
+ with open(env_example_path, 'r', encoding='utf-8') as f:
83
+ for line in f:
84
+ if '=' in line and not line.startswith('#'):
85
+ key = line.split('=')[0].strip()
86
+ example_keys.add(key)
87
+
88
+ used_keys = set()
89
+ process_env_pattern = re.compile(r'process\.env\.([A-Za-z0-9_]+)')
90
+ for root, dirs, files in os.walk(target_dir):
91
+ dirs[:] = [d for d in dirs if d not in exclude_dirs]
92
+ for file in files:
93
+ if not any(file.endswith(ext) for ext in js_py_exts): continue
94
+ filepath = os.path.join(root, file)
95
+ if os.path.getsize(filepath) > MAX_FILE_SIZE: continue
96
+ try:
97
+ with open(filepath, 'r', encoding='utf-8') as f:
98
+ for line in f:
99
+ for m in process_env_pattern.findall(line):
100
+ used_keys.add(m)
101
+ except: pass
102
+
103
+ for key in used_keys:
104
+ if key not in example_keys and key != 'NODE_ENV':
105
+ print_warn(f"process.env.{key} is used, but missing from .env.example", summary_mode)
106
+
107
+ def check_physical_imports(summary_mode):
108
+ if not summary_mode: print("\n--- MODULE 3: PHYSICAL IMPORT CHECKER ---")
109
+ import_pattern = re.compile(r'(?:import\s+.*?from\s+|require\()[\'"]([^\'"]+)[\'"]')
110
+ for root, dirs, files in os.walk(target_dir):
111
+ dirs[:] = [d for d in dirs if d not in exclude_dirs]
112
+ for file in files:
113
+ if not any(file.endswith(ext) for ext in js_py_exts): continue
114
+ filepath = os.path.join(root, file)
115
+ if os.path.getsize(filepath) > MAX_FILE_SIZE: continue
116
+ try:
117
+ with open(filepath, 'r', encoding='utf-8') as f:
118
+ for line_num, line in enumerate(f, 1):
119
+ for match in import_pattern.findall(line):
120
+ import_path = match
121
+ if import_path.startswith('.'):
122
+ dir_path = os.path.dirname(filepath)
123
+ target_p = os.path.normpath(os.path.join(dir_path, import_path))
124
+ found = False
125
+ for suffix in ['', '.js', '.jsx', '.ts', '.tsx', '/index.js', '/index.jsx']:
126
+ if os.path.exists(target_p + suffix):
127
+ found = True
128
+ break
129
+ if not found:
130
+ rel_source = os.path.relpath(filepath, target_dir)
131
+ print_warn(f"Relative import '{import_path}' at {rel_source}:{line_num} does not exist physically!", summary_mode)
132
+ except: pass
133
+
134
+ def check_dependencies(summary_mode):
135
+ if not summary_mode: print("\n--- MODULE 4: DEPENDENCIES & UNUSED PACKAGES ---")
136
+ package_json_path = os.path.join(target_dir, 'package.json')
137
+ if not os.path.exists(package_json_path):
138
+ return
139
+
140
+ try:
141
+ with open(package_json_path, 'r', encoding='utf-8') as f:
142
+ pkg = json.load(f)
143
+ except: return
144
+
145
+ deps = list(pkg.get('dependencies', {}).keys())
146
+ used_deps = set()
147
+ import_pattern = re.compile(r'(?:import\s+.*?from\s+|require\()[\'"]([^\'"]+)[\'"]')
148
+
149
+ for root, dirs, files in os.walk(target_dir):
150
+ dirs[:] = [d for d in dirs if d not in exclude_dirs]
151
+ for file in files:
152
+ if not any(file.endswith(ext) for ext in js_py_exts): continue
153
+ filepath = os.path.join(root, file)
154
+ if os.path.getsize(filepath) > MAX_FILE_SIZE: continue
155
+ try:
156
+ with open(filepath, 'r', encoding='utf-8') as f:
157
+ for line in f:
158
+ for match in import_pattern.findall(line):
159
+ pkg_name = match.split('/')[0]
160
+ if pkg_name.startswith('@'):
161
+ parts = match.split('/')
162
+ if len(parts) > 1:
163
+ pkg_name = f"{parts[0]}/{parts[1]}"
164
+ used_deps.add(pkg_name)
165
+ except: pass
166
+
167
+ for dep in deps:
168
+ if dep not in used_deps and not dep.startswith('@vite') and not dep.startswith('@babel') and not dep.startswith('react'):
169
+ print_warn(f"Package '{dep}' is installed but appears unused.", summary_mode)
170
+
171
+ if not summary_mode: print("\nRunning npm audit (this may take a while)...")
172
+ try:
173
+ result = subprocess.run('npm audit --json', shell=True, capture_output=True, text=True, check=False, timeout=15)
174
+ try:
175
+ audit_data = json.loads(result.stdout)
176
+ vulns = audit_data.get('metadata', {}).get('vulnerabilities', {})
177
+ high = vulns.get('high', 0)
178
+ critical = vulns.get('critical', 0)
179
+ if high > 0 or critical > 0:
180
+ print_fail(f"npm audit detected {high} HIGH and {critical} CRITICAL vulnerabilities!", summary_mode)
181
+ except: pass
182
+ except: pass
183
+
184
+ def main():
185
+ parser = argparse.ArgumentParser(description="Project Guardian")
186
+ parser.add_argument("--summary", action="store_true", help="Only show final score")
187
+ args = parser.parse_args()
188
+
189
+ if not args.summary:
190
+ print("🛡️ PROJECT GUARDIAN AUDITOR 🛡️")
191
+
192
+ scan_secrets(args.summary)
193
+ check_env_gitignore(args.summary)
194
+ check_physical_imports(args.summary)
195
+ check_dependencies(args.summary)
196
+
197
+ if args.summary:
198
+ print(f"🛡️ RINGKASAN GUARDIAN: 🔴 {total_fails} FAIL | 🟡 {total_warns} WARN | 🟢 Sektor lainnya Aman.")
199
+ else:
200
+ print("\n" + "=" * 60)
201
+ print(f"🛡️ RINGKASAN: 🔴 {total_fails} FAIL | 🟡 {total_warns} WARN | 🟢 Sektor lainnya Aman.")
202
+ print("\n💡 PROMPT UNTUK AI (Copy-Paste ini):")
203
+ print('"Tolong perbaiki semua temuan [FAIL] di atas (khususnya .gitignore dan env). Untuk [WARN], abaikan jika itu adalah dummy data atau file test."')
204
+
205
+ if __name__ == '__main__':
206
+ main()
package/run_all.py ADDED
@@ -0,0 +1,115 @@
1
+ import os
2
+ import sys
3
+ import subprocess
4
+
5
+ if sys.stdout.encoding != 'utf-8':
6
+ sys.stdout.reconfigure(encoding='utf-8')
7
+
8
+ TOOLS = [
9
+ {
10
+ "name": "Project Guardian (Security & Health Auditor)",
11
+ "script": "project_guardian/guardian.py",
12
+ "args": ["--summary"]
13
+ },
14
+ {
15
+ "name": "Clean Sweeper (Leftover & Tech Debt Scanner)",
16
+ "script": "clean_sweeper/sweeper.py",
17
+ "args": [os.getcwd()]
18
+ },
19
+ {
20
+ "name": "Context Mapper (Knowledge Catalog Builder)",
21
+ "script": "context_mapper/context_mapper.py",
22
+ "args": []
23
+ },
24
+ {
25
+ "name": "Smart Replace (Pure Python RegEx Replacer)",
26
+ "script": "smart_replace/replace_text.py",
27
+ "args": [os.getcwd(), "<search_string>", "<replace_string>"]
28
+ },
29
+ {
30
+ "name": "Smart Search (5-Lines Context Code Finder)",
31
+ "script": "smart_search/code_finder.py",
32
+ "args": [os.getcwd(), "<keyword>"]
33
+ },
34
+ {
35
+ "name": "Selective Reader (TOC Extractor)",
36
+ "script": "selective_reader/reader.py",
37
+ "args": ["<absolute_file_path>"]
38
+ },
39
+ {
40
+ "name": "Deep Analyzer (Project Profiler)",
41
+ "script": "deep_analyzer/analyzer.py",
42
+ "args": [os.getcwd()]
43
+ },
44
+ {
45
+ "name": "Crash Decoder (Error Trace Analyzer)",
46
+ "script": "crash_decoder/decoder.py",
47
+ "args": ["<path_to_error_log.txt>"]
48
+ },
49
+ {
50
+ "name": "Auto-Scaffolder (Pattern Generator)",
51
+ "script": "auto_scaffolder/scaffolder.py",
52
+ "args": ["<react_or_api>", "<ComponentName>"]
53
+ },
54
+ {
55
+ "name": "Smart Import Fixer",
56
+ "script": "import_fixer/fixer.py",
57
+ "args": ["<source_file>", "<broken_import_string>"]
58
+ }
59
+ ]
60
+
61
+ def print_menu():
62
+ print("\n" + "=" * 60)
63
+ print("❄️ SNOWLINE AGENT TOOLS - INTERACTIVE DASHBOARD ❄️")
64
+ print("=" * 60)
65
+ print("Select a tool to run (Powered by 4-Pillars Vision):")
66
+ for i, tool in enumerate(TOOLS, 1):
67
+ print(f"[{i}] {tool['name']}")
68
+ print("[0] Exit")
69
+ print("=" * 60)
70
+
71
+ def main():
72
+ while True:
73
+ print_menu()
74
+ try:
75
+ choice = input("\nEnter your choice (0-10): ").strip()
76
+ except KeyboardInterrupt:
77
+ break
78
+
79
+ if choice == '0':
80
+ print("Exiting dashboard.")
81
+ break
82
+
83
+ if not choice.isdigit() or not (1 <= int(choice) <= len(TOOLS)):
84
+ print("[FAIL] Invalid choice.")
85
+ continue
86
+
87
+ idx = int(choice) - 1
88
+ tool = TOOLS[idx]
89
+
90
+ script_path = os.path.join(os.path.dirname(__file__), tool["script"])
91
+ if not os.path.exists(script_path):
92
+ print(f"[FAIL] Script not found at {script_path}")
93
+ continue
94
+
95
+ args = []
96
+ for arg in tool["args"]:
97
+ if arg.startswith("<") and arg.endswith(">"):
98
+ val = input(f"Enter {arg}: ").strip()
99
+ args.append(val)
100
+ else:
101
+ args.append(arg)
102
+
103
+ cmd = [sys.executable, script_path] + args
104
+ print(f"\nRunning: {' '.join(cmd)}")
105
+ print("-" * 60)
106
+
107
+ try:
108
+ subprocess.run(cmd, check=False)
109
+ except Exception as e:
110
+ print(f"[FAIL] Error during execution: {e}")
111
+
112
+ input("\nPress Enter to return to main menu...")
113
+
114
+ if __name__ == "__main__":
115
+ main()
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: Selective File Reader (TOC Generator)
3
+ description: Use this skill when you need to inspect a large file but don't want to waste tokens reading the entire file. This script parses the file using regex heuristics and returns a Table of Contents with exact line numbers for all functions, components, and classes.
4
+ ---
5
+
6
+ ## Instructions for AI Agent
7
+
8
+ Reading massive files (800+ lines) blindly wastes context limits and degrades performance.
9
+
10
+ **When to use this skill:**
11
+ Before reading any JS/React/Py file that you suspect is large, or when the user asks you to "inspect" or "modify" a specific function within a file, use this skill to generate a "Table of Contents" (TOC) of that file first.
12
+
13
+ **Command to run:**
14
+ ```powershell
15
+ python .agents/skills/selective_reader/reader.py "<absolute_path_to_file>"
16
+ ```
17
+
18
+ **Expected Behavior & Next Steps:**
19
+ 1. The script will output a Markdown list of all classes, functions, and top-level variables along with their precise start lines.
20
+ 2. Review the TOC to locate the exact function or component you need.
21
+ 3. Once you know the line numbers, use your native `view_file` tool and supply `StartLine` and `EndLine` parameters to read ONLY that specific block of code!
22
+
23
+ By doing this, you act as a "Selective Reader" and dramatically optimize your memory usage.
24
+
25
+ ## ⚠️ Known Limitations & Future Enhancements
26
+ - **Monolithic JSX Components**: The heuristic regex currently only detects function and class declarations. If a file contains a single massive React component returning a huge block of JSX (e.g., hundreds of lines of <table> or <form>), the TOC will only show the top-level function. You may still need to manually paginate through the file to find specific HTML structures.
27
+ - *Future Enhancement Idea:* Add regex rules to detect major JSX landmarks (like <table, <form, or <Modal) to improve precision in UI components.
@@ -0,0 +1,67 @@
1
+ import os
2
+ import sys
3
+ import re
4
+
5
+ if sys.stdout.encoding != 'utf-8':
6
+ sys.stdout.reconfigure(encoding='utf-8')
7
+
8
+ MAX_FILE_SIZE = 500 * 1024 # 500 KB
9
+
10
+ def parse_js(content):
11
+ toc = []
12
+ class_pattern = re.compile(r'^\s*(?:export\s+)?(?:default\s+)?class\s+([A-Za-z0-9_]+)', re.MULTILINE)
13
+ func_pattern = re.compile(r'^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Za-z0-9_]+)', re.MULTILINE)
14
+ arrow_pattern = re.compile(r'^\s*(?:export\s+)?const\s+([A-Za-z0-9_]+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[A-Za-z0-9_]+)\s*=>', re.MULTILINE)
15
+
16
+ for match in class_pattern.finditer(content):
17
+ line = content.count('\n', 0, match.start()) + 1
18
+ toc.append((line, f"Class: {match.group(1)}"))
19
+
20
+ for match in func_pattern.finditer(content):
21
+ line = content.count('\n', 0, match.start()) + 1
22
+ toc.append((line, f"Function: {match.group(1)}"))
23
+
24
+ for match in arrow_pattern.finditer(content):
25
+ line = content.count('\n', 0, match.start()) + 1
26
+ toc.append((line, f"Arrow Function: {match.group(1)}"))
27
+
28
+ toc.sort(key=lambda x: x[0])
29
+ return toc
30
+
31
+ def main():
32
+ if len(sys.argv) < 2:
33
+ print("[FAIL] Usage: python reader.py <absolute_path_to_file>")
34
+ sys.exit(1)
35
+
36
+ filepath = sys.argv[1]
37
+ if not os.path.exists(filepath):
38
+ print(f"[FAIL] File not found: {filepath}")
39
+ sys.exit(1)
40
+
41
+ if os.path.getsize(filepath) > MAX_FILE_SIZE:
42
+ print(f"[FAIL] File too large (>500KB): {filepath}")
43
+ sys.exit(1)
44
+
45
+ try:
46
+ with open(filepath, 'r', encoding='utf-8') as f:
47
+ content = f.read()
48
+ except Exception as e:
49
+ print(f"[FAIL] Failed to read file: {e}")
50
+ sys.exit(1)
51
+
52
+ print(f"📄 TABLE OF CONTENTS: {os.path.basename(filepath)}")
53
+ print("-" * 50)
54
+
55
+ toc = parse_js(content)
56
+ if not toc:
57
+ print("[INFO] No primary class or function declarations found.")
58
+ else:
59
+ for line, desc in toc:
60
+ print(f"Line {line:<5}: {desc}")
61
+
62
+ print("-" * 50)
63
+ print("\n💡 PROMPT UNTUK AI (Copy-Paste ini):")
64
+ print(f'"Berdasarkan TOC di atas, tolong gunakan tool view_file untuk membaca hanya baris yang relevan dari fungsi yang bermasalah di file {os.path.basename(filepath)}."')
65
+
66
+ if __name__ == '__main__':
67
+ main()
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Smart Text Replacer (Pure Python)
3
+ description: Use this skill to safely replace strings or regex patterns across multiple files in a project. It uses pure Python for fast matching and provides mandatory dry-run previews and automatic backups before applying changes.
4
+ ---
5
+
6
+ ## Instructions for AI Agent
7
+
8
+ When you need to perform multi-file or token-intensive string replacements, use this skill instead of manually replacing content via `replace_file_content`. This is especially useful for refactoring names, updating URLs, or replacing specific terminology globally without reading the whole file into context.
9
+
10
+ **When to use this skill:**
11
+ - The user asks to rename a component, variable, or class globally.
12
+ - You need to replace a string across multiple files but want to preview the changes first to avoid false positives.
13
+ - The project is large and manual editing would exceed token limits.
14
+
15
+ **Command to run:**
16
+ ```powershell
17
+ python .agents/skills/smart_replace/replace_text.py "<absolute_target_directory>" "<search_string>" "<replace_string>" [options]
18
+ ```
19
+
20
+ **Options:**
21
+ - `--ext <ext>`: Comma-separated extensions to include (e.g. `js,jsx`).
22
+ - `--regex`: Treat `<search_string>` as a regular expression.
23
+ - `--whole-word`: Enforce word-boundary matching. (This is automatically set to `True` if `--regex` is not used).
24
+ - `--exclude <dir>`: Additional folders to exclude (node_modules, .git, etc are excluded by default).
25
+ - `--apply`: **CRITICAL FLAG**. Without this flag, the script runs in PREVIEW/DRY-RUN mode.
26
+
27
+ **Workflow Guardrails:**
28
+ 1. **ALWAYS** run the command *without* `--apply` first.
29
+ 2. Review the preview output in your terminal log. Check for false positives.
30
+ 3. Show the summary of the preview to the USER and ask for explicit confirmation.
31
+ 4. ONLY after the USER confirms, run the exact same command again but append `--apply` to execute the changes. The script will automatically backup original files to `.backup_replace/`.
32
+
33
+ **Comparison with other skills:**
34
+ - Use `smart_search` to understand *context* (how a function is used).
35
+ - Use `smart_replace` to safely *mutate* code (rename, replace) based on patterns.