salesforce-agent-optimizer 1.0.0__py3-none-any.whl
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.
- salesforce_agent_optimizer/__init__.py +23 -0
- salesforce_agent_optimizer/cli.py +211 -0
- salesforce_agent_optimizer/doctor.py +172 -0
- salesforce_agent_optimizer/installer.py +416 -0
- salesforce_agent_optimizer/knowledge.py +492 -0
- salesforce_agent_optimizer/templates/AGENTS.md +32 -0
- salesforce_agent_optimizer/templates/SKILL.md +83 -0
- salesforce_agent_optimizer/templates/agents/openai.yaml +7 -0
- salesforce_agent_optimizer/templates/claude/SKILL.md +83 -0
- salesforce_agent_optimizer/templates/codex/SKILL.md +83 -0
- salesforce_agent_optimizer/templates/github/copilot-instructions.md +32 -0
- salesforce_agent_optimizer/templates/github/instructions/salesforce-agent-optimizer.instructions.md +34 -0
- salesforce_agent_optimizer/templates/references/agent-installation.md +112 -0
- salesforce_agent_optimizer/templates/references/agent-instruction-spine.md +27 -0
- salesforce_agent_optimizer/templates/references/architecture-solution.md +50 -0
- salesforce_agent_optimizer/templates/references/backend-apex.md +43 -0
- salesforce_agent_optimizer/templates/references/completion-artifacts.md +161 -0
- salesforce_agent_optimizer/templates/references/deletion-guardrails.md +70 -0
- salesforce_agent_optimizer/templates/references/delivery-methodology.md +155 -0
- salesforce_agent_optimizer/templates/references/frontend-lwc.md +45 -0
- salesforce_agent_optimizer/templates/references/knowledge-init.md +116 -0
- salesforce_agent_optimizer/templates/references/least-privilege-planning.md +54 -0
- salesforce_agent_optimizer/templates/references/metadata-dependencies.md +69 -0
- salesforce_agent_optimizer/templates/references/official-salesforce-sources.md +32 -0
- salesforce_agent_optimizer/templates/references/products-packages/index.md +45 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/advanced-approvals.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/box-intelligent-content-management.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/declarative-lookup-rollup-summaries.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/docusign-esignature-for-salesforce.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/fieldspy-field-usage-report.md +28 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/linkedin-sales-navigator-for-salesforce.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/org-check.md +28 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/query-studio-for-marketing-cloud.md +28 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/salesforce-adoption-dashboards.md +28 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/user-access-and-permissions-assistant.md +28 -0
- salesforce_agent_optimizer/templates/references/products-packages/packages/zoom-for-lightning.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/agentforce.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/commerce-cloud.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/data-360-data-cloud.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/einstein-ai.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/experience-cloud.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/industry-clouds.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/marketing-cloud.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/mobile-development.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/mulesoft.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/net-zero-cloud.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/partner-prm.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/sales-cloud.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/salesforce-cpq.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/salesforce-field-service.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/salesforce-platform-data.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/service-cloud.md +30 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/slack.md +29 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/success-plans-professional-services.md +27 -0
- salesforce_agent_optimizer/templates/references/products-packages/products/tableau.md +29 -0
- salesforce_agent_optimizer/templates/references/routing.md +28 -0
- salesforce_agent_optimizer/templates/references/salesforce-current-version.md +48 -0
- salesforce_agent_optimizer/templates/references/salesforce-version.json +40 -0
- salesforce_agent_optimizer/templates/references/sf-agent-cli-commands.md +434 -0
- salesforce_agent_optimizer/templates/references/sf-cli-token-patterns.md +50 -0
- salesforce_agent_optimizer/templates/references/sf-official-command-catalog.json +2793 -0
- salesforce_agent_optimizer/templates/references/sf-official-command-catalog.md +250 -0
- salesforce_agent_optimizer/templates/references/testing-and-manifest-guardrails.md +61 -0
- salesforce_agent_optimizer/templates/references/version-update.md +72 -0
- salesforce_agent_optimizer/templates/scripts/build_release_artifacts.py +100 -0
- salesforce_agent_optimizer/templates/scripts/generate_package_manifest.py +285 -0
- salesforce_agent_optimizer/templates/scripts/git_knowledge_push.py +126 -0
- salesforce_agent_optimizer/templates/scripts/knowledge_history.py +179 -0
- salesforce_agent_optimizer/templates/scripts/self_test.py +488 -0
- salesforce_agent_optimizer/templates/scripts/sf_agent_cli.py +1003 -0
- salesforce_agent_optimizer/templates/scripts/sf_catalog_build.py +120 -0
- salesforce_agent_optimizer/templates/scripts/sf_knowledge_init.py +606 -0
- salesforce_agent_optimizer/templates/scripts/sf_min.py +150 -0
- salesforce_agent_optimizer/templates/scripts/sf_version_update.py +140 -0
- salesforce_agent_optimizer/templates/scripts/sync_agent_instructions.py +181 -0
- salesforce_agent_optimizer/templates/scripts/validate_skill.py +39 -0
- salesforce_agent_optimizer/validation.py +572 -0
- salesforce_agent_optimizer/version_context.py +319 -0
- salesforce_agent_optimizer-1.0.0.dist-info/METADATA +360 -0
- salesforce_agent_optimizer-1.0.0.dist-info/RECORD +84 -0
- salesforce_agent_optimizer-1.0.0.dist-info/WHEEL +5 -0
- salesforce_agent_optimizer-1.0.0.dist-info/entry_points.txt +2 -0
- salesforce_agent_optimizer-1.0.0.dist-info/licenses/LICENSE +21 -0
- salesforce_agent_optimizer-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Salesforce Agent Optimizer package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PACKAGE_NAME = "salesforce-agent-optimizer"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_version() -> str:
|
|
13
|
+
"""Return the installed package version."""
|
|
14
|
+
try:
|
|
15
|
+
return version(PACKAGE_NAME)
|
|
16
|
+
except PackageNotFoundError:
|
|
17
|
+
source_version = Path(__file__).resolve().parents[2] / "VERSION"
|
|
18
|
+
if source_version.exists():
|
|
19
|
+
return source_version.read_text(encoding="utf-8").strip()
|
|
20
|
+
return "0.0.0+local"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__version__ = get_version()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Command-line interface for Salesforce Agent Optimizer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from .doctor import format_report, report_to_json, run_doctor
|
|
12
|
+
from .installer import install, project_destination, uninstall, update, user_destination
|
|
13
|
+
from .knowledge import format_knowledge_report, run_knowledge
|
|
14
|
+
from .validation import validate_auto
|
|
15
|
+
from .version_context import (
|
|
16
|
+
format_report as format_version_context_report,
|
|
17
|
+
scaffold as version_context_scaffold,
|
|
18
|
+
update as version_context_update,
|
|
19
|
+
validate as version_context_validate,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
24
|
+
parser = argparse.ArgumentParser(prog="sfao", description="Salesforce Agent Optimizer CLI")
|
|
25
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
26
|
+
|
|
27
|
+
install_parser = subparsers.add_parser("install", help="Install agent skill files")
|
|
28
|
+
install_parser.add_argument("--project", action="store_true", help="Install into the current repository")
|
|
29
|
+
install_parser.add_argument(
|
|
30
|
+
"--platform",
|
|
31
|
+
choices=["codex", "claude", "copilot", "all"],
|
|
32
|
+
default="all",
|
|
33
|
+
help="Agent platform to install",
|
|
34
|
+
)
|
|
35
|
+
install_parser.add_argument("--target", type=Path, help="Override installation root")
|
|
36
|
+
install_parser.add_argument("--json", action="store_true", help="Emit compact JSON")
|
|
37
|
+
|
|
38
|
+
update_parser = subparsers.add_parser("update", help="Update generated installed files")
|
|
39
|
+
update_parser.add_argument("--project", action="store_true", help="Update files in the current repository")
|
|
40
|
+
update_parser.add_argument(
|
|
41
|
+
"--platform",
|
|
42
|
+
choices=["codex", "claude", "copilot", "all"],
|
|
43
|
+
default="all",
|
|
44
|
+
help="Agent platform to update",
|
|
45
|
+
)
|
|
46
|
+
update_parser.add_argument("--target", type=Path, help="Override installation root")
|
|
47
|
+
update_parser.add_argument("--json", action="store_true", help="Emit compact JSON")
|
|
48
|
+
|
|
49
|
+
uninstall_parser = subparsers.add_parser("uninstall", help="Remove generated installed files")
|
|
50
|
+
uninstall_parser.add_argument("--project", action="store_true", help="Remove files in the current repository")
|
|
51
|
+
uninstall_parser.add_argument(
|
|
52
|
+
"--platform",
|
|
53
|
+
choices=["codex", "claude", "copilot", "all"],
|
|
54
|
+
default="all",
|
|
55
|
+
help="Agent platform to uninstall",
|
|
56
|
+
)
|
|
57
|
+
uninstall_parser.add_argument("--target", type=Path, help="Override installation root")
|
|
58
|
+
uninstall_parser.add_argument("--yes", action="store_true", help="Confirm non-interactive uninstall")
|
|
59
|
+
uninstall_parser.add_argument("--json", action="store_true", help="Emit compact JSON")
|
|
60
|
+
|
|
61
|
+
doctor_parser = subparsers.add_parser("doctor", help="Diagnose environment and installed files")
|
|
62
|
+
doctor_parser.add_argument("--json", action="store_true", help="Emit JSON")
|
|
63
|
+
doctor_parser.add_argument("--root", type=Path, default=Path.cwd(), help="Root to inspect")
|
|
64
|
+
doctor_parser.add_argument("--summary", action="store_true", help="Emit compact summary")
|
|
65
|
+
doctor_parser.add_argument("--compact", action="store_true", help="Emit compact summary")
|
|
66
|
+
doctor_parser.add_argument("--verbose", action="store_true", help="Emit full diagnostics")
|
|
67
|
+
|
|
68
|
+
validate_parser = subparsers.add_parser("validate", help="Validate source tree or installation")
|
|
69
|
+
validate_parser.add_argument("--json", action="store_true", help="Emit JSON")
|
|
70
|
+
validate_parser.add_argument("--root", type=Path, default=Path.cwd(), help="Root to validate")
|
|
71
|
+
validate_parser.add_argument("--summary", action="store_true", help="Emit compact summary")
|
|
72
|
+
validate_parser.add_argument("--compact", action="store_true", help="Emit compact summary")
|
|
73
|
+
validate_parser.add_argument("--verbose", action="store_true", help="Emit validation details")
|
|
74
|
+
|
|
75
|
+
knowledge_parser = subparsers.add_parser("knowledge", help="Manage local Salesforce Knowledge")
|
|
76
|
+
knowledge_subparsers = knowledge_parser.add_subparsers(dest="knowledge_command", required=True)
|
|
77
|
+
for name in ("init", "refresh", "doctor"):
|
|
78
|
+
parser_for_command = knowledge_subparsers.add_parser(name, help=f"Knowledge {name}")
|
|
79
|
+
parser_for_command.add_argument("--project-root", type=Path, default=Path.cwd())
|
|
80
|
+
parser_for_command.add_argument("--target-org", help="Explicit org alias for optional enrichment")
|
|
81
|
+
parser_for_command.add_argument("--json", action="store_true", help="Emit compact JSON")
|
|
82
|
+
parser_for_command.add_argument("--summary", action="store_true", help="Emit compact summary")
|
|
83
|
+
parser_for_command.add_argument("--compact", action="store_true", help="Emit compact summary")
|
|
84
|
+
parser_for_command.add_argument("--verbose", action="store_true", help="Emit changed paths")
|
|
85
|
+
parser_for_command.add_argument("--max-items", type=int, help="Limit indexed entries")
|
|
86
|
+
|
|
87
|
+
version_context_parser = subparsers.add_parser(
|
|
88
|
+
"version-context",
|
|
89
|
+
help="Manage Salesforce release and API version context",
|
|
90
|
+
)
|
|
91
|
+
version_context_subparsers = version_context_parser.add_subparsers(
|
|
92
|
+
dest="version_context_command",
|
|
93
|
+
required=True,
|
|
94
|
+
)
|
|
95
|
+
for name in ("scaffold", "update", "validate"):
|
|
96
|
+
parser_for_command = version_context_subparsers.add_parser(
|
|
97
|
+
name,
|
|
98
|
+
help=f"Version-context {name}",
|
|
99
|
+
)
|
|
100
|
+
parser_for_command.add_argument("--root", type=Path, default=Path.cwd())
|
|
101
|
+
parser_for_command.add_argument("--json", action="store_true", help="Emit compact JSON")
|
|
102
|
+
parser_for_command.add_argument("--summary", action="store_true", help="Emit compact summary")
|
|
103
|
+
parser_for_command.add_argument("--compact", action="store_true", help="Emit compact summary")
|
|
104
|
+
parser_for_command.add_argument("--verbose", action="store_true", help="Emit details")
|
|
105
|
+
if name == "update":
|
|
106
|
+
parser_for_command.add_argument(
|
|
107
|
+
"--offline",
|
|
108
|
+
action="store_true",
|
|
109
|
+
help="Use existing official-source context without network checks",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
subparsers.add_parser("version", help="Print package version")
|
|
113
|
+
return parser
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def main(argv: list[str] | None = None) -> int:
|
|
117
|
+
parser = build_parser()
|
|
118
|
+
args = parser.parse_args(argv)
|
|
119
|
+
if args.command == "version":
|
|
120
|
+
print(f"salesforce-agent-optimizer {__version__}")
|
|
121
|
+
return 0
|
|
122
|
+
if args.command == "install":
|
|
123
|
+
target = args.target or (project_destination() if args.project else user_destination())
|
|
124
|
+
report = install(target, project=args.project, platform=args.platform)
|
|
125
|
+
print_operation_summary("install", report, json_output=args.json)
|
|
126
|
+
return 0 if report.ok else 1
|
|
127
|
+
if args.command == "update":
|
|
128
|
+
target = args.target or (project_destination() if args.project else user_destination())
|
|
129
|
+
report = update(target, project=args.project, platform=args.platform)
|
|
130
|
+
print_operation_summary("update", report, json_output=args.json)
|
|
131
|
+
return 0 if report.ok else 1
|
|
132
|
+
if args.command == "uninstall":
|
|
133
|
+
target = args.target or (project_destination() if args.project else user_destination())
|
|
134
|
+
report = uninstall(target, project=args.project, platform=args.platform, yes=args.yes)
|
|
135
|
+
print_operation_summary("uninstall", report, json_output=args.json)
|
|
136
|
+
return 0 if report.ok else 1
|
|
137
|
+
if args.command == "doctor":
|
|
138
|
+
report = run_doctor(args.root)
|
|
139
|
+
print(report_to_json(report) if args.json else format_report(report, verbose=args.verbose), end="")
|
|
140
|
+
return 1 if report.has_errors else 0
|
|
141
|
+
if args.command == "validate":
|
|
142
|
+
result = validate_auto(args.root, expected_version=__version__)
|
|
143
|
+
if args.json:
|
|
144
|
+
print(json.dumps(result.to_dict(), separators=(",", ":"), sort_keys=True))
|
|
145
|
+
else:
|
|
146
|
+
output = format_validation_result(result, verbose=args.verbose)
|
|
147
|
+
stream = sys.stdout if result.ok else sys.stderr
|
|
148
|
+
print(output, end="", file=stream)
|
|
149
|
+
return 0 if result.ok else 1
|
|
150
|
+
if args.command == "knowledge":
|
|
151
|
+
report = run_knowledge(
|
|
152
|
+
args.knowledge_command,
|
|
153
|
+
args.project_root,
|
|
154
|
+
target_org=args.target_org,
|
|
155
|
+
max_items=args.max_items,
|
|
156
|
+
)
|
|
157
|
+
if args.json:
|
|
158
|
+
print(json.dumps(report.to_dict(), separators=(",", ":"), sort_keys=True))
|
|
159
|
+
else:
|
|
160
|
+
print(format_knowledge_report(report, verbose=args.verbose), end="")
|
|
161
|
+
return 0 if report.ok else 1
|
|
162
|
+
if args.command == "version-context":
|
|
163
|
+
if args.version_context_command == "scaffold":
|
|
164
|
+
report = version_context_scaffold(args.root)
|
|
165
|
+
elif args.version_context_command == "update":
|
|
166
|
+
report = version_context_update(args.root, offline=args.offline)
|
|
167
|
+
else:
|
|
168
|
+
report = version_context_validate(args.root)
|
|
169
|
+
if args.json:
|
|
170
|
+
print(json.dumps(report.to_dict(), separators=(",", ":"), sort_keys=True))
|
|
171
|
+
else:
|
|
172
|
+
print(format_version_context_report(report, verbose=args.verbose), end="")
|
|
173
|
+
return 0 if report.ok else 1
|
|
174
|
+
parser.print_help()
|
|
175
|
+
return 2
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def format_validation_result(result, verbose: bool = False) -> str:
|
|
179
|
+
status = "OK" if result.ok else "ERROR"
|
|
180
|
+
lines = [f"Salesforce Agent Optimizer validation: {status}"]
|
|
181
|
+
if result.warnings or result.errors:
|
|
182
|
+
for error in result.errors:
|
|
183
|
+
lines.append(f"ERROR: {error}")
|
|
184
|
+
for warning in result.warnings:
|
|
185
|
+
lines.append(f"WARN: {warning}")
|
|
186
|
+
else:
|
|
187
|
+
lines.append("No warnings or errors.")
|
|
188
|
+
if verbose and result.ok:
|
|
189
|
+
lines.append("Validated required files, versions, frontmatter, YAML, TOML, JSON, Python, and text shape.")
|
|
190
|
+
return "\n".join(lines) + "\n"
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def print_operation_summary(action: str, report, json_output: bool = False) -> None:
|
|
194
|
+
if json_output:
|
|
195
|
+
print(json.dumps(report.to_dict(), separators=(",", ":"), sort_keys=True))
|
|
196
|
+
return
|
|
197
|
+
print(f"Salesforce Agent Optimizer {action} summary")
|
|
198
|
+
for label in ("installed", "updated", "removed", "skipped", "stale", "warnings", "errors"):
|
|
199
|
+
values = getattr(report, label)
|
|
200
|
+
print(f"{label.title()}: {len(values)}")
|
|
201
|
+
for item in values[:20]:
|
|
202
|
+
print(f"- {item}")
|
|
203
|
+
if len(values) > 20:
|
|
204
|
+
print(f"- ... {len(values) - 20} more")
|
|
205
|
+
print("Next steps:")
|
|
206
|
+
print("- Run: sfao doctor")
|
|
207
|
+
print("- Run: sfao validate")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Environment diagnostics for Salesforce Agent Optimizer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
import site
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from . import __version__
|
|
17
|
+
from .validation import SKILL_NAME, validate_auto
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Check:
|
|
22
|
+
name: str
|
|
23
|
+
status: str
|
|
24
|
+
detail: str = ""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class DoctorReport:
|
|
29
|
+
sections: dict[str, list[Check]] = field(default_factory=dict)
|
|
30
|
+
|
|
31
|
+
def add(self, section: str, name: str, status: str, detail: str = "") -> None:
|
|
32
|
+
self.sections.setdefault(section, []).append(Check(name, status, detail))
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def has_errors(self) -> bool:
|
|
36
|
+
return any(check.status == "ERROR" for checks in self.sections.values() for check in checks)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def has_warnings(self) -> bool:
|
|
40
|
+
return any(check.status == "WARN" for checks in self.sections.values() for check in checks)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def status(self) -> str:
|
|
44
|
+
if self.has_errors:
|
|
45
|
+
return "ERROR"
|
|
46
|
+
if self.has_warnings:
|
|
47
|
+
return "WARN"
|
|
48
|
+
return "OK"
|
|
49
|
+
|
|
50
|
+
def to_dict(self) -> dict[str, Any]:
|
|
51
|
+
payload: dict[str, Any] = {"ok": not self.has_errors, "status": self.status}
|
|
52
|
+
payload.update(
|
|
53
|
+
{
|
|
54
|
+
section: [check.__dict__ for check in checks]
|
|
55
|
+
for section, checks in self.sections.items()
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
return payload
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def run_doctor(root: Path | None = None) -> DoctorReport:
|
|
62
|
+
root = (root or Path.cwd()).resolve()
|
|
63
|
+
report = DoctorReport()
|
|
64
|
+
report.add("Core", "Package", "OK", f"salesforce-agent-optimizer v{__version__}")
|
|
65
|
+
py_status = "OK" if sys.version_info >= (3, 10) else "ERROR"
|
|
66
|
+
report.add("Core", "Python", py_status, platform.python_version())
|
|
67
|
+
report.add("Core", "OS", "OK", platform.system() or sys.platform)
|
|
68
|
+
report.add("Core", "Git", "OK" if shutil.which("git") else "WARN", shutil.which("git") or "not found")
|
|
69
|
+
report.add(
|
|
70
|
+
"Core",
|
|
71
|
+
"Salesforce CLI sf",
|
|
72
|
+
"OK" if shutil.which("sf") else "WARN",
|
|
73
|
+
shutil.which("sf") or "not found",
|
|
74
|
+
)
|
|
75
|
+
report.add(
|
|
76
|
+
"Core",
|
|
77
|
+
"Git repository",
|
|
78
|
+
"OK" if is_git_repo(root) else "WARN",
|
|
79
|
+
str(root),
|
|
80
|
+
)
|
|
81
|
+
report.add(
|
|
82
|
+
"Core",
|
|
83
|
+
"Salesforce DX project",
|
|
84
|
+
"OK" if (root / "sfdx-project.json").exists() else "WARN",
|
|
85
|
+
"sfdx-project.json found" if (root / "sfdx-project.json").exists() else "not found",
|
|
86
|
+
)
|
|
87
|
+
report.add(
|
|
88
|
+
"Agent adapters",
|
|
89
|
+
"Codex skill",
|
|
90
|
+
"OK" if (root / ".agents" / "skills" / SKILL_NAME / "SKILL.md").exists() else "WARN",
|
|
91
|
+
".agents/skills/salesforce-agent-optimizer",
|
|
92
|
+
)
|
|
93
|
+
report.add(
|
|
94
|
+
"Agent adapters",
|
|
95
|
+
"Claude skill",
|
|
96
|
+
"OK" if (root / ".claude" / "skills" / SKILL_NAME / "SKILL.md").exists() else "WARN",
|
|
97
|
+
".claude/skills/salesforce-agent-optimizer",
|
|
98
|
+
)
|
|
99
|
+
copilot_ok = (root / ".github" / "copilot-instructions.md").exists() and (
|
|
100
|
+
root / ".github" / "instructions" / "salesforce-agent-optimizer.instructions.md"
|
|
101
|
+
).exists()
|
|
102
|
+
report.add("Agent adapters", "GitHub Copilot instructions", "OK" if copilot_ok else "WARN")
|
|
103
|
+
report.add("Agent adapters", "AGENTS.md", "OK" if (root / "AGENTS.md").exists() else "WARN")
|
|
104
|
+
validation = validate_auto(root, expected_version=__version__)
|
|
105
|
+
report.add(
|
|
106
|
+
"Validation",
|
|
107
|
+
"Skill package",
|
|
108
|
+
"OK" if validation.ok else "ERROR",
|
|
109
|
+
"valid" if validation.ok else "; ".join(validation.errors[:3]),
|
|
110
|
+
)
|
|
111
|
+
if validation.warnings:
|
|
112
|
+
report.add("Validation", "Warnings", "WARN", "; ".join(validation.warnings[:3]))
|
|
113
|
+
report.add("Validation", "Version alignment", "OK" if validation.ok else "ERROR", f"v{__version__}")
|
|
114
|
+
if platform.system().lower().startswith("windows"):
|
|
115
|
+
report.add("Windows", "PATH", "OK" if user_scripts_on_path() else "WARN", windows_path_detail())
|
|
116
|
+
return report
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def is_git_repo(root: Path) -> bool:
|
|
120
|
+
completed = subprocess.run(
|
|
121
|
+
["git", "-C", str(root), "rev-parse", "--is-inside-work-tree"],
|
|
122
|
+
text=True,
|
|
123
|
+
stdout=subprocess.DEVNULL,
|
|
124
|
+
stderr=subprocess.DEVNULL,
|
|
125
|
+
check=False,
|
|
126
|
+
)
|
|
127
|
+
return completed.returncode == 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def user_scripts_on_path() -> bool:
|
|
131
|
+
scripts = Path(site.USER_BASE) / "Scripts"
|
|
132
|
+
path_parts = [Path(part) for part in (os.environ.get("PATH") or "").split(os.pathsep) if part]
|
|
133
|
+
return any(part == scripts for part in path_parts)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def windows_path_detail() -> str:
|
|
137
|
+
scripts = Path(site.USER_BASE) / "Scripts"
|
|
138
|
+
return f"{scripts} is {'on' if user_scripts_on_path() else 'not on'} PATH"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def format_report(report: DoctorReport, verbose: bool = False) -> str:
|
|
142
|
+
lines = [f"Salesforce Agent Optimizer Doctor: {report.status}"]
|
|
143
|
+
if not verbose:
|
|
144
|
+
issues = [
|
|
145
|
+
(section, check)
|
|
146
|
+
for section, checks in report.sections.items()
|
|
147
|
+
for check in checks
|
|
148
|
+
if check.status in {"WARN", "ERROR"}
|
|
149
|
+
]
|
|
150
|
+
if issues:
|
|
151
|
+
lines.append("Warnings and errors:")
|
|
152
|
+
for section, check in issues:
|
|
153
|
+
detail = f" {check.detail}" if check.detail else ""
|
|
154
|
+
lines.append(f"- {section} / {check.name}: {check.status}{detail}")
|
|
155
|
+
else:
|
|
156
|
+
lines.append("No warnings or errors.")
|
|
157
|
+
lines.append("Use --verbose for full diagnostics.")
|
|
158
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
159
|
+
lines.append("")
|
|
160
|
+
for section, checks in report.sections.items():
|
|
161
|
+
lines.append(f"{section}:")
|
|
162
|
+
for check in checks:
|
|
163
|
+
detail = f" {check.detail}" if check.detail else ""
|
|
164
|
+
lines.append(f"- {check.name}: {check.status}{detail}")
|
|
165
|
+
lines.append("")
|
|
166
|
+
lines.append("Status:")
|
|
167
|
+
lines.append("Everything looks good." if not report.has_errors else "Problems found.")
|
|
168
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def report_to_json(report: DoctorReport) -> str:
|
|
172
|
+
return json.dumps(report.to_dict(), separators=(",", ":"), sort_keys=True) + "\n"
|