kushi-agents 5.1.0 → 5.3.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.
- package/README.md +85 -0
- package/bin/cli.mjs +188 -1
- package/package.json +2 -2
- package/plugin/agents/kushi.agent.md +5 -0
- package/plugin/instructions/global-wiki.instructions.md +79 -0
- package/plugin/instructions/hooks.instructions.md +84 -0
- package/plugin/instructions/multi-wiki-routing.instructions.md +117 -0
- package/plugin/instructions/otel.instructions.md +75 -0
- package/plugin/instructions/parallel-execution.instructions.md +81 -0
- package/plugin/instructions/schema-evolve.instructions.md +73 -0
- package/plugin/skills/_shared/Emit-OtelSpan.ps1 +111 -0
- package/plugin/skills/_shared/Invoke-Hooks.ps1 +177 -0
- package/plugin/skills/_shared/hook-templates/console-debug.ps1 +15 -0
- package/plugin/skills/_shared/hook-templates/teams-notify.ps1 +47 -0
- package/plugin/skills/ask-project/SKILL.md +14 -0
- package/plugin/skills/global-wiki/.created-by-skill-creator +1 -0
- package/plugin/skills/global-wiki/SKILL.md +87 -0
- package/plugin/skills/global-wiki/evals/evals.json +43 -0
- package/plugin/skills/promote/.created-by-skill-creator +1 -0
- package/plugin/skills/promote/SKILL.md +125 -0
- package/plugin/skills/promote/evals/evals.json +35 -0
- package/plugin/skills/refresh-project/SKILL.md +8 -4
- package/plugin/skills/schema-evolve/.created-by-skill-creator +0 -0
- package/plugin/skills/schema-evolve/SKILL.md +106 -0
- package/plugin/skills/schema-evolve/evals/evals.json +37 -0
- package/plugin/skills/self-check/SKILL.md +12 -54
- package/plugin/skills/self-check/references/algorithm.md +55 -0
- package/plugin/skills/self-check/run.ps1 +155 -0
- package/plugin/skills/teach/.created-by-skill-creator +0 -0
- package/plugin/skills/teach/SKILL.md +79 -0
- package/plugin/skills/teach/evals/evals.json +59 -0
- package/src/global-wiki-cli.mjs +158 -0
- package/src/global-wiki.mjs +503 -0
- package/src/global-wiki.test.mjs +135 -0
- package/src/hooks-dispatcher.test.mjs +135 -0
- package/src/otel-emit.test.mjs +73 -0
- package/src/parallel-refresh.test.mjs +50 -0
- package/src/promote.test.mjs +161 -0
- package/src/schema-evolve.test.mjs +78 -0
- package/src/teach.test.mjs +45 -0
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
[](https://gim-home.github.io/kushi/)
|
|
8
8
|
[](https://agentskills.io/skill-creation/best-practices)
|
|
9
9
|
|
|
10
|
+
> **v5.2.0 — Hooks + parallel pulls + OTel + teach + schema-evolve.** Pipeline events trigger configurable hooks (`.kushi/hooks/`); pull dispatch is parallel by default (4 workers); OpenTelemetry export is opt-in via `KUSHI_OTEL_ENDPOINT`; `kushi explain <topic>` teaches concepts; `kushi remember <rule>` persists conventions.
|
|
11
|
+
|
|
10
12
|
> **v5.1.0 — Living wiki.** Build-state is now incremental: human edits outside `<!-- kushi:auto -->` fences are preserved, contradictions are flagged with Obsidian-compatible callouts (`> [!warning]`), and a new `lint-state` skill monitors wiki health. State/ is a valid [Obsidian](https://obsidian.md) vault — callout syntax, Dataview-compatible frontmatter, and `[[wikilinks]]` all work natively.
|
|
11
13
|
|
|
12
14
|
> **v5.0.1 spec-compliance pass.** Every `plugin/skills/<name>/SKILL.md` follows the [agentskills.io best practices](https://agentskills.io/skill-creation/best-practices) and [optimizing-descriptions](https://agentskills.io/skill-creation/optimizing-descriptions) guides: ≤ 500 lines & ≤ 5000 tokens, load-on-trigger references, top-5 gotchas, checklist orchestrators, validation loops, plan-validate-execute for graph + State writers, and "USE WHEN …" descriptions. Enforced by `self-check -Deep` D30.*.
|
|
@@ -93,6 +95,89 @@ To use: point Obsidian at `<project>/State/` as a vault. The `index.md` serves a
|
|
|
93
95
|
|
|
94
96
|
---
|
|
95
97
|
|
|
98
|
+
## Hooks (v5.2.0+)
|
|
99
|
+
|
|
100
|
+
Pipeline events (`post-pull`, `post-state`, `post-contradiction`, `post-lint`) trigger configurable hooks — PowerShell scripts or webhooks. Failures are logged but never block the pipeline.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# List configured hooks
|
|
104
|
+
kushi hooks list MyProject
|
|
105
|
+
|
|
106
|
+
# Test-fire a synthetic event
|
|
107
|
+
kushi hooks test MyProject post-pull
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Configure in `.kushi/hooks.yml` or drop scripts into `.kushi/hooks/<event>.ps1`. Templates at `plugin/skills/_shared/hook-templates/`.
|
|
111
|
+
|
|
112
|
+
## Parallel refresh (v5.2.0+)
|
|
113
|
+
|
|
114
|
+
Pull dispatch is parallel by default (4 workers). Each source writes to its own isolated subtree. Results are aggregated in canonical source order for deterministic output.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Default: parallel (up to 4 workers)
|
|
118
|
+
kushi refresh MyProject
|
|
119
|
+
|
|
120
|
+
# Force sequential (old behavior)
|
|
121
|
+
kushi refresh MyProject --sequential
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Configure `parallel.max_workers` in `.kushi/config.yml`. See `parallel-execution.instructions.md`.
|
|
125
|
+
|
|
126
|
+
## Telemetry (opt-in, v5.2.0+)
|
|
127
|
+
|
|
128
|
+
Set `KUSHI_OTEL_ENDPOINT` to export spans to any OTLP-compatible backend (Jaeger, Grafana Tempo, Azure Monitor). Zero overhead when unset. No evidence content or PII — metadata only.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
export KUSHI_OTEL_ENDPOINT=http://localhost:4318/v1/traces
|
|
132
|
+
kushi refresh MyProject # spans emitted for each pull + build-state + lint
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Teach mode (v5.2.0+)
|
|
136
|
+
|
|
137
|
+
Pedagogical, read-only. Explains kushi concepts by loading relevant doctrine + genealogy.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
kushi explain contradictions
|
|
141
|
+
kushi explain parallel
|
|
142
|
+
kushi explain hooks
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Schema evolve (v5.2.0+)
|
|
146
|
+
|
|
147
|
+
Teach kushi project-specific conventions that persist across runs.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
kushi remember "always use 'HCA' not 'Healthcare Accelerator' in summaries"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Rules stored in `Evidence/<alias>/State/CLAUDE.md`. Read by `build-state`, `ask-project`, `refresh-project` at run start.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Global wiki (v5.3.0+)
|
|
158
|
+
|
|
159
|
+
A per-user **global wiki** at `~/.kushi-global/State/` for cross-engagement knowledge — same Karpathy shape as a project `State/`, marked `scope: global`.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
kushi global init # scaffold ~/.kushi-global/State/
|
|
163
|
+
kushi global status # counts + last-modified summary
|
|
164
|
+
kushi global ask "what's our confidence ladder pattern?"
|
|
165
|
+
kushi global lint # privacy + structure pass
|
|
166
|
+
|
|
167
|
+
# Project → global is explicit-only:
|
|
168
|
+
kushi promote <project> <answer-page> # refuses if identifiers detected
|
|
169
|
+
kushi promote <project> <answer-page> --force # redacts + writes target + back-link
|
|
170
|
+
|
|
171
|
+
# Routing under ask-project:
|
|
172
|
+
kushi ask <project> "..." # project-first (default)
|
|
173
|
+
kushi ask <project> "..." --global # global-first
|
|
174
|
+
kushi ask <project> "..." --project-only # suppress global
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The global root honors `$KUSHI_GLOBAL_ROOT` (used by tests). There is no auto-promotion path — privacy is always your call. See `plugin/instructions/global-wiki.instructions.md` + `plugin/instructions/multi-wiki-routing.instructions.md`.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
96
181
|
## Three install profiles
|
|
97
182
|
|
|
98
183
|
Kushi ships in three tiers. Pick how much you take — the default (`standard`) matches v2.x behavior end-to-end.
|
package/bin/cli.mjs
CHANGED
|
@@ -17,15 +17,89 @@ if (args.length > 0 && SKILL_VERBS.has(args[0])) {
|
|
|
17
17
|
|
|
18
18
|
// ── lint verb (v5.1.0+) ──────────────────────────────────────────────────────
|
|
19
19
|
if (args.length > 0 && args[0] === 'lint') {
|
|
20
|
+
if (args.includes('--global')) {
|
|
21
|
+
const { runGlobalLint } = await import('../src/global-wiki-cli.mjs');
|
|
22
|
+
await runGlobalLint();
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
20
25
|
const project = args[1] || '';
|
|
21
26
|
if (!project) {
|
|
22
|
-
console.error('\n Usage: kushi lint <project>\n');
|
|
27
|
+
console.error('\n Usage: kushi lint <project>\n kushi lint --global\n');
|
|
23
28
|
process.exit(1);
|
|
24
29
|
}
|
|
25
30
|
await dispatchLint(project);
|
|
26
31
|
process.exit(0);
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
// ── global verb (v5.3.0+) ────────────────────────────────────────────────────
|
|
35
|
+
if (args.length > 0 && args[0] === 'global') {
|
|
36
|
+
const sub = args[1] || '';
|
|
37
|
+
const validSubs = ['init', 'status', 'ask', 'lint'];
|
|
38
|
+
if (!validSubs.includes(sub)) {
|
|
39
|
+
console.error('\n Usage: kushi global init Scaffold ~/.kushi-global/State/');
|
|
40
|
+
console.error(' kushi global status Show counts + freshness');
|
|
41
|
+
console.error(' kushi global ask <question> Ask the global wiki');
|
|
42
|
+
console.error(' kushi global lint Lint the global wiki\n');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const { runGlobalInit, runGlobalStatus, runGlobalAsk, runGlobalLint } = await import('../src/global-wiki-cli.mjs');
|
|
46
|
+
if (sub === 'init') await runGlobalInit();
|
|
47
|
+
else if (sub === 'status') await runGlobalStatus();
|
|
48
|
+
else if (sub === 'ask') await runGlobalAsk(args.slice(2).join(' '));
|
|
49
|
+
else if (sub === 'lint') await runGlobalLint();
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── promote verb (v5.3.0+) ───────────────────────────────────────────────────
|
|
54
|
+
if (args.length > 0 && args[0] === 'promote') {
|
|
55
|
+
const project = args[1] || '';
|
|
56
|
+
const page = args[2] || '';
|
|
57
|
+
if (!project || !page) {
|
|
58
|
+
console.error('\n Usage: kushi promote <project> <page-path>\n');
|
|
59
|
+
console.error(' Copies a project State page into the global wiki with provenance metadata.');
|
|
60
|
+
console.error(' Refuses by default if customer identifiers are detected; pass --force after review.\n');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const force = args.includes('--force');
|
|
64
|
+
const { runPromote } = await import('../src/global-wiki-cli.mjs');
|
|
65
|
+
await runPromote(project, page, { force });
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── hooks verb (v5.2.0+) ─────────────────────────────────────────────────────
|
|
70
|
+
if (args.length > 0 && args[0] === 'hooks') {
|
|
71
|
+
const sub = args[1] || '';
|
|
72
|
+
const project = args[2] || '';
|
|
73
|
+
if (!sub || !project || !['list', 'test'].includes(sub)) {
|
|
74
|
+
console.error('\n Usage: kushi hooks list <project>\n kushi hooks test <project> <event>\n');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
await dispatchHooks(sub, project, args.slice(3));
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── explain verb (v5.2.0+) ───────────────────────────────────────────────────
|
|
82
|
+
if (args.length > 0 && args[0] === 'explain') {
|
|
83
|
+
const topic = args.slice(1).join(' ');
|
|
84
|
+
if (!topic) {
|
|
85
|
+
console.error('\n Usage: kushi explain <topic>\n\n Available topics: contradictions, refresh, state, hooks, parallel, otel, csc, graph, workiq, schema, install, evals\n');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
await dispatchExplain(topic);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── remember verb (v5.2.0+) ──────────────────────────────────────────────────
|
|
93
|
+
if (args.length > 0 && args[0] === 'remember') {
|
|
94
|
+
const rule = args.slice(1).join(' ');
|
|
95
|
+
if (!rule) {
|
|
96
|
+
console.error('\n Usage: kushi remember <rule>\n\n Example: kushi remember "always use HCA not Healthcare Accelerator"\n');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
await dispatchRemember(rule);
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
29
103
|
if (args.includes('--help') || args.includes('-h')) {
|
|
30
104
|
console.log(`
|
|
31
105
|
Usage: npx kushi-agents [options]
|
|
@@ -74,6 +148,21 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
74
148
|
|
|
75
149
|
Wiki maintenance (v5.1.0+):
|
|
76
150
|
lint <project> Run wiki-lint checks on State/ (contradictions, stale claims, orphans).
|
|
151
|
+
lint --global Lint the global wiki at ~/.kushi-global/State/.
|
|
152
|
+
|
|
153
|
+
Hooks & observability (v5.2.0+):
|
|
154
|
+
hooks list <project> List configured hooks for a project.
|
|
155
|
+
hooks test <project> <event> Fire a synthetic hook event for testing.
|
|
156
|
+
explain <topic> Explain a kushi concept (pedagogical, read-only).
|
|
157
|
+
remember <rule> Persist a project convention to CLAUDE.md.
|
|
158
|
+
|
|
159
|
+
Global wiki (v5.3.0+):
|
|
160
|
+
global init Scaffold ~/.kushi-global/State/ (env: KUSHI_GLOBAL_ROOT)
|
|
161
|
+
global status Show page counts + freshness for the global wiki.
|
|
162
|
+
global ask <question> Search the global wiki specifically.
|
|
163
|
+
global lint Lint the global wiki (alias for 'lint --global').
|
|
164
|
+
promote <project> <page> Move a project State page into global with redaction + back-link.
|
|
165
|
+
Refuses if customer identifiers are detected; --force after review.
|
|
77
166
|
|
|
78
167
|
After install, talk to Kushi:
|
|
79
168
|
bootstrap <project> First-time setup
|
|
@@ -279,3 +368,101 @@ function pickFlag(args, flag) {
|
|
|
279
368
|
const m = args.find((a) => a.startsWith(prefix));
|
|
280
369
|
return m ? m.slice(prefix.length) : undefined;
|
|
281
370
|
}
|
|
371
|
+
|
|
372
|
+
// ── v5.2.0 dispatch helpers ──────────────────────────────────────────────────
|
|
373
|
+
|
|
374
|
+
async function dispatchHooks(sub, project, extra) {
|
|
375
|
+
const { spawn } = await import('node:child_process');
|
|
376
|
+
const { resolve } = await import('node:path');
|
|
377
|
+
const { fileURLToPath } = await import('node:url');
|
|
378
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
379
|
+
const invokeHooks = resolve(__dirname, '..', 'plugin', 'skills', '_shared', 'Invoke-Hooks.ps1');
|
|
380
|
+
|
|
381
|
+
if (sub === 'list') {
|
|
382
|
+
// List hooks from .kushi/hooks.yml
|
|
383
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
384
|
+
const hooksYml = resolve(process.cwd(), project, '.kushi', 'hooks.yml');
|
|
385
|
+
if (!existsSync(hooksYml)) {
|
|
386
|
+
console.log(`\n No hooks configured for '${project}'. Create ${hooksYml} to add hooks.\n`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
console.log(`\n Hooks for '${project}' (${hooksYml}):\n`);
|
|
390
|
+
console.log(readFileSync(hooksYml, 'utf8'));
|
|
391
|
+
} else if (sub === 'test') {
|
|
392
|
+
const event = extra[0] || 'post-pull';
|
|
393
|
+
const script = `
|
|
394
|
+
$payload = @{ project = '${project}'; source = 'test'; success = $true; duration_ms = 0; event = '${event}' }
|
|
395
|
+
& '${invokeHooks.replace(/\\/g, '\\\\')}' -ProjectRoot '${resolve(process.cwd(), project).replace(/\\/g, '\\\\')}' -Event '${event}' -Payload $payload
|
|
396
|
+
`;
|
|
397
|
+
const child = spawn('pwsh', ['-NoProfile', '-Command', script], { stdio: 'inherit' });
|
|
398
|
+
return new Promise((res, rej) => {
|
|
399
|
+
child.on('close', (code) => { if (code !== 0) rej(new Error(`hooks test exited ${code}`)); else res(); });
|
|
400
|
+
child.on('error', rej);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function dispatchExplain(topic) {
|
|
406
|
+
const { resolve } = await import('node:path');
|
|
407
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
408
|
+
const { fileURLToPath } = await import('node:url');
|
|
409
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
410
|
+
const repoRoot = resolve(__dirname, '..');
|
|
411
|
+
const instructionsDir = resolve(repoRoot, 'plugin', 'instructions');
|
|
412
|
+
|
|
413
|
+
const topicMap = {
|
|
414
|
+
contradictions: 'living-wiki.instructions.md',
|
|
415
|
+
conflicts: 'living-wiki.instructions.md',
|
|
416
|
+
refresh: 'parallel-execution.instructions.md',
|
|
417
|
+
pull: 'parallel-execution.instructions.md',
|
|
418
|
+
state: 'living-wiki.instructions.md',
|
|
419
|
+
'build-state': 'living-wiki.instructions.md',
|
|
420
|
+
wiki: 'living-wiki.instructions.md',
|
|
421
|
+
hooks: 'hooks.instructions.md',
|
|
422
|
+
events: 'hooks.instructions.md',
|
|
423
|
+
webhooks: 'hooks.instructions.md',
|
|
424
|
+
parallel: 'parallel-execution.instructions.md',
|
|
425
|
+
workers: 'parallel-execution.instructions.md',
|
|
426
|
+
otel: 'otel.instructions.md',
|
|
427
|
+
telemetry: 'otel.instructions.md',
|
|
428
|
+
tracing: 'otel.instructions.md',
|
|
429
|
+
csc: 'comprehensive-structured-capture.instructions.md',
|
|
430
|
+
capture: 'comprehensive-structured-capture.instructions.md',
|
|
431
|
+
graph: 'entity-graph.instructions.md',
|
|
432
|
+
entities: 'entity-graph.instructions.md',
|
|
433
|
+
workiq: 'workiq-only.instructions.md',
|
|
434
|
+
schema: 'schema-evolve.instructions.md',
|
|
435
|
+
conventions: 'schema-evolve.instructions.md',
|
|
436
|
+
remember: 'schema-evolve.instructions.md',
|
|
437
|
+
install: 'multi-host-install.instructions.md',
|
|
438
|
+
setup: 'multi-host-install.instructions.md',
|
|
439
|
+
evals: 'skill-evals.instructions.md',
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const key = Object.keys(topicMap).find(k => topic.toLowerCase().includes(k));
|
|
443
|
+
if (!key) {
|
|
444
|
+
console.log(`\n Topic not found: "${topic}"\n`);
|
|
445
|
+
console.log(' Available topics:', Object.keys(topicMap).filter((v, i, a) => a.indexOf(v) === i).join(', '));
|
|
446
|
+
console.log('');
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const docFile = resolve(instructionsDir, topicMap[key]);
|
|
451
|
+
if (!existsSync(docFile)) {
|
|
452
|
+
console.error(` Doctrine file missing: ${topicMap[key]}`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const content = readFileSync(docFile, 'utf8');
|
|
457
|
+
const lines = content.split('\n').slice(0, 40);
|
|
458
|
+
console.log(`\n 📖 Topic: ${key} → ${topicMap[key]}\n`);
|
|
459
|
+
console.log(lines.join('\n'));
|
|
460
|
+
console.log(`\n ... (full doctrine at: plugin/instructions/${topicMap[key]})\n`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function dispatchRemember(rule) {
|
|
464
|
+
console.log(`\n ✅ Rule noted: "${rule}"`);
|
|
465
|
+
console.log(' To persist this rule, run schema-evolve from within a project context:');
|
|
466
|
+
console.log(' @Kushi remember ' + rule);
|
|
467
|
+
console.log(' This will write to Evidence/<alias>/State/CLAUDE.md\n');
|
|
468
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Install Kushi — multi-source project evidence agent with Comprehensive Structured Capture (CSC) into weekly-only files across Email, Teams, OneNote, Loop, SharePoint, Meetings, CRM, ADO. Meetings retain a sibling verbatim/ audit folder. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"scripts": {
|
|
44
|
-
"test": "node --test src/check-workiq.test.mjs src/seed-config.test.mjs src/sanitize-workiq-input.test.mjs src/detect-vertex-repo.test.mjs src/vertex-validate.test.mjs src/emit-vertex.e2e.test.mjs src/config-root-resolve.test.mjs src/forbidden-workiq-phrasings.test.mjs src/multi-host-install.test.mjs src/eval-aggregator.test.mjs src/eval-runner.test.mjs src/skill-creator.test.mjs src/skill-checker.test.mjs",
|
|
44
|
+
"test": "node --test src/check-workiq.test.mjs src/seed-config.test.mjs src/sanitize-workiq-input.test.mjs src/detect-vertex-repo.test.mjs src/vertex-validate.test.mjs src/emit-vertex.e2e.test.mjs src/config-root-resolve.test.mjs src/forbidden-workiq-phrasings.test.mjs src/multi-host-install.test.mjs src/eval-aggregator.test.mjs src/eval-runner.test.mjs src/skill-creator.test.mjs src/skill-checker.test.mjs src/hooks-dispatcher.test.mjs src/parallel-refresh.test.mjs src/otel-emit.test.mjs src/teach.test.mjs src/schema-evolve.test.mjs src/global-wiki.test.mjs src/promote.test.mjs",
|
|
45
45
|
"test:integration:bootstrap": "node src/bootstrap-dryrun.integration.test.mjs",
|
|
46
46
|
"smoke": "node scripts/smoke.mjs",
|
|
47
47
|
"eval": "pwsh plugin/skills/eval/run-evals.ps1 -Skill",
|
|
@@ -51,6 +51,11 @@ The Evidence/ folder produced by `aggregate` is the **public contract** between
|
|
|
51
51
|
| `@Kushi fde-triage <project>` | **standard+** | n/a (read-only) | `fde-triage` — full 7-file triage bundle at `Reports/triage/<YYYY-MM-DD>/` |
|
|
52
52
|
| `@Kushi propose ado <project>` | **preview** | n/a (read-only) | `propose-ado-update` — generates `<engagement-root>/ado-updates/<YYYY-MM-DD>/proposed.md` from latest `_Consolidated/`. NO ADO writes. |
|
|
53
53
|
| `@Kushi apply ado <project>` | **preview** | n/a (gated) | `apply-ado-update` — gated apply skill. v0.1.0-preview is dry-mode only (writes `planned.jsonl`, no real ADO calls). Governed by `update-ledger.instructions.md`. |
|
|
54
|
+
| `@Kushi teach <topic>` | standard+ | n/a (write-only) | v5.2.0 — `teach` — persist a reusable fact/preference/pattern to `.kushi/learnings/`. Lookup via `explain`. |
|
|
55
|
+
| `@Kushi explain <topic>` | standard+ | n/a (read-only) | v5.2.0 — `teach` (explain mode) — retrieve a previously taught fact/preference/pattern from `.kushi/learnings/`. |
|
|
56
|
+
| `@Kushi schema-evolve <project>` | standard+ | n/a | v5.2.0 — `schema-evolve` — detect schema drift in Evidence/ layouts and propose safe migrations with rollback plans. |
|
|
57
|
+
| `@Kushi global init` / `status` / `ask <q>` / `lint` | standard+ | n/a | v5.3.0 — `global-wiki` — manage the per-user cross-engagement wiki at `~/.kushi-global/State/` (env `KUSHI_GLOBAL_ROOT` for tests). |
|
|
58
|
+
| `@Kushi promote <project> <page>` | standard+ | n/a | v5.3.0 — `promote` — copy a project State page into the global wiki with identifier redaction + back-link + dual log. Refuses without `--force` when identifiers detected. |
|
|
54
59
|
|
|
55
60
|
**Note on auto-routing**: `ask` does NOT require the `@Kushi ask` prefix. Any message that names a known project AND asks a question (what / who / when / status / summarize / etc.) auto-dispatches to `ask-project`. Producer verbs win in the unambiguous case.
|
|
56
61
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "global-wiki"
|
|
3
|
+
description: "v5.3.0 — Global wiki at ~/.kushi-global/State/ (env override KUSHI_GLOBAL_ROOT). Structurally identical to a project State/ wiki (Karpathy layout) but tagged scope: global in frontmatter. Holds cross-engagement patterns that don't belong in any one project. Never auto-populated — only explicit kushi promote moves content in. Linter checks for [!warning] potential-customer-leak callouts to enforce privacy posture."
|
|
4
|
+
applies_to: "global init/status/ask/lint, promote, ask-project routing, teach routing"
|
|
5
|
+
since: "kushi v5.3.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# global-wiki — doctrine
|
|
9
|
+
|
|
10
|
+
> **Authored to [agentskills.io](https://agentskills.io/skill-creation/best-practices) spec.**
|
|
11
|
+
> Deltas from source (living-wiki Karpathy pattern + Obsidian global-vault + Claude.md global-memory): adds a separate root, `scope: global` frontmatter marker, explicit-promotion-only contract, and a privacy linter (`potential-customer-leak`).
|
|
12
|
+
|
|
13
|
+
The global wiki is a personal, cross-engagement knowledge base that lives **outside** any single project's `Evidence/<alias>/State/`. Consultants accumulate cross-cutting patterns — "how I structure FDE intake", "my preferred Status taxonomy", "tools and snippets that work across all customers" — and those patterns belong in a single durable home, not re-derived per engagement and not silently bleeding between projects.
|
|
14
|
+
|
|
15
|
+
## Rules (HARD)
|
|
16
|
+
|
|
17
|
+
### 1. Location
|
|
18
|
+
|
|
19
|
+
- **Default:** `~/.kushi-global/State/` (`$HOME` on POSIX, `$env:USERPROFILE` on Windows).
|
|
20
|
+
- **Override:** `$KUSHI_GLOBAL_ROOT` environment variable (absolute path; tilde-expanded). Tests MUST set this to `.testtmp/.kushi-global/` — never touch the real path.
|
|
21
|
+
- The global root is **per-user**, never per-project, never per-host.
|
|
22
|
+
|
|
23
|
+
### 2. Shape
|
|
24
|
+
|
|
25
|
+
The global wiki MUST mirror the standard State/ layout (per `karpathy-state-layout.instructions.md` + `living-wiki.instructions.md`):
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
~/.kushi-global/State/
|
|
29
|
+
├── index.md # entry point, links to all pages
|
|
30
|
+
├── log.md # reverse-chronological op log (same format as project log.md)
|
|
31
|
+
├── tour.md # guided tour pointer (optional)
|
|
32
|
+
├── hot.md # hot-edits scratch (optional)
|
|
33
|
+
├── conventions.md # cross-engagement conventions / CLAUDE.md-shape rules
|
|
34
|
+
├── answers/ # promoted Q&A pages
|
|
35
|
+
├── reports/ # global lint reports, status snapshots
|
|
36
|
+
└── _review-queue.md # open privacy / contradiction items
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Every markdown page in the global wiki MUST carry frontmatter with `scope: global`:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
---
|
|
43
|
+
kushi_state_page: true
|
|
44
|
+
scope: global
|
|
45
|
+
---
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `scope: global` marker distinguishes a true global page from a project page that was *copied* locally for reference.
|
|
49
|
+
|
|
50
|
+
### 3. Initialization
|
|
51
|
+
|
|
52
|
+
- `kushi global init` creates the scaffold idempotently — re-running NEVER overwrites existing content.
|
|
53
|
+
- Skips files that already exist; only adds missing scaffolding.
|
|
54
|
+
- Logs `global-init` entry to `~/.kushi-global/State/log.md` on every run (even no-op).
|
|
55
|
+
|
|
56
|
+
### 4. Privacy posture
|
|
57
|
+
|
|
58
|
+
The global wiki crosses engagements. **Customer identifiers MUST NOT leak in.**
|
|
59
|
+
|
|
60
|
+
- The promote operation runs an identifier scan and refuses unless explicit `--force` is given.
|
|
61
|
+
- `kushi global lint` (and `kushi lint --global`) scans `*.md` under the global root for `> [!warning] potential-customer-leak` callouts (left there by promote when it had to redact).
|
|
62
|
+
- The user is responsible for resolving those callouts before sharing the global wiki.
|
|
63
|
+
|
|
64
|
+
### 5. Promotion is explicit only
|
|
65
|
+
|
|
66
|
+
- Pages NEVER auto-promote from a project to global. The only path is `kushi promote <project> <page-path>`.
|
|
67
|
+
- See `multi-wiki-routing.instructions.md` for the promotion contract + back-link rule.
|
|
68
|
+
|
|
69
|
+
### 6. Storage outside engagement tree
|
|
70
|
+
|
|
71
|
+
The global wiki lives **outside** any engagement root. Self-check `D40.global-wiki-shape` checks the configured location (env-overridden in CI) and is warn-only — the absence of `~/.kushi-global/` is normal until the user runs `kushi global init`.
|
|
72
|
+
|
|
73
|
+
## References
|
|
74
|
+
|
|
75
|
+
- `multi-wiki-routing.instructions.md` — routing across project + global
|
|
76
|
+
- `living-wiki.instructions.md` — incremental + contradiction lifecycle (same rules apply)
|
|
77
|
+
- `karpathy-state-layout.instructions.md` — page shape
|
|
78
|
+
- `schema-evolve.instructions.md` — `global` scope was placeholdered here in v5.2.0; v5.3.0 honors it.
|
|
79
|
+
- `wiki-lint.instructions.md` — finding classes; v5.3.0 adds `potential-customer-leak`.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Hooks system
|
|
2
|
+
|
|
3
|
+
> Doctrine: `hooks.instructions.md` — kushi v5.2.0+
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Hooks are user-configurable scripts or webhooks triggered **after** kushi pipeline events. They enable integrations (Teams notifications, ticket updates, dashboards) without modifying kushi's core skills.
|
|
8
|
+
|
|
9
|
+
## Events
|
|
10
|
+
|
|
11
|
+
| Event | Fires after | Payload keys |
|
|
12
|
+
|-------|-------------|--------------|
|
|
13
|
+
| `post-pull` | Each `pull-*` completes (per source) | `project`, `alias`, `source`, `items_pulled`, `duration_ms`, `success` |
|
|
14
|
+
| `post-state` | `build-state` completes | `project`, `alias`, `pages_written`, `contradictions_flagged`, `duration_ms` |
|
|
15
|
+
| `post-contradiction` | A new contradiction is flagged inside `build-state` | `project`, `alias`, `entity`, `field`, `old_value`, `new_value`, `source` |
|
|
16
|
+
| `post-lint` | `lint-state` completes | `project`, `alias`, `findings_count`, `report_path`, `duration_ms` |
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
Hooks are declared per-project at:
|
|
21
|
+
- `.kushi/hooks.yml` — webhook URLs + settings
|
|
22
|
+
- `.kushi/hooks/<event>.ps1` — PowerShell hook scripts
|
|
23
|
+
|
|
24
|
+
### `.kushi/hooks.yml` schema
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
hooks:
|
|
28
|
+
post-pull:
|
|
29
|
+
- type: webhook
|
|
30
|
+
url: https://example.webhook.office.com/...
|
|
31
|
+
timeout_ms: 5000
|
|
32
|
+
- type: script
|
|
33
|
+
path: .kushi/hooks/post-pull.ps1
|
|
34
|
+
post-state:
|
|
35
|
+
- type: webhook
|
|
36
|
+
url: https://...
|
|
37
|
+
post-contradiction:
|
|
38
|
+
- type: script
|
|
39
|
+
path: .kushi/hooks/post-contradiction.ps1
|
|
40
|
+
post-lint: []
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### PowerShell hooks
|
|
44
|
+
|
|
45
|
+
- Receive the event payload as JSON on **stdin**.
|
|
46
|
+
- Exit 0 = success; non-zero = logged warning (never blocks pipeline).
|
|
47
|
+
- Timeout: 30s default (configurable per hook via `timeout_ms`).
|
|
48
|
+
- Working directory: project root.
|
|
49
|
+
|
|
50
|
+
### Webhook hooks
|
|
51
|
+
|
|
52
|
+
- Payload sent as HTTP POST body (`Content-Type: application/json`).
|
|
53
|
+
- Timeout: 5000ms default.
|
|
54
|
+
- Non-2xx response = logged warning (never blocks).
|
|
55
|
+
|
|
56
|
+
## Execution semantics
|
|
57
|
+
|
|
58
|
+
1. **Warn-only**: Hook failures are logged to `Evidence/<alias>/State/hooks-log.md` but NEVER block the pipeline.
|
|
59
|
+
2. **Order**: Hooks for the same event fire sequentially in declaration order.
|
|
60
|
+
3. **Idempotent dispatch**: `Invoke-Hooks` is safe to call multiple times — the log deduplicates by event+timestamp.
|
|
61
|
+
4. **No content**: Hooks receive metadata only. Evidence content is NEVER included in payloads (privacy).
|
|
62
|
+
|
|
63
|
+
## CLI verbs
|
|
64
|
+
|
|
65
|
+
- `kushi hooks list <project>` — list configured hooks for a project.
|
|
66
|
+
- `kushi hooks test <project> <event>` — fire a synthetic event payload.
|
|
67
|
+
|
|
68
|
+
## Output
|
|
69
|
+
|
|
70
|
+
Hook invocations are logged to `Evidence/<alias>/State/hooks-log.md` with format:
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
## [2026-05-29 14:30] post-pull | hook: teams-notify
|
|
74
|
+
|
|
75
|
+
- status: success
|
|
76
|
+
- duration_ms: 342
|
|
77
|
+
- target: https://example.webhook.office.com/...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## References
|
|
81
|
+
|
|
82
|
+
- `Invoke-Hooks.ps1` — shared helper (`plugin/skills/_shared/Invoke-Hooks.ps1`)
|
|
83
|
+
- `log-format.instructions.md` — log entry format
|
|
84
|
+
- `living-wiki.instructions.md` — contradiction events trigger `post-contradiction`
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "multi-wiki-routing"
|
|
3
|
+
description: "v5.3.0 — Routing rules for project vs global wiki. ask-project: project-first; augment with global only if project confidence below threshold; --global forces global-first, --project-only suppresses global. build-state: project-only, never auto-promotes to global. lint-state: project-only; kushi lint --global lints global separately. teach: global-first for cross-cutting topics, project for project-specific. Promotion path: explicit kushi promote <project> <page>, never automatic."
|
|
4
|
+
applies_to: "ask-project, build-state, lint-state, teach, promote"
|
|
5
|
+
since: "kushi v5.3.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# multi-wiki-routing — doctrine
|
|
9
|
+
|
|
10
|
+
> **Authored to [agentskills.io](https://agentskills.io/skill-creation/best-practices) spec.**
|
|
11
|
+
> Deltas from source: kushi previously had a single wiki per project. This doctrine formalizes the project↔global routing rules introduced in v5.3.0.
|
|
12
|
+
|
|
13
|
+
Kushi maintains two kinds of wikis:
|
|
14
|
+
|
|
15
|
+
| Kind | Path | Scope |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| **Project** | `Evidence/<alias>/State/` | One engagement only. |
|
|
18
|
+
| **Global** | `$KUSHI_GLOBAL_ROOT` or `~/.kushi-global/State/` | Cross-engagement; per-user. |
|
|
19
|
+
|
|
20
|
+
Every reader/writer skill MUST be explicit about which it touches.
|
|
21
|
+
|
|
22
|
+
## Rules (HARD)
|
|
23
|
+
|
|
24
|
+
### 1. ask-project — project-first, global-augment
|
|
25
|
+
|
|
26
|
+
Default behavior (`kushi ask <project> <q>`):
|
|
27
|
+
|
|
28
|
+
1. Resolve the project and run the standard project-scoped answer chain (per `ask-project/SKILL.md`).
|
|
29
|
+
2. Compute the confidence (per `evidence-confidence-ladder.instructions.md`).
|
|
30
|
+
3. **If confidence is `low` (or `medium` with `--global-augment` set in config)** → also query `$KUSHI_GLOBAL_ROOT/State/answers/` for matching topics and append a `## From your global wiki` section, citing each hit with `[global: <page>]`.
|
|
31
|
+
4. **If confidence is `high`** → do NOT consult global (avoid noise).
|
|
32
|
+
|
|
33
|
+
Flags:
|
|
34
|
+
|
|
35
|
+
| Flag | Behavior |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `--global` | Reverse the order: search global first, project second. Show provenance `[global]` first, `[project: <alias>]` second. |
|
|
38
|
+
| `--project-only` | Hard-disable global consultation regardless of confidence. |
|
|
39
|
+
| (none) | Default project-first + augment-on-low-confidence. |
|
|
40
|
+
|
|
41
|
+
Source provenance MUST be visible per citation: `[project: <alias>]` vs `[global]`.
|
|
42
|
+
|
|
43
|
+
### 2. build-state — project-only
|
|
44
|
+
|
|
45
|
+
`build-state` writes to `Evidence/<alias>/State/` and **NEVER** writes to the global wiki. There is no auto-promotion path. Even if a fact has appeared in 10 projects, build-state does not move it to global — only the user, via `kushi promote`, does.
|
|
46
|
+
|
|
47
|
+
### 3. lint-state — project-only by default
|
|
48
|
+
|
|
49
|
+
- `kushi lint <project>` → lints the project State only. Unchanged from v5.1.0.
|
|
50
|
+
- `kushi lint --global` → lints the global wiki at `$KUSHI_GLOBAL_ROOT/State/` separately. Same finding classes plus `potential-customer-leak` (see `global-wiki.instructions.md`).
|
|
51
|
+
- Lint runs are independent; one does not implicitly run the other.
|
|
52
|
+
|
|
53
|
+
### 4. teach — global-first for cross-cutting topics
|
|
54
|
+
|
|
55
|
+
- For **cross-cutting topics** (kushi concepts, doctrines, releases — e.g. "explain confidence ladder", "how does CSC work"): `teach` consults the global wiki FIRST (if a matching page exists in `~/.kushi-global/State/answers/`), then falls back to in-repo doctrine, then to genealogy.
|
|
56
|
+
- For **project-specific topics** ("how did we structure intake for AGCO?"): `teach` declines and suggests `kushi ask <project> <q>` instead — that's an `ask-project` job.
|
|
57
|
+
- Citations distinguish `[global: <page>]` vs `[doctrine: <file>]` vs `[genealogy: <version>]`.
|
|
58
|
+
|
|
59
|
+
### 5. Promotion path — explicit only
|
|
60
|
+
|
|
61
|
+
`kushi promote <project> <page>` is the **only** way content moves project → global. See `Promote operation` below.
|
|
62
|
+
|
|
63
|
+
Auto-promotion is intentionally not supported because:
|
|
64
|
+
|
|
65
|
+
1. Customer-identifier detection is best-effort; the user must review.
|
|
66
|
+
2. What is "cross-cutting" depends on the consultant, not the machine.
|
|
67
|
+
3. Silent promotion would surprise the user — global is personal space.
|
|
68
|
+
|
|
69
|
+
### 6. Demotion / removal
|
|
70
|
+
|
|
71
|
+
There is no `kushi demote` in v5.3.0. To remove a global page the user deletes the file directly and appends a manual `log.md` entry. v6+ MAY add a managed demote verb.
|
|
72
|
+
|
|
73
|
+
## Promote operation
|
|
74
|
+
|
|
75
|
+
`kushi promote <project> <page-path>` performs the following, in order:
|
|
76
|
+
|
|
77
|
+
1. **Resolve source.** Find `<engagement-root>/<project>/Evidence/<alias>/State/<page-path>`. Must exist.
|
|
78
|
+
2. **Read source body + frontmatter.**
|
|
79
|
+
3. **Run identifier scan** against the body. Uses the same heuristics as `schema-evolve`-style detection:
|
|
80
|
+
- Known customer aliases registered in `.settings.yml` `discovery.project_aliases:` or the alias folder name itself.
|
|
81
|
+
- The literal project name.
|
|
82
|
+
- Email addresses with non-Microsoft domains.
|
|
83
|
+
- High-confidence proper-noun runs that match `<project>` aliases (case-insensitive, word boundaries).
|
|
84
|
+
4. **Build redaction list.** For each hit:
|
|
85
|
+
- Replace the literal string with `[REDACTED]` in the global copy.
|
|
86
|
+
- Record `{ pattern, count, line_numbers }` in the redactions list.
|
|
87
|
+
5. **Refuse without `--force`** if the redactions list is non-empty. Print the list with line numbers and the human-review prompt. The user is expected to inspect, then re-run with `--force` (which writes the redacted body and adds a `> [!warning] potential-customer-leak` callout near each redaction site for the lint pass to track).
|
|
88
|
+
6. **Write target.** Path: `$KUSHI_GLOBAL_ROOT/State/answers/<source-slug>.md` (slugify source filename; collisions append `-N`).
|
|
89
|
+
Frontmatter MUST contain:
|
|
90
|
+
```yaml
|
|
91
|
+
---
|
|
92
|
+
kushi_state_page: true
|
|
93
|
+
scope: global
|
|
94
|
+
promoted_from: "<project>/<alias>/<relative-source-path>"
|
|
95
|
+
promoted_at: "<ISO-8601 UTC>"
|
|
96
|
+
redactions: ["<pattern1>", "<pattern2>"]
|
|
97
|
+
---
|
|
98
|
+
```
|
|
99
|
+
7. **Add back-link to source.** Append (or update) a callout in the source page:
|
|
100
|
+
```markdown
|
|
101
|
+
> [!info] Promoted to global wiki
|
|
102
|
+
> <source-slug>.md @ <ISO-8601 UTC> · redactions: <N>
|
|
103
|
+
```
|
|
104
|
+
8. **Dual log.** Append a `promote` entry to BOTH:
|
|
105
|
+
- `<project>/Evidence/<alias>/State/log.md` (op = `promote`, title = `Promoted <slug> to global`).
|
|
106
|
+
- `$KUSHI_GLOBAL_ROOT/State/log.md` (op = `promote-in`, title = `Imported <slug> from <project>`).
|
|
107
|
+
9. **Print summary** with both file paths.
|
|
108
|
+
|
|
109
|
+
The operation is **atomic**: if any step fails (redaction refused, write denied, back-link fails), no writes are persisted. The implementation uses a stage-then-commit pattern.
|
|
110
|
+
|
|
111
|
+
## References
|
|
112
|
+
|
|
113
|
+
- `global-wiki.instructions.md` — global wiki location + privacy rules
|
|
114
|
+
- `evidence-confidence-ladder.instructions.md` — confidence threshold for routing
|
|
115
|
+
- `living-wiki.instructions.md` — same incremental + contradiction rules apply to both wikis
|
|
116
|
+
- `wiki-lint.instructions.md` — finding classes; v5.3.0 adds `potential-customer-leak`
|
|
117
|
+
- `schema-evolve.instructions.md` — identifier-detection heuristics reused here
|