vgxness 1.13.0 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ This package is proprietary software. The npm package ships inspectable JavaScri
8
8
 
9
9
  OpenCode is the primary supported provider. Claude setup support is secondary. VGX-managed OpenCode and Claude provider configuration is user-global only; provider config writes require explicit CLI confirmation.
10
10
 
11
- VGXNESS v1.10.x has a current project health audit describing the implemented CLI/MCP/SDD control plane, OpenCode health, known docs drift, interrupted runs, and the remaining execution/recovery gaps. See [Project health audit v1.10.x](./docs/project-health-audit-v1.10.x.md) for the current system snapshot; [v1.9.1](./docs/project-health-audit-v1.9.1.md) remains historical validation evidence for that release.
11
+ VGXNESS v1.14.x has a current project health audit describing the implemented CLI/MCP/SDD control plane, OpenCode-first workflow, release-readiness checks, and the remaining execution/recovery gaps. See [Project health audit v1.14.x](./docs/project-health-audit-v1.14.x.md) for the current system snapshot; [v1.10.x](./docs/project-health-audit-v1.10.x.md) and [v1.9.1](./docs/project-health-audit-v1.9.1.md) remain historical validation evidence for those releases.
12
12
 
13
13
  ## Requirements
14
14
 
@@ -256,6 +256,7 @@ Remove any user-global OpenCode/Claude config entries and local/global VGXNESS d
256
256
  ## More docs
257
257
 
258
258
  - [CLI reference](./docs/cli.md)
259
+ - [Project health audit v1.14.x](./docs/project-health-audit-v1.14.x.md)
259
260
  - [Project health audit v1.10.x](./docs/project-health-audit-v1.10.x.md)
260
261
  - [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md)
261
262
  - [Architecture](./docs/architecture.md)
@@ -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
  },
@@ -99,8 +100,13 @@ export class SkillResolver {
99
100
  });
100
101
  }
101
102
  resolveAgent(input) {
102
- if (input.agentId !== undefined)
103
- return this.agents.getById(input.agentId);
103
+ if (input.agentId !== undefined) {
104
+ const byId = this.agents.getById(input.agentId);
105
+ if (byId.ok || input.project === undefined)
106
+ return byId;
107
+ const byName = this.agents.getByName(input.project, input.scope ?? 'project', input.agentId);
108
+ return byName.ok ? byName : byId;
109
+ }
104
110
  if (input.agentName === undefined)
105
111
  return ok(undefined);
106
112
  if (input.project === undefined)
@@ -121,6 +127,8 @@ export class SkillResolver {
121
127
  }
122
128
  for (const phase of phases)
123
129
  targets.push({ targetType: 'workflow-phase', targetKey: phase });
130
+ for (const signal of normalizedIntentSignals(input.intentSignals))
131
+ targets.push({ targetType: 'intent-signal', targetKey: signal });
124
132
  if (input.providerAdapter !== undefined)
125
133
  targets.push({ targetType: 'provider-adapter', targetKey: input.providerAdapter });
126
134
  return dedupeTargets(targets);
@@ -229,6 +237,9 @@ function phaseTargetKeys(workflow, phase) {
229
237
  function isSddWorkflow(workflow) {
230
238
  return workflow?.trim().toLowerCase() === 'sdd';
231
239
  }
240
+ function normalizedIntentSignals(signals) {
241
+ return [...new Set((signals ?? []).map((signal) => signal.trim().toLowerCase()).filter(Boolean))];
242
+ }
232
243
  function stringMetadata(value) {
233
244
  return typeof value === 'string' && value.trim() ? value : undefined;
234
245
  }
@@ -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 } };
@@ -1,6 +1,6 @@
1
1
  # vgxness Architecture
2
2
 
3
- > **Scope:** this document describes the current v1.10.x architecture as it is actually built. It is the source of truth for how the product works today, with the latest health snapshot captured in [Project health audit v1.10.x](./project-health-audit-v1.10.x.md). Planned work that is not yet shipped lives in [Roadmap](./roadmap.md); historical planning context that no longer reflects reality has been retired. Where this doc disagrees with code, code wins — file a doc-sync task against the relevant module.
3
+ > **Scope:** this document describes the current v1.14.x architecture as it is actually built. It is the source of truth for how the product works today, with the latest health snapshot captured in [Project health audit v1.14.x](./project-health-audit-v1.14.x.md). Planned work that is not yet shipped lives in [Roadmap](./roadmap.md); historical planning context that no longer reflects reality has been retired. Where this doc disagrees with code, code wins — file a doc-sync task against the relevant module.
4
4
 
