kagent-ts 0.1.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/LICENSE +21 -0
- package/README.md +395 -0
- package/dist/compression/interface.d.ts +26 -0
- package/dist/compression/interface.d.ts.map +1 -0
- package/dist/compression/interface.js +3 -0
- package/dist/compression/interface.js.map +1 -0
- package/dist/compression/sliding-window.d.ts +21 -0
- package/dist/compression/sliding-window.d.ts.map +1 -0
- package/dist/compression/sliding-window.js +55 -0
- package/dist/compression/sliding-window.js.map +1 -0
- package/dist/compression/types.d.ts +12 -0
- package/dist/compression/types.d.ts.map +1 -0
- package/dist/compression/types.js +3 -0
- package/dist/compression/types.js.map +1 -0
- package/dist/context/context-manager.d.ts +76 -0
- package/dist/context/context-manager.d.ts.map +1 -0
- package/dist/context/context-manager.js +132 -0
- package/dist/context/context-manager.js.map +1 -0
- package/dist/context/types.d.ts +35 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +3 -0
- package/dist/context/types.js.map +1 -0
- package/dist/core/agent.d.ts +288 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +398 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/hooks.d.ts +34 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +3 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/plan-solve-agent.d.ts +114 -0
- package/dist/core/plan-solve-agent.d.ts.map +1 -0
- package/dist/core/plan-solve-agent.js +450 -0
- package/dist/core/plan-solve-agent.js.map +1 -0
- package/dist/core/react-agent.d.ts +52 -0
- package/dist/core/react-agent.d.ts.map +1 -0
- package/dist/core/react-agent.js +266 -0
- package/dist/core/react-agent.js.map +1 -0
- package/dist/core/response-schema.d.ts +91 -0
- package/dist/core/response-schema.d.ts.map +1 -0
- package/dist/core/response-schema.js +292 -0
- package/dist/core/response-schema.js.map +1 -0
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/interface.d.ts +87 -0
- package/dist/llm/interface.d.ts.map +1 -0
- package/dist/llm/interface.js +3 -0
- package/dist/llm/interface.js.map +1 -0
- package/dist/llm/openai-provider.d.ts +92 -0
- package/dist/llm/openai-provider.d.ts.map +1 -0
- package/dist/llm/openai-provider.js +262 -0
- package/dist/llm/openai-provider.js.map +1 -0
- package/dist/messages/message.d.ts +50 -0
- package/dist/messages/message.d.ts.map +1 -0
- package/dist/messages/message.js +87 -0
- package/dist/messages/message.js.map +1 -0
- package/dist/messages/types.d.ts +31 -0
- package/dist/messages/types.d.ts.map +1 -0
- package/dist/messages/types.js +14 -0
- package/dist/messages/types.js.map +1 -0
- package/dist/preferences/preference-manager.d.ts +88 -0
- package/dist/preferences/preference-manager.d.ts.map +1 -0
- package/dist/preferences/preference-manager.js +196 -0
- package/dist/preferences/preference-manager.js.map +1 -0
- package/dist/preferences/types.d.ts +27 -0
- package/dist/preferences/types.d.ts.map +1 -0
- package/dist/preferences/types.js +3 -0
- package/dist/preferences/types.js.map +1 -0
- package/dist/session/session-manager.d.ts +56 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +156 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/session-types.d.ts +51 -0
- package/dist/session/session-types.d.ts.map +1 -0
- package/dist/session/session-types.js +3 -0
- package/dist/session/session-types.js.map +1 -0
- package/dist/skills/file-skill-loader.d.ts +88 -0
- package/dist/skills/file-skill-loader.d.ts.map +1 -0
- package/dist/skills/file-skill-loader.js +365 -0
- package/dist/skills/file-skill-loader.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +10 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/skill-manager.d.ts +133 -0
- package/dist/skills/skill-manager.d.ts.map +1 -0
- package/dist/skills/skill-manager.js +310 -0
- package/dist/skills/skill-manager.js.map +1 -0
- package/dist/skills/types.d.ts +42 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +3 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/builtin/edit-file.d.ts +12 -0
- package/dist/tools/builtin/edit-file.d.ts.map +1 -0
- package/dist/tools/builtin/edit-file.js +123 -0
- package/dist/tools/builtin/edit-file.js.map +1 -0
- package/dist/tools/builtin/glob-search.d.ts +11 -0
- package/dist/tools/builtin/glob-search.d.ts.map +1 -0
- package/dist/tools/builtin/glob-search.js +264 -0
- package/dist/tools/builtin/glob-search.js.map +1 -0
- package/dist/tools/builtin/grep-search.d.ts +14 -0
- package/dist/tools/builtin/grep-search.d.ts.map +1 -0
- package/dist/tools/builtin/grep-search.js +264 -0
- package/dist/tools/builtin/grep-search.js.map +1 -0
- package/dist/tools/builtin/index.d.ts +21 -0
- package/dist/tools/builtin/index.d.ts.map +1 -0
- package/dist/tools/builtin/index.js +53 -0
- package/dist/tools/builtin/index.js.map +1 -0
- package/dist/tools/builtin/read-file.d.ts +11 -0
- package/dist/tools/builtin/read-file.d.ts.map +1 -0
- package/dist/tools/builtin/read-file.js +122 -0
- package/dist/tools/builtin/read-file.js.map +1 -0
- package/dist/tools/builtin/write-file.d.ts +10 -0
- package/dist/tools/builtin/write-file.d.ts.map +1 -0
- package/dist/tools/builtin/write-file.js +89 -0
- package/dist/tools/builtin/write-file.js.map +1 -0
- package/dist/tools/circuit-breaker.d.ts +77 -0
- package/dist/tools/circuit-breaker.d.ts.map +1 -0
- package/dist/tools/circuit-breaker.js +102 -0
- package/dist/tools/circuit-breaker.js.map +1 -0
- package/dist/tools/error-tracker.d.ts +116 -0
- package/dist/tools/error-tracker.d.ts.map +1 -0
- package/dist/tools/error-tracker.js +484 -0
- package/dist/tools/error-tracker.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +87 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +188 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/types.d.ts +95 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +14 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/token-counter.d.ts +31 -0
- package/dist/utils/token-counter.d.ts.map +1 -0
- package/dist/utils/token-counter.js +105 -0
- package/dist/utils/token-counter.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SessionManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Manages session state persistence to disk.
|
|
41
|
+
*
|
|
42
|
+
* Each session is stored as a single JSON file in the configured directory.
|
|
43
|
+
* Sessions are self-contained (messages are inline) so they survive any
|
|
44
|
+
* memory/component lifecycle and can be resumed after process restarts.
|
|
45
|
+
*/
|
|
46
|
+
class SessionManager {
|
|
47
|
+
sessionId;
|
|
48
|
+
sessionDir;
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.sessionId =
|
|
51
|
+
config?.sessionId ?? `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
52
|
+
this.sessionDir = path.resolve(config?.sessionDir ?? ".kagent-sessions");
|
|
53
|
+
fs.mkdirSync(this.sessionDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
// ─── Identity ──────────────────────────────────────────────────────────
|
|
56
|
+
getSessionId() {
|
|
57
|
+
return this.sessionId;
|
|
58
|
+
}
|
|
59
|
+
getSessionDir() {
|
|
60
|
+
return this.sessionDir;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Set the session ID (used during resume to match the restored session).
|
|
64
|
+
*/
|
|
65
|
+
setSessionId(id) {
|
|
66
|
+
this.sessionId = id;
|
|
67
|
+
}
|
|
68
|
+
// ─── Persistence ───────────────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Get the file path for the current session.
|
|
71
|
+
*/
|
|
72
|
+
filePath() {
|
|
73
|
+
return path.join(this.sessionDir, `${this.sessionId}.json`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Save a session checkpoint to disk.
|
|
77
|
+
*
|
|
78
|
+
* Preserves the original `createdAt` timestamp so resuming a session
|
|
79
|
+
* retains the original creation time. Updates `updatedAt` to now.
|
|
80
|
+
*/
|
|
81
|
+
saveCheckpoint(state) {
|
|
82
|
+
// Preserve the original createdAt (don't overwrite with a later timestamp)
|
|
83
|
+
const existing = this.loadSession(this.sessionId);
|
|
84
|
+
const stateToSave = {
|
|
85
|
+
...state,
|
|
86
|
+
createdAt: existing?.createdAt ?? state.createdAt,
|
|
87
|
+
updatedAt: new Date().toISOString(),
|
|
88
|
+
};
|
|
89
|
+
fs.writeFileSync(this.filePath(), JSON.stringify(stateToSave, null, 2), "utf-8");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load a session by ID. Returns null if the file is missing or corrupt.
|
|
93
|
+
*/
|
|
94
|
+
loadSession(sessionId) {
|
|
95
|
+
const filePath = path.join(this.sessionDir, `${sessionId}.json`);
|
|
96
|
+
try {
|
|
97
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
98
|
+
const parsed = JSON.parse(raw);
|
|
99
|
+
// Basic structural validation
|
|
100
|
+
if (!parsed.sessionId || !parsed.agentType || !parsed.messages) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Return all persisted session states, sorted by `updatedAt` descending.
|
|
111
|
+
*/
|
|
112
|
+
listSessions() {
|
|
113
|
+
const sessions = [];
|
|
114
|
+
try {
|
|
115
|
+
const files = fs.readdirSync(this.sessionDir);
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
if (!file.endsWith(".json"))
|
|
118
|
+
continue;
|
|
119
|
+
const sessionId = file.slice(0, -".json".length);
|
|
120
|
+
const state = this.loadSession(sessionId);
|
|
121
|
+
if (state)
|
|
122
|
+
sessions.push(state);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Directory doesn't exist or can't be read
|
|
127
|
+
}
|
|
128
|
+
sessions.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
129
|
+
return sessions;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Delete a session file from disk.
|
|
133
|
+
*/
|
|
134
|
+
deleteSession(sessionId) {
|
|
135
|
+
const filePath = path.join(this.sessionDir, `${sessionId}.json`);
|
|
136
|
+
try {
|
|
137
|
+
fs.unlinkSync(filePath);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// File doesn't exist — that's fine
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Update the status and timestamp of a session in-place.
|
|
145
|
+
*/
|
|
146
|
+
markStatus(sessionId, status) {
|
|
147
|
+
const state = this.loadSession(sessionId);
|
|
148
|
+
if (state) {
|
|
149
|
+
state.status = status;
|
|
150
|
+
state.updatedAt = new Date().toISOString();
|
|
151
|
+
this.saveCheckpoint(state);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.SessionManager = SessionManager;
|
|
156
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { MessageData } from "../messages/types";
|
|
2
|
+
/**
|
|
3
|
+
* Which agent paradigm the session belongs to.
|
|
4
|
+
*/
|
|
5
|
+
export type AgentType = "react" | "plan-solve";
|
|
6
|
+
/**
|
|
7
|
+
* Lifecycle status of a persisted session.
|
|
8
|
+
* - "active": Session is in progress, checkpoint was saved mid-run.
|
|
9
|
+
* - "interrupted": Session was interrupted by a network error, ready for resume.
|
|
10
|
+
* - "completed": Session finished normally.
|
|
11
|
+
*/
|
|
12
|
+
export type SessionStatus = "active" | "interrupted" | "completed";
|
|
13
|
+
/**
|
|
14
|
+
* Serializable plan state for PlanSolveAgent sessions.
|
|
15
|
+
*/
|
|
16
|
+
export interface PlanSolveSessionState {
|
|
17
|
+
currentPlan: string[];
|
|
18
|
+
hasPlan: boolean;
|
|
19
|
+
completedSteps: number;
|
|
20
|
+
consecutiveFailures: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Full session state snapshot.
|
|
24
|
+
*
|
|
25
|
+
* This is the serialised form of everything needed to reconstruct an agent
|
|
26
|
+
* and resume execution after an interruption (e.g. network outage).
|
|
27
|
+
*
|
|
28
|
+
* Messages are stored inline — not referenced via MediumTermMemory — so the
|
|
29
|
+
* checkpoint is self-contained regardless of memory configuration.
|
|
30
|
+
*/
|
|
31
|
+
export interface SessionState {
|
|
32
|
+
/** Unique identifier for this session (e.g. "session-1748234567890"). */
|
|
33
|
+
sessionId: string;
|
|
34
|
+
/** Which agent type created this session. */
|
|
35
|
+
agentType: AgentType;
|
|
36
|
+
/** The core system prompt used by the agent. */
|
|
37
|
+
systemPrompt: string;
|
|
38
|
+
/** Full message history at checkpoint time. */
|
|
39
|
+
messages: MessageData[];
|
|
40
|
+
/** Plan-Solve specific state (only present for plan-solve agents). */
|
|
41
|
+
planState?: PlanSolveSessionState;
|
|
42
|
+
/** ISO-8601 timestamp of session creation (stable across saves). */
|
|
43
|
+
createdAt: string;
|
|
44
|
+
/** ISO-8601 timestamp of last checkpoint save. */
|
|
45
|
+
updatedAt: string;
|
|
46
|
+
/** Current lifecycle status. */
|
|
47
|
+
status: SessionStatus;
|
|
48
|
+
/** Optional metadata (model name, token counts, etc.). */
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=session-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../src/session/session-types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,aAAa,GAAG,WAAW,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAElB,6CAA6C;IAC7C,SAAS,EAAE,SAAS,CAAC;IAErB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IAErB,+CAA+C;IAC/C,QAAQ,EAAE,WAAW,EAAE,CAAC;IAExB,sEAAsE;IACtE,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAElC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAElB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,gCAAgC;IAChC,MAAM,EAAE,aAAa,CAAC;IAEtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-types.js","sourceRoot":"","sources":["../../src/session/session-types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Skill } from "./types";
|
|
2
|
+
import { Tool } from "../tools/types";
|
|
3
|
+
/**
|
|
4
|
+
* Parse YAML-like frontmatter from a Markdown file.
|
|
5
|
+
*
|
|
6
|
+
* Expects content between `---` delimiters at the start of the file:
|
|
7
|
+
* ```
|
|
8
|
+
* ---
|
|
9
|
+
* key: value
|
|
10
|
+
* ---
|
|
11
|
+
* body content...
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Lines starting with `#` inside frontmatter are treated as comments.
|
|
15
|
+
* Returns empty frontmatter and full content as body when no `---` markers
|
|
16
|
+
* are found.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseFrontmatter(raw: string): {
|
|
19
|
+
frontmatter: Record<string, string>;
|
|
20
|
+
body: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Parse a comma-separated keywords string into an array.
|
|
24
|
+
* Returns `undefined` when the input is empty or absent.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseKeywords(raw?: string): string[] | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Loads skill definitions from a directory on disk.
|
|
29
|
+
*
|
|
30
|
+
* Expected directory structure:
|
|
31
|
+
* ```
|
|
32
|
+
* skills/
|
|
33
|
+
* ├── <skill-name>/
|
|
34
|
+
* │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
|
|
35
|
+
* │ ├── reference/ # Optional reference docs (*.md, *.txt)
|
|
36
|
+
* │ └── scripts/ # Optional executable scripts
|
|
37
|
+
* ├── <another-skill>/
|
|
38
|
+
* │ └── ...
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Loading is two-phase:
|
|
42
|
+
* 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
|
|
43
|
+
* 2. `loadSystemPrompt()` / `loadScriptsAsTools()` — full content, called
|
|
44
|
+
* lazily on activation.
|
|
45
|
+
*/
|
|
46
|
+
export declare class FileSkillLoader {
|
|
47
|
+
private directory;
|
|
48
|
+
/**
|
|
49
|
+
* @param directory Path to the skills root directory (default: `./skills`).
|
|
50
|
+
*/
|
|
51
|
+
constructor(directory?: string);
|
|
52
|
+
/**
|
|
53
|
+
* Get the root directory path.
|
|
54
|
+
*/
|
|
55
|
+
getDirectory(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Get the absolute path to a named skill's subdirectory.
|
|
58
|
+
*/
|
|
59
|
+
getSkillDir(name: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Scan the skills directory and return metadata-only Skill objects.
|
|
62
|
+
*
|
|
63
|
+
* Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
|
|
64
|
+
* and scripts are NOT loaded at this stage. They are loaded lazily when
|
|
65
|
+
* the skill is activated.
|
|
66
|
+
*
|
|
67
|
+
* Subdirectories without a valid SKILL.md (or without a `name` in
|
|
68
|
+
* frontmatter) are skipped with a warning.
|
|
69
|
+
*/
|
|
70
|
+
scan(): Skill[];
|
|
71
|
+
/**
|
|
72
|
+
* Fully load the system prompt for a skill:
|
|
73
|
+
* 1. SKILL.md body (the content after frontmatter)
|
|
74
|
+
* 2. All reference/*.md and reference/*.txt files (concatenated with headers)
|
|
75
|
+
*
|
|
76
|
+
* Throws if the skill directory does not exist.
|
|
77
|
+
*/
|
|
78
|
+
loadSystemPrompt(name: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Create Tool objects from scripts in the scripts/ subdirectory.
|
|
81
|
+
*
|
|
82
|
+
* Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
|
|
83
|
+
*
|
|
84
|
+
* Returns an empty array if the scripts/ directory is missing or empty.
|
|
85
|
+
*/
|
|
86
|
+
loadScriptsAsTools(name: string): Tool[];
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=file-skill-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-skill-loader.d.ts","sourceRoot":"","sources":["../../src/skills/file-skill-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAItC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd,CAkBA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOhE;AAiFD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAS;IAE1B;;OAEG;gBACS,SAAS,CAAC,EAAE,MAAM;IAI9B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC;;;;;;;;;OASG;IACH,IAAI,IAAI,KAAK,EAAE;IAmDf;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAsCtC;;;;;;OAMG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE;CAgFzC"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileSkillLoader = void 0;
|
|
37
|
+
exports.parseFrontmatter = parseFrontmatter;
|
|
38
|
+
exports.parseKeywords = parseKeywords;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
// ─── Frontmatter Parsing ───────────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* Parse YAML-like frontmatter from a Markdown file.
|
|
45
|
+
*
|
|
46
|
+
* Expects content between `---` delimiters at the start of the file:
|
|
47
|
+
* ```
|
|
48
|
+
* ---
|
|
49
|
+
* key: value
|
|
50
|
+
* ---
|
|
51
|
+
* body content...
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* Lines starting with `#` inside frontmatter are treated as comments.
|
|
55
|
+
* Returns empty frontmatter and full content as body when no `---` markers
|
|
56
|
+
* are found.
|
|
57
|
+
*/
|
|
58
|
+
function parseFrontmatter(raw) {
|
|
59
|
+
const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
60
|
+
if (!match) {
|
|
61
|
+
return { frontmatter: {}, body: raw.trim() };
|
|
62
|
+
}
|
|
63
|
+
const frontmatter = {};
|
|
64
|
+
for (const line of match[1].split("\n")) {
|
|
65
|
+
const trimmed = line.trim();
|
|
66
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
67
|
+
continue;
|
|
68
|
+
const colonIdx = trimmed.indexOf(":");
|
|
69
|
+
if (colonIdx <= 0)
|
|
70
|
+
continue;
|
|
71
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
72
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
73
|
+
if (key)
|
|
74
|
+
frontmatter[key] = value;
|
|
75
|
+
}
|
|
76
|
+
return { frontmatter, body: match[2].trim() };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse a comma-separated keywords string into an array.
|
|
80
|
+
* Returns `undefined` when the input is empty or absent.
|
|
81
|
+
*/
|
|
82
|
+
function parseKeywords(raw) {
|
|
83
|
+
if (!raw)
|
|
84
|
+
return undefined;
|
|
85
|
+
const keywords = raw
|
|
86
|
+
.split(",")
|
|
87
|
+
.map((k) => k.trim())
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
return keywords.length > 0 ? keywords : undefined;
|
|
90
|
+
}
|
|
91
|
+
// ─── Script Execution ──────────────────────────────────────────────────────
|
|
92
|
+
const SUPPORTED_SCRIPT_EXTENSIONS = new Set([
|
|
93
|
+
".sh",
|
|
94
|
+
".bat",
|
|
95
|
+
".cmd",
|
|
96
|
+
".ps1",
|
|
97
|
+
".js",
|
|
98
|
+
".py",
|
|
99
|
+
]);
|
|
100
|
+
const REFERENCE_EXTENSIONS = new Set([".md", ".txt"]);
|
|
101
|
+
/**
|
|
102
|
+
* Determine the interpreter and arguments needed to run a script file.
|
|
103
|
+
* Returns `null` if the file extension is not supported.
|
|
104
|
+
*/
|
|
105
|
+
function getInterpreter(filePath) {
|
|
106
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
107
|
+
switch (ext) {
|
|
108
|
+
case ".sh":
|
|
109
|
+
return ["bash", filePath];
|
|
110
|
+
case ".bat":
|
|
111
|
+
case ".cmd":
|
|
112
|
+
return ["cmd.exe", "/c", filePath];
|
|
113
|
+
case ".ps1":
|
|
114
|
+
return ["powershell.exe", "-File", filePath];
|
|
115
|
+
case ".js":
|
|
116
|
+
return ["node", filePath];
|
|
117
|
+
case ".py":
|
|
118
|
+
// Try python3 first, fall back to python
|
|
119
|
+
try {
|
|
120
|
+
(0, child_process_1.execFileSync)("python3", ["--version"], { timeout: 5000, stdio: "ignore" });
|
|
121
|
+
return ["python3", filePath];
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return ["python", filePath];
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extract a description for a script tool from the first comment/line
|
|
132
|
+
* of the file, or fall back to a generic description.
|
|
133
|
+
*/
|
|
134
|
+
function getScriptDescription(filePath) {
|
|
135
|
+
try {
|
|
136
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
137
|
+
const firstLine = content.split("\n")[0]?.trim();
|
|
138
|
+
if (firstLine) {
|
|
139
|
+
// Strip common comment markers and shebang
|
|
140
|
+
const clean = firstLine
|
|
141
|
+
.replace(/^#!\s*/, "")
|
|
142
|
+
.replace(/^#\s*/, "")
|
|
143
|
+
.replace(/^\/\/\s*/, "")
|
|
144
|
+
.replace(/^--\s*/, "")
|
|
145
|
+
.trim();
|
|
146
|
+
if (clean && !clean.startsWith("/"))
|
|
147
|
+
return clean;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ignore read errors
|
|
152
|
+
}
|
|
153
|
+
return `Execute the ${path.basename(filePath)} script`;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sanitize a string for use as a tool name (alphanumeric + underscores only).
|
|
157
|
+
*/
|
|
158
|
+
function sanitizeToolName(name) {
|
|
159
|
+
return name
|
|
160
|
+
.toLowerCase()
|
|
161
|
+
.replace(/[^a-z0-9_]/g, "_")
|
|
162
|
+
.replace(/_+/g, "_")
|
|
163
|
+
.replace(/^_|_$/g, "");
|
|
164
|
+
}
|
|
165
|
+
// ─── FileSkillLoader ───────────────────────────────────────────────────────
|
|
166
|
+
/**
|
|
167
|
+
* Loads skill definitions from a directory on disk.
|
|
168
|
+
*
|
|
169
|
+
* Expected directory structure:
|
|
170
|
+
* ```
|
|
171
|
+
* skills/
|
|
172
|
+
* ├── <skill-name>/
|
|
173
|
+
* │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
|
|
174
|
+
* │ ├── reference/ # Optional reference docs (*.md, *.txt)
|
|
175
|
+
* │ └── scripts/ # Optional executable scripts
|
|
176
|
+
* ├── <another-skill>/
|
|
177
|
+
* │ └── ...
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* Loading is two-phase:
|
|
181
|
+
* 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
|
|
182
|
+
* 2. `loadSystemPrompt()` / `loadScriptsAsTools()` — full content, called
|
|
183
|
+
* lazily on activation.
|
|
184
|
+
*/
|
|
185
|
+
class FileSkillLoader {
|
|
186
|
+
directory;
|
|
187
|
+
/**
|
|
188
|
+
* @param directory Path to the skills root directory (default: `./skills`).
|
|
189
|
+
*/
|
|
190
|
+
constructor(directory) {
|
|
191
|
+
this.directory = path.resolve(directory || "./skills");
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the root directory path.
|
|
195
|
+
*/
|
|
196
|
+
getDirectory() {
|
|
197
|
+
return this.directory;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get the absolute path to a named skill's subdirectory.
|
|
201
|
+
*/
|
|
202
|
+
getSkillDir(name) {
|
|
203
|
+
return path.join(this.directory, name);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Scan the skills directory and return metadata-only Skill objects.
|
|
207
|
+
*
|
|
208
|
+
* Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
|
|
209
|
+
* and scripts are NOT loaded at this stage. They are loaded lazily when
|
|
210
|
+
* the skill is activated.
|
|
211
|
+
*
|
|
212
|
+
* Subdirectories without a valid SKILL.md (or without a `name` in
|
|
213
|
+
* frontmatter) are skipped with a warning.
|
|
214
|
+
*/
|
|
215
|
+
scan() {
|
|
216
|
+
const skills = [];
|
|
217
|
+
let entries;
|
|
218
|
+
try {
|
|
219
|
+
entries = fs.readdirSync(this.directory, { withFileTypes: true });
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Directory doesn't exist or can't be read — return empty
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
for (const entry of entries) {
|
|
226
|
+
// Skip non-directories, dotfiles, and symlinks
|
|
227
|
+
if (!entry.isDirectory())
|
|
228
|
+
continue;
|
|
229
|
+
if (entry.name.startsWith("."))
|
|
230
|
+
continue;
|
|
231
|
+
const skillPath = path.join(this.directory, entry.name);
|
|
232
|
+
const skillMdPath = path.join(skillPath, "SKILL.md");
|
|
233
|
+
let raw;
|
|
234
|
+
try {
|
|
235
|
+
raw = fs.readFileSync(skillMdPath, "utf-8");
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
console.warn(`[Skills] Skipping "${entry.name}": no SKILL.md found in ${skillPath}`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
242
|
+
const name = frontmatter.name?.trim();
|
|
243
|
+
if (!name) {
|
|
244
|
+
console.warn(`[Skills] Skipping "${entry.name}": SKILL.md has no "name" in frontmatter`);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
skills.push({
|
|
248
|
+
name,
|
|
249
|
+
description: frontmatter.description?.trim() ?? "",
|
|
250
|
+
systemPrompt: "", // Loaded lazily on activation
|
|
251
|
+
keywords: parseKeywords(frontmatter.keywords),
|
|
252
|
+
tools: [], // Loaded lazily on activation
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return skills;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Fully load the system prompt for a skill:
|
|
259
|
+
* 1. SKILL.md body (the content after frontmatter)
|
|
260
|
+
* 2. All reference/*.md and reference/*.txt files (concatenated with headers)
|
|
261
|
+
*
|
|
262
|
+
* Throws if the skill directory does not exist.
|
|
263
|
+
*/
|
|
264
|
+
loadSystemPrompt(name) {
|
|
265
|
+
const skillDir = this.getSkillDir(name);
|
|
266
|
+
const skillMdPath = path.join(skillDir, "SKILL.md");
|
|
267
|
+
// Read SKILL.md body
|
|
268
|
+
const raw = fs.readFileSync(skillMdPath, "utf-8");
|
|
269
|
+
const { body } = parseFrontmatter(raw);
|
|
270
|
+
const parts = [body];
|
|
271
|
+
// Append reference docs
|
|
272
|
+
const refDir = path.join(skillDir, "reference");
|
|
273
|
+
try {
|
|
274
|
+
const refFiles = fs
|
|
275
|
+
.readdirSync(refDir, { withFileTypes: true })
|
|
276
|
+
.filter((f) => f.isFile() &&
|
|
277
|
+
REFERENCE_EXTENSIONS.has(path.extname(f.name).toLowerCase()))
|
|
278
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
279
|
+
if (refFiles.length > 0) {
|
|
280
|
+
parts.push("\n\n---\n### Reference Documents\n");
|
|
281
|
+
for (const ref of refFiles) {
|
|
282
|
+
const refContent = fs.readFileSync(path.join(refDir, ref.name), "utf-8");
|
|
283
|
+
parts.push(`\n**${ref.name}**\n${refContent.trim()}\n`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// No reference/ directory — skip silently
|
|
289
|
+
}
|
|
290
|
+
return parts.join("").trim();
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Create Tool objects from scripts in the scripts/ subdirectory.
|
|
294
|
+
*
|
|
295
|
+
* Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
|
|
296
|
+
*
|
|
297
|
+
* Returns an empty array if the scripts/ directory is missing or empty.
|
|
298
|
+
*/
|
|
299
|
+
loadScriptsAsTools(name) {
|
|
300
|
+
const scriptsDir = path.join(this.getSkillDir(name), "scripts");
|
|
301
|
+
const tools = [];
|
|
302
|
+
let entries;
|
|
303
|
+
try {
|
|
304
|
+
entries = fs.readdirSync(scriptsDir, { withFileTypes: true });
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// No scripts/ directory — return empty
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
for (const entry of entries) {
|
|
311
|
+
if (!entry.isFile())
|
|
312
|
+
continue;
|
|
313
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
314
|
+
if (!SUPPORTED_SCRIPT_EXTENSIONS.has(ext))
|
|
315
|
+
continue;
|
|
316
|
+
const scriptPath = path.join(scriptsDir, entry.name);
|
|
317
|
+
const scriptName = path.basename(entry.name, ext);
|
|
318
|
+
const toolName = sanitizeToolName(`${name}_${scriptName}`);
|
|
319
|
+
const toolDescription = getScriptDescription(scriptPath);
|
|
320
|
+
const tool = {
|
|
321
|
+
name: toolName,
|
|
322
|
+
description: toolDescription,
|
|
323
|
+
parameters: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
args: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: `Arguments to pass to the ${entry.name} script. ` +
|
|
329
|
+
`Use shell-style syntax (space-separated).`,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
required: ["args"],
|
|
333
|
+
},
|
|
334
|
+
async execute(params) {
|
|
335
|
+
const argsStr = String(params.args ?? "");
|
|
336
|
+
const interpreter = getInterpreter(scriptPath);
|
|
337
|
+
if (!interpreter) {
|
|
338
|
+
return `Error: Unsupported script type "${ext}" for ${entry.name}`;
|
|
339
|
+
}
|
|
340
|
+
const [cmd, ...cmdArgs] = interpreter;
|
|
341
|
+
// Append the script argument after the interpreter args
|
|
342
|
+
const allArgs = argsStr
|
|
343
|
+
? [...cmdArgs, ...argsStr.split(/\s+/).filter(Boolean)]
|
|
344
|
+
: cmdArgs;
|
|
345
|
+
return new Promise((resolve) => {
|
|
346
|
+
const child = (0, child_process_1.execFile)(cmd, allArgs, { timeout: 30000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
347
|
+
if (err) {
|
|
348
|
+
const details = stderr
|
|
349
|
+
? `\nstderr:\n${stderr.trim()}`
|
|
350
|
+
: "";
|
|
351
|
+
resolve(`Error executing ${entry.name}: ${err.message}${details}`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
resolve(stdout.trim() || "(no output)");
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
tools.push(tool);
|
|
360
|
+
}
|
|
361
|
+
return tools;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.FileSkillLoader = FileSkillLoader;
|
|
365
|
+
//# sourceMappingURL=file-skill-loader.js.map
|