kushi-agents 3.4.2 → 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.
Files changed (73) hide show
  1. package/.github/copilot-instructions.kushi.md +38 -0
  2. package/README.md +33 -0
  3. package/bin/cli.mjs +2 -0
  4. package/package.json +17 -4
  5. package/plugin/agents/kushi.agent.md +155 -147
  6. package/plugin/instructions/ado-bootstrap-discovery.instructions.md +111 -0
  7. package/plugin/instructions/ado-engagement-tree.instructions.md +73 -0
  8. package/plugin/instructions/answer-from-evidence.instructions.md +1 -1
  9. package/plugin/instructions/auth-and-retry.instructions.md +51 -16
  10. package/plugin/instructions/azure-auth-patterns.instructions.md +13 -6
  11. package/plugin/instructions/bootstrap-status-format.instructions.md +113 -0
  12. package/plugin/instructions/capture-learnings.instructions.md +95 -0
  13. package/plugin/instructions/cleanup-on-resolution.instructions.md +69 -0
  14. package/plugin/instructions/crm-bootstrap-discovery.instructions.md +79 -0
  15. package/plugin/instructions/crm-internal-vs-confirmed.instructions.md +79 -0
  16. package/plugin/instructions/evidence-confidence-ladder.instructions.md +66 -0
  17. package/plugin/instructions/evidence-layout-canonical.instructions.md +115 -0
  18. package/plugin/instructions/evidence-thoroughness.instructions.md +82 -12
  19. package/plugin/instructions/full-view-gate.instructions.md +91 -0
  20. package/plugin/instructions/m365-id-registry.instructions.md +134 -0
  21. package/plugin/instructions/meetings-verbatim-required.instructions.md +176 -0
  22. package/plugin/instructions/run-reports.instructions.md +129 -0
  23. package/plugin/instructions/scope-boundaries.instructions.md +218 -0
  24. package/plugin/instructions/snapshot-vs-stream.instructions.md +2 -0
  25. package/plugin/instructions/update-ledger.instructions.md +132 -0
  26. package/plugin/instructions/verbatim-by-default.instructions.md +73 -0
  27. package/plugin/instructions/workiq-first.instructions.md +15 -31
  28. package/plugin/instructions/workiq-only.instructions.md +193 -0
  29. package/plugin/learnings/README.md +50 -0
  30. package/plugin/learnings/ado.md +45 -0
  31. package/plugin/learnings/crm.md +96 -0
  32. package/plugin/learnings/cross-cutting.md +36 -0
  33. package/plugin/learnings/email.md +33 -0
  34. package/plugin/learnings/meetings.md +30 -0
  35. package/plugin/learnings/misc.md +46 -0
  36. package/plugin/learnings/onenote.md +215 -0
  37. package/plugin/learnings/sharepoint.md +5 -0
  38. package/plugin/learnings/teams.md +5 -0
  39. package/plugin/plugin.json +22 -2
  40. package/plugin/prompts/apply-ado.prompt.md +14 -0
  41. package/plugin/prompts/propose-ado.prompt.md +12 -0
  42. package/plugin/reference-packs/fde/crm-field-manifest.md +165 -0
  43. package/plugin/skills/apply-ado-update/SKILL.md +125 -0
  44. package/plugin/skills/ask-project/SKILL.md +2 -0
  45. package/plugin/skills/bootstrap-project/SKILL.md +81 -3
  46. package/plugin/skills/propose-ado-update/SKILL.md +108 -0
  47. package/plugin/skills/pull-ado/SKILL.md +173 -23
  48. package/plugin/skills/pull-crm/SKILL.md +168 -15
  49. package/plugin/skills/pull-email/SKILL.md +139 -22
  50. package/plugin/skills/pull-meetings/SKILL.md +109 -25
  51. package/plugin/skills/pull-misc/README.md +84 -0
  52. package/plugin/skills/pull-misc/SKILL.md +257 -0
  53. package/plugin/skills/pull-misc/runner.mjs +280 -0
  54. package/plugin/skills/pull-onenote/README.md +90 -0
  55. package/plugin/skills/pull-onenote/SKILL.md +400 -51
  56. package/plugin/skills/pull-onenote/runner.mjs +356 -0
  57. package/plugin/skills/pull-onenote/scripts/recapture-section-url.mjs +295 -0
  58. package/plugin/skills/pull-onenote/write-snapshot.mjs +271 -0
  59. package/plugin/skills/pull-sharepoint/SKILL.md +44 -12
  60. package/plugin/skills/pull-teams/SKILL.md +40 -11
  61. package/plugin/skills/refresh-project/SKILL.md +33 -2
  62. package/plugin/skills/self-check/run.ps1 +186 -4
  63. package/plugin/templates/ado-update/discussion-comment.template.md +26 -0
  64. package/plugin/templates/ado-update/integrations-ado-writes.example.yml +49 -0
  65. package/plugin/templates/ado-update/proposed.template.md +78 -0
  66. package/plugin/templates/init/external-links.template.txt +30 -0
  67. package/plugin/templates/init/project-integrations.template.yml +57 -2
  68. package/plugin/templates/snapshot/meeting-verbatim.template.md +110 -0
  69. package/plugin/templates/snapshot/meetings-series-index.template.md +3 -1
  70. package/plugin/templates/snapshot/onenote-page.template.md +92 -23
  71. package/plugin/templates/weekly/meetings-stream.template.md +11 -6
  72. package/src/copilot-instructions.mjs +80 -0
  73. package/src/main.mjs +18 -1
