kushi-agents 5.2.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 CHANGED
@@ -154,6 +154,30 @@ Rules stored in `Evidence/<alias>/State/CLAUDE.md`. Read by `build-state`, `ask-
154
154
 
155
155
  ---
156
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
+
157
181
  ## Three install profiles
158
182
 
159
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,55 @@ 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
+
29
69
  // ── hooks verb (v5.2.0+) ─────────────────────────────────────────────────────
30
70
  if (args.length > 0 && args[0] === 'hooks') {
31
71
  const sub = args[1] || '';
@@ -108,6 +148,7 @@ if (args.includes('--help') || args.includes('-h')) {
108
148
 
109
149
  Wiki maintenance (v5.1.0+):
110
150
  lint <project> Run wiki-lint checks on State/ (contradictions, stale claims, orphans).
151
+ lint --global Lint the global wiki at ~/.kushi-global/State/.
111
152
 
112
153
  Hooks & observability (v5.2.0+):
113
154
  hooks list <project> List configured hooks for a project.
@@ -115,6 +156,14 @@ if (args.includes('--help') || args.includes('-h')) {
115
156
  explain <topic> Explain a kushi concept (pedagogical, read-only).
116
157
  remember <rule> Persist a project convention to CLAUDE.md.
117
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.
166
+
118
167
  After install, talk to Kushi:
119
168
  bootstrap <project> First-time setup
120
169
  refresh <project> Incremental refresh + rebuild State/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kushi-agents",
3
- "version": "5.2.0",
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 src/hooks-dispatcher.test.mjs src/parallel-refresh.test.mjs src/otel-emit.test.mjs src/teach.test.mjs src/schema-evolve.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",
@@ -54,6 +54,8 @@ The Evidence/ folder produced by `aggregate` is the **public contract** between
54
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
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
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. |
57
59
 
58
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.
59
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,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
@@ -72,6 +72,20 @@ When the user passes `--file-back` (or says "file this answer back", "save this
72
72
 
73
73
  The `--file-back` flag is OPTIONAL. Without it, ask-project behaves exactly as before (read-only, no writes).
74
74
 
75
+ ## --global / --project-only (v5.3.0+)
76
+
77
+ Multi-wiki routing flags governed by `multi-wiki-routing.instructions.md`:
78
+
79
+ | Flag | Behavior |
80
+ |------|----------|
81
+ | *(no flag)* | **Project-first.** Search project `Evidence/<alias>/State/` first; only fall back to the global wiki when project sources are missing/stale or no hit is found. Cite each hit with `[project: <alias>]` or `[global]`. |
82
+ | `--global` | **Global-first.** Search `$KUSHI_GLOBAL_ROOT/State/answers/` first; fall back to the project. Use when you trust the cross-engagement note more than project state. |
83
+ | `--project-only` | **Hard-suppress global.** Never read the global wiki for this question (privacy-sensitive or strictly project-internal). |
84
+
85
+ Provenance is never silent. Every citation tags `[project: <alias>]` or `[global]`. If the answer mixes both, list each source with its provenance.
86
+
87
+ The global wiki itself is opt-in (created via `kushi global init`). If it does not exist on disk, all three modes degrade silently to project-only.
88
+
75
89
  ## Inputs
76
90
 
77
91
  - `<project>` — fuzzy-matched project name. If multiple plausible matches, ask the user.
@@ -0,0 +1 @@
1
+ global-wiki
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: "global-wiki"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says 'init global wiki', 'kushi global init/status/ask/lint', 'show me my global wiki', or wants to manage the cross-engagement knowledge base at ~/.kushi-global/State/. DO NOT USE for promoting individual pages (use promote) or for project-scoped Q&A (use ask-project). Capability: scaffold + status + ask + lint over the per-user global wiki; honors $KUSHI_GLOBAL_ROOT for tests."
5
+ ---
6
+
7
+ # Skill: global-wiki
8
+
9
+ Manages the per-user global wiki at `$KUSHI_GLOBAL_ROOT` (default `~/.kushi-global/State/`). Structurally identical to a project `State/` wiki but tagged `scope: global` in every page's frontmatter. Holds cross-engagement patterns that consultants want to keep across projects.
10
+
11
+ See `plugin/instructions/global-wiki.instructions.md` for the full doctrine and `multi-wiki-routing.instructions.md` for how readers + writers route between project and global.
12
+
13
+ ## Triggers
14
+
15
+ - `kushi global init`
16
+ - `kushi global status`
17
+ - `kushi global ask <question>`
18
+ - `kushi global lint`
19
+ - "init my global wiki"
20
+ - "what's in my global wiki"
21
+ - "lint global"
22
+
23
+ ## Inputs
24
+
25
+ - `<subcommand>` — one of `init`, `status`, `ask`, `lint`.
26
+ - `<question>` — required for `ask`.
27
+
28
+ ## Step checklist
29
+
30
+ - [ ] Step 1 — Resolve `$KUSHI_GLOBAL_ROOT` (env override) or fall back to `~/.kushi-global/`.
31
+ - [ ] Step 2 — Dispatch to the requested sub-operation.
32
+ - [ ] Step 3 — Print a one-line summary + paths.
33
+ - [ ] Step 4 — Append a `log.md` entry on init / lint / ask-fileback.
34
+
35
+ ### Step 1 — Resolve global root
36
+
37
+ Read `$env:KUSHI_GLOBAL_ROOT`. If unset, default to `$env:USERPROFILE/.kushi-global/` on Windows or `$HOME/.kushi-global/` on POSIX. Tests MUST set `$env:KUSHI_GLOBAL_ROOT='.testtmp/.kushi-global'` and NEVER touch the real path.
38
+
39
+ ### Step 2 — Sub-operations
40
+
41
+ | Sub | Behavior |
42
+ |---|---|
43
+ | `init` | Idempotent scaffold of `State/` with `index.md`, `log.md`, `conventions.md`, `_review-queue.md`, `answers/`, `reports/`. Every file carries `scope: global` frontmatter. |
44
+ | `status` | Count pages by kind + report newest mtime + open review-queue items. |
45
+ | `ask` | Full-text scan of `State/answers/*.md` (case-insensitive, ≥3-char terms). Cite hits as `[global: <page>]`. |
46
+ | `lint` | Scan all `State/**/*.md` for `[!warning] potential-customer-leak` + `[!warning] Contradicted`. Severity = warning. |
47
+
48
+ ### Step 3 — Output
49
+
50
+ Print resolved root, sub-operation result, and any actionable next steps.
51
+
52
+ ### Step 4 — Log
53
+
54
+ - `init` → append `global-init` to `State/log.md`.
55
+ - `lint` → append `global-lint` if findings produced.
56
+ - `ask` with `--file-back` (future) → append `global-ask-fileback`.
57
+
58
+ ## Hard rules
59
+
60
+ - **NEVER write to a project's Evidence/.** This skill is global-wiki only.
61
+ - **NEVER auto-promote.** Promotion is a separate `promote` skill, user-invoked.
62
+ - **Tests MUST use `$env:KUSHI_GLOBAL_ROOT='.testtmp/.kushi-global'`.** Real `~/.kushi-global/` is user data.
63
+ - **Privacy posture.** When ask returns hits, never silently mix them with project results — provenance `[global]` MUST be visible on every citation.
64
+
65
+ ## Stop conditions
66
+
67
+ - Sub-command missing → print usage, exit 1.
68
+ - `ask` with no question → print usage, exit 1.
69
+ - `status` / `lint` against an uninitialized root → report `not initialized` + suggest `kushi global init`.
70
+
71
+ ## Validation loop
72
+
73
+ 1. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted D40`.
74
+ 2. Fix any findings, re-run.
75
+ 3. Repeat until self-check exits 0.
76
+
77
+ ## References
78
+
79
+ - `../../instructions/global-wiki.instructions.md`
80
+ - `../../instructions/multi-wiki-routing.instructions.md`
81
+ - `../../instructions/log-format.instructions.md`
82
+ - `../../instructions/karpathy-state-layout.instructions.md`
83
+ - `../promote/SKILL.md` — the promotion verb
84
+
85
+ ## Issue Recovery
86
+
87
+ If this skill exposes a reusable defect (e.g. a scaffold file missing from the template, a privacy heuristic that misses a customer pattern), fix the smallest correct repo-owned artifact first (`src/global-wiki.mjs` or the scaffold) and re-run `self-check -Targeted D40`.
@@ -0,0 +1,43 @@
1
+ {
2
+ "skill": "global-wiki",
3
+ "cases": [
4
+ {
5
+ "id": "global-init-creates-scaffold",
6
+ "name": "kushi global init creates index/log/conventions/_review-queue and answers/+reports/",
7
+ "input": "kushi global init",
8
+ "expected_assertions": [
9
+ { "type": "contains", "value": "State" },
10
+ { "type": "contains", "value": "Created" }
11
+ ],
12
+ "grader_type": "script"
13
+ },
14
+ {
15
+ "id": "global-status-counts-pages",
16
+ "name": "kushi global status reports counts after init",
17
+ "input": "kushi global status",
18
+ "expected_assertions": [
19
+ { "type": "contains", "value": "Pages" },
20
+ { "type": "contains", "value": "Answers" }
21
+ ],
22
+ "grader_type": "script"
23
+ },
24
+ {
25
+ "id": "global-ask-finds-hit",
26
+ "name": "kushi global ask returns a citation with [global] provenance when a matching page exists",
27
+ "input": "kushi global ask confidence ladder",
28
+ "expected_assertions": [
29
+ { "type": "contains", "value": "[global]" }
30
+ ],
31
+ "grader_type": "script"
32
+ },
33
+ {
34
+ "id": "global-lint-clean-on-fresh-init",
35
+ "name": "kushi global lint emits no findings on a freshly-initialized wiki",
36
+ "input": "kushi global lint",
37
+ "expected_assertions": [
38
+ { "type": "contains", "value": "No findings" }
39
+ ],
40
+ "grader_type": "script"
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: "promote"
3
+ version: "1.0.0"
4
+ description: "USE WHEN the user says 'kushi promote <project> <page>', 'promote this answer to global', 'move this page to my global wiki', or wants to copy a project State page into the cross-engagement global wiki at ~/.kushi-global/. DO NOT USE for project Q&A (use ask-project) or for initializing the global wiki itself (use global-wiki). Capability: identifier-scan + redact + write + back-link + dual-log. Refuses without --force when customer identifiers are detected."
5
+ ---
6
+
7
+ # Skill: promote
8
+
9
+ Copies a single project State page into the global wiki (`$KUSHI_GLOBAL_ROOT/State/answers/`) with provenance metadata, an identifier-detection gate, and a back-link in the source page.
10
+
11
+ This is the **only** path from project → global. Auto-promotion is intentionally not supported. See `multi-wiki-routing.instructions.md` § Promote operation for the contract.
12
+
13
+ ## Triggers
14
+
15
+ - `kushi promote <project> <page-path>`
16
+ - `kushi promote <project> <page-path> --force`
17
+ - "promote this answer to global"
18
+ - "move <page> into my global wiki"
19
+
20
+ ## Inputs
21
+
22
+ - `<project>` — project name relative to cwd (or under `..`).
23
+ - `<page-path>` — path to the source page. Accepted forms:
24
+ - Absolute path.
25
+ - Path relative to the project root (e.g. `Evidence/<alias>/State/answers/2026-05-27_<slug>.md`).
26
+ - Path relative to a discovered `Evidence/<alias>/State/` (e.g. `answers/<slug>.md`).
27
+ - Path relative to a plain `<project>/State/` (e.g. `answers/<slug>.md`).
28
+ - `--force` — required when the identifier scan finds hits.
29
+
30
+ ## Step checklist
31
+
32
+ - [ ] Step 1 — Resolve project root + source page.
33
+ - [ ] Step 2 — Read source + run identifier scan.
34
+ - [ ] Step 3 — Refuse without `--force` if hits detected; print review prompt.
35
+ - [ ] Step 4 — Write redacted target with `scope: global` frontmatter.
36
+ - [ ] Step 5 — Add `[!info] Promoted to global wiki` back-link in source.
37
+ - [ ] Step 6 — Append `promote` entry to project `State/log.md`.
38
+ - [ ] Step 7 — Append `promote-in` entry to global `State/log.md`.
39
+ - [ ] Step 8 — Print summary.
40
+
41
+ ### Step 1 — Resolve
42
+
43
+ Find the source via `resolveProjectRoot` + `resolveSourcePage` in `src/global-wiki-cli.mjs`. Echo the resolved absolute path before any write.
44
+
45
+ ### Step 2 — Identifier scan
46
+
47
+ Use `detectIdentifiers()` from `src/global-wiki.mjs`. Patterns checked:
48
+
49
+ - The literal project name (case-insensitive, word boundary).
50
+ - The alias folder name (inferred from `Evidence/<alias>/` in the path).
51
+ - Any extra aliases supplied (optional flag, future).
52
+ - Non-Microsoft email addresses (`@*` where domain does not end in `microsoft.com`).
53
+
54
+ Each hit records `{ pattern, kind, count, line_numbers }`.
55
+
56
+ ### Step 3 — Refuse gate
57
+
58
+ If hits > 0 AND `--force` not supplied → refuse with exit code 2, print the hits with line numbers, suggest review. **No filesystem writes occur on refusal.**
59
+
60
+ ### Step 4 — Write target
61
+
62
+ Target path: `$KUSHI_GLOBAL_ROOT/State/answers/<slug>.md` where `<slug>` = slugified source filename (lowercase, alnum + hyphens, ≤60 chars). Collisions append `-N`.
63
+
64
+ Frontmatter MUST include:
65
+
66
+ ```yaml
67
+ ---
68
+ kushi_state_page: true
69
+ scope: global
70
+ promoted_from: "<project>/<relative-source-path>"
71
+ promoted_at: "<ISO-8601 UTC>"
72
+ redactions: ["<pattern1>", "<pattern2>"]
73
+ ---
74
+ ```
75
+
76
+ If any redactions occurred, append a `> [!warning] potential-customer-leak` callout listing the patterns so `global lint` tracks the open review item.
77
+
78
+ ### Step 5 — Back-link
79
+
80
+ Append to the source page (idempotent — skip if the target slug is already linked):
81
+
82
+ ```markdown
83
+ > [!info] Promoted to global wiki
84
+ > answers/<slug>.md @ <iso> · redactions: <N>
85
+ ```
86
+
87
+ ### Step 6 / 7 — Dual log
88
+
89
+ - Project `State/log.md`: `promote | Promoted <slug> to global (redactions: N)`.
90
+ - Global `State/log.md`: `promote-in | Imported <slug> from <project> (redactions: N)`.
91
+
92
+ ### Step 8 — Print summary
93
+
94
+ Echo `source`, `target`, `slug`, `redactions`, `promoted_at`.
95
+
96
+ ## Hard rules
97
+
98
+ - **Atomic.** No partial state. If any step fails, no writes persist (stage-then-commit).
99
+ - **--force is mandatory** when identifiers are detected. Never auto-redact without it.
100
+ - **Tests use `$env:KUSHI_GLOBAL_ROOT='.testtmp/.kushi-global'`.** Real `~/.kushi-global/` is user data.
101
+ - **Never modify project Evidence beyond the back-link.** No edits to the redacted lines in the source — only the global copy is redacted.
102
+ - **Provenance frontmatter required.** `promoted_from`, `promoted_at`, `redactions` are MANDATORY on the global copy.
103
+
104
+ ## Stop conditions
105
+
106
+ - Project unresolved → exit 1, ask the user to verify cwd.
107
+ - Source page unresolved → exit 1.
108
+ - Identifier hits without `--force` → exit 2, print hits.
109
+
110
+ ## Validation loop
111
+
112
+ 1. Run `pwsh plugin/skills/self-check/run.ps1 -Targeted D40`.
113
+ 2. Run `node --test src/promote.test.mjs`.
114
+ 3. Fix any findings + re-run.
115
+
116
+ ## References
117
+
118
+ - `../../instructions/global-wiki.instructions.md`
119
+ - `../../instructions/multi-wiki-routing.instructions.md`
120
+ - `../../instructions/schema-evolve.instructions.md`
121
+ - `../global-wiki/SKILL.md`
122
+
123
+ ## Issue Recovery
124
+
125
+ If the identifier scan misses a customer pattern (false negative) or flags a non-identifier (false positive), fix the heuristic in `src/global-wiki.mjs::detectIdentifiers` first, add a regression case to `src/promote.test.mjs`, then re-run.