kushi-agents 3.12.1 → 3.13.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.
@@ -0,0 +1,38 @@
1
+ ## Kushi — Project Evidence Agent
2
+
3
+ This workspace has [Kushi](https://gim-home.github.io/kushi/) installed under `.kushi/`. Kushi captures multi-source project evidence (Email, Teams, OneNote, SharePoint, Meetings, CRM, ADO) via **WorkIQ** and answers cited questions over the captured evidence.
4
+
5
+ ### Routing — no `@Kushi` prefix required
6
+
7
+ When the user types any of the following verbs followed by a project name, route to the corresponding Kushi prompt in `.kushi/prompts/` (no `@Kushi` needed):
8
+
9
+ | Verb | Prompt to run |
10
+ | -------------------------- | ------------------------------------------ |
11
+ | `bootstrap <project>` | `/pull-all-bootstrap` |
12
+ | `refresh <project>` | `/pull-all-refresh` |
13
+ | `aggregate <project>` | `/pull-all-aggregate` |
14
+ | `state <project>` | `/rebuild-state` |
15
+ | `consolidate <project>` | `/consolidate-evidence` |
16
+ | `status <project>` | `/show-run-log` |
17
+ | `ask <project> <question>` | `/ask-project` (auto-routes to evidence Q&A) |
18
+
19
+ Project Q&A also auto-dispatches when the user names a known project under the engagement root **and** asks a question about it — no prefix needed.
20
+
21
+ ### Hard rules (enforced by Kushi skills)
22
+
23
+ - **WorkIQ-first** for all M365 sources. `m365_*` / Microsoft Graph calls are only allowed as a classified fallback after a documented WorkIQ failure.
24
+ - **Every assertion** in any Kushi output (State files, Evidence summaries, consolidated reports, Open Questions) MUST carry an inline citation: `[source: <alias>/<folder>/<file> · YYYY-MM-DD]`.
25
+ - **Read-only Q&A** — `ask` never triggers any `pull-*` skill. Stale evidence (>14d) prompts the user to refresh; Kushi does not auto-refresh.
26
+ - **No cross-project bleed** — answers must be grounded only in the named project's `Evidence/` + `State/` + `snapshot/`.
27
+ - **Never reach out** — Kushi does not send Teams/email/CRM-note/ADO-comment on the user's behalf unless they explicitly request it that turn. Recommendations go into the project's Open Questions, not into outbound sends.
28
+
29
+ ### Layout
30
+
31
+ - `.kushi/agents/kushi.agent.md` — orchestrator (also addressable as `@Kushi`)
32
+ - `.kushi/prompts/` — verb prompts (`/pull-*`, `/ask-project`, etc.)
33
+ - `.kushi/skills/` — per-source pull + render skills
34
+ - `.kushi/instructions/` — doctrine (citations, WorkIQ-first, freshness gates)
35
+ - `.kushi/reference-packs/` — bundled domain doctrine (override at project / user / packaged layers)
36
+ - `.kushi/kushi-install.json` — profile manifest written by the installer
37
+
38
+ Full docs: <https://gim-home.github.io/kushi/>
package/bin/cli.mjs CHANGED
@@ -26,6 +26,7 @@ if (args.includes('--help') || args.includes('-h')) {
26
26
  --dest <path> Override destination (relative for vscode, absolute for clawpilot)
27
27
  --force Overwrite existing destination without asking
28
28
  --no-settings Skip .vscode/settings.json update (vscode target only)
29
+ --no-instructions Skip .github/copilot-instructions.md merge (vscode target only)
29
30
  --help, -h Show this help
30
31
 
31
32
  After install, talk to Kushi:
@@ -55,6 +56,7 @@ const options = {
55
56
  dest: getFlag('--dest'),
56
57
  force: args.includes('--force'),
57
58
  noSettings: args.includes('--no-settings'),
59
+ noInstructions: args.includes('--no-instructions'),
58
60
  target,
59
61
  profile: getFlag('--profile'),
60
62
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kushi-agents",
3
- "version": "3.12.1",
3
+ "version": "3.13.0",
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": {
@@ -11,7 +11,8 @@
11
11
  "src/",
12
12
  "plugin/",
13
13
  ".github/config/m365-auth.json.example",
14
- ".github/config/m365-mutable.json.example"
14
+ ".github/config/m365-mutable.json.example",
15
+ ".github/copilot-instructions.kushi.md"
15
16
  ],
16
17
  "engines": {
17
18
  "node": ">=18.0.0"
@@ -0,0 +1,80 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export const KUSHI_BEGIN_MARKER = '<!-- BEGIN kushi-agents (managed — do not edit between markers) -->';
5
+ export const KUSHI_END_MARKER = '<!-- END kushi-agents -->';
6
+
7
+ const MARKER_REGEX = new RegExp(
8
+ `${escapeRegex(KUSHI_BEGIN_MARKER)}[\\s\\S]*?${escapeRegex(KUSHI_END_MARKER)}`,
9
+ );
10
+
11
+ function escapeRegex(str) {
12
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13
+ }
14
+
15
+ /**
16
+ * Wrap the source content in our managed-block markers.
17
+ * @param {string} source
18
+ * @returns {string}
19
+ */
20
+ function buildBlock(source) {
21
+ return `${KUSHI_BEGIN_MARKER}\n${source.trim()}\n${KUSHI_END_MARKER}\n`;
22
+ }
23
+
24
+ /**
25
+ * Merge the Kushi section into .github/copilot-instructions.md using
26
+ * managed-block markers. Three behaviors:
27
+ *
28
+ * - File missing → create it with a brief header + our block
29
+ * - File has markers → replace block content in place (idempotent upgrade)
30
+ * - File without markers → append our block at the end, preserving user content
31
+ *
32
+ * @param {string} projectRoot
33
+ * @param {string} sourcePkgDir – root of the npm package (for reading the source template)
34
+ * @returns {{ action: 'created' | 'updated-block' | 'appended-block' | 'unchanged', path: string }}
35
+ */
36
+ export function mergeCopilotInstructions(projectRoot, sourcePkgDir) {
37
+ const srcTemplate = path.join(
38
+ sourcePkgDir,
39
+ '.github',
40
+ 'copilot-instructions.kushi.md',
41
+ );
42
+
43
+ if (!fs.existsSync(srcTemplate)) {
44
+ return { action: 'unchanged', path: '', reason: 'source template missing' };
45
+ }
46
+
47
+ const sourceContent = fs.readFileSync(srcTemplate, 'utf-8');
48
+ const block = buildBlock(sourceContent);
49
+
50
+ const githubDir = path.join(projectRoot, '.github');
51
+ const targetPath = path.join(githubDir, 'copilot-instructions.md');
52
+ const displayPath = '.github/copilot-instructions.md';
53
+
54
+ fs.mkdirSync(githubDir, { recursive: true });
55
+
56
+ if (!fs.existsSync(targetPath)) {
57
+ const header = `# Copilot Instructions\n\n_This file is auto-loaded by GitHub Copilot Chat into every chat turn in this workspace._\n\n`;
58
+ fs.writeFileSync(targetPath, header + block, 'utf-8');
59
+ return { action: 'created', path: displayPath };
60
+ }
61
+
62
+ const existing = fs.readFileSync(targetPath, 'utf-8');
63
+
64
+ if (MARKER_REGEX.test(existing)) {
65
+ const updated = existing.replace(MARKER_REGEX, block.trimEnd());
66
+ if (updated === existing) {
67
+ return { action: 'unchanged', path: displayPath };
68
+ }
69
+ fs.writeFileSync(targetPath, updated, 'utf-8');
70
+ return { action: 'updated-block', path: displayPath };
71
+ }
72
+
73
+ const separator = existing.endsWith('\n\n')
74
+ ? ''
75
+ : existing.endsWith('\n')
76
+ ? '\n'
77
+ : '\n\n';
78
+ fs.writeFileSync(targetPath, existing + separator + block, 'utf-8');
79
+ return { action: 'appended-block', path: displayPath };
80
+ }
package/src/main.mjs CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  import { promptForDestination } from './prompt.mjs';
15
15
  import { copyAssets, copyProjectFiles } from './copy-assets.mjs';
16
16
  import { mergeSettings } from './settings.mjs';
17
+ import { mergeCopilotInstructions } from './copilot-instructions.mjs';
17
18
  import {
18
19
  resolveProfile,
19
20
  makeIncludeFilter,
@@ -48,7 +49,7 @@ function resolveProfileForInstall(profileName) {
48
49
 
49
50
  /**
50
51
  * Orchestrator: prompt -> copy -> settings -> summary.
51
- * @param {{ dest?: string, force?: boolean, noSettings?: boolean, target?: string, profile?: string }} options
52
+ * @param {{ dest?: string, force?: boolean, noSettings?: boolean, noInstructions?: boolean, target?: string, profile?: string }} options
52
53
  */
53
54
  export async function main(options = {}) {
54
55
  const version = getVersion();
@@ -150,6 +151,22 @@ async function installVscode(options, resolved, version) {
150
151
  console.log('\n Skipped .vscode/settings.json (--no-settings)');
151
152
  }
152
153
 
154
+ if (!options.noInstructions) {
155
+ const result = mergeCopilotInstructions(projectRoot, PKG_ROOT);
156
+ const labelByAction = {
157
+ 'created': 'Created',
158
+ 'updated-block': 'Updated managed Kushi block in',
159
+ 'appended-block': 'Appended managed Kushi block to existing',
160
+ 'unchanged': 'Unchanged',
161
+ };
162
+ if (result.action !== 'unchanged' || result.path) {
163
+ const label = labelByAction[result.action] || 'Touched';
164
+ console.log(`\n ${label} ${result.path}`);
165
+ }
166
+ } else {
167
+ console.log('\n Skipped .github/copilot-instructions.md (--no-instructions)');
168
+ }
169
+
153
170
  const { copied: projCopied, skipped: projSkipped } = copyProjectFiles(PKG_ROOT);
154
171
  if (projCopied.length > 0) {
155
172
  console.log('\n Copied project files to .github/');