vgxness 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/canonical-agent-manifest.js +8 -7
- package/dist/cli/cli-flags.js +3 -3
- package/dist/cli/cli-help.js +4 -4
- package/dist/cli/commands/agent-skill-dispatcher.js +10 -1
- package/dist/mcp/control-plane.js +5 -0
- package/dist/mcp/schema.js +1 -0
- package/dist/mcp/validation.js +6 -0
- package/dist/memory/sqlite/migrations/017_intent_signal_skill_targets.sql +42 -0
- package/dist/orchestrator/natural-language-planner.js +53 -8
- package/dist/skills/boot-seed.js +42 -0
- package/dist/skills/skill-resolver.js +6 -0
- package/dist/skills/skill-seed-service.js +39 -16
- package/docs/sdd-flow.es.md +403 -0
- package/docs/sdd-flow.md +403 -0
- package/package.json +1 -1
- package/seeds/skills/skill-seed-v1.json +73 -1
|
@@ -2,7 +2,7 @@ import { canonicalBehaviorContractVersion } from '../behavior/behavior-contract-
|
|
|
2
2
|
export const canonicalDefaultAgentName = 'vgxness-manager';
|
|
3
3
|
export const canonicalOpenCodeDefaultModel = 'openai/gpt-5.5';
|
|
4
4
|
export const canonicalOpenCodeManagerReasoningEffort = 'high';
|
|
5
|
-
export const canonicalPromptContractVersion =
|
|
5
|
+
export const canonicalPromptContractVersion = 11;
|
|
6
6
|
export const canonicalSddSubagentNames = [
|
|
7
7
|
'vgxness-sdd-explore',
|
|
8
8
|
'vgxness-sdd-propose',
|
|
@@ -78,7 +78,7 @@ function managerDefinition() {
|
|
|
78
78
|
builtIn: true,
|
|
79
79
|
name: canonicalDefaultAgentName,
|
|
80
80
|
description: 'Coordinates VGXNESS MCP state and SDD sub-agents while routing Tier 0-2 lightweight work, Tier 3 preflight validation, and Tier 4 formal SDD.',
|
|
81
|
-
instructions: { kind: 'inline', value:
|
|
81
|
+
instructions: { kind: 'inline', value: registryManagerInstructionsV11 },
|
|
82
82
|
capabilities: ['sdd-orchestration', 'agent-routing', 'mcp-coordination', 'project-local-automation'],
|
|
83
83
|
permissions: { read: 'allow', edit: 'ask', shell: 'ask', git: 'ask', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' },
|
|
84
84
|
memory: { scopes: ['project'] },
|
|
@@ -166,7 +166,7 @@ Coordinate SDD; keep chat thin, use VGXNESS MCP as durable state, delegate to sm
|
|
|
166
166
|
Use the lightest safe path: Tier 0-2 direct; Tier 3 preflight; Tier 4 formal SDD for governance, permissions, acceptance, architecture/security, or cross-surface behavior. Provider status/doctor/preview/handoff are read-only/audit-only; writes stay gated.
|
|
167
167
|
|
|
168
168
|
## Provider-native daily flow
|
|
169
|
-
|
|
169
|
+
SDD happens in OpenCode via conversation, VGXNESS MCP, and SDD subagents. No terminal SDD phase commands for normal flow. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
|
|
170
170
|
|
|
171
171
|
## MCP playbook
|
|
172
172
|
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\`; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one and summarize.
|
|
@@ -174,9 +174,9 @@ Daily SDD happens inside OpenCode through conversation, VGXNESS MCP, and hidden
|
|
|
174
174
|
- Trusted draft autorun: when the exact \`proposal\` artifact is already accepted, you may run exactly \`spec -> design -> tasks\` without extra human confirmation via exact hidden subagents and save drafts with \`vgxness_sdd_save_artifact\`. This is draft-only planning, not acceptance or completion. Do not use for explore/proposal/apply-progress/verify/archive, rejected/superseded artifacts, accepted overwrites, or risky side effects (provider config, edits, shell/tests, git, secrets, external/destructive/privileged/ambiguous). Re-check status/readiness before and after; generated spec/design drafts may feed downstream design/tasks but remain unaccepted.
|
|
175
175
|
- Acceptance/readiness: check SDD status/readiness for the exact project/change/phase, confirm explicit human acceptance of that exact artifact, call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId), then re-check status/readiness before reporting state. Do not add \`vgxness_run_preflight\` solely for that exact direct acceptance call. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings.
|
|
176
176
|
- Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work or unclear context; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
|
|
177
|
-
- Agents/profile/payloads: resolve
|
|
178
|
-
- Runs/recovery: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for run work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`. Unknown runId: \`vgxness_run_resume_candidates\`. For interrupted runs, inspect with \`vgxness_run_resume_inspect\`, then call \`vgxness_run_resume_gate\` with approvalId from pendingApprovals/inspect before advice; never pass runId as approvalId
|
|
179
|
-
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\`
|
|
177
|
+
- Agents/profile/payloads: resolve phase with \`vgxness_agent_resolve\`. Preview mode does not execute a provider or write provider config for \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, or \`vgxness_skill_payload\`. Use \`vgxness_manager_profile_get\` before changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
|
|
178
|
+
- Runs/recovery: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for run work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`. Unknown runId: \`vgxness_run_resume_candidates\`. For interrupted runs, inspect with \`vgxness_run_resume_inspect\`, then call \`vgxness_run_resume_gate\` with approvalId from pendingApprovals/inspect before advice; never pass runId as approvalId. Follow safe \`recommendedActions[]\`.
|
|
179
|
+
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` read-only; report \`providerEvidence.evidenceLevel\`/\`hostToolPresenceVerified\` limits.
|
|
180
180
|
|
|
181
181
|
## Minimum flows
|
|
182
182
|
- Simple answer: inline; memory only for prior context; no run by default.
|
|
@@ -196,11 +196,12 @@ Be concise: delegated work, results, files/decisions, evidence/tests, risks, nex
|
|
|
196
196
|
* render baselines, and manager profile overlay baselines; they are
|
|
197
197
|
* intentionally distinct from the provider runtime prompt above.
|
|
198
198
|
*/
|
|
199
|
-
const
|
|
199
|
+
const registryManagerInstructionsV11 = [
|
|
200
200
|
'You are the VGXNESS SDD coordinator, not a monolithic executor. Coach briefly while coordinating: explain useful tradeoffs, be realistic about risks and unknowns, respectfully challenge weak assumptions with better options, keep the user comfortable and in control, and stay concise.',
|
|
201
201
|
'Default to delegation for SDD phase-shaped work, repository exploration beyond one narrow lookup, implementation, verification, incident recovery, or multi-step analysis. Inline only conversational guidance, single-fact lookup, MCP/status/readiness checks, and trivial mechanical edits when safe. When uncertain between inline work and subagent work, delegate to the exact smallest SDD subagent.',
|
|
202
202
|
'Use VGXNESS MCP as the durable control plane: restore session context with vgxness_session_restore before inferring start/resume state from chat, and close/pause/compact with vgxness_session_close using actor manager plus an actionable summary when a current session id exists.',
|
|
203
203
|
'Check SDD status/next/ready/cockpit, read prerequisites with sdd_get_artifact or sdd_list_artifacts, use public sdd_continue/internal vgxness_sdd_continue first for advisory read-only continuation plans, use sdd_reopen_artifact only for rejected artifacts returning to draft with explicit human actor/audit context, save phase output with sdd_save_artifact only by governance; when proposal is accepted, spec/design/tasks may run sequentially as draft-only autorun without extra confirmation, while acceptance remains human-only. Search/get/save/update memory only for reusable knowledge, resolve exact SDD subagents before substantial phase work, use runs/checkpoints/preflight/finalize for significant implementation or verification, use run_resume_candidates for unknown runId, inspect interrupted runs by runId with run_resume_inspect, then call run_resume_gate with approvalId from pendingApprovals/inspect; never pass runId as approvalId; use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health.',
|
|
204
|
+
'When sdd_continue/vgxness_sdd_continue returns recommendedActions, prefer the first safe action with agentCallable true and humanOnly false; use targetTool plus suggestedArgs as the starting point, and stop for requiresHumanConfirmation, requiresProviderWriteConsent, requiresPreflight, ambiguous, destructive, privileged, external, or outside-approval actions. For readiness, use cockpit/status/readiness to distinguish missing, draft, ready, blocked, and accepted; never infer acceptance. For provider evidence, report providerEvidence.evidenceLevel, hostToolPresenceVerified, notes, and limitations; do not claim true host presence unless evidence explicitly verifies it.',
|
|
204
205
|
'Prefer payloadMode=compact for manager-facing status/context reads, SDD artifact reads/lists, and activation handoffs so the primary context stays clean; request payloadMode=verbose only when full artifact contents, provider payloads, or skill context are actually needed, preferably inside delegated phase subagents.',
|
|
205
206
|
'MCP sdd_continue must not execute providers, create runs, mutate artifacts, write provider config/openspec, bypass human acceptance, or treat draft-run as apply-progress.',
|
|
206
207
|
'CLI is an escape hatch for bootstrap, doctor, rollback, recovery, MCP unavailable/setup missing, or explicit user request; do not tell users to run terminal SDD phase commands for normal daily flow. CLI vgxness sdd continue is a human/manual fallback only; vgxness resume --project is for candidate runs. The removed experimental vgxness code runtime is not an SDD fallback.',
|
package/dist/cli/cli-flags.js
CHANGED
|
@@ -180,13 +180,13 @@ function skillTargetTypeFlag(flags, name) {
|
|
|
180
180
|
return value;
|
|
181
181
|
return isSkillTargetType(value.value)
|
|
182
182
|
? { ok: true, value: value.value }
|
|
183
|
-
: validationFailure(`--${name} must be agent, subagent, workflow-phase, or
|
|
183
|
+
: validationFailure(`--${name} must be agent, subagent, workflow-phase, provider-adapter, or intent-signal`);
|
|
184
184
|
}
|
|
185
185
|
function optionalSkillTargetTypeFlag(flags, name) {
|
|
186
186
|
const value = optionalStringFlag(flags, name);
|
|
187
187
|
if (value === undefined)
|
|
188
188
|
return { ok: true, value: undefined };
|
|
189
|
-
return isSkillTargetType(value) ? { ok: true, value } : validationFailure(`--${name} must be agent, subagent, workflow-phase, or
|
|
189
|
+
return isSkillTargetType(value) ? { ok: true, value } : validationFailure(`--${name} must be agent, subagent, workflow-phase, provider-adapter, or intent-signal`);
|
|
190
190
|
}
|
|
191
191
|
function skillUsageOutcomeFlag(flags, name) {
|
|
192
192
|
const value = requiredFlag(flags, name);
|
|
@@ -228,7 +228,7 @@ function isSkillEvaluationResultStatus(value) {
|
|
|
228
228
|
return value === 'passed' || value === 'failed' || value === 'needs-review' || value === 'not-applicable';
|
|
229
229
|
}
|
|
230
230
|
function isSkillTargetType(value) {
|
|
231
|
-
return value === 'agent' || value === 'subagent' || value === 'workflow-phase' || value === 'provider-adapter';
|
|
231
|
+
return value === 'agent' || value === 'subagent' || value === 'workflow-phase' || value === 'provider-adapter' || value === 'intent-signal';
|
|
232
232
|
}
|
|
233
233
|
function instructionKindFlag(flags) {
|
|
234
234
|
const value = optionalStringFlag(flags, 'instructions-kind') ?? 'inline';
|
package/dist/cli/cli-help.js
CHANGED
|
@@ -77,12 +77,12 @@ Areas:
|
|
|
77
77
|
skills list [--project <name>] [--scope project|personal]
|
|
78
78
|
skills get --id <id> | --project <name> --name <name> [--scope project|personal]
|
|
79
79
|
skills add-version (--id <id> | --project <name> --name <name>) --version <version> --source-kind path|url|inline [--source-path <path>] [--source-url <url>] [--inline-metadata <json>] [--activate]
|
|
80
|
-
skills attach (--id <id> | --project <name> --name <name>) --target-type agent|subagent|workflow-phase|provider-adapter --target-key <key>
|
|
81
|
-
skills detach (--id <id> | --project <name> --name <name>) --target-type agent|subagent|workflow-phase|provider-adapter --target-key <key>
|
|
80
|
+
skills attach (--id <id> | --project <name> --name <name>) --target-type agent|subagent|workflow-phase|provider-adapter|intent-signal --target-key <key>
|
|
81
|
+
skills detach (--id <id> | --project <name> --name <name>) --target-type agent|subagent|workflow-phase|provider-adapter|intent-signal --target-key <key>
|
|
82
82
|
skills record-usage (--id <id> | --project <name> --name <name>) --outcome selected|injected|helped|failed|neutral [--run-id <id>]
|
|
83
|
-
skills resolve [--agent <name> | --agent-id <id>] [--project <name>] [--workflow <name>] [--phase <name>] [--provider <name>] [--run <id>] [--record-usage selected|injected]
|
|
83
|
+
skills resolve [--agent <name> | --agent-id <id>] [--project <name>] [--workflow <name>] [--phase <name>] [--intent-signals a,b] [--provider <name>] [--run <id>] [--record-usage selected|injected]
|
|
84
84
|
skills status --project <name> [--scope project|personal] [--provider opencode] [--mode agent|subagent] [--agent <name-or-id>]
|
|
85
|
-
skills payload [--agent <name> | --agent-id <id>] [--project <name>] [--workflow <name>] [--phase <name>] [--provider <name>]
|
|
85
|
+
skills payload [--agent <name> | --agent-id <id>] [--project <name>] [--workflow <name>] [--phase <name>] [--intent-signals a,b] [--provider <name>]
|
|
86
86
|
skills propose (--id <id> | --project <name> --name <name>) --proposed-version <version> --source-kind path|url|inline --rationale <text>
|
|
87
87
|
skills proposals [--skill-id <id>] [--status draft|pending-approval|approved|rejected|cancelled|applied]
|
|
88
88
|
skills create-scenario (--id <id> --name <scenario> | --project <name> --name <skill> --scenario-name <scenario>) --criteria <json> --created-by <actor> [--proposal <id>] [--version-id <id>]
|
|
@@ -4,6 +4,7 @@ import { ManagerProfileOverlayService } from '../../agents/manager-profile-overl
|
|
|
4
4
|
import { getProviderRenderer } from '../../agents/renderers/index.js';
|
|
5
5
|
import { ManagerProfileOverlayRepository } from '../../agents/repositories/manager-profile-overlays.js';
|
|
6
6
|
import { createAgentRegistryToolHandlers } from '../../harness/tools/agents.js';
|
|
7
|
+
import { runBootSkillSeed } from '../../skills/boot-seed.js';
|
|
7
8
|
import { SkillRegistryService } from '../../skills/skill-registry-service.js';
|
|
8
9
|
import { SkillStatusService } from '../../skills/skill-status-service.js';
|
|
9
10
|
import { csvFlag, instructionKindFlag, jsonFlag, optionalJsonFlag, optionalModeFlag, optionalScopeFlag, optionalSkillEvaluationResultStatusFlag, optionalSkillImprovementProposalStatusFlag, optionalSkillResolutionUsageFlag, optionalSkillTargetTypeFlag, optionalSkillVersionStatusFlag, optionalStringFlag, requiredFlag, scopeFlag, skillEvaluationResultStatusFlag, skillSourceFromFlags, skillTargetTypeFlag, skillUsageOutcomeFlag, } from '../cli-flags.js';
|
|
@@ -37,6 +38,7 @@ function resolveSkillsInput(flags) {
|
|
|
37
38
|
const agentName = optionalStringFlag(flags, 'agent');
|
|
38
39
|
const workflow = optionalStringFlag(flags, 'workflow');
|
|
39
40
|
const phase = optionalStringFlag(flags, 'phase');
|
|
41
|
+
const intentSignals = csvFlag(flags, 'intent-signals');
|
|
40
42
|
const providerAdapter = optionalStringFlag(flags, 'provider');
|
|
41
43
|
const runId = optionalStringFlag(flags, 'run');
|
|
42
44
|
if (project !== undefined)
|
|
@@ -51,6 +53,8 @@ function resolveSkillsInput(flags) {
|
|
|
51
53
|
input.workflow = workflow;
|
|
52
54
|
if (phase !== undefined)
|
|
53
55
|
input.phase = phase;
|
|
56
|
+
if (intentSignals.length > 0)
|
|
57
|
+
input.intentSignals = intentSignals;
|
|
54
58
|
if (providerAdapter !== undefined)
|
|
55
59
|
input.providerAdapter = providerAdapter;
|
|
56
60
|
if (runId !== undefined)
|
|
@@ -658,7 +662,12 @@ export function runSkillCommand(command, parsed, database, environment) {
|
|
|
658
662
|
}
|
|
659
663
|
if (command === 'payload') {
|
|
660
664
|
const input = skillPayloadInput(parsed.flags);
|
|
661
|
-
|
|
665
|
+
if (!input.ok)
|
|
666
|
+
return resultFailure(input);
|
|
667
|
+
const seeded = runBootSkillSeed(database, environment.env);
|
|
668
|
+
if (!seeded.ok)
|
|
669
|
+
return resultFailure(seeded);
|
|
670
|
+
return jsonResult(registry.buildSkillPayload(input.value, { workspaceRoot: environment.cwd }));
|
|
662
671
|
}
|
|
663
672
|
return usageFailure(`Unknown skills command: ${command}`);
|
|
664
673
|
}
|
|
@@ -11,6 +11,7 @@ import { RunService } from '../runs/run-service.js';
|
|
|
11
11
|
import { sddContinuationPlanFrom } from '../sdd/sdd-continuation-plan.js';
|
|
12
12
|
import { buildSddCockpitSurfaceResponse } from '../sdd/cockpit-read-model.js';
|
|
13
13
|
import { SddWorkflowService } from '../sdd/sdd-workflow-service.js';
|
|
14
|
+
import { runBootSkillSeed } from '../skills/boot-seed.js';
|
|
14
15
|
import { SkillRegistryService } from '../skills/skill-registry-service.js';
|
|
15
16
|
import { VerificationPlanService } from '../verification/index.js';
|
|
16
17
|
import { ProviderChangePlanService } from './provider-change-plan.js';
|
|
@@ -312,6 +313,7 @@ function createServices(database) {
|
|
|
312
313
|
agents,
|
|
313
314
|
managerProfiles,
|
|
314
315
|
skills,
|
|
316
|
+
seedBuiltInSkills: () => runBootSkillSeed(database),
|
|
315
317
|
opencodeManagerPayload,
|
|
316
318
|
opencodeHandoffPreview: new OpenCodeHandoffPreviewService({ managerPayload: opencodeManagerPayload, sdd, providerStatus }),
|
|
317
319
|
activation: new AgentActivationService({ agents, managerProfiles, runs, opencodeManagerPayload }),
|
|
@@ -358,6 +360,9 @@ function memoryContext(sessionId) {
|
|
|
358
360
|
return context;
|
|
359
361
|
}
|
|
360
362
|
function buildSkillPayloadEnvelope(input, services) {
|
|
363
|
+
const seeded = services.seedBuiltInSkills?.();
|
|
364
|
+
if (seeded !== undefined && !seeded.ok)
|
|
365
|
+
return errorEnvelope(seeded.error.code, seeded.error.message, 'vgxness_skill_payload');
|
|
361
366
|
const { workspaceRoot, maxSourceBytes, mode, ...resolverInput } = input;
|
|
362
367
|
const options = { workspaceRoot, ...(maxSourceBytes === undefined ? {} : { maxSourceBytes }), ...(mode === undefined ? {} : { mode }) };
|
|
363
368
|
return toEnvelope('vgxness_skill_payload', services.skills.buildSkillPayload(resolverInput, options));
|
package/dist/mcp/schema.js
CHANGED
|
@@ -469,6 +469,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
469
469
|
agentName: z.string().min(1).optional(),
|
|
470
470
|
workflow: z.string().min(1).optional(),
|
|
471
471
|
phase: z.string().min(1).optional(),
|
|
472
|
+
intentSignals: z.array(z.string().min(1)).optional(),
|
|
472
473
|
providerAdapter: z.string().min(1).optional(),
|
|
473
474
|
runId: z.string().min(1).optional(),
|
|
474
475
|
mode: z.enum(payloadModes).optional(),
|
package/dist/mcp/validation.js
CHANGED
|
@@ -736,6 +736,7 @@ function validateSkillPayloadInput(input, tool) {
|
|
|
736
736
|
'agentName',
|
|
737
737
|
'workflow',
|
|
738
738
|
'phase',
|
|
739
|
+
'intentSignals',
|
|
739
740
|
'providerAdapter',
|
|
740
741
|
'runId',
|
|
741
742
|
'mode',
|
|
@@ -749,6 +750,11 @@ function validateSkillPayloadInput(input, tool) {
|
|
|
749
750
|
const copied = copyOptionalStrings(result, record.value, tool, ['project', 'agentId', 'agentName', 'workflow', 'phase', 'providerAdapter', 'runId']);
|
|
750
751
|
if (!copied.ok)
|
|
751
752
|
return copied;
|
|
753
|
+
const intentSignals = readOptionalStringArray(record.value, 'intentSignals', tool);
|
|
754
|
+
if (!intentSignals.ok)
|
|
755
|
+
return intentSignals;
|
|
756
|
+
if (intentSignals.value !== undefined)
|
|
757
|
+
result.intentSignals = intentSignals.value;
|
|
752
758
|
const scope = readOptionalOneOf(record.value, 'scope', scopes, tool);
|
|
753
759
|
if (!scope.ok)
|
|
754
760
|
return scope;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS skill_attachments_next (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
4
|
+
version_id TEXT REFERENCES skill_versions(id) ON DELETE SET NULL,
|
|
5
|
+
target_type TEXT NOT NULL CHECK (target_type IN ('agent', 'subagent', 'workflow-phase', 'provider-adapter', 'intent-signal')),
|
|
6
|
+
target_key TEXT NOT NULL,
|
|
7
|
+
metadata_json TEXT NOT NULL,
|
|
8
|
+
created_at TEXT NOT NULL,
|
|
9
|
+
UNIQUE(skill_id, target_type, target_key)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
INSERT INTO skill_attachments_next(id, skill_id, version_id, target_type, target_key, metadata_json, created_at)
|
|
13
|
+
SELECT id, skill_id, version_id, target_type, target_key, metadata_json, created_at
|
|
14
|
+
FROM skill_attachments;
|
|
15
|
+
|
|
16
|
+
DROP TABLE skill_attachments;
|
|
17
|
+
ALTER TABLE skill_attachments_next RENAME TO skill_attachments;
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS skill_attachments_target_idx
|
|
20
|
+
ON skill_attachments(target_type, target_key);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS skill_usage_records_next (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
25
|
+
version_id TEXT REFERENCES skill_versions(id) ON DELETE SET NULL,
|
|
26
|
+
run_id TEXT,
|
|
27
|
+
target_type TEXT CHECK (target_type IN ('agent', 'subagent', 'workflow-phase', 'provider-adapter', 'intent-signal')),
|
|
28
|
+
target_key TEXT,
|
|
29
|
+
outcome TEXT NOT NULL CHECK (outcome IN ('selected', 'injected', 'helped', 'failed', 'neutral')),
|
|
30
|
+
notes TEXT,
|
|
31
|
+
created_at TEXT NOT NULL
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
INSERT INTO skill_usage_records_next(id, skill_id, version_id, run_id, target_type, target_key, outcome, notes, created_at)
|
|
35
|
+
SELECT id, skill_id, version_id, run_id, target_type, target_key, outcome, notes, created_at
|
|
36
|
+
FROM skill_usage_records;
|
|
37
|
+
|
|
38
|
+
DROP TABLE skill_usage_records;
|
|
39
|
+
ALTER TABLE skill_usage_records_next RENAME TO skill_usage_records;
|
|
40
|
+
|
|
41
|
+
CREATE INDEX IF NOT EXISTS skill_usage_records_run_idx
|
|
42
|
+
ON skill_usage_records(run_id, created_at DESC);
|
|
@@ -27,7 +27,7 @@ const signalRules = [
|
|
|
27
27
|
{ signal: 'workflow-change', terms: [/\bworkflow\b/, /\borchestrat(e|ion|or)\b/, /\bsdd\b/, /\bphase\b/] },
|
|
28
28
|
{ signal: 'persistence-change', terms: [/\bpersist(ent|ence)?\b/, /\bstorage\b/, /\bsqlite\b/, /\bdatabase\b/, /\bmemory\b/, /\bmigration\b/] },
|
|
29
29
|
{ signal: 'security-sensitive', terms: [/\bsecurity\b/, /\bauth\b/, /\btoken\b/, /\bsecret\b/, /\bpermission\b/, /\bpermiso\b/, /\baprobaci[oó]n\b/] },
|
|
30
|
-
{ signal: 'broad-change', terms: [/\bmulti[- ]file\b/, /\bacross\b/, /\bend[- ]to[- ]end\b/, /\bsystem\b/] },
|
|
30
|
+
{ signal: 'broad-change', terms: [/\bmulti[- ]file\b/, /\bacross\b/, /\bend[- ]to[- ]end\b/, /\bsystem\b/, /\blarge[- ]diff\b/, /\bbig[- ]diff\b/, /\bcross[- ]cutting\b/] },
|
|
31
31
|
{ signal: 'execution-request', terms: [/\brun\b/, /\bexecute\b/, /\bapply\b/, /\bstart\b/, /\binstall\b/, /\bpush\b/] },
|
|
32
32
|
{ signal: 'provider-execution', terms: [/\bprovider\b/, /\bopencode\b/, /\bclaude\b/, /\bmodel\b/, /\bllm\b/] },
|
|
33
33
|
{ signal: 'file-edit-request', terms: [/\bedit\b/, /\bwrite\b/, /\bmodify\b/, /\bpatch\b/, /\barreglar\b/] },
|
|
@@ -43,6 +43,7 @@ export function createNaturalLanguagePlan(input) {
|
|
|
43
43
|
const ambiguous = isAmbiguous(normalizedIntent);
|
|
44
44
|
if (ambiguous)
|
|
45
45
|
addSignal(signals, 'ambiguous');
|
|
46
|
+
const intentSignals = buildSkillIntentSignals(normalizedIntent, signals);
|
|
46
47
|
const risk = classifyIntentRisk({ project: input.project, intent: input.intent });
|
|
47
48
|
const flow = chooseFlow(signals, risk);
|
|
48
49
|
const workflow = workflowFor(flow);
|
|
@@ -55,7 +56,7 @@ export function createNaturalLanguagePlan(input) {
|
|
|
55
56
|
...(suggestedChangeId !== undefined ? { change: suggestedChangeId } : {}),
|
|
56
57
|
...(input.sdd !== undefined ? { sdd: input.sdd } : {}),
|
|
57
58
|
};
|
|
58
|
-
const previewActions = buildPreviewActions(actionInput, flow, needsClarification);
|
|
59
|
+
const previewActions = buildPreviewActions(actionInput, flow, workflow, intentSignals, needsClarification);
|
|
59
60
|
return {
|
|
60
61
|
version: 1,
|
|
61
62
|
project: input.project,
|
|
@@ -65,6 +66,7 @@ export function createNaturalLanguagePlan(input) {
|
|
|
65
66
|
confidence: confidenceFor(flow, signals, needsClarification),
|
|
66
67
|
reason: reasonFor(flow, signals, needsClarification),
|
|
67
68
|
signals,
|
|
69
|
+
intentSignals,
|
|
68
70
|
needsClarification,
|
|
69
71
|
...(needsClarification ? { clarifyingQuestion: 'What target should this change inspect or modify, and what outcome do you want?' } : {}),
|
|
70
72
|
...(suggestedChangeId !== undefined ? { suggestedChangeId } : {}),
|
|
@@ -231,20 +233,42 @@ function buildSafety(signals) {
|
|
|
231
233
|
notes.push('Privileged impact was detected; this preview does not request elevated access.');
|
|
232
234
|
return { executed: false, callsProvider: false, editsFiles: false, writesProviderConfig: false, recordsRuns: false, notes };
|
|
233
235
|
}
|
|
234
|
-
function
|
|
236
|
+
function buildSkillIntentSignals(intent, signals) {
|
|
237
|
+
const skillSignals = ['workflow-selection'];
|
|
238
|
+
if (signals.includes('broad-change'))
|
|
239
|
+
addSkillSignal(skillSignals, 'broad-change');
|
|
240
|
+
if (/\b(git|commit|branch|merge|rebase|push|stage|staging|diff|checkout)\b/.test(intent))
|
|
241
|
+
addSkillSignal(skillSignals, 'git');
|
|
242
|
+
if (/\b(pr|prs|pull[- ]request|pull request|reviewer|reviewers)\b/.test(intent))
|
|
243
|
+
addSkillSignal(skillSignals, 'pull-request');
|
|
244
|
+
if (/\b(stacked[- ]pr|stacked[- ]prs|stacked pull requests?|stacked|apilad[oa]s?)\b/.test(intent))
|
|
245
|
+
addSkillSignal(skillSignals, 'stacked-prs');
|
|
246
|
+
if (/\b(tdd|test[- ]driven|red[- ]green|strict[- ]tdd)\b/.test(intent))
|
|
247
|
+
addSkillSignal(skillSignals, intent.includes('strict') ? 'strict-tdd' : 'tdd');
|
|
248
|
+
if (/\b(review[- ]size|reviewable|split|slice|slices|large[- ]diff|big[- ]diff)\b/.test(intent))
|
|
249
|
+
addSkillSignal(skillSignals, 'review-size');
|
|
250
|
+
return skillSignals;
|
|
251
|
+
}
|
|
252
|
+
function addSkillSignal(signals, signal) {
|
|
253
|
+
if (!signals.includes(signal))
|
|
254
|
+
signals.push(signal);
|
|
255
|
+
}
|
|
256
|
+
function buildPreviewActions(input, flow, workflow, intentSignals, needsClarification) {
|
|
235
257
|
if (needsClarification)
|
|
236
258
|
return [{ kind: 'clarification', description: 'Ask for the missing target and desired outcome before previewing write actions.' }];
|
|
259
|
+
const skillAction = skillPayloadPreviewAction(input, workflow, intentSignals);
|
|
237
260
|
if (flow === 'debug' || flow === 'diagnose')
|
|
238
|
-
return [{ kind: 'diagnostic-preview', description: 'Preview read-only diagnostic commands such as status, doctor, logs, or SDD next checks.' }];
|
|
261
|
+
return [skillAction, { kind: 'diagnostic-preview', description: 'Preview read-only diagnostic commands such as status, doctor, logs, or SDD next checks.' }];
|
|
239
262
|
if (flow === 'plan')
|
|
240
|
-
return [{ kind: 'manual-plan', description: 'Draft a manual implementation plan without executing providers or editing files.' }];
|
|
263
|
+
return [skillAction, { kind: 'manual-plan', description: 'Draft a manual implementation plan without executing providers or editing files.' }];
|
|
241
264
|
if (flow === 'explore' || flow === 'direct')
|
|
242
|
-
return [{ kind: 'answer', description: 'Explore or answer directly without entering SDD.' }];
|
|
265
|
+
return [skillAction, { kind: 'answer', description: 'Explore or answer directly without entering SDD.' }];
|
|
243
266
|
if (flow === 'quickfix')
|
|
244
|
-
return [{ kind: 'workflow-preview', description: 'Preview a small localized quickfix; no execution occurs in this planner response.' }];
|
|
267
|
+
return [skillAction, { kind: 'workflow-preview', description: 'Preview a small localized quickfix; no execution occurs in this planner response.' }];
|
|
245
268
|
if (flow === 'build')
|
|
246
|
-
return [{ kind: 'workflow-preview', description: 'Preview a scoped build workflow; no execution occurs in this planner response.' }];
|
|
269
|
+
return [skillAction, { kind: 'workflow-preview', description: 'Preview a scoped build workflow; no execution occurs in this planner response.' }];
|
|
247
270
|
return [
|
|
271
|
+
skillAction,
|
|
248
272
|
{
|
|
249
273
|
kind: 'sdd-preview',
|
|
250
274
|
description: input.sdd?.next?.recommendedAction ??
|
|
@@ -252,3 +276,24 @@ function buildPreviewActions(input, flow, needsClarification) {
|
|
|
252
276
|
},
|
|
253
277
|
];
|
|
254
278
|
}
|
|
279
|
+
function skillPayloadPreviewAction(input, workflow, intentSignals) {
|
|
280
|
+
const phase = input.sdd?.next?.nextPhase;
|
|
281
|
+
const command = [
|
|
282
|
+
'vgxness skills payload',
|
|
283
|
+
'--project',
|
|
284
|
+
shellQuote(input.project),
|
|
285
|
+
'--workflow',
|
|
286
|
+
shellQuote(workflow),
|
|
287
|
+
...(phase === undefined ? [] : ['--phase', shellQuote(phase)]),
|
|
288
|
+
'--intent-signals',
|
|
289
|
+
shellQuote(intentSignals.join(',')),
|
|
290
|
+
].join(' ');
|
|
291
|
+
return {
|
|
292
|
+
kind: 'skill-payload-preview',
|
|
293
|
+
description: 'Preview the registry/context skills that match this intent; this is read-only and does not install provider-native skills.',
|
|
294
|
+
command,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function shellQuote(value) {
|
|
298
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
299
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SkillSeedService } from './skill-seed-service.js';
|
|
2
|
+
const skipEnvVar = 'VGXNESS_SKIP_SKILL_SEED_AUTO_UPGRADE';
|
|
3
|
+
export function runBootSkillSeed(database, env = process.env) {
|
|
4
|
+
if (isOptOut(env))
|
|
5
|
+
return okFromSeedResult({ skipped: true });
|
|
6
|
+
const seeded = new SkillSeedService(database).seedFromDefaultManifest();
|
|
7
|
+
if (!seeded.ok)
|
|
8
|
+
return seeded;
|
|
9
|
+
return okFromSeedResult({ skipped: false, seed: seeded.value });
|
|
10
|
+
}
|
|
11
|
+
function isOptOut(env) {
|
|
12
|
+
const value = env[skipEnvVar];
|
|
13
|
+
return value === '1' || value === 'true';
|
|
14
|
+
}
|
|
15
|
+
function okFromSeedResult(input) {
|
|
16
|
+
if (input.skipped) {
|
|
17
|
+
return {
|
|
18
|
+
ok: true,
|
|
19
|
+
value: {
|
|
20
|
+
skipped: true,
|
|
21
|
+
skillsCreated: 0,
|
|
22
|
+
skillsUpdated: 0,
|
|
23
|
+
versionsCreated: 0,
|
|
24
|
+
versionsSkipped: 0,
|
|
25
|
+
attachmentsCreated: 0,
|
|
26
|
+
attachmentsSkipped: 0,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
value: {
|
|
33
|
+
skipped: false,
|
|
34
|
+
skillsCreated: input.seed.skillsCreated,
|
|
35
|
+
skillsUpdated: input.seed.skillsUpdated,
|
|
36
|
+
versionsCreated: input.seed.versionsCreated,
|
|
37
|
+
versionsSkipped: input.seed.versionsSkipped,
|
|
38
|
+
attachmentsCreated: input.seed.attachmentsCreated,
|
|
39
|
+
attachmentsSkipped: input.seed.attachmentsSkipped,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -89,6 +89,7 @@ export class SkillResolver {
|
|
|
89
89
|
...(agent.value?.name !== undefined ? { agentName: agent.value.name } : {}),
|
|
90
90
|
...(input.workflow !== undefined ? { workflow: input.workflow } : {}),
|
|
91
91
|
...(input.phase !== undefined ? { phase: input.phase } : {}),
|
|
92
|
+
...(input.intentSignals !== undefined && input.intentSignals.length > 0 ? { intentSignals: normalizedIntentSignals(input.intentSignals) } : {}),
|
|
92
93
|
...(input.providerAdapter !== undefined ? { providerAdapter: input.providerAdapter } : {}),
|
|
93
94
|
...(input.runId !== undefined ? { runId: input.runId } : {}),
|
|
94
95
|
},
|
|
@@ -121,6 +122,8 @@ export class SkillResolver {
|
|
|
121
122
|
}
|
|
122
123
|
for (const phase of phases)
|
|
123
124
|
targets.push({ targetType: 'workflow-phase', targetKey: phase });
|
|
125
|
+
for (const signal of normalizedIntentSignals(input.intentSignals))
|
|
126
|
+
targets.push({ targetType: 'intent-signal', targetKey: signal });
|
|
124
127
|
if (input.providerAdapter !== undefined)
|
|
125
128
|
targets.push({ targetType: 'provider-adapter', targetKey: input.providerAdapter });
|
|
126
129
|
return dedupeTargets(targets);
|
|
@@ -229,6 +232,9 @@ function phaseTargetKeys(workflow, phase) {
|
|
|
229
232
|
function isSddWorkflow(workflow) {
|
|
230
233
|
return workflow?.trim().toLowerCase() === 'sdd';
|
|
231
234
|
}
|
|
235
|
+
function normalizedIntentSignals(signals) {
|
|
236
|
+
return [...new Set((signals ?? []).map((signal) => signal.trim().toLowerCase()).filter(Boolean))];
|
|
237
|
+
}
|
|
232
238
|
function stringMetadata(value) {
|
|
233
239
|
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
234
240
|
}
|
|
@@ -33,9 +33,8 @@ export class SkillSeedService {
|
|
|
33
33
|
if (!details.ok)
|
|
34
34
|
return details;
|
|
35
35
|
existingVersions.set(seed.name, details.value.versions.some((version) => version.version === seed.version));
|
|
36
|
-
for (const attachment of details.value.attachments)
|
|
36
|
+
for (const attachment of details.value.attachments)
|
|
37
37
|
existingAttachments.set(`${seed.name}:${attachment.targetType}:${attachment.targetKey}`, true);
|
|
38
|
-
}
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
const transaction = this.database.transaction(() => {
|
|
@@ -74,11 +73,12 @@ export class SkillSeedService {
|
|
|
74
73
|
const skill = this.registry.getSkillByName(manifest.project, manifest.scope, attachment.skill);
|
|
75
74
|
if (!skill.ok)
|
|
76
75
|
throw new SeedLoadError(skill.error);
|
|
77
|
-
const
|
|
78
|
-
const
|
|
76
|
+
const targetType = attachmentTargetType(attachment);
|
|
77
|
+
const targetKey = attachmentTargetKey(attachment);
|
|
78
|
+
const key = `${attachment.skill}:${targetType}:${targetKey}`;
|
|
79
79
|
const attachInput = {
|
|
80
80
|
skillId: skill.value.id,
|
|
81
|
-
targetType
|
|
81
|
+
targetType,
|
|
82
82
|
targetKey,
|
|
83
83
|
metadata: attachmentMetadata(attachment),
|
|
84
84
|
};
|
|
@@ -138,7 +138,7 @@ function validateManifest(manifest) {
|
|
|
138
138
|
const validation = validateAttachmentSeed(attachment, names);
|
|
139
139
|
if (!validation.ok)
|
|
140
140
|
return validation;
|
|
141
|
-
const key = `${attachment
|
|
141
|
+
const key = `${attachmentTargetType(attachment)}:${attachmentTargetKey(attachment)}:${attachment.skill}`;
|
|
142
142
|
if (attachments.has(key))
|
|
143
143
|
return validationFailure(`Duplicate skill seed attachment: ${key}`);
|
|
144
144
|
attachments.add(key);
|
|
@@ -159,10 +159,18 @@ function validateSkillSeed(skill) {
|
|
|
159
159
|
return rejectUnsupportedNativeInputs(skill.name, skill.metadata);
|
|
160
160
|
}
|
|
161
161
|
function validateAttachmentSeed(attachment, names) {
|
|
162
|
-
if (!attachment.
|
|
163
|
-
return validationFailure(
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
if (attachment.targetType !== undefined && !isSkillAttachmentTargetType(attachment.targetType))
|
|
163
|
+
return validationFailure(`Skill seed attachment targetType is invalid: ${String(attachment.targetType)}`);
|
|
164
|
+
const targetType = attachmentTargetType(attachment);
|
|
165
|
+
if (targetType === 'workflow-phase') {
|
|
166
|
+
if (!attachment.workflow?.trim())
|
|
167
|
+
return validationFailure('Skill seed attachment workflow is required');
|
|
168
|
+
if (!attachment.phase?.trim())
|
|
169
|
+
return validationFailure('Skill seed attachment phase is required');
|
|
170
|
+
}
|
|
171
|
+
else if (!attachment.targetKey?.trim()) {
|
|
172
|
+
return validationFailure(`Skill seed attachment targetKey is required for ${targetType}`);
|
|
173
|
+
}
|
|
166
174
|
if (!attachment.skill?.trim())
|
|
167
175
|
return validationFailure('Skill seed attachment skill is required');
|
|
168
176
|
if (!names.has(attachment.skill))
|
|
@@ -186,20 +194,35 @@ function rejectUnsupportedNativeInputs(name, metadata) {
|
|
|
186
194
|
function compatibilityFor(seed, attachments) {
|
|
187
195
|
const matching = attachments.filter((attachment) => attachment.skill === seed.name);
|
|
188
196
|
return {
|
|
189
|
-
targets: [
|
|
197
|
+
targets: [...new Set(matching.map(attachmentTargetType))],
|
|
190
198
|
adapters: ['opencode'],
|
|
191
|
-
workflows: [...new Set(matching.map((attachment) => attachment.workflow))],
|
|
192
|
-
phases: [...new Set(matching.map((attachment) => attachment.phase))],
|
|
199
|
+
workflows: [...new Set(matching.map((attachment) => attachment.workflow).filter((workflow) => workflow !== undefined))],
|
|
200
|
+
phases: [...new Set(matching.map((attachment) => attachment.phase).filter((phase) => phase !== undefined))],
|
|
193
201
|
};
|
|
194
202
|
}
|
|
195
203
|
function attachmentMetadata(attachment) {
|
|
196
|
-
|
|
204
|
+
const metadata = {
|
|
197
205
|
...(attachment.metadata ?? {}),
|
|
198
206
|
...(attachment.order !== undefined ? { order: attachment.order } : {}),
|
|
199
207
|
seededBy: 'vgxness',
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
targetType: attachmentTargetType(attachment),
|
|
209
|
+
targetKey: attachmentTargetKey(attachment),
|
|
202
210
|
};
|
|
211
|
+
if (attachment.workflow !== undefined)
|
|
212
|
+
metadata.workflow = attachment.workflow;
|
|
213
|
+
if (attachment.phase !== undefined)
|
|
214
|
+
metadata.phase = attachment.phase;
|
|
215
|
+
return metadata;
|
|
216
|
+
}
|
|
217
|
+
function attachmentTargetType(attachment) {
|
|
218
|
+
return attachment.targetType ?? 'workflow-phase';
|
|
219
|
+
}
|
|
220
|
+
function attachmentTargetKey(attachment) {
|
|
221
|
+
const targetType = attachmentTargetType(attachment);
|
|
222
|
+
return targetType === 'workflow-phase' ? `${attachment.workflow}:${attachment.phase}` : attachment.targetKey ?? '';
|
|
223
|
+
}
|
|
224
|
+
function isSkillAttachmentTargetType(value) {
|
|
225
|
+
return value === 'agent' || value === 'subagent' || value === 'workflow-phase' || value === 'provider-adapter' || value === 'intent-signal';
|
|
203
226
|
}
|
|
204
227
|
function validationFailure(message) {
|
|
205
228
|
return { ok: false, error: { code: 'validation_failed', message } };
|