notoken-core 1.6.0 → 2.0.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 (99) hide show
  1. package/config/chat-responses.json +767 -0
  2. package/config/concept-clusters.json +31 -0
  3. package/config/entities.json +93 -0
  4. package/config/image-prompts.json +20 -0
  5. package/config/intent-vectors.json +1 -0
  6. package/config/intents.json +4946 -83
  7. package/config/ollama-models.json +193 -0
  8. package/config/rules.json +32 -1
  9. package/dist/automation/discordPatchright.d.ts +35 -0
  10. package/dist/automation/discordPatchright.js +424 -0
  11. package/dist/automation/discordSetup.d.ts +31 -0
  12. package/dist/automation/discordSetup.js +338 -0
  13. package/dist/conversation/coreference.js +44 -4
  14. package/dist/conversation/pendingActions.d.ts +55 -0
  15. package/dist/conversation/pendingActions.js +127 -0
  16. package/dist/conversation/store.d.ts +72 -0
  17. package/dist/conversation/store.js +140 -1
  18. package/dist/conversation/topicTracker.d.ts +36 -0
  19. package/dist/conversation/topicTracker.js +141 -0
  20. package/dist/execution/ssh.d.ts +42 -1
  21. package/dist/execution/ssh.js +532 -3
  22. package/dist/handlers/executor.js +3981 -16
  23. package/dist/index.d.ts +25 -3
  24. package/dist/index.js +36 -2
  25. package/dist/nlp/batchParser.d.ts +30 -0
  26. package/dist/nlp/batchParser.js +77 -0
  27. package/dist/nlp/conceptExpansion.d.ts +54 -0
  28. package/dist/nlp/conceptExpansion.js +136 -0
  29. package/dist/nlp/conceptRouter.d.ts +49 -0
  30. package/dist/nlp/conceptRouter.js +302 -0
  31. package/dist/nlp/confidenceCalibrator.d.ts +62 -0
  32. package/dist/nlp/confidenceCalibrator.js +116 -0
  33. package/dist/nlp/correctionLearner.d.ts +45 -0
  34. package/dist/nlp/correctionLearner.js +207 -0
  35. package/dist/nlp/entitySpellCorrect.d.ts +35 -0
  36. package/dist/nlp/entitySpellCorrect.js +141 -0
  37. package/dist/nlp/knowledgeGraph.d.ts +70 -0
  38. package/dist/nlp/knowledgeGraph.js +380 -0
  39. package/dist/nlp/llmFallback.js +28 -1
  40. package/dist/nlp/multiClassifier.js +91 -6
  41. package/dist/nlp/multiIntent.d.ts +43 -0
  42. package/dist/nlp/multiIntent.js +154 -0
  43. package/dist/nlp/parseIntent.d.ts +6 -1
  44. package/dist/nlp/parseIntent.js +180 -5
  45. package/dist/nlp/ruleParser.js +315 -0
  46. package/dist/nlp/semanticSimilarity.d.ts +30 -0
  47. package/dist/nlp/semanticSimilarity.js +174 -0
  48. package/dist/nlp/vocabularyBuilder.d.ts +43 -0
  49. package/dist/nlp/vocabularyBuilder.js +224 -0
  50. package/dist/nlp/wikidata.d.ts +49 -0
  51. package/dist/nlp/wikidata.js +228 -0
  52. package/dist/policy/confirm.d.ts +10 -0
  53. package/dist/policy/confirm.js +39 -0
  54. package/dist/policy/safety.js +6 -4
  55. package/dist/utils/aliases.d.ts +5 -0
  56. package/dist/utils/aliases.js +39 -0
  57. package/dist/utils/analysis.js +71 -15
  58. package/dist/utils/browser.d.ts +64 -0
  59. package/dist/utils/browser.js +364 -0
  60. package/dist/utils/commandHistory.d.ts +20 -0
  61. package/dist/utils/commandHistory.js +108 -0
  62. package/dist/utils/completer.d.ts +17 -0
  63. package/dist/utils/completer.js +79 -0
  64. package/dist/utils/config.js +32 -2
  65. package/dist/utils/dbQuery.d.ts +25 -0
  66. package/dist/utils/dbQuery.js +248 -0
  67. package/dist/utils/discordDiag.d.ts +35 -0
  68. package/dist/utils/discordDiag.js +826 -0
  69. package/dist/utils/diskCleanup.d.ts +36 -0
  70. package/dist/utils/diskCleanup.js +775 -0
  71. package/dist/utils/entityResolver.d.ts +107 -0
  72. package/dist/utils/entityResolver.js +468 -0
  73. package/dist/utils/imageGen.d.ts +92 -0
  74. package/dist/utils/imageGen.js +2031 -0
  75. package/dist/utils/installTracker.d.ts +57 -0
  76. package/dist/utils/installTracker.js +160 -0
  77. package/dist/utils/multiExec.d.ts +21 -0
  78. package/dist/utils/multiExec.js +141 -0
  79. package/dist/utils/openclawDiag.d.ts +29 -0
  80. package/dist/utils/openclawDiag.js +1035 -0
  81. package/dist/utils/output.js +4 -0
  82. package/dist/utils/platform.js +2 -1
  83. package/dist/utils/progressReporter.d.ts +50 -0
  84. package/dist/utils/progressReporter.js +58 -0
  85. package/dist/utils/projectDetect.d.ts +44 -0
  86. package/dist/utils/projectDetect.js +319 -0
  87. package/dist/utils/projectScanner.d.ts +44 -0
  88. package/dist/utils/projectScanner.js +312 -0
  89. package/dist/utils/shellCompat.d.ts +78 -0
  90. package/dist/utils/shellCompat.js +186 -0
  91. package/dist/utils/smartArchive.d.ts +16 -0
  92. package/dist/utils/smartArchive.js +172 -0
  93. package/dist/utils/smartRetry.d.ts +26 -0
  94. package/dist/utils/smartRetry.js +114 -0
  95. package/dist/utils/updater.d.ts +1 -0
  96. package/dist/utils/updater.js +1 -1
  97. package/dist/utils/version.d.ts +20 -0
  98. package/dist/utils/version.js +212 -0
  99. package/package.json +6 -3
