claude-flow-novice 2.14.6 → 2.14.8
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/.claude/commands/cfn/run-tests.md +119 -0
- package/.claude/hooks/cfn-post-edit.config.json +11 -4
- package/.claude/skills/cfn-agent-selector/SKILL.md +3 -2
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +1 -1
- package/.claude/skills/cfn-product-owner-decision/execute-decision.sh +207 -113
- package/.claude/skills/cfn-product-owner-decision/test-backlog-integration.sh +148 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +86 -0
- package/.claude/skills/cfn-redis-coordination/store-context.sh +34 -0
- package/.claude/skills/pre-edit-backup/backup.sh +130 -0
- package/.claude/skills/pre-edit-backup/cleanup.sh +155 -0
- package/.claude/skills/pre-edit-backup/restore.sh +128 -0
- package/.claude/skills/pre-edit-backup/revert-file.sh +168 -0
- package/claude-assets/agents/README-AGENT_LIFECYCLE.md +522 -0
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +13 -8
- package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/test-agent.md +141 -0
- package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +35 -0
- package/claude-assets/commands/cfn/run-tests.md +119 -0
- package/claude-assets/hooks/cfn-post-edit.config.json +11 -4
- package/claude-assets/skills/agent-name-validation/README.md +28 -0
- package/claude-assets/skills/agent-name-validation/SKILL.md +168 -0
- package/claude-assets/skills/agent-name-validation/validate-agent-names.sh +47 -0
- package/claude-assets/skills/cfn-agent-selector/SKILL.md +3 -2
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +1 -1
- package/claude-assets/skills/cfn-product-owner-decision/execute-decision.sh +207 -113
- package/claude-assets/skills/cfn-product-owner-decision/test-backlog-integration.sh +148 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +86 -0
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +34 -0
- package/claude-assets/skills/cfn-task-classifier/SKILL.md +1 -1
- package/claude-assets/skills/cfn-test-runner/SKILL.md +288 -0
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +55 -0
- package/claude-assets/skills/cfn-test-runner/init-benchmark-db.sh +48 -0
- package/claude-assets/skills/cfn-test-runner/run-all-tests.sh +222 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +55 -0
- package/claude-assets/skills/cfn-test-runner/validate-redis-keys.sh +143 -0
- package/claude-assets/skills/hook-pipeline/bash-dependency-checker.sh +89 -0
- package/claude-assets/skills/hook-pipeline/bash-pipe-safety.sh +69 -0
- package/claude-assets/skills/hook-pipeline/enforce-lf.sh +36 -0
- package/claude-assets/skills/hook-pipeline/js-promise-safety.sh +110 -0
- package/claude-assets/skills/hook-pipeline/python-async-safety.py +124 -0
- package/claude-assets/skills/hook-pipeline/python-import-checker.py +114 -0
- package/claude-assets/skills/hook-pipeline/python-subprocess-safety.py +77 -0
- package/claude-assets/skills/hook-pipeline/rust-command-safety.sh +38 -0
- package/claude-assets/skills/hook-pipeline/rust-dependency-checker.sh +50 -0
- package/claude-assets/skills/hook-pipeline/rust-future-safety.sh +50 -0
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/cli/agent-executor.js +1 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +40 -30
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/package.json +2 -1
- package/scripts/init-project.js +4 -1
- package/scripts/switch-api.sh +7 -7
- package/claude-assets/agents/cfn-dev-team/developers/dev-backend-api.md +0 -147
- package/claude-assets/agents/cfn-dev-team/developers/frontend/spec-mobile-react-native.md +0 -199
- package/claude-assets/agents/cfn-dev-team/documentation/docs-api-openapi.md +0 -98
- package/claude-assets/agents/cfn-dev-team/product-owners/product-owner-agent.md +0 -155
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/analyze-code-quality.md +0 -141
- /package/claude-assets/agents/cfn-dev-team/developers/{backend-dev.md → backend-developer.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/documentation/{api-docs.md → api-documentation.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/documentation/{specification.md → specification-agent.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/reviewers/quality/{code-analyzer.md → code-quality-validator.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/testers/e2e/{playwright-agent.md → playwright-tester.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/testers/unit/{tdd-london-swarm.md → tdd-london-unit-swarm.md} +0 -0
- /package/claude-assets/agents/cfn-dev-team/testers/validation/{production-validator.md → validation-production-validator.md} +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import ast
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
class SubprocessSafetyVisitor(ast.NodeVisitor):
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.unsafe_calls = []
|
|
8
|
+
self.allowed_stderr_values = {'Attribute': ['PIPE', 'DEVNULL', 'STDOUT'],
|
|
9
|
+
'Name': ['subprocess.PIPE', 'subprocess.DEVNULL', 'subprocess.STDOUT']}
|
|
10
|
+
|
|
11
|
+
def is_safe_stderr(self, node):
|
|
12
|
+
if isinstance(node, ast.Attribute):
|
|
13
|
+
return node.attr in self.allowed_stderr_values['Attribute']
|
|
14
|
+
if isinstance(node, ast.Name):
|
|
15
|
+
return node.id in self.allowed_stderr_values['Name']
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
def visit_Call(self, node):
|
|
19
|
+
if isinstance(node.func, ast.Attribute):
|
|
20
|
+
func_name = node.func.attr
|
|
21
|
+
if func_name in ['run', 'Popen', 'check_output']:
|
|
22
|
+
# Check if from subprocess module
|
|
23
|
+
module_name = None
|
|
24
|
+
if isinstance(node.func.value, ast.Name):
|
|
25
|
+
module_name = node.func.value.id
|
|
26
|
+
|
|
27
|
+
if module_name == 'subprocess':
|
|
28
|
+
# Strategies for safe subprocess calls
|
|
29
|
+
stderr_found = False
|
|
30
|
+
capture_output_found = False
|
|
31
|
+
|
|
32
|
+
for kw in node.keywords:
|
|
33
|
+
if kw.arg == 'stderr' and self.is_safe_stderr(kw.value):
|
|
34
|
+
stderr_found = True
|
|
35
|
+
if kw.arg == 'capture_output' and isinstance(kw.value, ast.NameConstant) and kw.value.value is True:
|
|
36
|
+
capture_output_found = True
|
|
37
|
+
|
|
38
|
+
if not (stderr_found or capture_output_found):
|
|
39
|
+
self.unsafe_calls.append(
|
|
40
|
+
f"Line {node.lineno}: Subprocess call '{func_name}' without stderr parameter or capture_output"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
self.generic_visit(node)
|
|
44
|
+
|
|
45
|
+
def validate_subprocess_safety(file_path):
|
|
46
|
+
try:
|
|
47
|
+
with open(file_path, 'r') as f:
|
|
48
|
+
tree = ast.parse(f.read(), filename=file_path)
|
|
49
|
+
except SyntaxError as e:
|
|
50
|
+
print(f"Syntax error in {file_path}: {e}")
|
|
51
|
+
return 1
|
|
52
|
+
except FileNotFoundError:
|
|
53
|
+
print(f"File not found: {file_path}")
|
|
54
|
+
return 1
|
|
55
|
+
|
|
56
|
+
visitor = SubprocessSafetyVisitor()
|
|
57
|
+
visitor.visit(tree)
|
|
58
|
+
|
|
59
|
+
if visitor.unsafe_calls:
|
|
60
|
+
print("Subprocess safety warnings:")
|
|
61
|
+
for call in visitor.unsafe_calls:
|
|
62
|
+
print(call)
|
|
63
|
+
return 2
|
|
64
|
+
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
def main():
|
|
68
|
+
if len(sys.argv) < 2:
|
|
69
|
+
print("Usage: python3 python-subprocess-safety.py <python_file>")
|
|
70
|
+
return 1
|
|
71
|
+
|
|
72
|
+
file_path = sys.argv[1]
|
|
73
|
+
result = validate_subprocess_safety(file_path)
|
|
74
|
+
sys.exit(result)
|
|
75
|
+
|
|
76
|
+
if __name__ == '__main__':
|
|
77
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Rust Command Safety Validator
|
|
3
|
+
set -o pipefail
|
|
4
|
+
|
|
5
|
+
# Exit codes:
|
|
6
|
+
# 0 - No issues found
|
|
7
|
+
# 1 - File not found or cannot be read
|
|
8
|
+
# 2 - Command safety warnings found
|
|
9
|
+
|
|
10
|
+
if [ $# -eq 0 ]; then
|
|
11
|
+
echo "Usage: $0 <rust_file>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
file_path="$1"
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$file_path" ]; then
|
|
18
|
+
echo "File not found: $file_path"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Use grep to find Command::new() calls and check for stderr in next 5 lines
|
|
23
|
+
unsafe_commands=$(grep -n "Command::new(" "$file_path" | while read -r line; do
|
|
24
|
+
line_num=$(echo "$line" | cut -d':' -f1)
|
|
25
|
+
next_lines=$(tail -n +"$line_num" "$file_path" | head -n 5)
|
|
26
|
+
|
|
27
|
+
if ! echo "$next_lines" | grep -q "\.stderr("; then
|
|
28
|
+
echo "Line $line_num: Command::new() without stderr() configuration in next 5 lines"
|
|
29
|
+
fi
|
|
30
|
+
done)
|
|
31
|
+
|
|
32
|
+
if [ -n "$unsafe_commands" ]; then
|
|
33
|
+
echo "Command safety warnings:"
|
|
34
|
+
echo "$unsafe_commands"
|
|
35
|
+
exit 2
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
exit 0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Rust Dependency Checker
|
|
3
|
+
set -o pipefail
|
|
4
|
+
|
|
5
|
+
# Exit codes:
|
|
6
|
+
# 0 - No issues found
|
|
7
|
+
# 1 - Cargo.toml not found or cannot be read
|
|
8
|
+
# 2 - Dependency warnings found
|
|
9
|
+
|
|
10
|
+
if [ $# -eq 0 ]; then
|
|
11
|
+
echo "Usage: $0 <rust_file>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
file_path="$1"
|
|
16
|
+
project_dir=$(dirname "$file_path")
|
|
17
|
+
cargo_toml="${project_dir}/Cargo.toml"
|
|
18
|
+
|
|
19
|
+
if [ ! -f "$cargo_toml" ]; then
|
|
20
|
+
echo "Cargo.toml not found in project directory"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Skip list for built-in/standard libraries
|
|
25
|
+
skip_modules="std|core|alloc|proc_macro"
|
|
26
|
+
|
|
27
|
+
# Find local crate dependencies in the file
|
|
28
|
+
local_deps=$(grep -o "use crate::[a-zA-Z_][a-zA-Z0-9_:]*" "$file_path" |
|
|
29
|
+
sed 's/use crate:://g' |
|
|
30
|
+
cut -d':' -f1 |
|
|
31
|
+
sort -u |
|
|
32
|
+
grep -vE "^(${skip_modules})$")
|
|
33
|
+
|
|
34
|
+
# Check dependencies in Cargo.toml
|
|
35
|
+
if [ -n "$local_deps" ]; then
|
|
36
|
+
warnings=$(echo "$local_deps" | while read -r dep; do
|
|
37
|
+
if ! grep -q "path = \".*${dep}\"" "$cargo_toml"; then
|
|
38
|
+
echo "Undeclared local crate dependency: ${dep}"
|
|
39
|
+
fi
|
|
40
|
+
done)
|
|
41
|
+
|
|
42
|
+
if [ -n "$warnings" ]; then
|
|
43
|
+
echo "Dependency warnings:"
|
|
44
|
+
echo "$warnings"
|
|
45
|
+
echo "Recommendation: Declare local crate dependencies in Cargo.toml"
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
exit 0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Rust Future Safety Validator
|
|
3
|
+
set -o pipefail
|
|
4
|
+
|
|
5
|
+
# Exit codes:
|
|
6
|
+
# 0 - No issues found
|
|
7
|
+
# 1 - File not found or cannot be read
|
|
8
|
+
# 2 - Future safety warnings found
|
|
9
|
+
|
|
10
|
+
if [ $# -eq 0 ]; then
|
|
11
|
+
echo "Usage: $0 <rust_file>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
file_path="$1"
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$file_path" ]; then
|
|
18
|
+
echo "File not found: $file_path"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Find async function definitions
|
|
23
|
+
async_functions=$(grep -n "async fn" "$file_path")
|
|
24
|
+
|
|
25
|
+
if [ -n "$async_functions" ]; then
|
|
26
|
+
warnings=$(echo "$async_functions" | while read -r async_line; do
|
|
27
|
+
line_num=$(echo "$async_line" | cut -d':' -f1)
|
|
28
|
+
func_name=$(echo "$async_line" | sed -n 's/.*async fn \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/p')
|
|
29
|
+
|
|
30
|
+
# Search for function calls without .await
|
|
31
|
+
calls_without_await=$(grep -n "\\b${func_name}(" "$file_path" |
|
|
32
|
+
grep -v ".await" |
|
|
33
|
+
grep -v "async fn" |
|
|
34
|
+
grep -v "fn ${func_name}")
|
|
35
|
+
|
|
36
|
+
if [ -n "$calls_without_await" ]; then
|
|
37
|
+
echo "Lines related to async function '${func_name}':"
|
|
38
|
+
echo "$calls_without_await"
|
|
39
|
+
echo "Recommendation: Use .await for async function calls. Run cargo clippy for more details."
|
|
40
|
+
fi
|
|
41
|
+
done)
|
|
42
|
+
|
|
43
|
+
if [ -n "$warnings" ]; then
|
|
44
|
+
echo "Future safety warnings:"
|
|
45
|
+
echo "$warnings"
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
exit 0
|
|
@@ -1,145 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Dynamic Agent Loader - Reads agent definitions from .claude/agents/ directory
|
|
4
3
|
* Single source of truth for agent types in the system
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
return new (P || (P = Promise))(function(resolve, reject) {
|
|
12
|
-
function fulfilled(value) {
|
|
13
|
-
try {
|
|
14
|
-
step(generator.next(value));
|
|
15
|
-
} catch (e) {
|
|
16
|
-
reject(e);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function rejected(value) {
|
|
20
|
-
try {
|
|
21
|
-
step(generator["throw"](value));
|
|
22
|
-
} catch (e) {
|
|
23
|
-
reject(e);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function step(result) {
|
|
27
|
-
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
28
|
-
}
|
|
29
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
30
|
-
});
|
|
31
|
-
};
|
|
32
|
-
var __generator = this && this.__generator || function(thisArg, body) {
|
|
33
|
-
var _ = {
|
|
34
|
-
label: 0,
|
|
35
|
-
sent: function() {
|
|
36
|
-
if (t[0] & 1) throw t[1];
|
|
37
|
-
return t[1];
|
|
38
|
-
},
|
|
39
|
-
trys: [],
|
|
40
|
-
ops: []
|
|
41
|
-
}, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
42
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
43
|
-
return this;
|
|
44
|
-
}), g;
|
|
45
|
-
function verb(n) {
|
|
46
|
-
return function(v) {
|
|
47
|
-
return step([
|
|
48
|
-
n,
|
|
49
|
-
v
|
|
50
|
-
]);
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function step(op) {
|
|
54
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
55
|
-
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
56
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
57
|
-
if (y = 0, t) op = [
|
|
58
|
-
op[0] & 2,
|
|
59
|
-
t.value
|
|
60
|
-
];
|
|
61
|
-
switch(op[0]){
|
|
62
|
-
case 0:
|
|
63
|
-
case 1:
|
|
64
|
-
t = op;
|
|
65
|
-
break;
|
|
66
|
-
case 4:
|
|
67
|
-
_.label++;
|
|
68
|
-
return {
|
|
69
|
-
value: op[1],
|
|
70
|
-
done: false
|
|
71
|
-
};
|
|
72
|
-
case 5:
|
|
73
|
-
_.label++;
|
|
74
|
-
y = op[1];
|
|
75
|
-
op = [
|
|
76
|
-
0
|
|
77
|
-
];
|
|
78
|
-
continue;
|
|
79
|
-
case 7:
|
|
80
|
-
op = _.ops.pop();
|
|
81
|
-
_.trys.pop();
|
|
82
|
-
continue;
|
|
83
|
-
default:
|
|
84
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
85
|
-
_ = 0;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
89
|
-
_.label = op[1];
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
if (op[0] === 6 && _.label < t[1]) {
|
|
93
|
-
_.label = t[1];
|
|
94
|
-
t = op;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
if (t && _.label < t[2]) {
|
|
98
|
-
_.label = t[2];
|
|
99
|
-
_.ops.push(op);
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
if (t[2]) _.ops.pop();
|
|
103
|
-
_.trys.pop();
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
op = body.call(thisArg, _);
|
|
107
|
-
} catch (e) {
|
|
108
|
-
op = [
|
|
109
|
-
6,
|
|
110
|
-
e
|
|
111
|
-
];
|
|
112
|
-
y = 0;
|
|
113
|
-
} finally{
|
|
114
|
-
f = t = 0;
|
|
115
|
-
}
|
|
116
|
-
if (op[0] & 5) throw op[1];
|
|
117
|
-
return {
|
|
118
|
-
value: op[0] ? op[1] : void 0,
|
|
119
|
-
done: true
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
var __spreadArray = this && this.__spreadArray || function(to, from, pack) {
|
|
124
|
-
if (pack || arguments.length === 2) for(var i = 0, l = from.length, ar; i < l; i++){
|
|
125
|
-
if (ar || !(i in from)) {
|
|
126
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
127
|
-
ar[i] = from[i];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
131
|
-
};
|
|
132
|
-
Object.defineProperty(exports, "__esModule", {
|
|
133
|
-
value: true
|
|
134
|
-
});
|
|
135
|
-
exports.refreshAgents = exports.getAgentsByCategory = exports.isValidAgentType = exports.searchAgents = exports.getAgentCategories = exports.getAllAgents = exports.getAgent = exports.getAvailableAgentTypes = exports.agentLoader = exports.AgentLoader = void 0;
|
|
136
|
-
exports.resolveLegacyAgentType = resolveLegacyAgentType;
|
|
137
|
-
var node_fs_1 = require("node:fs");
|
|
138
|
-
var glob_1 = require("glob");
|
|
139
|
-
var node_path_1 = require("node:path");
|
|
140
|
-
var yaml_1 = require("yaml");
|
|
4
|
+
*/ import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
import { glob } from 'glob';
|
|
6
|
+
import { resolve, dirname } from 'node:path';
|
|
7
|
+
import { parse as parseYaml } from 'yaml';
|
|
141
8
|
// Legacy agent type mapping for backward compatibility
|
|
142
|
-
|
|
9
|
+
const LEGACY_AGENT_MAPPING = {
|
|
143
10
|
analyst: 'code-analyzer',
|
|
144
11
|
coordinator: 'hierarchical-coordinator',
|
|
145
12
|
optimizer: 'perf-analyzer',
|
|
@@ -150,40 +17,38 @@ var LEGACY_AGENT_MAPPING = {
|
|
|
150
17
|
};
|
|
151
18
|
/**
|
|
152
19
|
* Resolve legacy agent types to current equivalents
|
|
153
|
-
*/ function resolveLegacyAgentType(legacyType) {
|
|
20
|
+
*/ export function resolveLegacyAgentType(legacyType) {
|
|
154
21
|
return LEGACY_AGENT_MAPPING[legacyType] || legacyType;
|
|
155
22
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
AgentLoader.prototype.getAgentsDirectory = function() {
|
|
164
|
-
var currentDir = process.cwd();
|
|
23
|
+
export class AgentLoader {
|
|
24
|
+
agentCache = new Map();
|
|
25
|
+
categoriesCache = [];
|
|
26
|
+
lastLoadTime = 0;
|
|
27
|
+
CACHE_EXPIRY = 60_000;
|
|
28
|
+
getAgentsDirectory() {
|
|
29
|
+
let currentDir = process.cwd();
|
|
165
30
|
while(currentDir !== '/'){
|
|
166
|
-
|
|
167
|
-
if (
|
|
31
|
+
const claudeAgentsPath = resolve(currentDir, '.claude', 'agents');
|
|
32
|
+
if (existsSync(claudeAgentsPath)) {
|
|
168
33
|
return claudeAgentsPath;
|
|
169
34
|
}
|
|
170
|
-
currentDir =
|
|
35
|
+
currentDir = dirname(currentDir);
|
|
171
36
|
}
|
|
172
|
-
return
|
|
173
|
-
}
|
|
174
|
-
|
|
37
|
+
return resolve(process.cwd(), '.claude', 'agents');
|
|
38
|
+
}
|
|
39
|
+
parseAgentFile(filePath) {
|
|
175
40
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
41
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
42
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
178
43
|
if (!frontmatterMatch) {
|
|
179
|
-
console.warn(
|
|
44
|
+
console.warn(`No frontmatter found in ${filePath}`);
|
|
180
45
|
return null;
|
|
181
46
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
47
|
+
const [, yamlContent, markdownContent] = frontmatterMatch;
|
|
48
|
+
const frontmatter = parseYaml(yamlContent);
|
|
49
|
+
const description = frontmatter.description;
|
|
185
50
|
if (!frontmatter.name || !description) {
|
|
186
|
-
console.warn(
|
|
51
|
+
console.warn(`Missing required fields (name, description) in ${filePath}`);
|
|
187
52
|
return null;
|
|
188
53
|
}
|
|
189
54
|
return {
|
|
@@ -204,13 +69,129 @@ var AgentLoader = /** @class */ function() {
|
|
|
204
69
|
content: markdownContent.trim()
|
|
205
70
|
};
|
|
206
71
|
} catch (error) {
|
|
207
|
-
console.error(
|
|
72
|
+
console.error(`Error parsing agent file ${filePath}:`, error);
|
|
208
73
|
return null;
|
|
209
74
|
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
75
|
+
}
|
|
76
|
+
parseTools(frontmatter) {
|
|
77
|
+
const extractTools = (input)=>{
|
|
213
78
|
if (Array.isArray(input)) return input.map(String);
|
|
79
|
+
if (typeof input === 'string') {
|
|
80
|
+
return input.split(/[,\s]+/).map((t)=>t.trim()).filter((t)=>t.length > 0);
|
|
81
|
+
}
|
|
82
|
+
return [];
|
|
83
|
+
};
|
|
84
|
+
// Safely handle tools and capabilities.tools
|
|
85
|
+
const toolsFromFrontmatter = frontmatter.tools ? extractTools(frontmatter.tools) : [];
|
|
86
|
+
const toolsFromCapabilities = frontmatter.capabilities && typeof frontmatter.capabilities === 'object' ? extractTools(Object(frontmatter.capabilities).tools) : [];
|
|
87
|
+
return [
|
|
88
|
+
...toolsFromFrontmatter,
|
|
89
|
+
...toolsFromCapabilities
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
async loadAgents() {
|
|
93
|
+
const agentsDir = this.getAgentsDirectory();
|
|
94
|
+
if (!existsSync(agentsDir)) {
|
|
95
|
+
console.warn(`Agents directory not found: ${agentsDir}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const agentFiles = await new Promise((resolve, reject)=>{
|
|
99
|
+
glob('**/*.md', {
|
|
100
|
+
cwd: agentsDir,
|
|
101
|
+
ignore: [
|
|
102
|
+
'**/README.md',
|
|
103
|
+
'**/MIGRATION_SUMMARY.md'
|
|
104
|
+
],
|
|
105
|
+
absolute: true
|
|
106
|
+
}, (err, matches)=>{
|
|
107
|
+
if (err) reject(err);
|
|
108
|
+
else resolve(matches);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
this.agentCache.clear();
|
|
112
|
+
this.categoriesCache = [];
|
|
113
|
+
const categoryMap = new Map();
|
|
114
|
+
for (const filePath of agentFiles){
|
|
115
|
+
const agent = this.parseAgentFile(filePath);
|
|
116
|
+
if (agent) {
|
|
117
|
+
this.agentCache.set(agent.name, agent);
|
|
118
|
+
const relativePath = filePath.replace(agentsDir, '');
|
|
119
|
+
const pathParts = relativePath.split('/');
|
|
120
|
+
const category = pathParts[1] || 'uncategorized';
|
|
121
|
+
if (!categoryMap.has(category)) {
|
|
122
|
+
categoryMap.set(category, []);
|
|
123
|
+
}
|
|
124
|
+
categoryMap.get(category).push(agent);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.categoriesCache = Array.from(categoryMap.entries()).map(([name, agents])=>({
|
|
128
|
+
name,
|
|
129
|
+
agents: agents.sort((a, b)=>a.name.localeCompare(b.name))
|
|
130
|
+
}));
|
|
131
|
+
this.lastLoadTime = Date.now();
|
|
132
|
+
}
|
|
133
|
+
// Rest of the methods remain similar to the original implementation
|
|
134
|
+
needsRefresh() {
|
|
135
|
+
return Date.now() - this.lastLoadTime > this.CACHE_EXPIRY;
|
|
136
|
+
}
|
|
137
|
+
async ensureLoaded() {
|
|
138
|
+
if (this.agentCache.size === 0 || this.needsRefresh()) {
|
|
139
|
+
await this.loadAgents();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async getAvailableAgentTypes() {
|
|
143
|
+
await this.ensureLoaded();
|
|
144
|
+
const currentTypes = Array.from(this.agentCache.keys());
|
|
145
|
+
const legacyTypes = Object.keys(LEGACY_AGENT_MAPPING);
|
|
146
|
+
return Array.from(new Set([
|
|
147
|
+
...currentTypes,
|
|
148
|
+
...legacyTypes
|
|
149
|
+
])).sort();
|
|
150
|
+
}
|
|
151
|
+
async getAgent(name) {
|
|
152
|
+
await this.ensureLoaded();
|
|
153
|
+
return this.agentCache.get(name) || this.agentCache.get(resolveLegacyAgentType(name)) || null;
|
|
154
|
+
}
|
|
155
|
+
async getAllAgents() {
|
|
156
|
+
await this.ensureLoaded();
|
|
157
|
+
return Array.from(this.agentCache.values()).sort((a, b)=>a.name.localeCompare(b.name));
|
|
158
|
+
}
|
|
159
|
+
async getAgentCategories() {
|
|
160
|
+
await this.ensureLoaded();
|
|
161
|
+
return this.categoriesCache;
|
|
162
|
+
}
|
|
163
|
+
async searchAgents(query) {
|
|
164
|
+
await this.ensureLoaded();
|
|
165
|
+
const lowerQuery = query.toLowerCase();
|
|
166
|
+
return Array.from(this.agentCache.values()).filter((agent)=>agent.name.toLowerCase().includes(lowerQuery) || agent.description.toLowerCase().includes(lowerQuery) || agent.capabilities?.some((cap)=>cap.toLowerCase().includes(lowerQuery)));
|
|
167
|
+
}
|
|
168
|
+
async isValidAgentType(name) {
|
|
169
|
+
await this.ensureLoaded();
|
|
170
|
+
return this.agentCache.has(name) || this.agentCache.has(resolveLegacyAgentType(name));
|
|
171
|
+
}
|
|
172
|
+
async getAgentsByCategory(category) {
|
|
173
|
+
const categories = await this.getAgentCategories();
|
|
174
|
+
const found = categories.find((cat)=>cat.name === category);
|
|
175
|
+
return found?.agents || [];
|
|
176
|
+
}
|
|
177
|
+
async refresh() {
|
|
178
|
+
this.lastLoadTime = 0;
|
|
179
|
+
await this.loadAgents();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Singleton instance
|
|
183
|
+
export const agentLoader = new AgentLoader();
|
|
184
|
+
// Convenience exports for use in other modules
|
|
185
|
+
export const getAvailableAgentTypes = ()=>agentLoader.getAvailableAgentTypes();
|
|
186
|
+
export const getAgent = (name)=>agentLoader.getAgent(name);
|
|
187
|
+
export const getAllAgents = ()=>agentLoader.getAllAgents();
|
|
188
|
+
export const getAgentCategories = ()=>agentLoader.getAgentCategories();
|
|
189
|
+
export const searchAgents = (query)=>agentLoader.searchAgents(query);
|
|
190
|
+
export const isValidAgentType = (name)=>agentLoader.isValidAgentType(name);
|
|
191
|
+
export const getAgentsByCategory = (category)=>agentLoader.getAgentsByCategory(category);
|
|
192
|
+
export const refreshAgents = ()=>agentLoader.refresh();
|
|
193
|
+
|
|
194
|
+
//# sourceMappingURL=agent-loader.js.map.isArray(input)) return input.map(String);
|
|
214
195
|
if (typeof input === 'string') {
|
|
215
196
|
return input.split(/[,\s]+/).map(function(t) {
|
|
216
197
|
return t.trim();
|