@jaguilar87/gaia-ops 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/CHANGELOG.md +315 -0
- package/CLAUDE.md +154 -0
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/agents/aws-troubleshooter.md +50 -0
- package/agents/claude-architect.md +821 -0
- package/agents/devops-developer.md +92 -0
- package/agents/gcp-troubleshooter.md +50 -0
- package/agents/gitops-operator.md +360 -0
- package/agents/terraform-architect.md +289 -0
- package/bin/gaia-init.js +620 -0
- package/commands/architect.md +97 -0
- package/commands/restore-session.md +87 -0
- package/commands/save-session.md +88 -0
- package/commands/session-status.md +61 -0
- package/commands/speckit.add-task.md +144 -0
- package/commands/speckit.analyze-task.md +65 -0
- package/commands/speckit.implement.md +96 -0
- package/commands/speckit.init.md +237 -0
- package/commands/speckit.plan.md +88 -0
- package/commands/speckit.specify.md +161 -0
- package/commands/speckit.tasks.md +188 -0
- package/config/AGENTS.md +162 -0
- package/config/agent-catalog.md +604 -0
- package/config/context-contracts.md +682 -0
- package/config/git-standards.md +674 -0
- package/config/git_standards.json +69 -0
- package/config/orchestration-workflow.md +735 -0
- package/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/hooks/__pycache__/pre_kubectl_security.cpython-312.pyc +0 -0
- package/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
- package/hooks/__pycache__/subagent_stop.cpython-312.pyc +0 -0
- package/hooks/post_tool_use.py +463 -0
- package/hooks/pre_kubectl_security.py +205 -0
- package/hooks/pre_tool_use.py +530 -0
- package/hooks/session_start.py +315 -0
- package/hooks/subagent_stop.py +549 -0
- package/index.js +92 -0
- package/package.json +59 -0
- package/speckit/README.en.md +648 -0
- package/speckit/README.md +353 -0
- package/speckit/governance.md +169 -0
- package/speckit/scripts/check-prerequisites.sh +194 -0
- package/speckit/scripts/common.sh +126 -0
- package/speckit/scripts/create-new-feature.sh +131 -0
- package/speckit/scripts/init.sh +42 -0
- package/speckit/scripts/setup-plan.sh +95 -0
- package/speckit/scripts/update-agent-context.sh +718 -0
- package/speckit/templates/adr-template.md +118 -0
- package/speckit/templates/agent-file-template.md +23 -0
- package/speckit/templates/plan-template.md +233 -0
- package/speckit/templates/spec-template.md +116 -0
- package/speckit/templates/tasks-template-bkp.md +136 -0
- package/speckit/templates/tasks-template.md +345 -0
- package/templates/CLAUDE.template.md +170 -0
- package/templates/code-examples/approval_gate_workflow.py +141 -0
- package/templates/code-examples/clarification_workflow.py +94 -0
- package/templates/code-examples/commit_validation.py +86 -0
- package/templates/project-context.template.json +126 -0
- package/templates/settings.template.json +307 -0
- package/tools/__pycache__/agent_router.cpython-312.pyc +0 -0
- package/tools/__pycache__/approval_gate.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_engine.cpython-312.pyc +0 -0
- package/tools/__pycache__/clarify_patterns.cpython-312.pyc +0 -0
- package/tools/__pycache__/commit_validator.cpython-312.pyc +0 -0
- package/tools/__pycache__/context_section_reader.cpython-312.pyc +0 -0
- package/tools/__pycache__/routing_dashboard.cpython-312.pyc +0 -0
- package/tools/__pycache__/routing_feedback.cpython-312.pyc +0 -0
- package/tools/__pycache__/semantic_matcher.cpython-312.pyc +0 -0
- package/tools/__pycache__/task_manager.cpython-312.pyc +0 -0
- package/tools/agent_capabilities.json +231 -0
- package/tools/agent_invoker_helper.py +239 -0
- package/tools/agent_router.py +730 -0
- package/tools/approval_gate.py +318 -0
- package/tools/clarify_engine.py +511 -0
- package/tools/clarify_patterns.py +356 -0
- package/tools/commit_validator.py +338 -0
- package/tools/context_provider.py +181 -0
- package/tools/context_section_reader.py +301 -0
- package/tools/demo_clarify.py +104 -0
- package/tools/generate_embeddings.py +168 -0
- package/tools/quicktriage_aws_troubleshooter.sh +45 -0
- package/tools/quicktriage_devops_developer.sh +38 -0
- package/tools/quicktriage_gcp_troubleshooter.sh +51 -0
- package/tools/quicktriage_gitops_operator.sh +47 -0
- package/tools/quicktriage_terraform_architect.sh +40 -0
- package/tools/semantic_matcher.py +222 -0
- package/tools/task_manager.py +547 -0
- package/tools/task_manager_README.md +395 -0
- package/tools/task_manager_example.py +215 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate intent embeddings offline using sentence-transformers
|
|
4
|
+
|
|
5
|
+
This script is run ONCE to generate pre-computed embeddings.
|
|
6
|
+
At runtime, we only need numpy for similarity calculations.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 generate_embeddings.py
|
|
10
|
+
|
|
11
|
+
Output:
|
|
12
|
+
- .claude/configs/intent_embeddings.npy (binary, ~5MB)
|
|
13
|
+
- .claude/configs/intent_embeddings.json (readable metadata)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
# Define intent examples (from agent_router.py IntentClassifier)
|
|
21
|
+
INTENT_EXAMPLES = {
|
|
22
|
+
"infrastructure_creation": [
|
|
23
|
+
"create gke cluster with autopilot",
|
|
24
|
+
"provision new vpc and subnets",
|
|
25
|
+
"deploy terraform infrastructure",
|
|
26
|
+
"setup kubernetes cluster",
|
|
27
|
+
"build cloud resources",
|
|
28
|
+
"create new database instance",
|
|
29
|
+
"provision redis cache",
|
|
30
|
+
"deploy load balancer"
|
|
31
|
+
],
|
|
32
|
+
"infrastructure_diagnosis": [
|
|
33
|
+
"diagnose cluster connectivity",
|
|
34
|
+
"troubleshoot gke pod crashes",
|
|
35
|
+
"debug network latency problems",
|
|
36
|
+
"check kubernetes node status",
|
|
37
|
+
"analyze infrastructure errors",
|
|
38
|
+
"troubleshoot cloud sql connectivity",
|
|
39
|
+
"debug workload identity issues",
|
|
40
|
+
"diagnose firewall rule problems"
|
|
41
|
+
],
|
|
42
|
+
"kubernetes_operations": [
|
|
43
|
+
"check pod status in namespace",
|
|
44
|
+
"view deployment logs",
|
|
45
|
+
"verify flux reconciliation",
|
|
46
|
+
"monitor helm release status",
|
|
47
|
+
"inspect kubernetes resources",
|
|
48
|
+
"check service endpoints",
|
|
49
|
+
"scale deployment replicas",
|
|
50
|
+
"update configmap values"
|
|
51
|
+
],
|
|
52
|
+
"application_development": [
|
|
53
|
+
"build docker image for api",
|
|
54
|
+
"run unit tests for application",
|
|
55
|
+
"validate application configuration",
|
|
56
|
+
"compile typescript code",
|
|
57
|
+
"execute npm build command",
|
|
58
|
+
"lint code with eslint",
|
|
59
|
+
"run integration tests",
|
|
60
|
+
"package application for deployment"
|
|
61
|
+
],
|
|
62
|
+
"infrastructure_validation": [
|
|
63
|
+
"validate terraform configuration",
|
|
64
|
+
"check hcl syntax errors",
|
|
65
|
+
"run terraform plan",
|
|
66
|
+
"scan infrastructure security",
|
|
67
|
+
"verify module dependencies",
|
|
68
|
+
"check terraform state integrity",
|
|
69
|
+
"scan for policy violations",
|
|
70
|
+
"validate cloudformation template"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def generate_embeddings():
|
|
76
|
+
"""Generate and save embeddings using sentence-transformers"""
|
|
77
|
+
try:
|
|
78
|
+
from sentence_transformers import SentenceTransformer
|
|
79
|
+
import numpy as np
|
|
80
|
+
except ImportError:
|
|
81
|
+
print("❌ Error: sentence-transformers not installed")
|
|
82
|
+
print(" Run: pip install sentence-transformers torch")
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
print("\n" + "="*70)
|
|
86
|
+
print("🔧 Generating Intent Embeddings (Offline)")
|
|
87
|
+
print("="*70 + "\n")
|
|
88
|
+
|
|
89
|
+
# Load model
|
|
90
|
+
print("📥 Loading sentence-transformers model: all-MiniLM-L6-v2")
|
|
91
|
+
model = SentenceTransformer('all-MiniLM-L6-v2')
|
|
92
|
+
print(f" ✅ Model loaded (embedding dimension: 384)")
|
|
93
|
+
|
|
94
|
+
embeddings_data = {}
|
|
95
|
+
total_examples = 0
|
|
96
|
+
|
|
97
|
+
print("\n📊 Generating embeddings for intents:\n")
|
|
98
|
+
|
|
99
|
+
for intent_name, examples in INTENT_EXAMPLES.items():
|
|
100
|
+
print(f" 🎯 {intent_name}")
|
|
101
|
+
|
|
102
|
+
# Generate embeddings for all examples
|
|
103
|
+
embeddings = model.encode(examples, convert_to_numpy=True)
|
|
104
|
+
mean_embedding = embeddings.mean(axis=0)
|
|
105
|
+
|
|
106
|
+
# Store metadata and mean embedding
|
|
107
|
+
embeddings_data[intent_name] = {
|
|
108
|
+
"embedding": mean_embedding.tolist(), # Convert to list for JSON
|
|
109
|
+
"examples": examples,
|
|
110
|
+
"dimension": len(mean_embedding),
|
|
111
|
+
"count": len(examples)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
print(f" ✅ {len(examples):2d} examples → 384-dim embedding")
|
|
115
|
+
total_examples += len(examples)
|
|
116
|
+
|
|
117
|
+
print(f"\n Total examples processed: {total_examples}")
|
|
118
|
+
|
|
119
|
+
# Save as JSON (readable metadata)
|
|
120
|
+
output_dir = Path(__file__).parent.parent / "configs"
|
|
121
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
|
|
123
|
+
json_path = output_dir / "intent_embeddings.json"
|
|
124
|
+
with open(json_path, 'w', encoding='utf-8') as f:
|
|
125
|
+
json.dump(embeddings_data, f, indent=2)
|
|
126
|
+
|
|
127
|
+
print(f"\n✅ Saved metadata: {json_path}")
|
|
128
|
+
print(f" Size: {json_path.stat().st_size / 1024:.1f} KB")
|
|
129
|
+
|
|
130
|
+
# Save as numpy array (binary, optimized)
|
|
131
|
+
npy_path = output_dir / "intent_embeddings.npy"
|
|
132
|
+
import numpy as np
|
|
133
|
+
np.save(npy_path, embeddings_data)
|
|
134
|
+
|
|
135
|
+
print(f"✅ Saved embeddings: {npy_path}")
|
|
136
|
+
print(f" Size: {npy_path.stat().st_size / (1024*1024):.1f} MB")
|
|
137
|
+
|
|
138
|
+
# Create runtime loader info
|
|
139
|
+
info_path = output_dir / "embeddings_info.json"
|
|
140
|
+
info = {
|
|
141
|
+
"model": "all-MiniLM-L6-v2",
|
|
142
|
+
"dimension": 384,
|
|
143
|
+
"intents": list(embeddings_data.keys()),
|
|
144
|
+
"total_examples": total_examples,
|
|
145
|
+
"timestamp": str(Path(__file__).stat().st_mtime),
|
|
146
|
+
"note": "Pre-computed offline. At runtime, load with numpy (no torch needed)."
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
with open(info_path, 'w') as f:
|
|
150
|
+
json.dump(info, f, indent=2)
|
|
151
|
+
|
|
152
|
+
print(f"✅ Saved info: {info_path}")
|
|
153
|
+
|
|
154
|
+
print("\n" + "="*70)
|
|
155
|
+
print("🎉 Embeddings Generated Successfully!")
|
|
156
|
+
print("="*70)
|
|
157
|
+
print("\nUsage:")
|
|
158
|
+
print(" from semantic_matcher import SemanticMatcher")
|
|
159
|
+
print(" matcher = SemanticMatcher() # Loads embeddings automatically")
|
|
160
|
+
print(" intent, conf = matcher.find_similar_intent(request, keyword_scores)")
|
|
161
|
+
print()
|
|
162
|
+
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
success = generate_embeddings()
|
|
168
|
+
sys.exit(0 if success else 1)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QuickTriage script for aws-troubleshooter
|
|
3
|
+
# Provides a minimal health snapshot for AWS EKS clusters and supporting services.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
REGION="${AWS_REGION:-${1:-us-east-1}}"
|
|
8
|
+
CLUSTER="${EKS_CLUSTER:-${2:-}}"
|
|
9
|
+
VPC_ID="${VPC_ID:-${3:-}}"
|
|
10
|
+
|
|
11
|
+
info() {
|
|
12
|
+
printf '[quicktriage] %s\n' "$*"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
run_cmd() {
|
|
16
|
+
local description="$1"
|
|
17
|
+
shift
|
|
18
|
+
|
|
19
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
20
|
+
info "Skipping ${description} (command $1 not available)"
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
info "$description"
|
|
25
|
+
"$@" || info "Command failed: $*"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
info "Starting AWS quick triage (region=${REGION}, cluster=${CLUSTER:-unset})"
|
|
29
|
+
|
|
30
|
+
AWS_ARGS=(--region "$REGION")
|
|
31
|
+
|
|
32
|
+
if [[ -n "$CLUSTER" ]]; then
|
|
33
|
+
run_cmd "aws eks describe-cluster ${CLUSTER}" aws eks describe-cluster "${AWS_ARGS[@]}" --name "$CLUSTER" --query "cluster.{name:name,status:status,endpoint:endpoint,version:version}" --output table
|
|
34
|
+
run_cmd "kubectl get nodes (short)" kubectl get nodes -o wide
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
run_cmd "aws elbv2 describe-target-health (summary)" aws elbv2 describe-target-health "${AWS_ARGS[@]}" --target-group-arn "${TARGET_GROUP_ARN:-}" || info "Set TARGET_GROUP_ARN to include ALB status in triage."
|
|
38
|
+
|
|
39
|
+
run_cmd "aws cloudwatch describe-alarms (ALARM state)" aws cloudwatch describe-alarms "${AWS_ARGS[@]}" --state-value ALARM --max-items 10 --query "MetricAlarms[*].{Name:AlarmName,State:StateValue}" --output table
|
|
40
|
+
|
|
41
|
+
if [[ -n "$VPC_ID" ]]; then
|
|
42
|
+
run_cmd "aws ec2 describe-vpc-endpoints ${VPC_ID}" aws ec2 describe-vpc-endpoints "${AWS_ARGS[@]}" --filters "Name=vpc-id,Values=${VPC_ID}" --query "VpcEndpoints[*].{ServiceName:ServiceName,State:State}" --output table
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
info "Quick triage completed. Investigate failing components by describing pods/services or reviewing IAM/VPC configuration as next steps."
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QuickTriage script for devops-developer
|
|
3
|
+
# Checks repository hygiene, linting, and test discoverability quickly.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
WORKDIR="${1:-.}"
|
|
8
|
+
LINT_CMD="${LINT_CMD:-npm run lint -- --max-warnings=0}"
|
|
9
|
+
TEST_DISCOVERY_CMD="${TEST_DISCOVERY_CMD:-npm run test -- --watchAll=false --listTests}"
|
|
10
|
+
|
|
11
|
+
info() {
|
|
12
|
+
printf '[quicktriage] %s\n' "$*"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
run_in_repo() {
|
|
16
|
+
local description="$1"
|
|
17
|
+
shift
|
|
18
|
+
|
|
19
|
+
info "$description"
|
|
20
|
+
(cd "$WORKDIR" && eval "$*") || info "Command failed: $*"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
info "Starting devops quick triage (workdir=${WORKDIR})"
|
|
24
|
+
|
|
25
|
+
run_in_repo "git status --short" "git status --short"
|
|
26
|
+
|
|
27
|
+
if command -v npm >/dev/null 2>&1 || command -v pnpm >/dev/null 2>&1; then
|
|
28
|
+
run_in_repo "Lint check" "$LINT_CMD"
|
|
29
|
+
run_in_repo "Test discovery" "$TEST_DISCOVERY_CMD"
|
|
30
|
+
else
|
|
31
|
+
info "Skipping lint/test (npm/pnpm not available)"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [ -f "${WORKDIR}/package.json" ]; then
|
|
35
|
+
run_in_repo "npm audit --production (summary)" "npm audit --production --json | jq '.metadata.vulnerabilities' || npm audit --production"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
info "Quick triage completed. Use full test runs or profiling if deeper analysis is required."
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QuickTriage script for gcp-troubleshooter
|
|
3
|
+
# Provides a lightweight health snapshot for GKE clusters and key managed services.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
PROJECT="${GCP_PROJECT:-${1:-}}"
|
|
8
|
+
CLUSTER="${GKE_CLUSTER:-${2:-}}"
|
|
9
|
+
REGION="${GKE_REGION:-${3:-us-central1}}"
|
|
10
|
+
SQL_INSTANCE="${CLOUD_SQL_INSTANCE:-${4:-}}"
|
|
11
|
+
|
|
12
|
+
info() {
|
|
13
|
+
printf '[quicktriage] %s\n' "$*"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
run_cmd() {
|
|
17
|
+
local description="$1"
|
|
18
|
+
shift
|
|
19
|
+
|
|
20
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
21
|
+
info "Skipping ${description} (command $1 not available)"
|
|
22
|
+
return
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
info "$description"
|
|
26
|
+
"$@" || info "Command failed: $*"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
info "Starting GCP quick triage (project=${PROJECT:-unset}, cluster=${CLUSTER:-unset})"
|
|
30
|
+
|
|
31
|
+
if [[ -n "$PROJECT" ]]; then
|
|
32
|
+
gcloud config set project "$PROJECT" >/dev/null 2>&1 || true
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [[ -n "$CLUSTER" ]]; then
|
|
36
|
+
run_cmd "gcloud container clusters describe ${CLUSTER}" \
|
|
37
|
+
gcloud container clusters describe "$CLUSTER" --region "$REGION" --format="table(name,status,endpoint,releaseChannel.releaseChannel)"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
run_cmd "gcloud container clusters list (summary)" \
|
|
41
|
+
gcloud container clusters list --format="table(name,location,status,nodePools[0].status)"
|
|
42
|
+
|
|
43
|
+
if [[ -n "$SQL_INSTANCE" ]]; then
|
|
44
|
+
run_cmd "gcloud sql instances describe ${SQL_INSTANCE}" \
|
|
45
|
+
gcloud sql instances describe "$SQL_INSTANCE" --format="table(name,state,backendType,availabilityType,ipAddresses.ipAddress)"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
run_cmd "gcloud logging read (recent errors)" \
|
|
49
|
+
gcloud logging read 'severity>=ERROR' --limit=5 --format="table(timestamp, resource.labels.cluster_name, textPayload)"
|
|
50
|
+
|
|
51
|
+
info "Quick triage completed. Consider VPC connectivity, IAM bindings, or workload identity if issues persist."
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QuickTriage script for gitops-operator
|
|
3
|
+
# Provides a fast snapshot of workload health inside a Kubernetes cluster.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
NAMESPACE="${1:-tcm-non-prod}"
|
|
8
|
+
LABEL_SELECTOR="${2:-}"
|
|
9
|
+
|
|
10
|
+
info() {
|
|
11
|
+
printf '[quicktriage] %s\n' "$*"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run_cmd() {
|
|
15
|
+
local description="$1"
|
|
16
|
+
shift
|
|
17
|
+
|
|
18
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
19
|
+
info "Skipping ${description} (command $1 not available)"
|
|
20
|
+
return
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
info "$description"
|
|
24
|
+
"$@" || info "Command failed: $*"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
info "Starting gitops quick triage (namespace=${NAMESPACE:-all}, selector='${LABEL_SELECTOR}')"
|
|
28
|
+
|
|
29
|
+
KUBECTL_ARGS=(-o wide)
|
|
30
|
+
if [[ -n "$NAMESPACE" ]]; then
|
|
31
|
+
KUBECTL_ARGS=(-n "$NAMESPACE" "${KUBECTL_ARGS[@]}")
|
|
32
|
+
fi
|
|
33
|
+
if [[ -n "$LABEL_SELECTOR" ]]; then
|
|
34
|
+
KUBECTL_ARGS+=(-l "$LABEL_SELECTOR")
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
run_cmd "kubectl get pods" kubectl get pods "${KUBECTL_ARGS[@]}"
|
|
38
|
+
|
|
39
|
+
if [[ -n "$NAMESPACE" ]]; then
|
|
40
|
+
run_cmd "kubectl get deploy" kubectl get deploy -n "$NAMESPACE"
|
|
41
|
+
run_cmd "kubectl get helmrelease" kubectl get helmrelease -n "$NAMESPACE"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
run_cmd "flux get kustomizations" flux get kustomizations
|
|
45
|
+
run_cmd "flux get helmreleases" flux get helmreleases -A
|
|
46
|
+
|
|
47
|
+
info "Quick triage completed. Recommended next steps: describe failing pods or inspect logs if issues were detected."
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# QuickTriage script for terraform-architect
|
|
3
|
+
# Performs fast validation checks on Terraform/Terragrunt directories.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
TARGET_DIR="${1:-.}"
|
|
8
|
+
USE_TERRAGRUNT="${USE_TERRAGRUNT:-false}"
|
|
9
|
+
|
|
10
|
+
info() {
|
|
11
|
+
printf '[quicktriage] %s\n' "$*"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run_cmd() {
|
|
15
|
+
local description="$1"
|
|
16
|
+
shift
|
|
17
|
+
|
|
18
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
19
|
+
info "Skipping ${description} (command $1 not available)"
|
|
20
|
+
return
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
info "$description"
|
|
24
|
+
(cd "$TARGET_DIR" && "$@") || info "Command failed: $*"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
info "Starting Terraform quick triage (dir=${TARGET_DIR}, terragrunt=${USE_TERRAGRUNT})"
|
|
28
|
+
|
|
29
|
+
if [[ "${USE_TERRAGRUNT}" == "true" ]]; then
|
|
30
|
+
run_cmd "terragrunt fmt -check" terragrunt fmt -check
|
|
31
|
+
run_cmd "terragrunt validate" terragrunt validate
|
|
32
|
+
run_cmd "terragrunt plan (detailed exit code)" terragrunt plan -lock=false -detailed-exitcode || true
|
|
33
|
+
else
|
|
34
|
+
run_cmd "terraform fmt -check" terraform fmt -check
|
|
35
|
+
run_cmd "terraform init -backend=false" terraform init -backend=false
|
|
36
|
+
run_cmd "terraform validate" terraform validate
|
|
37
|
+
run_cmd "terraform plan (detailed exit code)" terraform plan -lock=false -refresh=false -detailed-exitcode || true
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
info "Quick triage completed. Exit code 1 on plan indicates drift; review the plan output if printed."
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Semantic matching using pre-computed embeddings
|
|
3
|
+
|
|
4
|
+
CRITICAL: This module does NOT require torch at runtime!
|
|
5
|
+
- Embeddings are pre-computed offline
|
|
6
|
+
- At runtime, we only load numpy + json
|
|
7
|
+
- Similarity is calculated with scipy/sklearn
|
|
8
|
+
|
|
9
|
+
Week 2 Addition
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Tuple, List, Optional, Dict, Any
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SemanticMatcher:
|
|
21
|
+
"""
|
|
22
|
+
Match requests to intents using pre-computed embeddings
|
|
23
|
+
|
|
24
|
+
- Loads pre-computed embeddings from .npy/.json
|
|
25
|
+
- Calculates similarity using numpy only
|
|
26
|
+
- No torch/transformers needed at runtime
|
|
27
|
+
- Provides fallback to keyword scores
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, embeddings_dir: Optional[Path] = None):
|
|
31
|
+
"""
|
|
32
|
+
Initialize semantic matcher
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
embeddings_dir: Directory containing embeddings (defaults to .claude/configs/)
|
|
36
|
+
"""
|
|
37
|
+
if embeddings_dir is None:
|
|
38
|
+
embeddings_dir = Path(__file__).parent.parent / "configs"
|
|
39
|
+
|
|
40
|
+
self.embeddings_dir = embeddings_dir
|
|
41
|
+
self.embeddings: Dict[str, Any] = {}
|
|
42
|
+
self.metadata: Dict[str, Any] = {}
|
|
43
|
+
self.available = False
|
|
44
|
+
|
|
45
|
+
self._load_embeddings()
|
|
46
|
+
|
|
47
|
+
def _load_embeddings(self):
|
|
48
|
+
"""Load pre-computed embeddings from JSON"""
|
|
49
|
+
json_path = self.embeddings_dir / "intent_embeddings.json"
|
|
50
|
+
|
|
51
|
+
if not json_path.exists():
|
|
52
|
+
logger.warning(
|
|
53
|
+
f"⚠️ Embeddings not found at {json_path}. "
|
|
54
|
+
"Run: python3 .claude/tools/generate_embeddings.py"
|
|
55
|
+
)
|
|
56
|
+
self.available = False
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
with open(json_path, 'r', encoding='utf-8') as f:
|
|
61
|
+
data = json.load(f)
|
|
62
|
+
|
|
63
|
+
# Convert lists back to numpy arrays
|
|
64
|
+
import numpy as np
|
|
65
|
+
|
|
66
|
+
for intent, info in data.items():
|
|
67
|
+
self.embeddings[intent] = {
|
|
68
|
+
"embedding": np.array(info["embedding"]),
|
|
69
|
+
"examples": info["examples"],
|
|
70
|
+
"dimension": info.get("dimension", 384)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
self.available = True
|
|
74
|
+
logger.info(f"✅ Loaded {len(self.embeddings)} intent embeddings")
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(f"Error loading embeddings: {e}")
|
|
78
|
+
self.available = False
|
|
79
|
+
|
|
80
|
+
def find_similar_intent(
|
|
81
|
+
self,
|
|
82
|
+
text: str,
|
|
83
|
+
keyword_scores: Dict[str, float]
|
|
84
|
+
) -> Tuple[Optional[str], float]:
|
|
85
|
+
"""
|
|
86
|
+
Find most similar intent combining keywords + embeddings
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
text: User request
|
|
90
|
+
keyword_scores: Scores from keyword matching {intent: score}
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
(best_intent, confidence)
|
|
94
|
+
|
|
95
|
+
Strategy:
|
|
96
|
+
1. If embeddings available, calculate text embedding (via TF-IDF approximation)
|
|
97
|
+
2. Find similarity to each intent embedding
|
|
98
|
+
3. Combine with keyword scores
|
|
99
|
+
4. Return best match
|
|
100
|
+
"""
|
|
101
|
+
if not keyword_scores:
|
|
102
|
+
return None, 0.0
|
|
103
|
+
|
|
104
|
+
# If embeddings not available, use keyword scores as primary
|
|
105
|
+
if not self.available:
|
|
106
|
+
logger.debug("Embeddings not available, using keyword scores only")
|
|
107
|
+
best_intent = max(keyword_scores, key=keyword_scores.get)
|
|
108
|
+
confidence = keyword_scores[best_intent]
|
|
109
|
+
return best_intent, confidence
|
|
110
|
+
|
|
111
|
+
# If embeddings available, enhance keyword scores with embedding similarity
|
|
112
|
+
try:
|
|
113
|
+
import numpy as np
|
|
114
|
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
115
|
+
|
|
116
|
+
# Create TF-IDF approximation of text embedding
|
|
117
|
+
# (lightweight alternative to transformer embeddings)
|
|
118
|
+
all_examples = []
|
|
119
|
+
intent_map = []
|
|
120
|
+
|
|
121
|
+
for intent, info in self.embeddings.items():
|
|
122
|
+
all_examples.extend(info["examples"])
|
|
123
|
+
intent_map.extend([intent] * len(info["examples"]))
|
|
124
|
+
|
|
125
|
+
all_examples.append(text) # Add query at end
|
|
126
|
+
|
|
127
|
+
# Vectorize
|
|
128
|
+
vectorizer = TfidfVectorizer(
|
|
129
|
+
analyzer='char',
|
|
130
|
+
ngram_range=(2, 3),
|
|
131
|
+
max_features=100
|
|
132
|
+
)
|
|
133
|
+
tfidf_matrix = vectorizer.fit_transform(all_examples)
|
|
134
|
+
|
|
135
|
+
# Get query vector (last row)
|
|
136
|
+
query_vector = tfidf_matrix[-1].toarray().flatten()
|
|
137
|
+
|
|
138
|
+
# Calculate similarities to each intent
|
|
139
|
+
embedding_scores = {}
|
|
140
|
+
|
|
141
|
+
for intent in self.embeddings.keys():
|
|
142
|
+
# Get example vectors for this intent
|
|
143
|
+
example_indices = [i for i, x in enumerate(intent_map) if x == intent]
|
|
144
|
+
example_vectors = tfidf_matrix[example_indices].toarray()
|
|
145
|
+
|
|
146
|
+
# Calculate mean similarity to examples
|
|
147
|
+
similarities = []
|
|
148
|
+
for example_vec in example_vectors:
|
|
149
|
+
# Cosine similarity
|
|
150
|
+
dot = np.dot(query_vector, example_vec)
|
|
151
|
+
norm1 = np.linalg.norm(query_vector)
|
|
152
|
+
norm2 = np.linalg.norm(example_vec)
|
|
153
|
+
if norm1 > 0 and norm2 > 0:
|
|
154
|
+
similarity = dot / (norm1 * norm2)
|
|
155
|
+
similarities.append(similarity)
|
|
156
|
+
|
|
157
|
+
# Mean similarity for this intent
|
|
158
|
+
if similarities:
|
|
159
|
+
embedding_scores[intent] = np.mean(similarities)
|
|
160
|
+
else:
|
|
161
|
+
embedding_scores[intent] = 0.0
|
|
162
|
+
|
|
163
|
+
# Combine keyword scores (70%) + embedding scores (30%)
|
|
164
|
+
combined_scores = {}
|
|
165
|
+
for intent in keyword_scores.keys():
|
|
166
|
+
kw_score = keyword_scores[intent]
|
|
167
|
+
emb_score = embedding_scores.get(intent, 0.0)
|
|
168
|
+
|
|
169
|
+
# Normalize both to 0-1
|
|
170
|
+
kw_norm = min(kw_score / 5.0, 1.0) # keyword scores are ~0-5
|
|
171
|
+
emb_norm = max(0.0, min(emb_score, 1.0)) # embedding scores already 0-1
|
|
172
|
+
|
|
173
|
+
# Weighted combination
|
|
174
|
+
combined = (kw_norm * 0.7) + (emb_norm * 0.3)
|
|
175
|
+
combined_scores[intent] = combined
|
|
176
|
+
|
|
177
|
+
# Select best
|
|
178
|
+
best_intent = max(combined_scores, key=combined_scores.get)
|
|
179
|
+
confidence = combined_scores[best_intent]
|
|
180
|
+
|
|
181
|
+
logger.debug(
|
|
182
|
+
f"Combined scores: {best_intent} = {confidence:.3f} "
|
|
183
|
+
f"(kw={keyword_scores.get(best_intent, 0):.2f}, "
|
|
184
|
+
f"emb={embedding_scores.get(best_intent, 0):.3f})"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
return best_intent, confidence
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
logger.warning(f"Error in embedding similarity: {e}")
|
|
191
|
+
# Fallback to keyword scores
|
|
192
|
+
best_intent = max(keyword_scores, key=keyword_scores.get)
|
|
193
|
+
confidence = keyword_scores[best_intent]
|
|
194
|
+
return best_intent, confidence
|
|
195
|
+
|
|
196
|
+
def get_intent_examples(self, intent: str) -> List[str]:
|
|
197
|
+
"""Get example requests for an intent"""
|
|
198
|
+
if intent in self.embeddings:
|
|
199
|
+
return self.embeddings[intent]["examples"]
|
|
200
|
+
return []
|
|
201
|
+
|
|
202
|
+
def list_intents(self) -> List[str]:
|
|
203
|
+
"""List all available intents"""
|
|
204
|
+
return list(self.embeddings.keys())
|
|
205
|
+
|
|
206
|
+
def is_available(self) -> bool:
|
|
207
|
+
"""Check if embeddings are loaded"""
|
|
208
|
+
return self.available
|
|
209
|
+
|
|
210
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
211
|
+
"""Get statistics about loaded embeddings"""
|
|
212
|
+
return {
|
|
213
|
+
"available": self.available,
|
|
214
|
+
"intents": len(self.embeddings),
|
|
215
|
+
"total_examples": sum(
|
|
216
|
+
len(info["examples"]) for info in self.embeddings.values()
|
|
217
|
+
),
|
|
218
|
+
"embedding_dimension": next(
|
|
219
|
+
(info["dimension"] for info in self.embeddings.values()),
|
|
220
|
+
None
|
|
221
|
+
)
|
|
222
|
+
}
|