skyloom 1.13.6 → 1.13.8
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/.github/workflows/ci.yml +36 -36
- package/README.md +220 -159
- package/config/providers.yaml +39 -39
- package/config/skills/api_integrator/SKILL.md +15 -15
- package/config/skills/arch_designer/SKILL.md +13 -13
- package/config/skills/ci_cd_manager/SKILL.md +14 -14
- package/config/skills/code_analysis/SKILL.md +13 -13
- package/config/skills/code_generator/SKILL.md +12 -12
- package/config/skills/code_reviewer/SKILL.md +13 -13
- package/config/skills/content_writer/SKILL.md +14 -14
- package/config/skills/data_transformer/SKILL.md +15 -15
- package/config/skills/document_analysis/SKILL.md +13 -13
- package/config/skills/emotional_companion/SKILL.md +15 -15
- package/config/skills/performance_checker/SKILL.md +14 -14
- package/config/skills/security_auditor/SKILL.md +14 -14
- package/config/skills/self_evolve/SKILL.md +13 -13
- package/config/skills/sys_operator/SKILL.md +15 -15
- package/config/skills/task_planner/SKILL.md +14 -14
- package/config/skills/web_research/SKILL.md +14 -14
- package/config/skills/workflow_designer/SKILL.md +13 -13
- package/dist/agents/dew.js +52 -52
- package/dist/agents/fair.js +84 -84
- package/dist/agents/fog.js +30 -30
- package/dist/agents/frost.js +32 -32
- package/dist/agents/rain.js +32 -32
- package/dist/agents/snow.js +68 -68
- package/dist/cli/commands_md.d.ts +41 -0
- package/dist/cli/commands_md.d.ts.map +1 -0
- package/dist/cli/commands_md.js +140 -0
- package/dist/cli/commands_md.js.map +1 -0
- package/dist/cli/input_macros.d.ts +28 -0
- package/dist/cli/input_macros.d.ts.map +1 -0
- package/dist/cli/input_macros.js +120 -0
- package/dist/cli/input_macros.js.map +1 -0
- package/dist/cli/loom.d.ts +220 -0
- package/dist/cli/loom.d.ts.map +1 -0
- package/dist/cli/loom.js +1094 -0
- package/dist/cli/loom.js.map +1 -0
- package/dist/cli/loom_chat.d.ts +20 -0
- package/dist/cli/loom_chat.d.ts.map +1 -0
- package/dist/cli/loom_chat.js +685 -0
- package/dist/cli/loom_chat.js.map +1 -0
- package/dist/cli/main.js +310 -14
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +7 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/core/agent.d.ts +20 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +199 -16
- package/dist/core/agent.js.map +1 -1
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +34 -2
- package/dist/core/factory.js.map +1 -1
- package/dist/core/file_checkpoint.d.ts +57 -0
- package/dist/core/file_checkpoint.d.ts.map +1 -0
- package/dist/core/file_checkpoint.js +162 -0
- package/dist/core/file_checkpoint.js.map +1 -0
- package/dist/core/hooks.d.ts +43 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +110 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/llm.d.ts.map +1 -1
- package/dist/core/llm.js +15 -9
- package/dist/core/llm.js.map +1 -1
- package/dist/core/longdoc.js +5 -5
- package/dist/core/mcp.d.ts +16 -0
- package/dist/core/mcp.d.ts.map +1 -1
- package/dist/core/mcp.js +55 -0
- package/dist/core/mcp.js.map +1 -1
- package/dist/core/model_config.d.ts +40 -0
- package/dist/core/model_config.d.ts.map +1 -0
- package/dist/core/model_config.js +191 -0
- package/dist/core/model_config.js.map +1 -0
- package/dist/core/skill.d.ts +7 -0
- package/dist/core/skill.d.ts.map +1 -1
- package/dist/core/skill.js +47 -0
- package/dist/core/skill.js.map +1 -1
- package/dist/core/skymd.d.ts +39 -0
- package/dist/core/skymd.d.ts.map +1 -0
- package/dist/core/skymd.js +177 -0
- package/dist/core/skymd.js.map +1 -0
- package/dist/core/tool.d.ts +12 -0
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +30 -0
- package/dist/core/tool.js.map +1 -1
- package/dist/core/verify.d.ts +27 -0
- package/dist/core/verify.d.ts.map +1 -0
- package/dist/core/verify.js +62 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/skills/loader.d.ts +22 -2
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +45 -15
- package/dist/skills/loader.js.map +1 -1
- package/dist/tools/builtin.d.ts.map +1 -1
- package/dist/tools/builtin.js +13 -3
- package/dist/tools/builtin.js.map +1 -1
- package/dist/tools/model_tool.d.ts +11 -0
- package/dist/tools/model_tool.d.ts.map +1 -0
- package/dist/tools/model_tool.js +71 -0
- package/dist/tools/model_tool.js.map +1 -0
- package/dist/tools/todo.d.ts +30 -0
- package/dist/tools/todo.d.ts.map +1 -0
- package/dist/tools/todo.js +78 -0
- package/dist/tools/todo.js.map +1 -0
- package/docs/AESTHETIC_DESIGN.md +152 -144
- package/docs/OPTIMIZATION_PLAN.md +178 -178
- package/package.json +68 -68
- package/scripts/install.js +48 -48
- package/scripts/link.js +10 -10
- package/setup.bat +79 -79
- package/skill-test-ty2fOA/test.md +10 -10
- package/src/agents/dew.ts +70 -70
- package/src/agents/fair.ts +102 -102
- package/src/agents/fog.ts +48 -48
- package/src/agents/frost.ts +50 -50
- package/src/agents/rain.ts +50 -50
- package/src/agents/snow.ts +239 -239
- package/src/cli/commands_md.ts +112 -0
- package/src/cli/input_macros.ts +83 -0
- package/src/cli/loom.ts +982 -0
- package/src/cli/loom_chat.ts +598 -0
- package/src/cli/main.ts +255 -9
- package/src/cli/mode.ts +58 -58
- package/src/cli/tui.ts +228 -222
- package/src/core/agent/guard.ts +134 -134
- package/src/core/agent/task.ts +100 -100
- package/src/core/agent.ts +195 -16
- package/src/core/arbitrate.ts +162 -162
- package/src/core/catalog.ts +178 -178
- package/src/core/checkpoint.ts +94 -94
- package/src/core/estimate.ts +104 -104
- package/src/core/evolve.ts +191 -191
- package/src/core/factory.ts +31 -2
- package/src/core/file_checkpoint.ts +136 -0
- package/src/core/filter.ts +103 -103
- package/src/core/graph.ts +156 -156
- package/src/core/hooks.ts +126 -0
- package/src/core/icons.ts +53 -53
- package/src/core/index.ts +37 -37
- package/src/core/learn.ts +146 -146
- package/src/core/llm.ts +15 -9
- package/src/core/longdoc.ts +155 -155
- package/src/core/mcp.ts +48 -0
- package/src/core/mcp_server.ts +176 -176
- package/src/core/model_config.ts +157 -0
- package/src/core/profile.ts +255 -255
- package/src/core/router.ts +124 -124
- package/src/core/sandbox.ts +142 -142
- package/src/core/security.ts +243 -243
- package/src/core/skill.ts +42 -0
- package/src/core/skymd.ts +143 -0
- package/src/core/theme.ts +65 -65
- package/src/core/tool.ts +30 -0
- package/src/core/tool_router.ts +193 -193
- package/src/core/vector.ts +152 -152
- package/src/core/verify.ts +71 -0
- package/src/core/workspace.ts +150 -150
- package/src/plugins/loader.ts +66 -66
- package/src/skills/loader.ts +45 -16
- package/src/sql.js.d.ts +29 -29
- package/src/tools/builtin.ts +13 -3
- package/src/tools/computer.ts +269 -269
- package/src/tools/delegate.ts +49 -49
- package/src/tools/model_tool.ts +74 -0
- package/src/tools/todo.ts +76 -0
- package/src/web/tts.ts +93 -93
- package/tests/agent.test.ts +159 -159
- package/tests/agent_helpers.test.ts +48 -48
- package/tests/bus.test.ts +121 -121
- package/tests/catalog.test.ts +86 -86
- package/tests/checkpoint_commands.test.ts +124 -0
- package/tests/claude_compat.test.ts +110 -0
- package/tests/config.test.ts +41 -41
- package/tests/guard.test.ts +75 -75
- package/tests/icons.test.ts +45 -45
- package/tests/loom.test.ts +248 -0
- package/tests/memory.test.ts +170 -170
- package/tests/model_config.test.ts +109 -0
- package/tests/router.test.ts +86 -86
- package/tests/schemas.test.ts +51 -51
- package/tests/semantic.test.ts +83 -83
- package/tests/setup.ts +10 -10
- package/tests/skill.test.ts +172 -172
- package/tests/skymd.test.ts +146 -0
- package/tests/task.test.ts +60 -60
- package/tests/todo_toolstats.test.ts +94 -0
- package/tests/tool.test.ts +108 -108
- package/tests/tool_router.test.ts +71 -71
- package/tests/tui.test.ts +67 -67
- package/vitest.config.ts +17 -17
- package/=12 +0 -0
- package/=8 +0 -0
package/src/plugins/loader.ts
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin loader — loads external plugins that register additional tools.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
import * as path from 'path';
|
|
7
|
-
import { ToolRegistry } from '../core/tool';
|
|
8
|
-
import { getLogger } from '../core/logger';
|
|
9
|
-
|
|
10
|
-
const log = getLogger('plugin-loader');
|
|
11
|
-
|
|
12
|
-
export class PluginLoader {
|
|
13
|
-
private toolRegistry: ToolRegistry;
|
|
14
|
-
|
|
15
|
-
constructor(toolRegistry: ToolRegistry) {
|
|
16
|
-
this.toolRegistry = toolRegistry;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Load plugins from specified directories.
|
|
21
|
-
*/
|
|
22
|
-
loadFromDirectories(directories: string[]): number {
|
|
23
|
-
let total = 0;
|
|
24
|
-
for (const dir of directories) {
|
|
25
|
-
total += this.loadDirectory(dir);
|
|
26
|
-
}
|
|
27
|
-
return total;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Load a single plugin directory.
|
|
32
|
-
*/
|
|
33
|
-
private loadDirectory(dir: string): number {
|
|
34
|
-
if (!fs.existsSync(dir)) {
|
|
35
|
-
log.debug('plugin_dir_not_found', { dir });
|
|
36
|
-
return 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let count = 0;
|
|
40
|
-
try {
|
|
41
|
-
const entries = fs.readdirSync(dir);
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
const pluginPath = path.join(dir, entry);
|
|
44
|
-
if (!fs.statSync(pluginPath).isDirectory()) continue;
|
|
45
|
-
|
|
46
|
-
const pluginFile = path.join(pluginPath, 'index.js');
|
|
47
|
-
if (!fs.existsSync(pluginFile)) continue;
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const plugin = require(pluginFile);
|
|
51
|
-
if (typeof plugin.register === 'function') {
|
|
52
|
-
plugin.register(this.toolRegistry);
|
|
53
|
-
count++;
|
|
54
|
-
log.info('plugin_loaded', { name: entry });
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
log.warn('plugin_load_failed', { name: entry, error: String(e) });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} catch (e) {
|
|
61
|
-
log.warn('plugin_dir_scan_failed', { dir, error: String(e) });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return count;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Plugin loader — loads external plugins that register additional tools.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { ToolRegistry } from '../core/tool';
|
|
8
|
+
import { getLogger } from '../core/logger';
|
|
9
|
+
|
|
10
|
+
const log = getLogger('plugin-loader');
|
|
11
|
+
|
|
12
|
+
export class PluginLoader {
|
|
13
|
+
private toolRegistry: ToolRegistry;
|
|
14
|
+
|
|
15
|
+
constructor(toolRegistry: ToolRegistry) {
|
|
16
|
+
this.toolRegistry = toolRegistry;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load plugins from specified directories.
|
|
21
|
+
*/
|
|
22
|
+
loadFromDirectories(directories: string[]): number {
|
|
23
|
+
let total = 0;
|
|
24
|
+
for (const dir of directories) {
|
|
25
|
+
total += this.loadDirectory(dir);
|
|
26
|
+
}
|
|
27
|
+
return total;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load a single plugin directory.
|
|
32
|
+
*/
|
|
33
|
+
private loadDirectory(dir: string): number {
|
|
34
|
+
if (!fs.existsSync(dir)) {
|
|
35
|
+
log.debug('plugin_dir_not_found', { dir });
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let count = 0;
|
|
40
|
+
try {
|
|
41
|
+
const entries = fs.readdirSync(dir);
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const pluginPath = path.join(dir, entry);
|
|
44
|
+
if (!fs.statSync(pluginPath).isDirectory()) continue;
|
|
45
|
+
|
|
46
|
+
const pluginFile = path.join(pluginPath, 'index.js');
|
|
47
|
+
if (!fs.existsSync(pluginFile)) continue;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const plugin = require(pluginFile);
|
|
51
|
+
if (typeof plugin.register === 'function') {
|
|
52
|
+
plugin.register(this.toolRegistry);
|
|
53
|
+
count++;
|
|
54
|
+
log.info('plugin_loaded', { name: entry });
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
log.warn('plugin_load_failed', { name: entry, error: String(e) });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
log.warn('plugin_dir_scan_failed', { dir, error: String(e) });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return count;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/skills/loader.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Skill loader —
|
|
2
|
+
* Skill loader — built-in skills plus user/project skill discovery.
|
|
3
|
+
*
|
|
4
|
+
* Discovery order (later registration wins, so project beats user beats
|
|
5
|
+
* built-in when names collide):
|
|
6
|
+
* 1. built-in: config/skills/, assets/builtin_skills/
|
|
7
|
+
* 2. user: ~/.claude/skills/ (Claude Code compatible — zero-copy
|
|
8
|
+
* migration), then ~/.skyloom/skills/ (native)
|
|
9
|
+
* 3. project: <cwd>/.claude/skills/, then <cwd>/.sky/skills/
|
|
10
|
+
*
|
|
11
|
+
* All locations use the same folder layout as Claude Code:
|
|
12
|
+
* <root>/<skill-name>/SKILL.md (+ optional reference files / scripts)
|
|
13
|
+
* Frontmatter is Claude Code compatible (name / description /
|
|
14
|
+
* allowed-tools — tool names like `bash`/`read` are aliased to sky tools).
|
|
3
15
|
*/
|
|
4
16
|
|
|
5
17
|
import * as fs from 'fs';
|
|
18
|
+
import * as os from 'os';
|
|
6
19
|
import * as path from 'path';
|
|
7
20
|
import { SkillRegistry } from '../core/skill';
|
|
8
21
|
import { getLogger } from '../core/logger';
|
|
@@ -15,32 +28,48 @@ const BUILTIN_SKILL_DIRS = [
|
|
|
15
28
|
path.join(__dirname, '..', '..', 'assets', 'builtin_skills'),
|
|
16
29
|
];
|
|
17
30
|
|
|
31
|
+
/** User/project skill roots, lowest precedence first. */
|
|
32
|
+
export function dynamicSkillDirs(cwd: string = process.cwd()): string[] {
|
|
33
|
+
return [
|
|
34
|
+
path.join(os.homedir(), '.claude', 'skills'),
|
|
35
|
+
path.join(os.homedir(), '.skyloom', 'skills'),
|
|
36
|
+
path.join(cwd, '.claude', 'skills'),
|
|
37
|
+
path.join(cwd, '.sky', 'skills'),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* (Re)scan user/project skill folders. Cheap enough to call on demand —
|
|
43
|
+
* list_skills triggers it, so edits to SKILL.md files apply live without
|
|
44
|
+
* restarting the session.
|
|
45
|
+
*/
|
|
46
|
+
export function registerDynamicSkills(registry: SkillRegistry, cwd: string = process.cwd()): number {
|
|
47
|
+
let count = 0;
|
|
48
|
+
for (const dir of dynamicSkillDirs(cwd)) {
|
|
49
|
+
try {
|
|
50
|
+
count += registry.loadSkillFolders(dir).length;
|
|
51
|
+
} catch (e) {
|
|
52
|
+
log.warn('dynamic_skill_load_failed', { dir, error: String(e) });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return count;
|
|
56
|
+
}
|
|
57
|
+
|
|
18
58
|
/**
|
|
19
|
-
* Register all available skills
|
|
59
|
+
* Register all available skills: built-ins, then user/project overlays.
|
|
20
60
|
*/
|
|
21
61
|
export function registerAllSkills(registry: SkillRegistry): void {
|
|
22
62
|
for (const dir of BUILTIN_SKILL_DIRS) {
|
|
23
63
|
if (!fs.existsSync(dir)) continue;
|
|
24
|
-
|
|
25
64
|
try {
|
|
26
|
-
|
|
27
|
-
for (const entry of entries) {
|
|
28
|
-
const skillDir = path.join(dir, entry);
|
|
29
|
-
if (!fs.statSync(skillDir).isDirectory()) continue;
|
|
30
|
-
|
|
31
|
-
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
32
|
-
if (!fs.existsSync(skillFile)) continue;
|
|
33
|
-
|
|
34
|
-
const skill = registry.loadSkillsFromDirectory(skillDir);
|
|
35
|
-
if (skill.length > 0) {
|
|
36
|
-
log.debug('registered_skill', { name: entry, file: skillFile });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
65
|
+
registry.loadSkillFolders(dir);
|
|
39
66
|
} catch (e) {
|
|
40
67
|
log.warn('skill_load_failed', { dir, error: String(e) });
|
|
41
68
|
}
|
|
42
69
|
}
|
|
43
70
|
|
|
71
|
+
registerDynamicSkills(registry);
|
|
72
|
+
|
|
44
73
|
const count = registry.listNames().length;
|
|
45
74
|
log.info('skills_loaded', { count });
|
|
46
75
|
}
|
package/src/sql.js.d.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
declare module 'sql.js' {
|
|
2
|
-
interface SqlJsStatic {
|
|
3
|
-
Database: new (data?: ArrayLike<number> | Buffer | null) => Database;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
interface QueryExecResult {
|
|
7
|
-
columns: string[];
|
|
8
|
-
values: any[][];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface Statement {
|
|
12
|
-
bind(params?: any[]): boolean;
|
|
13
|
-
step(): boolean;
|
|
14
|
-
getAsObject(params?: object): Record<string, any>;
|
|
15
|
-
free(): boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Database {
|
|
19
|
-
run(sql: string, params?: any[]): Database;
|
|
20
|
-
exec(sql: string): QueryExecResult[];
|
|
21
|
-
prepare(sql: string): Statement;
|
|
22
|
-
export(): Uint8Array;
|
|
23
|
-
close(): void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type { Database, SqlJsStatic, Statement };
|
|
27
|
-
|
|
28
|
-
export default function initSqlJs(opts?: any): Promise<SqlJsStatic>;
|
|
29
|
-
}
|
|
1
|
+
declare module 'sql.js' {
|
|
2
|
+
interface SqlJsStatic {
|
|
3
|
+
Database: new (data?: ArrayLike<number> | Buffer | null) => Database;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface QueryExecResult {
|
|
7
|
+
columns: string[];
|
|
8
|
+
values: any[][];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Statement {
|
|
12
|
+
bind(params?: any[]): boolean;
|
|
13
|
+
step(): boolean;
|
|
14
|
+
getAsObject(params?: object): Record<string, any>;
|
|
15
|
+
free(): boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Database {
|
|
19
|
+
run(sql: string, params?: any[]): Database;
|
|
20
|
+
exec(sql: string): QueryExecResult[];
|
|
21
|
+
prepare(sql: string): Statement;
|
|
22
|
+
export(): Uint8Array;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type { Database, SqlJsStatic, Statement };
|
|
27
|
+
|
|
28
|
+
export default function initSqlJs(opts?: any): Promise<SqlJsStatic>;
|
|
29
|
+
}
|
package/src/tools/builtin.ts
CHANGED
|
@@ -20,16 +20,26 @@ export function registerBuiltinTools(registry: ToolRegistry): void {
|
|
|
20
20
|
|
|
21
21
|
registry.register({
|
|
22
22
|
name: 'read_file',
|
|
23
|
-
description: 'Read the contents of a file
|
|
23
|
+
description: 'Read the contents of a file. Large files are paged: pass offset (1-based start line) and limit (line count) to read further sections; use grep to locate the right offset first.',
|
|
24
24
|
parameters: [
|
|
25
25
|
{ name: 'path', type: 'string', description: 'Absolute or relative path to the file', required: true },
|
|
26
|
+
{ name: 'offset', type: 'number', description: '1-based line number to start from (default 1)', required: false },
|
|
27
|
+
{ name: 'limit', type: 'number', description: 'Max lines to return (default 800)', required: false },
|
|
26
28
|
],
|
|
27
29
|
handler: async (params) => {
|
|
28
30
|
const filePath = path.resolve(params.path as string);
|
|
29
31
|
if (!fs.existsSync(filePath)) return `Error: File not found: ${filePath}`;
|
|
30
32
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
+
const lines = fs.readFileSync(filePath, 'utf-8').split('\n');
|
|
34
|
+
const offset = Math.max(1, Number(params.offset) || 1);
|
|
35
|
+
const limit = Math.max(1, Math.min(Number(params.limit) || 800, 4000));
|
|
36
|
+
const slice = lines.slice(offset - 1, offset - 1 + limit);
|
|
37
|
+
const remaining = lines.length - (offset - 1 + slice.length);
|
|
38
|
+
const tail = remaining > 0
|
|
39
|
+
? `\n…[还有 ${remaining} 行 — read_file(path, offset=${offset + slice.length}) 继续]`
|
|
40
|
+
: '';
|
|
41
|
+
const range = offset > 1 || remaining > 0 ? ` lines ${offset}-${offset + slice.length - 1}/${lines.length}` : '';
|
|
42
|
+
return `Successfully read ${filePath}${range}:\n${slice.join('\n')}${tail}`;
|
|
33
43
|
} catch (e) {
|
|
34
44
|
return `Error reading file: ${e}`;
|
|
35
45
|
}
|