@soleri/core 9.14.4 → 9.16.7
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/data/flows/deliver.flow.yaml +11 -0
- package/data/flows/design.flow.yaml +4 -14
- package/data/flows/enhance.flow.yaml +10 -0
- package/data/flows/explore.flow.yaml +16 -0
- package/data/flows/fix.flow.yaml +1 -1
- package/data/flows/review.flow.yaml +13 -4
- package/dist/brain/brain.d.ts +9 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +11 -1
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +24 -0
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +1 -0
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/capabilities/chain-mapping.d.ts.map +1 -1
- package/dist/capabilities/chain-mapping.js +5 -4
- package/dist/capabilities/chain-mapping.js.map +1 -1
- package/dist/capabilities/registry.d.ts +6 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +3 -2
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/chat/chat-session.d.ts +6 -0
- package/dist/chat/chat-session.d.ts.map +1 -1
- package/dist/chat/chat-session.js +68 -17
- package/dist/chat/chat-session.js.map +1 -1
- package/dist/context/context-engine.js +1 -1
- package/dist/context/context-engine.js.map +1 -1
- package/dist/curator/curator.d.ts +6 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +138 -0
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/types.d.ts +10 -0
- package/dist/curator/types.d.ts.map +1 -1
- package/dist/engine/bin/soleri-engine.js +0 -0
- package/dist/engine/core-ops.d.ts.map +1 -1
- package/dist/engine/core-ops.js +38 -1
- package/dist/engine/core-ops.js.map +1 -1
- package/dist/flows/epilogue.d.ts +5 -1
- package/dist/flows/epilogue.d.ts.map +1 -1
- package/dist/flows/epilogue.js +11 -3
- package/dist/flows/epilogue.js.map +1 -1
- package/dist/flows/executor.d.ts.map +1 -1
- package/dist/flows/executor.js +13 -5
- package/dist/flows/executor.js.map +1 -1
- package/dist/flows/index.d.ts +1 -2
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js +1 -0
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/plan-builder.d.ts +17 -1
- package/dist/flows/plan-builder.d.ts.map +1 -1
- package/dist/flows/plan-builder.js +67 -6
- package/dist/flows/plan-builder.js.map +1 -1
- package/dist/flows/probes.d.ts +1 -1
- package/dist/flows/probes.d.ts.map +1 -1
- package/dist/flows/probes.js +15 -3
- package/dist/flows/probes.js.map +1 -1
- package/dist/flows/types.d.ts +47 -20
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/flows/types.js +6 -1
- package/dist/flows/types.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +10 -4
- package/dist/intake/content-classifier.d.ts.map +1 -1
- package/dist/intake/content-classifier.js +19 -5
- package/dist/intake/content-classifier.js.map +1 -1
- package/dist/intake/text-ingester.d.ts +18 -0
- package/dist/intake/text-ingester.d.ts.map +1 -1
- package/dist/intake/text-ingester.js +37 -13
- package/dist/intake/text-ingester.js.map +1 -1
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +28 -2
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/planning/planner-types.d.ts +2 -0
- package/dist/planning/planner-types.d.ts.map +1 -1
- package/dist/planning/planner.d.ts +4 -0
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +50 -4
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/playbook-executor.d.ts +10 -1
- package/dist/playbooks/playbook-executor.d.ts.map +1 -1
- package/dist/playbooks/playbook-executor.js +8 -2
- package/dist/playbooks/playbook-executor.js.map +1 -1
- package/dist/playbooks/playbook-types.d.ts +8 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -1
- package/dist/plugins/types.d.ts +2 -2
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +30 -0
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +60 -21
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts +11 -0
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +146 -37
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +38 -12
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
- package/dist/runtime/facades/brain-facade.js +16 -4
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/context-facade.d.ts.map +1 -1
- package/dist/runtime/facades/context-facade.js +9 -3
- package/dist/runtime/facades/context-facade.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +20 -7
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.js +40 -1
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +113 -4
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +24 -3
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts +21 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +132 -38
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +16 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/schema-helpers.d.ts.map +1 -1
- package/dist/runtime/schema-helpers.js +4 -0
- package/dist/runtime/schema-helpers.js.map +1 -1
- package/dist/runtime/types.d.ts +19 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.js +16 -3
- package/dist/runtime/vault-linking-ops.js.map +1 -1
- package/dist/scheduler/cron-validator.d.ts +15 -0
- package/dist/scheduler/cron-validator.d.ts.map +1 -0
- package/dist/scheduler/cron-validator.js +93 -0
- package/dist/scheduler/cron-validator.js.map +1 -0
- package/dist/scheduler/platform-linux.d.ts +14 -0
- package/dist/scheduler/platform-linux.d.ts.map +1 -0
- package/dist/scheduler/platform-linux.js +107 -0
- package/dist/scheduler/platform-linux.js.map +1 -0
- package/dist/scheduler/platform-macos.d.ts +15 -0
- package/dist/scheduler/platform-macos.d.ts.map +1 -0
- package/dist/scheduler/platform-macos.js +131 -0
- package/dist/scheduler/platform-macos.js.map +1 -0
- package/dist/scheduler/scheduler-ops.d.ts +14 -0
- package/dist/scheduler/scheduler-ops.d.ts.map +1 -0
- package/dist/scheduler/scheduler-ops.js +77 -0
- package/dist/scheduler/scheduler-ops.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +144 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/types.d.ts +48 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +6 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/skills/sync-skills.d.ts +11 -0
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +132 -38
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/skills/validate-skills.d.ts +32 -0
- package/dist/skills/validate-skills.d.ts.map +1 -0
- package/dist/skills/validate-skills.js +396 -0
- package/dist/skills/validate-skills.js.map +1 -0
- package/dist/utils/worktree-reaper.d.ts +38 -0
- package/dist/utils/worktree-reaper.d.ts.map +1 -0
- package/dist/utils/worktree-reaper.js +85 -0
- package/dist/utils/worktree-reaper.js.map +1 -0
- package/dist/vault/default-canonical-tags.d.ts +15 -0
- package/dist/vault/default-canonical-tags.d.ts.map +1 -0
- package/dist/vault/default-canonical-tags.js +65 -0
- package/dist/vault/default-canonical-tags.js.map +1 -0
- package/dist/vault/scope-detector.d.ts.map +1 -1
- package/dist/vault/scope-detector.js +37 -4
- package/dist/vault/scope-detector.js.map +1 -1
- package/dist/vault/tag-normalizer.d.ts +42 -0
- package/dist/vault/tag-normalizer.d.ts.map +1 -0
- package/dist/vault/tag-normalizer.js +157 -0
- package/dist/vault/tag-normalizer.js.map +1 -0
- package/dist/vault/vault-entries.d.ts.map +1 -1
- package/dist/vault/vault-entries.js +3 -1
- package/dist/vault/vault-entries.js.map +1 -1
- package/package.json +5 -1
- package/src/__tests__/embeddings.test.ts +3 -3
- package/src/agency/agency-manager.test.ts +4 -4
- package/src/agency/default-rules.test.ts +0 -13
- package/src/brain/brain-intelligence.test.ts +0 -5
- package/src/brain/brain.ts +25 -1
- package/src/brain/intelligence.ts +25 -0
- package/src/brain/second-brain-features.test.ts +2 -14
- package/src/brain/types.ts +1 -0
- package/src/capabilities/chain-mapping.test.ts +1 -6
- package/src/capabilities/chain-mapping.ts +6 -4
- package/src/capabilities/registry.test.ts +1 -1
- package/src/capabilities/registry.ts +9 -2
- package/src/chat/agent-loop.test.ts +1 -1
- package/src/chat/chat-enhanced.test.ts +0 -8
- package/src/chat/chat-session.ts +75 -17
- package/src/chat/chat-transport.test.ts +31 -1
- package/src/claudemd/compose.test.ts +0 -5
- package/src/context/context-engine.test.ts +0 -1
- package/src/context/context-engine.ts +1 -1
- package/src/control/intent-router.test.ts +2 -2
- package/src/curator/curator.ts +180 -0
- package/src/curator/tag-manager.test.ts +0 -4
- package/src/curator/types.ts +10 -0
- package/src/domain-packs/types.test.ts +0 -5
- package/src/dream/dream.test.ts +0 -7
- package/src/enforcement/registry.test.ts +2 -2
- package/src/engine/core-ops.test.ts +4 -22
- package/src/engine/core-ops.ts +36 -1
- package/src/engine/module-manifest.test.ts +1 -31
- package/src/engine/register-engine.test.ts +3 -33
- package/src/errors/retry.test.ts +3 -1
- package/src/flows/chain-runner.test.ts +0 -6
- package/src/flows/context-router.test.ts +3 -3
- package/src/flows/epilogue.test.ts +40 -2
- package/src/flows/epilogue.ts +11 -2
- package/src/flows/executor.test.ts +48 -2
- package/src/flows/executor.ts +15 -5
- package/src/flows/index.ts +1 -3
- package/src/flows/plan-builder.test.ts +201 -0
- package/src/flows/plan-builder.ts +81 -5
- package/src/flows/probes.ts +17 -3
- package/src/flows/types.ts +31 -2
- package/src/health/health-registry.test.ts +3 -1
- package/src/index.ts +24 -0
- package/src/intake/content-classifier.ts +22 -4
- package/src/intake/dedup-gate.test.ts +2 -6
- package/src/intake/text-ingester.test.ts +3 -4
- package/src/intake/text-ingester.ts +61 -12
- package/src/llm/llm-client.test.ts +1 -1
- package/src/llm/utils.test.ts +1 -1
- package/src/migrations/migration-runner.test.ts +0 -1
- package/src/operator/operator-context-store.test.ts +0 -13
- package/src/operator/operator-profile.test.ts +2 -20
- package/src/packs/pack-installer.ts +28 -2
- package/src/packs/pack-system.test.ts +2 -2
- package/src/persona/defaults.test.ts +19 -19
- package/src/planning/gap-passes.test.ts +0 -46
- package/src/planning/gap-patterns.test.ts +0 -42
- package/src/planning/goal-ancestry.test.ts +3 -1
- package/src/planning/plan-lifecycle.test.ts +15 -7
- package/src/planning/planner-types.ts +2 -0
- package/src/planning/planner.test.ts +86 -90
- package/src/planning/planner.ts +56 -4
- package/src/planning/reconciliation-engine.test.ts +3 -10
- package/src/planning/task-complexity-assessor.test.ts +0 -5
- package/src/planning/task-verifier.test.ts +3 -1
- package/src/playbooks/generic/generic-playbooks.test.ts +0 -28
- package/src/playbooks/index.test.ts +0 -55
- package/src/playbooks/playbook-executor.test.ts +76 -0
- package/src/playbooks/playbook-executor.ts +24 -3
- package/src/playbooks/playbook-types.ts +8 -0
- package/src/plugins/plugin-registry.test.ts +6 -2
- package/src/project/project-registry.test.ts +2 -0
- package/src/queue/async-infrastructure.test.ts +6 -4
- package/src/queue/job-queue.test.ts +13 -7
- package/src/runtime/admin-extra-ops.test.ts +35 -30
- package/src/runtime/admin-extra-ops.ts +30 -0
- package/src/runtime/admin-ops.test.ts +0 -4
- package/src/runtime/admin-ops.ts +63 -21
- package/src/runtime/admin-setup-ops.test.ts +229 -13
- package/src/runtime/admin-setup-ops.ts +145 -36
- package/src/runtime/archive-ops.test.ts +0 -28
- package/src/runtime/branching-ops.test.ts +0 -17
- package/src/runtime/capture-ops.test.ts +41 -16
- package/src/runtime/capture-ops.ts +78 -46
- package/src/runtime/chain-ops.test.ts +0 -21
- package/src/runtime/facades/admin-facade.test.ts +0 -34
- package/src/runtime/facades/agency-facade.test.ts +0 -39
- package/src/runtime/facades/archive-facade.test.ts +0 -43
- package/src/runtime/facades/brain-facade.test.ts +8 -99
- package/src/runtime/facades/brain-facade.ts +29 -12
- package/src/runtime/facades/branching-facade.test.ts +30 -17
- package/src/runtime/facades/chat-facade.test.ts +0 -91
- package/src/runtime/facades/chat-service-ops.test.ts +0 -24
- package/src/runtime/facades/chat-session-ops.test.ts +0 -12
- package/src/runtime/facades/chat-transport-ops.test.ts +0 -23
- package/src/runtime/facades/context-facade.test.ts +0 -17
- package/src/runtime/facades/context-facade.ts +11 -4
- package/src/runtime/facades/control-facade.test.ts +0 -30
- package/src/runtime/facades/curator-facade.test.ts +0 -33
- package/src/runtime/facades/intake-facade.test.ts +0 -33
- package/src/runtime/facades/links-facade.test.ts +0 -37
- package/src/runtime/facades/loop-facade.test.ts +0 -26
- package/src/runtime/facades/memory-facade.test.ts +0 -18
- package/src/runtime/facades/memory-facade.ts +27 -11
- package/src/runtime/facades/operator-facade.test.ts +0 -31
- package/src/runtime/facades/orchestrate-facade.test.ts +0 -21
- package/src/runtime/facades/orchestrate-facade.ts +39 -1
- package/src/runtime/facades/plan-facade.test.ts +7 -32
- package/src/runtime/facades/plan-facade.ts +137 -4
- package/src/runtime/facades/review-facade.test.ts +1 -49
- package/src/runtime/facades/sync-facade.test.ts +24 -41
- package/src/runtime/facades/tier-facade.test.ts +30 -22
- package/src/runtime/facades/vault-facade.test.ts +0 -41
- package/src/runtime/facades/vault-facade.ts +26 -3
- package/src/runtime/grading-ops.test.ts +0 -27
- package/src/runtime/intake-ops.test.ts +0 -19
- package/src/runtime/loop-ops.test.ts +0 -48
- package/src/runtime/memory-cross-project-ops.test.ts +0 -14
- package/src/runtime/memory-extra-ops.test.ts +4 -8
- package/src/runtime/orchestrate-ops.test.ts +238 -19
- package/src/runtime/orchestrate-ops.ts +166 -41
- package/src/runtime/pack-ops.test.ts +0 -26
- package/src/runtime/planning-extra-ops.test.ts +2 -14
- package/src/runtime/playbook-ops-execution.test.ts +9 -20
- package/src/runtime/playbook-ops.test.ts +4 -67
- package/src/runtime/review-ops.test.ts +0 -15
- package/src/runtime/runtime.ts +18 -0
- package/src/runtime/schema-helpers.ts +4 -0
- package/src/runtime/sync-ops.test.ts +0 -18
- package/src/runtime/tier-ops.test.ts +0 -21
- package/src/runtime/types.ts +19 -0
- package/src/runtime/vault-extra-ops.test.ts +0 -12
- package/src/runtime/vault-linking-ops.test.ts +0 -4
- package/src/runtime/vault-linking-ops.ts +26 -8
- package/src/runtime/vault-sharing-ops.test.ts +0 -9
- package/src/scheduler/cron-validator.ts +101 -0
- package/src/scheduler/platform-linux.ts +122 -0
- package/src/scheduler/platform-macos.ts +150 -0
- package/src/scheduler/scheduler-ops.ts +77 -0
- package/src/scheduler/scheduler.test.ts +247 -0
- package/src/scheduler/scheduler.ts +174 -0
- package/src/scheduler/types.ts +52 -0
- package/src/skills/__tests__/sync-skills.test.ts +6 -17
- package/src/skills/global-claude-md.test.ts +113 -0
- package/src/skills/sync-skills.ts +143 -35
- package/src/skills/validate-skills.test.ts +206 -0
- package/src/skills/validate-skills.ts +470 -0
- package/src/telemetry/telemetry.test.ts +1 -0
- package/src/transport/http-server.test.ts +3 -0
- package/src/transport/session-manager.test.ts +3 -1
- package/src/transport/token-auth.test.ts +6 -9
- package/src/transport/ws-server.test.ts +10 -2
- package/src/utils/worktree-reaper.ts +113 -0
- package/src/vault/__tests__/vault-characterization.test.ts +0 -108
- package/src/vault/default-canonical-tags.ts +64 -0
- package/src/vault/linking.test.ts +0 -2
- package/src/vault/playbook.test.ts +4 -1
- package/src/vault/scope-detector.test.ts +3 -1
- package/src/vault/scope-detector.ts +42 -4
- package/src/vault/tag-normalizer.test.ts +214 -0
- package/src/vault/tag-normalizer.ts +188 -0
- package/src/vault/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +3 -1
- package/src/vault/vault.test.ts +23 -8
- package/dist/embeddings/index.d.ts +0 -5
- package/dist/embeddings/index.d.ts.map +0 -1
- package/dist/embeddings/index.js +0 -3
- package/dist/embeddings/index.js.map +0 -1
|
@@ -217,6 +217,82 @@ describe('PlaybookExecutor', () => {
|
|
|
217
217
|
const result = executor.complete(sessionId);
|
|
218
218
|
expect('error' in result).toBe(true);
|
|
219
219
|
});
|
|
220
|
+
|
|
221
|
+
// ── evidence source ──────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
describe('evidence source', () => {
|
|
224
|
+
function makePlaybookWithUserGate(): PlaybookDefinition {
|
|
225
|
+
return makePlaybook({
|
|
226
|
+
gates: [
|
|
227
|
+
{
|
|
228
|
+
phase: 'completion',
|
|
229
|
+
requirement: 'User confirmed result',
|
|
230
|
+
checkType: 'user-confirm',
|
|
231
|
+
requiresUserEvidence: true,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
it('agent-source evidence fails a requiresUserEvidence gate', () => {
|
|
238
|
+
const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
|
|
239
|
+
for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
|
|
240
|
+
|
|
241
|
+
const result = executor.complete(sessionId, {
|
|
242
|
+
gateResults: { 'user-confirm': { satisfied: true, source: 'agent' } },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect('error' in result).toBe(false);
|
|
246
|
+
if ('error' in result) return;
|
|
247
|
+
|
|
248
|
+
expect(result.gatesPassed).toBe(false);
|
|
249
|
+
expect(result.unsatisfiedGates[0]).toContain('user-confirm');
|
|
250
|
+
expect(result.unsatisfiedGates[0]).toContain('requires user confirmation');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('user-source evidence satisfies a requiresUserEvidence gate', () => {
|
|
254
|
+
const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
|
|
255
|
+
for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
|
|
256
|
+
|
|
257
|
+
const result = executor.complete(sessionId, {
|
|
258
|
+
gateResults: { 'user-confirm': { satisfied: true, source: 'user' } },
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
expect('error' in result).toBe(false);
|
|
262
|
+
if ('error' in result) return;
|
|
263
|
+
|
|
264
|
+
expect(result.gatesPassed).toBe(true);
|
|
265
|
+
expect(result.unsatisfiedGates).toHaveLength(0);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('plain boolean true satisfies a gate without requiresUserEvidence', () => {
|
|
269
|
+
const { sessionId, totalSteps } = executor.start(makePlaybook());
|
|
270
|
+
for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
|
|
271
|
+
|
|
272
|
+
const result = executor.complete(sessionId, {
|
|
273
|
+
gateResults: { 'test-pass': true },
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect('error' in result).toBe(false);
|
|
277
|
+
if ('error' in result) return;
|
|
278
|
+
expect(result.gatesPassed).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('plain boolean true does not satisfy a requiresUserEvidence gate', () => {
|
|
282
|
+
const { sessionId, totalSteps } = executor.start(makePlaybookWithUserGate());
|
|
283
|
+
for (let i = 0; i < totalSteps; i++) executor.step(sessionId);
|
|
284
|
+
|
|
285
|
+
// bare true = no source = treated as agent
|
|
286
|
+
const result = executor.complete(sessionId, {
|
|
287
|
+
gateResults: { 'user-confirm': true },
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
expect('error' in result).toBe(false);
|
|
291
|
+
if ('error' in result) return;
|
|
292
|
+
expect(result.gatesPassed).toBe(false);
|
|
293
|
+
expect(result.unsatisfiedGates[0]).toContain('requires user confirmation');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
220
296
|
});
|
|
221
297
|
|
|
222
298
|
// ─── getSession / listSessions ──────────────────────────────────
|
|
@@ -72,6 +72,16 @@ export interface CompleteResult {
|
|
|
72
72
|
duration: number;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Evidence record for a gate result.
|
|
77
|
+
* `source` distinguishes agent-collected evidence from explicit user confirmation.
|
|
78
|
+
*/
|
|
79
|
+
export interface GateEvidence {
|
|
80
|
+
satisfied: boolean;
|
|
81
|
+
/** Who produced the evidence. Defaults to 'agent' when omitted. */
|
|
82
|
+
source?: 'agent' | 'user';
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
// =============================================================================
|
|
76
86
|
// STEP PARSER
|
|
77
87
|
// =============================================================================
|
|
@@ -227,7 +237,7 @@ export class PlaybookExecutor {
|
|
|
227
237
|
*/
|
|
228
238
|
complete(
|
|
229
239
|
sessionId: string,
|
|
230
|
-
options?: { abort?: boolean; gateResults?: Record<string, boolean> },
|
|
240
|
+
options?: { abort?: boolean; gateResults?: Record<string, boolean | GateEvidence> },
|
|
231
241
|
): CompleteResult | { error: string } {
|
|
232
242
|
const session = this.sessions.get(sessionId);
|
|
233
243
|
if (!session) return { error: `Session not found: ${sessionId}` };
|
|
@@ -251,8 +261,19 @@ export class PlaybookExecutor {
|
|
|
251
261
|
const completionGates = session.gates.filter((g) => g.phase === 'completion');
|
|
252
262
|
const unsatisfiedGates: string[] = [];
|
|
253
263
|
for (const gate of completionGates) {
|
|
254
|
-
|
|
255
|
-
|
|
264
|
+
const raw = gateResults[gate.checkType];
|
|
265
|
+
const evidence: GateEvidence =
|
|
266
|
+
typeof raw === 'object' && raw !== null ? raw : { satisfied: !!raw, source: 'agent' };
|
|
267
|
+
|
|
268
|
+
const satisfied =
|
|
269
|
+
evidence.satisfied && !(gate.requiresUserEvidence && evidence.source !== 'user');
|
|
270
|
+
|
|
271
|
+
if (!satisfied) {
|
|
272
|
+
const reason =
|
|
273
|
+
gate.requiresUserEvidence && evidence.satisfied && evidence.source !== 'user'
|
|
274
|
+
? `${gate.checkType}: ${gate.requirement} (requires user confirmation)`
|
|
275
|
+
: `${gate.checkType}: ${gate.requirement}`;
|
|
276
|
+
unsatisfiedGates.push(reason);
|
|
256
277
|
}
|
|
257
278
|
}
|
|
258
279
|
|
|
@@ -48,6 +48,14 @@ export interface PlaybookGate {
|
|
|
48
48
|
requirement: string;
|
|
49
49
|
/** Check type to create/validate */
|
|
50
50
|
checkType: string;
|
|
51
|
+
/** Whether this gate blocks progression (blocking) or is advisory only (advisory). Defaults to 'blocking'. */
|
|
52
|
+
severity?: 'blocking' | 'advisory';
|
|
53
|
+
/**
|
|
54
|
+
* When true, only user-sourced evidence satisfies this gate.
|
|
55
|
+
* Agent-collected evidence (e.g. automated test runs) does not count.
|
|
56
|
+
* Defaults to false.
|
|
57
|
+
*/
|
|
58
|
+
requiresUserEvidence?: boolean;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
// =============================================================================
|
|
@@ -47,13 +47,15 @@ describe('PluginRegistry — colocated', () => {
|
|
|
47
47
|
|
|
48
48
|
describe('register', () => {
|
|
49
49
|
it('registers a plugin and sets initial status to registered', () => {
|
|
50
|
+
const before = Date.now();
|
|
50
51
|
const loaded = makeLoaded();
|
|
51
52
|
const registered = registry.register(loaded);
|
|
52
53
|
|
|
53
54
|
expect(registered.id).toBe('test-plugin');
|
|
54
55
|
expect(registered.status).toBe('registered');
|
|
55
56
|
expect(registered.facades).toEqual([]);
|
|
56
|
-
expect(registered.registeredAt).
|
|
57
|
+
expect(registered.registeredAt).toBeGreaterThanOrEqual(before);
|
|
58
|
+
expect(registered.registeredAt).toBeLessThanOrEqual(Date.now());
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
it('throws when registering duplicate id', () => {
|
|
@@ -86,11 +88,13 @@ describe('PluginRegistry — colocated', () => {
|
|
|
86
88
|
}),
|
|
87
89
|
});
|
|
88
90
|
|
|
91
|
+
const before = Date.now();
|
|
89
92
|
registry.register(loaded);
|
|
90
93
|
const result = await registry.activate('test-plugin', makeContext(loaded));
|
|
91
94
|
|
|
92
95
|
expect(result.status).toBe('active');
|
|
93
|
-
expect(result.activatedAt).
|
|
96
|
+
expect(result.activatedAt).toBeGreaterThanOrEqual(before);
|
|
97
|
+
expect(result.activatedAt).toBeLessThanOrEqual(Date.now());
|
|
94
98
|
expect(result.facades).toHaveLength(1);
|
|
95
99
|
expect(result.facades[0].name).toBe('my_facade');
|
|
96
100
|
expect(result.facades[0].ops).toHaveLength(2);
|
|
@@ -214,10 +214,12 @@ describe('ProjectRegistry', () => {
|
|
|
214
214
|
|
|
215
215
|
describe('register', () => {
|
|
216
216
|
it('creates a new project and returns it', () => {
|
|
217
|
+
const before = Date.now();
|
|
217
218
|
const proj = registry.register('/tmp/myproj', 'My Project');
|
|
218
219
|
expect(proj.path).toBe('/tmp/myproj');
|
|
219
220
|
expect(proj.name).toBe('My Project');
|
|
220
221
|
expect(proj.id).toBeTruthy();
|
|
222
|
+
expect(proj.registeredAt).toBeGreaterThanOrEqual(before);
|
|
221
223
|
expect(proj.registeredAt).toBeLessThanOrEqual(Date.now());
|
|
222
224
|
});
|
|
223
225
|
|
|
@@ -104,8 +104,7 @@ describe('JobQueue', () => {
|
|
|
104
104
|
|
|
105
105
|
it('enqueue creates a job and returns ID', () => {
|
|
106
106
|
const id = queue.enqueue('tag-normalize', { entryId: 'e1' });
|
|
107
|
-
expect(id).
|
|
108
|
-
expect(id.length).toBeGreaterThan(0);
|
|
107
|
+
expect(id).toHaveLength(12);
|
|
109
108
|
});
|
|
110
109
|
|
|
111
110
|
it('dequeue returns oldest pending job', () => {
|
|
@@ -200,16 +199,18 @@ describe('JobQueue', () => {
|
|
|
200
199
|
|
|
201
200
|
it('getStats returns correct counts', () => {
|
|
202
201
|
const stats = queue.getStats();
|
|
203
|
-
expect(stats.total).
|
|
202
|
+
expect(typeof stats.total).toBe('number');
|
|
204
203
|
expect(typeof stats.pending).toBe('number');
|
|
205
204
|
expect(typeof stats.running).toBe('number');
|
|
206
205
|
expect(typeof stats.completed).toBe('number');
|
|
207
206
|
expect(typeof stats.failed).toBe('number');
|
|
207
|
+
expect(stats.total).toBe(stats.pending + stats.running + stats.completed + stats.failed);
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
it('purge removes old completed/failed jobs', () => {
|
|
211
211
|
// purge with 0 days should remove all completed/failed
|
|
212
212
|
const purged = queue.purge(0);
|
|
213
|
+
expect(typeof purged).toBe('number');
|
|
213
214
|
expect(purged).toBeGreaterThanOrEqual(0);
|
|
214
215
|
});
|
|
215
216
|
});
|
|
@@ -275,7 +276,8 @@ describe('PipelineRunner', () => {
|
|
|
275
276
|
const status = runner.getStatus();
|
|
276
277
|
expect(status.running).toBe(false); // Not started yet
|
|
277
278
|
expect(status.pollIntervalMs).toBe(100);
|
|
278
|
-
|
|
279
|
+
// At this point: process-test succeeded = 1 processed; flaky-type threw so it's retried, not counted
|
|
280
|
+
expect(status.jobsProcessed).toBe(1);
|
|
279
281
|
});
|
|
280
282
|
|
|
281
283
|
it('start/stop controls background polling', async () => {
|
|
@@ -131,11 +131,14 @@ describe('JobQueue', () => {
|
|
|
131
131
|
queue = new JobQueue(provider);
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
it('initializes the table on construction', () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
it('initializes the job_queue table on construction', () => {
|
|
135
|
+
// Verify the correct DDL was executed — not just that something was called
|
|
136
|
+
const ddl = (provider.execSql as ReturnType<typeof vi.fn>).mock.calls[0]?.[0] as
|
|
137
|
+
| string
|
|
138
|
+
| undefined;
|
|
139
|
+
expect(ddl).toContain('CREATE TABLE IF NOT EXISTS job_queue');
|
|
140
|
+
expect(ddl).toContain('status');
|
|
141
|
+
expect(ddl).toContain('retry_count');
|
|
139
142
|
});
|
|
140
143
|
|
|
141
144
|
describe('enqueue', () => {
|
|
@@ -281,7 +284,8 @@ describe('JobQueue', () => {
|
|
|
281
284
|
queue.dequeue();
|
|
282
285
|
queue.complete(id);
|
|
283
286
|
const stats = queue.getStats();
|
|
284
|
-
expect(stats.total).
|
|
287
|
+
expect(stats.total).toBe(stats.pending + stats.running + stats.completed + stats.failed);
|
|
288
|
+
expect(stats.completed).toBeGreaterThanOrEqual(1);
|
|
285
289
|
});
|
|
286
290
|
});
|
|
287
291
|
|
|
@@ -321,7 +325,9 @@ describe('JobQueue', () => {
|
|
|
321
325
|
queue.complete(id1);
|
|
322
326
|
queue.fail(id2, 'err');
|
|
323
327
|
const deleted = queue.purge(30);
|
|
324
|
-
|
|
328
|
+
// The mock purges all completed/failed — 2 were just created
|
|
329
|
+
expect(typeof deleted).toBe('number');
|
|
330
|
+
expect(deleted).toBeGreaterThanOrEqual(2);
|
|
325
331
|
});
|
|
326
332
|
});
|
|
327
333
|
});
|
|
@@ -95,19 +95,6 @@ describe('createAdminExtraOps', () => {
|
|
|
95
95
|
ops = createAdminExtraOps(runtime);
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
it('returns at least 24 ops', () => {
|
|
99
|
-
expect(ops.length).toBeGreaterThanOrEqual(24);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('all ops have name, description, auth, and handler', () => {
|
|
103
|
-
for (const op of ops) {
|
|
104
|
-
expect(op.name).toBeTruthy();
|
|
105
|
-
expect(op.description).toBeTruthy();
|
|
106
|
-
expect(['read', 'write', 'admin']).toContain(op.auth);
|
|
107
|
-
expect(typeof op.handler).toBe('function');
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
98
|
describe('admin_telemetry', () => {
|
|
112
99
|
it('returns telemetry stats', async () => {
|
|
113
100
|
const result = await findOp(ops, 'admin_telemetry').handler({});
|
|
@@ -441,7 +428,7 @@ describe('createAdminExtraOps', () => {
|
|
|
441
428
|
subsystem: 'nonexistent',
|
|
442
429
|
})) as Record<string, unknown>;
|
|
443
430
|
expect(result.error).toContain('Unknown subsystem');
|
|
444
|
-
expect(result.available).
|
|
431
|
+
expect(result.available).toEqual(['vault', 'brain']); // keys from mock snapshot.subsystems
|
|
445
432
|
});
|
|
446
433
|
});
|
|
447
434
|
|
|
@@ -481,33 +468,51 @@ describe('createAdminExtraOps', () => {
|
|
|
481
468
|
});
|
|
482
469
|
|
|
483
470
|
describe('admin_persistence_info', () => {
|
|
484
|
-
it('returns backend and table counts', async () => {
|
|
485
|
-
const result = (await findOp(ops, 'admin_persistence_info').handler({})) as
|
|
486
|
-
string
|
|
487
|
-
|
|
488
|
-
|
|
471
|
+
it('returns backend and table counts for each known table', async () => {
|
|
472
|
+
const result = (await findOp(ops, 'admin_persistence_info').handler({})) as {
|
|
473
|
+
backend: string;
|
|
474
|
+
tables: Record<string, number>;
|
|
475
|
+
};
|
|
489
476
|
expect(result.backend).toBe('sqlite');
|
|
490
|
-
|
|
477
|
+
// Mock provider.get returns { count: 42 } for every table
|
|
478
|
+
expect(result.tables).toEqual({
|
|
479
|
+
entries: 42,
|
|
480
|
+
entries_archive: 42,
|
|
481
|
+
memories: 42,
|
|
482
|
+
projects: 42,
|
|
483
|
+
brain_vocabulary: 42,
|
|
484
|
+
brain_feedback: 42,
|
|
485
|
+
});
|
|
491
486
|
});
|
|
492
487
|
});
|
|
493
488
|
|
|
494
489
|
describe('admin_setup_check', () => {
|
|
495
|
-
it('returns
|
|
496
|
-
const result = (await findOp(ops, 'admin_setup_check').handler({})) as
|
|
497
|
-
string
|
|
498
|
-
|
|
499
|
-
|
|
490
|
+
it('returns ready: true with vault, brain, llm, and health checks all passing', async () => {
|
|
491
|
+
const result = (await findOp(ops, 'admin_setup_check').handler({})) as {
|
|
492
|
+
agentId: string;
|
|
493
|
+
ready: boolean;
|
|
494
|
+
checks: Record<string, { ok: boolean }>;
|
|
495
|
+
};
|
|
500
496
|
expect(result.agentId).toBe('test-agent');
|
|
501
|
-
expect(result.
|
|
502
|
-
expect(
|
|
497
|
+
expect(result.ready).toBe(true);
|
|
498
|
+
expect(result.checks.vault.ok).toBe(true);
|
|
499
|
+
expect(result.checks.brain.ok).toBe(true);
|
|
500
|
+
expect(result.checks.llm.ok).toBe(true);
|
|
503
501
|
});
|
|
504
502
|
});
|
|
505
503
|
|
|
506
504
|
describe('admin_setup_run', () => {
|
|
507
|
-
it('runs setup actions', async () => {
|
|
508
|
-
const result = (await findOp(ops, 'admin_setup_run').handler({})) as
|
|
505
|
+
it('runs all three setup actions and returns their names', async () => {
|
|
506
|
+
const result = (await findOp(ops, 'admin_setup_run').handler({})) as {
|
|
507
|
+
setup: boolean;
|
|
508
|
+
actions: string[];
|
|
509
|
+
};
|
|
509
510
|
expect(result.setup).toBe(true);
|
|
510
|
-
expect(
|
|
511
|
+
expect(result.actions).toEqual([
|
|
512
|
+
'brain_vocabulary_rebuilt',
|
|
513
|
+
'fts_index_rebuilt',
|
|
514
|
+
'templates_reloaded',
|
|
515
|
+
]);
|
|
511
516
|
});
|
|
512
517
|
});
|
|
513
518
|
});
|
|
@@ -849,5 +849,35 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
849
849
|
};
|
|
850
850
|
},
|
|
851
851
|
},
|
|
852
|
+
{
|
|
853
|
+
name: 'worktree_status',
|
|
854
|
+
description: 'List stale .claude/worktrees/ entries without removing them.',
|
|
855
|
+
auth: 'read' as const,
|
|
856
|
+
schema: z.object({
|
|
857
|
+
projectPath: z.string().optional().default('.'),
|
|
858
|
+
}),
|
|
859
|
+
handler: async (params) => {
|
|
860
|
+
const { worktreeStatus } = await import('../utils/worktree-reaper.js');
|
|
861
|
+
const { resolve } = await import('node:path');
|
|
862
|
+
const projectPath = resolve((params.projectPath as string) ?? '.');
|
|
863
|
+
const status = worktreeStatus(projectPath);
|
|
864
|
+
return { ...status, projectPath };
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
name: 'worktree_reap',
|
|
869
|
+
description: 'Remove stale .claude/worktrees/ entries and prune git worktree refs.',
|
|
870
|
+
auth: 'write' as const,
|
|
871
|
+
schema: z.object({
|
|
872
|
+
projectPath: z.string().optional().default('.'),
|
|
873
|
+
}),
|
|
874
|
+
handler: async (params) => {
|
|
875
|
+
const { worktreeReap } = await import('../utils/worktree-reaper.js');
|
|
876
|
+
const { resolve } = await import('node:path');
|
|
877
|
+
const projectPath = resolve((params.projectPath as string) ?? '.');
|
|
878
|
+
const report = worktreeReap(projectPath);
|
|
879
|
+
return { ...report, projectPath };
|
|
880
|
+
},
|
|
881
|
+
},
|
|
852
882
|
];
|
|
853
883
|
}
|
|
@@ -70,10 +70,6 @@ describe('createAdminOps', () => {
|
|
|
70
70
|
ops = createAdminOps(rt);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
it('returns ops array', () => {
|
|
74
|
-
expect(ops.length).toBeGreaterThan(0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
73
|
// ─── admin_health ─────────────────────────────────────────────
|
|
78
74
|
|
|
79
75
|
describe('admin_health', () => {
|
package/src/runtime/admin-ops.ts
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* runtime state. No new modules needed — uses existing runtime parts.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import { readFileSync, statSync, existsSync, readdirSync } from 'node:fs';
|
|
9
9
|
import { join, dirname } from 'node:path';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import type { OpDefinition } from '../facades/types.js';
|
|
12
13
|
import type { AgentRuntime } from './types.js';
|
|
@@ -147,20 +148,21 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
150
|
// Fallback — just describe admin ops
|
|
151
|
+
const adminOps = [
|
|
152
|
+
'admin_health',
|
|
153
|
+
'admin_tool_list',
|
|
154
|
+
'admin_config',
|
|
155
|
+
'admin_vault_size',
|
|
156
|
+
'admin_uptime',
|
|
157
|
+
'admin_version',
|
|
158
|
+
'admin_reset_cache',
|
|
159
|
+
'admin_diagnostic',
|
|
160
|
+
];
|
|
150
161
|
return {
|
|
151
|
-
count:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
'admin_tool_list',
|
|
156
|
-
'admin_config',
|
|
157
|
-
'admin_vault_size',
|
|
158
|
-
'admin_uptime',
|
|
159
|
-
'admin_version',
|
|
160
|
-
'admin_reset_cache',
|
|
161
|
-
'admin_diagnostic',
|
|
162
|
-
],
|
|
163
|
-
},
|
|
162
|
+
count: adminOps.length,
|
|
163
|
+
scope: 'admin-only',
|
|
164
|
+
note: 'Pass _allOps for full system op count',
|
|
165
|
+
ops: { admin: adminOps },
|
|
164
166
|
routing: buildRoutingHints(),
|
|
165
167
|
};
|
|
166
168
|
},
|
|
@@ -380,7 +382,7 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
380
382
|
});
|
|
381
383
|
}
|
|
382
384
|
|
|
383
|
-
// 7. Skills
|
|
385
|
+
// 7. Skills — check discovered vs registered in .claude/skills/
|
|
384
386
|
try {
|
|
385
387
|
const agentDir = runtime.config.agentDir;
|
|
386
388
|
const skillsDirs = agentDir ? [join(agentDir, 'skills')] : [];
|
|
@@ -388,12 +390,52 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
388
390
|
const installedPacks = packInstaller.list();
|
|
389
391
|
const packSkillCount = installedPacks.reduce((sum, p) => sum + p.skills.length, 0);
|
|
390
392
|
const totalSkills = agentSkills.length + packSkillCount;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
393
|
+
|
|
394
|
+
// Check registration status in .claude/skills/
|
|
395
|
+
const claudeSkillsDir = join(homedir(), '.claude', 'skills');
|
|
396
|
+
let registeredCount = 0;
|
|
397
|
+
let brokenCount = 0;
|
|
398
|
+
const unregistered: string[] = [];
|
|
399
|
+
|
|
400
|
+
if (existsSync(claudeSkillsDir)) {
|
|
401
|
+
try {
|
|
402
|
+
const registered = readdirSync(claudeSkillsDir, { withFileTypes: true });
|
|
403
|
+
registeredCount = registered.length;
|
|
404
|
+
for (const entry of registered) {
|
|
405
|
+
if (entry.isSymbolicLink()) {
|
|
406
|
+
try {
|
|
407
|
+
statSync(join(claudeSkillsDir, entry.name));
|
|
408
|
+
} catch {
|
|
409
|
+
brokenCount++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
} catch {
|
|
414
|
+
// Can't read .claude/skills/ — skip registration check
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const skill of agentSkills) {
|
|
419
|
+
const skillRegisteredDir = join(claudeSkillsDir, skill.name);
|
|
420
|
+
if (!existsSync(skillRegisteredDir)) {
|
|
421
|
+
unregistered.push(skill.name);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const hasIssues = unregistered.length > 0 || brokenCount > 0;
|
|
426
|
+
// Warn only when agentDir is set but no skills exist anywhere (local OR global)
|
|
427
|
+
const hasAnySkills = totalSkills > 0 || registeredCount > 0;
|
|
428
|
+
const skillStatus = !hasAnySkills && agentDir ? 'warn' : hasIssues ? 'warn' : 'ok';
|
|
429
|
+
const detail = [
|
|
430
|
+
`${totalSkills} discovered (${agentSkills.length} agent, ${packSkillCount} pack)`,
|
|
431
|
+
`${registeredCount} registered in .claude/skills/`,
|
|
432
|
+
...(unregistered.length > 0
|
|
433
|
+
? [`${unregistered.length} unregistered: ${unregistered.join(', ')}`]
|
|
434
|
+
: []),
|
|
435
|
+
...(brokenCount > 0 ? [`${brokenCount} broken links`] : []),
|
|
436
|
+
].join(' — ');
|
|
437
|
+
|
|
438
|
+
checks.push({ name: 'skills', status: skillStatus, detail });
|
|
397
439
|
} catch (err) {
|
|
398
440
|
checks.push({
|
|
399
441
|
name: 'skills',
|