@@ -1,28 +1,97 @@
1
- # OneNote Page: <Page Title>
1
+ ---
2
+ page_title: "<page title>"
3
+ section: "<one_sectionName>"
4
+ notebook: "<notebookName>"
5
+ section_id: "<one_sectionFileId>"
6
+ wdpartid: "<wdpartid from enumerate>"
7
+ last_modified: "<as reported by WorkIQ>"
8
+ last_status: "captured | BODY-NOT-EXPOSED | user-pasted | enumeration-only"
9
+ attempts: <int>
10
+ last_attempt: "<ISO-8601>"
11
+ captured_at: "<ISO-8601 if captured/user-pasted, else empty>"
12
+ ---
13
+
14
+ # OneNote Snapshot — Page: <page title>
15
+
16
+ - **Project:** <project>
17
+ - **Section:** <notebookName> / <one_sectionName> (`<one_sectionFileId>`)
18
+ - **Page wdpartid:** `<wdpartid>`
19
+ - **Last modified:** <as reported by WorkIQ>
20
+ - **Capture status:** `<last_status>`
21
+ - **Attempts:** <int>
22
+ - **Last attempt:** <ts>
23
+ - **Source basis:** WorkIQ enumerate + per-page verbatim probe (pull-onenote v2.5.0). Graph `/me/onenote/*` skipped per workspace policy. Per-page retry registry: `.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>.one_pages`.
24
+
25
+ <!-- IF last_status == "captured" or "user-pasted" -->
26
+
27
+ ## AI Narrative Summary
28
+
29
+ <3+ paragraphs covering what the page is about, structure, key points, anything noteworthy.>
30
+
31
+ ## Body (verbatim)
32
+
33
+ <full page body — NOT a summary, NOT a paraphrase. Reproduce content exactly as exposed by the enterprise retrieval surface, or exactly as the user pasted it.>
34
+
35
+ ## Source Basis
36
+
37
+ - Tool used: WorkIQ (canonical Step B query) | user-paste
38
+ - Captured at: <ISO-8601>
39
+
40
+ <!-- ELSE IF last_status == "BODY-NOT-EXPOSED" -->
41
+
42
+ ## Page body
43
+
44
+ ❌ **page-body-unavailable: WorkIQ returned BODY-NOT-EXPOSED on attempt <N>.**
45
+
46
+ > Per `pull-onenote` v2.5.0 § "Empirical contract": WorkIQ OneNote body retrieval is non-deterministic — the same page may return a body on a later refresh. This page IS registered for retry in `.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>.one_pages` and will be re-attempted on every refresh until `last_status` becomes `captured` or `user-pasted`. Inferring the page contents from adjacent emails or chat traffic is forbidden.
47
+
48
+ ### next_step
49
+
50
+ - (automatic) Next refresh will retry the verbatim body fetch (attempt <N+1>).
51
+ - (manual) Paste the verbatim page body to upgrade to `user-pasted`. Update the registry entry's `last_status` and `captured_at` accordingly, and append an AI Narrative Summary above the body.
52
+
53
+ ### Page metadata
54
+
55
+ | Field | Value |
56
+ |---|---|
57
+ | Page title | `<title>` |
58
+ | Section | <notebook> / <section> |
59
+ | wdpartid | `<wdpartid>` |
60
+ | Last modified (per WorkIQ) | <date> |
61
+ | Capture status | `BODY-NOT-EXPOSED` |
62
+ | Attempts so far | <N> |
63
+ | Last attempt | <ts> |
64
+
65
+ ## Source Basis
66
+
67
+ - Tool used: WorkIQ (Step A enumerate succeeded; Step B per-page probe returned BODY-NOT-EXPOSED).
68
+ - Graph `/me/onenote/*` not attempted — workspace policy blocks `Notes.Read.All` admin consent.
69
+
70
+ <!-- ELSE IF last_status == "enumeration-only" -->
71
+
72
+ ## Page body
73
+
74
+ ⚠️ **enumeration-only** — page enumerated in the section index, but no per-page body fetch attempted yet.
75
+
76
+ > Per `pull-onenote` v2.5.0: this is a transient state. The next refresh run will attempt Step B and either upgrade to `captured` or downgrade to `BODY-NOT-EXPOSED`.
77
+
78
+ ### next_step
79
+
80
+ - (automatic) Next refresh will attempt the verbatim body fetch.
81
+
82
+ ## Source Basis
83
+
84
+ - Tool used: WorkIQ Step A enumerate only. No Step B attempted in this run.
2
85
 
