wendkeep 0.80.2 → 0.85.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/CHANGELOG.md +117 -0
- package/README.en.md +28 -11
- package/README.md +28 -11
- package/docs/en/commands/capabilities.md +82 -0
- package/docs/en/commands/getting-started.md +3 -1
- package/docs/en/commands/mcp.md +99 -0
- package/docs/en/commands/portable.md +88 -0
- package/docs/en/commands/sync-protocol.md +58 -0
- package/docs/en/commands/tdd.md +96 -0
- package/docs/en/commands/verify.md +5 -0
- package/docs/pt-BR/commands/capabilities.md +82 -0
- package/docs/pt-BR/commands/getting-started.md +3 -2
- package/docs/pt-BR/commands/mcp.md +99 -0
- package/docs/pt-BR/commands/portable.md +87 -0
- package/docs/pt-BR/commands/sync-protocol.md +58 -0
- package/docs/pt-BR/commands/tdd.md +96 -0
- package/docs/pt-BR/commands/verify.md +5 -0
- package/hooks/active-context-store.mjs +2 -0
- package/hooks/change-core.mjs +5 -0
- package/hooks/project-scope.mjs +2 -1
- package/hooks/session-ensure.mjs +23 -7
- package/hooks/session-start.mjs +20 -5
- package/package.json +3 -3
- package/packages/cli/src/index.mjs +42 -2
- package/packages/harness/src/sensors-core.mjs +16 -3
- package/packages/integrations/src/capabilities.mjs +220 -0
- package/packages/integrations/src/index.mjs +1 -0
- package/packages/mcp/src/audit.mjs +49 -0
- package/packages/mcp/src/cli.mjs +78 -0
- package/packages/mcp/src/config.mjs +22 -1
- package/packages/mcp/src/effects.mjs +115 -0
- package/packages/mcp/src/executor.mjs +354 -0
- package/packages/mcp/src/index.mjs +7 -0
- package/packages/mcp/src/server.mjs +342 -0
- package/packages/mcp/src/stdio.mjs +38 -0
- package/packages/mcp/src/sync.mjs +56 -0
- package/packages/pi/package.json +2 -1
- package/packages/pi/src/index.mjs +29 -0
- package/schema/handoff-contract-v1.schema.json +4 -0
- package/schema/host-capability-manifest-v1.schema.json +46 -0
- package/schema/host-coverage-v1.schema.json +55 -0
- package/schema/mcp-effect-manifest-v1.schema.json +36 -0
- package/schema/mcp-tool-input-v1.schema.json +32 -0
- package/schema/mcp-tool-result-v1.schema.json +22 -0
- package/schema/portable-active-work-v1.schema.json +38 -0
- package/schema/portable-state-v1.schema.json +36 -0
- package/schema/sync-event-v1.schema.json +25 -0
- package/schema/sync-private-envelope-v1.schema.json +16 -0
- package/schema/sync-state-v1.schema.json +18 -0
- package/schema/task-contract-v1.schema.json +2 -0
- package/schema/tdd-attestation-v1.schema.json +39 -0
- package/schema/wendkeep.evidence-envelope-v2.schema.json +17 -0
- package/schema/wendkeep.sensors.schema.json +19 -0
- package/src/active-context-runtime.mjs +1 -0
- package/src/capabilities.mjs +50 -0
- package/src/doctor.mjs +28 -0
- package/src/evidence-envelope.mjs +12 -6
- package/src/host-capabilities.mjs +34 -0
- package/src/init.mjs +3 -3
- package/src/mcp.mjs +7 -0
- package/src/observer-snapshot.mjs +25 -0
- package/src/portable.mjs +558 -0
- package/src/skills-seed.mjs +26 -0
- package/src/sync-adapters.mjs +188 -0
- package/src/sync-outbox.mjs +155 -0
- package/src/sync-protocol-cli.mjs +277 -0
- package/src/sync-protocol.mjs +368 -0
- package/src/sync.mjs +8 -0
- package/src/task-contracts.mjs +67 -2
- package/src/task.mjs +5 -1
- package/src/tdd-attestation-store.mjs +98 -0
- package/src/tdd-attestation.mjs +254 -0
- package/src/tdd.mjs +198 -0
- package/src/vault-readme.mjs +4 -4
- package/src/verify.mjs +24 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "wendkeep://schema/mcp-tool-result-v1",
|
|
4
|
+
"title": "WendKeep MCP tool result v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schema_version"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema_version": { "const": 1 },
|
|
9
|
+
"items": { "type": "array" },
|
|
10
|
+
"next_cursor": { "type": "string" },
|
|
11
|
+
"error": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"required": ["code", "message", "retryable"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"code": { "type": "string", "pattern": "^MCP_[A-Z0-9_]+$" },
|
|
17
|
+
"message": { "type": "string", "maxLength": 500 },
|
|
18
|
+
"retryable": { "type": "boolean" }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/portable-active-work-v1.schema.json",
|
|
4
|
+
"title": "WendKeep portable active-work v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version", "active_work_id", "project_id", "repository_id", "change_slug",
|
|
9
|
+
"task_id", "branch", "base_sha", "head_sha", "spec_sha256", "tasks_sha256",
|
|
10
|
+
"status", "completed", "current_action", "next_actions", "blockers", "evidence_refs",
|
|
11
|
+
"updated_at", "revision"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"schema_version": { "const": 1 },
|
|
15
|
+
"active_work_id": { "type": "string", "pattern": "^[a-f0-9]{24}$" },
|
|
16
|
+
"project_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
17
|
+
"repository_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
18
|
+
"change_slug": { "type": "string", "maxLength": 160 },
|
|
19
|
+
"task_id": { "type": "string", "maxLength": 160 },
|
|
20
|
+
"branch": { "type": "string", "maxLength": 240 },
|
|
21
|
+
"base_sha": { "type": "string", "pattern": "^(|[a-f0-9]{40,64})$" },
|
|
22
|
+
"head_sha": { "type": "string", "pattern": "^(|[a-f0-9]{40,64})$" },
|
|
23
|
+
"spec_sha256": { "$ref": "#/$defs/hash" },
|
|
24
|
+
"tasks_sha256": { "$ref": "#/$defs/hash" },
|
|
25
|
+
"status": { "enum": ["in_progress", "completed", "blocked"] },
|
|
26
|
+
"completed": { "$ref": "#/$defs/strings" },
|
|
27
|
+
"current_action": { "type": "object" },
|
|
28
|
+
"next_actions": { "$ref": "#/$defs/strings" },
|
|
29
|
+
"blockers": { "$ref": "#/$defs/strings" },
|
|
30
|
+
"evidence_refs": { "$ref": "#/$defs/strings" },
|
|
31
|
+
"updated_at": { "type": "string", "format": "date-time" },
|
|
32
|
+
"revision": { "type": "integer", "minimum": 0 }
|
|
33
|
+
},
|
|
34
|
+
"$defs": {
|
|
35
|
+
"hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
36
|
+
"strings": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/portable-state-v1.schema.json",
|
|
4
|
+
"title": "WendKeep portable authored state v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "kind", "project_id", "repository_id", "authored_sha256", "artifacts", "active_work"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"kind": { "const": "wendkeep-portable-state" },
|
|
11
|
+
"project_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
12
|
+
"repository_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
13
|
+
"authored_sha256": { "$ref": "#/$defs/hash" },
|
|
14
|
+
"artifacts": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"items": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": ["path", "category", "content_sha256", "content"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"path": { "type": "string", "pattern": "^(?:\\.brain/CORE\\.md|(?:04-Decisões|04-Decisions|07-Specs|08-Mudanças|08-Changes)/[^.][^\\u0000]*)$" },
|
|
22
|
+
"category": { "enum": ["authored", "derived"] },
|
|
23
|
+
"content_sha256": { "$ref": "#/$defs/hash" },
|
|
24
|
+
"content": { "type": "string" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"active_work": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "$ref": "portable-active-work-v1.schema.json" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"$defs": {
|
|
34
|
+
"hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/sync-event-v1.schema.json",
|
|
4
|
+
"title": "WendKeep sync event v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "event_id", "project_id", "record_key", "revision", "base_revision", "content_hash", "causal_parent_ids", "actor_id", "device_id", "lease_id", "observed_at", "operation", "privacy", "payload"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"event_id": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
11
|
+
"project_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
12
|
+
"record_key": { "type": "string", "minLength": 1, "maxLength": 2048 },
|
|
13
|
+
"revision": { "type": "integer", "minimum": 1 },
|
|
14
|
+
"base_revision": { "type": "integer", "minimum": 0 },
|
|
15
|
+
"content_hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
16
|
+
"causal_parent_ids": { "type": "array", "uniqueItems": true, "items": { "type": "string", "maxLength": 128 } },
|
|
17
|
+
"actor_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
18
|
+
"device_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
19
|
+
"lease_id": { "type": "string", "maxLength": 160 },
|
|
20
|
+
"observed_at": { "type": "string", "format": "date-time" },
|
|
21
|
+
"operation": { "enum": ["put", "tombstone", "resolve"] },
|
|
22
|
+
"privacy": { "enum": ["shared", "private"] },
|
|
23
|
+
"payload": {}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/sync-private-envelope-v1.schema.json",
|
|
4
|
+
"title": "WendKeep private sync envelope v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "algorithm", "key_id", "iv", "ciphertext", "auth_tag"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"algorithm": { "const": "AES-256-GCM" },
|
|
11
|
+
"key_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
12
|
+
"iv": { "type": "string", "contentEncoding": "base64" },
|
|
13
|
+
"ciphertext": { "type": "string", "contentEncoding": "base64" },
|
|
14
|
+
"auth_tag": { "type": "string", "contentEncoding": "base64" }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/sync-state-v1.schema.json",
|
|
4
|
+
"title": "WendKeep sync state v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schema_version", "project_id", "records", "conflicts", "pending", "leases", "decisions", "applied_event_ids"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"project_id": { "type": "string", "minLength": 1, "maxLength": 160 },
|
|
11
|
+
"records": { "type": "object", "additionalProperties": { "type": "object" } },
|
|
12
|
+
"conflicts": { "type": "object", "additionalProperties": { "type": "object" } },
|
|
13
|
+
"pending": { "type": "object", "additionalProperties": { "type": "array", "items": { "$ref": "sync-event-v1.schema.json" } } },
|
|
14
|
+
"leases": { "type": "object", "additionalProperties": { "type": "object" } },
|
|
15
|
+
"decisions": { "type": "array", "items": { "type": "object" } },
|
|
16
|
+
"applied_event_ids": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-f0-9]{64}$" } }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"owner": { "type": ["string", "null"] },
|
|
29
29
|
"work_session_id": { "type": ["string", "null"] },
|
|
30
30
|
"evidence_envelope_id": { "type": ["string", "null"] },
|
|
31
|
+
"tdd_required": { "type": "boolean" },
|
|
32
|
+
"tdd_attestation_id": { "type": ["string", "null"] },
|
|
31
33
|
"checked": { "type": "boolean" },
|
|
32
34
|
"authored_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
33
35
|
"binding": {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://wendkeep.dev/schema/tdd-attestation-v1.schema.json",
|
|
4
|
+
"title": "WendKeep TDD Attestation v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version", "attestation_id", "project_id", "repository_id", "worktree_id",
|
|
9
|
+
"work_session_id", "change_slug", "task_id", "requirement_id", "profile", "state",
|
|
10
|
+
"test_paths", "red", "green", "green_history", "waiver", "review_flags", "invalid_reason"
|
|
11
|
+
],
|
|
12
|
+
"properties": {
|
|
13
|
+
"schema_version": { "const": 1 },
|
|
14
|
+
"attestation_id": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
15
|
+
"project_id": { "type": "string", "minLength": 1 },
|
|
16
|
+
"repository_id": { "type": "string", "minLength": 1 },
|
|
17
|
+
"worktree_id": { "type": "string", "minLength": 1 },
|
|
18
|
+
"work_session_id": { "type": "string", "minLength": 1 },
|
|
19
|
+
"change_slug": { "type": "string", "minLength": 1 },
|
|
20
|
+
"task_id": { "type": "string", "minLength": 1 },
|
|
21
|
+
"requirement_id": { "type": "string", "minLength": 1 },
|
|
22
|
+
"profile": { "enum": ["OFF", "FLOW", "GUIDE", "GOVERN", "ASSURE"] },
|
|
23
|
+
"state": { "enum": ["red-observed", "green-observed", "invalid", "waived"] },
|
|
24
|
+
"test_paths": { "$ref": "#/$defs/stringArray" },
|
|
25
|
+
"red": { "type": ["object", "null"] },
|
|
26
|
+
"green": { "type": ["object", "null"] },
|
|
27
|
+
"green_history": { "type": "array", "items": { "type": "object" } },
|
|
28
|
+
"waiver": { "type": ["object", "null"] },
|
|
29
|
+
"review_flags": { "$ref": "#/$defs/stringArray" },
|
|
30
|
+
"invalid_reason": { "type": ["string", "null"] }
|
|
31
|
+
},
|
|
32
|
+
"$defs": {
|
|
33
|
+
"stringArray": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": { "type": "string" },
|
|
36
|
+
"uniqueItems": true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -51,11 +51,28 @@
|
|
|
51
51
|
"sensors": {
|
|
52
52
|
"type": "array",
|
|
53
53
|
"items": { "$ref": "#/$defs/sensor" }
|
|
54
|
+
},
|
|
55
|
+
"host_coverage": { "$ref": "host-coverage-v1.schema.json" },
|
|
56
|
+
"tdd_attestations": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": { "$ref": "#/$defs/tddAttestation" }
|
|
54
59
|
}
|
|
55
60
|
},
|
|
56
61
|
"$defs": {
|
|
57
62
|
"gitObject": { "type": "string", "pattern": "^[a-f0-9]{40,64}$" },
|
|
58
63
|
"sha256": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
64
|
+
"tddAttestation": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"required": ["schema_version", "attestation_id", "task_id", "requirement_id", "state", "test_paths"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"schema_version": { "const": 1 },
|
|
69
|
+
"attestation_id": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
70
|
+
"task_id": { "type": "string", "minLength": 1 },
|
|
71
|
+
"requirement_id": { "type": "string", "minLength": 1 },
|
|
72
|
+
"state": { "enum": ["red-observed", "green-observed", "invalid", "waived"] },
|
|
73
|
+
"test_paths": { "type": "array", "items": { "type": "string" } }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
59
76
|
"sensor": {
|
|
60
77
|
"type": "object",
|
|
61
78
|
"required": [
|
|
@@ -9,6 +9,25 @@
|
|
|
9
9
|
"$schema": { "type": "string" },
|
|
10
10
|
"version": { "const": 1 },
|
|
11
11
|
"source": { "type": "string" },
|
|
12
|
+
"requires_host_capabilities": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"uniqueItems": true,
|
|
15
|
+
"items": { "$ref": "host-capability-manifest-v1.schema.json#/$defs/capabilityName" }
|
|
16
|
+
},
|
|
17
|
+
"host_capability_waivers": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"required": ["capability", "authority", "approved_by", "reason"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"capability": { "$ref": "host-capability-manifest-v1.schema.json#/$defs/capabilityName" },
|
|
25
|
+
"authority": { "const": "human" },
|
|
26
|
+
"approved_by": { "type": "string", "minLength": 1 },
|
|
27
|
+
"reason": { "type": "string", "minLength": 1 }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
12
31
|
"sensors": {
|
|
13
32
|
"type": "array",
|
|
14
33
|
"items": {
|
|
@@ -103,6 +103,7 @@ export function resolveRuntimeActiveContext({
|
|
|
103
103
|
branch: selected.actual.branch,
|
|
104
104
|
headSha: selected.actual.head,
|
|
105
105
|
sessionId: selected.sessionId,
|
|
106
|
+
...(selected.entry.host_coverage ? { hostCoverage: structuredClone(selected.entry.host_coverage) } : {}),
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { HOST_CAPABILITY_MANIFESTS } from '../packages/integrations/src/capabilities.mjs';
|
|
2
|
+
import { buildHostCoverage } from './host-capabilities.mjs';
|
|
3
|
+
|
|
4
|
+
export const CAPABILITIES_HELP = `wendkeep capabilities [options]
|
|
5
|
+
|
|
6
|
+
--host <id> claude | codex | pi | generic-mcp (default: all)
|
|
7
|
+
--host-version <v> observed host version for support classification
|
|
8
|
+
--json structured coverage
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
function option(argv, name) {
|
|
12
|
+
const rows = argv.filter((item) => item === name || item.startsWith(`${name}=`));
|
|
13
|
+
if (rows.length > 1) throw Object.assign(new Error(`${name} must be supplied once`), { code: 'HOST_ARGUMENT_INVALID' });
|
|
14
|
+
const index = argv.indexOf(name);
|
|
15
|
+
if (index >= 0) return argv[index + 1] || '';
|
|
16
|
+
return argv.find((item) => item.startsWith(`${name}=`))?.slice(name.length + 1) || '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function runCapabilities(argv = []) {
|
|
20
|
+
const json = argv.includes('--json');
|
|
21
|
+
try {
|
|
22
|
+
const known = new Set(['--host', '--host-version', '--json']);
|
|
23
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
24
|
+
const item = argv[index];
|
|
25
|
+
const flag = item.split('=', 1)[0];
|
|
26
|
+
if (!known.has(flag)) throw Object.assign(new Error(`unknown option: ${item}`), { code: 'HOST_ARGUMENT_INVALID' });
|
|
27
|
+
if (['--host', '--host-version'].includes(item)) {
|
|
28
|
+
const value = argv[index + 1];
|
|
29
|
+
if (!value || value.startsWith('--')) {
|
|
30
|
+
throw Object.assign(new Error(`${item} requires a value`), { code: 'HOST_ARGUMENT_INVALID' });
|
|
31
|
+
}
|
|
32
|
+
index += 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const host = option(argv, '--host');
|
|
36
|
+
const hostVersion = option(argv, '--host-version');
|
|
37
|
+
const ids = host ? [host] : Object.keys(HOST_CAPABILITY_MANIFESTS);
|
|
38
|
+
const rows = ids.map((hostId) => buildHostCoverage({ hostId, hostVersion }));
|
|
39
|
+
const payload = host ? rows[0] : { schema_version: 1, hosts: rows };
|
|
40
|
+
if (json) process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
41
|
+
else for (const coverage of rows) {
|
|
42
|
+
process.stdout.write(`${coverage.host_id}: ${coverage.degraded ? 'degraded' : 'complete'}\n`);
|
|
43
|
+
for (const item of coverage.capabilities) process.stdout.write(` ${item.capability}: ${item.state} (${item.authority})\n`);
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
process.stderr.write(`wendkeep capabilities: ${error?.code || 'HOST_CAPABILITIES_FAILED'}: ${error?.message || error}\n`);
|
|
48
|
+
return 2;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/doctor.mjs
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
inspectActiveContextHealth,
|
|
12
12
|
renderActiveContextHealthLines,
|
|
13
13
|
} from './active-context-health.mjs';
|
|
14
|
+
import { inspectPortableState } from './portable.mjs';
|
|
15
|
+
import { inspectSyncOutbox, readLocalSyncState } from './sync-outbox.mjs';
|
|
16
|
+
import { readProjectForValidation } from '../packages/vault/src/validate-memory.mjs';
|
|
14
17
|
|
|
15
18
|
const healthStatusLabel = (status) => ({
|
|
16
19
|
healthy: 'saudável', warning: 'atenção', degraded: 'degradada', blocked: 'bloqueada', legacy: 'legado',
|
|
@@ -147,6 +150,28 @@ export function runDoctor(argv) {
|
|
|
147
150
|
const activeContexts = inspectActiveContextHealth({ vaultBase, projectRoot });
|
|
148
151
|
process.stdout.write(`\n${renderActiveContextHealthLines(activeContexts).join('\n')}\n`);
|
|
149
152
|
|
|
153
|
+
const portable = inspectPortableState({ vaultBase, projectRoot });
|
|
154
|
+
process.stdout.write(`\n[portable] ${portable.status}${portable.issues.length ? ` — ${portable.issues.length} divergência(s)` : ''}\n`);
|
|
155
|
+
if (portable.status === 'diverged') process.stdout.write(' → wendkeep portable diff; revise e rode `wendkeep portable export`\n');
|
|
156
|
+
if (portable.status === 'invalid') process.stdout.write(` ✗ ${portable.issues.join(', ')}\n`);
|
|
157
|
+
|
|
158
|
+
const sync = inspectSyncOutbox(vaultBase);
|
|
159
|
+
let syncConflicts = 0;
|
|
160
|
+
if (sync.status !== 'disabled' && sync.status !== 'corrupt') {
|
|
161
|
+
try {
|
|
162
|
+
const projectIdentity = readProjectForValidation(vaultBase);
|
|
163
|
+
const syncState = readLocalSyncState(vaultBase, resolution.projectId || projectIdentity.projectId || 'unknown');
|
|
164
|
+
syncConflicts = Object.values(syncState.conflicts || {}).filter((item) => item?.status === 'open').length;
|
|
165
|
+
} catch {
|
|
166
|
+
sync.status = 'corrupt';
|
|
167
|
+
sync.code = 'WENDKEEP_SYNC_STATE_CORRUPT';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
process.stdout.write(`\n[sync] ${sync.status} · ${sync.pending} pendente(s) · ${syncConflicts} conflito(s) aberto(s)\n`);
|
|
171
|
+
if (sync.status === 'pending') process.stdout.write(' → wendkeep sync push --remote <diretório> (ou --url <endpoint>)\n');
|
|
172
|
+
if (sync.status === 'corrupt') process.stdout.write(` ✗ ${sync.code || 'WENDKEEP_SYNC_OUTBOX_CORRUPT'}\n`);
|
|
173
|
+
if (syncConflicts) process.stdout.write(' → wendkeep sync conflicts; resolva cada conflito explicitamente\n');
|
|
174
|
+
|
|
150
175
|
// 3. Link/graph health — órfãos que o grafo do Obsidian mostraria, com o comando de reparo.
|
|
151
176
|
const links = checkVaultLinks(vaultBase);
|
|
152
177
|
const graphLabel = links.graphColors === true ? 'com cores' : links.graphColors === false ? 'sem cores' : 'sem graph.json';
|
|
@@ -197,6 +222,9 @@ export function runDoctor(argv) {
|
|
|
197
222
|
|| warnings.length
|
|
198
223
|
|| worktrees.issues.length
|
|
199
224
|
|| activeContexts.issues.length
|
|
225
|
+
|| ['diverged', 'invalid'].includes(portable.status)
|
|
226
|
+
|| sync.status === 'corrupt'
|
|
227
|
+
|| syncConflicts
|
|
200
228
|
|| links.derivedOrphans
|
|
201
229
|
|| links.artifactOrphans
|
|
202
230
|
|| links.graphColors === false
|
|
@@ -17,10 +17,12 @@ import {
|
|
|
17
17
|
|
|
18
18
|
export { canonicalSha256, evaluateEvidenceBinding, evidenceSensors };
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
function packageVersion() {
|
|
21
|
+
return JSON.parse(readFileSync(
|
|
22
|
+
join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'),
|
|
23
|
+
'utf8',
|
|
24
|
+
)).version;
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
export function sensorConfigSha256(sensors, ids) {
|
|
26
28
|
const selected = new Set(ids || []);
|
|
@@ -260,10 +262,12 @@ export function buildEvidenceEnvelope({
|
|
|
260
262
|
effectiveSpecSha256,
|
|
261
263
|
sensorConfigSha256: configSha256,
|
|
262
264
|
sensors,
|
|
265
|
+
tddAttestations = [],
|
|
263
266
|
startedAt,
|
|
264
267
|
finishedAt,
|
|
265
|
-
version =
|
|
268
|
+
version = '',
|
|
266
269
|
runtimePlatform = `${process.platform}-${process.arch}`,
|
|
270
|
+
hostCoverage = null,
|
|
267
271
|
} = {}) {
|
|
268
272
|
const envelope = {
|
|
269
273
|
schema_version: 2,
|
|
@@ -278,11 +282,13 @@ export function buildEvidenceEnvelope({
|
|
|
278
282
|
tasks_sha256: tasksSha256,
|
|
279
283
|
effective_spec_sha256: effectiveSpecSha256,
|
|
280
284
|
sensor_config_sha256: configSha256,
|
|
281
|
-
wendkeep_version: version,
|
|
285
|
+
wendkeep_version: version || packageVersion(),
|
|
282
286
|
platform: runtimePlatform,
|
|
283
287
|
started_at: startedAt,
|
|
284
288
|
finished_at: finishedAt,
|
|
285
289
|
sensors,
|
|
290
|
+
...(hostCoverage ? { host_coverage: structuredClone(hostCoverage) } : {}),
|
|
291
|
+
tdd_attestations: tddAttestations,
|
|
286
292
|
};
|
|
287
293
|
return { ...envelope, envelope_id: canonicalSha256(envelope) };
|
|
288
294
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCoverageFromManifest,
|
|
3
|
+
} from '../packages/integrations/src/capabilities.mjs';
|
|
4
|
+
import {
|
|
5
|
+
MCP_EFFECT_MANIFEST,
|
|
6
|
+
verifyMcpEffectManifest,
|
|
7
|
+
} from '../packages/mcp/src/effects.mjs';
|
|
8
|
+
|
|
9
|
+
function summarizeToolEffects(manifest) {
|
|
10
|
+
const validation = verifyMcpEffectManifest(manifest);
|
|
11
|
+
const effects = new Set(validation.valid ? manifest.tools.map((tool) => tool.effect) : []);
|
|
12
|
+
return {
|
|
13
|
+
manifest_valid: validation.valid,
|
|
14
|
+
catalog_version: validation.valid ? manifest.catalog_version : '',
|
|
15
|
+
read: effects.has('read'),
|
|
16
|
+
write: effects.has('write'),
|
|
17
|
+
destructive: effects.has('destructive'),
|
|
18
|
+
unknown: 'fail-closed',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildHostCoverage({
|
|
23
|
+
hostId,
|
|
24
|
+
hostVersion = '',
|
|
25
|
+
effectManifest = MCP_EFFECT_MANIFEST,
|
|
26
|
+
observedAt,
|
|
27
|
+
} = {}) {
|
|
28
|
+
return buildCoverageFromManifest({
|
|
29
|
+
hostId,
|
|
30
|
+
hostVersion,
|
|
31
|
+
observedAt,
|
|
32
|
+
toolEffects: summarizeToolEffects(effectManifest),
|
|
33
|
+
});
|
|
34
|
+
}
|
package/src/init.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// `wendkeep init` — cross-platform replacement for setup-vault.ps1.
|
|
2
2
|
// Creates the vault taxonomy + provider-neutral project binding, NON-DESTRUCTIVELY
|
|
3
|
-
// merges the session hooks into .claude/settings.json, and adds the
|
|
3
|
+
// merges the session hooks into .claude/settings.json, and adds the native MCP server to
|
|
4
4
|
// .mcp.json. Idempotent: re-running only adds what is missing.
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
6
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
@@ -516,7 +516,7 @@ export async function runInit(argv) {
|
|
|
516
516
|
log(M.header);
|
|
517
517
|
log(`${M.lProject}: ${projectPath}`);
|
|
518
518
|
log(`${M.lVault}: ${vaultPath}`);
|
|
519
|
-
log(`${M.lMcp}: ${args.mcp ? '
|
|
519
|
+
log(`${M.lMcp}: ${args.mcp ? 'wendkeep native (wendkeep-vault)' : M.skipped}`);
|
|
520
520
|
log(`${M.lCompanions}: ${companions.length ? companions.join(', ') : M.none}`);
|
|
521
521
|
log(`${M.lColors}: ${args.noColors ? M.skipped : M.colorsOn}\n`);
|
|
522
522
|
|
|
@@ -640,7 +640,7 @@ export async function runInit(argv) {
|
|
|
640
640
|
log(M.codexTrust);
|
|
641
641
|
|
|
642
642
|
// 3. .mcp.json --------------------------------------------------------------
|
|
643
|
-
// Written when
|
|
643
|
+
// Written when the native WendKeep MCP is wanted OR a selected companion ships an MCP server.
|
|
644
644
|
const companionMcp = companionMcpPatch(companions, skipMcp);
|
|
645
645
|
if (args.mcp || Object.keys(companionMcp).length) {
|
|
646
646
|
const mcpPath = join(projectPath, '.mcp.json');
|
package/src/mcp.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Compatibility facade keeps the CLI and hooks from depending directly on a sibling adapter.
|
|
2
|
+
export { MCP_HELP, runMcp } from '../packages/mcp/src/cli.mjs';
|
|
3
|
+
export {
|
|
4
|
+
MCP_EFFECT_MANIFEST,
|
|
5
|
+
resolveMcpToolEffect,
|
|
6
|
+
verifyMcpEffectManifest,
|
|
7
|
+
} from '../packages/mcp/src/effects.mjs';
|
|
@@ -5,6 +5,7 @@ import { allChangesState } from '../hooks/change-core.mjs';
|
|
|
5
5
|
import { readControl, readSessionRegistry } from '../hooks/obsidian-common.mjs';
|
|
6
6
|
import { runVaultHealth } from '../hooks/vault-health.mjs';
|
|
7
7
|
import { readProjectForValidation } from '../packages/vault/src/validate-memory.mjs';
|
|
8
|
+
import { inspectSyncOutbox, readLocalSyncState } from './sync-outbox.mjs';
|
|
8
9
|
|
|
9
10
|
export const OBSERVER_SCHEMA_VERSION = 1;
|
|
10
11
|
export const MAX_SNAPSHOT_BYTES = 32 * 1024;
|
|
@@ -46,12 +47,21 @@ function activeSessionSummary(vaultBase, control, registry) {
|
|
|
46
47
|
.sort((a, b) => String(b.entry?.last_seen || b.entry?.updated_at || '').localeCompare(String(a.entry?.last_seen || a.entry?.updated_at || '')));
|
|
47
48
|
const selected = entries.find(({ sessionId }) => sessionId === control?.session_id) || entries[0];
|
|
48
49
|
const entry = selected?.entry || {};
|
|
50
|
+
const coverage = entry.host_coverage;
|
|
49
51
|
return {
|
|
50
52
|
status: safeText(control?.status || entry.status || 'inactive', 32),
|
|
51
53
|
session_id: safeText(control?.session_id || selected?.sessionId || '', 100),
|
|
52
54
|
provider: safeText(entry.provider || '', 32),
|
|
53
55
|
change_slug: safeText(entry.change_slug || '', 100),
|
|
54
56
|
last_seen: safeText(entry.last_seen || entry.updated_at || '', 40),
|
|
57
|
+
...(coverage ? {
|
|
58
|
+
coverage: {
|
|
59
|
+
host_id: safeText(coverage.host_id || 'unknown', 32),
|
|
60
|
+
degraded: coverage.degraded === true,
|
|
61
|
+
unavailable: (coverage.capabilities || []).filter((item) => item?.state === 'unavailable').length,
|
|
62
|
+
manual: (coverage.capabilities || []).filter((item) => item?.state === 'manual').length,
|
|
63
|
+
},
|
|
64
|
+
} : {}),
|
|
55
65
|
};
|
|
56
66
|
}
|
|
57
67
|
|
|
@@ -83,6 +93,20 @@ function hashEvent(snapshot) {
|
|
|
83
93
|
return `obs-${createHash('sha256').update(canonical).digest('hex').slice(0, 24)}`;
|
|
84
94
|
}
|
|
85
95
|
|
|
96
|
+
function syncSummary(vaultBase, projectId) {
|
|
97
|
+
const outbox = inspectSyncOutbox(vaultBase);
|
|
98
|
+
let openConflicts = 0;
|
|
99
|
+
if (!['disabled', 'corrupt'].includes(outbox.status)) {
|
|
100
|
+
try {
|
|
101
|
+
const state = readLocalSyncState(vaultBase, projectId);
|
|
102
|
+
openConflicts = Object.values(state.conflicts || {}).filter((item) => item?.status === 'open').length;
|
|
103
|
+
} catch {
|
|
104
|
+
return { status: 'corrupt', pending: outbox.pending, open_conflicts: 0 };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { status: outbox.status, pending: outbox.pending, open_conflicts: openConflicts };
|
|
108
|
+
}
|
|
109
|
+
|
|
86
110
|
function hasForbiddenKey(value) {
|
|
87
111
|
if (!value || typeof value !== 'object') return false;
|
|
88
112
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -145,6 +169,7 @@ export function buildProjectSnapshot({ vaultBase, projectRoot = process.cwd(), n
|
|
|
145
169
|
session: activeSessionSummary(vaultBase, control, registry),
|
|
146
170
|
changes,
|
|
147
171
|
health: healthSummary(vaultBase),
|
|
172
|
+
sync: syncSummary(vaultBase, project.projectId),
|
|
148
173
|
};
|
|
149
174
|
snapshot.event_id = hashEvent(snapshot);
|
|
150
175
|
const validation = validateObserverSnapshot(snapshot, { projectId: project.projectId });
|