@deeplake/hivemind 0.7.21 → 0.7.23
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/bundle/cli.js +66 -15
- package/codex/bundle/capture.js +60 -12
- package/codex/bundle/commands/auth-login.js +4 -2
- package/codex/bundle/pre-tool-use.js +4 -2
- package/codex/bundle/session-start-setup.js +52 -5
- package/codex/bundle/session-start.js +62 -11
- package/codex/bundle/shell/deeplake-shell.js +4 -2
- package/codex/bundle/skillify-worker.js +118 -29
- package/codex/bundle/stop.js +100 -52
- package/codex/bundle/wiki-worker.js +7 -3
- package/cursor/bundle/capture.js +87 -39
- package/cursor/bundle/commands/auth-login.js +4 -2
- package/cursor/bundle/pre-tool-use.js +4 -2
- package/cursor/bundle/session-end.js +78 -34
- package/cursor/bundle/session-start.js +67 -15
- package/cursor/bundle/shell/deeplake-shell.js +4 -2
- package/cursor/bundle/skillify-worker.js +118 -29
- package/cursor/bundle/wiki-worker.js +7 -3
- package/hermes/bundle/capture.js +87 -39
- package/hermes/bundle/commands/auth-login.js +4 -2
- package/hermes/bundle/pre-tool-use.js +4 -2
- package/hermes/bundle/session-end.js +78 -34
- package/hermes/bundle/session-start.js +67 -15
- package/hermes/bundle/shell/deeplake-shell.js +4 -2
- package/hermes/bundle/skillify-worker.js +118 -29
- package/hermes/bundle/wiki-worker.js +7 -3
- package/mcp/bundle/server.js +4 -2
- package/openclaw/dist/index.js +8 -6
- package/openclaw/dist/skillify-worker.js +118 -29
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/openclaw/skills/SKILL.md +1 -1
- package/package.json +1 -1
- package/pi/extension-source/hivemind.ts +18 -3
|
@@ -572,13 +572,14 @@ var DeeplakeApi = class {
|
|
|
572
572
|
const tables = await this.listTables();
|
|
573
573
|
if (!tables.includes(tbl)) {
|
|
574
574
|
log2(`table "${tbl}" not found, creating`);
|
|
575
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
575
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
576
576
|
log2(`table "${tbl}" created`);
|
|
577
577
|
if (!tables.includes(tbl))
|
|
578
578
|
this._tablesCache = [...tables, tbl];
|
|
579
579
|
}
|
|
580
580
|
await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
|
|
581
581
|
await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
582
|
+
await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
582
583
|
}
|
|
583
584
|
/** Create the sessions table (uses JSONB for message since every row is a JSON event). */
|
|
584
585
|
async ensureSessionsTable(name) {
|
|
@@ -586,13 +587,14 @@ var DeeplakeApi = class {
|
|
|
586
587
|
const tables = await this.listTables();
|
|
587
588
|
if (!tables.includes(safe)) {
|
|
588
589
|
log2(`table "${safe}" not found, creating`);
|
|
589
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
590
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
590
591
|
log2(`table "${safe}" created`);
|
|
591
592
|
if (!tables.includes(safe))
|
|
592
593
|
this._tablesCache = [...tables, safe];
|
|
593
594
|
}
|
|
594
595
|
await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
|
|
595
596
|
await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
597
|
+
await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
596
598
|
await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
|
|
597
599
|
}
|
|
598
600
|
/**
|
|
@@ -651,18 +653,25 @@ function parseFrontmatter(text) {
|
|
|
651
653
|
const head = text.slice(4, end).trim();
|
|
652
654
|
const body = text.slice(end + 4).replace(/^\r?\n/, "");
|
|
653
655
|
const fm = { source_sessions: [] };
|
|
654
|
-
let
|
|
656
|
+
let arrayKey = null;
|
|
655
657
|
for (const raw of head.split(/\r?\n/)) {
|
|
656
|
-
if (
|
|
658
|
+
if (arrayKey) {
|
|
657
659
|
const m2 = raw.match(/^\s+-\s+(.+)$/);
|
|
658
660
|
if (m2) {
|
|
659
|
-
fm
|
|
661
|
+
const arr = fm[arrayKey] ?? [];
|
|
662
|
+
arr.push(m2[1].trim());
|
|
663
|
+
fm[arrayKey] = arr;
|
|
660
664
|
continue;
|
|
661
665
|
}
|
|
662
|
-
|
|
666
|
+
arrayKey = null;
|
|
663
667
|
}
|
|
664
668
|
if (raw.startsWith("source_sessions:")) {
|
|
665
|
-
|
|
669
|
+
arrayKey = "source_sessions";
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (raw.startsWith("contributors:")) {
|
|
673
|
+
arrayKey = "contributors";
|
|
674
|
+
fm.contributors = [];
|
|
666
675
|
continue;
|
|
667
676
|
}
|
|
668
677
|
const m = raw.match(/^([a-zA-Z_]+):\s*(.*)$/);
|
|
@@ -877,11 +886,19 @@ function buildPullSql(args) {
|
|
|
877
886
|
where.push(`name = '${esc(args.skillName)}'`);
|
|
878
887
|
}
|
|
879
888
|
const whereClause = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
|
|
880
|
-
|
|
889
|
+
const contributorsCol = args.includeContributors === false ? "" : "contributors, ";
|
|
890
|
+
return `SELECT name, project, project_key, body, version, source_agent, scope, author, ${contributorsCol}description, trigger_text, source_sessions, install, created_at, updated_at FROM "${args.tableName}"${whereClause} ORDER BY project_key ASC, name ASC, version DESC`;
|
|
891
|
+
}
|
|
892
|
+
function isMissingContributorsColumnError(message) {
|
|
893
|
+
if (!message)
|
|
894
|
+
return false;
|
|
895
|
+
return /contributors.*(?:does not exist|not found|unknown)/i.test(message) || /(?:does not exist|unknown column).*contributors/i.test(message);
|
|
881
896
|
}
|
|
882
897
|
function isMissingTableError(message) {
|
|
883
898
|
if (!message)
|
|
884
899
|
return false;
|
|
900
|
+
if (/\bcolumn\b/i.test(message))
|
|
901
|
+
return false;
|
|
885
902
|
return /Table does not exist|relation .* does not exist|no such table/i.test(message);
|
|
886
903
|
}
|
|
887
904
|
function resolvePullDestination(install, cwd) {
|
|
@@ -977,11 +994,16 @@ function selectLatestPerName(rows) {
|
|
|
977
994
|
}
|
|
978
995
|
function renderSkillFile(row) {
|
|
979
996
|
const sources = parseSourceSessions(row.source_sessions);
|
|
997
|
+
const author = typeof row.author === "string" && row.author.length > 0 ? row.author : void 0;
|
|
998
|
+
const contributors = parseContributors(row.contributors);
|
|
999
|
+
const renderedContributors = contributors.length > 0 ? contributors : author ? [author] : [];
|
|
980
1000
|
const fm = {
|
|
981
1001
|
name: String(row.name ?? ""),
|
|
982
1002
|
description: String(row.description ?? ""),
|
|
983
1003
|
trigger: typeof row.trigger_text === "string" && row.trigger_text.length > 0 ? String(row.trigger_text) : void 0,
|
|
1004
|
+
author,
|
|
984
1005
|
source_sessions: sources,
|
|
1006
|
+
contributors: renderedContributors,
|
|
985
1007
|
version: Number(row.version ?? 1),
|
|
986
1008
|
created_by_agent: String(row.source_agent ?? "unknown"),
|
|
987
1009
|
created_at: String(row.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
|
|
@@ -1006,15 +1028,35 @@ function parseSourceSessions(v) {
|
|
|
1006
1028
|
}
|
|
1007
1029
|
return [];
|
|
1008
1030
|
}
|
|
1031
|
+
function parseContributors(v) {
|
|
1032
|
+
if (Array.isArray(v))
|
|
1033
|
+
return v.map(String);
|
|
1034
|
+
if (typeof v === "string") {
|
|
1035
|
+
try {
|
|
1036
|
+
const parsed = JSON.parse(v);
|
|
1037
|
+
if (Array.isArray(parsed))
|
|
1038
|
+
return parsed.map(String);
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1009
1044
|
function renderFrontmatter(fm) {
|
|
1010
1045
|
const lines = ["---"];
|
|
1011
1046
|
lines.push(`name: ${fm.name}`);
|
|
1012
1047
|
lines.push(`description: ${JSON.stringify(fm.description)}`);
|
|
1013
1048
|
if (fm.trigger)
|
|
1014
1049
|
lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
|
|
1050
|
+
if (fm.author)
|
|
1051
|
+
lines.push(`author: ${fm.author}`);
|
|
1015
1052
|
lines.push(`source_sessions:`);
|
|
1016
1053
|
for (const s of fm.source_sessions)
|
|
1017
1054
|
lines.push(` - ${s}`);
|
|
1055
|
+
if (fm.contributors && fm.contributors.length > 0) {
|
|
1056
|
+
lines.push(`contributors:`);
|
|
1057
|
+
for (const c of fm.contributors)
|
|
1058
|
+
lines.push(` - ${c}`);
|
|
1059
|
+
}
|
|
1018
1060
|
lines.push(`version: ${fm.version}`);
|
|
1019
1061
|
lines.push(`created_by_agent: ${fm.created_by_agent}`);
|
|
1020
1062
|
lines.push(`created_at: ${fm.created_at}`);
|
|
@@ -1054,10 +1096,19 @@ async function runPull(opts) {
|
|
|
1054
1096
|
try {
|
|
1055
1097
|
rows = await opts.query(sql);
|
|
1056
1098
|
} catch (e) {
|
|
1057
|
-
if (isMissingTableError(e?.message))
|
|
1099
|
+
if (isMissingTableError(e?.message)) {
|
|
1058
1100
|
rows = [];
|
|
1059
|
-
else
|
|
1101
|
+
} else if (isMissingContributorsColumnError(e?.message)) {
|
|
1102
|
+
const legacySql = buildPullSql({
|
|
1103
|
+
tableName: opts.tableName,
|
|
1104
|
+
users: opts.users,
|
|
1105
|
+
skillName: opts.skillName,
|
|
1106
|
+
includeContributors: false
|
|
1107
|
+
});
|
|
1108
|
+
rows = await opts.query(legacySql);
|
|
1109
|
+
} else {
|
|
1060
1110
|
throw e;
|
|
1111
|
+
}
|
|
1061
1112
|
}
|
|
1062
1113
|
const latest = selectLatestPerName(rows);
|
|
1063
1114
|
const root = resolvePullDestination(opts.install, opts.cwd);
|
|
@@ -1271,7 +1322,7 @@ SKILLS (skillify) \u2014 mine + share reusable skills across the org:
|
|
|
1271
1322
|
- hivemind skillify unpull --user <email> \u2014 remove only that author's pulls
|
|
1272
1323
|
- hivemind skillify unpull --not-mine \u2014 remove all pulls except your own
|
|
1273
1324
|
- hivemind skillify unpull --dry-run \u2014 preview without touching disk
|
|
1274
|
-
- hivemind skillify scope <me|team
|
|
1325
|
+
- hivemind skillify scope <me|team> \u2014 sharing scope for new skills
|
|
1275
1326
|
- hivemind skillify install <project|global> \u2014 default install location
|
|
1276
1327
|
- hivemind skillify team add|remove|list <name> \u2014 manage team list`;
|
|
1277
1328
|
async function main() {
|
|
@@ -67201,13 +67201,14 @@ var DeeplakeApi = class {
|
|
|
67201
67201
|
const tables = await this.listTables();
|
|
67202
67202
|
if (!tables.includes(tbl)) {
|
|
67203
67203
|
log2(`table "${tbl}" not found, creating`);
|
|
67204
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
67204
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
67205
67205
|
log2(`table "${tbl}" created`);
|
|
67206
67206
|
if (!tables.includes(tbl))
|
|
67207
67207
|
this._tablesCache = [...tables, tbl];
|
|
67208
67208
|
}
|
|
67209
67209
|
await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
|
|
67210
67210
|
await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
67211
|
+
await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
67211
67212
|
}
|
|
67212
67213
|
/** Create the sessions table (uses JSONB for message since every row is a JSON event). */
|
|
67213
67214
|
async ensureSessionsTable(name) {
|
|
@@ -67215,13 +67216,14 @@ var DeeplakeApi = class {
|
|
|
67215
67216
|
const tables = await this.listTables();
|
|
67216
67217
|
if (!tables.includes(safe)) {
|
|
67217
67218
|
log2(`table "${safe}" not found, creating`);
|
|
67218
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
67219
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
67219
67220
|
log2(`table "${safe}" created`);
|
|
67220
67221
|
if (!tables.includes(safe))
|
|
67221
67222
|
this._tablesCache = [...tables, safe];
|
|
67222
67223
|
}
|
|
67223
67224
|
await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
|
|
67224
67225
|
await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
67226
|
+
await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
67225
67227
|
await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
|
|
67226
67228
|
}
|
|
67227
67229
|
/**
|
|
@@ -90,9 +90,16 @@ function renderFrontmatter(fm) {
|
|
|
90
90
|
lines.push(`description: ${JSON.stringify(fm.description)}`);
|
|
91
91
|
if (fm.trigger)
|
|
92
92
|
lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
|
|
93
|
+
if (fm.author)
|
|
94
|
+
lines.push(`author: ${fm.author}`);
|
|
93
95
|
lines.push(`source_sessions:`);
|
|
94
96
|
for (const s of fm.source_sessions)
|
|
95
97
|
lines.push(` - ${s}`);
|
|
98
|
+
if (fm.contributors && fm.contributors.length > 0) {
|
|
99
|
+
lines.push(`contributors:`);
|
|
100
|
+
for (const c of fm.contributors)
|
|
101
|
+
lines.push(` - ${c}`);
|
|
102
|
+
}
|
|
96
103
|
lines.push(`version: ${fm.version}`);
|
|
97
104
|
lines.push(`created_by_agent: ${fm.created_by_agent}`);
|
|
98
105
|
lines.push(`created_at: ${fm.created_at}`);
|
|
@@ -109,18 +116,25 @@ function parseFrontmatter(text) {
|
|
|
109
116
|
const head = text.slice(4, end).trim();
|
|
110
117
|
const body = text.slice(end + 4).replace(/^\r?\n/, "");
|
|
111
118
|
const fm = { source_sessions: [] };
|
|
112
|
-
let
|
|
119
|
+
let arrayKey = null;
|
|
113
120
|
for (const raw of head.split(/\r?\n/)) {
|
|
114
|
-
if (
|
|
121
|
+
if (arrayKey) {
|
|
115
122
|
const m2 = raw.match(/^\s+-\s+(.+)$/);
|
|
116
123
|
if (m2) {
|
|
117
|
-
fm
|
|
124
|
+
const arr = fm[arrayKey] ?? [];
|
|
125
|
+
arr.push(m2[1].trim());
|
|
126
|
+
fm[arrayKey] = arr;
|
|
118
127
|
continue;
|
|
119
128
|
}
|
|
120
|
-
|
|
129
|
+
arrayKey = null;
|
|
121
130
|
}
|
|
122
131
|
if (raw.startsWith("source_sessions:")) {
|
|
123
|
-
|
|
132
|
+
arrayKey = "source_sessions";
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (raw.startsWith("contributors:")) {
|
|
136
|
+
arrayKey = "contributors";
|
|
137
|
+
fm.contributors = [];
|
|
124
138
|
continue;
|
|
125
139
|
}
|
|
126
140
|
const m = raw.match(/^([a-zA-Z_]+):\s*(.*)$/);
|
|
@@ -151,11 +165,15 @@ function writeNewSkill(args) {
|
|
|
151
165
|
}
|
|
152
166
|
mkdirSync(dir, { recursive: true });
|
|
153
167
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
168
|
+
const author = args.author && args.author.length > 0 ? args.author : void 0;
|
|
169
|
+
const contributors = author ? [author] : [];
|
|
154
170
|
const fm = {
|
|
155
171
|
name: args.name,
|
|
156
172
|
description: args.description,
|
|
157
173
|
trigger: args.trigger,
|
|
174
|
+
author,
|
|
158
175
|
source_sessions: args.sourceSessions,
|
|
176
|
+
contributors,
|
|
159
177
|
version: 1,
|
|
160
178
|
created_by_agent: args.agent,
|
|
161
179
|
created_at: now,
|
|
@@ -166,7 +184,15 @@ function writeNewSkill(args) {
|
|
|
166
184
|
${args.body.trim()}
|
|
167
185
|
`;
|
|
168
186
|
writeFileSync(path, text);
|
|
169
|
-
return {
|
|
187
|
+
return {
|
|
188
|
+
path,
|
|
189
|
+
action: "created",
|
|
190
|
+
version: 1,
|
|
191
|
+
createdAt: now,
|
|
192
|
+
updatedAt: now,
|
|
193
|
+
author,
|
|
194
|
+
contributors
|
|
195
|
+
};
|
|
170
196
|
}
|
|
171
197
|
function mergeSkill(args) {
|
|
172
198
|
assertValidSkillName(args.name);
|
|
@@ -179,12 +205,20 @@ function mergeSkill(args) {
|
|
|
179
205
|
const prevVersion = parsed?.fm.version ?? 1;
|
|
180
206
|
const prevSources = parsed?.fm.source_sessions ?? [];
|
|
181
207
|
const merged = Array.from(/* @__PURE__ */ new Set([...prevSources, ...args.newSourceSessions]));
|
|
208
|
+
const author = parsed?.fm.author;
|
|
209
|
+
const prevContribs = parsed?.fm.contributors && parsed.fm.contributors.length > 0 ? parsed.fm.contributors : author ? [author] : [];
|
|
210
|
+
const contributors = [...prevContribs];
|
|
211
|
+
if (args.editor && args.editor.length > 0 && !contributors.includes(args.editor)) {
|
|
212
|
+
contributors.push(args.editor);
|
|
213
|
+
}
|
|
182
214
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
183
215
|
const fm = {
|
|
184
216
|
name: args.name,
|
|
185
217
|
description: args.description ?? parsed?.fm.description ?? "",
|
|
186
218
|
trigger: parsed?.fm.trigger,
|
|
219
|
+
author,
|
|
187
220
|
source_sessions: merged,
|
|
221
|
+
contributors,
|
|
188
222
|
version: prevVersion + 1,
|
|
189
223
|
created_by_agent: parsed?.fm.created_by_agent ?? args.agent,
|
|
190
224
|
created_at: parsed?.fm.created_at ?? now,
|
|
@@ -195,7 +229,15 @@ function mergeSkill(args) {
|
|
|
195
229
|
${args.body.trim()}
|
|
196
230
|
`;
|
|
197
231
|
writeFileSync(path, text);
|
|
198
|
-
return {
|
|
232
|
+
return {
|
|
233
|
+
path,
|
|
234
|
+
action: "merged",
|
|
235
|
+
version: fm.version,
|
|
236
|
+
createdAt: fm.created_at,
|
|
237
|
+
updatedAt: fm.updated_at,
|
|
238
|
+
author,
|
|
239
|
+
contributors
|
|
240
|
+
};
|
|
199
241
|
}
|
|
200
242
|
function listSkills(skillsRoot) {
|
|
201
243
|
if (!existsSync(skillsRoot))
|
|
@@ -220,9 +262,14 @@ function resolveSkillsRoot(install, cwd) {
|
|
|
220
262
|
function listAllExistingSkills(cwd) {
|
|
221
263
|
const projectRoot = resolveSkillsRoot("project", cwd);
|
|
222
264
|
const globalRoot = resolveSkillsRoot("global", cwd);
|
|
265
|
+
const tag = (source) => (s) => {
|
|
266
|
+
const parsed = parseFrontmatter(s.body);
|
|
267
|
+
const author = typeof parsed?.fm.author === "string" && parsed.fm.author.length > 0 ? parsed.fm.author : void 0;
|
|
268
|
+
return { name: s.name, body: s.body, source, author };
|
|
269
|
+
};
|
|
223
270
|
const tagged = [
|
|
224
|
-
...listSkills(projectRoot).map((
|
|
225
|
-
...listSkills(globalRoot).map((
|
|
271
|
+
...listSkills(projectRoot).map(tag("project")),
|
|
272
|
+
...listSkills(globalRoot).map(tag("global"))
|
|
226
273
|
];
|
|
227
274
|
const seen = /* @__PURE__ */ new Set();
|
|
228
275
|
const out = [];
|
|
@@ -242,12 +289,13 @@ function renderExistingSkillsBlock(cwd, charCap) {
|
|
|
242
289
|
block: "(no existing skills \u2014 MERGE is NOT a valid choice; pick KEEP or SKIP only)"
|
|
243
290
|
};
|
|
244
291
|
}
|
|
245
|
-
const mergeTargetNames = skills.filter((s) => s.source === "project").map((s) => s.name);
|
|
246
292
|
let total = 0;
|
|
247
293
|
const out = [];
|
|
294
|
+
const mergeTargetNames = [];
|
|
248
295
|
for (const s of skills) {
|
|
249
|
-
const
|
|
250
|
-
const
|
|
296
|
+
const sourceTag = s.source === "project" ? "project" : "global, read-only";
|
|
297
|
+
const authorTag = s.author ? `, author=${s.author}` : "";
|
|
298
|
+
const block = `--- existing skill [${sourceTag}${authorTag}]: ${s.name} ---
|
|
251
299
|
${s.body}
|
|
252
300
|
`;
|
|
253
301
|
if (total + block.length > charCap) {
|
|
@@ -256,6 +304,8 @@ ${s.body}
|
|
|
256
304
|
}
|
|
257
305
|
out.push(block);
|
|
258
306
|
total += block.length;
|
|
307
|
+
if (s.source === "project")
|
|
308
|
+
mergeTargetNames.push(s.name);
|
|
259
309
|
}
|
|
260
310
|
return { mergeTargetNames, block: out.join("\n") };
|
|
261
311
|
}
|
|
@@ -274,7 +324,11 @@ function sqlIdent(name) {
|
|
|
274
324
|
// dist/src/skillify/skills-table.js
|
|
275
325
|
function createSkillsTableSql(tableName) {
|
|
276
326
|
const safe = sqlIdent(tableName);
|
|
277
|
-
return `CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`;
|
|
327
|
+
return `CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', contributors TEXT NOT NULL DEFAULT '[]', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`;
|
|
328
|
+
}
|
|
329
|
+
function addContributorsColumnSql(tableName) {
|
|
330
|
+
const safe = sqlIdent(tableName);
|
|
331
|
+
return `ALTER TABLE "${safe}" ADD COLUMN IF NOT EXISTS contributors TEXT NOT NULL DEFAULT '[]'`;
|
|
278
332
|
}
|
|
279
333
|
function esc(s) {
|
|
280
334
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
@@ -282,12 +336,20 @@ function esc(s) {
|
|
|
282
336
|
function isMissingTableError(message) {
|
|
283
337
|
if (!message)
|
|
284
338
|
return false;
|
|
339
|
+
if (/\bcolumn\b/i.test(message))
|
|
340
|
+
return false;
|
|
285
341
|
return /Table does not exist|relation .* does not exist|no such table/i.test(message);
|
|
286
342
|
}
|
|
343
|
+
function isMissingContributorsColumnError(message) {
|
|
344
|
+
if (!message)
|
|
345
|
+
return false;
|
|
346
|
+
return /contributors.*(?:does not exist|not found|unknown)/i.test(message) || /(?:does not exist|unknown column).*contributors/i.test(message);
|
|
347
|
+
}
|
|
287
348
|
async function insertSkillRow(args) {
|
|
288
349
|
const id = args.id ?? randomUUID();
|
|
289
350
|
const sourceSessionsJson = JSON.stringify(args.sourceSessions);
|
|
290
|
-
const
|
|
351
|
+
const contributorsJson = JSON.stringify(args.contributors);
|
|
352
|
+
const sql = `INSERT INTO "${sqlIdent(args.tableName)}" (id, name, project, project_key, local_path, install, source_sessions, source_agent, scope, author, contributors, description, trigger_text, body, version, created_at, updated_at) VALUES ('${esc(id)}', '${esc(args.name)}', '${esc(args.project)}', '${esc(args.projectKey)}', '${esc(args.localPath)}', '${esc(args.install)}', '${esc(sourceSessionsJson)}', '${esc(args.sourceAgent)}', '${esc(args.scope)}', '${esc(args.author)}', '${esc(contributorsJson)}', '${esc(args.description)}', '${esc(args.trigger ?? "")}', '${esc(args.body)}', ${args.version}, '${esc(args.createdAt)}', '${esc(args.updatedAt)}')`;
|
|
291
353
|
try {
|
|
292
354
|
await args.query(sql);
|
|
293
355
|
} catch (e) {
|
|
@@ -296,6 +358,11 @@ async function insertSkillRow(args) {
|
|
|
296
358
|
await args.query(sql);
|
|
297
359
|
return;
|
|
298
360
|
}
|
|
361
|
+
if (isMissingContributorsColumnError(e?.message)) {
|
|
362
|
+
await args.query(addContributorsColumnSql(args.tableName));
|
|
363
|
+
await args.query(sql);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
299
366
|
throw e;
|
|
300
367
|
}
|
|
301
368
|
}
|
|
@@ -447,6 +514,14 @@ function runGate(opts) {
|
|
|
447
514
|
}
|
|
448
515
|
}
|
|
449
516
|
|
|
517
|
+
// dist/src/skillify/scope-promotion.js
|
|
518
|
+
function isCrossAuthorMergeVerdict(args) {
|
|
519
|
+
return args.verdict === "MERGE" && args.resultAuthor !== void 0 && args.resultAuthor !== args.userName;
|
|
520
|
+
}
|
|
521
|
+
function resolveRecordScope(args) {
|
|
522
|
+
return args.isCrossAuthorMerge && args.configScope === "me" ? "team" : args.configScope;
|
|
523
|
+
}
|
|
524
|
+
|
|
450
525
|
// dist/src/skillify/state.js
|
|
451
526
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, writeSync, mkdirSync as mkdirSync2, renameSync as renameSync2, existsSync as existsSync4, unlinkSync, openSync, closeSync } from "node:fs";
|
|
452
527
|
import { execSync } from "node:child_process";
|
|
@@ -652,8 +727,6 @@ async function query(sql, retries = 4) {
|
|
|
652
727
|
return [];
|
|
653
728
|
}
|
|
654
729
|
function authorClause() {
|
|
655
|
-
if (cfg.scope === "org")
|
|
656
|
-
return "";
|
|
657
730
|
if (cfg.scope === "team" && cfg.team.length > 0) {
|
|
658
731
|
const list = cfg.team.map((n) => `'${esc2(n)}'`).join(", ");
|
|
659
732
|
return ` AND author IN (${list})`;
|
|
@@ -721,7 +794,7 @@ ${answer}
|
|
|
721
794
|
}
|
|
722
795
|
function buildPrompt(pairs) {
|
|
723
796
|
const existing = renderExistingSkillsBlock(cfg.cwd, EXISTING_SKILLS_CHAR_CAP);
|
|
724
|
-
const mergeTargetsClause = existing.mergeTargetNames.length > 0 ? `MERGE is allowed only if your "name" is EXACTLY one of: [${existing.mergeTargetNames.join(", ")}]. Any other name MUST use KEEP, not MERGE.` : `MERGE is FORBIDDEN \u2014 there are no
|
|
797
|
+
const mergeTargetsClause = existing.mergeTargetNames.length > 0 ? `MERGE is allowed only if your "name" is EXACTLY one of: [${existing.mergeTargetNames.join(", ")}]. Any other name MUST use KEEP, not MERGE.` : `MERGE is FORBIDDEN \u2014 there are no existing skills to merge into. Use KEEP or SKIP only.`;
|
|
725
798
|
return [
|
|
726
799
|
`You are a skill curator for the "${cfg.project}" project. You decide whether the recent`,
|
|
727
800
|
`agent activity below contains a recurring, non-trivial pattern worth crystallizing as a`,
|
|
@@ -731,18 +804,19 @@ function buildPrompt(pairs) {
|
|
|
731
804
|
`- KEEP only if the pattern recurs across at least 3 of the exchanges, is non-obvious to a`,
|
|
732
805
|
` competent engineer, and is not already covered by an existing skill below.`,
|
|
733
806
|
`- SKIP if the activity is one-off, generic engineering work, or already covered.`,
|
|
734
|
-
`- MERGE if the pattern is a meaningful extension of an existing
|
|
807
|
+
`- MERGE if the pattern is a meaningful extension of an existing skill \u2014 produce a`,
|
|
735
808
|
` merged body that incorporates the new evidence without exceeding ~3000 characters or`,
|
|
736
809
|
` covering unrelated domains.`,
|
|
737
810
|
`- ${mergeTargetsClause}`,
|
|
738
|
-
`-
|
|
739
|
-
`
|
|
740
|
-
`
|
|
741
|
-
`
|
|
811
|
+
`- Cross-author MERGE has a real cost: editing a skill authored by someone else is`,
|
|
812
|
+
` recorded as a team-level edit (scope=team, contributors+="${cfg.userName}"). Use it only`,
|
|
813
|
+
` when the new evidence genuinely extends the existing skill; otherwise pick KEEP or SKIP.`,
|
|
814
|
+
` Tags like [project, author=alice] / [global, author=bob] tell you whose skill it is.`,
|
|
742
815
|
`- Skill bodies should follow the existing style: short sections (When to use, Workflow,`,
|
|
743
816
|
` Anti-patterns), concrete commands and file paths drawn from the exchanges, no marketing.`,
|
|
744
817
|
``,
|
|
745
|
-
`=== EXISTING SKILLS (
|
|
818
|
+
`=== EXISTING SKILLS (all MERGE-eligible; [global, author=X] entries from teammate X mean`,
|
|
819
|
+
`cross-author MERGE auto-promotes scope to team) ===`,
|
|
746
820
|
existing.block,
|
|
747
821
|
``,
|
|
748
822
|
`=== RECENT EXCHANGES (prompt + answer pairs, tool calls already stripped) ===`,
|
|
@@ -865,6 +939,17 @@ async function main() {
|
|
|
865
939
|
const watermarkDate = oldest.lastMsg;
|
|
866
940
|
const sourceSessions = usable.map((c) => (c.path.split("/").pop() ?? "").replace(/\.[^.]+$/, ""));
|
|
867
941
|
async function recordToDeeplake(result, verdict2) {
|
|
942
|
+
const author = result.author ?? cfg.userName;
|
|
943
|
+
const isCrossAuthorMerge = isCrossAuthorMergeVerdict({
|
|
944
|
+
verdict: verdict2.verdict,
|
|
945
|
+
resultAuthor: result.author,
|
|
946
|
+
userName: cfg.userName
|
|
947
|
+
});
|
|
948
|
+
const scope = resolveRecordScope({
|
|
949
|
+
configScope: cfg.scope,
|
|
950
|
+
isCrossAuthorMerge
|
|
951
|
+
});
|
|
952
|
+
const contributors = result.contributors;
|
|
868
953
|
try {
|
|
869
954
|
await insertSkillRow({
|
|
870
955
|
query,
|
|
@@ -876,8 +961,9 @@ async function main() {
|
|
|
876
961
|
install: cfg.install,
|
|
877
962
|
sourceSessions,
|
|
878
963
|
sourceAgent: cfg.agent,
|
|
879
|
-
scope
|
|
880
|
-
author
|
|
964
|
+
scope,
|
|
965
|
+
author,
|
|
966
|
+
contributors,
|
|
881
967
|
description: verdict2.description ?? "",
|
|
882
968
|
trigger: verdict2.trigger,
|
|
883
969
|
body: verdict2.body,
|
|
@@ -885,7 +971,7 @@ async function main() {
|
|
|
885
971
|
createdAt: result.createdAt,
|
|
886
972
|
updatedAt: result.updatedAt
|
|
887
973
|
});
|
|
888
|
-
wlog(`recorded to skills table: name=${verdict2.name} v${result.version}`);
|
|
974
|
+
wlog(`recorded to skills table: name=${verdict2.name} v${result.version} author=${author} scope=${scope} contributors=${contributors.length}` + (isCrossAuthorMerge ? " [auto-promoted me->team]" : ""));
|
|
889
975
|
} catch (e) {
|
|
890
976
|
wlog(`skills table insert failed (non-fatal): ${e.message}`);
|
|
891
977
|
}
|
|
@@ -899,7 +985,8 @@ async function main() {
|
|
|
899
985
|
trigger: verdict.trigger,
|
|
900
986
|
body: verdict.body,
|
|
901
987
|
sourceSessions,
|
|
902
|
-
agent: cfg.agent
|
|
988
|
+
agent: cfg.agent,
|
|
989
|
+
author: cfg.userName
|
|
903
990
|
});
|
|
904
991
|
wlog(`wrote new skill: ${result.path}`);
|
|
905
992
|
recordSkill(cfg.projectKey, verdict.name, watermarkUuid, watermarkDate);
|
|
@@ -916,7 +1003,8 @@ async function main() {
|
|
|
916
1003
|
description: verdict.description,
|
|
917
1004
|
body: verdict.body,
|
|
918
1005
|
newSourceSessions: sourceSessions,
|
|
919
|
-
agent: cfg.agent
|
|
1006
|
+
agent: cfg.agent,
|
|
1007
|
+
editor: cfg.userName
|
|
920
1008
|
});
|
|
921
1009
|
wlog(`merged into skill: ${result.path} (v${result.version})`);
|
|
922
1010
|
recordSkill(cfg.projectKey, verdict.name, watermarkUuid, watermarkDate);
|
|
@@ -932,7 +1020,8 @@ async function main() {
|
|
|
932
1020
|
trigger: verdict.trigger,
|
|
933
1021
|
body: verdict.body,
|
|
934
1022
|
sourceSessions,
|
|
935
|
-
agent: cfg.agent
|
|
1023
|
+
agent: cfg.agent,
|
|
1024
|
+
author: cfg.userName
|
|
936
1025
|
});
|
|
937
1026
|
wlog(`wrote new skill (merge fallback): ${result.path}`);
|
|
938
1027
|
recordSkill(cfg.projectKey, verdict.name, watermarkUuid, watermarkDate);
|