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.
@@ -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 = 10;
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: registryManagerInstructionsV10 },
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
- Daily SDD happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for normal flow. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
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 exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` are preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior 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
179
- - Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` are read-only reports.
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 registryManagerInstructionsV10 = [
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.',
@@ -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 provider-adapter`);
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 provider-adapter`);
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';
@@ -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
- return input.ok ? jsonResult(registry.buildSkillPayload(input.value, { workspaceRoot: environment.cwd })) : resultFailure(input);
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));
@@ -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(),
@@ -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 buildPreviewActions(input, flow, needsClarification) {
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 targetKey = `${attachment.workflow}:${attachment.phase}`;
78
- const key = `${attachment.skill}:workflow-phase:${targetKey}`;
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: 'workflow-phase',
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.workflow}:${attachment.phase}:${attachment.skill}`;
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.workflow?.trim())
163
- return validationFailure('Skill seed attachment workflow is required');
164
- if (!attachment.phase?.trim())
165
- return validationFailure('Skill seed attachment phase is required');
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: ['workflow-phase'],
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
- return {
204
+ const metadata = {
197
205
  ...(attachment.metadata ?? {}),
198
206
  ...(attachment.order !== undefined ? { order: attachment.order } : {}),
199
207
  seededBy: 'vgxness',
200
- workflow: attachment.workflow,
201
- phase: attachment.phase,
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 } };