teamai-cli 0.20.0-beta.6 → 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 +511 -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);
|
|
@@ -9743,8 +9935,8 @@ async function pushCore(localConfig, teamConfig, options) {
|
|
|
9743
9935
|
process.exitCode = 2;
|
|
9744
9936
|
return;
|
|
9745
9937
|
}
|
|
9746
|
-
const
|
|
9747
|
-
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);
|
|
9748
9940
|
let matchedItem;
|
|
9749
9941
|
for (const item of allItems) {
|
|
9750
9942
|
if (item.type !== "skills") continue;
|
|
@@ -9756,7 +9948,7 @@ async function pushCore(localConfig, teamConfig, options) {
|
|
|
9756
9948
|
matchedItem = item;
|
|
9757
9949
|
break;
|
|
9758
9950
|
}
|
|
9759
|
-
const skillInput = options.skill.replace(/^~/,
|
|
9951
|
+
const skillInput = options.skill.replace(/^~/, os8.homedir());
|
|
9760
9952
|
if (item.sourcePath.endsWith(skillInput) || item.sourcePath.includes(path32.sep + skillInput)) {
|
|
9761
9953
|
matchedItem = item;
|
|
9762
9954
|
break;
|
|
@@ -11909,13 +12101,10 @@ function tokenize(text) {
|
|
|
11909
12101
|
if (t !== word) tokens.push(t);
|
|
11910
12102
|
}
|
|
11911
12103
|
}
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
} else {
|
|
11917
|
-
flushCjkRun();
|
|
11918
|
-
}
|
|
12104
|
+
if (word.length === 1 && /[一-鿿]/.test(word)) {
|
|
12105
|
+
cjkRun.push(word);
|
|
12106
|
+
} else {
|
|
12107
|
+
flushCjkRun();
|
|
11919
12108
|
}
|
|
11920
12109
|
}
|
|
11921
12110
|
flushCjkRun();
|
|
@@ -11930,6 +12119,40 @@ function tokenCount(text) {
|
|
|
11930
12119
|
}
|
|
11931
12120
|
return count;
|
|
11932
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
|
+
}
|
|
11933
12156
|
var MAX_TOKENIZE_CHARS, sharedSegmenter;
|
|
11934
12157
|
var init_tokenizer = __esm({
|
|
11935
12158
|
"src/utils/tokenizer.ts"() {
|
|
@@ -12244,7 +12467,8 @@ __export(search_index_exports, {
|
|
|
12244
12467
|
parseLearningDoc: () => parseLearningDoc,
|
|
12245
12468
|
search: () => search,
|
|
12246
12469
|
titleFromFilename: () => titleFromFilename,
|
|
12247
|
-
tokenize: () => tokenize
|
|
12470
|
+
tokenize: () => tokenize,
|
|
12471
|
+
wordSegments: () => wordSegments
|
|
12248
12472
|
});
|
|
12249
12473
|
import path42 from "path";
|
|
12250
12474
|
import matter3 from "gray-matter";
|
|
@@ -12548,6 +12772,8 @@ function search(query, index, limit = 5) {
|
|
|
12548
12772
|
const docFreq = df[token] ?? 0;
|
|
12549
12773
|
return Math.log((N + 1) / (docFreq + 1)) + 1;
|
|
12550
12774
|
};
|
|
12775
|
+
const lengthNorm = Math.sqrt(queryTokens.length);
|
|
12776
|
+
const wordTokens = wordSegments(query).map((w) => ({ word: w, tokens: tokenize(w) }));
|
|
12551
12777
|
const results = [];
|
|
12552
12778
|
for (const entry of index.entries) {
|
|
12553
12779
|
let score = 0;
|
|
@@ -12570,6 +12796,7 @@ function search(query, index, limit = 5) {
|
|
|
12570
12796
|
}
|
|
12571
12797
|
const isCodebaseDoc = entry.type === "docs" && (entry.path ?? entry.filename ?? "").includes("team-codebase");
|
|
12572
12798
|
if (score > 0 && (hasTitleOrTagMatch || isCodebaseDoc)) {
|
|
12799
|
+
score /= lengthNorm;
|
|
12573
12800
|
score += Math.min(entry.votes * 0.5, 5);
|
|
12574
12801
|
const domainMultiplier = domainWeightRow[entry.domain ?? "neutral"];
|
|
12575
12802
|
const typeMultiplier = TYPE_BONUS[entry.type];
|
|
@@ -12580,14 +12807,40 @@ function search(query, index, limit = 5) {
|
|
|
12580
12807
|
if (entry.hotness !== void 0 && entry.hotness < 1) {
|
|
12581
12808
|
score *= entry.hotness;
|
|
12582
12809
|
}
|
|
12583
|
-
|
|
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 });
|
|
12584
12821
|
}
|
|
12585
12822
|
}
|
|
12586
12823
|
results.sort((a, b) => {
|
|
12587
12824
|
if (b.score !== a.score) return b.score - a.score;
|
|
12588
12825
|
return (b.entry.date || "").localeCompare(a.entry.date || "");
|
|
12589
12826
|
});
|
|
12590
|
-
|
|
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;
|
|
12591
12844
|
}
|
|
12592
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;
|
|
12593
12846
|
var init_search_index = __esm({
|
|
@@ -13349,6 +13602,7 @@ var init_skill_health = __esm({
|
|
|
13349
13602
|
// src/digest.ts
|
|
13350
13603
|
var digest_exports = {};
|
|
13351
13604
|
__export(digest_exports, {
|
|
13605
|
+
buildSkillChangePathspec: () => buildSkillChangePathspec,
|
|
13352
13606
|
formatTokenCount: () => formatTokenCount,
|
|
13353
13607
|
generateDigest: () => generateDigest,
|
|
13354
13608
|
summarizeConversation: () => summarizeConversation,
|
|
@@ -13399,9 +13653,17 @@ function parseGitLogOutput(output) {
|
|
|
13399
13653
|
if (current) commits.push(current);
|
|
13400
13654
|
return commits;
|
|
13401
13655
|
}
|
|
13402
|
-
|
|
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 = "") {
|
|
13403
13664
|
const seen = /* @__PURE__ */ new Set();
|
|
13404
13665
|
const changes = [];
|
|
13666
|
+
const { pathspec, skillRe } = buildSkillChangePathspec(subdir);
|
|
13405
13667
|
try {
|
|
13406
13668
|
const git = createGit2(repoPath);
|
|
13407
13669
|
const rawOutput = await git.raw([
|
|
@@ -13411,13 +13673,13 @@ async function getRecentSkillChanges(repoPath) {
|
|
|
13411
13673
|
"--name-only",
|
|
13412
13674
|
"--pretty=format:%H|%an|%s",
|
|
13413
13675
|
"--",
|
|
13414
|
-
|
|
13676
|
+
pathspec
|
|
13415
13677
|
]);
|
|
13416
13678
|
if (!rawOutput.trim()) return changes;
|
|
13417
13679
|
const commits = parseGitLogOutput(rawOutput);
|
|
13418
13680
|
for (const commit of commits) {
|
|
13419
13681
|
for (const file of commit.files) {
|
|
13420
|
-
const match = file.match(
|
|
13682
|
+
const match = file.match(skillRe);
|
|
13421
13683
|
if (!match) continue;
|
|
13422
13684
|
const skillName = match[1];
|
|
13423
13685
|
if (seen.has(skillName)) continue;
|
|
@@ -13435,14 +13697,14 @@ async function getRecentSkillChanges(repoPath) {
|
|
|
13435
13697
|
"--name-only",
|
|
13436
13698
|
"--pretty=format:%H|%an|%s",
|
|
13437
13699
|
"--",
|
|
13438
|
-
|
|
13700
|
+
pathspec
|
|
13439
13701
|
]);
|
|
13440
13702
|
const newSkills = /* @__PURE__ */ new Set();
|
|
13441
13703
|
if (addedOutput.trim()) {
|
|
13442
13704
|
const addedCommits = parseGitLogOutput(addedOutput);
|
|
13443
13705
|
for (const commit of addedCommits) {
|
|
13444
13706
|
for (const file of commit.files) {
|
|
13445
|
-
const match = file.match(
|
|
13707
|
+
const match = file.match(skillRe);
|
|
13446
13708
|
if (match) {
|
|
13447
13709
|
newSkills.add(match[1]);
|
|
13448
13710
|
}
|
|
@@ -13623,7 +13885,10 @@ async function generateDigest(options) {
|
|
|
13623
13885
|
console.log(`\u{1F4CA} Knowledge base total: ${totalLearnings} learnings`);
|
|
13624
13886
|
console.log("");
|
|
13625
13887
|
}
|
|
13626
|
-
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);
|
|
13627
13892
|
const newSkills = skillChanges.filter((c) => c.type === "new");
|
|
13628
13893
|
const updatedSkills = skillChanges.filter((c) => c.type === "updated");
|
|
13629
13894
|
if (newSkills.length > 0) {
|
|
@@ -15092,7 +15357,15 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
15092
15357
|
const docsRepoDir = path48.join(localConfig.repo.localPath, "docs");
|
|
15093
15358
|
const rulesRepoDir = path48.join(localConfig.repo.localPath, "rules");
|
|
15094
15359
|
const skillsRepoDir = path48.join(localConfig.repo.localPath, "skills");
|
|
15095
|
-
|
|
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
|
+
}
|
|
15096
15369
|
let learningsCount = 0;
|
|
15097
15370
|
let effectiveLearningsDir;
|
|
15098
15371
|
if (localConfig.scope === "user") {
|
|
@@ -15258,7 +15531,15 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
15258
15531
|
const YAML20 = (await import("yaml")).default;
|
|
15259
15532
|
const { listFiles: listFiles2, readFileSafe: readFileSafe5 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15260
15533
|
const { getRecommendations: getRecommendations2, displayRecommendations: displayRecommendations2 } = await Promise.resolve().then(() => (init_skill_recommend(), skill_recommend_exports));
|
|
15261
|
-
|
|
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
|
+
}
|
|
15262
15543
|
const files = await listFiles2(statsDir);
|
|
15263
15544
|
const teamStats = [];
|
|
15264
15545
|
for (const file of files) {
|
|
@@ -17280,9 +17561,13 @@ async function discoverToolResources(tool, toolPath, baseDir, teamSkillNames, te
|
|
|
17280
17561
|
res.hookFiles.push({ path: settingsPath, tool });
|
|
17281
17562
|
}
|
|
17282
17563
|
} else {
|
|
17283
|
-
const
|
|
17284
|
-
|
|
17285
|
-
|
|
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
|
+
}
|
|
17286
17571
|
}
|
|
17287
17572
|
}
|
|
17288
17573
|
if (toolPath.claudemd) {
|
|
@@ -20784,15 +21069,31 @@ async function contributeSelf(localConfig, content, options) {
|
|
|
20784
21069
|
await ensureDir(path69.join(wtRepo, "learnings"));
|
|
20785
21070
|
await fs24.promises.writeFile(path69.join(wtRepo, relPath), content, "utf-8");
|
|
20786
21071
|
try {
|
|
21072
|
+
const { pathExists: pathExists3 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
20787
21073
|
const wtLearnings = path69.join(wtRepo, "learnings");
|
|
20788
21074
|
await fse11.copy(wtLearnings, LEARNINGS_LOCAL_DIR, {
|
|
20789
21075
|
overwrite: true,
|
|
20790
21076
|
filter: (src) => !path69.basename(src).startsWith(".")
|
|
20791
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
|
+
}
|
|
20792
21089
|
const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
|
|
20793
21090
|
const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
|
|
20794
21091
|
await buildIndex2({
|
|
20795
|
-
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,
|
|
20796
21097
|
indexPath: path69.join(teamaiHome, "search-index.json")
|
|
20797
21098
|
});
|
|
20798
21099
|
} catch (e) {
|
|
@@ -21568,7 +21869,7 @@ function formatResults(results) {
|
|
|
21568
21869
|
lines.push(`--- [teamai:recall:start] --- (${results.length} result${results.length !== 1 ? "s" : ""})`);
|
|
21569
21870
|
lines.push("");
|
|
21570
21871
|
for (let i = 0; i < results.length; i++) {
|
|
21571
|
-
const { entry, score, scope, learningsBase, sources } = results[i];
|
|
21872
|
+
const { entry, score, scope, learningsBase, sources, matchedTerms, missingTerms } = results[i];
|
|
21572
21873
|
const voteStr = entry.votes > 0 ? ` \u2605${entry.votes}` : "";
|
|
21573
21874
|
const scopeStr = scope ? ` [${scope}]` : "";
|
|
21574
21875
|
const typeTag = entry.type ? `[${entry.type}] ` : "";
|
|
@@ -21577,6 +21878,10 @@ function formatResults(results) {
|
|
|
21577
21878
|
if (entry.tags.length > 0) {
|
|
21578
21879
|
lines.push(`Tags: ${entry.tags.join(", ")}`);
|
|
21579
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
|
+
}
|
|
21580
21885
|
const filePath = entry.path ? entry.path : learningsBase ? `${learningsBase}/${entry.filename}` : `~/.teamai/learnings/${entry.filename}`;
|
|
21581
21886
|
lines.push(`File: ${filePath}`);
|
|
21582
21887
|
if (sources && sources.length > 0) {
|
|
@@ -21671,8 +21976,16 @@ async function recall(query, options) {
|
|
|
21671
21976
|
const rounded = Math.round(score * 10) / 10;
|
|
21672
21977
|
const verdict = isRelevantScore(score, isCodebaseHit, baseline) ? "RELEVANT" : "NOT_RELEVANT";
|
|
21673
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)}`;
|
|
21674
21981
|
if (verdict === "RELEVANT" && topResult) {
|
|
21675
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
|
+
}
|
|
21676
21989
|
if (topResult.sources && topResult.sources.length > 0) {
|
|
21677
21990
|
const srcStr = topResult.sources.map((s) => s.desc ? `${s.path}(${s.desc})` : s.path).join(",");
|
|
21678
21991
|
line += ` sources=${srcStr}`;
|
|
@@ -21964,10 +22277,10 @@ var init_recall_toggle = __esm({
|
|
|
21964
22277
|
|
|
21965
22278
|
// src/utils/cache-index.ts
|
|
21966
22279
|
import path75 from "path";
|
|
21967
|
-
import
|
|
22280
|
+
import os5 from "os";
|
|
21968
22281
|
import fs25 from "fs-extra";
|
|
21969
22282
|
function getCacheRoot() {
|
|
21970
|
-
return process.env.TEAMAI_CACHE_DIR ?? path75.join(
|
|
22283
|
+
return process.env.TEAMAI_CACHE_DIR ?? path75.join(os5.homedir(), ".teamai", "cache", "repos");
|
|
21971
22284
|
}
|
|
21972
22285
|
function buildKey(provider, owner, repo) {
|
|
21973
22286
|
return `${provider}/${owner}/${repo}`;
|
|
@@ -25913,6 +26226,23 @@ function parseJSON(raw) {
|
|
|
25913
26226
|
return null;
|
|
25914
26227
|
}
|
|
25915
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
|
+
}
|
|
25916
26246
|
async function enrichWithAI(ctx) {
|
|
25917
26247
|
const moduleEntries = [...ctx.modules.entries()].filter(([, facts]) => facts.length >= 5);
|
|
25918
26248
|
if (moduleEntries.length === 0) {
|
|
@@ -25976,9 +26306,9 @@ async function enrichWithAI(ctx) {
|
|
|
25976
26306
|
const moduleImports = ctx.facts.filter((f) => f.kind === "relation" && f.file.startsWith(name + "/"));
|
|
25977
26307
|
const targetModules = /* @__PURE__ */ new Set();
|
|
25978
26308
|
for (const imp of moduleImports) {
|
|
25979
|
-
const
|
|
25980
|
-
if (
|
|
25981
|
-
targetModules.add(
|
|
26309
|
+
const resolved = resolveImportToModule(imp.file, imp.name);
|
|
26310
|
+
if (resolved && resolved !== name) {
|
|
26311
|
+
targetModules.add(resolved);
|
|
25982
26312
|
}
|
|
25983
26313
|
}
|
|
25984
26314
|
for (const target of targetModules) {
|
|
@@ -26465,7 +26795,19 @@ async function extractCodebase(opts) {
|
|
|
26465
26795
|
let callChains;
|
|
26466
26796
|
const depPathsFile = path87.join(evidenceDir, "dependency-paths.md");
|
|
26467
26797
|
if (changedFiles) {
|
|
26468
|
-
|
|
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
|
+
}
|
|
26469
26811
|
} else {
|
|
26470
26812
|
callChains = traceCallChains(facts, files);
|
|
26471
26813
|
}
|
|
@@ -26750,15 +27092,16 @@ async function shallowClone(url, localPath, provider, opts) {
|
|
|
26750
27092
|
log.debug(`shallowClone: \u4F7F\u7528\u533F\u540D HTTPS \u514B\u9686 github \u4ED3\u5E93`);
|
|
26751
27093
|
}
|
|
26752
27094
|
} else if (provider === "tgit") {
|
|
26753
|
-
const
|
|
26754
|
-
|
|
26755
|
-
if (
|
|
26756
|
-
cloneUrl =
|
|
27095
|
+
const httpsUrl = url.replace(/^http:\/\//, "https://");
|
|
27096
|
+
const authed = tgitGitCloneUrl(httpsUrl);
|
|
27097
|
+
if (authed) {
|
|
27098
|
+
cloneUrl = authed;
|
|
26757
27099
|
cloneMethod = "https-token";
|
|
26758
|
-
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`);
|
|
26759
27101
|
} else {
|
|
27102
|
+
cloneUrl = httpsUrl;
|
|
26760
27103
|
cloneMethod = "https-anonymous";
|
|
26761
|
-
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`);
|
|
26762
27105
|
}
|
|
26763
27106
|
} else {
|
|
26764
27107
|
cloneUrl = url.replace(/^http:\/\//, "https://");
|
|
@@ -26826,10 +27169,10 @@ __export(repo_cache_exports, {
|
|
|
26826
27169
|
writeLastSync: () => writeLastSync
|
|
26827
27170
|
});
|
|
26828
27171
|
import path88 from "path";
|
|
26829
|
-
import
|
|
27172
|
+
import os6 from "os";
|
|
26830
27173
|
import fs31 from "fs-extra";
|
|
26831
27174
|
function getCacheRoot2() {
|
|
26832
|
-
return process.env.TEAMAI_CACHE_DIR ?? path88.join(
|
|
27175
|
+
return process.env.TEAMAI_CACHE_DIR ?? path88.join(os6.homedir(), ".teamai", "cache", "repos");
|
|
26833
27176
|
}
|
|
26834
27177
|
function getRepoCacheDir(provider, owner, repo) {
|
|
26835
27178
|
return path88.join(getCacheRoot2(), provider, owner, repo);
|
|
@@ -28884,7 +29227,7 @@ __export(import_exports, {
|
|
|
28884
29227
|
importCmd: () => importCmd
|
|
28885
29228
|
});
|
|
28886
29229
|
import path98 from "path";
|
|
28887
|
-
import
|
|
29230
|
+
import os7 from "os";
|
|
28888
29231
|
import fs38 from "fs-extra";
|
|
28889
29232
|
import { Listr, PRESET_TIMER } from "listr2";
|
|
28890
29233
|
async function importCmd(opts) {
|
|
@@ -29078,7 +29421,7 @@ async function importCmd(opts) {
|
|
|
29078
29421
|
log.success(`Local directory ${slug} import complete (dry-run)`);
|
|
29079
29422
|
return;
|
|
29080
29423
|
}
|
|
29081
|
-
const tmpExtractDir = await fs38.mkdtemp(path98.join(
|
|
29424
|
+
const tmpExtractDir = await fs38.mkdtemp(path98.join(os7.tmpdir(), "teamai-extract-"));
|
|
29082
29425
|
try {
|
|
29083
29426
|
const { extractCodebase: extractCodebase2 } = await Promise.resolve().then(() => (init_codebase_extract(), codebase_extract_exports));
|
|
29084
29427
|
await extractCodebase2({
|