@skillkit/core 1.12.0 → 1.13.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/dist/index.d.ts +687 -2
- package/dist/index.js +2027 -527
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10420,11 +10420,20 @@ import { parse as parseYaml7, stringify as stringifyYaml5 } from "yaml";
|
|
|
10420
10420
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
10421
10421
|
var ObservationStore = class {
|
|
10422
10422
|
filePath;
|
|
10423
|
+
projectPath;
|
|
10423
10424
|
data = null;
|
|
10424
10425
|
sessionId;
|
|
10425
|
-
|
|
10426
|
+
compressionThreshold;
|
|
10427
|
+
autoCompress;
|
|
10428
|
+
onThresholdReached;
|
|
10429
|
+
compressionInProgress = false;
|
|
10430
|
+
constructor(projectPath, sessionId, options = {}) {
|
|
10431
|
+
this.projectPath = projectPath;
|
|
10426
10432
|
this.filePath = join21(projectPath, ".skillkit", "memory", "observations.yaml");
|
|
10427
10433
|
this.sessionId = sessionId || randomUUID8();
|
|
10434
|
+
this.compressionThreshold = options.compressionThreshold ?? 50;
|
|
10435
|
+
this.autoCompress = options.autoCompress ?? true;
|
|
10436
|
+
this.onThresholdReached = options.onThresholdReached;
|
|
10428
10437
|
}
|
|
10429
10438
|
ensureDir() {
|
|
10430
10439
|
const dir = dirname4(this.filePath);
|
|
@@ -10475,8 +10484,66 @@ var ObservationStore = class {
|
|
|
10475
10484
|
};
|
|
10476
10485
|
data.observations.push(observation);
|
|
10477
10486
|
this.save();
|
|
10487
|
+
void this.checkAutoCompression().catch(() => {
|
|
10488
|
+
});
|
|
10478
10489
|
return observation;
|
|
10479
10490
|
}
|
|
10491
|
+
/**
|
|
10492
|
+
* Check if auto-compression should trigger
|
|
10493
|
+
*/
|
|
10494
|
+
async checkAutoCompression() {
|
|
10495
|
+
if (!this.autoCompress || this.compressionInProgress) return;
|
|
10496
|
+
if (!this.onThresholdReached) return;
|
|
10497
|
+
const count = this.count();
|
|
10498
|
+
if (count >= this.compressionThreshold) {
|
|
10499
|
+
this.compressionInProgress = true;
|
|
10500
|
+
try {
|
|
10501
|
+
const observations = this.getAll();
|
|
10502
|
+
await this.onThresholdReached(observations);
|
|
10503
|
+
} finally {
|
|
10504
|
+
this.compressionInProgress = false;
|
|
10505
|
+
}
|
|
10506
|
+
}
|
|
10507
|
+
}
|
|
10508
|
+
/**
|
|
10509
|
+
* Set auto-compression callback
|
|
10510
|
+
*/
|
|
10511
|
+
setAutoCompressCallback(callback) {
|
|
10512
|
+
this.onThresholdReached = callback;
|
|
10513
|
+
}
|
|
10514
|
+
/**
|
|
10515
|
+
* Enable/disable auto-compression
|
|
10516
|
+
*/
|
|
10517
|
+
setAutoCompress(enabled) {
|
|
10518
|
+
this.autoCompress = enabled;
|
|
10519
|
+
}
|
|
10520
|
+
/**
|
|
10521
|
+
* Set compression threshold
|
|
10522
|
+
*/
|
|
10523
|
+
setCompressionThreshold(threshold) {
|
|
10524
|
+
if (threshold < 1 || !Number.isInteger(threshold)) {
|
|
10525
|
+
throw new Error("Compression threshold must be a positive integer");
|
|
10526
|
+
}
|
|
10527
|
+
this.compressionThreshold = threshold;
|
|
10528
|
+
}
|
|
10529
|
+
/**
|
|
10530
|
+
* Get compression threshold
|
|
10531
|
+
*/
|
|
10532
|
+
getCompressionThreshold() {
|
|
10533
|
+
return this.compressionThreshold;
|
|
10534
|
+
}
|
|
10535
|
+
/**
|
|
10536
|
+
* Check if threshold is reached
|
|
10537
|
+
*/
|
|
10538
|
+
isThresholdReached() {
|
|
10539
|
+
return this.count() >= this.compressionThreshold;
|
|
10540
|
+
}
|
|
10541
|
+
/**
|
|
10542
|
+
* Get project path
|
|
10543
|
+
*/
|
|
10544
|
+
getProjectPath() {
|
|
10545
|
+
return this.projectPath;
|
|
10546
|
+
}
|
|
10480
10547
|
getAll() {
|
|
10481
10548
|
return this.load().observations;
|
|
10482
10549
|
}
|
|
@@ -11364,7 +11431,6 @@ var MemoryObserver = class {
|
|
|
11364
11431
|
}
|
|
11365
11432
|
let score = 50;
|
|
11366
11433
|
switch (event.type) {
|
|
11367
|
-
// High relevance events
|
|
11368
11434
|
case "error_encountered":
|
|
11369
11435
|
case "task_failed":
|
|
11370
11436
|
score = 85;
|
|
@@ -11384,7 +11450,6 @@ var MemoryObserver = class {
|
|
|
11384
11450
|
case "verification_failed":
|
|
11385
11451
|
score = 80;
|
|
11386
11452
|
break;
|
|
11387
|
-
// Medium relevance events
|
|
11388
11453
|
case "file_modified":
|
|
11389
11454
|
score = 60;
|
|
11390
11455
|
if (event.files && event.files.length > 3) {
|
|
@@ -11398,7 +11463,6 @@ var MemoryObserver = class {
|
|
|
11398
11463
|
case "verification_passed":
|
|
11399
11464
|
score = 50;
|
|
11400
11465
|
break;
|
|
11401
|
-
// Lower relevance events
|
|
11402
11466
|
case "task_start":
|
|
11403
11467
|
score = 30;
|
|
11404
11468
|
break;
|
|
@@ -12169,7 +12233,6 @@ Generate up to ${opts.maxLearnings} learnings. Only include learnings with impor
|
|
|
12169
12233
|
return typeof item === "object" && typeof item.title === "string" && typeof item.content === "string" && Array.isArray(item.tags) && typeof item.importance === "number";
|
|
12170
12234
|
}).map((item) => ({
|
|
12171
12235
|
...item,
|
|
12172
|
-
// Validate source observation IDs
|
|
12173
12236
|
sourceObservationIds: (item.sourceObservationIds || []).filter(
|
|
12174
12237
|
(id) => observationIds.has(id)
|
|
12175
12238
|
),
|
|
@@ -12284,7 +12347,6 @@ var LearningConsolidator = class {
|
|
|
12284
12347
|
const base = this.getBetterLearning(learning1, learning2);
|
|
12285
12348
|
const other = base === learning1 ? learning2 : learning1;
|
|
12286
12349
|
return {
|
|
12287
|
-
// Preserve the source from the base learning instead of hard-coding 'session'
|
|
12288
12350
|
source: base.source,
|
|
12289
12351
|
sourceObservations: [
|
|
12290
12352
|
...base.sourceObservations || [],
|
|
@@ -12716,169 +12778,1589 @@ var MemoryInjector = class {
|
|
|
12716
12778
|
escapeXml(text) {
|
|
12717
12779
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
12718
12780
|
}
|
|
12719
|
-
/**
|
|
12720
|
-
* Get memory summaries (for progressive disclosure)
|
|
12721
|
-
*/
|
|
12722
|
-
getSummaries(options = {}) {
|
|
12723
|
-
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
12724
|
-
const projectLearnings = this.projectStore.getAll();
|
|
12725
|
-
const globalLearnings = opts.includeGlobal ? this.globalStore.getAll() : [];
|
|
12726
|
-
const allLearnings = [...projectLearnings, ...globalLearnings];
|
|
12727
|
-
return allLearnings.map((learning) => ({
|
|
12781
|
+
/**
|
|
12782
|
+
* Get memory summaries (for progressive disclosure)
|
|
12783
|
+
*/
|
|
12784
|
+
getSummaries(options = {}) {
|
|
12785
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
12786
|
+
const projectLearnings = this.projectStore.getAll();
|
|
12787
|
+
const globalLearnings = opts.includeGlobal ? this.globalStore.getAll() : [];
|
|
12788
|
+
const allLearnings = [...projectLearnings, ...globalLearnings];
|
|
12789
|
+
return allLearnings.map((learning) => ({
|
|
12790
|
+
id: learning.id,
|
|
12791
|
+
title: learning.title,
|
|
12792
|
+
tags: learning.tags,
|
|
12793
|
+
relevance: this.scoreLearning(learning, opts).relevanceScore
|
|
12794
|
+
}));
|
|
12795
|
+
}
|
|
12796
|
+
/**
|
|
12797
|
+
* Get memory previews (for progressive disclosure)
|
|
12798
|
+
*/
|
|
12799
|
+
getPreviews(ids, options = {}) {
|
|
12800
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
12801
|
+
const previews = [];
|
|
12802
|
+
for (const id of ids) {
|
|
12803
|
+
let learning = this.projectStore.getById(id);
|
|
12804
|
+
if (!learning && opts.includeGlobal) {
|
|
12805
|
+
learning = this.globalStore.getById(id);
|
|
12806
|
+
}
|
|
12807
|
+
if (learning) {
|
|
12808
|
+
previews.push({
|
|
12809
|
+
id: learning.id,
|
|
12810
|
+
title: learning.title,
|
|
12811
|
+
tags: learning.tags,
|
|
12812
|
+
relevance: this.scoreLearning(learning, opts).relevanceScore,
|
|
12813
|
+
excerpt: learning.content.slice(0, 200),
|
|
12814
|
+
lastUsed: learning.lastUsed
|
|
12815
|
+
});
|
|
12816
|
+
}
|
|
12817
|
+
}
|
|
12818
|
+
return previews;
|
|
12819
|
+
}
|
|
12820
|
+
/**
|
|
12821
|
+
* Get full memories (for progressive disclosure)
|
|
12822
|
+
*/
|
|
12823
|
+
getFullMemories(ids, options = {}) {
|
|
12824
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
12825
|
+
const fullMemories = [];
|
|
12826
|
+
for (const id of ids) {
|
|
12827
|
+
let learning = this.projectStore.getById(id);
|
|
12828
|
+
if (!learning && opts.includeGlobal) {
|
|
12829
|
+
learning = this.globalStore.getById(id);
|
|
12830
|
+
}
|
|
12831
|
+
if (learning) {
|
|
12832
|
+
fullMemories.push({
|
|
12833
|
+
id: learning.id,
|
|
12834
|
+
title: learning.title,
|
|
12835
|
+
tags: learning.tags,
|
|
12836
|
+
relevance: this.scoreLearning(learning, opts).relevanceScore,
|
|
12837
|
+
excerpt: learning.content.slice(0, 200),
|
|
12838
|
+
lastUsed: learning.lastUsed,
|
|
12839
|
+
content: learning.content
|
|
12840
|
+
});
|
|
12841
|
+
}
|
|
12842
|
+
}
|
|
12843
|
+
return fullMemories;
|
|
12844
|
+
}
|
|
12845
|
+
/**
|
|
12846
|
+
* Search memories by query
|
|
12847
|
+
*/
|
|
12848
|
+
search(query, options = {}) {
|
|
12849
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options, currentTask: query };
|
|
12850
|
+
const projectResults = this.projectStore.search(query);
|
|
12851
|
+
const globalResults = opts.includeGlobal ? this.globalStore.search(query) : [];
|
|
12852
|
+
const allResults = [...projectResults, ...globalResults];
|
|
12853
|
+
return allResults.map((learning) => ({
|
|
12854
|
+
learning,
|
|
12855
|
+
...this.scoreLearning(learning, opts)
|
|
12856
|
+
})).filter((m) => m.relevanceScore >= opts.minRelevance).sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, opts.maxLearnings);
|
|
12857
|
+
}
|
|
12858
|
+
};
|
|
12859
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
12860
|
+
"the",
|
|
12861
|
+
"a",
|
|
12862
|
+
"an",
|
|
12863
|
+
"and",
|
|
12864
|
+
"or",
|
|
12865
|
+
"but",
|
|
12866
|
+
"in",
|
|
12867
|
+
"on",
|
|
12868
|
+
"at",
|
|
12869
|
+
"to",
|
|
12870
|
+
"for",
|
|
12871
|
+
"of",
|
|
12872
|
+
"with",
|
|
12873
|
+
"by",
|
|
12874
|
+
"from",
|
|
12875
|
+
"as",
|
|
12876
|
+
"is",
|
|
12877
|
+
"was",
|
|
12878
|
+
"are",
|
|
12879
|
+
"were",
|
|
12880
|
+
"been",
|
|
12881
|
+
"be",
|
|
12882
|
+
"have",
|
|
12883
|
+
"has",
|
|
12884
|
+
"had",
|
|
12885
|
+
"do",
|
|
12886
|
+
"does",
|
|
12887
|
+
"did",
|
|
12888
|
+
"will",
|
|
12889
|
+
"would",
|
|
12890
|
+
"could",
|
|
12891
|
+
"should",
|
|
12892
|
+
"may",
|
|
12893
|
+
"might",
|
|
12894
|
+
"must",
|
|
12895
|
+
"can",
|
|
12896
|
+
"this",
|
|
12897
|
+
"that",
|
|
12898
|
+
"these",
|
|
12899
|
+
"those",
|
|
12900
|
+
"i",
|
|
12901
|
+
"you",
|
|
12902
|
+
"he",
|
|
12903
|
+
"she",
|
|
12904
|
+
"it",
|
|
12905
|
+
"we",
|
|
12906
|
+
"they",
|
|
12907
|
+
"what",
|
|
12908
|
+
"which",
|
|
12909
|
+
"who",
|
|
12910
|
+
"when",
|
|
12911
|
+
"where",
|
|
12912
|
+
"why",
|
|
12913
|
+
"how",
|
|
12914
|
+
"all",
|
|
12915
|
+
"each",
|
|
12916
|
+
"every",
|
|
12917
|
+
"both",
|
|
12918
|
+
"few",
|
|
12919
|
+
"more",
|
|
12920
|
+
"most",
|
|
12921
|
+
"other",
|
|
12922
|
+
"some",
|
|
12923
|
+
"such",
|
|
12924
|
+
"no",
|
|
12925
|
+
"nor",
|
|
12926
|
+
"not",
|
|
12927
|
+
"only",
|
|
12928
|
+
"own",
|
|
12929
|
+
"same",
|
|
12930
|
+
"so",
|
|
12931
|
+
"than",
|
|
12932
|
+
"too",
|
|
12933
|
+
"very",
|
|
12934
|
+
"just",
|
|
12935
|
+
"also"
|
|
12936
|
+
]);
|
|
12937
|
+
function createMemoryInjector(projectPath, projectName, projectContext) {
|
|
12938
|
+
return new MemoryInjector(projectPath, projectName, projectContext);
|
|
12939
|
+
}
|
|
12940
|
+
|
|
12941
|
+
// src/memory/hooks/types.ts
|
|
12942
|
+
var DEFAULT_MEMORY_HOOK_CONFIG = {
|
|
12943
|
+
enabled: true,
|
|
12944
|
+
autoInjectOnSessionStart: true,
|
|
12945
|
+
autoCaptureToolUse: true,
|
|
12946
|
+
autoCompressOnSessionEnd: true,
|
|
12947
|
+
minRelevanceForCapture: 30,
|
|
12948
|
+
maxTokensForInjection: 2e3,
|
|
12949
|
+
compressionThreshold: 50,
|
|
12950
|
+
capturePatterns: ["*"],
|
|
12951
|
+
excludeTools: ["Read", "Glob", "Grep"]
|
|
12952
|
+
};
|
|
12953
|
+
|
|
12954
|
+
// src/memory/hooks/session-start.ts
|
|
12955
|
+
import { basename as basename9 } from "path";
|
|
12956
|
+
var SessionStartHook = class {
|
|
12957
|
+
config;
|
|
12958
|
+
projectPath;
|
|
12959
|
+
agent;
|
|
12960
|
+
constructor(projectPath, agent = "claude-code", config = {}) {
|
|
12961
|
+
this.projectPath = projectPath;
|
|
12962
|
+
this.agent = agent;
|
|
12963
|
+
this.config = { ...DEFAULT_MEMORY_HOOK_CONFIG, ...config };
|
|
12964
|
+
}
|
|
12965
|
+
/**
|
|
12966
|
+
* Execute the session start hook
|
|
12967
|
+
*/
|
|
12968
|
+
async execute(context) {
|
|
12969
|
+
if (!this.config.enabled || !this.config.autoInjectOnSessionStart) {
|
|
12970
|
+
return {
|
|
12971
|
+
injected: false,
|
|
12972
|
+
learnings: [],
|
|
12973
|
+
tokenCount: 0,
|
|
12974
|
+
formattedContent: ""
|
|
12975
|
+
};
|
|
12976
|
+
}
|
|
12977
|
+
const status = getMemoryStatus(this.projectPath);
|
|
12978
|
+
if (!status.projectMemoryExists && !status.globalMemoryExists) {
|
|
12979
|
+
return {
|
|
12980
|
+
injected: false,
|
|
12981
|
+
learnings: [],
|
|
12982
|
+
tokenCount: 0,
|
|
12983
|
+
formattedContent: ""
|
|
12984
|
+
};
|
|
12985
|
+
}
|
|
12986
|
+
const projectName = context.project_path ? basename9(context.project_path.replace(/\/+$/, "")) || void 0 : void 0;
|
|
12987
|
+
const injector = new MemoryInjector(this.projectPath, projectName);
|
|
12988
|
+
const result = await injector.injectForAgent(this.agent, {
|
|
12989
|
+
maxTokens: this.config.maxTokensForInjection,
|
|
12990
|
+
minRelevance: this.config.minRelevanceForCapture,
|
|
12991
|
+
maxLearnings: 10,
|
|
12992
|
+
includeGlobal: true,
|
|
12993
|
+
disclosureLevel: "preview"
|
|
12994
|
+
});
|
|
12995
|
+
const learnings = result.memories.map((m) => m.learning);
|
|
12996
|
+
return {
|
|
12997
|
+
injected: result.memories.length > 0,
|
|
12998
|
+
learnings,
|
|
12999
|
+
tokenCount: result.totalTokens,
|
|
13000
|
+
formattedContent: result.formattedContent
|
|
13001
|
+
};
|
|
13002
|
+
}
|
|
13003
|
+
/**
|
|
13004
|
+
* Generate Claude Code hook output format
|
|
13005
|
+
*/
|
|
13006
|
+
async generateHookOutput(context) {
|
|
13007
|
+
const result = await this.execute(context);
|
|
13008
|
+
return this.generateHookOutputFromResult(result);
|
|
13009
|
+
}
|
|
13010
|
+
/**
|
|
13011
|
+
* Generate hook output from pre-computed result (avoids double execution)
|
|
13012
|
+
*/
|
|
13013
|
+
generateHookOutputFromResult(result) {
|
|
13014
|
+
if (!result.injected || result.learnings.length === 0) {
|
|
13015
|
+
return { continue: true };
|
|
13016
|
+
}
|
|
13017
|
+
return {
|
|
13018
|
+
continue: true,
|
|
13019
|
+
inject: this.formatInjection(result)
|
|
13020
|
+
};
|
|
13021
|
+
}
|
|
13022
|
+
/**
|
|
13023
|
+
* Format learnings for injection into Claude Code context
|
|
13024
|
+
*/
|
|
13025
|
+
formatInjection(result) {
|
|
13026
|
+
if (result.learnings.length === 0) {
|
|
13027
|
+
return "";
|
|
13028
|
+
}
|
|
13029
|
+
const lines = [
|
|
13030
|
+
"<skillkit-memories>",
|
|
13031
|
+
`<!-- ${result.learnings.length} relevant learnings from previous sessions (${result.tokenCount} tokens) -->`,
|
|
13032
|
+
""
|
|
13033
|
+
];
|
|
13034
|
+
for (const learning of result.learnings) {
|
|
13035
|
+
lines.push(`## ${learning.title}`);
|
|
13036
|
+
lines.push(`Tags: ${learning.tags.join(", ")}`);
|
|
13037
|
+
if (learning.frameworks && learning.frameworks.length > 0) {
|
|
13038
|
+
lines.push(`Frameworks: ${learning.frameworks.join(", ")}`);
|
|
13039
|
+
}
|
|
13040
|
+
lines.push("");
|
|
13041
|
+
const preview = learning.content.slice(0, 200);
|
|
13042
|
+
lines.push(preview + (learning.content.length > 200 ? "..." : ""));
|
|
13043
|
+
lines.push("");
|
|
13044
|
+
}
|
|
13045
|
+
lines.push("</skillkit-memories>");
|
|
13046
|
+
return lines.join("\n");
|
|
13047
|
+
}
|
|
13048
|
+
/**
|
|
13049
|
+
* Get configuration
|
|
13050
|
+
*/
|
|
13051
|
+
getConfig() {
|
|
13052
|
+
return { ...this.config };
|
|
13053
|
+
}
|
|
13054
|
+
/**
|
|
13055
|
+
* Update configuration
|
|
13056
|
+
*/
|
|
13057
|
+
setConfig(config) {
|
|
13058
|
+
this.config = { ...this.config, ...config };
|
|
13059
|
+
}
|
|
13060
|
+
};
|
|
13061
|
+
function createSessionStartHook(projectPath, agent = "claude-code", config = {}) {
|
|
13062
|
+
return new SessionStartHook(projectPath, agent, config);
|
|
13063
|
+
}
|
|
13064
|
+
async function executeSessionStartHook(projectPath, context, config = {}) {
|
|
13065
|
+
const hook = new SessionStartHook(projectPath, context.agent || "claude-code", config);
|
|
13066
|
+
return hook.execute(context);
|
|
13067
|
+
}
|
|
13068
|
+
|
|
13069
|
+
// src/memory/hooks/post-tool-use.ts
|
|
13070
|
+
var TOOL_CATEGORIES = {
|
|
13071
|
+
Read: "tool_use",
|
|
13072
|
+
Write: "file_change",
|
|
13073
|
+
Edit: "file_change",
|
|
13074
|
+
Bash: "tool_use",
|
|
13075
|
+
Glob: "tool_use",
|
|
13076
|
+
Grep: "tool_use",
|
|
13077
|
+
WebFetch: "tool_use",
|
|
13078
|
+
WebSearch: "tool_use",
|
|
13079
|
+
Task: "checkpoint",
|
|
13080
|
+
AskUserQuestion: "decision"
|
|
13081
|
+
};
|
|
13082
|
+
var TOOL_RELEVANCE = {
|
|
13083
|
+
Write: 70,
|
|
13084
|
+
Edit: 70,
|
|
13085
|
+
Bash: 60,
|
|
13086
|
+
Task: 65,
|
|
13087
|
+
AskUserQuestion: 75,
|
|
13088
|
+
WebFetch: 50,
|
|
13089
|
+
WebSearch: 50,
|
|
13090
|
+
Read: 30,
|
|
13091
|
+
Glob: 25,
|
|
13092
|
+
Grep: 30
|
|
13093
|
+
};
|
|
13094
|
+
var PostToolUseHook = class {
|
|
13095
|
+
config;
|
|
13096
|
+
agent;
|
|
13097
|
+
store;
|
|
13098
|
+
pendingErrors = /* @__PURE__ */ new Map();
|
|
13099
|
+
constructor(projectPath, agent = "claude-code", config = {}, sessionId) {
|
|
13100
|
+
this.agent = agent;
|
|
13101
|
+
this.config = { ...DEFAULT_MEMORY_HOOK_CONFIG, ...config };
|
|
13102
|
+
this.store = new ObservationStore(projectPath, sessionId);
|
|
13103
|
+
}
|
|
13104
|
+
/**
|
|
13105
|
+
* Execute the post tool use hook
|
|
13106
|
+
*/
|
|
13107
|
+
async execute(event) {
|
|
13108
|
+
this.clearOldPendingErrors();
|
|
13109
|
+
if (!this.config.enabled || !this.config.autoCaptureToolUse) {
|
|
13110
|
+
return { captured: false, reason: "Hook disabled" };
|
|
13111
|
+
}
|
|
13112
|
+
if (this.shouldExcludeTool(event.tool_name)) {
|
|
13113
|
+
return { captured: false, reason: `Tool ${event.tool_name} is excluded` };
|
|
13114
|
+
}
|
|
13115
|
+
const relevance = this.calculateRelevance(event);
|
|
13116
|
+
if (relevance < this.config.minRelevanceForCapture) {
|
|
13117
|
+
return { captured: false, reason: `Relevance ${relevance} below threshold` };
|
|
13118
|
+
}
|
|
13119
|
+
const observationType = this.getObservationType(event);
|
|
13120
|
+
const content = this.extractContent(event);
|
|
13121
|
+
if (event.is_error) {
|
|
13122
|
+
const errorId = this.generateErrorId(content.error || content.action);
|
|
13123
|
+
this.pendingErrors.set(errorId, {
|
|
13124
|
+
error: content.error || content.action,
|
|
13125
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13126
|
+
});
|
|
13127
|
+
}
|
|
13128
|
+
const matchingSolution = event.is_error ? void 0 : this.findMatchingSolution(content);
|
|
13129
|
+
if (matchingSolution) {
|
|
13130
|
+
content.solution = content.action;
|
|
13131
|
+
content.context = `Solution for: ${matchingSolution.error}`;
|
|
13132
|
+
}
|
|
13133
|
+
const observation = this.store.add(
|
|
13134
|
+
observationType,
|
|
13135
|
+
content,
|
|
13136
|
+
this.agent,
|
|
13137
|
+
matchingSolution ? 95 : relevance
|
|
13138
|
+
);
|
|
13139
|
+
return { captured: true, observation };
|
|
13140
|
+
}
|
|
13141
|
+
/**
|
|
13142
|
+
* Generate Claude Code hook output format
|
|
13143
|
+
*/
|
|
13144
|
+
async generateHookOutput(event) {
|
|
13145
|
+
const result = await this.execute(event);
|
|
13146
|
+
return this.generateHookOutputFromResult(event, result);
|
|
13147
|
+
}
|
|
13148
|
+
/**
|
|
13149
|
+
* Generate hook output from pre-computed result (avoids double execution)
|
|
13150
|
+
*/
|
|
13151
|
+
generateHookOutputFromResult(event, result) {
|
|
13152
|
+
return {
|
|
13153
|
+
continue: true,
|
|
13154
|
+
message: result.captured ? `Observation captured: ${event.tool_name}` : void 0
|
|
13155
|
+
};
|
|
13156
|
+
}
|
|
13157
|
+
/**
|
|
13158
|
+
* Get configuration
|
|
13159
|
+
*/
|
|
13160
|
+
getConfig() {
|
|
13161
|
+
return { ...this.config };
|
|
13162
|
+
}
|
|
13163
|
+
/**
|
|
13164
|
+
* Update configuration
|
|
13165
|
+
*/
|
|
13166
|
+
setConfig(config) {
|
|
13167
|
+
this.config = { ...this.config, ...config };
|
|
13168
|
+
}
|
|
13169
|
+
/**
|
|
13170
|
+
* Record an error explicitly
|
|
13171
|
+
*/
|
|
13172
|
+
recordError(error, context) {
|
|
13173
|
+
const errorId = this.generateErrorId(error);
|
|
13174
|
+
this.pendingErrors.set(errorId, {
|
|
13175
|
+
error,
|
|
13176
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13177
|
+
});
|
|
13178
|
+
return this.store.add(
|
|
13179
|
+
"error",
|
|
13180
|
+
{
|
|
13181
|
+
action: "Error encountered",
|
|
13182
|
+
context,
|
|
13183
|
+
error,
|
|
13184
|
+
tags: ["error"]
|
|
13185
|
+
},
|
|
13186
|
+
this.agent,
|
|
13187
|
+
80
|
|
13188
|
+
);
|
|
13189
|
+
}
|
|
13190
|
+
/**
|
|
13191
|
+
* Record a solution explicitly
|
|
13192
|
+
*/
|
|
13193
|
+
recordSolution(solution, context, relatedError) {
|
|
13194
|
+
let relevance = 70;
|
|
13195
|
+
let solutionContext = context;
|
|
13196
|
+
if (relatedError) {
|
|
13197
|
+
const errorId = this.generateErrorId(relatedError);
|
|
13198
|
+
if (this.pendingErrors.has(errorId)) {
|
|
13199
|
+
this.pendingErrors.delete(errorId);
|
|
13200
|
+
relevance = 95;
|
|
13201
|
+
solutionContext = `Solution for: ${relatedError}`;
|
|
13202
|
+
}
|
|
13203
|
+
}
|
|
13204
|
+
return this.store.add(
|
|
13205
|
+
"solution",
|
|
13206
|
+
{
|
|
13207
|
+
action: solution,
|
|
13208
|
+
context: solutionContext,
|
|
13209
|
+
solution,
|
|
13210
|
+
tags: ["solution"]
|
|
13211
|
+
},
|
|
13212
|
+
this.agent,
|
|
13213
|
+
relevance
|
|
13214
|
+
);
|
|
13215
|
+
}
|
|
13216
|
+
/**
|
|
13217
|
+
* Record a decision explicitly
|
|
13218
|
+
*/
|
|
13219
|
+
recordDecision(decision, options, context) {
|
|
13220
|
+
return this.store.add(
|
|
13221
|
+
"decision",
|
|
13222
|
+
{
|
|
13223
|
+
action: decision,
|
|
13224
|
+
context: `Options: ${options.join(", ")}. Context: ${context}`,
|
|
13225
|
+
tags: ["decision", "architecture"]
|
|
13226
|
+
},
|
|
13227
|
+
this.agent,
|
|
13228
|
+
75
|
|
13229
|
+
);
|
|
13230
|
+
}
|
|
13231
|
+
/**
|
|
13232
|
+
* Record file modifications
|
|
13233
|
+
*/
|
|
13234
|
+
recordFileChange(files, action, context) {
|
|
13235
|
+
return this.store.add(
|
|
13236
|
+
"file_change",
|
|
13237
|
+
{
|
|
13238
|
+
action,
|
|
13239
|
+
context,
|
|
13240
|
+
files,
|
|
13241
|
+
tags: ["file-change"]
|
|
13242
|
+
},
|
|
13243
|
+
this.agent,
|
|
13244
|
+
files.length > 3 ? 75 : 60
|
|
13245
|
+
);
|
|
13246
|
+
}
|
|
13247
|
+
/**
|
|
13248
|
+
* Get pending errors that haven't been resolved
|
|
13249
|
+
*/
|
|
13250
|
+
getPendingErrors() {
|
|
13251
|
+
return Array.from(this.pendingErrors.values());
|
|
13252
|
+
}
|
|
13253
|
+
/**
|
|
13254
|
+
* Clear old pending errors (older than 30 minutes)
|
|
13255
|
+
*/
|
|
13256
|
+
clearOldPendingErrors() {
|
|
13257
|
+
const thirtyMinutesAgo = Date.now() - 30 * 60 * 1e3;
|
|
13258
|
+
let cleared = 0;
|
|
13259
|
+
for (const [id, { timestamp }] of this.pendingErrors) {
|
|
13260
|
+
if (new Date(timestamp).getTime() < thirtyMinutesAgo) {
|
|
13261
|
+
this.pendingErrors.delete(id);
|
|
13262
|
+
cleared++;
|
|
13263
|
+
}
|
|
13264
|
+
}
|
|
13265
|
+
return cleared;
|
|
13266
|
+
}
|
|
13267
|
+
/**
|
|
13268
|
+
* Get observation store
|
|
13269
|
+
*/
|
|
13270
|
+
getStore() {
|
|
13271
|
+
return this.store;
|
|
13272
|
+
}
|
|
13273
|
+
/**
|
|
13274
|
+
* Get observation count
|
|
13275
|
+
*/
|
|
13276
|
+
getObservationCount() {
|
|
13277
|
+
return this.store.count();
|
|
13278
|
+
}
|
|
13279
|
+
shouldExcludeTool(toolName) {
|
|
13280
|
+
return this.config.excludeTools?.includes(toolName) ?? false;
|
|
13281
|
+
}
|
|
13282
|
+
calculateRelevance(event) {
|
|
13283
|
+
let relevance = TOOL_RELEVANCE[event.tool_name] || 50;
|
|
13284
|
+
if (event.is_error) {
|
|
13285
|
+
relevance = Math.max(relevance, 80);
|
|
13286
|
+
}
|
|
13287
|
+
if (event.duration_ms && event.duration_ms > 1e4) {
|
|
13288
|
+
relevance = Math.min(relevance + 10, 100);
|
|
13289
|
+
}
|
|
13290
|
+
const result = event.tool_result || "";
|
|
13291
|
+
if (result.includes("error") || result.includes("Error") || result.includes("failed")) {
|
|
13292
|
+
relevance = Math.max(relevance, 75);
|
|
13293
|
+
}
|
|
13294
|
+
if (result.includes("success") || result.includes("created") || result.includes("updated")) {
|
|
13295
|
+
relevance = Math.min(relevance + 5, 100);
|
|
13296
|
+
}
|
|
13297
|
+
return relevance;
|
|
13298
|
+
}
|
|
13299
|
+
getObservationType(event) {
|
|
13300
|
+
if (event.is_error) {
|
|
13301
|
+
return "error";
|
|
13302
|
+
}
|
|
13303
|
+
return TOOL_CATEGORIES[event.tool_name] || "tool_use";
|
|
13304
|
+
}
|
|
13305
|
+
extractContent(event) {
|
|
13306
|
+
const content = {
|
|
13307
|
+
action: `${event.tool_name}: ${this.summarizeInput(event.tool_input)}`,
|
|
13308
|
+
context: this.extractContext(event),
|
|
13309
|
+
tags: [event.tool_name.toLowerCase()]
|
|
13310
|
+
};
|
|
13311
|
+
if (event.is_error && event.tool_result) {
|
|
13312
|
+
content.error = event.tool_result.slice(0, 500);
|
|
13313
|
+
}
|
|
13314
|
+
if (event.tool_result && !event.is_error) {
|
|
13315
|
+
content.result = event.tool_result.slice(0, 200);
|
|
13316
|
+
}
|
|
13317
|
+
const files = this.extractFiles(event);
|
|
13318
|
+
if (files.length > 0) {
|
|
13319
|
+
content.files = files;
|
|
13320
|
+
}
|
|
13321
|
+
return content;
|
|
13322
|
+
}
|
|
13323
|
+
summarizeInput(input) {
|
|
13324
|
+
if (input.file_path) return String(input.file_path);
|
|
13325
|
+
if (input.pattern) return String(input.pattern);
|
|
13326
|
+
if (input.command) {
|
|
13327
|
+
const cmd = String(input.command);
|
|
13328
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "..." : cmd;
|
|
13329
|
+
}
|
|
13330
|
+
if (input.query) return String(input.query).slice(0, 100);
|
|
13331
|
+
const keys = Object.keys(input);
|
|
13332
|
+
if (keys.length === 0) return "(no input)";
|
|
13333
|
+
return keys.slice(0, 3).join(", ");
|
|
13334
|
+
}
|
|
13335
|
+
extractContext(event) {
|
|
13336
|
+
const parts = [];
|
|
13337
|
+
if (event.duration_ms) {
|
|
13338
|
+
parts.push(`Duration: ${event.duration_ms}ms`);
|
|
13339
|
+
}
|
|
13340
|
+
if (event.is_error) {
|
|
13341
|
+
parts.push("Result: Error");
|
|
13342
|
+
} else if (event.tool_result) {
|
|
13343
|
+
const resultPreview = event.tool_result.slice(0, 100);
|
|
13344
|
+
parts.push(`Result: ${resultPreview}${event.tool_result.length > 100 ? "..." : ""}`);
|
|
13345
|
+
}
|
|
13346
|
+
return parts.join(". ") || "No additional context";
|
|
13347
|
+
}
|
|
13348
|
+
extractFiles(event) {
|
|
13349
|
+
const files = [];
|
|
13350
|
+
if (event.tool_input.file_path) {
|
|
13351
|
+
files.push(String(event.tool_input.file_path));
|
|
13352
|
+
}
|
|
13353
|
+
if (event.tool_input.path) {
|
|
13354
|
+
files.push(String(event.tool_input.path));
|
|
13355
|
+
}
|
|
13356
|
+
return files;
|
|
13357
|
+
}
|
|
13358
|
+
generateErrorId(error) {
|
|
13359
|
+
const normalized = error.toLowerCase().replace(/[0-9]+/g, "N").replace(/['"`]/g, "").replace(/\s+/g, " ").trim().slice(0, 100);
|
|
13360
|
+
return normalized;
|
|
13361
|
+
}
|
|
13362
|
+
findMatchingSolution(content) {
|
|
13363
|
+
const actionLower = content.action.toLowerCase();
|
|
13364
|
+
const contextLower = content.context.toLowerCase();
|
|
13365
|
+
for (const [errorId, errorData] of this.pendingErrors) {
|
|
13366
|
+
const hasKeywordMatch = actionLower.includes("fix") || actionLower.includes("resolve") || actionLower.includes("solution") || contextLower.includes("fix") || contextLower.includes("resolve");
|
|
13367
|
+
if (hasKeywordMatch) {
|
|
13368
|
+
const errorWords = errorId.split(" ").filter((w) => w.length > 3);
|
|
13369
|
+
const textWords = new Set((actionLower + " " + contextLower).split(/\s+/));
|
|
13370
|
+
const matchCount = errorWords.filter((w) => textWords.has(w)).length;
|
|
13371
|
+
if (matchCount >= 2 || matchCount >= 1 && hasKeywordMatch) {
|
|
13372
|
+
this.pendingErrors.delete(errorId);
|
|
13373
|
+
return errorData;
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13376
|
+
}
|
|
13377
|
+
return void 0;
|
|
13378
|
+
}
|
|
13379
|
+
};
|
|
13380
|
+
function createPostToolUseHook(projectPath, agent = "claude-code", config = {}, sessionId) {
|
|
13381
|
+
return new PostToolUseHook(projectPath, agent, config, sessionId);
|
|
13382
|
+
}
|
|
13383
|
+
async function executePostToolUseHook(projectPath, event, config = {}, sessionId) {
|
|
13384
|
+
const hook = new PostToolUseHook(projectPath, "claude-code", config, sessionId);
|
|
13385
|
+
return hook.execute(event);
|
|
13386
|
+
}
|
|
13387
|
+
|
|
13388
|
+
// src/memory/hooks/session-end.ts
|
|
13389
|
+
import { basename as basename10 } from "path";
|
|
13390
|
+
var SessionEndHook = class {
|
|
13391
|
+
config;
|
|
13392
|
+
projectPath;
|
|
13393
|
+
agent;
|
|
13394
|
+
constructor(projectPath, agent = "claude-code", config = {}) {
|
|
13395
|
+
this.projectPath = projectPath;
|
|
13396
|
+
this.agent = agent;
|
|
13397
|
+
this.config = { ...DEFAULT_MEMORY_HOOK_CONFIG, ...config };
|
|
13398
|
+
}
|
|
13399
|
+
/**
|
|
13400
|
+
* Execute the session end hook
|
|
13401
|
+
*/
|
|
13402
|
+
async execute(context) {
|
|
13403
|
+
if (!this.config.enabled || !this.config.autoCompressOnSessionEnd) {
|
|
13404
|
+
return {
|
|
13405
|
+
compressed: false,
|
|
13406
|
+
observationCount: 0,
|
|
13407
|
+
learningCount: 0,
|
|
13408
|
+
learnings: []
|
|
13409
|
+
};
|
|
13410
|
+
}
|
|
13411
|
+
const store = new ObservationStore(this.projectPath, context.session_id);
|
|
13412
|
+
const observations = store.getAll();
|
|
13413
|
+
if (observations.length < 3) {
|
|
13414
|
+
return {
|
|
13415
|
+
compressed: false,
|
|
13416
|
+
observationCount: observations.length,
|
|
13417
|
+
learningCount: 0,
|
|
13418
|
+
learnings: []
|
|
13419
|
+
};
|
|
13420
|
+
}
|
|
13421
|
+
const projectName = context.project_path ? basename10(context.project_path.replace(/\/+$/, "")) || void 0 : void 0;
|
|
13422
|
+
const compressor = new MemoryCompressor(this.projectPath, {
|
|
13423
|
+
scope: "project",
|
|
13424
|
+
projectName
|
|
13425
|
+
});
|
|
13426
|
+
const { learnings, result } = await compressor.compressAndStore(observations, {
|
|
13427
|
+
minObservations: 3,
|
|
13428
|
+
maxLearnings: 10,
|
|
13429
|
+
minImportance: 4,
|
|
13430
|
+
includeLowRelevance: false,
|
|
13431
|
+
additionalTags: ["session-end", this.agent]
|
|
13432
|
+
});
|
|
13433
|
+
if (result.processedObservationIds.length > 0) {
|
|
13434
|
+
store.deleteMany(result.processedObservationIds);
|
|
13435
|
+
}
|
|
13436
|
+
return {
|
|
13437
|
+
compressed: learnings.length > 0,
|
|
13438
|
+
observationCount: observations.length,
|
|
13439
|
+
learningCount: learnings.length,
|
|
13440
|
+
learnings
|
|
13441
|
+
};
|
|
13442
|
+
}
|
|
13443
|
+
/**
|
|
13444
|
+
* Generate Claude Code hook output format
|
|
13445
|
+
*/
|
|
13446
|
+
async generateHookOutput(context) {
|
|
13447
|
+
const result = await this.execute(context);
|
|
13448
|
+
return this.generateHookOutputFromResult(result);
|
|
13449
|
+
}
|
|
13450
|
+
/**
|
|
13451
|
+
* Generate hook output from pre-computed result (avoids double execution)
|
|
13452
|
+
*/
|
|
13453
|
+
generateHookOutputFromResult(result) {
|
|
13454
|
+
let message;
|
|
13455
|
+
if (result.compressed) {
|
|
13456
|
+
message = `Session memory: ${result.observationCount} observations \u2192 ${result.learningCount} learnings`;
|
|
13457
|
+
}
|
|
13458
|
+
return {
|
|
13459
|
+
continue: true,
|
|
13460
|
+
message
|
|
13461
|
+
};
|
|
13462
|
+
}
|
|
13463
|
+
/**
|
|
13464
|
+
* Force compression regardless of settings
|
|
13465
|
+
*/
|
|
13466
|
+
async forceCompress(sessionId) {
|
|
13467
|
+
const store = new ObservationStore(this.projectPath, sessionId);
|
|
13468
|
+
const observations = store.getAll();
|
|
13469
|
+
if (observations.length === 0) {
|
|
13470
|
+
return {
|
|
13471
|
+
compressed: false,
|
|
13472
|
+
observationCount: 0,
|
|
13473
|
+
learningCount: 0,
|
|
13474
|
+
learnings: []
|
|
13475
|
+
};
|
|
13476
|
+
}
|
|
13477
|
+
const compressor = new MemoryCompressor(this.projectPath, {
|
|
13478
|
+
scope: "project"
|
|
13479
|
+
});
|
|
13480
|
+
const { learnings, result } = await compressor.compressAndStore(observations, {
|
|
13481
|
+
minObservations: 1,
|
|
13482
|
+
maxLearnings: 20,
|
|
13483
|
+
minImportance: 3,
|
|
13484
|
+
includeLowRelevance: true
|
|
13485
|
+
});
|
|
13486
|
+
if (result.processedObservationIds.length > 0) {
|
|
13487
|
+
store.deleteMany(result.processedObservationIds);
|
|
13488
|
+
}
|
|
13489
|
+
return {
|
|
13490
|
+
compressed: learnings.length > 0,
|
|
13491
|
+
observationCount: observations.length,
|
|
13492
|
+
learningCount: learnings.length,
|
|
13493
|
+
learnings
|
|
13494
|
+
};
|
|
13495
|
+
}
|
|
13496
|
+
/**
|
|
13497
|
+
* Preview what would be compressed (dry-run)
|
|
13498
|
+
*/
|
|
13499
|
+
async preview(sessionId) {
|
|
13500
|
+
const store = new ObservationStore(this.projectPath, sessionId);
|
|
13501
|
+
const observations = store.getAll();
|
|
13502
|
+
const types = {};
|
|
13503
|
+
for (const obs of observations) {
|
|
13504
|
+
types[obs.type] = (types[obs.type] || 0) + 1;
|
|
13505
|
+
}
|
|
13506
|
+
const compressor = new MemoryCompressor(this.projectPath, {
|
|
13507
|
+
scope: "project"
|
|
13508
|
+
});
|
|
13509
|
+
const result = await compressor.compress(observations, {
|
|
13510
|
+
minObservations: 3,
|
|
13511
|
+
maxLearnings: 10,
|
|
13512
|
+
minImportance: 4
|
|
13513
|
+
});
|
|
13514
|
+
return {
|
|
13515
|
+
wouldCompress: result.learnings.length > 0,
|
|
13516
|
+
observationCount: observations.length,
|
|
13517
|
+
estimatedLearnings: result.learnings.length,
|
|
13518
|
+
observationTypes: types
|
|
13519
|
+
};
|
|
13520
|
+
}
|
|
13521
|
+
/**
|
|
13522
|
+
* Get configuration
|
|
13523
|
+
*/
|
|
13524
|
+
getConfig() {
|
|
13525
|
+
return { ...this.config };
|
|
13526
|
+
}
|
|
13527
|
+
/**
|
|
13528
|
+
* Update configuration
|
|
13529
|
+
*/
|
|
13530
|
+
setConfig(config) {
|
|
13531
|
+
this.config = { ...this.config, ...config };
|
|
13532
|
+
}
|
|
13533
|
+
};
|
|
13534
|
+
function createSessionEndHook(projectPath, agent = "claude-code", config = {}) {
|
|
13535
|
+
return new SessionEndHook(projectPath, agent, config);
|
|
13536
|
+
}
|
|
13537
|
+
async function executeSessionEndHook(projectPath, context, config = {}) {
|
|
13538
|
+
const hook = new SessionEndHook(projectPath, context.agent || "claude-code", config);
|
|
13539
|
+
return hook.execute(context);
|
|
13540
|
+
}
|
|
13541
|
+
|
|
13542
|
+
// src/memory/hooks/manager.ts
|
|
13543
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
13544
|
+
var MemoryHookManager = class {
|
|
13545
|
+
config;
|
|
13546
|
+
projectPath;
|
|
13547
|
+
agent;
|
|
13548
|
+
sessionId;
|
|
13549
|
+
startedAt;
|
|
13550
|
+
sessionStartHook;
|
|
13551
|
+
postToolUseHook;
|
|
13552
|
+
sessionEndHook;
|
|
13553
|
+
stats;
|
|
13554
|
+
constructor(projectPath, agent = "claude-code", config = {}, sessionId) {
|
|
13555
|
+
this.projectPath = projectPath;
|
|
13556
|
+
this.agent = agent;
|
|
13557
|
+
this.config = { ...DEFAULT_MEMORY_HOOK_CONFIG, ...config };
|
|
13558
|
+
this.sessionId = sessionId || randomUUID10();
|
|
13559
|
+
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13560
|
+
this.sessionStartHook = new SessionStartHook(projectPath, agent, this.config);
|
|
13561
|
+
this.postToolUseHook = new PostToolUseHook(projectPath, agent, this.config, this.sessionId);
|
|
13562
|
+
this.sessionEndHook = new SessionEndHook(projectPath, agent, this.config);
|
|
13563
|
+
this.stats = {
|
|
13564
|
+
sessionId: this.sessionId,
|
|
13565
|
+
startedAt: this.startedAt,
|
|
13566
|
+
observationsCaptured: 0,
|
|
13567
|
+
learningsInjected: 0,
|
|
13568
|
+
tokensUsed: 0,
|
|
13569
|
+
toolCallsCaptured: 0,
|
|
13570
|
+
errorsRecorded: 0,
|
|
13571
|
+
solutionsRecorded: 0
|
|
13572
|
+
};
|
|
13573
|
+
}
|
|
13574
|
+
/**
|
|
13575
|
+
* Handle session start
|
|
13576
|
+
*/
|
|
13577
|
+
async onSessionStart(workingDirectory) {
|
|
13578
|
+
const context = {
|
|
13579
|
+
session_id: this.sessionId,
|
|
13580
|
+
project_path: this.projectPath,
|
|
13581
|
+
agent: this.agent,
|
|
13582
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13583
|
+
working_directory: workingDirectory
|
|
13584
|
+
};
|
|
13585
|
+
const result = await this.sessionStartHook.execute(context);
|
|
13586
|
+
this.stats.learningsInjected = result.learnings.length;
|
|
13587
|
+
this.stats.tokensUsed = result.tokenCount;
|
|
13588
|
+
return this.sessionStartHook.generateHookOutputFromResult(result);
|
|
13589
|
+
}
|
|
13590
|
+
/**
|
|
13591
|
+
* Handle tool use
|
|
13592
|
+
*/
|
|
13593
|
+
async onToolUse(event) {
|
|
13594
|
+
const result = await this.postToolUseHook.execute(event);
|
|
13595
|
+
if (result.captured) {
|
|
13596
|
+
this.stats.observationsCaptured++;
|
|
13597
|
+
this.stats.toolCallsCaptured++;
|
|
13598
|
+
if (result.observation?.type === "error") {
|
|
13599
|
+
this.stats.errorsRecorded++;
|
|
13600
|
+
} else if (result.observation?.type === "solution") {
|
|
13601
|
+
this.stats.solutionsRecorded++;
|
|
13602
|
+
}
|
|
13603
|
+
}
|
|
13604
|
+
await this.checkAutoCompression();
|
|
13605
|
+
return this.postToolUseHook.generateHookOutputFromResult(event, result);
|
|
13606
|
+
}
|
|
13607
|
+
/**
|
|
13608
|
+
* Handle session end
|
|
13609
|
+
*/
|
|
13610
|
+
async onSessionEnd(toolCallsCount) {
|
|
13611
|
+
const context = {
|
|
13612
|
+
session_id: this.sessionId,
|
|
13613
|
+
project_path: this.projectPath,
|
|
13614
|
+
agent: this.agent,
|
|
13615
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13616
|
+
duration_ms: Date.now() - new Date(this.startedAt).getTime(),
|
|
13617
|
+
tool_calls_count: toolCallsCount
|
|
13618
|
+
};
|
|
13619
|
+
const result = await this.sessionEndHook.execute(context);
|
|
13620
|
+
return this.sessionEndHook.generateHookOutputFromResult(result);
|
|
13621
|
+
}
|
|
13622
|
+
/**
|
|
13623
|
+
* Record an error manually
|
|
13624
|
+
*/
|
|
13625
|
+
recordError(error, context) {
|
|
13626
|
+
const obs = this.postToolUseHook.recordError(error, context);
|
|
13627
|
+
this.stats.observationsCaptured++;
|
|
13628
|
+
this.stats.errorsRecorded++;
|
|
13629
|
+
return obs;
|
|
13630
|
+
}
|
|
13631
|
+
/**
|
|
13632
|
+
* Record a solution manually
|
|
13633
|
+
*/
|
|
13634
|
+
recordSolution(solution, context, relatedError) {
|
|
13635
|
+
const obs = this.postToolUseHook.recordSolution(solution, context, relatedError);
|
|
13636
|
+
this.stats.observationsCaptured++;
|
|
13637
|
+
this.stats.solutionsRecorded++;
|
|
13638
|
+
return obs;
|
|
13639
|
+
}
|
|
13640
|
+
/**
|
|
13641
|
+
* Record a decision manually
|
|
13642
|
+
*/
|
|
13643
|
+
recordDecision(decision, options, context) {
|
|
13644
|
+
const obs = this.postToolUseHook.recordDecision(decision, options, context);
|
|
13645
|
+
this.stats.observationsCaptured++;
|
|
13646
|
+
return obs;
|
|
13647
|
+
}
|
|
13648
|
+
/**
|
|
13649
|
+
* Record file changes manually
|
|
13650
|
+
*/
|
|
13651
|
+
recordFileChange(files, action, context) {
|
|
13652
|
+
const obs = this.postToolUseHook.recordFileChange(files, action, context);
|
|
13653
|
+
this.stats.observationsCaptured++;
|
|
13654
|
+
return obs;
|
|
13655
|
+
}
|
|
13656
|
+
/**
|
|
13657
|
+
* Force compression (regardless of threshold)
|
|
13658
|
+
*/
|
|
13659
|
+
async forceCompress() {
|
|
13660
|
+
const result = await this.sessionEndHook.forceCompress(this.sessionId);
|
|
13661
|
+
return result.learnings;
|
|
13662
|
+
}
|
|
13663
|
+
/**
|
|
13664
|
+
* Preview compression without executing
|
|
13665
|
+
*/
|
|
13666
|
+
async previewCompression() {
|
|
13667
|
+
return this.sessionEndHook.preview(this.sessionId);
|
|
13668
|
+
}
|
|
13669
|
+
/**
|
|
13670
|
+
* Get current stats
|
|
13671
|
+
*/
|
|
13672
|
+
getStats() {
|
|
13673
|
+
return { ...this.stats };
|
|
13674
|
+
}
|
|
13675
|
+
/**
|
|
13676
|
+
* Get session ID
|
|
13677
|
+
*/
|
|
13678
|
+
getSessionId() {
|
|
13679
|
+
return this.sessionId;
|
|
13680
|
+
}
|
|
13681
|
+
/**
|
|
13682
|
+
* Get observation count
|
|
13683
|
+
*/
|
|
13684
|
+
getObservationCount() {
|
|
13685
|
+
return this.postToolUseHook.getObservationCount();
|
|
13686
|
+
}
|
|
13687
|
+
/**
|
|
13688
|
+
* Get pending errors
|
|
13689
|
+
*/
|
|
13690
|
+
getPendingErrors() {
|
|
13691
|
+
return this.postToolUseHook.getPendingErrors();
|
|
13692
|
+
}
|
|
13693
|
+
/**
|
|
13694
|
+
* Get configuration
|
|
13695
|
+
*/
|
|
13696
|
+
getConfig() {
|
|
13697
|
+
return { ...this.config };
|
|
13698
|
+
}
|
|
13699
|
+
/**
|
|
13700
|
+
* Update configuration
|
|
13701
|
+
*/
|
|
13702
|
+
setConfig(config) {
|
|
13703
|
+
this.config = { ...this.config, ...config };
|
|
13704
|
+
this.sessionStartHook.setConfig(this.config);
|
|
13705
|
+
this.postToolUseHook.setConfig(this.config);
|
|
13706
|
+
this.sessionEndHook.setConfig(this.config);
|
|
13707
|
+
}
|
|
13708
|
+
/**
|
|
13709
|
+
* Check if auto-compression should trigger
|
|
13710
|
+
*/
|
|
13711
|
+
async checkAutoCompression() {
|
|
13712
|
+
if (!this.config.enabled) return;
|
|
13713
|
+
const count = this.postToolUseHook.getObservationCount();
|
|
13714
|
+
if (count >= this.config.compressionThreshold) {
|
|
13715
|
+
await this.sessionEndHook.forceCompress(this.sessionId);
|
|
13716
|
+
}
|
|
13717
|
+
}
|
|
13718
|
+
/**
|
|
13719
|
+
* Generate Claude Code hooks.json configuration
|
|
13720
|
+
*/
|
|
13721
|
+
generateClaudeCodeHooksConfig() {
|
|
13722
|
+
return {
|
|
13723
|
+
hooks: [
|
|
13724
|
+
{
|
|
13725
|
+
matcher: ".*",
|
|
13726
|
+
hooks: [
|
|
13727
|
+
{
|
|
13728
|
+
type: "command",
|
|
13729
|
+
command: `npx skillkit memory hook session-start --project "${this.projectPath}"`,
|
|
13730
|
+
event: "SessionStart"
|
|
13731
|
+
},
|
|
13732
|
+
{
|
|
13733
|
+
type: "command",
|
|
13734
|
+
command: `npx skillkit memory hook post-tool-use --project "${this.projectPath}"`,
|
|
13735
|
+
event: "PostToolUse"
|
|
13736
|
+
},
|
|
13737
|
+
{
|
|
13738
|
+
type: "command",
|
|
13739
|
+
command: `npx skillkit memory hook session-end --project "${this.projectPath}"`,
|
|
13740
|
+
event: "SessionEnd"
|
|
13741
|
+
}
|
|
13742
|
+
]
|
|
13743
|
+
}
|
|
13744
|
+
]
|
|
13745
|
+
};
|
|
13746
|
+
}
|
|
13747
|
+
};
|
|
13748
|
+
function createMemoryHookManager(projectPath, agent = "claude-code", config = {}, sessionId) {
|
|
13749
|
+
return new MemoryHookManager(projectPath, agent, config, sessionId);
|
|
13750
|
+
}
|
|
13751
|
+
|
|
13752
|
+
// src/memory/claude-md-updater.ts
|
|
13753
|
+
import { existsSync as existsSync26, readFileSync as readFileSync16, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15 } from "fs";
|
|
13754
|
+
import { join as join25, dirname as dirname7, basename as basename11 } from "path";
|
|
13755
|
+
import { homedir as homedir12 } from "os";
|
|
13756
|
+
var SKILLKIT_MARKER = "<!-- Auto-populated by SkillKit -->";
|
|
13757
|
+
var DEFAULT_UPDATE_OPTIONS = {
|
|
13758
|
+
minEffectiveness: 60,
|
|
13759
|
+
maxLearnings: 20,
|
|
13760
|
+
includeGlobal: false,
|
|
13761
|
+
preserveManualEntries: true,
|
|
13762
|
+
sectionTitle: "LEARNED",
|
|
13763
|
+
addTimestamp: true
|
|
13764
|
+
};
|
|
13765
|
+
var ClaudeMdUpdater = class {
|
|
13766
|
+
projectPath;
|
|
13767
|
+
claudeMdPath;
|
|
13768
|
+
constructor(projectPath, claudeMdPath) {
|
|
13769
|
+
this.projectPath = projectPath;
|
|
13770
|
+
this.claudeMdPath = claudeMdPath || join25(projectPath, "CLAUDE.md");
|
|
13771
|
+
}
|
|
13772
|
+
/**
|
|
13773
|
+
* Parse CLAUDE.md to extract structure
|
|
13774
|
+
*/
|
|
13775
|
+
parse() {
|
|
13776
|
+
if (!existsSync26(this.claudeMdPath)) {
|
|
13777
|
+
return {
|
|
13778
|
+
content: "",
|
|
13779
|
+
sections: /* @__PURE__ */ new Map(),
|
|
13780
|
+
hasLearnedSection: false
|
|
13781
|
+
};
|
|
13782
|
+
}
|
|
13783
|
+
const content = readFileSync16(this.claudeMdPath, "utf-8");
|
|
13784
|
+
const sections = /* @__PURE__ */ new Map();
|
|
13785
|
+
const lines = content.split("\n");
|
|
13786
|
+
let currentSection = null;
|
|
13787
|
+
let sectionStart = 0;
|
|
13788
|
+
let sectionContent = [];
|
|
13789
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13790
|
+
const line = lines[i];
|
|
13791
|
+
const headerMatch = line.match(/^(#{1,3})\s+(.+)$/);
|
|
13792
|
+
if (headerMatch) {
|
|
13793
|
+
if (currentSection) {
|
|
13794
|
+
sections.set(currentSection, {
|
|
13795
|
+
start: sectionStart,
|
|
13796
|
+
end: i - 1,
|
|
13797
|
+
content: sectionContent.join("\n")
|
|
13798
|
+
});
|
|
13799
|
+
}
|
|
13800
|
+
currentSection = headerMatch[2].trim();
|
|
13801
|
+
sectionStart = i;
|
|
13802
|
+
sectionContent = [line];
|
|
13803
|
+
} else if (currentSection) {
|
|
13804
|
+
sectionContent.push(line);
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13807
|
+
if (currentSection) {
|
|
13808
|
+
sections.set(currentSection, {
|
|
13809
|
+
start: sectionStart,
|
|
13810
|
+
end: lines.length - 1,
|
|
13811
|
+
content: sectionContent.join("\n")
|
|
13812
|
+
});
|
|
13813
|
+
}
|
|
13814
|
+
const learnedSection = sections.get("LEARNED") || sections.get("Learned");
|
|
13815
|
+
const hasLearnedSection = !!learnedSection;
|
|
13816
|
+
return {
|
|
13817
|
+
content,
|
|
13818
|
+
sections,
|
|
13819
|
+
hasLearnedSection,
|
|
13820
|
+
learnedSectionContent: learnedSection?.content,
|
|
13821
|
+
learnedSectionRange: learnedSection ? { start: learnedSection.start, end: learnedSection.end } : void 0
|
|
13822
|
+
};
|
|
13823
|
+
}
|
|
13824
|
+
/**
|
|
13825
|
+
* Get learnings to add to CLAUDE.md
|
|
13826
|
+
*/
|
|
13827
|
+
getLearningsForClaudeMd(options = {}) {
|
|
13828
|
+
const opts = { ...DEFAULT_UPDATE_OPTIONS, ...options };
|
|
13829
|
+
const projectStore = new LearningStore("project", this.projectPath);
|
|
13830
|
+
let learnings = projectStore.getAll();
|
|
13831
|
+
if (opts.includeGlobal) {
|
|
13832
|
+
const globalStore = new LearningStore("global");
|
|
13833
|
+
learnings = [...learnings, ...globalStore.getAll()];
|
|
13834
|
+
}
|
|
13835
|
+
return learnings.filter((l) => (l.effectiveness ?? 0) >= opts.minEffectiveness || l.useCount >= 3).sort((a, b) => {
|
|
13836
|
+
const scoreA = (a.effectiveness ?? 50) + a.useCount * 5;
|
|
13837
|
+
const scoreB = (b.effectiveness ?? 50) + b.useCount * 5;
|
|
13838
|
+
return scoreB - scoreA;
|
|
13839
|
+
}).slice(0, opts.maxLearnings);
|
|
13840
|
+
}
|
|
13841
|
+
/**
|
|
13842
|
+
* Format learnings as CLAUDE.md LEARNED section
|
|
13843
|
+
*/
|
|
13844
|
+
formatLearnedSection(learnings, options = {}) {
|
|
13845
|
+
const opts = { ...DEFAULT_UPDATE_OPTIONS, ...options };
|
|
13846
|
+
const lines = [];
|
|
13847
|
+
lines.push(`## ${opts.sectionTitle}`);
|
|
13848
|
+
lines.push("");
|
|
13849
|
+
lines.push(SKILLKIT_MARKER);
|
|
13850
|
+
if (opts.addTimestamp) {
|
|
13851
|
+
lines.push(`<!-- Last updated: ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
13852
|
+
}
|
|
13853
|
+
lines.push("");
|
|
13854
|
+
const byCategory = this.categorizeLearnings(learnings);
|
|
13855
|
+
for (const [category, categoryLearnings] of byCategory) {
|
|
13856
|
+
if (categoryLearnings.length > 0) {
|
|
13857
|
+
lines.push(`### ${category}`);
|
|
13858
|
+
for (const learning of categoryLearnings) {
|
|
13859
|
+
const title = this.formatLearningTitle(learning);
|
|
13860
|
+
const summary = this.extractSummary(learning.content);
|
|
13861
|
+
lines.push(`${title}`);
|
|
13862
|
+
lines.push(summary);
|
|
13863
|
+
lines.push("");
|
|
13864
|
+
}
|
|
13865
|
+
}
|
|
13866
|
+
}
|
|
13867
|
+
return lines.join("\n");
|
|
13868
|
+
}
|
|
13869
|
+
/**
|
|
13870
|
+
* Update CLAUDE.md with learnings
|
|
13871
|
+
*/
|
|
13872
|
+
update(options = {}) {
|
|
13873
|
+
const opts = { ...DEFAULT_UPDATE_OPTIONS, ...options };
|
|
13874
|
+
const learnings = this.getLearningsForClaudeMd(opts);
|
|
13875
|
+
if (learnings.length === 0) {
|
|
13876
|
+
return {
|
|
13877
|
+
updated: false,
|
|
13878
|
+
path: this.claudeMdPath,
|
|
13879
|
+
learningsAdded: 0,
|
|
13880
|
+
learningSummaries: [],
|
|
13881
|
+
previousLearnings: 0
|
|
13882
|
+
};
|
|
13883
|
+
}
|
|
13884
|
+
const parsed = this.parse();
|
|
13885
|
+
const newSection = this.formatLearnedSection(learnings, opts);
|
|
13886
|
+
let newContent;
|
|
13887
|
+
let previousLearnings = 0;
|
|
13888
|
+
if (parsed.hasLearnedSection && parsed.learnedSectionRange) {
|
|
13889
|
+
if (opts.preserveManualEntries) {
|
|
13890
|
+
const existingContent = parsed.learnedSectionContent || "";
|
|
13891
|
+
const manualEntries = this.extractManualEntries(existingContent);
|
|
13892
|
+
previousLearnings = this.countLearnings(existingContent);
|
|
13893
|
+
const combinedSection = this.combineWithManualEntries(newSection, manualEntries);
|
|
13894
|
+
const lines = parsed.content.split("\n");
|
|
13895
|
+
const before = lines.slice(0, parsed.learnedSectionRange.start).join("\n");
|
|
13896
|
+
const after = lines.slice(parsed.learnedSectionRange.end + 1).join("\n");
|
|
13897
|
+
newContent = before + (before ? "\n" : "") + combinedSection + (after ? "\n" + after : "");
|
|
13898
|
+
} else {
|
|
13899
|
+
const lines = parsed.content.split("\n");
|
|
13900
|
+
const before = lines.slice(0, parsed.learnedSectionRange.start).join("\n");
|
|
13901
|
+
const after = lines.slice(parsed.learnedSectionRange.end + 1).join("\n");
|
|
13902
|
+
newContent = before + (before ? "\n" : "") + newSection + (after ? "\n" + after : "");
|
|
13903
|
+
}
|
|
13904
|
+
} else if (parsed.content) {
|
|
13905
|
+
newContent = parsed.content + "\n\n" + newSection;
|
|
13906
|
+
} else {
|
|
13907
|
+
newContent = this.createNewClaudeMd(newSection);
|
|
13908
|
+
}
|
|
13909
|
+
const dir = dirname7(this.claudeMdPath);
|
|
13910
|
+
if (!existsSync26(dir)) {
|
|
13911
|
+
mkdirSync15(dir, { recursive: true });
|
|
13912
|
+
}
|
|
13913
|
+
writeFileSync14(this.claudeMdPath, newContent, "utf-8");
|
|
13914
|
+
return {
|
|
13915
|
+
updated: true,
|
|
13916
|
+
path: this.claudeMdPath,
|
|
13917
|
+
learningsAdded: learnings.length,
|
|
13918
|
+
learningSummaries: learnings.map((l) => l.title),
|
|
13919
|
+
previousLearnings
|
|
13920
|
+
};
|
|
13921
|
+
}
|
|
13922
|
+
/**
|
|
13923
|
+
* Preview update without writing
|
|
13924
|
+
*/
|
|
13925
|
+
preview(options = {}) {
|
|
13926
|
+
const learnings = this.getLearningsForClaudeMd(options);
|
|
13927
|
+
const formattedSection = this.formatLearnedSection(learnings, options);
|
|
13928
|
+
return {
|
|
13929
|
+
learnings,
|
|
13930
|
+
formattedSection,
|
|
13931
|
+
wouldUpdate: learnings.length > 0
|
|
13932
|
+
};
|
|
13933
|
+
}
|
|
13934
|
+
/**
|
|
13935
|
+
* Check if CLAUDE.md exists
|
|
13936
|
+
*/
|
|
13937
|
+
exists() {
|
|
13938
|
+
return existsSync26(this.claudeMdPath);
|
|
13939
|
+
}
|
|
13940
|
+
/**
|
|
13941
|
+
* Get CLAUDE.md path
|
|
13942
|
+
*/
|
|
13943
|
+
getPath() {
|
|
13944
|
+
return this.claudeMdPath;
|
|
13945
|
+
}
|
|
13946
|
+
categorizeLearnings(learnings) {
|
|
13947
|
+
const categories = /* @__PURE__ */ new Map();
|
|
13948
|
+
for (const learning of learnings) {
|
|
13949
|
+
let category = "General";
|
|
13950
|
+
if (learning.patterns?.includes("error-handling")) {
|
|
13951
|
+
category = "Error-Handling";
|
|
13952
|
+
} else if (learning.patterns?.includes("debugging")) {
|
|
13953
|
+
category = "Debugging";
|
|
13954
|
+
} else if (learning.patterns?.includes("architecture")) {
|
|
13955
|
+
category = "Architecture";
|
|
13956
|
+
} else if (learning.patterns?.includes("workflow")) {
|
|
13957
|
+
category = "Workflow";
|
|
13958
|
+
} else if (learning.tags.some((t) => t.includes("react") || t.includes("typescript"))) {
|
|
13959
|
+
category = "Code-Patterns";
|
|
13960
|
+
}
|
|
13961
|
+
const existing = categories.get(category) || [];
|
|
13962
|
+
existing.push(learning);
|
|
13963
|
+
categories.set(category, existing);
|
|
13964
|
+
}
|
|
13965
|
+
return categories;
|
|
13966
|
+
}
|
|
13967
|
+
formatLearningTitle(learning) {
|
|
13968
|
+
const tags = learning.tags.slice(0, 3).join(", ");
|
|
13969
|
+
return tags ? `**${tags}**: ${learning.title}` : `**${learning.title}**`;
|
|
13970
|
+
}
|
|
13971
|
+
extractSummary(content) {
|
|
13972
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
13973
|
+
for (const line of lines) {
|
|
13974
|
+
const cleaned = line.replace(/^#+\s*/, "").replace(/^\*\*.*?\*\*:?\s*/, "");
|
|
13975
|
+
if (cleaned.length > 20 && !cleaned.startsWith("#")) {
|
|
13976
|
+
return cleaned.slice(0, 200) + (cleaned.length > 200 ? "..." : "");
|
|
13977
|
+
}
|
|
13978
|
+
}
|
|
13979
|
+
return content.slice(0, 200) + (content.length > 200 ? "..." : "");
|
|
13980
|
+
}
|
|
13981
|
+
extractManualEntries(sectionContent) {
|
|
13982
|
+
const lines = sectionContent.split("\n");
|
|
13983
|
+
const manualEntries = [];
|
|
13984
|
+
let inManualEntry = false;
|
|
13985
|
+
let currentEntry = [];
|
|
13986
|
+
for (const line of lines) {
|
|
13987
|
+
if (line.includes(SKILLKIT_MARKER)) {
|
|
13988
|
+
inManualEntry = false;
|
|
13989
|
+
continue;
|
|
13990
|
+
}
|
|
13991
|
+
if (line.startsWith("### ") && !line.includes("Auto-populated")) {
|
|
13992
|
+
if (currentEntry.length > 0) {
|
|
13993
|
+
manualEntries.push(currentEntry.join("\n"));
|
|
13994
|
+
}
|
|
13995
|
+
inManualEntry = true;
|
|
13996
|
+
currentEntry = [line];
|
|
13997
|
+
} else if (inManualEntry) {
|
|
13998
|
+
currentEntry.push(line);
|
|
13999
|
+
}
|
|
14000
|
+
}
|
|
14001
|
+
if (currentEntry.length > 0) {
|
|
14002
|
+
manualEntries.push(currentEntry.join("\n"));
|
|
14003
|
+
}
|
|
14004
|
+
return manualEntries.filter((e) => e.trim().length > 0);
|
|
14005
|
+
}
|
|
14006
|
+
combineWithManualEntries(autoSection, manualEntries) {
|
|
14007
|
+
if (manualEntries.length === 0) {
|
|
14008
|
+
return autoSection;
|
|
14009
|
+
}
|
|
14010
|
+
const lines = autoSection.split("\n");
|
|
14011
|
+
const combinedLines = [...lines];
|
|
14012
|
+
combinedLines.push("");
|
|
14013
|
+
combinedLines.push("### Manual Entries");
|
|
14014
|
+
combinedLines.push("<!-- Preserved from previous edits -->");
|
|
14015
|
+
combinedLines.push("");
|
|
14016
|
+
for (const entry of manualEntries) {
|
|
14017
|
+
combinedLines.push(entry);
|
|
14018
|
+
combinedLines.push("");
|
|
14019
|
+
}
|
|
14020
|
+
return combinedLines.join("\n");
|
|
14021
|
+
}
|
|
14022
|
+
countLearnings(content) {
|
|
14023
|
+
const matches = content.match(/^\*\*[^*]+\*\*/gm);
|
|
14024
|
+
return matches ? matches.length : 0;
|
|
14025
|
+
}
|
|
14026
|
+
createNewClaudeMd(learnedSection) {
|
|
14027
|
+
const projectName = basename11(this.projectPath) || "Project";
|
|
14028
|
+
return `# ${projectName}
|
|
14029
|
+
|
|
14030
|
+
${learnedSection}
|
|
14031
|
+
`;
|
|
14032
|
+
}
|
|
14033
|
+
};
|
|
14034
|
+
function createClaudeMdUpdater(projectPath, claudeMdPath) {
|
|
14035
|
+
return new ClaudeMdUpdater(projectPath, claudeMdPath);
|
|
14036
|
+
}
|
|
14037
|
+
function updateClaudeMd(projectPath, options = {}) {
|
|
14038
|
+
const updater = new ClaudeMdUpdater(projectPath);
|
|
14039
|
+
return updater.update(options);
|
|
14040
|
+
}
|
|
14041
|
+
function syncGlobalClaudeMd(options = {}) {
|
|
14042
|
+
const globalClaudeMdPath = join25(homedir12(), ".claude", "CLAUDE.md");
|
|
14043
|
+
const updater = new ClaudeMdUpdater(homedir12(), globalClaudeMdPath);
|
|
14044
|
+
const globalOpts = {
|
|
14045
|
+
...options,
|
|
14046
|
+
includeGlobal: true,
|
|
14047
|
+
sectionTitle: "Global Learnings"
|
|
14048
|
+
};
|
|
14049
|
+
return updater.update(globalOpts);
|
|
14050
|
+
}
|
|
14051
|
+
|
|
14052
|
+
// src/memory/progressive-disclosure.ts
|
|
14053
|
+
var TOKEN_ESTIMATES = {
|
|
14054
|
+
index: 50,
|
|
14055
|
+
timeline: 200,
|
|
14056
|
+
details: 600
|
|
14057
|
+
};
|
|
14058
|
+
var ProgressiveDisclosureManager = class {
|
|
14059
|
+
projectStore;
|
|
14060
|
+
globalStore;
|
|
14061
|
+
constructor(projectPath, projectName) {
|
|
14062
|
+
this.projectStore = new LearningStore("project", projectPath, projectName);
|
|
14063
|
+
this.globalStore = new LearningStore("global");
|
|
14064
|
+
}
|
|
14065
|
+
/**
|
|
14066
|
+
* Layer 1: Get index of all learnings
|
|
14067
|
+
* Minimal tokens (~50-100 per entry)
|
|
14068
|
+
*/
|
|
14069
|
+
getIndex(options = {}) {
|
|
14070
|
+
const projectLearnings = this.projectStore.getAll();
|
|
14071
|
+
const globalLearnings = options.includeGlobal ? this.globalStore.getAll() : [];
|
|
14072
|
+
const allLearnings = [...projectLearnings, ...globalLearnings];
|
|
14073
|
+
return allLearnings.map((learning) => this.toIndexEntry(learning)).sort((a, b) => {
|
|
14074
|
+
const scoreA = (a.effectiveness ?? 50) + a.useCount * 5;
|
|
14075
|
+
const scoreB = (b.effectiveness ?? 50) + b.useCount * 5;
|
|
14076
|
+
return scoreB - scoreA;
|
|
14077
|
+
}).slice(0, options.maxResults ?? 50);
|
|
14078
|
+
}
|
|
14079
|
+
/**
|
|
14080
|
+
* Layer 2: Get timeline entries for specific IDs
|
|
14081
|
+
* Medium tokens (~200 per entry)
|
|
14082
|
+
*/
|
|
14083
|
+
getTimeline(ids, options = {}) {
|
|
14084
|
+
const entries = [];
|
|
14085
|
+
for (const id of ids) {
|
|
14086
|
+
let learning = this.projectStore.getById(id);
|
|
14087
|
+
if (!learning && options.includeGlobal) {
|
|
14088
|
+
learning = this.globalStore.getById(id);
|
|
14089
|
+
}
|
|
14090
|
+
if (learning) {
|
|
14091
|
+
entries.push(this.toTimelineEntry(learning));
|
|
14092
|
+
}
|
|
14093
|
+
}
|
|
14094
|
+
return entries.slice(0, options.maxResults ?? 20);
|
|
14095
|
+
}
|
|
14096
|
+
/**
|
|
14097
|
+
* Layer 3: Get full details for specific IDs
|
|
14098
|
+
* High tokens (~500-1000 per entry)
|
|
14099
|
+
*/
|
|
14100
|
+
getDetails(ids, options = {}) {
|
|
14101
|
+
const entries = [];
|
|
14102
|
+
for (const id of ids) {
|
|
14103
|
+
let learning = this.projectStore.getById(id);
|
|
14104
|
+
let store = this.projectStore;
|
|
14105
|
+
if (!learning && options.includeGlobal) {
|
|
14106
|
+
learning = this.globalStore.getById(id);
|
|
14107
|
+
store = this.globalStore;
|
|
14108
|
+
}
|
|
14109
|
+
if (learning) {
|
|
14110
|
+
store.incrementUseCount(id);
|
|
14111
|
+
entries.push(this.toDetailsEntry(learning));
|
|
14112
|
+
}
|
|
14113
|
+
}
|
|
14114
|
+
return entries.slice(0, options.maxResults ?? 10);
|
|
14115
|
+
}
|
|
14116
|
+
/**
|
|
14117
|
+
* Smart retrieval with automatic layer selection
|
|
14118
|
+
* Uses minimum tokens needed to satisfy the query.
|
|
14119
|
+
*
|
|
14120
|
+
* Note: tokensUsed reflects cumulative cost of the retrieval operation
|
|
14121
|
+
* (index lookup + any deeper layer fetches), not just the returned entries.
|
|
14122
|
+
* This is intentional since progressive disclosure requires scanning
|
|
14123
|
+
* the index first before fetching timeline/details.
|
|
14124
|
+
*/
|
|
14125
|
+
smartRetrieve(query, tokenBudget = 2e3, options = {}) {
|
|
14126
|
+
if (tokenBudget <= 0) {
|
|
14127
|
+
return {
|
|
14128
|
+
layer: 1,
|
|
14129
|
+
entries: [],
|
|
14130
|
+
tokensUsed: 0,
|
|
14131
|
+
tokensRemaining: 0
|
|
14132
|
+
};
|
|
14133
|
+
}
|
|
14134
|
+
const index = this.getIndex(options);
|
|
14135
|
+
if (index.length === 0) {
|
|
14136
|
+
return {
|
|
14137
|
+
layer: 1,
|
|
14138
|
+
entries: [],
|
|
14139
|
+
tokensUsed: 0,
|
|
14140
|
+
tokensRemaining: tokenBudget
|
|
14141
|
+
};
|
|
14142
|
+
}
|
|
14143
|
+
const indexTokens = index.length * TOKEN_ESTIMATES.index;
|
|
14144
|
+
if (tokenBudget < indexTokens) {
|
|
14145
|
+
const maxEntries = Math.floor(tokenBudget / TOKEN_ESTIMATES.index);
|
|
14146
|
+
const limitedIndex = index.slice(0, maxEntries);
|
|
14147
|
+
return {
|
|
14148
|
+
layer: 1,
|
|
14149
|
+
entries: limitedIndex,
|
|
14150
|
+
tokensUsed: limitedIndex.length * TOKEN_ESTIMATES.index,
|
|
14151
|
+
tokensRemaining: tokenBudget - limitedIndex.length * TOKEN_ESTIMATES.index
|
|
14152
|
+
};
|
|
14153
|
+
}
|
|
14154
|
+
const relevantIds = this.findRelevantIds(index, query, options.minRelevance ?? 0);
|
|
14155
|
+
if (relevantIds.length === 0) {
|
|
14156
|
+
return {
|
|
14157
|
+
layer: 1,
|
|
14158
|
+
entries: index,
|
|
14159
|
+
tokensUsed: indexTokens,
|
|
14160
|
+
tokensRemaining: tokenBudget - indexTokens
|
|
14161
|
+
};
|
|
14162
|
+
}
|
|
14163
|
+
const remainingBudget = tokenBudget - indexTokens;
|
|
14164
|
+
const maxTimelineEntries = Math.floor(remainingBudget / TOKEN_ESTIMATES.timeline);
|
|
14165
|
+
if (maxTimelineEntries >= 1) {
|
|
14166
|
+
const timelineIds = relevantIds.slice(0, Math.min(maxTimelineEntries, 10));
|
|
14167
|
+
const timeline = this.getTimeline(timelineIds, options);
|
|
14168
|
+
const timelineTokens = timeline.length * TOKEN_ESTIMATES.timeline;
|
|
14169
|
+
const afterTimelineBudget = remainingBudget - timelineTokens;
|
|
14170
|
+
const maxDetailsEntries = Math.floor(afterTimelineBudget / TOKEN_ESTIMATES.details);
|
|
14171
|
+
if (maxDetailsEntries >= 1) {
|
|
14172
|
+
const detailsIds = timelineIds.slice(0, Math.min(maxDetailsEntries, 5));
|
|
14173
|
+
const details = this.getDetails(detailsIds, options);
|
|
14174
|
+
const detailsTokens = details.length * TOKEN_ESTIMATES.details;
|
|
14175
|
+
return {
|
|
14176
|
+
layer: 3,
|
|
14177
|
+
entries: details,
|
|
14178
|
+
tokensUsed: indexTokens + timelineTokens + detailsTokens,
|
|
14179
|
+
tokensRemaining: tokenBudget - (indexTokens + timelineTokens + detailsTokens)
|
|
14180
|
+
};
|
|
14181
|
+
}
|
|
14182
|
+
return {
|
|
14183
|
+
layer: 2,
|
|
14184
|
+
entries: timeline,
|
|
14185
|
+
tokensUsed: indexTokens + timelineTokens,
|
|
14186
|
+
tokensRemaining: tokenBudget - (indexTokens + timelineTokens)
|
|
14187
|
+
};
|
|
14188
|
+
}
|
|
14189
|
+
return {
|
|
14190
|
+
layer: 1,
|
|
14191
|
+
entries: index,
|
|
14192
|
+
tokensUsed: indexTokens,
|
|
14193
|
+
tokensRemaining: tokenBudget - indexTokens
|
|
14194
|
+
};
|
|
14195
|
+
}
|
|
14196
|
+
/**
|
|
14197
|
+
* Estimate tokens for a given layer and count
|
|
14198
|
+
*/
|
|
14199
|
+
estimateTokens(layer, count) {
|
|
14200
|
+
const estimate = {
|
|
14201
|
+
1: TOKEN_ESTIMATES.index,
|
|
14202
|
+
2: TOKEN_ESTIMATES.timeline,
|
|
14203
|
+
3: TOKEN_ESTIMATES.details
|
|
14204
|
+
};
|
|
14205
|
+
return estimate[layer] * count;
|
|
14206
|
+
}
|
|
14207
|
+
/**
|
|
14208
|
+
* Format entries for injection
|
|
14209
|
+
*/
|
|
14210
|
+
formatForInjection(entries, layer) {
|
|
14211
|
+
if (entries.length === 0) return "";
|
|
14212
|
+
const lines = ["<skillkit-memories>"];
|
|
14213
|
+
switch (layer) {
|
|
14214
|
+
case 1:
|
|
14215
|
+
lines.push("<!-- Memory Index (summaries only) -->");
|
|
14216
|
+
for (const entry of entries) {
|
|
14217
|
+
lines.push(`- [${entry.id.slice(0, 8)}] ${entry.title} (${entry.tags.join(", ")})`);
|
|
14218
|
+
}
|
|
14219
|
+
break;
|
|
14220
|
+
case 2:
|
|
14221
|
+
lines.push("<!-- Memory Timeline (with context) -->");
|
|
14222
|
+
for (const entry of entries) {
|
|
14223
|
+
lines.push(`## ${entry.title}`);
|
|
14224
|
+
lines.push(`ID: ${entry.id.slice(0, 8)} | Tags: ${entry.tags.join(", ")}`);
|
|
14225
|
+
if (entry.frameworks && entry.frameworks.length > 0) {
|
|
14226
|
+
lines.push(`Frameworks: ${entry.frameworks.join(", ")}`);
|
|
14227
|
+
}
|
|
14228
|
+
lines.push("");
|
|
14229
|
+
lines.push(entry.excerpt);
|
|
14230
|
+
lines.push("");
|
|
14231
|
+
}
|
|
14232
|
+
break;
|
|
14233
|
+
case 3:
|
|
14234
|
+
lines.push("<!-- Memory Details (full content) -->");
|
|
14235
|
+
for (const entry of entries) {
|
|
14236
|
+
lines.push(`## ${entry.title}`);
|
|
14237
|
+
lines.push(`ID: ${entry.id.slice(0, 8)} | Tags: ${entry.tags.join(", ")}`);
|
|
14238
|
+
if (entry.frameworks && entry.frameworks.length > 0) {
|
|
14239
|
+
lines.push(`Frameworks: ${entry.frameworks.join(", ")}`);
|
|
14240
|
+
}
|
|
14241
|
+
if (entry.patterns && entry.patterns.length > 0) {
|
|
14242
|
+
lines.push(`Patterns: ${entry.patterns.join(", ")}`);
|
|
14243
|
+
}
|
|
14244
|
+
lines.push("");
|
|
14245
|
+
lines.push(entry.content);
|
|
14246
|
+
lines.push("");
|
|
14247
|
+
}
|
|
14248
|
+
break;
|
|
14249
|
+
}
|
|
14250
|
+
lines.push("</skillkit-memories>");
|
|
14251
|
+
return lines.join("\n");
|
|
14252
|
+
}
|
|
14253
|
+
toIndexEntry(learning) {
|
|
14254
|
+
return {
|
|
14255
|
+
id: learning.id,
|
|
14256
|
+
title: learning.title,
|
|
14257
|
+
timestamp: learning.updatedAt,
|
|
14258
|
+
tags: learning.tags,
|
|
14259
|
+
scope: learning.scope,
|
|
14260
|
+
effectiveness: learning.effectiveness,
|
|
14261
|
+
useCount: learning.useCount
|
|
14262
|
+
};
|
|
14263
|
+
}
|
|
14264
|
+
toTimelineEntry(learning) {
|
|
14265
|
+
const timeline = this.buildActivityTimeline(learning);
|
|
14266
|
+
return {
|
|
14267
|
+
id: learning.id,
|
|
14268
|
+
title: learning.title,
|
|
14269
|
+
timestamp: learning.updatedAt,
|
|
14270
|
+
tags: learning.tags,
|
|
14271
|
+
scope: learning.scope,
|
|
14272
|
+
effectiveness: learning.effectiveness,
|
|
14273
|
+
useCount: learning.useCount,
|
|
14274
|
+
excerpt: learning.content.slice(0, 200) + (learning.content.length > 200 ? "..." : ""),
|
|
14275
|
+
frameworks: learning.frameworks,
|
|
14276
|
+
patterns: learning.patterns,
|
|
14277
|
+
sourceCount: learning.sourceObservations?.length ?? 0,
|
|
14278
|
+
lastUsed: learning.lastUsed,
|
|
14279
|
+
activityTimeline: timeline
|
|
14280
|
+
};
|
|
14281
|
+
}
|
|
14282
|
+
toDetailsEntry(learning) {
|
|
14283
|
+
const timeline = this.buildActivityTimeline(learning);
|
|
14284
|
+
return {
|
|
12728
14285
|
id: learning.id,
|
|
12729
14286
|
title: learning.title,
|
|
14287
|
+
timestamp: learning.updatedAt,
|
|
12730
14288
|
tags: learning.tags,
|
|
12731
|
-
|
|
12732
|
-
|
|
14289
|
+
scope: learning.scope,
|
|
14290
|
+
effectiveness: learning.effectiveness,
|
|
14291
|
+
useCount: learning.useCount,
|
|
14292
|
+
excerpt: learning.content.slice(0, 200) + (learning.content.length > 200 ? "..." : ""),
|
|
14293
|
+
frameworks: learning.frameworks,
|
|
14294
|
+
patterns: learning.patterns,
|
|
14295
|
+
sourceCount: learning.sourceObservations?.length ?? 0,
|
|
14296
|
+
lastUsed: learning.lastUsed,
|
|
14297
|
+
activityTimeline: timeline,
|
|
14298
|
+
content: learning.content,
|
|
14299
|
+
sourceObservations: learning.sourceObservations
|
|
14300
|
+
};
|
|
12733
14301
|
}
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
learning
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
previews.push({
|
|
12747
|
-
id: learning.id,
|
|
12748
|
-
title: learning.title,
|
|
12749
|
-
tags: learning.tags,
|
|
12750
|
-
relevance: this.scoreLearning(learning, opts).relevanceScore,
|
|
12751
|
-
excerpt: learning.content.slice(0, 200),
|
|
12752
|
-
lastUsed: learning.lastUsed
|
|
12753
|
-
});
|
|
12754
|
-
}
|
|
14302
|
+
buildActivityTimeline(learning) {
|
|
14303
|
+
const timeline = [];
|
|
14304
|
+
timeline.push({
|
|
14305
|
+
timestamp: learning.createdAt,
|
|
14306
|
+
type: "created",
|
|
14307
|
+
description: `Learning created from ${learning.source}`
|
|
14308
|
+
});
|
|
14309
|
+
if (learning.updatedAt !== learning.createdAt) {
|
|
14310
|
+
timeline.push({
|
|
14311
|
+
timestamp: learning.updatedAt,
|
|
14312
|
+
type: "updated"
|
|
14313
|
+
});
|
|
12755
14314
|
}
|
|
12756
|
-
|
|
14315
|
+
if (learning.lastUsed) {
|
|
14316
|
+
timeline.push({
|
|
14317
|
+
timestamp: learning.lastUsed,
|
|
14318
|
+
type: "used",
|
|
14319
|
+
description: `Used ${learning.useCount} times`
|
|
14320
|
+
});
|
|
14321
|
+
}
|
|
14322
|
+
if (learning.effectiveness !== void 0) {
|
|
14323
|
+
timeline.push({
|
|
14324
|
+
timestamp: learning.updatedAt,
|
|
14325
|
+
type: "rated",
|
|
14326
|
+
description: `Effectiveness: ${learning.effectiveness}%`
|
|
14327
|
+
});
|
|
14328
|
+
}
|
|
14329
|
+
return timeline.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
12757
14330
|
}
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
14331
|
+
findRelevantIds(index, query, minRelevance = 0) {
|
|
14332
|
+
const queryWords = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
14333
|
+
const scored = [];
|
|
14334
|
+
for (const entry of index) {
|
|
14335
|
+
let score = 0;
|
|
14336
|
+
const titleWords = entry.title.toLowerCase().split(/\s+/);
|
|
14337
|
+
for (const qw of queryWords) {
|
|
14338
|
+
if (titleWords.some((tw) => tw.includes(qw))) {
|
|
14339
|
+
score += 10;
|
|
14340
|
+
}
|
|
12768
14341
|
}
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
});
|
|
14342
|
+
const tags = entry.tags.map((t) => t.toLowerCase());
|
|
14343
|
+
for (const qw of queryWords) {
|
|
14344
|
+
if (tags.includes(qw)) {
|
|
14345
|
+
score += 20;
|
|
14346
|
+
}
|
|
14347
|
+
}
|
|
14348
|
+
score += (entry.effectiveness ?? 50) / 10;
|
|
14349
|
+
score += Math.min(entry.useCount * 2, 20);
|
|
14350
|
+
if (score >= minRelevance) {
|
|
14351
|
+
scored.push({ id: entry.id, score });
|
|
12779
14352
|
}
|
|
12780
14353
|
}
|
|
12781
|
-
return
|
|
12782
|
-
}
|
|
12783
|
-
/**
|
|
12784
|
-
* Search memories by query
|
|
12785
|
-
*/
|
|
12786
|
-
search(query, options = {}) {
|
|
12787
|
-
const opts = { ...DEFAULT_OPTIONS2, ...options, currentTask: query };
|
|
12788
|
-
const projectResults = this.projectStore.search(query);
|
|
12789
|
-
const globalResults = opts.includeGlobal ? this.globalStore.search(query) : [];
|
|
12790
|
-
const allResults = [...projectResults, ...globalResults];
|
|
12791
|
-
return allResults.map((learning) => ({
|
|
12792
|
-
learning,
|
|
12793
|
-
...this.scoreLearning(learning, opts)
|
|
12794
|
-
})).filter((m) => m.relevanceScore >= opts.minRelevance).sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, opts.maxLearnings);
|
|
14354
|
+
return scored.sort((a, b) => b.score - a.score).map((s) => s.id);
|
|
12795
14355
|
}
|
|
12796
14356
|
};
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
"a",
|
|
12800
|
-
"an",
|
|
12801
|
-
"and",
|
|
12802
|
-
"or",
|
|
12803
|
-
"but",
|
|
12804
|
-
"in",
|
|
12805
|
-
"on",
|
|
12806
|
-
"at",
|
|
12807
|
-
"to",
|
|
12808
|
-
"for",
|
|
12809
|
-
"of",
|
|
12810
|
-
"with",
|
|
12811
|
-
"by",
|
|
12812
|
-
"from",
|
|
12813
|
-
"as",
|
|
12814
|
-
"is",
|
|
12815
|
-
"was",
|
|
12816
|
-
"are",
|
|
12817
|
-
"were",
|
|
12818
|
-
"been",
|
|
12819
|
-
"be",
|
|
12820
|
-
"have",
|
|
12821
|
-
"has",
|
|
12822
|
-
"had",
|
|
12823
|
-
"do",
|
|
12824
|
-
"does",
|
|
12825
|
-
"did",
|
|
12826
|
-
"will",
|
|
12827
|
-
"would",
|
|
12828
|
-
"could",
|
|
12829
|
-
"should",
|
|
12830
|
-
"may",
|
|
12831
|
-
"might",
|
|
12832
|
-
"must",
|
|
12833
|
-
"can",
|
|
12834
|
-
"this",
|
|
12835
|
-
"that",
|
|
12836
|
-
"these",
|
|
12837
|
-
"those",
|
|
12838
|
-
"i",
|
|
12839
|
-
"you",
|
|
12840
|
-
"he",
|
|
12841
|
-
"she",
|
|
12842
|
-
"it",
|
|
12843
|
-
"we",
|
|
12844
|
-
"they",
|
|
12845
|
-
"what",
|
|
12846
|
-
"which",
|
|
12847
|
-
"who",
|
|
12848
|
-
"when",
|
|
12849
|
-
"where",
|
|
12850
|
-
"why",
|
|
12851
|
-
"how",
|
|
12852
|
-
"all",
|
|
12853
|
-
"each",
|
|
12854
|
-
"every",
|
|
12855
|
-
"both",
|
|
12856
|
-
"few",
|
|
12857
|
-
"more",
|
|
12858
|
-
"most",
|
|
12859
|
-
"other",
|
|
12860
|
-
"some",
|
|
12861
|
-
"such",
|
|
12862
|
-
"no",
|
|
12863
|
-
"nor",
|
|
12864
|
-
"not",
|
|
12865
|
-
"only",
|
|
12866
|
-
"own",
|
|
12867
|
-
"same",
|
|
12868
|
-
"so",
|
|
12869
|
-
"than",
|
|
12870
|
-
"too",
|
|
12871
|
-
"very",
|
|
12872
|
-
"just",
|
|
12873
|
-
"also"
|
|
12874
|
-
]);
|
|
12875
|
-
function createMemoryInjector(projectPath, projectName, projectContext) {
|
|
12876
|
-
return new MemoryInjector(projectPath, projectName, projectContext);
|
|
14357
|
+
function createProgressiveDisclosureManager(projectPath, projectName) {
|
|
14358
|
+
return new ProgressiveDisclosureManager(projectPath, projectName);
|
|
12877
14359
|
}
|
|
12878
14360
|
|
|
12879
14361
|
// src/team/manager.ts
|
|
12880
|
-
import { existsSync as
|
|
12881
|
-
import { join as
|
|
14362
|
+
import { existsSync as existsSync27, readFileSync as readFileSync17, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16 } from "fs";
|
|
14363
|
+
import { join as join26, dirname as dirname8 } from "path";
|
|
12882
14364
|
import { execSync as execSync6 } from "child_process";
|
|
12883
14365
|
import { parse as yamlParse, stringify as yamlStringify } from "yaml";
|
|
12884
14366
|
var TEAM_CONFIG_FILE = "team.yaml";
|
|
@@ -12899,9 +14381,9 @@ var TeamManager = class {
|
|
|
12899
14381
|
...config,
|
|
12900
14382
|
teamId
|
|
12901
14383
|
};
|
|
12902
|
-
const teamDir =
|
|
12903
|
-
if (!
|
|
12904
|
-
|
|
14384
|
+
const teamDir = join26(this.projectPath, TEAM_DIR);
|
|
14385
|
+
if (!existsSync27(teamDir)) {
|
|
14386
|
+
mkdirSync16(teamDir, { recursive: true });
|
|
12905
14387
|
}
|
|
12906
14388
|
this.saveConfig(fullConfig);
|
|
12907
14389
|
this.config = fullConfig;
|
|
@@ -12921,12 +14403,12 @@ var TeamManager = class {
|
|
|
12921
14403
|
* Load existing team configuration
|
|
12922
14404
|
*/
|
|
12923
14405
|
load() {
|
|
12924
|
-
const configPath =
|
|
12925
|
-
if (!
|
|
14406
|
+
const configPath = join26(this.projectPath, TEAM_DIR, TEAM_CONFIG_FILE);
|
|
14407
|
+
if (!existsSync27(configPath)) {
|
|
12926
14408
|
return null;
|
|
12927
14409
|
}
|
|
12928
14410
|
try {
|
|
12929
|
-
const content =
|
|
14411
|
+
const content = readFileSync17(configPath, "utf-8");
|
|
12930
14412
|
this.config = this.parseYaml(content);
|
|
12931
14413
|
this.loadRegistry();
|
|
12932
14414
|
return this.config;
|
|
@@ -12957,8 +14439,8 @@ var TeamManager = class {
|
|
|
12957
14439
|
if (!skillPath) {
|
|
12958
14440
|
throw new Error(`Skill "${options.skillName}" not found locally.`);
|
|
12959
14441
|
}
|
|
12960
|
-
const skillMdPath =
|
|
12961
|
-
const skillContent =
|
|
14442
|
+
const skillMdPath = join26(skillPath, "SKILL.md");
|
|
14443
|
+
const skillContent = existsSync27(skillMdPath) ? readFileSync17(skillMdPath, "utf-8") : "";
|
|
12962
14444
|
const metadata = this.extractFrontmatter(skillContent);
|
|
12963
14445
|
const shared = {
|
|
12964
14446
|
name: options.skillName,
|
|
@@ -13023,7 +14505,7 @@ var TeamManager = class {
|
|
|
13023
14505
|
if (options.dryRun) {
|
|
13024
14506
|
return {
|
|
13025
14507
|
success: true,
|
|
13026
|
-
path:
|
|
14508
|
+
path: join26(this.projectPath, ".skillkit", "skills", skillName)
|
|
13027
14509
|
};
|
|
13028
14510
|
}
|
|
13029
14511
|
try {
|
|
@@ -13073,9 +14555,9 @@ var TeamManager = class {
|
|
|
13073
14555
|
if (!fetchResult.success || !fetchResult.path) {
|
|
13074
14556
|
throw new Error(fetchResult.error || "Failed to fetch remote registry");
|
|
13075
14557
|
}
|
|
13076
|
-
const remoteRegistryPath =
|
|
13077
|
-
if (
|
|
13078
|
-
const remoteContent =
|
|
14558
|
+
const remoteRegistryPath = join26(fetchResult.path, TEAM_DIR, "registry.yaml");
|
|
14559
|
+
if (existsSync27(remoteRegistryPath)) {
|
|
14560
|
+
const remoteContent = readFileSync17(remoteRegistryPath, "utf-8");
|
|
13079
14561
|
const remoteRegistry = this.parseYaml(remoteContent);
|
|
13080
14562
|
const localSkillNames = new Set(this.registry?.skills.map((s) => s.name) || []);
|
|
13081
14563
|
for (const skill of remoteRegistry.skills) {
|
|
@@ -13121,32 +14603,32 @@ var TeamManager = class {
|
|
|
13121
14603
|
return `team-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
13122
14604
|
}
|
|
13123
14605
|
saveConfig(config) {
|
|
13124
|
-
const configPath =
|
|
13125
|
-
const dir =
|
|
13126
|
-
if (!
|
|
13127
|
-
|
|
14606
|
+
const configPath = join26(this.projectPath, TEAM_DIR, TEAM_CONFIG_FILE);
|
|
14607
|
+
const dir = dirname8(configPath);
|
|
14608
|
+
if (!existsSync27(dir)) {
|
|
14609
|
+
mkdirSync16(dir, { recursive: true });
|
|
13128
14610
|
}
|
|
13129
|
-
|
|
14611
|
+
writeFileSync15(configPath, this.toYaml(config), "utf-8");
|
|
13130
14612
|
}
|
|
13131
14613
|
loadRegistry() {
|
|
13132
|
-
const registryPath =
|
|
13133
|
-
if (
|
|
13134
|
-
const content =
|
|
14614
|
+
const registryPath = join26(this.projectPath, TEAM_DIR, "registry.yaml");
|
|
14615
|
+
if (existsSync27(registryPath)) {
|
|
14616
|
+
const content = readFileSync17(registryPath, "utf-8");
|
|
13135
14617
|
this.registry = this.parseYaml(content);
|
|
13136
14618
|
}
|
|
13137
14619
|
}
|
|
13138
14620
|
saveRegistry(registry) {
|
|
13139
|
-
const registryPath =
|
|
13140
|
-
|
|
14621
|
+
const registryPath = join26(this.projectPath, TEAM_DIR, "registry.yaml");
|
|
14622
|
+
writeFileSync15(registryPath, this.toYaml(registry), "utf-8");
|
|
13141
14623
|
}
|
|
13142
14624
|
findLocalSkill(skillName) {
|
|
13143
14625
|
const possiblePaths = [
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
14626
|
+
join26(this.projectPath, ".skillkit", "skills", skillName),
|
|
14627
|
+
join26(this.projectPath, "skills", skillName),
|
|
14628
|
+
join26(this.projectPath, ".claude", "skills", skillName)
|
|
13147
14629
|
];
|
|
13148
14630
|
for (const p of possiblePaths) {
|
|
13149
|
-
if (
|
|
14631
|
+
if (existsSync27(p)) {
|
|
13150
14632
|
return p;
|
|
13151
14633
|
}
|
|
13152
14634
|
}
|
|
@@ -13163,13 +14645,13 @@ var TeamManager = class {
|
|
|
13163
14645
|
}
|
|
13164
14646
|
detectCompatibleAgents(skillPath) {
|
|
13165
14647
|
const agents = [];
|
|
13166
|
-
if (
|
|
14648
|
+
if (existsSync27(join26(skillPath, "SKILL.md"))) {
|
|
13167
14649
|
agents.push("claude-code", "codex", "gemini-cli", "universal");
|
|
13168
14650
|
}
|
|
13169
|
-
if (
|
|
14651
|
+
if (existsSync27(join26(skillPath, "skill.mdc"))) {
|
|
13170
14652
|
agents.push("cursor");
|
|
13171
14653
|
}
|
|
13172
|
-
if (
|
|
14654
|
+
if (existsSync27(join26(skillPath, "rules.md"))) {
|
|
13173
14655
|
agents.push("windsurf");
|
|
13174
14656
|
}
|
|
13175
14657
|
return agents.length > 0 ? agents : ["universal"];
|
|
@@ -13195,8 +14677,8 @@ function createTeamManager(projectPath) {
|
|
|
13195
14677
|
}
|
|
13196
14678
|
|
|
13197
14679
|
// src/team/bundle.ts
|
|
13198
|
-
import { existsSync as
|
|
13199
|
-
import { join as
|
|
14680
|
+
import { existsSync as existsSync28, readFileSync as readFileSync18, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
14681
|
+
import { join as join27, basename as basename12, resolve as resolve4, relative, dirname as dirname9, sep as sep3 } from "path";
|
|
13200
14682
|
import { createHash } from "crypto";
|
|
13201
14683
|
var BUNDLE_VERSION = 1;
|
|
13202
14684
|
var SkillBundle = class {
|
|
@@ -13218,7 +14700,7 @@ var SkillBundle = class {
|
|
|
13218
14700
|
* Add a skill to the bundle
|
|
13219
14701
|
*/
|
|
13220
14702
|
addSkill(skillPath, agents) {
|
|
13221
|
-
const skillName =
|
|
14703
|
+
const skillName = basename12(skillPath);
|
|
13222
14704
|
if (this.skills.has(skillName)) {
|
|
13223
14705
|
throw new Error(`Skill "${skillName}" already exists in bundle`);
|
|
13224
14706
|
}
|
|
@@ -13280,13 +14762,13 @@ var SkillBundle = class {
|
|
|
13280
14762
|
const readDir = (dir, prefix = "") => {
|
|
13281
14763
|
const entries = readdirSync6(dir);
|
|
13282
14764
|
for (const entry of entries) {
|
|
13283
|
-
const fullPath =
|
|
14765
|
+
const fullPath = join27(dir, entry);
|
|
13284
14766
|
const relativePath = prefix ? `${prefix}/${entry}` : entry;
|
|
13285
14767
|
const stat = statSync3(fullPath);
|
|
13286
14768
|
if (stat.isDirectory()) {
|
|
13287
14769
|
readDir(fullPath, relativePath);
|
|
13288
14770
|
} else if (stat.isFile()) {
|
|
13289
|
-
const content =
|
|
14771
|
+
const content = readFileSync18(fullPath, "utf-8");
|
|
13290
14772
|
contents.push(`--- ${relativePath} ---
|
|
13291
14773
|
${content}`);
|
|
13292
14774
|
}
|
|
@@ -13295,14 +14777,14 @@ ${content}`);
|
|
|
13295
14777
|
if (statSync3(skillPath).isDirectory()) {
|
|
13296
14778
|
readDir(skillPath);
|
|
13297
14779
|
} else {
|
|
13298
|
-
contents.push(
|
|
14780
|
+
contents.push(readFileSync18(skillPath, "utf-8"));
|
|
13299
14781
|
}
|
|
13300
14782
|
return contents.join("\n\n");
|
|
13301
14783
|
}
|
|
13302
14784
|
detectAgents(skillPath) {
|
|
13303
14785
|
const agents = [];
|
|
13304
|
-
if (
|
|
13305
|
-
const fileName =
|
|
14786
|
+
if (existsSync28(skillPath) && statSync3(skillPath).isFile()) {
|
|
14787
|
+
const fileName = basename12(skillPath).toLowerCase();
|
|
13306
14788
|
if (fileName === "skill.md") {
|
|
13307
14789
|
return ["claude-code", "codex", "gemini-cli", "universal"];
|
|
13308
14790
|
}
|
|
@@ -13314,13 +14796,13 @@ ${content}`);
|
|
|
13314
14796
|
}
|
|
13315
14797
|
return ["universal"];
|
|
13316
14798
|
}
|
|
13317
|
-
if (
|
|
14799
|
+
if (existsSync28(join27(skillPath, "SKILL.md"))) {
|
|
13318
14800
|
agents.push("claude-code", "codex", "gemini-cli", "universal");
|
|
13319
14801
|
}
|
|
13320
|
-
if (
|
|
14802
|
+
if (existsSync28(join27(skillPath, "skill.mdc"))) {
|
|
13321
14803
|
agents.push("cursor");
|
|
13322
14804
|
}
|
|
13323
|
-
if (
|
|
14805
|
+
if (existsSync28(join27(skillPath, "rules.md"))) {
|
|
13324
14806
|
agents.push("windsurf");
|
|
13325
14807
|
}
|
|
13326
14808
|
return agents.length > 0 ? agents : ["universal"];
|
|
@@ -13342,11 +14824,11 @@ function exportBundle(bundle, outputPath) {
|
|
|
13342
14824
|
exportData.skills[skill.name] = content;
|
|
13343
14825
|
}
|
|
13344
14826
|
}
|
|
13345
|
-
const dir =
|
|
13346
|
-
if (dir && !
|
|
13347
|
-
|
|
14827
|
+
const dir = dirname9(outputPath);
|
|
14828
|
+
if (dir && !existsSync28(dir)) {
|
|
14829
|
+
mkdirSync17(dir, { recursive: true });
|
|
13348
14830
|
}
|
|
13349
|
-
|
|
14831
|
+
writeFileSync16(outputPath, JSON.stringify(exportData, null, 2), "utf-8");
|
|
13350
14832
|
return { success: true, path: outputPath };
|
|
13351
14833
|
} catch (err) {
|
|
13352
14834
|
return {
|
|
@@ -13359,7 +14841,7 @@ function importBundle(bundlePath, targetDir, options = {}) {
|
|
|
13359
14841
|
const imported = [];
|
|
13360
14842
|
const errors = [];
|
|
13361
14843
|
try {
|
|
13362
|
-
const content =
|
|
14844
|
+
const content = readFileSync18(bundlePath, "utf-8");
|
|
13363
14845
|
const data = JSON.parse(content);
|
|
13364
14846
|
const absoluteTargetDir = resolve4(targetDir);
|
|
13365
14847
|
for (const skill of data.manifest.skills) {
|
|
@@ -13372,19 +14854,19 @@ function importBundle(bundlePath, targetDir, options = {}) {
|
|
|
13372
14854
|
errors.push(`Skill "${skill.name}" has no content in bundle`);
|
|
13373
14855
|
continue;
|
|
13374
14856
|
}
|
|
13375
|
-
const skillDir =
|
|
14857
|
+
const skillDir = join27(absoluteTargetDir, skill.name);
|
|
13376
14858
|
const resolvedSkillDir = resolve4(skillDir);
|
|
13377
14859
|
const relativeToTarget = relative(absoluteTargetDir, resolvedSkillDir);
|
|
13378
14860
|
if (relativeToTarget.startsWith("..") || relativeToTarget.startsWith(sep3)) {
|
|
13379
14861
|
errors.push(`Skill "${skill.name}" would escape target directory`);
|
|
13380
14862
|
continue;
|
|
13381
14863
|
}
|
|
13382
|
-
if (
|
|
14864
|
+
if (existsSync28(skillDir) && !options.overwrite) {
|
|
13383
14865
|
errors.push(`Skill "${skill.name}" already exists (use --overwrite)`);
|
|
13384
14866
|
continue;
|
|
13385
14867
|
}
|
|
13386
|
-
if (!
|
|
13387
|
-
|
|
14868
|
+
if (!existsSync28(skillDir)) {
|
|
14869
|
+
mkdirSync17(skillDir, { recursive: true });
|
|
13388
14870
|
}
|
|
13389
14871
|
const files = parseSkillContent2(skillContent);
|
|
13390
14872
|
for (const [filePath, fileContent] of Object.entries(files)) {
|
|
@@ -13394,11 +14876,11 @@ function importBundle(bundlePath, targetDir, options = {}) {
|
|
|
13394
14876
|
errors.push(`Skill "${skill.name}" contains invalid file path: ${filePath}`);
|
|
13395
14877
|
continue;
|
|
13396
14878
|
}
|
|
13397
|
-
const fileDir =
|
|
13398
|
-
if (!
|
|
13399
|
-
|
|
14879
|
+
const fileDir = dirname9(fullPath);
|
|
14880
|
+
if (!existsSync28(fileDir)) {
|
|
14881
|
+
mkdirSync17(fileDir, { recursive: true });
|
|
13400
14882
|
}
|
|
13401
|
-
|
|
14883
|
+
writeFileSync16(fullPath, fileContent, "utf-8");
|
|
13402
14884
|
}
|
|
13403
14885
|
imported.push(skill.name);
|
|
13404
14886
|
}
|
|
@@ -13427,8 +14909,8 @@ function parseSkillContent2(content) {
|
|
|
13427
14909
|
}
|
|
13428
14910
|
|
|
13429
14911
|
// src/plugins/manager.ts
|
|
13430
|
-
import { existsSync as
|
|
13431
|
-
import { join as
|
|
14912
|
+
import { existsSync as existsSync29, readFileSync as readFileSync19, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18 } from "fs";
|
|
14913
|
+
import { join as join28 } from "path";
|
|
13432
14914
|
var PLUGINS_DIR = ".skillkit/plugins";
|
|
13433
14915
|
var PLUGINS_CONFIG_FILE = "plugins.json";
|
|
13434
14916
|
var PluginManager = class {
|
|
@@ -13669,22 +15151,22 @@ var PluginManager = class {
|
|
|
13669
15151
|
}
|
|
13670
15152
|
// Private helpers
|
|
13671
15153
|
loadState() {
|
|
13672
|
-
const statePath =
|
|
13673
|
-
if (
|
|
15154
|
+
const statePath = join28(this.projectPath, PLUGINS_DIR, PLUGINS_CONFIG_FILE);
|
|
15155
|
+
if (existsSync29(statePath)) {
|
|
13674
15156
|
try {
|
|
13675
|
-
return JSON.parse(
|
|
15157
|
+
return JSON.parse(readFileSync19(statePath, "utf-8"));
|
|
13676
15158
|
} catch {
|
|
13677
15159
|
}
|
|
13678
15160
|
}
|
|
13679
15161
|
return { version: 1, plugins: {} };
|
|
13680
15162
|
}
|
|
13681
15163
|
saveState() {
|
|
13682
|
-
const pluginsDir =
|
|
13683
|
-
if (!
|
|
13684
|
-
|
|
15164
|
+
const pluginsDir = join28(this.projectPath, PLUGINS_DIR);
|
|
15165
|
+
if (!existsSync29(pluginsDir)) {
|
|
15166
|
+
mkdirSync18(pluginsDir, { recursive: true });
|
|
13685
15167
|
}
|
|
13686
|
-
const statePath =
|
|
13687
|
-
|
|
15168
|
+
const statePath = join28(pluginsDir, PLUGINS_CONFIG_FILE);
|
|
15169
|
+
writeFileSync17(statePath, JSON.stringify(this.state, null, 2), "utf-8");
|
|
13688
15170
|
}
|
|
13689
15171
|
createContext() {
|
|
13690
15172
|
return {
|
|
@@ -13709,15 +15191,15 @@ function createPluginManager(projectPath) {
|
|
|
13709
15191
|
}
|
|
13710
15192
|
|
|
13711
15193
|
// src/plugins/loader.ts
|
|
13712
|
-
import { existsSync as
|
|
13713
|
-
import { join as
|
|
15194
|
+
import { existsSync as existsSync30, readFileSync as readFileSync20, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
15195
|
+
import { join as join29, extname, basename as basename13, isAbsolute } from "path";
|
|
13714
15196
|
import { pathToFileURL } from "url";
|
|
13715
15197
|
var PluginLoader = class {
|
|
13716
15198
|
/**
|
|
13717
15199
|
* Load a plugin from a file path
|
|
13718
15200
|
*/
|
|
13719
15201
|
async loadFromFile(filePath) {
|
|
13720
|
-
if (!
|
|
15202
|
+
if (!existsSync30(filePath)) {
|
|
13721
15203
|
throw new Error(`Plugin file not found: ${filePath}`);
|
|
13722
15204
|
}
|
|
13723
15205
|
const ext = extname(filePath);
|
|
@@ -13759,11 +15241,11 @@ var PluginLoader = class {
|
|
|
13759
15241
|
* Load a plugin from a JSON definition (for simple plugins)
|
|
13760
15242
|
*/
|
|
13761
15243
|
loadFromJson(jsonPath) {
|
|
13762
|
-
if (!
|
|
15244
|
+
if (!existsSync30(jsonPath)) {
|
|
13763
15245
|
throw new Error(`Plugin JSON not found: ${jsonPath}`);
|
|
13764
15246
|
}
|
|
13765
15247
|
try {
|
|
13766
|
-
const content =
|
|
15248
|
+
const content = readFileSync20(jsonPath, "utf-8");
|
|
13767
15249
|
const data = JSON.parse(content);
|
|
13768
15250
|
if (!data.metadata) {
|
|
13769
15251
|
throw new Error("Invalid plugin JSON: missing metadata");
|
|
@@ -13782,13 +15264,13 @@ var PluginLoader = class {
|
|
|
13782
15264
|
* Scan a directory for plugins
|
|
13783
15265
|
*/
|
|
13784
15266
|
async scanDirectory(dirPath) {
|
|
13785
|
-
if (!
|
|
15267
|
+
if (!existsSync30(dirPath)) {
|
|
13786
15268
|
return [];
|
|
13787
15269
|
}
|
|
13788
15270
|
const plugins = [];
|
|
13789
15271
|
const entries = readdirSync7(dirPath);
|
|
13790
15272
|
for (const entry of entries) {
|
|
13791
|
-
const fullPath =
|
|
15273
|
+
const fullPath = join29(dirPath, entry);
|
|
13792
15274
|
let stat;
|
|
13793
15275
|
try {
|
|
13794
15276
|
stat = statSync4(fullPath);
|
|
@@ -13796,11 +15278,11 @@ var PluginLoader = class {
|
|
|
13796
15278
|
continue;
|
|
13797
15279
|
}
|
|
13798
15280
|
if (stat.isDirectory()) {
|
|
13799
|
-
const pkgPath =
|
|
13800
|
-
const pluginPath =
|
|
13801
|
-
if (
|
|
15281
|
+
const pkgPath = join29(fullPath, "package.json");
|
|
15282
|
+
const pluginPath = join29(fullPath, "plugin.json");
|
|
15283
|
+
if (existsSync30(pkgPath)) {
|
|
13802
15284
|
try {
|
|
13803
|
-
const pkg = JSON.parse(
|
|
15285
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
|
|
13804
15286
|
if ((pkg.skillkitPlugin || pkg.keywords?.includes("skillkit-plugin")) && pkg.name && typeof pkg.name === "string" && pkg.version && typeof pkg.version === "string") {
|
|
13805
15287
|
plugins.push({
|
|
13806
15288
|
name: pkg.name,
|
|
@@ -13812,9 +15294,9 @@ var PluginLoader = class {
|
|
|
13812
15294
|
}
|
|
13813
15295
|
} catch {
|
|
13814
15296
|
}
|
|
13815
|
-
} else if (
|
|
15297
|
+
} else if (existsSync30(pluginPath)) {
|
|
13816
15298
|
try {
|
|
13817
|
-
const data = JSON.parse(
|
|
15299
|
+
const data = JSON.parse(readFileSync20(pluginPath, "utf-8"));
|
|
13818
15300
|
if (data.metadata && data.metadata.name && typeof data.metadata.name === "string" && data.metadata.version && typeof data.metadata.version === "string") {
|
|
13819
15301
|
plugins.push(data.metadata);
|
|
13820
15302
|
}
|
|
@@ -13824,7 +15306,7 @@ var PluginLoader = class {
|
|
|
13824
15306
|
} else if (stat.isFile()) {
|
|
13825
15307
|
const ext = extname(entry);
|
|
13826
15308
|
if (ext === ".js" || ext === ".mjs") {
|
|
13827
|
-
const name =
|
|
15309
|
+
const name = basename13(entry, ext);
|
|
13828
15310
|
if (name.includes("plugin") || name.startsWith("skillkit-")) {
|
|
13829
15311
|
plugins.push({
|
|
13830
15312
|
name,
|
|
@@ -13902,12 +15384,12 @@ async function loadPlugin(source) {
|
|
|
13902
15384
|
async function loadPluginsFromDirectory(dirPath) {
|
|
13903
15385
|
const loader = new PluginLoader();
|
|
13904
15386
|
const plugins = [];
|
|
13905
|
-
if (!
|
|
15387
|
+
if (!existsSync30(dirPath)) {
|
|
13906
15388
|
return plugins;
|
|
13907
15389
|
}
|
|
13908
15390
|
const entries = readdirSync7(dirPath);
|
|
13909
15391
|
for (const entry of entries) {
|
|
13910
|
-
const fullPath =
|
|
15392
|
+
const fullPath = join29(dirPath, entry);
|
|
13911
15393
|
let stat;
|
|
13912
15394
|
try {
|
|
13913
15395
|
stat = statSync4(fullPath);
|
|
@@ -13916,20 +15398,20 @@ async function loadPluginsFromDirectory(dirPath) {
|
|
|
13916
15398
|
}
|
|
13917
15399
|
try {
|
|
13918
15400
|
if (stat.isDirectory()) {
|
|
13919
|
-
const indexMjsPath =
|
|
13920
|
-
const indexPath =
|
|
13921
|
-
const mainMjsPath =
|
|
13922
|
-
const mainPath =
|
|
13923
|
-
const jsonPath =
|
|
13924
|
-
if (
|
|
15401
|
+
const indexMjsPath = join29(fullPath, "index.mjs");
|
|
15402
|
+
const indexPath = join29(fullPath, "index.js");
|
|
15403
|
+
const mainMjsPath = join29(fullPath, "plugin.mjs");
|
|
15404
|
+
const mainPath = join29(fullPath, "plugin.js");
|
|
15405
|
+
const jsonPath = join29(fullPath, "plugin.json");
|
|
15406
|
+
if (existsSync30(indexMjsPath)) {
|
|
13925
15407
|
plugins.push(await loader.loadFromFile(indexMjsPath));
|
|
13926
|
-
} else if (
|
|
15408
|
+
} else if (existsSync30(indexPath)) {
|
|
13927
15409
|
plugins.push(await loader.loadFromFile(indexPath));
|
|
13928
|
-
} else if (
|
|
15410
|
+
} else if (existsSync30(mainMjsPath)) {
|
|
13929
15411
|
plugins.push(await loader.loadFromFile(mainMjsPath));
|
|
13930
|
-
} else if (
|
|
15412
|
+
} else if (existsSync30(mainPath)) {
|
|
13931
15413
|
plugins.push(await loader.loadFromFile(mainPath));
|
|
13932
|
-
} else if (
|
|
15414
|
+
} else if (existsSync30(jsonPath)) {
|
|
13933
15415
|
plugins.push(loader.loadFromJson(jsonPath));
|
|
13934
15416
|
}
|
|
13935
15417
|
} else if (stat.isFile()) {
|
|
@@ -13948,17 +15430,17 @@ async function loadPluginsFromDirectory(dirPath) {
|
|
|
13948
15430
|
}
|
|
13949
15431
|
|
|
13950
15432
|
// src/methodology/manager.ts
|
|
13951
|
-
import { existsSync as
|
|
13952
|
-
import { join as
|
|
15433
|
+
import { existsSync as existsSync33, readFileSync as readFileSync23, writeFileSync as writeFileSync18, mkdirSync as mkdirSync19 } from "fs";
|
|
15434
|
+
import { join as join32, dirname as dirname11 } from "path";
|
|
13953
15435
|
|
|
13954
15436
|
// src/methodology/loader.ts
|
|
13955
|
-
import { existsSync as
|
|
13956
|
-
import { join as
|
|
15437
|
+
import { existsSync as existsSync32, readFileSync as readFileSync22, readdirSync as readdirSync9, statSync as statSync6 } from "fs";
|
|
15438
|
+
import { join as join31, dirname as dirname10 } from "path";
|
|
13957
15439
|
import { fileURLToPath } from "url";
|
|
13958
15440
|
|
|
13959
15441
|
// src/methodology/validator.ts
|
|
13960
|
-
import { existsSync as
|
|
13961
|
-
import { join as
|
|
15442
|
+
import { existsSync as existsSync31, readFileSync as readFileSync21, readdirSync as readdirSync8, statSync as statSync5 } from "fs";
|
|
15443
|
+
import { join as join30 } from "path";
|
|
13962
15444
|
function validatePackManifest(pack) {
|
|
13963
15445
|
const errors = [];
|
|
13964
15446
|
const warnings = [];
|
|
@@ -14108,15 +15590,15 @@ function validateSkillContent(content) {
|
|
|
14108
15590
|
function validatePackDirectory(packPath) {
|
|
14109
15591
|
const errors = [];
|
|
14110
15592
|
const warnings = [];
|
|
14111
|
-
if (!
|
|
15593
|
+
if (!existsSync31(packPath)) {
|
|
14112
15594
|
errors.push({
|
|
14113
15595
|
code: "DIR_NOT_FOUND",
|
|
14114
15596
|
message: `Pack directory not found: ${packPath}`
|
|
14115
15597
|
});
|
|
14116
15598
|
return { valid: false, errors, warnings };
|
|
14117
15599
|
}
|
|
14118
|
-
const manifestPath =
|
|
14119
|
-
if (!
|
|
15600
|
+
const manifestPath = join30(packPath, "pack.json");
|
|
15601
|
+
if (!existsSync31(manifestPath)) {
|
|
14120
15602
|
errors.push({
|
|
14121
15603
|
code: "MANIFEST_NOT_FOUND",
|
|
14122
15604
|
message: "Pack must have a pack.json manifest",
|
|
@@ -14126,7 +15608,7 @@ function validatePackDirectory(packPath) {
|
|
|
14126
15608
|
}
|
|
14127
15609
|
let manifest;
|
|
14128
15610
|
try {
|
|
14129
|
-
const raw =
|
|
15611
|
+
const raw = readFileSync21(manifestPath, "utf-8");
|
|
14130
15612
|
manifest = JSON.parse(raw);
|
|
14131
15613
|
const manifestResult = validatePackManifest(manifest);
|
|
14132
15614
|
errors.push(...manifestResult.errors);
|
|
@@ -14141,8 +15623,8 @@ function validatePackDirectory(packPath) {
|
|
|
14141
15623
|
}
|
|
14142
15624
|
if (manifest.skills) {
|
|
14143
15625
|
for (const skillName of manifest.skills) {
|
|
14144
|
-
const skillDir =
|
|
14145
|
-
if (!
|
|
15626
|
+
const skillDir = join30(packPath, skillName);
|
|
15627
|
+
if (!existsSync31(skillDir)) {
|
|
14146
15628
|
errors.push({
|
|
14147
15629
|
code: "SKILL_DIR_NOT_FOUND",
|
|
14148
15630
|
message: `Skill directory not found: ${skillName}`,
|
|
@@ -14158,8 +15640,8 @@ function validatePackDirectory(packPath) {
|
|
|
14158
15640
|
});
|
|
14159
15641
|
continue;
|
|
14160
15642
|
}
|
|
14161
|
-
const skillFile =
|
|
14162
|
-
if (!
|
|
15643
|
+
const skillFile = join30(skillDir, "SKILL.md");
|
|
15644
|
+
if (!existsSync31(skillFile)) {
|
|
14163
15645
|
errors.push({
|
|
14164
15646
|
code: "SKILL_FILE_NOT_FOUND",
|
|
14165
15647
|
message: `Skill must have a SKILL.md file: ${skillName}`,
|
|
@@ -14167,7 +15649,7 @@ function validatePackDirectory(packPath) {
|
|
|
14167
15649
|
});
|
|
14168
15650
|
continue;
|
|
14169
15651
|
}
|
|
14170
|
-
const skillContent =
|
|
15652
|
+
const skillContent = readFileSync21(skillFile, "utf-8");
|
|
14171
15653
|
const skillResult = validateSkillContent(skillContent);
|
|
14172
15654
|
for (const err of skillResult.errors) {
|
|
14173
15655
|
errors.push({
|
|
@@ -14191,15 +15673,15 @@ function validatePackDirectory(packPath) {
|
|
|
14191
15673
|
}
|
|
14192
15674
|
function validateBuiltinPacks(packsDir) {
|
|
14193
15675
|
const results = /* @__PURE__ */ new Map();
|
|
14194
|
-
if (!
|
|
15676
|
+
if (!existsSync31(packsDir)) {
|
|
14195
15677
|
return results;
|
|
14196
15678
|
}
|
|
14197
15679
|
const packDirs = readdirSync8(packsDir).filter((name) => {
|
|
14198
|
-
const packPath =
|
|
15680
|
+
const packPath = join30(packsDir, name);
|
|
14199
15681
|
return statSync5(packPath).isDirectory();
|
|
14200
15682
|
});
|
|
14201
15683
|
for (const packName of packDirs) {
|
|
14202
|
-
const packPath =
|
|
15684
|
+
const packPath = join30(packsDir, packName);
|
|
14203
15685
|
results.set(packName, validatePackDirectory(packPath));
|
|
14204
15686
|
}
|
|
14205
15687
|
return results;
|
|
@@ -14257,8 +15739,8 @@ function extractSkillMetadata(content) {
|
|
|
14257
15739
|
|
|
14258
15740
|
// src/methodology/loader.ts
|
|
14259
15741
|
var __filename = fileURLToPath(import.meta.url);
|
|
14260
|
-
var __dirname =
|
|
14261
|
-
var BUILTIN_PACKS_DIR =
|
|
15742
|
+
var __dirname = dirname10(__filename);
|
|
15743
|
+
var BUILTIN_PACKS_DIR = join31(__dirname, "packs");
|
|
14262
15744
|
var MethodologyLoader = class {
|
|
14263
15745
|
packsDir;
|
|
14264
15746
|
loadedPacks = /* @__PURE__ */ new Map();
|
|
@@ -14271,11 +15753,11 @@ var MethodologyLoader = class {
|
|
|
14271
15753
|
*/
|
|
14272
15754
|
async loadAllPacks() {
|
|
14273
15755
|
const packs = [];
|
|
14274
|
-
if (!
|
|
15756
|
+
if (!existsSync32(this.packsDir)) {
|
|
14275
15757
|
return packs;
|
|
14276
15758
|
}
|
|
14277
15759
|
const packDirs = readdirSync9(this.packsDir).filter((name) => {
|
|
14278
|
-
const packPath =
|
|
15760
|
+
const packPath = join31(this.packsDir, name);
|
|
14279
15761
|
return statSync6(packPath).isDirectory();
|
|
14280
15762
|
});
|
|
14281
15763
|
for (const packName of packDirs) {
|
|
@@ -14296,9 +15778,9 @@ var MethodologyLoader = class {
|
|
|
14296
15778
|
if (this.loadedPacks.has(packName)) {
|
|
14297
15779
|
return this.loadedPacks.get(packName);
|
|
14298
15780
|
}
|
|
14299
|
-
const packPath =
|
|
14300
|
-
const manifestPath =
|
|
14301
|
-
if (!
|
|
15781
|
+
const packPath = join31(this.packsDir, packName);
|
|
15782
|
+
const manifestPath = join31(packPath, "pack.json");
|
|
15783
|
+
if (!existsSync32(manifestPath)) {
|
|
14302
15784
|
return null;
|
|
14303
15785
|
}
|
|
14304
15786
|
const validation = validatePackDirectory(packPath);
|
|
@@ -14308,7 +15790,7 @@ var MethodologyLoader = class {
|
|
|
14308
15790
|
);
|
|
14309
15791
|
}
|
|
14310
15792
|
const manifest = JSON.parse(
|
|
14311
|
-
|
|
15793
|
+
readFileSync22(manifestPath, "utf-8")
|
|
14312
15794
|
);
|
|
14313
15795
|
this.loadedPacks.set(packName, manifest);
|
|
14314
15796
|
return manifest;
|
|
@@ -14338,11 +15820,11 @@ var MethodologyLoader = class {
|
|
|
14338
15820
|
if (this.loadedSkills.has(skillId)) {
|
|
14339
15821
|
return this.loadedSkills.get(skillId);
|
|
14340
15822
|
}
|
|
14341
|
-
const skillPath =
|
|
14342
|
-
if (!
|
|
15823
|
+
const skillPath = join31(this.packsDir, packName, skillName, "SKILL.md");
|
|
15824
|
+
if (!existsSync32(skillPath)) {
|
|
14343
15825
|
return null;
|
|
14344
15826
|
}
|
|
14345
|
-
const content =
|
|
15827
|
+
const content = readFileSync22(skillPath, "utf-8");
|
|
14346
15828
|
const rawMetadata = extractSkillMetadata(content);
|
|
14347
15829
|
const metadata = {
|
|
14348
15830
|
triggers: rawMetadata.triggers,
|
|
@@ -14421,9 +15903,9 @@ var MethodologyLoader = class {
|
|
|
14421
15903
|
* Check if a pack exists
|
|
14422
15904
|
*/
|
|
14423
15905
|
packExists(packName) {
|
|
14424
|
-
const packPath =
|
|
14425
|
-
const manifestPath =
|
|
14426
|
-
return
|
|
15906
|
+
const packPath = join31(this.packsDir, packName);
|
|
15907
|
+
const manifestPath = join31(packPath, "pack.json");
|
|
15908
|
+
return existsSync32(manifestPath);
|
|
14427
15909
|
}
|
|
14428
15910
|
/**
|
|
14429
15911
|
* Clear cache
|
|
@@ -14789,10 +16271,10 @@ var MethodologyManager = class {
|
|
|
14789
16271
|
}
|
|
14790
16272
|
// Private helpers
|
|
14791
16273
|
loadState() {
|
|
14792
|
-
const statePath =
|
|
14793
|
-
if (
|
|
16274
|
+
const statePath = join32(this.projectPath, METHODOLOGY_STATE_FILE);
|
|
16275
|
+
if (existsSync33(statePath)) {
|
|
14794
16276
|
try {
|
|
14795
|
-
return JSON.parse(
|
|
16277
|
+
return JSON.parse(readFileSync23(statePath, "utf-8"));
|
|
14796
16278
|
} catch {
|
|
14797
16279
|
}
|
|
14798
16280
|
}
|
|
@@ -14803,21 +16285,21 @@ var MethodologyManager = class {
|
|
|
14803
16285
|
};
|
|
14804
16286
|
}
|
|
14805
16287
|
saveState() {
|
|
14806
|
-
const statePath =
|
|
14807
|
-
const dir =
|
|
14808
|
-
if (!
|
|
14809
|
-
|
|
16288
|
+
const statePath = join32(this.projectPath, METHODOLOGY_STATE_FILE);
|
|
16289
|
+
const dir = dirname11(statePath);
|
|
16290
|
+
if (!existsSync33(dir)) {
|
|
16291
|
+
mkdirSync19(dir, { recursive: true });
|
|
14810
16292
|
}
|
|
14811
|
-
|
|
16293
|
+
writeFileSync18(statePath, JSON.stringify(this.state, null, 2), "utf-8");
|
|
14812
16294
|
}
|
|
14813
16295
|
async installSkillLocally(skill) {
|
|
14814
|
-
const skillsDir =
|
|
14815
|
-
const skillDir =
|
|
14816
|
-
if (!
|
|
14817
|
-
|
|
16296
|
+
const skillsDir = join32(this.projectPath, ".skillkit", "methodology", "skills");
|
|
16297
|
+
const skillDir = join32(skillsDir, skill.pack, skill.id.split("/")[1]);
|
|
16298
|
+
if (!existsSync33(skillDir)) {
|
|
16299
|
+
mkdirSync19(skillDir, { recursive: true });
|
|
14818
16300
|
}
|
|
14819
|
-
const targetPath =
|
|
14820
|
-
|
|
16301
|
+
const targetPath = join32(skillDir, "SKILL.md");
|
|
16302
|
+
writeFileSync18(targetPath, skill.content, "utf-8");
|
|
14821
16303
|
}
|
|
14822
16304
|
async removeSkillLocally(_skillId) {
|
|
14823
16305
|
}
|
|
@@ -14845,9 +16327,9 @@ var MethodologyManager = class {
|
|
|
14845
16327
|
if (!agentDir) {
|
|
14846
16328
|
throw new Error(`Unknown agent: ${agent}`);
|
|
14847
16329
|
}
|
|
14848
|
-
const skillsDir =
|
|
14849
|
-
if (!
|
|
14850
|
-
|
|
16330
|
+
const skillsDir = join32(this.projectPath, agentDir);
|
|
16331
|
+
if (!existsSync33(skillsDir)) {
|
|
16332
|
+
mkdirSync19(skillsDir, { recursive: true });
|
|
14851
16333
|
}
|
|
14852
16334
|
const skillName = skillId.split("/")[1];
|
|
14853
16335
|
let filename;
|
|
@@ -14858,8 +16340,8 @@ var MethodologyManager = class {
|
|
|
14858
16340
|
} else {
|
|
14859
16341
|
filename = `${skillName}.md`;
|
|
14860
16342
|
}
|
|
14861
|
-
const targetPath =
|
|
14862
|
-
|
|
16343
|
+
const targetPath = join32(skillsDir, filename);
|
|
16344
|
+
writeFileSync18(targetPath, content, "utf-8");
|
|
14863
16345
|
}
|
|
14864
16346
|
detectAgents() {
|
|
14865
16347
|
const agents = [];
|
|
@@ -14882,7 +16364,7 @@ var MethodologyManager = class {
|
|
|
14882
16364
|
["windsurf", ".windsurf"]
|
|
14883
16365
|
];
|
|
14884
16366
|
for (const [agent, dir] of agentDirs) {
|
|
14885
|
-
if (
|
|
16367
|
+
if (existsSync33(join32(this.projectPath, dir))) {
|
|
14886
16368
|
agents.push(agent);
|
|
14887
16369
|
}
|
|
14888
16370
|
}
|
|
@@ -14895,9 +16377,9 @@ function createMethodologyManager(options) {
|
|
|
14895
16377
|
}
|
|
14896
16378
|
|
|
14897
16379
|
// src/hooks/manager.ts
|
|
14898
|
-
import { existsSync as
|
|
14899
|
-
import { join as
|
|
14900
|
-
import { randomUUID as
|
|
16380
|
+
import { existsSync as existsSync34, readFileSync as readFileSync24, writeFileSync as writeFileSync19, mkdirSync as mkdirSync20 } from "fs";
|
|
16381
|
+
import { join as join33, dirname as dirname12 } from "path";
|
|
16382
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
14901
16383
|
import { minimatch } from "minimatch";
|
|
14902
16384
|
var DEFAULT_CONFIG_PATH = ".skillkit/hooks.json";
|
|
14903
16385
|
var HookManager = class {
|
|
@@ -14907,7 +16389,7 @@ var HookManager = class {
|
|
|
14907
16389
|
constructor(options) {
|
|
14908
16390
|
this.options = {
|
|
14909
16391
|
projectPath: options.projectPath,
|
|
14910
|
-
configPath: options.configPath ||
|
|
16392
|
+
configPath: options.configPath || join33(options.projectPath, DEFAULT_CONFIG_PATH),
|
|
14911
16393
|
autoLoad: options.autoLoad ?? true,
|
|
14912
16394
|
defaultInjectionMode: options.defaultInjectionMode || "reference"
|
|
14913
16395
|
};
|
|
@@ -14921,7 +16403,7 @@ var HookManager = class {
|
|
|
14921
16403
|
registerHook(hook) {
|
|
14922
16404
|
const fullHook = {
|
|
14923
16405
|
...hook,
|
|
14924
|
-
id: hook.id ||
|
|
16406
|
+
id: hook.id || randomUUID11(),
|
|
14925
16407
|
inject: hook.inject || this.options.defaultInjectionMode,
|
|
14926
16408
|
enabled: hook.enabled ?? true,
|
|
14927
16409
|
priority: hook.priority ?? 0
|
|
@@ -15047,11 +16529,11 @@ var HookManager = class {
|
|
|
15047
16529
|
* Load hooks from config file
|
|
15048
16530
|
*/
|
|
15049
16531
|
load() {
|
|
15050
|
-
if (!
|
|
16532
|
+
if (!existsSync34(this.options.configPath)) {
|
|
15051
16533
|
return;
|
|
15052
16534
|
}
|
|
15053
16535
|
try {
|
|
15054
|
-
const content =
|
|
16536
|
+
const content = readFileSync24(this.options.configPath, "utf-8");
|
|
15055
16537
|
const config = JSON.parse(content);
|
|
15056
16538
|
this.hooks.clear();
|
|
15057
16539
|
for (const hook of config.hooks) {
|
|
@@ -15078,11 +16560,11 @@ var HookManager = class {
|
|
|
15078
16560
|
enabled: true
|
|
15079
16561
|
}
|
|
15080
16562
|
};
|
|
15081
|
-
const dir =
|
|
15082
|
-
if (!
|
|
15083
|
-
|
|
16563
|
+
const dir = dirname12(this.options.configPath);
|
|
16564
|
+
if (!existsSync34(dir)) {
|
|
16565
|
+
mkdirSync20(dir, { recursive: true });
|
|
15084
16566
|
}
|
|
15085
|
-
|
|
16567
|
+
writeFileSync19(this.options.configPath, JSON.stringify(config, null, 2));
|
|
15086
16568
|
}
|
|
15087
16569
|
/**
|
|
15088
16570
|
* Generate agent-specific hook configuration
|
|
@@ -15324,7 +16806,7 @@ function createHookManager(options) {
|
|
|
15324
16806
|
|
|
15325
16807
|
// src/hooks/triggers.ts
|
|
15326
16808
|
import { watch } from "fs";
|
|
15327
|
-
import { join as
|
|
16809
|
+
import { join as join34, relative as relative2 } from "path";
|
|
15328
16810
|
function debounce(fn, delay) {
|
|
15329
16811
|
let timeoutId = null;
|
|
15330
16812
|
return (...args) => {
|
|
@@ -15509,7 +16991,7 @@ var SkillTriggerEngine = class {
|
|
|
15509
16991
|
{ recursive: true },
|
|
15510
16992
|
debounce((eventType, filename) => {
|
|
15511
16993
|
if (!filename) return;
|
|
15512
|
-
const relativePath = relative2(this.options.projectPath,
|
|
16994
|
+
const relativePath = relative2(this.options.projectPath, join34(this.options.projectPath, filename));
|
|
15513
16995
|
if (this.shouldIgnore(relativePath)) return;
|
|
15514
16996
|
if (eventType === "rename") {
|
|
15515
16997
|
this.triggerFileCreate(relativePath);
|
|
@@ -15553,10 +17035,10 @@ function createTriggerEngine(hookManager, options) {
|
|
|
15553
17035
|
}
|
|
15554
17036
|
|
|
15555
17037
|
// src/orchestrator/team.ts
|
|
15556
|
-
import { randomUUID as
|
|
17038
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
15557
17039
|
|
|
15558
17040
|
// src/orchestrator/task.ts
|
|
15559
|
-
import { randomUUID as
|
|
17041
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
15560
17042
|
var TaskManager = class {
|
|
15561
17043
|
tasks = /* @__PURE__ */ new Map();
|
|
15562
17044
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -15566,7 +17048,7 @@ var TaskManager = class {
|
|
|
15566
17048
|
createTask(name, description, spec, options) {
|
|
15567
17049
|
const now = /* @__PURE__ */ new Date();
|
|
15568
17050
|
const task = {
|
|
15569
|
-
id:
|
|
17051
|
+
id: randomUUID12(),
|
|
15570
17052
|
name,
|
|
15571
17053
|
description,
|
|
15572
17054
|
spec,
|
|
@@ -15885,7 +17367,7 @@ function createTaskManager() {
|
|
|
15885
17367
|
}
|
|
15886
17368
|
|
|
15887
17369
|
// src/orchestrator/messages.ts
|
|
15888
|
-
import { randomUUID as
|
|
17370
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
15889
17371
|
var TeamMessageBus = class {
|
|
15890
17372
|
messages = [];
|
|
15891
17373
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -15899,7 +17381,7 @@ var TeamMessageBus = class {
|
|
|
15899
17381
|
*/
|
|
15900
17382
|
async send(from, to, content, options) {
|
|
15901
17383
|
const message = {
|
|
15902
|
-
id:
|
|
17384
|
+
id: randomUUID13(),
|
|
15903
17385
|
type: options?.type || "direct",
|
|
15904
17386
|
from,
|
|
15905
17387
|
to,
|
|
@@ -15918,7 +17400,7 @@ var TeamMessageBus = class {
|
|
|
15918
17400
|
*/
|
|
15919
17401
|
async broadcast(from, content, options) {
|
|
15920
17402
|
const message = {
|
|
15921
|
-
id:
|
|
17403
|
+
id: randomUUID13(),
|
|
15922
17404
|
type: "broadcast",
|
|
15923
17405
|
from,
|
|
15924
17406
|
content,
|
|
@@ -16178,10 +17660,10 @@ var TeamOrchestrator = class {
|
|
|
16178
17660
|
* Spawn a new team
|
|
16179
17661
|
*/
|
|
16180
17662
|
async spawnTeam(config) {
|
|
16181
|
-
const teamId =
|
|
17663
|
+
const teamId = randomUUID14();
|
|
16182
17664
|
const now = /* @__PURE__ */ new Date();
|
|
16183
17665
|
const leader = {
|
|
16184
|
-
id:
|
|
17666
|
+
id: randomUUID14(),
|
|
16185
17667
|
role: "leader",
|
|
16186
17668
|
agentType: config.leaderAgent,
|
|
16187
17669
|
status: "idle",
|
|
@@ -16223,7 +17705,7 @@ var TeamOrchestrator = class {
|
|
|
16223
17705
|
if (!team) throw new Error(`Team not found: ${teamId}`);
|
|
16224
17706
|
const now = /* @__PURE__ */ new Date();
|
|
16225
17707
|
const teammate = {
|
|
16226
|
-
id:
|
|
17708
|
+
id: randomUUID14(),
|
|
16227
17709
|
role: "teammate",
|
|
16228
17710
|
agentType,
|
|
16229
17711
|
status: "idle",
|
|
@@ -19610,8 +21092,8 @@ var CUSTOM_AGENT_FORMAT_MAP = {
|
|
|
19610
21092
|
};
|
|
19611
21093
|
|
|
19612
21094
|
// src/agents/parser.ts
|
|
19613
|
-
import { existsSync as
|
|
19614
|
-
import { join as
|
|
21095
|
+
import { existsSync as existsSync35, readFileSync as readFileSync25, statSync as statSync7 } from "fs";
|
|
21096
|
+
import { join as join35, basename as basename14, extname as extname2 } from "path";
|
|
19615
21097
|
import { parse as parseYaml10 } from "yaml";
|
|
19616
21098
|
function extractAgentFrontmatter(content) {
|
|
19617
21099
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
@@ -19629,18 +21111,18 @@ function extractAgentContent(content) {
|
|
|
19629
21111
|
return withoutFrontmatter.trim();
|
|
19630
21112
|
}
|
|
19631
21113
|
function parseAgentFile(filePath, location = "project") {
|
|
19632
|
-
if (!
|
|
21114
|
+
if (!existsSync35(filePath)) {
|
|
19633
21115
|
return null;
|
|
19634
21116
|
}
|
|
19635
21117
|
if (extname2(filePath) !== ".md") {
|
|
19636
21118
|
return null;
|
|
19637
21119
|
}
|
|
19638
21120
|
try {
|
|
19639
|
-
const content =
|
|
21121
|
+
const content = readFileSync25(filePath, "utf-8");
|
|
19640
21122
|
const rawFrontmatter = extractAgentFrontmatter(content);
|
|
19641
21123
|
const agentContent = extractAgentContent(content);
|
|
19642
21124
|
if (!rawFrontmatter) {
|
|
19643
|
-
const name =
|
|
21125
|
+
const name = basename14(filePath, ".md");
|
|
19644
21126
|
return {
|
|
19645
21127
|
name,
|
|
19646
21128
|
description: "No description available",
|
|
@@ -19656,7 +21138,7 @@ function parseAgentFile(filePath, location = "project") {
|
|
|
19656
21138
|
}
|
|
19657
21139
|
const parsed = AgentFrontmatter.safeParse(rawFrontmatter);
|
|
19658
21140
|
if (!parsed.success) {
|
|
19659
|
-
const name = rawFrontmatter.name ||
|
|
21141
|
+
const name = rawFrontmatter.name || basename14(filePath, ".md");
|
|
19660
21142
|
return {
|
|
19661
21143
|
name,
|
|
19662
21144
|
description: rawFrontmatter.description || "No description available",
|
|
@@ -19688,16 +21170,16 @@ function parseAgentFile(filePath, location = "project") {
|
|
|
19688
21170
|
}
|
|
19689
21171
|
}
|
|
19690
21172
|
function parseAgentDir(dirPath, location = "project") {
|
|
19691
|
-
const agentMdPath =
|
|
19692
|
-
if (
|
|
21173
|
+
const agentMdPath = join35(dirPath, "AGENT.md");
|
|
21174
|
+
if (existsSync35(agentMdPath)) {
|
|
19693
21175
|
const agent = parseAgentFile(agentMdPath, location);
|
|
19694
21176
|
if (agent) {
|
|
19695
21177
|
agent.path = dirPath;
|
|
19696
21178
|
}
|
|
19697
21179
|
return agent;
|
|
19698
21180
|
}
|
|
19699
|
-
const indexMdPath =
|
|
19700
|
-
if (
|
|
21181
|
+
const indexMdPath = join35(dirPath, "index.md");
|
|
21182
|
+
if (existsSync35(indexMdPath)) {
|
|
19701
21183
|
const agent = parseAgentFile(indexMdPath, location);
|
|
19702
21184
|
if (agent) {
|
|
19703
21185
|
agent.path = dirPath;
|
|
@@ -19707,11 +21189,11 @@ function parseAgentDir(dirPath, location = "project") {
|
|
|
19707
21189
|
return null;
|
|
19708
21190
|
}
|
|
19709
21191
|
function loadAgentMetadata(metadataPath) {
|
|
19710
|
-
if (!
|
|
21192
|
+
if (!existsSync35(metadataPath)) {
|
|
19711
21193
|
return null;
|
|
19712
21194
|
}
|
|
19713
21195
|
try {
|
|
19714
|
-
const content =
|
|
21196
|
+
const content = readFileSync25(metadataPath, "utf-8");
|
|
19715
21197
|
const data = JSON.parse(content);
|
|
19716
21198
|
const parsed = AgentMetadata.safeParse(data);
|
|
19717
21199
|
return parsed.success ? parsed.data : null;
|
|
@@ -19812,25 +21294,25 @@ function fromCanonicalAgent(canonical, _targetFormat = "claude-agent") {
|
|
|
19812
21294
|
return lines.join("\n");
|
|
19813
21295
|
}
|
|
19814
21296
|
function readAgentContent(agentPath) {
|
|
19815
|
-
if (
|
|
21297
|
+
if (existsSync35(agentPath) && extname2(agentPath) === ".md") {
|
|
19816
21298
|
try {
|
|
19817
|
-
return
|
|
21299
|
+
return readFileSync25(agentPath, "utf-8");
|
|
19818
21300
|
} catch {
|
|
19819
21301
|
return null;
|
|
19820
21302
|
}
|
|
19821
21303
|
}
|
|
19822
|
-
const agentMdPath =
|
|
19823
|
-
if (
|
|
21304
|
+
const agentMdPath = join35(agentPath, "AGENT.md");
|
|
21305
|
+
if (existsSync35(agentMdPath)) {
|
|
19824
21306
|
try {
|
|
19825
|
-
return
|
|
21307
|
+
return readFileSync25(agentMdPath, "utf-8");
|
|
19826
21308
|
} catch {
|
|
19827
21309
|
return null;
|
|
19828
21310
|
}
|
|
19829
21311
|
}
|
|
19830
|
-
const indexMdPath =
|
|
19831
|
-
if (
|
|
21312
|
+
const indexMdPath = join35(agentPath, "index.md");
|
|
21313
|
+
if (existsSync35(indexMdPath)) {
|
|
19832
21314
|
try {
|
|
19833
|
-
return
|
|
21315
|
+
return readFileSync25(indexMdPath, "utf-8");
|
|
19834
21316
|
} catch {
|
|
19835
21317
|
return null;
|
|
19836
21318
|
}
|
|
@@ -19841,17 +21323,17 @@ function validateAgent(agentPath) {
|
|
|
19841
21323
|
const errors = [];
|
|
19842
21324
|
const warnings = [];
|
|
19843
21325
|
let filePath = agentPath;
|
|
19844
|
-
if (!
|
|
21326
|
+
if (!existsSync35(agentPath)) {
|
|
19845
21327
|
errors.push(`Agent file not found: ${agentPath}`);
|
|
19846
21328
|
return { valid: false, errors, warnings };
|
|
19847
21329
|
}
|
|
19848
21330
|
const stats = statSync7(agentPath);
|
|
19849
21331
|
if (stats.isDirectory()) {
|
|
19850
|
-
const agentMd =
|
|
19851
|
-
const indexMd =
|
|
19852
|
-
if (
|
|
21332
|
+
const agentMd = join35(agentPath, "AGENT.md");
|
|
21333
|
+
const indexMd = join35(agentPath, "index.md");
|
|
21334
|
+
if (existsSync35(agentMd)) {
|
|
19853
21335
|
filePath = agentMd;
|
|
19854
|
-
} else if (
|
|
21336
|
+
} else if (existsSync35(indexMd)) {
|
|
19855
21337
|
filePath = indexMd;
|
|
19856
21338
|
} else {
|
|
19857
21339
|
errors.push("Directory must contain AGENT.md or index.md");
|
|
@@ -19860,7 +21342,7 @@ function validateAgent(agentPath) {
|
|
|
19860
21342
|
}
|
|
19861
21343
|
let content;
|
|
19862
21344
|
try {
|
|
19863
|
-
content =
|
|
21345
|
+
content = readFileSync25(filePath, "utf-8");
|
|
19864
21346
|
} catch {
|
|
19865
21347
|
errors.push(`Cannot read file: ${filePath}`);
|
|
19866
21348
|
return { valid: false, errors, warnings };
|
|
@@ -19878,7 +21360,7 @@ function validateAgent(agentPath) {
|
|
|
19878
21360
|
}
|
|
19879
21361
|
if (parsed.success) {
|
|
19880
21362
|
const data = parsed.data;
|
|
19881
|
-
const fileName =
|
|
21363
|
+
const fileName = basename14(filePath, ".md");
|
|
19882
21364
|
if (data.name !== fileName && fileName !== "AGENT" && fileName !== "index") {
|
|
19883
21365
|
warnings.push(`name "${data.name}" does not match filename "${fileName}"`);
|
|
19884
21366
|
}
|
|
@@ -19897,18 +21379,18 @@ function validateAgent(agentPath) {
|
|
|
19897
21379
|
}
|
|
19898
21380
|
|
|
19899
21381
|
// src/agents/discovery.ts
|
|
19900
|
-
import { existsSync as
|
|
19901
|
-
import { join as
|
|
19902
|
-
import { homedir as
|
|
21382
|
+
import { existsSync as existsSync36, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
21383
|
+
import { join as join36, extname as extname3 } from "path";
|
|
21384
|
+
import { homedir as homedir13 } from "os";
|
|
19903
21385
|
function discoverAgentsInDir(dir, location) {
|
|
19904
21386
|
const agents = [];
|
|
19905
|
-
if (!
|
|
21387
|
+
if (!existsSync36(dir)) {
|
|
19906
21388
|
return agents;
|
|
19907
21389
|
}
|
|
19908
21390
|
try {
|
|
19909
21391
|
const entries = readdirSync10(dir, { withFileTypes: true });
|
|
19910
21392
|
for (const entry of entries) {
|
|
19911
|
-
const entryPath =
|
|
21393
|
+
const entryPath = join36(dir, entry.name);
|
|
19912
21394
|
if (entry.isFile() && extname3(entry.name) === ".md") {
|
|
19913
21395
|
const agent = parseAgentFile(entryPath, location);
|
|
19914
21396
|
if (agent) {
|
|
@@ -19929,8 +21411,8 @@ function discoverAgents(rootDir) {
|
|
|
19929
21411
|
const agents = [];
|
|
19930
21412
|
const seen = /* @__PURE__ */ new Set();
|
|
19931
21413
|
for (const searchPath of ALL_AGENT_DISCOVERY_PATHS) {
|
|
19932
|
-
const fullPath =
|
|
19933
|
-
if (
|
|
21414
|
+
const fullPath = join36(rootDir, searchPath);
|
|
21415
|
+
if (existsSync36(fullPath)) {
|
|
19934
21416
|
for (const agent of discoverAgentsInDir(fullPath, "project")) {
|
|
19935
21417
|
if (!seen.has(agent.name)) {
|
|
19936
21418
|
seen.add(agent.name);
|
|
@@ -19946,8 +21428,8 @@ function discoverAgentsForAgent(rootDir, agentType) {
|
|
|
19946
21428
|
const seen = /* @__PURE__ */ new Set();
|
|
19947
21429
|
const paths = AGENT_DISCOVERY_PATHS[agentType] || [];
|
|
19948
21430
|
for (const searchPath of paths) {
|
|
19949
|
-
const fullPath =
|
|
19950
|
-
if (
|
|
21431
|
+
const fullPath = join36(rootDir, searchPath);
|
|
21432
|
+
if (existsSync36(fullPath)) {
|
|
19951
21433
|
for (const agent of discoverAgentsInDir(fullPath, "project")) {
|
|
19952
21434
|
if (!seen.has(agent.name)) {
|
|
19953
21435
|
seen.add(agent.name);
|
|
@@ -19961,14 +21443,14 @@ function discoverAgentsForAgent(rootDir, agentType) {
|
|
|
19961
21443
|
function discoverGlobalAgents() {
|
|
19962
21444
|
const agents = [];
|
|
19963
21445
|
const seen = /* @__PURE__ */ new Set();
|
|
19964
|
-
const home =
|
|
21446
|
+
const home = homedir13();
|
|
19965
21447
|
const globalPaths = [
|
|
19966
|
-
|
|
19967
|
-
|
|
19968
|
-
|
|
21448
|
+
join36(home, ".claude", "agents"),
|
|
21449
|
+
join36(home, ".skillkit", "agents"),
|
|
21450
|
+
join36(home, ".config", "skillkit", "agents")
|
|
19969
21451
|
];
|
|
19970
21452
|
for (const searchPath of globalPaths) {
|
|
19971
|
-
if (
|
|
21453
|
+
if (existsSync36(searchPath)) {
|
|
19972
21454
|
for (const agent of discoverAgentsInDir(searchPath, "global")) {
|
|
19973
21455
|
if (!seen.has(agent.name)) {
|
|
19974
21456
|
seen.add(agent.name);
|
|
@@ -19983,7 +21465,7 @@ function findAllAgents(searchDirs) {
|
|
|
19983
21465
|
const agents = [];
|
|
19984
21466
|
const seen = /* @__PURE__ */ new Set();
|
|
19985
21467
|
for (const dir of searchDirs) {
|
|
19986
|
-
if (!
|
|
21468
|
+
if (!existsSync36(dir)) continue;
|
|
19987
21469
|
const discovered = discoverAgents(dir);
|
|
19988
21470
|
for (const agent of discovered) {
|
|
19989
21471
|
if (!seen.has(agent.name)) {
|
|
@@ -20002,33 +21484,33 @@ function findAllAgents(searchDirs) {
|
|
|
20002
21484
|
}
|
|
20003
21485
|
function findAgent(name, searchDirs) {
|
|
20004
21486
|
for (const dir of searchDirs) {
|
|
20005
|
-
if (!
|
|
21487
|
+
if (!existsSync36(dir)) continue;
|
|
20006
21488
|
for (const searchPath of ALL_AGENT_DISCOVERY_PATHS) {
|
|
20007
|
-
const agentsDir =
|
|
20008
|
-
if (!
|
|
20009
|
-
const agentFile =
|
|
20010
|
-
if (
|
|
21489
|
+
const agentsDir = join36(dir, searchPath);
|
|
21490
|
+
if (!existsSync36(agentsDir)) continue;
|
|
21491
|
+
const agentFile = join36(agentsDir, `${name}.md`);
|
|
21492
|
+
if (existsSync36(agentFile)) {
|
|
20011
21493
|
return parseAgentFile(agentFile, "project");
|
|
20012
21494
|
}
|
|
20013
|
-
const agentDir =
|
|
20014
|
-
if (
|
|
21495
|
+
const agentDir = join36(agentsDir, name);
|
|
21496
|
+
if (existsSync36(agentDir) && statSync8(agentDir).isDirectory()) {
|
|
20015
21497
|
return parseAgentDir(agentDir, "project");
|
|
20016
21498
|
}
|
|
20017
21499
|
}
|
|
20018
21500
|
}
|
|
20019
|
-
const home =
|
|
21501
|
+
const home = homedir13();
|
|
20020
21502
|
const globalPaths = [
|
|
20021
|
-
|
|
20022
|
-
|
|
21503
|
+
join36(home, ".claude", "agents"),
|
|
21504
|
+
join36(home, ".skillkit", "agents")
|
|
20023
21505
|
];
|
|
20024
21506
|
for (const agentsDir of globalPaths) {
|
|
20025
|
-
if (!
|
|
20026
|
-
const agentFile =
|
|
20027
|
-
if (
|
|
21507
|
+
if (!existsSync36(agentsDir)) continue;
|
|
21508
|
+
const agentFile = join36(agentsDir, `${name}.md`);
|
|
21509
|
+
if (existsSync36(agentFile)) {
|
|
20028
21510
|
return parseAgentFile(agentFile, "global");
|
|
20029
21511
|
}
|
|
20030
|
-
const agentDir =
|
|
20031
|
-
if (
|
|
21512
|
+
const agentDir = join36(agentsDir, name);
|
|
21513
|
+
if (existsSync36(agentDir) && statSync8(agentDir).isDirectory()) {
|
|
20032
21514
|
return parseAgentDir(agentDir, "global");
|
|
20033
21515
|
}
|
|
20034
21516
|
}
|
|
@@ -20037,9 +21519,9 @@ function findAgent(name, searchDirs) {
|
|
|
20037
21519
|
function getAgentsDirectory(rootDir, agentType) {
|
|
20038
21520
|
const paths = AGENT_DISCOVERY_PATHS[agentType];
|
|
20039
21521
|
if (paths && paths.length > 0) {
|
|
20040
|
-
return
|
|
21522
|
+
return join36(rootDir, paths[0]);
|
|
20041
21523
|
}
|
|
20042
|
-
return
|
|
21524
|
+
return join36(rootDir, "agents");
|
|
20043
21525
|
}
|
|
20044
21526
|
function agentExists(name, searchDirs) {
|
|
20045
21527
|
return findAgent(name, searchDirs) !== null;
|
|
@@ -20057,14 +21539,14 @@ function getAgentStats(searchDirs) {
|
|
|
20057
21539
|
function discoverAgentsRecursive(rootDir, location = "project") {
|
|
20058
21540
|
const agents = [];
|
|
20059
21541
|
const seen = /* @__PURE__ */ new Set();
|
|
20060
|
-
if (!
|
|
21542
|
+
if (!existsSync36(rootDir)) {
|
|
20061
21543
|
return agents;
|
|
20062
21544
|
}
|
|
20063
21545
|
function scanDirectory(dir) {
|
|
20064
21546
|
try {
|
|
20065
21547
|
const entries = readdirSync10(dir, { withFileTypes: true });
|
|
20066
21548
|
for (const entry of entries) {
|
|
20067
|
-
const entryPath =
|
|
21549
|
+
const entryPath = join36(dir, entry.name);
|
|
20068
21550
|
if (entry.isFile() && extname3(entry.name) === ".md") {
|
|
20069
21551
|
const agent = parseAgentFile(entryPath, location);
|
|
20070
21552
|
if (agent && !seen.has(agent.name)) {
|
|
@@ -20091,7 +21573,7 @@ function discoverAgentsRecursive(rootDir, location = "project") {
|
|
|
20091
21573
|
return agents;
|
|
20092
21574
|
}
|
|
20093
21575
|
function discoverAgentsFromPath(sourcePath, recursive = false, location = "project") {
|
|
20094
|
-
if (!
|
|
21576
|
+
if (!existsSync36(sourcePath)) {
|
|
20095
21577
|
return [];
|
|
20096
21578
|
}
|
|
20097
21579
|
const stats = statSync8(sourcePath);
|
|
@@ -20114,7 +21596,7 @@ function discoverAgentsFromPath(sourcePath, recursive = false, location = "proje
|
|
|
20114
21596
|
try {
|
|
20115
21597
|
const entries = readdirSync10(sourcePath, { withFileTypes: true });
|
|
20116
21598
|
for (const entry of entries) {
|
|
20117
|
-
const entryPath =
|
|
21599
|
+
const entryPath = join36(sourcePath, entry.name);
|
|
20118
21600
|
if (entry.isFile() && extname3(entry.name) === ".md") {
|
|
20119
21601
|
const a = parseAgentFile(entryPath, location);
|
|
20120
21602
|
if (a) agents.push(a);
|
|
@@ -20131,7 +21613,7 @@ function discoverAgentsFromPath(sourcePath, recursive = false, location = "proje
|
|
|
20131
21613
|
}
|
|
20132
21614
|
|
|
20133
21615
|
// src/agents/translator.ts
|
|
20134
|
-
import { join as
|
|
21616
|
+
import { join as join37 } from "path";
|
|
20135
21617
|
function translateAgent(agent, targetAgent, options) {
|
|
20136
21618
|
const canonical = toCanonicalAgent(agent);
|
|
20137
21619
|
return translateCanonicalAgent(canonical, targetAgent, options);
|
|
@@ -20352,9 +21834,9 @@ function getAgentFilename(agentName, targetAgent) {
|
|
|
20352
21834
|
function getAgentTargetDirectory(rootDir, targetAgent) {
|
|
20353
21835
|
const paths = AGENT_DISCOVERY_PATHS[targetAgent];
|
|
20354
21836
|
if (paths && paths.length > 0) {
|
|
20355
|
-
return
|
|
21837
|
+
return join37(rootDir, paths[0]);
|
|
20356
21838
|
}
|
|
20357
|
-
return
|
|
21839
|
+
return join37(rootDir, "agents");
|
|
20358
21840
|
}
|
|
20359
21841
|
function escapeYamlString(str) {
|
|
20360
21842
|
if (/[:\{\}\[\],&*#?|\-<>=!%@`]/.test(str) || str.includes("\n")) {
|
|
@@ -20541,8 +22023,8 @@ function loadAndConvertSkill(skill, options) {
|
|
|
20541
22023
|
}
|
|
20542
22024
|
|
|
20543
22025
|
// src/skill-translator.ts
|
|
20544
|
-
import { readFileSync as
|
|
20545
|
-
import { join as
|
|
22026
|
+
import { readFileSync as readFileSync26, existsSync as existsSync37, writeFileSync as writeFileSync20, mkdirSync as mkdirSync21 } from "fs";
|
|
22027
|
+
import { join as join38, basename as basename15, dirname as dirname13, resolve as resolve5, relative as relative3, isAbsolute as isAbsolute2 } from "path";
|
|
20546
22028
|
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
20547
22029
|
var AGENT_SKILL_FORMATS = Object.fromEntries(
|
|
20548
22030
|
Object.entries(AGENT_CONFIG).map(([agent, config]) => [
|
|
@@ -20551,12 +22033,12 @@ var AGENT_SKILL_FORMATS = Object.fromEntries(
|
|
|
20551
22033
|
])
|
|
20552
22034
|
);
|
|
20553
22035
|
function parseSkillToCanonical(skillPath, sourceAgent) {
|
|
20554
|
-
const skillMdPath = skillPath.endsWith("SKILL.md") ? skillPath :
|
|
20555
|
-
if (!
|
|
22036
|
+
const skillMdPath = skillPath.endsWith("SKILL.md") ? skillPath : join38(skillPath, "SKILL.md");
|
|
22037
|
+
if (!existsSync37(skillMdPath)) {
|
|
20556
22038
|
return null;
|
|
20557
22039
|
}
|
|
20558
22040
|
try {
|
|
20559
|
-
const content =
|
|
22041
|
+
const content = readFileSync26(skillMdPath, "utf-8");
|
|
20560
22042
|
return parseSkillContentToCanonical(content, skillPath, sourceAgent);
|
|
20561
22043
|
} catch {
|
|
20562
22044
|
return null;
|
|
@@ -20573,7 +22055,7 @@ function parseSkillContentToCanonical(content, sourcePath, sourceAgent) {
|
|
|
20573
22055
|
} catch {
|
|
20574
22056
|
}
|
|
20575
22057
|
}
|
|
20576
|
-
const name = frontmatter.name ||
|
|
22058
|
+
const name = frontmatter.name || basename15(sourcePath.endsWith(".md") ? dirname13(sourcePath) : sourcePath);
|
|
20577
22059
|
const description = frontmatter.description || extractDescriptionFromContent(bodyContent);
|
|
20578
22060
|
return {
|
|
20579
22061
|
name,
|
|
@@ -20644,7 +22126,7 @@ function translateSkillToAgent(canonical, targetAgent, options) {
|
|
|
20644
22126
|
}
|
|
20645
22127
|
const content = generateSkillContent(canonical, targetAgent, format, options);
|
|
20646
22128
|
const filename = options?.outputFilename || "SKILL.md";
|
|
20647
|
-
const targetDir = options?.outputDir ||
|
|
22129
|
+
const targetDir = options?.outputDir || join38(format.skillsDir, canonical.name);
|
|
20648
22130
|
return {
|
|
20649
22131
|
success: true,
|
|
20650
22132
|
content,
|
|
@@ -20783,7 +22265,7 @@ function writeTranslatedSkill(result, rootDir, options) {
|
|
|
20783
22265
|
try {
|
|
20784
22266
|
const rootPath = resolve5(rootDir);
|
|
20785
22267
|
const filePath = resolve5(rootDir, result.targetDir, result.filename);
|
|
20786
|
-
const targetPath =
|
|
22268
|
+
const targetPath = dirname13(filePath);
|
|
20787
22269
|
const relPath = relative3(rootPath, filePath);
|
|
20788
22270
|
if (relPath.startsWith("..") || isAbsolute2(relPath)) {
|
|
20789
22271
|
return {
|
|
@@ -20792,7 +22274,7 @@ function writeTranslatedSkill(result, rootDir, options) {
|
|
|
20792
22274
|
error: "Resolved output path escapes rootDir"
|
|
20793
22275
|
};
|
|
20794
22276
|
}
|
|
20795
|
-
if (
|
|
22277
|
+
if (existsSync37(filePath) && options?.overwrite === false) {
|
|
20796
22278
|
return {
|
|
20797
22279
|
success: false,
|
|
20798
22280
|
path: filePath,
|
|
@@ -20800,10 +22282,10 @@ function writeTranslatedSkill(result, rootDir, options) {
|
|
|
20800
22282
|
skipped: true
|
|
20801
22283
|
};
|
|
20802
22284
|
}
|
|
20803
|
-
if (!
|
|
20804
|
-
|
|
22285
|
+
if (!existsSync37(targetPath)) {
|
|
22286
|
+
mkdirSync21(targetPath, { recursive: true });
|
|
20805
22287
|
}
|
|
20806
|
-
|
|
22288
|
+
writeFileSync20(filePath, result.content, "utf-8");
|
|
20807
22289
|
return { success: true, path: filePath };
|
|
20808
22290
|
} catch (error) {
|
|
20809
22291
|
return {
|
|
@@ -20986,30 +22468,30 @@ var getAgentSkillsDir = getSkillsDir;
|
|
|
20986
22468
|
var getAgentConfigFile = getConfigFile;
|
|
20987
22469
|
|
|
20988
22470
|
// src/manifest/index.ts
|
|
20989
|
-
import { readFileSync as
|
|
20990
|
-
import { join as
|
|
22471
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync21, existsSync as existsSync38 } from "fs";
|
|
22472
|
+
import { join as join39, dirname as dirname14 } from "path";
|
|
20991
22473
|
import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
|
|
20992
22474
|
var MANIFEST_FILENAMES = [".skills", ".skills.yaml", ".skills.yml", "skills.yaml"];
|
|
20993
22475
|
function findManifestPath(startDir = process.cwd()) {
|
|
20994
22476
|
let dir = startDir;
|
|
20995
|
-
while (dir !==
|
|
22477
|
+
while (dir !== dirname14(dir)) {
|
|
20996
22478
|
for (const filename of MANIFEST_FILENAMES) {
|
|
20997
|
-
const manifestPath =
|
|
20998
|
-
if (
|
|
22479
|
+
const manifestPath = join39(dir, filename);
|
|
22480
|
+
if (existsSync38(manifestPath)) {
|
|
20999
22481
|
return manifestPath;
|
|
21000
22482
|
}
|
|
21001
22483
|
}
|
|
21002
|
-
dir =
|
|
22484
|
+
dir = dirname14(dir);
|
|
21003
22485
|
}
|
|
21004
22486
|
return null;
|
|
21005
22487
|
}
|
|
21006
22488
|
function loadManifest(manifestPath) {
|
|
21007
22489
|
const path4 = manifestPath || findManifestPath();
|
|
21008
|
-
if (!path4 || !
|
|
22490
|
+
if (!path4 || !existsSync38(path4)) {
|
|
21009
22491
|
return null;
|
|
21010
22492
|
}
|
|
21011
22493
|
try {
|
|
21012
|
-
const content =
|
|
22494
|
+
const content = readFileSync27(path4, "utf-8");
|
|
21013
22495
|
const parsed = parseYaml12(content);
|
|
21014
22496
|
if (!parsed || typeof parsed !== "object") {
|
|
21015
22497
|
return null;
|
|
@@ -21051,7 +22533,7 @@ function normalizeSkills(skills) {
|
|
|
21051
22533
|
}).filter((s) => s.source);
|
|
21052
22534
|
}
|
|
21053
22535
|
function saveManifest(manifest, manifestPath) {
|
|
21054
|
-
const path4 = manifestPath ||
|
|
22536
|
+
const path4 = manifestPath || join39(process.cwd(), ".skills");
|
|
21055
22537
|
const content = stringifyYaml9({
|
|
21056
22538
|
version: manifest.version || 1,
|
|
21057
22539
|
skills: manifest.skills.map((s) => {
|
|
@@ -21064,7 +22546,7 @@ function saveManifest(manifest, manifestPath) {
|
|
|
21064
22546
|
...manifest.installMethod && { installMethod: manifest.installMethod },
|
|
21065
22547
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21066
22548
|
});
|
|
21067
|
-
|
|
22549
|
+
writeFileSync21(path4, content, "utf-8");
|
|
21068
22550
|
}
|
|
21069
22551
|
function addToManifest(source, options, manifestPath) {
|
|
21070
22552
|
const existing = loadManifest(manifestPath) || {
|
|
@@ -21141,8 +22623,8 @@ function generateManifestFromInstalled(installedSkills) {
|
|
|
21141
22623
|
}
|
|
21142
22624
|
|
|
21143
22625
|
// src/quality/index.ts
|
|
21144
|
-
import { readFileSync as
|
|
21145
|
-
import { join as
|
|
22626
|
+
import { readFileSync as readFileSync28, existsSync as existsSync39 } from "fs";
|
|
22627
|
+
import { join as join40, basename as basename16 } from "path";
|
|
21146
22628
|
|
|
21147
22629
|
// src/quality/benchmark.ts
|
|
21148
22630
|
var CATEGORY_BENCHMARKS = {
|
|
@@ -21744,23 +23226,23 @@ function evaluateSkillContent(content) {
|
|
|
21744
23226
|
};
|
|
21745
23227
|
}
|
|
21746
23228
|
function evaluateSkillFile(filePath) {
|
|
21747
|
-
if (!
|
|
23229
|
+
if (!existsSync39(filePath)) return null;
|
|
21748
23230
|
try {
|
|
21749
|
-
const content =
|
|
23231
|
+
const content = readFileSync28(filePath, "utf-8");
|
|
21750
23232
|
return evaluateSkillContent(content);
|
|
21751
23233
|
} catch {
|
|
21752
23234
|
return null;
|
|
21753
23235
|
}
|
|
21754
23236
|
}
|
|
21755
23237
|
function evaluateSkillDirectory(dirPath) {
|
|
21756
|
-
const skillMdPath =
|
|
21757
|
-
if (
|
|
23238
|
+
const skillMdPath = join40(dirPath, "SKILL.md");
|
|
23239
|
+
if (existsSync39(skillMdPath)) {
|
|
21758
23240
|
return evaluateSkillFile(skillMdPath);
|
|
21759
23241
|
}
|
|
21760
|
-
const mdcFiles = ["index.mdc", `${
|
|
23242
|
+
const mdcFiles = ["index.mdc", `${basename16(dirPath)}.mdc`];
|
|
21761
23243
|
for (const file of mdcFiles) {
|
|
21762
|
-
const mdcPath =
|
|
21763
|
-
if (
|
|
23244
|
+
const mdcPath = join40(dirPath, file);
|
|
23245
|
+
if (existsSync39(mdcPath)) {
|
|
21764
23246
|
return evaluateSkillFile(mdcPath);
|
|
21765
23247
|
}
|
|
21766
23248
|
}
|
|
@@ -21930,8 +23412,8 @@ var AGENT_INSTRUCTION_TEMPLATES = {
|
|
|
21930
23412
|
};
|
|
21931
23413
|
|
|
21932
23414
|
// src/primer/analyzer.ts
|
|
21933
|
-
import { existsSync as
|
|
21934
|
-
import { join as
|
|
23415
|
+
import { existsSync as existsSync40, readFileSync as readFileSync29, readdirSync as readdirSync11 } from "fs";
|
|
23416
|
+
import { join as join41, basename as basename17, relative as relative4, sep as sep4 } from "path";
|
|
21935
23417
|
var PACKAGE_MANAGER_FILES = {
|
|
21936
23418
|
"package-lock.json": "npm",
|
|
21937
23419
|
"pnpm-lock.yaml": "pnpm",
|
|
@@ -22019,10 +23501,10 @@ var PrimerAnalyzer = class {
|
|
|
22019
23501
|
return analysis;
|
|
22020
23502
|
}
|
|
22021
23503
|
loadPackageJson() {
|
|
22022
|
-
const packageJsonPath =
|
|
22023
|
-
if (
|
|
23504
|
+
const packageJsonPath = join41(this.projectPath, "package.json");
|
|
23505
|
+
if (existsSync40(packageJsonPath)) {
|
|
22024
23506
|
try {
|
|
22025
|
-
const content =
|
|
23507
|
+
const content = readFileSync29(packageJsonPath, "utf-8");
|
|
22026
23508
|
this.packageJson = JSON.parse(content);
|
|
22027
23509
|
} catch {
|
|
22028
23510
|
this.packageJson = null;
|
|
@@ -22035,7 +23517,7 @@ var PrimerAnalyzer = class {
|
|
|
22035
23517
|
try {
|
|
22036
23518
|
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
22037
23519
|
for (const entry of entries) {
|
|
22038
|
-
const fullPath =
|
|
23520
|
+
const fullPath = join41(dir, entry.name);
|
|
22039
23521
|
const relativePath = relative4(this.projectPath, fullPath);
|
|
22040
23522
|
if (entry.name.startsWith(".") && entry.name !== ".github" && entry.name !== ".env.example") {
|
|
22041
23523
|
if (!entry.isDirectory()) {
|
|
@@ -22058,7 +23540,7 @@ var PrimerAnalyzer = class {
|
|
|
22058
23540
|
}
|
|
22059
23541
|
getProjectInfo() {
|
|
22060
23542
|
const info = {
|
|
22061
|
-
name:
|
|
23543
|
+
name: basename17(this.projectPath)
|
|
22062
23544
|
};
|
|
22063
23545
|
if (this.packageJson) {
|
|
22064
23546
|
if (typeof this.packageJson.name === "string") {
|
|
@@ -22080,10 +23562,10 @@ var PrimerAnalyzer = class {
|
|
|
22080
23562
|
info.repository = String(repo.url);
|
|
22081
23563
|
}
|
|
22082
23564
|
}
|
|
22083
|
-
const pyprojectPath =
|
|
22084
|
-
if (!this.packageJson &&
|
|
23565
|
+
const pyprojectPath = join41(this.projectPath, "pyproject.toml");
|
|
23566
|
+
if (!this.packageJson && existsSync40(pyprojectPath)) {
|
|
22085
23567
|
try {
|
|
22086
|
-
const content =
|
|
23568
|
+
const content = readFileSync29(pyprojectPath, "utf-8");
|
|
22087
23569
|
const nameMatch = content.match(/name\s*=\s*["']([^"']+)["']/);
|
|
22088
23570
|
const versionMatch = content.match(/version\s*=\s*["']([^"']+)["']/);
|
|
22089
23571
|
const descMatch = content.match(/description\s*=\s*["']([^"']+)["']/);
|
|
@@ -22093,10 +23575,10 @@ var PrimerAnalyzer = class {
|
|
|
22093
23575
|
} catch {
|
|
22094
23576
|
}
|
|
22095
23577
|
}
|
|
22096
|
-
const cargoPath =
|
|
22097
|
-
if (!this.packageJson &&
|
|
23578
|
+
const cargoPath = join41(this.projectPath, "Cargo.toml");
|
|
23579
|
+
if (!this.packageJson && existsSync40(cargoPath)) {
|
|
22098
23580
|
try {
|
|
22099
|
-
const content =
|
|
23581
|
+
const content = readFileSync29(cargoPath, "utf-8");
|
|
22100
23582
|
const nameMatch = content.match(/name\s*=\s*["']([^"']+)["']/);
|
|
22101
23583
|
const versionMatch = content.match(/version\s*=\s*["']([^"']+)["']/);
|
|
22102
23584
|
const descMatch = content.match(/description\s*=\s*["']([^"']+)["']/);
|
|
@@ -22276,8 +23758,8 @@ var PrimerAnalyzer = class {
|
|
|
22276
23758
|
}
|
|
22277
23759
|
if (this.hasFile("tsconfig.json")) {
|
|
22278
23760
|
try {
|
|
22279
|
-
const tsconfigPath =
|
|
22280
|
-
const content =
|
|
23761
|
+
const tsconfigPath = join41(this.projectPath, "tsconfig.json");
|
|
23762
|
+
const content = readFileSync29(tsconfigPath, "utf-8");
|
|
22281
23763
|
const tsconfig = JSON.parse(content.replace(/\/\/.*$/gm, "").replace(/,\s*}/g, "}"));
|
|
22282
23764
|
const paths = tsconfig.compilerOptions?.paths;
|
|
22283
23765
|
if (paths && Object.keys(paths).some((k) => k.startsWith("@"))) {
|
|
@@ -22306,7 +23788,7 @@ var PrimerAnalyzer = class {
|
|
|
22306
23788
|
ci.provider = "github-actions";
|
|
22307
23789
|
ci.configFile = ".github/workflows";
|
|
22308
23790
|
try {
|
|
22309
|
-
const workflowsDir =
|
|
23791
|
+
const workflowsDir = join41(this.projectPath, ".github/workflows");
|
|
22310
23792
|
const workflows = readdirSync11(workflowsDir);
|
|
22311
23793
|
const deployWorkflows = workflows.filter(
|
|
22312
23794
|
(f) => f.includes("deploy") || f.includes("release") || f.includes("publish")
|
|
@@ -22327,12 +23809,12 @@ var PrimerAnalyzer = class {
|
|
|
22327
23809
|
env.hasEnvFile = this.hasFile(".env");
|
|
22328
23810
|
env.hasEnvExample = this.hasFile(".env.example") || this.hasFile(".env.template") || this.hasFile(".env.sample");
|
|
22329
23811
|
if (env.hasEnvExample) {
|
|
22330
|
-
const envExamplePath =
|
|
23812
|
+
const envExamplePath = join41(
|
|
22331
23813
|
this.projectPath,
|
|
22332
23814
|
this.hasFile(".env.example") ? ".env.example" : this.hasFile(".env.template") ? ".env.template" : ".env.sample"
|
|
22333
23815
|
);
|
|
22334
23816
|
try {
|
|
22335
|
-
const content =
|
|
23817
|
+
const content = readFileSync29(envExamplePath, "utf-8");
|
|
22336
23818
|
const variables = content.split("\n").filter((line) => line.trim() && !line.startsWith("#")).map((line) => line.split("=")[0].trim()).filter((v) => v);
|
|
22337
23819
|
env.envVariables = variables;
|
|
22338
23820
|
} catch {
|
|
@@ -22349,8 +23831,8 @@ var PrimerAnalyzer = class {
|
|
|
22349
23831
|
docker.hasCompose = this.hasFile("docker-compose.yml") || this.hasFile("docker-compose.yaml") || this.hasFile("compose.yml");
|
|
22350
23832
|
if (docker.hasDockerfile) {
|
|
22351
23833
|
try {
|
|
22352
|
-
const dockerfilePath =
|
|
22353
|
-
const content =
|
|
23834
|
+
const dockerfilePath = join41(this.projectPath, "Dockerfile");
|
|
23835
|
+
const content = readFileSync29(dockerfilePath, "utf-8");
|
|
22354
23836
|
const fromMatch = content.match(/^FROM\s+(\S+)/m);
|
|
22355
23837
|
if (fromMatch) {
|
|
22356
23838
|
docker.baseImage = fromMatch[1];
|
|
@@ -22439,7 +23921,7 @@ var PrimerAnalyzer = class {
|
|
|
22439
23921
|
}
|
|
22440
23922
|
return false;
|
|
22441
23923
|
}
|
|
22442
|
-
return this.files.has(name) ||
|
|
23924
|
+
return this.files.has(name) || existsSync40(join41(this.projectPath, name));
|
|
22443
23925
|
}
|
|
22444
23926
|
getDepVersion(dep) {
|
|
22445
23927
|
if (!this.packageJson) return void 0;
|
|
@@ -22461,10 +23943,10 @@ var PrimerAnalyzer = class {
|
|
|
22461
23943
|
}
|
|
22462
23944
|
readConfigFile(files) {
|
|
22463
23945
|
for (const file of files) {
|
|
22464
|
-
const filePath =
|
|
22465
|
-
if (
|
|
23946
|
+
const filePath = join41(this.projectPath, file);
|
|
23947
|
+
if (existsSync40(filePath)) {
|
|
22466
23948
|
try {
|
|
22467
|
-
const content =
|
|
23949
|
+
const content = readFileSync29(filePath, "utf-8");
|
|
22468
23950
|
if (file.endsWith(".json")) {
|
|
22469
23951
|
return JSON.parse(content);
|
|
22470
23952
|
}
|
|
@@ -22482,8 +23964,8 @@ function analyzePrimer(projectPath) {
|
|
|
22482
23964
|
}
|
|
22483
23965
|
|
|
22484
23966
|
// src/primer/generator.ts
|
|
22485
|
-
import { existsSync as
|
|
22486
|
-
import { join as
|
|
23967
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
23968
|
+
import { join as join42, dirname as dirname15 } from "path";
|
|
22487
23969
|
var ALL_AGENTS = Object.keys(AGENT_CONFIG);
|
|
22488
23970
|
var markdownRenderer = {
|
|
22489
23971
|
h1: (text) => `# ${text}`,
|
|
@@ -22647,14 +24129,14 @@ var PrimerGenerator = class {
|
|
|
22647
24129
|
detectInstalledAgents() {
|
|
22648
24130
|
const detected = [];
|
|
22649
24131
|
for (const [agent, config] of Object.entries(AGENT_CONFIG)) {
|
|
22650
|
-
const configPath =
|
|
22651
|
-
const skillsPath =
|
|
22652
|
-
if (
|
|
24132
|
+
const configPath = join42(this.projectPath, config.configFile);
|
|
24133
|
+
const skillsPath = join42(this.projectPath, config.skillsDir);
|
|
24134
|
+
if (existsSync41(configPath) || existsSync41(skillsPath)) {
|
|
22653
24135
|
detected.push(agent);
|
|
22654
24136
|
}
|
|
22655
24137
|
if (config.altSkillsDirs) {
|
|
22656
24138
|
for (const altDir of config.altSkillsDirs) {
|
|
22657
|
-
if (
|
|
24139
|
+
if (existsSync41(join42(this.projectPath, altDir))) {
|
|
22658
24140
|
if (!detected.includes(agent)) {
|
|
22659
24141
|
detected.push(agent);
|
|
22660
24142
|
}
|
|
@@ -22674,7 +24156,7 @@ var PrimerGenerator = class {
|
|
|
22674
24156
|
const template = AGENT_INSTRUCTION_TEMPLATES[agent] || this.getDefaultTemplate(agent, config);
|
|
22675
24157
|
const content = this.generateContent(template);
|
|
22676
24158
|
const outputDir = this.options.outputDir || this.projectPath;
|
|
22677
|
-
const filepath =
|
|
24159
|
+
const filepath = join42(outputDir, template.filename);
|
|
22678
24160
|
return {
|
|
22679
24161
|
agent,
|
|
22680
24162
|
filename: template.filename,
|
|
@@ -23046,11 +24528,11 @@ var PrimerGenerator = class {
|
|
|
23046
24528
|
return lines.join("\n");
|
|
23047
24529
|
}
|
|
23048
24530
|
writeInstruction(instruction) {
|
|
23049
|
-
const dir =
|
|
23050
|
-
if (!
|
|
23051
|
-
|
|
24531
|
+
const dir = dirname15(instruction.filepath);
|
|
24532
|
+
if (!existsSync41(dir)) {
|
|
24533
|
+
mkdirSync22(dir, { recursive: true });
|
|
23052
24534
|
}
|
|
23053
|
-
|
|
24535
|
+
writeFileSync22(instruction.filepath, instruction.content, "utf-8");
|
|
23054
24536
|
}
|
|
23055
24537
|
formatProjectType(type) {
|
|
23056
24538
|
const typeMap = {
|
|
@@ -23092,23 +24574,23 @@ var DEFAULT_LEARNING_CONFIG = {
|
|
|
23092
24574
|
};
|
|
23093
24575
|
|
|
23094
24576
|
// src/learning/config.ts
|
|
23095
|
-
import { existsSync as
|
|
23096
|
-
import { join as
|
|
23097
|
-
import { homedir as
|
|
24577
|
+
import { existsSync as existsSync42, readFileSync as readFileSync30, writeFileSync as writeFileSync23, mkdirSync as mkdirSync23 } from "fs";
|
|
24578
|
+
import { join as join43, dirname as dirname16 } from "path";
|
|
24579
|
+
import { homedir as homedir14 } from "os";
|
|
23098
24580
|
import yaml from "yaml";
|
|
23099
24581
|
function getDefaultConfigPath() {
|
|
23100
|
-
return
|
|
24582
|
+
return join43(homedir14(), ".skillkit", "learning.yaml");
|
|
23101
24583
|
}
|
|
23102
24584
|
function getDefaultStorePath() {
|
|
23103
|
-
return
|
|
24585
|
+
return join43(homedir14(), ".skillkit", "learned", "patterns.json");
|
|
23104
24586
|
}
|
|
23105
24587
|
function loadLearningConfig(configPath) {
|
|
23106
24588
|
const path4 = configPath || getDefaultConfigPath();
|
|
23107
|
-
if (!
|
|
24589
|
+
if (!existsSync42(path4)) {
|
|
23108
24590
|
return DEFAULT_LEARNING_CONFIG;
|
|
23109
24591
|
}
|
|
23110
24592
|
try {
|
|
23111
|
-
const content =
|
|
24593
|
+
const content = readFileSync30(path4, "utf-8");
|
|
23112
24594
|
const config = yaml.parse(content);
|
|
23113
24595
|
return { ...DEFAULT_LEARNING_CONFIG, ...config };
|
|
23114
24596
|
} catch {
|
|
@@ -23117,16 +24599,16 @@ function loadLearningConfig(configPath) {
|
|
|
23117
24599
|
}
|
|
23118
24600
|
function saveLearningConfig(config, configPath) {
|
|
23119
24601
|
const path4 = configPath || getDefaultConfigPath();
|
|
23120
|
-
const dir =
|
|
23121
|
-
if (!
|
|
23122
|
-
|
|
24602
|
+
const dir = dirname16(path4);
|
|
24603
|
+
if (!existsSync42(dir)) {
|
|
24604
|
+
mkdirSync23(dir, { recursive: true });
|
|
23123
24605
|
}
|
|
23124
24606
|
const content = yaml.stringify(config);
|
|
23125
|
-
|
|
24607
|
+
writeFileSync23(path4, content);
|
|
23126
24608
|
}
|
|
23127
24609
|
function loadPatternStore(storePath) {
|
|
23128
24610
|
const path4 = storePath || getDefaultStorePath();
|
|
23129
|
-
if (!
|
|
24611
|
+
if (!existsSync42(path4)) {
|
|
23130
24612
|
return {
|
|
23131
24613
|
version: 1,
|
|
23132
24614
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -23135,7 +24617,7 @@ function loadPatternStore(storePath) {
|
|
|
23135
24617
|
};
|
|
23136
24618
|
}
|
|
23137
24619
|
try {
|
|
23138
|
-
const content =
|
|
24620
|
+
const content = readFileSync30(path4, "utf-8");
|
|
23139
24621
|
return JSON.parse(content);
|
|
23140
24622
|
} catch {
|
|
23141
24623
|
return {
|
|
@@ -23148,13 +24630,13 @@ function loadPatternStore(storePath) {
|
|
|
23148
24630
|
}
|
|
23149
24631
|
function savePatternStore(store, storePath) {
|
|
23150
24632
|
const path4 = storePath || getDefaultStorePath();
|
|
23151
|
-
const dir =
|
|
23152
|
-
if (!
|
|
23153
|
-
|
|
24633
|
+
const dir = dirname16(path4);
|
|
24634
|
+
if (!existsSync42(dir)) {
|
|
24635
|
+
mkdirSync23(dir, { recursive: true });
|
|
23154
24636
|
}
|
|
23155
24637
|
store.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23156
24638
|
const content = JSON.stringify(store, null, 2);
|
|
23157
|
-
|
|
24639
|
+
writeFileSync23(path4, content);
|
|
23158
24640
|
}
|
|
23159
24641
|
function addPattern(pattern, storePath) {
|
|
23160
24642
|
const store = loadPatternStore(storePath);
|
|
@@ -23197,8 +24679,8 @@ function getApprovedPatterns(storePath) {
|
|
|
23197
24679
|
|
|
23198
24680
|
// src/learning/git-analyzer.ts
|
|
23199
24681
|
import { execSync as execSync7 } from "child_process";
|
|
23200
|
-
import { existsSync as
|
|
23201
|
-
import { join as
|
|
24682
|
+
import { existsSync as existsSync43 } from "fs";
|
|
24683
|
+
import { join as join44 } from "path";
|
|
23202
24684
|
function runGitCommand(command, cwd) {
|
|
23203
24685
|
try {
|
|
23204
24686
|
return execSync7(command, { cwd, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 });
|
|
@@ -23207,7 +24689,7 @@ function runGitCommand(command, cwd) {
|
|
|
23207
24689
|
}
|
|
23208
24690
|
}
|
|
23209
24691
|
function isGitRepository(path4) {
|
|
23210
|
-
return
|
|
24692
|
+
return existsSync43(join44(path4, ".git"));
|
|
23211
24693
|
}
|
|
23212
24694
|
function getGitCommits(repoPath, options = {}) {
|
|
23213
24695
|
if (!isGitRepository(repoPath)) {
|
|
@@ -23712,9 +25194,9 @@ function mergePatterns(existing, newPatterns) {
|
|
|
23712
25194
|
}
|
|
23713
25195
|
|
|
23714
25196
|
// src/learning/generator.ts
|
|
23715
|
-
import { writeFileSync as
|
|
23716
|
-
import { join as
|
|
23717
|
-
import { homedir as
|
|
25197
|
+
import { writeFileSync as writeFileSync24, mkdirSync as mkdirSync24, existsSync as existsSync44 } from "fs";
|
|
25198
|
+
import { join as join45 } from "path";
|
|
25199
|
+
import { homedir as homedir15 } from "os";
|
|
23718
25200
|
function generateSkillFromPatterns(patterns, options = {}) {
|
|
23719
25201
|
const minConfidence = options.minConfidence ?? 0.5;
|
|
23720
25202
|
const minPatterns = options.minPatterns ?? 1;
|
|
@@ -23804,12 +25286,12 @@ function generateSkillContent2(name, patterns, category) {
|
|
|
23804
25286
|
return lines.join("\n");
|
|
23805
25287
|
}
|
|
23806
25288
|
function saveGeneratedSkill(skill, outputDir) {
|
|
23807
|
-
const dir = outputDir ||
|
|
23808
|
-
if (!
|
|
23809
|
-
|
|
25289
|
+
const dir = outputDir || join45(homedir15(), ".skillkit", "skills", "learned");
|
|
25290
|
+
if (!existsSync44(dir)) {
|
|
25291
|
+
mkdirSync24(dir, { recursive: true });
|
|
23810
25292
|
}
|
|
23811
|
-
const filepath =
|
|
23812
|
-
|
|
25293
|
+
const filepath = join45(dir, skill.filename);
|
|
25294
|
+
writeFileSync24(filepath, skill.content);
|
|
23813
25295
|
return filepath;
|
|
23814
25296
|
}
|
|
23815
25297
|
function generatePatternReport(patterns) {
|
|
@@ -24039,9 +25521,9 @@ var DEFAULT_PROFILE_CONFIG = {
|
|
|
24039
25521
|
};
|
|
24040
25522
|
|
|
24041
25523
|
// src/profiles/manager.ts
|
|
24042
|
-
import { existsSync as
|
|
24043
|
-
import { join as
|
|
24044
|
-
import { homedir as
|
|
25524
|
+
import { existsSync as existsSync45, readFileSync as readFileSync31, writeFileSync as writeFileSync25, mkdirSync as mkdirSync25 } from "fs";
|
|
25525
|
+
import { join as join46, dirname as dirname17 } from "path";
|
|
25526
|
+
import { homedir as homedir16 } from "os";
|
|
24045
25527
|
import yaml2 from "yaml";
|
|
24046
25528
|
var BUILTIN_PROFILES = [
|
|
24047
25529
|
{
|
|
@@ -24121,15 +25603,15 @@ var BUILTIN_PROFILES = [
|
|
|
24121
25603
|
}
|
|
24122
25604
|
];
|
|
24123
25605
|
function getConfigPath() {
|
|
24124
|
-
return
|
|
25606
|
+
return join46(homedir16(), ".skillkit", "profiles.yaml");
|
|
24125
25607
|
}
|
|
24126
25608
|
function loadProfileConfig() {
|
|
24127
25609
|
const path4 = getConfigPath();
|
|
24128
|
-
if (!
|
|
25610
|
+
if (!existsSync45(path4)) {
|
|
24129
25611
|
return DEFAULT_PROFILE_CONFIG;
|
|
24130
25612
|
}
|
|
24131
25613
|
try {
|
|
24132
|
-
const content =
|
|
25614
|
+
const content = readFileSync31(path4, "utf-8");
|
|
24133
25615
|
return { ...DEFAULT_PROFILE_CONFIG, ...yaml2.parse(content) };
|
|
24134
25616
|
} catch {
|
|
24135
25617
|
return DEFAULT_PROFILE_CONFIG;
|
|
@@ -24137,11 +25619,11 @@ function loadProfileConfig() {
|
|
|
24137
25619
|
}
|
|
24138
25620
|
function saveProfileConfig(config) {
|
|
24139
25621
|
const path4 = getConfigPath();
|
|
24140
|
-
const dir =
|
|
24141
|
-
if (!
|
|
24142
|
-
|
|
25622
|
+
const dir = dirname17(path4);
|
|
25623
|
+
if (!existsSync45(dir)) {
|
|
25624
|
+
mkdirSync25(dir, { recursive: true });
|
|
24143
25625
|
}
|
|
24144
|
-
|
|
25626
|
+
writeFileSync25(path4, yaml2.stringify(config));
|
|
24145
25627
|
}
|
|
24146
25628
|
function getActiveProfile() {
|
|
24147
25629
|
const config = loadProfileConfig();
|
|
@@ -24208,9 +25690,9 @@ var DEFAULT_GUIDELINE_CONFIG = {
|
|
|
24208
25690
|
};
|
|
24209
25691
|
|
|
24210
25692
|
// src/guidelines/manager.ts
|
|
24211
|
-
import { existsSync as
|
|
24212
|
-
import { join as
|
|
24213
|
-
import { homedir as
|
|
25693
|
+
import { existsSync as existsSync46, readFileSync as readFileSync32, writeFileSync as writeFileSync26, mkdirSync as mkdirSync26 } from "fs";
|
|
25694
|
+
import { join as join47, dirname as dirname18 } from "path";
|
|
25695
|
+
import { homedir as homedir17 } from "os";
|
|
24214
25696
|
import yaml3 from "yaml";
|
|
24215
25697
|
var BUILTIN_GUIDELINES = [
|
|
24216
25698
|
{
|
|
@@ -24387,15 +25869,15 @@ Types: feat, fix, docs, style, refactor, test, chore`
|
|
|
24387
25869
|
}
|
|
24388
25870
|
];
|
|
24389
25871
|
function getConfigPath2() {
|
|
24390
|
-
return
|
|
25872
|
+
return join47(homedir17(), ".skillkit", "guidelines.yaml");
|
|
24391
25873
|
}
|
|
24392
25874
|
function loadGuidelineConfig() {
|
|
24393
25875
|
const path4 = getConfigPath2();
|
|
24394
|
-
if (!
|
|
25876
|
+
if (!existsSync46(path4)) {
|
|
24395
25877
|
return DEFAULT_GUIDELINE_CONFIG;
|
|
24396
25878
|
}
|
|
24397
25879
|
try {
|
|
24398
|
-
const content =
|
|
25880
|
+
const content = readFileSync32(path4, "utf-8");
|
|
24399
25881
|
return { ...DEFAULT_GUIDELINE_CONFIG, ...yaml3.parse(content) };
|
|
24400
25882
|
} catch {
|
|
24401
25883
|
return DEFAULT_GUIDELINE_CONFIG;
|
|
@@ -24403,11 +25885,11 @@ function loadGuidelineConfig() {
|
|
|
24403
25885
|
}
|
|
24404
25886
|
function saveGuidelineConfig(config) {
|
|
24405
25887
|
const path4 = getConfigPath2();
|
|
24406
|
-
const dir =
|
|
24407
|
-
if (!
|
|
24408
|
-
|
|
25888
|
+
const dir = dirname18(path4);
|
|
25889
|
+
if (!existsSync46(dir)) {
|
|
25890
|
+
mkdirSync26(dir, { recursive: true });
|
|
24409
25891
|
}
|
|
24410
|
-
|
|
25892
|
+
writeFileSync26(path4, yaml3.stringify(config));
|
|
24411
25893
|
}
|
|
24412
25894
|
function getGuideline(id) {
|
|
24413
25895
|
const builtin = BUILTIN_GUIDELINES.find((g) => g.id === id);
|
|
@@ -24489,8 +25971,8 @@ init_generator();
|
|
|
24489
25971
|
|
|
24490
25972
|
// src/tree/serializer.ts
|
|
24491
25973
|
init_types3();
|
|
24492
|
-
import { readFileSync as
|
|
24493
|
-
import { dirname as
|
|
25974
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync27, existsSync as existsSync47, mkdirSync as mkdirSync27 } from "fs";
|
|
25975
|
+
import { dirname as dirname19 } from "path";
|
|
24494
25976
|
var TREE_FILE_NAME = "skill-tree.json";
|
|
24495
25977
|
function serializeTree(tree) {
|
|
24496
25978
|
return JSON.stringify(tree, null, 2);
|
|
@@ -24500,19 +25982,19 @@ function deserializeTree(json) {
|
|
|
24500
25982
|
return SkillTreeSchema.parse(parsed);
|
|
24501
25983
|
}
|
|
24502
25984
|
function saveTree(tree, path4) {
|
|
24503
|
-
const dir =
|
|
24504
|
-
if (!
|
|
24505
|
-
|
|
25985
|
+
const dir = dirname19(path4);
|
|
25986
|
+
if (!existsSync47(dir)) {
|
|
25987
|
+
mkdirSync27(dir, { recursive: true });
|
|
24506
25988
|
}
|
|
24507
25989
|
const json = serializeTree(tree);
|
|
24508
|
-
|
|
25990
|
+
writeFileSync27(path4, json, "utf-8");
|
|
24509
25991
|
}
|
|
24510
25992
|
function loadTree(path4) {
|
|
24511
|
-
if (!
|
|
25993
|
+
if (!existsSync47(path4)) {
|
|
24512
25994
|
return null;
|
|
24513
25995
|
}
|
|
24514
25996
|
try {
|
|
24515
|
-
const json =
|
|
25997
|
+
const json = readFileSync33(path4, "utf-8");
|
|
24516
25998
|
return deserializeTree(json);
|
|
24517
25999
|
} catch {
|
|
24518
26000
|
return null;
|
|
@@ -25229,7 +26711,7 @@ var ExecutionFlowSchema = z11.object({
|
|
|
25229
26711
|
});
|
|
25230
26712
|
|
|
25231
26713
|
// src/execution/manager.ts
|
|
25232
|
-
import { randomUUID as
|
|
26714
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
25233
26715
|
var DEFAULT_MAX_RETRIES = 3;
|
|
25234
26716
|
var DEFAULT_RETRY_DELAY = 1e3;
|
|
25235
26717
|
var DEFAULT_TIMEOUT2 = 3e4;
|
|
@@ -25249,7 +26731,7 @@ var ExecutionManager = class {
|
|
|
25249
26731
|
};
|
|
25250
26732
|
}
|
|
25251
26733
|
createFlow(skillName, steps, options = {}) {
|
|
25252
|
-
const flowId =
|
|
26734
|
+
const flowId = randomUUID15();
|
|
25253
26735
|
const executionSteps = steps.map((step, index) => ({
|
|
25254
26736
|
id: `${flowId}-step-${index}`,
|
|
25255
26737
|
name: step.name,
|
|
@@ -25467,12 +26949,12 @@ function createExecutionManager(config) {
|
|
|
25467
26949
|
}
|
|
25468
26950
|
|
|
25469
26951
|
// src/execution/mode.ts
|
|
25470
|
-
import { existsSync as
|
|
25471
|
-
import { join as
|
|
25472
|
-
import { homedir as
|
|
26952
|
+
import { existsSync as existsSync48, readFileSync as readFileSync34 } from "fs";
|
|
26953
|
+
import { join as join48 } from "path";
|
|
26954
|
+
import { homedir as homedir18 } from "os";
|
|
25473
26955
|
var DEFAULT_MCP_CONFIG_PATHS = [
|
|
25474
|
-
|
|
25475
|
-
|
|
26956
|
+
join48(homedir18(), ".mcp.json"),
|
|
26957
|
+
join48(homedir18(), ".config", "claude", "mcp.json"),
|
|
25476
26958
|
".mcp.json",
|
|
25477
26959
|
"mcp.json"
|
|
25478
26960
|
];
|
|
@@ -25521,8 +27003,8 @@ function detectMcpServers(configPaths) {
|
|
|
25521
27003
|
for (const configPath of configPaths) {
|
|
25522
27004
|
try {
|
|
25523
27005
|
const fullPath = configPath.startsWith("~") ? configPath.replace("~", process.env.HOME || "") : configPath;
|
|
25524
|
-
if (
|
|
25525
|
-
const content =
|
|
27006
|
+
if (existsSync48(fullPath)) {
|
|
27007
|
+
const content = readFileSync34(fullPath, "utf-8");
|
|
25526
27008
|
const config = JSON.parse(content);
|
|
25527
27009
|
if (config.mcpServers) {
|
|
25528
27010
|
servers.push(...Object.keys(config.mcpServers));
|
|
@@ -25660,8 +27142,8 @@ init_expansion();
|
|
|
25660
27142
|
init_hybrid();
|
|
25661
27143
|
|
|
25662
27144
|
// src/registry/community.ts
|
|
25663
|
-
import { readFileSync as
|
|
25664
|
-
import { join as
|
|
27145
|
+
import { readFileSync as readFileSync35, existsSync as existsSync49 } from "fs";
|
|
27146
|
+
import { join as join49, dirname as dirname20 } from "path";
|
|
25665
27147
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
25666
27148
|
var CommunityRegistry = class {
|
|
25667
27149
|
constructor(skillsMdPath) {
|
|
@@ -25674,13 +27156,13 @@ var CommunityRegistry = class {
|
|
|
25674
27156
|
if (this.loaded) return;
|
|
25675
27157
|
this.loaded = true;
|
|
25676
27158
|
const paths = this.skillsMdPath ? [this.skillsMdPath] : [
|
|
25677
|
-
|
|
25678
|
-
|
|
27159
|
+
join49(process.cwd(), "registry", "SKILLS.md"),
|
|
27160
|
+
join49(dirname20(fileURLToPath2(import.meta.url)), "..", "..", "..", "..", "registry", "SKILLS.md")
|
|
25679
27161
|
];
|
|
25680
27162
|
for (const path4 of paths) {
|
|
25681
|
-
if (!
|
|
27163
|
+
if (!existsSync49(path4)) continue;
|
|
25682
27164
|
try {
|
|
25683
|
-
const content =
|
|
27165
|
+
const content = readFileSync35(path4, "utf-8");
|
|
25684
27166
|
this.entries = this.parse(content);
|
|
25685
27167
|
return;
|
|
25686
27168
|
} catch {
|
|
@@ -25979,8 +27461,8 @@ var RelevanceRanker = class {
|
|
|
25979
27461
|
};
|
|
25980
27462
|
|
|
25981
27463
|
// src/parser/references.ts
|
|
25982
|
-
import { readdirSync as readdirSync12, statSync as statSync9, existsSync as
|
|
25983
|
-
import { join as
|
|
27464
|
+
import { readdirSync as readdirSync12, statSync as statSync9, existsSync as existsSync50 } from "fs";
|
|
27465
|
+
import { join as join50 } from "path";
|
|
25984
27466
|
var REFERENCE_DIRS = ["references", "resources", "docs", "examples", "assets", "scripts"];
|
|
25985
27467
|
var DIR_TYPE_MAP = {
|
|
25986
27468
|
references: "resource",
|
|
@@ -25993,8 +27475,8 @@ var DIR_TYPE_MAP = {
|
|
|
25993
27475
|
function discoverReferences(skillDir) {
|
|
25994
27476
|
const refs = [];
|
|
25995
27477
|
for (const dir of REFERENCE_DIRS) {
|
|
25996
|
-
const fullPath =
|
|
25997
|
-
if (!
|
|
27478
|
+
const fullPath = join50(skillDir, dir);
|
|
27479
|
+
if (!existsSync50(fullPath)) continue;
|
|
25998
27480
|
try {
|
|
25999
27481
|
const stat = statSync9(fullPath);
|
|
26000
27482
|
if (!stat.isDirectory()) continue;
|
|
@@ -26005,12 +27487,12 @@ function discoverReferences(skillDir) {
|
|
|
26005
27487
|
try {
|
|
26006
27488
|
const entries = readdirSync12(fullPath);
|
|
26007
27489
|
for (const entry of entries) {
|
|
26008
|
-
const entryPath =
|
|
27490
|
+
const entryPath = join50(fullPath, entry);
|
|
26009
27491
|
try {
|
|
26010
27492
|
const entryStat = statSync9(entryPath);
|
|
26011
27493
|
if (entryStat.isFile()) {
|
|
26012
27494
|
refs.push({
|
|
26013
|
-
path:
|
|
27495
|
+
path: join50(dir, entry),
|
|
26014
27496
|
type,
|
|
26015
27497
|
name: entry
|
|
26016
27498
|
});
|
|
@@ -26157,6 +27639,7 @@ export {
|
|
|
26157
27639
|
CONTEXT_DIR,
|
|
26158
27640
|
CONTEXT_FILE,
|
|
26159
27641
|
CUSTOM_AGENT_FORMAT_MAP,
|
|
27642
|
+
ClaudeMdUpdater,
|
|
26160
27643
|
CommandGenerator,
|
|
26161
27644
|
CommandRegistry,
|
|
26162
27645
|
CommunityRegistry,
|
|
@@ -26176,6 +27659,7 @@ export {
|
|
|
26176
27659
|
DEFAULT_HYBRID_CONFIG,
|
|
26177
27660
|
DEFAULT_LEARNING_CONFIG,
|
|
26178
27661
|
DEFAULT_MEMORY_CONFIG,
|
|
27662
|
+
DEFAULT_MEMORY_HOOK_CONFIG,
|
|
26179
27663
|
DEFAULT_PROFILE_CONFIG,
|
|
26180
27664
|
DEFAULT_REASONING_CONFIG,
|
|
26181
27665
|
DEFAULT_SCORING_WEIGHTS,
|
|
@@ -26209,6 +27693,7 @@ export {
|
|
|
26209
27693
|
MemoryCache,
|
|
26210
27694
|
MemoryCompressor,
|
|
26211
27695
|
MemoryEnabledEngine,
|
|
27696
|
+
MemoryHookManager,
|
|
26212
27697
|
MemoryIndexStore,
|
|
26213
27698
|
MemoryInjector,
|
|
26214
27699
|
MemoryObserver,
|
|
@@ -26225,8 +27710,10 @@ export {
|
|
|
26225
27710
|
PlanValidator,
|
|
26226
27711
|
PluginLoader,
|
|
26227
27712
|
PluginManager,
|
|
27713
|
+
PostToolUseHook,
|
|
26228
27714
|
PrimerAnalyzer,
|
|
26229
27715
|
PrimerGenerator,
|
|
27716
|
+
ProgressiveDisclosureManager,
|
|
26230
27717
|
ProjectDetector,
|
|
26231
27718
|
QueryExpander,
|
|
26232
27719
|
RateLimitError,
|
|
@@ -26241,7 +27728,9 @@ export {
|
|
|
26241
27728
|
SKILL_DISCOVERY_PATHS,
|
|
26242
27729
|
SKILL_MATCH_PROMPT,
|
|
26243
27730
|
STANDARD_PLACEHOLDERS,
|
|
27731
|
+
SessionEndHook,
|
|
26244
27732
|
SessionManager,
|
|
27733
|
+
SessionStartHook,
|
|
26245
27734
|
Skill,
|
|
26246
27735
|
SkillBundle,
|
|
26247
27736
|
SkillChunkSchema,
|
|
@@ -26300,6 +27789,7 @@ export {
|
|
|
26300
27789
|
computeRRFScore,
|
|
26301
27790
|
copilotTranslator,
|
|
26302
27791
|
createAPIBasedCompressor,
|
|
27792
|
+
createClaudeMdUpdater,
|
|
26303
27793
|
createCommandGenerator,
|
|
26304
27794
|
createCommandRegistry,
|
|
26305
27795
|
createConnectorConfig,
|
|
@@ -26314,6 +27804,7 @@ export {
|
|
|
26314
27804
|
createMarketplaceAggregator,
|
|
26315
27805
|
createMemoryCompressor,
|
|
26316
27806
|
createMemoryEnabledEngine,
|
|
27807
|
+
createMemoryHookManager,
|
|
26317
27808
|
createMemoryInjector,
|
|
26318
27809
|
createMemoryObserver,
|
|
26319
27810
|
createMessageBus,
|
|
@@ -26325,13 +27816,17 @@ export {
|
|
|
26325
27816
|
createPlanParser,
|
|
26326
27817
|
createPlanValidator,
|
|
26327
27818
|
createPluginManager,
|
|
27819
|
+
createPostToolUseHook,
|
|
27820
|
+
createProgressiveDisclosureManager,
|
|
26328
27821
|
createQueryExpander,
|
|
26329
27822
|
createReasoningEngine,
|
|
26330
27823
|
createReasoningRecommendationEngine,
|
|
26331
27824
|
createRecommendationEngine,
|
|
26332
27825
|
createRuleBasedCompressor,
|
|
27826
|
+
createSessionEndHook,
|
|
26333
27827
|
createSessionFile,
|
|
26334
27828
|
createSessionManager,
|
|
27829
|
+
createSessionStartHook,
|
|
26335
27830
|
createSimulatedSkillExecutor,
|
|
26336
27831
|
createSkillBundle,
|
|
26337
27832
|
createSkillExecutor,
|
|
@@ -26365,6 +27860,9 @@ export {
|
|
|
26365
27860
|
evaluateSkillContent,
|
|
26366
27861
|
evaluateSkillDirectory,
|
|
26367
27862
|
evaluateSkillFile,
|
|
27863
|
+
executePostToolUseHook,
|
|
27864
|
+
executeSessionEndHook,
|
|
27865
|
+
executeSessionStartHook,
|
|
26368
27866
|
executeWithAgent,
|
|
26369
27867
|
expandQuerySimple,
|
|
26370
27868
|
exportBundle,
|
|
@@ -26565,6 +28063,7 @@ export {
|
|
|
26565
28063
|
suggestMappingsFromMcp,
|
|
26566
28064
|
supportsAutoDiscovery,
|
|
26567
28065
|
supportsSlashCommands,
|
|
28066
|
+
syncGlobalClaudeMd,
|
|
26568
28067
|
syncToAgent,
|
|
26569
28068
|
syncToAllAgents,
|
|
26570
28069
|
toCanonicalAgent,
|
|
@@ -26579,6 +28078,7 @@ export {
|
|
|
26579
28078
|
translatorRegistry,
|
|
26580
28079
|
treeToMarkdown,
|
|
26581
28080
|
treeToText,
|
|
28081
|
+
updateClaudeMd,
|
|
26582
28082
|
updateSessionFile,
|
|
26583
28083
|
validateAgent,
|
|
26584
28084
|
validateBuiltinPacks,
|