3
- > **Snapshot.** Replaced on every refresh.
4
- > Section: <section name>
5
- > Notebook: <notebook name>
6
- > Last modified: <YYYY-MM-DD HH:mm TZ> by <author>
7
- > Last fetched: <YYYY-MM-DD HH:mm TZ>
8
-
9
- ## Source Basis
10
- - Tool used: <WorkIQ | Graph (often 40001) | user-paste>
11
- - Mutable hint: `one_sectionFileId` = <id>
12
-
13
- ## Body (verbatim)
14
-
15
- <full page body — NOT a summary, NOT a paraphrase. Reproduce content as faithfully as the source allows.>
16
-
17
- ---
18
-
19
- > Edits to this page during refresh windows are captured in `stream/<week>_onenote-stream.md`.
20
86
 
21
87
  ## Validation
22
88
 
23
- > Thoroughness detector checklist — per `thoroughness-detector.instructions.md`.
89
+ Per `thoroughness-detector.instructions.md`, the writer must tick all that apply before saving:
24
90
 
25
- - [ ] bodies-present (full body, not just metadata/link)
26
- - [ ] metadata-fields-complete
27
- - [ ] no-link-only-stubs
28
- - [ ] coverage-notes-explain-any-gaps
91
+ - [ ] yaml front-matter present with `page_title`, `section`, `notebook`, `section_id`, `wdpartid`, `last_status`, `attempts`, `last_attempt`
92
+ - [ ] `last_status` is one of: `captured` | `BODY-NOT-EXPOSED` | `user-pasted` | `enumeration-only`
93
+ - [ ] If `last_status == captured` or `user-pasted`: AI Narrative Summary (3+ paragraphs) AND verbatim Body section are present and non-empty
94
+ - [ ] If `last_status == BODY-NOT-EXPOSED`: explicit marker, `next_step` block, and metadata table all present; NO inferred or paraphrased body content
95
+ - [ ] If `last_status == enumeration-only`: explicit transient marker present; NO body content of any kind
96
+ - [ ] Page is registered in `.project-evidence/m365/m365-mutable.json#knownSections.<projectKey>.one_pages` with matching `last_status` and `attempts`
97
+ - [ ] No fabrication: nothing in this file came from adjacent emails, chats, or other sources to "fill in" what WorkIQ did not return
@@ -58,7 +58,8 @@ _2–4 sentence overview of meetings this week for this project: how many, key t
58
58
  - **Actual Attendees:** {{PEOPLE}}
