duclaw-cli 1.9.7 → 1.9.9
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/bundle.js +779 -684
- package/dist/main.js +1 -1
- package/dist/web/assets/{index-BYLnL8Rp.js → index-CiEOENzL.js} +2 -2
- package/dist/web/index.html +1 -1
- package/dist/worker-main.js +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -30242,7 +30242,7 @@ function printHelp() {
|
|
|
30242
30242
|
`);
|
|
30243
30243
|
}
|
|
30244
30244
|
function printVersion() {
|
|
30245
|
-
console.log(`duclaw-cli v${true ? "1.9.
|
|
30245
|
+
console.log(`duclaw-cli v${true ? "1.9.9" : "unknown"}`);
|
|
30246
30246
|
}
|
|
30247
30247
|
function getDuclawTemplate() {
|
|
30248
30248
|
return {
|
|
@@ -41782,9 +41782,9 @@ var imageUnderstand = {
|
|
|
41782
41782
|
};
|
|
41783
41783
|
|
|
41784
41784
|
// src/skill/SkillRegistry.ts
|
|
41785
|
-
var
|
|
41785
|
+
var import_fs13 = require("fs");
|
|
41786
41786
|
var import_os3 = require("os");
|
|
41787
|
-
var
|
|
41787
|
+
var import_path18 = __toESM(require("path"));
|
|
41788
41788
|
|
|
41789
41789
|
// src/runtime/paths.ts
|
|
41790
41790
|
var import_fs10 = require("fs");
|
|
@@ -41813,33 +41813,608 @@ var resolveCoreRoot = () => {
|
|
|
41813
41813
|
return candidate;
|
|
41814
41814
|
}
|
|
41815
41815
|
}
|
|
41816
|
-
return candidates[0];
|
|
41816
|
+
return candidates[0];
|
|
41817
|
+
};
|
|
41818
|
+
var resolveWebDistRoot = () => {
|
|
41819
|
+
const coreRoot = resolveCoreRoot();
|
|
41820
|
+
const candidates = [
|
|
41821
|
+
import_path15.default.join(coreRoot, "dist", "web"),
|
|
41822
|
+
import_path15.default.join(coreRoot, "web", "dist")
|
|
41823
|
+
];
|
|
41824
|
+
for (const candidate of candidates) {
|
|
41825
|
+
if ((0, import_fs10.existsSync)(import_path15.default.join(candidate, "index.html"))) {
|
|
41826
|
+
return candidate;
|
|
41827
|
+
}
|
|
41828
|
+
}
|
|
41829
|
+
return candidates[0];
|
|
41830
|
+
};
|
|
41831
|
+
|
|
41832
|
+
// src/department/Department.ts
|
|
41833
|
+
var import_path16 = __toESM(require("path"));
|
|
41834
|
+
var import_fs11 = require("fs");
|
|
41835
|
+
var legacyMigrationChecked = false;
|
|
41836
|
+
var getDepartmentBaseDir = () => {
|
|
41837
|
+
return import_path16.default.join(getDuclawHomeDir(), "department");
|
|
41838
|
+
};
|
|
41839
|
+
var getLegacyTeamBaseDir = () => {
|
|
41840
|
+
return import_path16.default.join(getDuclawHomeDir(), "team");
|
|
41841
|
+
};
|
|
41842
|
+
var getDepartmentWorkSpaceDir = (departmentName) => {
|
|
41843
|
+
return import_path16.default.join(getDepartmentBaseDir(), "workspace", departmentName);
|
|
41844
|
+
};
|
|
41845
|
+
var getLegacyTeamWorkSpaceDir = (teamName) => {
|
|
41846
|
+
return import_path16.default.join(getLegacyTeamBaseDir(), "workspace", teamName);
|
|
41847
|
+
};
|
|
41848
|
+
var getDepartmentJsonPath = (departmentName) => {
|
|
41849
|
+
return import_path16.default.join(getDepartmentWorkSpaceDir(departmentName), "department.json");
|
|
41850
|
+
};
|
|
41851
|
+
var getLegacyTeamJsonPath = (teamName) => {
|
|
41852
|
+
return import_path16.default.join(getLegacyTeamWorkSpaceDir(teamName), "team.json");
|
|
41853
|
+
};
|
|
41854
|
+
var mapLegacyRole = (role) => {
|
|
41855
|
+
return role === "team_manager" ? "department_head" : "executor";
|
|
41856
|
+
};
|
|
41857
|
+
var mapLegacyDepartment = (legacy) => {
|
|
41858
|
+
const departmentMembers = (legacy.teamMembers ?? []).map((member) => ({
|
|
41859
|
+
id: member.id,
|
|
41860
|
+
name: member.name,
|
|
41861
|
+
departmentId: legacy.id,
|
|
41862
|
+
mailBoxId: member.mailBoxId,
|
|
41863
|
+
workspaceId: member.workspaceId,
|
|
41864
|
+
role: mapLegacyRole(member.role),
|
|
41865
|
+
focusOn: member.focusOn
|
|
41866
|
+
}));
|
|
41867
|
+
const headMemberId = legacy.managerMemberId ?? departmentMembers.find((member) => member.role === "department_head")?.id;
|
|
41868
|
+
return {
|
|
41869
|
+
id: legacy.id,
|
|
41870
|
+
name: legacy.name,
|
|
41871
|
+
sourceGoalId: legacy.goalId,
|
|
41872
|
+
charter: legacy.goalId ? `Legacy department migrated from team goal ${legacy.goalId}.` : "Legacy department migrated from team data.",
|
|
41873
|
+
workpath: legacy.workpath,
|
|
41874
|
+
headMemberId,
|
|
41875
|
+
departmentMembers
|
|
41876
|
+
};
|
|
41877
|
+
};
|
|
41878
|
+
var migrateLegacyTeamsToDepartments = () => {
|
|
41879
|
+
if (legacyMigrationChecked) return;
|
|
41880
|
+
legacyMigrationChecked = true;
|
|
41881
|
+
const legacyWorkspaceDir = import_path16.default.join(getLegacyTeamBaseDir(), "workspace");
|
|
41882
|
+
if (!(0, import_fs11.existsSync)(legacyWorkspaceDir)) return;
|
|
41883
|
+
for (const legacyName of (0, import_fs11.readdirSync)(legacyWorkspaceDir)) {
|
|
41884
|
+
const legacyJsonPath = getLegacyTeamJsonPath(legacyName);
|
|
41885
|
+
if (!(0, import_fs11.existsSync)(legacyJsonPath)) continue;
|
|
41886
|
+
const departmentJsonPath = getDepartmentJsonPath(legacyName);
|
|
41887
|
+
if ((0, import_fs11.existsSync)(departmentJsonPath)) continue;
|
|
41888
|
+
try {
|
|
41889
|
+
const legacy = JSON.parse((0, import_fs11.readFileSync)(legacyJsonPath, "utf-8"));
|
|
41890
|
+
const department = mapLegacyDepartment(legacy);
|
|
41891
|
+
(0, import_fs11.mkdirSync)(getDepartmentWorkSpaceDir(department.name), { recursive: true });
|
|
41892
|
+
(0, import_fs11.writeFileSync)(departmentJsonPath, JSON.stringify(department, null, " "), "utf-8");
|
|
41893
|
+
} catch (err) {
|
|
41894
|
+
console.warn(`[department] Failed to migrate legacy team ${legacyName}: ${err.message}`);
|
|
41895
|
+
}
|
|
41896
|
+
}
|
|
41897
|
+
};
|
|
41898
|
+
var createDepartment = (departmentDefinition) => {
|
|
41899
|
+
if (!departmentDefinition) throw new Error(`[createDepartment] departmentDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
41900
|
+
const departmentPath = getDepartmentWorkSpaceDir(departmentDefinition.name);
|
|
41901
|
+
(0, import_fs11.mkdirSync)(departmentPath, { recursive: true });
|
|
41902
|
+
(0, import_fs11.writeFileSync)(getDepartmentJsonPath(departmentDefinition.name), JSON.stringify(departmentDefinition, null, " "), "utf-8");
|
|
41903
|
+
return departmentDefinition;
|
|
41904
|
+
};
|
|
41905
|
+
var getDepartment = (name) => {
|
|
41906
|
+
migrateLegacyTeamsToDepartments();
|
|
41907
|
+
const departmentJsonPath = getDepartmentJsonPath(name);
|
|
41908
|
+
if (!(0, import_fs11.existsSync)(departmentJsonPath)) return null;
|
|
41909
|
+
const text2 = (0, import_fs11.readFileSync)(departmentJsonPath, "utf-8");
|
|
41910
|
+
return JSON.parse(text2);
|
|
41911
|
+
};
|
|
41912
|
+
var listDepartments = () => {
|
|
41913
|
+
migrateLegacyTeamsToDepartments();
|
|
41914
|
+
const workspaceDir = import_path16.default.join(getDepartmentBaseDir(), "workspace");
|
|
41915
|
+
if (!(0, import_fs11.existsSync)(workspaceDir)) return [];
|
|
41916
|
+
const departments = [];
|
|
41917
|
+
for (const departmentName of (0, import_fs11.readdirSync)(workspaceDir)) {
|
|
41918
|
+
const department = getDepartment(departmentName);
|
|
41919
|
+
if (department) departments.push(department);
|
|
41920
|
+
}
|
|
41921
|
+
return departments;
|
|
41922
|
+
};
|
|
41923
|
+
var getDepartmentById = (id) => {
|
|
41924
|
+
const department = listDepartments().find((item) => item.id === id);
|
|
41925
|
+
return department ?? null;
|
|
41926
|
+
};
|
|
41927
|
+
var deleteDepartment = (name) => {
|
|
41928
|
+
if (!(0, import_fs11.existsSync)(getDepartmentJsonPath(name))) {
|
|
41929
|
+
throw new Error(`[deleteDepartment] \u4E0D\u5B58\u5728\u5BF9\u5E94\u7684\u90E8\u95E8 ${name} \u7684 department.json \u6587\u4EF6`);
|
|
41930
|
+
}
|
|
41931
|
+
(0, import_fs11.rmSync)(getDepartmentJsonPath(name));
|
|
41932
|
+
};
|
|
41933
|
+
|
|
41934
|
+
// src/department/learning.ts
|
|
41935
|
+
var import_node_fs4 = require("node:fs");
|
|
41936
|
+
var import_node_path13 = __toESM(require("node:path"));
|
|
41937
|
+
var import_node_crypto4 = require("node:crypto");
|
|
41938
|
+
|
|
41939
|
+
// src/department/DepartmentMember.ts
|
|
41940
|
+
var import_fs12 = require("fs");
|
|
41941
|
+
var import_path17 = __toESM(require("path"));
|
|
41942
|
+
|
|
41943
|
+
// src/department/workspace/workspace.ts
|
|
41944
|
+
var db = createSqliteDB();
|
|
41945
|
+
var getWorkspaceId = (departmentName, memberName) => {
|
|
41946
|
+
return `${departmentName}::${memberName}`;
|
|
41947
|
+
};
|
|
41948
|
+
var createWorkspace = (workspace) => {
|
|
41949
|
+
const stmt = db.prepare(`INSERT INTO workspace (id, team_name, teammate_name, team_workpath) VALUES (?, ?, ?, ?) `);
|
|
41950
|
+
stmt.run(
|
|
41951
|
+
workspace.id,
|
|
41952
|
+
workspace.departmentName,
|
|
41953
|
+
workspace.memberName,
|
|
41954
|
+
workspace.departmentWorkPath
|
|
41955
|
+
);
|
|
41956
|
+
};
|
|
41957
|
+
var deleteWorkspace = (workspaceId) => {
|
|
41958
|
+
const stmt = db.prepare(`delete from workspace where id = ?`);
|
|
41959
|
+
stmt.run(workspaceId);
|
|
41960
|
+
};
|
|
41961
|
+
|
|
41962
|
+
// src/department/DepartmentMember.ts
|
|
41963
|
+
var createDepartmentMember = (departmentMemberDefinition) => {
|
|
41964
|
+
if (!departmentMemberDefinition) throw new Error(`[createDepartmentMember] departmentMemberDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
41965
|
+
const { name, departmentId, workspaceId } = departmentMemberDefinition;
|
|
41966
|
+
const department = getDepartmentById(departmentId);
|
|
41967
|
+
if (!department) throw new Error(`[createDepartmentMember] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentId}`);
|
|
41968
|
+
const memberPath = import_path17.default.join(getDepartmentWorkSpaceDir(department.name), name);
|
|
41969
|
+
(0, import_fs12.mkdirSync)(memberPath, { recursive: true });
|
|
41970
|
+
const workspace = {
|
|
41971
|
+
id: getWorkspaceId(department.name, departmentMemberDefinition.name),
|
|
41972
|
+
departmentName: department.name,
|
|
41973
|
+
memberName: name,
|
|
41974
|
+
departmentWorkPath: memberPath
|
|
41975
|
+
};
|
|
41976
|
+
createWorkspace(workspace);
|
|
41977
|
+
if (!department.departmentMembers) {
|
|
41978
|
+
department.departmentMembers = [];
|
|
41979
|
+
}
|
|
41980
|
+
if (department.departmentMembers.some((member) => member.id === departmentMemberDefinition.id || member.name === departmentMemberDefinition.name)) {
|
|
41981
|
+
throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728\u540C\u540D\u6216\u540C id \u6210\u5458: ${departmentMemberDefinition.name}/${departmentMemberDefinition.id}`);
|
|
41982
|
+
}
|
|
41983
|
+
if (departmentMemberDefinition.role === "department_head") {
|
|
41984
|
+
const existingHead = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
|
|
41985
|
+
if (existingHead) {
|
|
41986
|
+
throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728 Department Head: ${existingHead.name}`);
|
|
41987
|
+
}
|
|
41988
|
+
department.headMemberId = departmentMemberDefinition.id;
|
|
41989
|
+
}
|
|
41990
|
+
department.departmentMembers.push(departmentMemberDefinition);
|
|
41991
|
+
(0, import_fs12.writeFileSync)(getDepartmentJsonPath(department.name), JSON.stringify(department, null, " "), "utf-8");
|
|
41992
|
+
return departmentMemberDefinition;
|
|
41993
|
+
};
|
|
41994
|
+
var listDepartmentMembers = (departmentName) => {
|
|
41995
|
+
const departmentJsonPath = getDepartmentJsonPath(departmentName);
|
|
41996
|
+
if (!(0, import_fs12.existsSync)(departmentJsonPath)) return [];
|
|
41997
|
+
const text2 = (0, import_fs12.readFileSync)(departmentJsonPath, "utf-8");
|
|
41998
|
+
const department = JSON.parse(text2);
|
|
41999
|
+
if (!department) throw new Error(`[listDepartmentMembers] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
|
|
42000
|
+
return department.departmentMembers ?? [];
|
|
42001
|
+
};
|
|
42002
|
+
var getDepartmentMember = (departmentName, departmentMemberId) => {
|
|
42003
|
+
const members = listDepartmentMembers(departmentName);
|
|
42004
|
+
return members.find((member) => member.id === departmentMemberId) || null;
|
|
42005
|
+
};
|
|
42006
|
+
var getDepartmentMemberByName = (departmentName, departmentMemberName) => {
|
|
42007
|
+
const members = listDepartmentMembers(departmentName);
|
|
42008
|
+
return members.find((member) => member.name === departmentMemberName) || null;
|
|
42009
|
+
};
|
|
42010
|
+
var getDepartmentMemberByMailboxId = (mailboxId) => {
|
|
42011
|
+
const [departmentName, memberName] = mailboxId.split("::");
|
|
42012
|
+
if (!departmentName || !memberName) return null;
|
|
42013
|
+
return getDepartmentMemberByName(departmentName, memberName);
|
|
42014
|
+
};
|
|
42015
|
+
var getDepartmentMemberById = (memberId) => {
|
|
42016
|
+
for (const department of listDepartments()) {
|
|
42017
|
+
const targetMember = department.departmentMembers.find((member) => member.id === memberId);
|
|
42018
|
+
if (targetMember) return targetMember;
|
|
42019
|
+
}
|
|
42020
|
+
return null;
|
|
42021
|
+
};
|
|
42022
|
+
var deleteDepartmentMemberById = (departmentName, memberId) => {
|
|
42023
|
+
const department = getDepartment(departmentName);
|
|
42024
|
+
if (!department) throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
|
|
42025
|
+
const memberIdx = department.departmentMembers.findIndex((member) => member.id === memberId);
|
|
42026
|
+
if (memberIdx === -1) {
|
|
42027
|
+
throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94 id \u7684 department member: ${memberId}`);
|
|
42028
|
+
}
|
|
42029
|
+
const workspaceId = department.departmentMembers[memberIdx].workspaceId;
|
|
42030
|
+
if (department.headMemberId === memberId) {
|
|
42031
|
+
delete department.headMemberId;
|
|
42032
|
+
}
|
|
42033
|
+
department.departmentMembers = department.departmentMembers.filter((_, index) => index !== memberIdx);
|
|
42034
|
+
(0, import_fs12.writeFileSync)(getDepartmentJsonPath(departmentName), JSON.stringify(department, null, " "), "utf-8");
|
|
42035
|
+
deleteWorkspace(workspaceId);
|
|
42036
|
+
};
|
|
42037
|
+
|
|
42038
|
+
// src/skill/SkillValidator.ts
|
|
42039
|
+
var import_node_fs3 = require("node:fs");
|
|
42040
|
+
var import_node_path12 = __toESM(require("node:path"));
|
|
42041
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
42042
|
+
var REQUIRED_SECTIONS = [
|
|
42043
|
+
/(^|\n)##\s+(when to use|何时使用|trigger|triggers)\b/i,
|
|
42044
|
+
/(^|\n)##\s+(steps|workflow|procedure|执行步骤|工作流)\b/i
|
|
42045
|
+
];
|
|
42046
|
+
var stripYamlQuotes = (value) => {
|
|
42047
|
+
const trimmed = value.trim();
|
|
42048
|
+
if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) {
|
|
42049
|
+
return trimmed.slice(1, -1).trim();
|
|
42050
|
+
}
|
|
42051
|
+
return trimmed;
|
|
42052
|
+
};
|
|
42053
|
+
var addError = (errors, code, message) => {
|
|
42054
|
+
errors.push({ code, message });
|
|
42055
|
+
};
|
|
42056
|
+
var addWarning = (warnings, code, message) => {
|
|
42057
|
+
warnings.push({ code, message });
|
|
42058
|
+
};
|
|
42059
|
+
var isValidSkillName = (name) => {
|
|
42060
|
+
return SKILL_NAME_PATTERN.test(name) && !name.includes(`--`);
|
|
42061
|
+
};
|
|
42062
|
+
var assertSafeSkillTarget = (rootDir, skillName) => {
|
|
42063
|
+
if (!isValidSkillName(skillName)) {
|
|
42064
|
+
throw new Error(`Invalid skill name "${skillName}". Use lowercase letters, digits, and single hyphens only.`);
|
|
42065
|
+
}
|
|
42066
|
+
const root = import_node_path12.default.resolve(rootDir);
|
|
42067
|
+
const target = import_node_path12.default.resolve(root, skillName);
|
|
42068
|
+
if (target !== import_node_path12.default.join(root, skillName) || !target.startsWith(root + import_node_path12.default.sep)) {
|
|
42069
|
+
throw new Error(`Invalid skill install target for "${skillName}".`);
|
|
42070
|
+
}
|
|
42071
|
+
return target;
|
|
42072
|
+
};
|
|
42073
|
+
var parseSkillDocument = (skillMd) => {
|
|
42074
|
+
const fmMatch = skillMd.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
42075
|
+
if (!fmMatch) return null;
|
|
42076
|
+
const frontmatter = {};
|
|
42077
|
+
for (const rawLine of fmMatch[1].split(/\r?\n/)) {
|
|
42078
|
+
const line = rawLine.trim();
|
|
42079
|
+
if (!line || line.startsWith(`#`)) continue;
|
|
42080
|
+
const match2 = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
42081
|
+
if (!match2) {
|
|
42082
|
+
frontmatter[`__invalid__${Object.keys(frontmatter).length}`] = line;
|
|
42083
|
+
continue;
|
|
42084
|
+
}
|
|
42085
|
+
frontmatter[match2[1]] = stripYamlQuotes(match2[2]);
|
|
42086
|
+
}
|
|
42087
|
+
return {
|
|
42088
|
+
name: frontmatter.name ?? ``,
|
|
42089
|
+
description: frontmatter.description ?? ``,
|
|
42090
|
+
body: skillMd.slice(fmMatch[0].length).trim(),
|
|
42091
|
+
frontmatter
|
|
42092
|
+
};
|
|
42093
|
+
};
|
|
42094
|
+
var validateSkillDocument = (input) => {
|
|
42095
|
+
const errors = [];
|
|
42096
|
+
const warnings = [];
|
|
42097
|
+
const skillMd = input.skillMd;
|
|
42098
|
+
if (!skillMd || !skillMd.trim()) {
|
|
42099
|
+
addError(errors, `empty_skill_md`, `SKILL.md content is required.`);
|
|
42100
|
+
return { ok: false, errors, warnings };
|
|
42101
|
+
}
|
|
42102
|
+
const parsed = parseSkillDocument(skillMd);
|
|
42103
|
+
if (!parsed) {
|
|
42104
|
+
addError(errors, `missing_frontmatter`, `SKILL.md must start with YAML frontmatter delimited by "---".`);
|
|
42105
|
+
return { ok: false, errors, warnings };
|
|
42106
|
+
}
|
|
42107
|
+
const invalidFrontmatterLines = Object.keys(parsed.frontmatter).filter((key) => key.startsWith(`__invalid__`));
|
|
42108
|
+
if (invalidFrontmatterLines.length > 0) {
|
|
42109
|
+
addError(errors, `invalid_frontmatter`, `Frontmatter must contain simple "key: value" lines.`);
|
|
42110
|
+
}
|
|
42111
|
+
if (!parsed.name) {
|
|
42112
|
+
addError(errors, `missing_name`, `Frontmatter field "name" is required.`);
|
|
42113
|
+
} else if (!isValidSkillName(parsed.name)) {
|
|
42114
|
+
addError(errors, `invalid_name`, `Skill name "${parsed.name}" must be kebab-case, under 64 characters, and path-safe.`);
|
|
42115
|
+
}
|
|
42116
|
+
if (!parsed.description) {
|
|
42117
|
+
addError(errors, `missing_description`, `Frontmatter field "description" is required.`);
|
|
42118
|
+
} else if (parsed.description.length > 500) {
|
|
42119
|
+
addWarning(warnings, `long_description`, `Skill description is long; keep trigger metadata concise.`);
|
|
42120
|
+
}
|
|
42121
|
+
if (input.skillName && parsed.name && input.skillName !== parsed.name) {
|
|
42122
|
+
addError(errors, `name_mismatch`, `Input skillName "${input.skillName}" must match frontmatter name "${parsed.name}".`);
|
|
42123
|
+
}
|
|
42124
|
+
if (input.description && parsed.description && input.description !== parsed.description) {
|
|
42125
|
+
addError(errors, `description_mismatch`, `Input description must match frontmatter description.`);
|
|
42126
|
+
}
|
|
42127
|
+
if (!parsed.body) {
|
|
42128
|
+
addError(errors, `empty_body`, `SKILL.md body is required.`);
|
|
42129
|
+
} else {
|
|
42130
|
+
if (!/(^|\n)#\s+\S/.test(parsed.body)) {
|
|
42131
|
+
addWarning(warnings, `missing_title`, `Skill body should start with a Markdown H1 title.`);
|
|
42132
|
+
}
|
|
42133
|
+
for (const sectionPattern of REQUIRED_SECTIONS) {
|
|
42134
|
+
if (!sectionPattern.test(parsed.body)) {
|
|
42135
|
+
addWarning(warnings, `missing_recommended_section`, `Skill body should include when-to-use and execution workflow sections.`);
|
|
42136
|
+
break;
|
|
42137
|
+
}
|
|
42138
|
+
}
|
|
42139
|
+
}
|
|
42140
|
+
if (input.baseDir) {
|
|
42141
|
+
const normalizedBase = import_node_path12.default.resolve(input.baseDir);
|
|
42142
|
+
for (const match2 of parsed.body.matchAll(/\]\(([^)]+)\)/g)) {
|
|
42143
|
+
const href = match2[1].trim();
|
|
42144
|
+
if (!href || /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith(`#`)) continue;
|
|
42145
|
+
const target = import_node_path12.default.resolve(normalizedBase, href.split(`#`)[0]);
|
|
42146
|
+
if (!target.startsWith(normalizedBase + import_node_path12.default.sep) && target !== normalizedBase) {
|
|
42147
|
+
addError(errors, `unsafe_reference`, `Relative link "${href}" escapes the skill directory.`);
|
|
42148
|
+
} else if (!(0, import_node_fs3.existsSync)(target)) {
|
|
42149
|
+
addError(errors, `missing_reference`, `Relative link "${href}" does not exist in the skill directory.`);
|
|
42150
|
+
}
|
|
42151
|
+
}
|
|
42152
|
+
}
|
|
42153
|
+
return {
|
|
42154
|
+
ok: errors.length === 0,
|
|
42155
|
+
skill: parsed,
|
|
42156
|
+
errors,
|
|
42157
|
+
warnings
|
|
42158
|
+
};
|
|
42159
|
+
};
|
|
42160
|
+
var mergeIssues = (target, source) => {
|
|
42161
|
+
target.errors.push(...source.errors);
|
|
42162
|
+
target.warnings.push(...source.warnings);
|
|
42163
|
+
};
|
|
42164
|
+
var validateScriptsDirectory = (skillDir, warnings) => {
|
|
42165
|
+
const scriptsDir = import_node_path12.default.join(skillDir, `scripts`);
|
|
42166
|
+
if (!(0, import_node_fs3.existsSync)(scriptsDir)) return;
|
|
42167
|
+
const entries = (0, import_node_fs3.readdirSync)(scriptsDir, { withFileTypes: true }).filter((entry) => entry.isFile());
|
|
42168
|
+
if (entries.length === 0) {
|
|
42169
|
+
addWarning(warnings, `empty_scripts_dir`, `scripts/ exists but contains no files.`);
|
|
42170
|
+
}
|
|
42171
|
+
};
|
|
42172
|
+
var validateSkillDirectory = (skillDir, options = {}) => {
|
|
42173
|
+
const errors = [];
|
|
42174
|
+
const warnings = [];
|
|
42175
|
+
const normalizedSkillDir = import_node_path12.default.resolve(skillDir);
|
|
42176
|
+
const skillMdPath = import_node_path12.default.join(normalizedSkillDir, `SKILL.md`);
|
|
42177
|
+
if (!(0, import_node_fs3.existsSync)(normalizedSkillDir) || !(0, import_node_fs3.statSync)(normalizedSkillDir).isDirectory()) {
|
|
42178
|
+
addError(errors, `missing_skill_dir`, `Skill directory does not exist: ${normalizedSkillDir}`);
|
|
42179
|
+
return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
|
|
42180
|
+
}
|
|
42181
|
+
if (!(0, import_node_fs3.existsSync)(skillMdPath) || !(0, import_node_fs3.statSync)(skillMdPath).isFile()) {
|
|
42182
|
+
addError(errors, `missing_skill_md`, `Skill directory must contain SKILL.md.`);
|
|
42183
|
+
return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
|
|
42184
|
+
}
|
|
42185
|
+
const raw2 = (0, import_node_fs3.readFileSync)(skillMdPath, `utf-8`);
|
|
42186
|
+
const documentResult = validateSkillDocument({
|
|
42187
|
+
skillName: options.expectedName ?? import_node_path12.default.basename(normalizedSkillDir),
|
|
42188
|
+
skillMd: raw2,
|
|
42189
|
+
baseDir: normalizedSkillDir
|
|
42190
|
+
});
|
|
42191
|
+
mergeIssues({ errors, warnings }, documentResult);
|
|
42192
|
+
validateScriptsDirectory(normalizedSkillDir, warnings);
|
|
42193
|
+
return {
|
|
42194
|
+
ok: errors.length === 0,
|
|
42195
|
+
skill: documentResult.skill,
|
|
42196
|
+
errors,
|
|
42197
|
+
warnings,
|
|
42198
|
+
skillDir: normalizedSkillDir,
|
|
42199
|
+
skillMdPath
|
|
42200
|
+
};
|
|
42201
|
+
};
|
|
42202
|
+
var smokeTestSkillDirectory = (skillDir, options = {}) => {
|
|
42203
|
+
const directoryResult = validateSkillDirectory(skillDir, options);
|
|
42204
|
+
const errors = [...directoryResult.errors];
|
|
42205
|
+
const warnings = [...directoryResult.warnings];
|
|
42206
|
+
let detail;
|
|
42207
|
+
if (directoryResult.ok && directoryResult.skill) {
|
|
42208
|
+
detail = `Base directory for this skill: ${directoryResult.skillDir}
|
|
42209
|
+
|
|
42210
|
+
${directoryResult.skill.body}`;
|
|
42211
|
+
if (!detail.includes(directoryResult.skillDir)) {
|
|
42212
|
+
addError(errors, `smoke_missing_base_dir`, `Skill detail does not include its base directory.`);
|
|
42213
|
+
}
|
|
42214
|
+
if (!detail.includes(directoryResult.skill.body)) {
|
|
42215
|
+
addError(errors, `smoke_missing_body`, `Skill detail does not include the SKILL.md body.`);
|
|
42216
|
+
}
|
|
42217
|
+
if (detail.length < 80) {
|
|
42218
|
+
addWarning(warnings, `smoke_short_detail`, `Skill detail is unusually short.`);
|
|
42219
|
+
}
|
|
42220
|
+
}
|
|
42221
|
+
return {
|
|
42222
|
+
...directoryResult,
|
|
42223
|
+
ok: errors.length === 0,
|
|
42224
|
+
errors,
|
|
42225
|
+
warnings,
|
|
42226
|
+
detail
|
|
42227
|
+
};
|
|
42228
|
+
};
|
|
42229
|
+
var formatSkillValidationIssues = (result) => {
|
|
42230
|
+
const lines = [
|
|
42231
|
+
...result.errors.map((issue) => `- [${issue.code}] ${issue.message}`),
|
|
42232
|
+
...result.warnings.map((issue) => `- [warning:${issue.code}] ${issue.message}`)
|
|
42233
|
+
];
|
|
42234
|
+
return lines.join(`
|
|
42235
|
+
`);
|
|
42236
|
+
};
|
|
42237
|
+
|
|
42238
|
+
// src/department/learning.ts
|
|
42239
|
+
var ensureDir = (dir) => (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
42240
|
+
var readJsonArray = (filePath) => {
|
|
42241
|
+
if (!(0, import_node_fs4.existsSync)(filePath)) return [];
|
|
42242
|
+
return JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf-8"));
|
|
42243
|
+
};
|
|
42244
|
+
var writeJsonArray = (filePath, records) => {
|
|
42245
|
+
ensureDir(import_node_path13.default.dirname(filePath));
|
|
42246
|
+
(0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(records, null, " "), "utf-8");
|
|
42247
|
+
};
|
|
42248
|
+
var departmentMemoryPath = (departmentName) => {
|
|
42249
|
+
return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-memory.json");
|
|
42250
|
+
};
|
|
42251
|
+
var departmentSkillPath = (departmentName) => {
|
|
42252
|
+
return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-skills.json");
|
|
42253
|
+
};
|
|
42254
|
+
var departmentSkillDir = (departmentName, skillName) => {
|
|
42255
|
+
return assertSafeSkillTarget(import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "skills"), skillName);
|
|
42256
|
+
};
|
|
42257
|
+
var proposalsPath = () => {
|
|
42258
|
+
return import_node_path13.default.join(getDepartmentBaseDir(), "department-proposals.json");
|
|
42259
|
+
};
|
|
42260
|
+
var getDepartmentNameForHead = (request) => {
|
|
42261
|
+
const mailboxId = request?.departmentAgentId;
|
|
42262
|
+
if (!mailboxId) return null;
|
|
42263
|
+
const member = getDepartmentMemberByMailboxId(mailboxId);
|
|
42264
|
+
if (!member || member.role !== "department_head") return null;
|
|
42265
|
+
const [departmentName] = mailboxId.split("::");
|
|
42266
|
+
return departmentName || null;
|
|
42267
|
+
};
|
|
42268
|
+
var listDepartmentMemories = (departmentName) => {
|
|
42269
|
+
return readJsonArray(departmentMemoryPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
42270
|
+
};
|
|
42271
|
+
var createDepartmentMemory = (departmentName, input) => {
|
|
42272
|
+
const now = Date.now();
|
|
42273
|
+
const memory = {
|
|
42274
|
+
id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
|
|
42275
|
+
departmentName,
|
|
42276
|
+
title: input.title,
|
|
42277
|
+
content: input.content,
|
|
42278
|
+
sourceMailboxId: input.sourceMailboxId,
|
|
42279
|
+
createdAt: now,
|
|
42280
|
+
updatedAt: now
|
|
42281
|
+
};
|
|
42282
|
+
const records = listDepartmentMemories(departmentName);
|
|
42283
|
+
records.push(memory);
|
|
42284
|
+
writeJsonArray(departmentMemoryPath(departmentName), records);
|
|
42285
|
+
return memory;
|
|
42286
|
+
};
|
|
42287
|
+
var updateDepartmentMemory = (departmentName, id, patch) => {
|
|
42288
|
+
const records = listDepartmentMemories(departmentName);
|
|
42289
|
+
const idx = records.findIndex((record) => record.id === id);
|
|
42290
|
+
if (idx < 0) return null;
|
|
42291
|
+
records[idx] = {
|
|
42292
|
+
...records[idx],
|
|
42293
|
+
...patch.title !== void 0 ? { title: patch.title } : {},
|
|
42294
|
+
...patch.content !== void 0 ? { content: patch.content } : {},
|
|
42295
|
+
updatedAt: Date.now()
|
|
42296
|
+
};
|
|
42297
|
+
writeJsonArray(departmentMemoryPath(departmentName), records);
|
|
42298
|
+
return records[idx];
|
|
42299
|
+
};
|
|
42300
|
+
var deleteDepartmentMemory = (departmentName, id) => {
|
|
42301
|
+
const records = listDepartmentMemories(departmentName);
|
|
42302
|
+
const target = records.find((record) => record.id === id) ?? null;
|
|
42303
|
+
writeJsonArray(departmentMemoryPath(departmentName), records.filter((record) => record.id !== id));
|
|
42304
|
+
return target;
|
|
42305
|
+
};
|
|
42306
|
+
var listDepartmentSkills = (departmentName) => {
|
|
42307
|
+
return readJsonArray(departmentSkillPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
42308
|
+
};
|
|
42309
|
+
var proposeDepartmentSkill = (departmentName, input) => {
|
|
42310
|
+
const validation = validateSkillDocument({
|
|
42311
|
+
skillName: input.skillName,
|
|
42312
|
+
description: input.description,
|
|
42313
|
+
skillMd: input.skillMd
|
|
42314
|
+
});
|
|
42315
|
+
if (!validation.ok) {
|
|
42316
|
+
throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF1A
|
|
42317
|
+
${formatSkillValidationIssues(validation)}`);
|
|
42318
|
+
}
|
|
42319
|
+
const records = listDepartmentSkills(departmentName);
|
|
42320
|
+
if (records.some((record) => record.skillName === input.skillName && record.status !== "dropped")) {
|
|
42321
|
+
return null;
|
|
42322
|
+
}
|
|
42323
|
+
const now = Date.now();
|
|
42324
|
+
const skill = {
|
|
42325
|
+
id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
|
|
42326
|
+
departmentName,
|
|
42327
|
+
skillName: input.skillName,
|
|
42328
|
+
description: input.description,
|
|
42329
|
+
skillMd: input.skillMd,
|
|
42330
|
+
status: "pending",
|
|
42331
|
+
createdByMailboxId: input.createdByMailboxId,
|
|
42332
|
+
createdAt: now,
|
|
42333
|
+
updatedAt: now
|
|
42334
|
+
};
|
|
42335
|
+
records.push(skill);
|
|
42336
|
+
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
42337
|
+
return skill;
|
|
42338
|
+
};
|
|
42339
|
+
var keepDepartmentSkill = (departmentName, id) => {
|
|
42340
|
+
const records = listDepartmentSkills(departmentName);
|
|
42341
|
+
const idx = records.findIndex((record) => record.id === id);
|
|
42342
|
+
if (idx < 0) return null;
|
|
42343
|
+
records[idx] = { ...records[idx], status: "active", updatedAt: Date.now() };
|
|
42344
|
+
const skillDir = departmentSkillDir(departmentName, records[idx].skillName);
|
|
42345
|
+
const validation = validateSkillDocument({
|
|
42346
|
+
skillName: records[idx].skillName,
|
|
42347
|
+
description: records[idx].description,
|
|
42348
|
+
skillMd: records[idx].skillMd,
|
|
42349
|
+
baseDir: skillDir
|
|
42350
|
+
});
|
|
42351
|
+
if (!validation.ok) {
|
|
42352
|
+
throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u4FDD\u7559\uFF1A
|
|
42353
|
+
${formatSkillValidationIssues(validation)}`);
|
|
42354
|
+
}
|
|
42355
|
+
ensureDir(skillDir);
|
|
42356
|
+
(0, import_node_fs4.writeFileSync)(import_node_path13.default.join(skillDir, "SKILL.md"), records[idx].skillMd, "utf-8");
|
|
42357
|
+
const smokeTest = smokeTestSkillDirectory(skillDir, { expectedName: records[idx].skillName });
|
|
42358
|
+
if (!smokeTest.ok) {
|
|
42359
|
+
throw new Error(`[departmentSkill] Skill smoke test \u5931\u8D25\uFF1A
|
|
42360
|
+
${formatSkillValidationIssues(smokeTest)}`);
|
|
42361
|
+
}
|
|
42362
|
+
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
42363
|
+
return records[idx];
|
|
41817
42364
|
};
|
|
41818
|
-
var
|
|
41819
|
-
const
|
|
41820
|
-
const
|
|
41821
|
-
|
|
41822
|
-
|
|
41823
|
-
|
|
41824
|
-
|
|
41825
|
-
|
|
41826
|
-
|
|
41827
|
-
|
|
41828
|
-
|
|
41829
|
-
|
|
42365
|
+
var dropDepartmentSkill = (departmentName, id) => {
|
|
42366
|
+
const records = listDepartmentSkills(departmentName);
|
|
42367
|
+
const idx = records.findIndex((record) => record.id === id);
|
|
42368
|
+
if (idx < 0) return null;
|
|
42369
|
+
records[idx] = { ...records[idx], status: "dropped", updatedAt: Date.now() };
|
|
42370
|
+
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
42371
|
+
return records[idx];
|
|
42372
|
+
};
|
|
42373
|
+
var createDepartmentProposal = (input) => {
|
|
42374
|
+
const records = readJsonArray(proposalsPath());
|
|
42375
|
+
const proposal = {
|
|
42376
|
+
...input,
|
|
42377
|
+
id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
|
|
42378
|
+
status: "pending",
|
|
42379
|
+
createdAt: Date.now()
|
|
42380
|
+
};
|
|
42381
|
+
records.push(proposal);
|
|
42382
|
+
writeJsonArray(proposalsPath(), records);
|
|
42383
|
+
return proposal;
|
|
42384
|
+
};
|
|
42385
|
+
var buildDepartmentLearningContext = (departmentName) => {
|
|
42386
|
+
if (!departmentName) return "";
|
|
42387
|
+
const memories = listDepartmentMemories(departmentName);
|
|
42388
|
+
const activeSkills = listDepartmentSkills(departmentName).filter((skill) => skill.status === "active");
|
|
42389
|
+
if (memories.length === 0 && activeSkills.length === 0) return "";
|
|
42390
|
+
const memoryLines = memories.map(
|
|
42391
|
+
(memory) => ` - [id=${memory.id}] ${memory.title}
|
|
42392
|
+
${memory.content.replace(/\n/g, "\n ")}`
|
|
42393
|
+
).join("\n");
|
|
42394
|
+
const skillLines = activeSkills.map(
|
|
42395
|
+
(skill) => ` - ${skill.skillName}: ${skill.description}`
|
|
42396
|
+
).join("\n");
|
|
42397
|
+
return [
|
|
42398
|
+
`<department-learning-context department="${departmentName}">`,
|
|
42399
|
+
memories.length > 0 ? `Department memories:
|
|
42400
|
+
${memoryLines}` : "",
|
|
42401
|
+
activeSkills.length > 0 ? `Department skills:
|
|
42402
|
+
${skillLines}` : "",
|
|
42403
|
+
`</department-learning-context>`
|
|
42404
|
+
].filter(Boolean).join("\n");
|
|
41830
42405
|
};
|
|
41831
42406
|
|
|
41832
42407
|
// src/skill/SkillRegistry.ts
|
|
41833
42408
|
var getProjectSkillsPath = () => {
|
|
41834
42409
|
const projectRoot = findProjectRoot();
|
|
41835
|
-
return projectRoot ? (0,
|
|
42410
|
+
return projectRoot ? (0, import_path18.join)(projectRoot, "skills") : null;
|
|
41836
42411
|
};
|
|
41837
42412
|
var getSkillPaths = () => {
|
|
41838
42413
|
const paths = [];
|
|
41839
42414
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
41840
42415
|
const pushPath = (candidate) => {
|
|
41841
|
-
if (!(0,
|
|
41842
|
-
const normalized =
|
|
42416
|
+
if (!(0, import_fs13.existsSync)(candidate) || !(0, import_fs13.statSync)(candidate).isDirectory()) return;
|
|
42417
|
+
const normalized = import_path18.default.resolve(candidate);
|
|
41843
42418
|
if (seenPaths.has(normalized)) return;
|
|
41844
42419
|
seenPaths.add(normalized);
|
|
41845
42420
|
paths.push(normalized);
|
|
@@ -41848,16 +42423,16 @@ var getSkillPaths = () => {
|
|
|
41848
42423
|
if (projectSkillsPath) {
|
|
41849
42424
|
pushPath(projectSkillsPath);
|
|
41850
42425
|
}
|
|
41851
|
-
pushPath((0,
|
|
41852
|
-
pushPath((0,
|
|
42426
|
+
pushPath((0, import_path18.join)((0, import_os3.homedir)(), ".duclaw", "skills"));
|
|
42427
|
+
pushPath((0, import_path18.join)((0, import_os3.homedir)(), ".agents", "skills"));
|
|
41853
42428
|
return paths;
|
|
41854
42429
|
};
|
|
41855
42430
|
var getDirectories = (dirPath) => {
|
|
41856
|
-
return (0,
|
|
42431
|
+
return (0, import_fs13.readdirSync)(dirPath).filter((name) => (0, import_fs13.statSync)((0, import_path18.join)(dirPath, name)).isDirectory());
|
|
41857
42432
|
};
|
|
41858
|
-
var parseSkill = (mdPath, skillDir) => {
|
|
41859
|
-
if (!(0,
|
|
41860
|
-
const raw2 = (0,
|
|
42433
|
+
var parseSkill = (mdPath, skillDir, options = {}) => {
|
|
42434
|
+
if (!(0, import_fs13.existsSync)(mdPath)) return null;
|
|
42435
|
+
const raw2 = (0, import_fs13.readFileSync)(mdPath, "utf-8");
|
|
41861
42436
|
let name = "";
|
|
41862
42437
|
let description = "";
|
|
41863
42438
|
let body = raw2;
|
|
@@ -41873,18 +42448,22 @@ var parseSkill = (mdPath, skillDir) => {
|
|
|
41873
42448
|
}
|
|
41874
42449
|
}
|
|
41875
42450
|
if (!name) {
|
|
41876
|
-
name =
|
|
42451
|
+
name = import_path18.default.basename(import_path18.default.dirname(mdPath));
|
|
41877
42452
|
}
|
|
41878
42453
|
if (!description && body) {
|
|
41879
42454
|
description = body.split("\n")[0].replace(/^#+\s*/, "").trim();
|
|
41880
42455
|
}
|
|
42456
|
+
const normalizedSkillDir = import_path18.default.resolve(skillDir);
|
|
41881
42457
|
const projectSkillsPath = getProjectSkillsPath();
|
|
41882
|
-
const
|
|
41883
|
-
const
|
|
42458
|
+
const isProjectSkill = projectSkillsPath ? normalizedSkillDir === import_path18.default.resolve(projectSkillsPath, import_path18.default.basename(normalizedSkillDir)) && normalizedSkillDir.startsWith(import_path18.default.resolve(projectSkillsPath) + import_path18.default.sep) : false;
|
|
42459
|
+
const scope = options.scope ?? (isProjectSkill ? "project" : "global");
|
|
42460
|
+
const deletable = options.deletable ?? (scope === "project" && isProjectSkill);
|
|
41884
42461
|
return {
|
|
41885
42462
|
name,
|
|
41886
42463
|
description,
|
|
41887
42464
|
baseDir: skillDir,
|
|
42465
|
+
scope,
|
|
42466
|
+
departmentName: options.departmentName,
|
|
41888
42467
|
deletable,
|
|
41889
42468
|
getDetail: () => {
|
|
41890
42469
|
let content = body.replace(/<Skill目录>/g, skillDir).replace(/\$\{CLAUDE_SKILL_DIR\}/g, skillDir);
|
|
@@ -41894,16 +42473,35 @@ ${content}`;
|
|
|
41894
42473
|
}
|
|
41895
42474
|
};
|
|
41896
42475
|
};
|
|
42476
|
+
var loadDepartmentSkills = (seen) => {
|
|
42477
|
+
const skills = [];
|
|
42478
|
+
for (const department of listDepartments()) {
|
|
42479
|
+
const activeSkills = listDepartmentSkills(department.name).filter((skill) => skill.status === "active");
|
|
42480
|
+
for (const departmentSkill of activeSkills) {
|
|
42481
|
+
if (seen.has(departmentSkill.skillName)) continue;
|
|
42482
|
+
const skillDir = (0, import_path18.join)(getDepartmentWorkSpaceDir(department.name), "skills", departmentSkill.skillName);
|
|
42483
|
+
const skill = parseSkill((0, import_path18.join)(skillDir, "SKILL.md"), skillDir, {
|
|
42484
|
+
scope: "department",
|
|
42485
|
+
departmentName: department.name,
|
|
42486
|
+
deletable: false
|
|
42487
|
+
});
|
|
42488
|
+
if (skill) {
|
|
42489
|
+
seen.add(skill.name);
|
|
42490
|
+
skills.push(skill);
|
|
42491
|
+
}
|
|
42492
|
+
}
|
|
42493
|
+
}
|
|
42494
|
+
return skills;
|
|
42495
|
+
};
|
|
41897
42496
|
var loadSkill = () => {
|
|
41898
42497
|
const skillPaths = getSkillPaths();
|
|
41899
|
-
if (skillPaths.length === 0) return [];
|
|
41900
42498
|
const seen = /* @__PURE__ */ new Set();
|
|
41901
42499
|
const skills = [];
|
|
41902
42500
|
for (const skillPath of skillPaths) {
|
|
41903
42501
|
const dirs = getDirectories(skillPath);
|
|
41904
42502
|
for (const skillName of dirs) {
|
|
41905
|
-
const eachSkillPath = (0,
|
|
41906
|
-
const eachSkillMdPath = (0,
|
|
42503
|
+
const eachSkillPath = (0, import_path18.join)(skillPath, skillName);
|
|
42504
|
+
const eachSkillMdPath = (0, import_path18.join)(eachSkillPath, "SKILL.md");
|
|
41907
42505
|
const skill = parseSkill(eachSkillMdPath, eachSkillPath);
|
|
41908
42506
|
if (skill && !seen.has(skill.name)) {
|
|
41909
42507
|
seen.add(skill.name);
|
|
@@ -41911,13 +42509,14 @@ var loadSkill = () => {
|
|
|
41911
42509
|
}
|
|
41912
42510
|
}
|
|
41913
42511
|
}
|
|
42512
|
+
skills.push(...loadDepartmentSkills(seen));
|
|
41914
42513
|
return skills;
|
|
41915
42514
|
};
|
|
41916
42515
|
var deleteSkill = (name) => {
|
|
41917
42516
|
const skills = loadSkill();
|
|
41918
42517
|
const skill = skills.find((s) => s.name === name);
|
|
41919
42518
|
if (!skill || !skill.deletable) return false;
|
|
41920
|
-
(0,
|
|
42519
|
+
(0, import_fs13.rmSync)(skill.baseDir, { recursive: true, force: false });
|
|
41921
42520
|
return true;
|
|
41922
42521
|
};
|
|
41923
42522
|
var SkillRegistry_default = loadSkill;
|
|
@@ -42204,10 +42803,10 @@ var goalDelete = {
|
|
|
42204
42803
|
};
|
|
42205
42804
|
|
|
42206
42805
|
// src/tools/tools/department/DepartmentCreate.ts
|
|
42207
|
-
var
|
|
42806
|
+
var import_node_crypto9 = require("node:crypto");
|
|
42208
42807
|
|
|
42209
42808
|
// src/department/mailbox/mailbox.ts
|
|
42210
|
-
var
|
|
42809
|
+
var import_node_crypto8 = require("node:crypto");
|
|
42211
42810
|
|
|
42212
42811
|
// src/agent/interruptRegistry.ts
|
|
42213
42812
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -42258,7 +42857,7 @@ var drainInterrupts = (userId) => {
|
|
|
42258
42857
|
};
|
|
42259
42858
|
|
|
42260
42859
|
// src/agent/events.ts
|
|
42261
|
-
var
|
|
42860
|
+
var import_node_crypto5 = require("node:crypto");
|
|
42262
42861
|
var rowToEvent = (row) => ({
|
|
42263
42862
|
id: row.id,
|
|
42264
42863
|
userId: row.userId,
|
|
@@ -42275,7 +42874,7 @@ var rowToEvent = (row) => ({
|
|
|
42275
42874
|
var recordAgentEvent = (input) => {
|
|
42276
42875
|
const db3 = createSqliteDB();
|
|
42277
42876
|
const now = Date.now();
|
|
42278
|
-
const id = `evt_${(0,
|
|
42877
|
+
const id = `evt_${(0, import_node_crypto5.randomUUID)().slice(0, 12)}`;
|
|
42279
42878
|
const payloadJson = JSON.stringify(input.payload);
|
|
42280
42879
|
db3.prepare(`
|
|
42281
42880
|
INSERT INTO agent_events (
|
|
@@ -42406,7 +43005,7 @@ ${ceoFollowupInstruction}
|
|
|
42406
43005
|
};
|
|
42407
43006
|
|
|
42408
43007
|
// src/department/mailbox/events.ts
|
|
42409
|
-
var
|
|
43008
|
+
var import_node_crypto6 = require("node:crypto");
|
|
42410
43009
|
var parseDetail = (detailJson) => {
|
|
42411
43010
|
if (!detailJson) return void 0;
|
|
42412
43011
|
try {
|
|
@@ -42447,7 +43046,7 @@ var mapMailboxEventRow = (row) => {
|
|
|
42447
43046
|
var recordMailboxEvent = (input) => {
|
|
42448
43047
|
const db3 = createSqliteDB();
|
|
42449
43048
|
const event = {
|
|
42450
|
-
id: (0,
|
|
43049
|
+
id: (0, import_node_crypto6.randomUUID)().slice(0, 12),
|
|
42451
43050
|
messageId: input.messageId,
|
|
42452
43051
|
mailboxId: input.mailboxId,
|
|
42453
43052
|
actorMailboxId: input.actorMailboxId,
|
|
@@ -42516,222 +43115,21 @@ var listMailboxEvents = (params) => {
|
|
|
42516
43115
|
m.send_time as sendTime,
|
|
42517
43116
|
m.status as status,
|
|
42518
43117
|
m.origin_user_id as originUserId,
|
|
42519
|
-
m.origin_platform as originPlatform,
|
|
42520
|
-
m.thread_id as threadId,
|
|
42521
|
-
m.parent_message_id as parentMessageId
|
|
42522
|
-
FROM mailbox_events e
|
|
42523
|
-
LEFT JOIN mailbox m ON m.id = e.message_id
|
|
42524
|
-
WHERE e.mailbox_id = ? OR e.actor_mailbox_id = ? OR e.counterpart_mailbox_id = ?
|
|
42525
|
-
ORDER BY e.created_at DESC
|
|
42526
|
-
LIMIT ?`
|
|
42527
|
-
);
|
|
42528
|
-
const rows = stmt.all(params.mailboxId, params.mailboxId, params.mailboxId, limit);
|
|
42529
|
-
return rows.map(mapMailboxEventRow);
|
|
42530
|
-
};
|
|
42531
|
-
|
|
42532
|
-
// src/department/DepartmentMember.ts
|
|
42533
|
-
var import_fs13 = require("fs");
|
|
42534
|
-
var import_path18 = __toESM(require("path"));
|
|
42535
|
-
|
|
42536
|
-
// src/department/Department.ts
|
|
42537
|
-
var import_path17 = __toESM(require("path"));
|
|
42538
|
-
var import_fs12 = require("fs");
|
|
42539
|
-
var legacyMigrationChecked = false;
|
|
42540
|
-
var getDepartmentBaseDir = () => {
|
|
42541
|
-
return import_path17.default.join(getDuclawHomeDir(), "department");
|
|
42542
|
-
};
|
|
42543
|
-
var getLegacyTeamBaseDir = () => {
|
|
42544
|
-
return import_path17.default.join(getDuclawHomeDir(), "team");
|
|
42545
|
-
};
|
|
42546
|
-
var getDepartmentWorkSpaceDir = (departmentName) => {
|
|
42547
|
-
return import_path17.default.join(getDepartmentBaseDir(), "workspace", departmentName);
|
|
42548
|
-
};
|
|
42549
|
-
var getLegacyTeamWorkSpaceDir = (teamName) => {
|
|
42550
|
-
return import_path17.default.join(getLegacyTeamBaseDir(), "workspace", teamName);
|
|
42551
|
-
};
|
|
42552
|
-
var getDepartmentJsonPath = (departmentName) => {
|
|
42553
|
-
return import_path17.default.join(getDepartmentWorkSpaceDir(departmentName), "department.json");
|
|
42554
|
-
};
|
|
42555
|
-
var getLegacyTeamJsonPath = (teamName) => {
|
|
42556
|
-
return import_path17.default.join(getLegacyTeamWorkSpaceDir(teamName), "team.json");
|
|
42557
|
-
};
|
|
42558
|
-
var mapLegacyRole = (role) => {
|
|
42559
|
-
return role === "team_manager" ? "department_head" : "executor";
|
|
42560
|
-
};
|
|
42561
|
-
var mapLegacyDepartment = (legacy) => {
|
|
42562
|
-
const departmentMembers = (legacy.teamMembers ?? []).map((member) => ({
|
|
42563
|
-
id: member.id,
|
|
42564
|
-
name: member.name,
|
|
42565
|
-
departmentId: legacy.id,
|
|
42566
|
-
mailBoxId: member.mailBoxId,
|
|
42567
|
-
workspaceId: member.workspaceId,
|
|
42568
|
-
role: mapLegacyRole(member.role),
|
|
42569
|
-
focusOn: member.focusOn
|
|
42570
|
-
}));
|
|
42571
|
-
const headMemberId = legacy.managerMemberId ?? departmentMembers.find((member) => member.role === "department_head")?.id;
|
|
42572
|
-
return {
|
|
42573
|
-
id: legacy.id,
|
|
42574
|
-
name: legacy.name,
|
|
42575
|
-
sourceGoalId: legacy.goalId,
|
|
42576
|
-
charter: legacy.goalId ? `Legacy department migrated from team goal ${legacy.goalId}.` : "Legacy department migrated from team data.",
|
|
42577
|
-
workpath: legacy.workpath,
|
|
42578
|
-
headMemberId,
|
|
42579
|
-
departmentMembers
|
|
42580
|
-
};
|
|
42581
|
-
};
|
|
42582
|
-
var migrateLegacyTeamsToDepartments = () => {
|
|
42583
|
-
if (legacyMigrationChecked) return;
|
|
42584
|
-
legacyMigrationChecked = true;
|
|
42585
|
-
const legacyWorkspaceDir = import_path17.default.join(getLegacyTeamBaseDir(), "workspace");
|
|
42586
|
-
if (!(0, import_fs12.existsSync)(legacyWorkspaceDir)) return;
|
|
42587
|
-
for (const legacyName of (0, import_fs12.readdirSync)(legacyWorkspaceDir)) {
|
|
42588
|
-
const legacyJsonPath = getLegacyTeamJsonPath(legacyName);
|
|
42589
|
-
if (!(0, import_fs12.existsSync)(legacyJsonPath)) continue;
|
|
42590
|
-
const departmentJsonPath = getDepartmentJsonPath(legacyName);
|
|
42591
|
-
if ((0, import_fs12.existsSync)(departmentJsonPath)) continue;
|
|
42592
|
-
try {
|
|
42593
|
-
const legacy = JSON.parse((0, import_fs12.readFileSync)(legacyJsonPath, "utf-8"));
|
|
42594
|
-
const department = mapLegacyDepartment(legacy);
|
|
42595
|
-
(0, import_fs12.mkdirSync)(getDepartmentWorkSpaceDir(department.name), { recursive: true });
|
|
42596
|
-
(0, import_fs12.writeFileSync)(departmentJsonPath, JSON.stringify(department, null, " "), "utf-8");
|
|
42597
|
-
} catch (err) {
|
|
42598
|
-
console.warn(`[department] Failed to migrate legacy team ${legacyName}: ${err.message}`);
|
|
42599
|
-
}
|
|
42600
|
-
}
|
|
42601
|
-
};
|
|
42602
|
-
var createDepartment = (departmentDefinition) => {
|
|
42603
|
-
if (!departmentDefinition) throw new Error(`[createDepartment] departmentDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
42604
|
-
const departmentPath = getDepartmentWorkSpaceDir(departmentDefinition.name);
|
|
42605
|
-
(0, import_fs12.mkdirSync)(departmentPath, { recursive: true });
|
|
42606
|
-
(0, import_fs12.writeFileSync)(getDepartmentJsonPath(departmentDefinition.name), JSON.stringify(departmentDefinition, null, " "), "utf-8");
|
|
42607
|
-
return departmentDefinition;
|
|
42608
|
-
};
|
|
42609
|
-
var getDepartment = (name) => {
|
|
42610
|
-
migrateLegacyTeamsToDepartments();
|
|
42611
|
-
const departmentJsonPath = getDepartmentJsonPath(name);
|
|
42612
|
-
if (!(0, import_fs12.existsSync)(departmentJsonPath)) return null;
|
|
42613
|
-
const text2 = (0, import_fs12.readFileSync)(departmentJsonPath, "utf-8");
|
|
42614
|
-
return JSON.parse(text2);
|
|
42615
|
-
};
|
|
42616
|
-
var listDepartments = () => {
|
|
42617
|
-
migrateLegacyTeamsToDepartments();
|
|
42618
|
-
const workspaceDir = import_path17.default.join(getDepartmentBaseDir(), "workspace");
|
|
42619
|
-
if (!(0, import_fs12.existsSync)(workspaceDir)) return [];
|
|
42620
|
-
const departments = [];
|
|
42621
|
-
for (const departmentName of (0, import_fs12.readdirSync)(workspaceDir)) {
|
|
42622
|
-
const department = getDepartment(departmentName);
|
|
42623
|
-
if (department) departments.push(department);
|
|
42624
|
-
}
|
|
42625
|
-
return departments;
|
|
42626
|
-
};
|
|
42627
|
-
var getDepartmentById = (id) => {
|
|
42628
|
-
const department = listDepartments().find((item) => item.id === id);
|
|
42629
|
-
return department ?? null;
|
|
42630
|
-
};
|
|
42631
|
-
var deleteDepartment = (name) => {
|
|
42632
|
-
if (!(0, import_fs12.existsSync)(getDepartmentJsonPath(name))) {
|
|
42633
|
-
throw new Error(`[deleteDepartment] \u4E0D\u5B58\u5728\u5BF9\u5E94\u7684\u90E8\u95E8 ${name} \u7684 department.json \u6587\u4EF6`);
|
|
42634
|
-
}
|
|
42635
|
-
(0, import_fs12.rmSync)(getDepartmentJsonPath(name));
|
|
42636
|
-
};
|
|
42637
|
-
|
|
42638
|
-
// src/department/workspace/workspace.ts
|
|
42639
|
-
var db = createSqliteDB();
|
|
42640
|
-
var getWorkspaceId = (departmentName, memberName) => {
|
|
42641
|
-
return `${departmentName}::${memberName}`;
|
|
42642
|
-
};
|
|
42643
|
-
var createWorkspace = (workspace) => {
|
|
42644
|
-
const stmt = db.prepare(`INSERT INTO workspace (id, team_name, teammate_name, team_workpath) VALUES (?, ?, ?, ?) `);
|
|
42645
|
-
stmt.run(
|
|
42646
|
-
workspace.id,
|
|
42647
|
-
workspace.departmentName,
|
|
42648
|
-
workspace.memberName,
|
|
42649
|
-
workspace.departmentWorkPath
|
|
42650
|
-
);
|
|
42651
|
-
};
|
|
42652
|
-
var deleteWorkspace = (workspaceId) => {
|
|
42653
|
-
const stmt = db.prepare(`delete from workspace where id = ?`);
|
|
42654
|
-
stmt.run(workspaceId);
|
|
42655
|
-
};
|
|
42656
|
-
|
|
42657
|
-
// src/department/DepartmentMember.ts
|
|
42658
|
-
var createDepartmentMember = (departmentMemberDefinition) => {
|
|
42659
|
-
if (!departmentMemberDefinition) throw new Error(`[createDepartmentMember] departmentMemberDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
|
|
42660
|
-
const { name, departmentId, workspaceId } = departmentMemberDefinition;
|
|
42661
|
-
const department = getDepartmentById(departmentId);
|
|
42662
|
-
if (!department) throw new Error(`[createDepartmentMember] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentId}`);
|
|
42663
|
-
const memberPath = import_path18.default.join(getDepartmentWorkSpaceDir(department.name), name);
|
|
42664
|
-
(0, import_fs13.mkdirSync)(memberPath, { recursive: true });
|
|
42665
|
-
const workspace = {
|
|
42666
|
-
id: getWorkspaceId(department.name, departmentMemberDefinition.name),
|
|
42667
|
-
departmentName: department.name,
|
|
42668
|
-
memberName: name,
|
|
42669
|
-
departmentWorkPath: memberPath
|
|
42670
|
-
};
|
|
42671
|
-
createWorkspace(workspace);
|
|
42672
|
-
if (!department.departmentMembers) {
|
|
42673
|
-
department.departmentMembers = [];
|
|
42674
|
-
}
|
|
42675
|
-
if (department.departmentMembers.some((member) => member.id === departmentMemberDefinition.id || member.name === departmentMemberDefinition.name)) {
|
|
42676
|
-
throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728\u540C\u540D\u6216\u540C id \u6210\u5458: ${departmentMemberDefinition.name}/${departmentMemberDefinition.id}`);
|
|
42677
|
-
}
|
|
42678
|
-
if (departmentMemberDefinition.role === "department_head") {
|
|
42679
|
-
const existingHead = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
|
|
42680
|
-
if (existingHead) {
|
|
42681
|
-
throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728 Department Head: ${existingHead.name}`);
|
|
42682
|
-
}
|
|
42683
|
-
department.headMemberId = departmentMemberDefinition.id;
|
|
42684
|
-
}
|
|
42685
|
-
department.departmentMembers.push(departmentMemberDefinition);
|
|
42686
|
-
(0, import_fs13.writeFileSync)(getDepartmentJsonPath(department.name), JSON.stringify(department, null, " "), "utf-8");
|
|
42687
|
-
return departmentMemberDefinition;
|
|
42688
|
-
};
|
|
42689
|
-
var listDepartmentMembers = (departmentName) => {
|
|
42690
|
-
const departmentJsonPath = getDepartmentJsonPath(departmentName);
|
|
42691
|
-
if (!(0, import_fs13.existsSync)(departmentJsonPath)) return [];
|
|
42692
|
-
const text2 = (0, import_fs13.readFileSync)(departmentJsonPath, "utf-8");
|
|
42693
|
-
const department = JSON.parse(text2);
|
|
42694
|
-
if (!department) throw new Error(`[listDepartmentMembers] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
|
|
42695
|
-
return department.departmentMembers ?? [];
|
|
42696
|
-
};
|
|
42697
|
-
var getDepartmentMember = (departmentName, departmentMemberId) => {
|
|
42698
|
-
const members = listDepartmentMembers(departmentName);
|
|
42699
|
-
return members.find((member) => member.id === departmentMemberId) || null;
|
|
42700
|
-
};
|
|
42701
|
-
var getDepartmentMemberByName = (departmentName, departmentMemberName) => {
|
|
42702
|
-
const members = listDepartmentMembers(departmentName);
|
|
42703
|
-
return members.find((member) => member.name === departmentMemberName) || null;
|
|
42704
|
-
};
|
|
42705
|
-
var getDepartmentMemberByMailboxId = (mailboxId) => {
|
|
42706
|
-
const [departmentName, memberName] = mailboxId.split("::");
|
|
42707
|
-
if (!departmentName || !memberName) return null;
|
|
42708
|
-
return getDepartmentMemberByName(departmentName, memberName);
|
|
42709
|
-
};
|
|
42710
|
-
var getDepartmentMemberById = (memberId) => {
|
|
42711
|
-
for (const department of listDepartments()) {
|
|
42712
|
-
const targetMember = department.departmentMembers.find((member) => member.id === memberId);
|
|
42713
|
-
if (targetMember) return targetMember;
|
|
42714
|
-
}
|
|
42715
|
-
return null;
|
|
42716
|
-
};
|
|
42717
|
-
var deleteDepartmentMemberById = (departmentName, memberId) => {
|
|
42718
|
-
const department = getDepartment(departmentName);
|
|
42719
|
-
if (!department) throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
|
|
42720
|
-
const memberIdx = department.departmentMembers.findIndex((member) => member.id === memberId);
|
|
42721
|
-
if (memberIdx === -1) {
|
|
42722
|
-
throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94 id \u7684 department member: ${memberId}`);
|
|
42723
|
-
}
|
|
42724
|
-
const workspaceId = department.departmentMembers[memberIdx].workspaceId;
|
|
42725
|
-
if (department.headMemberId === memberId) {
|
|
42726
|
-
delete department.headMemberId;
|
|
42727
|
-
}
|
|
42728
|
-
department.departmentMembers = department.departmentMembers.filter((_, index) => index !== memberIdx);
|
|
42729
|
-
(0, import_fs13.writeFileSync)(getDepartmentJsonPath(departmentName), JSON.stringify(department, null, " "), "utf-8");
|
|
42730
|
-
deleteWorkspace(workspaceId);
|
|
43118
|
+
m.origin_platform as originPlatform,
|
|
43119
|
+
m.thread_id as threadId,
|
|
43120
|
+
m.parent_message_id as parentMessageId
|
|
43121
|
+
FROM mailbox_events e
|
|
43122
|
+
LEFT JOIN mailbox m ON m.id = e.message_id
|
|
43123
|
+
WHERE e.mailbox_id = ? OR e.actor_mailbox_id = ? OR e.counterpart_mailbox_id = ?
|
|
43124
|
+
ORDER BY e.created_at DESC
|
|
43125
|
+
LIMIT ?`
|
|
43126
|
+
);
|
|
43127
|
+
const rows = stmt.all(params.mailboxId, params.mailboxId, params.mailboxId, limit);
|
|
43128
|
+
return rows.map(mapMailboxEventRow);
|
|
42731
43129
|
};
|
|
42732
43130
|
|
|
42733
43131
|
// src/department/mailbox/ceoFollowup.ts
|
|
42734
|
-
var
|
|
43132
|
+
var import_node_crypto7 = require("node:crypto");
|
|
42735
43133
|
var rowToFollowup = (row) => ({
|
|
42736
43134
|
id: row.id,
|
|
42737
43135
|
sourceMessageId: row.sourceMessageId,
|
|
@@ -42778,7 +43176,7 @@ var enqueueCeoFollowupFromMailbox = (message) => {
|
|
|
42778
43176
|
if (!message.originUserId || !message.originPlatform) return null;
|
|
42779
43177
|
const db3 = createSqliteDB();
|
|
42780
43178
|
const now = Date.now();
|
|
42781
|
-
const id = `cfu_${(0,
|
|
43179
|
+
const id = `cfu_${(0, import_node_crypto7.randomUUID)().slice(0, 12)}`;
|
|
42782
43180
|
db3.prepare(`
|
|
42783
43181
|
INSERT INTO ceo_followups (
|
|
42784
43182
|
id,
|
|
@@ -43108,7 +43506,7 @@ var recordMailboxReceivedAgentEvent = (msg) => {
|
|
|
43108
43506
|
};
|
|
43109
43507
|
var sendMessage2 = (fromMailboxId, toMailboxId, content, options) => {
|
|
43110
43508
|
const db3 = createSqliteDB();
|
|
43111
|
-
const id = (0,
|
|
43509
|
+
const id = (0, import_node_crypto8.randomUUID)().slice(0, 8);
|
|
43112
43510
|
const threadId = options?.threadId || id;
|
|
43113
43511
|
const workItemContext = resolveWorkItemContext(fromMailboxId, toMailboxId, id, options);
|
|
43114
43512
|
const stmt = db3.prepare(`insert into mailbox (
|
|
@@ -43251,7 +43649,7 @@ var departmentCreate = {
|
|
|
43251
43649
|
return `[departmentCreate] \u4E0D\u5B58\u5728 id=${sourceGoalId} \u7684\u76EE\u6807`;
|
|
43252
43650
|
}
|
|
43253
43651
|
let departmentDefinition = {
|
|
43254
|
-
id: (0,
|
|
43652
|
+
id: (0, import_node_crypto9.randomUUID)().slice(0, 8),
|
|
43255
43653
|
name,
|
|
43256
43654
|
charter,
|
|
43257
43655
|
sourceGoalId,
|
|
@@ -43518,7 +43916,7 @@ var departmentList = {
|
|
|
43518
43916
|
};
|
|
43519
43917
|
|
|
43520
43918
|
// src/tools/tools/department/DepartmentMemberCreate.ts
|
|
43521
|
-
var
|
|
43919
|
+
var import_node_crypto10 = require("node:crypto");
|
|
43522
43920
|
var DESCRIPTION24 = `
|
|
43523
43921
|
\u521B\u5EFA\u90E8\u95E8\u6210\u5458\u3002
|
|
43524
43922
|
|
|
@@ -43584,7 +43982,7 @@ var departmentMemberCreate = {
|
|
|
43584
43982
|
}
|
|
43585
43983
|
}
|
|
43586
43984
|
let departmentMember = {
|
|
43587
|
-
id: (0,
|
|
43985
|
+
id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
|
|
43588
43986
|
name,
|
|
43589
43987
|
departmentId: department.id,
|
|
43590
43988
|
mailBoxId: getMailBoxId(department.name, name),
|
|
@@ -43714,437 +44112,63 @@ var checkDepartmentReplies = {
|
|
|
43714
44112
|
type: `object`,
|
|
43715
44113
|
properties: {
|
|
43716
44114
|
from_member: {
|
|
43717
|
-
type: `string`,
|
|
43718
|
-
description: `\u53EF\u9009\uFF1A\u53EA\u67E5\u770B\u6307\u5B9A mailboxId \u7684\u56DE\u4FE1\uFF0C\u4E0D\u586B\u5219\u67E5\u770B\u6240\u6709\u56DE\u4FE1`
|
|
43719
|
-
}
|
|
43720
|
-
},
|
|
43721
|
-
required: []
|
|
43722
|
-
},
|
|
43723
|
-
async execute(input) {
|
|
43724
|
-
const db3 = createSqliteDB();
|
|
43725
|
-
const fromMember = input.from_member;
|
|
43726
|
-
let msgs;
|
|
43727
|
-
if (fromMember) {
|
|
43728
|
-
const stmt = db3.prepare(
|
|
43729
|
-
`SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
|
|
43730
|
-
FROM mailbox
|
|
43731
|
-
WHERE to_mailbox_id = 'manager' AND from_mailbox_id = ? AND status in ('pending', 'processing')
|
|
43732
|
-
ORDER BY send_time ASC`
|
|
43733
|
-
);
|
|
43734
|
-
msgs = stmt.all(fromMember);
|
|
43735
|
-
} else {
|
|
43736
|
-
const stmt = db3.prepare(
|
|
43737
|
-
`SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
|
|
43738
|
-
FROM mailbox
|
|
43739
|
-
WHERE to_mailbox_id = 'manager' AND status in ('pending', 'processing')
|
|
43740
|
-
ORDER BY send_time ASC`
|
|
43741
|
-
);
|
|
43742
|
-
msgs = stmt.all();
|
|
43743
|
-
}
|
|
43744
|
-
if (msgs.length === 0) {
|
|
43745
|
-
return [
|
|
43746
|
-
`[checkDepartmentReplies] \u56E2\u961F\u8FD9\u4F1A\u513F\u8FD8\u6CA1\u56DE\u4FE1\uFF0C\u591A\u534A\u662F\u8D1F\u8D23\u4EBA\u4ECD\u5728\u5904\u7406\uFF0C\u8FC7\u4F1A\u513F\u518D\u6765\u770B\u3002`,
|
|
43747
|
-
``,
|
|
43748
|
-
`\u51E0\u53E5\u63D0\u9192\uFF1A`,
|
|
43749
|
-
`- \u8FD9\u4E0D\u4EE3\u8868\u8981\u4F60\u4EB2\u81EA\u4E0B\u573A read/bash/\u6D4B\u8BD5/\u6539\u4EE3\u7801\u2014\u2014\u56E2\u961F\u6CA1\u56DE\uFF0C\u4E0D\u662F\u4F60\u8BE5\u63A5\u624B\u7684\u4FE1\u53F7\u3002`,
|
|
43750
|
-
`- \u8001\u677F\u8981\u662F\u6B63\u7B49\u7740\uFF0C\u5148\u7528 send_message \u5982\u5B9E\u8DDF\u4ED6\u8BF4\u4E00\u53E5\u201C\u8D1F\u8D23\u4EBA\u8FD8\u5728\u5904\u7406\uFF0C\u6211\u62FF\u5230\u7ED3\u679C\u5C31\u540C\u6B65\u201D\u3002`,
|
|
43751
|
-
`- \u5982\u679C\u4EFB\u52A1\u521A\u6D3E\u51FA\u53BB\uFF0C\u4E0D\u8981\u5728\u540C\u4E00\u8F6E\u9A6C\u4E0A\u50AC\u529E\uFF1B\u7ED9\u8D1F\u8D23\u4EBA\u5408\u7406\u5904\u7406\u65F6\u95F4\u3002`,
|
|
43752
|
-
`- \u5DF2\u7ECF\u7B49\u4E86\u4E00\u9635\u3001\u8001\u677F\u53C8\u8FFD\u95EE\uFF0C\u6216\u53D1\u73B0\u660E\u786E\u98CE\u9669\u65F6\uFF0C\u518D\u7528 mailbox_followup \u6216 department_communicate \u53BB\u95EE\u5BF9\u5E94 Department Head \u771F\u5B9E\u8FDB\u5C55\u3001\u5361\u5728\u54EA\u3001\u5927\u6982\u4EC0\u4E48\u65F6\u5019\u597D\u3002`,
|
|
43753
|
-
`- \u522B\u5728\u8FD9\u4E00\u8F6E\u91CC\u53CD\u590D\u7A7A\u67E5 check_department_replies\uFF1B\u7B49\u56E2\u961F\u56DE\u4FE1\u3001\u8001\u677F\u8FFD\u95EE\u6216\u50AC\u529E\u6709\u4E86\u56DE\u97F3\uFF0C\u518D\u6765\u770B\u3002`
|
|
43754
|
-
].join("\n");
|
|
43755
|
-
}
|
|
43756
|
-
const updateStmt = db3.prepare(`UPDATE mailbox SET status = 'read' WHERE id = ?`);
|
|
43757
|
-
for (const msg of msgs) {
|
|
43758
|
-
updateStmt.run(msg.id);
|
|
43759
|
-
}
|
|
43760
|
-
const replies = msgs.map((msg, i) => {
|
|
43761
|
-
const time = new Date(msg.sendTime).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
43762
|
-
return `--- \u56DE\u4FE1 ${i + 1} ---
|
|
43763
|
-
id: ${msg.id}
|
|
43764
|
-
\u6765\u81EA: ${msg.fromMailboxId}
|
|
43765
|
-
\u65F6\u95F4: ${time}
|
|
43766
|
-
\u72B6\u6001: ${msg.status}
|
|
43767
|
-
\u5185\u5BB9:
|
|
43768
|
-
${msg.content}`;
|
|
43769
|
-
}).join("\n\n");
|
|
43770
|
-
return `[checkDepartmentReplies] \u6536\u5230 ${msgs.length} \u6761\u90E8\u95E8\u56DE\u4FE1\uFF1A
|
|
43771
|
-
|
|
43772
|
-
${replies}`;
|
|
43773
|
-
}
|
|
43774
|
-
};
|
|
43775
|
-
|
|
43776
|
-
// src/department/learning.ts
|
|
43777
|
-
var import_node_fs4 = require("node:fs");
|
|
43778
|
-
var import_node_path13 = __toESM(require("node:path"));
|
|
43779
|
-
var import_node_crypto10 = require("node:crypto");
|
|
43780
|
-
|
|
43781
|
-
// src/skill/SkillValidator.ts
|
|
43782
|
-
var import_node_fs3 = require("node:fs");
|
|
43783
|
-
var import_node_path12 = __toESM(require("node:path"));
|
|
43784
|
-
var SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
43785
|
-
var REQUIRED_SECTIONS = [
|
|
43786
|
-
/(^|\n)##\s+(when to use|何时使用|trigger|triggers)\b/i,
|
|
43787
|
-
/(^|\n)##\s+(steps|workflow|procedure|执行步骤|工作流)\b/i
|
|
43788
|
-
];
|
|
43789
|
-
var stripYamlQuotes = (value) => {
|
|
43790
|
-
const trimmed = value.trim();
|
|
43791
|
-
if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) {
|
|
43792
|
-
return trimmed.slice(1, -1).trim();
|
|
43793
|
-
}
|
|
43794
|
-
return trimmed;
|
|
43795
|
-
};
|
|
43796
|
-
var addError = (errors, code, message) => {
|
|
43797
|
-
errors.push({ code, message });
|
|
43798
|
-
};
|
|
43799
|
-
var addWarning = (warnings, code, message) => {
|
|
43800
|
-
warnings.push({ code, message });
|
|
43801
|
-
};
|
|
43802
|
-
var isValidSkillName = (name) => {
|
|
43803
|
-
return SKILL_NAME_PATTERN.test(name) && !name.includes(`--`);
|
|
43804
|
-
};
|
|
43805
|
-
var assertSafeSkillTarget = (rootDir, skillName) => {
|
|
43806
|
-
if (!isValidSkillName(skillName)) {
|
|
43807
|
-
throw new Error(`Invalid skill name "${skillName}". Use lowercase letters, digits, and single hyphens only.`);
|
|
43808
|
-
}
|
|
43809
|
-
const root = import_node_path12.default.resolve(rootDir);
|
|
43810
|
-
const target = import_node_path12.default.resolve(root, skillName);
|
|
43811
|
-
if (target !== import_node_path12.default.join(root, skillName) || !target.startsWith(root + import_node_path12.default.sep)) {
|
|
43812
|
-
throw new Error(`Invalid skill install target for "${skillName}".`);
|
|
43813
|
-
}
|
|
43814
|
-
return target;
|
|
43815
|
-
};
|
|
43816
|
-
var parseSkillDocument = (skillMd) => {
|
|
43817
|
-
const fmMatch = skillMd.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
43818
|
-
if (!fmMatch) return null;
|
|
43819
|
-
const frontmatter = {};
|
|
43820
|
-
for (const rawLine of fmMatch[1].split(/\r?\n/)) {
|
|
43821
|
-
const line = rawLine.trim();
|
|
43822
|
-
if (!line || line.startsWith(`#`)) continue;
|
|
43823
|
-
const match2 = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
43824
|
-
if (!match2) {
|
|
43825
|
-
frontmatter[`__invalid__${Object.keys(frontmatter).length}`] = line;
|
|
43826
|
-
continue;
|
|
43827
|
-
}
|
|
43828
|
-
frontmatter[match2[1]] = stripYamlQuotes(match2[2]);
|
|
43829
|
-
}
|
|
43830
|
-
return {
|
|
43831
|
-
name: frontmatter.name ?? ``,
|
|
43832
|
-
description: frontmatter.description ?? ``,
|
|
43833
|
-
body: skillMd.slice(fmMatch[0].length).trim(),
|
|
43834
|
-
frontmatter
|
|
43835
|
-
};
|
|
43836
|
-
};
|
|
43837
|
-
var validateSkillDocument = (input) => {
|
|
43838
|
-
const errors = [];
|
|
43839
|
-
const warnings = [];
|
|
43840
|
-
const skillMd = input.skillMd;
|
|
43841
|
-
if (!skillMd || !skillMd.trim()) {
|
|
43842
|
-
addError(errors, `empty_skill_md`, `SKILL.md content is required.`);
|
|
43843
|
-
return { ok: false, errors, warnings };
|
|
43844
|
-
}
|
|
43845
|
-
const parsed = parseSkillDocument(skillMd);
|
|
43846
|
-
if (!parsed) {
|
|
43847
|
-
addError(errors, `missing_frontmatter`, `SKILL.md must start with YAML frontmatter delimited by "---".`);
|
|
43848
|
-
return { ok: false, errors, warnings };
|
|
43849
|
-
}
|
|
43850
|
-
const invalidFrontmatterLines = Object.keys(parsed.frontmatter).filter((key) => key.startsWith(`__invalid__`));
|
|
43851
|
-
if (invalidFrontmatterLines.length > 0) {
|
|
43852
|
-
addError(errors, `invalid_frontmatter`, `Frontmatter must contain simple "key: value" lines.`);
|
|
43853
|
-
}
|
|
43854
|
-
if (!parsed.name) {
|
|
43855
|
-
addError(errors, `missing_name`, `Frontmatter field "name" is required.`);
|
|
43856
|
-
} else if (!isValidSkillName(parsed.name)) {
|
|
43857
|
-
addError(errors, `invalid_name`, `Skill name "${parsed.name}" must be kebab-case, under 64 characters, and path-safe.`);
|
|
43858
|
-
}
|
|
43859
|
-
if (!parsed.description) {
|
|
43860
|
-
addError(errors, `missing_description`, `Frontmatter field "description" is required.`);
|
|
43861
|
-
} else if (parsed.description.length > 500) {
|
|
43862
|
-
addWarning(warnings, `long_description`, `Skill description is long; keep trigger metadata concise.`);
|
|
43863
|
-
}
|
|
43864
|
-
if (input.skillName && parsed.name && input.skillName !== parsed.name) {
|
|
43865
|
-
addError(errors, `name_mismatch`, `Input skillName "${input.skillName}" must match frontmatter name "${parsed.name}".`);
|
|
43866
|
-
}
|
|
43867
|
-
if (input.description && parsed.description && input.description !== parsed.description) {
|
|
43868
|
-
addError(errors, `description_mismatch`, `Input description must match frontmatter description.`);
|
|
43869
|
-
}
|
|
43870
|
-
if (!parsed.body) {
|
|
43871
|
-
addError(errors, `empty_body`, `SKILL.md body is required.`);
|
|
43872
|
-
} else {
|
|
43873
|
-
if (!/(^|\n)#\s+\S/.test(parsed.body)) {
|
|
43874
|
-
addWarning(warnings, `missing_title`, `Skill body should start with a Markdown H1 title.`);
|
|
43875
|
-
}
|
|
43876
|
-
for (const sectionPattern of REQUIRED_SECTIONS) {
|
|
43877
|
-
if (!sectionPattern.test(parsed.body)) {
|
|
43878
|
-
addWarning(warnings, `missing_recommended_section`, `Skill body should include when-to-use and execution workflow sections.`);
|
|
43879
|
-
break;
|
|
43880
|
-
}
|
|
43881
|
-
}
|
|
43882
|
-
}
|
|
43883
|
-
if (input.baseDir) {
|
|
43884
|
-
const normalizedBase = import_node_path12.default.resolve(input.baseDir);
|
|
43885
|
-
for (const match2 of parsed.body.matchAll(/\]\(([^)]+)\)/g)) {
|
|
43886
|
-
const href = match2[1].trim();
|
|
43887
|
-
if (!href || /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith(`#`)) continue;
|
|
43888
|
-
const target = import_node_path12.default.resolve(normalizedBase, href.split(`#`)[0]);
|
|
43889
|
-
if (!target.startsWith(normalizedBase + import_node_path12.default.sep) && target !== normalizedBase) {
|
|
43890
|
-
addError(errors, `unsafe_reference`, `Relative link "${href}" escapes the skill directory.`);
|
|
43891
|
-
} else if (!(0, import_node_fs3.existsSync)(target)) {
|
|
43892
|
-
addError(errors, `missing_reference`, `Relative link "${href}" does not exist in the skill directory.`);
|
|
43893
|
-
}
|
|
43894
|
-
}
|
|
43895
|
-
}
|
|
43896
|
-
return {
|
|
43897
|
-
ok: errors.length === 0,
|
|
43898
|
-
skill: parsed,
|
|
43899
|
-
errors,
|
|
43900
|
-
warnings
|
|
43901
|
-
};
|
|
43902
|
-
};
|
|
43903
|
-
var mergeIssues = (target, source) => {
|
|
43904
|
-
target.errors.push(...source.errors);
|
|
43905
|
-
target.warnings.push(...source.warnings);
|
|
43906
|
-
};
|
|
43907
|
-
var validateScriptsDirectory = (skillDir, warnings) => {
|
|
43908
|
-
const scriptsDir = import_node_path12.default.join(skillDir, `scripts`);
|
|
43909
|
-
if (!(0, import_node_fs3.existsSync)(scriptsDir)) return;
|
|
43910
|
-
const entries = (0, import_node_fs3.readdirSync)(scriptsDir, { withFileTypes: true }).filter((entry) => entry.isFile());
|
|
43911
|
-
if (entries.length === 0) {
|
|
43912
|
-
addWarning(warnings, `empty_scripts_dir`, `scripts/ exists but contains no files.`);
|
|
43913
|
-
}
|
|
43914
|
-
};
|
|
43915
|
-
var validateSkillDirectory = (skillDir, options = {}) => {
|
|
43916
|
-
const errors = [];
|
|
43917
|
-
const warnings = [];
|
|
43918
|
-
const normalizedSkillDir = import_node_path12.default.resolve(skillDir);
|
|
43919
|
-
const skillMdPath = import_node_path12.default.join(normalizedSkillDir, `SKILL.md`);
|
|
43920
|
-
if (!(0, import_node_fs3.existsSync)(normalizedSkillDir) || !(0, import_node_fs3.statSync)(normalizedSkillDir).isDirectory()) {
|
|
43921
|
-
addError(errors, `missing_skill_dir`, `Skill directory does not exist: ${normalizedSkillDir}`);
|
|
43922
|
-
return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
|
|
43923
|
-
}
|
|
43924
|
-
if (!(0, import_node_fs3.existsSync)(skillMdPath) || !(0, import_node_fs3.statSync)(skillMdPath).isFile()) {
|
|
43925
|
-
addError(errors, `missing_skill_md`, `Skill directory must contain SKILL.md.`);
|
|
43926
|
-
return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
|
|
43927
|
-
}
|
|
43928
|
-
const raw2 = (0, import_node_fs3.readFileSync)(skillMdPath, `utf-8`);
|
|
43929
|
-
const documentResult = validateSkillDocument({
|
|
43930
|
-
skillName: options.expectedName ?? import_node_path12.default.basename(normalizedSkillDir),
|
|
43931
|
-
skillMd: raw2,
|
|
43932
|
-
baseDir: normalizedSkillDir
|
|
43933
|
-
});
|
|
43934
|
-
mergeIssues({ errors, warnings }, documentResult);
|
|
43935
|
-
validateScriptsDirectory(normalizedSkillDir, warnings);
|
|
43936
|
-
return {
|
|
43937
|
-
ok: errors.length === 0,
|
|
43938
|
-
skill: documentResult.skill,
|
|
43939
|
-
errors,
|
|
43940
|
-
warnings,
|
|
43941
|
-
skillDir: normalizedSkillDir,
|
|
43942
|
-
skillMdPath
|
|
43943
|
-
};
|
|
43944
|
-
};
|
|
43945
|
-
var smokeTestSkillDirectory = (skillDir, options = {}) => {
|
|
43946
|
-
const directoryResult = validateSkillDirectory(skillDir, options);
|
|
43947
|
-
const errors = [...directoryResult.errors];
|
|
43948
|
-
const warnings = [...directoryResult.warnings];
|
|
43949
|
-
let detail;
|
|
43950
|
-
if (directoryResult.ok && directoryResult.skill) {
|
|
43951
|
-
detail = `Base directory for this skill: ${directoryResult.skillDir}
|
|
43952
|
-
|
|
43953
|
-
${directoryResult.skill.body}`;
|
|
43954
|
-
if (!detail.includes(directoryResult.skillDir)) {
|
|
43955
|
-
addError(errors, `smoke_missing_base_dir`, `Skill detail does not include its base directory.`);
|
|
44115
|
+
type: `string`,
|
|
44116
|
+
description: `\u53EF\u9009\uFF1A\u53EA\u67E5\u770B\u6307\u5B9A mailboxId \u7684\u56DE\u4FE1\uFF0C\u4E0D\u586B\u5219\u67E5\u770B\u6240\u6709\u56DE\u4FE1`
|
|
44117
|
+
}
|
|
44118
|
+
},
|
|
44119
|
+
required: []
|
|
44120
|
+
},
|
|
44121
|
+
async execute(input) {
|
|
44122
|
+
const db3 = createSqliteDB();
|
|
44123
|
+
const fromMember = input.from_member;
|
|
44124
|
+
let msgs;
|
|
44125
|
+
if (fromMember) {
|
|
44126
|
+
const stmt = db3.prepare(
|
|
44127
|
+
`SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
|
|
44128
|
+
FROM mailbox
|
|
44129
|
+
WHERE to_mailbox_id = 'manager' AND from_mailbox_id = ? AND status in ('pending', 'processing')
|
|
44130
|
+
ORDER BY send_time ASC`
|
|
44131
|
+
);
|
|
44132
|
+
msgs = stmt.all(fromMember);
|
|
44133
|
+
} else {
|
|
44134
|
+
const stmt = db3.prepare(
|
|
44135
|
+
`SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
|
|
44136
|
+
FROM mailbox
|
|
44137
|
+
WHERE to_mailbox_id = 'manager' AND status in ('pending', 'processing')
|
|
44138
|
+
ORDER BY send_time ASC`
|
|
44139
|
+
);
|
|
44140
|
+
msgs = stmt.all();
|
|
43956
44141
|
}
|
|
43957
|
-
if (
|
|
43958
|
-
|
|
44142
|
+
if (msgs.length === 0) {
|
|
44143
|
+
return [
|
|
44144
|
+
`[checkDepartmentReplies] \u56E2\u961F\u8FD9\u4F1A\u513F\u8FD8\u6CA1\u56DE\u4FE1\uFF0C\u591A\u534A\u662F\u8D1F\u8D23\u4EBA\u4ECD\u5728\u5904\u7406\uFF0C\u8FC7\u4F1A\u513F\u518D\u6765\u770B\u3002`,
|
|
44145
|
+
``,
|
|
44146
|
+
`\u51E0\u53E5\u63D0\u9192\uFF1A`,
|
|
44147
|
+
`- \u8FD9\u4E0D\u4EE3\u8868\u8981\u4F60\u4EB2\u81EA\u4E0B\u573A read/bash/\u6D4B\u8BD5/\u6539\u4EE3\u7801\u2014\u2014\u56E2\u961F\u6CA1\u56DE\uFF0C\u4E0D\u662F\u4F60\u8BE5\u63A5\u624B\u7684\u4FE1\u53F7\u3002`,
|
|
44148
|
+
`- \u8001\u677F\u8981\u662F\u6B63\u7B49\u7740\uFF0C\u5148\u7528 send_message \u5982\u5B9E\u8DDF\u4ED6\u8BF4\u4E00\u53E5\u201C\u8D1F\u8D23\u4EBA\u8FD8\u5728\u5904\u7406\uFF0C\u6211\u62FF\u5230\u7ED3\u679C\u5C31\u540C\u6B65\u201D\u3002`,
|
|
44149
|
+
`- \u5982\u679C\u4EFB\u52A1\u521A\u6D3E\u51FA\u53BB\uFF0C\u4E0D\u8981\u5728\u540C\u4E00\u8F6E\u9A6C\u4E0A\u50AC\u529E\uFF1B\u7ED9\u8D1F\u8D23\u4EBA\u5408\u7406\u5904\u7406\u65F6\u95F4\u3002`,
|
|
44150
|
+
`- \u5DF2\u7ECF\u7B49\u4E86\u4E00\u9635\u3001\u8001\u677F\u53C8\u8FFD\u95EE\uFF0C\u6216\u53D1\u73B0\u660E\u786E\u98CE\u9669\u65F6\uFF0C\u518D\u7528 mailbox_followup \u6216 department_communicate \u53BB\u95EE\u5BF9\u5E94 Department Head \u771F\u5B9E\u8FDB\u5C55\u3001\u5361\u5728\u54EA\u3001\u5927\u6982\u4EC0\u4E48\u65F6\u5019\u597D\u3002`,
|
|
44151
|
+
`- \u522B\u5728\u8FD9\u4E00\u8F6E\u91CC\u53CD\u590D\u7A7A\u67E5 check_department_replies\uFF1B\u7B49\u56E2\u961F\u56DE\u4FE1\u3001\u8001\u677F\u8FFD\u95EE\u6216\u50AC\u529E\u6709\u4E86\u56DE\u97F3\uFF0C\u518D\u6765\u770B\u3002`
|
|
44152
|
+
].join("\n");
|
|
43959
44153
|
}
|
|
43960
|
-
|
|
43961
|
-
|
|
44154
|
+
const updateStmt = db3.prepare(`UPDATE mailbox SET status = 'read' WHERE id = ?`);
|
|
44155
|
+
for (const msg of msgs) {
|
|
44156
|
+
updateStmt.run(msg.id);
|
|
43962
44157
|
}
|
|
43963
|
-
|
|
43964
|
-
|
|
43965
|
-
|
|
43966
|
-
|
|
43967
|
-
|
|
43968
|
-
|
|
43969
|
-
|
|
43970
|
-
|
|
43971
|
-
}
|
|
43972
|
-
|
|
43973
|
-
|
|
43974
|
-
...result.errors.map((issue) => `- [${issue.code}] ${issue.message}`),
|
|
43975
|
-
...result.warnings.map((issue) => `- [warning:${issue.code}] ${issue.message}`)
|
|
43976
|
-
];
|
|
43977
|
-
return lines.join(`
|
|
43978
|
-
`);
|
|
43979
|
-
};
|
|
44158
|
+
const replies = msgs.map((msg, i) => {
|
|
44159
|
+
const time = new Date(msg.sendTime).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
44160
|
+
return `--- \u56DE\u4FE1 ${i + 1} ---
|
|
44161
|
+
id: ${msg.id}
|
|
44162
|
+
\u6765\u81EA: ${msg.fromMailboxId}
|
|
44163
|
+
\u65F6\u95F4: ${time}
|
|
44164
|
+
\u72B6\u6001: ${msg.status}
|
|
44165
|
+
\u5185\u5BB9:
|
|
44166
|
+
${msg.content}`;
|
|
44167
|
+
}).join("\n\n");
|
|
44168
|
+
return `[checkDepartmentReplies] \u6536\u5230 ${msgs.length} \u6761\u90E8\u95E8\u56DE\u4FE1\uFF1A
|
|
43980
44169
|
|
|
43981
|
-
|
|
43982
|
-
var ensureDir = (dir) => (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
43983
|
-
var readJsonArray = (filePath) => {
|
|
43984
|
-
if (!(0, import_node_fs4.existsSync)(filePath)) return [];
|
|
43985
|
-
return JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf-8"));
|
|
43986
|
-
};
|
|
43987
|
-
var writeJsonArray = (filePath, records) => {
|
|
43988
|
-
ensureDir(import_node_path13.default.dirname(filePath));
|
|
43989
|
-
(0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(records, null, " "), "utf-8");
|
|
43990
|
-
};
|
|
43991
|
-
var departmentMemoryPath = (departmentName) => {
|
|
43992
|
-
return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-memory.json");
|
|
43993
|
-
};
|
|
43994
|
-
var departmentSkillPath = (departmentName) => {
|
|
43995
|
-
return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-skills.json");
|
|
43996
|
-
};
|
|
43997
|
-
var departmentSkillDir = (departmentName, skillName) => {
|
|
43998
|
-
return assertSafeSkillTarget(import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "skills"), skillName);
|
|
43999
|
-
};
|
|
44000
|
-
var proposalsPath = () => {
|
|
44001
|
-
return import_node_path13.default.join(getDepartmentBaseDir(), "department-proposals.json");
|
|
44002
|
-
};
|
|
44003
|
-
var getDepartmentNameForHead = (request) => {
|
|
44004
|
-
const mailboxId = request?.departmentAgentId;
|
|
44005
|
-
if (!mailboxId) return null;
|
|
44006
|
-
const member = getDepartmentMemberByMailboxId(mailboxId);
|
|
44007
|
-
if (!member || member.role !== "department_head") return null;
|
|
44008
|
-
const [departmentName] = mailboxId.split("::");
|
|
44009
|
-
return departmentName || null;
|
|
44010
|
-
};
|
|
44011
|
-
var listDepartmentMemories = (departmentName) => {
|
|
44012
|
-
return readJsonArray(departmentMemoryPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
44013
|
-
};
|
|
44014
|
-
var createDepartmentMemory = (departmentName, input) => {
|
|
44015
|
-
const now = Date.now();
|
|
44016
|
-
const memory = {
|
|
44017
|
-
id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
|
|
44018
|
-
departmentName,
|
|
44019
|
-
title: input.title,
|
|
44020
|
-
content: input.content,
|
|
44021
|
-
sourceMailboxId: input.sourceMailboxId,
|
|
44022
|
-
createdAt: now,
|
|
44023
|
-
updatedAt: now
|
|
44024
|
-
};
|
|
44025
|
-
const records = listDepartmentMemories(departmentName);
|
|
44026
|
-
records.push(memory);
|
|
44027
|
-
writeJsonArray(departmentMemoryPath(departmentName), records);
|
|
44028
|
-
return memory;
|
|
44029
|
-
};
|
|
44030
|
-
var updateDepartmentMemory = (departmentName, id, patch) => {
|
|
44031
|
-
const records = listDepartmentMemories(departmentName);
|
|
44032
|
-
const idx = records.findIndex((record) => record.id === id);
|
|
44033
|
-
if (idx < 0) return null;
|
|
44034
|
-
records[idx] = {
|
|
44035
|
-
...records[idx],
|
|
44036
|
-
...patch.title !== void 0 ? { title: patch.title } : {},
|
|
44037
|
-
...patch.content !== void 0 ? { content: patch.content } : {},
|
|
44038
|
-
updatedAt: Date.now()
|
|
44039
|
-
};
|
|
44040
|
-
writeJsonArray(departmentMemoryPath(departmentName), records);
|
|
44041
|
-
return records[idx];
|
|
44042
|
-
};
|
|
44043
|
-
var deleteDepartmentMemory = (departmentName, id) => {
|
|
44044
|
-
const records = listDepartmentMemories(departmentName);
|
|
44045
|
-
const target = records.find((record) => record.id === id) ?? null;
|
|
44046
|
-
writeJsonArray(departmentMemoryPath(departmentName), records.filter((record) => record.id !== id));
|
|
44047
|
-
return target;
|
|
44048
|
-
};
|
|
44049
|
-
var listDepartmentSkills = (departmentName) => {
|
|
44050
|
-
return readJsonArray(departmentSkillPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
44051
|
-
};
|
|
44052
|
-
var proposeDepartmentSkill = (departmentName, input) => {
|
|
44053
|
-
const validation = validateSkillDocument({
|
|
44054
|
-
skillName: input.skillName,
|
|
44055
|
-
description: input.description,
|
|
44056
|
-
skillMd: input.skillMd
|
|
44057
|
-
});
|
|
44058
|
-
if (!validation.ok) {
|
|
44059
|
-
throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF1A
|
|
44060
|
-
${formatSkillValidationIssues(validation)}`);
|
|
44061
|
-
}
|
|
44062
|
-
const records = listDepartmentSkills(departmentName);
|
|
44063
|
-
if (records.some((record) => record.skillName === input.skillName && record.status !== "dropped")) {
|
|
44064
|
-
return null;
|
|
44065
|
-
}
|
|
44066
|
-
const now = Date.now();
|
|
44067
|
-
const skill = {
|
|
44068
|
-
id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
|
|
44069
|
-
departmentName,
|
|
44070
|
-
skillName: input.skillName,
|
|
44071
|
-
description: input.description,
|
|
44072
|
-
skillMd: input.skillMd,
|
|
44073
|
-
status: "pending",
|
|
44074
|
-
createdByMailboxId: input.createdByMailboxId,
|
|
44075
|
-
createdAt: now,
|
|
44076
|
-
updatedAt: now
|
|
44077
|
-
};
|
|
44078
|
-
records.push(skill);
|
|
44079
|
-
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
44080
|
-
return skill;
|
|
44081
|
-
};
|
|
44082
|
-
var keepDepartmentSkill = (departmentName, id) => {
|
|
44083
|
-
const records = listDepartmentSkills(departmentName);
|
|
44084
|
-
const idx = records.findIndex((record) => record.id === id);
|
|
44085
|
-
if (idx < 0) return null;
|
|
44086
|
-
records[idx] = { ...records[idx], status: "active", updatedAt: Date.now() };
|
|
44087
|
-
const skillDir = departmentSkillDir(departmentName, records[idx].skillName);
|
|
44088
|
-
const validation = validateSkillDocument({
|
|
44089
|
-
skillName: records[idx].skillName,
|
|
44090
|
-
description: records[idx].description,
|
|
44091
|
-
skillMd: records[idx].skillMd,
|
|
44092
|
-
baseDir: skillDir
|
|
44093
|
-
});
|
|
44094
|
-
if (!validation.ok) {
|
|
44095
|
-
throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u4FDD\u7559\uFF1A
|
|
44096
|
-
${formatSkillValidationIssues(validation)}`);
|
|
44097
|
-
}
|
|
44098
|
-
ensureDir(skillDir);
|
|
44099
|
-
(0, import_node_fs4.writeFileSync)(import_node_path13.default.join(skillDir, "SKILL.md"), records[idx].skillMd, "utf-8");
|
|
44100
|
-
const smokeTest = smokeTestSkillDirectory(skillDir, { expectedName: records[idx].skillName });
|
|
44101
|
-
if (!smokeTest.ok) {
|
|
44102
|
-
throw new Error(`[departmentSkill] Skill smoke test \u5931\u8D25\uFF1A
|
|
44103
|
-
${formatSkillValidationIssues(smokeTest)}`);
|
|
44170
|
+
${replies}`;
|
|
44104
44171
|
}
|
|
44105
|
-
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
44106
|
-
return records[idx];
|
|
44107
|
-
};
|
|
44108
|
-
var dropDepartmentSkill = (departmentName, id) => {
|
|
44109
|
-
const records = listDepartmentSkills(departmentName);
|
|
44110
|
-
const idx = records.findIndex((record) => record.id === id);
|
|
44111
|
-
if (idx < 0) return null;
|
|
44112
|
-
records[idx] = { ...records[idx], status: "dropped", updatedAt: Date.now() };
|
|
44113
|
-
writeJsonArray(departmentSkillPath(departmentName), records);
|
|
44114
|
-
return records[idx];
|
|
44115
|
-
};
|
|
44116
|
-
var createDepartmentProposal = (input) => {
|
|
44117
|
-
const records = readJsonArray(proposalsPath());
|
|
44118
|
-
const proposal = {
|
|
44119
|
-
...input,
|
|
44120
|
-
id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
|
|
44121
|
-
status: "pending",
|
|
44122
|
-
createdAt: Date.now()
|
|
44123
|
-
};
|
|
44124
|
-
records.push(proposal);
|
|
44125
|
-
writeJsonArray(proposalsPath(), records);
|
|
44126
|
-
return proposal;
|
|
44127
|
-
};
|
|
44128
|
-
var buildDepartmentLearningContext = (departmentName) => {
|
|
44129
|
-
if (!departmentName) return "";
|
|
44130
|
-
const memories = listDepartmentMemories(departmentName);
|
|
44131
|
-
const activeSkills = listDepartmentSkills(departmentName).filter((skill) => skill.status === "active");
|
|
44132
|
-
if (memories.length === 0 && activeSkills.length === 0) return "";
|
|
44133
|
-
const memoryLines = memories.map(
|
|
44134
|
-
(memory) => ` - [id=${memory.id}] ${memory.title}
|
|
44135
|
-
${memory.content.replace(/\n/g, "\n ")}`
|
|
44136
|
-
).join("\n");
|
|
44137
|
-
const skillLines = activeSkills.map(
|
|
44138
|
-
(skill) => ` - ${skill.skillName}: ${skill.description}`
|
|
44139
|
-
).join("\n");
|
|
44140
|
-
return [
|
|
44141
|
-
`<department-learning-context department="${departmentName}">`,
|
|
44142
|
-
memories.length > 0 ? `Department memories:
|
|
44143
|
-
${memoryLines}` : "",
|
|
44144
|
-
activeSkills.length > 0 ? `Department skills:
|
|
44145
|
-
${skillLines}` : "",
|
|
44146
|
-
`</department-learning-context>`
|
|
44147
|
-
].filter(Boolean).join("\n");
|
|
44148
44172
|
};
|
|
44149
44173
|
|
|
44150
44174
|
// src/tools/tools/department/DepartmentLearning.ts
|
|
@@ -44508,6 +44532,7 @@ var MAX_SESSION_OUTPUT_LENGTH = 5e4;
|
|
|
44508
44532
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
44509
44533
|
var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
44510
44534
|
var SHELL_CANDIDATES2 = ["/bin/sh", "/usr/bin/sh", "/bin/bash"];
|
|
44535
|
+
var DEFAULT_PROTECTED_PORTS = ["3100"];
|
|
44511
44536
|
var sessions = /* @__PURE__ */ new Map();
|
|
44512
44537
|
function findExecutableShell2() {
|
|
44513
44538
|
for (const shell of SHELL_CANDIDATES2) {
|
|
@@ -44538,6 +44563,64 @@ function validateCwd(cwd) {
|
|
|
44538
44563
|
return `[bash] \u9519\u8BEF: cwd \u4E0D\u53EF\u7528: ${cwd} (${err.message})`;
|
|
44539
44564
|
}
|
|
44540
44565
|
}
|
|
44566
|
+
function getProtectedPorts() {
|
|
44567
|
+
const configured = process.env.DUCLAW_BASH_PROTECTED_PORTS ?? [process.env.KANBAN_PORT, process.env.PORT, ...DEFAULT_PROTECTED_PORTS].filter(Boolean).join(",");
|
|
44568
|
+
return [...new Set(
|
|
44569
|
+
configured.split(",").map((port) => port.trim()).filter((port) => /^\d{1,5}$/.test(port))
|
|
44570
|
+
)];
|
|
44571
|
+
}
|
|
44572
|
+
function validateProtectedRuntimeCommand(command) {
|
|
44573
|
+
const ports = getProtectedPorts();
|
|
44574
|
+
if (ports.length === 0) return null;
|
|
44575
|
+
for (const port of ports) {
|
|
44576
|
+
const portRef = new RegExp(`(^|[^\\d])(:${port}|-${port}\\b|${port}/tcp\\b)`);
|
|
44577
|
+
const targetsProtectedPort = portRef.test(command);
|
|
44578
|
+
if (!targetsProtectedPort) continue;
|
|
44579
|
+
const killsProcess = /\b(kill|pkill|killall|fuser)\b/.test(command) || /\blsof\b[\s\S]*\bxargs\b[\s\S]*\bkill\b/.test(command) || /\bss\b[\s\S]*\bxargs\b[\s\S]*\bkill\b/.test(command);
|
|
44580
|
+
if (killsProcess) {
|
|
44581
|
+
return [
|
|
44582
|
+
`[bash] \u62D2\u7EDD\u6267\u884C: \u547D\u4EE4\u8BD5\u56FE\u6E05\u7406 Duclaw runtime \u4FDD\u7559\u7AEF\u53E3 ${port}\u3002`,
|
|
44583
|
+
`\u8BE5\u7AEF\u53E3\u627F\u8F7D\u5F53\u524D\u667A\u80FD\u4F53\u7684 HTTP/gateway\uFF0C\u6740\u6389\u5B83\u4F1A\u5BFC\u81F4 iOS \u663E\u793A\u5DF2\u5173\u673A\u6216 502\u3002`,
|
|
44584
|
+
`\u8BF7\u6362\u7528\u9879\u76EE\u81EA\u5DF1\u7684\u5F00\u53D1\u7AEF\u53E3\uFF08\u4F8B\u5982 3001/5173\uFF09\uFF0C\u4E0D\u8981\u5BF9 ${port} \u6267\u884C kill/lsof/fuser/pkill\u3002`
|
|
44585
|
+
].join("\n");
|
|
44586
|
+
}
|
|
44587
|
+
}
|
|
44588
|
+
if (/\b(pkill|killall)\b[\s\S]*\b(node|tsx|pnpm|npm)\b/.test(command)) {
|
|
44589
|
+
return [
|
|
44590
|
+
`[bash] \u62D2\u7EDD\u6267\u884C: \u547D\u4EE4\u4F1A\u6309\u8FDB\u7A0B\u540D\u6279\u91CF\u7EC8\u6B62 Node/JS \u8FDB\u7A0B\u3002`,
|
|
44591
|
+
`\u8FD9\u53EF\u80FD\u8BEF\u6740 Duclaw runtime\u3002\u8BF7\u53EA\u7EC8\u6B62\u660E\u786E\u5C5E\u4E8E\u5F53\u524D\u9879\u76EE\u7684 PID \u6216 session_id\u3002`
|
|
44592
|
+
].join("\n");
|
|
44593
|
+
}
|
|
44594
|
+
return null;
|
|
44595
|
+
}
|
|
44596
|
+
function listProtectedRuntimeProcesses() {
|
|
44597
|
+
try {
|
|
44598
|
+
const output = (0, import_node_child_process.execFileSync)("ps", ["-eo", "pid=,ppid=,args="], { encoding: "utf8" });
|
|
44599
|
+
const currentPid = process.pid;
|
|
44600
|
+
return output.split("\n").map((line) => line.trim().match(/^(\d+)\s+(\d+)\s+(.*)$/)).filter((match2) => Boolean(match2)).map((match2) => ({
|
|
44601
|
+
pid: Number(match2[1]),
|
|
44602
|
+
command: match2[3] ?? ""
|
|
44603
|
+
})).filter(
|
|
44604
|
+
(processInfo) => Number.isFinite(processInfo.pid) && processInfo.pid !== currentPid && (processInfo.command.includes("duclaw-cli start") || processInfo.command.includes("duclaw-worker"))
|
|
44605
|
+
).map(({ pid, command }) => ({ pid, command }));
|
|
44606
|
+
} catch {
|
|
44607
|
+
return [];
|
|
44608
|
+
}
|
|
44609
|
+
}
|
|
44610
|
+
function validateProtectedRuntimePidCommand(command) {
|
|
44611
|
+
if (!/\bkill\b/.test(command)) return null;
|
|
44612
|
+
const protectedProcesses = listProtectedRuntimeProcesses();
|
|
44613
|
+
for (const protectedProcess of protectedProcesses) {
|
|
44614
|
+
const pidRef = new RegExp(`(^|[^\\d])${protectedProcess.pid}([^\\d]|$)`);
|
|
44615
|
+
if (!pidRef.test(command)) continue;
|
|
44616
|
+
return [
|
|
44617
|
+
`[bash] \u62D2\u7EDD\u6267\u884C: \u547D\u4EE4\u8BD5\u56FE\u7EC8\u6B62 Duclaw runtime \u8FDB\u7A0B PID ${protectedProcess.pid}\u3002`,
|
|
44618
|
+
`\u5339\u914D\u8FDB\u7A0B: ${protectedProcess.command}`,
|
|
44619
|
+
`\u8BF7\u53EA\u7EC8\u6B62\u660E\u786E\u5C5E\u4E8E\u5F53\u524D\u9879\u76EE\u7684\u5F00\u53D1\u670D\u52A1 PID\uFF0C\u4E0D\u8981\u6740 duclaw-cli start \u6216 duclaw-worker\u3002`
|
|
44620
|
+
].join("\n");
|
|
44621
|
+
}
|
|
44622
|
+
return null;
|
|
44623
|
+
}
|
|
44541
44624
|
function truncateOutput(output, limit = MAX_OUTPUT_LENGTH) {
|
|
44542
44625
|
if (output.length <= limit) return output;
|
|
44543
44626
|
return output.slice(0, limit) + `
|
|
@@ -44659,6 +44742,10 @@ var bashTool = {
|
|
|
44659
44742
|
if (!command || command.trim() === "") {
|
|
44660
44743
|
return `[bash] \u9519\u8BEF: command \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
44661
44744
|
}
|
|
44745
|
+
const protectedCommandError = validateProtectedRuntimeCommand(command);
|
|
44746
|
+
if (protectedCommandError) return protectedCommandError;
|
|
44747
|
+
const protectedRuntimePidError = validateProtectedRuntimePidCommand(command);
|
|
44748
|
+
if (protectedRuntimePidError) return protectedRuntimePidError;
|
|
44662
44749
|
if (userRequest?.workspacePath && explicitCwd) {
|
|
44663
44750
|
const rejection = validateWorkspacePath(explicitCwd, userRequest.workspacePath);
|
|
44664
44751
|
if (rejection) return `[bash] ${rejection}`;
|
|
@@ -53467,7 +53554,13 @@ toolRoutes.get("/tools", (c) => {
|
|
|
53467
53554
|
toolRoutes.get("/skills", (c) => {
|
|
53468
53555
|
try {
|
|
53469
53556
|
const skills = SkillRegistry_default();
|
|
53470
|
-
return c.json(skills.map((s) => ({
|
|
53557
|
+
return c.json(skills.map((s) => ({
|
|
53558
|
+
name: s.name,
|
|
53559
|
+
description: s.description,
|
|
53560
|
+
deletable: !!s.deletable,
|
|
53561
|
+
scope: s.scope ?? (s.deletable ? "project" : "global"),
|
|
53562
|
+
departmentName: s.departmentName
|
|
53563
|
+
})));
|
|
53471
53564
|
} catch (err) {
|
|
53472
53565
|
return c.json({ error: err.message || "Failed to list skills" }, 500);
|
|
53473
53566
|
}
|
|
@@ -53482,6 +53575,8 @@ toolRoutes.get("/skills/:name", (c) => {
|
|
|
53482
53575
|
name: skill.name,
|
|
53483
53576
|
description: skill.description,
|
|
53484
53577
|
deletable: !!skill.deletable,
|
|
53578
|
+
scope: skill.scope ?? (skill.deletable ? "project" : "global"),
|
|
53579
|
+
departmentName: skill.departmentName,
|
|
53485
53580
|
detail: skill.getDetail()
|
|
53486
53581
|
});
|
|
53487
53582
|
} catch (err) {
|
|
@@ -53504,7 +53599,7 @@ var systemRoutes = new Hono2();
|
|
|
53504
53599
|
var startTime = Date.now();
|
|
53505
53600
|
systemRoutes.get("/system/info", (c) => {
|
|
53506
53601
|
return c.json({
|
|
53507
|
-
version: true ? "1.9.
|
|
53602
|
+
version: true ? "1.9.9" : "unknown",
|
|
53508
53603
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
53509
53604
|
env: process.env.NODE_ENV || "development",
|
|
53510
53605
|
nodeVersion: process.version
|