5
5
  `vgxness` is a local-first, provider-agnostic, Gentle-AI-like harness for agentic development. Its core architecture separates the product domain from provider-specific tooling so agents, skills, memory, SDD workflows, runs, and traces can work across OpenCode, Claude Code, and future adapters such as Pi.
6
6
 
@@ -387,12 +387,13 @@ Skill improvement proposals are the current controlled foundation for self-impro
387
387
  CLI examples:
388
388
 
389
389
  ```bash
390
- vgxness skills register --project vgxness --name sdd-apply --description "Applies SDD tasks"
391
- vgxness skills add-version --project vgxness --name sdd-apply --version 1.0.0 --source-kind path --source-path .config/opencode/skills/sdd-apply/SKILL.md --activate
392
- vgxness skills attach --project vgxness --name sdd-apply --target-type workflow-phase --target-key sdd:apply
393
- vgxness skills resolve --agent apply-agent --project vgxness --workflow sdd --phase apply --provider opencode
394
- vgxness skills payload --agent apply-agent --project vgxness --workflow sdd --phase apply --provider opencode
395
- vgxness skills propose --project vgxness --name sdd-apply --proposed-version 1.1.0 --source-kind inline --inline-metadata '{"content":"Updated skill"}' --rationale "Repeated correction from trace"
390
+ vgxness skills get --project vgxness --name vgxness-sdd-apply
391
+ vgxness skills resolve --project vgxness --workflow sdd --phase apply-progress --intent-signals git,tdd,review-size --provider opencode
392
+ vgxness skills payload --project vgxness --workflow sdd --phase apply-progress --intent-signals git,tdd,review-size --provider opencode
393
+ vgxness skills register --project vgxness --name team-review-guidance --description "Team-specific review guidance"
394
+ vgxness skills add-version --project vgxness --name team-review-guidance --version 1.0.0 --source-kind inline --inline-metadata '{"content":"Check product risk, review size, and rollback notes."}' --activate
395
+ vgxness skills attach --project vgxness --name team-review-guidance --target-type intent-signal --target-key review-size
396
+ vgxness skills propose --project vgxness --name team-review-guidance --proposed-version 1.1.0 --source-kind inline --inline-metadata '{"content":"Revised team guidance."}' --rationale "Repeated correction from trace"
396
397
  vgxness skills submit-proposal --proposal <proposal-id> --actor uziel
397
398
  vgxness skills approve-proposal --proposal <proposal-id> --actor uziel --reason "Reviewed diff"
398
399
  vgxness skills apply-proposal --proposal <proposal-id> --actor uziel
@@ -470,7 +471,7 @@ The envelope is always `installable:false` and `readOnly:true`. It does **not**
470
471
 
471
472
  ### OpenCode manager orchestration
472
473
 
473
- The checked-in OpenCode default config and `seeds/agents/agent-seed-v1.json` define `vgxness-manager` as an MCP-first orchestrator. The manager should use `vgxness_session_restore` with the project and workspace directory when starting, resuming, or recovering context, then use SDD artifact/status tools, memory tools, agent resolution, run/preflight tools, and read-only payload/profile previews before delegating substantial phase work to exact SDD subagents. Before ending, pausing, handing off, or compacting, it should call `vgxness_session_close` with the current session id, actor `manager`, and an actionable summary; if no current session id is available, it must not invent one and should preserve the summary in its final response. Its OpenCode `permission.task` remains deny-by-default: `*` is denied and only the canonical `vgxness-sdd-*` subagent names are allowed explicitly. The seed/default prompts are self-contained and do not require external `~/.config/opencode/skills/sdd-*` files; such skills are optional registry assets if a project registers them separately.
474
+ The checked-in OpenCode default config and `seeds/agents/agent-seed-v1.json` define `vgxness-manager` as an MCP-first orchestrator. The manager should use `vgxness_session_restore` with the project and workspace directory when starting, resuming, or recovering context, then use SDD artifact/status tools, memory tools, agent resolution, run/preflight tools, and read-only payload/profile previews before delegating substantial phase work to exact SDD subagents. Before ending, pausing, handing off, or compacting, it should call `vgxness_session_close` with the current session id, actor `manager`, and an actionable summary; if no current session id is available, it must not invent one and should preserve the summary in its final response. Its OpenCode `permission.task` remains deny-by-default: `*` is denied and only the canonical `vgxness-sdd-*` subagent names are allowed explicitly. The seed/default prompts are self-contained and do not require external provider-native skill files; optional guidance should live in VGXNESS registry assets unless a future provider-native integration is explicitly designed.
474
475
 
475
476
  ## Run lifecycle
476
477