vgxness 1.20.14 → 1.21.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.
Files changed (161) hide show
  1. package/README.md +13 -10
  2. package/dist/adapters/claude/install/claude-code-agent-config.js +1 -1
  3. package/dist/adapters/claude/install/claude-code-project-memory.js +1 -1
  4. package/dist/adapters/claude/install/claude-code-user-memory.js +1 -1
  5. package/dist/adapters/claude/install/client-install-claude-code-contract.js +1 -1
  6. package/dist/adapters/opencode/install/client-install-opencode-contract.js +2 -2
  7. package/dist/adapters/opencode/install/client-install-opencode.js +2 -2
  8. package/dist/adapters/opencode/install/opencode-default-agent-config.js +2 -2
  9. package/dist/application/agents/agent-resolver.js +10 -2
  10. package/dist/application/knowledge-workspace/canvas-renderer.js +36 -0
  11. package/dist/application/knowledge-workspace/filesystem-workspace-port.js +98 -0
  12. package/dist/application/knowledge-workspace/markdown-renderer.js +68 -0
  13. package/dist/application/knowledge-workspace/obsidian-export-service.js +157 -0
  14. package/dist/application/knowledge-workspace/obsidian-mcp-workspace-port.js +109 -0
  15. package/dist/application/knowledge-workspace/workspace-port.js +20 -0
  16. package/dist/application/memory/import/observation-writer.js +2 -2
  17. package/dist/application/memory/import/package.js +1 -1
  18. package/dist/application/provider-setup/provider-change-plan.js +1 -1
  19. package/dist/application/provider-setup/provider-doctor.js +1 -1
  20. package/dist/application/runs/run-service.js +1 -1
  21. package/dist/application/sdd/sdd-continuation-plan.js +4 -3
  22. package/dist/application/skills/skill-registry-service.js +1 -1
  23. package/dist/application/skills/skill-resolver.js +3 -3
  24. package/dist/cli/bun-bin.js +1 -1
  25. package/dist/domain/agents/canonical-agent-manifest.js +13 -13
  26. package/dist/domain/knowledge-workspace/schema.js +1 -0
  27. package/dist/governance/governance-report-builder.js +1 -1
  28. package/dist/interfaces/cli/bun-bin.js +1 -1
  29. package/dist/interfaces/cli/cli-flags.js +2 -2
  30. package/dist/interfaces/cli/cli-help.js +11 -5
  31. package/dist/interfaces/cli/cli-helpers.js +2 -2
  32. package/dist/interfaces/cli/commands/agent-skill-dispatcher.js +10 -10
  33. package/dist/interfaces/cli/commands/index.js +1 -0
  34. package/dist/interfaces/cli/commands/knowledge-workspace-dispatcher.js +252 -0
  35. package/dist/interfaces/cli/commands/mcp-dispatcher.js +3 -3
  36. package/dist/interfaces/cli/commands/memory-sdd-dispatcher.js +11 -11
  37. package/dist/interfaces/cli/commands/run-permission-dispatcher.js +4 -4
  38. package/dist/interfaces/cli/commands/setup-dispatcher.js +3 -3
  39. package/dist/interfaces/cli/commands/status-dispatcher.js +4 -4
  40. package/dist/interfaces/cli/commands/verification-dispatcher.js +2 -2
  41. package/dist/interfaces/cli/commands/workflow-dispatcher.js +4 -4
  42. package/dist/interfaces/cli/dispatcher.js +10 -2
  43. package/dist/interfaces/cli/home-tui-app.js +2 -2
  44. package/dist/interfaces/cli/home-tui-controller.js +8 -8
  45. package/dist/interfaces/cli/mcp-start-path.js +1 -1
  46. package/dist/interfaces/cli/sdd-renderer.js +8 -5
  47. package/dist/interfaces/cli/setup-tui-controller.js +1 -1
  48. package/dist/interfaces/cli/verification-plan-renderer.js +2 -0
  49. package/dist/interfaces/cli/verification-report-renderer.js +1 -1
  50. package/dist/interfaces/mcp/control-plane/handlers/memory.js +1 -1
  51. package/dist/interfaces/mcp/control-plane/handlers/runs.js +1 -1
  52. package/dist/interfaces/mcp/control-plane/handlers/sdd.js +3 -3
  53. package/dist/interfaces/mcp/control-plane/handlers/sessions.js +1 -1
  54. package/dist/interfaces/mcp/control-plane.js +13 -13
  55. package/dist/interfaces/mcp/schemas/common.js +1 -1
  56. package/dist/interfaces/mcp/stdio-server.js +1 -1
  57. package/dist/interfaces/mcp/validation/common.js +1 -1
  58. package/dist/interfaces/mcp/validation/runs.js +1 -1
  59. package/dist/mcp/control-plane-snapshot-service.js +1 -1
  60. package/dist/mcp/doctor.js +1 -1
  61. package/dist/mcp/provider-canonical-agent-manifest.js +2 -2
  62. package/dist/mcp/resume-context-gate-service.js +1 -1
  63. package/dist/permissions/policy-evaluator.js +1 -1
  64. package/dist/providers/opencode/injection-preview.js +1 -1
  65. package/dist/providers/opencode/manager-payload.js +2 -2
  66. package/dist/setup/setup-plan.js +1 -1
  67. package/dist/status/product-status.js +2 -1
  68. package/dist/verification/verification-plan-service.js +5 -1
  69. package/dist/verification/verification-report-service.js +1 -1
  70. package/dist/workflows/command-allowlist-adapter.js +1 -1
  71. package/docs/architecture.md +19 -19
  72. package/docs/cli.md +15 -2
  73. package/docs/glossary.md +2 -2
  74. package/docs/knowledge-workspace.md +135 -0
  75. package/docs/mcp.md +4 -1
  76. package/docs/module-boundaries.md +0 -2
  77. package/docs/obsidian-mcp.md +130 -0
  78. package/docs/providers.md +6 -6
  79. package/docs/roadmap.md +4 -4
  80. package/docs/safety.md +2 -2
  81. package/docs/sdd/sqlite-obsidian-knowledge-workflow/design.md +118 -0
  82. package/docs/sdd/sqlite-obsidian-knowledge-workflow/proposal.md +80 -0
  83. package/docs/sdd/sqlite-obsidian-knowledge-workflow/spec.md +88 -0
  84. package/docs/sdd/sqlite-obsidian-knowledge-workflow/tasks.md +70 -0
  85. package/docs/sdd-flow.es.md +23 -19
  86. package/docs/sdd-flow.md +15 -20
  87. package/docs/storage.md +2 -0
  88. package/package.json +7 -3
  89. package/dist/agents/agent-activation-service.js +0 -1
  90. package/dist/agents/agent-lookup-contexts.js +0 -1
  91. package/dist/agents/agent-registry-service.js +0 -1
  92. package/dist/agents/agent-resolver.js +0 -1
  93. package/dist/agents/agent-seed-service.js +0 -1
  94. package/dist/agents/agent-seed-upgrade-service.js +0 -1
  95. package/dist/agents/agent-selector-resolver.js +0 -1
  96. package/dist/agents/boot-upgrade.js +0 -1
  97. package/dist/agents/canonical-agent-manifest.js +0 -1
  98. package/dist/agents/canonical-agent-projection.js +0 -1
  99. package/dist/agents/manager-profile-overlay-service.js +0 -1
  100. package/dist/agents/profile-model-routing.js +0 -1
  101. package/dist/agents/renderers/claude-renderer.js +0 -1
  102. package/dist/agents/renderers/index.js +0 -1
  103. package/dist/agents/renderers/json-renderer.js +0 -1
  104. package/dist/agents/renderers/opencode-renderer.js +0 -1
  105. package/dist/agents/renderers/provider-adapter.js +0 -1
  106. package/dist/agents/repositories/agent-seed-history.js +0 -1
  107. package/dist/agents/repositories/agents.js +0 -1
  108. package/dist/agents/repositories/manager-profile-overlays.js +0 -1
  109. package/dist/agents/schema.js +0 -1
  110. package/dist/memory/active-work-preview.js +0 -1
  111. package/dist/memory/active-work-topics.js +0 -1
  112. package/dist/memory/agent-memory-capture-service.js +0 -1
  113. package/dist/memory/import/dry-run-planner.js +0 -1
  114. package/dist/memory/import/index.js +0 -1
  115. package/dist/memory/import/observation-writer.js +0 -1
  116. package/dist/memory/import/package.js +0 -1
  117. package/dist/memory/memory-capture-schema.js +0 -1
  118. package/dist/memory/memory-service.js +0 -1
  119. package/dist/memory/repositories/artifacts.js +0 -1
  120. package/dist/memory/repositories/observations.js +0 -1
  121. package/dist/memory/repositories/sessions.js +0 -1
  122. package/dist/memory/repositories/traces.js +0 -1
  123. package/dist/memory/schema.js +0 -1
  124. package/dist/memory/search.js +0 -1
  125. package/dist/memory/sqlite/database.js +0 -1
  126. package/dist/memory/storage-paths.js +0 -1
  127. package/dist/permissions/index.js +0 -2
  128. package/dist/runs/execution-planning.js +0 -1
  129. package/dist/runs/operation-execution.js +0 -1
  130. package/dist/runs/operation-retry.js +0 -1
  131. package/dist/runs/repositories/runs.js +0 -1
  132. package/dist/runs/repositories/task-scoped-command-grants.js +0 -1
  133. package/dist/runs/resume-after-approval-result.js +0 -1
  134. package/dist/runs/run-insights.js +0 -1
  135. package/dist/runs/run-service.js +0 -1
  136. package/dist/runs/run-snapshot-export-service.js +0 -1
  137. package/dist/runs/sandbox-process-execution.js +0 -1
  138. package/dist/runs/sandbox-worktree-planning.js +0 -1
  139. package/dist/runs/schema.js +0 -1
  140. package/dist/runs/task-scoped-command-grant-service.js +0 -1
  141. package/dist/sdd/artifact-portability-service.js +0 -1
  142. package/dist/sdd/cockpit-read-model.js +0 -1
  143. package/dist/sdd/cockpit-types.js +0 -1
  144. package/dist/sdd/schema.js +0 -1
  145. package/dist/sdd/sdd-continuation-plan.js +0 -1
  146. package/dist/sdd/sdd-workflow-service.js +0 -1
  147. package/dist/setup/index.js +0 -3
  148. package/dist/skills/boot-seed.js +0 -1
  149. package/dist/skills/personal-skills.js +0 -1
  150. package/dist/skills/repositories/skill-evaluation-requests.js +0 -1
  151. package/dist/skills/repositories/skill-evaluation-scenarios.js +0 -1
  152. package/dist/skills/repositories/skill-improvement-proposals.js +0 -1
  153. package/dist/skills/repositories/skills.js +0 -1
  154. package/dist/skills/schema.js +0 -1
  155. package/dist/skills/skill-export-service.js +0 -1
  156. package/dist/skills/skill-index-service.js +0 -1
  157. package/dist/skills/skill-payload.js +0 -1
  158. package/dist/skills/skill-registry-service.js +0 -1
  159. package/dist/skills/skill-resolver.js +0 -1
  160. package/dist/skills/skill-seed-service.js +0 -1
  161. package/dist/skills/skill-status-service.js +0 -1
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # VGXNESS
2
2
 
