vgxness 1.5.0 → 1.5.2

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 (46) hide show
  1. package/README.md +23 -2
  2. package/dist/agents/agent-seed-service.js +10 -0
  3. package/dist/agents/canonical-agent-manifest.js +177 -0
  4. package/dist/agents/canonical-agent-projection.js +146 -0
  5. package/dist/agents/renderers/claude-renderer.js +30 -52
  6. package/dist/cli/bun-bin.js +6 -0
  7. package/dist/cli/cli-help.js +3 -0
  8. package/dist/cli/commands/agent-skill-dispatcher.js +6 -5
  9. package/dist/cli/commands/mcp-dispatcher.js +65 -3
  10. package/dist/cli/index.js +1 -1
  11. package/dist/governance/governance-report-builder.js +45 -26
  12. package/dist/mcp/claude-code-agent-config.js +79 -0
  13. package/dist/mcp/claude-code-config.js +84 -0
  14. package/dist/mcp/client-install-claude-code-contract.js +86 -0
  15. package/dist/mcp/client-install-claude-code.js +85 -0
  16. package/dist/mcp/control-plane.js +2 -0
  17. package/dist/mcp/index.js +5 -0
  18. package/dist/mcp/opencode-default-agent-config.js +7 -113
  19. package/dist/mcp/provider-canonical-agent-manifest.js +39 -0
  20. package/dist/mcp/provider-change-plan.js +57 -1
  21. package/dist/mcp/provider-doctor.js +54 -0
  22. package/dist/mcp/provider-health-types.js +3 -1
  23. package/dist/mcp/provider-status.js +82 -2
  24. package/dist/mcp/schema.js +11 -2
  25. package/dist/mcp/stdio-server.js +2 -0
  26. package/dist/mcp/validation.js +23 -1
  27. package/dist/memory/memory-service.js +59 -0
  28. package/dist/memory/repositories/sessions.js +1 -1
  29. package/dist/sdd/sdd-workflow-service.js +129 -59
  30. package/dist/setup/providers/claude-setup-adapter.js +7 -4
  31. package/docs/architecture.md +54 -112
  32. package/docs/cli.md +53 -0
  33. package/docs/code-runtime.md +218 -0
  34. package/docs/contributing.md +120 -0
  35. package/docs/glossary.md +211 -0
  36. package/docs/mcp.md +144 -0
  37. package/docs/prd.md +23 -26
  38. package/docs/providers.md +123 -0
  39. package/docs/roadmap.md +88 -0
  40. package/docs/safety.md +147 -0
  41. package/docs/storage.md +93 -0
  42. package/package.json +1 -1
  43. package/docs/funcionamiento-del-sistema.md +0 -865
  44. package/docs/harness-gap-analysis.md +0 -243
  45. package/docs/vgxcode.md +0 -87
  46. package/docs/vgxness-code.md +0 -48