59
59
  - **No-shows:** {{PEOPLE}}
60
60
  - **Purpose / Stated Agenda:** _one or two paragraphs — what this meeting was for, what was on the agenda, what was the framing._
61
- - **Source basis:** _transcript (Copilot recap) | transcript (raw VTT) | reconstructed from meeting chat (no transcript) | partial — chat + recap only._
61
+ - **Source basis:** _transcript (raw VTT) | transcript (WorkIQ summary, not raw) | reconstructed from meeting chat (no transcript) | partial — chat + recap only | ❌ source-expired-or-unrecoverable._
62
+ - **Verbatim folder (REQUIRED):** `Evidence/<alias>/meetings/verbatim/<YYYY-MM-DD-HHMM>_<slug>/` — raw immutable capture (chat-messages.json/md, transcript.vtt or transcript-source.md, recording-url.txt, attachments/, coverage.md). All assertions below cite files inside this folder per `meetings-verbatim-required.instructions.md`.
62
63
 
63
64
  #### Detailed Discussion Summary
64
65
  _Multi-paragraph narrative of what was actually discussed end-to-end. Do NOT compress to 3 bullets. Include who said what, what context emerged, what was challenged, what was confirmed. This is the section that lets a State-file reader reconstruct the meeting without watching the recording._
@@ -99,10 +100,11 @@ _Every substantive exchange. Collapse only routine acks ("got it", "thanks"). Fo
99
100
  - _e.g._ Customer requested executive sponsor reciprocal commitment from Microsoft.
100
101
 
101
102
  #### Artifacts
102
- - Recording: {{URL}}
103
- - Transcript: {{URL}}
104
- - Copilot recap: {{URL}}
105
- - Shared files / decks / docs: {{URL list}}
103
+ - **Verbatim folder:** `Evidence/<alias>/meetings/verbatim/{{YYYY-MM-DD-HHMM}}_{{slug}}/`
104
+ - Recording (live URL — expires): {{URL}} _(also persisted in verbatim/recording-url.txt)_
105
+ - Transcript: {{URL}} _(raw VTT in verbatim/transcript.vtt if available)_
106
+ - Copilot recap: {{URL}} _(in verbatim/recap-card.md)_
107
+ - Shared files / decks / docs: {{URL list}} _(downloaded copies in verbatim/attachments/)_
106
108
 
107
109
  #### Coverage Notes
108
110
  - _What was retrievable vs what wasn't_ — e.g. "Transcript not generated; reconstructed from 47-message chat thread + Copilot recap card. Decisions and Action Items high confidence; Transcript Walk-Through derived from chat exchange timestamps."
@@ -179,4 +181,7 @@ _Generated from Teams meetings (calendar + transcripts + recordings + Copilot re
179
181
  - [ ] action-items-have-owner-and-due
180
182
  - [ ] transcript-walk-through-present
181
183
  - [ ] no-link-only-stubs
182
- - [ ] coverage-notes-explain-any-gaps
184
+ - [ ] coverage-notes-explain-any-gaps
185
+ - [ ] verbatim-folder-exists-per-meeting (REQUIRED v3.10.0 — sibling `verbatim/<YYYY-MM-DD-HHMM>_<slug>/` dir per meeting block, enforced by self-check D13.a)
186
+ - [ ] verbatim-folder-non-empty (at least chat-messages.json OR transcript.* OR transcript-source.md beyond captured-at.txt — D13.b)
187
+ - [ ] transcript-class-file-present (REQUIRED v3.10.1 — `transcript.vtt` OR `transcript.txt` OR `transcript-source.md` non-empty; chat alone is NOT a transcript — D13.c). Run the exhaustive cascade per `meetings-verbatim-required.instructions.md`: `m365_get_transcript` → Graph REST `/onlineMeetings/.../transcripts/.../content` → `m365_get_facilitator_notes` → WorkIQ strict full-text pull → recording download.
@@ -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/');