@fideliosai/server 2026.331.0-canary.0
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/LICENSE +22 -0
- package/dist/adapters/codex-models.d.ts +4 -0
- package/dist/adapters/codex-models.d.ts.map +1 -0
- package/dist/adapters/codex-models.js +98 -0
- package/dist/adapters/codex-models.js.map +1 -0
- package/dist/adapters/cursor-models.d.ts +13 -0
- package/dist/adapters/cursor-models.d.ts.map +1 -0
- package/dist/adapters/cursor-models.js +148 -0
- package/dist/adapters/cursor-models.js.map +1 -0
- package/dist/adapters/http/execute.d.ts +3 -0
- package/dist/adapters/http/execute.d.ts.map +1 -0
- package/dist/adapters/http/execute.js +39 -0
- package/dist/adapters/http/execute.js.map +1 -0
- package/dist/adapters/http/index.d.ts +3 -0
- package/dist/adapters/http/index.d.ts.map +1 -0
- package/dist/adapters/http/index.js +20 -0
- package/dist/adapters/http/index.js.map +1 -0
- package/dist/adapters/http/test.d.ts +3 -0
- package/dist/adapters/http/test.d.ts.map +1 -0
- package/dist/adapters/http/test.js +106 -0
- package/dist/adapters/http/test.js.map +1 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/process/execute.d.ts +3 -0
- package/dist/adapters/process/execute.d.ts.map +1 -0
- package/dist/adapters/process/execute.js +63 -0
- package/dist/adapters/process/execute.js.map +1 -0
- package/dist/adapters/process/index.d.ts +3 -0
- package/dist/adapters/process/index.d.ts.map +1 -0
- package/dist/adapters/process/index.js +23 -0
- package/dist/adapters/process/index.js.map +1 -0
- package/dist/adapters/process/test.d.ts +3 -0
- package/dist/adapters/process/test.d.ts.map +1 -0
- package/dist/adapters/process/test.js +77 -0
- package/dist/adapters/process/test.js.map +1 -0
- package/dist/adapters/registry.d.ts +14 -0
- package/dist/adapters/registry.d.ts.map +1 -0
- package/dist/adapters/registry.js +164 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/adapters/utils.d.ts +10 -0
- package/dist/adapters/utils.d.ts.map +1 -0
- package/dist/adapters/utils.js +14 -0
- package/dist/adapters/utils.js.map +1 -0
- package/dist/agent-auth-jwt.d.ts +14 -0
- package/dist/agent-auth-jwt.d.ts.map +1 -0
- package/dist/agent-auth-jwt.js +117 -0
- package/dist/agent-auth-jwt.js.map +1 -0
- package/dist/app.d.ts +25 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +265 -0
- package/dist/app.js.map +1 -0
- package/dist/attachment-types.d.ts +33 -0
- package/dist/attachment-types.d.ts.map +1 -0
- package/dist/attachment-types.js +67 -0
- package/dist/attachment-types.js.map +1 -0
- package/dist/auth/better-auth.d.ts +24 -0
- package/dist/auth/better-auth.d.ts.map +1 -0
- package/dist/auth/better-auth.js +108 -0
- package/dist/auth/better-auth.js.map +1 -0
- package/dist/board-claim.d.ts +23 -0
- package/dist/board-claim.d.ts.map +1 -0
- package/dist/board-claim.js +115 -0
- package/dist/board-claim.js.map +1 -0
- package/dist/config-file.d.ts +3 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +16 -0
- package/dist/config-file.js.map +1 -0
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +171 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-server-status.d.ts +27 -0
- package/dist/dev-server-status.d.ts.map +1 -0
- package/dist/dev-server-status.js +70 -0
- package/dist/dev-server-status.js.map +1 -0
- package/dist/dev-watch-ignore.d.ts +2 -0
- package/dist/dev-watch-ignore.d.ts.map +1 -0
- package/dist/dev-watch-ignore.js +33 -0
- package/dist/dev-watch-ignore.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -0
- package/dist/home-paths.d.ts +17 -0
- package/dist/home-paths.d.ts.map +1 -0
- package/dist/home-paths.js +75 -0
- package/dist/home-paths.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +642 -0
- package/dist/index.js.map +1 -0
- package/dist/log-redaction.d.ts +11 -0
- package/dist/log-redaction.d.ts.map +1 -0
- package/dist/log-redaction.js +118 -0
- package/dist/log-redaction.js.map +1 -0
- package/dist/middleware/auth.d.ts +12 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +144 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/board-mutation-guard.d.ts +3 -0
- package/dist/middleware/board-mutation-guard.d.ts.map +1 -0
- package/dist/middleware/board-mutation-guard.js +59 -0
- package/dist/middleware/board-mutation-guard.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +17 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +37 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logger.d.ts +4 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +87 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/private-hostname-guard.d.ts +11 -0
- package/dist/middleware/private-hostname-guard.d.ts.map +1 -0
- package/dist/middleware/private-hostname-guard.js +78 -0
- package/dist/middleware/private-hostname-guard.js.map +1 -0
- package/dist/middleware/validate.d.ts +4 -0
- package/dist/middleware/validate.d.ts.map +1 -0
- package/dist/middleware/validate.js +7 -0
- package/dist/middleware/validate.js.map +1 -0
- package/dist/onboarding-assets/ceo/AGENTS.md +54 -0
- package/dist/onboarding-assets/ceo/HEARTBEAT.md +72 -0
- package/dist/onboarding-assets/ceo/SOUL.md +33 -0
- package/dist/onboarding-assets/ceo/TOOLS.md +3 -0
- package/dist/onboarding-assets/default/AGENTS.md +3 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +31 -0
- package/dist/paths.js.map +1 -0
- package/dist/realtime/live-events-ws.d.ts +28 -0
- package/dist/realtime/live-events-ws.d.ts.map +1 -0
- package/dist/realtime/live-events-ws.js +187 -0
- package/dist/realtime/live-events-ws.js.map +1 -0
- package/dist/redaction.d.ts +4 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +63 -0
- package/dist/redaction.js.map +1 -0
- package/dist/routes/access.d.ts +61 -0
- package/dist/routes/access.d.ts.map +1 -0
- package/dist/routes/access.js +2265 -0
- package/dist/routes/access.js.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -0
- package/dist/routes/activity.js +78 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/agents.d.ts +3 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +1828 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/approvals.d.ts +3 -0
- package/dist/routes/approvals.d.ts.map +1 -0
- package/dist/routes/approvals.js +275 -0
- package/dist/routes/approvals.js.map +1 -0
- package/dist/routes/assets.d.ts +4 -0
- package/dist/routes/assets.d.ts.map +1 -0
- package/dist/routes/assets.js +309 -0
- package/dist/routes/assets.js.map +1 -0
- package/dist/routes/authz.d.ts +16 -0
- package/dist/routes/authz.d.ts.map +1 -0
- package/dist/routes/authz.js +47 -0
- package/dist/routes/authz.js.map +1 -0
- package/dist/routes/companies.d.ts +4 -0
- package/dist/routes/companies.d.ts.map +1 -0
- package/dist/routes/companies.js +303 -0
- package/dist/routes/companies.js.map +1 -0
- package/dist/routes/company-skills.d.ts +3 -0
- package/dist/routes/company-skills.d.ts.map +1 -0
- package/dist/routes/company-skills.js +228 -0
- package/dist/routes/company-skills.js.map +1 -0
- package/dist/routes/costs.d.ts +3 -0
- package/dist/routes/costs.d.ts.map +1 -0
- package/dist/routes/costs.js +268 -0
- package/dist/routes/costs.js.map +1 -0
- package/dist/routes/dashboard.d.ts +3 -0
- package/dist/routes/dashboard.d.ts.map +1 -0
- package/dist/routes/dashboard.js +15 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/execution-workspaces.d.ts +3 -0
- package/dist/routes/execution-workspaces.d.ts.map +1 -0
- package/dist/routes/execution-workspaces.js +165 -0
- package/dist/routes/execution-workspaces.js.map +1 -0
- package/dist/routes/goals.d.ts +3 -0
- package/dist/routes/goals.d.ts.map +1 -0
- package/dist/routes/goals.js +95 -0
- package/dist/routes/goals.js.map +1 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +69 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +18 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +18 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/instance-settings.d.ts +3 -0
- package/dist/routes/instance-settings.d.ts.map +1 -0
- package/dist/routes/instance-settings.js +71 -0
- package/dist/routes/instance-settings.js.map +1 -0
- package/dist/routes/issues-checkout-wakeup.d.ts +9 -0
- package/dist/routes/issues-checkout-wakeup.d.ts.map +1 -0
- package/dist/routes/issues-checkout-wakeup.js +12 -0
- package/dist/routes/issues-checkout-wakeup.js.map +1 -0
- package/dist/routes/issues.d.ts +4 -0
- package/dist/routes/issues.d.ts.map +1 -0
- package/dist/routes/issues.js +1520 -0
- package/dist/routes/issues.js.map +1 -0
- package/dist/routes/llms.d.ts +3 -0
- package/dist/routes/llms.d.ts.map +1 -0
- package/dist/routes/llms.js +78 -0
- package/dist/routes/llms.js.map +1 -0
- package/dist/routes/org-chart-svg.d.ts +25 -0
- package/dist/routes/org-chart-svg.d.ts.map +1 -0
- package/dist/routes/org-chart-svg.js +657 -0
- package/dist/routes/org-chart-svg.js.map +1 -0
- package/dist/routes/plugin-ui-static.d.ts +69 -0
- package/dist/routes/plugin-ui-static.d.ts.map +1 -0
- package/dist/routes/plugin-ui-static.js +411 -0
- package/dist/routes/plugin-ui-static.js.map +1 -0
- package/dist/routes/plugins.d.ts +120 -0
- package/dist/routes/plugins.d.ts.map +1 -0
- package/dist/routes/plugins.js +1784 -0
- package/dist/routes/plugins.js.map +1 -0
- package/dist/routes/projects.d.ts +3 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +257 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/routines.d.ts +3 -0
- package/dist/routes/routines.d.ts.map +1 -0
- package/dist/routes/routines.js +277 -0
- package/dist/routes/routines.js.map +1 -0
- package/dist/routes/secrets.d.ts +3 -0
- package/dist/routes/secrets.d.ts.map +1 -0
- package/dist/routes/secrets.js +128 -0
- package/dist/routes/secrets.js.map +1 -0
- package/dist/routes/sidebar-badges.d.ts +3 -0
- package/dist/routes/sidebar-badges.d.ts.map +1 -0
- package/dist/routes/sidebar-badges.js +45 -0
- package/dist/routes/sidebar-badges.js.map +1 -0
- package/dist/secrets/external-stub-providers.d.ts +5 -0
- package/dist/secrets/external-stub-providers.d.ts.map +1 -0
- package/dist/secrets/external-stub-providers.js +21 -0
- package/dist/secrets/external-stub-providers.js.map +1 -0
- package/dist/secrets/local-encrypted-provider.d.ts +3 -0
- package/dist/secrets/local-encrypted-provider.d.ts.map +1 -0
- package/dist/secrets/local-encrypted-provider.js +116 -0
- package/dist/secrets/local-encrypted-provider.js.map +1 -0
- package/dist/secrets/provider-registry.d.ts +5 -0
- package/dist/secrets/provider-registry.d.ts.map +1 -0
- package/dist/secrets/provider-registry.js +20 -0
- package/dist/secrets/provider-registry.js.map +1 -0
- package/dist/secrets/types.d.ts +21 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +2 -0
- package/dist/secrets/types.js.map +1 -0
- package/dist/services/access.d.ts +113 -0
- package/dist/services/access.d.ts.map +1 -0
- package/dist/services/access.js +247 -0
- package/dist/services/access.js.map +1 -0
- package/dist/services/activity-log.d.ts +17 -0
- package/dist/services/activity-log.d.ts.map +1 -0
- package/dist/services/activity-log.js +74 -0
- package/dist/services/activity-log.js.map +1 -0
- package/dist/services/activity.d.ts +764 -0
- package/dist/services/activity.d.ts.map +1 -0
- package/dist/services/activity.js +105 -0
- package/dist/services/activity.js.map +1 -0
- package/dist/services/agent-instructions.d.ts +91 -0
- package/dist/services/agent-instructions.d.ts.map +1 -0
- package/dist/services/agent-instructions.js +580 -0
- package/dist/services/agent-instructions.js.map +1 -0
- package/dist/services/agent-permissions.d.ts +6 -0
- package/dist/services/agent-permissions.d.ts.map +1 -0
- package/dist/services/agent-permissions.js +18 -0
- package/dist/services/agent-permissions.js.map +1 -0
- package/dist/services/agents.d.ts +1670 -0
- package/dist/services/agents.d.ts.map +1 -0
- package/dist/services/agents.js +566 -0
- package/dist/services/agents.js.map +1 -0
- package/dist/services/approvals.d.ts +546 -0
- package/dist/services/approvals.d.ts.map +1 -0
- package/dist/services/approvals.js +212 -0
- package/dist/services/approvals.js.map +1 -0
- package/dist/services/assets.d.ts +33 -0
- package/dist/services/assets.d.ts.map +1 -0
- package/dist/services/assets.js +17 -0
- package/dist/services/assets.js.map +1 -0
- package/dist/services/board-auth.d.ts +234 -0
- package/dist/services/board-auth.d.ts.map +1 -0
- package/dist/services/board-auth.js +295 -0
- package/dist/services/board-auth.js.map +1 -0
- package/dist/services/budgets.d.ts +38 -0
- package/dist/services/budgets.d.ts.map +1 -0
- package/dist/services/budgets.js +784 -0
- package/dist/services/budgets.js.map +1 -0
- package/dist/services/companies.d.ts +124 -0
- package/dist/services/companies.d.ts.map +1 -0
- package/dist/services/companies.js +256 -0
- package/dist/services/companies.js.map +1 -0
- package/dist/services/company-export-readme.d.ts +17 -0
- package/dist/services/company-export-readme.d.ts.map +1 -0
- package/dist/services/company-export-readme.js +148 -0
- package/dist/services/company-export-readme.js.map +1 -0
- package/dist/services/company-portability.d.ts +23 -0
- package/dist/services/company-portability.d.ts.map +1 -0
- package/dist/services/company-portability.js +3739 -0
- package/dist/services/company-portability.js.map +1 -0
- package/dist/services/company-skills.d.ts +77 -0
- package/dist/services/company-skills.d.ts.map +1 -0
- package/dist/services/company-skills.js +2042 -0
- package/dist/services/company-skills.js.map +1 -0
- package/dist/services/costs.d.ts +114 -0
- package/dist/services/costs.d.ts.map +1 -0
- package/dist/services/costs.js +294 -0
- package/dist/services/costs.js.map +1 -0
- package/dist/services/cron.d.ts +80 -0
- package/dist/services/cron.d.ts.map +1 -0
- package/dist/services/cron.js +300 -0
- package/dist/services/cron.js.map +1 -0
- package/dist/services/dashboard.d.ts +26 -0
- package/dist/services/dashboard.d.ts.map +1 -0
- package/dist/services/dashboard.js +98 -0
- package/dist/services/dashboard.js.map +1 -0
- package/dist/services/default-agent-instructions.d.ts +9 -0
- package/dist/services/default-agent-instructions.d.ts.map +1 -0
- package/dist/services/default-agent-instructions.js +20 -0
- package/dist/services/default-agent-instructions.js.map +1 -0
- package/dist/services/documents.d.ts +164 -0
- package/dist/services/documents.d.ts.map +1 -0
- package/dist/services/documents.js +382 -0
- package/dist/services/documents.js.map +1 -0
- package/dist/services/execution-workspace-policy.d.ts +21 -0
- package/dist/services/execution-workspace-policy.d.ts.map +1 -0
- package/dist/services/execution-workspace-policy.js +177 -0
- package/dist/services/execution-workspace-policy.js.map +1 -0
- package/dist/services/execution-workspaces.d.ts +19 -0
- package/dist/services/execution-workspaces.d.ts.map +1 -0
- package/dist/services/execution-workspaces.js +87 -0
- package/dist/services/execution-workspaces.js.map +1 -0
- package/dist/services/finance.d.ts +93 -0
- package/dist/services/finance.d.ts.map +1 -0
- package/dist/services/finance.js +120 -0
- package/dist/services/finance.js.map +1 -0
- package/dist/services/goals.d.ts +433 -0
- package/dist/services/goals.d.ts.map +1 -0
- package/dist/services/goals.js +54 -0
- package/dist/services/goals.js.map +1 -0
- package/dist/services/heartbeat-run-summary.d.ts +2 -0
- package/dist/services/heartbeat-run-summary.d.ts.map +1 -0
- package/dist/services/heartbeat-run-summary.js +30 -0
- package/dist/services/heartbeat-run-summary.js.map +1 -0
- package/dist/services/heartbeat.d.ts +812 -0
- package/dist/services/heartbeat.d.ts.map +1 -0
- package/dist/services/heartbeat.js +3156 -0
- package/dist/services/heartbeat.js.map +1 -0
- package/dist/services/hire-hook.d.ts +14 -0
- package/dist/services/hire-hook.d.ts.map +1 -0
- package/dist/services/hire-hook.js +85 -0
- package/dist/services/hire-hook.js.map +1 -0
- package/dist/services/index.d.ts +33 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +33 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/instance-settings.d.ts +11 -0
- package/dist/services/instance-settings.d.ts.map +1 -0
- package/dist/services/instance-settings.js +116 -0
- package/dist/services/instance-settings.js.map +1 -0
- package/dist/services/issue-approvals.d.ts +56 -0
- package/dist/services/issue-approvals.d.ts.map +1 -0
- package/dist/services/issue-approvals.js +153 -0
- package/dist/services/issue-approvals.js.map +1 -0
- package/dist/services/issue-assignment-wakeup.d.ts +29 -0
- package/dist/services/issue-assignment-wakeup.d.ts.map +1 -0
- package/dist/services/issue-assignment-wakeup.js +22 -0
- package/dist/services/issue-assignment-wakeup.js.map +1 -0
- package/dist/services/issue-goal-fallback.d.ts +18 -0
- package/dist/services/issue-goal-fallback.d.ts.map +1 -0
- package/dist/services/issue-goal-fallback.js +33 -0
- package/dist/services/issue-goal-fallback.js.map +1 -0
- package/dist/services/issues.d.ts +560 -0
- package/dist/services/issues.d.ts.map +1 -0
- package/dist/services/issues.js +1478 -0
- package/dist/services/issues.js.map +1 -0
- package/dist/services/live-events.d.ts +17 -0
- package/dist/services/live-events.d.ts.map +1 -0
- package/dist/services/live-events.js +33 -0
- package/dist/services/live-events.js.map +1 -0
- package/dist/services/plugin-capability-validator.d.ts +108 -0
- package/dist/services/plugin-capability-validator.d.ts.map +1 -0
- package/dist/services/plugin-capability-validator.js +268 -0
- package/dist/services/plugin-capability-validator.js.map +1 -0
- package/dist/services/plugin-config-validator.d.ts +26 -0
- package/dist/services/plugin-config-validator.d.ts.map +1 -0
- package/dist/services/plugin-config-validator.js +41 -0
- package/dist/services/plugin-config-validator.js.map +1 -0
- package/dist/services/plugin-dev-watcher.d.ts +30 -0
- package/dist/services/plugin-dev-watcher.d.ts.map +1 -0
- package/dist/services/plugin-dev-watcher.js +241 -0
- package/dist/services/plugin-dev-watcher.js.map +1 -0
- package/dist/services/plugin-event-bus.d.ts +149 -0
- package/dist/services/plugin-event-bus.d.ts.map +1 -0
- package/dist/services/plugin-event-bus.js +258 -0
- package/dist/services/plugin-event-bus.js.map +1 -0
- package/dist/services/plugin-host-service-cleanup.d.ts +14 -0
- package/dist/services/plugin-host-service-cleanup.d.ts.map +1 -0
- package/dist/services/plugin-host-service-cleanup.js +37 -0
- package/dist/services/plugin-host-service-cleanup.js.map +1 -0
- package/dist/services/plugin-host-services.d.ts +13 -0
- package/dist/services/plugin-host-services.d.ts.map +1 -0
- package/dist/services/plugin-host-services.js +969 -0
- package/dist/services/plugin-host-services.js.map +1 -0
- package/dist/services/plugin-job-coordinator.d.ts +81 -0
- package/dist/services/plugin-job-coordinator.d.ts.map +1 -0
- package/dist/services/plugin-job-coordinator.js +172 -0
- package/dist/services/plugin-job-coordinator.js.map +1 -0
- package/dist/services/plugin-job-scheduler.d.ts +163 -0
- package/dist/services/plugin-job-scheduler.d.ts.map +1 -0
- package/dist/services/plugin-job-scheduler.js +454 -0
- package/dist/services/plugin-job-scheduler.js.map +1 -0
- package/dist/services/plugin-job-store.d.ts +208 -0
- package/dist/services/plugin-job-store.d.ts.map +1 -0
- package/dist/services/plugin-job-store.js +350 -0
- package/dist/services/plugin-job-store.js.map +1 -0
- package/dist/services/plugin-lifecycle.d.ts +203 -0
- package/dist/services/plugin-lifecycle.d.ts.map +1 -0
- package/dist/services/plugin-lifecycle.js +476 -0
- package/dist/services/plugin-lifecycle.js.map +1 -0
- package/dist/services/plugin-loader.d.ts +441 -0
- package/dist/services/plugin-loader.d.ts.map +1 -0
- package/dist/services/plugin-loader.js +1192 -0
- package/dist/services/plugin-loader.js.map +1 -0
- package/dist/services/plugin-log-retention.d.ts +20 -0
- package/dist/services/plugin-log-retention.d.ts.map +1 -0
- package/dist/services/plugin-log-retention.js +63 -0
- package/dist/services/plugin-log-retention.js.map +1 -0
- package/dist/services/plugin-manifest-validator.d.ts +79 -0
- package/dist/services/plugin-manifest-validator.d.ts.map +1 -0
- package/dist/services/plugin-manifest-validator.js +84 -0
- package/dist/services/plugin-manifest-validator.js.map +1 -0
- package/dist/services/plugin-registry.d.ts +2542 -0
- package/dist/services/plugin-registry.d.ts.map +1 -0
- package/dist/services/plugin-registry.js +539 -0
- package/dist/services/plugin-registry.js.map +1 -0
- package/dist/services/plugin-runtime-sandbox.d.ts +40 -0
- package/dist/services/plugin-runtime-sandbox.d.ts.map +1 -0
- package/dist/services/plugin-runtime-sandbox.js +154 -0
- package/dist/services/plugin-runtime-sandbox.js.map +1 -0
- package/dist/services/plugin-secrets-handler.d.ts +81 -0
- package/dist/services/plugin-secrets-handler.d.ts.map +1 -0
- package/dist/services/plugin-secrets-handler.js +275 -0
- package/dist/services/plugin-secrets-handler.js.map +1 -0
- package/dist/services/plugin-state-store.d.ts +92 -0
- package/dist/services/plugin-state-store.d.ts.map +1 -0
- package/dist/services/plugin-state-store.js +190 -0
- package/dist/services/plugin-state-store.js.map +1 -0
- package/dist/services/plugin-stream-bus.d.ts +29 -0
- package/dist/services/plugin-stream-bus.d.ts.map +1 -0
- package/dist/services/plugin-stream-bus.js +48 -0
- package/dist/services/plugin-stream-bus.js.map +1 -0
- package/dist/services/plugin-tool-dispatcher.d.ts +180 -0
- package/dist/services/plugin-tool-dispatcher.d.ts.map +1 -0
- package/dist/services/plugin-tool-dispatcher.js +224 -0
- package/dist/services/plugin-tool-dispatcher.js.map +1 -0
- package/dist/services/plugin-tool-registry.d.ts +192 -0
- package/dist/services/plugin-tool-registry.d.ts.map +1 -0
- package/dist/services/plugin-tool-registry.js +224 -0
- package/dist/services/plugin-tool-registry.js.map +1 -0
- package/dist/services/plugin-worker-manager.d.ts +260 -0
- package/dist/services/plugin-worker-manager.d.ts.map +1 -0
- package/dist/services/plugin-worker-manager.js +835 -0
- package/dist/services/plugin-worker-manager.js.map +1 -0
- package/dist/services/projects.d.ts +87 -0
- package/dist/services/projects.d.ts.map +1 -0
- package/dist/services/projects.js +656 -0
- package/dist/services/projects.js.map +1 -0
- package/dist/services/quota-windows.d.ts +9 -0
- package/dist/services/quota-windows.d.ts.map +1 -0
- package/dist/services/quota-windows.js +56 -0
- package/dist/services/quota-windows.js.map +1 -0
- package/dist/services/routines.d.ts +135 -0
- package/dist/services/routines.d.ts.map +1 -0
- package/dist/services/routines.js +1105 -0
- package/dist/services/routines.js.map +1 -0
- package/dist/services/run-log-store.d.ts +34 -0
- package/dist/services/run-log-store.d.ts.map +1 -0
- package/dist/services/run-log-store.js +109 -0
- package/dist/services/run-log-store.js.map +1 -0
- package/dist/services/secrets.d.ts +511 -0
- package/dist/services/secrets.d.ts.map +1 -0
- package/dist/services/secrets.js +289 -0
- package/dist/services/secrets.js.map +1 -0
- package/dist/services/sidebar-badges.d.ts +9 -0
- package/dist/services/sidebar-badges.d.ts.map +1 -0
- package/dist/services/sidebar-badges.js +33 -0
- package/dist/services/sidebar-badges.js.map +1 -0
- package/dist/services/work-products.d.ts +14 -0
- package/dist/services/work-products.d.ts.map +1 -0
- package/dist/services/work-products.js +100 -0
- package/dist/services/work-products.js.map +1 -0
- package/dist/services/workspace-operation-log-store.d.ts +33 -0
- package/dist/services/workspace-operation-log-store.d.ts.map +1 -0
- package/dist/services/workspace-operation-log-store.js +110 -0
- package/dist/services/workspace-operation-log-store.js.map +1 -0
- package/dist/services/workspace-operations.d.ts +44 -0
- package/dist/services/workspace-operations.d.ts.map +1 -0
- package/dist/services/workspace-operations.js +211 -0
- package/dist/services/workspace-operations.js.map +1 -0
- package/dist/services/workspace-runtime.d.ts +164 -0
- package/dist/services/workspace-runtime.d.ts.map +1 -0
- package/dist/services/workspace-runtime.js +1235 -0
- package/dist/services/workspace-runtime.js.map +1 -0
- package/dist/startup-banner.d.ts +31 -0
- package/dist/startup-banner.d.ts.map +1 -0
- package/dist/startup-banner.js +117 -0
- package/dist/startup-banner.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +29 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local-disk-provider.d.ts +3 -0
- package/dist/storage/local-disk-provider.d.ts.map +1 -0
- package/dist/storage/local-disk-provider.js +79 -0
- package/dist/storage/local-disk-provider.js.map +1 -0
- package/dist/storage/provider-registry.d.ts +4 -0
- package/dist/storage/provider-registry.d.ts.map +1 -0
- package/dist/storage/provider-registry.js +15 -0
- package/dist/storage/provider-registry.js.map +1 -0
- package/dist/storage/s3-provider.d.ts +11 -0
- package/dist/storage/s3-provider.d.ts.map +1 -0
- package/dist/storage/s3-provider.js +123 -0
- package/dist/storage/s3-provider.js.map +1 -0
- package/dist/storage/service.d.ts +3 -0
- package/dist/storage/service.d.ts.map +1 -0
- package/dist/storage/service.js +120 -0
- package/dist/storage/service.js.map +1 -0
- package/dist/storage/types.d.ts +55 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/ui-branding.d.ts +13 -0
- package/dist/ui-branding.d.ts.map +1 -0
- package/dist/ui-branding.js +188 -0
- package/dist/ui-branding.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/dist/worktree-config.d.ts +19 -0
- package/dist/worktree-config.d.ts.map +1 -0
- package/dist/worktree-config.js +365 -0
- package/dist/worktree-config.js.map +1 -0
- package/package.json +90 -0
- package/skills/fidelios/SKILL.md +365 -0
- package/skills/fidelios/references/api-reference.md +647 -0
- package/skills/fidelios/references/company-skills.md +193 -0
- package/skills/fidelios-create-agent/SKILL.md +142 -0
- package/skills/fidelios-create-agent/references/api-reference.md +105 -0
- package/skills/fidelios-create-plugin/SKILL.md +101 -0
- package/skills/para-memory-files/SKILL.md +104 -0
- package/skills/para-memory-files/references/schemas.md +35 -0
- package/ui-dist/android-chrome-192x192.png +0 -0
- package/ui-dist/android-chrome-512x512.png +0 -0
- package/ui-dist/apple-touch-icon.png +0 -0
- package/ui-dist/assets/_basePickBy-DDS8rFE9.js +1 -0
- package/ui-dist/assets/_baseUniq-BTIdqnfJ.js +1 -0
- package/ui-dist/assets/apl-B4CMkyY2.js +1 -0
- package/ui-dist/assets/arc-CER6ytAf.js +1 -0
- package/ui-dist/assets/architectureDiagram-VXUJARFQ-CrJvVSPh.js +36 -0
- package/ui-dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/ui-dist/assets/asn1-EdZsLKOL.js +1 -0
- package/ui-dist/assets/asterisk-B-8jnY81.js +1 -0
- package/ui-dist/assets/blockDiagram-VD42YOAC-p-DB7nkA.js +122 -0
- package/ui-dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/ui-dist/assets/c4Diagram-YG6GDRKO-D1K75fYz.js +10 -0
- package/ui-dist/assets/channel-B1viE-VZ.js +1 -0
- package/ui-dist/assets/chunk-4BX2VUAB-C1J-fCaK.js +1 -0
- package/ui-dist/assets/chunk-55IACEB6-CjEOgVYA.js +1 -0
- package/ui-dist/assets/chunk-B4BG7PRW-CGZwFaze.js +165 -0
- package/ui-dist/assets/chunk-DI55MBZ5-Dp2ZahPN.js +220 -0
- package/ui-dist/assets/chunk-FMBD7UC4-nXC1OkzD.js +15 -0
- package/ui-dist/assets/chunk-QN33PNHL-D_uFCkMK.js +1 -0
- package/ui-dist/assets/chunk-QZHKN3VN-CG3WK_AN.js +1 -0
- package/ui-dist/assets/chunk-TZMSLE5B-COlBSWdP.js +1 -0
- package/ui-dist/assets/classDiagram-2ON5EDUG-Cs4NEMXI.js +1 -0
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-Cs4NEMXI.js +1 -0
- package/ui-dist/assets/clike-B9uivgTg.js +1 -0
- package/ui-dist/assets/clojure-BMjYHr_A.js +1 -0
- package/ui-dist/assets/clone-CGwZV8ud.js +1 -0
- package/ui-dist/assets/cmake-BQqOBYOt.js +1 -0
- package/ui-dist/assets/cobol-CWcv1MsR.js +1 -0
- package/ui-dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/ui-dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/ui-dist/assets/cose-bilkent-S5V4N54A-B9XYqCMb.js +1 -0
- package/ui-dist/assets/crystal-SjHAIU92.js +1 -0
- package/ui-dist/assets/css-BnMrqG3P.js +1 -0
- package/ui-dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/ui-dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/ui-dist/assets/d-pRatUO7H.js +1 -0
- package/ui-dist/assets/dagre-6UL2VRFP-DcCdBLC7.js +4 -0
- package/ui-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/ui-dist/assets/diagram-PSM6KHXK-np1kLquy.js +24 -0
- package/ui-dist/assets/diagram-QEK2KX5R-C-b4qIN1.js +43 -0
- package/ui-dist/assets/diagram-S2PKOQOG-Ba-173Ug.js +24 -0
- package/ui-dist/assets/diff-DbItnlRl.js +1 -0
- package/ui-dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/ui-dist/assets/dtd-DF_7sFjM.js +1 -0
- package/ui-dist/assets/dylan-DwRh75JA.js +1 -0
- package/ui-dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/ui-dist/assets/ecl-Cabwm37j.js +1 -0
- package/ui-dist/assets/eiffel-CnydiIhH.js +1 -0
- package/ui-dist/assets/elm-vLlmbW-K.js +1 -0
- package/ui-dist/assets/erDiagram-Q2GNP2WA-BBmkHiJP.js +60 -0
- package/ui-dist/assets/erlang-BNw1qcRV.js +1 -0
- package/ui-dist/assets/factor-kuTfRLto.js +1 -0
- package/ui-dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/ui-dist/assets/flowDiagram-NV44I4VS-Dj_iTDkp.js +162 -0
- package/ui-dist/assets/forth-Ffai-XNe.js +1 -0
- package/ui-dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/ui-dist/assets/ganttDiagram-JELNMOA3-Bn1hanTg.js +267 -0
- package/ui-dist/assets/gas-Bneqetm1.js +1 -0
- package/ui-dist/assets/gherkin-heZmZLOM.js +1 -0
- package/ui-dist/assets/gitGraphDiagram-V2S2FVAM-BjmRpty0.js +65 -0
- package/ui-dist/assets/graph-CWBOAGTW.js +1 -0
- package/ui-dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/ui-dist/assets/haskell-Cw1EW3IL.js +1 -0
- package/ui-dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/ui-dist/assets/http-DBlCnlav.js +1 -0
- package/ui-dist/assets/idl-BEugSyMb.js +1 -0
- package/ui-dist/assets/index-B52MtqBm.js +1 -0
- package/ui-dist/assets/index-BEBYIFOJ.js +1 -0
- package/ui-dist/assets/index-BIvl9YFB.js +1 -0
- package/ui-dist/assets/index-BNyP1gwD.js +1 -0
- package/ui-dist/assets/index-BRW6bV_B.js +6 -0
- package/ui-dist/assets/index-BcQTWaKH.js +1 -0
- package/ui-dist/assets/index-BfKYbH5T.js +13 -0
- package/ui-dist/assets/index-BhX49pA0.js +1 -0
- package/ui-dist/assets/index-BoFaTgOC.js +2 -0
- package/ui-dist/assets/index-C-Es83iE.js +7 -0
- package/ui-dist/assets/index-C3LG8kvr.js +1 -0
- package/ui-dist/assets/index-C5Z9j0rD.js +1 -0
- package/ui-dist/assets/index-CFlEF-gp.js +1 -0
- package/ui-dist/assets/index-Cjm12V39.js +1 -0
- package/ui-dist/assets/index-Cp84QmJD.css +1 -0
- package/ui-dist/assets/index-D2t01AH0.js +1 -0
- package/ui-dist/assets/index-DEt1jkxJ.js +1 -0
- package/ui-dist/assets/index-DeAKBJuz.js +3 -0
- package/ui-dist/assets/index-Du65R_Zq.js +1 -0
- package/ui-dist/assets/index-WUHteAuP.js +1 -0
- package/ui-dist/assets/index-Y_jO6IK_.js +1180 -0
- package/ui-dist/assets/index-ZQU9QA5y.js +1 -0
- package/ui-dist/assets/index-f6wRGThx.js +1 -0
- package/ui-dist/assets/index-hMuLlvYa.js +1 -0
- package/ui-dist/assets/infoDiagram-HS3SLOUP-CVMKJlmV.js +2 -0
- package/ui-dist/assets/init-Gi6I4Gst.js +1 -0
- package/ui-dist/assets/javascript-iXu5QeM3.js +1 -0
- package/ui-dist/assets/journeyDiagram-XKPGCS4Q-FrNTHHMi.js +139 -0
- package/ui-dist/assets/julia-DuME0IfC.js +1 -0
- package/ui-dist/assets/kanban-definition-3W4ZIXB7-BQYKwdVh.js +89 -0
- package/ui-dist/assets/katex-O9d3_IXG.js +261 -0
- package/ui-dist/assets/layout-BxccZ6zb.js +1 -0
- package/ui-dist/assets/linear-Db-Yv5jO.js +1 -0
- package/ui-dist/assets/livescript-BwQOo05w.js +1 -0
- package/ui-dist/assets/lua-BgMRiT3U.js +1 -0
- package/ui-dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/ui-dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/ui-dist/assets/mermaid.core-BCE9tDOe.js +256 -0
- package/ui-dist/assets/mindmap-definition-VGOIOE7T-ZWLuqirD.js +68 -0
- package/ui-dist/assets/mirc-CjQqDB4T.js +1 -0
- package/ui-dist/assets/mllike-CXdrOF99.js +1 -0
- package/ui-dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/ui-dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/ui-dist/assets/mumps-BT43cFF4.js +1 -0
- package/ui-dist/assets/nginx-DdIZxoE0.js +1 -0
- package/ui-dist/assets/nsis-LdVXkNf5.js +1 -0
- package/ui-dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/ui-dist/assets/octave-Ck1zUtKM.js +1 -0
- package/ui-dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/ui-dist/assets/oz-BzwKVEFT.js +1 -0
- package/ui-dist/assets/pascal--L3eBynH.js +1 -0
- package/ui-dist/assets/perl-CdXCOZ3F.js +1 -0
- package/ui-dist/assets/pieDiagram-ADFJNKIX-D01HRHJF.js +30 -0
- package/ui-dist/assets/pig-CevX1Tat.js +1 -0
- package/ui-dist/assets/powershell-CFHJl5sT.js +1 -0
- package/ui-dist/assets/properties-C78fOPTZ.js +1 -0
- package/ui-dist/assets/protobuf-ChK-085T.js +1 -0
- package/ui-dist/assets/pug-DeIclll2.js +1 -0
- package/ui-dist/assets/puppet-DMA9R1ak.js +1 -0
- package/ui-dist/assets/python-BuPzkPfP.js +1 -0
- package/ui-dist/assets/q-pXgVlZs6.js +1 -0
- package/ui-dist/assets/quadrantDiagram-AYHSOK5B-lAmOPnB4.js +7 -0
- package/ui-dist/assets/r-B6wPVr8A.js +1 -0
- package/ui-dist/assets/requirementDiagram-UZGBJVZJ-ByNWbh-O.js +64 -0
- package/ui-dist/assets/rpm-CTu-6PCP.js +1 -0
- package/ui-dist/assets/ruby-B2Rjki9n.js +1 -0
- package/ui-dist/assets/sankeyDiagram-TZEHDZUN-Cf_Gq84u.js +10 -0
- package/ui-dist/assets/sas-B4kiWyti.js +1 -0
- package/ui-dist/assets/scheme-C41bIUwD.js +1 -0
- package/ui-dist/assets/sequenceDiagram-WL72ISMW-Bx2VZbdr.js +145 -0
- package/ui-dist/assets/shell-CjFT_Tl9.js +1 -0
- package/ui-dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/ui-dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/ui-dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/ui-dist/assets/solr-DehyRSwq.js +1 -0
- package/ui-dist/assets/sparql-DkYu6x3z.js +1 -0
- package/ui-dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/ui-dist/assets/sql-D0XecflT.js +1 -0
- package/ui-dist/assets/stateDiagram-FKZM4ZOC-vP7G7A65.js +1 -0
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-C2xpsAAO.js +1 -0
- package/ui-dist/assets/stex-C3f8Ysf7.js +1 -0
- package/ui-dist/assets/stylus-B533Al4x.js +1 -0
- package/ui-dist/assets/swift-BzpIVaGY.js +1 -0
- package/ui-dist/assets/tcl-DVfN8rqt.js +1 -0
- package/ui-dist/assets/textile-CnDTJFAw.js +1 -0
- package/ui-dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/ui-dist/assets/tiki-DGYXhP31.js +1 -0
- package/ui-dist/assets/timeline-definition-IT6M3QCI-BJww-sEp.js +61 -0
- package/ui-dist/assets/toml-Bm5Em-hy.js +1 -0
- package/ui-dist/assets/treemap-GDKQZRPO-DUux14NY.js +162 -0
- package/ui-dist/assets/troff-wAsdV37c.js +1 -0
- package/ui-dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/ui-dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/ui-dist/assets/turtle-B1tBg_DP.js +1 -0
- package/ui-dist/assets/vb-CmGdzxic.js +1 -0
- package/ui-dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/ui-dist/assets/velocity-D8B20fx6.js +1 -0
- package/ui-dist/assets/verilog-C6RDOZhf.js +1 -0
- package/ui-dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/ui-dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/ui-dist/assets/xquery-DzFWVndE.js +1 -0
- package/ui-dist/assets/xychartDiagram-PRI3JC2R-AU5ZOwAw.js +7 -0
- package/ui-dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/ui-dist/assets/z80-Hz9HOZM7.js +1 -0
- package/ui-dist/brands/opencode-logo-dark-square.svg +18 -0
- package/ui-dist/brands/opencode-logo-light-square.svg +18 -0
- package/ui-dist/favicon-16x16.png +0 -0
- package/ui-dist/favicon-32x32.png +0 -0
- package/ui-dist/favicon-96x96.png +0 -0
- package/ui-dist/favicon.ico +0 -0
- package/ui-dist/favicon.svg +3 -0
- package/ui-dist/index.html +48 -0
- package/ui-dist/site.webmanifest +21 -0
- package/ui-dist/sw.js +42 -0
- package/ui-dist/worktree-favicon-16x16.png +0 -0
- package/ui-dist/worktree-favicon-32x32.png +0 -0
- package/ui-dist/worktree-favicon.ico +0 -0
- package/ui-dist/worktree-favicon.svg +3 -0
|
@@ -0,0 +1,2042 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { and, asc, eq } from "drizzle-orm";
|
|
6
|
+
import { companySkills } from "@fideliosai/db";
|
|
7
|
+
import { readFideliOSSkillSyncPreference, writeFideliOSSkillSyncPreference } from "@fideliosai/adapter-utils/server-utils";
|
|
8
|
+
import { normalizeAgentUrlKey } from "@fideliosai/shared";
|
|
9
|
+
import { findServerAdapter } from "../adapters/index.js";
|
|
10
|
+
import { resolveFideliOSInstanceRoot } from "../home-paths.js";
|
|
11
|
+
import { notFound, unprocessable } from "../errors.js";
|
|
12
|
+
import { agentService } from "./agents.js";
|
|
13
|
+
import { projectService } from "./projects.js";
|
|
14
|
+
import { secretService } from "./secrets.js";
|
|
15
|
+
const skillInventoryRefreshPromises = new Map();
|
|
16
|
+
const PROJECT_SCAN_DIRECTORY_ROOTS = [
|
|
17
|
+
"skills",
|
|
18
|
+
"skills/.curated",
|
|
19
|
+
"skills/.experimental",
|
|
20
|
+
"skills/.system",
|
|
21
|
+
".agents/skills",
|
|
22
|
+
".agent/skills",
|
|
23
|
+
".augment/skills",
|
|
24
|
+
".claude/skills",
|
|
25
|
+
".codebuddy/skills",
|
|
26
|
+
".commandcode/skills",
|
|
27
|
+
".continue/skills",
|
|
28
|
+
".cortex/skills",
|
|
29
|
+
".crush/skills",
|
|
30
|
+
".factory/skills",
|
|
31
|
+
".goose/skills",
|
|
32
|
+
".junie/skills",
|
|
33
|
+
".iflow/skills",
|
|
34
|
+
".kilocode/skills",
|
|
35
|
+
".kiro/skills",
|
|
36
|
+
".kode/skills",
|
|
37
|
+
".mcpjam/skills",
|
|
38
|
+
".vibe/skills",
|
|
39
|
+
".mux/skills",
|
|
40
|
+
".openhands/skills",
|
|
41
|
+
".pi/skills",
|
|
42
|
+
".qoder/skills",
|
|
43
|
+
".qwen/skills",
|
|
44
|
+
".roo/skills",
|
|
45
|
+
".trae/skills",
|
|
46
|
+
".windsurf/skills",
|
|
47
|
+
".zencoder/skills",
|
|
48
|
+
".neovate/skills",
|
|
49
|
+
".pochi/skills",
|
|
50
|
+
".adal/skills",
|
|
51
|
+
];
|
|
52
|
+
const PROJECT_ROOT_SKILL_SUBDIRECTORIES = [
|
|
53
|
+
"references",
|
|
54
|
+
"scripts",
|
|
55
|
+
"assets",
|
|
56
|
+
];
|
|
57
|
+
function asString(value) {
|
|
58
|
+
if (typeof value !== "string")
|
|
59
|
+
return null;
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
62
|
+
}
|
|
63
|
+
function isPlainRecord(value) {
|
|
64
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65
|
+
}
|
|
66
|
+
function normalizePortablePath(input) {
|
|
67
|
+
const parts = [];
|
|
68
|
+
for (const segment of input.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "").split("/")) {
|
|
69
|
+
if (!segment || segment === ".")
|
|
70
|
+
continue;
|
|
71
|
+
if (segment === "..") {
|
|
72
|
+
if (parts.length > 0)
|
|
73
|
+
parts.pop();
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
parts.push(segment);
|
|
77
|
+
}
|
|
78
|
+
return parts.join("/");
|
|
79
|
+
}
|
|
80
|
+
function normalizePackageFileMap(files) {
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const [rawPath, content] of Object.entries(files)) {
|
|
83
|
+
const nextPath = normalizePortablePath(rawPath);
|
|
84
|
+
if (!nextPath)
|
|
85
|
+
continue;
|
|
86
|
+
out[nextPath] = content;
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function normalizeSkillSlug(value) {
|
|
91
|
+
return value ? normalizeAgentUrlKey(value) ?? null : null;
|
|
92
|
+
}
|
|
93
|
+
function normalizeSkillKey(value) {
|
|
94
|
+
if (!value)
|
|
95
|
+
return null;
|
|
96
|
+
const segments = value
|
|
97
|
+
.split("/")
|
|
98
|
+
.map((segment) => normalizeSkillSlug(segment))
|
|
99
|
+
.filter((segment) => Boolean(segment));
|
|
100
|
+
return segments.length > 0 ? segments.join("/") : null;
|
|
101
|
+
}
|
|
102
|
+
export function normalizeGitHubSkillDirectory(value, fallback) {
|
|
103
|
+
const normalized = normalizePortablePath(value ?? "");
|
|
104
|
+
if (!normalized)
|
|
105
|
+
return normalizePortablePath(fallback);
|
|
106
|
+
if (path.posix.basename(normalized).toLowerCase() === "skill.md") {
|
|
107
|
+
return normalizePortablePath(path.posix.dirname(normalized));
|
|
108
|
+
}
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
function hashSkillValue(value) {
|
|
112
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 10);
|
|
113
|
+
}
|
|
114
|
+
function uniqueSkillSlug(baseSlug, usedSlugs) {
|
|
115
|
+
if (!usedSlugs.has(baseSlug))
|
|
116
|
+
return baseSlug;
|
|
117
|
+
let attempt = 2;
|
|
118
|
+
let candidate = `${baseSlug}-${attempt}`;
|
|
119
|
+
while (usedSlugs.has(candidate)) {
|
|
120
|
+
attempt += 1;
|
|
121
|
+
candidate = `${baseSlug}-${attempt}`;
|
|
122
|
+
}
|
|
123
|
+
return candidate;
|
|
124
|
+
}
|
|
125
|
+
function uniqueImportedSkillKey(companyId, baseSlug, usedKeys) {
|
|
126
|
+
const initial = `company/${companyId}/${baseSlug}`;
|
|
127
|
+
if (!usedKeys.has(initial))
|
|
128
|
+
return initial;
|
|
129
|
+
let attempt = 2;
|
|
130
|
+
let candidate = `company/${companyId}/${baseSlug}-${attempt}`;
|
|
131
|
+
while (usedKeys.has(candidate)) {
|
|
132
|
+
attempt += 1;
|
|
133
|
+
candidate = `company/${companyId}/${baseSlug}-${attempt}`;
|
|
134
|
+
}
|
|
135
|
+
return candidate;
|
|
136
|
+
}
|
|
137
|
+
function buildSkillRuntimeName(key, slug) {
|
|
138
|
+
if (key.startsWith("fideliosai/fidelios/"))
|
|
139
|
+
return slug;
|
|
140
|
+
return `${slug}--${hashSkillValue(key)}`;
|
|
141
|
+
}
|
|
142
|
+
function readCanonicalSkillKey(frontmatter, metadata) {
|
|
143
|
+
const direct = normalizeSkillKey(asString(frontmatter.key)
|
|
144
|
+
?? asString(frontmatter.skillKey)
|
|
145
|
+
?? asString(metadata?.skillKey)
|
|
146
|
+
?? asString(metadata?.canonicalKey)
|
|
147
|
+
?? asString(metadata?.fideliosSkillKey));
|
|
148
|
+
if (direct)
|
|
149
|
+
return direct;
|
|
150
|
+
const fidelios = isPlainRecord(metadata?.fidelios) ? metadata?.fidelios : null;
|
|
151
|
+
return normalizeSkillKey(asString(fidelios?.skillKey)
|
|
152
|
+
?? asString(fidelios?.key));
|
|
153
|
+
}
|
|
154
|
+
function deriveCanonicalSkillKey(companyId, input) {
|
|
155
|
+
const slug = normalizeSkillSlug(input.slug) ?? "skill";
|
|
156
|
+
const metadata = isPlainRecord(input.metadata) ? input.metadata : null;
|
|
157
|
+
const explicitKey = readCanonicalSkillKey({}, metadata);
|
|
158
|
+
if (explicitKey)
|
|
159
|
+
return explicitKey;
|
|
160
|
+
const sourceKind = asString(metadata?.sourceKind);
|
|
161
|
+
if (sourceKind === "fidelios_bundled") {
|
|
162
|
+
return `fideliosai/fidelios/${slug}`;
|
|
163
|
+
}
|
|
164
|
+
const owner = normalizeSkillSlug(asString(metadata?.owner));
|
|
165
|
+
const repo = normalizeSkillSlug(asString(metadata?.repo));
|
|
166
|
+
if ((input.sourceType === "github" || input.sourceType === "skills_sh" || sourceKind === "github" || sourceKind === "skills_sh") && owner && repo) {
|
|
167
|
+
return `${owner}/${repo}/${slug}`;
|
|
168
|
+
}
|
|
169
|
+
if (input.sourceType === "url" || sourceKind === "url") {
|
|
170
|
+
const locator = asString(input.sourceLocator);
|
|
171
|
+
if (locator) {
|
|
172
|
+
try {
|
|
173
|
+
const url = new URL(locator);
|
|
174
|
+
const host = normalizeSkillSlug(url.host) ?? "url";
|
|
175
|
+
return `url/${host}/${hashSkillValue(locator)}/${slug}`;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return `url/unknown/${hashSkillValue(locator)}/${slug}`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (input.sourceType === "local_path") {
|
|
183
|
+
if (sourceKind === "managed_local") {
|
|
184
|
+
return `company/${companyId}/${slug}`;
|
|
185
|
+
}
|
|
186
|
+
const locator = asString(input.sourceLocator);
|
|
187
|
+
if (locator) {
|
|
188
|
+
return `local/${hashSkillValue(path.resolve(locator))}/${slug}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return `company/${companyId}/${slug}`;
|
|
192
|
+
}
|
|
193
|
+
function classifyInventoryKind(relativePath) {
|
|
194
|
+
const normalized = normalizePortablePath(relativePath).toLowerCase();
|
|
195
|
+
if (normalized.endsWith("/skill.md") || normalized === "skill.md")
|
|
196
|
+
return "skill";
|
|
197
|
+
if (normalized.startsWith("references/"))
|
|
198
|
+
return "reference";
|
|
199
|
+
if (normalized.startsWith("scripts/"))
|
|
200
|
+
return "script";
|
|
201
|
+
if (normalized.startsWith("assets/"))
|
|
202
|
+
return "asset";
|
|
203
|
+
if (normalized.endsWith(".md"))
|
|
204
|
+
return "markdown";
|
|
205
|
+
const fileName = path.posix.basename(normalized);
|
|
206
|
+
if (fileName.endsWith(".sh")
|
|
207
|
+
|| fileName.endsWith(".js")
|
|
208
|
+
|| fileName.endsWith(".mjs")
|
|
209
|
+
|| fileName.endsWith(".cjs")
|
|
210
|
+
|| fileName.endsWith(".ts")
|
|
211
|
+
|| fileName.endsWith(".py")
|
|
212
|
+
|| fileName.endsWith(".rb")
|
|
213
|
+
|| fileName.endsWith(".bash")) {
|
|
214
|
+
return "script";
|
|
215
|
+
}
|
|
216
|
+
if (fileName.endsWith(".png")
|
|
217
|
+
|| fileName.endsWith(".jpg")
|
|
218
|
+
|| fileName.endsWith(".jpeg")
|
|
219
|
+
|| fileName.endsWith(".gif")
|
|
220
|
+
|| fileName.endsWith(".svg")
|
|
221
|
+
|| fileName.endsWith(".webp")
|
|
222
|
+
|| fileName.endsWith(".pdf")) {
|
|
223
|
+
return "asset";
|
|
224
|
+
}
|
|
225
|
+
return "other";
|
|
226
|
+
}
|
|
227
|
+
function deriveTrustLevel(fileInventory) {
|
|
228
|
+
if (fileInventory.some((entry) => entry.kind === "script"))
|
|
229
|
+
return "scripts_executables";
|
|
230
|
+
if (fileInventory.some((entry) => entry.kind === "asset" || entry.kind === "other"))
|
|
231
|
+
return "assets";
|
|
232
|
+
return "markdown_only";
|
|
233
|
+
}
|
|
234
|
+
function prepareYamlLines(raw) {
|
|
235
|
+
return raw
|
|
236
|
+
.split("\n")
|
|
237
|
+
.map((line) => ({
|
|
238
|
+
indent: line.match(/^ */)?.[0].length ?? 0,
|
|
239
|
+
content: line.trim(),
|
|
240
|
+
}))
|
|
241
|
+
.filter((line) => line.content.length > 0 && !line.content.startsWith("#"));
|
|
242
|
+
}
|
|
243
|
+
function parseYamlScalar(rawValue) {
|
|
244
|
+
const trimmed = rawValue.trim();
|
|
245
|
+
if (trimmed === "")
|
|
246
|
+
return "";
|
|
247
|
+
if (trimmed === "null" || trimmed === "~")
|
|
248
|
+
return null;
|
|
249
|
+
if (trimmed === "true")
|
|
250
|
+
return true;
|
|
251
|
+
if (trimmed === "false")
|
|
252
|
+
return false;
|
|
253
|
+
if (trimmed === "[]")
|
|
254
|
+
return [];
|
|
255
|
+
if (trimmed === "{}")
|
|
256
|
+
return {};
|
|
257
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed))
|
|
258
|
+
return Number(trimmed);
|
|
259
|
+
if (trimmed.startsWith("\"") || trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
260
|
+
try {
|
|
261
|
+
return JSON.parse(trimmed);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return trimmed;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return trimmed;
|
|
268
|
+
}
|
|
269
|
+
function parseYamlBlock(lines, startIndex, indentLevel) {
|
|
270
|
+
let index = startIndex;
|
|
271
|
+
while (index < lines.length && lines[index].content.length === 0)
|
|
272
|
+
index += 1;
|
|
273
|
+
if (index >= lines.length || lines[index].indent < indentLevel) {
|
|
274
|
+
return { value: {}, nextIndex: index };
|
|
275
|
+
}
|
|
276
|
+
const isArray = lines[index].indent === indentLevel && lines[index].content.startsWith("-");
|
|
277
|
+
if (isArray) {
|
|
278
|
+
const values = [];
|
|
279
|
+
while (index < lines.length) {
|
|
280
|
+
const line = lines[index];
|
|
281
|
+
if (line.indent < indentLevel)
|
|
282
|
+
break;
|
|
283
|
+
if (line.indent !== indentLevel || !line.content.startsWith("-"))
|
|
284
|
+
break;
|
|
285
|
+
const remainder = line.content.slice(1).trim();
|
|
286
|
+
index += 1;
|
|
287
|
+
if (!remainder) {
|
|
288
|
+
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
289
|
+
values.push(nested.value);
|
|
290
|
+
index = nested.nextIndex;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const inlineObjectSeparator = remainder.indexOf(":");
|
|
294
|
+
if (inlineObjectSeparator > 0 &&
|
|
295
|
+
!remainder.startsWith("\"") &&
|
|
296
|
+
!remainder.startsWith("{") &&
|
|
297
|
+
!remainder.startsWith("[")) {
|
|
298
|
+
const key = remainder.slice(0, inlineObjectSeparator).trim();
|
|
299
|
+
const rawValue = remainder.slice(inlineObjectSeparator + 1).trim();
|
|
300
|
+
const nextObject = {
|
|
301
|
+
[key]: parseYamlScalar(rawValue),
|
|
302
|
+
};
|
|
303
|
+
if (index < lines.length && lines[index].indent > indentLevel) {
|
|
304
|
+
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
305
|
+
if (isPlainRecord(nested.value)) {
|
|
306
|
+
Object.assign(nextObject, nested.value);
|
|
307
|
+
}
|
|
308
|
+
index = nested.nextIndex;
|
|
309
|
+
}
|
|
310
|
+
values.push(nextObject);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
values.push(parseYamlScalar(remainder));
|
|
314
|
+
}
|
|
315
|
+
return { value: values, nextIndex: index };
|
|
316
|
+
}
|
|
317
|
+
const record = {};
|
|
318
|
+
while (index < lines.length) {
|
|
319
|
+
const line = lines[index];
|
|
320
|
+
if (line.indent < indentLevel)
|
|
321
|
+
break;
|
|
322
|
+
if (line.indent !== indentLevel) {
|
|
323
|
+
index += 1;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const separatorIndex = line.content.indexOf(":");
|
|
327
|
+
if (separatorIndex <= 0) {
|
|
328
|
+
index += 1;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const key = line.content.slice(0, separatorIndex).trim();
|
|
332
|
+
const remainder = line.content.slice(separatorIndex + 1).trim();
|
|
333
|
+
index += 1;
|
|
334
|
+
if (!remainder) {
|
|
335
|
+
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
336
|
+
record[key] = nested.value;
|
|
337
|
+
index = nested.nextIndex;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
record[key] = parseYamlScalar(remainder);
|
|
341
|
+
}
|
|
342
|
+
return { value: record, nextIndex: index };
|
|
343
|
+
}
|
|
344
|
+
function parseYamlFrontmatter(raw) {
|
|
345
|
+
const prepared = prepareYamlLines(raw);
|
|
346
|
+
if (prepared.length === 0)
|
|
347
|
+
return {};
|
|
348
|
+
const parsed = parseYamlBlock(prepared, 0, prepared[0].indent);
|
|
349
|
+
return isPlainRecord(parsed.value) ? parsed.value : {};
|
|
350
|
+
}
|
|
351
|
+
function parseFrontmatterMarkdown(raw) {
|
|
352
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
353
|
+
if (!normalized.startsWith("---\n")) {
|
|
354
|
+
return { frontmatter: {}, body: normalized.trim() };
|
|
355
|
+
}
|
|
356
|
+
const closing = normalized.indexOf("\n---\n", 4);
|
|
357
|
+
if (closing < 0) {
|
|
358
|
+
return { frontmatter: {}, body: normalized.trim() };
|
|
359
|
+
}
|
|
360
|
+
const frontmatterRaw = normalized.slice(4, closing).trim();
|
|
361
|
+
const body = normalized.slice(closing + 5).trim();
|
|
362
|
+
return {
|
|
363
|
+
frontmatter: parseYamlFrontmatter(frontmatterRaw),
|
|
364
|
+
body,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async function fetchText(url) {
|
|
368
|
+
const response = await fetch(url);
|
|
369
|
+
if (!response.ok) {
|
|
370
|
+
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
|
371
|
+
}
|
|
372
|
+
return response.text();
|
|
373
|
+
}
|
|
374
|
+
async function fetchJson(url) {
|
|
375
|
+
const response = await fetch(url, {
|
|
376
|
+
headers: {
|
|
377
|
+
accept: "application/vnd.github+json",
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
if (!response.ok) {
|
|
381
|
+
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
|
382
|
+
}
|
|
383
|
+
return response.json();
|
|
384
|
+
}
|
|
385
|
+
async function resolveGitHubDefaultBranch(owner, repo) {
|
|
386
|
+
const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}`);
|
|
387
|
+
return asString(response.default_branch) ?? "main";
|
|
388
|
+
}
|
|
389
|
+
async function resolveGitHubCommitSha(owner, repo, ref) {
|
|
390
|
+
const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}`);
|
|
391
|
+
const sha = asString(response.sha);
|
|
392
|
+
if (!sha) {
|
|
393
|
+
throw unprocessable(`Failed to resolve GitHub ref ${ref}`);
|
|
394
|
+
}
|
|
395
|
+
return sha;
|
|
396
|
+
}
|
|
397
|
+
function parseGitHubSourceUrl(rawUrl) {
|
|
398
|
+
const url = new URL(rawUrl);
|
|
399
|
+
if (url.hostname !== "github.com") {
|
|
400
|
+
throw unprocessable("GitHub source must use github.com URL");
|
|
401
|
+
}
|
|
402
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
403
|
+
if (parts.length < 2) {
|
|
404
|
+
throw unprocessable("Invalid GitHub URL");
|
|
405
|
+
}
|
|
406
|
+
const owner = parts[0];
|
|
407
|
+
const repo = parts[1].replace(/\.git$/i, "");
|
|
408
|
+
let ref = "main";
|
|
409
|
+
let basePath = "";
|
|
410
|
+
let filePath = null;
|
|
411
|
+
let explicitRef = false;
|
|
412
|
+
if (parts[2] === "tree") {
|
|
413
|
+
ref = parts[3] ?? "main";
|
|
414
|
+
basePath = parts.slice(4).join("/");
|
|
415
|
+
explicitRef = true;
|
|
416
|
+
}
|
|
417
|
+
else if (parts[2] === "blob") {
|
|
418
|
+
ref = parts[3] ?? "main";
|
|
419
|
+
filePath = parts.slice(4).join("/");
|
|
420
|
+
basePath = filePath ? path.posix.dirname(filePath) : "";
|
|
421
|
+
explicitRef = true;
|
|
422
|
+
}
|
|
423
|
+
return { owner, repo, ref, basePath, filePath, explicitRef };
|
|
424
|
+
}
|
|
425
|
+
async function resolveGitHubPinnedRef(parsed) {
|
|
426
|
+
if (/^[0-9a-f]{40}$/i.test(parsed.ref.trim())) {
|
|
427
|
+
return {
|
|
428
|
+
pinnedRef: parsed.ref,
|
|
429
|
+
trackingRef: parsed.explicitRef ? parsed.ref : null,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
const trackingRef = parsed.explicitRef
|
|
433
|
+
? parsed.ref
|
|
434
|
+
: await resolveGitHubDefaultBranch(parsed.owner, parsed.repo);
|
|
435
|
+
const pinnedRef = await resolveGitHubCommitSha(parsed.owner, parsed.repo, trackingRef);
|
|
436
|
+
return { pinnedRef, trackingRef };
|
|
437
|
+
}
|
|
438
|
+
function resolveRawGitHubUrl(owner, repo, ref, filePath) {
|
|
439
|
+
return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath.replace(/^\/+/, "")}`;
|
|
440
|
+
}
|
|
441
|
+
function extractCommandTokens(raw) {
|
|
442
|
+
const matches = raw.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
443
|
+
return matches.map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
444
|
+
}
|
|
445
|
+
export function parseSkillImportSourceInput(rawInput) {
|
|
446
|
+
const trimmed = rawInput.trim();
|
|
447
|
+
if (!trimmed) {
|
|
448
|
+
throw unprocessable("Skill source is required.");
|
|
449
|
+
}
|
|
450
|
+
const warnings = [];
|
|
451
|
+
let source = trimmed;
|
|
452
|
+
let requestedSkillSlug = null;
|
|
453
|
+
if (/^npx\s+skills\s+add\s+/i.test(trimmed)) {
|
|
454
|
+
const tokens = extractCommandTokens(trimmed);
|
|
455
|
+
const addIndex = tokens.findIndex((token, index) => token === "add"
|
|
456
|
+
&& index > 0
|
|
457
|
+
&& tokens[index - 1]?.toLowerCase() === "skills");
|
|
458
|
+
if (addIndex >= 0) {
|
|
459
|
+
source = tokens[addIndex + 1] ?? "";
|
|
460
|
+
for (let index = addIndex + 2; index < tokens.length; index += 1) {
|
|
461
|
+
const token = tokens[index];
|
|
462
|
+
if (token === "--skill") {
|
|
463
|
+
requestedSkillSlug = normalizeSkillSlug(tokens[index + 1] ?? null);
|
|
464
|
+
index += 1;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (token.startsWith("--skill=")) {
|
|
468
|
+
requestedSkillSlug = normalizeSkillSlug(token.slice("--skill=".length));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const normalizedSource = source.trim();
|
|
474
|
+
if (!normalizedSource) {
|
|
475
|
+
throw unprocessable("Skill source is required.");
|
|
476
|
+
}
|
|
477
|
+
// Key-style imports (org/repo/skill) originate from the skills.sh registry
|
|
478
|
+
if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
|
|
479
|
+
const [owner, repo, skillSlugRaw] = normalizedSource.split("/");
|
|
480
|
+
return {
|
|
481
|
+
resolvedSource: `https://github.com/${owner}/${repo}`,
|
|
482
|
+
requestedSkillSlug: normalizeSkillSlug(skillSlugRaw),
|
|
483
|
+
originalSkillsShUrl: `https://skills.sh/${owner}/${repo}/${skillSlugRaw}`,
|
|
484
|
+
warnings,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
|
|
488
|
+
return {
|
|
489
|
+
resolvedSource: `https://github.com/${normalizedSource}`,
|
|
490
|
+
requestedSkillSlug,
|
|
491
|
+
originalSkillsShUrl: null,
|
|
492
|
+
warnings,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
// Detect skills.sh URLs and resolve to GitHub: https://skills.sh/org/repo/skill → org/repo/skill key
|
|
496
|
+
const skillsShMatch = normalizedSource.match(/^https?:\/\/(?:www\.)?skills\.sh\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:\/([A-Za-z0-9_.-]+))?(?:[?#].*)?$/i);
|
|
497
|
+
if (skillsShMatch) {
|
|
498
|
+
const [, owner, repo, skillSlugRaw] = skillsShMatch;
|
|
499
|
+
return {
|
|
500
|
+
resolvedSource: `https://github.com/${owner}/${repo}`,
|
|
501
|
+
requestedSkillSlug: skillSlugRaw ? normalizeSkillSlug(skillSlugRaw) : requestedSkillSlug,
|
|
502
|
+
originalSkillsShUrl: normalizedSource,
|
|
503
|
+
warnings,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
resolvedSource: normalizedSource,
|
|
508
|
+
requestedSkillSlug,
|
|
509
|
+
originalSkillsShUrl: null,
|
|
510
|
+
warnings,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function resolveBundledSkillsRoot() {
|
|
514
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
515
|
+
return [
|
|
516
|
+
path.resolve(moduleDir, "../../skills"),
|
|
517
|
+
path.resolve(process.cwd(), "skills"),
|
|
518
|
+
path.resolve(moduleDir, "../../../skills"),
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
function matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) {
|
|
522
|
+
if (!requestedSkillSlug)
|
|
523
|
+
return true;
|
|
524
|
+
const skillDir = path.posix.dirname(relativeSkillPath);
|
|
525
|
+
return normalizeSkillSlug(path.posix.basename(skillDir)) === requestedSkillSlug;
|
|
526
|
+
}
|
|
527
|
+
function deriveImportedSkillSlug(frontmatter, fallback) {
|
|
528
|
+
return normalizeSkillSlug(asString(frontmatter.slug))
|
|
529
|
+
?? normalizeSkillSlug(asString(frontmatter.name))
|
|
530
|
+
?? normalizeAgentUrlKey(fallback)
|
|
531
|
+
?? "skill";
|
|
532
|
+
}
|
|
533
|
+
function deriveImportedSkillSource(frontmatter, fallbackSlug) {
|
|
534
|
+
const metadata = isPlainRecord(frontmatter.metadata) ? frontmatter.metadata : null;
|
|
535
|
+
const canonicalKey = readCanonicalSkillKey(frontmatter, metadata);
|
|
536
|
+
const rawSources = metadata && Array.isArray(metadata.sources) ? metadata.sources : [];
|
|
537
|
+
const sourceEntry = rawSources.find((entry) => isPlainRecord(entry));
|
|
538
|
+
const kind = asString(sourceEntry?.kind);
|
|
539
|
+
if (kind === "github-dir" || kind === "github-file") {
|
|
540
|
+
const repo = asString(sourceEntry?.repo);
|
|
541
|
+
const repoPath = asString(sourceEntry?.path);
|
|
542
|
+
const commit = asString(sourceEntry?.commit);
|
|
543
|
+
const trackingRef = asString(sourceEntry?.trackingRef);
|
|
544
|
+
const url = asString(sourceEntry?.url)
|
|
545
|
+
?? (repo
|
|
546
|
+
? `https://github.com/${repo}${repoPath ? `/tree/${trackingRef ?? commit ?? "main"}/${repoPath}` : ""}`
|
|
547
|
+
: null);
|
|
548
|
+
const [owner, repoName] = (repo ?? "").split("/");
|
|
549
|
+
if (repo && owner && repoName) {
|
|
550
|
+
return {
|
|
551
|
+
sourceType: "github",
|
|
552
|
+
sourceLocator: url,
|
|
553
|
+
sourceRef: commit,
|
|
554
|
+
metadata: {
|
|
555
|
+
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
556
|
+
sourceKind: "github",
|
|
557
|
+
owner,
|
|
558
|
+
repo: repoName,
|
|
559
|
+
ref: commit,
|
|
560
|
+
trackingRef,
|
|
561
|
+
repoSkillDir: repoPath ?? `skills/${fallbackSlug}`,
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (kind === "url") {
|
|
567
|
+
const url = asString(sourceEntry?.url) ?? asString(sourceEntry?.rawUrl);
|
|
568
|
+
if (url) {
|
|
569
|
+
return {
|
|
570
|
+
sourceType: "url",
|
|
571
|
+
sourceLocator: url,
|
|
572
|
+
sourceRef: null,
|
|
573
|
+
metadata: {
|
|
574
|
+
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
575
|
+
sourceKind: "url",
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
sourceType: "catalog",
|
|
582
|
+
sourceLocator: null,
|
|
583
|
+
sourceRef: null,
|
|
584
|
+
metadata: {
|
|
585
|
+
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
586
|
+
sourceKind: "catalog",
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function readInlineSkillImports(companyId, files) {
|
|
591
|
+
const normalizedFiles = normalizePackageFileMap(files);
|
|
592
|
+
const skillPaths = Object.keys(normalizedFiles).filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
|
|
593
|
+
const imports = [];
|
|
594
|
+
for (const skillPath of skillPaths) {
|
|
595
|
+
const dir = path.posix.dirname(skillPath);
|
|
596
|
+
const skillDir = dir === "." ? "" : dir;
|
|
597
|
+
const slugFallback = path.posix.basename(skillDir || path.posix.dirname(skillPath));
|
|
598
|
+
const markdown = normalizedFiles[skillPath];
|
|
599
|
+
const parsed = parseFrontmatterMarkdown(markdown);
|
|
600
|
+
const slug = deriveImportedSkillSlug(parsed.frontmatter, slugFallback);
|
|
601
|
+
const source = deriveImportedSkillSource(parsed.frontmatter, slug);
|
|
602
|
+
const inventory = Object.keys(normalizedFiles)
|
|
603
|
+
.filter((entry) => entry === skillPath || (skillDir ? entry.startsWith(`${skillDir}/`) : false))
|
|
604
|
+
.map((entry) => {
|
|
605
|
+
const relative = entry === skillPath ? "SKILL.md" : entry.slice(skillDir.length + 1);
|
|
606
|
+
return {
|
|
607
|
+
path: normalizePortablePath(relative),
|
|
608
|
+
kind: classifyInventoryKind(relative),
|
|
609
|
+
};
|
|
610
|
+
})
|
|
611
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
612
|
+
imports.push({
|
|
613
|
+
key: "",
|
|
614
|
+
slug,
|
|
615
|
+
name: asString(parsed.frontmatter.name) ?? slug,
|
|
616
|
+
description: asString(parsed.frontmatter.description),
|
|
617
|
+
markdown,
|
|
618
|
+
packageDir: skillDir,
|
|
619
|
+
sourceType: source.sourceType,
|
|
620
|
+
sourceLocator: source.sourceLocator,
|
|
621
|
+
sourceRef: source.sourceRef,
|
|
622
|
+
trustLevel: deriveTrustLevel(inventory),
|
|
623
|
+
compatibility: "compatible",
|
|
624
|
+
fileInventory: inventory,
|
|
625
|
+
metadata: source.metadata,
|
|
626
|
+
});
|
|
627
|
+
imports[imports.length - 1].key = deriveCanonicalSkillKey(companyId, imports[imports.length - 1]);
|
|
628
|
+
}
|
|
629
|
+
return imports;
|
|
630
|
+
}
|
|
631
|
+
async function walkLocalFiles(root, current, out) {
|
|
632
|
+
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
633
|
+
for (const entry of entries) {
|
|
634
|
+
if (entry.name === ".git" || entry.name === "node_modules")
|
|
635
|
+
continue;
|
|
636
|
+
const absolutePath = path.join(current, entry.name);
|
|
637
|
+
if (entry.isDirectory()) {
|
|
638
|
+
await walkLocalFiles(root, absolutePath, out);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
if (!entry.isFile())
|
|
642
|
+
continue;
|
|
643
|
+
out.push(normalizePortablePath(path.relative(root, absolutePath)));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function statPath(targetPath) {
|
|
647
|
+
return fs.stat(targetPath).catch(() => null);
|
|
648
|
+
}
|
|
649
|
+
async function collectLocalSkillInventory(skillDir, mode = "full") {
|
|
650
|
+
const skillFilePath = path.join(skillDir, "SKILL.md");
|
|
651
|
+
const skillFileStat = await statPath(skillFilePath);
|
|
652
|
+
if (!skillFileStat?.isFile()) {
|
|
653
|
+
throw unprocessable(`No SKILL.md file was found in ${skillDir}.`);
|
|
654
|
+
}
|
|
655
|
+
const allFiles = new Set(["SKILL.md"]);
|
|
656
|
+
if (mode === "full") {
|
|
657
|
+
const discoveredFiles = [];
|
|
658
|
+
await walkLocalFiles(skillDir, skillDir, discoveredFiles);
|
|
659
|
+
for (const relativePath of discoveredFiles) {
|
|
660
|
+
allFiles.add(relativePath);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
for (const relativeDir of PROJECT_ROOT_SKILL_SUBDIRECTORIES) {
|
|
665
|
+
const absoluteDir = path.join(skillDir, relativeDir);
|
|
666
|
+
const dirStat = await statPath(absoluteDir);
|
|
667
|
+
if (!dirStat?.isDirectory())
|
|
668
|
+
continue;
|
|
669
|
+
const discoveredFiles = [];
|
|
670
|
+
await walkLocalFiles(skillDir, absoluteDir, discoveredFiles);
|
|
671
|
+
for (const relativePath of discoveredFiles) {
|
|
672
|
+
allFiles.add(relativePath);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return Array.from(allFiles)
|
|
677
|
+
.map((relativePath) => ({
|
|
678
|
+
path: normalizePortablePath(relativePath),
|
|
679
|
+
kind: classifyInventoryKind(relativePath),
|
|
680
|
+
}))
|
|
681
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
682
|
+
}
|
|
683
|
+
export async function readLocalSkillImportFromDirectory(companyId, skillDir, options) {
|
|
684
|
+
const resolvedSkillDir = path.resolve(skillDir);
|
|
685
|
+
const skillFilePath = path.join(resolvedSkillDir, "SKILL.md");
|
|
686
|
+
const markdown = await fs.readFile(skillFilePath, "utf8");
|
|
687
|
+
const parsed = parseFrontmatterMarkdown(markdown);
|
|
688
|
+
const slug = deriveImportedSkillSlug(parsed.frontmatter, path.basename(resolvedSkillDir));
|
|
689
|
+
const parsedMetadata = isPlainRecord(parsed.frontmatter.metadata) ? parsed.frontmatter.metadata : null;
|
|
690
|
+
const skillKey = readCanonicalSkillKey(parsed.frontmatter, parsedMetadata);
|
|
691
|
+
const metadata = {
|
|
692
|
+
...(skillKey ? { skillKey } : {}),
|
|
693
|
+
...(parsedMetadata ?? {}),
|
|
694
|
+
sourceKind: "local_path",
|
|
695
|
+
...(options?.metadata ?? {}),
|
|
696
|
+
};
|
|
697
|
+
const inventory = await collectLocalSkillInventory(resolvedSkillDir, options?.inventoryMode ?? "full");
|
|
698
|
+
return {
|
|
699
|
+
key: deriveCanonicalSkillKey(companyId, {
|
|
700
|
+
slug,
|
|
701
|
+
sourceType: "local_path",
|
|
702
|
+
sourceLocator: resolvedSkillDir,
|
|
703
|
+
metadata,
|
|
704
|
+
}),
|
|
705
|
+
slug,
|
|
706
|
+
name: asString(parsed.frontmatter.name) ?? slug,
|
|
707
|
+
description: asString(parsed.frontmatter.description),
|
|
708
|
+
markdown,
|
|
709
|
+
packageDir: resolvedSkillDir,
|
|
710
|
+
sourceType: "local_path",
|
|
711
|
+
sourceLocator: resolvedSkillDir,
|
|
712
|
+
sourceRef: null,
|
|
713
|
+
trustLevel: deriveTrustLevel(inventory),
|
|
714
|
+
compatibility: "compatible",
|
|
715
|
+
fileInventory: inventory,
|
|
716
|
+
metadata,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
export async function discoverProjectWorkspaceSkillDirectories(target) {
|
|
720
|
+
const discovered = new Map();
|
|
721
|
+
const rootSkillPath = path.join(target.workspaceCwd, "SKILL.md");
|
|
722
|
+
if ((await statPath(rootSkillPath))?.isFile()) {
|
|
723
|
+
discovered.set(path.resolve(target.workspaceCwd), "project_root");
|
|
724
|
+
}
|
|
725
|
+
for (const relativeRoot of PROJECT_SCAN_DIRECTORY_ROOTS) {
|
|
726
|
+
const absoluteRoot = path.join(target.workspaceCwd, relativeRoot);
|
|
727
|
+
const rootStat = await statPath(absoluteRoot);
|
|
728
|
+
if (!rootStat?.isDirectory())
|
|
729
|
+
continue;
|
|
730
|
+
const entries = await fs.readdir(absoluteRoot, { withFileTypes: true }).catch(() => []);
|
|
731
|
+
for (const entry of entries) {
|
|
732
|
+
if (!entry.isDirectory())
|
|
733
|
+
continue;
|
|
734
|
+
const absoluteSkillDir = path.resolve(absoluteRoot, entry.name);
|
|
735
|
+
if (!(await statPath(path.join(absoluteSkillDir, "SKILL.md")))?.isFile())
|
|
736
|
+
continue;
|
|
737
|
+
discovered.set(absoluteSkillDir, "full");
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return Array.from(discovered.entries())
|
|
741
|
+
.map(([skillDir, inventoryMode]) => ({ skillDir, inventoryMode }))
|
|
742
|
+
.sort((left, right) => left.skillDir.localeCompare(right.skillDir));
|
|
743
|
+
}
|
|
744
|
+
async function readLocalSkillImports(companyId, sourcePath) {
|
|
745
|
+
const resolvedPath = path.resolve(sourcePath);
|
|
746
|
+
const stat = await fs.stat(resolvedPath).catch(() => null);
|
|
747
|
+
if (!stat) {
|
|
748
|
+
throw unprocessable(`Skill source path does not exist: ${sourcePath}`);
|
|
749
|
+
}
|
|
750
|
+
if (stat.isFile()) {
|
|
751
|
+
const markdown = await fs.readFile(resolvedPath, "utf8");
|
|
752
|
+
const parsed = parseFrontmatterMarkdown(markdown);
|
|
753
|
+
const slug = deriveImportedSkillSlug(parsed.frontmatter, path.basename(path.dirname(resolvedPath)));
|
|
754
|
+
const parsedMetadata = isPlainRecord(parsed.frontmatter.metadata) ? parsed.frontmatter.metadata : null;
|
|
755
|
+
const skillKey = readCanonicalSkillKey(parsed.frontmatter, parsedMetadata);
|
|
756
|
+
const metadata = {
|
|
757
|
+
...(skillKey ? { skillKey } : {}),
|
|
758
|
+
...(parsedMetadata ?? {}),
|
|
759
|
+
sourceKind: "local_path",
|
|
760
|
+
};
|
|
761
|
+
const inventory = [
|
|
762
|
+
{ path: "SKILL.md", kind: "skill" },
|
|
763
|
+
];
|
|
764
|
+
return [{
|
|
765
|
+
key: deriveCanonicalSkillKey(companyId, {
|
|
766
|
+
slug,
|
|
767
|
+
sourceType: "local_path",
|
|
768
|
+
sourceLocator: path.dirname(resolvedPath),
|
|
769
|
+
metadata,
|
|
770
|
+
}),
|
|
771
|
+
slug,
|
|
772
|
+
name: asString(parsed.frontmatter.name) ?? slug,
|
|
773
|
+
description: asString(parsed.frontmatter.description),
|
|
774
|
+
markdown,
|
|
775
|
+
packageDir: path.dirname(resolvedPath),
|
|
776
|
+
sourceType: "local_path",
|
|
777
|
+
sourceLocator: path.dirname(resolvedPath),
|
|
778
|
+
sourceRef: null,
|
|
779
|
+
trustLevel: deriveTrustLevel(inventory),
|
|
780
|
+
compatibility: "compatible",
|
|
781
|
+
fileInventory: inventory,
|
|
782
|
+
metadata,
|
|
783
|
+
}];
|
|
784
|
+
}
|
|
785
|
+
const root = resolvedPath;
|
|
786
|
+
const allFiles = [];
|
|
787
|
+
await walkLocalFiles(root, root, allFiles);
|
|
788
|
+
const skillPaths = allFiles.filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
|
|
789
|
+
if (skillPaths.length === 0) {
|
|
790
|
+
throw unprocessable("No SKILL.md files were found in the provided path.");
|
|
791
|
+
}
|
|
792
|
+
const imports = [];
|
|
793
|
+
for (const skillPath of skillPaths) {
|
|
794
|
+
const skillDir = path.posix.dirname(skillPath);
|
|
795
|
+
const inventory = allFiles
|
|
796
|
+
.filter((entry) => entry === skillPath || entry.startsWith(`${skillDir}/`))
|
|
797
|
+
.map((entry) => {
|
|
798
|
+
const relative = entry === skillPath ? "SKILL.md" : entry.slice(skillDir.length + 1);
|
|
799
|
+
return {
|
|
800
|
+
path: normalizePortablePath(relative),
|
|
801
|
+
kind: classifyInventoryKind(relative),
|
|
802
|
+
};
|
|
803
|
+
})
|
|
804
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
805
|
+
const imported = await readLocalSkillImportFromDirectory(companyId, path.join(root, skillDir));
|
|
806
|
+
imported.fileInventory = inventory;
|
|
807
|
+
imported.trustLevel = deriveTrustLevel(inventory);
|
|
808
|
+
imports.push(imported);
|
|
809
|
+
}
|
|
810
|
+
return imports;
|
|
811
|
+
}
|
|
812
|
+
async function readUrlSkillImports(companyId, sourceUrl, requestedSkillSlug = null) {
|
|
813
|
+
const url = sourceUrl.trim();
|
|
814
|
+
const warnings = [];
|
|
815
|
+
if (url.includes("github.com/")) {
|
|
816
|
+
const parsed = parseGitHubSourceUrl(url);
|
|
817
|
+
const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed);
|
|
818
|
+
let ref = pinnedRef;
|
|
819
|
+
const tree = await fetchJson(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/git/trees/${ref}?recursive=1`).catch(() => {
|
|
820
|
+
throw unprocessable(`Failed to read GitHub tree for ${url}`);
|
|
821
|
+
});
|
|
822
|
+
const allPaths = (tree.tree ?? [])
|
|
823
|
+
.filter((entry) => entry.type === "blob")
|
|
824
|
+
.map((entry) => entry.path)
|
|
825
|
+
.filter((entry) => typeof entry === "string");
|
|
826
|
+
const basePrefix = parsed.basePath ? `${parsed.basePath.replace(/^\/+|\/+$/g, "")}/` : "";
|
|
827
|
+
const scopedPaths = basePrefix
|
|
828
|
+
? allPaths.filter((entry) => entry.startsWith(basePrefix))
|
|
829
|
+
: allPaths;
|
|
830
|
+
const relativePaths = scopedPaths.map((entry) => basePrefix ? entry.slice(basePrefix.length) : entry);
|
|
831
|
+
const filteredPaths = parsed.filePath
|
|
832
|
+
? relativePaths.filter((entry) => entry === path.posix.relative(parsed.basePath || ".", parsed.filePath))
|
|
833
|
+
: relativePaths;
|
|
834
|
+
const skillPaths = filteredPaths.filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
|
|
835
|
+
if (skillPaths.length === 0) {
|
|
836
|
+
throw unprocessable("No SKILL.md files were found in the provided GitHub source.");
|
|
837
|
+
}
|
|
838
|
+
const skills = [];
|
|
839
|
+
for (const relativeSkillPath of skillPaths) {
|
|
840
|
+
const repoSkillPath = basePrefix ? `${basePrefix}${relativeSkillPath}` : relativeSkillPath;
|
|
841
|
+
const markdown = await fetchText(resolveRawGitHubUrl(parsed.owner, parsed.repo, ref, repoSkillPath));
|
|
842
|
+
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
|
843
|
+
const skillDir = path.posix.dirname(relativeSkillPath);
|
|
844
|
+
const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, path.posix.basename(skillDir));
|
|
845
|
+
const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
|
|
846
|
+
if (requestedSkillSlug && !matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) && slug !== requestedSkillSlug) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const metadata = {
|
|
850
|
+
...(skillKey ? { skillKey } : {}),
|
|
851
|
+
sourceKind: "github",
|
|
852
|
+
owner: parsed.owner,
|
|
853
|
+
repo: parsed.repo,
|
|
854
|
+
ref: ref,
|
|
855
|
+
trackingRef,
|
|
856
|
+
repoSkillDir: normalizeGitHubSkillDirectory(basePrefix ? `${basePrefix}${skillDir}` : skillDir, slug),
|
|
857
|
+
};
|
|
858
|
+
const inventory = filteredPaths
|
|
859
|
+
.filter((entry) => entry === relativeSkillPath || entry.startsWith(`${skillDir}/`))
|
|
860
|
+
.map((entry) => ({
|
|
861
|
+
path: entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1),
|
|
862
|
+
kind: classifyInventoryKind(entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1)),
|
|
863
|
+
}))
|
|
864
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
865
|
+
skills.push({
|
|
866
|
+
key: deriveCanonicalSkillKey(companyId, {
|
|
867
|
+
slug,
|
|
868
|
+
sourceType: "github",
|
|
869
|
+
sourceLocator: sourceUrl,
|
|
870
|
+
metadata,
|
|
871
|
+
}),
|
|
872
|
+
slug,
|
|
873
|
+
name: asString(parsedMarkdown.frontmatter.name) ?? slug,
|
|
874
|
+
description: asString(parsedMarkdown.frontmatter.description),
|
|
875
|
+
markdown,
|
|
876
|
+
sourceType: "github",
|
|
877
|
+
sourceLocator: sourceUrl,
|
|
878
|
+
sourceRef: ref,
|
|
879
|
+
trustLevel: deriveTrustLevel(inventory),
|
|
880
|
+
compatibility: "compatible",
|
|
881
|
+
fileInventory: inventory,
|
|
882
|
+
metadata,
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
if (skills.length === 0) {
|
|
886
|
+
throw unprocessable(requestedSkillSlug
|
|
887
|
+
? `Skill ${requestedSkillSlug} was not found in the provided GitHub source.`
|
|
888
|
+
: "No SKILL.md files were found in the provided GitHub source.");
|
|
889
|
+
}
|
|
890
|
+
return { skills, warnings };
|
|
891
|
+
}
|
|
892
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
893
|
+
const markdown = await fetchText(url);
|
|
894
|
+
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
|
895
|
+
const urlObj = new URL(url);
|
|
896
|
+
const fileName = path.posix.basename(urlObj.pathname);
|
|
897
|
+
const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, fileName.replace(/\.md$/i, ""));
|
|
898
|
+
const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
|
|
899
|
+
const metadata = {
|
|
900
|
+
...(skillKey ? { skillKey } : {}),
|
|
901
|
+
sourceKind: "url",
|
|
902
|
+
};
|
|
903
|
+
const inventory = [{ path: "SKILL.md", kind: "skill" }];
|
|
904
|
+
return {
|
|
905
|
+
skills: [{
|
|
906
|
+
key: deriveCanonicalSkillKey(companyId, {
|
|
907
|
+
slug,
|
|
908
|
+
sourceType: "url",
|
|
909
|
+
sourceLocator: url,
|
|
910
|
+
metadata,
|
|
911
|
+
}),
|
|
912
|
+
slug,
|
|
913
|
+
name: asString(parsedMarkdown.frontmatter.name) ?? slug,
|
|
914
|
+
description: asString(parsedMarkdown.frontmatter.description),
|
|
915
|
+
markdown,
|
|
916
|
+
sourceType: "url",
|
|
917
|
+
sourceLocator: url,
|
|
918
|
+
sourceRef: null,
|
|
919
|
+
trustLevel: deriveTrustLevel(inventory),
|
|
920
|
+
compatibility: "compatible",
|
|
921
|
+
fileInventory: inventory,
|
|
922
|
+
metadata,
|
|
923
|
+
}],
|
|
924
|
+
warnings,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
throw unprocessable("Unsupported skill source. Use a local path or URL.");
|
|
928
|
+
}
|
|
929
|
+
function toCompanySkill(row) {
|
|
930
|
+
return {
|
|
931
|
+
...row,
|
|
932
|
+
description: row.description ?? null,
|
|
933
|
+
sourceType: row.sourceType,
|
|
934
|
+
sourceLocator: row.sourceLocator ?? null,
|
|
935
|
+
sourceRef: row.sourceRef ?? null,
|
|
936
|
+
trustLevel: row.trustLevel,
|
|
937
|
+
compatibility: row.compatibility,
|
|
938
|
+
fileInventory: Array.isArray(row.fileInventory)
|
|
939
|
+
? row.fileInventory.flatMap((entry) => {
|
|
940
|
+
if (!isPlainRecord(entry))
|
|
941
|
+
return [];
|
|
942
|
+
return [{
|
|
943
|
+
path: String(entry.path ?? ""),
|
|
944
|
+
kind: String(entry.kind ?? "other"),
|
|
945
|
+
}];
|
|
946
|
+
})
|
|
947
|
+
: [],
|
|
948
|
+
metadata: isPlainRecord(row.metadata) ? row.metadata : null,
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function serializeFileInventory(fileInventory) {
|
|
952
|
+
return fileInventory.map((entry) => ({
|
|
953
|
+
path: entry.path,
|
|
954
|
+
kind: entry.kind,
|
|
955
|
+
}));
|
|
956
|
+
}
|
|
957
|
+
function getSkillMeta(skill) {
|
|
958
|
+
return isPlainRecord(skill.metadata) ? skill.metadata : {};
|
|
959
|
+
}
|
|
960
|
+
function resolveSkillReference(skills, reference) {
|
|
961
|
+
const trimmed = reference.trim();
|
|
962
|
+
if (!trimmed) {
|
|
963
|
+
return { skill: null, ambiguous: false };
|
|
964
|
+
}
|
|
965
|
+
const byId = skills.find((skill) => skill.id === trimmed);
|
|
966
|
+
if (byId) {
|
|
967
|
+
return { skill: byId, ambiguous: false };
|
|
968
|
+
}
|
|
969
|
+
const normalizedKey = normalizeSkillKey(trimmed);
|
|
970
|
+
if (normalizedKey) {
|
|
971
|
+
const byKey = skills.find((skill) => skill.key === normalizedKey);
|
|
972
|
+
if (byKey) {
|
|
973
|
+
return { skill: byKey, ambiguous: false };
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const normalizedSlug = normalizeSkillSlug(trimmed);
|
|
977
|
+
if (!normalizedSlug) {
|
|
978
|
+
return { skill: null, ambiguous: false };
|
|
979
|
+
}
|
|
980
|
+
const bySlug = skills.filter((skill) => skill.slug === normalizedSlug);
|
|
981
|
+
if (bySlug.length === 1) {
|
|
982
|
+
return { skill: bySlug[0] ?? null, ambiguous: false };
|
|
983
|
+
}
|
|
984
|
+
if (bySlug.length > 1) {
|
|
985
|
+
return { skill: null, ambiguous: true };
|
|
986
|
+
}
|
|
987
|
+
return { skill: null, ambiguous: false };
|
|
988
|
+
}
|
|
989
|
+
function resolveRequestedSkillKeysOrThrow(skills, requestedReferences) {
|
|
990
|
+
const missing = new Set();
|
|
991
|
+
const ambiguous = new Set();
|
|
992
|
+
const resolved = new Set();
|
|
993
|
+
for (const reference of requestedReferences) {
|
|
994
|
+
const trimmed = reference.trim();
|
|
995
|
+
if (!trimmed)
|
|
996
|
+
continue;
|
|
997
|
+
const match = resolveSkillReference(skills, trimmed);
|
|
998
|
+
if (match.skill) {
|
|
999
|
+
resolved.add(match.skill.key);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (match.ambiguous) {
|
|
1003
|
+
ambiguous.add(trimmed);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
missing.add(trimmed);
|
|
1007
|
+
}
|
|
1008
|
+
if (ambiguous.size > 0 || missing.size > 0) {
|
|
1009
|
+
const problems = [];
|
|
1010
|
+
if (ambiguous.size > 0) {
|
|
1011
|
+
problems.push(`ambiguous references: ${Array.from(ambiguous).sort().join(", ")}`);
|
|
1012
|
+
}
|
|
1013
|
+
if (missing.size > 0) {
|
|
1014
|
+
problems.push(`unknown references: ${Array.from(missing).sort().join(", ")}`);
|
|
1015
|
+
}
|
|
1016
|
+
throw unprocessable(`Invalid company skill selection (${problems.join("; ")}).`);
|
|
1017
|
+
}
|
|
1018
|
+
return Array.from(resolved);
|
|
1019
|
+
}
|
|
1020
|
+
function resolveDesiredSkillKeys(skills, config) {
|
|
1021
|
+
const preference = readFideliOSSkillSyncPreference(config);
|
|
1022
|
+
return Array.from(new Set(preference.desiredSkills
|
|
1023
|
+
.map((reference) => resolveSkillReference(skills, reference).skill?.key ?? normalizeSkillKey(reference))
|
|
1024
|
+
.filter((value) => Boolean(value))));
|
|
1025
|
+
}
|
|
1026
|
+
function normalizeSkillDirectory(skill) {
|
|
1027
|
+
if ((skill.sourceType !== "local_path" && skill.sourceType !== "catalog") || !skill.sourceLocator)
|
|
1028
|
+
return null;
|
|
1029
|
+
const resolved = path.resolve(skill.sourceLocator);
|
|
1030
|
+
if (path.basename(resolved).toLowerCase() === "skill.md") {
|
|
1031
|
+
return path.dirname(resolved);
|
|
1032
|
+
}
|
|
1033
|
+
return resolved;
|
|
1034
|
+
}
|
|
1035
|
+
function normalizeSourceLocatorDirectory(sourceLocator) {
|
|
1036
|
+
if (!sourceLocator)
|
|
1037
|
+
return null;
|
|
1038
|
+
const resolved = path.resolve(sourceLocator);
|
|
1039
|
+
return path.basename(resolved).toLowerCase() === "skill.md" ? path.dirname(resolved) : resolved;
|
|
1040
|
+
}
|
|
1041
|
+
export async function findMissingLocalSkillIds(skills) {
|
|
1042
|
+
const missingIds = [];
|
|
1043
|
+
for (const skill of skills) {
|
|
1044
|
+
if (skill.sourceType !== "local_path")
|
|
1045
|
+
continue;
|
|
1046
|
+
const skillDir = normalizeSourceLocatorDirectory(skill.sourceLocator);
|
|
1047
|
+
if (!skillDir) {
|
|
1048
|
+
missingIds.push(skill.id);
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const skillDirStat = await statPath(skillDir);
|
|
1052
|
+
const skillFileStat = await statPath(path.join(skillDir, "SKILL.md"));
|
|
1053
|
+
if (!skillDirStat?.isDirectory() || !skillFileStat?.isFile()) {
|
|
1054
|
+
missingIds.push(skill.id);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return missingIds;
|
|
1058
|
+
}
|
|
1059
|
+
function resolveManagedSkillsRoot(companyId) {
|
|
1060
|
+
return path.resolve(resolveFideliOSInstanceRoot(), "skills", companyId);
|
|
1061
|
+
}
|
|
1062
|
+
function resolveLocalSkillFilePath(skill, relativePath) {
|
|
1063
|
+
const normalized = normalizePortablePath(relativePath);
|
|
1064
|
+
const skillDir = normalizeSkillDirectory(skill);
|
|
1065
|
+
if (skillDir) {
|
|
1066
|
+
return path.resolve(skillDir, normalized);
|
|
1067
|
+
}
|
|
1068
|
+
if (!skill.sourceLocator)
|
|
1069
|
+
return null;
|
|
1070
|
+
const fallbackRoot = path.resolve(skill.sourceLocator);
|
|
1071
|
+
const directPath = path.resolve(fallbackRoot, normalized);
|
|
1072
|
+
return directPath;
|
|
1073
|
+
}
|
|
1074
|
+
function inferLanguageFromPath(filePath) {
|
|
1075
|
+
const fileName = path.posix.basename(filePath).toLowerCase();
|
|
1076
|
+
if (fileName === "skill.md" || fileName.endsWith(".md"))
|
|
1077
|
+
return "markdown";
|
|
1078
|
+
if (fileName.endsWith(".ts"))
|
|
1079
|
+
return "typescript";
|
|
1080
|
+
if (fileName.endsWith(".tsx"))
|
|
1081
|
+
return "tsx";
|
|
1082
|
+
if (fileName.endsWith(".js"))
|
|
1083
|
+
return "javascript";
|
|
1084
|
+
if (fileName.endsWith(".jsx"))
|
|
1085
|
+
return "jsx";
|
|
1086
|
+
if (fileName.endsWith(".json"))
|
|
1087
|
+
return "json";
|
|
1088
|
+
if (fileName.endsWith(".yml") || fileName.endsWith(".yaml"))
|
|
1089
|
+
return "yaml";
|
|
1090
|
+
if (fileName.endsWith(".sh"))
|
|
1091
|
+
return "bash";
|
|
1092
|
+
if (fileName.endsWith(".py"))
|
|
1093
|
+
return "python";
|
|
1094
|
+
if (fileName.endsWith(".html"))
|
|
1095
|
+
return "html";
|
|
1096
|
+
if (fileName.endsWith(".css"))
|
|
1097
|
+
return "css";
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
function isMarkdownPath(filePath) {
|
|
1101
|
+
const fileName = path.posix.basename(filePath).toLowerCase();
|
|
1102
|
+
return fileName === "skill.md" || fileName.endsWith(".md");
|
|
1103
|
+
}
|
|
1104
|
+
function deriveSkillSourceInfo(skill) {
|
|
1105
|
+
const metadata = getSkillMeta(skill);
|
|
1106
|
+
const localSkillDir = normalizeSkillDirectory(skill);
|
|
1107
|
+
if (metadata.sourceKind === "fidelios_bundled") {
|
|
1108
|
+
return {
|
|
1109
|
+
editable: false,
|
|
1110
|
+
editableReason: "Bundled FideliOS skills are read-only.",
|
|
1111
|
+
sourceLabel: "FideliOS bundled",
|
|
1112
|
+
sourceBadge: "fidelios",
|
|
1113
|
+
sourcePath: null,
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
if (skill.sourceType === "skills_sh") {
|
|
1117
|
+
const owner = asString(metadata.owner) ?? null;
|
|
1118
|
+
const repo = asString(metadata.repo) ?? null;
|
|
1119
|
+
return {
|
|
1120
|
+
editable: false,
|
|
1121
|
+
editableReason: "Skills.sh-managed skills are read-only.",
|
|
1122
|
+
sourceLabel: skill.sourceLocator ?? (owner && repo ? `${owner}/${repo}` : null),
|
|
1123
|
+
sourceBadge: "skills_sh",
|
|
1124
|
+
sourcePath: null,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
if (skill.sourceType === "github") {
|
|
1128
|
+
const owner = asString(metadata.owner) ?? null;
|
|
1129
|
+
const repo = asString(metadata.repo) ?? null;
|
|
1130
|
+
return {
|
|
1131
|
+
editable: false,
|
|
1132
|
+
editableReason: "Remote GitHub skills are read-only. Fork or import locally to edit them.",
|
|
1133
|
+
sourceLabel: owner && repo ? `${owner}/${repo}` : skill.sourceLocator,
|
|
1134
|
+
sourceBadge: "github",
|
|
1135
|
+
sourcePath: null,
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
if (skill.sourceType === "url") {
|
|
1139
|
+
return {
|
|
1140
|
+
editable: false,
|
|
1141
|
+
editableReason: "URL-based skills are read-only. Save them locally to edit them.",
|
|
1142
|
+
sourceLabel: skill.sourceLocator,
|
|
1143
|
+
sourceBadge: "url",
|
|
1144
|
+
sourcePath: null,
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
if (skill.sourceType === "local_path") {
|
|
1148
|
+
const managedRoot = resolveManagedSkillsRoot(skill.companyId);
|
|
1149
|
+
const projectName = asString(metadata.projectName);
|
|
1150
|
+
const workspaceName = asString(metadata.workspaceName);
|
|
1151
|
+
const isProjectScan = metadata.sourceKind === "project_scan";
|
|
1152
|
+
if (localSkillDir && localSkillDir.startsWith(managedRoot)) {
|
|
1153
|
+
return {
|
|
1154
|
+
editable: true,
|
|
1155
|
+
editableReason: null,
|
|
1156
|
+
sourceLabel: "FideliOS workspace",
|
|
1157
|
+
sourceBadge: "fidelios",
|
|
1158
|
+
sourcePath: managedRoot,
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
editable: true,
|
|
1163
|
+
editableReason: null,
|
|
1164
|
+
sourceLabel: isProjectScan
|
|
1165
|
+
? [projectName, workspaceName].filter((value) => Boolean(value)).join(" / ")
|
|
1166
|
+
|| skill.sourceLocator
|
|
1167
|
+
: skill.sourceLocator,
|
|
1168
|
+
sourceBadge: "local",
|
|
1169
|
+
sourcePath: null,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
return {
|
|
1173
|
+
editable: false,
|
|
1174
|
+
editableReason: "This skill source is read-only.",
|
|
1175
|
+
sourceLabel: skill.sourceLocator,
|
|
1176
|
+
sourceBadge: "catalog",
|
|
1177
|
+
sourcePath: null,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
function enrichSkill(skill, attachedAgentCount, usedByAgents = []) {
|
|
1181
|
+
const source = deriveSkillSourceInfo(skill);
|
|
1182
|
+
return {
|
|
1183
|
+
...skill,
|
|
1184
|
+
attachedAgentCount,
|
|
1185
|
+
usedByAgents,
|
|
1186
|
+
...source,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
function toCompanySkillListItem(skill, attachedAgentCount) {
|
|
1190
|
+
const source = deriveSkillSourceInfo(skill);
|
|
1191
|
+
return {
|
|
1192
|
+
id: skill.id,
|
|
1193
|
+
companyId: skill.companyId,
|
|
1194
|
+
key: skill.key,
|
|
1195
|
+
slug: skill.slug,
|
|
1196
|
+
name: skill.name,
|
|
1197
|
+
description: skill.description,
|
|
1198
|
+
sourceType: skill.sourceType,
|
|
1199
|
+
sourceLocator: skill.sourceLocator,
|
|
1200
|
+
sourceRef: skill.sourceRef,
|
|
1201
|
+
trustLevel: skill.trustLevel,
|
|
1202
|
+
compatibility: skill.compatibility,
|
|
1203
|
+
fileInventory: skill.fileInventory,
|
|
1204
|
+
createdAt: skill.createdAt,
|
|
1205
|
+
updatedAt: skill.updatedAt,
|
|
1206
|
+
attachedAgentCount,
|
|
1207
|
+
editable: source.editable,
|
|
1208
|
+
editableReason: source.editableReason,
|
|
1209
|
+
sourceLabel: source.sourceLabel,
|
|
1210
|
+
sourceBadge: source.sourceBadge,
|
|
1211
|
+
sourcePath: source.sourcePath,
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
export function companySkillService(db) {
|
|
1215
|
+
const agents = agentService(db);
|
|
1216
|
+
const projects = projectService(db);
|
|
1217
|
+
const secretsSvc = secretService(db);
|
|
1218
|
+
async function ensureBundledSkills(companyId) {
|
|
1219
|
+
for (const skillsRoot of resolveBundledSkillsRoot()) {
|
|
1220
|
+
const stats = await fs.stat(skillsRoot).catch(() => null);
|
|
1221
|
+
if (!stats?.isDirectory())
|
|
1222
|
+
continue;
|
|
1223
|
+
const bundledSkills = await readLocalSkillImports(companyId, skillsRoot)
|
|
1224
|
+
.then((skills) => skills.map((skill) => ({
|
|
1225
|
+
...skill,
|
|
1226
|
+
key: deriveCanonicalSkillKey(companyId, {
|
|
1227
|
+
...skill,
|
|
1228
|
+
metadata: {
|
|
1229
|
+
...(skill.metadata ?? {}),
|
|
1230
|
+
sourceKind: "fidelios_bundled",
|
|
1231
|
+
},
|
|
1232
|
+
}),
|
|
1233
|
+
metadata: {
|
|
1234
|
+
...(skill.metadata ?? {}),
|
|
1235
|
+
sourceKind: "fidelios_bundled",
|
|
1236
|
+
},
|
|
1237
|
+
})))
|
|
1238
|
+
.catch(() => []);
|
|
1239
|
+
if (bundledSkills.length === 0)
|
|
1240
|
+
continue;
|
|
1241
|
+
return upsertImportedSkills(companyId, bundledSkills);
|
|
1242
|
+
}
|
|
1243
|
+
return [];
|
|
1244
|
+
}
|
|
1245
|
+
async function pruneMissingLocalPathSkills(companyId) {
|
|
1246
|
+
const rows = await db
|
|
1247
|
+
.select()
|
|
1248
|
+
.from(companySkills)
|
|
1249
|
+
.where(eq(companySkills.companyId, companyId));
|
|
1250
|
+
const skills = rows.map((row) => toCompanySkill(row));
|
|
1251
|
+
const missingIds = new Set(await findMissingLocalSkillIds(skills));
|
|
1252
|
+
if (missingIds.size === 0)
|
|
1253
|
+
return;
|
|
1254
|
+
for (const skill of skills) {
|
|
1255
|
+
if (!missingIds.has(skill.id))
|
|
1256
|
+
continue;
|
|
1257
|
+
await db
|
|
1258
|
+
.delete(companySkills)
|
|
1259
|
+
.where(eq(companySkills.id, skill.id));
|
|
1260
|
+
await fs.rm(resolveRuntimeSkillMaterializedPath(companyId, skill), { recursive: true, force: true });
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
async function ensureSkillInventoryCurrent(companyId) {
|
|
1264
|
+
const existingRefresh = skillInventoryRefreshPromises.get(companyId);
|
|
1265
|
+
if (existingRefresh) {
|
|
1266
|
+
await existingRefresh;
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
const refreshPromise = (async () => {
|
|
1270
|
+
await ensureBundledSkills(companyId);
|
|
1271
|
+
await pruneMissingLocalPathSkills(companyId);
|
|
1272
|
+
})();
|
|
1273
|
+
skillInventoryRefreshPromises.set(companyId, refreshPromise);
|
|
1274
|
+
try {
|
|
1275
|
+
await refreshPromise;
|
|
1276
|
+
}
|
|
1277
|
+
finally {
|
|
1278
|
+
if (skillInventoryRefreshPromises.get(companyId) === refreshPromise) {
|
|
1279
|
+
skillInventoryRefreshPromises.delete(companyId);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
async function list(companyId) {
|
|
1284
|
+
const rows = await listFull(companyId);
|
|
1285
|
+
const agentRows = await agents.list(companyId);
|
|
1286
|
+
return rows.map((skill) => {
|
|
1287
|
+
const attachedAgentCount = agentRows.filter((agent) => {
|
|
1288
|
+
const desiredSkills = resolveDesiredSkillKeys(rows, agent.adapterConfig);
|
|
1289
|
+
return desiredSkills.includes(skill.key);
|
|
1290
|
+
}).length;
|
|
1291
|
+
return toCompanySkillListItem(skill, attachedAgentCount);
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
async function listFull(companyId) {
|
|
1295
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1296
|
+
const rows = await db
|
|
1297
|
+
.select()
|
|
1298
|
+
.from(companySkills)
|
|
1299
|
+
.where(eq(companySkills.companyId, companyId))
|
|
1300
|
+
.orderBy(asc(companySkills.name), asc(companySkills.key));
|
|
1301
|
+
return rows.map((row) => toCompanySkill(row));
|
|
1302
|
+
}
|
|
1303
|
+
async function getById(id) {
|
|
1304
|
+
const row = await db
|
|
1305
|
+
.select()
|
|
1306
|
+
.from(companySkills)
|
|
1307
|
+
.where(eq(companySkills.id, id))
|
|
1308
|
+
.then((rows) => rows[0] ?? null);
|
|
1309
|
+
return row ? toCompanySkill(row) : null;
|
|
1310
|
+
}
|
|
1311
|
+
async function getByKey(companyId, key) {
|
|
1312
|
+
const row = await db
|
|
1313
|
+
.select()
|
|
1314
|
+
.from(companySkills)
|
|
1315
|
+
.where(and(eq(companySkills.companyId, companyId), eq(companySkills.key, key)))
|
|
1316
|
+
.then((rows) => rows[0] ?? null);
|
|
1317
|
+
return row ? toCompanySkill(row) : null;
|
|
1318
|
+
}
|
|
1319
|
+
async function usage(companyId, key) {
|
|
1320
|
+
const skills = await listFull(companyId);
|
|
1321
|
+
const agentRows = await agents.list(companyId);
|
|
1322
|
+
const desiredAgents = agentRows.filter((agent) => {
|
|
1323
|
+
const desiredSkills = resolveDesiredSkillKeys(skills, agent.adapterConfig);
|
|
1324
|
+
return desiredSkills.includes(key);
|
|
1325
|
+
});
|
|
1326
|
+
return Promise.all(desiredAgents.map(async (agent) => {
|
|
1327
|
+
const adapter = findServerAdapter(agent.adapterType);
|
|
1328
|
+
let actualState = null;
|
|
1329
|
+
if (!adapter?.listSkills) {
|
|
1330
|
+
actualState = "unsupported";
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
try {
|
|
1334
|
+
const { config: runtimeConfig } = await secretsSvc.resolveAdapterConfigForRuntime(agent.companyId, agent.adapterConfig);
|
|
1335
|
+
const runtimeSkillEntries = await listRuntimeSkillEntries(agent.companyId);
|
|
1336
|
+
const snapshot = await adapter.listSkills({
|
|
1337
|
+
agentId: agent.id,
|
|
1338
|
+
companyId: agent.companyId,
|
|
1339
|
+
adapterType: agent.adapterType,
|
|
1340
|
+
config: {
|
|
1341
|
+
...runtimeConfig,
|
|
1342
|
+
fideliosRuntimeSkills: runtimeSkillEntries,
|
|
1343
|
+
},
|
|
1344
|
+
});
|
|
1345
|
+
actualState = snapshot.entries.find((entry) => entry.key === key)?.state
|
|
1346
|
+
?? (snapshot.supported ? "missing" : "unsupported");
|
|
1347
|
+
}
|
|
1348
|
+
catch {
|
|
1349
|
+
actualState = "unknown";
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
return {
|
|
1353
|
+
id: agent.id,
|
|
1354
|
+
name: agent.name,
|
|
1355
|
+
urlKey: agent.urlKey,
|
|
1356
|
+
adapterType: agent.adapterType,
|
|
1357
|
+
desired: true,
|
|
1358
|
+
actualState,
|
|
1359
|
+
};
|
|
1360
|
+
}));
|
|
1361
|
+
}
|
|
1362
|
+
async function detail(companyId, id) {
|
|
1363
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1364
|
+
const skill = await getById(id);
|
|
1365
|
+
if (!skill || skill.companyId !== companyId)
|
|
1366
|
+
return null;
|
|
1367
|
+
const usedByAgents = await usage(companyId, skill.key);
|
|
1368
|
+
return enrichSkill(skill, usedByAgents.length, usedByAgents);
|
|
1369
|
+
}
|
|
1370
|
+
async function updateStatus(companyId, skillId) {
|
|
1371
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1372
|
+
const skill = await getById(skillId);
|
|
1373
|
+
if (!skill || skill.companyId !== companyId)
|
|
1374
|
+
return null;
|
|
1375
|
+
if (skill.sourceType !== "github" && skill.sourceType !== "skills_sh") {
|
|
1376
|
+
return {
|
|
1377
|
+
supported: false,
|
|
1378
|
+
reason: "Only GitHub-managed skills support update checks.",
|
|
1379
|
+
trackingRef: null,
|
|
1380
|
+
currentRef: skill.sourceRef ?? null,
|
|
1381
|
+
latestRef: null,
|
|
1382
|
+
hasUpdate: false,
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
const metadata = getSkillMeta(skill);
|
|
1386
|
+
const owner = asString(metadata.owner);
|
|
1387
|
+
const repo = asString(metadata.repo);
|
|
1388
|
+
const trackingRef = asString(metadata.trackingRef) ?? asString(metadata.ref);
|
|
1389
|
+
if (!owner || !repo || !trackingRef) {
|
|
1390
|
+
return {
|
|
1391
|
+
supported: false,
|
|
1392
|
+
reason: "This GitHub skill does not have enough metadata to track updates.",
|
|
1393
|
+
trackingRef: trackingRef ?? null,
|
|
1394
|
+
currentRef: skill.sourceRef ?? null,
|
|
1395
|
+
latestRef: null,
|
|
1396
|
+
hasUpdate: false,
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
const latestRef = await resolveGitHubCommitSha(owner, repo, trackingRef);
|
|
1400
|
+
return {
|
|
1401
|
+
supported: true,
|
|
1402
|
+
reason: null,
|
|
1403
|
+
trackingRef,
|
|
1404
|
+
currentRef: skill.sourceRef ?? null,
|
|
1405
|
+
latestRef,
|
|
1406
|
+
hasUpdate: latestRef !== (skill.sourceRef ?? null),
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
async function readFile(companyId, skillId, relativePath) {
|
|
1410
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1411
|
+
const skill = await getById(skillId);
|
|
1412
|
+
if (!skill || skill.companyId !== companyId)
|
|
1413
|
+
return null;
|
|
1414
|
+
const normalizedPath = normalizePortablePath(relativePath || "SKILL.md");
|
|
1415
|
+
const fileEntry = skill.fileInventory.find((entry) => entry.path === normalizedPath);
|
|
1416
|
+
if (!fileEntry) {
|
|
1417
|
+
throw notFound("Skill file not found");
|
|
1418
|
+
}
|
|
1419
|
+
const source = deriveSkillSourceInfo(skill);
|
|
1420
|
+
let content = "";
|
|
1421
|
+
if (skill.sourceType === "local_path" || skill.sourceType === "catalog") {
|
|
1422
|
+
const absolutePath = resolveLocalSkillFilePath(skill, normalizedPath);
|
|
1423
|
+
if (absolutePath) {
|
|
1424
|
+
content = await fs.readFile(absolutePath, "utf8");
|
|
1425
|
+
}
|
|
1426
|
+
else if (normalizedPath === "SKILL.md") {
|
|
1427
|
+
content = skill.markdown;
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
throw notFound("Skill file not found");
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
else if (skill.sourceType === "github" || skill.sourceType === "skills_sh") {
|
|
1434
|
+
const metadata = getSkillMeta(skill);
|
|
1435
|
+
const owner = asString(metadata.owner);
|
|
1436
|
+
const repo = asString(metadata.repo);
|
|
1437
|
+
const ref = skill.sourceRef ?? asString(metadata.ref) ?? "main";
|
|
1438
|
+
const repoSkillDir = normalizeGitHubSkillDirectory(asString(metadata.repoSkillDir), skill.slug);
|
|
1439
|
+
if (!owner || !repo) {
|
|
1440
|
+
throw unprocessable("Skill source metadata is incomplete.");
|
|
1441
|
+
}
|
|
1442
|
+
const repoPath = normalizePortablePath(path.posix.join(repoSkillDir, normalizedPath));
|
|
1443
|
+
content = await fetchText(resolveRawGitHubUrl(owner, repo, ref, repoPath));
|
|
1444
|
+
}
|
|
1445
|
+
else if (skill.sourceType === "url") {
|
|
1446
|
+
if (normalizedPath !== "SKILL.md") {
|
|
1447
|
+
throw notFound("This skill source only exposes SKILL.md");
|
|
1448
|
+
}
|
|
1449
|
+
content = skill.markdown;
|
|
1450
|
+
}
|
|
1451
|
+
else {
|
|
1452
|
+
throw unprocessable("Unsupported skill source.");
|
|
1453
|
+
}
|
|
1454
|
+
return {
|
|
1455
|
+
skillId: skill.id,
|
|
1456
|
+
path: normalizedPath,
|
|
1457
|
+
kind: fileEntry.kind,
|
|
1458
|
+
content,
|
|
1459
|
+
language: inferLanguageFromPath(normalizedPath),
|
|
1460
|
+
markdown: isMarkdownPath(normalizedPath),
|
|
1461
|
+
editable: source.editable,
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
async function createLocalSkill(companyId, input) {
|
|
1465
|
+
const slug = normalizeSkillSlug(input.slug ?? input.name) ?? "skill";
|
|
1466
|
+
const managedRoot = resolveManagedSkillsRoot(companyId);
|
|
1467
|
+
const skillDir = path.resolve(managedRoot, slug);
|
|
1468
|
+
const skillFilePath = path.resolve(skillDir, "SKILL.md");
|
|
1469
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
1470
|
+
const markdown = (input.markdown?.trim().length
|
|
1471
|
+
? input.markdown
|
|
1472
|
+
: [
|
|
1473
|
+
"---",
|
|
1474
|
+
`name: ${input.name}`,
|
|
1475
|
+
...(input.description?.trim() ? [`description: ${input.description.trim()}`] : []),
|
|
1476
|
+
"---",
|
|
1477
|
+
"",
|
|
1478
|
+
`# ${input.name}`,
|
|
1479
|
+
"",
|
|
1480
|
+
input.description?.trim() ? input.description.trim() : "Describe what this skill does.",
|
|
1481
|
+
"",
|
|
1482
|
+
].join("\n"));
|
|
1483
|
+
await fs.writeFile(skillFilePath, markdown, "utf8");
|
|
1484
|
+
const parsed = parseFrontmatterMarkdown(markdown);
|
|
1485
|
+
const imported = await upsertImportedSkills(companyId, [{
|
|
1486
|
+
key: `company/${companyId}/${slug}`,
|
|
1487
|
+
slug,
|
|
1488
|
+
name: asString(parsed.frontmatter.name) ?? input.name,
|
|
1489
|
+
description: asString(parsed.frontmatter.description) ?? input.description?.trim() ?? null,
|
|
1490
|
+
markdown,
|
|
1491
|
+
sourceType: "local_path",
|
|
1492
|
+
sourceLocator: skillDir,
|
|
1493
|
+
sourceRef: null,
|
|
1494
|
+
trustLevel: "markdown_only",
|
|
1495
|
+
compatibility: "compatible",
|
|
1496
|
+
fileInventory: [{ path: "SKILL.md", kind: "skill" }],
|
|
1497
|
+
metadata: { sourceKind: "managed_local" },
|
|
1498
|
+
}]);
|
|
1499
|
+
return imported[0];
|
|
1500
|
+
}
|
|
1501
|
+
async function updateFile(companyId, skillId, relativePath, content) {
|
|
1502
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1503
|
+
const skill = await getById(skillId);
|
|
1504
|
+
if (!skill || skill.companyId !== companyId)
|
|
1505
|
+
throw notFound("Skill not found");
|
|
1506
|
+
const source = deriveSkillSourceInfo(skill);
|
|
1507
|
+
if (!source.editable || skill.sourceType !== "local_path") {
|
|
1508
|
+
throw unprocessable(source.editableReason ?? "This skill cannot be edited.");
|
|
1509
|
+
}
|
|
1510
|
+
const normalizedPath = normalizePortablePath(relativePath);
|
|
1511
|
+
const absolutePath = resolveLocalSkillFilePath(skill, normalizedPath);
|
|
1512
|
+
if (!absolutePath)
|
|
1513
|
+
throw notFound("Skill file not found");
|
|
1514
|
+
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
1515
|
+
await fs.writeFile(absolutePath, content, "utf8");
|
|
1516
|
+
if (normalizedPath === "SKILL.md") {
|
|
1517
|
+
const parsed = parseFrontmatterMarkdown(content);
|
|
1518
|
+
await db
|
|
1519
|
+
.update(companySkills)
|
|
1520
|
+
.set({
|
|
1521
|
+
name: asString(parsed.frontmatter.name) ?? skill.name,
|
|
1522
|
+
description: asString(parsed.frontmatter.description) ?? skill.description,
|
|
1523
|
+
markdown: content,
|
|
1524
|
+
updatedAt: new Date(),
|
|
1525
|
+
})
|
|
1526
|
+
.where(eq(companySkills.id, skill.id));
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
await db
|
|
1530
|
+
.update(companySkills)
|
|
1531
|
+
.set({ updatedAt: new Date() })
|
|
1532
|
+
.where(eq(companySkills.id, skill.id));
|
|
1533
|
+
}
|
|
1534
|
+
const detail = await readFile(companyId, skillId, normalizedPath);
|
|
1535
|
+
if (!detail)
|
|
1536
|
+
throw notFound("Skill file not found");
|
|
1537
|
+
return detail;
|
|
1538
|
+
}
|
|
1539
|
+
async function installUpdate(companyId, skillId) {
|
|
1540
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1541
|
+
const skill = await getById(skillId);
|
|
1542
|
+
if (!skill || skill.companyId !== companyId)
|
|
1543
|
+
return null;
|
|
1544
|
+
const status = await updateStatus(companyId, skillId);
|
|
1545
|
+
if (!status?.supported) {
|
|
1546
|
+
throw unprocessable(status?.reason ?? "This skill does not support updates.");
|
|
1547
|
+
}
|
|
1548
|
+
if (!skill.sourceLocator) {
|
|
1549
|
+
throw unprocessable("Skill source locator is missing.");
|
|
1550
|
+
}
|
|
1551
|
+
const result = await readUrlSkillImports(companyId, skill.sourceLocator, skill.slug);
|
|
1552
|
+
const matching = result.skills.find((entry) => entry.key === skill.key) ?? result.skills[0] ?? null;
|
|
1553
|
+
if (!matching) {
|
|
1554
|
+
throw unprocessable(`Skill ${skill.key} could not be re-imported from its source.`);
|
|
1555
|
+
}
|
|
1556
|
+
const imported = await upsertImportedSkills(companyId, [matching]);
|
|
1557
|
+
return imported[0] ?? null;
|
|
1558
|
+
}
|
|
1559
|
+
async function scanProjectWorkspaces(companyId, input = {}) {
|
|
1560
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1561
|
+
const projectRows = input.projectIds?.length
|
|
1562
|
+
? await projects.listByIds(companyId, input.projectIds)
|
|
1563
|
+
: await projects.list(companyId);
|
|
1564
|
+
const workspaceFilter = new Set(input.workspaceIds ?? []);
|
|
1565
|
+
const skipped = [];
|
|
1566
|
+
const conflicts = [];
|
|
1567
|
+
const warnings = [];
|
|
1568
|
+
const imported = [];
|
|
1569
|
+
const updated = [];
|
|
1570
|
+
const availableSkills = await listFull(companyId);
|
|
1571
|
+
const acceptedSkills = [...availableSkills];
|
|
1572
|
+
const acceptedByKey = new Map(acceptedSkills.map((skill) => [skill.key, skill]));
|
|
1573
|
+
const scanTargets = [];
|
|
1574
|
+
const scannedProjectIds = new Set();
|
|
1575
|
+
let discovered = 0;
|
|
1576
|
+
const trackWarning = (message) => {
|
|
1577
|
+
warnings.push(message);
|
|
1578
|
+
return message;
|
|
1579
|
+
};
|
|
1580
|
+
const upsertAcceptedSkill = (skill) => {
|
|
1581
|
+
const nextIndex = acceptedSkills.findIndex((entry) => entry.id === skill.id || entry.key === skill.key);
|
|
1582
|
+
if (nextIndex >= 0)
|
|
1583
|
+
acceptedSkills[nextIndex] = skill;
|
|
1584
|
+
else
|
|
1585
|
+
acceptedSkills.push(skill);
|
|
1586
|
+
acceptedByKey.set(skill.key, skill);
|
|
1587
|
+
};
|
|
1588
|
+
for (const project of projectRows) {
|
|
1589
|
+
for (const workspace of project.workspaces) {
|
|
1590
|
+
if (workspaceFilter.size > 0 && !workspaceFilter.has(workspace.id))
|
|
1591
|
+
continue;
|
|
1592
|
+
const workspaceCwd = asString(workspace.cwd);
|
|
1593
|
+
if (!workspaceCwd) {
|
|
1594
|
+
skipped.push({
|
|
1595
|
+
projectId: project.id,
|
|
1596
|
+
projectName: project.name,
|
|
1597
|
+
workspaceId: workspace.id,
|
|
1598
|
+
workspaceName: workspace.name,
|
|
1599
|
+
path: null,
|
|
1600
|
+
reason: trackWarning(`Skipped ${project.name} / ${workspace.name}: no local workspace path is configured.`),
|
|
1601
|
+
});
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
const workspaceStat = await statPath(workspaceCwd);
|
|
1605
|
+
if (!workspaceStat?.isDirectory()) {
|
|
1606
|
+
skipped.push({
|
|
1607
|
+
projectId: project.id,
|
|
1608
|
+
projectName: project.name,
|
|
1609
|
+
workspaceId: workspace.id,
|
|
1610
|
+
workspaceName: workspace.name,
|
|
1611
|
+
path: workspaceCwd,
|
|
1612
|
+
reason: trackWarning(`Skipped ${project.name} / ${workspace.name}: local workspace path is not available at ${workspaceCwd}.`),
|
|
1613
|
+
});
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
scanTargets.push({
|
|
1617
|
+
projectId: project.id,
|
|
1618
|
+
projectName: project.name,
|
|
1619
|
+
workspaceId: workspace.id,
|
|
1620
|
+
workspaceName: workspace.name,
|
|
1621
|
+
workspaceCwd,
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
for (const target of scanTargets) {
|
|
1626
|
+
scannedProjectIds.add(target.projectId);
|
|
1627
|
+
const directories = await discoverProjectWorkspaceSkillDirectories(target);
|
|
1628
|
+
for (const directory of directories) {
|
|
1629
|
+
discovered += 1;
|
|
1630
|
+
let nextSkill;
|
|
1631
|
+
try {
|
|
1632
|
+
nextSkill = await readLocalSkillImportFromDirectory(companyId, directory.skillDir, {
|
|
1633
|
+
inventoryMode: directory.inventoryMode,
|
|
1634
|
+
metadata: {
|
|
1635
|
+
sourceKind: "project_scan",
|
|
1636
|
+
projectId: target.projectId,
|
|
1637
|
+
projectName: target.projectName,
|
|
1638
|
+
workspaceId: target.workspaceId,
|
|
1639
|
+
workspaceName: target.workspaceName,
|
|
1640
|
+
workspaceCwd: target.workspaceCwd,
|
|
1641
|
+
},
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
catch (error) {
|
|
1645
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1646
|
+
skipped.push({
|
|
1647
|
+
projectId: target.projectId,
|
|
1648
|
+
projectName: target.projectName,
|
|
1649
|
+
workspaceId: target.workspaceId,
|
|
1650
|
+
workspaceName: target.workspaceName,
|
|
1651
|
+
path: directory.skillDir,
|
|
1652
|
+
reason: trackWarning(`Skipped ${directory.skillDir}: ${message}`),
|
|
1653
|
+
});
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
const normalizedSourceDir = normalizeSourceLocatorDirectory(nextSkill.sourceLocator);
|
|
1657
|
+
const existingByKey = acceptedByKey.get(nextSkill.key) ?? null;
|
|
1658
|
+
if (existingByKey) {
|
|
1659
|
+
const existingSourceDir = normalizeSkillDirectory(existingByKey);
|
|
1660
|
+
if (existingByKey.sourceType !== "local_path"
|
|
1661
|
+
|| !existingSourceDir
|
|
1662
|
+
|| !normalizedSourceDir
|
|
1663
|
+
|| existingSourceDir !== normalizedSourceDir) {
|
|
1664
|
+
conflicts.push({
|
|
1665
|
+
slug: nextSkill.slug,
|
|
1666
|
+
key: nextSkill.key,
|
|
1667
|
+
projectId: target.projectId,
|
|
1668
|
+
projectName: target.projectName,
|
|
1669
|
+
workspaceId: target.workspaceId,
|
|
1670
|
+
workspaceName: target.workspaceName,
|
|
1671
|
+
path: directory.skillDir,
|
|
1672
|
+
existingSkillId: existingByKey.id,
|
|
1673
|
+
existingSkillKey: existingByKey.key,
|
|
1674
|
+
existingSourceLocator: existingByKey.sourceLocator,
|
|
1675
|
+
reason: `Skill key ${nextSkill.key} already points at ${existingByKey.sourceLocator ?? "another source"}.`,
|
|
1676
|
+
});
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
const persisted = (await upsertImportedSkills(companyId, [nextSkill]))[0];
|
|
1680
|
+
if (!persisted)
|
|
1681
|
+
continue;
|
|
1682
|
+
updated.push(persisted);
|
|
1683
|
+
upsertAcceptedSkill(persisted);
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
const slugConflict = acceptedSkills.find((skill) => {
|
|
1687
|
+
if (skill.slug !== nextSkill.slug)
|
|
1688
|
+
return false;
|
|
1689
|
+
return normalizeSkillDirectory(skill) !== normalizedSourceDir;
|
|
1690
|
+
});
|
|
1691
|
+
if (slugConflict) {
|
|
1692
|
+
conflicts.push({
|
|
1693
|
+
slug: nextSkill.slug,
|
|
1694
|
+
key: nextSkill.key,
|
|
1695
|
+
projectId: target.projectId,
|
|
1696
|
+
projectName: target.projectName,
|
|
1697
|
+
workspaceId: target.workspaceId,
|
|
1698
|
+
workspaceName: target.workspaceName,
|
|
1699
|
+
path: directory.skillDir,
|
|
1700
|
+
existingSkillId: slugConflict.id,
|
|
1701
|
+
existingSkillKey: slugConflict.key,
|
|
1702
|
+
existingSourceLocator: slugConflict.sourceLocator,
|
|
1703
|
+
reason: `Slug ${nextSkill.slug} is already in use by ${slugConflict.sourceLocator ?? slugConflict.key}.`,
|
|
1704
|
+
});
|
|
1705
|
+
continue;
|
|
1706
|
+
}
|
|
1707
|
+
const persisted = (await upsertImportedSkills(companyId, [nextSkill]))[0];
|
|
1708
|
+
if (!persisted)
|
|
1709
|
+
continue;
|
|
1710
|
+
imported.push(persisted);
|
|
1711
|
+
upsertAcceptedSkill(persisted);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
return {
|
|
1715
|
+
scannedProjects: scannedProjectIds.size,
|
|
1716
|
+
scannedWorkspaces: scanTargets.length,
|
|
1717
|
+
discovered,
|
|
1718
|
+
imported,
|
|
1719
|
+
updated,
|
|
1720
|
+
skipped,
|
|
1721
|
+
conflicts,
|
|
1722
|
+
warnings,
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
async function materializeCatalogSkillFiles(companyId, skill, normalizedFiles) {
|
|
1726
|
+
const packageDir = skill.packageDir ? normalizePortablePath(skill.packageDir) : null;
|
|
1727
|
+
if (!packageDir)
|
|
1728
|
+
return null;
|
|
1729
|
+
const catalogRoot = path.resolve(resolveManagedSkillsRoot(companyId), "__catalog__");
|
|
1730
|
+
const skillDir = path.resolve(catalogRoot, buildSkillRuntimeName(skill.key, skill.slug));
|
|
1731
|
+
await fs.rm(skillDir, { recursive: true, force: true });
|
|
1732
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
1733
|
+
for (const entry of skill.fileInventory) {
|
|
1734
|
+
const sourcePath = entry.path === "SKILL.md"
|
|
1735
|
+
? `${packageDir}/SKILL.md`
|
|
1736
|
+
: `${packageDir}/${entry.path}`;
|
|
1737
|
+
const content = normalizedFiles[sourcePath];
|
|
1738
|
+
if (typeof content !== "string")
|
|
1739
|
+
continue;
|
|
1740
|
+
const targetPath = path.resolve(skillDir, entry.path);
|
|
1741
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
1742
|
+
await fs.writeFile(targetPath, content, "utf8");
|
|
1743
|
+
}
|
|
1744
|
+
return skillDir;
|
|
1745
|
+
}
|
|
1746
|
+
async function materializeRuntimeSkillFiles(companyId, skill) {
|
|
1747
|
+
const runtimeRoot = path.resolve(resolveManagedSkillsRoot(companyId), "__runtime__");
|
|
1748
|
+
const skillDir = path.resolve(runtimeRoot, buildSkillRuntimeName(skill.key, skill.slug));
|
|
1749
|
+
await fs.rm(skillDir, { recursive: true, force: true });
|
|
1750
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
1751
|
+
for (const entry of skill.fileInventory) {
|
|
1752
|
+
const detail = await readFile(companyId, skill.id, entry.path).catch(() => null);
|
|
1753
|
+
if (!detail)
|
|
1754
|
+
continue;
|
|
1755
|
+
const targetPath = path.resolve(skillDir, entry.path);
|
|
1756
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
1757
|
+
await fs.writeFile(targetPath, detail.content, "utf8");
|
|
1758
|
+
}
|
|
1759
|
+
return skillDir;
|
|
1760
|
+
}
|
|
1761
|
+
function resolveRuntimeSkillMaterializedPath(companyId, skill) {
|
|
1762
|
+
const runtimeRoot = path.resolve(resolveManagedSkillsRoot(companyId), "__runtime__");
|
|
1763
|
+
return path.resolve(runtimeRoot, buildSkillRuntimeName(skill.key, skill.slug));
|
|
1764
|
+
}
|
|
1765
|
+
async function listRuntimeSkillEntries(companyId, options = {}) {
|
|
1766
|
+
const skills = await listFull(companyId);
|
|
1767
|
+
const out = [];
|
|
1768
|
+
for (const skill of skills) {
|
|
1769
|
+
const sourceKind = asString(getSkillMeta(skill).sourceKind);
|
|
1770
|
+
let source = normalizeSkillDirectory(skill);
|
|
1771
|
+
if (!source) {
|
|
1772
|
+
source = options.materializeMissing === false
|
|
1773
|
+
? resolveRuntimeSkillMaterializedPath(companyId, skill)
|
|
1774
|
+
: await materializeRuntimeSkillFiles(companyId, skill).catch(() => null);
|
|
1775
|
+
}
|
|
1776
|
+
if (!source)
|
|
1777
|
+
continue;
|
|
1778
|
+
const required = sourceKind === "fidelios_bundled";
|
|
1779
|
+
out.push({
|
|
1780
|
+
key: skill.key,
|
|
1781
|
+
runtimeName: buildSkillRuntimeName(skill.key, skill.slug),
|
|
1782
|
+
source,
|
|
1783
|
+
required,
|
|
1784
|
+
requiredReason: required
|
|
1785
|
+
? "Bundled FideliOS skills are always available for local adapters."
|
|
1786
|
+
: null,
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
out.sort((left, right) => left.key.localeCompare(right.key));
|
|
1790
|
+
return out;
|
|
1791
|
+
}
|
|
1792
|
+
async function importPackageFiles(companyId, files, options) {
|
|
1793
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1794
|
+
const normalizedFiles = normalizePackageFileMap(files);
|
|
1795
|
+
const importedSkills = readInlineSkillImports(companyId, normalizedFiles);
|
|
1796
|
+
if (importedSkills.length === 0)
|
|
1797
|
+
return [];
|
|
1798
|
+
for (const skill of importedSkills) {
|
|
1799
|
+
if (skill.sourceType !== "catalog")
|
|
1800
|
+
continue;
|
|
1801
|
+
const materializedDir = await materializeCatalogSkillFiles(companyId, skill, normalizedFiles);
|
|
1802
|
+
if (materializedDir) {
|
|
1803
|
+
skill.sourceLocator = materializedDir;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
const conflictStrategy = options?.onConflict ?? "replace";
|
|
1807
|
+
const existingSkills = await listFull(companyId);
|
|
1808
|
+
const existingByKey = new Map(existingSkills.map((skill) => [skill.key, skill]));
|
|
1809
|
+
const existingBySlug = new Map(existingSkills.map((skill) => [normalizeSkillSlug(skill.slug) ?? skill.slug, skill]));
|
|
1810
|
+
const usedSlugs = new Set(existingBySlug.keys());
|
|
1811
|
+
const usedKeys = new Set(existingByKey.keys());
|
|
1812
|
+
const toPersist = [];
|
|
1813
|
+
const prepared = [];
|
|
1814
|
+
const out = [];
|
|
1815
|
+
for (const importedSkill of importedSkills) {
|
|
1816
|
+
const originalKey = importedSkill.key;
|
|
1817
|
+
const originalSlug = importedSkill.slug;
|
|
1818
|
+
const normalizedSlug = normalizeSkillSlug(importedSkill.slug) ?? importedSkill.slug;
|
|
1819
|
+
const existingByIncomingKey = existingByKey.get(importedSkill.key) ?? null;
|
|
1820
|
+
const existingByIncomingSlug = existingBySlug.get(normalizedSlug) ?? null;
|
|
1821
|
+
const conflict = existingByIncomingKey ?? existingByIncomingSlug;
|
|
1822
|
+
if (!conflict || conflictStrategy === "replace") {
|
|
1823
|
+
toPersist.push(importedSkill);
|
|
1824
|
+
prepared.push({
|
|
1825
|
+
skill: importedSkill,
|
|
1826
|
+
originalKey,
|
|
1827
|
+
originalSlug,
|
|
1828
|
+
existingBefore: existingByIncomingKey,
|
|
1829
|
+
actionHint: existingByIncomingKey ? "updated" : "created",
|
|
1830
|
+
reason: existingByIncomingKey ? "Existing skill key matched; replace strategy." : null,
|
|
1831
|
+
});
|
|
1832
|
+
usedSlugs.add(normalizedSlug);
|
|
1833
|
+
usedKeys.add(importedSkill.key);
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
if (conflictStrategy === "skip") {
|
|
1837
|
+
out.push({
|
|
1838
|
+
skill: conflict,
|
|
1839
|
+
action: "skipped",
|
|
1840
|
+
originalKey,
|
|
1841
|
+
originalSlug,
|
|
1842
|
+
requestedRefs: Array.from(new Set([originalKey, originalSlug])),
|
|
1843
|
+
reason: "Existing skill matched; skip strategy.",
|
|
1844
|
+
});
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
const renamedSlug = uniqueSkillSlug(normalizedSlug || "skill", usedSlugs);
|
|
1848
|
+
const renamedKey = uniqueImportedSkillKey(companyId, renamedSlug, usedKeys);
|
|
1849
|
+
const renamedSkill = {
|
|
1850
|
+
...importedSkill,
|
|
1851
|
+
slug: renamedSlug,
|
|
1852
|
+
key: renamedKey,
|
|
1853
|
+
metadata: {
|
|
1854
|
+
...(importedSkill.metadata ?? {}),
|
|
1855
|
+
skillKey: renamedKey,
|
|
1856
|
+
importedFromSkillKey: originalKey,
|
|
1857
|
+
importedFromSkillSlug: originalSlug,
|
|
1858
|
+
},
|
|
1859
|
+
};
|
|
1860
|
+
toPersist.push(renamedSkill);
|
|
1861
|
+
prepared.push({
|
|
1862
|
+
skill: renamedSkill,
|
|
1863
|
+
originalKey,
|
|
1864
|
+
originalSlug,
|
|
1865
|
+
existingBefore: null,
|
|
1866
|
+
actionHint: "created",
|
|
1867
|
+
reason: `Existing skill matched; renamed to ${renamedSlug}.`,
|
|
1868
|
+
});
|
|
1869
|
+
usedSlugs.add(renamedSlug);
|
|
1870
|
+
usedKeys.add(renamedKey);
|
|
1871
|
+
}
|
|
1872
|
+
if (toPersist.length === 0)
|
|
1873
|
+
return out;
|
|
1874
|
+
const persisted = await upsertImportedSkills(companyId, toPersist);
|
|
1875
|
+
for (let index = 0; index < prepared.length; index += 1) {
|
|
1876
|
+
const persistedSkill = persisted[index];
|
|
1877
|
+
const preparedSkill = prepared[index];
|
|
1878
|
+
if (!persistedSkill || !preparedSkill)
|
|
1879
|
+
continue;
|
|
1880
|
+
out.push({
|
|
1881
|
+
skill: persistedSkill,
|
|
1882
|
+
action: preparedSkill.actionHint,
|
|
1883
|
+
originalKey: preparedSkill.originalKey,
|
|
1884
|
+
originalSlug: preparedSkill.originalSlug,
|
|
1885
|
+
requestedRefs: Array.from(new Set([preparedSkill.originalKey, preparedSkill.originalSlug])),
|
|
1886
|
+
reason: preparedSkill.reason,
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
return out;
|
|
1890
|
+
}
|
|
1891
|
+
async function upsertImportedSkills(companyId, imported) {
|
|
1892
|
+
const out = [];
|
|
1893
|
+
for (const skill of imported) {
|
|
1894
|
+
const existing = await getByKey(companyId, skill.key);
|
|
1895
|
+
const existingMeta = existing ? getSkillMeta(existing) : {};
|
|
1896
|
+
const incomingMeta = skill.metadata && isPlainRecord(skill.metadata) ? skill.metadata : {};
|
|
1897
|
+
const incomingOwner = asString(incomingMeta.owner);
|
|
1898
|
+
const incomingRepo = asString(incomingMeta.repo);
|
|
1899
|
+
const incomingKind = asString(incomingMeta.sourceKind);
|
|
1900
|
+
if (existing
|
|
1901
|
+
&& existingMeta.sourceKind === "fidelios_bundled"
|
|
1902
|
+
&& incomingKind === "github"
|
|
1903
|
+
&& incomingOwner === "fidelios"
|
|
1904
|
+
&& incomingRepo === "fidelios") {
|
|
1905
|
+
out.push(existing);
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
const metadata = {
|
|
1909
|
+
...(skill.metadata ?? {}),
|
|
1910
|
+
skillKey: skill.key,
|
|
1911
|
+
};
|
|
1912
|
+
const values = {
|
|
1913
|
+
companyId,
|
|
1914
|
+
key: skill.key,
|
|
1915
|
+
slug: skill.slug,
|
|
1916
|
+
name: skill.name,
|
|
1917
|
+
description: skill.description,
|
|
1918
|
+
markdown: skill.markdown,
|
|
1919
|
+
sourceType: skill.sourceType,
|
|
1920
|
+
sourceLocator: skill.sourceLocator,
|
|
1921
|
+
sourceRef: skill.sourceRef,
|
|
1922
|
+
trustLevel: skill.trustLevel,
|
|
1923
|
+
compatibility: skill.compatibility,
|
|
1924
|
+
fileInventory: serializeFileInventory(skill.fileInventory),
|
|
1925
|
+
metadata,
|
|
1926
|
+
updatedAt: new Date(),
|
|
1927
|
+
};
|
|
1928
|
+
const row = existing
|
|
1929
|
+
? await db
|
|
1930
|
+
.update(companySkills)
|
|
1931
|
+
.set(values)
|
|
1932
|
+
.where(eq(companySkills.id, existing.id))
|
|
1933
|
+
.returning()
|
|
1934
|
+
.then((rows) => rows[0] ?? null)
|
|
1935
|
+
: await db
|
|
1936
|
+
.insert(companySkills)
|
|
1937
|
+
.values(values)
|
|
1938
|
+
.returning()
|
|
1939
|
+
.then((rows) => rows[0] ?? null);
|
|
1940
|
+
if (!row)
|
|
1941
|
+
throw notFound("Failed to persist company skill");
|
|
1942
|
+
out.push(toCompanySkill(row));
|
|
1943
|
+
}
|
|
1944
|
+
return out;
|
|
1945
|
+
}
|
|
1946
|
+
async function importFromSource(companyId, source) {
|
|
1947
|
+
await ensureSkillInventoryCurrent(companyId);
|
|
1948
|
+
const parsed = parseSkillImportSourceInput(source);
|
|
1949
|
+
const local = !/^https?:\/\//i.test(parsed.resolvedSource);
|
|
1950
|
+
const { skills, warnings } = local
|
|
1951
|
+
? {
|
|
1952
|
+
skills: (await readLocalSkillImports(companyId, parsed.resolvedSource))
|
|
1953
|
+
.filter((skill) => !parsed.requestedSkillSlug || skill.slug === parsed.requestedSkillSlug),
|
|
1954
|
+
warnings: parsed.warnings,
|
|
1955
|
+
}
|
|
1956
|
+
: await readUrlSkillImports(companyId, parsed.resolvedSource, parsed.requestedSkillSlug)
|
|
1957
|
+
.then((result) => ({
|
|
1958
|
+
skills: result.skills,
|
|
1959
|
+
warnings: [...parsed.warnings, ...result.warnings],
|
|
1960
|
+
}));
|
|
1961
|
+
const filteredSkills = parsed.requestedSkillSlug
|
|
1962
|
+
? skills.filter((skill) => skill.slug === parsed.requestedSkillSlug)
|
|
1963
|
+
: skills;
|
|
1964
|
+
if (filteredSkills.length === 0) {
|
|
1965
|
+
throw unprocessable(parsed.requestedSkillSlug
|
|
1966
|
+
? `Skill ${parsed.requestedSkillSlug} was not found in the provided source.`
|
|
1967
|
+
: "No skills were found in the provided source.");
|
|
1968
|
+
}
|
|
1969
|
+
// Override sourceType/sourceLocator for skills imported via skills.sh
|
|
1970
|
+
if (parsed.originalSkillsShUrl) {
|
|
1971
|
+
for (const skill of filteredSkills) {
|
|
1972
|
+
skill.sourceType = "skills_sh";
|
|
1973
|
+
skill.sourceLocator = parsed.originalSkillsShUrl;
|
|
1974
|
+
if (skill.metadata) {
|
|
1975
|
+
skill.metadata.sourceKind = "skills_sh";
|
|
1976
|
+
}
|
|
1977
|
+
skill.key = deriveCanonicalSkillKey(companyId, skill);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
const imported = await upsertImportedSkills(companyId, filteredSkills);
|
|
1981
|
+
return { imported, warnings };
|
|
1982
|
+
}
|
|
1983
|
+
async function deleteSkill(companyId, skillId) {
|
|
1984
|
+
const row = await db
|
|
1985
|
+
.select()
|
|
1986
|
+
.from(companySkills)
|
|
1987
|
+
.where(and(eq(companySkills.id, skillId), eq(companySkills.companyId, companyId)))
|
|
1988
|
+
.then((rows) => rows[0] ?? null);
|
|
1989
|
+
if (!row)
|
|
1990
|
+
return null;
|
|
1991
|
+
const skill = toCompanySkill(row);
|
|
1992
|
+
// Remove from any agent desiredSkills that reference this skill
|
|
1993
|
+
const agentRows = await agents.list(companyId);
|
|
1994
|
+
const allSkills = await listFull(companyId);
|
|
1995
|
+
for (const agent of agentRows) {
|
|
1996
|
+
const config = agent.adapterConfig;
|
|
1997
|
+
const preference = readFideliOSSkillSyncPreference(config);
|
|
1998
|
+
const referencesSkill = preference.desiredSkills.some((ref) => {
|
|
1999
|
+
const resolved = resolveSkillReference(allSkills, ref);
|
|
2000
|
+
return resolved.skill?.id === skillId;
|
|
2001
|
+
});
|
|
2002
|
+
if (referencesSkill) {
|
|
2003
|
+
const filtered = preference.desiredSkills.filter((ref) => {
|
|
2004
|
+
const resolved = resolveSkillReference(allSkills, ref);
|
|
2005
|
+
return resolved.skill?.id !== skillId;
|
|
2006
|
+
});
|
|
2007
|
+
await agents.update(agent.id, {
|
|
2008
|
+
adapterConfig: writeFideliOSSkillSyncPreference(config, filtered),
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
// Delete DB row
|
|
2013
|
+
await db
|
|
2014
|
+
.delete(companySkills)
|
|
2015
|
+
.where(eq(companySkills.id, skillId));
|
|
2016
|
+
// Clean up materialized runtime files
|
|
2017
|
+
await fs.rm(resolveRuntimeSkillMaterializedPath(companyId, skill), { recursive: true, force: true });
|
|
2018
|
+
return skill;
|
|
2019
|
+
}
|
|
2020
|
+
return {
|
|
2021
|
+
list,
|
|
2022
|
+
listFull,
|
|
2023
|
+
getById,
|
|
2024
|
+
getByKey,
|
|
2025
|
+
resolveRequestedSkillKeys: async (companyId, requestedReferences) => {
|
|
2026
|
+
const skills = await listFull(companyId);
|
|
2027
|
+
return resolveRequestedSkillKeysOrThrow(skills, requestedReferences);
|
|
2028
|
+
},
|
|
2029
|
+
detail,
|
|
2030
|
+
updateStatus,
|
|
2031
|
+
readFile,
|
|
2032
|
+
updateFile,
|
|
2033
|
+
createLocalSkill,
|
|
2034
|
+
deleteSkill,
|
|
2035
|
+
importFromSource,
|
|
2036
|
+
scanProjectWorkspaces,
|
|
2037
|
+
importPackageFiles,
|
|
2038
|
+
installUpdate,
|
|
2039
|
+
listRuntimeSkillEntries,
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
//# sourceMappingURL=company-skills.js.map
|