kushi-agents 5.8.1 → 5.8.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.2",
|
|
4
4
|
"description": "Install Kushi — multi-source project evidence agent with Comprehensive Structured Capture (CSC) into weekly-only files across Email, Teams, OneNote, Loop, SharePoint, Meetings, CRM, ADO. Meetings retain a sibling verbatim/ audit folder. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -61,9 +61,7 @@ $script:RequiredFields = @{
|
|
|
61
61
|
'm365-auth' = @(
|
|
62
62
|
'm365Auth.defaultTenantId',
|
|
63
63
|
'm365Auth.oneNote.defaultNotebookName',
|
|
64
|
-
'm365Auth.oneNote.defaultNotebookId',
|
|
65
64
|
'm365Auth.oneNote.defaultLinkOwner',
|
|
66
|
-
'm365Auth.emailContext.folders',
|
|
67
65
|
'm365Auth.sharePointContext.localProjectsRoot'
|
|
68
66
|
)
|
|
69
67
|
'project-evidence' = @(
|
|
@@ -49,7 +49,10 @@ function emit(obj) { process.stdout.write(JSON.stringify(obj) + '\n'); }
|
|
|
49
49
|
function describeScope(source, scope) {
|
|
50
50
|
if (!scope || scope.enabled === false) return null;
|
|
51
51
|
const parts = [];
|
|
52
|
-
if (source === 'email'
|
|
52
|
+
if (source === 'email') {
|
|
53
|
+
if (scope.folders?.length) parts.push(`${scope.folders.length} folder(s)`);
|
|
54
|
+
else parts.push('default Inbox+subfolders');
|
|
55
|
+
}
|
|
53
56
|
if (scope.dateFloor) parts.push(`from ${scope.dateFloor}`);
|
|
54
57
|
if (source === 'onenote' && scope.notebookName) parts.push(`notebook "${scope.notebookName}"`);
|
|
55
58
|
return parts.length ? 'scope: ' + parts.join(', ') : null;
|
|
@@ -82,13 +85,22 @@ function buildPrompt(source, projectName, scope = null) {
|
|
|
82
85
|
// search, typically dropping query time from 60–180s to 5–15s.
|
|
83
86
|
if (scope && scope.enabled !== false) {
|
|
84
87
|
if (source === 'email') {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
lines.push('
|
|
88
|
+
const folders = (Array.isArray(scope.folders) && scope.folders.length > 0)
|
|
89
|
+
? scope.folders
|
|
90
|
+
: ['Inbox']; // safe default — bounds the query so WorkIQ uses Graph filter, not mailbox-wide semantic search
|
|
91
|
+
const isDefault = !(Array.isArray(scope.folders) && scope.folders.length > 0);
|
|
92
|
+
lines.push('');
|
|
93
|
+
if (scope.fuzzy !== false) {
|
|
94
|
+
lines.push('Restrict your search to Outlook mail folders whose name CONTAINS any of these tokens (case-insensitive, fuzzy substring match — e.g. "FDE" matches "1. FDE", "01 FDE Active", "FDE-archive"):');
|
|
95
|
+
} else {
|
|
96
|
+
lines.push('Restrict your search to ONLY these Outlook mail folders (exact name match):');
|
|
97
|
+
}
|
|
98
|
+
for (const f of folders) {
|
|
99
|
+
lines.push(` • "${f}"${scope.includeSubfolders ? ' (and all subfolders)' : ''}`);
|
|
100
|
+
}
|
|
101
|
+
lines.push('Do NOT scan any other mailbox folders.');
|
|
102
|
+
if (isDefault) {
|
|
103
|
+
lines.push('(Note: no project-specific folders configured — defaulting to Inbox+subfolders. For faster, more accurate results, populate emailContext.folders in m365-auth.json.)');
|
|
92
104
|
}
|
|
93
105
|
if (scope.dateFloor) lines.push(`Only consider mail received on or after ${scope.dateFloor}.`);
|
|
94
106
|
} else if (source === 'teams') {
|
|
@@ -62,11 +62,14 @@ export function scopeForSource(m365Auth, source) {
|
|
|
62
62
|
case 'email': {
|
|
63
63
|
const ec = m365Auth.emailContext || {};
|
|
64
64
|
if (ec.enabled === false) return { enabled: false };
|
|
65
|
+
const mp = ec.matchingPolicy || {};
|
|
65
66
|
return {
|
|
66
67
|
enabled: true,
|
|
67
68
|
folders: cleanArr(ec.folders),
|
|
68
69
|
includeSubfolders: ec.includeSubfolders !== false,
|
|
69
70
|
dateFloor: cleanStr(ec.dateFloor),
|
|
71
|
+
fuzzy: mp.alwaysFuzzy !== false,
|
|
72
|
+
matchMode: mp.mode || 'hybrid',
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
case 'teams': {
|
package/src/quickstart-m365.mjs
CHANGED
|
@@ -20,7 +20,7 @@ function isoDateNDaysAgo(n) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Run the
|
|
23
|
+
* Run the 4-question quickstart. Returns a small report object:
|
|
24
24
|
* { ran: bool, reason?: string, file?: string, fields?: string[] }
|
|
25
25
|
*
|
|
26
26
|
* @param {object} opts
|
|
@@ -33,6 +33,7 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
33
33
|
if (!process.stdin.isTTY || !process.stderr.isTTY) return { ran: false, reason: 'not-a-tty' };
|
|
34
34
|
|
|
35
35
|
const target = path.join(destRoot, 'config', 'user', 'm365-auth.json');
|
|
36
|
+
const projectEvidenceTarget = path.join(destRoot, 'config', 'user', 'project-evidence.yml');
|
|
36
37
|
if (!fs.existsSync(target)) return { ran: false, reason: 'm365-auth-not-found' };
|
|
37
38
|
|
|
38
39
|
let parsed;
|
|
@@ -44,12 +45,14 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
44
45
|
m.teamsChatContext ??= {};
|
|
45
46
|
m.calendarContext ??= {};
|
|
46
47
|
m.oneNote ??= {};
|
|
48
|
+
m.sharePointContext ??= {};
|
|
47
49
|
|
|
48
|
-
// Skip silently if all
|
|
50
|
+
// Skip silently if all 4 fields are already populated and --force not passed.
|
|
49
51
|
const foldersSet = Array.isArray(m.emailContext.folders) && m.emailContext.folders.length > 0;
|
|
50
52
|
const floorSet = !looksLikeSentinel(m.emailContext.dateFloor);
|
|
51
53
|
const notebookSet = !looksLikeSentinel(m.oneNote.defaultNotebookName);
|
|
52
|
-
|
|
54
|
+
const rootSet = !looksLikeSentinel(m.sharePointContext.localProjectsRoot);
|
|
55
|
+
if (foldersSet && floorSet && notebookSet && rootSet && !force) {
|
|
53
56
|
return { ran: false, reason: 'already-populated' };
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -60,21 +63,42 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
60
63
|
return ans === '' ? def : ans;
|
|
61
64
|
};
|
|
62
65
|
|
|
63
|
-
process.stderr.write('\n ┌─ Quickstart:
|
|
66
|
+
process.stderr.write('\n ┌─ Quickstart: 4 questions that make `kushi discover` fast ──\n');
|
|
64
67
|
process.stderr.write(` │ Editing: ${target}\n`);
|
|
65
|
-
process.stderr.write(' │ Press Enter at any prompt to
|
|
68
|
+
process.stderr.write(' │ Press Enter at any prompt to keep the shown default.\n');
|
|
66
69
|
process.stderr.write(' └────────────────────────────────────────────────────────────\n\n');
|
|
67
70
|
|
|
68
71
|
const fields = [];
|
|
69
72
|
|
|
70
|
-
// 1.
|
|
71
|
-
process.stderr.write(' [1/
|
|
73
|
+
// 1. Engagement root (where projects live on disk)
|
|
74
|
+
process.stderr.write(' [1/4] Where do your engagement project folders live on disk?\n');
|
|
75
|
+
process.stderr.write(' This is the parent folder containing each <project>/ directory.\n');
|
|
76
|
+
process.stderr.write(' Example: C:\\Users\\<you>\\OneDrive - Microsoft\\ISE\\Engagement Assets\n');
|
|
77
|
+
const curRoot = m.sharePointContext.localProjectsRoot || '';
|
|
78
|
+
const rootAns = await ask(' Engagement root', curRoot);
|
|
79
|
+
let engagementRoot = null;
|
|
80
|
+
if (rootAns) {
|
|
81
|
+
if (!fs.existsSync(rootAns)) {
|
|
82
|
+
process.stderr.write(` ⚠ Path does not exist: ${rootAns}\n`);
|
|
83
|
+
process.stderr.write(' Saving anyway — create it before your first bootstrap, or re-run --force to change.\n');
|
|
84
|
+
}
|
|
85
|
+
if (rootAns !== curRoot) {
|
|
86
|
+
m.sharePointContext.localProjectsRoot = rootAns;
|
|
87
|
+
if (m.sharePointContext.enabled == null) m.sharePointContext.enabled = true;
|
|
88
|
+
fields.push(`sharePointContext.localProjectsRoot=${rootAns}`);
|
|
89
|
+
}
|
|
90
|
+
engagementRoot = rootAns;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. Email folders
|
|
94
|
+
process.stderr.write('\n [2/4] Which Outlook mail folders contain your project mail?\n');
|
|
72
95
|
process.stderr.write(' Examples:\n');
|
|
73
96
|
process.stderr.write(' • "1. FDE, 99. FDE Not Active" (Microsoft FDE consultant)\n');
|
|
74
97
|
process.stderr.write(' • "Inbox, Archive, Projects" (generic)\n');
|
|
75
98
|
process.stderr.write(' • "Inbox" (everything in inbox)\n');
|
|
76
99
|
const curFolders = Array.isArray(m.emailContext.folders) ? m.emailContext.folders.join(', ') : '';
|
|
77
|
-
const
|
|
100
|
+
const foldersDefault = curFolders || '1. FDE, 99. FDE Not Active';
|
|
101
|
+
const foldersAns = await ask(' Comma-separated folder names', foldersDefault);
|
|
78
102
|
if (foldersAns && foldersAns !== curFolders) {
|
|
79
103
|
const arr = foldersAns.split(',').map(s => s.trim()).filter(Boolean);
|
|
80
104
|
m.emailContext.folders = arr;
|
|
@@ -85,8 +109,8 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
85
109
|
process.stderr.write(' (skipped — discover will scan the whole mailbox, which can take minutes per source)\n');
|
|
86
110
|
}
|
|
87
111
|
|
|
88
|
-
//
|
|
89
|
-
process.stderr.write('\n [
|
|
112
|
+
// 3. Look-back days
|
|
113
|
+
process.stderr.write('\n [3/4] How many days back should discover scan? (smaller = faster)\n');
|
|
90
114
|
process.stderr.write(' Recommended: 60 for active engagements, 90 for setup.\n');
|
|
91
115
|
const curFloor = m.emailContext.dateFloor;
|
|
92
116
|
const defaultDays = curFloor && /^\d{4}-\d{2}-\d{2}$/.test(curFloor)
|
|
@@ -104,8 +128,8 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
104
128
|
process.stderr.write(` ⚠ "${daysAns}" is not a valid number 1–3650 — skipping dateFloor.\n`);
|
|
105
129
|
}
|
|
106
130
|
|
|
107
|
-
//
|
|
108
|
-
process.stderr.write('\n [
|
|
131
|
+
// 4. OneNote notebook
|
|
132
|
+
process.stderr.write('\n [4/4] Which OneNote notebook holds your project notes?\n');
|
|
109
133
|
process.stderr.write(' Default for Microsoft consultants: "ISE Work"\n');
|
|
110
134
|
const curNb = m.oneNote.defaultNotebookName || '';
|
|
111
135
|
const nbAns = await ask(' Notebook name', curNb || 'ISE Work');
|
|
@@ -122,7 +146,31 @@ export async function runM365Quickstart({ destRoot, force = false, noPrompt = fa
|
|
|
122
146
|
return { ran: true, reason: 'no-changes', file: target, fields: [] };
|
|
123
147
|
}
|
|
124
148
|
fs.writeFileSync(target, JSON.stringify(parsed, null, 2) + '\n');
|
|
125
|
-
|
|
149
|
+
|
|
150
|
+
// Mirror engagement root into project-evidence.yml#projects_root so bootstrap
|
|
151
|
+
// and the resolver agree. Best-effort: if file is missing or unparseable we
|
|
152
|
+
// skip silently — m365-auth is the canonical source for sharePoint scoping.
|
|
153
|
+
if (engagementRoot && fs.existsSync(projectEvidenceTarget)) {
|
|
154
|
+
try {
|
|
155
|
+
const txt = fs.readFileSync(projectEvidenceTarget, 'utf8');
|
|
156
|
+
// Simple line replacement to avoid pulling in YAML lib at install time.
|
|
157
|
+
// The seed file ships with `projects_root: <value>` on its own line.
|
|
158
|
+
if (/^projects_root:/m.test(txt)) {
|
|
159
|
+
const updated = txt.replace(/^projects_root:.*$/m, `projects_root: ${JSON.stringify(engagementRoot)}`);
|
|
160
|
+
if (updated !== txt) {
|
|
161
|
+
fs.writeFileSync(projectEvidenceTarget, updated);
|
|
162
|
+
fields.push(`project-evidence.yml#projects_root=${engagementRoot}`);
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
fs.appendFileSync(projectEvidenceTarget, `\nprojects_root: ${JSON.stringify(engagementRoot)}\n`);
|
|
166
|
+
fields.push(`project-evidence.yml#projects_root=${engagementRoot} (appended)`);
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
process.stderr.write(` ⚠ Could not update project-evidence.yml: ${e.message}\n`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
process.stderr.write(`\n ✓ Wrote ${fields.length} field(s):\n`);
|
|
126
174
|
for (const f of fields) process.stderr.write(` • ${f}\n`);
|
|
127
175
|
process.stderr.write('\n');
|
|
128
176
|
return { ran: true, file: target, fields };
|