@hasna/skills 0.1.62 → 0.1.64
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/README.md +38 -12
- package/bin/index.js +15743 -21709
- package/bin/mcp.js +3866 -11522
- package/bin/migrate.js +281 -35
- package/bin/server.js +5805 -2682
- package/bin/worker.js +5280 -2388
- package/dist/cli/commands/publish.d.ts +2 -0
- package/dist/cli/commands/registry-reconcile.d.ts +2 -0
- package/dist/cli/components/App.d.ts +2 -1
- package/dist/cli/components/CategorySelect.d.ts +2 -1
- package/dist/cli/components/Header.d.ts +2 -1
- package/dist/cli/components/InstallProgress.d.ts +2 -1
- package/dist/cli/components/SearchView.d.ts +2 -1
- package/dist/cli/components/SkillSelect.d.ts +2 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5386 -13739
- package/dist/lib/agent-sync.d.ts +61 -6
- package/dist/lib/auth-store.d.ts +37 -0
- package/dist/lib/config.d.ts +51 -0
- package/dist/lib/home-adoption.d.ts +91 -0
- package/dist/lib/home-census.d.ts +26 -0
- package/dist/lib/home-migration.d.ts +60 -0
- package/dist/lib/native-storage.d.ts +1 -1
- package/dist/lib/portable-skills-files.d.ts +21 -1
- package/dist/lib/portable-skills-types.d.ts +46 -0
- package/dist/lib/portable-skills.d.ts +35 -6
- package/dist/lib/pull.d.ts +97 -0
- package/dist/lib/registry-reconcile.d.ts +114 -0
- package/dist/lib/registry.d.ts +7 -4
- package/dist/lib/remote-client.d.ts +124 -1
- package/dist/lib/remote-registry.d.ts +26 -0
- package/dist/lib/revision.d.ts +29 -0
- package/dist/lib/run-state.d.ts +5 -0
- package/dist/lib/skill-bundles.d.ts +54 -0
- package/dist/lib/skill-contract.d.ts +35 -0
- package/dist/lib/skill-hash.d.ts +77 -0
- package/dist/sdk/cancel.d.ts +30 -0
- package/dist/sdk/dispatcher.d.ts +40 -0
- package/dist/sdk/event-sink.d.ts +2 -0
- package/dist/sdk/events.d.ts +79 -0
- package/dist/sdk/execution/admission.d.ts +44 -0
- package/dist/sdk/execution/dispatchers/e2b.d.ts +17 -0
- package/dist/sdk/execution/dispatchers/ecs.d.ts +151 -0
- package/dist/sdk/execution/image-profile.d.ts +73 -0
- package/dist/sdk/execution/index.d.ts +15 -0
- package/dist/sdk/execution/receipts.d.ts +44 -0
- package/dist/sdk/execution/state-machine.d.ts +38 -0
- package/dist/sdk/execution/storage.d.ts +158 -0
- package/dist/sdk/execution/types.d.ts +130 -0
- package/dist/sdk/executor.d.ts +17 -0
- package/dist/sdk/governance-store.d.ts +156 -0
- package/dist/sdk/governance.d.ts +92 -0
- package/dist/sdk/index.d.ts +26 -0
- package/dist/sdk/index.js +49728 -0
- package/dist/sdk/offline.d.ts +16 -0
- package/dist/sdk/outputs.d.ts +71 -0
- package/dist/sdk/registry.d.ts +28 -0
- package/dist/sdk/runs.d.ts +229 -0
- package/dist/sdk/server.d.ts +35 -0
- package/dist/sdk/spend.d.ts +36 -0
- package/dist/sdk/storage.d.ts +25 -0
- package/dist/server/app.d.ts +42 -0
- package/dist/server/artifact-storage.d.ts +102 -0
- package/dist/server/auth.d.ts +5 -0
- package/dist/server/config.d.ts +50 -0
- package/dist/server/database-url.d.ts +42 -0
- package/dist/server/handlers.d.ts +3 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/migrate.d.ts +13 -0
- package/dist/server/migrations-dir.d.ts +18 -0
- package/dist/server/redaction.d.ts +1 -0
- package/dist/server/registry.d.ts +5 -0
- package/dist/server/rows.d.ts +24 -0
- package/dist/server/skills-api.d.ts +170 -0
- package/dist/server/sqlite-store.d.ts +131 -0
- package/dist/server/store-fixtures.d.ts +25 -0
- package/dist/server/store.d.ts +170 -0
- package/dist/server/types.d.ts +337 -0
- package/dist/server/worker.d.ts +4 -0
- package/dist/storage.js +39 -2
- package/docs/skill-standard.md +211 -39
- package/migrations/postgres/0003_run_outputs_governance.sql +83 -0
- package/migrations/postgres/0004_hosted_pins.sql +28 -0
- package/migrations/postgres/0005_revision_tombstone_registry.sql +37 -0
- package/migrations/postgres/0005_tag_projection.sql +36 -0
- package/migrations/sqlite/0003_run_outputs_governance.sql +58 -0
- package/migrations/sqlite/0004_hosted_pins.sql +21 -0
- package/migrations/sqlite/0005_revision_tombstone_registry.sql +18 -0
- package/migrations/sqlite/0005_tag_projection.sql +25 -0
- package/package.json +12 -8
- package/schemas/skill.schema.json +181 -0
- package/agent-skills/README.md +0 -41
- package/agent-skills/fleet-package-rollout/SKILL.md +0 -169
- package/agent-skills/inbox/SKILL.md +0 -25
- package/agent-skills/inbox/scripts/inbox +0 -109
- package/agent-skills/inbox/tests/test_registered_sender_id.sh +0 -92
- package/agent-skills/inbox-monitor/SKILL.md +0 -41
- package/agent-skills/inbox-monitor/scripts/inbox_monitor.sh +0 -91
- package/agent-skills/inbox-monitor/scripts/test_inbox_monitor.sh +0 -71
- package/agent-skills/merge-pr/SKILL.md +0 -85
- package/agent-skills/merge-pr/references/merge-safety.md +0 -82
- package/agent-skills/merge-pr/scripts/merge_pr_guard.py +0 -694
- package/agent-skills/merge-pr/scripts/test_merge_pr_guard.py +0 -647
- package/agent-skills/merge-pr/tests/fixtures/multi-commit-synthesized.json +0 -28
- package/agent-skills/merge-pr/tests/fixtures/trailer-free-provider.json +0 -28
- package/agent-skills/skill-goal-execute/SKILL.md +0 -115
- package/agent-skills/skill-login/SKILL.md +0 -109
- package/agent-skills/skill-project-create/SKILL.md +0 -91
- package/agent-skills/skill-publish/SKILL.md +0 -254
- package/agent-skills/skill-publish/scripts/capture_registry.js +0 -47
- package/agent-skills/skill-publish/scripts/publish_with_git_head.sh +0 -143
- package/agent-skills/skill-publish/scripts/test_publish_with_git_head.sh +0 -231
- package/skills/_common/auth.ts +0 -45
- package/skills/_common/http-client.ts +0 -143
- package/skills/_common/index.ts +0 -26
- package/skills/_common/install.sh +0 -7
- package/skills/_common/installer.ts +0 -72
- package/skills/_common/skill-install.ts +0 -37
- package/skills/_common/vision.ts +0 -374
- package/skills/ad-creative-pack/SKILL.md +0 -55
- package/skills/ad-creative-pack/package.json +0 -10
- package/skills/analyze-data/CLAUDE.md +0 -16
- package/skills/analyze-data/SKILL.md +0 -112
- package/skills/analyze-data/package.json +0 -23
- package/skills/analyze-data/src/analysis.ts +0 -401
- package/skills/analyze-data/src/cli.ts +0 -66
- package/skills/analyze-data/src/formatters.ts +0 -235
- package/skills/analyze-data/src/index.ts +0 -74
- package/skills/analyze-data/src/logger.ts +0 -36
- package/skills/analyze-data/src/parsers.ts +0 -57
- package/skills/analyze-data/src/stats.ts +0 -151
- package/skills/analyze-data/src/types.ts +0 -85
- package/skills/analyze-data/tsconfig.json +0 -19
- package/skills/api-docs-portal/SKILL.md +0 -84
- package/skills/api-docs-portal/package.json +0 -35
- package/skills/api-docs-portal/src/index.ts +0 -919
- package/skills/api-docs-portal/tsconfig.json +0 -17
- package/skills/api-test-suite/CLAUDE.md +0 -19
- package/skills/api-test-suite/package.json +0 -27
- package/skills/api-test-suite/src/index.ts +0 -381
- package/skills/api-test-suite/tsconfig.json +0 -10
- package/skills/audio-extract/CLAUDE.md +0 -9
- package/skills/audio-extract/SKILL.md +0 -73
- package/skills/audio-extract/package.json +0 -23
- package/skills/audio-extract/src/index.ts +0 -323
- package/skills/audio-extract/tsconfig.json +0 -11
- package/skills/bio-sequence-tool/CLAUDE.md +0 -19
- package/skills/bio-sequence-tool/package.json +0 -27
- package/skills/bio-sequence-tool/src/index.ts +0 -85
- package/skills/bio-sequence-tool/tsconfig.json +0 -10
- package/skills/blog-article/CLAUDE.md +0 -12
- package/skills/blog-article/SKILL.md +0 -55
- package/skills/blog-article/package.json +0 -10
- package/skills/blog-article/tsconfig.json +0 -10
- package/skills/brand-assets/SKILL.md +0 -118
- package/skills/brand-assets/package.json +0 -35
- package/skills/brand-assets/src/cli.ts +0 -155
- package/skills/brand-assets/src/constants.ts +0 -8
- package/skills/brand-assets/src/extract.ts +0 -236
- package/skills/brand-assets/src/index.ts +0 -668
- package/skills/brand-assets/src/types.ts +0 -59
- package/skills/brand-assets/tsconfig.json +0 -17
- package/skills/brand-kit/SKILL.md +0 -54
- package/skills/brand-kit/package.json +0 -10
- package/skills/businessactivity/CLAUDE.md +0 -176
- package/skills/businessactivity/LICENSE +0 -192
- package/skills/businessactivity/README.md +0 -287
- package/skills/businessactivity/package.json +0 -25
- package/skills/businessactivity/scripts/publish.ts +0 -210
- package/skills/businessactivity/src/commands/activity.ts +0 -211
- package/skills/businessactivity/src/commands/function.ts +0 -227
- package/skills/businessactivity/src/commands/owner.ts +0 -193
- package/skills/businessactivity/src/commands/profile.ts +0 -121
- package/skills/businessactivity/src/commands/responsibility.ts +0 -264
- package/skills/businessactivity/src/commands/team.ts +0 -244
- package/skills/businessactivity/src/commands/workflow.ts +0 -359
- package/skills/businessactivity/src/index.ts +0 -3
- package/skills/businessactivity/src/lib/config.ts +0 -167
- package/skills/businessactivity/src/lib/output.ts +0 -76
- package/skills/businessactivity/src/lib/paths.ts +0 -79
- package/skills/businessactivity/src/lib/storage.ts +0 -95
- package/skills/businessactivity/src/types/index.ts +0 -86
- package/skills/businessactivity/tsconfig.json +0 -29
- package/skills/codefix/LICENSE +0 -192
- package/skills/codefix/README.md +0 -232
- package/skills/codefix/SKILL.md +0 -94
- package/skills/codefix/package.json +0 -21
- package/skills/codefix/src/fixers/batch.ts +0 -213
- package/skills/codefix/src/fixers/detector.ts +0 -160
- package/skills/codefix/src/fixers/index.ts +0 -7
- package/skills/codefix/src/fixers/runner.ts +0 -522
- package/skills/codefix/src/index-local.ts +0 -431
- package/skills/codefix/src/index.ts +0 -4
- package/skills/codefix/src/types.ts +0 -211
- package/skills/codefix/tsconfig.json +0 -15
- package/skills/commitpush/SKILL.md +0 -57
- package/skills/commitpush/package.json +0 -33
- package/skills/commitpush/src/index.ts +0 -34
- package/skills/commitpush/tsconfig.json +0 -17
- package/skills/commitpushpr/SKILL.md +0 -55
- package/skills/commitpushpr/package.json +0 -33
- package/skills/commitpushpr/src/index.ts +0 -34
- package/skills/commitpushpr/tsconfig.json +0 -17
- package/skills/compress-video/CLAUDE.md +0 -9
- package/skills/compress-video/SKILL.md +0 -79
- package/skills/compress-video/package.json +0 -23
- package/skills/compress-video/src/index.ts +0 -473
- package/skills/compress-video/tsconfig.json +0 -11
- package/skills/contract-review-report/SKILL.md +0 -52
- package/skills/contract-review-report/package.json +0 -10
- package/skills/customer-feedback-report/SKILL.md +0 -52
- package/skills/customer-feedback-report/package.json +0 -10
- package/skills/dashboard-builder/CLAUDE.md +0 -19
- package/skills/dashboard-builder/package.json +0 -29
- package/skills/dashboard-builder/src/index.ts +0 -150
- package/skills/dashboard-builder/tsconfig.json +0 -10
- package/skills/data-anonymizer/CLAUDE.md +0 -19
- package/skills/data-anonymizer/package.json +0 -27
- package/skills/data-anonymizer/src/index.ts +0 -183
- package/skills/data-anonymizer/tsconfig.json +0 -10
- package/skills/database-explorer/CLAUDE.md +0 -19
- package/skills/database-explorer/package.json +0 -29
- package/skills/database-explorer/src/index.ts +0 -95
- package/skills/database-explorer/tsconfig.json +0 -10
- package/skills/diff-viewer/CLAUDE.md +0 -19
- package/skills/diff-viewer/package.json +0 -30
- package/skills/diff-viewer/src/index.ts +0 -164
- package/skills/diff-viewer/tsconfig.json +0 -10
- package/skills/doc-read/SKILL.md +0 -45
- package/skills/doc-read/package.json +0 -29
- package/skills/doc-read/src/index.ts +0 -324
- package/skills/doc-read/tsconfig.json +0 -8
- package/skills/domainpurchase/LICENSE +0 -192
- package/skills/domainpurchase/SKILL.md +0 -29
- package/skills/domainpurchase/package.json +0 -30
- package/skills/domainpurchase/src/index.ts +0 -3
- package/skills/domainpurchase/tsconfig.json +0 -21
- package/skills/email-sequence/SKILL.md +0 -53
- package/skills/email-sequence/package.json +0 -10
- package/skills/experiment-power-calculator/CLAUDE.md +0 -19
- package/skills/experiment-power-calculator/package.json +0 -29
- package/skills/experiment-power-calculator/src/index.ts +0 -86
- package/skills/experiment-power-calculator/tsconfig.json +0 -10
- package/skills/extract-frames/CLAUDE.md +0 -19
- package/skills/extract-frames/package.json +0 -27
- package/skills/extract-frames/src/index.ts +0 -616
- package/skills/extract-frames/tsconfig.json +0 -10
- package/skills/file-organizer/CLAUDE.md +0 -19
- package/skills/file-organizer/package.json +0 -27
- package/skills/file-organizer/src/index.ts +0 -91
- package/skills/file-organizer/tsconfig.json +0 -10
- package/skills/folder-tree/CLAUDE.md +0 -19
- package/skills/folder-tree/package.json +0 -29
- package/skills/folder-tree/src/index.ts +0 -568
- package/skills/folder-tree/tsconfig.json +0 -10
- package/skills/form-filler/CLAUDE.md +0 -19
- package/skills/form-filler/package.json +0 -29
- package/skills/form-filler/src/index.ts +0 -81
- package/skills/form-filler/tsconfig.json +0 -10
- package/skills/generate-api-client/CLAUDE.md +0 -19
- package/skills/generate-api-client/package.json +0 -29
- package/skills/generate-api-client/src/generator.ts +0 -47
- package/skills/generate-api-client/src/index.ts +0 -92
- package/skills/generate-api-client/src/logger.ts +0 -34
- package/skills/generate-api-client/src/python-generator.ts +0 -245
- package/skills/generate-api-client/src/spec-loader.ts +0 -48
- package/skills/generate-api-client/src/type-mappers.ts +0 -79
- package/skills/generate-api-client/src/types.ts +0 -76
- package/skills/generate-api-client/src/typescript-generator.ts +0 -247
- package/skills/generate-api-client/tsconfig.json +0 -10
- package/skills/generate-chart/CLAUDE.md +0 -19
- package/skills/generate-chart/package.json +0 -27
- package/skills/generate-chart/src/index.ts +0 -510
- package/skills/generate-chart/tsconfig.json +0 -10
- package/skills/generate-dockerfile/CLAUDE.md +0 -19
- package/skills/generate-dockerfile/package.json +0 -27
- package/skills/generate-dockerfile/src/cli.ts +0 -78
- package/skills/generate-dockerfile/src/dockerfile-templates.ts +0 -392
- package/skills/generate-dockerfile/src/index.ts +0 -81
- package/skills/generate-dockerfile/src/logger.ts +0 -30
- package/skills/generate-dockerfile/src/project-detection.ts +0 -68
- package/skills/generate-dockerfile/src/support-files.ts +0 -171
- package/skills/generate-dockerfile/src/types.ts +0 -29
- package/skills/generate-dockerfile/tsconfig.json +0 -10
- package/skills/generate-env/CLAUDE.md +0 -19
- package/skills/generate-env/package.json +0 -29
- package/skills/generate-env/src/cli.ts +0 -85
- package/skills/generate-env/src/detection.ts +0 -161
- package/skills/generate-env/src/env-data.ts +0 -66
- package/skills/generate-env/src/generators.ts +0 -240
- package/skills/generate-env/src/index.ts +0 -120
- package/skills/generate-env/src/runtime.ts +0 -30
- package/skills/generate-env/src/types.ts +0 -32
- package/skills/generate-env/tsconfig.json +0 -10
- package/skills/generate-favicon/CLAUDE.md +0 -19
- package/skills/generate-favicon/package.json +0 -29
- package/skills/generate-favicon/src/index.ts +0 -421
- package/skills/generate-favicon/tsconfig.json +0 -10
- package/skills/generate-qrcode/CLAUDE.md +0 -19
- package/skills/generate-qrcode/package.json +0 -30
- package/skills/generate-qrcode/src/index.ts +0 -584
- package/skills/generate-qrcode/tsconfig.json +0 -10
- package/skills/generate-resume/CLAUDE.md +0 -19
- package/skills/generate-resume/package.json +0 -27
- package/skills/generate-resume/src/cli.ts +0 -157
- package/skills/generate-resume/src/index.ts +0 -178
- package/skills/generate-resume/src/parsers.ts +0 -95
- package/skills/generate-resume/src/renderers.ts +0 -538
- package/skills/generate-resume/src/security.ts +0 -39
- package/skills/generate-resume/src/types.ts +0 -86
- package/skills/generate-resume/tsconfig.json +0 -10
- package/skills/generate-sitemap/CLAUDE.md +0 -19
- package/skills/generate-sitemap/package.json +0 -27
- package/skills/generate-sitemap/src/index.ts +0 -626
- package/skills/generate-sitemap/tsconfig.json +0 -10
- package/skills/gif-maker/CLAUDE.md +0 -19
- package/skills/gif-maker/package.json +0 -30
- package/skills/gif-maker/src/index.ts +0 -600
- package/skills/gif-maker/tsconfig.json +0 -10
- package/skills/hook/LICENSE +0 -192
- package/skills/hook/README.md +0 -74
- package/skills/hook/SKILL.md +0 -85
- package/skills/hook/bunfig.toml +0 -5
- package/skills/hook/package.json +0 -28
- package/skills/hook/src/commands/create.ts +0 -112
- package/skills/hook/src/commands/events.ts +0 -37
- package/skills/hook/src/commands/validate.ts +0 -117
- package/skills/hook/src/index.ts +0 -118
- package/skills/hook/src/templates/root.ts +0 -196
- package/skills/hook/src/templates/source.ts +0 -647
- package/skills/hook/tsconfig.json +0 -18
- package/skills/http-server/CLAUDE.md +0 -19
- package/skills/http-server/package.json +0 -29
- package/skills/http-server/src/index.ts +0 -51
- package/skills/http-server/tsconfig.json +0 -10
- package/skills/implementation/LICENSE +0 -192
- package/skills/implementation/README.md +0 -53
- package/skills/implementation/SKILL.md +0 -57
- package/skills/implementation/bunfig.toml +0 -5
- package/skills/implementation/package.json +0 -28
- package/skills/implementation/src/commands/create.ts +0 -187
- package/skills/implementation/src/index.ts +0 -62
- package/skills/implementation/tsconfig.json +0 -18
- package/skills/implementation-plan/CLAUDE.md +0 -19
- package/skills/implementation-plan/package.json +0 -29
- package/skills/implementation-plan/src/index.ts +0 -520
- package/skills/implementation-plan/tsconfig.json +0 -10
- package/skills/implementation-todo/CLAUDE.md +0 -19
- package/skills/implementation-todo/package.json +0 -29
- package/skills/implementation-todo/src/index.ts +0 -429
- package/skills/implementation-todo/tsconfig.json +0 -10
- package/skills/invoice/.env.example +0 -3
- package/skills/invoice/CLAUDE.md +0 -31
- package/skills/invoice/LICENSE +0 -192
- package/skills/invoice/README.md +0 -70
- package/skills/invoice/package.json +0 -26
- package/skills/invoice/src/commands/companies.ts +0 -202
- package/skills/invoice/src/commands/config.ts +0 -132
- package/skills/invoice/src/commands/generate.ts +0 -177
- package/skills/invoice/src/commands/invoices.ts +0 -218
- package/skills/invoice/src/index.ts +0 -3
- package/skills/invoice/src/lib/api.ts +0 -124
- package/skills/invoice/src/lib/storage.ts +0 -171
- package/skills/invoice/src/types/index.ts +0 -86
- package/skills/invoice/src/utils/logger.ts +0 -33
- package/skills/invoice/src/utils/paths.ts +0 -25
- package/skills/invoice/tsconfig.json +0 -23
- package/skills/invoice-reconciliation/SKILL.md +0 -98
- package/skills/invoice-reconciliation/package.json +0 -36
- package/skills/invoice-reconciliation/src/index.ts +0 -743
- package/skills/invoice-reconciliation/tsconfig.json +0 -17
- package/skills/landing-page-pack/SKILL.md +0 -55
- package/skills/landing-page-pack/package.json +0 -10
- package/skills/latex-table-generator/CLAUDE.md +0 -19
- package/skills/latex-table-generator/package.json +0 -29
- package/skills/latex-table-generator/src/index.ts +0 -113
- package/skills/latex-table-generator/tsconfig.json +0 -10
- package/skills/logo-design/SKILL.md +0 -94
- package/skills/logo-design/package.json +0 -35
- package/skills/logo-design/src/cli.ts +0 -132
- package/skills/logo-design/src/constants.ts +0 -5
- package/skills/logo-design/src/docs.ts +0 -102
- package/skills/logo-design/src/geometry.ts +0 -324
- package/skills/logo-design/src/index.ts +0 -212
- package/skills/logo-design/src/palette.ts +0 -258
- package/skills/logo-design/src/types.ts +0 -50
- package/skills/logo-design/tsconfig.json +0 -17
- package/skills/lorem-generator/CLAUDE.md +0 -19
- package/skills/lorem-generator/package.json +0 -29
- package/skills/lorem-generator/src/index.ts +0 -59
- package/skills/lorem-generator/tsconfig.json +0 -10
- package/skills/managemcp/CLAUDE.md +0 -14
- package/skills/managemcp/README.md +0 -24
- package/skills/managemcp/package.json +0 -16
- package/skills/managemcp/src/index.ts +0 -3
- package/skills/managemcp/tsconfig.json +0 -27
- package/skills/markdown-validator/CLAUDE.md +0 -19
- package/skills/markdown-validator/package.json +0 -29
- package/skills/markdown-validator/src/index.ts +0 -92
- package/skills/markdown-validator/tsconfig.json +0 -10
- package/skills/market-research-report/SKILL.md +0 -51
- package/skills/market-research-report/package.json +0 -10
- package/skills/meeting-pack/SKILL.md +0 -52
- package/skills/meeting-pack/package.json +0 -10
- package/skills/merge-pdfs/CLAUDE.md +0 -19
- package/skills/merge-pdfs/package.json +0 -31
- package/skills/merge-pdfs/src/index.ts +0 -427
- package/skills/merge-pdfs/tsconfig.json +0 -10
- package/skills/migration-plan-pack/SKILL.md +0 -56
- package/skills/migration-plan-pack/package.json +0 -10
- package/skills/monitor/SKILL.md +0 -69
- package/skills/monitor/package.json +0 -33
- package/skills/monitor/src/index.ts +0 -34
- package/skills/monitor/tsconfig.json +0 -17
- package/skills/one-page-website/SKILL.md +0 -128
- package/skills/one-page-website/package.json +0 -34
- package/skills/one-page-website/src/design.ts +0 -336
- package/skills/one-page-website/src/docs.ts +0 -159
- package/skills/one-page-website/src/index.ts +0 -357
- package/skills/one-page-website/src/page.ts +0 -340
- package/skills/one-page-website/src/styles.ts +0 -508
- package/skills/one-page-website/src/types.ts +0 -78
- package/skills/one-page-website/tsconfig.json +0 -17
- package/skills/pdf-generate/CLAUDE.md +0 -19
- package/skills/pdf-generate/SKILL.md +0 -86
- package/skills/pdf-generate/package.json +0 -21
- package/skills/pdf-generate/src/cli.ts +0 -162
- package/skills/pdf-generate/src/html.ts +0 -84
- package/skills/pdf-generate/src/index.ts +0 -62
- package/skills/pdf-generate/src/markdown.ts +0 -27
- package/skills/pdf-generate/src/pdf-writer.ts +0 -78
- package/skills/pdf-generate/src/runtime.ts +0 -34
- package/skills/pdf-generate/src/security.ts +0 -37
- package/skills/pdf-generate/src/templates.ts +0 -311
- package/skills/pdf-generate/src/types.ts +0 -28
- package/skills/pdf-generate/tsconfig.json +0 -16
- package/skills/pdf-to-dataset/SKILL.md +0 -88
- package/skills/pdf-to-dataset/package.json +0 -37
- package/skills/pdf-to-dataset/src/index.ts +0 -790
- package/skills/pdf-to-dataset/tsconfig.json +0 -17
- package/skills/pdf-to-markdown/SKILL.md +0 -81
- package/skills/pdf-to-markdown/package.json +0 -35
- package/skills/pdf-to-markdown/src/index.ts +0 -694
- package/skills/pdf-to-markdown/tsconfig.json +0 -17
- package/skills/performance-audit-report/SKILL.md +0 -49
- package/skills/performance-audit-report/package.json +0 -10
- package/skills/pitch-deck/SKILL.md +0 -51
- package/skills/pitch-deck/package.json +0 -10
- package/skills/product-mockup/SKILL.md +0 -107
- package/skills/product-mockup/package.json +0 -32
- package/skills/product-mockup/src/cli.ts +0 -150
- package/skills/product-mockup/src/design.ts +0 -151
- package/skills/product-mockup/src/docs.ts +0 -134
- package/skills/product-mockup/src/index.ts +0 -182
- package/skills/product-mockup/src/render.ts +0 -407
- package/skills/product-mockup/src/types.ts +0 -66
- package/skills/product-mockup/tsconfig.json +0 -17
- package/skills/proposal-pack/SKILL.md +0 -53
- package/skills/proposal-pack/package.json +0 -10
- package/skills/read-csv/SKILL.md +0 -62
- package/skills/read-csv/package.json +0 -37
- package/skills/read-csv/src/index.ts +0 -356
- package/skills/read-csv/tsconfig.json +0 -17
- package/skills/read-excel/SKILL.md +0 -64
- package/skills/read-excel/package.json +0 -36
- package/skills/read-excel/src/index.ts +0 -266
- package/skills/read-excel/tsconfig.json +0 -17
- package/skills/regex-tester/CLAUDE.md +0 -19
- package/skills/regex-tester/package.json +0 -27
- package/skills/regex-tester/src/index.ts +0 -183
- package/skills/regex-tester/tsconfig.json +0 -10
- package/skills/repo-onboarding-report/SKILL.md +0 -52
- package/skills/repo-onboarding-report/package.json +0 -10
- package/skills/scancommitpr/SKILL.md +0 -178
- package/skills/scancommitpr/package.json +0 -34
- package/skills/scancommitpr/src/index.ts +0 -44
- package/skills/scancommitpr/tsconfig.json +0 -13
- package/skills/scancommitpush/SKILL.md +0 -174
- package/skills/scancommitpush/package.json +0 -32
- package/skills/scancommitpush/src/index.ts +0 -44
- package/skills/scancommitpush/tsconfig.json +0 -13
- package/skills/scientific-figure-check/CLAUDE.md +0 -19
- package/skills/scientific-figure-check/package.json +0 -29
- package/skills/scientific-figure-check/src/index.ts +0 -83
- package/skills/scientific-figure-check/tsconfig.json +0 -10
- package/skills/security-audit/CLAUDE.md +0 -19
- package/skills/security-audit/package.json +0 -27
- package/skills/security-audit/src/index.ts +0 -145
- package/skills/security-audit/tsconfig.json +0 -10
- package/skills/security-audit-report/SKILL.md +0 -52
- package/skills/security-audit-report/package.json +0 -10
- package/skills/seo-content-pack/SKILL.md +0 -54
- package/skills/seo-content-pack/package.json +0 -10
- package/skills/siteanalyze/SKILL.md +0 -25
- package/skills/siteanalyze/package.json +0 -27
- package/skills/siteanalyze/src/index.ts +0 -592
- package/skills/slide-deck-generator/SKILL.md +0 -93
- package/skills/slide-deck-generator/package.json +0 -38
- package/skills/slide-deck-generator/src/cli.ts +0 -133
- package/skills/slide-deck-generator/src/index.ts +0 -148
- package/skills/slide-deck-generator/src/model.ts +0 -307
- package/skills/slide-deck-generator/src/pptx.ts +0 -143
- package/skills/slide-deck-generator/src/render.ts +0 -248
- package/skills/slide-deck-generator/src/types.ts +0 -114
- package/skills/slide-deck-generator/src/utils.ts +0 -53
- package/skills/slide-deck-generator/tsconfig.json +0 -17
- package/skills/social-content-calendar/SKILL.md +0 -54
- package/skills/social-content-calendar/package.json +0 -10
- package/skills/split-pdf/CLAUDE.md +0 -19
- package/skills/split-pdf/package.json +0 -30
- package/skills/split-pdf/src/index.ts +0 -301
- package/skills/split-pdf/tsconfig.json +0 -10
- package/skills/statistical-test-selector/CLAUDE.md +0 -19
- package/skills/statistical-test-selector/package.json +0 -27
- package/skills/statistical-test-selector/src/index.ts +0 -75
- package/skills/statistical-test-selector/tsconfig.json +0 -10
- package/skills/test-suite-generator/SKILL.md +0 -51
- package/skills/test-suite-generator/package.json +0 -10
- package/skills/tmux-session/SKILL.md +0 -109
- package/skills/tmux-session/package.json +0 -33
- package/skills/tmux-session/src/index.ts +0 -34
- package/skills/tmux-session/tsconfig.json +0 -17
- package/skills/todos-plan/SKILL.md +0 -363
- package/skills/todos-plan/agents/openai.yaml +0 -4
- package/skills/todos-plan/package.json +0 -9
- package/skills/todos-plan/src/index.ts +0 -68
- package/skills/tsconfig.base.json +0 -12
- package/skills/validate-config/CLAUDE.md +0 -19
- package/skills/validate-config/package.json +0 -29
- package/skills/validate-config/src/index.ts +0 -56
- package/skills/validate-config/tsconfig.json +0 -10
- package/skills/video-downloader/CLAUDE.md +0 -19
- package/skills/video-downloader/package.json +0 -29
- package/skills/video-downloader/src/index.ts +0 -561
- package/skills/video-downloader/tsconfig.json +0 -10
- package/skills/video-highlight-pack/SKILL.md +0 -54
- package/skills/video-highlight-pack/package.json +0 -10
- package/skills/watermark/CLAUDE.md +0 -19
- package/skills/watermark/package.json +0 -32
- package/skills/watermark/src/index.ts +0 -587
- package/skills/watermark/tsconfig.json +0 -10
package/docs/skill-standard.md
CHANGED
|
@@ -17,9 +17,34 @@ Portable skills live in one folder each:
|
|
|
17
17
|
`skills port <path>` and `skills add <path>` copy an existing skill folder into
|
|
18
18
|
this layout and add missing standard files.
|
|
19
19
|
|
|
20
|
+
## The manifest split
|
|
21
|
+
|
|
22
|
+
Two metadata surfaces exist, with one rule: **portable metadata lives in
|
|
23
|
+
`skill.json`; consumer frontmatter stays minimal.**
|
|
24
|
+
|
|
25
|
+
- `skill.json` is the machine-readable manifest and the **source of truth**
|
|
26
|
+
for everything the fleet needs to know about a skill: identity, version,
|
|
27
|
+
runtime contract, provenance. It is the `hasna.skill.v1` contract defined
|
|
28
|
+
in `schemas/skill.schema.json` (published at
|
|
29
|
+
`https://hasna.dev/schemas/skill.v1.json`).
|
|
30
|
+
- `SKILL.md` is the agent-facing document. Its frontmatter carries only
|
|
31
|
+
`name` and `description` (plus `user_invocable: true` where a Claude
|
|
32
|
+
consumer renderer needs it). Everything else — `kind`, `version`,
|
|
33
|
+
`source`, `category`, `tags`, `displayName` — lives in `skill.json`.
|
|
34
|
+
See `docs/authoring-rule-amendment.md` for the proposed fleet rule text.
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
---
|
|
38
|
+
name: my-skill
|
|
39
|
+
description: What this skill does and when to use it.
|
|
40
|
+
---
|
|
41
|
+
```
|
|
42
|
+
|
|
20
43
|
## Skill Kinds
|
|
21
44
|
|
|
22
|
-
A skill declares its artifact class with the `kind`
|
|
45
|
+
A skill declares its artifact class with the `kind` field in `skill.json`
|
|
46
|
+
(`kind` may also appear in `SKILL.md` frontmatter for compatibility with
|
|
47
|
+
existing Codewith conventions):
|
|
23
48
|
|
|
24
49
|
- `kind: executable` (default when omitted) — a runnable skill folder with
|
|
25
50
|
`package.json`, a non-empty `bin`, and `src/index.ts`. `skills run` executes it.
|
|
@@ -33,45 +58,65 @@ A skill declares its artifact class with the `kind` frontmatter field:
|
|
|
33
58
|
Missing `kind` defaults to `executable` so the bundled corpus is unaffected.
|
|
34
59
|
Migrated operational (prose) skills should set `kind: instruction` explicitly.
|
|
35
60
|
|
|
36
|
-
Minimum `SKILL.md` frontmatter for an instruction skill:
|
|
37
|
-
|
|
38
|
-
```yaml
|
|
39
|
-
---
|
|
40
|
-
name: project
|
|
41
|
-
description: Open or resume an existing Hasna repo project using the projects CLI.
|
|
42
|
-
kind: instruction
|
|
43
|
-
version: 0.1.0
|
|
44
|
-
source: private
|
|
45
|
-
---
|
|
46
|
-
```
|
|
47
|
-
|
|
48
61
|
## Naming
|
|
49
62
|
|
|
50
63
|
Skill names are lowercase slugs: letters, numbers, dots, underscores, and
|
|
51
64
|
hyphens. The folder name, `SKILL.md` frontmatter `name`, `skill.json` `name`,
|
|
52
|
-
and `package.json` `name`
|
|
53
|
-
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
Every portable skill needs a
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
and `package.json` `name` must match.
|
|
66
|
+
|
|
67
|
+
## The `hasna.skill.v1` manifest
|
|
68
|
+
|
|
69
|
+
Every portable skill needs a `skill.json` manifest conforming to
|
|
70
|
+
`hasna.skill.v1` (schema: `schemas/skill.schema.json`). The templates always
|
|
71
|
+
emit a complete valid manifest; `skills validate` rejects one that is
|
|
72
|
+
incomplete, invalid, or whose `content_hash` does not match the bundle.
|
|
73
|
+
|
|
74
|
+
### Required fields
|
|
75
|
+
|
|
76
|
+
| field | type | meaning |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `standard` | string | `"hasna.skill.v1"` |
|
|
79
|
+
| `name` | string | Lowercase slug; matches the folder name |
|
|
80
|
+
| `description` | string | What this skill does and when to use it |
|
|
81
|
+
| `version` | string | Semver (e.g. `0.1.0`). Any content change requires a version bump |
|
|
82
|
+
| `runtime` | object | The runtime contract below |
|
|
83
|
+
| `provenance` | object | Source and integrity fields; `content_hash` is required |
|
|
84
|
+
|
|
85
|
+
### Optional fields (legacy-compatible)
|
|
86
|
+
|
|
87
|
+
| field | type | meaning |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `$schema` | string | `https://hasna.dev/schemas/skill.v1.json` |
|
|
90
|
+
| `displayName` | string | Human-facing name |
|
|
91
|
+
| `category` | string | Marketplace / registry category |
|
|
92
|
+
| `tags` | string[] | Searchable tags |
|
|
93
|
+
| `kind` | `"executable" \| "instruction"` | Artifact class |
|
|
94
|
+
| `inputs` | object[] | Declared inputs (`name`, `type`, optional `required`, `description`) |
|
|
95
|
+
| `commands` | object[] | Runnable commands (`name`, `entry`/`command`, optional `args`, `description`) |
|
|
96
|
+
|
|
97
|
+
### The runtime contract
|
|
98
|
+
|
|
99
|
+
| field | type | default | meaning |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `runtime` | `"bun" \| "node" \| "python3"` | *(required)* | Execution runtime. `bun` is the Hasna default |
|
|
102
|
+
| `version` | string | — | Optional runtime version constraint (e.g. `"22"`, `">=3.12"`) |
|
|
103
|
+
| `entrypoint` | string | `commands[0].entry` | Relative path to the runnable entrypoint |
|
|
104
|
+
| `timeout` | integer | `900` | Max execution seconds, **capped at 900** |
|
|
105
|
+
| `needs_network` | boolean | `false` | Whether execution requires network egress |
|
|
106
|
+
| `env` | string[] | `[]` | **Secret REFERENCE names only, never values** — uppercase identifiers such as `OPENAI_API_KEY` |
|
|
107
|
+
| `sandbox` | `"readonly-fs" \| "workspace-write" \| "full"` | `"readonly-fs"` | Filesystem access granted to execution |
|
|
108
|
+
| `system_deps` | string[] | `[]` | Allowlisted system binaries (see schema for the list) |
|
|
109
|
+
| `artifacts` | string[] | `[]` | Glob patterns of artifacts the skill may produce (e.g. `"out/**"`) |
|
|
110
|
+
|
|
111
|
+
### The provenance fields
|
|
112
|
+
|
|
113
|
+
| field | type | meaning |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `source_commit` | string | Git SHA of the source revision, or `"unknown"` for locally scaffolded skills |
|
|
116
|
+
| `content_hash` | string | **Self-referencing** canonical SHA-256 of the normalized bundle (see below). Required on every manifest |
|
|
117
|
+
| `changelog` | string | Pointer to changelog / release notes (relative path or URL) |
|
|
118
|
+
|
|
119
|
+
### Example
|
|
75
120
|
|
|
76
121
|
```json
|
|
77
122
|
{
|
|
@@ -80,6 +125,9 @@ Minimum `skill.json`:
|
|
|
80
125
|
"name": "my-skill",
|
|
81
126
|
"description": "What this skill does and when to use it.",
|
|
82
127
|
"version": "0.1.0",
|
|
128
|
+
"displayName": "My Skill",
|
|
129
|
+
"category": "Development Tools",
|
|
130
|
+
"tags": ["custom"],
|
|
83
131
|
"inputs": [
|
|
84
132
|
{
|
|
85
133
|
"name": "args",
|
|
@@ -95,13 +143,61 @@ Minimum `skill.json`:
|
|
|
95
143
|
"description": "Run my-skill.",
|
|
96
144
|
"args": ["...args"]
|
|
97
145
|
}
|
|
98
|
-
]
|
|
146
|
+
],
|
|
147
|
+
"runtime": {
|
|
148
|
+
"runtime": "bun",
|
|
149
|
+
"entrypoint": "src/index.ts",
|
|
150
|
+
"timeout": 900,
|
|
151
|
+
"needs_network": false,
|
|
152
|
+
"env": [],
|
|
153
|
+
"sandbox": "readonly-fs",
|
|
154
|
+
"system_deps": [],
|
|
155
|
+
"artifacts": []
|
|
156
|
+
},
|
|
157
|
+
"provenance": {
|
|
158
|
+
"source_commit": "unknown",
|
|
159
|
+
"content_hash": "<64 lowercase hex chars>",
|
|
160
|
+
"changelog": "CHANGELOG.md"
|
|
161
|
+
}
|
|
99
162
|
}
|
|
100
163
|
```
|
|
101
164
|
|
|
102
165
|
If `skill.json` is absent, the CLI can infer a portable manifest from
|
|
103
166
|
`SKILL.md` frontmatter plus `package.json` `bin`, but scaffolded and ported
|
|
104
|
-
skills
|
|
167
|
+
skills always keep a complete `skill.json` checked in. A manifest present
|
|
168
|
+
without a valid `content_hash` is rejected by `skills validate`.
|
|
169
|
+
|
|
170
|
+
## Canonical content hashing
|
|
171
|
+
|
|
172
|
+
`content_hash` is a **self-referencing** SHA-256 over the normalized skill
|
|
173
|
+
bundle:
|
|
174
|
+
|
|
175
|
+
1. **Coverage** — every file at the skill root (`SKILL.md`, `skill.json`,
|
|
176
|
+
`AGENTS.md`, `package.json`, `tsconfig.json`) plus every file under
|
|
177
|
+
`src/`, `scripts/`, `assets/`, and `references/`, recursively.
|
|
178
|
+
2. **Exclusions** — `node_modules`, `.git`, `dist`, `build`, `.turbo`,
|
|
179
|
+
dot-entries, and symlinks never enter the hash.
|
|
180
|
+
3. **Normalization** — line endings are normalized to LF; for `skill.json`
|
|
181
|
+
the manifest is **blank-canonicalized**: parsed, its own `content_hash`
|
|
182
|
+
field (top-level or under `provenance`) removed, and re-serialized with
|
|
183
|
+
sorted keys. Key order, CRLF, and the hash's own presence therefore never
|
|
184
|
+
change the digest.
|
|
185
|
+
4. **Determinism** — files are hashed in sorted relative-path order (posix
|
|
186
|
+
separators), each entry length-prefixed, so the digest is identical on
|
|
187
|
+
every platform for the same logical content.
|
|
188
|
+
5. **Verification** — `skills validate` recomputes the hash and rejects any
|
|
189
|
+
manifest whose declared `content_hash` does not match, with the computed
|
|
190
|
+
value in the error message.
|
|
191
|
+
|
|
192
|
+
### Versioning rule
|
|
193
|
+
|
|
194
|
+
**A content change requires a version bump; the same version with different
|
|
195
|
+
content is rejected.** Enforcement is structural: any content change alters
|
|
196
|
+
the canonical bundle, so the declared `content_hash` no longer matches and
|
|
197
|
+
`skills validate` fails (`contract.content_hash_mismatch`). Re-hashing
|
|
198
|
+
without a version bump is still a rejection signal for the registry/server
|
|
199
|
+
later, which pins by version + hash — same semver + different hash is
|
|
200
|
+
unpublishable.
|
|
105
201
|
|
|
106
202
|
## Agent Handoff
|
|
107
203
|
|
|
@@ -136,6 +232,11 @@ Validation checks:
|
|
|
136
232
|
|
|
137
233
|
- folder and name safety;
|
|
138
234
|
- `SKILL.md` frontmatter compatibility (including a valid `kind`);
|
|
235
|
+
- the `hasna.skill.v1` contract: `standard`, `name`, `description`, `version`
|
|
236
|
+
(semver), the runtime contract (runtime, timeout cap, sandbox, env reference
|
|
237
|
+
names, system-deps allowlist), and provenance;
|
|
238
|
+
- the canonical `content_hash`: present, well-formed, and matching the bundle
|
|
239
|
+
(`contract.content_hash_mismatch` on drift);
|
|
139
240
|
- `skill.json` standard, version, inputs, and commands (relaxed for
|
|
140
241
|
`kind: instruction`, which needs neither `commands`, `inputs`, nor `AGENTS.md`);
|
|
141
242
|
- `AGENTS.md` presence (executable skills only);
|
|
@@ -143,6 +244,8 @@ Validation checks:
|
|
|
143
244
|
optional for `kind: instruction`);
|
|
144
245
|
- no reserved files such as `.env` or symlinks.
|
|
145
246
|
|
|
247
|
+
An invalid manifest exits non-zero with the exact field errors.
|
|
248
|
+
|
|
146
249
|
## Porting Existing Skills
|
|
147
250
|
|
|
148
251
|
```bash
|
|
@@ -153,4 +256,73 @@ skills add ./old-skill --name new-name
|
|
|
153
256
|
Porting copies the folder into `~/.hasna/skills/<name>/`, skips generated and
|
|
154
257
|
dependency directories such as `node_modules`, `dist`, and `.git`, then adds or
|
|
155
258
|
normalizes `skill.json`, `AGENTS.md`, `package.json`, `tsconfig.json`, and an
|
|
156
|
-
entrypoint when they are missing.
|
|
259
|
+
entrypoint when they are missing. Missing contract fields (runtime defaults,
|
|
260
|
+
provenance) are filled and the `content_hash` is recomputed over the ported
|
|
261
|
+
bundle; unknown keys in an existing `skill.json` are preserved.
|
|
262
|
+
|
|
263
|
+
## Machine Layout
|
|
264
|
+
|
|
265
|
+
The skills app folder (`~/.hasna/skills/`, relocatable with
|
|
266
|
+
`$HASNA_SKILLS_DIR`) hosts the owner layout:
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
~/.hasna/skills/
|
|
270
|
+
├── skills/ canonical corpus cache — the sync source
|
|
271
|
+
├── logs/ run/sync logs (created lazily)
|
|
272
|
+
├── outputs/ run outputs (created lazily)
|
|
273
|
+
├── custom/ experiments, retained as-is
|
|
274
|
+
├── config.json
|
|
275
|
+
└── skills.db
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`skills/` replaces the older `installed/` corpus home, and legacy flat skill
|
|
279
|
+
dirs that predate `installed/` migrate into it. Both moves are opt-in and
|
|
280
|
+
idempotent:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
skills storage migrate # installed/ + legacy dirs -> skills/, creates logs/ + outputs/
|
|
284
|
+
skills storage migrate --dry-run # show what would move, write nothing
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Migration refuses to run against a non-empty `skills/` that carries no
|
|
288
|
+
migration record (`skills/.layout-migration.json`), and never touches
|
|
289
|
+
`custom/`. After a successful migration `skills sync` reads the corpus from
|
|
290
|
+
the new cache automatically.
|
|
291
|
+
|
|
292
|
+
### Unmarked-home adoption
|
|
293
|
+
|
|
294
|
+
Agent homes are full of skill directories the CLI never wrote (the ad-hoc
|
|
295
|
+
sed/scp/rsync era). Those carry no `.hasna-skills.json` marker and sync leaves
|
|
296
|
+
them alone by design. Adoption is the migration mode for that population:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
skills sync --adopt # dry-run: hash unmarked home skills vs the corpus
|
|
300
|
+
skills sync --adopt --apply # write markers for exact matches; ledger the rest
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Each unmarked home skill's `SKILL.md` is hashed (line endings normalized,
|
|
304
|
+
`user_invocable` stripped) and compared against the canonical corpus cache:
|
|
305
|
+
|
|
306
|
+
- exact match -> a marker is written and the dir is adopted;
|
|
307
|
+
- content differs -> recorded in `~/.hasna/skills/conflicts.json` (home, skill,
|
|
308
|
+
hash, canonical hash, mtime) and skipped — an unmarked dir is never
|
|
309
|
+
overwritten;
|
|
310
|
+
- no canonical entry -> reported as unknown and skipped.
|
|
311
|
+
|
|
312
|
+
Every written marker is listed in a rollback record under
|
|
313
|
+
`~/.hasna/skills/rollback/`. Nothing is ever deleted by adoption.
|
|
314
|
+
|
|
315
|
+
### Home drift census
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
skills sync --check # exits non-zero while drift exists
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Compares each existing agent home against the canonical corpus and lists
|
|
322
|
+
`missing-from-home`, `stray-in-home` (marked dir, no canonical entry), and
|
|
323
|
+
`diverged` (marked dir whose hash differs). Unmarked dirs are adoption
|
|
324
|
+
candidates, not drift. `skills diff <name>` and `skills outdated` use the same
|
|
325
|
+
home-vs-canonical comparison; the pinned-skill version comparison remains as a
|
|
326
|
+
subset. `skills sync --prune [--apply]` removes only marked-and-stray dirs,
|
|
327
|
+
recording each removal in the rollback store before it happens.
|
|
328
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
-- Run outputs governance: privacy, retention, cancellation fencing, spend.
|
|
2
|
+
--
|
|
3
|
+
-- Postgres dialect of 0003_run_outputs_governance. Read the SQLite file for why
|
|
4
|
+
-- the migration is additive; the rationale is not repeated here, only the
|
|
5
|
+
-- dialect differences are:
|
|
6
|
+
-- * timestamptz -> text holding a UTC ISO-8601 instant (expires_at, reconciled_at).
|
|
7
|
+
-- * text JSON -> jsonb (metadata_json).
|
|
8
|
+
--
|
|
9
|
+
-- SQLite has no row-level security; tenant isolation there rests on the
|
|
10
|
+
-- org-scoped predicates in the store and the service-layer negative tests. This
|
|
11
|
+
-- dialect additionally arms PostgreSQL RLS on the two tenant tables, so a
|
|
12
|
+
-- statement that never set the tenant context sees zero rows (fail closed):
|
|
13
|
+
--
|
|
14
|
+
-- CREATE POLICY ... USING (
|
|
15
|
+
-- org_id = current_setting('app.skills_org_id', true)
|
|
16
|
+
-- OR current_setting('app.skills_claim_context', true) = 'worker'
|
|
17
|
+
-- )
|
|
18
|
+
--
|
|
19
|
+
-- The first clause is the principal path: the store runs every tenant-facing
|
|
20
|
+
-- read under SET LOCAL app.skills_org_id = <org>, and an unset setting matches
|
|
21
|
+
-- nothing. The second clause is the worker path, stated as the deliberate
|
|
22
|
+
-- exception it is: skills-worker claims and finalises runs for every org on the
|
|
23
|
+
-- instance (claimNextRun is not org-scoped by design), so it writes under an
|
|
24
|
+
-- explicit claim context rather than a tenant context. Both clauses must be
|
|
25
|
+
-- present for the shipped server to work at all, and neither weakens the tenant
|
|
26
|
+
-- fence: a caller with no context and no claim role reads no tenant rows.
|
|
27
|
+
|
|
28
|
+
ALTER TABLE skills_runs ADD COLUMN lease_generation integer NOT NULL DEFAULT 0;
|
|
29
|
+
|
|
30
|
+
ALTER TABLE skills_artifacts ADD COLUMN visibility text NOT NULL DEFAULT 'private';
|
|
31
|
+
ALTER TABLE skills_artifacts ADD COLUMN expires_at timestamptz;
|
|
32
|
+
|
|
33
|
+
CREATE TABLE IF NOT EXISTS skills_lifecycle_receipts (
|
|
34
|
+
id text PRIMARY KEY,
|
|
35
|
+
kind text NOT NULL,
|
|
36
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
37
|
+
run_id text NOT NULL REFERENCES skills_runs(id) ON DELETE CASCADE,
|
|
38
|
+
artifact_id text,
|
|
39
|
+
requested_by text NOT NULL,
|
|
40
|
+
metadata_json jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
41
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
42
|
+
CHECK (kind IN ('delete','quarantine','cancel'))
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS skills_lifecycle_receipts_org_run_idx
|
|
46
|
+
ON skills_lifecycle_receipts (org_id, run_id);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS skills_credit_reservations (
|
|
49
|
+
id text PRIMARY KEY,
|
|
50
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
51
|
+
run_id text NOT NULL REFERENCES skills_runs(id) ON DELETE CASCADE,
|
|
52
|
+
estimated_cents integer NOT NULL,
|
|
53
|
+
actual_cents integer,
|
|
54
|
+
status text NOT NULL,
|
|
55
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
56
|
+
reconciled_at timestamptz,
|
|
57
|
+
CHECK (status IN ('reserved','charged','released'))
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE INDEX IF NOT EXISTS skills_credit_reservations_org_run_idx
|
|
61
|
+
ON skills_credit_reservations (org_id, run_id);
|
|
62
|
+
|
|
63
|
+
ALTER TABLE skills_runs ENABLE ROW LEVEL SECURITY;
|
|
64
|
+
ALTER TABLE skills_artifacts ENABLE ROW LEVEL SECURITY;
|
|
65
|
+
|
|
66
|
+
-- FORCE closes the owner exemption: Postgres exempts a table's owner from RLS,
|
|
67
|
+
-- and this instance runs migrations and the server as one role, so without
|
|
68
|
+
-- FORCE the fence would not bind the app path. With FORCE the store's
|
|
69
|
+
-- tenant/worker contexts apply to that role like any other.
|
|
70
|
+
ALTER TABLE skills_runs FORCE ROW LEVEL SECURITY;
|
|
71
|
+
ALTER TABLE skills_artifacts FORCE ROW LEVEL SECURITY;
|
|
72
|
+
|
|
73
|
+
CREATE POLICY skills_runs_tenant_isolation ON skills_runs
|
|
74
|
+
USING (
|
|
75
|
+
org_id = current_setting('app.skills_org_id', true)
|
|
76
|
+
OR current_setting('app.skills_claim_context', true) = 'worker'
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE POLICY skills_artifacts_tenant_isolation ON skills_artifacts
|
|
80
|
+
USING (
|
|
81
|
+
org_id = current_setting('app.skills_org_id', true)
|
|
82
|
+
OR current_setting('app.skills_claim_context', true) = 'worker'
|
|
83
|
+
);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- Hosted pins: the cloud becomes a source of truth for what the user selected.
|
|
2
|
+
--
|
|
3
|
+
-- Today a pin lives only in local project state (.skills/project.json); an
|
|
4
|
+
-- instance has no pins surface, so nothing a user selected is ever visible to
|
|
5
|
+
-- the server, to a second machine, or to another operator of the same org.
|
|
6
|
+
-- This table is that surface.
|
|
7
|
+
--
|
|
8
|
+
-- Row identity is (org_id, principal, slug): one org, one API key, one slug -
|
|
9
|
+
-- a pin is a fact about a specific principal's selection. `principal` stores
|
|
10
|
+
-- the api_keys.id of the API key that pinned (ApiPrincipal.apiKeyId), so two
|
|
11
|
+
-- API keys in the same org each have their own pin set, and two organizations
|
|
12
|
+
-- can pin the same slug without colliding. The task's UNIQUE(org,principal,slug)
|
|
13
|
+
-- is expressed as the composite primary key, which implies the same uniqueness
|
|
14
|
+
-- and is the row's natural identity - there is no separate id to mint.
|
|
15
|
+
--
|
|
16
|
+
-- Pins are metadata-only: slug plus a free-form metadata object. Nothing here
|
|
17
|
+
-- references skills_registry, so a pin may name a skill the org has not
|
|
18
|
+
-- published (a bundled skill, or one that will exist later) without a
|
|
19
|
+
-- foreign-key constraint choosing which of those futures is allowed.
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS skills_pins (
|
|
22
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
23
|
+
principal text NOT NULL,
|
|
24
|
+
slug text NOT NULL,
|
|
25
|
+
pinned_at timestamptz NOT NULL DEFAULT now(),
|
|
26
|
+
metadata_json jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
27
|
+
PRIMARY KEY (org_id, principal, slug)
|
|
28
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Revision identity, optimistic concurrency, and the tombstone contract for the hosted
|
|
2
|
+
-- registry (todos d061fcda, plan 8022d27f).
|
|
3
|
+
--
|
|
4
|
+
-- Three behaviours this migration makes possible, all in one table because they are
|
|
5
|
+
-- three properties of the same row:
|
|
6
|
+
--
|
|
7
|
+
-- 1. IMMUTABLE REVISION IDENTITY. revision_id is a sha-256 over the row's published
|
|
8
|
+
-- content (computed in TypeScript, src/lib/revision.ts) and revision_number is a
|
|
9
|
+
-- monotonic per-slug write counter. Together they let a client prove WHICH revision
|
|
10
|
+
-- it installed and let the server refuse a stale overwrite.
|
|
11
|
+
--
|
|
12
|
+
-- 2. OPTIMISTIC CONCURRENCY. publish/update require an If-Match carrying the current
|
|
13
|
+
-- revision_id; a mismatch (or a missing guard against an existing row) returns 409
|
|
14
|
+
-- instead of silently overwriting a newer revision. The guard is enforced in the
|
|
15
|
+
-- store's SQL (WHERE ... revision_id = <expected> on the upsert), never in
|
|
16
|
+
-- read-then-write JS, so two concurrent writers cannot both pass a stale check.
|
|
17
|
+
--
|
|
18
|
+
-- 3. TOMBSTONES. delete no longer drops the row: it stamps tombstoned_at +
|
|
19
|
+
-- tombstone_purge_after (delete time + the configured window). Reads within the
|
|
20
|
+
-- window answer 410 with the tombstone marker so a client's pull can reconcile
|
|
21
|
+
-- (remove the local copy); after the window a read or list purges the row and its
|
|
22
|
+
-- bundle. Re-publish over a tombstoned slug revives it as a fresh revision.
|
|
23
|
+
--
|
|
24
|
+
-- The columns are added by ALTER rather than by the 0002 drop-and-recreate pattern:
|
|
25
|
+
-- the registry can hold tenant rows now (publishing shipped in the 0002 change), so a
|
|
26
|
+
-- rebuild would be data loss. Rows that predate this migration get revision_id '' and
|
|
27
|
+
-- revision_number 0; the store backfills a content sha for them on first open, because
|
|
28
|
+
-- an empty revision id would make If-Match vacuous for legacy rows (every stale client
|
|
29
|
+
-- would "match" the same empty string and two concurrent writers could both land).
|
|
30
|
+
--
|
|
31
|
+
-- Hand-written parallel of migrations/sqlite/0005_revision_tombstone_registry.sql; the
|
|
32
|
+
-- dialect differences are the same documented set as 0001-0003 (timestamptz -> text).
|
|
33
|
+
|
|
34
|
+
ALTER TABLE skills_registry ADD COLUMN revision_id text NOT NULL DEFAULT '';
|
|
35
|
+
ALTER TABLE skills_registry ADD COLUMN revision_number integer NOT NULL DEFAULT 0;
|
|
36
|
+
ALTER TABLE skills_registry ADD COLUMN tombstoned_at timestamptz;
|
|
37
|
+
ALTER TABLE skills_registry ADD COLUMN tombstone_purge_after timestamptz;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- The hosted tag surface (T7) queries tag membership from this relational
|
|
2
|
+
-- projection of skills_registry.tags_json, in both dialects with one identical
|
|
3
|
+
-- shape. SQLite cannot index json_each() over the JSON column, and Postgres
|
|
4
|
+
-- rows written by the current path hold the array text inside a jsonb scalar
|
|
5
|
+
-- (bun's driver binds a JS string against a ::jsonb cast as a JSON string), so
|
|
6
|
+
-- neither dialect can serve an indexed array-expansion query directly. The
|
|
7
|
+
-- projection turns tag membership into a plain indexed lookup: the PRIMARY KEY
|
|
8
|
+
-- serves (org_id, slug) maintenance and the org_tag index serves (org_id, tag)
|
|
9
|
+
-- filters. The write paths keep it in step; the backfill below covers rows
|
|
10
|
+
-- that predate it.
|
|
11
|
+
-- No composite FK to skills_registry: the schema-parity parser cannot
|
|
12
|
+
-- represent a multi-column FK identically in both dialects, and every write
|
|
13
|
+
-- path here maintains the projection explicitly (publish/update replace its
|
|
14
|
+
-- rows, the tombstone purge removes them with the row), so the constraint
|
|
15
|
+
-- would be redundancy.
|
|
16
|
+
CREATE TABLE skills_tags (
|
|
17
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
18
|
+
slug text NOT NULL,
|
|
19
|
+
tag text NOT NULL,
|
|
20
|
+
PRIMARY KEY (org_id, slug, tag)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX skills_tags_org_tag_idx ON skills_tags (org_id, tag);
|
|
24
|
+
|
|
25
|
+
-- Backfill every existing row's tags. tags_json may be a real array or,
|
|
26
|
+
-- because of the jsonb-scalar write path, the array text inside a jsonb
|
|
27
|
+
-- string; both shapes are expanded here.
|
|
28
|
+
INSERT INTO skills_tags (org_id, slug, tag)
|
|
29
|
+
SELECT DISTINCT r.org_id, r.slug, t.tag
|
|
30
|
+
FROM skills_registry r
|
|
31
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(
|
|
32
|
+
CASE WHEN jsonb_typeof(r.tags_json) = 'array' THEN r.tags_json
|
|
33
|
+
WHEN jsonb_typeof(r.tags_json) = 'string' THEN (r.tags_json #>> '{}')::jsonb
|
|
34
|
+
ELSE '[]'::jsonb END
|
|
35
|
+
) AS t(tag)
|
|
36
|
+
WHERE t.tag <> '';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- Run outputs governance: privacy, retention, cancellation fencing, spend.
|
|
2
|
+
--
|
|
3
|
+
-- Three columns and two tables, all additive. skills_runs/skills_artifacts hold
|
|
4
|
+
-- tenant rows today, so this migration must not drop or rebuild them - the 0002
|
|
5
|
+
-- drop-and-recreate pattern was safe only while the tables had never had a writer.
|
|
6
|
+
--
|
|
7
|
+
-- * skills_artifacts.visibility - outputs are PRIVATE by default.
|
|
8
|
+
-- * skills_artifacts.expires_at - finite retention: set at write time from
|
|
9
|
+
-- the configured TTL, swept by the expiry
|
|
10
|
+
-- service, never open-ended.
|
|
11
|
+
-- * skills_runs.lease_generation - the cancellation fence. Each claim bumps
|
|
12
|
+
-- it; a worker that keeps writing with a
|
|
13
|
+
-- stale generation is rejected. Cancel bumps
|
|
14
|
+
-- it too, fencing the worker it interrupts.
|
|
15
|
+
--
|
|
16
|
+
-- skills_lifecycle_receipts is the immutable, append-only record of every
|
|
17
|
+
-- deletion, quarantine, and cancellation. There is no UPDATE or DELETE path in
|
|
18
|
+
-- the store for this table - a receipt is a fact that happened, not state to
|
|
19
|
+
-- correct.
|
|
20
|
+
--
|
|
21
|
+
-- skills_credit_reservations is the spend ledger: a reservation is made before
|
|
22
|
+
-- dispatch, reconciled once at terminal state (charged with the actual cost, or
|
|
23
|
+
-- released when the run used nothing).
|
|
24
|
+
|
|
25
|
+
ALTER TABLE skills_runs ADD COLUMN lease_generation integer NOT NULL DEFAULT 0;
|
|
26
|
+
|
|
27
|
+
ALTER TABLE skills_artifacts ADD COLUMN visibility text NOT NULL DEFAULT 'private';
|
|
28
|
+
ALTER TABLE skills_artifacts ADD COLUMN expires_at text;
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS skills_lifecycle_receipts (
|
|
31
|
+
id text PRIMARY KEY,
|
|
32
|
+
kind text NOT NULL,
|
|
33
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
34
|
+
run_id text NOT NULL REFERENCES skills_runs(id) ON DELETE CASCADE,
|
|
35
|
+
artifact_id text,
|
|
36
|
+
requested_by text NOT NULL,
|
|
37
|
+
metadata_json text NOT NULL DEFAULT '{}',
|
|
38
|
+
created_at text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
39
|
+
CHECK (kind IN ('delete','quarantine','cancel'))
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
CREATE INDEX IF NOT EXISTS skills_lifecycle_receipts_org_run_idx
|
|
43
|
+
ON skills_lifecycle_receipts (org_id, run_id);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS skills_credit_reservations (
|
|
46
|
+
id text PRIMARY KEY,
|
|
47
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
48
|
+
run_id text NOT NULL REFERENCES skills_runs(id) ON DELETE CASCADE,
|
|
49
|
+
estimated_cents integer NOT NULL,
|
|
50
|
+
actual_cents integer,
|
|
51
|
+
status text NOT NULL,
|
|
52
|
+
created_at text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
53
|
+
reconciled_at text,
|
|
54
|
+
CHECK (status IN ('reserved','charged','released'))
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS skills_credit_reservations_org_run_idx
|
|
58
|
+
ON skills_credit_reservations (org_id, run_id);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Hosted pins: the cloud becomes a source of truth for what the user selected.
|
|
2
|
+
--
|
|
3
|
+
-- SQLite dialect of 0004_hosted_pins. Read the Postgres file for the why; the
|
|
4
|
+
-- only differences here are the documented dialect mapping:
|
|
5
|
+
-- * timestamptz -> text holding a UTC ISO-8601 instant (pinned_at).
|
|
6
|
+
-- * jsonb -> text holding JSON (metadata_json).
|
|
7
|
+
--
|
|
8
|
+
-- No RLS: the tenant fence on this table is the org-scoped predicate in the
|
|
9
|
+
-- store, exactly as it is for skills_registry and skills_bundles. RLS was
|
|
10
|
+
-- armed in 0003 only for the run-output tables, where a worker path writes
|
|
11
|
+
-- outside a tenant context; pins are written and read only by the API under
|
|
12
|
+
-- the requesting principal's org, so there is no context-less writer to fence.
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS skills_pins (
|
|
15
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
16
|
+
principal text NOT NULL,
|
|
17
|
+
slug text NOT NULL,
|
|
18
|
+
pinned_at text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
19
|
+
metadata_json text NOT NULL DEFAULT '{}',
|
|
20
|
+
PRIMARY KEY (org_id, principal, slug)
|
|
21
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- SQLite dialect of 0005_revision_tombstone_registry.
|
|
2
|
+
--
|
|
3
|
+
-- Hand-written parallel of migrations/postgres/0005_revision_tombstone_registry.sql. Read
|
|
4
|
+
-- that file for why the registry gains revision_id/revision_number (immutable revision
|
|
5
|
+
-- identity), the optimistic-concurrency guard, and the tombstone contract; the rationale
|
|
6
|
+
-- is not repeated here, only the dialect differences are.
|
|
7
|
+
--
|
|
8
|
+
-- Deliberate, shape-preserving dialect differences (same set as 0001):
|
|
9
|
+
-- * timestamptz -> text holding a UTC ISO-8601 instant.
|
|
10
|
+
--
|
|
11
|
+
-- SQLite requires a non-null literal DEFAULT for a NOT NULL ADD COLUMN; the '' default is
|
|
12
|
+
-- the legacy-row marker the store's backfill (src/lib/revision.ts) replaces with a
|
|
13
|
+
-- content sha on first open, exactly as on Postgres.
|
|
14
|
+
|
|
15
|
+
ALTER TABLE skills_registry ADD COLUMN revision_id text NOT NULL DEFAULT '';
|
|
16
|
+
ALTER TABLE skills_registry ADD COLUMN revision_number integer NOT NULL DEFAULT 0;
|
|
17
|
+
ALTER TABLE skills_registry ADD COLUMN tombstoned_at text;
|
|
18
|
+
ALTER TABLE skills_registry ADD COLUMN tombstone_purge_after text;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- The hosted tag surface (T7) queries tag membership from this relational
|
|
2
|
+
-- projection of skills_registry.tags_json, in both dialects with one identical
|
|
3
|
+
-- shape. SQLite cannot index json_each() over the JSON column, so the tag
|
|
4
|
+
-- lookup becomes an indexed read here: the PRIMARY KEY serves (org_id, slug)
|
|
5
|
+
-- maintenance and the org_tag index serves (org_id, tag) filters. The write
|
|
6
|
+
-- paths keep it in step; the backfill below covers rows that predate it.
|
|
7
|
+
-- No composite FK to skills_registry: the schema-parity parser cannot
|
|
8
|
+
-- represent a multi-column FK identically in both dialects, and every write
|
|
9
|
+
-- path here maintains the projection explicitly (publish/update replace its
|
|
10
|
+
-- rows, the tombstone purge removes them with the row), so the constraint
|
|
11
|
+
-- would be redundancy.
|
|
12
|
+
CREATE TABLE skills_tags (
|
|
13
|
+
org_id text NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
14
|
+
slug text NOT NULL,
|
|
15
|
+
tag text NOT NULL,
|
|
16
|
+
PRIMARY KEY (org_id, slug, tag)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX skills_tags_org_tag_idx ON skills_tags (org_id, tag);
|
|
20
|
+
|
|
21
|
+
-- Backfill every existing row's tags from the stored JSON array.
|
|
22
|
+
INSERT INTO skills_tags (org_id, slug, tag)
|
|
23
|
+
SELECT DISTINCT r.org_id, r.slug, je.value
|
|
24
|
+
FROM skills_registry r, json_each(r.tags_json) AS je
|
|
25
|
+
WHERE je.value <> '';
|