create-yss-spec 3.5.2 → 3.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/template/asset-runtime.js +3 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/codebase-design/SKILL.md +4 -0
- package/template/{.codex/skills/improve-codebase-architecture/SKILL.md → .agents/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.agents/skills/frontend-commit/SKILL.md +4 -93
- package/template/.agents/skills/git-commit-core/SKILL.md +18 -0
- package/template/.agents/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.agents/skills/llm-wiki/SKILL.md +12 -13
- package/template/.agents/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.agents/skills/llm-wiki/references/compile.md +21 -114
- package/template/.agents/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.agents/skills/llm-wiki/references/lint.md +9 -46
- package/template/.agents/skills/llm-wiki/references/query.md +9 -18
- package/template/.agents/skills/llm-wiki/references/schema.md +56 -29
- package/template/.agents/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.agents/skills/llm-wiki/references/writing.md +4 -4
- package/template/.agents/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.agents/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.agents/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.agents/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.agents/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.agents/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.agents/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.agents/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.agents/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.agents/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.agents/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.agents/skills/prototype/LOGIC.md +1 -1
- package/template/.agents/skills/prototype/SKILL.md +5 -19
- package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.agents/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.agents/skills/yss-research/SKILL.md +4 -0
- package/template/.codex/skills/codebase-design/SKILL.md +4 -0
- package/template/{.pi/skills/improve-codebase-architecture/SKILL.md → .codex/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.codex/skills/frontend-commit/SKILL.md +4 -93
- package/template/.codex/skills/git-commit-core/SKILL.md +18 -0
- package/template/.codex/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.codex/skills/llm-wiki/SKILL.md +12 -13
- package/template/.codex/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.codex/skills/llm-wiki/references/compile.md +21 -114
- package/template/.codex/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.codex/skills/llm-wiki/references/lint.md +9 -46
- package/template/.codex/skills/llm-wiki/references/query.md +9 -18
- package/template/.codex/skills/llm-wiki/references/schema.md +56 -29
- package/template/.codex/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.codex/skills/llm-wiki/references/writing.md +4 -4
- package/template/.codex/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.codex/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.codex/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.codex/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.codex/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.codex/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.codex/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.codex/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.codex/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.codex/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.codex/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.codex/skills/prototype/LOGIC.md +1 -1
- package/template/.codex/skills/prototype/SKILL.md +5 -19
- package/template/.codex/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.codex/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.codex/skills/yss-research/SKILL.md +4 -0
- package/template/.cursor/skills/codebase-design/SKILL.md +4 -0
- package/template/.cursor/skills/{improve-codebase-architecture/SKILL.md → codebase-design/references/architecture-audit.md} +3 -9
- package/template/.cursor/skills/frontend-commit/SKILL.md +4 -93
- package/template/.cursor/skills/git-commit-core/SKILL.md +18 -0
- package/template/.cursor/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.cursor/skills/llm-wiki/SKILL.md +12 -13
- package/template/.cursor/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.cursor/skills/llm-wiki/references/compile.md +21 -114
- package/template/.cursor/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.cursor/skills/llm-wiki/references/lint.md +9 -46
- package/template/.cursor/skills/llm-wiki/references/query.md +9 -18
- package/template/.cursor/skills/llm-wiki/references/schema.md +56 -29
- package/template/.cursor/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.cursor/skills/llm-wiki/references/writing.md +4 -4
- package/template/.cursor/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.cursor/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.cursor/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.cursor/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.cursor/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.cursor/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.cursor/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.cursor/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.cursor/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.cursor/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.cursor/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.cursor/skills/prototype/LOGIC.md +1 -1
- package/template/.cursor/skills/prototype/SKILL.md +5 -19
- package/template/.cursor/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.cursor/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.cursor/skills/yss-research/SKILL.md +4 -0
- package/template/.pi/skills/codebase-design/SKILL.md +4 -0
- package/template/{.agents/skills/improve-codebase-architecture/SKILL.md → .pi/skills/codebase-design/references/architecture-audit.md} +3 -9
- package/template/.pi/skills/frontend-commit/SKILL.md +4 -93
- package/template/.pi/skills/git-commit-core/SKILL.md +18 -0
- package/template/.pi/skills/java-backend-commit/SKILL.md +4 -96
- package/template/.pi/skills/llm-wiki/SKILL.md +12 -13
- package/template/.pi/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
- package/template/.pi/skills/llm-wiki/references/compile.md +21 -114
- package/template/.pi/skills/llm-wiki/references/ingest.md +8 -33
- package/template/.pi/skills/llm-wiki/references/lint.md +9 -46
- package/template/.pi/skills/llm-wiki/references/query.md +9 -18
- package/template/.pi/skills/llm-wiki/references/schema.md +56 -29
- package/template/.pi/skills/llm-wiki/references/transactions.md +48 -0
- package/template/.pi/skills/llm-wiki/references/writing.md +4 -4
- package/template/.pi/skills/llm-wiki/scripts/advise.mjs +46 -58
- package/template/.pi/skills/llm-wiki/scripts/advise.test.mjs +11 -16
- package/template/.pi/skills/llm-wiki/scripts/core.mjs +262 -0
- package/template/.pi/skills/llm-wiki/scripts/extract.mjs +35 -18
- package/template/.pi/skills/llm-wiki/scripts/extract.test.mjs +5 -8
- package/template/.pi/skills/llm-wiki/scripts/feedback.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/inventory.mjs +43 -171
- package/template/.pi/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
- package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
- package/template/.pi/skills/llm-wiki/scripts/migrate.mjs +51 -0
- package/template/.pi/skills/llm-wiki/scripts/query.mjs +149 -0
- package/template/.pi/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
- package/template/.pi/skills/llm-wiki/scripts/sources.mjs +166 -0
- package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +384 -0
- package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +354 -0
- package/template/.pi/skills/prototype/LOGIC.md +1 -1
- package/template/.pi/skills/prototype/SKILL.md +5 -19
- package/template/.pi/skills/setup-matt-pocock-skills/SKILL.md +3 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +2 -3
- package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +8 -8
- package/template/.pi/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +5 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +20 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +1 -1
- package/template/.pi/skills/yss-research/SKILL.md +4 -0
- package/template/.template-spec/agents/README.md +1 -1
- package/template/.template-spec/agents/digital-human-roles.yaml +1 -1
- package/template/.template-spec/agents/skill-migrations.md +15 -0
- package/template/.template-spec/agents/yss-skill-registry.yaml +10 -29
- package/template/.template-spec/architecture/README.md +3 -34
- package/template/.template-spec/plan/templates/competitive-analysis-template.md +2 -2
- package/template/.template-spec/process/contract-reading.md +24 -0
- package/template/.template-spec/process/lifecycle-registry-baseline.json +3 -1
- package/template/.template-spec/process/lifecycle-registry.yaml +6 -0
- package/template/.template-spec/process/research-completion.md +23 -0
- package/template/.template-spec/process/schemas/digital-human-task-package.schema.json +202 -12
- package/template/.template-spec/process/schemas/frontend-implementation-evidence.schema.json +7 -15
- package/template/.template-spec/process/subagent-collaboration.md +8 -0
- package/template/.template-spec/process/templates/frontend-implementation-verification-template.yaml +15 -1
- package/template/AGENTS.md +1 -1
- package/template/CONTEXT.md +5 -1
- package/template/README.md +12 -24
- package/template/scripts/contract +5 -5
- package/template/scripts/lib/contract-views.mjs +57 -2
- package/template/scripts/lib/execution-evidence.mjs +51 -0
- package/template/scripts/lib/harness-execution-scope.mjs +1 -1
- package/template/scripts/lib/implementation-contract-compiler.mjs +2 -0
- package/template/scripts/lib/lifecycle-context-query.mjs +15 -3
- package/template/scripts/lib/lifecycle-status.mjs +32 -6
- package/template/scripts/lib/lifecycle-transition.mjs +19 -1
- package/template/scripts/lib/maintenance-research.mjs +63 -0
- package/template/scripts/lib/read-only-intake.mjs +132 -0
- package/template/scripts/lib/skill-registry.mjs +2 -1
- package/template/scripts/lib/skill-supply-chain.mjs +4 -0
- package/template/scripts/lib/slice-task-package.mjs +1 -1
- package/template/scripts/lib/task-package.mjs +9 -2
- package/template/scripts/lifecycle-status +2 -2
- package/template/scripts/prepare-read-only-intake +16 -0
- package/template/scripts/run-read-only-intake +14 -0
- package/template/scripts/verify-digital-human-task-package +2 -2
- package/template/scripts/verify-frontend-implementation-evidence +24 -1
- package/template/scripts/verify-maintenance-research +13 -0
- package/template/skills-lock.json +19 -76
- package/template.manifest.json +118 -29
- package/template.snapshot.json +5 -6
- package/template/.agents/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.agents/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.agents/skills/prototype/UI.md +0 -112
- package/template/.agents/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.agents/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.agents/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.agents/skills/wait-what/SKILL.md +0 -7
- package/template/.agents/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.codex/skills/data-analytics/.app.json +0 -84
- package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +0 -66
- package/template/.codex/skills/data-analytics/.mcp.json +0 -30
- package/template/.codex/skills/data-analytics/AGENTS.md +0 -20
- package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +0 -27
- package/template/.codex/skills/data-analytics/README.md +0 -60
- package/template/.codex/skills/data-analytics/__yss_dotfile__.gitignore +0 -2
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-small.svg +0 -5
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +0 -16
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +0 -1
- package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
- package/template/.codex/skills/data-analytics/assets/datascience.svg +0 -10
- package/template/.codex/skills/data-analytics/mcp/server.cjs +0 -2964
- package/template/.codex/skills/data-analytics/package-lock.json +0 -3048
- package/template/.codex/skills/data-analytics/package.json +0 -43
- package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +0 -75
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +0 -137
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/references/quality-checks.md +0 -29
- package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +0 -148
- package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +0 -18
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +0 -24
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +0 -71
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +0 -85
- package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +0 -207
- package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +0 -70
- package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +0 -66
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +0 -108
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +0 -1
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +0 -52
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +0 -61
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +0 -362
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +0 -829
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +0 -30
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +0 -127
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +0 -374
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +0 -613
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +0 -54
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +0 -16
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +0 -9
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/tests/test_delivery_plan.py +0 -45
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +0 -77
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +0 -2379
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +0 -88
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +0 -97
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +0 -59
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +0 -75
- package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +0 -103
- package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +0 -68
- package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/index/SKILL.md +0 -251
- package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +0 -131
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +0 -32
- package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +0 -106
- package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +0 -130
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +0 -141
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +0 -178
- package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +0 -9
- package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +0 -31
- package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +0 -466
- package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +0 -99
- package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +0 -197
- package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +0 -26
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +0 -51
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +0 -66
- package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +0 -69
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +0 -204
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +0 -87
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +0 -497
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +0 -74
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +0 -65
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +0 -160
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +0 -75
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +0 -96
- package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +0 -263
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +0 -1462
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +0 -128
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +0 -101
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +0 -499
- package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +0 -978
- package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +0 -126
- package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/validate-data/references/validation-methods.md +0 -70
- package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +0 -157
- package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +0 -6
- package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +0 -774
- package/template/.codex/skills/data-analytics/src/DESIGN.md +0 -222
- package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +0 -3666
- package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +0 -136
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +0 -54
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +0 -96
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +0 -1647
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +0 -245
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +0 -462
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +0 -107
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +0 -192
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +0 -203
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +0 -619
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +0 -402
- package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +0 -370
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +0 -536
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +0 -681
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +0 -164
- package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +0 -21
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +0 -472
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +0 -75
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +0 -142
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +0 -377
- package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +0 -3468
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +0 -44
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +0 -639
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +0 -327
- package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +0 -225
- package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +0 -204
- package/template/.codex/skills/data-analytics/src/analytics-app-core.md +0 -85
- package/template/.codex/skills/data-analytics/src/codex-style-contract.md +0 -30
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +0 -12
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +0 -1008
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +0 -2117
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +0 -122
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +0 -3588
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +0 -483
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +0 -32
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +0 -333
- package/template/.codex/skills/data-analytics/src/mcp-host.js +0 -153
- package/template/.codex/skills/data-analytics/src/recharts-config.js +0 -315
- package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +0 -294
- package/template/.codex/skills/data-analytics/src/sql-source-view.css +0 -33
- package/template/.codex/skills/data-analytics/src/sql-source-view.js +0 -157
- package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +0 -121
- package/template/.codex/skills/data-analytics/src/table-renderer.jsx +0 -28
- package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +0 -87
- package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +0 -105
- package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +0 -233
- package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +0 -1500
- package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +0 -219
- package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +0 -144
- package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +0 -11
- package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +0 -54
- package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +0 -321
- package/template/.codex/skills/data-analytics/tsconfig.json +0 -19
- package/template/.codex/skills/data-analytics/vite.config.ts +0 -46
- package/template/.codex/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.codex/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.codex/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.codex/skills/prototype/UI.md +0 -112
- package/template/.codex/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.codex/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.codex/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.codex/skills/wait-what/SKILL.md +0 -7
- package/template/.codex/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.cursor/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.cursor/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.cursor/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.cursor/skills/prototype/UI.md +0 -112
- package/template/.cursor/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.cursor/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.cursor/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.cursor/skills/wait-what/SKILL.md +0 -7
- package/template/.cursor/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.pi/skills/grill-with-docs/SKILL.md +0 -20
- package/template/.pi/skills/grill-with-docs/agents/openai.yaml +0 -5
- package/template/.pi/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/template/.pi/skills/prototype/UI.md +0 -112
- package/template/.pi/skills/setup-matt-pocock-skills/domain.md +0 -37
- package/template/.pi/skills/to-questionnaire/SKILL.md +0 -55
- package/template/.pi/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/template/.pi/skills/wait-what/SKILL.md +0 -7
- package/template/.pi/skills/wait-what/agents/openai.yaml +0 -5
- package/template/.template-spec/agents/domain.md +0 -31
- /package/template/.agents/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.codex/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.cursor/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
- /package/template/.pi/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
|
@@ -1,1462 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Read Data Analytics local state and emit a deterministic preflight payload."""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import hashlib
|
|
8
|
-
import json
|
|
9
|
-
import os
|
|
10
|
-
import re
|
|
11
|
-
import sys
|
|
12
|
-
from datetime import datetime, timezone
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import Any
|
|
15
|
-
|
|
16
|
-
PLUGIN_ROOT = Path(__file__).resolve().parents[3]
|
|
17
|
-
ONBOARDING_REFERENCE = PLUGIN_ROOT / "skills/user-context/references/onboarding.md"
|
|
18
|
-
SOURCE_CATEGORY_CONFIG_REFERENCE = (
|
|
19
|
-
PLUGIN_ROOT / "skills/user-context/plugin-author-config/source-category-config.json"
|
|
20
|
-
)
|
|
21
|
-
APP_MANIFEST_REFERENCE = PLUGIN_ROOT / ".app.json"
|
|
22
|
-
DEFAULT_MAX_CONTEXT_BYTES = 200_000
|
|
23
|
-
CORE_CATEGORY_IDS = ("structured_data", "team_communication", "company_docs")
|
|
24
|
-
ONBOARDING_RESOLVED_SOURCE_STATUSES = {
|
|
25
|
-
"declined",
|
|
26
|
-
"deferred",
|
|
27
|
-
"deferred_environment_api_limitations",
|
|
28
|
-
"not_applicable",
|
|
29
|
-
"skipped",
|
|
30
|
-
"skipped_for_now",
|
|
31
|
-
"unavailable",
|
|
32
|
-
}
|
|
33
|
-
SOURCE_CLASSIFIED_STATUSES = {
|
|
34
|
-
"active",
|
|
35
|
-
"needs_confirmation",
|
|
36
|
-
"missing",
|
|
37
|
-
*ONBOARDING_RESOLVED_SOURCE_STATUSES,
|
|
38
|
-
}
|
|
39
|
-
SOURCE_ACTION_REQUIRED_STATUSES = {"needs_confirmation", "missing"}
|
|
40
|
-
CORE_SOURCE_EXPLICIT_FALLBACK_RESOLUTIONS = {
|
|
41
|
-
"user_continued_with_known_gap",
|
|
42
|
-
"user_declined",
|
|
43
|
-
"user_deferred",
|
|
44
|
-
"user_marked_unavailable",
|
|
45
|
-
"user_skipped",
|
|
46
|
-
"user_confirmed_not_applicable",
|
|
47
|
-
}
|
|
48
|
-
SEMANTIC_SETUP_COMPLETE_STATUSES = {
|
|
49
|
-
"created",
|
|
50
|
-
"refreshed",
|
|
51
|
-
"inspected",
|
|
52
|
-
"repaired",
|
|
53
|
-
"planned",
|
|
54
|
-
"skipped",
|
|
55
|
-
"deferred",
|
|
56
|
-
"unavailable",
|
|
57
|
-
"blocked",
|
|
58
|
-
}
|
|
59
|
-
SEMANTIC_SETUP_TARGET_STATUSES = {"created", "refreshed", "inspected", "repaired", "planned"}
|
|
60
|
-
SEMANTIC_REFRESH_COMPLETE_STATUSES = {
|
|
61
|
-
"accepted",
|
|
62
|
-
"declined",
|
|
63
|
-
"deferred",
|
|
64
|
-
"unavailable",
|
|
65
|
-
"skipped",
|
|
66
|
-
"blocked",
|
|
67
|
-
}
|
|
68
|
-
HERO_PROMPT_COMPLETE_STATUSES = {"tried", "skipped", "deferred", "completed"}
|
|
69
|
-
HERO_SKILLS = ("metric-diagnostics", "product-business-analysis", "kpi-reporting")
|
|
70
|
-
SOURCE_ROUTING_PREFERENCES_HEADING = "data analytics source routing preferences"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def parse_args() -> argparse.Namespace:
|
|
74
|
-
parser = argparse.ArgumentParser(
|
|
75
|
-
description=(
|
|
76
|
-
"Read Data Analytics user-context and onboarding-state files and return "
|
|
77
|
-
"context plus workflow control obligations as JSON."
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
parser.add_argument("--workflow", default="ordinary")
|
|
81
|
-
parser.add_argument(
|
|
82
|
-
"--request-mode",
|
|
83
|
-
choices=("ordinary_workflow", "direct_onboarding_status", "guided_onboarding_workflow"),
|
|
84
|
-
default="ordinary_workflow",
|
|
85
|
-
)
|
|
86
|
-
parser.add_argument("--codex-home", type=Path, default=None)
|
|
87
|
-
parser.add_argument("--state-dir", type=Path, default=None)
|
|
88
|
-
parser.add_argument("--max-context-bytes", type=int, default=DEFAULT_MAX_CONTEXT_BYTES)
|
|
89
|
-
parser.add_argument("--section", action="append", default=[])
|
|
90
|
-
return parser.parse_args()
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def default_codex_home() -> Path:
|
|
94
|
-
env_home = os.environ.get("CODEX_HOME")
|
|
95
|
-
if env_home:
|
|
96
|
-
return Path(env_home).expanduser()
|
|
97
|
-
return Path.home() / ".codex"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def state_dir_from_args(args: argparse.Namespace) -> Path:
|
|
101
|
-
if args.state_dir:
|
|
102
|
-
return args.state_dir.expanduser()
|
|
103
|
-
codex_home = args.codex_home.expanduser() if args.codex_home else default_codex_home()
|
|
104
|
-
return codex_home / "state/plugins/data-analytics"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def sha256_text(text: str) -> str:
|
|
108
|
-
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def file_mtime(path: Path) -> str | None:
|
|
112
|
-
try:
|
|
113
|
-
return datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc).isoformat()
|
|
114
|
-
except OSError:
|
|
115
|
-
return None
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def section_bounds(markdown: str) -> dict[str, tuple[int, int, str]]:
|
|
119
|
-
matches = list(re.finditer(r"(?m)^(#{1,6})\s+(.+?)\s*$", markdown))
|
|
120
|
-
bounds: dict[str, tuple[int, int, str]] = {}
|
|
121
|
-
for index, match in enumerate(matches):
|
|
122
|
-
start = match.start()
|
|
123
|
-
level = len(match.group(1))
|
|
124
|
-
end = len(markdown)
|
|
125
|
-
for next_match in matches[index + 1 :]:
|
|
126
|
-
if len(next_match.group(1)) <= level:
|
|
127
|
-
end = next_match.start()
|
|
128
|
-
break
|
|
129
|
-
heading = match.group(2).strip()
|
|
130
|
-
bounds[heading.casefold()] = (start, end, heading)
|
|
131
|
-
return bounds
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def extract_sections(markdown: str, requested: list[str]) -> list[dict[str, str]]:
|
|
135
|
-
if not requested:
|
|
136
|
-
return []
|
|
137
|
-
bounds = section_bounds(markdown)
|
|
138
|
-
sections: list[dict[str, str]] = []
|
|
139
|
-
for heading in requested:
|
|
140
|
-
bound = bounds.get(heading.casefold())
|
|
141
|
-
if not bound:
|
|
142
|
-
sections.append({"heading": heading, "status": "missing", "content": ""})
|
|
143
|
-
continue
|
|
144
|
-
start, end, canonical_heading = bound
|
|
145
|
-
sections.append(
|
|
146
|
-
{
|
|
147
|
-
"heading": canonical_heading,
|
|
148
|
-
"status": "present",
|
|
149
|
-
"content": markdown[start:end].strip(),
|
|
150
|
-
}
|
|
151
|
-
)
|
|
152
|
-
return sections
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def normalize_source_name(value: Any) -> str:
|
|
156
|
-
return re.sub(r"[^a-z0-9]+", "", str(value or "").casefold())
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def parse_source_preference_values(raw_value: str) -> list[str]:
|
|
160
|
-
return [value.strip() for value in raw_value.split(",") if value.strip()]
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def extract_saved_source_preferences(
|
|
164
|
-
markdown: str | None,
|
|
165
|
-
source_category_config: dict[str, dict[str, Any]],
|
|
166
|
-
) -> dict[str, dict[str, list[str]]]:
|
|
167
|
-
"""Read durable Data Analytics source-routing choices from user-context.md."""
|
|
168
|
-
if not markdown:
|
|
169
|
-
return {}
|
|
170
|
-
bounds = section_bounds(markdown)
|
|
171
|
-
source_routing_section = bounds.get(SOURCE_ROUTING_PREFERENCES_HEADING)
|
|
172
|
-
if not source_routing_section:
|
|
173
|
-
return {}
|
|
174
|
-
start, end, _ = source_routing_section
|
|
175
|
-
section = markdown[start:end]
|
|
176
|
-
preferences: dict[str, dict[str, list[str]]] = {}
|
|
177
|
-
current_category: str | None = None
|
|
178
|
-
for raw_line in section.splitlines():
|
|
179
|
-
line = raw_line.strip()
|
|
180
|
-
category_match = re.match(r"^##\s+([a-z0-9_]+)\s*$", line)
|
|
181
|
-
if category_match:
|
|
182
|
-
category_id = category_match.group(1)
|
|
183
|
-
current_category = category_id if category_id in source_category_config else None
|
|
184
|
-
continue
|
|
185
|
-
if not current_category:
|
|
186
|
-
continue
|
|
187
|
-
preference_match = re.match(r"^-\s*(Prefer|Avoid):\s*(.*?)\s*$", line)
|
|
188
|
-
if not preference_match:
|
|
189
|
-
continue
|
|
190
|
-
disposition = "preferred" if preference_match.group(1) == "Prefer" else "avoid"
|
|
191
|
-
configured_sources = {
|
|
192
|
-
normalize_source_name(str(source)): str(source)
|
|
193
|
-
for source in preferred_source_routes(source_category_config[current_category])
|
|
194
|
-
}
|
|
195
|
-
for source_name in parse_source_preference_values(preference_match.group(2)):
|
|
196
|
-
configured_source = configured_sources.get(normalize_source_name(source_name))
|
|
197
|
-
if not configured_source:
|
|
198
|
-
continue
|
|
199
|
-
category_preferences = preferences.setdefault(current_category, {})
|
|
200
|
-
values = category_preferences.setdefault(disposition, [])
|
|
201
|
-
if configured_source not in values:
|
|
202
|
-
values.append(configured_source)
|
|
203
|
-
return preferences
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
def summarize_user_context(
|
|
207
|
-
markdown: str | None,
|
|
208
|
-
source_category_config: dict[str, dict[str, Any]],
|
|
209
|
-
) -> dict[str, Any]:
|
|
210
|
-
"""Return the narrow normalized Data Analytics user-context surface."""
|
|
211
|
-
return {
|
|
212
|
-
"scope": "source_routing_preferences_and_semantic_layer_registry",
|
|
213
|
-
"source_routing_preferences": extract_saved_source_preferences(
|
|
214
|
-
markdown,
|
|
215
|
-
source_category_config,
|
|
216
|
-
),
|
|
217
|
-
"semantic_layer_count": len(semantic_layer_registry(markdown)),
|
|
218
|
-
"normalization_complete": markdown is not None,
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def read_text_payload(path: Path, max_context_bytes: int, sections: list[str]) -> dict[str, Any]:
|
|
223
|
-
payload: dict[str, Any] = {
|
|
224
|
-
"path": str(path),
|
|
225
|
-
"status": "missing",
|
|
226
|
-
"bytes": 0,
|
|
227
|
-
"sha256": None,
|
|
228
|
-
"mtime": None,
|
|
229
|
-
"content": None,
|
|
230
|
-
"sections": [],
|
|
231
|
-
"omitted": False,
|
|
232
|
-
"error": None,
|
|
233
|
-
}
|
|
234
|
-
if not path.exists():
|
|
235
|
-
return payload
|
|
236
|
-
try:
|
|
237
|
-
text = path.read_text(encoding="utf-8")
|
|
238
|
-
except (OSError, UnicodeDecodeError) as exc:
|
|
239
|
-
payload["status"] = "unreadable"
|
|
240
|
-
payload["error"] = str(exc)
|
|
241
|
-
return payload
|
|
242
|
-
|
|
243
|
-
encoded_len = len(text.encode("utf-8"))
|
|
244
|
-
payload.update(
|
|
245
|
-
{
|
|
246
|
-
"status": "present",
|
|
247
|
-
"bytes": encoded_len,
|
|
248
|
-
"sha256": sha256_text(text),
|
|
249
|
-
"mtime": file_mtime(path),
|
|
250
|
-
"sections": extract_sections(text, sections),
|
|
251
|
-
"_content": text,
|
|
252
|
-
}
|
|
253
|
-
)
|
|
254
|
-
if encoded_len <= max_context_bytes:
|
|
255
|
-
payload["content"] = text
|
|
256
|
-
else:
|
|
257
|
-
payload["omitted"] = True
|
|
258
|
-
payload["omission_reason"] = (
|
|
259
|
-
f"file exceeds max_context_bytes ({encoded_len} > {max_context_bytes})"
|
|
260
|
-
)
|
|
261
|
-
return payload
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
def payload_text(payload: dict[str, Any]) -> str | None:
|
|
265
|
-
text = payload.get("_content")
|
|
266
|
-
if isinstance(text, str):
|
|
267
|
-
return text
|
|
268
|
-
content = payload.get("content")
|
|
269
|
-
return content if isinstance(content, str) else None
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def public_file_payload(
|
|
273
|
-
payload: dict[str, Any],
|
|
274
|
-
*,
|
|
275
|
-
include_content: bool,
|
|
276
|
-
) -> dict[str, Any]:
|
|
277
|
-
public = {
|
|
278
|
-
key: value for key, value in payload.items() if key not in {"_content", "data"}
|
|
279
|
-
}
|
|
280
|
-
if not include_content:
|
|
281
|
-
public.pop("content", None)
|
|
282
|
-
return public
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def read_json_payload(path: Path, max_context_bytes: int) -> dict[str, Any]:
|
|
286
|
-
payload = read_text_payload(path, max_context_bytes=max_context_bytes, sections=[])
|
|
287
|
-
payload["data"] = None
|
|
288
|
-
if payload["status"] != "present":
|
|
289
|
-
return payload
|
|
290
|
-
try:
|
|
291
|
-
payload["data"] = json.loads(payload_text(payload) or path.read_text(encoding="utf-8"))
|
|
292
|
-
except json.JSONDecodeError as exc:
|
|
293
|
-
payload["status"] = "unreadable"
|
|
294
|
-
payload["error"] = f"invalid json: {exc}"
|
|
295
|
-
return payload
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
def markdown_template_from_onboarding_reference(heading_name: str) -> str:
|
|
299
|
-
text = ONBOARDING_REFERENCE.read_text(encoding="utf-8")
|
|
300
|
-
heading = re.search(rf"(?m)^## {re.escape(heading_name)}\s*$", text)
|
|
301
|
-
if not heading:
|
|
302
|
-
raise RuntimeError(f"{heading_name} heading not found")
|
|
303
|
-
following = text[heading.end() :]
|
|
304
|
-
code_block = re.search(r"```md\n(.*?)\n```", following, flags=re.DOTALL)
|
|
305
|
-
if not code_block:
|
|
306
|
-
raise RuntimeError(f"{heading_name} markdown block not found")
|
|
307
|
-
return code_block.group(1).strip()
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def load_source_category_config() -> dict[str, Any]:
|
|
311
|
-
return json.loads(SOURCE_CATEGORY_CONFIG_REFERENCE.read_text(encoding="utf-8"))["categories"]
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
def unique_strings(values: list[Any]) -> list[str]:
|
|
315
|
-
return list(dict.fromkeys(str(value) for value in values if isinstance(value, str) and value))
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
def preferred_source_routes(metadata: dict[str, Any]) -> list[str]:
|
|
319
|
-
return unique_strings(
|
|
320
|
-
[
|
|
321
|
-
*(metadata.get("preferred_plugins") or []),
|
|
322
|
-
*(metadata.get("preferred_apps") or []),
|
|
323
|
-
]
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
def normalize_source_kind(value: Any) -> str | None:
|
|
328
|
-
source_kind = str(value or "").casefold()
|
|
329
|
-
if source_kind in {"plugin", "app", "connector", "manual"}:
|
|
330
|
-
return source_kind
|
|
331
|
-
return None
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
PLUGIN_INSTALL_EVIDENCE = {
|
|
335
|
-
"installed_plugin_skill_visible",
|
|
336
|
-
"plugin_in_available_plugins",
|
|
337
|
-
"plugin_owned_skill_visible",
|
|
338
|
-
"request_plugin_install_completed",
|
|
339
|
-
"user_confirmed_installed_plugin",
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
PLUGIN_SKILL_SURFACE_EVIDENCE = {
|
|
343
|
-
"plugin_skills_visible",
|
|
344
|
-
"plugin_owned_skill_visible",
|
|
345
|
-
"plugin_owned_skills_visible",
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
PLUGIN_TOOL_SURFACE_EVIDENCE = {
|
|
349
|
-
"callable_app_tools_visible",
|
|
350
|
-
"callable_plugin_tools_visible",
|
|
351
|
-
"plugin_tools_visible",
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
PLUGIN_FIRST_SETUP_STATUSES = {
|
|
355
|
-
"active",
|
|
356
|
-
"missing",
|
|
357
|
-
"needs_confirmation",
|
|
358
|
-
"unavailable",
|
|
359
|
-
"deferred",
|
|
360
|
-
"deferred_environment_api_limitations",
|
|
361
|
-
"skipped_for_now",
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def compact_mapping(value: Any) -> dict[str, Any] | None:
|
|
366
|
-
if not isinstance(value, dict):
|
|
367
|
-
return None
|
|
368
|
-
compact = {
|
|
369
|
-
str(key): item
|
|
370
|
-
for key, item in value.items()
|
|
371
|
-
if isinstance(key, str) and (isinstance(item, (str, int, float, bool)) or item is None)
|
|
372
|
-
}
|
|
373
|
-
return compact or None
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
def default_skill_surface(source_kind: str | None, metadata: dict[str, Any]) -> str | None:
|
|
377
|
-
if source_kind == "plugin":
|
|
378
|
-
return "plugin_owned"
|
|
379
|
-
if source_kind in {"app", "connector"}:
|
|
380
|
-
if metadata.get("relevant_skills"):
|
|
381
|
-
return "data_analytics_vendored_helper"
|
|
382
|
-
return "direct_tool"
|
|
383
|
-
if source_kind == "manual":
|
|
384
|
-
return "manual"
|
|
385
|
-
return None
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
def default_runtime_action(source_kind: str | None, skill_surface: str | None) -> str | None:
|
|
389
|
-
if source_kind == "plugin":
|
|
390
|
-
return "use_plugin_owned_skill_or_tools"
|
|
391
|
-
if source_kind in {"app", "connector"}:
|
|
392
|
-
if skill_surface == "data_analytics_vendored_helper":
|
|
393
|
-
return "use_connector_or_app_with_data_analytics_helper_guidance"
|
|
394
|
-
return "use_direct_tool_when_workflow_needs_source"
|
|
395
|
-
if source_kind == "manual":
|
|
396
|
-
return "use_manual_or_exported_context"
|
|
397
|
-
return None
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
def copy_configured_route_metadata(
|
|
401
|
-
entry: dict[str, Any],
|
|
402
|
-
raw_entry: dict[str, Any],
|
|
403
|
-
metadata: dict[str, Any],
|
|
404
|
-
) -> None:
|
|
405
|
-
source_kind = normalize_source_kind(raw_entry.get("source_kind"))
|
|
406
|
-
if source_kind is None:
|
|
407
|
-
if isinstance(raw_entry.get("plugin"), dict):
|
|
408
|
-
source_kind = "plugin"
|
|
409
|
-
elif isinstance(raw_entry.get("connector"), dict):
|
|
410
|
-
source_kind = "connector"
|
|
411
|
-
elif isinstance(raw_entry.get("app"), dict):
|
|
412
|
-
source_kind = "app"
|
|
413
|
-
elif isinstance(raw_entry.get("manual"), dict) or raw_entry.get("manual_source"):
|
|
414
|
-
source_kind = "manual"
|
|
415
|
-
elif entry.get("status") == "active" and entry.get("preferred"):
|
|
416
|
-
source_kind = "connector"
|
|
417
|
-
|
|
418
|
-
if source_kind:
|
|
419
|
-
entry["source_kind"] = source_kind
|
|
420
|
-
|
|
421
|
-
for key in ("plugin", "app", "connector", "manual"):
|
|
422
|
-
compact = compact_mapping(raw_entry.get(key))
|
|
423
|
-
if compact is not None:
|
|
424
|
-
entry[key] = compact
|
|
425
|
-
|
|
426
|
-
manual_source = raw_entry.get("manual_source")
|
|
427
|
-
if isinstance(manual_source, str) and manual_source:
|
|
428
|
-
entry["manual_source"] = manual_source
|
|
429
|
-
|
|
430
|
-
skill_surface = str(raw_entry.get("skill_surface") or "") or default_skill_surface(
|
|
431
|
-
source_kind,
|
|
432
|
-
metadata,
|
|
433
|
-
)
|
|
434
|
-
if skill_surface:
|
|
435
|
-
entry["skill_surface"] = skill_surface
|
|
436
|
-
|
|
437
|
-
runtime_action = str(raw_entry.get("runtime_action") or "") or default_runtime_action(
|
|
438
|
-
source_kind,
|
|
439
|
-
skill_surface,
|
|
440
|
-
)
|
|
441
|
-
if runtime_action:
|
|
442
|
-
entry["runtime_action"] = runtime_action
|
|
443
|
-
|
|
444
|
-
for evidence_key in (
|
|
445
|
-
"installed_evidence",
|
|
446
|
-
"plugin_install_evidence",
|
|
447
|
-
"app_surface_evidence",
|
|
448
|
-
"connector_surface_evidence",
|
|
449
|
-
"skill_surface_evidence",
|
|
450
|
-
"tool_surface_evidence",
|
|
451
|
-
):
|
|
452
|
-
evidence_value = raw_entry.get(evidence_key)
|
|
453
|
-
if isinstance(evidence_value, str) and evidence_value:
|
|
454
|
-
entry[evidence_key] = evidence_value
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
def copy_source_resolution_metadata(
|
|
458
|
-
entry: dict[str, Any],
|
|
459
|
-
raw_entry: dict[str, Any],
|
|
460
|
-
) -> None:
|
|
461
|
-
for key in ("resolution", "resolved_at", "resolution_note"):
|
|
462
|
-
value = raw_entry.get(key)
|
|
463
|
-
if isinstance(value, str) and value:
|
|
464
|
-
entry[key] = value
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
def core_source_fallback_is_explicit(entry: dict[str, Any]) -> bool:
|
|
468
|
-
return (
|
|
469
|
-
str(entry.get("resolution") or "").casefold()
|
|
470
|
-
in CORE_SOURCE_EXPLICIT_FALLBACK_RESOLUTIONS
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
def normalize_unacknowledged_core_source_fallback(
|
|
475
|
-
category_id: str,
|
|
476
|
-
entry: dict[str, Any],
|
|
477
|
-
metadata: dict[str, Any],
|
|
478
|
-
) -> None:
|
|
479
|
-
if category_id not in CORE_CATEGORY_IDS:
|
|
480
|
-
return
|
|
481
|
-
if entry.get("status") not in ONBOARDING_RESOLVED_SOURCE_STATUSES:
|
|
482
|
-
return
|
|
483
|
-
if core_source_fallback_is_explicit(entry):
|
|
484
|
-
return
|
|
485
|
-
|
|
486
|
-
recorded_status = str(entry["status"])
|
|
487
|
-
previous_setup_action = str(
|
|
488
|
-
entry.get("fallback_setup_action") or entry.get("setup_action") or ""
|
|
489
|
-
)
|
|
490
|
-
preferred_routes = preferred_source_routes(metadata)
|
|
491
|
-
entry["recorded_status"] = recorded_status
|
|
492
|
-
entry["status"] = "needs_confirmation"
|
|
493
|
-
entry["candidates"] = preferred_routes
|
|
494
|
-
entry["setup_action"] = "run_plugin_first_source_setup"
|
|
495
|
-
if previous_setup_action and previous_setup_action != entry["setup_action"]:
|
|
496
|
-
entry["fallback_setup_action"] = previous_setup_action
|
|
497
|
-
entry["setup_note"] = "core_source_requires_explicit_user_resolution_before_fallback"
|
|
498
|
-
entry["plugin_preference_order"] = preferred_routes
|
|
499
|
-
entry["setup_recovery"] = plugin_first_setup_recovery(metadata, previous_setup_action)
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
def compact_source_route(raw_route: Any, metadata: dict[str, Any]) -> dict[str, Any] | None:
|
|
503
|
-
if not isinstance(raw_route, dict):
|
|
504
|
-
return None
|
|
505
|
-
route: dict[str, Any] = {}
|
|
506
|
-
for key in ("status", "preferred", "name", "label", "fallback", "setup_action"):
|
|
507
|
-
value = raw_route.get(key)
|
|
508
|
-
if isinstance(value, str) and value:
|
|
509
|
-
route[key] = value
|
|
510
|
-
copy_configured_route_metadata(route, raw_route, metadata)
|
|
511
|
-
if "preferred" not in route:
|
|
512
|
-
for route_key in ("plugin", "app", "connector", "manual"):
|
|
513
|
-
raw_value = raw_route.get(route_key)
|
|
514
|
-
if isinstance(raw_value, str) and raw_value:
|
|
515
|
-
route["preferred"] = raw_value
|
|
516
|
-
break
|
|
517
|
-
if isinstance(raw_value, dict):
|
|
518
|
-
for name_key in ("name", "display_name", "id"):
|
|
519
|
-
name_value = raw_value.get(name_key)
|
|
520
|
-
if isinstance(name_value, str) and name_value:
|
|
521
|
-
route["preferred"] = name_value
|
|
522
|
-
break
|
|
523
|
-
if "preferred" in route:
|
|
524
|
-
break
|
|
525
|
-
if "runtime_action" not in route and route.get("source_kind"):
|
|
526
|
-
runtime_action = default_runtime_action(
|
|
527
|
-
str(route.get("source_kind") or ""),
|
|
528
|
-
str(route.get("skill_surface") or ""),
|
|
529
|
-
)
|
|
530
|
-
if runtime_action:
|
|
531
|
-
route["runtime_action"] = runtime_action
|
|
532
|
-
return route or None
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
def active_plugin_route_present(entry: dict[str, Any]) -> bool:
|
|
536
|
-
if entry.get("source_kind") == "plugin":
|
|
537
|
-
return True
|
|
538
|
-
routes = entry.get("routes")
|
|
539
|
-
if not isinstance(routes, list):
|
|
540
|
-
return False
|
|
541
|
-
return any(isinstance(route, dict) and route.get("source_kind") == "plugin" for route in routes)
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
def unproven_plugin_route(entry: dict[str, Any]) -> bool:
|
|
545
|
-
if entry.get("source_kind") != "plugin":
|
|
546
|
-
return False
|
|
547
|
-
installed_evidence = str(entry.get("installed_evidence") or "")
|
|
548
|
-
plugin_install_evidence = str(entry.get("plugin_install_evidence") or "")
|
|
549
|
-
if installed_evidence in PLUGIN_INSTALL_EVIDENCE:
|
|
550
|
-
return False
|
|
551
|
-
if plugin_install_evidence not in PLUGIN_INSTALL_EVIDENCE:
|
|
552
|
-
return True
|
|
553
|
-
if entry.get("skill_surface") == "plugin_owned":
|
|
554
|
-
skill_surface_evidence = str(entry.get("skill_surface_evidence") or "")
|
|
555
|
-
if skill_surface_evidence not in PLUGIN_SKILL_SURFACE_EVIDENCE:
|
|
556
|
-
return True
|
|
557
|
-
surface_evidence = {
|
|
558
|
-
str(entry.get("app_surface_evidence") or ""),
|
|
559
|
-
str(entry.get("tool_surface_evidence") or ""),
|
|
560
|
-
str(entry.get("skill_surface_evidence") or ""),
|
|
561
|
-
}
|
|
562
|
-
return not surface_evidence.intersection(
|
|
563
|
-
PLUGIN_TOOL_SURFACE_EVIDENCE | PLUGIN_SKILL_SURFACE_EVIDENCE
|
|
564
|
-
)
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
def list_from_inventory(inventory: dict[str, Any], *keys: str) -> list[str]:
|
|
568
|
-
values: list[str] = []
|
|
569
|
-
for key in keys:
|
|
570
|
-
raw = inventory.get(key)
|
|
571
|
-
if isinstance(raw, list):
|
|
572
|
-
values.extend(str(item) for item in raw if isinstance(item, str) and item)
|
|
573
|
-
return values
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
def matching_configured_sources(preferred_sources: list[str], candidates: set[str]) -> list[str]:
|
|
577
|
-
return [
|
|
578
|
-
source for source in preferred_sources if normalize_source_name(source) in candidates
|
|
579
|
-
]
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
def missing_source_it_options(preferred_sources: list[str]) -> list[str]:
|
|
583
|
-
source_list = ", ".join(preferred_sources) if preferred_sources else "a matching source"
|
|
584
|
-
return [
|
|
585
|
-
f"Ask IT or a workspace admin to install or enable one of: {source_list}.",
|
|
586
|
-
"Use exported, pasted, or manually supplied context for the current workflow.",
|
|
587
|
-
]
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
def load_app_connector_ids() -> dict[str, str]:
|
|
591
|
-
manifest = json.loads(APP_MANIFEST_REFERENCE.read_text(encoding="utf-8"))
|
|
592
|
-
apps = manifest.get("apps") if isinstance(manifest, dict) else None
|
|
593
|
-
if not isinstance(apps, dict):
|
|
594
|
-
return {}
|
|
595
|
-
return {
|
|
596
|
-
normalize_source_name(app_name): str(metadata["id"])
|
|
597
|
-
for app_name, metadata in apps.items()
|
|
598
|
-
if isinstance(app_name, str)
|
|
599
|
-
and isinstance(metadata, dict)
|
|
600
|
-
and isinstance(metadata.get("id"), str)
|
|
601
|
-
and metadata["id"]
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
def plugin_connector_ids(
|
|
606
|
-
plugin_name: str,
|
|
607
|
-
metadata: dict[str, Any],
|
|
608
|
-
app_connector_ids: dict[str, str],
|
|
609
|
-
) -> set[str]:
|
|
610
|
-
plugin_alias = normalize_source_name(plugin_name)
|
|
611
|
-
return {
|
|
612
|
-
connector_id
|
|
613
|
-
for source_name in preferred_source_routes(metadata)
|
|
614
|
-
if normalize_source_name(source_name) == plugin_alias
|
|
615
|
-
for connector_id in [app_connector_ids.get(normalize_source_name(source_name))]
|
|
616
|
-
if connector_id
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
def compact_install_candidate(candidate: dict[str, Any]) -> dict[str, Any]:
|
|
621
|
-
return {
|
|
622
|
-
key: candidate[key]
|
|
623
|
-
for key in (
|
|
624
|
-
"id",
|
|
625
|
-
"name",
|
|
626
|
-
"display_name",
|
|
627
|
-
"tool_type",
|
|
628
|
-
"has_skills",
|
|
629
|
-
"app_connector_ids",
|
|
630
|
-
)
|
|
631
|
-
if key in candidate
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
def match_installable_plugin_candidates(
|
|
636
|
-
source_category_config: dict[str, Any],
|
|
637
|
-
candidates: list[dict[str, Any]] | dict[str, Any],
|
|
638
|
-
app_connector_ids: dict[str, str] | None = None,
|
|
639
|
-
) -> dict[str, list[dict[str, Any]]]:
|
|
640
|
-
"""Rank installable plugins using config order, plugin slugs, and connector ids."""
|
|
641
|
-
if isinstance(candidates, dict):
|
|
642
|
-
raw_candidates = candidates.get("tools")
|
|
643
|
-
candidates = raw_candidates if isinstance(raw_candidates, list) else []
|
|
644
|
-
connector_ids = app_connector_ids if app_connector_ids is not None else load_app_connector_ids()
|
|
645
|
-
matches: dict[str, list[dict[str, Any]]] = {}
|
|
646
|
-
for category_id, metadata in source_category_config.items():
|
|
647
|
-
category_matches: list[dict[str, Any]] = []
|
|
648
|
-
for rank, plugin_name in enumerate(metadata.get("preferred_plugins") or []):
|
|
649
|
-
plugin_alias = normalize_source_name(plugin_name)
|
|
650
|
-
configured_connector_ids = plugin_connector_ids(plugin_name, metadata, connector_ids)
|
|
651
|
-
for candidate in candidates:
|
|
652
|
-
if not isinstance(candidate, dict) or candidate.get("tool_type") != "plugin":
|
|
653
|
-
continue
|
|
654
|
-
candidate_aliases = {
|
|
655
|
-
normalize_source_name(candidate.get("name")),
|
|
656
|
-
normalize_source_name(candidate.get("display_name")),
|
|
657
|
-
normalize_source_name(str(candidate.get("id") or "").split("@", 1)[0]),
|
|
658
|
-
}
|
|
659
|
-
candidate_connector_ids = {
|
|
660
|
-
str(connector_id)
|
|
661
|
-
for connector_id in candidate.get("app_connector_ids") or []
|
|
662
|
-
if isinstance(connector_id, str)
|
|
663
|
-
}
|
|
664
|
-
reasons: list[str] = []
|
|
665
|
-
if plugin_alias and plugin_alias in candidate_aliases:
|
|
666
|
-
reasons.append("plugin_name")
|
|
667
|
-
if configured_connector_ids & candidate_connector_ids:
|
|
668
|
-
reasons.append("app_connector_id")
|
|
669
|
-
if not reasons:
|
|
670
|
-
continue
|
|
671
|
-
category_matches.append(
|
|
672
|
-
{
|
|
673
|
-
"preferred_plugin": plugin_name,
|
|
674
|
-
"preference_rank": rank,
|
|
675
|
-
"match_reasons": reasons,
|
|
676
|
-
"candidate": compact_install_candidate(candidate),
|
|
677
|
-
}
|
|
678
|
-
)
|
|
679
|
-
break
|
|
680
|
-
if category_matches:
|
|
681
|
-
matches[category_id] = category_matches
|
|
682
|
-
return matches
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
def plugin_first_setup_recovery(
|
|
686
|
-
metadata: dict[str, Any],
|
|
687
|
-
fallback_setup_action: str,
|
|
688
|
-
) -> dict[str, Any]:
|
|
689
|
-
recovery = {
|
|
690
|
-
"type": "plugin_first_source_setup",
|
|
691
|
-
"preferred_plugins": unique_strings(metadata.get("preferred_plugins") or []),
|
|
692
|
-
"configured_source_routes": preferred_source_routes(metadata),
|
|
693
|
-
"candidate_lookup": "host_available_plugin_catalog",
|
|
694
|
-
"candidate_match": "plugin_name_slug_or_app_connector_id_intersection",
|
|
695
|
-
"install_request": "host_authorized_plugin_install",
|
|
696
|
-
"install_requires_user_approval": True,
|
|
697
|
-
"fallback_setup_action": fallback_setup_action,
|
|
698
|
-
}
|
|
699
|
-
legacy_app_fallbacks = unique_strings(metadata.get("preferred_apps") or [])
|
|
700
|
-
if legacy_app_fallbacks:
|
|
701
|
-
recovery["legacy_app_fallbacks"] = legacy_app_fallbacks
|
|
702
|
-
return recovery
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
def connector_confirmation_base_entry(
|
|
706
|
-
category_id: str,
|
|
707
|
-
metadata: dict[str, Any],
|
|
708
|
-
) -> dict[str, Any]:
|
|
709
|
-
preferred_plugins = unique_strings(metadata.get("preferred_plugins") or [])
|
|
710
|
-
preferred_apps = unique_strings(metadata.get("preferred_apps") or [])
|
|
711
|
-
entry: dict[str, Any] = {
|
|
712
|
-
"id": category_id,
|
|
713
|
-
"label": metadata["label"],
|
|
714
|
-
"state_scope": "onboarding_only_not_durable_connector_readiness",
|
|
715
|
-
"workflow_time_behavior": "attempt_actual_reads_only_when_a_workflow_needs_the_source",
|
|
716
|
-
"eager_read": False,
|
|
717
|
-
}
|
|
718
|
-
if preferred_plugins:
|
|
719
|
-
entry["preferred_plugins"] = preferred_plugins
|
|
720
|
-
if preferred_apps:
|
|
721
|
-
entry["preferred_apps"] = preferred_apps
|
|
722
|
-
return entry
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
def onboarding_status(onboarding_state: dict[str, Any] | None) -> str:
|
|
726
|
-
if not onboarding_state:
|
|
727
|
-
return "missing"
|
|
728
|
-
return str(onboarding_state.get("status") or "active")
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
def connector_categories(onboarding_state: dict[str, Any] | None) -> dict[str, dict[str, Any]]:
|
|
732
|
-
state = onboarding_state or {}
|
|
733
|
-
connector_confirmation = state.get("connector_confirmation") or {}
|
|
734
|
-
if not isinstance(connector_confirmation, dict):
|
|
735
|
-
return {}
|
|
736
|
-
categories = connector_confirmation.get("categories")
|
|
737
|
-
if isinstance(categories, dict):
|
|
738
|
-
return categories
|
|
739
|
-
return {
|
|
740
|
-
str(category_id): entry
|
|
741
|
-
for category_id, entry in connector_confirmation.items()
|
|
742
|
-
if category_id not in {"status", "last_shown", "preferences"}
|
|
743
|
-
and isinstance(entry, dict)
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
def source_status(entry: dict[str, Any] | None) -> str:
|
|
748
|
-
raw_status = str((entry or {}).get("status") or "needs_confirmation").casefold()
|
|
749
|
-
aliases = {
|
|
750
|
-
"available": "needs_confirmation",
|
|
751
|
-
"blocked": "unavailable",
|
|
752
|
-
"choose_source": "needs_confirmation",
|
|
753
|
-
"connected": "active",
|
|
754
|
-
"manual": "active",
|
|
755
|
-
"needs_setup_probe": "needs_confirmation",
|
|
756
|
-
"not_available": "missing",
|
|
757
|
-
"selected": "needs_confirmation",
|
|
758
|
-
"selected_for_verification": "needs_confirmation",
|
|
759
|
-
"selected_needs_auth": "needs_confirmation",
|
|
760
|
-
}
|
|
761
|
-
return aliases.get(raw_status, raw_status)
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
def source_setup_action(status: str) -> str:
|
|
765
|
-
actions = {
|
|
766
|
-
"active": "none_try_on_use",
|
|
767
|
-
"needs_confirmation": "ask_user_to_choose_or_confirm_source",
|
|
768
|
-
"missing": "explain_workflow_impact_and_it_admin_options",
|
|
769
|
-
"unavailable": "ask_admin_or_manual_fallback",
|
|
770
|
-
"declined": "none_declined",
|
|
771
|
-
"skipped": "none_skipped",
|
|
772
|
-
"skipped_for_now": "none_skipped",
|
|
773
|
-
"deferred": "none_deferred",
|
|
774
|
-
"deferred_environment_api_limitations": "none_deferred",
|
|
775
|
-
"not_applicable": "none_not_applicable",
|
|
776
|
-
}
|
|
777
|
-
return actions.get(status, "ask_user_to_choose_or_confirm_source")
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
def normalized_connector_confirmation(
|
|
781
|
-
source_category_config: dict[str, Any],
|
|
782
|
-
onboarding_state: dict[str, Any] | None,
|
|
783
|
-
) -> dict[str, dict[str, Any]]:
|
|
784
|
-
raw_categories = connector_categories(onboarding_state)
|
|
785
|
-
state = onboarding_state or {}
|
|
786
|
-
raw_confirmation = state.get("connector_confirmation") or {}
|
|
787
|
-
raw_preferences = raw_confirmation.get("preferences") if isinstance(raw_confirmation, dict) else {}
|
|
788
|
-
if not isinstance(raw_preferences, dict):
|
|
789
|
-
raw_preferences = {}
|
|
790
|
-
inventory = state.get("connector_inventory") or {}
|
|
791
|
-
if not isinstance(inventory, dict):
|
|
792
|
-
inventory = {}
|
|
793
|
-
confirmation: dict[str, dict[str, Any]] = {}
|
|
794
|
-
for category_id, metadata in source_category_config.items():
|
|
795
|
-
raw_entry = raw_categories.get(category_id)
|
|
796
|
-
if raw_entry is None:
|
|
797
|
-
raw_entry = raw_preferences.get(category_id)
|
|
798
|
-
raw_entry = raw_entry if isinstance(raw_entry, dict) else {}
|
|
799
|
-
preferred_plugins = unique_strings(metadata.get("preferred_plugins") or [])
|
|
800
|
-
preferred_routes = preferred_source_routes(metadata)
|
|
801
|
-
entry = connector_confirmation_base_entry(category_id, metadata)
|
|
802
|
-
|
|
803
|
-
if raw_entry:
|
|
804
|
-
status = source_status(raw_entry)
|
|
805
|
-
if status in ONBOARDING_RESOLVED_SOURCE_STATUSES:
|
|
806
|
-
entry["status"] = status
|
|
807
|
-
entry["setup_action"] = source_setup_action(status)
|
|
808
|
-
copy_configured_route_metadata(entry, raw_entry, metadata)
|
|
809
|
-
copy_source_resolution_metadata(entry, raw_entry)
|
|
810
|
-
elif status in {"active", "needs_confirmation", "missing"}:
|
|
811
|
-
entry["status"] = status
|
|
812
|
-
preferred = raw_entry.get("preferred") or raw_entry.get("app")
|
|
813
|
-
if isinstance(preferred, str) and preferred:
|
|
814
|
-
entry["preferred"] = preferred
|
|
815
|
-
fallback = raw_entry.get("fallback") or raw_entry.get("backup")
|
|
816
|
-
if isinstance(fallback, str) and fallback:
|
|
817
|
-
entry["fallback"] = fallback
|
|
818
|
-
if status == "active":
|
|
819
|
-
raw_routes = raw_entry.get("routes")
|
|
820
|
-
if isinstance(raw_routes, list):
|
|
821
|
-
compacted_routes = [
|
|
822
|
-
route
|
|
823
|
-
for route in (
|
|
824
|
-
compact_source_route(raw_route, metadata) for raw_route in raw_routes
|
|
825
|
-
)
|
|
826
|
-
if route is not None
|
|
827
|
-
]
|
|
828
|
-
routes = [
|
|
829
|
-
route
|
|
830
|
-
for route in compacted_routes
|
|
831
|
-
if not unproven_plugin_route(route)
|
|
832
|
-
]
|
|
833
|
-
if routes:
|
|
834
|
-
entry["routes"] = routes
|
|
835
|
-
if "preferred" not in entry:
|
|
836
|
-
first_preferred = routes[0].get("preferred")
|
|
837
|
-
if isinstance(first_preferred, str) and first_preferred:
|
|
838
|
-
entry["preferred"] = first_preferred
|
|
839
|
-
elif compacted_routes:
|
|
840
|
-
entry = connector_confirmation_base_entry(category_id, metadata)
|
|
841
|
-
entry.update(
|
|
842
|
-
{
|
|
843
|
-
"status": "needs_confirmation",
|
|
844
|
-
"candidates": preferred_routes,
|
|
845
|
-
"setup_action": "run_plugin_first_source_setup",
|
|
846
|
-
"setup_note": (
|
|
847
|
-
"plugin_route_requires_plugin_install_and_surface_evidence_before_active"
|
|
848
|
-
),
|
|
849
|
-
}
|
|
850
|
-
)
|
|
851
|
-
if entry.get("status") == "active":
|
|
852
|
-
if raw_entry.get("confirmed_at"):
|
|
853
|
-
entry["confirmed_at"] = raw_entry["confirmed_at"]
|
|
854
|
-
entry["setup_action"] = "none_try_on_use"
|
|
855
|
-
copy_configured_route_metadata(entry, raw_entry, metadata)
|
|
856
|
-
copy_source_resolution_metadata(entry, raw_entry)
|
|
857
|
-
if unproven_plugin_route(entry):
|
|
858
|
-
entry = connector_confirmation_base_entry(category_id, metadata)
|
|
859
|
-
entry.update(
|
|
860
|
-
{
|
|
861
|
-
"status": "needs_confirmation",
|
|
862
|
-
"candidates": preferred_routes,
|
|
863
|
-
"setup_action": "run_plugin_first_source_setup",
|
|
864
|
-
"setup_note": (
|
|
865
|
-
"plugin_route_requires_plugin_install_and_surface_evidence_before_active"
|
|
866
|
-
),
|
|
867
|
-
}
|
|
868
|
-
)
|
|
869
|
-
elif status == "needs_confirmation":
|
|
870
|
-
candidates = raw_entry.get("candidates")
|
|
871
|
-
if not isinstance(candidates, list) or not candidates:
|
|
872
|
-
if preferred:
|
|
873
|
-
candidates = [preferred]
|
|
874
|
-
else:
|
|
875
|
-
candidates = preferred_routes
|
|
876
|
-
entry["candidates"] = unique_strings(candidates)
|
|
877
|
-
entry["setup_action"] = source_setup_action(status)
|
|
878
|
-
copy_configured_route_metadata(entry, raw_entry, metadata)
|
|
879
|
-
copy_source_resolution_metadata(entry, raw_entry)
|
|
880
|
-
else:
|
|
881
|
-
options = raw_entry.get("options")
|
|
882
|
-
if not isinstance(options, list) or not options:
|
|
883
|
-
options = preferred_routes
|
|
884
|
-
entry["options"] = unique_strings(options)
|
|
885
|
-
entry["it_admin_options"] = raw_entry.get(
|
|
886
|
-
"it_admin_options"
|
|
887
|
-
) or missing_source_it_options(entry["options"])
|
|
888
|
-
entry["setup_action"] = source_setup_action(status)
|
|
889
|
-
copy_configured_route_metadata(entry, raw_entry, metadata)
|
|
890
|
-
copy_source_resolution_metadata(entry, raw_entry)
|
|
891
|
-
else:
|
|
892
|
-
entry["status"] = "needs_confirmation"
|
|
893
|
-
entry["candidates"] = preferred_routes
|
|
894
|
-
entry["setup_action"] = source_setup_action("needs_confirmation")
|
|
895
|
-
else:
|
|
896
|
-
configured_apps = {
|
|
897
|
-
normalize_source_name(app)
|
|
898
|
-
for app in list_from_inventory(
|
|
899
|
-
inventory,
|
|
900
|
-
"active_apps",
|
|
901
|
-
"active",
|
|
902
|
-
"installed_and_active",
|
|
903
|
-
"connected_apps",
|
|
904
|
-
"available_apps",
|
|
905
|
-
"available",
|
|
906
|
-
"installed_apps",
|
|
907
|
-
"surfaced_apps",
|
|
908
|
-
)
|
|
909
|
-
}
|
|
910
|
-
available_matches = matching_configured_sources(preferred_routes, configured_apps)
|
|
911
|
-
if available_matches:
|
|
912
|
-
entry["status"] = "needs_confirmation"
|
|
913
|
-
entry["candidates"] = available_matches
|
|
914
|
-
entry["setup_action"] = source_setup_action("needs_confirmation")
|
|
915
|
-
elif inventory:
|
|
916
|
-
entry["status"] = "missing"
|
|
917
|
-
entry["options"] = preferred_routes
|
|
918
|
-
entry["it_admin_options"] = missing_source_it_options(preferred_routes)
|
|
919
|
-
entry["setup_action"] = source_setup_action("missing")
|
|
920
|
-
else:
|
|
921
|
-
entry["status"] = "needs_confirmation"
|
|
922
|
-
entry["candidates"] = preferred_routes
|
|
923
|
-
entry["setup_action"] = source_setup_action("needs_confirmation")
|
|
924
|
-
|
|
925
|
-
if entry.get("status") == "active" and raw_entry.get("status") == "manual":
|
|
926
|
-
entry.setdefault("source_kind", "manual")
|
|
927
|
-
entry.setdefault("skill_surface", "manual")
|
|
928
|
-
entry.setdefault("runtime_action", "use_manual_or_exported_context")
|
|
929
|
-
|
|
930
|
-
if "preferred" not in entry:
|
|
931
|
-
entry["preferred"] = raw_entry.get("preferred")
|
|
932
|
-
|
|
933
|
-
relevant_skills = metadata.get("relevant_skills") or []
|
|
934
|
-
if relevant_skills and str(entry.get("source_kind") or "").casefold() != "plugin":
|
|
935
|
-
entry["helper_skills"] = relevant_skills
|
|
936
|
-
entry["helper_skills_apply_when"] = ["app", "connector"]
|
|
937
|
-
|
|
938
|
-
plugin_preference_order = preferred_source_routes(metadata)
|
|
939
|
-
if (
|
|
940
|
-
preferred_plugins
|
|
941
|
-
and plugin_preference_order
|
|
942
|
-
and entry["status"] in PLUGIN_FIRST_SETUP_STATUSES
|
|
943
|
-
):
|
|
944
|
-
entry["plugin_preference_order"] = plugin_preference_order
|
|
945
|
-
if not active_plugin_route_present(entry):
|
|
946
|
-
previous_setup_action = entry.get("setup_action")
|
|
947
|
-
entry["setup_action"] = "run_plugin_first_source_setup"
|
|
948
|
-
if previous_setup_action and previous_setup_action != entry["setup_action"]:
|
|
949
|
-
entry["fallback_setup_action"] = previous_setup_action
|
|
950
|
-
entry["setup_recovery"] = plugin_first_setup_recovery(
|
|
951
|
-
metadata,
|
|
952
|
-
str(previous_setup_action or source_setup_action(entry["status"])),
|
|
953
|
-
)
|
|
954
|
-
normalize_unacknowledged_core_source_fallback(category_id, entry, metadata)
|
|
955
|
-
confirmation[category_id] = entry
|
|
956
|
-
return confirmation
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
def connector_confirmation_status(
|
|
960
|
-
source_category_config: dict[str, Any],
|
|
961
|
-
onboarding_state: dict[str, Any] | None,
|
|
962
|
-
) -> str:
|
|
963
|
-
state = onboarding_state or {}
|
|
964
|
-
if not connector_categories(onboarding_state) and not state.get("connector_inventory"):
|
|
965
|
-
return "pending"
|
|
966
|
-
confirmation = normalized_connector_confirmation(source_category_config, onboarding_state)
|
|
967
|
-
statuses = [confirmation[category_id]["status"] for category_id in CORE_CATEGORY_IDS]
|
|
968
|
-
if statuses and all(status in SOURCE_CLASSIFIED_STATUSES for status in statuses):
|
|
969
|
-
return "completed"
|
|
970
|
-
if any(status for status in statuses):
|
|
971
|
-
return "in_progress"
|
|
972
|
-
return "pending"
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
def connector_confirmation_action_required(
|
|
976
|
-
source_category_config: dict[str, Any],
|
|
977
|
-
onboarding_state: dict[str, Any] | None,
|
|
978
|
-
) -> bool:
|
|
979
|
-
if connector_confirmation_status(source_category_config, onboarding_state) == "pending":
|
|
980
|
-
return False
|
|
981
|
-
confirmation = normalized_connector_confirmation(source_category_config, onboarding_state)
|
|
982
|
-
return any(
|
|
983
|
-
confirmation[category_id]["status"] in SOURCE_ACTION_REQUIRED_STATUSES
|
|
984
|
-
for category_id in CORE_CATEGORY_IDS
|
|
985
|
-
)
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
def connector_setup_confirmation_status(
|
|
989
|
-
source_category_config: dict[str, Any],
|
|
990
|
-
onboarding_state: dict[str, Any] | None,
|
|
991
|
-
) -> str:
|
|
992
|
-
confirmation_status = connector_confirmation_status(source_category_config, onboarding_state)
|
|
993
|
-
if confirmation_status == "pending":
|
|
994
|
-
return "pending"
|
|
995
|
-
if connector_confirmation_action_required(source_category_config, onboarding_state):
|
|
996
|
-
return "in_progress"
|
|
997
|
-
return "completed"
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
def compact_connector_setup_summary_entry(entry: dict[str, Any]) -> dict[str, Any]:
|
|
1001
|
-
compact = {
|
|
1002
|
-
key: entry[key]
|
|
1003
|
-
for key in (
|
|
1004
|
-
"id",
|
|
1005
|
-
"label",
|
|
1006
|
-
"status",
|
|
1007
|
-
"preferred",
|
|
1008
|
-
"setup_action",
|
|
1009
|
-
"fallback_setup_action",
|
|
1010
|
-
"source_kind",
|
|
1011
|
-
)
|
|
1012
|
-
if key in entry
|
|
1013
|
-
}
|
|
1014
|
-
for key in ("candidates", "options"):
|
|
1015
|
-
values = entry.get(key)
|
|
1016
|
-
if isinstance(values, list) and values:
|
|
1017
|
-
compact[key] = unique_strings(values)
|
|
1018
|
-
return compact
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
def build_connector_setup_summary(
|
|
1022
|
-
source_category_config: dict[str, Any],
|
|
1023
|
-
onboarding_state: dict[str, Any] | None,
|
|
1024
|
-
) -> dict[str, Any]:
|
|
1025
|
-
confirmation = normalized_connector_confirmation(source_category_config, onboarding_state)
|
|
1026
|
-
ready: list[dict[str, Any]] = []
|
|
1027
|
-
needs_attention: list[dict[str, Any]] = []
|
|
1028
|
-
fallback_or_closed: list[dict[str, Any]] = []
|
|
1029
|
-
plugin_setup_opportunities: list[dict[str, Any]] = []
|
|
1030
|
-
unresolved_core_ids: list[str] = []
|
|
1031
|
-
for category_id, entry in confirmation.items():
|
|
1032
|
-
status = entry["status"]
|
|
1033
|
-
summary_entry = compact_connector_setup_summary_entry(entry)
|
|
1034
|
-
if status == "active":
|
|
1035
|
-
ready.append(summary_entry)
|
|
1036
|
-
elif status in ONBOARDING_RESOLVED_SOURCE_STATUSES:
|
|
1037
|
-
fallback_or_closed.append(summary_entry)
|
|
1038
|
-
else:
|
|
1039
|
-
needs_attention.append(summary_entry)
|
|
1040
|
-
if entry.get("setup_action") == "run_plugin_first_source_setup":
|
|
1041
|
-
plugin_setup_opportunities.append(summary_entry)
|
|
1042
|
-
if category_id in CORE_CATEGORY_IDS and status in SOURCE_ACTION_REQUIRED_STATUSES:
|
|
1043
|
-
unresolved_core_ids.append(category_id)
|
|
1044
|
-
next_entry = needs_attention[0] if needs_attention else None
|
|
1045
|
-
return {
|
|
1046
|
-
"status": connector_setup_confirmation_status(source_category_config, onboarding_state),
|
|
1047
|
-
"classification_status": connector_confirmation_status(
|
|
1048
|
-
source_category_config,
|
|
1049
|
-
onboarding_state,
|
|
1050
|
-
),
|
|
1051
|
-
"action_required": connector_confirmation_action_required(
|
|
1052
|
-
source_category_config,
|
|
1053
|
-
onboarding_state,
|
|
1054
|
-
),
|
|
1055
|
-
"has_setup_gaps": bool(needs_attention or plugin_setup_opportunities),
|
|
1056
|
-
"ready": ready,
|
|
1057
|
-
"active": ready,
|
|
1058
|
-
"needs_attention": needs_attention,
|
|
1059
|
-
"needs_choice": needs_attention,
|
|
1060
|
-
"fallback_or_closed": fallback_or_closed,
|
|
1061
|
-
"not_set_up": fallback_or_closed,
|
|
1062
|
-
"plugin_setup_opportunities": plugin_setup_opportunities,
|
|
1063
|
-
"unresolved_core_ids": unresolved_core_ids,
|
|
1064
|
-
"core_complete": connector_setup_confirmation_status(
|
|
1065
|
-
source_category_config,
|
|
1066
|
-
onboarding_state,
|
|
1067
|
-
)
|
|
1068
|
-
== "completed",
|
|
1069
|
-
"next_action": (
|
|
1070
|
-
{
|
|
1071
|
-
"category_id": next_entry["id"],
|
|
1072
|
-
"label": next_entry["label"],
|
|
1073
|
-
"status": next_entry["status"],
|
|
1074
|
-
"preferred": next_entry["preferred"],
|
|
1075
|
-
"setup_action": next_entry["setup_action"],
|
|
1076
|
-
}
|
|
1077
|
-
if next_entry
|
|
1078
|
-
else None
|
|
1079
|
-
),
|
|
1080
|
-
"user_facing_guidance": (
|
|
1081
|
-
"Use this summary for capability or setup-status answers. During onboarding, "
|
|
1082
|
-
"resolve one highest-value source action at a time. Active sources are FYI only; "
|
|
1083
|
-
"attempt actual reads only when a workflow needs the source."
|
|
1084
|
-
),
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
def semantic_layer_setup_status(onboarding_state: dict[str, Any] | None) -> str:
|
|
1089
|
-
state = onboarding_state or {}
|
|
1090
|
-
return str((state.get("semantic_layer_setup") or {}).get("status") or "pending")
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
def semantic_layer_refresh_status(onboarding_state: dict[str, Any] | None) -> str:
|
|
1094
|
-
state = onboarding_state or {}
|
|
1095
|
-
return str((state.get("semantic_layer_refresh") or {}).get("status") or "pending")
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
def semantic_layer_setup_resolved(onboarding_state: dict[str, Any] | None) -> bool:
|
|
1099
|
-
setup_status = semantic_layer_setup_status(onboarding_state)
|
|
1100
|
-
if setup_status not in SEMANTIC_SETUP_COMPLETE_STATUSES:
|
|
1101
|
-
return False
|
|
1102
|
-
if setup_status in SEMANTIC_SETUP_TARGET_STATUSES:
|
|
1103
|
-
return semantic_layer_refresh_status(onboarding_state) in SEMANTIC_REFRESH_COMPLETE_STATUSES
|
|
1104
|
-
return True
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
def hero_prompt_state(onboarding_state: dict[str, Any] | None) -> dict[str, Any]:
|
|
1108
|
-
state = onboarding_state or {}
|
|
1109
|
-
choice = state.get("hero_prompt_choice")
|
|
1110
|
-
if isinstance(choice, dict):
|
|
1111
|
-
return choice
|
|
1112
|
-
legacy = state.get("hero_prompts")
|
|
1113
|
-
return legacy if isinstance(legacy, dict) else {}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
def first_hero_prompt_status(onboarding_state: dict[str, Any] | None) -> str:
|
|
1117
|
-
choice = hero_prompt_state(onboarding_state)
|
|
1118
|
-
status = str(choice.get("status") or "")
|
|
1119
|
-
if status in HERO_PROMPT_COMPLETE_STATUSES:
|
|
1120
|
-
return "completed"
|
|
1121
|
-
if status == "selected":
|
|
1122
|
-
return "in_progress"
|
|
1123
|
-
skill_experience = (onboarding_state or {}).get("skill_experience") or {}
|
|
1124
|
-
if any(
|
|
1125
|
-
isinstance(skill_experience.get(skill), dict)
|
|
1126
|
-
and skill_experience[skill].get("first_tried_at")
|
|
1127
|
-
for skill in HERO_SKILLS
|
|
1128
|
-
):
|
|
1129
|
-
return "completed"
|
|
1130
|
-
if (onboarding_state or {}).get("completed_hero_prompts"):
|
|
1131
|
-
return "completed"
|
|
1132
|
-
return "pending"
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
def core_onboarding_complete(
|
|
1136
|
-
source_category_config: dict[str, Any],
|
|
1137
|
-
onboarding_state: dict[str, Any] | None,
|
|
1138
|
-
) -> bool:
|
|
1139
|
-
if not onboarding_state:
|
|
1140
|
-
return False
|
|
1141
|
-
explicit_status = str(
|
|
1142
|
-
(onboarding_state.get("core_onboarding") or {}).get("status") or ""
|
|
1143
|
-
).casefold()
|
|
1144
|
-
if explicit_status in {"completed", "complete", "complete_with_fallback"}:
|
|
1145
|
-
return True
|
|
1146
|
-
return (
|
|
1147
|
-
connector_setup_confirmation_status(source_category_config, onboarding_state)
|
|
1148
|
-
== "completed"
|
|
1149
|
-
and semantic_layer_setup_resolved(onboarding_state)
|
|
1150
|
-
)
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
def progress_task_list(
|
|
1154
|
-
source_category_config: dict[str, Any],
|
|
1155
|
-
onboarding_state: dict[str, Any] | None,
|
|
1156
|
-
) -> list[dict[str, str]]:
|
|
1157
|
-
state = onboarding_state or {}
|
|
1158
|
-
status = onboarding_status(onboarding_state)
|
|
1159
|
-
task_specs = (
|
|
1160
|
-
(
|
|
1161
|
-
"orientation",
|
|
1162
|
-
"Orientation",
|
|
1163
|
-
(state.get("orientation") or {}).get("status") in {"shown", "completed"},
|
|
1164
|
-
),
|
|
1165
|
-
(
|
|
1166
|
-
"source_setup_confirmation",
|
|
1167
|
-
"Check main analytics sources",
|
|
1168
|
-
connector_setup_confirmation_status(source_category_config, onboarding_state)
|
|
1169
|
-
== "completed",
|
|
1170
|
-
),
|
|
1171
|
-
(
|
|
1172
|
-
"semantic_layer_setup",
|
|
1173
|
-
"Set Up Data Context",
|
|
1174
|
-
semantic_layer_setup_resolved(onboarding_state),
|
|
1175
|
-
),
|
|
1176
|
-
("hero_prompt", "Hero prompt", first_hero_prompt_status(onboarding_state) == "completed"),
|
|
1177
|
-
)
|
|
1178
|
-
found_in_progress = False
|
|
1179
|
-
task_list: list[dict[str, str]] = []
|
|
1180
|
-
for task_id, label, completed in task_specs:
|
|
1181
|
-
if completed:
|
|
1182
|
-
task_status = "completed"
|
|
1183
|
-
elif not found_in_progress and status not in {"complete", "quiet"}:
|
|
1184
|
-
task_status = "in_progress"
|
|
1185
|
-
found_in_progress = True
|
|
1186
|
-
else:
|
|
1187
|
-
task_status = "pending"
|
|
1188
|
-
task_list.append({"id": task_id, "label": label, "status": task_status})
|
|
1189
|
-
return task_list
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
def next_onboarding_action(
|
|
1193
|
-
source_category_config: dict[str, Any],
|
|
1194
|
-
onboarding_state: dict[str, Any] | None,
|
|
1195
|
-
) -> dict[str, str] | None:
|
|
1196
|
-
tasks = progress_task_list(source_category_config, onboarding_state)
|
|
1197
|
-
for task in tasks:
|
|
1198
|
-
if task["status"] == "completed":
|
|
1199
|
-
continue
|
|
1200
|
-
mapping = {
|
|
1201
|
-
"orientation": ("start_data_analytics_onboarding", "show orientation"),
|
|
1202
|
-
"source_setup_confirmation": (
|
|
1203
|
-
"confirm_analytics_sources",
|
|
1204
|
-
"confirm active or missing sources",
|
|
1205
|
-
),
|
|
1206
|
-
"semantic_layer_setup": (
|
|
1207
|
-
"introduce_semantic_layer_setup",
|
|
1208
|
-
"set up data context",
|
|
1209
|
-
),
|
|
1210
|
-
"hero_prompt": ("offer_first_hero_prompt", "run or resolve the first hero prompt"),
|
|
1211
|
-
}
|
|
1212
|
-
action_id, label = mapping[task["id"]]
|
|
1213
|
-
return {"id": action_id, "label": label}
|
|
1214
|
-
return None
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
def semantic_layer_registry(user_context_text: str | None) -> list[dict[str, str]]:
|
|
1218
|
-
if not user_context_text:
|
|
1219
|
-
return []
|
|
1220
|
-
bounds = section_bounds(user_context_text)
|
|
1221
|
-
semantic_section = bounds.get("semantic layers")
|
|
1222
|
-
if not semantic_section:
|
|
1223
|
-
return []
|
|
1224
|
-
start, end, _ = semantic_section
|
|
1225
|
-
section = user_context_text[start:end]
|
|
1226
|
-
entries: list[dict[str, str]] = []
|
|
1227
|
-
for block in re.split(r"(?m)^- Area:\s*", section)[1:]:
|
|
1228
|
-
lines = block.splitlines()
|
|
1229
|
-
area = lines[0].strip()
|
|
1230
|
-
entry: dict[str, str] = {"area": area}
|
|
1231
|
-
for line in lines[1:]:
|
|
1232
|
-
match = re.match(
|
|
1233
|
-
r"\s*-\s*(Skill Name|Skill Path|Source Inventory Path|Last Updated):\s*(.+?)\s*$",
|
|
1234
|
-
line,
|
|
1235
|
-
)
|
|
1236
|
-
if match:
|
|
1237
|
-
entry[match.group(1).lower().replace(" ", "_")] = match.group(2)
|
|
1238
|
-
if "skill_path" in entry:
|
|
1239
|
-
entries.append(entry)
|
|
1240
|
-
return entries
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
def saved_anchor_values(user_context_text: str | None) -> dict[str, list[str]]:
|
|
1244
|
-
if not user_context_text:
|
|
1245
|
-
return {"areas": [], "metrics": [], "dashboards": [], "tables": [], "goals": []}
|
|
1246
|
-
return {
|
|
1247
|
-
"areas": [entry["area"] for entry in semantic_layer_registry(user_context_text)],
|
|
1248
|
-
"metrics": [],
|
|
1249
|
-
"dashboards": [],
|
|
1250
|
-
"tables": [],
|
|
1251
|
-
"goals": [],
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
def build_hero_prompt_candidates(
|
|
1256
|
-
user_context_text: str | None,
|
|
1257
|
-
onboarding_state: dict[str, Any] | None,
|
|
1258
|
-
) -> list[dict[str, str]]:
|
|
1259
|
-
anchors = saved_anchor_values(user_context_text)
|
|
1260
|
-
semantic_setup = (onboarding_state or {}).get("semantic_layer_setup") or {}
|
|
1261
|
-
area = semantic_setup.get("area") or (anchors["areas"][0] if anchors["areas"] else None)
|
|
1262
|
-
metric = anchors["metrics"][0] if anchors["metrics"] else None
|
|
1263
|
-
dashboard = anchors["dashboards"][0] if anchors["dashboards"] else None
|
|
1264
|
-
goal = anchors["goals"][0] if anchors["goals"] else None
|
|
1265
|
-
target = area or goal or dashboard or metric
|
|
1266
|
-
if not target:
|
|
1267
|
-
return []
|
|
1268
|
-
|
|
1269
|
-
return [
|
|
1270
|
-
{
|
|
1271
|
-
"skill": "product-business-analysis",
|
|
1272
|
-
"label": "Build a decision-ready report",
|
|
1273
|
-
"prompt": (
|
|
1274
|
-
f"Build a decision-ready report for {target}: explain what is happening, "
|
|
1275
|
-
"identify the main drivers, recommend where the team should focus next, "
|
|
1276
|
-
"and include sources, caveats, and useful charts."
|
|
1277
|
-
),
|
|
1278
|
-
}
|
|
1279
|
-
]
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
def reorder_hero_prompt_candidates(
|
|
1283
|
-
candidates: list[dict[str, str]],
|
|
1284
|
-
onboarding_state: dict[str, Any] | None,
|
|
1285
|
-
) -> list[dict[str, str]]:
|
|
1286
|
-
selected_skill = str(hero_prompt_state(onboarding_state).get("selected_skill") or "")
|
|
1287
|
-
if not selected_skill:
|
|
1288
|
-
return candidates
|
|
1289
|
-
selected = [candidate for candidate in candidates if candidate["skill"] == selected_skill]
|
|
1290
|
-
remaining = [candidate for candidate in candidates if candidate["skill"] != selected_skill]
|
|
1291
|
-
return selected + remaining
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
def current_skill_experience(
|
|
1295
|
-
workflow: str,
|
|
1296
|
-
request_mode: str,
|
|
1297
|
-
onboarding_state: dict[str, Any] | None,
|
|
1298
|
-
) -> dict[str, Any]:
|
|
1299
|
-
skill_experience = (onboarding_state or {}).get("skill_experience") or {}
|
|
1300
|
-
entry = skill_experience.get(workflow) if isinstance(skill_experience, dict) else None
|
|
1301
|
-
entry = entry if isinstance(entry, dict) else {}
|
|
1302
|
-
introduced = bool(entry.get("introduced_at"))
|
|
1303
|
-
tried = bool(entry.get("first_tried_at"))
|
|
1304
|
-
dismissed = bool(entry.get("dismissed_at"))
|
|
1305
|
-
return {
|
|
1306
|
-
"skill": workflow,
|
|
1307
|
-
"introduced": introduced,
|
|
1308
|
-
"tried": tried,
|
|
1309
|
-
"dismissed": dismissed,
|
|
1310
|
-
"intro_eligible": workflow in HERO_SKILLS and not introduced and not dismissed,
|
|
1311
|
-
"cta_owner": "onboarding" if request_mode == "guided_onboarding_workflow" else "skill",
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
def final_obligations(
|
|
1316
|
-
request_mode: str,
|
|
1317
|
-
user_context_status: str,
|
|
1318
|
-
source_category_config: dict[str, Any],
|
|
1319
|
-
onboarding_state: dict[str, Any] | None,
|
|
1320
|
-
) -> list[dict[str, Any]]:
|
|
1321
|
-
if request_mode != "ordinary_workflow":
|
|
1322
|
-
return []
|
|
1323
|
-
status = onboarding_status(onboarding_state)
|
|
1324
|
-
if status in {"complete", "quiet"}:
|
|
1325
|
-
return []
|
|
1326
|
-
if user_context_status in {"missing", "unreadable"}:
|
|
1327
|
-
return [
|
|
1328
|
-
{
|
|
1329
|
-
"id": "offer_data_analytics_onboarding_next_step",
|
|
1330
|
-
"timing": "append_after_main_answer",
|
|
1331
|
-
"only_when": "missing setup materially affects the requested output and the user has not requested inline-only or quiet delivery",
|
|
1332
|
-
"template": markdown_template_from_onboarding_reference(
|
|
1333
|
-
"Ordinary Workflow Onboarding CTA"
|
|
1334
|
-
),
|
|
1335
|
-
"next_action": {"id": "start_data_analytics_onboarding"},
|
|
1336
|
-
"skip_when": "the response requires a clarification or the user asked for quiet behavior",
|
|
1337
|
-
}
|
|
1338
|
-
]
|
|
1339
|
-
if not core_onboarding_complete(source_category_config, onboarding_state):
|
|
1340
|
-
return [
|
|
1341
|
-
{
|
|
1342
|
-
"id": "complete_data_analytics_core_onboarding",
|
|
1343
|
-
"timing": "append_after_main_answer",
|
|
1344
|
-
"only_when": "missing setup materially affects the requested output and the user has not requested inline-only or quiet delivery",
|
|
1345
|
-
"template": markdown_template_from_onboarding_reference(
|
|
1346
|
-
"Active Core Onboarding Reminder"
|
|
1347
|
-
),
|
|
1348
|
-
"requirement": "core onboarding is not complete",
|
|
1349
|
-
"next_action": next_onboarding_action(source_category_config, onboarding_state),
|
|
1350
|
-
"skip_when": "the response requires a clarification or the user asked for quiet behavior",
|
|
1351
|
-
}
|
|
1352
|
-
]
|
|
1353
|
-
return []
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
def conditional_guidance(request_mode: str, user_context_status: str) -> list[dict[str, Any]]:
|
|
1357
|
-
if request_mode != "ordinary_workflow":
|
|
1358
|
-
return []
|
|
1359
|
-
if user_context_status not in {"missing", "unreadable"}:
|
|
1360
|
-
return []
|
|
1361
|
-
return [
|
|
1362
|
-
{
|
|
1363
|
-
"id": "context_gap_note",
|
|
1364
|
-
"template": markdown_template_from_onboarding_reference("Context Gap Note"),
|
|
1365
|
-
"potential_gaps": [
|
|
1366
|
-
"Data Analytics source-routing preferences or semantic-layer registry are missing or unreadable"
|
|
1367
|
-
],
|
|
1368
|
-
}
|
|
1369
|
-
]
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
def main() -> int:
|
|
1373
|
-
args = parse_args()
|
|
1374
|
-
state_dir = state_dir_from_args(args)
|
|
1375
|
-
user_context_path = state_dir / "user-context.md"
|
|
1376
|
-
onboarding_state_path = state_dir / "onboarding-state.json"
|
|
1377
|
-
|
|
1378
|
-
user_context = read_text_payload(
|
|
1379
|
-
user_context_path,
|
|
1380
|
-
max_context_bytes=args.max_context_bytes,
|
|
1381
|
-
sections=args.section,
|
|
1382
|
-
)
|
|
1383
|
-
onboarding_state = read_json_payload(
|
|
1384
|
-
onboarding_state_path,
|
|
1385
|
-
max_context_bytes=args.max_context_bytes,
|
|
1386
|
-
)
|
|
1387
|
-
onboarding_data = (
|
|
1388
|
-
onboarding_state.get("data") if onboarding_state["status"] == "present" else None
|
|
1389
|
-
)
|
|
1390
|
-
source_category_config = load_source_category_config()
|
|
1391
|
-
user_context_text = payload_text(user_context)
|
|
1392
|
-
saved_source_preferences = extract_saved_source_preferences(
|
|
1393
|
-
user_context_text,
|
|
1394
|
-
source_category_config,
|
|
1395
|
-
)
|
|
1396
|
-
connector_confirmation = normalized_connector_confirmation(
|
|
1397
|
-
source_category_config, onboarding_data
|
|
1398
|
-
)
|
|
1399
|
-
connector_setup_summary = build_connector_setup_summary(source_category_config, onboarding_data)
|
|
1400
|
-
hero_prompt_candidates = reorder_hero_prompt_candidates(
|
|
1401
|
-
build_hero_prompt_candidates(user_context_text, onboarding_data),
|
|
1402
|
-
onboarding_data,
|
|
1403
|
-
)
|
|
1404
|
-
task_list = progress_task_list(source_category_config, onboarding_data)
|
|
1405
|
-
payload = {
|
|
1406
|
-
"schema": "data_analytics_preflight.v1",
|
|
1407
|
-
"read_only": True,
|
|
1408
|
-
"workflow": args.workflow,
|
|
1409
|
-
"request_mode": args.request_mode,
|
|
1410
|
-
"state": {
|
|
1411
|
-
"state_dir": str(state_dir),
|
|
1412
|
-
"user_context_status": user_context["status"],
|
|
1413
|
-
"onboarding_state_status": onboarding_state["status"],
|
|
1414
|
-
},
|
|
1415
|
-
"files": {
|
|
1416
|
-
"user_context": public_file_payload(user_context, include_content=False),
|
|
1417
|
-
"onboarding_state": public_file_payload(onboarding_state, include_content=False),
|
|
1418
|
-
},
|
|
1419
|
-
"current_skill_experience": current_skill_experience(
|
|
1420
|
-
args.workflow,
|
|
1421
|
-
args.request_mode,
|
|
1422
|
-
onboarding_data,
|
|
1423
|
-
),
|
|
1424
|
-
"context": {
|
|
1425
|
-
"user_context": summarize_user_context(user_context_text, source_category_config),
|
|
1426
|
-
"source_preferences": saved_source_preferences,
|
|
1427
|
-
"source_category_config": source_category_config,
|
|
1428
|
-
"connector_confirmation": connector_confirmation,
|
|
1429
|
-
"connector_setup_summary": connector_setup_summary,
|
|
1430
|
-
"semantic_layers": semantic_layer_registry(user_context_text),
|
|
1431
|
-
"hero_prompt_candidates": hero_prompt_candidates,
|
|
1432
|
-
"primary_hero_prompt": hero_prompt_candidates[0] if hero_prompt_candidates else None,
|
|
1433
|
-
"extra_hero_prompt_candidates": hero_prompt_candidates[1:3],
|
|
1434
|
-
},
|
|
1435
|
-
"control": {
|
|
1436
|
-
"response_mode": args.request_mode,
|
|
1437
|
-
"final_obligations": final_obligations(
|
|
1438
|
-
args.request_mode,
|
|
1439
|
-
user_context["status"],
|
|
1440
|
-
source_category_config,
|
|
1441
|
-
onboarding_data,
|
|
1442
|
-
),
|
|
1443
|
-
"conditional_guidance": conditional_guidance(args.request_mode, user_context["status"]),
|
|
1444
|
-
"onboarding_progress": {
|
|
1445
|
-
"status": onboarding_status(onboarding_data),
|
|
1446
|
-
"core_onboarding": {
|
|
1447
|
-
"complete": core_onboarding_complete(source_category_config, onboarding_data),
|
|
1448
|
-
"remaining_step_ids": [
|
|
1449
|
-
item["id"] for item in task_list if item["status"] != "completed"
|
|
1450
|
-
],
|
|
1451
|
-
"next_action": next_onboarding_action(source_category_config, onboarding_data),
|
|
1452
|
-
},
|
|
1453
|
-
"task_list": task_list,
|
|
1454
|
-
},
|
|
1455
|
-
},
|
|
1456
|
-
}
|
|
1457
|
-
print(json.dumps(payload, indent=2))
|
|
1458
|
-
return 0
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
if __name__ == "__main__":
|
|
1462
|
-
sys.exit(main())
|