3
- VGXNESS is an installable CLI and MCP control plane for guided AI-agent workflows, SDD artifacts, local memory, and OpenCode integration.
3
+ VGXNESS is an installable, local-first governance OS and MCP/CLI control plane for existing coding agents. It governs SDD artifacts, local memory, runs, permissions, skills, provider setup, and verification evidence while OpenCode, Claude Code, and future adapters keep executing provider-native.
4
4
 
5
5
  ## Release status and license
6
6
 
7
7
  This package is proprietary software. The npm package ships inspectable JavaScript (`dist/`) so Node can run it, but it is **not open-source licensed** and may not be redistributed unless you have written permission. See [LICENSE](./LICENSE).
8
8
 
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.
9
+ OpenCode is the primary supported provider. Claude Code setup support is secondary/preview guidance. VGX-managed OpenCode and Claude provider configuration is user-global only; provider config writes require explicit CLI confirmation.
10
10
 
11
- VGXNESS is currently versioned from `package.json` (`1.20.0`). The latest full project health audit is the historical [v1.14.x snapshot](./docs/project-health-audit-v1.14.x.md), which documents the implemented CLI/MCP/SDD control plane, OpenCode-first workflow, release-readiness checks, and the remaining execution/recovery gaps at that point in time. [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.
11
+ VGXNESS is versioned from `package.json`. The latest full project health audit is the historical [v1.14.x snapshot](./docs/project-health-audit-v1.14.x.md), which documents the implemented CLI/MCP/SDD governance control plane, OpenCode-first workflow, release-readiness checks, and the remaining execution/recovery gaps at that point in time. [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
 
@@ -163,7 +163,7 @@ vgxness setup apply --yes
163
163
 
164
164
  `vgxness setup plan` and `vgxness setup status` are human-readable and do not write provider config by default; local VGXNESS store initialization may occur when a command needs the selected SQLite store. `vgxness doctor`, `vgxness status`, `vgxness next`, `vgxness sdd status`, `vgxness sdd next`, `vgxness sdd continue`, and `vgxness sdd accept-artifact` are also human-readable by default. Pass `--json` when you need parseable automation output. `vgxness setup apply --yes` is the explicit provider-config write path.
165
165
 
166
- The daily SDD happy path is OpenCode conversation with the installed VGXNESS MCP server and hidden SDD subagents. Use CLI commands for bootstrap, setup, diagnostics, recovery, fallback, and scripting: `status`, `next`, and `sdd continue` inspect state and print read-only continuation guidance; `resume` helps inspect interrupted work. After a draft is ready, `sdd accept-artifact` records explicit human acceptance, and `sdd reopen-artifact` returns rejected artifacts to draft.
166
+ The daily SDD happy path is an OpenCode conversation with the installed VGXNESS MCP server and provider-native hidden SDD subagents. VGXNESS supplies the governance state and guardrails; OpenCode executes the agent loop. Use CLI commands for bootstrap, setup, diagnostics, recovery, fallback, and scripting: `status`, `next`, and `sdd continue` inspect state and print read-only continuation guidance; `resume` helps inspect interrupted work. In the normal organic flow, proposal acceptance is the first mandatory human continue gate; after that, `spec -> design -> tasks` can be drafted without separate confirmations, then VGXNESS asks before apply/implementation. `sdd accept-artifact` records explicit human-only acceptance when a real gate applies, and `sdd reopen-artifact` returns rejected artifacts to draft.
167
167
 
168
168
  Copy this prompt into OpenCode for normal daily SDD work:
169
169
 
@@ -171,24 +171,25 @@ Copy this prompt into OpenCode for normal daily SDD work:
171
171
  Continue VGXNESS SDD for project <project> and change <change>.
172
172
  Use the VGXNESS MCP tools to inspect status/readiness/artifacts, then delegate phase work to the exact hidden SDD subagent when needed.
173
173
  Save draft artifacts when appropriate and report evidence, tests, blockers, and the next human decision.
174
- Do not accept or reopen artifacts for me; ask me before any human-only, provider-config, git, destructive, external, install, or secret-sensitive action.
174
+ Treat proposal acceptance as the first mandatory human continue gate; after that, draft spec/design/tasks without separate confirmations, then ask before apply/implementation.
175
+ Do not accept or reopen artifacts for me; ask before provider/global config, git/publish, destructive, external, privileged, install, network, secret-sensitive, or official archive actions.
175
176
  ```
176
177
 
177
178
  ## Code runtime status
178
179
 
179
- The experimental `vgxness code` runtime and principal OpenTUI code surface have been removed temporarily because they were copied OpenCode-like runtime work. Continue SDD work in OpenCode through VGXNESS MCP, and use the safe CLI commands above for diagnostics and recovery.
180
+ The experimental `vgxness code` runtime and principal OpenTUI code surface have been removed and are not the default product direction. Continue SDD work in OpenCode through VGXNESS MCP, and use the safe CLI commands above for diagnostics and recovery. Reintroducing a first-party coding-agent runtime would require a separate accepted SDD change.
180
181
 
181
182
  ## Safety model
182
183
 
183
184
  - Preview, status, and plan commands do not write provider config; local VGXNESS store initialization may occur where the command needs SQLite-backed state.
184
185
  - VGX-managed OpenCode and Claude provider config writes are user-global only and require explicit `--yes` confirmation.
185
186
  - Runtime VGXNESS state remains project-aware; project-local provider files are read-only external/manual diagnostics and may still affect provider behavior outside VGXNESS.
186
- - Setup/status TUI surfaces are read-only setup, diagnostics, recovery, and fallback surfaces; daily SDD progression stays in OpenCode with VGXNESS MCP.
187
+ - Setup/status TUI surfaces are read-only setup, diagnostics, recovery, and fallback surfaces; daily SDD progression stays in OpenCode with VGXNESS MCP and provider-native execution.
187
188
  - SDD artifacts are SQLite-backed through VGXNESS services. Do not create or write `openspec/`.
188
- - `vgxness sdd accept-artifact` records explicit human-only acceptance; saving a draft never implies acceptance.
189
+ - `vgxness sdd accept-artifact` records explicit human-only acceptance; saving a draft never implies acceptance, and normal spec/design/tasks draft autorun after proposal acceptance is not silent acceptance.
189
190
  - `vgxness sdd reopen-artifact` is the explicit path from a rejected artifact back to draft before revision.
190
- - OpenCode is the primary supported provider; other providers are preview/manual extension points.
191
- - VGXNESS exposes 42 typed MCP tools across SDD, memory, sessions, agents, skills, runs, providers, and verification. See [MCP tools](./docs/mcp.md).
191
+ - OpenCode is the primary supported provider; Claude Code is secondary/preview guidance; other providers are preview/manual extension points.
192
+ - VGXNESS exposes 63 typed MCP tools across SDD, memory, sessions, agents, skills, runs, providers, and verification. See [MCP tools](./docs/mcp.md).
192
193
 
193
194
  ## Principal entrypoint
194
195
 
@@ -269,6 +270,8 @@ Remove any user-global OpenCode/Claude config entries and local/global VGXNESS d
269
270
  - [Project health audit v1.10.x](./docs/project-health-audit-v1.10.x.md)
270
271
  - [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md)
271
272
  - [Architecture](./docs/architecture.md)
273
+ - [Knowledge Workspace](./docs/knowledge-workspace.md)
274
+ - [Obsidian MCP integration](./docs/obsidian-mcp.md)
272
275
  - [MCP tools](./docs/mcp.md)
273
276
  - [Safety model](./docs/safety.md)
274
277
  - [Storage](./docs/storage.md)
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { join, parse, resolve } from 'node:path';
4
- import { projectCanonicalAgentManifestToClaudeCode } from '../../../agents/canonical-agent-projection.js';
4
+ import { projectCanonicalAgentManifestToClaudeCode } from '../../../application/agents/canonical-agent-projection.js';
5
5
  import { assertInsideWorkspace } from './claude-code-config.js';
6
6
  export const claudeCodeGeneratedMarker = 'VGXNESS-GENERATED';
7
7
  export function expectedClaudeCodeAgentFiles(input) {
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
- import { projectCanonicalAgentManifestToClaudeProjectMemory } from '../../../agents/canonical-agent-projection.js';
3
+ import { projectCanonicalAgentManifestToClaudeProjectMemory } from '../../../application/agents/canonical-agent-projection.js';
4
4
  import { assertInsideWorkspace } from './claude-code-config.js';
5
5
  export const claudeProjectMemoryBeginMarker = '<!-- BEGIN VGXNESS-MANAGED CLAUDE PROJECT MEMORY -->';
6
6
  export const claudeProjectMemoryEndMarker = '<!-- END VGXNESS-MANAGED CLAUDE PROJECT MEMORY -->';
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { projectCanonicalAgentManifestToClaudeProjectMemory } from '../../../agents/canonical-agent-projection.js';
3
+ import { projectCanonicalAgentManifestToClaudeProjectMemory } from '../../../application/agents/canonical-agent-projection.js';
4
4
  import { safeHomeDirectory } from './claude-code-agent-config.js';
5
5
  export const claudeUserMemoryBeginMarker = '<!-- BEGIN VGXNESS-MANAGED CLAUDE USER MEMORY -->';
6
6
  export const claudeUserMemoryEndMarker = '<!-- END VGXNESS-MANAGED CLAUDE USER MEMORY -->';
@@ -1,4 +1,4 @@
1
- import { withEffectiveManagerInstructions } from '../../../agents/canonical-agent-projection.js';
1
+ import { withEffectiveManagerInstructions } from '../../../application/agents/canonical-agent-projection.js';
2
2
  import { expectedClaudeCodeAgentFiles, inspectClaudeCodeAgents, renderClaudeCodeAgentMarkdown } from './claude-code-agent-config.js';
3
3
  import { buildClaudeCodeMcpAddCommand } from './claude-code-cli.js';
4
4
  import { createClaudeCodeMcpDoctorCommand, createClaudeCodeMcpServerConfig } from './claude-code-config.js';
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { canonicalCapabilitySubagentLegacyAliases } from '../../../agents/canonical-agent-manifest.js';
3
+ import { canonicalCapabilitySubagentLegacyAliases } from '../../../domain/agents/canonical-agent-manifest.js';
4
4
  import { createOpenCodeDefaultAgentConfig, createOpenCodeDefaultAgentInstallPlan, } from './opencode-default-agent-config.js';
5
5
  const opencodeConfigSchema = 'https://opencode.ai/config.json';
6
6
  const opencodeUserGlobalOnlyMessage = 'VGX-managed OpenCode configuration is user-global only. Project/local OpenCode files are treated as external/manual diagnostics and will not be written by VGXNESS. Re-run without the project/local install scope.';
@@ -201,7 +201,7 @@ function warningsForScope(_scope, overwriteVgxness, agentPlan, bashPermissionPol
201
201
  : [];
202
202
  const bashWarnings = bashPermissionPolicy.manager === 'deny'
203
203
  ? [
204
- 'OpenCode config sets top-level permission.bash = ask; manager read/search discovery tools (read/glob/grep) are allowed while manager bash/edit/write are denied; capability subagents keep explicit permissions plus legacy SDD task aliases. This is config-level evidence only and does not verify provider runtime enforcement.',
204
+ 'OpenCode config sets top-level permission.bash = ask; manager read/search discovery tools (read/glob/grep) are allowed while manager bash/edit/write are denied; capability subagents keep explicit canonical permissions only. This is config-level evidence only and does not verify provider runtime enforcement.',
205
205
  ]
206
206
  : ['OpenCode top-level permission.bash is set to ask.'];
207
207
  return [
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname } from 'node:path';
3
- import { canonicalCapabilitySubagentLegacyAliases } from '../../../agents/canonical-agent-manifest.js';
3
+ import { canonicalCapabilitySubagentLegacyAliases } from '../../../domain/agents/canonical-agent-manifest.js';
4
4
  import { createManagedProviderConfigBackup } from '../../../setup/backup-rollback-service.js';
5
5
  import { findConflictingVgxnessAgents, planOpenCodeMcpInstall, } from './client-install-opencode-contract.js';
6
6
  import { createOpenCodeDefaultAgentConfig, createOpenCodeDefaultAgentInstallPlan, vgxnessOpenCodeInstructionsPath, } from './opencode-default-agent-config.js';
@@ -220,7 +220,7 @@ function confirmationRequiredMessage(scope) {
220
220
  function warnings() {
221
221
  return [
222
222
  'Restart OpenCode after installation so it reloads the project MCP config.',
223
- 'OpenCode config sets top-level permission.bash = ask; manager read/search discovery tools (read/glob/grep) are allowed while manager bash/edit/write are denied; capability subagents keep explicit permissions plus legacy SDD task aliases. This is config-level evidence only and does not verify provider runtime enforcement.',
223
+ 'OpenCode config sets top-level permission.bash = ask; manager read/search discovery tools (read/glob/grep) are allowed while manager bash/edit/write are denied; capability subagents keep explicit canonical permissions only. This is config-level evidence only and does not verify provider runtime enforcement.',
224
224
  ];
225
225
  }
226
226
  function manualTest(databasePath, source) {
@@ -1,5 +1,5 @@
1
- import { canonicalDefaultAgentName, canonicalPromptContractVersion, canonicalSddSubagentNames, createCanonicalOpenCodeSddMcpToolPermissions, createCanonicalOpenCodeSddTaskPermissions, } from '../../../agents/canonical-agent-manifest.js';
2
- import { projectCanonicalAgentManifestToOpenCode, withEffectiveOpenCodeManagerInstructions, } from '../../../agents/canonical-agent-projection.js';
1
+ import { canonicalDefaultAgentName, canonicalPromptContractVersion, canonicalSddSubagentNames, createCanonicalOpenCodeSddMcpToolPermissions, createCanonicalOpenCodeSddTaskPermissions, } from '../../../domain/agents/canonical-agent-manifest.js';
2
+ import { projectCanonicalAgentManifestToOpenCode, withEffectiveOpenCodeManagerInstructions, } from '../../../application/agents/canonical-agent-projection.js';
3
3
  export const vgxnessOpenCodeDefaultAgent = canonicalDefaultAgentName;
4
4
  export const vgxnessOpenCodePromptContractVersion = canonicalPromptContractVersion;
5
5
  export const vgxnessOpenCodeInstructionsPath = 'AGENTS.md';
@@ -1,4 +1,5 @@
1
- import { normalizeSddPhaseInput } from '../../sdd/schema.js';
1
+ import { canonicalCapabilitySubagentLegacyAliases } from '../../domain/agents/canonical-agent-manifest.js';
2
+ import { normalizeSddPhaseInput } from '../../domain/sdd/schema.js';
2
3
  import { agentLookupContexts } from './agent-lookup-contexts.js';
3
4
  export class AgentResolver {
4
5
  loadAgents;
@@ -12,7 +13,7 @@ export class AgentResolver {
12
13
  const normalized = normalizeInput(input);
13
14
  const candidates = [];
14
15
  const skipped = [];
15
- for (const agent of agents.value) {
16
+ for (const agent of suppressLegacyAliasAgents(agents.value)) {
16
17
  const skipReasons = hardSkipReasons(agent, normalized);
17
18
  if (skipReasons.length > 0) {
18
19
  skipped.push(toSkipped(agent, skipReasons));
@@ -44,6 +45,13 @@ export class AgentResolver {
44
45
  });
45
46
  }
46
47
  }
48
+ function suppressLegacyAliasAgents(agents) {
49
+ const names = new Set(agents.map((agent) => agent.name));
50
+ return agents.filter((agent) => {
51
+ const canonical = canonicalCapabilitySubagentLegacyAliases[agent.name];
52
+ return canonical === undefined || !names.has(canonical);
53
+ });
54
+ }
47
55
  const SDD_PHASE_SEMANTICS = [
48
56
  {
49
57
  name: 'sdd-planning',
@@ -0,0 +1,36 @@
1
+ export function renderObsidianCanvas(input) {
2
+ const nodes = [...input.nodes].sort(compareById).map(mapNode);
3
+ const edges = [...input.edges].sort(compareById).map(mapEdge);
4
+ return `${JSON.stringify({ nodes, edges }, null, 2)}\n`;
5
+ }
6
+ function mapNode(node) {
7
+ const base = {
8
+ id: node.id,
9
+ x: node.x,
10
+ y: node.y,
11
+ width: node.width ?? 360,
12
+ height: node.height ?? 220,
13
+ };
14
+ if (node.type === 'file') {
15
+ if (node.file === undefined || node.file.trim() === '') {
16
+ throw new Error(`Canvas file node requires a file path: ${node.id}`);
17
+ }
18
+ return { ...base, type: 'file', file: node.file };
19
+ }
20
+ return { ...base, type: 'text', text: node.label };
21
+ }
22
+ function mapEdge(edge) {
23
+ const mapped = {
24
+ id: edge.id,
25
+ fromNode: edge.fromNode,
26
+ fromSide: edge.fromSide ?? 'right',
27
+ toNode: edge.toNode,
28
+ toSide: edge.toSide ?? 'left',
29
+ };
30
+ if (edge.label !== undefined)
31
+ mapped.label = edge.label;
32
+ return mapped;
33
+ }
34
+ function compareById(a, b) {
35
+ return a.id.localeCompare(b.id);
36
+ }
@@ -0,0 +1,98 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import { assertSafeWorkspaceRelativePath, isGeneratedByVgxness, normalizeWorkspacePath, } from './workspace-port.js';
4
+ export class FilesystemWorkspacePort {
5
+ vaultRoot;
6
+ workspaceRoot;
7
+ absoluteWorkspaceRoot;
8
+ constructor(options) {
9
+ this.vaultRoot = resolve(options.vaultRoot);
10
+ this.workspaceRoot = normalizeWorkspacePath(options.workspaceRoot ?? 'VGXNESS');
11
+ this.absoluteWorkspaceRoot = resolve(this.vaultRoot, this.workspaceRoot);
12
+ }
13
+ async status() {
14
+ return {
15
+ kind: 'knowledge-workspace-status',
16
+ status: existsSync(this.absoluteWorkspaceRoot) ? 'available' : 'unavailable',
17
+ mode: 'filesystem',
18
+ workspaceRoot: this.absoluteWorkspaceRoot,
19
+ diagnostics: existsSync(this.absoluteWorkspaceRoot) ? [] : ['Workspace root does not exist yet.'],
20
+ safety: { readOnly: true, writesNotes: false, deletesNotes: false, executesObsidianCommands: false },
21
+ };
22
+ }
23
+ async listNotes(input) {
24
+ const absolute = this.resolveContained(input.pathPrefix, false);
25
+ if (!existsSync(absolute))
26
+ return [];
27
+ const stat = statSync(absolute);
28
+ if (stat.isFile())
29
+ return [{ path: this.toWorkspacePath(absolute), type: 'file' }];
30
+ const entries = readdirSync(absolute, { withFileTypes: true });
31
+ const refs = [];
32
+ for (const entry of entries) {
33
+ const child = join(absolute, entry.name);
34
+ if (entry.isDirectory()) {
35
+ refs.push({ path: this.toWorkspacePath(child), type: 'directory' });
36
+ if (input.recursive ?? true)
37
+ refs.push(...(await this.listNotes({ pathPrefix: this.toWorkspacePath(child), recursive: true })));
38
+ }
39
+ else {
40
+ refs.push({ path: this.toWorkspacePath(child), type: 'file' });
41
+ }
42
+ }
43
+ return refs.sort((a, b) => a.path.localeCompare(b.path));
44
+ }
45
+ async getNote(input) {
46
+ const absolute = this.resolveContained(input.path, false);
47
+ return { path: this.toWorkspacePath(absolute), content: readFileSync(absolute, 'utf8') };
48
+ }
49
+ async writeGeneratedNote(input) {
50
+ const absolute = this.resolveContained(input.path, true);
51
+ const exists = existsSync(absolute);
52
+ const previousContent = exists ? readFileSync(absolute, 'utf8') : undefined;
53
+ if (previousContent !== undefined && input.overwriteGeneratedOnly && !isGeneratedByVgxness(previousContent)) {
54
+ throw new Error(`Refusing to overwrite non-generated Obsidian note: ${this.toWorkspacePath(absolute)}`);
55
+ }
56
+ mkdirSync(dirname(absolute), { recursive: true });
57
+ writeFileSync(absolute, input.content, 'utf8');
58
+ const output = {
59
+ path: this.toWorkspacePath(absolute),
60
+ created: !exists,
61
+ currentSizeInBytes: Buffer.byteLength(input.content),
62
+ };
63
+ if (previousContent !== undefined)
64
+ output.previousSizeInBytes = Buffer.byteLength(previousContent);
65
+ return output;
66
+ }
67
+ async patchNote(input) {
68
+ const note = await this.getNote({ path: input.path });
69
+ const marker = `## ${input.section}`;
70
+ if (!note.content.includes(marker))
71
+ throw new Error(`Section not found: ${input.section}`);
72
+ const nextContent = applySimpleHeadingPatch(note.content, marker, input.operation, input.content);
73
+ return this.writeGeneratedNote({ path: note.path, content: nextContent, overwriteGeneratedOnly: false });
74
+ }
75
+ resolveContained(path, allowNonexistentLeaf) {
76
+ const relativePath = assertSafeWorkspaceRelativePath(path, this.workspaceRoot);
77
+ const absolute = resolve(this.vaultRoot, relativePath);
78
+ if (!isPathInside(absolute, this.absoluteWorkspaceRoot))
79
+ throw new Error(`Path must stay under ${this.absoluteWorkspaceRoot}: ${path}`);
80
+ if (!allowNonexistentLeaf && !existsSync(absolute))
81
+ throw new Error(`Note not found: ${relativePath}`);
82
+ return absolute;
83
+ }
84
+ toWorkspacePath(absolutePath) {
85
+ const relativePath = relative(this.vaultRoot, absolutePath).replaceAll(sep, '/');
86
+ return normalizeWorkspacePath(relativePath);
87
+ }
88
+ }
89
+ function applySimpleHeadingPatch(content, marker, operation, patch) {
90
+ if (operation === 'prepend')
91
+ return content.replace(marker, `${patch}\n${marker}`);
92
+ if (operation === 'append')
93
+ return content.replace(marker, `${marker}\n${patch}`);
94
+ return content.replace(marker, patch);
95
+ }
96
+ function isPathInside(path, root) {
97
+ return path === root || path.startsWith(`${root}${sep}`);
98
+ }
@@ -0,0 +1,68 @@
1
+ export function renderGeneratedFrontmatter(metadata) {
2
+ return [
3
+ '---',
4
+ 'generated_by: vgxness',
5
+ `source_of_truth: ${metadata.sourceOfTruth}`,
6
+ 'manual_edits: overwritten_on_export',
7
+ `transport: ${metadata.transport}`,
8
+ `generated_at: ${quoteYamlScalar(metadata.generatedAt)}`,
9
+ `project: ${quoteYamlScalar(metadata.project)}`,
10
+ '---',
11
+ '',
12
+ ].join('\n');
13
+ }
14
+ export function renderMemorySummaryMarkdown(input) {
15
+ const observations = [...input.observations].sort(compareMemoryItems);
16
+ const frontmatter = renderGeneratedFrontmatter({
17
+ generatedBy: 'vgxness',
18
+ sourceOfTruth: 'sqlite',
19
+ manualEdits: 'overwritten_on_export',
20
+ transport: input.transport ?? 'preview',
21
+ generatedAt: input.generatedAt,
22
+ project: input.project,
23
+ });
24
+ const rows = observations.map((item) => [item.scope, item.type, item.title, item.topicKey ?? '', item.updatedAt, item.preview].map(escapeTableCell).join(' | '));
25
+ return `${frontmatter}# ${input.project} Memory Summary\n\n> Generated by VGXNESS from SQLite memory previews. SQLite remains the source of truth; this note is a human-facing projection.\n\n${rows.length === 0
26
+ ? 'No memory observations matched this export.\n'
27
+ : ['| Scope | Type | Title | Topic Key | Updated | Preview |', '|---|---|---|---|---|---|', ...rows.map((row) => `| ${row} |`)].join('\n') + '\n'}`;
28
+ }
29
+ export function renderSddBoardMarkdown(input) {
30
+ const items = [...input.items].sort(compareSddItems);
31
+ const frontmatter = renderGeneratedFrontmatter({
32
+ generatedBy: 'vgxness',
33
+ sourceOfTruth: 'sqlite',
34
+ manualEdits: 'overwritten_on_export',
35
+ transport: input.transport ?? 'preview',
36
+ generatedAt: input.generatedAt,
37
+ project: input.project,
38
+ });
39
+ const rows = items.map((item) => [item.changeId, item.phase, item.state, item.updatedAt ?? '', item.notePath === undefined ? '' : `[[${item.notePath}]]`].map(escapeTableCell).join(' | '));
40
+ const grouped = groupSddItemsByState(items);
41
+ const summary = [...grouped.entries()]
42
+ .map(([state, stateItems]) => `- ${state}: ${stateItems.length}`)
43
+ .join('\n');
44
+ return `${frontmatter}# ${input.project} SDD Board\n\n> Generated by VGXNESS from SQLite SDD artifact/governance state. Obsidian does not prove acceptance, readiness, verification, or authorization.\n\n## Summary\n\n${summary || '- No SDD items matched this export.'}\n\n## Board\n\n${rows.length === 0
45
+ ? 'No SDD items matched this export.\n'
46
+ : ['| Change | Phase | State | Updated | Note |', '|---|---|---|---|---|', ...rows.map((row) => `| ${row} |`)].join('\n') + '\n'}`;
47
+ }
48
+ function compareMemoryItems(a, b) {
49
+ return b.updatedAt.localeCompare(a.updatedAt) || a.scope.localeCompare(b.scope) || a.type.localeCompare(b.type) || a.title.localeCompare(b.title);
50
+ }
51
+ function compareSddItems(a, b) {
52
+ return a.changeId.localeCompare(b.changeId) || a.phase.localeCompare(b.phase) || a.state.localeCompare(b.state);
53
+ }
54
+ function groupSddItemsByState(items) {
55
+ const groups = new Map();
56
+ for (const item of items) {
57
+ const existing = groups.get(item.state) ?? [];
58
+ existing.push(item);
59
+ groups.set(item.state, existing);
60
+ }
61
+ return groups;
62
+ }
63
+ function escapeTableCell(value) {
64
+ return value.replace(/\r?\n/g, '<br>').replace(/\|/g, '\\|').trim();
65
+ }
66
+ function quoteYamlScalar(value) {
67
+ return JSON.stringify(value);
68
+ }
@@ -0,0 +1,157 @@
1
+ import { normalizeSddArtifact } from '../../domain/sdd/schema.js';
2
+ import { renderGeneratedFrontmatter, renderMemorySummaryMarkdown, renderSddBoardMarkdown } from './markdown-renderer.js';
3
+ export class KnowledgeWorkspaceExportService {
4
+ options;
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async export(input) {
9
+ const status = await this.options.workspace.status();
10
+ if (status.status !== 'available') {
11
+ return { ok: false, error: { code: 'validation_failed', message: `Knowledge workspace unavailable: ${status.diagnostics.join('; ') || status.mode}` } };
12
+ }
13
+ const transport = status.mode;
14
+ const kinds = input.kind === 'all' ? ['docs', 'memory', 'sdd', 'canvas'] : [input.kind];
15
+ const exportedNotes = [];
16
+ for (const kind of kinds) {
17
+ if (kind === 'docs')
18
+ exportedNotes.push(await this.exportDocs(input.project, transport));
19
+ if (kind === 'memory') {
20
+ const memory = this.requireReadModel();
21
+ if (!memory.ok)
22
+ return memory;
23
+ const exported = await this.exportMemorySummary({ project: input.project, transport, readModel: memory.value, ...(input.limit === undefined ? {} : { limit: input.limit }) });
24
+ if (!exported.ok)
25
+ return exported;
26
+ exportedNotes.push(exported.value);
27
+ }
28
+ if (kind === 'sdd') {
29
+ const memory = this.requireReadModel();
30
+ if (!memory.ok)
31
+ return memory;
32
+ const exported = await this.exportSdd({ project: input.project, transport, readModel: memory.value });
33
+ if (!exported.ok)
34
+ return exported;
35
+ exportedNotes.push(...exported.value);
36
+ }
37
+ if (kind === 'canvas')
38
+ exportedNotes.push(await this.exportArchitectureCanvas(input.project));
39
+ }
40
+ return {
41
+ ok: true,
42
+ value: {
43
+ kind: 'knowledge-workspace-export',
44
+ project: input.project,
45
+ requestedKind: input.kind,
46
+ transport,
47
+ exportedNotes,
48
+ safety: {
49
+ sqliteRemainsSourceOfTruth: true,
50
+ overwritesGeneratedNotesOnly: true,
51
+ exportsMemoryPreviewsOnly: true,
52
+ infersSddAcceptanceFromObsidian: false,
53
+ },
54
+ },
55
+ };
56
+ }
57
+ async exportDocs(project, transport) {
58
+ const content = `${renderGeneratedFrontmatter({
59
+ generatedBy: 'vgxness',
60
+ sourceOfTruth: 'repository-docs',
61
+ manualEdits: 'overwritten_on_export',
62
+ transport,
63
+ generatedAt: this.now(),
64
+ project,
65
+ })}# ${project} Knowledge Workspace\n\nThis workspace is a human-facing projection generated by VGXNESS.\n\n## Boundaries\n\n- SQLite remains the source of truth for memory, SDD governance, runs, traces, and acceptance.\n- Obsidian is for review, planning, docs, boards, and canvas navigation.\n- Generated notes disclose that manual edits may be overwritten on export.\n`;
66
+ return toExportedNote(await this.options.workspace.writeGeneratedNote({ path: `VGXNESS/00 Home/${project} Knowledge Workspace.md`, content, overwriteGeneratedOnly: true }), 'docs');
67
+ }
68
+ async exportMemorySummary(input) {
69
+ const previews = input.readModel.searchObservationPreviewsNoTrace({ project: input.project, limit: input.limit ?? 50 });
70
+ if (!previews.ok)
71
+ return previews;
72
+ const content = renderMemorySummaryMarkdown({
73
+ project: input.project,
74
+ generatedAt: this.now(),
75
+ transport: input.transport,
76
+ observations: previews.value.map((observation) => ({
77
+ title: observation.title,
78
+ type: observation.type,
79
+ scope: observation.scope,
80
+ preview: observation.preview,
81
+ updatedAt: observation.updatedAt,
82
+ ...(observation.topicKey === undefined ? {} : { topicKey: observation.topicKey }),
83
+ })),
84
+ });
85
+ const result = await this.options.workspace.writeGeneratedNote({
86
+ path: `VGXNESS/06 Memory Summaries/${input.project}.md`,
87
+ content,
88
+ overwriteGeneratedOnly: true,
89
+ });
90
+ return { ok: true, value: toExportedNote(result, 'memory') };
91
+ }
92
+ async exportSdd(input) {
93
+ const artifacts = input.readModel.listArtifactsByTopicPrefixNoTrace(input.project, 'sdd/');
94
+ if (!artifacts.ok)
95
+ return artifacts;
96
+ const artifactNotes = [];
97
+ const boardItems = [];
98
+ for (const artifact of artifacts.value) {
99
+ const parsedTopic = parseSddTopicKey(artifact.topicKey);
100
+ if (parsedTopic === undefined)
101
+ continue;
102
+ const normalized = normalizeSddArtifact(artifact);
103
+ const notePath = `VGXNESS/03 SDD/${parsedTopic.change}/${parsedTopic.phase}.md`;
104
+ const content = `${renderGeneratedFrontmatter({
105
+ generatedBy: 'vgxness',
106
+ sourceOfTruth: 'sqlite',
107
+ manualEdits: 'overwritten_on_export',
108
+ transport: input.transport,
109
+ generatedAt: this.now(),
110
+ project: input.project,
111
+ })}# ${parsedTopic.change} / ${parsedTopic.phase}\n\n> Generated from SQLite SDD artifact state. Obsidian does not prove readiness or acceptance.\n\n## State\n\n- Status: ${normalized.metadata.status}\n- Accepted: ${normalized.metadata.status === 'accepted' ? 'yes' : 'no'}\n- Updated: ${artifact.updatedAt}\n\n## Artifact\n\n${artifact.content}\n`;
112
+ const result = await this.options.workspace.writeGeneratedNote({ path: notePath, content, overwriteGeneratedOnly: true });
113
+ artifactNotes.push(toExportedNote(result, 'sdd'));
114
+ boardItems.push({
115
+ changeId: parsedTopic.change,
116
+ phase: parsedTopic.phase,
117
+ state: normalized.metadata.status,
118
+ updatedAt: artifact.updatedAt,
119
+ notePath,
120
+ });
121
+ }
122
+ const board = renderSddBoardMarkdown({ project: input.project, generatedAt: this.now(), transport: input.transport, items: boardItems });
123
+ const boardResult = await this.options.workspace.writeGeneratedNote({
124
+ path: 'VGXNESS/08 Boards/SDD Board.md',
125
+ content: board,
126
+ overwriteGeneratedOnly: true,
127
+ });
128
+ return { ok: true, value: [...artifactNotes, toExportedNote(boardResult, 'sdd')] };
129
+ }
130
+ async exportArchitectureCanvas(project) {
131
+ const content = `${JSON.stringify({
132
+ nodes: [
133
+ { id: 'sqlite', type: 'text', text: 'SQLite\nOperational source of truth', x: 0, y: 0, width: 360, height: 180 },
134
+ { id: 'obsidian', type: 'text', text: 'Obsidian\nHuman-facing projection workspace', x: 520, y: 0, width: 360, height: 180 },
135
+ ],
136
+ edges: [{ id: 'project', fromNode: 'sqlite', fromSide: 'right', toNode: 'obsidian', toSide: 'left', label: 'project via MCP/export' }],
137
+ }, null, 2)}\n`;
138
+ return toExportedNote(await this.options.workspace.writeGeneratedNote({ path: `VGXNESS/07 Canvas/${project} Knowledge Architecture.canvas`, content, overwriteGeneratedOnly: true }), 'canvas');
139
+ }
140
+ requireReadModel() {
141
+ if (this.options.readModel !== undefined)
142
+ return { ok: true, value: this.options.readModel };
143
+ return { ok: false, error: { code: 'validation_failed', message: 'Knowledge export kind requires a SQLite read model' } };
144
+ }
145
+ now() {
146
+ return this.options.now?.() ?? new Date().toISOString();
147
+ }
148
+ }
149
+ function toExportedNote(result, kind) {
150
+ return { path: result.path, created: result.created, kind };
151
+ }
152
+ function parseSddTopicKey(topicKey) {
153
+ const match = /^sdd\/([^/]+)\/([^/]+)$/u.exec(topicKey);
154
+ if (match === null)
155
+ return undefined;
156
+ return { change: match[1], phase: match[2] };
157
+ }