kushi-agents 3.14.0 → 3.15.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.
- package/bin/cli.mjs +3 -0
- package/package.json +1 -1
- package/src/constants.mjs +40 -0
- package/src/main.mjs +91 -8
package/bin/cli.mjs
CHANGED
|
@@ -25,6 +25,8 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
25
25
|
Options:
|
|
26
26
|
--dest <path> Override destination (relative for vscode, absolute for clawpilot)
|
|
27
27
|
--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)
|
|
28
30
|
--no-settings Skip .vscode/settings.json update (vscode target only)
|
|
29
31
|
--no-instructions Skip .github/copilot-instructions.md merge (vscode target only)
|
|
30
32
|
--help, -h Show this help
|
|
@@ -55,6 +57,7 @@ if (args.includes('--clawpilot')) {
|
|
|
55
57
|
const options = {
|
|
56
58
|
dest: getFlag('--dest'),
|
|
57
59
|
force: args.includes('--force'),
|
|
60
|
+
yes: args.includes('--yes') || args.includes('-y'),
|
|
58
61
|
noSettings: args.includes('--no-settings'),
|
|
59
62
|
noInstructions: args.includes('--no-instructions'),
|
|
60
63
|
target,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.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": {
|
package/src/constants.mjs
CHANGED
|
@@ -40,6 +40,46 @@ export const PROJECT_GITHUB_EXCLUDED_FILES = [
|
|
|
40
40
|
'config/m365-mutable.json'
|
|
41
41
|
];
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Files / directories that indicate the cwd is a sane install target.
|
|
45
|
+
*
|
|
46
|
+
* Two flavors:
|
|
47
|
+
* - 'code' — a software project root (any common ecosystem).
|
|
48
|
+
* - 'engagement' — a Kushi engagement folder (Evidence/State/integrations live here,
|
|
49
|
+
* or this dir already has a prior Kushi install / reference pack).
|
|
50
|
+
*
|
|
51
|
+
* Either flavor suppresses the "this directory does not look like a project root"
|
|
52
|
+
* warning. If neither is present and the directory is non-empty, the installer
|
|
53
|
+
* shows an actionable warning. If the directory is empty, it shows a friendly
|
|
54
|
+
* INFO message instead — empty dirs are the normal "starting fresh" case.
|
|
55
|
+
*/
|
|
56
|
+
export const PROJECT_MARKERS = [
|
|
57
|
+
// ── code project roots ──────────────────────────────────────────────────
|
|
58
|
+
{ name: 'package.json', kind: 'code', type: 'file' },
|
|
59
|
+
{ name: '.git', kind: 'code', type: 'dir' },
|
|
60
|
+
{ name: 'pyproject.toml', kind: 'code', type: 'file' },
|
|
61
|
+
{ name: 'requirements.txt', kind: 'code', type: 'file' },
|
|
62
|
+
{ name: 'Cargo.toml', kind: 'code', type: 'file' },
|
|
63
|
+
{ name: 'go.mod', kind: 'code', type: 'file' },
|
|
64
|
+
{ name: 'pom.xml', kind: 'code', type: 'file' },
|
|
65
|
+
{ name: 'build.gradle', kind: 'code', type: 'file' },
|
|
66
|
+
{ name: 'build.gradle.kts', kind: 'code', type: 'file' },
|
|
67
|
+
{ name: 'composer.json', kind: 'code', type: 'file' },
|
|
68
|
+
{ name: 'Gemfile', kind: 'code', type: 'file' },
|
|
69
|
+
{ name: 'mix.exs', kind: 'code', type: 'file' },
|
|
70
|
+
// ── .NET (glob, not literal name) ───────────────────────────────────────
|
|
71
|
+
{ glob: /\.csproj$/i, kind: 'code', type: 'file' },
|
|
72
|
+
{ glob: /\.fsproj$/i, kind: 'code', type: 'file' },
|
|
73
|
+
{ glob: /\.sln$/i, kind: 'code', type: 'file' },
|
|
74
|
+
// ── Kushi engagement folders ────────────────────────────────────────────
|
|
75
|
+
{ name: 'Evidence', kind: 'engagement', type: 'dir' },
|
|
76
|
+
{ name: 'State', kind: 'engagement', type: 'dir' },
|
|
77
|
+
{ name: 'integrations.yml', kind: 'engagement', type: 'file' },
|
|
78
|
+
{ name: 'integrations.yaml', kind: 'engagement', type: 'file' },
|
|
79
|
+
{ name: '.kushi', kind: 'engagement', type: 'dir' },
|
|
80
|
+
{ name: '.kushi-reference', kind: 'engagement', type: 'dir' },
|
|
81
|
+
];
|
|
82
|
+
|
|
43
83
|
/** VS Code settings keys → the .kushi subdirectory they point at. */
|
|
44
84
|
export const SETTINGS_MAP = {
|
|
45
85
|
'chat.agentFilesLocations': 'agents',
|
package/src/main.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CLAWPILOT_AGENT_SOURCE,
|
|
11
11
|
CLAWPILOT_SKILL_DEST,
|
|
12
12
|
PLUGIN_SOURCE_DIR,
|
|
13
|
+
PROJECT_MARKERS,
|
|
13
14
|
} from './constants.mjs';
|
|
14
15
|
import { promptForDestination } from './prompt.mjs';
|
|
15
16
|
import { copyAssets, copyProjectFiles } from './copy-assets.mjs';
|
|
@@ -82,14 +83,8 @@ export async function main(options = {}) {
|
|
|
82
83
|
*/
|
|
83
84
|
async function installVscode(options, resolved, version) {
|
|
84
85
|
const projectRoot = process.cwd();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fs.existsSync(path.join(projectRoot, '.git'));
|
|
88
|
-
|
|
89
|
-
if (!hasProjectMarker) {
|
|
90
|
-
console.warn(
|
|
91
|
-
' WARN: This directory does not look like a project root (no package.json or .git found). Proceeding anyway.\n',
|
|
92
|
-
);
|
|
86
|
+
if (!options.yes) {
|
|
87
|
+
warnIfNotProjectRoot(projectRoot);
|
|
93
88
|
}
|
|
94
89
|
|
|
95
90
|
let dest;
|
|
@@ -112,6 +107,8 @@ async function installVscode(options, resolved, version) {
|
|
|
112
107
|
console.error('\n Destination must be within the current project.\n');
|
|
113
108
|
process.exit(1);
|
|
114
109
|
}
|
|
110
|
+
} else if (options.yes) {
|
|
111
|
+
dest = DEFAULT_DEST;
|
|
115
112
|
} else {
|
|
116
113
|
dest = await promptForDestination(DEFAULT_DEST);
|
|
117
114
|
}
|
|
@@ -276,4 +273,90 @@ async function confirmOverwriteIfExists(fullDest, displayDest, force) {
|
|
|
276
273
|
console.log('\n Cancelled.\n');
|
|
277
274
|
process.exit(0);
|
|
278
275
|
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Detect whether the cwd looks like a sane install target and, if not, print
|
|
280
|
+
* an actionable message. Three cases:
|
|
281
|
+
*
|
|
282
|
+
* 1. A code marker (package.json, .git, pyproject.toml, *.csproj, ...) or an
|
|
283
|
+
* engagement marker (Evidence/, State/, integrations.yml, .kushi/, ...)
|
|
284
|
+
* is present → silent, proceed.
|
|
285
|
+
* 2. The directory is empty → friendly INFO ("starting fresh — Kushi will
|
|
286
|
+
* create a new install here"). Empty dirs are the normal first-run case
|
|
287
|
+
* and should not produce a WARN.
|
|
288
|
+
* 3. The directory has files but no recognized marker → WARN with the
|
|
289
|
+
* actual fix (run `git init`, cd to an engagement folder, or pass --yes
|
|
290
|
+
* to skip this check in scripted use).
|
|
291
|
+
*
|
|
292
|
+
* @param {string} projectRoot
|
|
293
|
+
*/
|
|
294
|
+
function warnIfNotProjectRoot(projectRoot) {
|
|
295
|
+
const marker = findProjectMarker(projectRoot);
|
|
296
|
+
if (marker) return;
|
|
297
|
+
|
|
298
|
+
let entries;
|
|
299
|
+
try {
|
|
300
|
+
entries = fs
|
|
301
|
+
.readdirSync(projectRoot)
|
|
302
|
+
.filter((n) => n !== '.DS_Store' && n !== 'Thumbs.db');
|
|
303
|
+
} catch {
|
|
304
|
+
entries = [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (entries.length === 0) {
|
|
308
|
+
console.log(
|
|
309
|
+
' INFO: Empty directory detected — Kushi will create a fresh install here.\n',
|
|
310
|
+
);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.warn(
|
|
315
|
+
' WARN: This directory has files but no recognized project marker',
|
|
316
|
+
);
|
|
317
|
+
console.warn(
|
|
318
|
+
' (e.g. package.json, .git, pyproject.toml, *.csproj, or a Kushi',
|
|
319
|
+
);
|
|
320
|
+
console.warn(
|
|
321
|
+
' engagement folder containing Evidence/, State/, or integrations.yml).',
|
|
322
|
+
);
|
|
323
|
+
console.warn('');
|
|
324
|
+
console.warn(' If this is intentional, press Enter to accept the default path.');
|
|
325
|
+
console.warn(' Otherwise, Ctrl+C and either:');
|
|
326
|
+
console.warn(' • run `git init` first (for a new code project), or');
|
|
327
|
+
console.warn(' • cd into your engagement folder (where Evidence/ lives), or');
|
|
328
|
+
console.warn(' • re-run with `--yes` to suppress this check in scripted use.\n');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Return the first matching project/engagement marker in `dir`, or null.
|
|
333
|
+
* @param {string} dir
|
|
334
|
+
* @returns {{ name: string, kind: 'code'|'engagement', type: 'file'|'dir' } | null}
|
|
335
|
+
*/
|
|
336
|
+
function findProjectMarker(dir) {
|
|
337
|
+
let entries;
|
|
338
|
+
try {
|
|
339
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
340
|
+
} catch {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
for (const marker of PROJECT_MARKERS) {
|
|
345
|
+
if (marker.glob) {
|
|
346
|
+
const hit = entries.find(
|
|
347
|
+
(e) => e.isFile() && marker.glob.test(e.name),
|
|
348
|
+
);
|
|
349
|
+
if (hit) return { name: hit.name, kind: marker.kind, type: 'file' };
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const hit = entries.find((e) => e.name === marker.name);
|
|
353
|
+
if (!hit) continue;
|
|
354
|
+
if (marker.type === 'dir' && hit.isDirectory()) {
|
|
355
|
+
return { name: marker.name, kind: marker.kind, type: 'dir' };
|
|
356
|
+
}
|
|
357
|
+
if (marker.type === 'file' && hit.isFile()) {
|
|
358
|
+
return { name: marker.name, kind: marker.kind, type: 'file' };
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
279
362
|
}
|