@@ -66,6 +66,8 @@ export function validateVgxMcpToolCall(call) {
66
66
  return validationSuccess(tool.value, validateSessionCloseInput(input, tool.value));
67
67
  case 'vgxness_session_restore':
68
68
  return validationSuccess(tool.value, validateSessionRestoreInput(input, tool.value));
69
+ case 'vgxness_context_cockpit':
70
+ return validationSuccess(tool.value, validateContextCockpitInput(input, tool.value));
69
71
  case 'vgxness_agent_resolve':
70
72
  return validationSuccess(tool.value, validateAgentResolveInput(input, tool.value));
71
73
  case 'vgxness_agent_activate':
@@ -185,7 +187,7 @@ function validateProviderHealthInput(input, tool) {
185
187
  return scope;
186
188
  if (scope.value !== undefined)
187
189
  result.scope = scope.value;
188
- const providerAdapter = readOptionalOneOf(record.value, 'providerAdapter', ['opencode'], tool);
190
+ const providerAdapter = readOptionalOneOf(record.value, 'providerAdapter', ['opencode', 'claude'], tool);
189
191
  if (!providerAdapter.ok)
190
192
  return providerAdapter;
191
193
  if (providerAdapter.value !== undefined)
@@ -234,6 +236,26 @@ function validateSddCockpitInput(input, tool) {
234
236
  return record;
235
237
  return readProjectAndChange(record.value, tool);
236
238
  }
239
+ function validateContextCockpitInput(input, tool) {
240
+ const record = inputRecord(input, tool, ['project', 'directory', 'limit']);
241
+ if (!record.ok)
242
+ return record;
243
+ const project = readNonEmptyString(record.value, 'project', tool);
244
+ if (!project.ok)
245
+ return project;
246
+ const result = { project: project.value };
247
+ const directory = readOptionalNonEmptyString(record.value, 'directory', tool);
248
+ if (!directory.ok)
249
+ return directory;
250
+ if (directory.value !== undefined)
251
+ result.directory = directory.value;
252
+ if (record.value.limit !== undefined) {
253
+ if (typeof record.value.limit !== 'number' || !Number.isSafeInteger(record.value.limit) || record.value.limit < 1 || record.value.limit > 100)
254
+ return validationFailure('limit must be an integer between 1 and 100', tool);
255
+ result.limit = record.value.limit;
256
+ }
257
+ return { ok: true, value: result };
258
+ }
237
259
  function readProjectAndChange(record, tool) {
238
260
  const project = readNonEmptyString(record, 'project', tool);
239
261
  if (!project.ok)
@@ -50,6 +50,54 @@ export class MemoryService {
50
50
  const result = this.observations.searchPreviews(filters);
51
51
  return this.record(result, context, { operation: 'observation.search', targetType: 'observation', topicKey: filters.topicKey });
52
52
  }
53
+ /** Read memory previews without writing provenance traces. */
54
+ searchObservationPreviewsNoTrace(filters) {
55
+ return this.observations.searchPreviews(filters);
56
+ }
57
+ /** Read compact current context without writing traces or mutating durable state. */
58
+ getContextCockpit(input) {
59
+ const limit = normalizeContextLimit(input.limit);
60
+ const memories = this.searchObservationPreviewsNoTrace({ project: input.project, limit });
61
+ if (!memories.ok)
62
+ return { ok: false, error: memories.error };
63
+ const session = this.restoreSession(input.directory === undefined ? { project: input.project } : { project: input.project, directory: input.directory });
64
+ if (!session.ok && session.error.code !== 'not_found')
65
+ return { ok: false, error: session.error };
66
+ const latestRestorableSession = session.ok ? session.value : undefined;
67
+ const newestMemoryUpdatedAt = memories.value[0]?.updatedAt;
68
+ const latestSessionEndedAt = latestRestorableSession?.endedAt;
69
+ const stale = latestRestorableSession === undefined || (newestMemoryUpdatedAt !== undefined && latestSessionEndedAt !== undefined && newestMemoryUpdatedAt > latestSessionEndedAt);
70
+ const staleness = {
71
+ stale,
72
+ reason: latestRestorableSession === undefined ? 'no-restorable-session' : stale ? 'memory-newer-than-session' : 'current',
73
+ ...(newestMemoryUpdatedAt === undefined ? {} : { newestMemoryUpdatedAt }),
74
+ ...(latestSessionEndedAt === undefined ? {} : { latestSessionEndedAt }),
75
+ };
76
+ return {
77
+ ok: true,
78
+ value: {
79
+ version: 1,
80
+ kind: 'context-cockpit',
81
+ project: input.project,
82
+ ...(input.directory === undefined ? {} : { directory: input.directory }),
83
+ ...(latestRestorableSession === undefined ? {} : { latestRestorableSession }),
84
+ memoryPreviews: memories.value,
85
+ staleness,
86
+ optionalSectionsOmitted: ['sdd', 'runs'],
87
+ notes: ['SDD and run summaries are omitted because safe no-trace metadata-only aggregation is not part of this cockpit path yet.'],
88
+ safety: {
89
+ readOnly: true,
90
+ recordsTraces: false,
91
+ mutatesSessions: false,
92
+ mutatesMemories: false,
93
+ mutatesArtifacts: false,
94
+ mutatesRuns: false,
95
+ writesProviderConfig: false,
96
+ mutatesRepository: false,
97
+ },
98
+ },
99
+ };
100
+ }
53
101
  listObservationRevisions(id, context) {
54
102
  const result = this.observations.listRevisions(id);
55
103
  return this.record(result, context, { operation: 'observation.revisions.list', targetType: 'observation', targetId: id });
@@ -113,6 +161,10 @@ export class MemoryService {
113
161
  const result = this.artifacts.listByTopicPrefix(project, topicPrefix);
114
162
  return this.record(result, context, { operation: 'artifact.list', targetType: 'artifact', topicKey: topicPrefix });
115
163
  }
164
+ /** Read artifacts for read-only/status projections without writing provenance traces. */
165
+ listArtifactsByTopicPrefixNoTrace(project, topicPrefix) {
166
+ return this.artifacts.listByTopicPrefix(project, topicPrefix);
167
+ }
116
168
  close() {
117
169
  this.database.close();
118
170
  }
@@ -140,3 +192,10 @@ class MemoryServiceTransactionRollback extends Error {
140
192
  super('Memory service transaction rolled back');
141
193
  }
142
194
  }
195
+ function normalizeContextLimit(limit) {
196
+ if (limit === undefined)
197
+ return 5;
198
+ if (!Number.isFinite(limit))
199
+ return 5;
200
+ return Math.max(1, Math.min(100, Math.trunc(limit)));
201
+ }
@@ -39,7 +39,7 @@ export class SessionRepository {
39
39
  AND summary IS NOT NULL
40
40
  AND TRIM(summary) <> ''
41
41
  AND (@directory IS NULL OR directory = @directory)
42
- ORDER BY ended_at DESC
42
+ ORDER BY ended_at DESC, started_at DESC, id DESC
43
43
  LIMIT 1
44
44
  `)
45
45
  .get({ project: input.project, directory: input.directory ?? null });
@@ -21,7 +21,9 @@ export class SddWorkflowService {
21
21
  if (!validated.ok)
22
22
  return validated;
23
23
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
24
- const readiness = getReadinessFromStatuses(validated.value.change, validated.value.phase, phases);
24
+ if (!phases.ok)
25
+ return phases;
26
+ const readiness = getReadinessFromStatuses(validated.value.change, validated.value.phase, phases.value);
25
27
  return {
26
28
  ok: true,
27
29
  value: {
@@ -36,20 +38,27 @@ export class SddWorkflowService {
36
38
  if (!validated.ok)
37
39
  return validated;
38
40
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
39
- return statusFromPhases(validated.value.change, phases);
41
+ if (!phases.ok)
42
+ return phases;
43
+ return statusFromPhases(validated.value.change, phases.value);
40
44
  }
41
45
  getNext(input) {
42
46
  const validated = validateProjectAndChange(input.project, input.change);
43
47
  if (!validated.ok)
44
48
  return validated;
45
49
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
46
- return ok(nextDecisionFromStatuses(validated.value.change, phases));
50
+ if (!phases.ok)
51
+ return phases;
52
+ return ok(nextDecisionFromStatuses(validated.value.change, phases.value));
47
53
  }
48
54
  getCockpit(input) {
49
55
  const validated = validateProjectAndChange(input.project, input.change);
50
56
  if (!validated.ok)
51
57
  return validated;
52
- const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
58
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
59
+ if (!snapshot.ok)
60
+ return snapshot;
61
+ const phases = snapshot.value.phases;
53
62
  const next = nextDecisionFromStatuses(validated.value.change, phases);
54
63
  const cockpitPhases = phases.map((phaseStatus) => {
55
64
  const readiness = {
@@ -57,9 +66,7 @@ export class SddWorkflowService {
57
66
  phase: phaseStatus.phase,
58
67
  ...getReadinessFromStatuses(validated.value.change, phaseStatus.phase, phases),
59
68
  };
60
- const artifact = phaseStatus.present
61
- ? this.artifactSummaryForPhase(validated.value.project, validated.value.change, phaseStatus.phase, phaseStatus.topicKey)
62
- : undefined;
69
+ const artifact = phaseStatus.present ? cockpitArtifactSummaryFromSnapshotItem(phaseStatus) : undefined;
63
70
  const blockers = cockpitBlockersForPhase(phaseStatus, readiness);
64
71
  return {
65
72
  phase: phaseStatus.phase,
@@ -100,6 +107,39 @@ export class SddWorkflowService {
100
107
  };
101
108
  return ok(cockpit);
102
109
  }
110
+ getGovernanceSnapshot(input) {
111
+ const validated = validateProjectAndChange(input.project, input.change);
112
+ if (!validated.ok)
113
+ return validated;
114
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
115
+ if (!snapshot.ok)
116
+ return snapshot;
117
+ const phases = snapshot.value.phases;
118
+ const status = statusFromPhases(validated.value.change, phases);
119
+ if (!status.ok)
120
+ return status;
121
+ const warnings = [];
122
+ const artifacts = phases.flatMap((phase) => {
123
+ if (phase.artifact === undefined)
124
+ return [];
125
+ const envelope = normalizeSddArtifact(phase.artifact);
126
+ warnings.push(...envelope.warnings);
127
+ if ((input.payloadMode ?? 'compact') === 'compact') {
128
+ const compactArtifact = compactGovernanceArtifact(envelope.artifact);
129
+ return [
130
+ {
131
+ phase: phase.phase,
132
+ topicKey: phase.topicKey,
133
+ artifact: compactArtifact,
134
+ envelope: { ...envelope, artifact: compactArtifact },
135
+ },
136
+ ];
137
+ }
138
+ return [{ phase: phase.phase, topicKey: phase.topicKey, artifact: phase.artifact, envelope }];
139
+ });
140
+ const readiness = input.phase === undefined ? undefined : { change: validated.value.change, phase: input.phase, ...getReadinessFromStatuses(validated.value.change, input.phase, phases) };
141
+ return ok({ status: status.value, artifacts, ...(readiness === undefined ? {} : { readiness }), warnings });
142
+ }
103
143
  saveArtifact(input) {
104
144
  const validated = this.validatePhaseInput(input);
105
145
  if (!validated.ok)
@@ -161,12 +201,33 @@ export class SddWorkflowService {
161
201
  if (input.payloadMode !== 'compact')
162
202
  return artifact;
163
203
  const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
164
- return ok(compactArtifactProjection(artifact.value, validated.value.change, validated.value.phase, getReadinessFromStatuses(validated.value.change, validated.value.phase, statuses)));
204
+ if (!statuses.ok)
205
+ return statuses;
206
+ return ok(compactArtifactProjection(artifact.value, validated.value.change, validated.value.phase, getReadinessFromStatuses(validated.value.change, validated.value.phase, statuses.value)));
165
207
  }
166
208
  listArtifacts(input) {
167
209
  const validated = validateProjectAndChange(input.project, input.change);
168
210
  if (!validated.ok)
169
211
  return validated;
212
+ if (input.payloadMode === 'compact') {
213
+ const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
214
+ if (!snapshot.ok)
215
+ return snapshot;
216
+ return ok({
217
+ project: validated.value.project,
218
+ change: validated.value.change,
219
+ artifacts: snapshot.value.phases.flatMap((phase) => {
220
+ if (phase.artifact === undefined)
221
+ return [];
222
+ return [compactArtifactProjection(phase.artifact, validated.value.change, phase.phase, getReadinessFromStatuses(validated.value.change, phase.phase, snapshot.value.phases))];
223
+ }),
224
+ fullRetrieval: {
225
+ tool: 'vgxness_sdd_list_artifacts',
226
+ payloadMode: 'verbose',
227
+ input: { project: validated.value.project, change: validated.value.change },
228
+ },
229
+ });
230
+ }
170
231
  const listed = this.memory.listArtifactsByTopicPrefix(validated.value.project, `sdd/${validated.value.change}/`, this.context);
171
232
  if (!listed.ok)
172
233
  return listed;
@@ -174,22 +235,7 @@ export class SddWorkflowService {
174
235
  const artifacts = sddPhases
175
236
  .map((phase) => artifactsByTopicKey.get(sddTopicKey(validated.value.change, phase)))
176
237
  .filter((artifact) => artifact !== undefined);
177
- if (input.payloadMode !== 'compact')
178
- return ok({ project: validated.value.project, change: validated.value.change, artifacts });
179
- const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
180
- return ok({
181
- project: validated.value.project,
182
- change: validated.value.change,
183
- artifacts: artifacts.map((artifact) => {
184
- const phase = isSddPhase(artifact.phase) ? artifact.phase : phaseFromTopicKey(validated.value.change, artifact.topicKey);
185
- return compactArtifactProjection(artifact, validated.value.change, phase, getReadinessFromStatuses(validated.value.change, phase, statuses));
186
- }),
187
- fullRetrieval: {
188
- tool: 'vgxness_sdd_list_artifacts',
189
- payloadMode: 'verbose',
190
- input: { project: validated.value.project, change: validated.value.change },
191
- },
192
- });
238
+ return ok({ project: validated.value.project, change: validated.value.change, artifacts });
193
239
  }
194
240
  validatePhaseInput(input) {
195
241
  const validated = validateProjectAndChange(input.project, input.change);
@@ -200,31 +246,38 @@ export class SddWorkflowService {
200
246
  return { ok: true, value: { ...validated.value, phase: input.phase } };
201
247
  }
202
248
  getPhaseStatuses(project, change) {
203
- return sddPhases.map((phase) => {
249
+ const snapshot = this.loadPhaseSnapshot(project, change);
250
+ if (!snapshot.ok)
251
+ return snapshot;
252
+ return ok(snapshot.value.phases.map(({ artifact, acceptance, createdAt, updatedAt, warnings, ...status }) => status));
253
+ }
254
+ loadPhaseSnapshot(project, change) {
255
+ const listed = this.memory.listArtifactsByTopicPrefixNoTrace(project, `sdd/${change}/`);
256
+ if (!listed.ok)
257
+ return listed;
258
+ const artifactsByTopicKey = new Map(listed.value.map((artifact) => [artifact.topicKey, artifact]));
259
+ const phases = sddPhases.map((phase) => {
204
260
  const topicKey = sddTopicKey(change, phase);
205
- const artifact = this.memory.getArtifact(project, topicKey, this.context);
206
- if (!artifact.ok)
207
- return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false };
208
- return phaseStatusFromArtifact(phase, topicKey, artifact.value);
261
+ const artifact = artifactsByTopicKey.get(topicKey);
262
+ if (artifact === undefined)
263
+ return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false, warnings: [] };
264
+ const envelope = normalizeSddArtifact(artifact);
265
+ return {
266
+ phase,
267
+ topicKey,
268
+ present: true,
269
+ state: envelope.metadata.status,
270
+ accepted: envelope.metadata.status === 'accepted',
271
+ legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
272
+ artifactId: artifact.id,
273
+ artifact,
274
+ ...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
275
+ createdAt: artifact.createdAt,
276
+ updatedAt: artifact.updatedAt,
277
+ warnings: envelope.warnings,
278
+ };
209
279
  });
210
- }
211
- artifactSummaryForPhase(project, change, phase, topicKey) {
212
- const artifact = this.memory.getArtifact(project, topicKey, this.context);
213
- if (!artifact.ok)
214
- return undefined;
215
- const envelope = normalizeSddArtifact(artifact.value);
216
- return {
217
- phase,
218
- topicKey,
219
- present: true,
220
- accepted: envelope.metadata.status === 'accepted',
221
- legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
222
- state: envelope.metadata.status,
223
- artifactId: artifact.value.id,
224
- ...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
225
- createdAt: artifact.value.createdAt,
226
- updatedAt: artifact.value.updatedAt,
227
- };
280
+ return ok({ project, change, phases });
228
281
  }
229
282
  }
230
283
  export function nextDecisionFromStatuses(change, phases) {
@@ -336,18 +389,6 @@ function getReadinessFromStatuses(change, phase, phases) {
336
389
  blockedPrerequisites,
337
390
  };
338
391
  }
339
- function phaseStatusFromArtifact(phase, topicKey, artifact) {
340
- const envelope = normalizeSddArtifact(artifact);
341
- return {
342
- phase,
343
- topicKey,
344
- present: true,
345
- state: envelope.metadata.status,
346
- accepted: envelope.metadata.status === 'accepted',
347
- legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
348
- artifactId: artifact.id,
349
- };
350
- }
351
392
  function compactArtifactProjection(artifact, change, phase, readiness) {
352
393
  const envelope = normalizeSddArtifact(artifact);
353
394
  const summary = summarizePayloadContent(artifact.content);
@@ -370,6 +411,35 @@ function compactArtifactProjection(artifact, change, phase, readiness) {
370
411
  updatedAt: artifact.updatedAt,
371
412
  };
372
413
  }
414
+ function cockpitArtifactSummaryFromSnapshotItem(item) {
415
+ if (item.artifact === undefined || item.artifactId === undefined || item.createdAt === undefined || item.updatedAt === undefined)
416
+ return undefined;
417
+ return {
418
+ phase: item.phase,
419
+ topicKey: item.topicKey,
420
+ present: true,
421
+ accepted: item.accepted === true,
422
+ legacy: item.legacy === true,
423
+ state: item.state ?? 'draft',
424
+ artifactId: item.artifactId,
425
+ ...(item.acceptance === undefined ? {} : { acceptance: item.acceptance }),
426
+ createdAt: item.createdAt,
427
+ updatedAt: item.updatedAt,
428
+ };
429
+ }
430
+ function compactGovernanceArtifact(artifact) {
431
+ return {
432
+ id: artifact.id,
433
+ project: artifact.project,
434
+ topicKey: artifact.topicKey,
435
+ phase: artifact.phase,
436
+ observationId: artifact.observationId,
437
+ createdAt: artifact.createdAt,
438
+ updatedAt: artifact.updatedAt,
439
+ contentLength: artifact.content.length,
440
+ contentOmitted: true,
441
+ };
442
+ }
373
443
  function cockpitBlockersForPhase(status, readiness) {
374
444
  const blockers = [];
375
445
  if (!status.present)
@@ -4,17 +4,20 @@ export const claudeSetupAdapter = {
4
4
  id: 'claude',
5
5
  displayName: 'Claude',
6
6
  supportLevel: 'preview-only',
7
- capabilities: ['mcp-preview', 'manual-guidance'],
8
- targets: [{ kind: 'manual', label: 'Claude MCP config snippet', writableBySetup: false }],
7
+ capabilities: ['mcp-preview', 'mcp-install-plan', 'agent-preview', 'doctor', 'manual-guidance'],
8
+ targets: [
9
+ { kind: 'project-config', label: 'Project .mcp.json', path: '.mcp.json', writableBySetup: false },
10
+ { kind: 'project-config', label: 'Project Claude agents', path: '.claude/agents/*.md', writableBySetup: false },
11
+ ],
9
12
  getStatus(context) {
10
13
  return {
11
14
  providerId: 'claude',
12
15
  status: 'preview-only',
13
- summary: 'Claude setup is preview-only with manual MCP guidance.',
16
+ summary: 'Claude setup supports project-local read-only planning for .mcp.json and .claude/agents/*.md; confirmed writes are handled by guarded install flows.',
14
17
  evidence: context.databasePath !== undefined
15
18
  ? ['Claude MCP preview can be generated from the selected database path.']
16
19
  : ['Claude MCP preview uses a placeholder until a database path is selected.'],
17
- guidance: ['Copy snippets manually after reviewing them. The TUI does not install or apply Claude config.'],
20
+ guidance: ['Review the Claude plan before any write. VGXNESS never writes ~/.claude.json, CLAUDE.md, or .claude/CLAUDE.md and does not execute/install Claude Code.'],
18
21
  actions: [
19
22
  {
20
23
  id: 'claude-manual-guidance',