procesio-cli 0.1.1__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.
- procesio_cli/__init__.py +18 -0
- procesio_cli/_cli.py +71 -0
- procesio_cli/agents/__init__.py +0 -0
- procesio_cli/agents/_lib/__init__.py +0 -0
- procesio_cli/agents/_lib/actiondef.py +24 -0
- procesio_cli/agents/_lib/errors.py +40 -0
- procesio_cli/agents/_lib/reversibility.py +236 -0
- procesio_cli/agents/_lib/toolrunner.py +141 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
- procesio_cli/agents/connector-builder/README.md +51 -0
- procesio_cli/agents/connector-builder/agent.yaml +61 -0
- procesio_cli/agents/connector-builder/context.py +19 -0
- procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
- procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
- procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
- procesio_cli/agents/connector-builder/handlers/status.py +35 -0
- procesio_cli/agents/connector-builder/knowledge.py +47 -0
- procesio_cli/agents/connector-builder/main.py +93 -0
- procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
- procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
- procesio_cli/agents/orchestrator/__init__.py +0 -0
- procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
- procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
- procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
- procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
- procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
- procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
- procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
- procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
- procesio_cli/agents/procesio/README.md +97 -0
- procesio_cli/agents/procesio/__init__.py +0 -0
- procesio_cli/agents/procesio/agent.yaml +73 -0
- procesio_cli/agents/procesio/audit.py +226 -0
- procesio_cli/agents/procesio/checklist.py +135 -0
- procesio_cli/agents/procesio/context.py +19 -0
- procesio_cli/agents/procesio/handlers/__init__.py +0 -0
- procesio_cli/agents/procesio/handlers/audit.py +39 -0
- procesio_cli/agents/procesio/handlers/checklist.py +34 -0
- procesio_cli/agents/procesio/handlers/guidance.py +36 -0
- procesio_cli/agents/procesio/handlers/status.py +33 -0
- procesio_cli/agents/procesio/handlers/verify.py +39 -0
- procesio_cli/agents/procesio/knowledge.py +115 -0
- procesio_cli/agents/procesio/main.py +89 -0
- procesio_cli/agents/procesio/tests/__init__.py +0 -0
- procesio_cli/agents/procesio/tests/conftest.py +62 -0
- procesio_cli/agents/procesio/tests/test_audit.py +91 -0
- procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
- procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
- procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
- procesio_cli/agents/procesio/tests/test_verify.py +134 -0
- procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
- procesio_cli/agents/procesio/verifylib.py +222 -0
- procesio_cli/dashboard/README.md +117 -0
- procesio_cli/dashboard/__init__.py +18 -0
- procesio_cli/dashboard/serve.py +98 -0
- procesio_cli/dashboard/server/__init__.py +1 -0
- procesio_cli/dashboard/server/config_schemas.py +92 -0
- procesio_cli/dashboard/server/google.py +103 -0
- procesio_cli/dashboard/server/handler.py +160 -0
- procesio_cli/dashboard/server/inventory.py +213 -0
- procesio_cli/dashboard/server/jobs.py +118 -0
- procesio_cli/dashboard/server/llmtest.py +181 -0
- procesio_cli/dashboard/server/procesio_env.py +94 -0
- procesio_cli/dashboard/server/routes.py +74 -0
- procesio_cli/dashboard/server/runner.py +150 -0
- procesio_cli/dashboard/server/security.py +29 -0
- procesio_cli/dashboard/server/setup.py +211 -0
- procesio_cli/dashboard/server/validate.py +152 -0
- procesio_cli/dashboard/tests/conftest.py +16 -0
- procesio_cli/dashboard/tests/test_dashboard.py +225 -0
- procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
- procesio_cli/dashboard/web/actions.js +274 -0
- procesio_cli/dashboard/web/api.js +79 -0
- procesio_cli/dashboard/web/app.js +385 -0
- procesio_cli/dashboard/web/copilot.js +72 -0
- procesio_cli/dashboard/web/google.js +121 -0
- procesio_cli/dashboard/web/index.html +67 -0
- procesio_cli/dashboard/web/procesio_env.js +187 -0
- procesio_cli/dashboard/web/styles.css +207 -0
- procesio_cli/registry.py +370 -0
- procesio_cli/scripts/build-router.py +249 -0
- procesio_cli/scripts/build-tool-skill.py +202 -0
- procesio_cli/scripts/check-requirements.py +131 -0
- procesio_cli/scripts/check-skill-governance.py +174 -0
- procesio_cli/scripts/evaluate-skill-routing.py +217 -0
- procesio_cli/scripts/evaluate-skills.py +316 -0
- procesio_cli/scripts/get-skill.py +122 -0
- procesio_cli/scripts/list-skills.py +68 -0
- procesio_cli/scripts/list-tools.py +73 -0
- procesio_cli/scripts/run-agent.py +59 -0
- procesio_cli/scripts/run-tool.py +123 -0
- procesio_cli/scripts/secret_scan.py +584 -0
- procesio_cli/scripts/set-credential.py +126 -0
- procesio_cli/scripts/validate-skills.py +530 -0
- procesio_cli/skills/PORTFOLIO.md +134 -0
- procesio_cli/skills/__init__.py +6 -0
- procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
- procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
- procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
- procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
- procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
- procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
- procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
- procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
- procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
- procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
- procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
- procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
- procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
- procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
- procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
- procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
- procesio_cli/skills/evals/baseline-catalog.json +14 -0
- procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
- procesio_cli/skills/evals/baseline.json +37 -0
- procesio_cli/skills/evals/behavioral.json +367 -0
- procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
- procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
- procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
- procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
- procesio_cli/skills/evals/routing.json +363 -0
- procesio_cli/skills/evals/validation-baseline.json +4 -0
- procesio_cli/skills/procesio-cli/SKILL.md +134 -0
- procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
- procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
- procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
- procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
- procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
- procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
- procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
- procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
- procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
- procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
- procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
- procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
- procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
- procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
- procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
- procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
- procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
- procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
- procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
- procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
- procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
- procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
- procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
- procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
- procesio_cli/skills/procesio-setup/SKILL.md +180 -0
- procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
- procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
- procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
- procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
- procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
- procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
- procesio_cli/skills/tests/__init__.py +0 -0
- procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
- procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
- procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
- procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
- procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
- procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
- procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
- procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
- procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
- procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
- procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
- procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
- procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
- procesio_cli/skills/tests/test_skill_routing.py +74 -0
- procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
- procesio_cli/skills/tests/test_validate_skills.py +115 -0
- procesio_cli/tools/__init__.py +0 -0
- procesio_cli/tools/_lib/README.md +119 -0
- procesio_cli/tools/_lib/__init__.py +0 -0
- procesio_cli/tools/_lib/accounting.py +120 -0
- procesio_cli/tools/_lib/contacts.py +544 -0
- procesio_cli/tools/_lib/creds.py +83 -0
- procesio_cli/tools/_lib/creds_backends.py +578 -0
- procesio_cli/tools/_lib/dbprofiles.py +147 -0
- procesio_cli/tools/_lib/dbquery.py +286 -0
- procesio_cli/tools/_lib/dbschema.py +225 -0
- procesio_cli/tools/_lib/io.py +54 -0
- procesio_cli/tools/_lib/manifest.py +297 -0
- procesio_cli/tools/_lib/skill_release.py +127 -0
- procesio_cli/tools/_lib/skill_resources.py +118 -0
- procesio_cli/tools/_lib/userdata.py +228 -0
- procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
- procesio_cli/tools/_lib/webserialize/admin.py +61 -0
- procesio_cli/tools/_lib/webserialize/client.py +264 -0
- procesio_cli/tools/_lib/webserialize/config.py +96 -0
- procesio_cli/tools/_lib/webserialize/errors.py +27 -0
- procesio_cli/tools/_lib/webserialize/framing.py +45 -0
- procesio_cli/tools/_lib/webserialize/registry.py +47 -0
- procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
- procesio_cli/tools/_lib/webserialize/server.py +510 -0
- procesio_cli/tools/_lib/websession.py +194 -0
- procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
- procesio_cli/tools/connector-builder/README.md +101 -0
- procesio_cli/tools/connector-builder/SKILL.md +248 -0
- procesio_cli/tools/connector-builder/__init__.py +7 -0
- procesio_cli/tools/connector-builder/actiondef.py +24 -0
- procesio_cli/tools/connector-builder/client.py +251 -0
- procesio_cli/tools/connector-builder/errors.py +44 -0
- procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
- procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
- procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
- procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
- procesio_cli/tools/connector-builder/handlers/common.py +104 -0
- procesio_cli/tools/connector-builder/handlers/files.py +111 -0
- procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
- procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
- procesio_cli/tools/connector-builder/main.py +94 -0
- procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
- procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
- procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
- procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
- procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
- procesio_cli/tools/connector-builder/tool.yaml +671 -0
- procesio_cli/tools/framework-map/.gitattributes +2 -0
- procesio_cli/tools/framework-map/README.md +98 -0
- procesio_cli/tools/framework-map/SKILL.md +30 -0
- procesio_cli/tools/framework-map/app.js +188 -0
- procesio_cli/tools/framework-map/fm_builder.py +476 -0
- procesio_cli/tools/framework-map/fm_catalog.py +19 -0
- procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
- procesio_cli/tools/framework-map/fm_strings.py +101 -0
- procesio_cli/tools/framework-map/main.py +95 -0
- procesio_cli/tools/framework-map/styles.css +214 -0
- procesio_cli/tools/framework-map/tests/conftest.py +33 -0
- procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
- procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
- procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
- procesio_cli/tools/framework-map/tool.yaml +61 -0
- procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/mysql/README.md +196 -0
- procesio_cli/tools/mysql/SKILL.md +37 -0
- procesio_cli/tools/mysql/__init__.py +0 -0
- procesio_cli/tools/mysql/connection.py +191 -0
- procesio_cli/tools/mysql/main.py +487 -0
- procesio_cli/tools/mysql/profiles.template.json +1 -0
- procesio_cli/tools/mysql/schema.py +434 -0
- procesio_cli/tools/mysql/tests/__init__.py +0 -0
- procesio_cli/tools/mysql/tests/conftest.py +21 -0
- procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
- procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
- procesio_cli/tools/mysql/tool.yaml +112 -0
- procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
- procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
- procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
- procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
- procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
- procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
- procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
- procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
- procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
- procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
- procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
- procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
- procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
- procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
- procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
- procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
- procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
- procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
- procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
- procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
- procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
- procesio_cli/tools/procesio/README.md +341 -0
- procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
- procesio_cli/tools/procesio/SKILL.md +694 -0
- procesio_cli/tools/procesio/__init__.py +1 -0
- procesio_cli/tools/procesio/actiondef.py +27 -0
- procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
- procesio_cli/tools/procesio/auth.py +354 -0
- procesio_cli/tools/procesio/autogen.py +121 -0
- procesio_cli/tools/procesio/client.py +355 -0
- procesio_cli/tools/procesio/config.py +96 -0
- procesio_cli/tools/procesio/data/endpoints.json +2901 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
- procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
- procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
- procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
- procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
- procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
- procesio_cli/tools/procesio/dto/__init__.py +13 -0
- procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
- procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
- procesio_cli/tools/procesio/dto/credential/description.md +193 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
- procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
- procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
- procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
- procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
- procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
- procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
- procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
- procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
- procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
- procesio_cli/tools/procesio/dto/document/builder.py +193 -0
- procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
- procesio_cli/tools/procesio/dto/document/description.md +124 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
- procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
- procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
- procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
- procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
- procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
- procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
- procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
- procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
- procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
- procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
- procesio_cli/tools/procesio/dto/form/description.md +346 -0
- procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
- procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
- procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
- procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
- procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
- procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
- procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
- procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
- procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
- procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
- procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
- procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
- procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
- procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
- procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
- procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
- procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
- procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
- procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
- procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
- procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
- procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
- procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
- procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
- procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
- procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
- procesio_cli/tools/procesio/dto/framework.py +125 -0
- procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
- procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
- procesio_cli/tools/procesio/dto/process/description.md +218 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
- procesio_cli/tools/procesio/dto/process/naming.py +305 -0
- procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
- procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
- procesio_cli/tools/procesio/dto/refdata.py +87 -0
- procesio_cli/tools/procesio/dto/registry.py +27 -0
- procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
- procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
- procesio_cli/tools/procesio/environments.py +264 -0
- procesio_cli/tools/procesio/errors.py +166 -0
- procesio_cli/tools/procesio/flowmodel/README.md +37 -0
- procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
- procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
- procesio_cli/tools/procesio/flowmodel/families.py +34 -0
- procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
- procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
- procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
- procesio_cli/tools/procesio/flowmodel/model.py +82 -0
- procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
- procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
- procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
- procesio_cli/tools/procesio/form_code_keys.py +50 -0
- procesio_cli/tools/procesio/formlint.py +292 -0
- procesio_cli/tools/procesio/gen_manifest.py +118 -0
- procesio_cli/tools/procesio/handlers/__init__.py +1 -0
- procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
- procesio_cli/tools/procesio/handlers/catalog.py +147 -0
- procesio_cli/tools/procesio/handlers/common.py +90 -0
- procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
- procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
- procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
- procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
- procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
- procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
- procesio_cli/tools/procesio/handlers/files.py +141 -0
- procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
- procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
- procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
- procesio_cli/tools/procesio/handlers/form_code.py +277 -0
- procesio_cli/tools/procesio/handlers/form_element.py +199 -0
- procesio_cli/tools/procesio/handlers/form_events.py +441 -0
- procesio_cli/tools/procesio/handlers/form_update.py +270 -0
- procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
- procesio_cli/tools/procesio/handlers/generic.py +135 -0
- procesio_cli/tools/procesio/handlers/layout.py +137 -0
- procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
- procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
- procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
- procesio_cli/tools/procesio/handlers/processes.py +416 -0
- procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
- procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
- procesio_cli/tools/procesio/handlers/schedules.py +320 -0
- procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
- procesio_cli/tools/procesio/handlers/transport.py +463 -0
- procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
- procesio_cli/tools/procesio/layout/README.md +196 -0
- procesio_cli/tools/procesio/layout/__init__.py +11 -0
- procesio_cli/tools/procesio/layout/adapter.py +736 -0
- procesio_cli/tools/procesio/layout/cycles.py +186 -0
- procesio_cli/tools/procesio/layout/dispatch.py +39 -0
- procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
- procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
- procesio_cli/tools/procesio/layout/elk/package.json +11 -0
- procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
- procesio_cli/tools/procesio/layout/engine.py +1311 -0
- procesio_cli/tools/procesio/layout/report.py +187 -0
- procesio_cli/tools/procesio/main.py +179 -0
- procesio_cli/tools/procesio/profiles.py +193 -0
- procesio_cli/tools/procesio/reliability.py +193 -0
- procesio_cli/tools/procesio/sqlformat.py +167 -0
- procesio_cli/tools/procesio/swagger.py +51 -0
- procesio_cli/tools/procesio/tests/__init__.py +0 -0
- procesio_cli/tools/procesio/tests/conftest.py +129 -0
- procesio_cli/tools/procesio/tests/test_auth.py +200 -0
- procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
- procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
- procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
- procesio_cli/tools/procesio/tests/test_client.py +114 -0
- procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
- procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
- procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
- procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
- procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
- procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
- procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
- procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
- procesio_cli/tools/procesio/tests/test_dto.py +734 -0
- procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
- procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
- procesio_cli/tools/procesio/tests/test_environments.py +214 -0
- procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
- procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
- procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
- procesio_cli/tools/procesio/tests/test_files.py +72 -0
- procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
- procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
- procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
- procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
- procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
- procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
- procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
- procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
- procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
- procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
- procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
- procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
- procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
- procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
- procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
- procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
- procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
- procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
- procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
- procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
- procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
- procesio_cli/tools/procesio/tests/test_guards.py +110 -0
- procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
- procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
- procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
- procesio_cli/tools/procesio/tests/test_layout.py +574 -0
- procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
- procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
- procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
- procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
- procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
- procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
- procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
- procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
- procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
- procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
- procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
- procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
- procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
- procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
- procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
- procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
- procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
- procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
- procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
- procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
- procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
- procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
- procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
- procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
- procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
- procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
- procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
- procesio_cli/tools/procesio/tests/test_transport.py +147 -0
- procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
- procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
- procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
- procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
- procesio_cli/tools/procesio/tool.yaml +12486 -0
- procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/sqlserver/README.md +192 -0
- procesio_cli/tools/sqlserver/SKILL.md +37 -0
- procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
- procesio_cli/tools/sqlserver/__init__.py +0 -0
- procesio_cli/tools/sqlserver/connection.py +242 -0
- procesio_cli/tools/sqlserver/main.py +521 -0
- procesio_cli/tools/sqlserver/profiles.template.json +1 -0
- procesio_cli/tools/sqlserver/schema.py +600 -0
- procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
- procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
- procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
- procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
- procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
- procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
- procesio_cli/tools/sqlserver/tool.yaml +123 -0
- procesio_cli/tools/web/README.md +381 -0
- procesio_cli/tools/web/SKILL.md +37 -0
- procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
- procesio_cli/tools/web/__init__.py +21 -0
- procesio_cli/tools/web/actiondef.py +24 -0
- procesio_cli/tools/web/api.py +444 -0
- procesio_cli/tools/web/brokered.py +107 -0
- procesio_cli/tools/web/driver.py +839 -0
- procesio_cli/tools/web/drivercontract.py +44 -0
- procesio_cli/tools/web/errors.py +54 -0
- procesio_cli/tools/web/handlers/__init__.py +1 -0
- procesio_cli/tools/web/handlers/archive.py +367 -0
- procesio_cli/tools/web/handlers/browse.py +182 -0
- procesio_cli/tools/web/handlers/contacts.py +74 -0
- procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
- procesio_cli/tools/web/main.py +128 -0
- procesio_cli/tools/web/sessions.py +253 -0
- procesio_cli/tools/web/steps.py +162 -0
- procesio_cli/tools/web/tests/__init__.py +0 -0
- procesio_cli/tools/web/tests/conftest.py +197 -0
- procesio_cli/tools/web/tests/test_api.py +330 -0
- procesio_cli/tools/web/tests/test_archive.py +190 -0
- procesio_cli/tools/web/tests/test_brokered.py +138 -0
- procesio_cli/tools/web/tests/test_dispatch.py +132 -0
- procesio_cli/tools/web/tests/test_errors.py +60 -0
- procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
- procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
- procesio_cli/tools/web/tests/test_sessions.py +145 -0
- procesio_cli/tools/web/tests/test_steps.py +210 -0
- procesio_cli/tools/web/tool.yaml +184 -0
- procesio_cli/tools/xlsx/README.md +34 -0
- procesio_cli/tools/xlsx/SKILL.md +28 -0
- procesio_cli/tools/xlsx/__init__.py +0 -0
- procesio_cli/tools/xlsx/main.py +129 -0
- procesio_cli/tools/xlsx/reader.py +91 -0
- procesio_cli/tools/xlsx/tests/__init__.py +0 -0
- procesio_cli/tools/xlsx/tests/conftest.py +39 -0
- procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
- procesio_cli/tools/xlsx/tool.yaml +30 -0
- procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
- procesio_cli/webplatform/aat_mcp/README.md +78 -0
- procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
- procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
- procesio_cli/webplatform/aat_mcp/gate.py +68 -0
- procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
- procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
- procesio_cli/webplatform/aat_mcp/server.py +306 -0
- procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
- procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
- procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
- procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
- procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
- procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
- procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
- procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
- procesio_cli-0.1.1.dist-info/METADATA +467 -0
- procesio_cli-0.1.1.dist-info/RECORD +647 -0
- procesio_cli-0.1.1.dist-info/WHEEL +4 -0
- procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
- procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
- procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Deterministically audit one or more Agent Skill packages.
|
|
3
|
+
|
|
4
|
+
The audit checks portable structure, frontmatter, discovery descriptions,
|
|
5
|
+
progressive resources, local references, common secret signatures, governed
|
|
6
|
+
metadata, and fixed-rubric evaluation cases. It makes no model or network calls.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from dataclasses import asdict, dataclass
|
|
16
|
+
from datetime import date
|
|
17
|
+
from pathlib import Path, PurePosixPath
|
|
18
|
+
from typing import Any, Iterable
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
import yaml
|
|
22
|
+
except ImportError as exc: # pragma: no cover - actionable dependency failure
|
|
23
|
+
raise SystemExit("audit_skill.py requires PyYAML (`python -m pip install pyyaml`)") from exc
|
|
24
|
+
|
|
25
|
+
NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
26
|
+
CRITERION_RE = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$")
|
|
27
|
+
MARKDOWN_LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
|
|
28
|
+
CODE_PATH_RE = re.compile(
|
|
29
|
+
r"`((?:(?:references|scripts|assets)/)[^`\n]+)`", re.IGNORECASE
|
|
30
|
+
)
|
|
31
|
+
PLACEHOLDER_RE = re.compile(
|
|
32
|
+
r"\{\{\s*REPLACE[^}]*\}\}|\b(?:TODO|TBD|FIXME|YOUR_[A-Z0-9_]+)\b",
|
|
33
|
+
re.IGNORECASE,
|
|
34
|
+
)
|
|
35
|
+
TRIGGER_RE = re.compile(r"\b(?:use when|use for|use to|when users?|when the user)\b", re.I)
|
|
36
|
+
HYPE_RE = re.compile(
|
|
37
|
+
r"\b(?:ultimate|world[- ]class|unparalleled|best[- ]in[- ]class|"
|
|
38
|
+
r"0\.0*1%|genius|superhuman|omniscient)\b",
|
|
39
|
+
re.I,
|
|
40
|
+
)
|
|
41
|
+
TOC_RE = re.compile(r"^##?\s+(?:contents|table of contents)\b", re.I | re.M)
|
|
42
|
+
ALLOWED_FRONTMATTER = {
|
|
43
|
+
"name",
|
|
44
|
+
"description",
|
|
45
|
+
"version",
|
|
46
|
+
"compatibility",
|
|
47
|
+
"license",
|
|
48
|
+
"allowed-tools",
|
|
49
|
+
"disable-model-invocation",
|
|
50
|
+
"argument-hint",
|
|
51
|
+
"routing",
|
|
52
|
+
"metadata",
|
|
53
|
+
"owner",
|
|
54
|
+
"last_verified",
|
|
55
|
+
"baseline_version",
|
|
56
|
+
"eval_suite",
|
|
57
|
+
"source_policy",
|
|
58
|
+
}
|
|
59
|
+
GOVERNED_FIELDS = {
|
|
60
|
+
"version",
|
|
61
|
+
"owner",
|
|
62
|
+
"last_verified",
|
|
63
|
+
"baseline_version",
|
|
64
|
+
"eval_suite",
|
|
65
|
+
"source_policy",
|
|
66
|
+
"routing",
|
|
67
|
+
}
|
|
68
|
+
REQUIRED_BODY_SECTIONS = ("boundary", "workflow", "verification")
|
|
69
|
+
CASE_KINDS = {"positive", "negative", "overlap", "pressure"}
|
|
70
|
+
IGNORED_PARTS = {"__pycache__", ".git", ".venv", "node_modules"}
|
|
71
|
+
|
|
72
|
+
SECRET_PATTERNS: tuple[tuple[str, re.Pattern[str]], ...] = (
|
|
73
|
+
("private-key", re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----")),
|
|
74
|
+
("openai-key", re.compile(r"\bsk-[A-Za-z0-9_-]{20,}\b")),
|
|
75
|
+
("github-token", re.compile(r"\bgh[opsu]_[A-Za-z0-9]{20,}\b")),
|
|
76
|
+
("aws-access-key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
|
|
77
|
+
("bearer-token", re.compile(r"\bBearer\s+[A-Za-z0-9._~+/-]{24,}=*\b", re.I)),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass(frozen=True, order=True)
|
|
82
|
+
class Finding:
|
|
83
|
+
severity: str
|
|
84
|
+
code: str
|
|
85
|
+
path: str
|
|
86
|
+
message: str
|
|
87
|
+
remedy: str
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _finding(
|
|
91
|
+
severity: str,
|
|
92
|
+
code: str,
|
|
93
|
+
path: Path | str,
|
|
94
|
+
message: str,
|
|
95
|
+
remedy: str,
|
|
96
|
+
) -> Finding:
|
|
97
|
+
return Finding(
|
|
98
|
+
severity=severity,
|
|
99
|
+
code=code,
|
|
100
|
+
path=str(path).replace("\\", "/"),
|
|
101
|
+
message=message,
|
|
102
|
+
remedy=remedy,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _split_frontmatter(path: Path) -> tuple[dict[str, Any], str]:
|
|
107
|
+
text = path.read_text(encoding="utf-8")
|
|
108
|
+
lines = text.splitlines()
|
|
109
|
+
if not lines or lines[0].strip() != "---":
|
|
110
|
+
raise ValueError("missing opening YAML delimiter `---`")
|
|
111
|
+
for index in range(1, len(lines)):
|
|
112
|
+
if lines[index].strip() == "---":
|
|
113
|
+
raw = yaml.safe_load("\n".join(lines[1:index]))
|
|
114
|
+
if not isinstance(raw, dict):
|
|
115
|
+
raise ValueError("frontmatter must be a YAML mapping")
|
|
116
|
+
return raw, "\n".join(lines[index + 1 :])
|
|
117
|
+
raise ValueError("missing closing YAML delimiter `---`")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _ignored(path: Path) -> bool:
|
|
121
|
+
return path.suffix == ".pyc" or any(part in IGNORED_PARTS for part in path.parts)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _safe_relative(path: Path, root: Path) -> bool:
|
|
125
|
+
try:
|
|
126
|
+
path.resolve().relative_to(root.resolve())
|
|
127
|
+
return True
|
|
128
|
+
except ValueError:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _candidate_reference(raw: str, source: Path, root: Path) -> Path | None:
|
|
133
|
+
cleaned = raw.strip().split("#", 1)[0].split("?", 1)[0]
|
|
134
|
+
if not cleaned or cleaned.startswith(("http://", "https://", "mailto:", "#")):
|
|
135
|
+
return None
|
|
136
|
+
posix = PurePosixPath(cleaned)
|
|
137
|
+
if posix.is_absolute() or ".." in posix.parts:
|
|
138
|
+
return root.parent / "__unsafe_reference__"
|
|
139
|
+
# Category-prefixed resources are skill-root-relative, including when
|
|
140
|
+
# referenced from another resource. Bare links remain document-relative.
|
|
141
|
+
base = root if posix.parts and posix.parts[0] in {"references", "scripts", "assets"} else source.parent
|
|
142
|
+
candidate = (base / Path(*posix.parts)).resolve()
|
|
143
|
+
if not candidate.exists() and len(posix.parts) == 1:
|
|
144
|
+
alternatives = [
|
|
145
|
+
(root / cleaned).resolve(),
|
|
146
|
+
(root / "references" / cleaned).resolve(),
|
|
147
|
+
(root / "scripts" / cleaned).resolve(),
|
|
148
|
+
(root / "assets" / cleaned).resolve(),
|
|
149
|
+
]
|
|
150
|
+
candidate = next((item for item in alternatives if item.exists()), candidate)
|
|
151
|
+
return candidate
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _scan_links(root: Path, markdown_files: list[Path]) -> list[Finding]:
|
|
155
|
+
findings: list[Finding] = []
|
|
156
|
+
mentioned: set[Path] = set()
|
|
157
|
+
for source in markdown_files:
|
|
158
|
+
text = source.read_text(encoding="utf-8")
|
|
159
|
+
raw_refs = [match.group(1).strip().split()[0] for match in MARKDOWN_LINK_RE.finditer(text)]
|
|
160
|
+
raw_refs += [match.group(1).strip() for match in CODE_PATH_RE.finditer(text)]
|
|
161
|
+
for raw in sorted(set(raw_refs)):
|
|
162
|
+
candidate = _candidate_reference(raw, source, root)
|
|
163
|
+
if candidate is None:
|
|
164
|
+
continue
|
|
165
|
+
rel_source = source.relative_to(root)
|
|
166
|
+
if "__unsafe_reference__" in candidate.parts or not _safe_relative(candidate, root):
|
|
167
|
+
findings.append(
|
|
168
|
+
_finding(
|
|
169
|
+
"error",
|
|
170
|
+
"unsafe-reference",
|
|
171
|
+
rel_source,
|
|
172
|
+
f"reference escapes the skill root: {raw}",
|
|
173
|
+
"Use a path-confined relative reference inside this skill package.",
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
elif not candidate.exists():
|
|
177
|
+
findings.append(
|
|
178
|
+
_finding(
|
|
179
|
+
"error",
|
|
180
|
+
"missing-reference",
|
|
181
|
+
rel_source,
|
|
182
|
+
f"referenced resource does not exist: {raw}",
|
|
183
|
+
"Create the referenced file or remove the stale pointer.",
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
elif candidate.is_file():
|
|
187
|
+
mentioned.add(candidate.resolve())
|
|
188
|
+
|
|
189
|
+
resources = [
|
|
190
|
+
path
|
|
191
|
+
for category in ("references", "scripts", "assets")
|
|
192
|
+
for path in (root / category).glob("*")
|
|
193
|
+
if path.is_file() and not path.name.startswith(".") and not _ignored(path.relative_to(root))
|
|
194
|
+
]
|
|
195
|
+
for path in resources:
|
|
196
|
+
if path.resolve() not in mentioned:
|
|
197
|
+
findings.append(
|
|
198
|
+
_finding(
|
|
199
|
+
"warning",
|
|
200
|
+
"unreferenced-resource",
|
|
201
|
+
path.relative_to(root),
|
|
202
|
+
"resource is not discoverable from the skill's Markdown",
|
|
203
|
+
"Link it from SKILL.md with a clear load/run condition or remove it.",
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
return findings
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _scan_secret_signatures(root: Path, files: Iterable[Path]) -> list[Finding]:
|
|
210
|
+
findings: list[Finding] = []
|
|
211
|
+
for path in files:
|
|
212
|
+
try:
|
|
213
|
+
text = path.read_text(encoding="utf-8")
|
|
214
|
+
except (UnicodeDecodeError, OSError):
|
|
215
|
+
continue
|
|
216
|
+
for code, pattern in SECRET_PATTERNS:
|
|
217
|
+
if pattern.search(text):
|
|
218
|
+
findings.append(
|
|
219
|
+
_finding(
|
|
220
|
+
"error",
|
|
221
|
+
f"secret-signature-{code}",
|
|
222
|
+
path.relative_to(root),
|
|
223
|
+
f"content matches a {code} secret signature",
|
|
224
|
+
"Remove and rotate the secret; keep only secret names or placeholders.",
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
return findings
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _criterion_findings(
|
|
231
|
+
criteria: Any,
|
|
232
|
+
*,
|
|
233
|
+
path: str,
|
|
234
|
+
case_id: str,
|
|
235
|
+
) -> list[Finding]:
|
|
236
|
+
findings: list[Finding] = []
|
|
237
|
+
if not isinstance(criteria, list) or not 2 <= len(criteria) <= 8:
|
|
238
|
+
return [
|
|
239
|
+
_finding(
|
|
240
|
+
"error",
|
|
241
|
+
"invalid-fixed-rubric-size",
|
|
242
|
+
path,
|
|
243
|
+
f"case {case_id!r} must contain 2 to 8 atomic criteria",
|
|
244
|
+
"Split the expected behavior into fixed binary criteria before evaluation.",
|
|
245
|
+
)
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
seen: set[str] = set()
|
|
249
|
+
for index, entry in enumerate(criteria, 1):
|
|
250
|
+
if not isinstance(entry, dict):
|
|
251
|
+
findings.append(
|
|
252
|
+
_finding(
|
|
253
|
+
"error",
|
|
254
|
+
"invalid-criterion",
|
|
255
|
+
path,
|
|
256
|
+
f"case {case_id!r} criterion {index} is not an object",
|
|
257
|
+
"Use {id, description, required} for every criterion.",
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
continue
|
|
261
|
+
criterion_id = entry.get("id")
|
|
262
|
+
description = entry.get("description")
|
|
263
|
+
required = entry.get("required")
|
|
264
|
+
if not isinstance(criterion_id, str) or not CRITERION_RE.fullmatch(criterion_id):
|
|
265
|
+
findings.append(
|
|
266
|
+
_finding(
|
|
267
|
+
"error",
|
|
268
|
+
"invalid-criterion-id",
|
|
269
|
+
path,
|
|
270
|
+
f"case {case_id!r} criterion {index} needs descriptive snake_case id",
|
|
271
|
+
"Give every juror the same stable, descriptive criterion ID.",
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
elif criterion_id in seen:
|
|
275
|
+
findings.append(
|
|
276
|
+
_finding(
|
|
277
|
+
"error",
|
|
278
|
+
"duplicate-criterion-id",
|
|
279
|
+
path,
|
|
280
|
+
f"case {case_id!r} repeats criterion id {criterion_id!r}",
|
|
281
|
+
"Use each atomic criterion ID exactly once.",
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
seen.add(criterion_id)
|
|
286
|
+
if not isinstance(description, str) or not description.strip():
|
|
287
|
+
findings.append(
|
|
288
|
+
_finding(
|
|
289
|
+
"error",
|
|
290
|
+
"missing-criterion-description",
|
|
291
|
+
path,
|
|
292
|
+
f"case {case_id!r} criterion {criterion_id!r} has no pass condition",
|
|
293
|
+
"Write one binary `Pass only when ...` description.",
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
elif not description.strip().startswith("Pass only when"):
|
|
297
|
+
findings.append(
|
|
298
|
+
_finding(
|
|
299
|
+
"warning",
|
|
300
|
+
"nonbinary-criterion-description",
|
|
301
|
+
path,
|
|
302
|
+
f"case {case_id!r} criterion {criterion_id!r} is not phrased as a binary pass condition",
|
|
303
|
+
"Start with `Pass only when` and test one observable requirement.",
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
if not isinstance(required, bool):
|
|
307
|
+
findings.append(
|
|
308
|
+
_finding(
|
|
309
|
+
"error",
|
|
310
|
+
"invalid-criterion-required",
|
|
311
|
+
path,
|
|
312
|
+
f"case {case_id!r} criterion {criterion_id!r} required flag is not Boolean",
|
|
313
|
+
"Set required to true or false explicitly.",
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
return findings
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _scan_eval_suite(root: Path, frontmatter: dict[str, Any]) -> list[Finding]:
|
|
320
|
+
findings: list[Finding] = []
|
|
321
|
+
raw_path = frontmatter.get("eval_suite")
|
|
322
|
+
if not raw_path:
|
|
323
|
+
findings.append(
|
|
324
|
+
_finding(
|
|
325
|
+
"warning",
|
|
326
|
+
"missing-eval-suite",
|
|
327
|
+
"SKILL.md",
|
|
328
|
+
"no evaluation suite is declared",
|
|
329
|
+
"Add a fixed-rubric suite for objective or operational skills, or document a human review method.",
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
return findings
|
|
333
|
+
|
|
334
|
+
eval_path = (root / str(raw_path)).resolve()
|
|
335
|
+
if not _safe_relative(eval_path, root):
|
|
336
|
+
return [
|
|
337
|
+
_finding(
|
|
338
|
+
"error",
|
|
339
|
+
"unsafe-eval-suite-path",
|
|
340
|
+
"SKILL.md",
|
|
341
|
+
f"eval_suite escapes the skill root: {raw_path}",
|
|
342
|
+
"Use a relative path inside the skill package.",
|
|
343
|
+
)
|
|
344
|
+
]
|
|
345
|
+
if not eval_path.is_file():
|
|
346
|
+
return [
|
|
347
|
+
_finding(
|
|
348
|
+
"error",
|
|
349
|
+
"missing-eval-suite-file",
|
|
350
|
+
"SKILL.md",
|
|
351
|
+
f"eval_suite does not exist: {raw_path}",
|
|
352
|
+
"Create the declared evaluation file or correct the path.",
|
|
353
|
+
)
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
data = json.loads(eval_path.read_text(encoding="utf-8"))
|
|
358
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
359
|
+
return [
|
|
360
|
+
_finding(
|
|
361
|
+
"error",
|
|
362
|
+
"invalid-eval-suite-json",
|
|
363
|
+
eval_path.relative_to(root),
|
|
364
|
+
str(exc),
|
|
365
|
+
"Write valid UTF-8 JSON using a versioned evaluation schema.",
|
|
366
|
+
)
|
|
367
|
+
]
|
|
368
|
+
if not isinstance(data, dict):
|
|
369
|
+
return [
|
|
370
|
+
_finding(
|
|
371
|
+
"error",
|
|
372
|
+
"invalid-eval-suite-shape",
|
|
373
|
+
eval_path.relative_to(root),
|
|
374
|
+
"evaluation suite must be a JSON object",
|
|
375
|
+
"Use an object containing skill_name and cases/evals.",
|
|
376
|
+
)
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
if data.get("skill_name") not in (None, frontmatter.get("name")):
|
|
380
|
+
findings.append(
|
|
381
|
+
_finding(
|
|
382
|
+
"error",
|
|
383
|
+
"eval-skill-name-mismatch",
|
|
384
|
+
eval_path.relative_to(root),
|
|
385
|
+
f"skill_name {data.get('skill_name')!r} does not match frontmatter",
|
|
386
|
+
"Use the exact skill name in both files.",
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
if data.get("status") == "draft":
|
|
390
|
+
findings.append(
|
|
391
|
+
_finding(
|
|
392
|
+
"warning",
|
|
393
|
+
"draft-eval-suite",
|
|
394
|
+
eval_path.relative_to(root),
|
|
395
|
+
"evaluation suite is still marked draft",
|
|
396
|
+
"Replace placeholders, run the cases, and remove draft status before publication.",
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
cases = data.get("cases") if isinstance(data.get("cases"), list) else data.get("evals")
|
|
401
|
+
if not isinstance(cases, list) or not cases:
|
|
402
|
+
findings.append(
|
|
403
|
+
_finding(
|
|
404
|
+
"error",
|
|
405
|
+
"missing-eval-cases",
|
|
406
|
+
eval_path.relative_to(root),
|
|
407
|
+
"evaluation suite has no cases",
|
|
408
|
+
"Add realistic positive, negative, overlap, and pressure cases.",
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
return findings
|
|
412
|
+
|
|
413
|
+
ids: set[str] = set()
|
|
414
|
+
kinds: set[str] = set()
|
|
415
|
+
for index, case in enumerate(cases, 1):
|
|
416
|
+
if not isinstance(case, dict):
|
|
417
|
+
findings.append(
|
|
418
|
+
_finding(
|
|
419
|
+
"error",
|
|
420
|
+
"invalid-eval-case",
|
|
421
|
+
eval_path.relative_to(root),
|
|
422
|
+
f"case {index} is not an object",
|
|
423
|
+
"Use an object with id, prompt, kind, routing expectation, and fixed rubric.",
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
continue
|
|
427
|
+
case_id = str(case.get("id") or "").strip()
|
|
428
|
+
if not case_id:
|
|
429
|
+
findings.append(
|
|
430
|
+
_finding(
|
|
431
|
+
"error",
|
|
432
|
+
"missing-eval-case-id",
|
|
433
|
+
eval_path.relative_to(root),
|
|
434
|
+
f"case {index} has no id",
|
|
435
|
+
"Assign a stable descriptive ID.",
|
|
436
|
+
)
|
|
437
|
+
)
|
|
438
|
+
case_id = f"<case-{index}>"
|
|
439
|
+
elif case_id in ids:
|
|
440
|
+
findings.append(
|
|
441
|
+
_finding(
|
|
442
|
+
"error",
|
|
443
|
+
"duplicate-eval-case-id",
|
|
444
|
+
eval_path.relative_to(root),
|
|
445
|
+
f"duplicate case id {case_id!r}",
|
|
446
|
+
"Use each case ID exactly once.",
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
ids.add(case_id)
|
|
450
|
+
|
|
451
|
+
prompt = case.get("prompt")
|
|
452
|
+
if not isinstance(prompt, str) or not prompt.strip():
|
|
453
|
+
findings.append(
|
|
454
|
+
_finding(
|
|
455
|
+
"error",
|
|
456
|
+
"missing-eval-prompt",
|
|
457
|
+
eval_path.relative_to(root),
|
|
458
|
+
f"case {case_id!r} has no realistic prompt",
|
|
459
|
+
"Add the exact task a real user would submit.",
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
kind = case.get("kind")
|
|
463
|
+
if isinstance(kind, str):
|
|
464
|
+
kinds.add(kind)
|
|
465
|
+
if kind not in CASE_KINDS:
|
|
466
|
+
findings.append(
|
|
467
|
+
_finding(
|
|
468
|
+
"warning",
|
|
469
|
+
"unknown-eval-kind",
|
|
470
|
+
eval_path.relative_to(root),
|
|
471
|
+
f"case {case_id!r} uses unrecognized kind {kind!r}",
|
|
472
|
+
"Use positive, negative, overlap, or pressure, or document the extension.",
|
|
473
|
+
)
|
|
474
|
+
)
|
|
475
|
+
else:
|
|
476
|
+
findings.append(
|
|
477
|
+
_finding(
|
|
478
|
+
"warning",
|
|
479
|
+
"missing-eval-kind",
|
|
480
|
+
eval_path.relative_to(root),
|
|
481
|
+
f"case {case_id!r} has no kind",
|
|
482
|
+
"Classify the case so corpus coverage can be audited.",
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
expected = case.get("expected_output")
|
|
487
|
+
if isinstance(expected, str):
|
|
488
|
+
findings.append(
|
|
489
|
+
_finding(
|
|
490
|
+
"error",
|
|
491
|
+
"dynamic-jury-rubric",
|
|
492
|
+
eval_path.relative_to(root),
|
|
493
|
+
f"case {case_id!r} stores only prose expected_output",
|
|
494
|
+
"Freeze ordered atomic criterion IDs, descriptions, and required flags for every juror.",
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
continue
|
|
498
|
+
if not isinstance(expected, dict):
|
|
499
|
+
findings.append(
|
|
500
|
+
_finding(
|
|
501
|
+
"error",
|
|
502
|
+
"missing-fixed-rubric",
|
|
503
|
+
eval_path.relative_to(root),
|
|
504
|
+
f"case {case_id!r} has no fixed rubric object",
|
|
505
|
+
"Add expected_output.rubric_version and expected_output.criteria.",
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
continue
|
|
509
|
+
if not isinstance(expected.get("rubric_version"), int):
|
|
510
|
+
findings.append(
|
|
511
|
+
_finding(
|
|
512
|
+
"error",
|
|
513
|
+
"missing-rubric-version",
|
|
514
|
+
eval_path.relative_to(root),
|
|
515
|
+
f"case {case_id!r} has no integer rubric_version",
|
|
516
|
+
"Version the fixed rubric so completed evidence cannot be silently reinterpreted.",
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
findings.extend(
|
|
520
|
+
_criterion_findings(
|
|
521
|
+
expected.get("criteria"),
|
|
522
|
+
path=str(eval_path.relative_to(root)),
|
|
523
|
+
case_id=case_id,
|
|
524
|
+
)
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
missing_kinds = sorted(CASE_KINDS - kinds)
|
|
528
|
+
if missing_kinds:
|
|
529
|
+
findings.append(
|
|
530
|
+
_finding(
|
|
531
|
+
"warning",
|
|
532
|
+
"incomplete-eval-case-coverage",
|
|
533
|
+
eval_path.relative_to(root),
|
|
534
|
+
"missing case kinds: " + ", ".join(missing_kinds),
|
|
535
|
+
"Cover positive, negative, nearest-overlap, and shortcut-pressure behavior.",
|
|
536
|
+
)
|
|
537
|
+
)
|
|
538
|
+
return findings
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def audit_skill(root: Path) -> dict[str, Any]:
|
|
542
|
+
root = root.expanduser().resolve()
|
|
543
|
+
skill_md = root if root.name == "SKILL.md" else root / "SKILL.md"
|
|
544
|
+
if root.name == "SKILL.md":
|
|
545
|
+
root = root.parent
|
|
546
|
+
findings: list[Finding] = []
|
|
547
|
+
|
|
548
|
+
if not skill_md.is_file():
|
|
549
|
+
findings.append(
|
|
550
|
+
_finding(
|
|
551
|
+
"error",
|
|
552
|
+
"missing-skill-md",
|
|
553
|
+
"SKILL.md",
|
|
554
|
+
f"no SKILL.md found under {root}",
|
|
555
|
+
"Create a skill directory containing SKILL.md.",
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
return _report(root, None, findings)
|
|
559
|
+
|
|
560
|
+
try:
|
|
561
|
+
frontmatter, body = _split_frontmatter(skill_md)
|
|
562
|
+
except (OSError, UnicodeError, yaml.YAMLError, ValueError) as exc:
|
|
563
|
+
findings.append(
|
|
564
|
+
_finding(
|
|
565
|
+
"error",
|
|
566
|
+
"invalid-frontmatter",
|
|
567
|
+
"SKILL.md",
|
|
568
|
+
str(exc),
|
|
569
|
+
"Repair the YAML delimiters and mapping before any other review.",
|
|
570
|
+
)
|
|
571
|
+
)
|
|
572
|
+
return _report(root, None, findings)
|
|
573
|
+
|
|
574
|
+
name = str(frontmatter.get("name") or "").strip()
|
|
575
|
+
description = " ".join(str(frontmatter.get("description") or "").split())
|
|
576
|
+
if not name:
|
|
577
|
+
findings.append(_finding("error", "missing-name", "SKILL.md", "name is required", "Add a portable skill name."))
|
|
578
|
+
elif len(name) > 64 or not NAME_RE.fullmatch(name):
|
|
579
|
+
findings.append(
|
|
580
|
+
_finding(
|
|
581
|
+
"error",
|
|
582
|
+
"invalid-name",
|
|
583
|
+
"SKILL.md",
|
|
584
|
+
"name must be <=64 lowercase letters, digits, and hyphens",
|
|
585
|
+
"Normalize the name and make the folder match it.",
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
elif root.name != name:
|
|
589
|
+
findings.append(
|
|
590
|
+
_finding(
|
|
591
|
+
"error",
|
|
592
|
+
"folder-name-mismatch",
|
|
593
|
+
"SKILL.md",
|
|
594
|
+
f"folder {root.name!r} does not match name {name!r}",
|
|
595
|
+
"Rename the folder or frontmatter so they match exactly.",
|
|
596
|
+
)
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
if not description:
|
|
600
|
+
findings.append(_finding("error", "missing-description", "SKILL.md", "description is required", "Describe capability and concrete trigger conditions."))
|
|
601
|
+
else:
|
|
602
|
+
if len(description) > 1024:
|
|
603
|
+
findings.append(
|
|
604
|
+
_finding(
|
|
605
|
+
"error",
|
|
606
|
+
"description-too-long",
|
|
607
|
+
"SKILL.md",
|
|
608
|
+
f"description is {len(description)} characters; maximum is 1024",
|
|
609
|
+
"Keep only capability, trigger context, and nearest exclusion.",
|
|
610
|
+
)
|
|
611
|
+
)
|
|
612
|
+
if not TRIGGER_RE.search(description):
|
|
613
|
+
findings.append(
|
|
614
|
+
_finding(
|
|
615
|
+
"warning",
|
|
616
|
+
"description-missing-trigger-context",
|
|
617
|
+
"SKILL.md",
|
|
618
|
+
"description does not clearly say when to use the skill",
|
|
619
|
+
"Add concrete `Use when ...` or equivalent trigger language.",
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
if HYPE_RE.search(description):
|
|
623
|
+
findings.append(
|
|
624
|
+
_finding(
|
|
625
|
+
"warning",
|
|
626
|
+
"description-hype",
|
|
627
|
+
"SKILL.md",
|
|
628
|
+
"description contains promotional or persona language",
|
|
629
|
+
"Replace self-praise with concrete capability and boundary terms.",
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
unknown = sorted(set(frontmatter) - ALLOWED_FRONTMATTER)
|
|
634
|
+
for key in unknown:
|
|
635
|
+
findings.append(
|
|
636
|
+
_finding(
|
|
637
|
+
"warning",
|
|
638
|
+
"unknown-frontmatter-key",
|
|
639
|
+
"SKILL.md",
|
|
640
|
+
f"frontmatter key may be host-specific or unsupported: {key}",
|
|
641
|
+
"Confirm every target client accepts it or move it under documented metadata.",
|
|
642
|
+
)
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
governed = bool(set(frontmatter) & GOVERNED_FIELDS)
|
|
646
|
+
if governed:
|
|
647
|
+
for key in sorted(GOVERNED_FIELDS):
|
|
648
|
+
if frontmatter.get(key) in (None, "", [], {}):
|
|
649
|
+
findings.append(
|
|
650
|
+
_finding(
|
|
651
|
+
"error",
|
|
652
|
+
f"missing-governed-{key.replace('_', '-')}",
|
|
653
|
+
"SKILL.md",
|
|
654
|
+
f"governed skill is missing {key}",
|
|
655
|
+
"Complete the repository governance metadata or use a portable profile consistently.",
|
|
656
|
+
)
|
|
657
|
+
)
|
|
658
|
+
if frontmatter.get("last_verified"):
|
|
659
|
+
try:
|
|
660
|
+
verified = date.fromisoformat(str(frontmatter["last_verified"]))
|
|
661
|
+
if verified > date.today():
|
|
662
|
+
findings.append(
|
|
663
|
+
_finding(
|
|
664
|
+
"warning",
|
|
665
|
+
"future-last-verified",
|
|
666
|
+
"SKILL.md",
|
|
667
|
+
"last_verified is in the future",
|
|
668
|
+
"Use the date on which sources and behavior were actually checked.",
|
|
669
|
+
)
|
|
670
|
+
)
|
|
671
|
+
except ValueError:
|
|
672
|
+
findings.append(
|
|
673
|
+
_finding(
|
|
674
|
+
"error",
|
|
675
|
+
"invalid-last-verified",
|
|
676
|
+
"SKILL.md",
|
|
677
|
+
"last_verified must be YYYY-MM-DD",
|
|
678
|
+
"Use an ISO calendar date.",
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
routing = frontmatter.get("routing")
|
|
682
|
+
if isinstance(routing, dict):
|
|
683
|
+
triggers = routing.get("triggers")
|
|
684
|
+
if not isinstance(triggers, list) or not all(
|
|
685
|
+
isinstance(item, str) and item.strip() for item in triggers
|
|
686
|
+
):
|
|
687
|
+
findings.append(
|
|
688
|
+
_finding(
|
|
689
|
+
"error",
|
|
690
|
+
"invalid-routing-triggers",
|
|
691
|
+
"SKILL.md",
|
|
692
|
+
"routing.triggers must be a non-empty list of strings",
|
|
693
|
+
"Add concrete curated trigger phrases.",
|
|
694
|
+
)
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
metadata = frontmatter.get("metadata")
|
|
698
|
+
if isinstance(metadata, dict) and metadata.get("status") == "draft":
|
|
699
|
+
findings.append(
|
|
700
|
+
_finding(
|
|
701
|
+
"warning",
|
|
702
|
+
"draft-skill",
|
|
703
|
+
"SKILL.md",
|
|
704
|
+
"skill metadata is still marked draft",
|
|
705
|
+
"Remove draft status only after placeholders and required proof are complete.",
|
|
706
|
+
)
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
body_lines = len(body.splitlines())
|
|
710
|
+
if body_lines > 500:
|
|
711
|
+
findings.append(
|
|
712
|
+
_finding(
|
|
713
|
+
"error",
|
|
714
|
+
"body-too-long",
|
|
715
|
+
"SKILL.md",
|
|
716
|
+
f"body has {body_lines} lines; maximum is 500",
|
|
717
|
+
"Move optional detail into directly linked references.",
|
|
718
|
+
)
|
|
719
|
+
)
|
|
720
|
+
elif body_lines > 250:
|
|
721
|
+
findings.append(
|
|
722
|
+
_finding(
|
|
723
|
+
"warning",
|
|
724
|
+
"body-context-heavy",
|
|
725
|
+
"SKILL.md",
|
|
726
|
+
f"body has {body_lines} lines; target is <=250",
|
|
727
|
+
"Keep only always-needed decisions and progressively disclose the rest.",
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
headings = {
|
|
732
|
+
match.group(1).strip().lower()
|
|
733
|
+
for match in re.finditer(r"^##\s+(.+?)\s*$", body, re.M)
|
|
734
|
+
}
|
|
735
|
+
for required in REQUIRED_BODY_SECTIONS:
|
|
736
|
+
if not any(required in heading for heading in headings):
|
|
737
|
+
findings.append(
|
|
738
|
+
_finding(
|
|
739
|
+
"warning",
|
|
740
|
+
f"missing-body-{required}",
|
|
741
|
+
"SKILL.md",
|
|
742
|
+
f"no H2 section communicates the {required} contract",
|
|
743
|
+
f"Add a concise {required.title()} section or make the equivalent section explicit.",
|
|
744
|
+
)
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
if PLACEHOLDER_RE.search(body):
|
|
748
|
+
findings.append(
|
|
749
|
+
_finding(
|
|
750
|
+
"error",
|
|
751
|
+
"draft-placeholder",
|
|
752
|
+
"SKILL.md",
|
|
753
|
+
"body contains an unresolved placeholder or TODO marker",
|
|
754
|
+
"Replace every placeholder with grounded instructions before publication.",
|
|
755
|
+
)
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
all_files = [
|
|
759
|
+
path for path in root.rglob("*") if path.is_file() and not _ignored(path.relative_to(root))
|
|
760
|
+
]
|
|
761
|
+
for path in root.rglob("*"):
|
|
762
|
+
if _ignored(path.relative_to(root)):
|
|
763
|
+
continue
|
|
764
|
+
rel = path.relative_to(root)
|
|
765
|
+
if path.is_symlink():
|
|
766
|
+
findings.append(
|
|
767
|
+
_finding(
|
|
768
|
+
"error",
|
|
769
|
+
"symlink-resource",
|
|
770
|
+
rel,
|
|
771
|
+
"skill packages must not use symlinks",
|
|
772
|
+
"Copy or generate a path-confined resource instead.",
|
|
773
|
+
)
|
|
774
|
+
)
|
|
775
|
+
if rel.parts and rel.parts[0] in {"references", "scripts", "assets"} and len(rel.parts) > 2:
|
|
776
|
+
findings.append(
|
|
777
|
+
_finding(
|
|
778
|
+
"error",
|
|
779
|
+
"nested-resource",
|
|
780
|
+
rel,
|
|
781
|
+
"resource is nested more than one level below its category",
|
|
782
|
+
"Flatten the resource and link it directly from SKILL.md.",
|
|
783
|
+
)
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
markdown_files: list[Path] = []
|
|
787
|
+
for path in all_files:
|
|
788
|
+
if path.suffix.lower() in {".md", ".markdown"}:
|
|
789
|
+
try:
|
|
790
|
+
text = path.read_text(encoding="utf-8")
|
|
791
|
+
except (OSError, UnicodeError) as exc:
|
|
792
|
+
findings.append(
|
|
793
|
+
_finding(
|
|
794
|
+
"error",
|
|
795
|
+
"unreadable-markdown",
|
|
796
|
+
path.relative_to(root),
|
|
797
|
+
str(exc),
|
|
798
|
+
"Store the resource as readable UTF-8 text.",
|
|
799
|
+
)
|
|
800
|
+
)
|
|
801
|
+
continue
|
|
802
|
+
markdown_files.append(path)
|
|
803
|
+
if path.parent.name == "references" and len(text.splitlines()) > 100 and not TOC_RE.search(text):
|
|
804
|
+
findings.append(
|
|
805
|
+
_finding(
|
|
806
|
+
"warning",
|
|
807
|
+
"long-reference-without-contents",
|
|
808
|
+
path.relative_to(root),
|
|
809
|
+
"reference exceeds 100 lines without a contents heading",
|
|
810
|
+
"Add a concise contents list so the reader can navigate selectively.",
|
|
811
|
+
)
|
|
812
|
+
)
|
|
813
|
+
if path != skill_md and PLACEHOLDER_RE.search(text):
|
|
814
|
+
findings.append(
|
|
815
|
+
_finding(
|
|
816
|
+
"error",
|
|
817
|
+
"resource-placeholder",
|
|
818
|
+
path.relative_to(root),
|
|
819
|
+
"resource contains an unresolved placeholder or TODO marker",
|
|
820
|
+
"Replace the placeholder or mark the whole skill as a draft outside publication paths.",
|
|
821
|
+
)
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
for path in (root / "scripts").glob("*") if (root / "scripts").is_dir() else []:
|
|
825
|
+
if not path.is_file():
|
|
826
|
+
continue
|
|
827
|
+
if path.suffix.lower() in {".py", ".sh"}:
|
|
828
|
+
try:
|
|
829
|
+
first = path.read_text(encoding="utf-8").splitlines()[0]
|
|
830
|
+
except (OSError, UnicodeError, IndexError):
|
|
831
|
+
first = ""
|
|
832
|
+
if not first.startswith("#!"):
|
|
833
|
+
findings.append(
|
|
834
|
+
_finding(
|
|
835
|
+
"warning",
|
|
836
|
+
"script-missing-shebang",
|
|
837
|
+
path.relative_to(root),
|
|
838
|
+
"executable helper has no shebang",
|
|
839
|
+
"Add an interpreter shebang and document dependencies.",
|
|
840
|
+
)
|
|
841
|
+
)
|
|
842
|
+
if os.name != "nt" and path.suffix.lower() in {".py", ".sh"} and not os.access(path, os.X_OK):
|
|
843
|
+
findings.append(
|
|
844
|
+
_finding(
|
|
845
|
+
"warning",
|
|
846
|
+
"script-not-executable",
|
|
847
|
+
path.relative_to(root),
|
|
848
|
+
"helper is not executable on this filesystem",
|
|
849
|
+
"Set the executable bit when the repository and target client preserve it.",
|
|
850
|
+
)
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
findings.extend(_scan_links(root, markdown_files))
|
|
854
|
+
findings.extend(_scan_secret_signatures(root, all_files))
|
|
855
|
+
findings.extend(_scan_eval_suite(root, frontmatter))
|
|
856
|
+
return _report(root, name or None, findings)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def _report(root: Path, name: str | None, findings: list[Finding]) -> dict[str, Any]:
|
|
860
|
+
unique = sorted(set(findings))
|
|
861
|
+
errors = sum(item.severity == "error" for item in unique)
|
|
862
|
+
warnings = sum(item.severity == "warning" for item in unique)
|
|
863
|
+
score = max(0, 100 - errors * 20 - warnings * 3)
|
|
864
|
+
return {
|
|
865
|
+
"schema_version": 1,
|
|
866
|
+
"skill": name or root.name,
|
|
867
|
+
"root": str(root),
|
|
868
|
+
"passed": errors == 0,
|
|
869
|
+
"errors": errors,
|
|
870
|
+
"warnings": warnings,
|
|
871
|
+
"quality_score": score,
|
|
872
|
+
"findings": [asdict(item) for item in unique],
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def _discover(inputs: list[Path]) -> list[Path]:
|
|
877
|
+
roots: list[Path] = []
|
|
878
|
+
for raw in inputs:
|
|
879
|
+
path = raw.expanduser().resolve()
|
|
880
|
+
if path.name == "SKILL.md" and path.is_file():
|
|
881
|
+
roots.append(path.parent)
|
|
882
|
+
elif (path / "SKILL.md").is_file():
|
|
883
|
+
roots.append(path)
|
|
884
|
+
elif path.is_dir():
|
|
885
|
+
roots.extend(sorted(item.parent for item in path.glob("*/SKILL.md")))
|
|
886
|
+
else:
|
|
887
|
+
roots.append(path)
|
|
888
|
+
ordered: list[Path] = []
|
|
889
|
+
seen: set[Path] = set()
|
|
890
|
+
for root in roots:
|
|
891
|
+
if root not in seen:
|
|
892
|
+
seen.add(root)
|
|
893
|
+
ordered.append(root)
|
|
894
|
+
return ordered
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
def _render_text(reports: list[dict[str, Any]]) -> str:
|
|
898
|
+
lines: list[str] = []
|
|
899
|
+
for report in reports:
|
|
900
|
+
lines.append(
|
|
901
|
+
f"{report['skill']}: {report['errors']} error(s), "
|
|
902
|
+
f"{report['warnings']} warning(s), score {report['quality_score']}"
|
|
903
|
+
)
|
|
904
|
+
for item in report["findings"]:
|
|
905
|
+
lines.append(
|
|
906
|
+
f" {item['severity'].upper()} {item['code']} "
|
|
907
|
+
f"{item['path']}: {item['message']}"
|
|
908
|
+
)
|
|
909
|
+
return "\n".join(lines)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def main(argv: list[str] | None = None) -> int:
|
|
913
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
914
|
+
parser.add_argument("paths", nargs="+", type=Path)
|
|
915
|
+
parser.add_argument("--strict", action="store_true", help="treat warnings as blocking")
|
|
916
|
+
parser.add_argument("--format", choices=("json", "text"), default="json")
|
|
917
|
+
parser.add_argument("--write-report", type=Path)
|
|
918
|
+
args = parser.parse_args(argv)
|
|
919
|
+
|
|
920
|
+
roots = _discover(args.paths)
|
|
921
|
+
if not roots:
|
|
922
|
+
payload = {
|
|
923
|
+
"schema_version": 1,
|
|
924
|
+
"passed": False,
|
|
925
|
+
"error": {"code": "no_skills_found", "message": "no skill packages found"},
|
|
926
|
+
}
|
|
927
|
+
print(json.dumps(payload, ensure_ascii=False, separators=(",", ":")))
|
|
928
|
+
return 1
|
|
929
|
+
|
|
930
|
+
reports = [audit_skill(root) for root in roots]
|
|
931
|
+
total_errors = sum(report["errors"] for report in reports)
|
|
932
|
+
total_warnings = sum(report["warnings"] for report in reports)
|
|
933
|
+
passed = total_errors == 0 and (not args.strict or total_warnings == 0)
|
|
934
|
+
payload = {
|
|
935
|
+
"schema_version": 1,
|
|
936
|
+
"passed": passed,
|
|
937
|
+
"strict": args.strict,
|
|
938
|
+
"skill_count": len(reports),
|
|
939
|
+
"errors": total_errors,
|
|
940
|
+
"warnings": total_warnings,
|
|
941
|
+
"reports": reports,
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if args.write_report:
|
|
945
|
+
args.write_report.parent.mkdir(parents=True, exist_ok=True)
|
|
946
|
+
args.write_report.write_text(
|
|
947
|
+
json.dumps(payload, indent=2, ensure_ascii=False, sort_keys=True) + "\n",
|
|
948
|
+
encoding="utf-8",
|
|
949
|
+
newline="\n",
|
|
950
|
+
)
|
|
951
|
+
if args.format == "text":
|
|
952
|
+
print(_render_text(reports))
|
|
953
|
+
else:
|
|
954
|
+
print(json.dumps(payload, ensure_ascii=False, separators=(",", ":")))
|
|
955
|
+
return 0 if passed else 1
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
if __name__ == "__main__":
|
|
959
|
+
raise SystemExit(main())
|