@@ -3,6 +3,10 @@ export function formatParsedCommand(cmd) {
3
3
  const { intent } = cmd;
4
4
  lines.push(`Intent: ${intent.intent}`);
5
5
  lines.push(`Confidence: ${(intent.confidence * 100).toFixed(0)}%`);
6
+ // Show Computer: Local/Remote so user knows where the command runs
7
+ const env = intent.fields.environment ?? "local";
8
+ const isLocal = env === "local" || env === "localhost" || env === "dev";
9
+ lines.push(`Computer: ${isLocal ? "\x1b[32mLocal\x1b[0m" : `\x1b[36mRemote (${env})\x1b[0m`}`);
6
10
  const entries = Object.entries(intent.fields).filter(([, v]) => v !== undefined);
7
11
  if (entries.length > 0) {
8
12
  lines.push("Fields:");
@@ -245,7 +245,8 @@ function tryExec(cmd) {
245
245
  }
246
246
  }
247
247
  function commandExists(cmd) {
248
- return tryExec(`command -v ${cmd}`) !== null;
248
+ const checkCmd = process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`;
249
+ return tryExec(checkCmd) !== null;
249
250
  }
250
251
  function extractField(content, key) {
251
252
  const match = content.match(new RegExp(`^${key}="?([^"\\n]*)"?`, "m"));
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Progress Reporter — emit live progress events from executors.
3
+ *
4
+ * Allows handlers to report step-by-step progress that gets
5
+ * displayed in real-time to the user, even during background execution.
6
+ *
7
+ * Usage in a handler:
8
+ * reportProgress("Step 1/3: Checking ports...");
9
+ * reportProgress("Step 2/3: Scanning connections...");
10
+ * reportProgress("Step 3/3: Generating report...", 100);
11
+ */
12
+ import { EventEmitter } from "node:events";
13
+ export interface ProgressEvent {
14
+ taskId: number;
15
+ intent: string;
16
+ message: string;
17
+ percent?: number;
18
+ step?: number;
19
+ totalSteps?: number;
20
+ timestamp: number;
21
+ }
22
+ declare class ProgressReporterClass extends EventEmitter {
23
+ private currentTaskId;
24
+ private currentIntent;
25
+ /** Set the current task context for progress reports. */
26
+ setContext(taskId: number, intent: string): void;
27
+ /** Report progress from inside a handler. */
28
+ report(message: string, opts?: {
29
+ percent?: number;
30
+ step?: number;
31
+ totalSteps?: number;
32
+ }): void;
33
+ /** Convenience: report a numbered step. */
34
+ step(current: number, total: number, message: string): void;
35
+ /** Report completion. */
36
+ done(message?: string): void;
37
+ }
38
+ /** Singleton progress reporter. */
39
+ export declare const progressReporter: ProgressReporterClass;
40
+ /**
41
+ * Convenience function for handlers to report progress.
42
+ * Import this in executor.ts handlers.
43
+ */
44
+ export declare function reportProgress(message: string, opts?: {
45
+ percent?: number;
46
+ step?: number;
47
+ totalSteps?: number;
48
+ }): void;
49
+ export declare function reportStep(current: number, total: number, message: string): void;
50
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Progress Reporter — emit live progress events from executors.
3
+ *
4
+ * Allows handlers to report step-by-step progress that gets
5
+ * displayed in real-time to the user, even during background execution.
6
+ *
7
+ * Usage in a handler:
8
+ * reportProgress("Step 1/3: Checking ports...");
9
+ * reportProgress("Step 2/3: Scanning connections...");
10
+ * reportProgress("Step 3/3: Generating report...", 100);
11
+ */
12
+ import { EventEmitter } from "node:events";
13
+ class ProgressReporterClass extends EventEmitter {
14
+ currentTaskId = 0;
15
+ currentIntent = "";
16
+ /** Set the current task context for progress reports. */
17
+ setContext(taskId, intent) {
18
+ this.currentTaskId = taskId;
19
+ this.currentIntent = intent;
20
+ }
21
+ /** Report progress from inside a handler. */
22
+ report(message, opts) {
23
+ const event = {
24
+ taskId: this.currentTaskId,
25
+ intent: this.currentIntent,
26
+ message,
27
+ percent: opts?.percent,
28
+ step: opts?.step,
29
+ totalSteps: opts?.totalSteps,
30
+ timestamp: Date.now(),
31
+ };
32
+ this.emit("progress", event);
33
+ }
34
+ /** Convenience: report a numbered step. */
35
+ step(current, total, message) {
36
+ this.report(`Step ${current}/${total}: ${message}`, {
37
+ step: current,
38
+ totalSteps: total,
39
+ percent: Math.round((current / total) * 100),
40
+ });
41
+ }
42
+ /** Report completion. */
43
+ done(message) {
44
+ this.report(message ?? "Done", { percent: 100 });
45
+ }
46
+ }
47
+ /** Singleton progress reporter. */
48
+ export const progressReporter = new ProgressReporterClass();
49
+ /**
50
+ * Convenience function for handlers to report progress.
51
+ * Import this in executor.ts handlers.
52
+ */
53
+ export function reportProgress(message, opts) {
54
+ progressReporter.report(message, opts);
55
+ }
56
+ export function reportStep(current, total, message) {
57
+ progressReporter.step(current, total, message);
58
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Smart project detection and package manager resolution.
3
+ *
4
+ * Detects the project type in a directory and returns the correct
5
+ * install/update/build commands for that project's ecosystem.
6
+ */
7
+ export interface DetectedProject {
8
+ type: string;
9
+ packageManager: string;
10
+ installCmd: string;
11
+ updateCmd: string;
12
+ buildCmd?: string;
13
+ lockFile?: string;
14
+ configFile: string;
15
+ }
16
+ /**
17
+ * Detect all projects in a directory.
18
+ * Returns them in priority order (most specific first).
19
+ */
20
+ export declare function detectProjects(dir?: string): DetectedProject[];
21
+ /** Format project detection results for display. */
22
+ export declare function formatProjectDetection(projects: DetectedProject[]): string;
23
+ /** Get the install command for the primary project in cwd. */
24
+ export declare function getProjectInstallCmd(dir?: string): string | null;
25
+ /** Get the update command for the primary project in cwd. */
26
+ export declare function getProjectUpdateCmd(dir?: string): string | null;
27
+ export interface ProjectScripts {
28
+ packageManager: string;
29
+ scripts: Record<string, string>;
30
+ dependencies: string[];
31
+ devDependencies: string[];
32
+ name: string;
33
+ version: string;
34
+ }
35
+ /** Read package.json or composer.json and extract scripts, deps, and metadata. */
36
+ export declare function readProjectConfig(dir?: string): ProjectScripts | null;
37
+ /** Format package.json scripts for display. */
38
+ export declare function formatPackageScripts(info: ProjectScripts): string;
39
+ /** Get the run command for a named script. */
40
+ export declare function getScriptRunCmd(scriptName: string, dir?: string): string | null;
41
+ /**
42
+ * Get the system-level update command based on the detected platform.
43
+ */
44
+ export declare function getSystemUpdateCmd(): string;
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Smart project detection and package manager resolution.
3
+ *
4
+ * Detects the project type in a directory and returns the correct
5
+ * install/update/build commands for that project's ecosystem.
6
+ */
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ import { detectLocalPlatform } from "./platform.js";
10
+ const c = {
11
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
12
+ green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", magenta: "\x1b[35m",
13
+ };
14
+ /** Check which files exist in a directory. */
15
+ function has(dir, ...files) {
16
+ return files.some((f) => existsSync(resolve(dir, f)));
17
+ }
18
+ /**
19
+ * Detect all projects in a directory.
20
+ * Returns them in priority order (most specific first).
21
+ */
22
+ export function detectProjects(dir = process.cwd()) {
23
+ const projects = [];
24
+ // ── Node.js ecosystem ──
25
+ if (has(dir, "package.json")) {
26
+ // Detect which package manager
27
+ let pm = "npm";
28
+ let installCmd = "npm install";
29
+ let updateCmd = "npm update";
30
+ let lockFile;
31
+ if (has(dir, "bun.lockb", "bun.lock")) {
32
+ pm = "bun";
33
+ installCmd = "bun install";
34
+ updateCmd = "bun update";
35
+ lockFile = "bun.lockb";
36
+ }
37
+ else if (has(dir, "pnpm-lock.yaml")) {
38
+ pm = "pnpm";
39
+ installCmd = "pnpm install";
40
+ updateCmd = "pnpm update";
41
+ lockFile = "pnpm-lock.yaml";
42
+ }
43
+ else if (has(dir, "yarn.lock")) {
44
+ pm = "yarn";
45
+ installCmd = "yarn install";
46
+ updateCmd = "yarn upgrade";
47
+ lockFile = "yarn.lock";
48
+ }
49
+ else if (has(dir, "package-lock.json")) {
50
+ lockFile = "package-lock.json";
51
+ }
52
+ // Detect framework for type label
53
+ let type = "Node.js";
54
+ if (has(dir, "next.config.js", "next.config.ts", "next.config.mjs"))
55
+ type = "Next.js";
56
+ else if (has(dir, "nuxt.config.ts", "nuxt.config.js"))
57
+ type = "Nuxt.js";
58
+ else if (has(dir, "svelte.config.js"))
59
+ type = "SvelteKit";
60
+ else if (has(dir, "remix.config.js"))
61
+ type = "Remix";
62
+ else if (has(dir, "astro.config.mjs"))
63
+ type = "Astro";
64
+ else if (has(dir, "angular.json"))
65
+ type = "Angular";
66
+ else if (has(dir, "vue.config.js"))
67
+ type = "Vue.js";
68
+ else if (has(dir, "tsconfig.json"))
69
+ type = "TypeScript/Node.js";
70
+ const buildCmd = has(dir, "tsconfig.json") ? `${pm} run build` : undefined;
71
+ projects.push({ type, packageManager: pm, installCmd, updateCmd, buildCmd, lockFile, configFile: "package.json" });
72
+ }
73
+ // ── Python ecosystem ──
74
+ if (has(dir, "pyproject.toml")) {
75
+ if (has(dir, "poetry.lock")) {
76
+ projects.push({ type: "Python (Poetry)", packageManager: "poetry", installCmd: "poetry install", updateCmd: "poetry update", configFile: "pyproject.toml", lockFile: "poetry.lock" });
77
+ }
78
+ else if (has(dir, "uv.lock")) {
79
+ projects.push({ type: "Python (uv)", packageManager: "uv", installCmd: "uv sync", updateCmd: "uv lock --upgrade && uv sync", configFile: "pyproject.toml", lockFile: "uv.lock" });
80
+ }
81
+ else if (has(dir, "pdm.lock")) {
82
+ projects.push({ type: "Python (pdm)", packageManager: "pdm", installCmd: "pdm install", updateCmd: "pdm update", configFile: "pyproject.toml", lockFile: "pdm.lock" });
83
+ }
84
+ else {
85
+ projects.push({ type: "Python", packageManager: "pip", installCmd: "pip install -e .", updateCmd: "pip install -e . --upgrade", configFile: "pyproject.toml" });
86
+ }
87
+ }
88
+ else if (has(dir, "Pipfile")) {
89
+ projects.push({ type: "Python (Pipenv)", packageManager: "pipenv", installCmd: "pipenv install", updateCmd: "pipenv update", configFile: "Pipfile", lockFile: "Pipfile.lock" });
90
+ }
91
+ else if (has(dir, "requirements.txt")) {
92
+ projects.push({ type: "Python", packageManager: "pip", installCmd: "pip install -r requirements.txt", updateCmd: "pip install -r requirements.txt --upgrade", configFile: "requirements.txt" });
93
+ }
94
+ // ── Go ──
95
+ if (has(dir, "go.mod")) {
96
+ projects.push({ type: "Go", packageManager: "go", installCmd: "go mod download", updateCmd: "go get -u ./... && go mod tidy", configFile: "go.mod", lockFile: "go.sum" });
97
+ }
98
+ // ── Rust ──
99
+ if (has(dir, "Cargo.toml")) {
100
+ projects.push({ type: "Rust", packageManager: "cargo", installCmd: "cargo build", updateCmd: "cargo update", buildCmd: "cargo build --release", configFile: "Cargo.toml", lockFile: "Cargo.lock" });
101
+ }
102
+ // ── Ruby ──
103
+ if (has(dir, "Gemfile")) {
104
+ projects.push({ type: "Ruby", packageManager: "bundler", installCmd: "bundle install", updateCmd: "bundle update", configFile: "Gemfile", lockFile: "Gemfile.lock" });
105
+ }
106
+ // ── PHP ──
107
+ if (has(dir, "composer.json")) {
108
+ projects.push({ type: "PHP (Composer)", packageManager: "composer", installCmd: "composer install", updateCmd: "composer update", configFile: "composer.json", lockFile: "composer.lock" });
109
+ }
110
+ // ── .NET ──
111
+ const csproj = ["*.csproj", "*.fsproj"].some(() => {
112
+ try {
113
+ const { execSync } = require("node:child_process");
114
+ return execSync(`ls ${dir}/*.csproj ${dir}/*.fsproj 2>/dev/null`, { encoding: "utf-8" }).trim().length > 0;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ });
120
+ if (has(dir, "*.sln") || csproj) {
121
+ projects.push({ type: ".NET", packageManager: "dotnet", installCmd: "dotnet restore", updateCmd: "dotnet restore", buildCmd: "dotnet build", configFile: "*.csproj" });
122
+ }
123
+ // ── Java ──
124
+ if (has(dir, "pom.xml")) {
125
+ projects.push({ type: "Java (Maven)", packageManager: "maven", installCmd: "mvn install", updateCmd: "mvn versions:use-latest-versions", buildCmd: "mvn package", configFile: "pom.xml" });
126
+ }
127
+ else if (has(dir, "build.gradle", "build.gradle.kts")) {
128
+ projects.push({ type: "Java (Gradle)", packageManager: "gradle", installCmd: "./gradlew build", updateCmd: "./gradlew dependencies --write-locks", buildCmd: "./gradlew build", configFile: "build.gradle" });
129
+ }
130
+ return projects;
131
+ }
132
+ /** Format project detection results for display. */
133
+ export function formatProjectDetection(projects) {
134
+ if (projects.length === 0) {
135
+ return `${c.dim}No recognized projects in current directory.${c.reset}`;
136
+ }
137
+ const lines = [];
138
+ lines.push(`\n${c.bold}${c.cyan}── Detected Projects ──${c.reset}\n`);
139
+ for (const p of projects) {
140
+ const lockLabel = p.lockFile && existsSync(resolve(process.cwd(), p.lockFile))
141
+ ? `${c.green}✓${c.reset} ${p.lockFile}`
142
+ : `${c.yellow}⚠ no lock file${c.reset}`;
143
+ lines.push(` ${c.magenta}${c.bold}${p.type}${c.reset} ${c.dim}(${p.packageManager})${c.reset} ${lockLabel}`);
144
+ lines.push(` Install: ${c.cyan}${p.installCmd}${c.reset}`);
145
+ lines.push(` Update: ${c.cyan}${p.updateCmd}${c.reset}`);
146
+ if (p.buildCmd) {
147
+ lines.push(` Build: ${c.cyan}${p.buildCmd}${c.reset}`);
148
+ }
149
+ lines.push("");
150
+ }
151
+ return lines.join("\n");
152
+ }
153
+ /** Get the install command for the primary project in cwd. */
154
+ export function getProjectInstallCmd(dir) {
155
+ const projects = detectProjects(dir);
156
+ return projects.length > 0 ? projects[0].installCmd : null;
157
+ }
158
+ /** Get the update command for the primary project in cwd. */
159
+ export function getProjectUpdateCmd(dir) {
160
+ const projects = detectProjects(dir);
161
+ return projects.length > 0 ? projects[0].updateCmd : null;
162
+ }
163
+ /** Read package.json or composer.json and extract scripts, deps, and metadata. */
164
+ export function readProjectConfig(dir = process.cwd()) {
165
+ // Try package.json first (Node.js)
166
+ if (existsSync(resolve(dir, "package.json"))) {
167
+ return readPackageJson(dir);
168
+ }
169
+ // Try composer.json (PHP)
170
+ if (existsSync(resolve(dir, "composer.json"))) {
171
+ return readComposerJson(dir);
172
+ }
173
+ // Try pyproject.toml scripts
174
+ if (existsSync(resolve(dir, "pyproject.toml"))) {
175
+ return readPyprojectScripts(dir);
176
+ }
177
+ return null;
178
+ }
179
+ function readPackageJson(dir) {
180
+ try {
181
+ const pkg = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf-8"));
182
+ let pm = "npm";
183
+ if (existsSync(resolve(dir, "bun.lockb")) || existsSync(resolve(dir, "bun.lock")))
184
+ pm = "bun";
185
+ else if (existsSync(resolve(dir, "pnpm-lock.yaml")))
186
+ pm = "pnpm";
187
+ else if (existsSync(resolve(dir, "yarn.lock")))
188
+ pm = "yarn";
189
+ return {
190
+ packageManager: pm,
191
+ scripts: pkg.scripts ?? {},
192
+ dependencies: Object.keys(pkg.dependencies ?? {}),
193
+ devDependencies: Object.keys(pkg.devDependencies ?? {}),
194
+ name: pkg.name ?? "unknown",
195
+ version: pkg.version ?? "0.0.0",
196
+ };
197
+ }
198
+ catch {
199
+ return null;
200
+ }
201
+ }
202
+ function readComposerJson(dir) {
203
+ try {
204
+ const composer = JSON.parse(readFileSync(resolve(dir, "composer.json"), "utf-8"));
205
+ return {
206
+ packageManager: "composer",
207
+ scripts: composer.scripts ?? {},
208
+ dependencies: Object.keys(composer.require ?? {}),
209
+ devDependencies: Object.keys(composer["require-dev"] ?? {}),
210
+ name: composer.name ?? "unknown",
211
+ version: composer.version ?? "0.0.0",
212
+ };
213
+ }
214
+ catch {
215
+ return null;
216
+ }
217
+ }
218
+ function readPyprojectScripts(dir) {
219
+ try {
220
+ const content = readFileSync(resolve(dir, "pyproject.toml"), "utf-8");
221
+ // Basic TOML script extraction — look for [tool.poetry.scripts] or [project.scripts]
222
+ const scripts = {};
223
+ const scriptMatch = content.match(/\[(?:tool\.poetry\.scripts|project\.scripts)\]\n([\s\S]*?)(?:\n\[|$)/);
224
+ if (scriptMatch) {
225
+ for (const line of scriptMatch[1].split("\n")) {
226
+ const kv = line.match(/^(\w[\w-]*)\s*=\s*"([^"]+)"/);
227
+ if (kv)
228
+ scripts[kv[1]] = kv[2];
229
+ }
230
+ }
231
+ const pm = existsSync(resolve(dir, "poetry.lock")) ? "poetry"
232
+ : existsSync(resolve(dir, "uv.lock")) ? "uv"
233
+ : "pip";
234
+ // Extract project name
235
+ const nameMatch = content.match(/name\s*=\s*"([^"]+)"/);
236
+ const versionMatch = content.match(/version\s*=\s*"([^"]+)"/);
237
+ return {
238
+ packageManager: pm,
239
+ scripts,
240
+ dependencies: [],
241
+ devDependencies: [],
242
+ name: nameMatch?.[1] ?? "unknown",
243
+ version: versionMatch?.[1] ?? "0.0.0",
244
+ };
245
+ }
246
+ catch {
247
+ return null;
248
+ }
249
+ }
250
+ /** Format package.json scripts for display. */
251
+ export function formatPackageScripts(info) {
252
+ const lines = [];
253
+ const scripts = Object.entries(info.scripts);
254
+ lines.push(`\n${c.bold}${c.cyan}── ${info.name}@${info.version} ──${c.reset}\n`);
255
+ lines.push(` ${c.bold}Package manager:${c.reset} ${info.packageManager}`);
256
+ if (scripts.length > 0) {
257
+ lines.push(`\n ${c.bold}Available scripts:${c.reset}`);
258
+ for (const [name, cmd] of scripts) {
259
+ const runCmd = info.packageManager === "npm" ? `npm run ${name}` : `${info.packageManager} ${name}`;
260
+ lines.push(` ${c.cyan}${runCmd.padEnd(30)}${c.reset} ${c.dim}→ ${cmd}${c.reset}`);
261
+ }
262
+ }
263
+ else {
264
+ lines.push(` ${c.dim}No scripts defined.${c.reset}`);
265
+ }
266
+ if (info.dependencies.length > 0) {
267
+ lines.push(`\n ${c.bold}Dependencies:${c.reset} ${c.dim}(${info.dependencies.length})${c.reset}`);
268
+ const shown = info.dependencies.slice(0, 10);
269
+ lines.push(` ${c.dim}${shown.join(", ")}${info.dependencies.length > 10 ? ` +${info.dependencies.length - 10} more` : ""}${c.reset}`);
270
+ }
271
+ if (info.devDependencies.length > 0) {
272
+ lines.push(` ${c.bold}Dev dependencies:${c.reset} ${c.dim}(${info.devDependencies.length})${c.reset}`);
273
+ const shown = info.devDependencies.slice(0, 10);
274
+ lines.push(` ${c.dim}${shown.join(", ")}${info.devDependencies.length > 10 ? ` +${info.devDependencies.length - 10} more` : ""}${c.reset}`);
275
+ }
276
+ return lines.join("\n");
277
+ }
278
+ /** Build the run command prefix for a package manager. */
279
+ function runPrefix(pm, script) {
280
+ switch (pm) {
281
+ case "npm": return `npm run ${script}`;
282
+ case "composer": return `composer ${script}`;
283
+ case "poetry": return `poetry run ${script}`;
284
+ case "uv": return `uv run ${script}`;
285
+ default: return `${pm} ${script}`;
286
+ }
287
+ }
288
+ /** Get the run command for a named script. */
289
+ export function getScriptRunCmd(scriptName, dir) {
290
+ const info = readProjectConfig(dir);
291
+ if (!info)
292
+ return null;
293
+ // Exact match
294
+ if (info.scripts[scriptName]) {
295
+ return runPrefix(info.packageManager, scriptName);
296
+ }
297
+ // Fuzzy match — "dev" matches "dev", "start:dev", etc.
298
+ const fuzzy = Object.keys(info.scripts).find((s) => s === scriptName || s.includes(scriptName) || scriptName.includes(s));
299
+ if (fuzzy) {
300
+ return runPrefix(info.packageManager, fuzzy);
301
+ }
302
+ return null;
303
+ }
304
+ /**
305
+ * Get the system-level update command based on the detected platform.
306
+ */
307
+ export function getSystemUpdateCmd() {
308
+ const plat = detectLocalPlatform();
309
+ switch (plat.packageManager) {
310
+ case "apt": return "sudo apt-get update && sudo apt-get upgrade -y";
311
+ case "dnf": return "sudo dnf upgrade -y";
312
+ case "yum": return "sudo yum update -y";
313
+ case "pacman": return "sudo pacman -Syu --noconfirm";
314
+ case "apk": return "sudo apk update && sudo apk upgrade";
315
+ case "brew": return "brew update && brew upgrade";
316
+ case "choco": return "choco upgrade all -y";
317
+ default: return "echo 'Unknown package manager — update manually'";
318
+ }
319
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Project scanner.
3
+ *
4
+ * Scans a directory tree to find and describe software projects.
5
+ * Also lists directory contents with rich output.
6
+ *
7
+ * Used by:
8
+ * "what projects do I have here?"
9
+ * "where are my files?"
10
+ * "what's in this folder?"
11
+ */
12
+ export interface ProjectInfo {
13
+ path: string;
14
+ name: string;
15
+ type: string;
16
+ description?: string;
17
+ version?: string;
18
+ deps?: number;
19
+ scripts?: string[];
20
+ indicators: string[];
21
+ }
22
+ export interface DirSummary {
23
+ path: string;
24
+ totalFiles: number;
25
+ totalDirs: number;
26
+ projects: ProjectInfo[];
27
+ fileCounts: Record<string, number>;
28
+ largestFiles: Array<{
29
+ name: string;
30
+ size: number;
31
+ }>;
32
+ notable: string[];
33
+ totalSize: number;
34
+ }
35
+ /**
36
+ * Scan for projects in a directory (recursive, up to maxDepth).
37
+ */
38
+ export declare function scanProjects(rootPath: string, maxDepth?: number): ProjectInfo[];
39
+ /**
40
+ * Summarize a directory — files, projects, sizes.
41
+ */
42
+ export declare function summarizeDirectory(dirPath: string): DirSummary;
43
+ export declare function formatProjectList(projects: ProjectInfo[], rootPath: string): string;
44
+ export declare function formatDirSummary(summary: DirSummary): string;