synapse-hub 2.7.0 → 3.0.1
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/.agents/agents/live-explorer.md +2 -0
- package/.agents/agents/pom-crafter.md +2 -0
- package/.agents/agents/test-runner.md +3 -0
- package/.agents/rules/communication-style.md +4 -0
- package/.agents/rules/context-engineering.md +1 -0
- package/.agents/rules/git-push-protocol.md +17 -0
- package/.agents/skills/agentless-qa-repair/examples/three_phase_repair_workflow.md +40 -0
- package/.agents/skills/antigravity-test-sentinel/examples/sprint_dod_enforcement.md +47 -0
- package/.agents/skills/auth-session-and-cookie-manager/examples/multi_role_auth_matrix.md +70 -0
- package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_playwright_pom.ts +52 -0
- package/.agents/skills/failure-autopsy-and-healing/examples/closed_loop_healing_walkthrough.md +48 -0
- package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
- package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +1 -1
- package/.agents/skills/mipro-prompt-compiler/examples/dspy_assertion_optimizer_case.md +46 -0
- package/.agents/skills/persistent-planning-files/SKILL.md +1 -1
- package/.agents/skills/qa-aci-interface/examples/surgical_code_window_walkthrough.md +40 -0
- package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
- package/.agents/skills/review-spec/SKILL.md +1 -1
- package/.agents/skills/review-spec/examples/pocock_grilling_decision_rubric.md +40 -0
- package/.agents/skills/senior-code-review/SKILL.md +1 -1
- package/.agents/skills/skillspector-security-guard/examples/audit_threat_vectors_report.json +35 -0
- package/.agents/skills/surgical-coding-principles/SKILL.md +1 -1
- package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
- package/.agents/skills/tot-exploratory-testing/examples/payment_gateway_tot_tree.json +62 -0
- package/.agents/skills/verification-before-completion/SKILL.md +2 -2
- package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +2 -2
- package/.agents/skills/web-agent-gym-benchmarking/examples/enterprise_tasks_suite.json +38 -0
- package/.agents/skills/web-vitals-and-performance-profiler/examples/playwright_cwv_profiler_fixture.ts +61 -0
- package/AGENTS.md +6 -4
- package/CLAUDE.md +2 -1
- package/README.en.md +71 -292
- package/README.md +69 -304
- package/action.yml +1 -1
- package/bin/agent-qa.js +2 -2
- package/docs/ARCHITECTURE.en.md +192 -0
- package/docs/ARCHITECTURE.md +135 -88
- package/docs/CI_CD_INTEGRATION.en.md +96 -0
- package/docs/CI_CD_INTEGRATION.md +24 -22
- package/docs/CLI_REFERENCE.en.md +174 -0
- package/docs/CLI_REFERENCE.md +120 -115
- package/docs/CONTRIBUTING.en.md +49 -0
- package/docs/CONTRIBUTING.md +26 -68
- package/docs/QUALITY_GATES.en.md +115 -0
- package/docs/QUALITY_GATES.md +115 -0
- package/docs/research/agent_skill_training_analysis.md +1 -1
- package/package.json +1 -1
- package/scripts/package_dist.py +2 -1
- package/skills_manifest.json +6 -6
- package/synapse.config.json +1 -1
- package/templates/ai_bridges/CLAUDE.md +2 -1
- package/templates/ai_bridges/copilot-instructions.md +2 -2
- package/tools/__init__.py +18 -1
- package/tools/_openapi_parser.py +1 -1
- package/tools/agent_canary.py +42 -30
- package/tools/agentless_patcher.py +2 -2
- package/tools/analyze_project.py +3 -5
- package/tools/api_drift_sentinel.py +4 -4
- package/tools/api_fuzzer.py +8 -8
- package/tools/auto_heal.py +4 -5
- package/tools/auto_heal_reflexion.py +10 -2
- package/tools/autogen_qa_broker.py +2 -2
- package/tools/browser_helpers.js +2 -2
- package/tools/browsergym_eval.py +2 -3
- package/tools/bug_reporter.py +11 -13
- package/tools/bump_version.py +90 -19
- package/tools/cache_resolver.py +78 -83
- package/tools/check_env.py +9 -1
- package/tools/check_version_sync.py +58 -4
- package/tools/ci_healing_hook.py +4 -6
- package/tools/cli.py +5 -7
- package/tools/config_decoupler.py +2 -3
- package/tools/contract_validator.py +141 -0
- package/tools/core/__init__.py +52 -0
- package/tools/count_tokens.py +22 -20
- package/tools/dashboard_generator.py +629 -1000
- package/tools/db_sandbox.py +87 -62
- package/tools/debt_harvester.py +3 -3
- package/tools/distill_log.py +3 -3
- package/tools/dspy_qa_signatures.py +3 -4
- package/tools/export_postman_collection.py +8 -8
- package/tools/flaky_detector.py +2 -2
- package/tools/fuzz_payloads.py +5 -5
- package/tools/generate_allure_report.py +1 -1
- package/tools/generate_jmx_plan.py +4 -4
- package/tools/gherkin_suite.py +1 -1
- package/tools/git_pre_commit_guard.py +2 -2
- package/tools/goldfish_review.py +2 -2
- package/tools/heal/__init__.py +28 -0
- package/tools/heal_pr_automator.py +6 -6
- package/tools/heuristic_locator_matcher.py +2 -2
- package/tools/infer_linter.py +143 -29
- package/tools/interview_story.py +0 -1
- package/tools/jtl_analyzer.py +11 -11
- package/tools/lint/__init__.py +30 -0
- package/tools/lint_ai_docs.py +42 -0
- package/tools/lookup_tc.py +1 -1
- package/tools/matrix_sharder.py +15 -13
- package/tools/mcp/__init__.py +16 -0
- package/tools/mcp_excel_server.py +2 -2
- package/tools/mcp_server.py +9 -7
- package/tools/md_to_xlsx.py +26 -26
- package/tools/micro_mutator.py +32 -15
- package/tools/network_mock_shield.py +27 -27
- package/tools/newman_analyzer.py +2 -3
- package/tools/omni_emitter.py +5 -8
- package/tools/omni_parser.py +5 -5
- package/tools/omni_transpiler.py +5 -5
- package/tools/output_formatter.py +19 -0
- package/tools/perf/__init__.py +24 -0
- package/tools/pinpoint_failure.py +1 -1
- package/tools/project_state.py +0 -2
- package/tools/qa_event_stream.py +1 -1
- package/tools/repo_map.py +8 -8
- package/tools/report/__init__.py +32 -0
- package/tools/reversible_sandbox.py +3 -4
- package/tools/safety_firewall.py +2 -2
- package/tools/senior_code_review.py +4 -3
- package/tools/setup_project.py +10 -12
- package/tools/skill_collision_guard.py +3 -3
- package/tools/skill_vault.py +94 -6
- package/tools/skillspector.py +10 -8
- package/tools/smart_visual_masker.py +4 -6
- package/tools/spec_drift_sentinel.py +1 -2
- package/tools/swe_qa_aci.py +2 -2
- package/tools/synapse_qa_bench.py +3 -3
- package/tools/sync_agent_rules.py +18 -3
- package/tools/telemetry_correlator.py +3 -2
- package/tools/test_impact.py +2 -1
- package/tools/test_memory_os.py +15 -5
- package/tools/textgrad_mutation_optimizer.py +3 -3
- package/tools/tms_sync_bridge.py +5 -5
- package/tools/tot_exploratory_tester.py +5 -6
- package/tools/traffic_session_converter.py +9 -9
- package/tools/universal_ir.py +2 -3
- package/tools/update_sentinel.py +6 -9
- package/tools/verify_all.py +12 -2
- package/tools/visual_diff_canvas.py +4 -4
- package/tools/visual_som_helper.py +15 -13
- package/tools/watch_mode.py +1 -1
- package/tools/wcag_audit.py +22 -1
- package/tools/welcome_page.py +5 -6
package/tools/cache_resolver.py
CHANGED
|
@@ -313,7 +313,7 @@ def init_db(seed=True, target_db=None):
|
|
|
313
313
|
try:
|
|
314
314
|
cursor.execute("ALTER TABLE healing_cache ADD COLUMN code_patch TEXT DEFAULT ''")
|
|
315
315
|
except sqlite3.OperationalError:
|
|
316
|
-
|
|
316
|
+
_ = True
|
|
317
317
|
|
|
318
318
|
# 2. Negative Memory Table (Blacklist of Failed Fix Attempts)
|
|
319
319
|
cursor.execute("""
|
|
@@ -362,30 +362,29 @@ def init_db(seed=True, target_db=None):
|
|
|
362
362
|
# Migrate existing solutions from local DB if initializing global DB for the first time
|
|
363
363
|
if current_db == GLOBAL_DB and os.path.exists(LOCAL_DB):
|
|
364
364
|
try:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
local_conn.close()
|
|
365
|
+
with get_db(LOCAL_DB) as local_conn:
|
|
366
|
+
rows = local_conn.execute("SELECT * FROM healing_cache").fetchall()
|
|
367
|
+
for r in rows:
|
|
368
|
+
cursor.execute("""
|
|
369
|
+
INSERT OR IGNORE INTO healing_cache
|
|
370
|
+
(error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
|
|
371
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
372
|
+
""", (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
|
|
373
|
+
conn.commit()
|
|
375
374
|
except (sqlite3.Error, OSError) as e:
|
|
376
375
|
sys.stderr.write(f"Notice: Local cache migration skipped: {e}\n")
|
|
377
376
|
|
|
378
377
|
if seed:
|
|
379
378
|
for s in SEED_SOLUTIONS:
|
|
380
379
|
cursor.execute("""
|
|
381
|
-
INSERT OR IGNORE INTO healing_cache
|
|
380
|
+
INSERT OR IGNORE INTO healing_cache
|
|
382
381
|
(error_signature, module_name, symptom, root_cause, groovy_patch)
|
|
383
382
|
VALUES (?, ?, ?, ?, ?)
|
|
384
383
|
""", (s["error_signature"], s["module_name"], s["symptom"], s["root_cause"], s["groovy_patch"]))
|
|
385
384
|
|
|
386
385
|
for f in SEED_FAILED_FIXES:
|
|
387
386
|
cursor.execute("""
|
|
388
|
-
SELECT COUNT(*) as cnt FROM failed_fixes
|
|
387
|
+
SELECT COUNT(*) as cnt FROM failed_fixes
|
|
389
388
|
WHERE error_signature = ? AND attempted_patch = ?
|
|
390
389
|
""", (f["error_signature"], f["attempted_patch"]))
|
|
391
390
|
if cursor.fetchone()["cnt"] == 0:
|
|
@@ -394,17 +393,17 @@ def init_db(seed=True, target_db=None):
|
|
|
394
393
|
VALUES (?, ?, ?, ?)
|
|
395
394
|
""", (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
|
|
396
395
|
|
|
397
|
-
for
|
|
396
|
+
for les in SEED_LESSONS_LEARNED:
|
|
398
397
|
cursor.execute("""
|
|
399
|
-
SELECT COUNT(*) as cnt FROM lessons_learned
|
|
398
|
+
SELECT COUNT(*) as cnt FROM lessons_learned
|
|
400
399
|
WHERE error_signature = ? AND page_context = ?
|
|
401
|
-
""", (
|
|
400
|
+
""", (les["error_signature"], les["page_context"]))
|
|
402
401
|
if cursor.fetchone()["cnt"] == 0:
|
|
403
402
|
cursor.execute("""
|
|
404
|
-
INSERT INTO lessons_learned
|
|
403
|
+
INSERT INTO lessons_learned
|
|
405
404
|
(page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary)
|
|
406
405
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
407
|
-
""", (
|
|
406
|
+
""", (les["page_context"], les["error_signature"], les["root_cause"], les["failed_attempt"], les["successful_fix"], les["lesson_summary"]))
|
|
408
407
|
conn.commit()
|
|
409
408
|
|
|
410
409
|
|
|
@@ -552,7 +551,7 @@ def get_failed_fixes(error_signature):
|
|
|
552
551
|
with get_db() as conn:
|
|
553
552
|
cursor = conn.cursor()
|
|
554
553
|
cursor.execute("""
|
|
555
|
-
SELECT * FROM failed_fixes
|
|
554
|
+
SELECT * FROM failed_fixes
|
|
556
555
|
WHERE error_signature = ? OR error_signature LIKE ?
|
|
557
556
|
ORDER BY created_at DESC
|
|
558
557
|
""", (error_signature, f"%{error_signature}%"))
|
|
@@ -587,7 +586,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
|
|
|
587
586
|
cursor = conn.cursor()
|
|
588
587
|
# 1. Exact match with module
|
|
589
588
|
cursor.execute("""
|
|
590
|
-
SELECT * FROM healing_cache
|
|
589
|
+
SELECT * FROM healing_cache
|
|
591
590
|
WHERE error_signature = ? AND module_name = ?
|
|
592
591
|
""", (error_signature, module_name))
|
|
593
592
|
row = cursor.fetchone()
|
|
@@ -596,7 +595,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
|
|
|
596
595
|
# 2. Fallback to GLOBAL match
|
|
597
596
|
if not row:
|
|
598
597
|
cursor.execute("""
|
|
599
|
-
SELECT * FROM healing_cache
|
|
598
|
+
SELECT * FROM healing_cache
|
|
600
599
|
WHERE error_signature = ? AND module_name = 'GLOBAL'
|
|
601
600
|
""", (error_signature,))
|
|
602
601
|
row = cursor.fetchone()
|
|
@@ -605,7 +604,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
|
|
|
605
604
|
# 3. Fuzzy search on symptom/error
|
|
606
605
|
if not row:
|
|
607
606
|
cursor.execute("""
|
|
608
|
-
SELECT * FROM healing_cache
|
|
607
|
+
SELECT * FROM healing_cache
|
|
609
608
|
WHERE error_signature LIKE ? OR symptom LIKE ?
|
|
610
609
|
""", (f"%{error_signature}%", f"%{error_signature}%"))
|
|
611
610
|
row = cursor.fetchone()
|
|
@@ -639,8 +638,8 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
|
|
|
639
638
|
|
|
640
639
|
if row:
|
|
641
640
|
cursor.execute("""
|
|
642
|
-
UPDATE healing_cache
|
|
643
|
-
SET hit_count = hit_count + 1
|
|
641
|
+
UPDATE healing_cache
|
|
642
|
+
SET hit_count = hit_count + 1
|
|
644
643
|
WHERE id = ?
|
|
645
644
|
""", (row["id"],))
|
|
646
645
|
conn.commit()
|
|
@@ -700,57 +699,53 @@ def sync_db():
|
|
|
700
699
|
init_db(seed=True, target_db=GLOBAL_DB)
|
|
701
700
|
DB_FILE = resolve_db_path(use_local=True)
|
|
702
701
|
init_db(seed=False, target_db=LOCAL_DB)
|
|
703
|
-
|
|
704
|
-
g_conn = get_db(GLOBAL_DB)
|
|
705
|
-
l_conn = get_db(LOCAL_DB)
|
|
706
|
-
|
|
707
|
-
# 1. Sync positive cache
|
|
708
|
-
g_rows = g_conn.execute("SELECT * FROM healing_cache").fetchall()
|
|
709
|
-
for r in g_rows:
|
|
710
|
-
l_conn.execute("""
|
|
711
|
-
INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
|
|
712
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
713
|
-
ON CONFLICT(error_signature, module_name) DO UPDATE SET
|
|
714
|
-
symptom = excluded.symptom,
|
|
715
|
-
root_cause = excluded.root_cause,
|
|
716
|
-
groovy_patch = excluded.groovy_patch
|
|
717
|
-
""", (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
|
|
718
|
-
l_conn.commit()
|
|
719
|
-
|
|
720
|
-
l_rows = l_conn.execute("SELECT * FROM healing_cache").fetchall()
|
|
721
|
-
for r in l_rows:
|
|
722
|
-
g_conn.execute("""
|
|
723
|
-
INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
|
|
724
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
725
|
-
ON CONFLICT(error_signature, module_name) DO UPDATE SET
|
|
726
|
-
symptom = excluded.symptom,
|
|
727
|
-
root_cause = excluded.root_cause,
|
|
728
|
-
groovy_patch = excluded.groovy_patch
|
|
729
|
-
""", (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
|
|
730
|
-
g_conn.commit()
|
|
731
|
-
|
|
732
|
-
# 2. Sync negative failed fixes
|
|
733
|
-
g_failed = g_conn.execute("SELECT * FROM failed_fixes").fetchall()
|
|
734
|
-
for f in g_failed:
|
|
735
|
-
l_conn.execute("""
|
|
736
|
-
INSERT OR IGNORE INTO failed_fixes (error_signature, attempted_patch, failure_reason, agent_role)
|
|
737
|
-
VALUES (?, ?, ?, ?)
|
|
738
|
-
""", (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
|
|
739
|
-
l_conn.commit()
|
|
740
|
-
|
|
741
|
-
# 3. Sync lessons learned
|
|
742
|
-
g_lessons = g_conn.execute("SELECT * FROM lessons_learned").fetchall()
|
|
743
|
-
for r in g_lessons:
|
|
744
|
-
l_conn.execute("""
|
|
745
|
-
INSERT OR IGNORE INTO lessons_learned
|
|
746
|
-
(page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary, hit_count)
|
|
747
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
748
|
-
""", (r["page_context"], r["error_signature"], r["root_cause"], r["failed_attempt"], r["successful_fix"], r["lesson_summary"], r["hit_count"]))
|
|
749
|
-
l_conn.commit()
|
|
750
702
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
703
|
+
with get_db(GLOBAL_DB) as g_conn, get_db(LOCAL_DB) as l_conn:
|
|
704
|
+
# 1. Sync positive cache
|
|
705
|
+
g_rows = g_conn.execute("SELECT * FROM healing_cache").fetchall()
|
|
706
|
+
for r in g_rows:
|
|
707
|
+
l_conn.execute("""
|
|
708
|
+
INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
|
|
709
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
710
|
+
ON CONFLICT(error_signature, module_name) DO UPDATE SET
|
|
711
|
+
symptom = excluded.symptom,
|
|
712
|
+
root_cause = excluded.root_cause,
|
|
713
|
+
groovy_patch = excluded.groovy_patch
|
|
714
|
+
""", (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
|
|
715
|
+
l_conn.commit()
|
|
716
|
+
|
|
717
|
+
l_rows = l_conn.execute("SELECT * FROM healing_cache").fetchall()
|
|
718
|
+
for r in l_rows:
|
|
719
|
+
g_conn.execute("""
|
|
720
|
+
INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
|
|
721
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
722
|
+
ON CONFLICT(error_signature, module_name) DO UPDATE SET
|
|
723
|
+
symptom = excluded.symptom,
|
|
724
|
+
root_cause = excluded.root_cause,
|
|
725
|
+
groovy_patch = excluded.groovy_patch
|
|
726
|
+
""", (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
|
|
727
|
+
g_conn.commit()
|
|
728
|
+
|
|
729
|
+
# 2. Sync negative failed fixes
|
|
730
|
+
g_failed = g_conn.execute("SELECT * FROM failed_fixes").fetchall()
|
|
731
|
+
for f in g_failed:
|
|
732
|
+
l_conn.execute("""
|
|
733
|
+
INSERT OR IGNORE INTO failed_fixes (error_signature, attempted_patch, failure_reason, agent_role)
|
|
734
|
+
VALUES (?, ?, ?, ?)
|
|
735
|
+
""", (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
|
|
736
|
+
l_conn.commit()
|
|
737
|
+
|
|
738
|
+
# 3. Sync lessons learned
|
|
739
|
+
g_lessons = g_conn.execute("SELECT * FROM lessons_learned").fetchall()
|
|
740
|
+
for r in g_lessons:
|
|
741
|
+
l_conn.execute("""
|
|
742
|
+
INSERT OR IGNORE INTO lessons_learned
|
|
743
|
+
(page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary, hit_count)
|
|
744
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
745
|
+
""", (r["page_context"], r["error_signature"], r["root_cause"], r["failed_attempt"], r["successful_fix"], r["lesson_summary"], r["hit_count"]))
|
|
746
|
+
l_conn.commit()
|
|
747
|
+
|
|
748
|
+
return len(g_rows)
|
|
754
749
|
|
|
755
750
|
|
|
756
751
|
@retry_on_locked()
|
|
@@ -769,7 +764,7 @@ def record_lesson(page_context, error_signature, root_cause, failed_attempt, suc
|
|
|
769
764
|
with get_db() as conn:
|
|
770
765
|
cursor = conn.cursor()
|
|
771
766
|
cursor.execute("""
|
|
772
|
-
INSERT INTO lessons_learned
|
|
767
|
+
INSERT INTO lessons_learned
|
|
773
768
|
(page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary)
|
|
774
769
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
775
770
|
""", (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary))
|
|
@@ -847,7 +842,7 @@ def get_flaky_telemetry(days=30, min_runs=2, threshold=0.15, target_db=None):
|
|
|
847
842
|
with get_db(current_db) as conn:
|
|
848
843
|
cursor = conn.cursor()
|
|
849
844
|
cursor.execute("""
|
|
850
|
-
SELECT
|
|
845
|
+
SELECT
|
|
851
846
|
test_id,
|
|
852
847
|
COUNT(*) as total_runs,
|
|
853
848
|
SUM(CASE WHEN status = 'PASS' THEN 1 ELSE 0 END) as pass_count,
|
|
@@ -1051,7 +1046,7 @@ def main():
|
|
|
1051
1046
|
if sol:
|
|
1052
1047
|
print("==================================================")
|
|
1053
1048
|
if sol.get("groovy_patch"):
|
|
1054
|
-
print(
|
|
1049
|
+
print("✅ [CACHE HIT - 0 TOKENS] Found pre-tested solution!")
|
|
1055
1050
|
print(f"Database Scope : {'LOCAL' if args.local else 'GLOBAL CENTRAL (~/.agent_memory)'}")
|
|
1056
1051
|
print(f"Match Strategy : {sol.get('match_type', 'EXACT')}")
|
|
1057
1052
|
print(f"Error Signature : {sol['error_signature']}")
|
|
@@ -1104,12 +1099,12 @@ def main():
|
|
|
1104
1099
|
if not lessons:
|
|
1105
1100
|
print(" (Zero relevant lessons in cache yet. Proceeding with clean reasoning.)")
|
|
1106
1101
|
else:
|
|
1107
|
-
for idx,
|
|
1108
|
-
print(f"📌 [Lesson {idx}] Context: {
|
|
1109
|
-
print(f" 💡 Rule: {
|
|
1110
|
-
if
|
|
1111
|
-
print(f" ⚠️ Anti-Pattern: {
|
|
1112
|
-
print(f" ✅ Proven Fix : {
|
|
1102
|
+
for idx, les in enumerate(lessons, 1):
|
|
1103
|
+
print(f"📌 [Lesson {idx}] Context: {les['page_context']} | Trigger: {les['error_signature']}")
|
|
1104
|
+
print(f" 💡 Rule: {les['lesson_summary']}")
|
|
1105
|
+
if les.get("failed_attempt") or les.get("successful_fix"):
|
|
1106
|
+
print(f" ⚠️ Anti-Pattern: {les.get('failed_attempt') or 'N/A'}")
|
|
1107
|
+
print(f" ✅ Proven Fix : {les.get('successful_fix') or 'N/A'}")
|
|
1113
1108
|
print("==================================================")
|
|
1114
1109
|
|
|
1115
1110
|
elif args.command == "record-telemetry":
|
package/tools/check_env.py
CHANGED
|
@@ -12,7 +12,6 @@ Autonomously detects Target Project or QA Hub (CLI, CWD, ENV).
|
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
14
|
import sys
|
|
15
|
-
import shutil
|
|
16
15
|
import shlex
|
|
17
16
|
import platform
|
|
18
17
|
import subprocess
|
|
@@ -101,12 +100,14 @@ class DiagnosticRunner:
|
|
|
101
100
|
return False, str(e)
|
|
102
101
|
|
|
103
102
|
def check_os(self):
|
|
103
|
+
"""Verifies operating system platform, kernel version, and architecture."""
|
|
104
104
|
os_name = platform.system()
|
|
105
105
|
os_ver = platform.release()
|
|
106
106
|
arch = platform.machine()
|
|
107
107
|
self.log_check("Operating System", "PASS", f"{os_name} {os_ver} ({arch})")
|
|
108
108
|
|
|
109
109
|
def check_chrome(self):
|
|
110
|
+
"""Checks for Google Chrome, Chromium, Brave, or Playwright browser runtimes."""
|
|
110
111
|
# 1. Search via 'where' (Windows) or 'which' (Unix)
|
|
111
112
|
cmd = "where chrome" if platform.system() == "Windows" else "which google-chrome google-chrome-stable chromium chromium-browser"
|
|
112
113
|
found, out = self.run_command(cmd)
|
|
@@ -149,6 +150,7 @@ class DiagnosticRunner:
|
|
|
149
150
|
"Download and install Google Chrome from https://www.google.com/chrome/")
|
|
150
151
|
|
|
151
152
|
def check_java(self):
|
|
153
|
+
"""Verifies presence of Java JRE/JDK runtime via CLI or JAVA_HOME."""
|
|
152
154
|
found, out = self.run_command("java -version")
|
|
153
155
|
if found or "version" in out:
|
|
154
156
|
ver = out.splitlines()[0] if out else "Installed"
|
|
@@ -162,6 +164,7 @@ class DiagnosticRunner:
|
|
|
162
164
|
"Katalon includes embedded JRE, but for Gradle/Jenkins CLI, install OpenJDK 17.")
|
|
163
165
|
|
|
164
166
|
def check_katalon(self):
|
|
167
|
+
"""Verifies Katalon Studio / Katalon Runtime Engine installation if applicable."""
|
|
165
168
|
# Context-aware: check if current target is actually a Katalon project
|
|
166
169
|
is_katalon_project = (
|
|
167
170
|
any(self.target_dir.glob("*.prj"))
|
|
@@ -196,6 +199,7 @@ class DiagnosticRunner:
|
|
|
196
199
|
self.log_check("Katalon Studio IDE", "PASS", "Not installed (Optional - not required for Playwright/Cypress/Pytest)")
|
|
197
200
|
|
|
198
201
|
def check_node_allure(self):
|
|
202
|
+
"""Verifies presence of Node.js runtime for test runners and Allure reports."""
|
|
199
203
|
found_node, node_ver = self.run_command("node -v")
|
|
200
204
|
if found_node:
|
|
201
205
|
self.log_check("Node.js Runtime", "PASS", f"Node.js {node_ver}")
|
|
@@ -204,6 +208,7 @@ class DiagnosticRunner:
|
|
|
204
208
|
"Install Node.js from https://nodejs.org/ to execute './tools/generate_allure_report.sh serve'")
|
|
205
209
|
|
|
206
210
|
def check_python_runner(self):
|
|
211
|
+
"""Verifies active Python environment and optional virtual environment presence."""
|
|
207
212
|
py_ver = sys.version.split()[0]
|
|
208
213
|
venv_path = self.target_dir / ".venv"
|
|
209
214
|
|
|
@@ -213,6 +218,7 @@ class DiagnosticRunner:
|
|
|
213
218
|
self.log_check("Python Virtual Environment", "PASS", f"Python {py_ver} (Optional: create .venv if using auxiliary runners)")
|
|
214
219
|
|
|
215
220
|
def check_project_structure(self):
|
|
221
|
+
"""Audits folder layout and configuration integrity for Hub or Spoke projects."""
|
|
216
222
|
# 1. QA Hub Repository Check
|
|
217
223
|
is_hub = (self.target_dir / "skills_manifest.json").exists() or (self.target_dir / ".agents" / "skills").is_dir()
|
|
218
224
|
if is_hub:
|
|
@@ -301,6 +307,7 @@ class DiagnosticRunner:
|
|
|
301
307
|
"Run 'agent-qa setup .' to initialize Clean POM / Playwright configuration for this project.")
|
|
302
308
|
|
|
303
309
|
def check_network(self):
|
|
310
|
+
"""Audits AUT network reachability via BASE_URL configured in environment."""
|
|
304
311
|
env_file = self.target_dir / ".env"
|
|
305
312
|
if env_file.exists():
|
|
306
313
|
try:
|
|
@@ -345,6 +352,7 @@ class DiagnosticRunner:
|
|
|
345
352
|
"Check internet connection or enable VPN if corporate network required.")
|
|
346
353
|
|
|
347
354
|
def run_all(self):
|
|
355
|
+
"""Runs the complete diagnostic suite and returns 0 if all prerequisites pass."""
|
|
348
356
|
print(f"\n{BOLD}{'='*70}{RESET}")
|
|
349
357
|
print(f"{BOLD}{CYAN} ENTERPRISE AUTOMATION - ENVIRONMENT & ONBOARDING DIAGNOSTIC{RESET}")
|
|
350
358
|
print(f"{BOLD}{'='*70}{RESET}")
|
|
@@ -102,7 +102,7 @@ def extract_versions(repo_root: str) -> Dict[str, str]:
|
|
|
102
102
|
if m:
|
|
103
103
|
versions["tools/mcp_excel_server.py"] = m.group(1)
|
|
104
104
|
|
|
105
|
-
# 10. docs/ARCHITECTURE.md
|
|
105
|
+
# 10. docs/ARCHITECTURE.md & docs/ARCHITECTURE.en.md
|
|
106
106
|
arch_path = os.path.join(repo_root, "docs", "ARCHITECTURE.md")
|
|
107
107
|
if os.path.exists(arch_path):
|
|
108
108
|
with open(arch_path, "r", encoding="utf-8") as f:
|
|
@@ -110,6 +110,50 @@ def extract_versions(repo_root: str) -> Dict[str, str]:
|
|
|
110
110
|
m = re.search(r'>\s*\*\*Version:\*\*\s*`([^`]+)`', content)
|
|
111
111
|
if m:
|
|
112
112
|
versions["docs/ARCHITECTURE.md"] = m.group(1)
|
|
113
|
+
arch_en_path = os.path.join(repo_root, "docs", "ARCHITECTURE.en.md")
|
|
114
|
+
if os.path.exists(arch_en_path):
|
|
115
|
+
with open(arch_en_path, "r", encoding="utf-8") as f:
|
|
116
|
+
content = f.read()
|
|
117
|
+
m = re.search(r'>\s*\*\*Version:\*\*\s*`([^`]+)`', content)
|
|
118
|
+
if m:
|
|
119
|
+
versions["docs/ARCHITECTURE.en.md"] = m.group(1)
|
|
120
|
+
|
|
121
|
+
# 11. README.md
|
|
122
|
+
readme_path = os.path.join(repo_root, "README.md")
|
|
123
|
+
if os.path.exists(readme_path):
|
|
124
|
+
with open(readme_path, "r", encoding="utf-8") as f:
|
|
125
|
+
content = f.read()
|
|
126
|
+
m_badge = re.search(r'Phiên bản(?: Hub)?:\*\*\s*`v([0-9]+\.[0-9]+\.[0-9]+)', content)
|
|
127
|
+
m_rev = re.search(r'rev:\s*v([0-9]+\.[0-9]+\.[0-9]+)', content)
|
|
128
|
+
if m_badge:
|
|
129
|
+
versions["README.md"] = m_badge.group(1)
|
|
130
|
+
elif m_rev:
|
|
131
|
+
versions["README.md"] = m_rev.group(1)
|
|
132
|
+
if m_badge and m_rev:
|
|
133
|
+
versions["README.md (rev)"] = m_rev.group(1)
|
|
134
|
+
|
|
135
|
+
# 12. README.en.md
|
|
136
|
+
readme_en_path = os.path.join(repo_root, "README.en.md")
|
|
137
|
+
if os.path.exists(readme_en_path):
|
|
138
|
+
with open(readme_en_path, "r", encoding="utf-8") as f:
|
|
139
|
+
content = f.read()
|
|
140
|
+
m_badge = re.search(r'(?:Hub )?Version:\*\*\s*`v([0-9]+\.[0-9]+\.[0-9]+)', content)
|
|
141
|
+
m_rev = re.search(r'rev:\s*v([0-9]+\.[0-9]+\.[0-9]+)', content)
|
|
142
|
+
if m_badge:
|
|
143
|
+
versions["README.en.md"] = m_badge.group(1)
|
|
144
|
+
elif m_rev:
|
|
145
|
+
versions["README.en.md"] = m_rev.group(1)
|
|
146
|
+
if m_badge and m_rev:
|
|
147
|
+
versions["README.en.md (rev)"] = m_rev.group(1)
|
|
148
|
+
|
|
149
|
+
# 13. SECURITY.md
|
|
150
|
+
sec_path = os.path.join(repo_root, "SECURITY.md")
|
|
151
|
+
if os.path.exists(sec_path):
|
|
152
|
+
with open(sec_path, "r", encoding="utf-8") as f:
|
|
153
|
+
content = f.read()
|
|
154
|
+
m = re.search(r'\|\s*`([0-9]+\.[0-9]+\.x)`\s*\|\s*✅', content)
|
|
155
|
+
if m:
|
|
156
|
+
versions["SECURITY.md"] = m.group(1)
|
|
113
157
|
|
|
114
158
|
return versions
|
|
115
159
|
|
|
@@ -127,7 +171,15 @@ def check_sync(repo_root: str, expected_version: Optional[str] = None) -> Tuple[
|
|
|
127
171
|
if not target_ver:
|
|
128
172
|
target_ver = next(iter(versions.values()))
|
|
129
173
|
|
|
130
|
-
|
|
174
|
+
target_prefix = ".".join(target_ver.split(".")[:2]) + ".x"
|
|
175
|
+
mismatches = {}
|
|
176
|
+
for path, ver in versions.items():
|
|
177
|
+
if path == "SECURITY.md":
|
|
178
|
+
if ver != target_prefix:
|
|
179
|
+
mismatches[path] = ver
|
|
180
|
+
elif ver != target_ver:
|
|
181
|
+
mismatches[path] = ver
|
|
182
|
+
|
|
131
183
|
if mismatches:
|
|
132
184
|
return False, versions, target_ver
|
|
133
185
|
return True, versions, target_ver
|
|
@@ -151,10 +203,12 @@ def main() -> int:
|
|
|
151
203
|
print(f"Target Version: v{canonical}")
|
|
152
204
|
print("----------------------------------------------------------------------")
|
|
153
205
|
|
|
206
|
+
target_prefix = ".".join(canonical.split(".")[:2]) + ".x"
|
|
154
207
|
all_matched = True
|
|
155
208
|
for file_path, ver in sorted(versions.items()):
|
|
156
|
-
|
|
157
|
-
if
|
|
209
|
+
is_match = (ver == target_prefix) if file_path == "SECURITY.md" else (ver == canonical)
|
|
210
|
+
status = "[MATCH]" if is_match else "[MISMATCH]"
|
|
211
|
+
if not is_match:
|
|
158
212
|
all_matched = False
|
|
159
213
|
print(f" {status:<11} {file_path:<26} -> {ver}")
|
|
160
214
|
|
package/tools/ci_healing_hook.py
CHANGED
|
@@ -10,10 +10,8 @@ When a test fails due to a broken locator:
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import os
|
|
13
|
-
import sys
|
|
14
|
-
import json
|
|
15
13
|
import argparse
|
|
16
|
-
from typing import Dict, Any
|
|
14
|
+
from typing import Dict, Any
|
|
17
15
|
from datetime import datetime
|
|
18
16
|
|
|
19
17
|
|
|
@@ -35,11 +33,11 @@ class CIHealingHook:
|
|
|
35
33
|
"""
|
|
36
34
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
37
35
|
branch_name = f"auto-heal/{test_id.lower()}_{timestamp}"
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
pr_description = f"""## 🤖 Autonomous CI/CD Self-Healing Report
|
|
40
38
|
|
|
41
|
-
> **Target File:** `{file_path}`
|
|
42
|
-
> **Confidence Score:** `{confidence * 100:.1f}%`
|
|
39
|
+
> **Target File:** `{file_path}`
|
|
40
|
+
> **Confidence Score:** `{confidence * 100:.1f}%`
|
|
43
41
|
> **Timestamp:** `{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}`
|
|
44
42
|
|
|
45
43
|
### 📝 Summary of Changes
|
package/tools/cli.py
CHANGED
|
@@ -41,7 +41,7 @@ RESET = "\033[0m"
|
|
|
41
41
|
def cmd_version(args):
|
|
42
42
|
"""Displays the current hub version, skill catalog count, and license information."""
|
|
43
43
|
manifest_path = os.path.join(REPO_ROOT, "skills_manifest.json")
|
|
44
|
-
version = "
|
|
44
|
+
version = "3.0.1"
|
|
45
45
|
if os.path.exists(manifest_path):
|
|
46
46
|
try:
|
|
47
47
|
import json
|
|
@@ -145,7 +145,7 @@ def cmd_clean(args):
|
|
|
145
145
|
os.remove(os.path.join(root, f))
|
|
146
146
|
removed_count += 1
|
|
147
147
|
except OSError as err:
|
|
148
|
-
|
|
148
|
+
getattr(args, "verbose", False) and print(f"Notice: Could not remove {f}: {err}")
|
|
149
149
|
print(f"✅ Cleanup finished: {removed_count} build artifacts and caches removed.")
|
|
150
150
|
|
|
151
151
|
|
|
@@ -255,7 +255,6 @@ def cmd_transpile(args):
|
|
|
255
255
|
def cmd_omni_scan(args):
|
|
256
256
|
from omni_transpiler import scan_repository
|
|
257
257
|
inv = scan_repository(args.path)
|
|
258
|
-
import json
|
|
259
258
|
if getattr(args, "json", False):
|
|
260
259
|
print(json.dumps(inv, indent=2))
|
|
261
260
|
else:
|
|
@@ -542,21 +541,20 @@ def cmd_release(args):
|
|
|
542
541
|
|
|
543
542
|
def _hub_version():
|
|
544
543
|
manifest_path = os.path.join(REPO_ROOT, "skills_manifest.json")
|
|
545
|
-
version = "
|
|
544
|
+
version = "3.0.1"
|
|
546
545
|
if os.path.exists(manifest_path):
|
|
547
546
|
try:
|
|
548
547
|
import json
|
|
549
548
|
with open(manifest_path, "r", encoding="utf-8") as f:
|
|
550
549
|
version = json.load(f).get("version", version)
|
|
551
550
|
except (OSError, json.JSONDecodeError):
|
|
552
|
-
version = "
|
|
551
|
+
version = "3.0.1"
|
|
553
552
|
return version
|
|
554
553
|
|
|
555
554
|
|
|
556
555
|
class SynapseArgumentParser(argparse.ArgumentParser):
|
|
557
556
|
"""Enhanced ArgumentParser providing fuzzy Did-You-Mean suggestions on command typos."""
|
|
558
557
|
def error(self, message):
|
|
559
|
-
import difflib
|
|
560
558
|
m = re.search(r"invalid choice: '([^']+)'", message)
|
|
561
559
|
if m:
|
|
562
560
|
bad_choice = m.group(1)
|
|
@@ -1008,7 +1006,7 @@ def main():
|
|
|
1008
1006
|
import update_sentinel
|
|
1009
1007
|
update_sentinel.check_for_updates(force=False, notify=True)
|
|
1010
1008
|
except (ImportError, OSError, TimeoutError):
|
|
1011
|
-
|
|
1009
|
+
_ = False
|
|
1012
1010
|
|
|
1013
1011
|
# Dispatch subcommand
|
|
1014
1012
|
args.func(args)
|
|
@@ -15,8 +15,7 @@ import re
|
|
|
15
15
|
import sys
|
|
16
16
|
import json
|
|
17
17
|
import argparse
|
|
18
|
-
from
|
|
19
|
-
from typing import Dict, List, Any, Tuple
|
|
18
|
+
from typing import Dict, List, Any, Tuple, Optional
|
|
20
19
|
|
|
21
20
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
22
21
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -129,7 +128,7 @@ class ConfigDecoupler:
|
|
|
129
128
|
"replacement": f"process.env.{key}"
|
|
130
129
|
})
|
|
131
130
|
|
|
132
|
-
def export_configs(self, output_dir: str = None) -> Tuple[str, str]:
|
|
131
|
+
def export_configs(self, output_dir: Optional[str] = None) -> Tuple[str, str]:
|
|
133
132
|
"""Generates env.default.json and .env.example configuration files."""
|
|
134
133
|
out_base = output_dir or os.path.join(self.target_dir, "config", "profiles")
|
|
135
134
|
os.makedirs(out_base, exist_ok=True)
|