teamai-cli 0.20.0-beta.5 → 0.20.0-beta.7
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/README.md +9 -0
- package/README.zh-CN.md +6 -0
- package/agents/teamai-recall.md +135 -9
- package/dist/index.js +562 -168
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -444,8 +444,8 @@ var init_types = __esm({
|
|
|
444
444
|
tcodex: { skills: ".tcodex/skills", rules: ".tcodex/rules", settings: ".tcodex/hooks.json", agents: ".tcodex/agents" },
|
|
445
445
|
cursor: { skills: ".cursor/skills", rules: ".cursor/rules", settings: ".cursor/hooks.json", agents: ".cursor/agents", mcp: ".cursor/mcp.json", mcpProject: ".cursor/mcp.json" },
|
|
446
446
|
codebuddy: { skills: ".codebuddy/skills", rules: ".codebuddy/rules", settings: ".codebuddy/settings.json", claudemd: ".codebuddy/CODEBUDDY.md", agents: ".codebuddy/agents", mcp: ".codebuddy/mcp.json", mcpProject: ".codebuddy/mcp.json" },
|
|
447
|
-
openclaw: { skills: ".openclaw/skills", rules: ".openclaw/rules" },
|
|
448
|
-
hermes: { skills: ".hermes/skills" },
|
|
447
|
+
openclaw: { skills: ".openclaw/skills", rules: ".openclaw/rules", claudemd: ".openclaw/workspace/AGENTS.md" },
|
|
448
|
+
hermes: { skills: ".hermes/skills", claudemd: "AGENTS.md" },
|
|
449
449
|
workbuddy: { skills: ".workbuddy/skills", rules: ".workbuddy/rules", settings: ".workbuddy/settings.json", claudemd: "AGENTS.md", mcp: ".workbuddy/mcp.json", mcpProject: ".workbuddy/mcp.json" }
|
|
450
450
|
})
|
|
451
451
|
});
|
|
@@ -1349,15 +1349,12 @@ function getTGitToken() {
|
|
|
1349
1349
|
"No TGit credentials found. Set the TGIT_TOKEN environment variable (a git.woa.com Personal Access Token) or run `gf auth login`."
|
|
1350
1350
|
);
|
|
1351
1351
|
}
|
|
1352
|
-
function
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
function tryGetTGitToken() {
|
|
1356
|
-
try {
|
|
1357
|
-
return getTGitToken();
|
|
1358
|
-
} catch {
|
|
1352
|
+
function tgitGitCloneUrl(httpsUrl) {
|
|
1353
|
+
const oauthToken = gfGetOAuthToken();
|
|
1354
|
+
if (!oauthToken) {
|
|
1359
1355
|
return null;
|
|
1360
1356
|
}
|
|
1357
|
+
return httpsUrl.replace(/^https:\/\//, `https://oauth2:${oauthToken}@`);
|
|
1361
1358
|
}
|
|
1362
1359
|
function tgitAuthHeaders(token, scheme) {
|
|
1363
1360
|
if (scheme === "bearer") {
|
|
@@ -1579,10 +1576,8 @@ function gitOutputSaysRepoMissing(output) {
|
|
|
1579
1576
|
return output.includes("not found") || output.includes("does not exist") || output.includes("Repository not found");
|
|
1580
1577
|
}
|
|
1581
1578
|
function gfRepoClone(repo, localPath) {
|
|
1582
|
-
const
|
|
1583
|
-
if (
|
|
1584
|
-
const user = tgitGitUser(creds.scheme);
|
|
1585
|
-
const cloneUrl = `https://${user}:${creds.token}@git.woa.com/${repo}.git`;
|
|
1579
|
+
const cloneUrl = tgitGitCloneUrl(`https://git.woa.com/${repo}.git`);
|
|
1580
|
+
if (cloneUrl) {
|
|
1586
1581
|
const result2 = spawnSync("git", ["clone", cloneUrl, localPath], {
|
|
1587
1582
|
encoding: "utf-8",
|
|
1588
1583
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1593,7 +1588,7 @@ function gfRepoClone(repo, localPath) {
|
|
|
1593
1588
|
throw new RepoNotFoundError2(repo);
|
|
1594
1589
|
}
|
|
1595
1590
|
if (result2.status !== 0) {
|
|
1596
|
-
const sanitized = allOutput2.replace(/
|
|
1591
|
+
const sanitized = allOutput2.replace(/oauth2:[^@]+@/g, "oauth2:***@");
|
|
1597
1592
|
throw new Error(`git clone failed: ${sanitized.trim()}`);
|
|
1598
1593
|
}
|
|
1599
1594
|
return;
|
|
@@ -2856,9 +2851,191 @@ var init_builtin_skills = __esm({
|
|
|
2856
2851
|
}
|
|
2857
2852
|
});
|
|
2858
2853
|
|
|
2854
|
+
// src/openclaw-hooks.ts
|
|
2855
|
+
var openclaw_hooks_exports = {};
|
|
2856
|
+
__export(openclaw_hooks_exports, {
|
|
2857
|
+
OPENCLAW_HOOK_DIR: () => OPENCLAW_HOOK_DIR,
|
|
2858
|
+
applyOpenClawAgentHook: () => applyOpenClawAgentHook,
|
|
2859
|
+
injectOpenClawHooks: () => injectOpenClawHooks,
|
|
2860
|
+
removeOpenClawAgentHook: () => removeOpenClawAgentHook,
|
|
2861
|
+
removeOpenClawHooks: () => removeOpenClawHooks,
|
|
2862
|
+
resolveOpenClawHooksDir: () => resolveOpenClawHooksDir,
|
|
2863
|
+
resolveOpenclawWorkspaceDir: () => resolveOpenclawWorkspaceDir
|
|
2864
|
+
});
|
|
2865
|
+
import path10 from "path";
|
|
2866
|
+
function resolveOpenClawHooksDir(tool) {
|
|
2867
|
+
if (tool === "openclaw" && process.env.OPENCLAW_STATE_DIR) {
|
|
2868
|
+
return path10.join(process.env.OPENCLAW_STATE_DIR, "hooks");
|
|
2869
|
+
}
|
|
2870
|
+
const home = process.env.HOME ?? "";
|
|
2871
|
+
return path10.join(home, `.${tool}`, "hooks");
|
|
2872
|
+
}
|
|
2873
|
+
function buildHookMd(tool) {
|
|
2874
|
+
const events = Object.keys(EVENT_MAP);
|
|
2875
|
+
const metadata = JSON.stringify({ openclaw: { events } });
|
|
2876
|
+
return [
|
|
2877
|
+
"---",
|
|
2878
|
+
`name: ${TEAMAI_MARKER} status-report`,
|
|
2879
|
+
`metadata:`,
|
|
2880
|
+
` ${metadata}`,
|
|
2881
|
+
`handler: ./handler.ts`,
|
|
2882
|
+
"---",
|
|
2883
|
+
"",
|
|
2884
|
+
`${TEAMAI_MARKER} Reports agent status to the team backend (report/sync/ack) for tool \`${tool}\`.`,
|
|
2885
|
+
"Managed by teamai \u2014 do not edit by hand.",
|
|
2886
|
+
""
|
|
2887
|
+
].join("\n");
|
|
2888
|
+
}
|
|
2889
|
+
function buildHandlerTs(tool) {
|
|
2890
|
+
const mapLiteral = JSON.stringify(EVENT_MAP);
|
|
2891
|
+
return `// ${TEAMAI_MARKER} status-report handler \u2014 generated by teamai, do not edit.
|
|
2892
|
+
import { spawn } from 'node:child_process';
|
|
2893
|
+
|
|
2894
|
+
const EVENT_MAP: Record<string, string> = ${mapLiteral};
|
|
2895
|
+
const TOOL = ${JSON.stringify(tool)};
|
|
2896
|
+
|
|
2897
|
+
export default async function handler(ctx: { event?: string } = {}): Promise<void> {
|
|
2898
|
+
const dispatchEvent = ctx.event ? EVENT_MAP[ctx.event] : undefined;
|
|
2899
|
+
if (!dispatchEvent) return;
|
|
2900
|
+
try {
|
|
2901
|
+
const child = spawn('teamai', ['hook-dispatch', dispatchEvent, '--tool', TOOL], {
|
|
2902
|
+
stdio: ['inherit', 'ignore', 'ignore'],
|
|
2903
|
+
});
|
|
2904
|
+
child.on('error', () => {});
|
|
2905
|
+
} catch {
|
|
2906
|
+
// never block the agent
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
`;
|
|
2910
|
+
}
|
|
2911
|
+
async function injectOpenClawHooks(hooksDir, tool = "openclaw") {
|
|
2912
|
+
const effectiveHooksDir = resolveOpenClawHooksDir(tool);
|
|
2913
|
+
const dir = path10.join(effectiveHooksDir, OPENCLAW_HOOK_DIR);
|
|
2914
|
+
await ensureDir(dir);
|
|
2915
|
+
await writeFile(path10.join(dir, "HOOK.md"), buildHookMd(tool));
|
|
2916
|
+
await writeFile(path10.join(dir, "handler.ts"), buildHandlerTs(tool));
|
|
2917
|
+
log.success(`Injected teamai OpenClaw hook into ${dir}`);
|
|
2918
|
+
}
|
|
2919
|
+
async function removeOpenClawHooks(hooksDir) {
|
|
2920
|
+
const dir = path10.join(hooksDir, OPENCLAW_HOOK_DIR);
|
|
2921
|
+
if (await pathExists(dir)) {
|
|
2922
|
+
await remove(dir);
|
|
2923
|
+
log.success(`Removed teamai OpenClaw hook from ${dir}`);
|
|
2924
|
+
}
|
|
2925
|
+
if (process.env.OPENCLAW_STATE_DIR) {
|
|
2926
|
+
const altDir = path10.join(process.env.OPENCLAW_STATE_DIR, "hooks", OPENCLAW_HOOK_DIR);
|
|
2927
|
+
if (altDir !== dir && await pathExists(altDir)) {
|
|
2928
|
+
await remove(altDir);
|
|
2929
|
+
log.success(`Removed teamai OpenClaw hook from ${altDir}`);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
function buildAgentHookMd(slug, openclawEvent) {
|
|
2934
|
+
const metadata = JSON.stringify({ openclaw: { events: [openclawEvent] } });
|
|
2935
|
+
return [
|
|
2936
|
+
"---",
|
|
2937
|
+
`name: ${TEAMAI_MARKER} ${slug}`,
|
|
2938
|
+
`metadata:`,
|
|
2939
|
+
` ${metadata}`,
|
|
2940
|
+
`handler: ./handler.ts`,
|
|
2941
|
+
"---",
|
|
2942
|
+
"",
|
|
2943
|
+
`${TEAMAI_MARKER} Agent hook [${slug}] \u2014 managed by teamai, do not edit by hand.`,
|
|
2944
|
+
""
|
|
2945
|
+
].join("\n");
|
|
2946
|
+
}
|
|
2947
|
+
function buildAgentHandlerTs(command, timeout) {
|
|
2948
|
+
const timeoutMs = timeout * 1e3;
|
|
2949
|
+
return [
|
|
2950
|
+
`// ${TEAMAI_MARKER} agent hook handler \u2014 generated by teamai, do not edit.`,
|
|
2951
|
+
`import { spawn } from 'node:child_process';`,
|
|
2952
|
+
"",
|
|
2953
|
+
`export default async function handler(): Promise<void> {`,
|
|
2954
|
+
" try {",
|
|
2955
|
+
` const child = spawn('sh', ['-c', ${JSON.stringify(command)}], {`,
|
|
2956
|
+
` stdio: ['inherit', 'ignore', 'ignore'],`,
|
|
2957
|
+
` timeout: ${timeoutMs},`,
|
|
2958
|
+
" });",
|
|
2959
|
+
" child.on('error', () => {});",
|
|
2960
|
+
" } catch {",
|
|
2961
|
+
" // never block the agent",
|
|
2962
|
+
" }",
|
|
2963
|
+
"}",
|
|
2964
|
+
""
|
|
2965
|
+
].join("\n");
|
|
2966
|
+
}
|
|
2967
|
+
async function applyOpenClawAgentHook(def) {
|
|
2968
|
+
const openclawEvent = CLAUDE_TO_OPENCLAW_EVENTS[def.event];
|
|
2969
|
+
if (!openclawEvent) {
|
|
2970
|
+
log.warn(`OpenClaw does not support event "${def.event}" \u2014 skipping hook [${def.slug}]`);
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
const tool = def.tool ?? "openclaw";
|
|
2974
|
+
const hooksDir = resolveOpenClawHooksDir(tool);
|
|
2975
|
+
const dir = path10.join(hooksDir, def.slug);
|
|
2976
|
+
await ensureDir(dir);
|
|
2977
|
+
await writeFile(path10.join(dir, "HOOK.md"), buildAgentHookMd(def.slug, openclawEvent));
|
|
2978
|
+
await writeFile(path10.join(dir, "handler.ts"), buildAgentHandlerTs(def.command, def.timeout ?? 10));
|
|
2979
|
+
log.success(`Installed OpenClaw agent hook [${def.slug}] in ${dir}`);
|
|
2980
|
+
}
|
|
2981
|
+
async function removeOpenClawAgentHook(opts) {
|
|
2982
|
+
const tool = opts.tool ?? "openclaw";
|
|
2983
|
+
const hooksDir = resolveOpenClawHooksDir(tool);
|
|
2984
|
+
const dir = path10.join(hooksDir, opts.slug);
|
|
2985
|
+
if (await pathExists(dir)) {
|
|
2986
|
+
await remove(dir);
|
|
2987
|
+
log.success(`Removed OpenClaw agent hook [${opts.slug}] from ${dir}`);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
async function resolveOpenclawWorkspaceDir(workspacePath) {
|
|
2991
|
+
const candidates = [];
|
|
2992
|
+
if (workspacePath) candidates.push(workspacePath);
|
|
2993
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR;
|
|
2994
|
+
if (stateDir && path10.isAbsolute(stateDir)) {
|
|
2995
|
+
const cfgRaw = await readFileSafe(path10.join(stateDir, "openclaw.json"));
|
|
2996
|
+
if (cfgRaw) {
|
|
2997
|
+
try {
|
|
2998
|
+
const cfg = JSON.parse(cfgRaw);
|
|
2999
|
+
const ws = cfg?.agents?.defaults?.workspace;
|
|
3000
|
+
if (typeof ws === "string" && ws) candidates.push(ws);
|
|
3001
|
+
} catch (e) {
|
|
3002
|
+
log.debug(`openclaw: failed to parse openclaw.json: ${e.message}`);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
const home = process.env.HOME;
|
|
3007
|
+
if (home) candidates.push(path10.join(home, ".openclaw", "workspace"));
|
|
3008
|
+
for (const candidate of candidates) {
|
|
3009
|
+
if (await pathExists(candidate)) {
|
|
3010
|
+
log.debug(`openclaw: resolved workspace dir to ${candidate}`);
|
|
3011
|
+
return candidate;
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
log.warn(`openclaw: no workspace dir found (tried: ${candidates.join(", ") || "none"})`);
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
var OPENCLAW_HOOK_DIR, TEAMAI_MARKER, EVENT_MAP, CLAUDE_TO_OPENCLAW_EVENTS;
|
|
3018
|
+
var init_openclaw_hooks = __esm({
|
|
3019
|
+
"src/openclaw-hooks.ts"() {
|
|
3020
|
+
"use strict";
|
|
3021
|
+
init_fs();
|
|
3022
|
+
init_logger();
|
|
3023
|
+
OPENCLAW_HOOK_DIR = "teamai-status-report";
|
|
3024
|
+
TEAMAI_MARKER = "[teamai]";
|
|
3025
|
+
EVENT_MAP = {
|
|
3026
|
+
"session:start": "session-start",
|
|
3027
|
+
"command:new": "prompt-submit"
|
|
3028
|
+
};
|
|
3029
|
+
CLAUDE_TO_OPENCLAW_EVENTS = {
|
|
3030
|
+
SessionStart: "session:start",
|
|
3031
|
+
UserPromptSubmit: "command:new"
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
});
|
|
3035
|
+
|
|
2859
3036
|
// src/builtin-hooks.ts
|
|
2860
3037
|
import fs5 from "fs";
|
|
2861
|
-
import
|
|
3038
|
+
import path11 from "path";
|
|
2862
3039
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2863
3040
|
function hasShell() {
|
|
2864
3041
|
if (_hasShellCache === void 0) {
|
|
@@ -2886,12 +3063,12 @@ function pickLatestVersion(versions) {
|
|
|
2886
3063
|
}
|
|
2887
3064
|
function resolveWorkbuddyNode() {
|
|
2888
3065
|
const home = process.env.HOME ?? "";
|
|
2889
|
-
const versionsDir =
|
|
3066
|
+
const versionsDir = path11.join(home, WORKBUDDY_BUNDLED_NODE_DIR);
|
|
2890
3067
|
try {
|
|
2891
3068
|
const versions = fs5.readdirSync(versionsDir).filter((d) => !d.startsWith("."));
|
|
2892
3069
|
const latest = pickLatestVersion(versions);
|
|
2893
3070
|
if (!latest) return null;
|
|
2894
|
-
const nodeBin =
|
|
3071
|
+
const nodeBin = path11.join(versionsDir, latest, "bin", "node");
|
|
2895
3072
|
if (fs5.existsSync(nodeBin)) return nodeBin;
|
|
2896
3073
|
} catch {
|
|
2897
3074
|
}
|
|
@@ -2904,10 +3081,10 @@ function resolveCodebuddyNode() {
|
|
|
2904
3081
|
for (const entry of entries) {
|
|
2905
3082
|
if (!entry.startsWith(".codebuddy-server")) continue;
|
|
2906
3083
|
try {
|
|
2907
|
-
const binDir =
|
|
3084
|
+
const binDir = path11.join(home, entry, "bin");
|
|
2908
3085
|
const stableDirs = fs5.readdirSync(binDir).filter((d) => d.startsWith("stable-"));
|
|
2909
3086
|
for (const stable of stableDirs) {
|
|
2910
|
-
const nodeBin =
|
|
3087
|
+
const nodeBin = path11.join(binDir, stable, "node");
|
|
2911
3088
|
if (fs5.existsSync(nodeBin)) return nodeBin;
|
|
2912
3089
|
}
|
|
2913
3090
|
} catch {
|
|
@@ -2920,8 +3097,8 @@ function resolveCodebuddyNode() {
|
|
|
2920
3097
|
function resolveTeamaiEntryScript() {
|
|
2921
3098
|
try {
|
|
2922
3099
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
2923
|
-
const distDir =
|
|
2924
|
-
const candidate =
|
|
3100
|
+
const distDir = path11.dirname(thisFile);
|
|
3101
|
+
const candidate = path11.join(distDir, "index.js");
|
|
2925
3102
|
if (fs5.existsSync(candidate)) return candidate;
|
|
2926
3103
|
} catch {
|
|
2927
3104
|
}
|
|
@@ -2932,8 +3109,8 @@ function ensureTeamaiWrapper() {
|
|
|
2932
3109
|
if (!entryScript) return null;
|
|
2933
3110
|
const nodeBin = resolveWorkbuddyNode() ?? resolveCodebuddyNode() ?? process.argv[0];
|
|
2934
3111
|
const home = process.env.HOME ?? "";
|
|
2935
|
-
const binDir =
|
|
2936
|
-
const wrapperPath =
|
|
3112
|
+
const binDir = path11.join(home, TEAMAI_BIN_DIR);
|
|
3113
|
+
const wrapperPath = path11.join(binDir, WRAPPER_NAME);
|
|
2937
3114
|
const script = [
|
|
2938
3115
|
"#!/bin/sh",
|
|
2939
3116
|
`# Auto-generated by teamai \u2014 do not edit.`,
|
|
@@ -3007,11 +3184,11 @@ var init_builtin_hooks = __esm({
|
|
|
3007
3184
|
});
|
|
3008
3185
|
|
|
3009
3186
|
// src/resources/hooks.ts
|
|
3010
|
-
import
|
|
3187
|
+
import path12 from "path";
|
|
3011
3188
|
import { z as z3 } from "zod";
|
|
3012
3189
|
import YAML2 from "yaml";
|
|
3013
3190
|
function teamHooksYamlPath(repoPath) {
|
|
3014
|
-
return
|
|
3191
|
+
return path12.join(repoPath, "hooks", "hooks.yaml");
|
|
3015
3192
|
}
|
|
3016
3193
|
async function parseHooksYaml(repoPath) {
|
|
3017
3194
|
const content = await readFileSafe(teamHooksYamlPath(repoPath));
|
|
@@ -3134,80 +3311,6 @@ var init_hooks = __esm({
|
|
|
3134
3311
|
}
|
|
3135
3312
|
});
|
|
3136
3313
|
|
|
3137
|
-
// src/openclaw-hooks.ts
|
|
3138
|
-
var openclaw_hooks_exports = {};
|
|
3139
|
-
__export(openclaw_hooks_exports, {
|
|
3140
|
-
OPENCLAW_HOOK_DIR: () => OPENCLAW_HOOK_DIR,
|
|
3141
|
-
injectOpenClawHooks: () => injectOpenClawHooks,
|
|
3142
|
-
removeOpenClawHooks: () => removeOpenClawHooks
|
|
3143
|
-
});
|
|
3144
|
-
import path12 from "path";
|
|
3145
|
-
function buildHookMd(tool) {
|
|
3146
|
-
const events = Object.keys(EVENT_MAP);
|
|
3147
|
-
return [
|
|
3148
|
-
"---",
|
|
3149
|
-
`name: ${TEAMAI_MARKER} status-report`,
|
|
3150
|
-
"events:",
|
|
3151
|
-
...events.map((e) => ` - ${e}`),
|
|
3152
|
-
`handler: ./handler.ts`,
|
|
3153
|
-
"---",
|
|
3154
|
-
"",
|
|
3155
|
-
`${TEAMAI_MARKER} Reports agent status to the team backend (report/sync/ack) for tool \`${tool}\`.`,
|
|
3156
|
-
"Managed by teamai \u2014 do not edit by hand.",
|
|
3157
|
-
""
|
|
3158
|
-
].join("\n");
|
|
3159
|
-
}
|
|
3160
|
-
function buildHandlerTs(tool) {
|
|
3161
|
-
const mapLiteral = JSON.stringify(EVENT_MAP);
|
|
3162
|
-
return `// ${TEAMAI_MARKER} status-report handler \u2014 generated by teamai, do not edit.
|
|
3163
|
-
import { spawn } from 'node:child_process';
|
|
3164
|
-
|
|
3165
|
-
const EVENT_MAP: Record<string, string> = ${mapLiteral};
|
|
3166
|
-
const TOOL = ${JSON.stringify(tool)};
|
|
3167
|
-
|
|
3168
|
-
export default async function handler(ctx: { event?: string } = {}): Promise<void> {
|
|
3169
|
-
const dispatchEvent = ctx.event ? EVENT_MAP[ctx.event] : undefined;
|
|
3170
|
-
if (!dispatchEvent) return;
|
|
3171
|
-
try {
|
|
3172
|
-
const child = spawn('teamai', ['hook-dispatch', dispatchEvent, '--tool', TOOL], {
|
|
3173
|
-
stdio: ['inherit', 'ignore', 'ignore'],
|
|
3174
|
-
});
|
|
3175
|
-
child.on('error', () => {});
|
|
3176
|
-
} catch {
|
|
3177
|
-
// never block the agent
|
|
3178
|
-
}
|
|
3179
|
-
}
|
|
3180
|
-
`;
|
|
3181
|
-
}
|
|
3182
|
-
async function injectOpenClawHooks(hooksDir, tool = "openclaw") {
|
|
3183
|
-
const dir = path12.join(hooksDir, OPENCLAW_HOOK_DIR);
|
|
3184
|
-
await ensureDir(dir);
|
|
3185
|
-
await writeFile(path12.join(dir, "HOOK.md"), buildHookMd(tool));
|
|
3186
|
-
await writeFile(path12.join(dir, "handler.ts"), buildHandlerTs(tool));
|
|
3187
|
-
log.success(`Injected teamai OpenClaw hook into ${dir}`);
|
|
3188
|
-
}
|
|
3189
|
-
async function removeOpenClawHooks(hooksDir) {
|
|
3190
|
-
const dir = path12.join(hooksDir, OPENCLAW_HOOK_DIR);
|
|
3191
|
-
if (await pathExists(dir)) {
|
|
3192
|
-
await remove(dir);
|
|
3193
|
-
log.success(`Removed teamai OpenClaw hook from ${dir}`);
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
var OPENCLAW_HOOK_DIR, TEAMAI_MARKER, EVENT_MAP;
|
|
3197
|
-
var init_openclaw_hooks = __esm({
|
|
3198
|
-
"src/openclaw-hooks.ts"() {
|
|
3199
|
-
"use strict";
|
|
3200
|
-
init_fs();
|
|
3201
|
-
init_logger();
|
|
3202
|
-
OPENCLAW_HOOK_DIR = "teamai-status-report";
|
|
3203
|
-
TEAMAI_MARKER = "[teamai]";
|
|
3204
|
-
EVENT_MAP = {
|
|
3205
|
-
"session:start": "session-start",
|
|
3206
|
-
"command:new": "prompt-submit"
|
|
3207
|
-
};
|
|
3208
|
-
}
|
|
3209
|
-
});
|
|
3210
|
-
|
|
3211
3314
|
// src/hermes-home.ts
|
|
3212
3315
|
var hermes_home_exports = {};
|
|
3213
3316
|
__export(hermes_home_exports, {
|
|
@@ -3477,6 +3580,7 @@ var hooks_exports = {};
|
|
|
3477
3580
|
__export(hooks_exports, {
|
|
3478
3581
|
AGENT_HOOK_EVENTS: () => AGENT_HOOK_EVENTS,
|
|
3479
3582
|
CLAUDE_TO_CURSOR_EVENTS: () => CLAUDE_TO_CURSOR_EVENTS,
|
|
3583
|
+
OPENCLAW_TOOLS: () => OPENCLAW_TOOLS,
|
|
3480
3584
|
TEAMAI_HOOK_SUBCOMMANDS: () => TEAMAI_HOOK_SUBCOMMANDS,
|
|
3481
3585
|
TEAMAI_LEGACY_HOOK_SUBCOMMANDS: () => TEAMAI_LEGACY_HOOK_SUBCOMMANDS,
|
|
3482
3586
|
agentHookDescription: () => agentHookDescription,
|
|
@@ -3684,7 +3788,7 @@ async function reconcileCodexFormat(hooksPath, tool, teamDefs, opts, priorTeamCo
|
|
|
3684
3788
|
}
|
|
3685
3789
|
}
|
|
3686
3790
|
function isAgentHookSupportedTool(tool) {
|
|
3687
|
-
return !CURSOR_TOOLS.has(tool)
|
|
3791
|
+
return !CURSOR_TOOLS.has(tool);
|
|
3688
3792
|
}
|
|
3689
3793
|
function isAgentHookEvent(event) {
|
|
3690
3794
|
return AGENT_HOOK_EVENTS.has(event);
|
|
@@ -4793,6 +4897,16 @@ async function detectWorkbuddyVersion() {
|
|
|
4793
4897
|
}
|
|
4794
4898
|
return "";
|
|
4795
4899
|
}
|
|
4900
|
+
async function detectHermesVersion() {
|
|
4901
|
+
const raw = await execVersion("hermes");
|
|
4902
|
+
const match = raw.match(/^\(?(\d+(?:\.\d+)*)\)?/);
|
|
4903
|
+
return match?.[1] ?? "";
|
|
4904
|
+
}
|
|
4905
|
+
async function detectOpenclawVersion() {
|
|
4906
|
+
const raw = await execVersion("openclaw");
|
|
4907
|
+
const match = raw.match(/^([\d.]+)/);
|
|
4908
|
+
return match?.[1] ?? "";
|
|
4909
|
+
}
|
|
4796
4910
|
async function getAgentVersion(agentType) {
|
|
4797
4911
|
if (VERSION_CACHE.has(agentType)) return VERSION_CACHE.get(agentType);
|
|
4798
4912
|
const detector = DETECTORS[agentType];
|
|
@@ -4825,7 +4939,9 @@ var init_agent_version = __esm({
|
|
|
4825
4939
|
cursor: detectCursorVersion,
|
|
4826
4940
|
codebuddy: detectCodebuddyCliVersion,
|
|
4827
4941
|
"codebuddy-ide": detectCodebuddyIdeVersion,
|
|
4828
|
-
workbuddy: detectWorkbuddyVersion
|
|
4942
|
+
workbuddy: detectWorkbuddyVersion,
|
|
4943
|
+
hermes: detectHermesVersion,
|
|
4944
|
+
openclaw: detectOpenclawVersion
|
|
4829
4945
|
};
|
|
4830
4946
|
}
|
|
4831
4947
|
});
|
|
@@ -4834,24 +4950,29 @@ var init_agent_version = __esm({
|
|
|
4834
4950
|
import crypto2 from "crypto";
|
|
4835
4951
|
import fs8 from "fs";
|
|
4836
4952
|
import { execFileSync } from "child_process";
|
|
4953
|
+
import os2 from "os";
|
|
4837
4954
|
function getMachineId() {
|
|
4838
4955
|
if (cachedMachineId !== null) return cachedMachineId;
|
|
4839
4956
|
cachedMachineId = detectMachineId();
|
|
4840
4957
|
return cachedMachineId;
|
|
4841
4958
|
}
|
|
4842
4959
|
function detectMachineId(platform = process.platform) {
|
|
4960
|
+
let id = "";
|
|
4843
4961
|
try {
|
|
4844
4962
|
switch (platform) {
|
|
4845
4963
|
case "darwin":
|
|
4846
|
-
|
|
4964
|
+
id = readDarwinMachineId();
|
|
4965
|
+
break;
|
|
4847
4966
|
case "win32":
|
|
4848
|
-
|
|
4967
|
+
id = readWindowsMachineId();
|
|
4968
|
+
break;
|
|
4849
4969
|
default:
|
|
4850
|
-
|
|
4970
|
+
id = readLinuxMachineId();
|
|
4971
|
+
break;
|
|
4851
4972
|
}
|
|
4852
4973
|
} catch {
|
|
4853
|
-
return "";
|
|
4854
4974
|
}
|
|
4975
|
+
return id || os2.hostname() || "";
|
|
4855
4976
|
}
|
|
4856
4977
|
function readDarwinMachineId() {
|
|
4857
4978
|
const out = execFileSync("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
@@ -4896,7 +5017,7 @@ var init_machine_id = __esm({
|
|
|
4896
5017
|
});
|
|
4897
5018
|
|
|
4898
5019
|
// src/utils/path-safety.ts
|
|
4899
|
-
import
|
|
5020
|
+
import os3 from "os";
|
|
4900
5021
|
import path19 from "path";
|
|
4901
5022
|
import fs9 from "fs";
|
|
4902
5023
|
function assertSafePath(target, allowedRoots) {
|
|
@@ -4912,7 +5033,7 @@ function assertSafePath(target, allowedRoots) {
|
|
|
4912
5033
|
);
|
|
4913
5034
|
}
|
|
4914
5035
|
function resolveReal(p) {
|
|
4915
|
-
const expanded = p.startsWith("~") ? path19.join(
|
|
5036
|
+
const expanded = p.startsWith("~") ? path19.join(os3.homedir(), p.slice(1)) : p;
|
|
4916
5037
|
const abs = path19.resolve(expanded);
|
|
4917
5038
|
try {
|
|
4918
5039
|
return fs9.realpathSync(abs);
|
|
@@ -4921,7 +5042,7 @@ function resolveReal(p) {
|
|
|
4921
5042
|
}
|
|
4922
5043
|
}
|
|
4923
5044
|
function defaultAllowedRoots() {
|
|
4924
|
-
return [process.cwd(),
|
|
5045
|
+
return [process.cwd(), os3.homedir()];
|
|
4925
5046
|
}
|
|
4926
5047
|
function assertSafeResourceName(name) {
|
|
4927
5048
|
if (name.includes("\0")) {
|
|
@@ -5300,7 +5421,7 @@ __export(local_agent_exports, {
|
|
|
5300
5421
|
writeTokenFile: () => writeTokenFile
|
|
5301
5422
|
});
|
|
5302
5423
|
import fs10 from "fs";
|
|
5303
|
-
import
|
|
5424
|
+
import os4 from "os";
|
|
5304
5425
|
import path20 from "path";
|
|
5305
5426
|
import readline3 from "readline";
|
|
5306
5427
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -5525,7 +5646,7 @@ function createResourceLocalConfig(config, scope, repoPath, workspacePath) {
|
|
|
5525
5646
|
const projectScope = scope === "project";
|
|
5526
5647
|
return {
|
|
5527
5648
|
repo: { localPath: repoPath, remote: config.endpoint },
|
|
5528
|
-
username:
|
|
5649
|
+
username: os4.userInfo().username,
|
|
5529
5650
|
scope: projectScope ? "project" : "user",
|
|
5530
5651
|
projectRoot: projectScope ? workspacePath : void 0,
|
|
5531
5652
|
additionalRoles: []
|
|
@@ -5835,7 +5956,7 @@ async function bindWorkspaceToProject(workspacePath, projectId) {
|
|
|
5835
5956
|
async function ensureWorkspaceBinding(config, workspacePath, sessionId) {
|
|
5836
5957
|
if (config.workspaceBindings[workspacePath]) return;
|
|
5837
5958
|
const markerKey = sessionId || `ppid-${process.ppid}`;
|
|
5838
|
-
const hintMarker = path20.join(
|
|
5959
|
+
const hintMarker = path20.join(os4.tmpdir(), `teamai-bind-session-${markerKey}`);
|
|
5839
5960
|
if (fs10.existsSync(hintMarker)) return;
|
|
5840
5961
|
try {
|
|
5841
5962
|
fs10.writeFileSync(hintMarker, "");
|
|
@@ -5883,7 +6004,7 @@ function isBindPromptEnabled() {
|
|
|
5883
6004
|
async function emitBindingHint(config, workspacePath, sessionId) {
|
|
5884
6005
|
if (config.workspaceBindings[workspacePath]) return;
|
|
5885
6006
|
const markerKey = sessionId || `ppid-${process.ppid}`;
|
|
5886
|
-
const hintMarker = path20.join(
|
|
6007
|
+
const hintMarker = path20.join(os4.tmpdir(), `teamai-bind-hint-${markerKey}`);
|
|
5887
6008
|
if (fs10.existsSync(hintMarker)) return;
|
|
5888
6009
|
try {
|
|
5889
6010
|
fs10.writeFileSync(hintMarker, "");
|
|
@@ -6044,8 +6165,8 @@ async function buildReportPayload(config, context) {
|
|
|
6044
6165
|
agent_type: normalizeAgentType(tool),
|
|
6045
6166
|
agent_version: await getAgentVersion(tool),
|
|
6046
6167
|
local_agent_id: resolveLocalAgentId(context),
|
|
6047
|
-
host_name:
|
|
6048
|
-
os:
|
|
6168
|
+
host_name: os4.hostname(),
|
|
6169
|
+
os: os4.platform(),
|
|
6049
6170
|
started_at: config.createdAt,
|
|
6050
6171
|
last_status: context.status ?? "running",
|
|
6051
6172
|
// Instance-level skills/rules are a phase-1 legacy concept. They are
|
|
@@ -6156,7 +6277,7 @@ function assertHttpUrl(rawUrl) {
|
|
|
6156
6277
|
return parsed;
|
|
6157
6278
|
}
|
|
6158
6279
|
async function downloadResource(downloadUrl) {
|
|
6159
|
-
const tmpDir = await fs10.promises.mkdtemp(path20.join(
|
|
6280
|
+
const tmpDir = await fs10.promises.mkdtemp(path20.join(os4.tmpdir(), "teamai-local-agent-"));
|
|
6160
6281
|
const filePath = path20.join(tmpDir, "resource");
|
|
6161
6282
|
let current = assertHttpUrl(downloadUrl);
|
|
6162
6283
|
let response;
|
|
@@ -6326,7 +6447,7 @@ async function installDownloadedResource(input) {
|
|
|
6326
6447
|
const dest = path20.join(repoPath, "claudemd", `${input.slug}.md`);
|
|
6327
6448
|
await fse3.ensureDir(path20.dirname(dest));
|
|
6328
6449
|
await fse3.copyFile(mdFile, dest);
|
|
6329
|
-
await syncClaudemd(teamConfig, localConfig, repoPath);
|
|
6450
|
+
await syncClaudemd(teamConfig, localConfig, repoPath, input.workspacePath);
|
|
6330
6451
|
}
|
|
6331
6452
|
const version2 = commandVersion(input.command, input.kind);
|
|
6332
6453
|
const manifest = await loadManifest();
|
|
@@ -6364,12 +6485,28 @@ async function uninstallResource(input) {
|
|
|
6364
6485
|
await new RulesHandler().removeItem(input.slug, teamConfig, localConfig);
|
|
6365
6486
|
} else {
|
|
6366
6487
|
await remove(path20.join(repoPath, "claudemd", `${input.slug}.md`));
|
|
6367
|
-
await syncClaudemd(teamConfig, localConfig, repoPath);
|
|
6488
|
+
await syncClaudemd(teamConfig, localConfig, repoPath, input.workspacePath);
|
|
6368
6489
|
}
|
|
6369
6490
|
delete scopeManifest[manifestKind(input.kind)][input.slug];
|
|
6370
6491
|
await saveManifest(manifest);
|
|
6371
6492
|
}
|
|
6372
|
-
async function
|
|
6493
|
+
async function resolveHermesUserBaseDir() {
|
|
6494
|
+
try {
|
|
6495
|
+
const envWs = process.env.TEAMAI_HERMES_WORKSPACE;
|
|
6496
|
+
if (envWs && path20.isAbsolute(envWs)) return envWs;
|
|
6497
|
+
const cfg = await readJson(getConfigPath2());
|
|
6498
|
+
const bindings = cfg?.workspaceBindings;
|
|
6499
|
+
if (bindings && typeof bindings === "object") {
|
|
6500
|
+
const entries = Object.entries(bindings).filter(([p, v]) => path20.isAbsolute(p) && v?.ideType === "hermes").sort((a, b) => (b[1].boundAt ?? "").localeCompare(a[1].boundAt ?? ""));
|
|
6501
|
+
for (const [p] of entries) {
|
|
6502
|
+
if (await pathExists(path20.join(p, ".hermes"))) return p;
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
} catch {
|
|
6506
|
+
}
|
|
6507
|
+
return void 0;
|
|
6508
|
+
}
|
|
6509
|
+
async function syncClaudemd(teamConfig, localConfig, repoPath, workspacePath) {
|
|
6373
6510
|
const claudemdDir = path20.join(repoPath, "claudemd");
|
|
6374
6511
|
const files = await pathExists(claudemdDir) ? (await fse3.readdir(claudemdDir)).filter((file) => file.endsWith(".md")).sort() : [];
|
|
6375
6512
|
const contents = [];
|
|
@@ -6378,24 +6515,48 @@ async function syncClaudemd(teamConfig, localConfig, repoPath) {
|
|
|
6378
6515
|
if (content) contents.push(content);
|
|
6379
6516
|
}
|
|
6380
6517
|
const block = compileClaudemdBlock(contents);
|
|
6381
|
-
|
|
6518
|
+
let syncedAny = false;
|
|
6519
|
+
const defaultBaseDir = localConfig.scope === "project" && localConfig.projectRoot ? localConfig.projectRoot : process.env.HOME ?? "";
|
|
6382
6520
|
for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
|
|
6383
6521
|
if (!toolPath.claudemd) continue;
|
|
6384
|
-
|
|
6385
|
-
|
|
6522
|
+
let baseDir = defaultBaseDir;
|
|
6523
|
+
let resolvedAbsPath = null;
|
|
6524
|
+
if (tool === "openclaw" && localConfig.scope !== "project") {
|
|
6525
|
+
const openclawWs = await resolveOpenclawWorkspaceDir(workspacePath);
|
|
6526
|
+
if (openclawWs) {
|
|
6527
|
+
resolvedAbsPath = path20.join(openclawWs, path20.basename(toolPath.claudemd));
|
|
6528
|
+
}
|
|
6529
|
+
} else if (tool === "hermes" && localConfig.scope !== "project") {
|
|
6530
|
+
const hermesBase = workspacePath ?? await resolveHermesUserBaseDir();
|
|
6531
|
+
if (hermesBase) {
|
|
6532
|
+
baseDir = hermesBase;
|
|
6533
|
+
log.debug(`local-agent: hermes user-scope baseDir resolved to ${baseDir}`);
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
const toolInstalled = resolvedAbsPath ? await pathExists(resolvedAbsPath) : toolPath.claudemd.includes("/") ? await ResourceHandler.isToolInstalled(toolPath.claudemd, baseDir) : await pathExists(path20.join(baseDir, `.${tool}`));
|
|
6537
|
+
if (!toolInstalled) {
|
|
6538
|
+
log.debug(`Skipped CLAUDE.md sync for ${tool}: target not found`);
|
|
6539
|
+
continue;
|
|
6540
|
+
}
|
|
6541
|
+
const claudeMdPath = resolvedAbsPath ?? path20.join(baseDir, toolPath.claudemd);
|
|
6386
6542
|
try {
|
|
6387
6543
|
const { injectClaudeMdSection: injectClaudeMdSection2 } = await Promise.resolve().then(() => (init_claudemd(), claudemd_exports));
|
|
6388
6544
|
if (block) {
|
|
6389
6545
|
await injectClaudeMdSection2(claudeMdPath, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, block);
|
|
6390
6546
|
log.debug(`local-agent: synced CLAUDE.md instructions to ${tool}`);
|
|
6547
|
+
syncedAny = true;
|
|
6391
6548
|
} else {
|
|
6392
6549
|
await removeClaudeMdSection(claudeMdPath, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END);
|
|
6393
6550
|
log.debug(`local-agent: removed CLAUDE.md instructions from ${tool}`);
|
|
6551
|
+
syncedAny = true;
|
|
6394
6552
|
}
|
|
6395
6553
|
} catch (e) {
|
|
6396
6554
|
log.warn(`Failed to sync CLAUDE.md instructions to ${tool}: ${e.message}`);
|
|
6397
6555
|
}
|
|
6398
6556
|
}
|
|
6557
|
+
if (files.length > 0 && !syncedAny) {
|
|
6558
|
+
throw new Error("CLAUDE.md sync landed on no tool: every configured target was skipped");
|
|
6559
|
+
}
|
|
6399
6560
|
}
|
|
6400
6561
|
async function removeClaudeMdSection(filePath, startMarker, endMarker) {
|
|
6401
6562
|
const existing = await readFileSafe(filePath);
|
|
@@ -6521,6 +6682,9 @@ async function runHookRuleCommand(config, command, context) {
|
|
|
6521
6682
|
if (rec.tool === "hermes") {
|
|
6522
6683
|
const { removeHermesAgentHook: removeHermesAgentHook2 } = await Promise.resolve().then(() => (init_hermes_hooks(), hermes_hooks_exports));
|
|
6523
6684
|
await removeHermesAgentHook2({ slug, event: rec.event, command: rec.command });
|
|
6685
|
+
} else if (OPENCLAW_TOOLS.has(rec.tool)) {
|
|
6686
|
+
const { removeOpenClawAgentHook: removeOpenClawAgentHook2 } = await Promise.resolve().then(() => (init_openclaw_hooks(), openclaw_hooks_exports));
|
|
6687
|
+
await removeOpenClawAgentHook2({ slug, tool: rec.tool });
|
|
6524
6688
|
} else {
|
|
6525
6689
|
const settingsPath = resolveToolSettingsPath(config, rec.tool);
|
|
6526
6690
|
await removeAgentHook(settingsPath, rec.tool, { slug, command: rec.command });
|
|
@@ -6546,6 +6710,9 @@ async function runHookRuleCommand(config, command, context) {
|
|
|
6546
6710
|
if (prior.tool === "hermes") {
|
|
6547
6711
|
const { removeHermesAgentHook: removeHermesAgentHook2 } = await Promise.resolve().then(() => (init_hermes_hooks(), hermes_hooks_exports));
|
|
6548
6712
|
await removeHermesAgentHook2({ slug, event: prior.event, command: prior.command });
|
|
6713
|
+
} else if (OPENCLAW_TOOLS.has(prior.tool)) {
|
|
6714
|
+
const { removeOpenClawAgentHook: removeOpenClawAgentHook2 } = await Promise.resolve().then(() => (init_openclaw_hooks(), openclaw_hooks_exports));
|
|
6715
|
+
await removeOpenClawAgentHook2({ slug, tool: prior.tool });
|
|
6549
6716
|
} else {
|
|
6550
6717
|
const priorPath = resolveToolSettingsPath(config, prior.tool);
|
|
6551
6718
|
await removeAgentHook(priorPath, prior.tool, { slug, command: prior.command });
|
|
@@ -6557,6 +6724,9 @@ async function runHookRuleCommand(config, command, context) {
|
|
|
6557
6724
|
if (tool === "hermes") {
|
|
6558
6725
|
const { applyHermesAgentHook: applyHermesAgentHook2 } = await Promise.resolve().then(() => (init_hermes_hooks(), hermes_hooks_exports));
|
|
6559
6726
|
await applyHermesAgentHook2({ slug, event, command: cmd, matcher, timeout });
|
|
6727
|
+
} else if (OPENCLAW_TOOLS.has(tool)) {
|
|
6728
|
+
const { applyOpenClawAgentHook: applyOpenClawAgentHook2 } = await Promise.resolve().then(() => (init_openclaw_hooks(), openclaw_hooks_exports));
|
|
6729
|
+
await applyOpenClawAgentHook2({ slug, event, command: cmd, tool, matcher, timeout });
|
|
6560
6730
|
} else {
|
|
6561
6731
|
const settingsPath = resolveToolSettingsPath(config, tool);
|
|
6562
6732
|
await applyAgentHook(settingsPath, tool, { slug, event, command: cmd, matcher, timeout });
|
|
@@ -6781,6 +6951,9 @@ async function removeAllAgentHooks() {
|
|
|
6781
6951
|
if (rec.tool === "hermes") {
|
|
6782
6952
|
const { removeHermesAgentHook: removeHermesAgentHook2 } = await Promise.resolve().then(() => (init_hermes_hooks(), hermes_hooks_exports));
|
|
6783
6953
|
await removeHermesAgentHook2({ slug, event: rec.event, command: rec.command });
|
|
6954
|
+
} else if (OPENCLAW_TOOLS.has(rec.tool)) {
|
|
6955
|
+
const { removeOpenClawAgentHook: removeOpenClawAgentHook2 } = await Promise.resolve().then(() => (init_openclaw_hooks(), openclaw_hooks_exports));
|
|
6956
|
+
await removeOpenClawAgentHook2({ slug, tool: rec.tool });
|
|
6784
6957
|
} else {
|
|
6785
6958
|
const settingsPath = resolveToolSettingsPath(config, rec.tool);
|
|
6786
6959
|
await removeAgentHook(settingsPath, rec.tool, { slug, command: rec.command });
|
|
@@ -6854,6 +7027,7 @@ var init_local_agent = __esm({
|
|
|
6854
7027
|
init_machine_id();
|
|
6855
7028
|
init_builtin_rules();
|
|
6856
7029
|
init_builtin_hooks();
|
|
7030
|
+
init_openclaw_hooks();
|
|
6857
7031
|
init_path_safety();
|
|
6858
7032
|
init_tool_names();
|
|
6859
7033
|
init_http_log();
|
|
@@ -7442,6 +7616,7 @@ var init_skills = __esm({
|
|
|
7442
7616
|
init_fs();
|
|
7443
7617
|
init_logger();
|
|
7444
7618
|
init_builtin_skills();
|
|
7619
|
+
init_openclaw_hooks();
|
|
7445
7620
|
init_roles();
|
|
7446
7621
|
CONTRIBUTORS_FILE = "CONTRIBUTORS";
|
|
7447
7622
|
SKILL_MD = "SKILL.md";
|
|
@@ -7627,11 +7802,21 @@ var init_skills = __esm({
|
|
|
7627
7802
|
for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
|
|
7628
7803
|
if (isAgentDisabled(localConfig, tool)) continue;
|
|
7629
7804
|
if (!toolPath.skills) continue;
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7805
|
+
let dest;
|
|
7806
|
+
if (tool === "openclaw" && localConfig.scope !== "project") {
|
|
7807
|
+
const wsDir = await resolveOpenclawWorkspaceDir();
|
|
7808
|
+
if (!wsDir) {
|
|
7809
|
+
log.debug(`Skipping skill sync for openclaw: workspace dir not found`);
|
|
7810
|
+
continue;
|
|
7811
|
+
}
|
|
7812
|
+
dest = path22.join(wsDir, "skills", item.name);
|
|
7813
|
+
} else {
|
|
7814
|
+
if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) {
|
|
7815
|
+
log.debug(`Skipping skill sync for ${tool}: tool not installed`);
|
|
7816
|
+
continue;
|
|
7817
|
+
}
|
|
7818
|
+
dest = path22.join(baseDir, toolPath.skills, item.name);
|
|
7633
7819
|
}
|
|
7634
|
-
const dest = path22.join(baseDir, toolPath.skills, item.name);
|
|
7635
7820
|
try {
|
|
7636
7821
|
await copyDir(item.sourcePath, dest);
|
|
7637
7822
|
await ensureSkillFrontmatter(dest, item.name);
|
|
@@ -7666,7 +7851,14 @@ var init_skills = __esm({
|
|
|
7666
7851
|
await this.addTombstone(name, localConfig);
|
|
7667
7852
|
for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
|
|
7668
7853
|
if (!toolPath.skills) continue;
|
|
7669
|
-
|
|
7854
|
+
let skillDir;
|
|
7855
|
+
if (tool === "openclaw" && localConfig.scope !== "project") {
|
|
7856
|
+
const wsDir = await resolveOpenclawWorkspaceDir();
|
|
7857
|
+
if (!wsDir) continue;
|
|
7858
|
+
skillDir = path22.join(wsDir, "skills", name);
|
|
7859
|
+
} else {
|
|
7860
|
+
skillDir = path22.join(baseDir, toolPath.skills, name);
|
|
7861
|
+
}
|
|
7670
7862
|
if (await pathExists(skillDir)) {
|
|
7671
7863
|
await remove(skillDir);
|
|
7672
7864
|
removed.push(skillDir);
|
|
@@ -9676,6 +9868,11 @@ async function push(options) {
|
|
|
9676
9868
|
const { localConfig, teamConfig } = await autoDetectInit();
|
|
9677
9869
|
assertNotReadOnly(localConfig, "teamai push");
|
|
9678
9870
|
if (localConfig.repo.kind === "self") {
|
|
9871
|
+
try {
|
|
9872
|
+
const { migrateSelfModeGitignore: migrateSelfModeGitignore2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
9873
|
+
await migrateSelfModeGitignore2(localConfig);
|
|
9874
|
+
} catch {
|
|
9875
|
+
}
|
|
9679
9876
|
const { withKnowledgeWorktree: withKnowledgeWorktree2, EmptyRepoError: EmptyRepoError2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
9680
9877
|
try {
|
|
9681
9878
|
await withKnowledgeWorktree2(localConfig, (wtConfig) => pushCore(wtConfig, teamConfig, options));
|
|
@@ -9738,8 +9935,8 @@ async function pushCore(localConfig, teamConfig, options) {
|
|
|
9738
9935
|
process.exitCode = 2;
|
|
9739
9936
|
return;
|
|
9740
9937
|
}
|
|
9741
|
-
const
|
|
9742
|
-
const skillPath = options.skill.startsWith("~") ? path32.join(
|
|
9938
|
+
const os8 = await import("os");
|
|
9939
|
+
const skillPath = options.skill.startsWith("~") ? path32.join(os8.homedir(), options.skill.slice(1)) : path32.resolve(options.skill);
|
|
9743
9940
|
let matchedItem;
|
|
9744
9941
|
for (const item of allItems) {
|
|
9745
9942
|
if (item.type !== "skills") continue;
|
|
@@ -9751,7 +9948,7 @@ async function pushCore(localConfig, teamConfig, options) {
|
|
|
9751
9948
|
matchedItem = item;
|
|
9752
9949
|
break;
|
|
9753
9950
|
}
|
|
9754
|
-
const skillInput = options.skill.replace(/^~/,
|
|
9951
|
+
const skillInput = options.skill.replace(/^~/, os8.homedir());
|
|
9755
9952
|
if (item.sourcePath.endsWith(skillInput) || item.sourcePath.includes(path32.sep + skillInput)) {
|
|
9756
9953
|
matchedItem = item;
|
|
9757
9954
|
break;
|
|
@@ -10845,6 +11042,8 @@ __export(init_exports, {
|
|
|
10845
11042
|
init: () => init,
|
|
10846
11043
|
initHttp: () => initHttp,
|
|
10847
11044
|
initSelfRepo: () => initSelfRepo,
|
|
11045
|
+
migrateSelfModeGitignore: () => migrateSelfModeGitignore,
|
|
11046
|
+
migrateSelfModeGitignoreContent: () => migrateSelfModeGitignoreContent,
|
|
10848
11047
|
promptForSelfModeAgents: () => promptForSelfModeAgents,
|
|
10849
11048
|
resolveInheritUserScope: () => resolveInheritUserScope,
|
|
10850
11049
|
resolveInitRepo: () => resolveInitRepo,
|
|
@@ -11136,6 +11335,45 @@ function buildSelfModeGitignore() {
|
|
|
11136
11335
|
""
|
|
11137
11336
|
].join("\n");
|
|
11138
11337
|
}
|
|
11338
|
+
function migrateSelfModeGitignoreContent(content) {
|
|
11339
|
+
const lines = content.split("\n");
|
|
11340
|
+
let changed = false;
|
|
11341
|
+
const filtered = lines.filter((line) => {
|
|
11342
|
+
if (line.trim() === "env") {
|
|
11343
|
+
changed = true;
|
|
11344
|
+
return false;
|
|
11345
|
+
}
|
|
11346
|
+
return true;
|
|
11347
|
+
});
|
|
11348
|
+
const hasEnvLocal = filtered.some((l) => l.trim() === "env.local");
|
|
11349
|
+
if (!hasEnvLocal) {
|
|
11350
|
+
const envShIdx = filtered.findIndex((l) => l.trim() === "env.sh");
|
|
11351
|
+
if (envShIdx >= 0) {
|
|
11352
|
+
filtered.splice(envShIdx + 1, 0, "env.local");
|
|
11353
|
+
} else {
|
|
11354
|
+
const lastNonEmpty = filtered.reduce((acc, l, i) => l.trim() ? i : acc, -1);
|
|
11355
|
+
filtered.splice(lastNonEmpty + 1, 0, "env.local");
|
|
11356
|
+
}
|
|
11357
|
+
changed = true;
|
|
11358
|
+
}
|
|
11359
|
+
return { changed, content: filtered.join("\n") };
|
|
11360
|
+
}
|
|
11361
|
+
async function migrateSelfModeGitignore(localConfig) {
|
|
11362
|
+
if (localConfig.repo.kind !== "self" || !localConfig.projectRoot) return;
|
|
11363
|
+
const gitignorePath = path38.join(localConfig.projectRoot, ".teamai", ".gitignore");
|
|
11364
|
+
try {
|
|
11365
|
+
const current = await readFileSafe(gitignorePath);
|
|
11366
|
+
if (current === null) return;
|
|
11367
|
+
const { changed, content } = migrateSelfModeGitignoreContent(current);
|
|
11368
|
+
if (!changed) return;
|
|
11369
|
+
await writeFile(gitignorePath, content);
|
|
11370
|
+
log.info(
|
|
11371
|
+
"Updated .teamai/.gitignore so team env vars (.teamai/env/env.yaml) can be shared \u2014 please `git add .teamai/.gitignore` and commit it."
|
|
11372
|
+
);
|
|
11373
|
+
} catch (e) {
|
|
11374
|
+
log.debug(`[self-mode] gitignore migration skipped: ${e.message}`);
|
|
11375
|
+
}
|
|
11376
|
+
}
|
|
11139
11377
|
function resolveSelfModeSelection(indices, detected) {
|
|
11140
11378
|
const out = [];
|
|
11141
11379
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11863,13 +12101,10 @@ function tokenize(text) {
|
|
|
11863
12101
|
if (t !== word) tokens.push(t);
|
|
11864
12102
|
}
|
|
11865
12103
|
}
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
} else {
|
|
11871
|
-
flushCjkRun();
|
|
11872
|
-
}
|
|
12104
|
+
if (word.length === 1 && /[一-鿿]/.test(word)) {
|
|
12105
|
+
cjkRun.push(word);
|
|
12106
|
+
} else {
|
|
12107
|
+
flushCjkRun();
|
|
11873
12108
|
}
|
|
11874
12109
|
}
|
|
11875
12110
|
flushCjkRun();
|
|
@@ -11884,6 +12119,40 @@ function tokenCount(text) {
|
|
|
11884
12119
|
}
|
|
11885
12120
|
return count;
|
|
11886
12121
|
}
|
|
12122
|
+
function wordSegments(text) {
|
|
12123
|
+
if (!text) return [];
|
|
12124
|
+
const input = text.length > MAX_TOKENIZE_CHARS ? text.slice(0, MAX_TOKENIZE_CHARS) : text;
|
|
12125
|
+
const words = [];
|
|
12126
|
+
let run = [];
|
|
12127
|
+
let lastWasCjkWord = false;
|
|
12128
|
+
const flushRun = () => {
|
|
12129
|
+
if (run.length > 0 && lastWasCjkWord) {
|
|
12130
|
+
words[words.length - 1] += run.shift();
|
|
12131
|
+
lastWasCjkWord = false;
|
|
12132
|
+
}
|
|
12133
|
+
if (run.length > 0) {
|
|
12134
|
+
words.push(run.join(""));
|
|
12135
|
+
lastWasCjkWord = false;
|
|
12136
|
+
}
|
|
12137
|
+
run = [];
|
|
12138
|
+
};
|
|
12139
|
+
for (const seg of sharedSegmenter.segment(input)) {
|
|
12140
|
+
if (!seg.isWordLike) {
|
|
12141
|
+
flushRun();
|
|
12142
|
+
lastWasCjkWord = false;
|
|
12143
|
+
continue;
|
|
12144
|
+
}
|
|
12145
|
+
if (seg.segment.length === 1 && /[一-鿿]/.test(seg.segment)) {
|
|
12146
|
+
run.push(seg.segment);
|
|
12147
|
+
continue;
|
|
12148
|
+
}
|
|
12149
|
+
flushRun();
|
|
12150
|
+
words.push(seg.segment);
|
|
12151
|
+
lastWasCjkWord = /^[一-鿿]+$/.test(seg.segment);
|
|
12152
|
+
}
|
|
12153
|
+
flushRun();
|
|
12154
|
+
return words;
|
|
12155
|
+
}
|
|
11887
12156
|
var MAX_TOKENIZE_CHARS, sharedSegmenter;
|
|
11888
12157
|
var init_tokenizer = __esm({
|
|
11889
12158
|
"src/utils/tokenizer.ts"() {
|
|
@@ -12198,7 +12467,8 @@ __export(search_index_exports, {
|
|
|
12198
12467
|
parseLearningDoc: () => parseLearningDoc,
|
|
12199
12468
|
search: () => search,
|
|
12200
12469
|
titleFromFilename: () => titleFromFilename,
|
|
12201
|
-
tokenize: () => tokenize
|
|
12470
|
+
tokenize: () => tokenize,
|
|
12471
|
+
wordSegments: () => wordSegments
|
|
12202
12472
|
});
|
|
12203
12473
|
import path42 from "path";
|
|
12204
12474
|
import matter3 from "gray-matter";
|
|
@@ -12502,6 +12772,8 @@ function search(query, index, limit = 5) {
|
|
|
12502
12772
|
const docFreq = df[token] ?? 0;
|
|
12503
12773
|
return Math.log((N + 1) / (docFreq + 1)) + 1;
|
|
12504
12774
|
};
|
|
12775
|
+
const lengthNorm = Math.sqrt(queryTokens.length);
|
|
12776
|
+
const wordTokens = wordSegments(query).map((w) => ({ word: w, tokens: tokenize(w) }));
|
|
12505
12777
|
const results = [];
|
|
12506
12778
|
for (const entry of index.entries) {
|
|
12507
12779
|
let score = 0;
|
|
@@ -12524,6 +12796,7 @@ function search(query, index, limit = 5) {
|
|
|
12524
12796
|
}
|
|
12525
12797
|
const isCodebaseDoc = entry.type === "docs" && (entry.path ?? entry.filename ?? "").includes("team-codebase");
|
|
12526
12798
|
if (score > 0 && (hasTitleOrTagMatch || isCodebaseDoc)) {
|
|
12799
|
+
score /= lengthNorm;
|
|
12527
12800
|
score += Math.min(entry.votes * 0.5, 5);
|
|
12528
12801
|
const domainMultiplier = domainWeightRow[entry.domain ?? "neutral"];
|
|
12529
12802
|
const typeMultiplier = TYPE_BONUS[entry.type];
|
|
@@ -12534,14 +12807,40 @@ function search(query, index, limit = 5) {
|
|
|
12534
12807
|
if (entry.hotness !== void 0 && entry.hotness < 1) {
|
|
12535
12808
|
score *= entry.hotness;
|
|
12536
12809
|
}
|
|
12537
|
-
|
|
12810
|
+
if (!hasTitleOrTagMatch) {
|
|
12811
|
+
results.push({ entry, score });
|
|
12812
|
+
continue;
|
|
12813
|
+
}
|
|
12814
|
+
const matchedTerms = [];
|
|
12815
|
+
const missingTerms = [];
|
|
12816
|
+
for (const { word, tokens: wt } of wordTokens) {
|
|
12817
|
+
const hit = wt.some((t) => entryTokens.has(`title:${t}`) || entryTokens.has(`tag:${t}`));
|
|
12818
|
+
(hit ? matchedTerms : missingTerms).push(word);
|
|
12819
|
+
}
|
|
12820
|
+
results.push({ entry, score, matchedTerms, missingTerms });
|
|
12538
12821
|
}
|
|
12539
12822
|
}
|
|
12540
12823
|
results.sort((a, b) => {
|
|
12541
12824
|
if (b.score !== a.score) return b.score - a.score;
|
|
12542
12825
|
return (b.entry.date || "").localeCompare(a.entry.date || "");
|
|
12543
12826
|
});
|
|
12544
|
-
|
|
12827
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12828
|
+
const deduped = [];
|
|
12829
|
+
for (const r of results) {
|
|
12830
|
+
const key = [
|
|
12831
|
+
r.entry.type,
|
|
12832
|
+
r.entry.title,
|
|
12833
|
+
r.entry.date,
|
|
12834
|
+
r.entry.author,
|
|
12835
|
+
r.entry.tokens.length,
|
|
12836
|
+
r.score
|
|
12837
|
+
].join(" ");
|
|
12838
|
+
if (seen.has(key)) continue;
|
|
12839
|
+
seen.add(key);
|
|
12840
|
+
deduped.push(r);
|
|
12841
|
+
if (deduped.length === limit) break;
|
|
12842
|
+
}
|
|
12843
|
+
return deduped;
|
|
12545
12844
|
}
|
|
12546
12845
|
var MAX_BODY_CHARS, MAX_DOC_BYTES, TECHNICAL_TAGS, OPS_TAGS, SUPPORT_TAGS, TECHNICAL_PATH_PATTERNS, OPS_PATH_PATTERNS, SUPPORT_PATH_PATTERNS, DOMAIN_WEIGHT, TYPE_BONUS, CODEBASE_INDEX_FILENAME, CODEBASE_FULL_FILENAME, CODEBASE_INDEX_WEIGHT_BOOST;
|
|
12547
12846
|
var init_search_index = __esm({
|
|
@@ -13303,6 +13602,7 @@ var init_skill_health = __esm({
|
|
|
13303
13602
|
// src/digest.ts
|
|
13304
13603
|
var digest_exports = {};
|
|
13305
13604
|
__export(digest_exports, {
|
|
13605
|
+
buildSkillChangePathspec: () => buildSkillChangePathspec,
|
|
13306
13606
|
formatTokenCount: () => formatTokenCount,
|
|
13307
13607
|
generateDigest: () => generateDigest,
|
|
13308
13608
|
summarizeConversation: () => summarizeConversation,
|
|
@@ -13353,9 +13653,17 @@ function parseGitLogOutput(output) {
|
|
|
13353
13653
|
if (current) commits.push(current);
|
|
13354
13654
|
return commits;
|
|
13355
13655
|
}
|
|
13356
|
-
|
|
13656
|
+
function buildSkillChangePathspec(subdir = "") {
|
|
13657
|
+
const prefix = subdir ? `${subdir.replace(/\/+$/, "")}/` : "";
|
|
13658
|
+
const pathspec = `${prefix}skills/*/SKILL.md`;
|
|
13659
|
+
const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13660
|
+
const skillRe = new RegExp(`^${escaped}skills/([^/]+)/SKILL\\.md$`);
|
|
13661
|
+
return { pathspec, skillRe };
|
|
13662
|
+
}
|
|
13663
|
+
async function getRecentSkillChanges(repoPath, subdir = "") {
|
|
13357
13664
|
const seen = /* @__PURE__ */ new Set();
|
|
13358
13665
|
const changes = [];
|
|
13666
|
+
const { pathspec, skillRe } = buildSkillChangePathspec(subdir);
|
|
13359
13667
|
try {
|
|
13360
13668
|
const git = createGit2(repoPath);
|
|
13361
13669
|
const rawOutput = await git.raw([
|
|
@@ -13365,13 +13673,13 @@ async function getRecentSkillChanges(repoPath) {
|
|
|
13365
13673
|
"--name-only",
|
|
13366
13674
|
"--pretty=format:%H|%an|%s",
|
|
13367
13675
|
"--",
|
|
13368
|
-
|
|
13676
|
+
pathspec
|
|
13369
13677
|
]);
|
|
13370
13678
|
if (!rawOutput.trim()) return changes;
|
|
13371
13679
|
const commits = parseGitLogOutput(rawOutput);
|
|
13372
13680
|
for (const commit of commits) {
|
|
13373
13681
|
for (const file of commit.files) {
|
|
13374
|
-
const match = file.match(
|
|
13682
|
+
const match = file.match(skillRe);
|
|
13375
13683
|
if (!match) continue;
|
|
13376
13684
|
const skillName = match[1];
|
|
13377
13685
|
if (seen.has(skillName)) continue;
|
|
@@ -13389,14 +13697,14 @@ async function getRecentSkillChanges(repoPath) {
|
|
|
13389
13697
|
"--name-only",
|
|
13390
13698
|
"--pretty=format:%H|%an|%s",
|
|
13391
13699
|
"--",
|
|
13392
|
-
|
|
13700
|
+
pathspec
|
|
13393
13701
|
]);
|
|
13394
13702
|
const newSkills = /* @__PURE__ */ new Set();
|
|
13395
13703
|
if (addedOutput.trim()) {
|
|
13396
13704
|
const addedCommits = parseGitLogOutput(addedOutput);
|
|
13397
13705
|
for (const commit of addedCommits) {
|
|
13398
13706
|
for (const file of commit.files) {
|
|
13399
|
-
const match = file.match(
|
|
13707
|
+
const match = file.match(skillRe);
|
|
13400
13708
|
if (match) {
|
|
13401
13709
|
newSkills.add(match[1]);
|
|
13402
13710
|
}
|
|
@@ -13577,7 +13885,10 @@ async function generateDigest(options) {
|
|
|
13577
13885
|
console.log(`\u{1F4CA} Knowledge base total: ${totalLearnings} learnings`);
|
|
13578
13886
|
console.log("");
|
|
13579
13887
|
}
|
|
13580
|
-
const skillChanges = await getRecentSkillChanges(
|
|
13888
|
+
const skillChanges = localConfig.repo.kind === "self" ? await getRecentSkillChanges(
|
|
13889
|
+
localConfig.repo.businessRepoRoot ?? path44.dirname(repoPath),
|
|
13890
|
+
".teamai"
|
|
13891
|
+
) : await getRecentSkillChanges(repoPath);
|
|
13581
13892
|
const newSkills = skillChanges.filter((c) => c.type === "new");
|
|
13582
13893
|
const updatedSkills = skillChanges.filter((c) => c.type === "updated");
|
|
13583
13894
|
if (newSkills.length > 0) {
|
|
@@ -14648,6 +14959,11 @@ async function refreshTeamRepo(localConfig) {
|
|
|
14648
14959
|
return { label: "HTTP (report/sync delivery)", version: null, reportingOnly: true };
|
|
14649
14960
|
}
|
|
14650
14961
|
if (localConfig.repo.kind === "self") {
|
|
14962
|
+
try {
|
|
14963
|
+
const { migrateSelfModeGitignore: migrateSelfModeGitignore2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
14964
|
+
await migrateSelfModeGitignore2(localConfig);
|
|
14965
|
+
} catch {
|
|
14966
|
+
}
|
|
14651
14967
|
let version3 = null;
|
|
14652
14968
|
try {
|
|
14653
14969
|
version3 = await getHeadRev(localConfig.repo.localPath);
|
|
@@ -15041,7 +15357,15 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
15041
15357
|
const docsRepoDir = path48.join(localConfig.repo.localPath, "docs");
|
|
15042
15358
|
const rulesRepoDir = path48.join(localConfig.repo.localPath, "rules");
|
|
15043
15359
|
const skillsRepoDir = path48.join(localConfig.repo.localPath, "skills");
|
|
15044
|
-
|
|
15360
|
+
let votesDir = path48.join(localConfig.repo.localPath, "votes");
|
|
15361
|
+
if (localConfig.repo.kind === "self") {
|
|
15362
|
+
try {
|
|
15363
|
+
const { ensureReportsWorktree: ensureReportsWorktree2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
15364
|
+
votesDir = path48.join(await ensureReportsWorktree2(localConfig), "votes");
|
|
15365
|
+
} catch (e) {
|
|
15366
|
+
log.debug(`[self] reports worktree for votes unavailable: ${e.message}`);
|
|
15367
|
+
}
|
|
15368
|
+
}
|
|
15045
15369
|
let learningsCount = 0;
|
|
15046
15370
|
let effectiveLearningsDir;
|
|
15047
15371
|
if (localConfig.scope === "user") {
|
|
@@ -15207,7 +15531,15 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
15207
15531
|
const YAML20 = (await import("yaml")).default;
|
|
15208
15532
|
const { listFiles: listFiles2, readFileSafe: readFileSafe5 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15209
15533
|
const { getRecommendations: getRecommendations2, displayRecommendations: displayRecommendations2 } = await Promise.resolve().then(() => (init_skill_recommend(), skill_recommend_exports));
|
|
15210
|
-
|
|
15534
|
+
let statsDir = path48.join(localConfig.repo.localPath, "stats");
|
|
15535
|
+
if (localConfig.repo.kind === "self") {
|
|
15536
|
+
try {
|
|
15537
|
+
const { ensureReportsWorktree: ensureReportsWorktree2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
15538
|
+
statsDir = path48.join(await ensureReportsWorktree2(localConfig), "stats");
|
|
15539
|
+
} catch (e) {
|
|
15540
|
+
log.debug(`[self] reports worktree for stats unavailable: ${e.message}`);
|
|
15541
|
+
}
|
|
15542
|
+
}
|
|
15211
15543
|
const files = await listFiles2(statsDir);
|
|
15212
15544
|
const teamStats = [];
|
|
15213
15545
|
for (const file of files) {
|
|
@@ -17229,9 +17561,13 @@ async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, te
|
|
|
17229
17561
|
res.hookFiles.push({ path: settingsPath, tool });
|
|
17230
17562
|
}
|
|
17231
17563
|
} else {
|
|
17232
|
-
const
|
|
17233
|
-
|
|
17234
|
-
|
|
17564
|
+
const defaultHooksDir = path56.join(baseDir, `.${tool}`, "hooks");
|
|
17565
|
+
const resolvedHooksDir = resolveOpenClawHooksDir(tool);
|
|
17566
|
+
const dirsToCheck = /* @__PURE__ */ new Set([defaultHooksDir, resolvedHooksDir]);
|
|
17567
|
+
for (const hooksDir of dirsToCheck) {
|
|
17568
|
+
if (await pathExists(path56.join(hooksDir, OPENCLAW_HOOK_DIR))) {
|
|
17569
|
+
res.openclawHookDirs.push({ hooksDir, tool });
|
|
17570
|
+
}
|
|
17235
17571
|
}
|
|
17236
17572
|
}
|
|
17237
17573
|
if (toolPath.claudemd) {
|
|
@@ -20733,15 +21069,31 @@ async function contributeSelf(localConfig, content, options) {
|
|
|
20733
21069
|
await ensureDir(path69.join(wtRepo, "learnings"));
|
|
20734
21070
|
await fs24.promises.writeFile(path69.join(wtRepo, relPath), content, "utf-8");
|
|
20735
21071
|
try {
|
|
21072
|
+
const { pathExists: pathExists3 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
20736
21073
|
const wtLearnings = path69.join(wtRepo, "learnings");
|
|
20737
21074
|
await fse11.copy(wtLearnings, LEARNINGS_LOCAL_DIR, {
|
|
20738
21075
|
overwrite: true,
|
|
20739
21076
|
filter: (src) => !path69.basename(src).startsWith(".")
|
|
20740
21077
|
});
|
|
21078
|
+
const repoPath = localConfig.repo.localPath;
|
|
21079
|
+
const docsDir = path69.join(repoPath, "docs");
|
|
21080
|
+
const rulesDir = path69.join(repoPath, "rules");
|
|
21081
|
+
const skillsDir = path69.join(repoPath, "skills");
|
|
21082
|
+
let votesDir;
|
|
21083
|
+
try {
|
|
21084
|
+
const { ensureReportsWorktree: ensureReportsWorktree2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
21085
|
+
const candidate = path69.join(await ensureReportsWorktree2(localConfig), "votes");
|
|
21086
|
+
if (await pathExists3(candidate)) votesDir = candidate;
|
|
21087
|
+
} catch {
|
|
21088
|
+
}
|
|
20741
21089
|
const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
|
|
20742
21090
|
const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
|
|
20743
21091
|
await buildIndex2({
|
|
20744
|
-
learningsDir: LEARNINGS_LOCAL_DIR,
|
|
21092
|
+
learningsDir: await pathExists3(LEARNINGS_LOCAL_DIR) ? LEARNINGS_LOCAL_DIR : void 0,
|
|
21093
|
+
docsDir: await pathExists3(docsDir) ? docsDir : void 0,
|
|
21094
|
+
rulesDir: await pathExists3(rulesDir) ? rulesDir : void 0,
|
|
21095
|
+
skillsDir: await pathExists3(skillsDir) ? skillsDir : void 0,
|
|
21096
|
+
votesDir,
|
|
20745
21097
|
indexPath: path69.join(teamaiHome, "search-index.json")
|
|
20746
21098
|
});
|
|
20747
21099
|
} catch (e) {
|
|
@@ -21517,7 +21869,7 @@ function formatResults(results) {
|
|
|
21517
21869
|
lines.push(`--- [teamai:recall:start] --- (${results.length} result${results.length !== 1 ? "s" : ""})`);
|
|
21518
21870
|
lines.push("");
|
|
21519
21871
|
for (let i = 0; i < results.length; i++) {
|
|
21520
|
-
const { entry, score, scope, learningsBase, sources } = results[i];
|
|
21872
|
+
const { entry, score, scope, learningsBase, sources, matchedTerms, missingTerms } = results[i];
|
|
21521
21873
|
const voteStr = entry.votes > 0 ? ` \u2605${entry.votes}` : "";
|
|
21522
21874
|
const scopeStr = scope ? ` [${scope}]` : "";
|
|
21523
21875
|
const typeTag = entry.type ? `[${entry.type}] ` : "";
|
|
@@ -21526,6 +21878,10 @@ function formatResults(results) {
|
|
|
21526
21878
|
if (entry.tags.length > 0) {
|
|
21527
21879
|
lines.push(`Tags: ${entry.tags.join(", ")}`);
|
|
21528
21880
|
}
|
|
21881
|
+
if (missingTerms && missingTerms.length > 0) {
|
|
21882
|
+
const matchedStr = matchedTerms && matchedTerms.length > 0 ? matchedTerms.join(", ") : "none";
|
|
21883
|
+
lines.push(`Matched: ${matchedStr} | Missing: ${missingTerms.join(", ")}`);
|
|
21884
|
+
}
|
|
21529
21885
|
const filePath = entry.path ? entry.path : learningsBase ? `${learningsBase}/${entry.filename}` : `~/.teamai/learnings/${entry.filename}`;
|
|
21530
21886
|
lines.push(`File: ${filePath}`);
|
|
21531
21887
|
if (sources && sources.length > 0) {
|
|
@@ -21620,8 +21976,16 @@ async function recall(query, options) {
|
|
|
21620
21976
|
const rounded = Math.round(score * 10) / 10;
|
|
21621
21977
|
const verdict = isRelevantScore(score, isCodebaseHit, baseline) ? "RELEVANT" : "NOT_RELEVANT";
|
|
21622
21978
|
let line = `${verdict} score=${rounded.toFixed(1)}`;
|
|
21979
|
+
const cutoff = isCodebaseHit ? CODEBASE_RELEVANCE_THRESHOLD : Math.max((baseline > 0 ? baseline : 1) * LEARNINGS_RELEVANCE_RATIO, LEARNINGS_ABSOLUTE_FLOOR);
|
|
21980
|
+
line += ` threshold=${(Math.round(cutoff * 10) / 10).toFixed(1)}`;
|
|
21623
21981
|
if (verdict === "RELEVANT" && topResult) {
|
|
21624
21982
|
line += ` title="${topResult.entry.title}"`;
|
|
21983
|
+
if (topResult.matchedTerms && topResult.matchedTerms.length > 0) {
|
|
21984
|
+
line += ` matched=${topResult.matchedTerms.join(",")}`;
|
|
21985
|
+
}
|
|
21986
|
+
if (topResult.missingTerms && topResult.missingTerms.length > 0) {
|
|
21987
|
+
line += ` missing=${topResult.missingTerms.join(",")}`;
|
|
21988
|
+
}
|
|
21625
21989
|
if (topResult.sources && topResult.sources.length > 0) {
|
|
21626
21990
|
const srcStr = topResult.sources.map((s) => s.desc ? `${s.path}(${s.desc})` : s.path).join(",");
|
|
21627
21991
|
line += ` sources=${srcStr}`;
|
|
@@ -21913,10 +22277,10 @@ var init_recall_toggle = __esm({
|
|
|
21913
22277
|
|
|
21914
22278
|
// src/utils/cache-index.ts
|
|
21915
22279
|
import path75 from "path";
|
|
21916
|
-
import
|
|
22280
|
+
import os5 from "os";
|
|
21917
22281
|
import fs25 from "fs-extra";
|
|
21918
22282
|
function getCacheRoot() {
|
|
21919
|
-
return process.env.TEAMAI_CACHE_DIR ?? path75.join(
|
|
22283
|
+
return process.env.TEAMAI_CACHE_DIR ?? path75.join(os5.homedir(), ".teamai", "cache", "repos");
|
|
21920
22284
|
}
|
|
21921
22285
|
function buildKey(provider, owner, repo) {
|
|
21922
22286
|
return `${provider}/${owner}/${repo}`;
|
|
@@ -25862,6 +26226,23 @@ function parseJSON(raw) {
|
|
|
25862
26226
|
return null;
|
|
25863
26227
|
}
|
|
25864
26228
|
}
|
|
26229
|
+
function resolveImportToModule(importerFile, importPath) {
|
|
26230
|
+
if (importPath.startsWith(".")) {
|
|
26231
|
+
const importerDir = path86.dirname(importerFile);
|
|
26232
|
+
const resolved = path86.normalize(path86.join(importerDir, importPath));
|
|
26233
|
+
const topLevel = resolved.split("/")[0];
|
|
26234
|
+
if (!topLevel || topLevel === ".." || topLevel === ".") return void 0;
|
|
26235
|
+
return topLevel;
|
|
26236
|
+
}
|
|
26237
|
+
if (importPath.includes(".") && !importPath.includes("/")) {
|
|
26238
|
+
return importPath.split(".")[0];
|
|
26239
|
+
}
|
|
26240
|
+
const parts = importPath.split("/");
|
|
26241
|
+
const first = parts[0];
|
|
26242
|
+
if (!first) return void 0;
|
|
26243
|
+
if (first.startsWith("@")) return void 0;
|
|
26244
|
+
return first;
|
|
26245
|
+
}
|
|
25865
26246
|
async function enrichWithAI(ctx) {
|
|
25866
26247
|
const moduleEntries = [...ctx.modules.entries()].filter(([, facts]) => facts.length >= 5);
|
|
25867
26248
|
if (moduleEntries.length === 0) {
|
|
@@ -25925,9 +26306,9 @@ async function enrichWithAI(ctx) {
|
|
|
25925
26306
|
const moduleImports = ctx.facts.filter((f) => f.kind === "relation" && f.file.startsWith(name + "/"));
|
|
25926
26307
|
const targetModules = /* @__PURE__ */ new Set();
|
|
25927
26308
|
for (const imp of moduleImports) {
|
|
25928
|
-
const
|
|
25929
|
-
if (
|
|
25930
|
-
targetModules.add(
|
|
26309
|
+
const resolved = resolveImportToModule(imp.file, imp.name);
|
|
26310
|
+
if (resolved && resolved !== name) {
|
|
26311
|
+
targetModules.add(resolved);
|
|
25931
26312
|
}
|
|
25932
26313
|
}
|
|
25933
26314
|
for (const target of targetModules) {
|
|
@@ -26414,7 +26795,19 @@ async function extractCodebase(opts) {
|
|
|
26414
26795
|
let callChains;
|
|
26415
26796
|
const depPathsFile = path87.join(evidenceDir, "dependency-paths.md");
|
|
26416
26797
|
if (changedFiles) {
|
|
26417
|
-
|
|
26798
|
+
let reused = false;
|
|
26799
|
+
try {
|
|
26800
|
+
const existing = await readFile8(depPathsFile, "utf-8");
|
|
26801
|
+
if (existing.trim()) {
|
|
26802
|
+
reused = true;
|
|
26803
|
+
}
|
|
26804
|
+
} catch {
|
|
26805
|
+
}
|
|
26806
|
+
if (reused) {
|
|
26807
|
+
callChains = [];
|
|
26808
|
+
} else {
|
|
26809
|
+
callChains = traceCallChains(facts, files);
|
|
26810
|
+
}
|
|
26418
26811
|
} else {
|
|
26419
26812
|
callChains = traceCallChains(facts, files);
|
|
26420
26813
|
}
|
|
@@ -26699,15 +27092,16 @@ async function shallowClone(url, localPath, provider, opts) {
|
|
|
26699
27092
|
log.debug(`shallowClone: \u4F7F\u7528\u533F\u540D HTTPS \u514B\u9686 github \u4ED3\u5E93`);
|
|
26700
27093
|
}
|
|
26701
27094
|
} else if (provider === "tgit") {
|
|
26702
|
-
const
|
|
26703
|
-
|
|
26704
|
-
if (
|
|
26705
|
-
cloneUrl =
|
|
27095
|
+
const httpsUrl = url.replace(/^http:\/\//, "https://");
|
|
27096
|
+
const authed = tgitGitCloneUrl(httpsUrl);
|
|
27097
|
+
if (authed) {
|
|
27098
|
+
cloneUrl = authed;
|
|
26706
27099
|
cloneMethod = "https-token";
|
|
26707
|
-
log.debug(`shallowClone: \u4F7F\u7528 HTTPS+token \u514B\u9686 tgit \u4ED3\u5E93`);
|
|
27100
|
+
log.debug(`shallowClone: \u4F7F\u7528 HTTPS+OAuth token \u514B\u9686 tgit \u4ED3\u5E93`);
|
|
26708
27101
|
} else {
|
|
27102
|
+
cloneUrl = httpsUrl;
|
|
26709
27103
|
cloneMethod = "https-anonymous";
|
|
26710
|
-
log.debug(`shallowClone: \u65E0 TGit token\uFF0C\u5C1D\u8BD5\u533F\u540D HTTPS \u514B\u9686`);
|
|
27104
|
+
log.debug(`shallowClone: \u65E0 TGit OAuth token\uFF0C\u5C1D\u8BD5\u533F\u540D HTTPS \u514B\u9686`);
|
|
26711
27105
|
}
|
|
26712
27106
|
} else {
|
|
26713
27107
|
cloneUrl = url.replace(/^http:\/\//, "https://");
|
|
@@ -26775,10 +27169,10 @@ __export(repo_cache_exports, {
|
|
|
26775
27169
|
writeLastSync: () => writeLastSync
|
|
26776
27170
|
});
|
|
26777
27171
|
import path88 from "path";
|
|
26778
|
-
import
|
|
27172
|
+
import os6 from "os";
|
|
26779
27173
|
import fs31 from "fs-extra";
|
|
26780
27174
|
function getCacheRoot2() {
|
|
26781
|
-
return process.env.TEAMAI_CACHE_DIR ?? path88.join(
|
|
27175
|
+
return process.env.TEAMAI_CACHE_DIR ?? path88.join(os6.homedir(), ".teamai", "cache", "repos");
|
|
26782
27176
|
}
|
|
26783
27177
|
function getRepoCacheDir(provider, owner, repo) {
|
|
26784
27178
|
return path88.join(getCacheRoot2(), provider, owner, repo);
|
|
@@ -28833,7 +29227,7 @@ __export(import_exports, {
|
|
|
28833
29227
|
importCmd: () => importCmd
|
|
28834
29228
|
});
|
|
28835
29229
|
import path98 from "path";
|
|
28836
|
-
import
|
|
29230
|
+
import os7 from "os";
|
|
28837
29231
|
import fs38 from "fs-extra";
|
|
28838
29232
|
import { Listr, PRESET_TIMER } from "listr2";
|
|
28839
29233
|
async function importCmd(opts) {
|
|
@@ -29027,7 +29421,7 @@ async function importCmd(opts) {
|
|
|
29027
29421
|
log.success(`Local directory ${slug} import complete (dry-run)`);
|
|
29028
29422
|
return;
|
|
29029
29423
|
}
|
|
29030
|
-
const tmpExtractDir = await fs38.mkdtemp(path98.join(
|
|
29424
|
+
const tmpExtractDir = await fs38.mkdtemp(path98.join(os7.tmpdir(), "teamai-extract-"));
|
|
29031
29425
|
try {
|
|
29032
29426
|
const { extractCodebase: extractCodebase2 } = await Promise.resolve().then(() => (init_codebase_extract(), codebase_extract_exports));
|
|
29033
29427
|
await extractCodebase2({
|