@kontextmind/kxm 0.7.0 → 0.7.10
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/.claude-plugin/marketplace.json +1 -1
- package/.kxm/roles/writer.yaml +2 -0
- package/docs/README.md +3 -0
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
- package/docs/agent-skills.md +19 -2
- package/docs/browser-automation.md +116 -0
- package/docs/configuration.md +10 -1
- package/docs/getting-started.md +21 -0
- package/docs/kb/how-credentials-retrieved-safely.md +31 -0
- package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
- package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
- package/docs/kb/how-to-resume-after-mfa.md +28 -0
- package/docs/kb/how-to-take-over-session.md +32 -0
- package/docs/kb/why-authentication-disappeared.md +32 -0
- package/docs/kb/why-automation-opened-different-browser.md +32 -0
- package/docs/kb/why-session-viewer-cannot-control.md +31 -0
- package/docs/operations.md +24 -0
- package/docs/prompts/browser-annotate-feedback.md +41 -0
- package/docs/prompts/browser-diagnose-recover.md +38 -0
- package/docs/prompts/browser-explore.md +42 -0
- package/docs/prompts/browser-repro-fix.md +48 -0
- package/docs/prompts/browser-start.md +41 -0
- package/docs/prompts/browser-takeover.md +50 -0
- package/docs/skills/repo-work-delivery.md +5 -0
- package/docs/skills.md +2 -0
- package/docs/troubleshooting.md +22 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +41203 -38364
- package/plugins/kxm/dist/core.js +57 -0
- package/plugins/kxm/dist/extension.js +40 -3
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +1582 -81
- package/plugins/kxm/dist/server.js +129 -4
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +221 -41
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/hints.json +30 -0
- package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
- package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +9 -2
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/browser.ts +603 -0
- package/plugins/kxm/src/cli/context-skills.ts +373 -0
- package/plugins/kxm/src/cli/hub.ts +614 -0
- package/plugins/kxm/src/cli/roles.ts +615 -0
- package/plugins/kxm/src/cli/system.ts +906 -0
- package/plugins/kxm/src/cli/tasks.ts +364 -0
- package/plugins/kxm/src/cli/types.ts +270 -0
- package/plugins/kxm/src/cli/vnext.ts +698 -0
- package/plugins/kxm/src/cli/workflows.ts +699 -0
- package/plugins/kxm/src/cli.ts +238 -3791
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/external-effects.ts +1 -1
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +8 -8
- package/plugins/kxm/src/modes.ts +348 -0
- package/plugins/kxm/src/protocol.ts +111 -0
- package/plugins/kxm/src/role.ts +335 -0
- package/plugins/kxm/src/runtime.ts +4 -0
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/ssh-remote.ts +560 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/subagent-control.ts +312 -0
- package/plugins/kxm/src/vnext-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +38 -1
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +16 -0
- package/plugins/kxm/src/vnext-harness.ts +92 -22
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +39 -7
- package/plugins/kxm/src/vnext-oneshot-process.ts +46 -8
- package/plugins/kxm/src/vnext-oneshot-producer.ts +22 -1
- package/plugins/kxm/src/vnext-pi-producer.ts +11 -7
- package/plugins/kxm/src/vnext-runtime-store.ts +1 -1
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +5 -3
- package/plugins/kxm/src/workflow-tui.ts +1 -1
- package/plugins/kxm/src/workflow.ts +144 -0
- package/schemas/vnext/modes.schema.json +56 -0
- package/scripts/kxm-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +145 -3
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive post-init setup for workflow-guide agents and workflows.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth: `docs/workflow-guide.md`. The catalog below transcribes the
|
|
5
|
+
* software-engineering area (workflows, stages, role slugs, and the guide's
|
|
6
|
+
* ordered candidate lists). Guide candidates are dated research; each role's
|
|
7
|
+
* first candidate whose harness is installed AND authenticated wins. No
|
|
8
|
+
* candidate is admitted without an authenticated harness (fail closed).
|
|
9
|
+
*
|
|
10
|
+
* This module writes only current vNext project resources:
|
|
11
|
+
* - `.kxm/agents/<role-slug>.yaml` (kxm.agent.v1)
|
|
12
|
+
* - `.kxm/workflows/<slug>.yaml` (kxm.workflow.v1)
|
|
13
|
+
* It never writes retired legacy authority (`.kxm/config`, `.kxm/roster.json`)
|
|
14
|
+
* and does not use the kxm.role.v1 subsystem.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { stringify } from "yaml";
|
|
20
|
+
import { NATIVE_HARNESS_PROVIDERS, type HarnessInventory } from "./vnext-harness.ts";
|
|
21
|
+
|
|
22
|
+
export interface GuideCandidate {
|
|
23
|
+
readonly vendor: string;
|
|
24
|
+
readonly model: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GuideStage {
|
|
28
|
+
readonly slug: string;
|
|
29
|
+
readonly title: string;
|
|
30
|
+
readonly role: string;
|
|
31
|
+
readonly domain: string;
|
|
32
|
+
readonly candidates: readonly GuideCandidate[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface GuideWorkflow {
|
|
36
|
+
readonly slug: string;
|
|
37
|
+
readonly area: string;
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly summary: string;
|
|
40
|
+
readonly stages: readonly GuideStage[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Vendor prefix → native harness, using the guide's vendor spellings.
|
|
44
|
+
* Everything else routes via Pi/OpenRouter. Native-vendor candidates never
|
|
45
|
+
* fall back to OpenRouter when their native harness is unavailable: fail
|
|
46
|
+
* closed instead of billing the same vendor through a second provider. */
|
|
47
|
+
const NATIVE_VENDOR_HARNESS: Readonly<Record<string, string>> = Object.freeze({
|
|
48
|
+
anthropic: "claude",
|
|
49
|
+
openai: "codex",
|
|
50
|
+
"x-ai": "grok",
|
|
51
|
+
xai: "grok",
|
|
52
|
+
google: "agy",
|
|
53
|
+
moonshotai: "kimi",
|
|
54
|
+
moonshot: "kimi",
|
|
55
|
+
deepseek: "deepseek",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/** Guide candidates are ordered by preference; the first eligible wins. */
|
|
59
|
+
export const GUIDE_WORKFLOWS: readonly GuideWorkflow[] = [
|
|
60
|
+
{
|
|
61
|
+
slug: "build-feature",
|
|
62
|
+
area: "software-engineering",
|
|
63
|
+
name: "Build Feature",
|
|
64
|
+
summary: "PRD ingestion → system planning → spec/contract → scaffold → backend → frontend → SDK integration",
|
|
65
|
+
stages: [
|
|
66
|
+
{
|
|
67
|
+
slug: "system-planning",
|
|
68
|
+
title: "System planning",
|
|
69
|
+
role: "lead-systems-planner",
|
|
70
|
+
domain: "Decomposes requirements into an executable, dependency-ordered DAG of modules, tasks, and verification gates.",
|
|
71
|
+
candidates: [
|
|
72
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
73
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
74
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
75
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
76
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
slug: "spec-contract",
|
|
81
|
+
title: "Spec and contract",
|
|
82
|
+
role: "spec-contract-generator",
|
|
83
|
+
domain: "Authors zero-ambiguity API contracts, type definitions, and schema validation rules before implementation begins.",
|
|
84
|
+
candidates: [
|
|
85
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
86
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
87
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
88
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
89
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
slug: "scaffold",
|
|
94
|
+
title: "Scaffold",
|
|
95
|
+
role: "scaffold-build-specialist",
|
|
96
|
+
domain: "Provisions monorepo workspace topologies, linters, Docker multi-stage builds, and CI pipelines.",
|
|
97
|
+
candidates: [
|
|
98
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
99
|
+
{ vendor: "mistralai", model: "devstral-2512" },
|
|
100
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
101
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
102
|
+
{ vendor: "kwaipilot", model: "kat-coder-pro-v2.5" },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
slug: "backend-data",
|
|
107
|
+
title: "Backend and data",
|
|
108
|
+
role: "backend-data-implementer",
|
|
109
|
+
domain: "Implements domain entities, SQL queries/migrations, ORM persistence, concurrency primitives, and transactional boundaries.",
|
|
110
|
+
candidates: [
|
|
111
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
112
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
113
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
114
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
115
|
+
{ vendor: "kwaipilot", model: "kat-coder-pro-v2.5" },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
slug: "frontend",
|
|
120
|
+
title: "Frontend",
|
|
121
|
+
role: "frontend-fullstack-implementer",
|
|
122
|
+
domain: "Constructs stateful UI components, routing, client cache management, responsive styles, and accessible interactions.",
|
|
123
|
+
candidates: [
|
|
124
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" },
|
|
125
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
126
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
127
|
+
{ vendor: "bytedance-seed", model: "seed-2.0-code" },
|
|
128
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
slug: "sdk-integration",
|
|
133
|
+
title: "SDK integration",
|
|
134
|
+
role: "sdk-integration-specialist",
|
|
135
|
+
domain: "Wires external APIs, authentication flows (OAuth2/OIDC), webhook validation, cloud SDKs, and async worker queues.",
|
|
136
|
+
candidates: [
|
|
137
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
138
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
139
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
140
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
141
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
slug: "refactor-repair-regressions",
|
|
148
|
+
area: "software-engineering",
|
|
149
|
+
name: "Refactor and Repair Regressions",
|
|
150
|
+
summary: "Codebase smell analysis → modular decomposition → surgical multi-file transforms → equivalence verification",
|
|
151
|
+
stages: [
|
|
152
|
+
{
|
|
153
|
+
slug: "refactor-architecture",
|
|
154
|
+
title: "Refactor architecture",
|
|
155
|
+
role: "refactoring-architect",
|
|
156
|
+
domain: "Analyzes code coupling, designs clean modular boundaries, and authors step-by-step non-breaking migration plans.",
|
|
157
|
+
candidates: [
|
|
158
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
159
|
+
{ vendor: "z-ai", model: "glm-5.3" },
|
|
160
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
161
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
162
|
+
{ vendor: "moonshotai", model: "kimi-k3" },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
slug: "code-transform",
|
|
167
|
+
title: "Code transform",
|
|
168
|
+
role: "code-transform-implementer",
|
|
169
|
+
domain: "Executes surgical multi-file edits, applying design patterns while preserving comments and formatting.",
|
|
170
|
+
candidates: [
|
|
171
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
172
|
+
{ vendor: "relace", model: "relace-apply-3" },
|
|
173
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
174
|
+
{ vendor: "morph", model: "morph-v3-large" },
|
|
175
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" },
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
slug: "equivalence-verification",
|
|
180
|
+
title: "Equivalence verification",
|
|
181
|
+
role: "semantic-equivalence-verifier",
|
|
182
|
+
domain: "Critic-with-gate: reviews public contract compliance and behavioral equivalence; deterministic checks remain the fixed witness gate and are never delegated to an LLM.",
|
|
183
|
+
candidates: [
|
|
184
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
185
|
+
{ vendor: "openai", model: "o3-mini-high" },
|
|
186
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
187
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
188
|
+
{ vendor: "mistralai", model: "devstral-2512" },
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
slug: "stabilize-flaky-tests",
|
|
195
|
+
area: "software-engineering",
|
|
196
|
+
name: "Stabilize Flaky Tests",
|
|
197
|
+
summary: "Flakiness root-cause extraction → deterministic mock/async hardening",
|
|
198
|
+
stages: [
|
|
199
|
+
{
|
|
200
|
+
slug: "flakiness-diagnosis",
|
|
201
|
+
title: "Flakiness diagnosis",
|
|
202
|
+
role: "concurrency-flakiness-detective",
|
|
203
|
+
domain: "Untangles timing hazards, async wait conditions, shared state pollution, and unhandled promise rejections.",
|
|
204
|
+
candidates: [
|
|
205
|
+
{ vendor: "openai", model: "o3" },
|
|
206
|
+
{ vendor: "deepseek", model: "deepseek-r1-0528" },
|
|
207
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
208
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
209
|
+
{ vendor: "openai", model: "gpt-6-astra" },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
slug: "design-software-system",
|
|
216
|
+
area: "software-engineering",
|
|
217
|
+
name: "Design Software System",
|
|
218
|
+
summary: "Architecture → database topology → threat modeling → IaC → reliability review → protocol audit",
|
|
219
|
+
stages: [
|
|
220
|
+
{
|
|
221
|
+
slug: "distributed-architecture",
|
|
222
|
+
title: "Distributed architecture",
|
|
223
|
+
role: "principal-distributed-architect",
|
|
224
|
+
domain: "Evaluates CAP/PACELC trade-offs, consensus protocols (Raft, Sagas), partition boundaries, and authors primary RFCs.",
|
|
225
|
+
candidates: [
|
|
226
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
227
|
+
{ vendor: "openai", model: "o3" },
|
|
228
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
229
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
230
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
slug: "database-topology",
|
|
235
|
+
title: "Database topology",
|
|
236
|
+
role: "scalability-database-topologist",
|
|
237
|
+
domain: "Designs relational/NoSQL schemas, horizontal sharding keys, caching topologies, and zero-downtime migration plans.",
|
|
238
|
+
candidates: [
|
|
239
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
240
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
241
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
242
|
+
{ vendor: "qwen", model: "qwen3.8-2.4t-a95b" },
|
|
243
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
slug: "threat-modeling",
|
|
248
|
+
title: "Threat modeling",
|
|
249
|
+
role: "security-architect-threat-modeler",
|
|
250
|
+
domain: "Conducts STRIDE threat assessments, reviews IAM least-privilege policies, mTLS boundaries, and zero-trust architectures.",
|
|
251
|
+
candidates: [
|
|
252
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
253
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
254
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
255
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
256
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
slug: "iac-topology",
|
|
261
|
+
title: "IaC topology",
|
|
262
|
+
role: "iac-cloud-topology-engineer",
|
|
263
|
+
domain: "Generates declarative Terraform/OpenTofu, Pulumi, Kubernetes CRDs, and VPC networking definitions.",
|
|
264
|
+
candidates: [
|
|
265
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
266
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
267
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
268
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
269
|
+
{ vendor: "mistralai", model: "devstral-2512" },
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
slug: "reliability-review",
|
|
274
|
+
title: "Reliability review",
|
|
275
|
+
role: "reliability-chaos-reviewer",
|
|
276
|
+
domain: "Evaluates single points of failure, RPO/RTO metrics, cascading timeouts, circuit breakers, and active-active failovers.",
|
|
277
|
+
candidates: [
|
|
278
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
279
|
+
{ vendor: "openai", model: "o3" },
|
|
280
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
281
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
282
|
+
{ vendor: "moonshotai", model: "kimi-k3" },
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
slug: "protocol-audit",
|
|
287
|
+
title: "Protocol audit",
|
|
288
|
+
role: "architecture-critic-protocol-auditor",
|
|
289
|
+
domain: "Independent peer-review; challenges unvalidated assumptions, prevents architectural drift, and verifies contracts.",
|
|
290
|
+
candidates: [
|
|
291
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
292
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
293
|
+
{ vendor: "z-ai", model: "glm-5.3" },
|
|
294
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
295
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
slug: "maintain-documentation",
|
|
302
|
+
area: "software-engineering",
|
|
303
|
+
name: "Maintain Documentation",
|
|
304
|
+
summary: "Accuracy audit → documentation write → review",
|
|
305
|
+
stages: [
|
|
306
|
+
{
|
|
307
|
+
slug: "accuracy-audit",
|
|
308
|
+
title: "Accuracy audit",
|
|
309
|
+
role: "documentation-accuracy-auditor",
|
|
310
|
+
domain: "Read-only audit of developer and operator documentation against CLI --help outputs, source environment variables, tool schemas, and repository configuration.",
|
|
311
|
+
candidates: [
|
|
312
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
313
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
314
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
315
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" },
|
|
316
|
+
{ vendor: "z-ai", model: "glm-5.3" },
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
slug: "documentation-write",
|
|
321
|
+
title: "Documentation write",
|
|
322
|
+
role: "documentation-writer",
|
|
323
|
+
domain: "Authors concise, accurate developer and operator documentation with clear terminology, correct command flags, and consistent formatting without marketing drift.",
|
|
324
|
+
candidates: [
|
|
325
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
326
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
327
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" },
|
|
328
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
329
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
slug: "review",
|
|
334
|
+
title: "Review",
|
|
335
|
+
role: "documentation-reviewer",
|
|
336
|
+
domain: "Critic-with-gate: the architecture critic verifies authority and permission wording; the CLI critic verifies command/flag accuracy; deterministic docs lint remains the fixed witness gate.",
|
|
337
|
+
candidates: [
|
|
338
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
339
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
340
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
341
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
342
|
+
{ vendor: "mistralai", model: "devstral-2512" },
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
export interface AgentBinding {
|
|
350
|
+
readonly harness: string;
|
|
351
|
+
readonly provider: string;
|
|
352
|
+
readonly model: string;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export interface SkippedStage {
|
|
356
|
+
readonly workflow: string;
|
|
357
|
+
readonly stage: string;
|
|
358
|
+
readonly role: string;
|
|
359
|
+
readonly reason: string;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export interface GuideSetupPlan {
|
|
363
|
+
readonly agents: ReadonlyMap<string, AgentBinding>;
|
|
364
|
+
readonly workflows: GuideWorkflow[];
|
|
365
|
+
readonly skipped: SkippedStage[];
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface GuideSetupFile {
|
|
369
|
+
readonly path: string;
|
|
370
|
+
readonly content: string;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function authenticatedHarnesses(inventory: HarnessInventory): ReadonlySet<string> {
|
|
374
|
+
return new Set(
|
|
375
|
+
inventory.harnesses
|
|
376
|
+
.filter((entry) => entry.detected && entry.authenticated === true)
|
|
377
|
+
.map((entry) => entry.id),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Resolve a guide candidate to a dispatch binding, or undefined when no
|
|
383
|
+
* candidate's harness is authenticated. Non-native vendors route through the
|
|
384
|
+
* Pi OpenRouter provider (guide ids are already OpenRouter-style slugs).
|
|
385
|
+
*/
|
|
386
|
+
export function resolveCandidate(
|
|
387
|
+
candidates: readonly GuideCandidate[],
|
|
388
|
+
eligible: ReadonlySet<string>,
|
|
389
|
+
): AgentBinding | undefined {
|
|
390
|
+
for (const candidate of candidates) {
|
|
391
|
+
const nativeHarness = NATIVE_VENDOR_HARNESS[candidate.vendor];
|
|
392
|
+
if (nativeHarness) {
|
|
393
|
+
if (eligible.has(nativeHarness)) {
|
|
394
|
+
return { harness: nativeHarness, provider: NATIVE_HARNESS_PROVIDERS[nativeHarness]!, model: candidate.model };
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (eligible.has("pi")) {
|
|
399
|
+
return { harness: "pi", provider: "openrouter", model: `${candidate.vendor}/${candidate.model}` };
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Plan agents/workflows for the selected guide slugs without touching disk. */
|
|
406
|
+
export function planGuideSetup(options: {
|
|
407
|
+
inventory: HarnessInventory;
|
|
408
|
+
selected: readonly string[];
|
|
409
|
+
}): GuideSetupPlan {
|
|
410
|
+
const eligible = authenticatedHarnesses(options.inventory);
|
|
411
|
+
const agents = new Map<string, AgentBinding>();
|
|
412
|
+
const workflows: GuideWorkflow[] = [];
|
|
413
|
+
const skipped: SkippedStage[] = [];
|
|
414
|
+
const wanted = new Set(options.selected);
|
|
415
|
+
for (const workflow of GUIDE_WORKFLOWS) {
|
|
416
|
+
if (!wanted.has(workflow.slug)) continue;
|
|
417
|
+
let covered = true;
|
|
418
|
+
for (const stage of workflow.stages) {
|
|
419
|
+
if (agents.has(stage.role)) continue;
|
|
420
|
+
const binding = resolveCandidate(stage.candidates, eligible);
|
|
421
|
+
if (!binding) {
|
|
422
|
+
skipped.push({
|
|
423
|
+
workflow: workflow.slug,
|
|
424
|
+
stage: stage.slug,
|
|
425
|
+
role: stage.role,
|
|
426
|
+
reason: "no guide candidate has an authenticated harness (see `kxm harness list`)",
|
|
427
|
+
});
|
|
428
|
+
covered = false;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
agents.set(stage.role, binding);
|
|
432
|
+
}
|
|
433
|
+
if (covered) workflows.push(workflow);
|
|
434
|
+
else skipped.push({ workflow: workflow.slug, stage: "*", role: "-", reason: "workflow skipped: at least one stage has no eligible agent" });
|
|
435
|
+
}
|
|
436
|
+
return { agents, workflows, skipped };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const WRITER_ROLE_PATTERN = /(writer|implementer|scaffold|backend|frontend|sdk-integration|iac-cloud|code-transform|build-specialist)/;
|
|
440
|
+
|
|
441
|
+
function isWriterRole(roleSlug: string): boolean {
|
|
442
|
+
return WRITER_ROLE_PATTERN.test(roleSlug);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function agentDocument(roleSlug: string, stage: GuideStage, binding: AgentBinding): Record<string, unknown> {
|
|
446
|
+
const writer = isWriterRole(roleSlug);
|
|
447
|
+
return {
|
|
448
|
+
schema: "kxm.agent.v1",
|
|
449
|
+
purpose: `${stage.domain} (workflow-guide ${stage.title}; candidate verification is the operator's responsibility)`,
|
|
450
|
+
harness: binding.harness,
|
|
451
|
+
model: { provider: binding.provider, model: binding.model },
|
|
452
|
+
tools: { preset: writer ? "workspace-writer" : "read-only" },
|
|
453
|
+
defaultRepositoryAccess: writer ? "none" : "read",
|
|
454
|
+
repositories: { control: writer ? "write" : "read" },
|
|
455
|
+
network: "provider-only",
|
|
456
|
+
resultSchema: "kxm.assignment-result.v1",
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function workflowDocument(workflow: GuideWorkflow): Record<string, unknown> {
|
|
461
|
+
const steps = workflow.stages.map((stage, index) => {
|
|
462
|
+
const last = index === workflow.stages.length - 1;
|
|
463
|
+
return {
|
|
464
|
+
id: stage.slug,
|
|
465
|
+
kind: "agent",
|
|
466
|
+
agent: stage.role,
|
|
467
|
+
maxAttempts: 2,
|
|
468
|
+
on: {
|
|
469
|
+
passed: last ? { target: "$terminal", terminalStatus: "completed" } : workflow.stages[index + 1]!.slug,
|
|
470
|
+
failed: { target: "$terminal", terminalStatus: "failed" },
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
});
|
|
474
|
+
return {
|
|
475
|
+
schema: "kxm.workflow.v1",
|
|
476
|
+
description: `${workflow.name} (${workflow.area}, workflow-guide): ${workflow.summary}`,
|
|
477
|
+
coordinator: "coordinator",
|
|
478
|
+
limits: { maxTransitions: Math.max(4, workflow.stages.length * 4) },
|
|
479
|
+
steps,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Render the planned vNext resource files (`.kxm/agents/*.yaml`,
|
|
485
|
+
* `.kxm/workflows/*.yaml`). Pure: no disk access.
|
|
486
|
+
*/
|
|
487
|
+
export function renderGuideSetupFiles(projectRoot: string, plan: GuideSetupPlan): GuideSetupFile[] {
|
|
488
|
+
const files: GuideSetupFile[] = [];
|
|
489
|
+
const roleStages = new Map<string, GuideStage>();
|
|
490
|
+
for (const workflow of plan.workflows) {
|
|
491
|
+
for (const stage of workflow.stages) roleStages.set(stage.role, stage);
|
|
492
|
+
}
|
|
493
|
+
for (const [role, binding] of [...plan.agents.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
494
|
+
const stage = roleStages.get(role);
|
|
495
|
+
if (!stage) continue;
|
|
496
|
+
files.push({
|
|
497
|
+
path: join(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
498
|
+
content: stringify(agentDocument(role, stage, binding)),
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
for (const workflow of plan.workflows) {
|
|
502
|
+
files.push({
|
|
503
|
+
path: join(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
504
|
+
content: stringify(workflowDocument(workflow)),
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return files;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export interface WriteReport {
|
|
511
|
+
readonly written: string[];
|
|
512
|
+
readonly existed: string[];
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Write rendered files, creating directories as needed; never overwrites. */
|
|
516
|
+
export function writeGuideSetupFiles(files: readonly GuideSetupFile[]): WriteReport {
|
|
517
|
+
const written: string[] = [];
|
|
518
|
+
const existed: string[] = [];
|
|
519
|
+
for (const file of files) {
|
|
520
|
+
if (existsSync(file.path)) {
|
|
521
|
+
existed.push(file.path);
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
mkdirSync(dirname(file.path), { recursive: true });
|
|
525
|
+
writeFileSync(file.path, file.content, "utf8");
|
|
526
|
+
written.push(file.path);
|
|
527
|
+
}
|
|
528
|
+
return { written, existed };
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Parse a prompt answer like "1,3", "build-feature, all", or "none". */
|
|
532
|
+
export function parseGuideSelection(input: string, catalog: readonly GuideWorkflow[] = GUIDE_WORKFLOWS): string[] {
|
|
533
|
+
const trimmed = input.trim().toLowerCase();
|
|
534
|
+
if (!trimmed || trimmed === "none" || trimmed === "n" || trimmed === "no") return [];
|
|
535
|
+
if (trimmed === "all") return catalog.map((workflow) => workflow.slug);
|
|
536
|
+
const selected = new Set<string>();
|
|
537
|
+
for (const token of trimmed.split(/[,\s]+/).filter(Boolean)) {
|
|
538
|
+
const index = Number.parseInt(token, 10);
|
|
539
|
+
if (Number.isFinite(index) && index >= 1 && index <= catalog.length) {
|
|
540
|
+
selected.add(catalog[index - 1]!.slug);
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const match = catalog.find((workflow) => workflow.slug === token);
|
|
544
|
+
if (match) selected.add(match.slug);
|
|
545
|
+
}
|
|
546
|
+
return [...selected];
|
|
547
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { isAbsolute, join, resolve } from "node:path";
|
|
4
|
-
import { DatabaseSync } from "
|
|
4
|
+
import { DatabaseSync } from "./sqlite.ts";
|
|
5
5
|
import type { AgentRecord, MessageRecord } from "./protocol.ts";
|
|
6
6
|
import type { WorkflowRun } from "./workflow.ts";
|
|
7
7
|
import { readRoutingRecords } from "./telemetry.ts";
|
|
@@ -7,7 +7,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
|
|
|
7
7
|
import { deliverInboxNotification } from "./inbox.ts";
|
|
8
8
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
9
9
|
|
|
10
|
-
const VERSION = "0.7.
|
|
10
|
+
const VERSION = "0.7.10";
|
|
11
11
|
const inbox = new Map<string, MessageRecord>();
|
|
12
12
|
const notifiedInbox = new Set<string>();
|
|
13
13
|
let meshClient: HubClient | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import { stringify } from "yaml";
|
|
4
|
+
import { defaultSpawn } from "./vnext-oneshot-process.ts";
|
|
5
5
|
|
|
6
6
|
export interface InventoryPrice {
|
|
7
7
|
inputPerMillion: number | null;
|
|
@@ -95,10 +95,10 @@ export async function refreshModelInventory(options: { outputRoot: string; env?:
|
|
|
95
95
|
if (item.fast === true) current.capabilities.speed = { fast: true, tiers: ["fast"], source };
|
|
96
96
|
byId.set(key, current);
|
|
97
97
|
};
|
|
98
|
-
const native = (command: string, args: string[], source: string) => {
|
|
99
|
-
const result =
|
|
100
|
-
const ok = result.
|
|
101
|
-
sources[source] = { url: `${command} ${args.join(" ")}`, ok, ...(ok ? {} : { error: (result.stderr || "command failed").trim().slice(0, 240) }) };
|
|
98
|
+
const native = async (command: string, args: string[], source: string) => {
|
|
99
|
+
const result = await defaultSpawn(command, args, { env, timeoutMs: 20_000 });
|
|
100
|
+
const ok = result.code === 0 && !result.error;
|
|
101
|
+
sources[source] = { url: `${command} ${args.join(" ")}`, ok, ...(ok ? {} : { error: (result.stderr || result.error?.message || "command failed").trim().slice(0, 240) }) };
|
|
102
102
|
if (!ok) return;
|
|
103
103
|
for (const line of (result.stdout || "").split(/\r?\n/)) {
|
|
104
104
|
const parts = line.trim().split(/\s+/);
|
|
@@ -109,9 +109,9 @@ export async function refreshModelInventory(options: { outputRoot: string; env?:
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
|
-
native("pi", ["--list-models"], "pi");
|
|
113
|
-
native("grok", ["models"], "grok");
|
|
114
|
-
native("agy", ["models"], "agy");
|
|
112
|
+
await native("pi", ["--list-models"], "pi");
|
|
113
|
+
await native("grok", ["models"], "grok");
|
|
114
|
+
await native("agy", ["models"], "agy");
|
|
115
115
|
const openrouter = await fetchModels(env.KXM_OPENROUTER_MODELS_URL?.trim() || OR_URL, env.OPENROUTER_API_KEY);
|
|
116
116
|
const openrouterUrl = env.KXM_OPENROUTER_MODELS_URL?.trim() || OR_URL;
|
|
117
117
|
sources.openrouter = { url: openrouterUrl, ok: !openrouter.error, ...(openrouter.error ? { error: openrouter.error } : {}) };
|