kushi-agents 5.7.0 → 5.7.1
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/bin/cli.mjs +7 -0
- package/package.json +1 -1
- package/plugin/runners/lib/workiq.mjs +53 -3
- package/src/main.mjs +1 -1
- package/src/multi-host.mjs +35 -0
package/bin/cli.mjs
CHANGED
|
@@ -182,10 +182,16 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
182
182
|
--clawpilot Install to ~/.copilot/m-skills/kushi/
|
|
183
183
|
--vscode Install to ~/.vscode/chat/skills/kushi/ (a.k.a. GitHub Copilot Chat)
|
|
184
184
|
--all-hosts Install to BOTH hosts
|
|
185
|
+
--no-workspace Skip the auto workspace install (host install only)
|
|
185
186
|
--uninstall [--clawpilot|--vscode|--all]
|
|
186
187
|
Cleanly remove the kushi install + skills-metadata.json entry
|
|
187
188
|
from the chosen host(s). Default = all detected hosts.
|
|
188
189
|
|
|
190
|
+
Note: when run from inside a project directory (any of: package.json, .git,
|
|
191
|
+
.kushi/, Evidence/, etc.), host installs ALSO refresh the workspace
|
|
192
|
+
.kushi/ install in cwd. One command covers both. Pass --no-workspace to
|
|
193
|
+
suppress, or run from a non-project directory.
|
|
194
|
+
|
|
189
195
|
Workspace install (legacy / default when no host flag is given):
|
|
190
196
|
--target vscode Install to <cwd>/.kushi/ + update .vscode/settings.json [default]
|
|
191
197
|
--target clawpilot Alias for --clawpilot (kept for back-compat)
|
|
@@ -274,6 +280,7 @@ if (wantsVscode || wantsAllHosts || wantsUninstall) {
|
|
|
274
280
|
all,
|
|
275
281
|
uninstall: wantsUninstall,
|
|
276
282
|
profile: getFlag('--profile'),
|
|
283
|
+
includeWorkspace: !args.includes('--no-workspace'),
|
|
277
284
|
}).catch((err) => {
|
|
278
285
|
console.error(`\n ${err.message}\n`);
|
|
279
286
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "5.7.
|
|
3
|
+
"version": "5.7.1",
|
|
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": {
|
|
@@ -4,15 +4,45 @@
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import { platform } from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { promises as fs } from 'node:fs';
|
|
7
|
+
import { promises as fs, existsSync } from 'node:fs';
|
|
8
8
|
|
|
9
9
|
/** Resolve the workiq binary for the current platform. */
|
|
10
10
|
export function resolveWorkiqBin(explicit = process.env.KUSHI_WORKIQ_BIN) {
|
|
11
|
-
if (explicit)
|
|
11
|
+
if (explicit) {
|
|
12
|
+
if (path.isAbsolute(explicit)) return explicit;
|
|
13
|
+
const found = whichSync(explicit);
|
|
14
|
+
return found || explicit;
|
|
15
|
+
}
|
|
16
|
+
// 1. Try PATH first (npm-installed workiq lands in AppData\Roaming\npm).
|
|
17
|
+
const onPath = whichSync(platform() === 'win32' ? 'workiq.cmd' : 'workiq');
|
|
18
|
+
if (onPath) return onPath;
|
|
19
|
+
// 2. Fallback to Clawpilot-managed location.
|
|
12
20
|
if (platform() === 'win32') return path.join(process.env.USERPROFILE || '', '.copilot', 'bin', 'workiq.cmd');
|
|
13
21
|
return path.join(process.env.HOME || '', '.copilot', 'bin', 'workiq');
|
|
14
22
|
}
|
|
15
23
|
|
|
24
|
+
function whichSync(name) {
|
|
25
|
+
const PATH = process.env.PATH || process.env.Path || '';
|
|
26
|
+
const sep = platform() === 'win32' ? ';' : ':';
|
|
27
|
+
const exts = platform() === 'win32'
|
|
28
|
+
? (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';')
|
|
29
|
+
: [''];
|
|
30
|
+
const hasExt = /\.[a-zA-Z0-9]{1,5}$/.test(name);
|
|
31
|
+
for (const dir of PATH.split(sep)) {
|
|
32
|
+
if (!dir) continue;
|
|
33
|
+
if (hasExt) {
|
|
34
|
+
const candidate = path.join(dir, name);
|
|
35
|
+
if (existsSync(candidate)) return candidate;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
for (const ext of exts) {
|
|
39
|
+
const candidate = path.join(dir, name + ext);
|
|
40
|
+
if (existsSync(candidate)) return candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
16
46
|
/**
|
|
17
47
|
* Ask WorkIQ a question. Returns the raw stdout text and any parsed CSC blocks.
|
|
18
48
|
*
|
|
@@ -86,7 +116,20 @@ async function pathExists(p) { try { await fs.access(p); return true; } catch {
|
|
|
86
116
|
|
|
87
117
|
function runProcess(exe, args, { timeoutMs, env }) {
|
|
88
118
|
return new Promise((resolve, reject) => {
|
|
89
|
-
|
|
119
|
+
// Node 20.12+ refuses to spawn .cmd/.bat on Windows without shell:true
|
|
120
|
+
// (CVE-2024-27980). Detect and route through cmd.exe explicitly with
|
|
121
|
+
// verbatim args so quoting in the prompt survives intact.
|
|
122
|
+
const isWinShim = platform() === 'win32' && /\.(cmd|bat)$/i.test(exe);
|
|
123
|
+
const spawnOpts = { env, shell: false };
|
|
124
|
+
let spawnExe = exe;
|
|
125
|
+
let spawnArgs = args;
|
|
126
|
+
if (isWinShim) {
|
|
127
|
+
const quoted = [exe, ...args].map(quoteForCmd).join(' ');
|
|
128
|
+
spawnExe = process.env.ComSpec || 'cmd.exe';
|
|
129
|
+
spawnArgs = ['/d', '/s', '/c', quoted];
|
|
130
|
+
spawnOpts.windowsVerbatimArguments = true;
|
|
131
|
+
}
|
|
132
|
+
const child = spawn(spawnExe, spawnArgs, spawnOpts);
|
|
90
133
|
let stdout = '';
|
|
91
134
|
let stderr = '';
|
|
92
135
|
let timeoutTimer = null;
|
|
@@ -102,3 +145,10 @@ function runProcess(exe, args, { timeoutMs, env }) {
|
|
|
102
145
|
child.on('close', code => { if (timeoutTimer) clearTimeout(timeoutTimer); resolve({ stdout, stderr, exitCode: code ?? 0 }); });
|
|
103
146
|
});
|
|
104
147
|
}
|
|
148
|
+
|
|
149
|
+
function quoteForCmd(s) {
|
|
150
|
+
if (s === '' || /[\s"&|<>^()]/.test(s)) {
|
|
151
|
+
return '"' + String(s).replace(/"/g, '""') + '"';
|
|
152
|
+
}
|
|
153
|
+
return String(s);
|
|
154
|
+
}
|
package/src/main.mjs
CHANGED
|
@@ -394,7 +394,7 @@ function warnIfNotProjectRoot(projectRoot) {
|
|
|
394
394
|
* @param {string} dir
|
|
395
395
|
* @returns {{ name: string, kind: 'code'|'engagement', type: 'file'|'dir' } | null}
|
|
396
396
|
*/
|
|
397
|
-
function findProjectMarker(dir) {
|
|
397
|
+
export function findProjectMarker(dir) {
|
|
398
398
|
let entries;
|
|
399
399
|
try {
|
|
400
400
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
package/src/multi-host.mjs
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
import { copyAssets } from './copy-assets.mjs';
|
|
38
38
|
import { installRunnerDeps } from './install-runner-deps.mjs';
|
|
39
39
|
import { seedConfig } from './seed-config.mjs';
|
|
40
|
+
import { findProjectMarker } from './main.mjs';
|
|
40
41
|
import {
|
|
41
42
|
resolveProfile,
|
|
42
43
|
makeIncludeFilter,
|
|
@@ -275,6 +276,40 @@ export async function runMultiHost(opts) {
|
|
|
275
276
|
results.push(installHost(t.id, { home, force: true, profile: opts.profile }));
|
|
276
277
|
}
|
|
277
278
|
}
|
|
279
|
+
|
|
280
|
+
// Unified install (v5.7.1+): when the user upgrades hosts AND is sitting in
|
|
281
|
+
// a project directory, also refresh the workspace `.kushi/` install in cwd.
|
|
282
|
+
// This collapses the historical two-command dance:
|
|
283
|
+
// npx kushi-agents@latest --all-hosts --profile full
|
|
284
|
+
// cd <repo> && npx kushi-agents@latest --profile full
|
|
285
|
+
// into a single invocation. Suppress with `--no-workspace`. Skipped on
|
|
286
|
+
// uninstall and when cwd is not a recognized project (no marker, empty dir,
|
|
287
|
+
// or just plain files).
|
|
288
|
+
if (!opts.uninstall && opts.includeWorkspace !== false) {
|
|
289
|
+
const cwd = opts.cwd || process.cwd();
|
|
290
|
+
const marker = findProjectMarker(cwd);
|
|
291
|
+
if (marker) {
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log(` Workspace install detected (${marker.kind}: ${marker.name}) — installing into ${cwd}/.kushi/ ...`);
|
|
294
|
+
try {
|
|
295
|
+
const { main } = await import('./main.mjs');
|
|
296
|
+
await main({
|
|
297
|
+
target: 'vscode',
|
|
298
|
+
yes: true,
|
|
299
|
+
force: true,
|
|
300
|
+
profile: opts.profile,
|
|
301
|
+
skipWorkiqCheck: true,
|
|
302
|
+
});
|
|
303
|
+
} catch (err) {
|
|
304
|
+
console.error(` Workspace install failed: ${err.message}`);
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
console.log('');
|
|
308
|
+
console.log(' Workspace install skipped — current directory is not a recognized project.');
|
|
309
|
+
console.log(' (cd into a repo or engagement folder and re-run, or pass --no-workspace to silence.)');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
278
313
|
console.log('');
|
|
279
314
|
console.log(` Done. ${results.length} host(s) processed.\n`);
|
|
280
315
|
return results;
|