chore: auto-commit (799 archivos)
- .claude/CLAUDE.md - .claude/commands/subagentes.md - .claude/rules/INDEX.md - .mcp.json - bash/functions/cybersecurity/analyze_dns.md - bash/functions/cybersecurity/audit_http_headers.md - bash/functions/cybersecurity/audit_ssh_config.md - bash/functions/cybersecurity/check_firewall.md - bash/functions/cybersecurity/detect_suspicious_users.md - bash/functions/cybersecurity/encrypt_file.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,10 @@ func cmdDoctor(args []string) {
|
||||
doctorML(r, jsonOut)
|
||||
case "vaults":
|
||||
doctorVaults(r, jsonOut)
|
||||
case "copied-code":
|
||||
doctorCopiedCode(r, jsonOut)
|
||||
case "capabilities":
|
||||
doctorCapabilities(r, jsonOut)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown doctor subcommand: %s\n", sub)
|
||||
doctorUsage()
|
||||
@@ -71,6 +75,8 @@ Subcommands:
|
||||
cpp-apps Conformidad de apps C++ con cpp/PATTERNS.md (cfg.about, dockspace, menubar)
|
||||
ml Entorno ML: GPUs NVIDIA, CUDA toolkit, venv Python, paquetes torch/diffusers, CLIs y vault
|
||||
vaults Salud de vaults: directorio, layout, índice, staleness, drift
|
||||
copied-code Detecta cuerpos de funcion del registry copiados en apps sin import (issue 0085k)
|
||||
capabilities Drift entre docs/capabilities/INDEX.md, tags de funciones, y paginas <grupo>.md (issue 0086)
|
||||
|
||||
Flags:
|
||||
--json Salida JSON (para scripting/agentes)`)
|
||||
@@ -119,6 +125,11 @@ func doctorAll(root string, jsonOut bool) {
|
||||
} else {
|
||||
all["vaults_error"] = err.Error()
|
||||
}
|
||||
if v, err := infra.AuditCapabilityGroups(root); err == nil {
|
||||
all["capabilities"] = v
|
||||
} else {
|
||||
all["capabilities_error"] = err.Error()
|
||||
}
|
||||
emit(all)
|
||||
return
|
||||
}
|
||||
@@ -139,6 +150,8 @@ func doctorAll(root string, jsonOut bool) {
|
||||
doctorML(root, false)
|
||||
fmt.Println("\n=== Vaults ===")
|
||||
doctorVaults(root, false)
|
||||
fmt.Println("\n=== Capability groups ===")
|
||||
doctorCapabilities(root, false)
|
||||
}
|
||||
|
||||
func doctorCppApps(root string, jsonOut bool) {
|
||||
@@ -383,3 +396,62 @@ func emit(v any) {
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
func doctorCapabilities(root string, jsonOut bool) {
|
||||
audits, err := infra.AuditCapabilityGroups(root)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if jsonOut {
|
||||
emit(audits)
|
||||
return
|
||||
}
|
||||
bad := 0
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "STATUS\tGROUP\tIN_INDEX\tDOC\tFN_COUNT\tISSUES")
|
||||
for _, a := range audits {
|
||||
status := "OK"
|
||||
issues := "-"
|
||||
if !a.OK {
|
||||
status = "FAIL"
|
||||
issues = strings.Join(a.Issues, "; ")
|
||||
bad++
|
||||
}
|
||||
doc := "no"
|
||||
if a.DocExists {
|
||||
doc = "yes"
|
||||
}
|
||||
inIdx := "no"
|
||||
if a.DeclaredInIndex {
|
||||
inIdx = "yes"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n", status, a.Group, inIdx, doc, a.FunctionCount, issues)
|
||||
}
|
||||
w.Flush()
|
||||
fmt.Printf("\n%d/%d capability groups healthy.\n", len(audits)-bad, len(audits))
|
||||
}
|
||||
|
||||
func doctorCopiedCode(root string, jsonOut bool) {
|
||||
entries, err := infra.AuditCopiedCode(root)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if jsonOut {
|
||||
emit(entries)
|
||||
return
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
fmt.Println("No copied-code detected (exact_copy match).")
|
||||
return
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "KIND\tSIMILARITY\tAPP_FILE\tAPP_FUNCTION\tREGISTRY_ID")
|
||||
for _, e := range entries {
|
||||
fmt.Fprintf(w, "%s\t%.2f\t%s\t%s\t%s\n",
|
||||
e.Kind, e.Similarity, e.AppFile, e.AppFunction, e.RegistryID)
|
||||
}
|
||||
w.Flush()
|
||||
fmt.Printf("\n%d suspected copy match(es).\n", len(entries))
|
||||
}
|
||||
|
||||
+49
-5
@@ -1,11 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
"fn-registry/registry"
|
||||
)
|
||||
@@ -58,13 +62,53 @@ func cmdRun(args []string) {
|
||||
|
||||
fmt.Fprintf(os.Stderr, "[fn run] %s (%s/%s) %s\n", fn.ID, fn.Lang, fn.Kind, strings.Join(passArgs, " "))
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
os.Exit(exitErr.ExitCode())
|
||||
t0 := time.Now()
|
||||
runErr := cmd.Run()
|
||||
durationMs := time.Since(t0).Milliseconds()
|
||||
|
||||
exitCode := 0
|
||||
errClass := ""
|
||||
if runErr != nil {
|
||||
if exitErr, ok := runErr.(*exec.ExitError); ok {
|
||||
exitCode = exitErr.ExitCode()
|
||||
errClass = fmt.Sprintf("exit_%d", exitCode)
|
||||
} else {
|
||||
exitCode = 1
|
||||
errClass = "spawn_error"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logFnRunTelemetry(registryRoot, fn.ID, durationMs, exitCode == 0, errClass)
|
||||
|
||||
if runErr != nil {
|
||||
if errClass == "spawn_error" {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", runErr)
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
// logFnRunTelemetry inserts a row into call_monitor.operations.db.calls if
|
||||
// the database is present. No-op silently otherwise — never blocks the run.
|
||||
func logFnRunTelemetry(registryRoot, functionID string, durationMs int64, success bool, errClass string) {
|
||||
callDB := filepath.Join(registryRoot, "projects", "fn_monitoring", "apps", "call_monitor", "operations.db")
|
||||
if _, err := os.Stat(callDB); err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := sql.Open("sqlite3", callDB+"?_journal_mode=WAL&_busy_timeout=100")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
sessionID := os.Getenv("CLAUDE_SESSION_ID")
|
||||
successInt := 0
|
||||
if success {
|
||||
successInt = 1
|
||||
}
|
||||
_, _ = conn.Exec(
|
||||
`INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts)
|
||||
VALUES (?, ?, 'fn_run_cli', '', ?, ?, ?, '', CAST(strftime('%s','now') AS INTEGER))`,
|
||||
sessionID, functionID, durationMs, successInt, errClass,
|
||||
)
|
||||
}
|
||||
|
||||
func resolveFunction(db *registry.DB, idOrName string) (*registry.Function, error) {
|
||||
|
||||
Reference in New Issue
Block a user