kushi-agents 4.4.1 → 4.4.4

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
@@ -79,6 +79,7 @@ The Evidence/ folder produced by every profile is a **stable public contract**
79
79
 
80
80
  | Verb | Profile | Window | What it does |
81
81
  |------|---------|--------|--------------|
82
+ | `setup` | core+ | n/a | Functionally verify WorkIQ is reachable + auto-fill contributor identity. Idempotent. Run once per machine before first `bootstrap`/`refresh`. |
82
83
  | `aggregate` | core+ | last watermark → now | Pull every enabled source + consolidate. **No State/ rebuild.** |
83
84
  | `bootstrap` | standard+ | 30 days (default) | First-time setup — scaffold folders, lay configs side-by-side, initial pull. Profile-aware (builds State only on `full`). |
84
85
  | `refresh` | standard+ | last watermark → now | Pull only what changed. Profile-aware (rebuilds State only on `full`). |
package/bin/cli.mjs CHANGED
@@ -23,10 +23,9 @@ if (args.includes('--help') || args.includes('-h')) {
23
23
  --profile full Standard + report packs (FDE weekly/customer/handoff).
24
24
 
25
25
  Options:
26
- --dest <path> Override destination (relative for vscode, absolute for clawpilot)
27
26
  --force Overwrite existing destination without asking
28
- --yes, -y Accept the default destination and skip the project-root
29
- check (useful for scripted or agent-driven installs)
27
+ --yes, -y Skip the project-root check (useful for scripted or
28
+ agent-driven installs)
30
29
  --no-settings Skip .vscode/settings.json update (vscode target only)
31
30
  --no-instructions Skip .github/copilot-instructions.md merge (vscode target only)
32
31
 
@@ -62,7 +61,6 @@ if (args.includes('--clawpilot')) {
62
61
  }
63
62
 
64
63
  const options = {
65
- dest: getFlag('--dest'),
66
64
  force: args.includes('--force'),
67
65
  yes: args.includes('--yes') || args.includes('-y'),
68
66
  noSettings: args.includes('--no-settings'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kushi-agents",
3
- "version": "4.4.1",
3
+ "version": "4.4.4",
4
4
  "description": "Install Kushi — multi-source project evidence agent with snapshot+stream capture across Email, Teams, OneNote, SharePoint, Meetings, CRM, ADO. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@ Kushi ships in three profiles. The installed profile is recorded in `kushi-insta
16
16
 
17
17
  | Profile | What's installed | Verbs available |
18
18
  |---|---|---|
19
- | `core` | Aggregator only: `pull-*`, `consolidate-evidence`, `aggregate-project`, `ask-project`, `project-status`, `self-check`, `intro` | `aggregate`, `consolidate`, `status`, `pull`, `ask` |
19
+ | `core` | Aggregator only: `setup`, `pull-*`, `consolidate-evidence`, `aggregate-project`, `ask-project`, `project-status`, `self-check`, `intro` | `setup`, `aggregate`, `consolidate`, `status`, `pull`, `ask` |
20
20
  | `standard` *(default)* | core + `bootstrap-project`, `refresh-project`, `fde-intake`, `fde-report`, `fde-triage` + FDE reference pack | core + `bootstrap`, `refresh`, `fde-intake`, `fde-report`, `fde-triage` |
21
21
  | `full` | standard + `build-state` | standard + `state` |
22
22
  | **`preview`** *(opt-in)* | standard + `propose-ado-update`, `apply-ado-update` | standard + `propose-ado`, `apply-ado` |
@@ -29,6 +29,7 @@ The Evidence/ folder produced by `aggregate` is the **public contract** between
29
29
 
30
30
  | Verb | Profile | Default window | Calls (in order) |
31
31
  |---|---|---|---|
32
+ | `@Kushi setup` | core+ | n/a (read-only) | `setup` — functional WorkIQ ping + identity auto-fill. Idempotent; safe to re-run. Required before first bootstrap/refresh. |
32
33
  | `@Kushi aggregate <project>` | core+ | since last watermark | `aggregate-project` → `pull-*` (all enabled) → `consolidate-evidence`. **No build-state.** |
33
34
  | `@Kushi aggregate <project> last N days` / `since <date>` / `<from>..<to>` | core+ | as supplied | same |
34
35
  | `@Kushi bootstrap <project>` | standard+ | last 30 days | `bootstrap-project` → `pull-*` → `consolidate-evidence` → (full only) `build-state` |
@@ -75,14 +76,19 @@ See `instructions/snapshot-vs-stream.instructions.md` for the full rule.
75
76
  - **Auth pre-flight, retry, error logging** (mandatory before every external call) — `instructions/auth-and-retry.instructions.md`
76
77
  - **Citation Ledger** (every assertion cites source) — `instructions/citation-ledger.instructions.md`
77
78
  - **Answer-from-Evidence** (read-only Q&A doctrine for `ask-project`) — `instructions/answer-from-evidence.instructions.md`
79
+ - **Status taxonomy** (closed-set Status / Retry-Signal vocabulary for every artifact) — `instructions/status-taxonomy.instructions.md`
80
+ - **Fallback status reporting** (direct-path failure recovered by fallback → `completed-via-fallback`) — `instructions/fallback-status-reporting.instructions.md`
81
+ - **FDE grounding** (always-on FDE doctrine via `reference-packs/fde/`) — `instructions/fde-grounding.instructions.md`
82
+ - **Communication guidelines** (chat-reply tone; scoped to user-facing replies, NOT artifacts) — `instructions/communication-guidelines.instructions.md`
78
83
  - **NEVER reach out** — Kushi never sends outbound messages without explicit user approval in the current turn. Park recommendations in `State/09_open-questions.md` under `## ✋ Pending Outbound`.
79
84
 
80
85
  ## Routing
81
86
 
82
87
  When a user message arrives:
83
88
 
84
- 1. Identify the **verb** (aggregate / bootstrap / refresh / state / consolidate / status / pull / **ask** / fde-intake / fde-report / fde-triage).
89
+ 1. Identify the **verb** (setup / aggregate / bootstrap / refresh / state / consolidate / status / pull / **ask** / fde-intake / fde-report / fde-triage).
85
90
  - If the message starts with an explicit producer verb → use it.
91
+ - **setup** dispatches immediately on `setup`, `verify workiq`, `who am I to kushi`, `fix my install`, `setup kushi`.
86
92
  - Else if the message contains a known project name AND a question shape (interrogative, "status of", "summarize", bare `<project> <topic>`) → `ask`.
87
93
  - Else → ask the user to clarify.
88
94
  2. **Profile check**: confirm the chosen verb is listed in `kushi-install.json#verbs`. If not, surface: *"Verb `<verb>` requires the `<profile>` profile. Re-install with `npx kushi-agents --clawpilot --profile <profile> --force`."* and stop.
@@ -7,6 +7,8 @@ excludeAgent: "code-review"
7
7
 
8
8
  When `bootstrap-project` (or any full refresh / retry workflow) finishes a project run, it MUST write `<project>/bootstrap-status.md` using the format below. This file is the **fast orientation artifact** for the project — what was bootstrapped, what durable context now exists, what stage the engagement is in, what gaps remain. It is NOT a transcript of the run.
9
9
 
10
+ > **v4.4.2 contracts** — every `Status` cell in this artifact MUST come from the closed taxonomy in `status-taxonomy.instructions.md`. Direct-path failures that were recovered by a documented fallback MUST be rendered as `completed-via-fallback` per `fallback-status-reporting.instructions.md`, not `failed` / `blocked`. The pre-v4.4.2 ad-hoc strings (`populated`, `unsynced`, `degraded`, `partial-cap`, etc.) are mapped onto the taxonomy in `status-taxonomy.instructions.md §1`.
11
+
10
12
  ## Required section order
11
13
 
12
14
  ```
@@ -0,0 +1,79 @@
1
+ ---
2
+ applyTo: "**"
3
+ excludeAgent: "code-review"
4
+ description: "How kushi talks to the user in chat — coach-like thinking, colleague-like speaking, options not directives. Scoped to user-facing chat replies and ask-project answer prose; does NOT override evidence-thoroughness / verbatim-by-default for artifacts."
5
+ ---
6
+
7
+ # Communication Guidelines — User-Facing Chat
8
+
9
+ This rule shapes how kushi **talks to the user in chat**: how it acknowledges asks, surfaces findings, offers next steps, and runs interactive flows (`ask-project`, `fde-intake` interview, refresh confirmations, candidate-pick prompts).
10
+
11
+ **Scope boundary (important):** this rule applies to the **chat-reply prose** and to the **interactive Q&A surface of `ask-project`**. It does NOT govern Evidence/, State/, or Reports/ artifact content. Inside artifacts, `evidence-thoroughness.instructions.md` and `verbatim-by-default.instructions.md` rule — write the long, complete, comprehensive narrative the doctrine demands. The "avoid 'comprehensive' / 'thorough'" line below applies only to the conversational replies, not to artifacts where those words are accurate.
12
+
13
+ ## THINK (internally) — like a coach
14
+
15
+ - What questions would surface insights here?
16
+ - What patterns am I seeing in the user's project / corpus?
17
+ - What doctrine guidance (boundaries, confidence ladder, FDE fitness) would help them?
18
+ - Where might they get stuck or miss something — empty boundary, stale CRM field, missing OneNote section pin?
19
+
20
+ ## SPEAK (externally in chat) — like a helpful colleague
21
+
22
+ - Share thinking briefly: "I'm noticing the CRM field flipped 2026-03-26 but the latest customer transcript is from 2026-03-23 — that's still `internal-only`, not `confirmed`."
23
+ - Offer concrete observations: "The fitness scorecard has 3 criteria at risk; funding clarity is the dominant gap."
24
+ - Give helpful context: "Per FDE doctrine, ECIF status flips are aspirational until deal-desk approves — want me to flag this as an Open Question?"
25
+ - Keep it conversational and human, 2–3 sentences at a time. Reserve depth for the artifact.
26
+ - When citing evidence in chat, use the short kushi citation form: `[source: <relative-path> · <date>]` — full ledgers belong in the artifact, not the chat.
27
+
28
+ ## EMPOWER (always) — offer choices
29
+
30
+ - End replies with options, not directives.
31
+ - "Does that resonate? Want to explore funding gaps next, or move on to the stage-readiness check?"
32
+ - "Make sense? Should I deepen the risks section, or run a refresh on email first?"
33
+ - "Two candidates matched the project token. Want me to show both, or do you have a preference?"
34
+
35
+ When the user is making a discrete choice (2–5 options) prefer surfacing it as a structured question rather than burying it in prose. The host UI may render structured asks better.
36
+
37
+ ## AVOID in chat (not in artifacts)
38
+
39
+ - Do NOT say "Comprehensive", "Thorough", "Exhaustive", "In-depth", "Complete picture" in conversational replies — these set expectations a single chat turn cannot meet AND can mask the underlying source coverage. Save those words for artifact descriptions, where they are evidence-backed.
40
+ - Do NOT pile on hyphens / colons / em-dashes in chat suggestions — keep replies conversational.
41
+ - Do NOT give a single closed answer when multiple valid paths exist — surface the options.
42
+ - Do NOT paraphrase a `communicated` decision as `final`, or an `internal-only` field as `confirmed`. Use the ladder labels (per `evidence-confidence-ladder.instructions.md`) in chat too, not just in artifacts.
43
+ - Do NOT skip the source basis when surfacing a finding in chat. Even a one-line citation is enough — `[source: Evidence/<alias>/crm/snapshot/.../<id>.md · 2026-05-13]`.
44
+
45
+ ## When the user asks a project question (ask-project surface)
46
+
47
+ `ask-project` is the highest-touch chat surface kushi has. Apply this rule rigorously there:
48
+
49
+ 1. Always read evidence first (per `answer-from-evidence.instructions.md`); never answer FDE-shaped questions from priors.
50
+ 2. Lead the answer with the most-recent dated source that bears on the question (per `fde-grounding.instructions.md` recency precedence).
51
+ 3. Carry inline citations for every assertion (per `citation-ledger.instructions.md`).
52
+ 4. Footer the answer with a Source basis line + the Status taxonomy value (`completed-via-fallback`, `completed-with-coverage-gaps`, etc.) so the user knows the coverage shape — per `status-taxonomy.instructions.md` + `fallback-status-reporting.instructions.md`.
53
+ 5. End with one offered next step ("Want me to refresh this slice?", "Should I open this as an Open Question?", "Want the long-shape report?") — empower, don't direct.
54
+
55
+ ## Confirmation flows (refresh, retry, candidate-pick)
56
+
57
+ When kushi is about to do something with side effects (run a refresh, drain deferred retries, pick a candidate, apply an ADO update):
58
+
59
+ - Surface what kushi is about to do, in one sentence, before doing it.
60
+ - Show the inputs and the scope (boundary, window, target file paths).
61
+ - Offer the user a clear choice (proceed / change scope / cancel).
62
+ - For outbound communications (CRM notes, ADO comments, emails) — per the workspace privacy rules, ALWAYS preview the message + recipient and wait for explicit confirmation before sending. Never auto-send.
63
+
64
+ ## What this rule does NOT apply to
65
+
66
+ - `Evidence/<alias>/<source>/{snapshot,stream}/` artifact bodies — use full verbatim depth per `verbatim-by-default.instructions.md`.
67
+ - `State/` rollups — use the section headings and depth per `evidence-thoroughness.instructions.md`.
68
+ - `Reports/fde-<shape>-<date>.md` — follow the per-shape template from `reference-packs/fde/report-doctrine.md`.
69
+ - Run-log / refresh-report tables — use the normalized Status taxonomy; "comprehensive" is fine in artifact summaries when supported by source coverage.
70
+
71
+ ## References
72
+
73
+ - `tracking.instructions.md` — what kushi writes about itself (journal, separate from artifacts).
74
+ - `answer-from-evidence.instructions.md` — `ask-project` retrieval contract.
75
+ - `citation-ledger.instructions.md` — citation format.
76
+ - `evidence-confidence-ladder.instructions.md` — `internal-only | communicated | confirmed`.
77
+ - `fallback-status-reporting.instructions.md` — Source basis in chat answers.
78
+ - `status-taxonomy.instructions.md` — closed-set Status vocabulary.
79
+ - `fde-grounding.instructions.md` — recency precedence + FDE doctrine framing in chat.
@@ -0,0 +1,104 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "When a fallback path recovers the needed evidence or output, report the overall task as completed-via-fallback — not failed. Distinguish direct-path status from overall-task status everywhere status is rendered (bootstrap-status.md, run-log.yml, refresh reports, ask-project answers)."
4
+ ---
5
+
6
+ # Fallback Status Reporting (HARD RULE)
7
+
8
+ When a project workflow uses a fallback path successfully, report the **overall task** as completed via fallback — never as failed. This is the rule that prevents a successful refresh from being mis-summarized as a broken one because one direct path (Graph, REST, default config) did not work.
9
+
10
+ ## Scope
11
+
12
+ This applies to every kushi workflow that can produce a usable result through more than one path:
13
+
14
+ - `bootstrap-project`, `refresh-project`, `aggregate-project`, `consolidate-evidence`
15
+ - every `pull-*` skill (email, teams, meetings, onenote, sharepoint, crm, ado, misc)
16
+ - `build-state`, `project-status`, `fde-report`, `fde-triage`, `fde-intake`
17
+ - `ask-project` (when the answer is assembled from evidence retrieved via a fallback)
18
+ - the deferred-retry drainer in `refresh-project` Step 2a (per `deferred-retry-on-workiq-fail.instructions.md`)
19
+
20
+ Concrete fallback patterns kushi runs into:
21
+
22
+ - direct OneNote Graph retrieval fails → Playwright/WorkIQ recovers the pages
23
+ - direct CRM query shape fails (e.g. `new_companyname` 400) → resolution-order step 2 (account lookup) finds the record
24
+ - transcript pull is incomplete from one path → evidence reconstructed from the meeting chat per `auth-and-retry §5`
25
+ - global `<workspace>/.kushi/config/shared/integrations.yml` is empty → per-project `sources.crm.environment_url_override` / `sources.ado.engagement_id` satisfies the effective-config preflight (per `scope-boundaries.instructions.md` Rule 3 v3.7.6+)
26
+ - WorkIQ throttled → narrower query (`one_sectionFileId` instead of section-name search) returns the same pages
27
+ - chat thread WorkIQ-empty after doubled-strict retry → `m365_list_chat_messages` parallel structured-data dump still provides the JSON record (NOT a substitute for the WorkIQ artifact, but coverage is partial-via-fallback rather than failed)
28
+
29
+ ## Required behavior
30
+
31
+ 1. **Distinguish two statuses, always.** Every place a status is rendered (bootstrap-status.md, refresh report, run-log.yml, ask-project answer footer) MUST separate:
32
+ - **Direct-path status** — what the preferred path did (e.g. `direct path failed`, `graph-401`, `workiq-empty-after-retry`, `crm-config-global-missing`).
33
+ - **Overall-task status** — what the task delivered (e.g. `completed`, `completed-via-fallback`, `completed-with-coverage-gaps`, `partial`, `failed`).
34
+ 2. **Mark the overall task `completed-via-fallback` (or `resolved-via-fallback`) when the fallback recovered the needed result.** Do not write `failed` or `blocked` for the overall task when usable evidence was actually produced.
35
+ 3. **If the fallback result is partial rather than complete, say so explicitly** with one of:
36
+ - `completed-via-fallback (partial)` — fallback ran but only covered part of the requested scope (e.g. transcript reconstructed from chat instead of true transcript).
37
+ - `completed-with-coverage-gaps` — task wrote usable evidence but specific known gaps remain (cite the gaps).
38
+ 4. **Only mark the overall task `failed` (or `unresolved`) when BOTH the primary path AND every fallback failed** to recover any usable result.
39
+ 5. **Record both statuses in source coverage**. In `run-log.yml#sources.<source>` and in `bootstrap-status.md`'s Context Artifact Status table:
40
+ - the primary-path status (e.g. `direct path failed`, `graph-401`)
41
+ - the fallback-path status (e.g. `fallback completed`, `reconstructed from chat`, `effective-config from project override`)
42
+ 6. **In bootstrap / refresh / status artifacts, prefer this wording**:
43
+ - `OneNote context recovery completed via WorkIQ fallback`
44
+ - `CRM resolution completed via per-project environment_url_override`
45
+ - `ADO preflight completed via per-project engagement_id pin (global ado: block was empty)`
46
+ - `Transcript coverage completed via chat reconstruction (no Teams transcription)`
47
+ - `Email refresh completed via failed-fetch register retry (transient m365_get_email 0-segment parse bug)`
48
+
49
+ ## Interaction with existing kushi doctrine
50
+
51
+ This rule is the **summary-time companion** to:
52
+
53
+ - `auth-and-retry.instructions.md §4` — defines per-source `last_status: ok | partial | failed | skipped-auth`. This file extends that vocabulary with `completed-via-fallback` and clarifies that **a fallback success must roll up to `ok` or `partial`, never `failed`** at the source level when usable evidence was written.
54
+ - `deferred-retry-on-workiq-fail.instructions.md` — the deferred-retry queue is a fallback mechanism. A drained marker (next refresh recovered the call) is `resolved-via-fallback`, not `recovered after failure`.
55
+ - `evidence-confidence-ladder.instructions.md` — orthogonal: confidence is about evidence kind (`internal-only | communicated | confirmed`); this rule is about run outcome. A `completed-via-fallback` run can still produce `internal-only` evidence, and vice versa.
56
+ - `scope-boundaries.instructions.md` — the per-project override / global default resolution is a documented fallback path; effective-config success is `completed`, not `completed-via-fallback`, because the schema explicitly allows either layer. Reserve `completed-via-fallback` for runtime path switches, not config-layer resolution.
57
+
58
+ ## Anti-patterns (defects)
59
+
60
+ 1. **Overall task `failed` when evidence was written.** If `pull-crm` resolved the record via account-lookup (resolution step 2) and wrote the snapshot file, `sources.crm.last_status` is `ok`, not `failed`. The direct-title-match attempt status goes into `errors[]` with `action_taken: fell-back-to-account-lookup`.
61
+ 2. **Hiding the direct-path failure when fallback succeeded.** Do not omit the original failure from `errors[]` just because the fallback worked. The audit trail matters; future runs and learnings depend on it.
62
+ 3. **Conflating `completed-via-fallback` with `partial`.** They are different: `partial` = task delivered less than requested scope; `completed-via-fallback` = task delivered the requested scope but via the alternate path. Use `completed-via-fallback (partial)` when both apply.
63
+ 4. **Writing `blocked: missing X` when X was satisfied via an override.** The HCA bootstrap (2026-05-20) mis-reported CRM/ADO/Teams as `blocked: missing shared connection values` even though per-project `integrations.yml` had `environment_url_override`, `engagement_id`, and `chat_ids` fully pinned. The correct status was `completed via per-project effective-config`. Always check the effective-config result, not just the global-config layer.
64
+
65
+ ## Render examples
66
+
67
+ In `bootstrap-status.md#Access Limitations`:
68
+
69
+ ```
70
+ | System | Status | Reason | Workaround |
71
+ |---|---|---|---|
72
+ | CRM | completed-via-fallback | global crm: block empty; resolved via project environment_url_override | none — recommend filling global for tenant-wide default |
73
+ | OneNote | completed-via-fallback (partial) | Graph 401 on 3 pages; recovered via WorkIQ tier-C with page-body-unavailable on 2 of those | deferred-retry markers written for 2; will drain on next refresh |
74
+ | Teams | completed | direct path via WorkIQ + m365_list_chat_messages parallel dump | n/a |
75
+ ```
76
+
77
+ In `run-log.yml`:
78
+
79
+ ```yaml
80
+ sources:
81
+ crm:
82
+ last_status: ok
83
+ last_path: 'project-override' # NEW field — which layer / path delivered the result
84
+ errors:
85
+ - phase: preflight
86
+ path: global-config
87
+ signature: crm-config-global-empty
88
+ action_taken: fell-back-to-project-override
89
+ next_step: 'Optional — fill <workspace>/.kushi/config/shared/integrations.yml#crm for tenant-wide default'
90
+ ```
91
+
92
+ In `ask-project` answer footers:
93
+
94
+ ```
95
+ Source basis: completed-via-fallback (3 of 18 OneNote pages reconstructed from WorkIQ tier-C; others direct from cached snapshot).
96
+ ```
97
+
98
+ ## References
99
+
100
+ - `auth-and-retry.instructions.md` — error-log schema + per-source `last_status` values.
101
+ - `deferred-retry-on-workiq-fail.instructions.md` — deferred-retry queue (a fallback mechanism that drains on next refresh).
102
+ - `bootstrap-status-format.instructions.md` — where the Context Artifact Status + Access Limitations + Current Bootstrap Outcome tables live.
103
+ - `status-taxonomy.instructions.md` — the unified vocabulary of Status + Retry-Signal values.
104
+ - `scope-boundaries.instructions.md` — effective-config rule (Rule 3).
@@ -0,0 +1,146 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "Always-on: every kushi FDE-shaped output (fde-intake, fde-report, fde-triage, build-state, project-status, ask-project FDE answers) MUST ground in the FDE reference pack at reference-packs/fde/, apply recency precedence, and pair every CRM-recorded decision with the confidence ladder. This is the doctrinal companion to the reference pack itself."
4
+ ---
5
+
6
+ # FDE Reference Grounding (HARD RULE)
7
+
8
+ Every kushi output that interprets engagement health, FDE stage, fitness, risks, readiness, status, or recommended next steps MUST be **grounded in the FDE reference pack** rather than inferred from intuition or model priors.
9
+
10
+ The reference pack lives at:
11
+
12
+ ```
13
+ plugin/reference-packs/fde/
14
+ README.md
15
+ core-fde-reference.md # operating model, fitness criteria, anti-patterns, stage gates, risk categories
16
+ crm-field-manifest.md # FDE-CRM field shape (FE-* triage entity)
17
+ intake-questions.md # the 10-question intake (used by fde-intake skill)
18
+ report-doctrine.md # report shape doctrine (used by fde-report skill)
19
+ ```
20
+
21
+ Per `reference-packs` doctrine, the **effective** pack is the 3-layer override:
22
+
23
+ 1. Project: `<engagement-root>/<project>/.kushi-reference/fde/<file>.md` (highest)
24
+ 2. User: `~/.copilot/kushi-reference/fde/<file>.md` (override)
25
+ 3. Packaged: `<install-dest>/reference-packs/fde/<file>.md` (default)
26
+
27
+ Citations MUST carry the layer marker: `[source: reference-packs/fde/<file>.md · packaged|user-override|project-override]`.
28
+
29
+ ## Required behavior
30
+
31
+ ### For every FDE-shaped output
32
+
33
+ 1. **Read the pack first.** Before drafting any FDE-shaped artifact, read `reference-packs/fde/README.md` and enumerate the human-readable files. Treat the folder as ONE reference set; any file may contribute.
34
+ 2. **Score against the 10-criterion FDE Fitness Checklist** from `core-fde-reference.md`:
35
+ - use case fit, platform commitment, C-level sponsor, hypervelocity culture, co-build commitment, funding clarity, measurable outcomes, contracting speed, business owner engagement, non-standard scope.
36
+ - Report which criteria are met, unclear, at risk — never just an overall thumbs-up.
37
+ 3. **Map every risk to one of the eight FDE risk categories** (also from `core-fde-reference.md`):
38
+ - Competitive & Strategic Fit, Funding & Commercial, Contracting / Procurement, Sponsorship, Hypervelocity / Culture, Scope / Use Case Fit, Resourcing (MS side), Handover / Sustainability.
39
+ - Free-form risk taxonomies are a defect.
40
+ 4. **Flag FDE anti-patterns explicitly** when evidence suggests drift toward what FDE is not: advisory-only, long governance cycles, platform-agnostic, staff-aug, out-of-box use case.
41
+ 5. **Use CRM `Status` as a primary lifecycle signal** when available. Translate it via the CRM Triage Plan mapping in `core-fde-reference.md`. Distinguish carefully between `Completed`, `Withdrawn`, `On Hold`, discovery-stage, and active-delivery statuses.
42
+ 6. **Call out mismatches explicitly** when structured system state, older notes, or older artifacts imply a different reality than newer evidence.
43
+
44
+ ### Recency precedence (every conclusion, not just status)
45
+
46
+ When dated sources conflict, newer evidence has more authority — and not only for status, but also for **conclusions, risk posture, momentum, blockers, ownership assumptions, and recommended next steps**.
47
+
48
+ Default precedence order (unless the user explicitly asks otherwise):
49
+
50
+ 1. latest CRM note entries
51
+ 2. latest meeting transcripts
52
+ 3. latest dated local project docs / assets in `<project>/Evidence/`
53
+ 4. latest external linked notes / docs (OneNote, SharePoint)
54
+ 5. older CRM notes and historical project artifacts
55
+
56
+ **When newer evidence supersedes an older conclusion or signal, say so directly** — do not blend the two into an averaged summary. If a recommendation from an older source is no longer appropriate because of newer evidence, state that it has been superseded; do not repeat it as active guidance.
57
+
58
+ Do not let stale evidence dominate synthesis. If an older source says a decision is open, a blocker is active, or a plan is current, but a newer dated source shows the issue was resolved or reversed, the newer source drives the narrative.
59
+
60
+ ### CRM field values vs confirmed facts
61
+
62
+ This rule is the FDE-specific application of `evidence-confidence-ladder.instructions.md`. Every materially important FDE response (funding, owner, scope decision, timeline, legal / commercial gate, SI lane) MUST carry one of:
63
+
64
+ - `internal-only` — CRM/ADO field recorded; no customer-facing confirmation in evidence
65
+ - `communicated` — customer or external stakeholder acknowledged in transcript / email / Teams
66
+ - `confirmed` — signed / approved / executed artifact on record
67
+
68
+ A CRM-only signal is **not** a confirmed fact. Do not say "ECIF is the selected funding model" based on a `statuscode` flip alone. Say "Internal team decision favors ECIF (CRM field updated <date>, `internal-only`); latest customer-facing transcript predates the change; no `communicated` evidence yet."
69
+
70
+ For living triage reports (`fde-triage` output), every row that drives a decision (`FDE Fit First`, `ECIF Confirmation Gate`, `Verification Evidence`) MUST classify the response as one of `CRM-only` / `Cross-verified` / `Conflicting evidence` and cite the confirming source + date for the upgrade past `CRM-only`.
71
+
72
+ ### Full customer view requires in-session source resolution
73
+
74
+ This grounding doctrine defines **how to interpret** evidence. It does NOT by itself satisfy the requirement to retrieve every applicable customer artifact.
75
+
76
+ Before drafting a "full view", "complete picture", or equivalent FDE-shaped output, the current session MUST attempt retrieval of every applicable source category that has a defined retrieval path:
77
+
78
+ 1. CRM (Dataverse / FDE triage entity)
79
+ 2. Meeting transcripts
80
+ 3. Email
81
+ 4. Teams chats
82
+ 5. OneNote
83
+ 6. SharePoint / external linked artifacts
84
+ 7. ADO (when an engagement WI exists)
85
+
86
+ For each, record one of: `retrieved` / `not present for project` / `attempted but blocked` (with reason). Per `status-taxonomy.instructions.md`, use the closed-set Status values. Per `fallback-status-reporting.instructions.md`, mark fallback recoveries explicitly.
87
+
88
+ Do not describe an output as a `full view`, `complete picture`, `comprehensive`, or equivalent unless these source categories were retrieved or explicitly attempted+dispositioned in the current session.
89
+
90
+ ### Workspace evidence boundary + source quotes
91
+
92
+ For every report, summary, recommendation, status response, or inferred conclusion:
93
+
94
+ 1. Use workspace evidence only (this repository's context, with priority on `<project>/Evidence/` and the configured reference pack).
95
+ 2. Do not invent or assume facts not present in retrieved evidence.
96
+ 3. Do not fill gaps with unstated assumptions. If evidence is missing, state `insufficient evidence in workspace context`.
97
+ 4. Quote sources for material claims using the kushi citation format: `[source: <relative-path> · <YYYY-MM-DD>]`. For reference-pack citations: `[source: reference-packs/fde/<file>.md · packaged|user-override|project-override]`.
98
+ 5. For every key conclusion, include at least one source line.
99
+ 6. If sources are ambiguous or conflicting, state so clearly and apply date-based recency precedence.
100
+ 7. Living triage reports MUST include a complete `Sources Used (Complete Ledger)` section per `citation-ledger.instructions.md`.
101
+
102
+ ### CRM notes integration
103
+
104
+ When kushi assists with CRM note authoring (manual today; `propose-crm-update` future), include a brief FDE fitness signal (1–2 sentences) whenever the note summarizes engagement status — e.g. "Funding model unconfirmed (criterion 6 at risk)" or "C-level sponsor accessible and engaged (criterion 3 met)."
105
+
106
+ ### Engagement authoring artifacts
107
+
108
+ - `fde-intake` 10-question output MUST use `reference-packs/fde/intake-questions.md` as its prompt scaffold; do not paraphrase the question wording.
109
+ - `fde-report` shapes (weekly / short / long / fitness / stage-readiness) MUST cite `reference-packs/fde/report-doctrine.md` and `core-fde-reference.md` for stage gates and fitness mapping.
110
+ - `fde-triage` bundle MUST include the FDE Fitness Checklist (file 01) and map every risk to the eight categories (file 03).
111
+
112
+ ### Status reports / readouts
113
+
114
+ - Treat CRM `Status` as one of the first facts established in the summary.
115
+ - Translate raw `statuscode` into plain-language stage meaning, not just the numeric label.
116
+ - Do not describe `Withdrawn` or `On Hold` engagements with active-delivery language.
117
+ - Do not describe `Technical Assessment` or `Assigned` as `In Progress` unless execution evidence supports the distinction and the mismatch is called out.
118
+ - Let the newer dated source drive the narrative; mark the older interpretation as superseded.
119
+
120
+ ### Inferred conclusions / recommendations / summaries
121
+
122
+ Apply the same recency precedence to conclusions about health, urgency, readiness, commitment level, decision ownership, and likely next actions. Older plans and recommendations are historical context when newer evidence changes the picture.
123
+
124
+ ## Anti-patterns (defects)
125
+
126
+ 1. Drafting an FDE-shaped output without reading the reference pack.
127
+ 2. Inventing risk categories beyond the eight defined.
128
+ 3. Treating a CRM field flip as a confirmed customer-facing decision.
129
+ 4. Blending older and newer dated evidence into an averaged summary instead of letting newer evidence supersede.
130
+ 5. Calling the output "comprehensive" / "full view" without running the in-session source-resolution sweep.
131
+ 6. Citing the FDE pack without the layer marker (`packaged | user-override | project-override`).
132
+
133
+ ## Always-on
134
+
135
+ This instruction applies to every kushi agent invocation and to any external consumer rendering kushi evidence into an FDE-shaped artifact.
136
+
137
+ ## References
138
+
139
+ - `reference-packs/fde/README.md` — the pack itself.
140
+ - `evidence-confidence-ladder.instructions.md` — the three-state confidence vocabulary used here.
141
+ - `citation-ledger.instructions.md` — citation format (Sources Used ledger).
142
+ - `evidence-thoroughness.instructions.md` — depth bar (orthogonal: depth applies inside scope).
143
+ - `scope-boundaries.instructions.md` — what's in scope (orthogonal: scope defines surface).
144
+ - `fallback-status-reporting.instructions.md` — how to report retrieval outcome.
145
+ - `status-taxonomy.instructions.md` — closed-set Status values.
146
+ - skills: `fde-intake`, `fde-report`, `fde-triage`, `build-state`, `project-status`, `ask-project` (FDE-shaped answers).
@@ -0,0 +1,118 @@
1
+ ---
2
+ applyTo: "**"
3
+ description: "The single normalized vocabulary for Status + Retry-Signal values used in every kushi status artifact (bootstrap-status.md, refresh reports, run-log.yml, ask-project footers, fde-report cover lines). Replaces the ad-hoc strings that drifted across skills."
4
+ ---
5
+
6
+ # Status Taxonomy (HARD RULE)
7
+
8
+ Every place a kushi skill renders a per-source or per-task status MUST use a value from the closed taxonomy below. Pre-v4.4.2 skills used ad-hoc strings (`populated`, `unsynced`, `degraded`, `partial-cap`, `blocked-sandbox`, `no stream`, `half done`, `waiting`) — those are now mapped onto the canonical values here.
9
+
10
+ ## 1. Status values (closed set)
11
+
12
+ | Status | Meaning | Use when |
13
+ |---|---|---|
14
+ | `completed` | Direct path succeeded; full coverage. | Preferred path returned everything in scope. |
15
+ | `completed-via-fallback` | Alternate path recovered the result; full coverage. | Direct path failed but a documented fallback delivered the requested scope. See `fallback-status-reporting.instructions.md`. |
16
+ | `completed-with-coverage-gaps` | Result was produced but specific known gaps remain. | Some items in scope were not retrievable; gaps are enumerated. |
17
+ | `partial` | Less than the requested scope was delivered; rerun recommended. | Fewer items than the boundary called for; not all gaps are enumerable. |
18
+ | `failed` | Both primary and fallback paths failed; no usable evidence written. | Every documented path returned no usable result. |
19
+ | `blocked-auth` | Pre-flight or token acquisition failed; the source could not be queried. | `az account show` non-zero, `m365_status` not signed in, tenant mismatch, WorkIQ EULA pending, etc. |
20
+ | `blocked-config` | A required configuration value (per-project + global both empty) prevented querying. | `<source>-config-missing` or `<source>-boundary-missing` per `scope-boundaries.instructions.md` Rule 3. |
21
+ | `blocked-permission` | Auth succeeded but the principal lacks access to the target. | HTTP 403 / `accessDenied` / SharePoint `UnauthorizedAccessException` on the canonical API. |
22
+ | `blocked-throttled` | Service throttled the request; no narrower query succeeded. | `tooManyRequests`, `More than 3 retries performed`, `high demand` per `auth-and-retry §3`. |
23
+ | `unresolved` | Discovery returned no matches inside the configured boundary. | All resolution-order steps returned 0 candidates; user has not yet picked or widened. |
24
+ | `deferred` | WorkIQ failed after doubled-strict retry; deferred-retry marker written. | Per `deferred-retry-on-workiq-fail.instructions.md`. Next refresh drains the queue. |
25
+ | `not-applicable` | Source is not configured for this project. | Source enabled=false or boundary list empty by design (e.g. SharePoint when project has no team site). |
26
+ | `no-run-history` | Source has never been pulled for this project. | Used in backfill rows when creating status artifacts retroactively. |
27
+
28
+ ### Mapping from legacy strings
29
+
30
+ | Legacy ad-hoc string | Canonical value |
31
+ |---|---|
32
+ | `populated` | `completed` (or `completed-with-coverage-gaps` if `## Coverage Notes` flags gaps) |
33
+ | `unsynced` | `partial` (rerun recommended) OR `not-applicable` (if intentionally deferred) — pick by intent |
34
+ | `degraded` | `completed-with-coverage-gaps` |
35
+ | `degraded-list-only` | `completed-with-coverage-gaps` (note: bodies missing, index present) |
36
+ | `partial-cap` | `partial` |
37
+ | `partial-template` | `completed-with-coverage-gaps` |
38
+ | `blocked-sandbox` | `blocked-permission` (with reason: agent sandbox) |
39
+ | `throttled-tooManyRequests` | `blocked-throttled` |
40
+ | `cli-available` (preflight pass) | `completed` (preflight row only) |
41
+ | `resolved` (preflight pass) | `completed` (preflight row only) |
42
+ | `missing` (preflight) | `blocked-config` |
43
+ | `ado-not-complete` | `partial` (with reason) |
44
+ | `no-match` | `unresolved` |
45
+ | `❌ all paths failed` | `failed` |
46
+ | `❌ workiq-empty-after-retry` | `deferred` (marker written) OR `failed` (if no marker for permanent reasons) |
47
+
48
+ ## 2. Retry-Signal values (closed set)
49
+
50
+ | Retry Signal | Meaning |
51
+ |---|---|
52
+ | `none` | No retry needed; result is settled. |
53
+ | `watch` | Usable result exists but direct-path gaps are worth reviewing later. |
54
+ | `retry` | Rerun is recommended; the next `refresh <project>` should pick this up. |
55
+ | `user-action` | Retry requires a user step first (paste verbatim, widen boundary, `az login`, install workiq, sign in to m365). |
56
+
57
+ ### Default mapping (Status → Retry Signal)
58
+
59
+ | Status | Default Retry Signal |
60
+ |---|---|
61
+ | `completed` | `none` |
62
+ | `completed-via-fallback` | `watch` |
63
+ | `completed-with-coverage-gaps` | `watch` |
64
+ | `partial` | `retry` |
65
+ | `failed` | `retry` |
66
+ | `blocked-auth` | `user-action` |
67
+ | `blocked-config` | `user-action` |
68
+ | `blocked-permission` | `user-action` |
69
+ | `blocked-throttled` | `retry` (next run, after backoff) |
70
+ | `unresolved` | `user-action` (pick a candidate or widen boundary) |
71
+ | `deferred` | `retry` (auto-drained by refresh Step 2a) |
72
+ | `not-applicable` | `none` |
73
+ | `no-run-history` | `none` |
74
+
75
+ A skill MAY override the default — but it must include the override reason in the `notes` column.
76
+
77
+ ## 3. Required usage
78
+
79
+ 1. `bootstrap-status.md` Context Artifact Status table — `Status` column values MUST come from §1. A new `Retry` column (added v4.4.2) MUST contain a §2 value.
80
+ 2. `Evidence/run-log.yml#sources.<source>.last_status` — MUST be a §1 value.
81
+ 3. `Evidence/run-log.yml#sources.<source>.retry_signal` (new v4.4.2 field) — MUST be a §2 value.
82
+ 4. Refresh report (`Evidence/<alias>/refresh-reports/<ts>_refresh.md`) per-source summary table — same columns + same vocabulary.
83
+ 5. `fde-report` cover line — when summarizing source coverage, use these values; do not invent new ones.
84
+ 6. `ask-project` answer footer — when surfacing coverage caveats (`Source basis: completed-via-fallback`), use these values.
85
+
86
+ ## 4. Retry review behavior
87
+
88
+ When the user asks "what failed?", "anything to retry?", "redo errors", or "/refresh failed" (`refresh-project` accepts this argument variant):
89
+
90
+ 1. Read every `<project>/Evidence/<alias>/run-log.yml` and every `<project>/bootstrap-status.md` newest-first.
91
+ 2. Filter to rows where `retry_signal` is `retry` or `deferred` is the status.
92
+ 3. If the user named a time window (e.g. "past 2 days"), filter by run timestamp first.
93
+ 4. Offer or execute the per-source `pull-*` again — scoped to the original boundary + window (no scope widening).
94
+ 5. After retry, update the per-source row with the new Status + Retry Signal. Never delete history; append a new row newest-first.
95
+
96
+ ## 5. Backfill rule
97
+
98
+ If a project has artifacts (`bootstrap-status.md`, snapshots) but no normalized status row yet (pre-v4.4.2 history):
99
+
100
+ - Create a backfill row using the best available evidence.
101
+ - Set `Status` from the legacy-string mapping in §1.
102
+ - Set `Retry Signal` from §2 default mapping.
103
+ - Note `(backfill)` in the row's `Notes` column.
104
+ - Do not invent precision: if exact timestamps are unavailable, write the date and note `(time inferred)`.
105
+
106
+ ## 6. Non-destructive rule
107
+
108
+ - Status artifacts are append-only at the row level. Never delete a historical row to "clean up".
109
+ - Newest rows go at the top of every status table.
110
+ - When a status changes between runs, write a new row; do not edit the old row in place.
111
+
112
+ ## References
113
+
114
+ - `fallback-status-reporting.instructions.md` — when `completed-via-fallback` applies.
115
+ - `auth-and-retry.instructions.md §4` — `errors[]` schema (signatures map to Status via the legacy table above).
116
+ - `bootstrap-status-format.instructions.md` — the artifact that uses this vocabulary most prominently.
117
+ - `deferred-retry-on-workiq-fail.instructions.md` — `deferred` status semantics.
118
+ - `scope-boundaries.instructions.md` — `blocked-config` rules.
@@ -48,7 +48,7 @@ For every M365 source in scope:
48
48
 
49
49
  ## Canonical WorkIQ commands (CODIFIED — do not re-discover)
50
50
 
51
- The CLI is at `<USER_HOME>\.kushi\bin\workiq.cmd` (resolved from `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path`; fall back to PATH; fall back to `~/.kushi/bin/workiq.cmd`).
51
+ The CLI is resolved in this order: `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path` `Get-Command workiq` (PATH) `~/.copilot/bin/workiq[.cmd]` (Clawpilot-managed convenience fallback, probed only if it already exists). The legacy `~/.kushi/bin/workiq.cmd` path was removed in v4.4.4 — see the `setup` skill for the functional verification flow.
52
52
 
53
53
  Invocation shape:
54
54
 
@@ -153,7 +153,7 @@ List my Teams meetings between <YYYY-MM-DD> and <YYYY-MM-DD> where the subject c
153
153
 
154
154
  Before the first WorkIQ query in a run:
155
155
 
156
- 1. Resolve CLI path: `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path` → `Get-Command workiq` → `~/.kushi/bin/workiq.cmd`. If none resolves → log `workiq-not-on-path`, write evidence file pointing at install docs, STOP this source.
156
+ 1. Resolve CLI path: `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path` → `Get-Command workiq` → `~/.copilot/bin/workiq[.cmd]` (Clawpilot-managed, only if present). If none resolves → log `workiq-not-on-path`, write evidence file pointing at install docs, STOP this source. The legacy `~/.kushi/bin/workiq.cmd` probe was removed in v4.4.4; the `setup` skill handles the recovery flow.
157
157
  2. Probe with `workiq ask -q "ping"`. If EULA prompt → `workiq accept-eula` once, retry.
158
158
  3. Capture `--version` once into run-log for audit.
159
159
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kushi",
3
3
  "description": "Multi-source project evidence + Q&A agent. Snapshot + stream capture across Email, Teams, OneNote, SharePoint, Meetings, CRM, ADO; plus read-only natural-language Q&A over the captured evidence. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic. Three install profiles: core (aggregator only), standard (default — adds bootstrap/refresh + FDE authoring), full (adds State/ rollup).",
4
- "version": "3.12.1",
4
+ "version": "3.12.2",
5
5
  "author": "ushakrishnan",
6
6
  "repository": "https://github.com/gim-home/kushi",
7
7
  "default_profile": "standard",
@@ -11,6 +11,7 @@
11
11
  "skills": [
12
12
  "intro",
13
13
  "self-check",
14
+ "setup",
14
15
  "pull-email",
15
16
  "pull-teams",
16
17
  "pull-meetings",
@@ -24,6 +25,7 @@
24
25
  "ask-project"
25
26
  ],
26
27
  "prompts": [
28
+ "setup",
27
29
  "aggregate",
28
30
  "consolidate",
29
31
  "status",
@@ -36,6 +38,7 @@
36
38
  ],
37
39
  "reference_packs": [],
38
40
  "verbs": [
41
+ "setup",
39
42
  "aggregate",
40
43
  "consolidate",
41
44
  "status",
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: setup
3
+ description: "Functionally verify WorkIQ is reachable + auto-fill contributor identity into project-evidence.yml. Run once per machine; idempotent."
4
+ argument-hint: "(no args) — pings WorkIQ, captures identity, persists to ~/.kushi config"
5
+ agent: kushi
6
+ tools: [search, read/readFile, edit, agent]
7
+ ---
8
+
9
+ ## User Input
10
+
11
+ ```text
12
+ (none)
13
+ ```
14
+
15
+ # /setup
16
+
17
+ Route to `@Kushi setup`.
18
+
19
+ Runs the **setup** skill:
20
+
21
+ 1. Resolves the workiq CLI path (pinned `cli_path` → PATH → Clawpilot-managed `~/.copilot/bin/workiq` fallback). **No `~/.kushi/bin/` filesystem probe** (removed in v4.4.4).
22
+ 2. Functionally verifies WorkIQ by sending: *"Who am I? Return UPN, displayName, mailNickname as JSON."*
23
+ 3. Parses the response and writes `identity` (alias / display_name / email) into `<workspace>/.kushi/config/user/project-evidence.yml`, preserving comments.
24
+ 4. On failure, walks the user through install / sign-in / retry using `ask_user` (no outbound sends).
25
+ 5. Prints a green status table summarizing host, cli_path, identity, and persistence target.
26
+
27
+ **Idempotent.** Re-running on a healthy machine re-verifies the connection and exits cleanly — bootstrap / refresh are blocked until this skill records `identity_status: verified`.
28
+
29
+ Delegates to `setup` skill.
@@ -61,11 +61,7 @@ Hard stop if WorkIQ returns auth error or empty — print the WorkIQ sign-in hin
61
61
  Verify in order. Stop on hard failures.
62
62
 
63
63
  1. **OS + host runtime** — display OS + Node/PowerShell version. Informational.
64
- 2. **WorkIQ install (REQUIRED, hard stop)** — read `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path`. If missing, probe known paths:
65
- - `$HOME\.kushi\bin\workiq.cmd`
66
- - `$env:LOCALAPPDATA\Programs\WorkIQ\workiq.cmd`
67
- - `$env:ProgramFiles\WorkIQ\workiq.cmd`
68
- If found, persist path. If not found, ask user for path (or to install). Test with `<workiq.cli_path> --help`. Without WorkIQ, M365 sources will all fail — STOP.
64
+ 2. **WorkIQ install (REQUIRED, hard stop)** — read `<workspace>/.kushi/config/user/project-evidence.yml workiq.cli_path`. If missing, dispatch the `setup` skill (which does a functional `workiq ask -q "Who am I?"` check, captures identity, and walks the user through install/sign-in on failure). **Do NOT probe `$HOME\.kushi\bin\` — removed in v4.4.4** because it triggered VS Code's "Allow reading external directory?" prompt even on machines where workiq was already on PATH. If `setup` returns `identity_status: skipped-*`, STOP — bootstrap is blocked until WorkIQ is reachable.
69
65
  3. **Conditional az login** — only if `<workspace>/.kushi/config/shared/integrations.yml` OR `<workspace>/.kushi/config/shared/integrations.yml` exists. Per `az-auth-conditional.instructions.md`. Soft warning on failure, never blocking.
70
66
  4. **Engagement-root resolution** — per `engagement-root-resolution.instructions.md`. Persist to `<workspace>/.kushi/config/user/project-evidence.yml engagement_root` if newly resolved.
71
67
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "pull-ado"
3
- version: "2.3.1"
3
+ version: "2.3.2"
4
4
  description: "Pull ADO evidence as an engagement TREE (parent Engagement + every child WI). Per-item discussion comments + revision history (verbatim). Deterministic resolution; no parent-only summaries. Per ado-engagement-tree.instructions.md."
5
5
  ---
6
6
 
@@ -40,23 +40,37 @@ WorkIQ-first per `workiq-first.instructions.md` — but **for ADO, REST is prefe
40
40
 
41
41
  ## Hard prerequisites (REQUIRED — see `scope-boundaries.instructions.md` Rule 3)
42
42
 
43
- This skill is **HARD-fail** without both:
43
+ This skill is **HARD-fail** without ALL of the following (effective-config rule):
44
44
 
45
- 1. Global config: `<workspace>/.kushi/config/shared/integrations.yml` exists with non-placeholder `organization` AND `defaultProject`.
46
- 2. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.ado.area_paths` is non-empty (OR `boundaries.ado.work_item_ids` is pinned).
45
+ 1. A resolvable `organization` from EITHER:
46
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.ado.organization_override`, OR
47
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#ado.organization` (non-placeholder).
48
+ 2. A resolvable `defaultProject` from EITHER:
49
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.ado.project_override`, OR
50
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#ado.project` (non-placeholder).
51
+ 3. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.ado.area_paths` is non-empty OR `boundaries.ado.work_item_ids` is pinned OR `sources.ado.engagement_id` is pinned (pinned WI IDs are sufficient — they bypass area-path discovery).
47
52
 
48
- If either is missing, refuse with the exact message:
53
+ **Resolution order at runtime:**
54
+ ```
55
+ effective.organization = project.sources.ado.organization_override ?? global.ado.organization
56
+ effective.project = project.sources.ado.project_override ?? global.ado.project
57
+ effective.tenant_id = project.sources.ado.tenant_id_override ?? global.ado.tenant_id
58
+ ```
59
+
60
+ If any required value resolves to null/placeholder, refuse with:
49
61
 
50
62
  ```
51
- ado-config-missing — drop a filled config.yml at
52
- <workspace>/.kushi/config/shared/integrations.yml. See template at
53
- plugin/templates/init/integrations.template.yml (ado: block). Then add boundaries.ado.area_paths
54
- to <project>/integrations.yml.
63
+ ado-config-missing — fill the ado: block in
64
+ <workspace>/.kushi/config/shared/integrations.yml, OR set the corresponding
65
+ sources.ado.*_override in <project>/integrations.yml. Then ensure
66
+ boundaries.ado.area_paths (or work_item_ids, or sources.ado.engagement_id)
67
+ is pinned.
55
68
  ```
56
69
 
57
70
  Discovery loops (`title_contains`, `tags_contains`, `custom_iscrm_id`) are still
58
71
  permitted — but ONLY inside `boundaries.ado.area_paths`. There is no
59
- tenant-wide title-fuzzy scan in v3.7.0+.
72
+ tenant-wide title-fuzzy scan in v3.7.0+. If `sources.ado.engagement_id` is
73
+ already pinned, skip discovery entirely and proceed to Step 2 (tree expansion).
60
74
 
61
75
  ## Auth (deterministic, no improvisation)
62
76
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "pull-crm"
3
- version: "2.3.1"
3
+ version: "2.3.2"
4
4
  description: "Pull CRM (Dataverse) evidence (snapshot: engagement record VERBATIM long-text fields + ALL annotations with formatted values; stream: notes/activities/status changes). Per-record explicit $select + formatted-value annotations. FDE entity sets use crm-field-manifest.md. v2.3.0: exhaustive 4-step resolution sequence (title → account[s] → wide-text → recent-slice → ask). Anti-patterns codified: no statecode filter, new_companyname is NOT a field, never disable from one shallow probe."
5
5
  ---
6
6
 
@@ -38,20 +38,31 @@ WorkIQ-first per `workiq-first.instructions.md`. Thoroughness per `evidence-thor
38
38
 
39
39
  ## Hard prerequisites (REQUIRED — see `scope-boundaries.instructions.md` Rule 3)
40
40
 
41
- This skill is **HARD-fail** without both:
41
+ This skill is **HARD-fail** without BOTH (effective-config rule):
42
42
 
43
- 1. Global config: `<workspace>/.kushi/config/shared/integrations.yml` exists with non-placeholder `environmentUrl`.
43
+ 1. A resolvable Dataverse `environment_url` from EITHER:
44
+ - per-project override: `<engagement-root>/<project>/integrations.yml#sources.crm.environment_url_override` (preferred — survives global config drift), OR
45
+ - global default: `<workspace>/.kushi/config/shared/integrations.yml#crm.environment_url` (non-placeholder).
44
46
  2. Per-project boundary: `<engagement-root>/<project>/integrations.yml#boundaries.crm.record_ids` OR `boundaries.crm.request_ids` is non-empty.
45
47
 
46
- If either is missing, refuse with the exact message:
48
+ **Resolution order at runtime:**
49
+ ```
50
+ effective.environment_url = project.sources.crm.environment_url_override ?? global.crm.environment_url
51
+ effective.entity_set = project.sources.crm.entity_set_override ?? global.crm.default_entity_set
52
+ effective.tenant_id = project.sources.crm.tenant_id_override ?? global.crm.tenant_id
53
+ ```
54
+
55
+ If `effective.environment_url` is null/placeholder, refuse with:
47
56
 
48
57
  ```
49
58
  crm-config-missing — drop a filled config.yml at
50
- <workspace>/.kushi/config/shared/integrations.yml. See template at
51
- plugin/templates/init/integrations.template.yml (crm: block). Then add boundaries.crm.record_ids
52
- (or request_ids) to <project>/integrations.yml.
59
+ <workspace>/.kushi/config/shared/integrations.yml (crm: block), OR set
60
+ sources.crm.environment_url_override in <project>/integrations.yml.
61
+ Then ensure boundaries.crm.record_ids (or request_ids) is non-empty.
53
62
  ```
54
63
 
64
+ If `effective.environment_url` resolves but `boundaries.crm.record_ids` AND `boundaries.crm.request_ids` are both empty, refuse with the analogous `crm-boundary-missing` message.
65
+
55
66
  **No "narrate from email" fallback exists in v3.7.0+.** CRM evidence comes from CRM
56
67
  or it is absent — Coverage Notes will say so explicitly. Synthesis across sources
57
68
  happens at consolidation, not at pull.
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: "setup"
3
+ version: "1.0.0"
4
+ description: "First-run machine setup for Kushi. Functionally verifies WorkIQ is reachable by sending a real query, captures contributor identity (alias / display_name / email) from the response, persists it to `<workspace>/.kushi/config/user/project-evidence.yml`, and walks the user through workiq install/start if it is not running. Idempotent — re-running on a healthy machine prints a green status table and exits. Modeled on the wss-cto-write-connect `setup.agent.md` Step 6 pattern: ping the service, ask the user nothing the service can already answer."
5
+ ---
6
+
7
+ # Skill: setup
8
+
9
+ > **Doctrine**: WorkIQ is a **service**, not a binary on disk. This skill probes it functionally (sends a real query) instead of probing the filesystem. Avoids the v3.x `~/.kushi/bin/` external-directory prompt and the dead-config UX where `<auto>` placeholders surface to the user.
10
+ >
11
+ > **No outbound messages.** This skill never sends Teams / email / RSVP. Identity capture only writes the local YAML.
12
+ >
13
+ > **Idempotent.** Re-running on a machine where everything is healthy prints the green status table and exits cleanly. Safe to wire into `bootstrap-project` Step 1.
14
+
15
+ Run this skill when:
16
+
17
+ - The user says `setup`, `kushi setup`, `verify workiq`, `who am I to kushi`, `fix my install`, `setup kushi`.
18
+ - The installer finished and printed `Run \`setup\` once to verify WorkIQ + auto-fill identity`.
19
+ - A `pull-*` or `bootstrap-project` skill is about to fire its first WorkIQ query in a workspace whose `project-evidence.yml#identity` is still `<auto>` / missing.
20
+
21
+ ## What it does
22
+
23
+ 1. Detects the host runtime + workiq.cli_path (no filesystem probes beyond an explicit `cli_path`).
24
+ 2. **Functionally verifies WorkIQ** by sending one query: *"Who am I? Return UPN, displayName, mailNickname as JSON."*
25
+ 3. On success, persists the captured identity to `<workspace>/.kushi/config/user/project-evidence.yml#identity` (preserving comments + other keys).
26
+ 4. On failure, walks the user through starting WorkIQ via the host's documented entry point, then retries.
27
+ 5. Prints a final status table the user can screenshot to file with IT support.
28
+
29
+ ## Inputs
30
+
31
+ None. The skill discovers everything it needs.
32
+
33
+ Optional flags surfaced by the user:
34
+ - `non-interactive` / `--yes` — skip `ask_user` retries on failure; emit a single actionable error and exit.
35
+ - `verbose` — print every probe step + raw workiq response.
36
+
37
+ ## Steps
38
+
39
+ ### Step 1 — Detect host runtime (informational)
40
+
41
+ Run `node --version`, `pwsh --version` (or `powershell -Command "$PSVersionTable.PSVersion"`), and capture the platform (`win32` / `darwin` / `linux`). Display:
42
+
43
+ ```
44
+ 🔍 Host: <platform> · Node <ver> · PowerShell <ver>
45
+ ```
46
+
47
+ Never blocking. Used only for the final status table.
48
+
49
+ ### Step 2 — Resolve workiq CLI path (NO filesystem probe beyond explicit cli_path)
50
+
51
+ Resolution order (FIRST match wins):
52
+
53
+ 1. **Explicit pin** — read `<workspace>/.kushi/config/user/project-evidence.yml#workiq.cli_path`. If present and the file exists on disk, use it.
54
+ 2. **PATH lookup** — run `Get-Command workiq -ErrorAction SilentlyContinue` (Windows) / `command -v workiq` (Unix). If a result is returned, use the first executable hit.
55
+ 3. **Host integration** — if the host is Clawpilot and `~/.copilot/bin/workiq[.cmd]` exists (this is the Clawpilot-managed location, not a kushi-managed location), use it as a final convenience fallback. **Do NOT probe `~/.kushi/bin/` — that path is removed from kushi doctrine as of v4.4.4.**
56
+
57
+ If none of the three resolves, jump to **Step 4 — Recovery prompt** with reason `cli-not-resolved`.
58
+
59
+ ### Step 3 — Functional verification (the actual check)
60
+
61
+ Send this exact query (PowerShell shape — adapt single-quoting for bash):
62
+
63
+ ```powershell
64
+ & "<resolved-cli-path>" ask -q 'Who am I? Return UPN, displayName, mailNickname strictly as a single JSON object with keys upn, displayName, mailNickname. No prose, no markdown fences.' 2>&1
65
+ ```
66
+
67
+ Timeout: 30 seconds. Capture both stdout and stderr.
68
+
69
+ **Parse the response.** Look for a JSON object containing all three keys. Strip any leading `request-id:` line that WorkIQ prepends. If WorkIQ wrapped the JSON in markdown fences (```json ... ```), strip them.
70
+
71
+ | Outcome | Next |
72
+ |---|---|
73
+ | Valid JSON with non-empty `upn`, `displayName`, `mailNickname` | Go to Step 5 (persist identity) |
74
+ | WorkIQ returned a summary / refusal / empty | Retry ONCE with stricter prompt: *"Output ONLY the JSON object, nothing else. Do not narrate, do not explain. {upn, displayName, mailNickname}."* If still no JSON, jump to Step 4 with reason `workiq-no-identity`. |
75
+ | Process timed out | Step 4 with reason `workiq-timeout` |
76
+ | Non-zero exit (auth error, ENOENT, etc.) | Step 4 with reason `workiq-exit-<code>` |
77
+
78
+ ### Step 4 — Recovery prompt (only on Step-2 or Step-3 failure)
79
+
80
+ Display the platform-appropriate install/start block. Use `ask_user` to drive the recovery loop.
81
+
82
+ **Recovery messages by reason:**
83
+
84
+ ```
85
+ cli-not-resolved (Windows)
86
+ ⚙️ WorkIQ CLI not found.
87
+
88
+ Kushi could not locate the workiq executable in:
89
+ - <workspace>/.kushi/config/user/project-evidence.yml#workiq.cli_path (unset or path missing)
90
+ - the system PATH (no `workiq` on PATH)
91
+ - ~/.copilot/bin/workiq.cmd (Clawpilot-managed fallback; not present)
92
+
93
+ To install / start WorkIQ:
94
+ 1. Open a NEW terminal window (keep this one open).
95
+ 2. Run: winget install Microsoft.WorkIQ
96
+ (macOS: brew install --cask microsoft-workiq)
97
+ 3. After install completes, run: workiq accept-eula
98
+ 4. Then run: workiq ask -q "ping" — should print a non-empty response.
99
+ 5. Come back to this window when done.
100
+ ```
101
+
102
+ ```
103
+ workiq-no-identity / workiq-timeout / workiq-exit-*
104
+ ⚙️ WorkIQ is installed but did not respond to the identity query.
105
+
106
+ Likely causes (in priority order):
107
+ 1. Not signed in. Run: workiq ask -q "ping"
108
+ (You'll be prompted to sign in on first call.)
109
+ 2. Off-corpnet / VPN required. Reconnect and retry.
110
+ 3. WorkIQ daemon not running. (Host-specific. Clawpilot users: restart Clawpilot.
111
+ VS Code Chat users: restart the workiq host process.)
112
+ 4. Tenant misconfiguration. Check the configured tenant in your WorkIQ profile.
113
+
114
+ When ready, type 'retry' below.
115
+ ```
116
+
117
+ `ask_user` choices:
118
+
119
+ - `retry` — re-run Step 3.
120
+ - `change-path` — prompt for an absolute path. Validate the file exists, persist to `project-evidence.yml#workiq.cli_path`, then re-run Step 2 → Step 3.
121
+ - `skip` — write a stub identity (`alias: 'unknown'`, `display_name: '(WorkIQ unreachable)'`, `email: 'unknown@unknown'`) AND mark `<workspace>/.kushi/config/user/project-evidence.yml#identity_status: "skipped-workiq-down"`. **bootstrap / refresh MUST refuse to run while `identity_status: skipped-*` is set**, surfacing "Run `setup` first" as the gate.
122
+
123
+ Recovery loop budget: **3 retries**. After the third failure, force `skip` mode and exit cleanly. Never crash — the user still has read-only verbs available.
124
+
125
+ ### Step 5 — Persist identity (success path)
126
+
127
+ Open `<workspace>/.kushi/config/user/project-evidence.yml`. If the file does not exist, create it from `templates/init/project-evidence.template.yml`. Update the `identity:` block in place (preserve every other key + every comment) using a YAML-comment-safe rewrite. Map:
128
+
129
+ | WorkIQ field | YAML field | Notes |
130
+ |---|---|---|
131
+ | `upn` | `identity.email` | Verbatim. |
132
+ | `displayName` | `identity.display_name` | Verbatim. |
133
+ | `mailNickname` | `identity.alias` | Lowercase. Fallback to `email.split('@')[0]` if missing. |
134
+
135
+ Also write:
136
+
137
+ - `identity_status: "verified"` (so bootstrap/refresh stop pestering)
138
+ - `identity_verified_at: "<ISO-8601 UTC>"`
139
+ - `workiq.cli_path: "<resolved-path>"` (only if it was discovered via PATH or host-integration and not yet pinned)
140
+
141
+ ### Step 6 — Print status table
142
+
143
+ ```
144
+ WorkIQ Setup Complete
145
+ ─────────────────────
146
+ ✅ Host: win32 · Node v22.x · PowerShell 7.x
147
+ ✅ workiq.cli_path C:\Users\<you>\.copilot\bin\workiq.cmd
148
+ ✅ Identity Usha Krishnan <ushak@microsoft.com> (alias=ushak)
149
+ ✅ Persisted to <workspace>/.kushi/config/user/project-evidence.yml
150
+
151
+ Next: kushi bootstrap <project> to bootstrap a project
152
+ kushi refresh <project> to refresh evidence
153
+ ```
154
+
155
+ If recovery ended in `skip`, change the trailing lines to:
156
+
157
+ ```
158
+ ⚠️ WorkIQ unreachable. Identity not verified.
159
+ Re-run `setup` once WorkIQ is running. bootstrap / refresh are blocked
160
+ until then; `ask`, `status`, and Q&A verbs remain available.
161
+ ```
162
+
163
+ ## Notes
164
+
165
+ - This skill writes ONLY to `<workspace>/.kushi/config/user/project-evidence.yml`. It does NOT touch `shared/integrations.yml` (team-owned) or any project-level file.
166
+ - The functional check replaces the pre-v4.4.4 filesystem-probe pattern in `check-workiq.mjs` + `bootstrap-project SKILL.md` Step 1.2. Those still ship for backwards-compat at install time but are no longer the source of truth for "is WorkIQ healthy."
167
+ - See `instructions/identity-resolution.instructions.md` for the contract this skill implements.
168
+
169
+ ## References
170
+
171
+ - `instructions/workiq-only.instructions.md` — WorkIQ-only doctrine for M365 sources.
172
+ - `instructions/identity-resolution.instructions.md` — contributor identity contract.
173
+ - `instructions/communication-guidelines.instructions.md` — chat-reply tone for the recovery prompts.
174
+ - `instructions/status-taxonomy.instructions.md` — `identity_status` value vocabulary.
@@ -1,19 +1,32 @@
1
- # Kushi — optional global integrations defaults.
2
- #
3
- # Seeded by the installer on first install. Never overwritten on upgrade.
4
- # Per-project `integrations.yml` (inside the engagement folder) always
5
- # takes precedence over this file.
6
- #
7
- # Most users can leave this file as-is. Fill it in only if you want a
8
- # default CRM / ADO configuration that applies across all projects.
9
-
10
- # Microsoft Dataverse (CRM) defaults.
11
- # crm:
12
- # environment_url: 'https://<org>.crm.dynamics.com'
13
- # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
14
-
15
- # Azure DevOps defaults.
16
- # ado:
17
- # organization: 'https://dev.azure.com/<org>'
18
- # project: '<default-project>'
19
- # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
1
+ # Kushi — optional global integrations defaults.
2
+ #
3
+ # Seeded by the installer on first install. Never overwritten on upgrade.
4
+ # Per-project `integrations.yml` (inside the engagement folder) always
5
+ # takes precedence over this file. Effective-config rule:
6
+ # effective.X = project.override ?? global.X (hard-fail only if both empty)
7
+ #
8
+ # Most users can leave this file as-is. Uncomment + edit only if you want a
9
+ # default CRM / ADO configuration that applies across all projects.
10
+ #
11
+ # Microsoft (MSFT) tenant defaults shown below. If you work in the Microsoft
12
+ # tenant (most Industry Solutions FDEs), these values are correct as-is.
13
+
14
+ # Microsoft Dataverse (CRM) defaults.
15
+ # Per-project overrides in <engagement-root>/<project>/integrations.yml under
16
+ # sources.crm.environment_url_override / entity_set_override always take
17
+ # precedence (e.g. FDE intake projects point at iscrm.crm.dynamics.com /
18
+ # new_frontierengineeringtriages).
19
+ # crm:
20
+ # environment_url: 'https://microsoftsales.crm.dynamics.com'
21
+ # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
22
+ # default_entity_set: 'opportunities'
23
+ # az_resource_id: 'https://microsoftsales.crm.dynamics.com'
24
+
25
+ # Azure DevOps defaults.
26
+ # Per-project pin in <engagement-root>/<project>/integrations.yml sources.ado
27
+ # (engagement_id, area_paths) overrides the discovery defaults here.
28
+ # ado:
29
+ # organization: 'https://dev.azure.com/IndustrySolutions'
30
+ # project: 'IS Engagements'
31
+ # tenant_id: '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft tenant
32
+ # az_resource_id: '499b84ac-1321-427f-aa17-267ca6975798' # ADO well-known resource id
@@ -42,7 +42,11 @@ active_projects:
42
42
  # (Optional) Path to the WorkIQ CLI. If omitted, Kushi resolves in this order:
43
43
  # 1. this `cli_path` value
44
44
  # 2. `workiq` on PATH
45
- # 3. ~/.kushi/bin/workiq.cmd (Windows) / ~/.kushi/bin/workiq (Linux/macOS)
45
+ # 3. ~/.copilot/bin/workiq[.cmd] (Clawpilot-managed convenience fallback —
46
+ # probed ONLY if it already exists; never created by kushi)
47
+ # The legacy ~/.kushi/bin/ path is no longer probed (removed in v4.4.4 to avoid
48
+ # the VS Code "Allow reading external directory?" prompt). Run the `setup` skill
49
+ # to functionally verify WorkIQ is reachable and auto-fill your identity.
46
50
  # workiq:
47
- # cli_path: 'C:\Users\<you>\.kushi\bin\workiq.cmd'
51
+ # cli_path: 'C:\Program Files\WorkIQ\workiq.cmd'
48
52
 
@@ -6,10 +6,17 @@ import os from 'node:os';
6
6
  /**
7
7
  * Probe for a working WorkIQ install.
8
8
  *
9
- * Resolution order:
9
+ * Resolution order (NO kushi-owned filesystem probe — see v4.4.4 doctrine):
10
10
  * 1. explicit absolute path supplied via --workiq-path / opts.workiqPath
11
11
  * 2. `workiq` on PATH (via `where` on Windows, `command -v` elsewhere)
12
- * 3. Kushi-managed bin: ~/.kushi/bin/workiq.cmd (Win) or ~/.kushi/bin/workiq
12
+ * 3. Clawpilot-managed convenience fallback: ~/.copilot/bin/workiq[.cmd]
13
+ * (only probed when it ALREADY exists — never created, never assumed)
14
+ *
15
+ * The old `~/.kushi/bin/workiq.cmd` probe was removed in v4.4.4: it caused
16
+ * VS Code to surface an "Allow reading external directory?" prompt for `bin`
17
+ * even on machines where workiq was already discoverable via PATH. The
18
+ * functional verification now happens in the `setup` skill via a real
19
+ * `workiq ask -q "Who am I?"` call, not a filesystem probe.
13
20
  *
14
21
  * Returns:
15
22
  * { ok: true, path, version } — usable WorkIQ found
@@ -19,16 +26,9 @@ import os from 'node:os';
19
26
  * 'not-found' — no workiq binary anywhere we looked
20
27
  * 'path-invalid' — explicit --workiq-path was given but the file doesn't exist
21
28
  * 'not-executable' — found but `--version` failed (corrupt install, missing deps, etc.)
29
+ * 'not-responsive' — runs `--version` ok but functional ping returned no JSON-shaped output
22
30
  */
23
31
  export function checkWorkIQ(opts = {}) {
24
- const isWin = process.platform === 'win32';
25
- const homeBin = path.join(
26
- os.homedir(),
27
- '.kushi',
28
- 'bin',
29
- isWin ? 'workiq.cmd' : 'workiq',
30
- );
31
-
32
32
  if (opts.workiqPath) {
33
33
  const abs = path.resolve(opts.workiqPath);
34
34
  if (!existsSync(abs) || !statSync(abs).isFile()) {
@@ -44,9 +44,8 @@ export function checkWorkIQ(opts = {}) {
44
44
  const onPath = findOnPath('workiq');
45
45
  if (onPath) return runVersion(onPath);
46
46
 
47
- if (existsSync(homeBin) && statSync(homeBin).isFile()) {
48
- return runVersion(homeBin);
49
- }
47
+ const clawpilotBin = clawpilotManagedBin();
48
+ if (clawpilotBin) return runVersion(clawpilotBin);
50
49
 
51
50
  return {
52
51
  ok: false,
@@ -55,6 +54,27 @@ export function checkWorkIQ(opts = {}) {
55
54
  };
56
55
  }
57
56
 
57
+ /**
58
+ * Clawpilot installs workiq at ~/.copilot/bin/workiq[.cmd] as part of its own
59
+ * setup. We probe it ONLY if it already exists — we never create the dir, and
60
+ * we never probe `~/.kushi/bin/` (the v3.x location, removed in v4.4.4).
61
+ */
62
+ function clawpilotManagedBin() {
63
+ const isWin = process.platform === 'win32';
64
+ const candidate = path.join(
65
+ os.homedir(),
66
+ '.copilot',
67
+ 'bin',
68
+ isWin ? 'workiq.cmd' : 'workiq',
69
+ );
70
+ try {
71
+ if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
72
+ } catch {
73
+ // permission denied / external-dir prompt declined — treat as not present
74
+ }
75
+ return null;
76
+ }
77
+
58
78
  function findOnPath(name) {
59
79
  const isWin = process.platform === 'win32';
60
80
  const cmd = isWin ? 'where' : 'command';
@@ -106,13 +126,54 @@ function runVersion(binPath) {
106
126
  return { ok: true, path: binPath, version };
107
127
  }
108
128
 
129
+ /**
130
+ * Functional check — sends a real `workiq ask -q "ping"` and confirms a
131
+ * non-empty response. Used by the installer's preflight AFTER runVersion()
132
+ * passes, so we don't ship users to bootstrap with a workiq binary that
133
+ * exists but can't actually answer queries (sign-in missing, daemon down,
134
+ * tenant misconfigured, etc.).
135
+ *
136
+ * Returns the same shape as runVersion(). On a soft failure we still return
137
+ * { ok: true } and just attach `.warning` so the installer can print a hint
138
+ * without hard-failing — the `setup` skill is responsible for the deep
139
+ * recovery flow.
140
+ */
141
+ export function pingWorkIQ(binPath, { timeoutMs = 30_000 } = {}) {
142
+ const isWin = process.platform === 'win32';
143
+ const res = isWin
144
+ ? spawnSync(`"${binPath}" ask -q "ping"`, {
145
+ encoding: 'utf-8',
146
+ timeout: timeoutMs,
147
+ shell: true,
148
+ })
149
+ : spawnSync(binPath, ['ask', '-q', 'ping'], {
150
+ encoding: 'utf-8',
151
+ timeout: timeoutMs,
152
+ });
153
+ const stdout = (res.stdout || '').trim();
154
+ const stderr = (res.stderr || '').trim();
155
+ if (res.status !== 0) {
156
+ return {
157
+ ok: true,
158
+ warning: `workiq ping returned exit ${res.status}. Run \`workiq ask -q "ping"\` manually to confirm sign-in. (${stderr.split(/\r?\n/)[0] || 'no stderr'})`,
159
+ };
160
+ }
161
+ if (!stdout) {
162
+ return {
163
+ ok: true,
164
+ warning: 'workiq ping returned empty stdout. The CLI is reachable but may not be signed in. Run `workiq ask -q "ping"` manually.',
165
+ };
166
+ }
167
+ return { ok: true };
168
+ }
169
+
109
170
  function installHint() {
110
171
  const plat = process.platform;
111
172
  if (plat === 'win32') {
112
- return 'Install with: winget install Microsoft.WorkIQ';
173
+ return 'Install with: winget install Microsoft.WorkIQ — then run `workiq ask -q "ping"` to sign in.';
113
174
  }
114
175
  if (plat === 'darwin') {
115
- return 'Install with: brew install --cask microsoft-workiq';
176
+ return 'Install with: brew install --cask microsoft-workiq — then run `workiq ask -q "ping"` to sign in.';
116
177
  }
117
178
  return 'See https://gim-home.github.io/kushi/getting-started/install-workiq/ for Linux instructions.';
118
179
  }
package/src/main.mjs CHANGED
@@ -12,12 +12,11 @@ import {
12
12
  PLUGIN_SOURCE_DIR,
13
13
  PROJECT_MARKERS,
14
14
  } from './constants.mjs';
15
- import { promptForDestination } from './prompt.mjs';
16
15
  import { copyAssets } from './copy-assets.mjs';
17
16
  import { mergeSettings } from './settings.mjs';
18
17
  import { mergeCopilotInstructions } from './copilot-instructions.mjs';
19
18
  import { seedConfig } from './seed-config.mjs';
20
- import { checkWorkIQ, tryInstallWorkIQ } from './check-workiq.mjs';
19
+ import { checkWorkIQ, pingWorkIQ, tryInstallWorkIQ } from './check-workiq.mjs';
21
20
  import {
22
21
  resolveProfile,
23
22
  makeIncludeFilter,
@@ -92,32 +91,7 @@ async function installVscode(options, resolved, version) {
92
91
  warnIfNotProjectRoot(projectRoot);
93
92
  }
94
93
 
95
- let dest;
96
- if (options.dest) {
97
- dest = options.dest.replace(/\\/g, '/');
98
- if (path.isAbsolute(dest)) {
99
- console.error('\n Please use a relative path (e.g., .kushi).\n');
100
- process.exit(1);
101
- }
102
- dest = dest.replace(/\/+$/, '');
103
- if (!dest) {
104
- console.error('\n Please provide a non-empty destination path.\n');
105
- process.exit(1);
106
- }
107
- const resolvedPath = path.resolve(projectRoot, dest);
108
- if (
109
- !resolvedPath.startsWith(projectRoot + path.sep) &&
110
- resolvedPath !== projectRoot
111
- ) {
112
- console.error('\n Destination must be within the current project.\n');
113
- process.exit(1);
114
- }
115
- } else if (options.yes) {
116
- dest = DEFAULT_DEST;
117
- } else {
118
- dest = await promptForDestination(DEFAULT_DEST);
119
- }
120
-
94
+ const dest = DEFAULT_DEST;
121
95
  const fullDest = path.resolve(projectRoot, dest);
122
96
  await confirmOverwriteIfExists(fullDest, dest, options.force);
123
97
 
@@ -182,9 +156,7 @@ async function installVscode(options, resolved, version) {
182
156
  */
183
157
  async function installClawpilot(options, resolved, version) {
184
158
  const home = os.homedir();
185
- const fullDest = options.dest
186
- ? path.resolve(options.dest)
187
- : path.join(home, ...CLAWPILOT_DEST_SUBPATH.split('/'));
159
+ const fullDest = path.join(home, ...CLAWPILOT_DEST_SUBPATH.split('/'));
188
160
 
189
161
  const displayDest = fullDest.startsWith(home + path.sep)
190
162
  ? '~' + path.sep + path.relative(home, fullDest)
@@ -345,7 +317,16 @@ async function preflightWorkIQ(options) {
345
317
  process.exit(1);
346
318
  }
347
319
 
348
- console.log(` ✓ WorkIQ detected at ${result.path} (${result.version})\n`);
320
+ console.log(` ✓ WorkIQ detected at ${result.path} (${result.version})`);
321
+
322
+ // Functional ping — confirm the binary actually answers queries, not just
323
+ // that it exists. Soft warning only; the `setup` skill does the deep
324
+ // recovery + identity capture on first verb run.
325
+ const ping = pingWorkIQ(result.path);
326
+ if (ping.warning) {
327
+ console.warn(` ⚠ ${ping.warning}`);
328
+ }
329
+ console.log(' Next: run `setup` once to verify sign-in + auto-fill your identity.\n');
349
330
  }
350
331
 
351
332
  /**
package/src/prompt.mjs DELETED
@@ -1,42 +0,0 @@
1
- import { createInterface } from 'node:readline/promises';
2
- import path from 'node:path';
3
-
4
- /**
5
- * Prompt the user for a destination directory.
6
- * @param {string} defaultDest
7
- * @returns {Promise<string>}
8
- */
9
- export async function promptForDestination(defaultDest) {
10
- const { stdin: input, stdout: output } = await import('node:process');
11
- const rl = createInterface({ input, output });
12
-
13
- rl.on('close', () => {});
14
-
15
- let dest;
16
- try {
17
- const answer = await rl.question(
18
- ` Where should Kushi assets be installed? (${defaultDest}): `,
19
- );
20
- dest = answer.trim() || defaultDest;
21
- } catch {
22
- process.exit(0);
23
- } finally {
24
- rl.close();
25
- }
26
-
27
- return validatePath(dest);
28
- }
29
-
30
- function validatePath(raw) {
31
- if (path.isAbsolute(raw)) {
32
- console.error('\n Please use a relative path (e.g., .kushi).\n');
33
- process.exit(1);
34
- }
35
- const resolved = path.resolve(process.cwd(), raw);
36
- const root = process.cwd();
37
- if (!resolved.startsWith(root + path.sep) && resolved !== root) {
38
- console.error('\n Destination must be within the current project.\n');
39
- process.exit(1);
40
- }
41
- return raw.replace(/\\/g, '/').replace(/\/+$/, '');
42
- }