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/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 tgitGitUser(scheme) {
1353
- return scheme === "private-token" ? "private" : "oauth2";
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 creds = tryGetTGitToken();
1583
- if (creds) {
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(/(oauth2|private):[^@]+@/g, "$1:***@");
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 path10 from "path";
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 = path10.join(home, WORKBUDDY_BUNDLED_NODE_DIR);
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 = path10.join(versionsDir, latest, "bin", "node");
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 = path10.join(home, entry, "bin");
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 = path10.join(binDir, stable, "node");
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 = path10.dirname(thisFile);
2924
- const candidate = path10.join(distDir, "index.js");
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 = path10.join(home, TEAMAI_BIN_DIR);
2936
- const wrapperPath = path10.join(binDir, WRAPPER_NAME);
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 path11 from "path";
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 path11.join(repoPath, "hooks", "hooks.yaml");
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) && !OPENCLAW_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
- return readDarwinMachineId();
4964
+ id = readDarwinMachineId();
4965
+ break;
4847
4966
  case "win32":
4848
- return readWindowsMachineId();
4967
+ id = readWindowsMachineId();
4968
+ break;
4849
4969
  default:
4850
- return readLinuxMachineId();
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 os2 from "os";
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(os2.homedir(), p.slice(1)) : p;
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(), os2.homedir()];
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 os3 from "os";
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: os3.userInfo().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(os3.tmpdir(), `teamai-bind-session-${markerKey}`);
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(os3.tmpdir(), `teamai-bind-hint-${markerKey}`);
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: os3.hostname(),
6048
- os: os3.platform(),
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(os3.tmpdir(), "teamai-local-agent-"));
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 syncClaudemd(teamConfig, localConfig, repoPath) {
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
- const baseDir = localConfig.scope === "project" && localConfig.projectRoot ? localConfig.projectRoot : process.env.HOME ?? "";
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
- if (!await ResourceHandler.isToolInstalled(toolPath.claudemd, baseDir)) continue;
6385
- const claudeMdPath = path20.join(baseDir, toolPath.claudemd);
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
- if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) {
7631
- log.debug(`Skipping skill sync for ${tool}: tool not installed`);
7632
- continue;
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
- const skillDir = path22.join(baseDir, toolPath.skills, name);
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 os7 = await import("os");
9742
- const skillPath = options.skill.startsWith("~") ? path32.join(os7.homedir(), options.skill.slice(1)) : path32.resolve(options.skill);
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(/^~/, os7.homedir());
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
- const chars = [...word];
11867
- for (const ch of chars) {
11868
- if (/[一-鿿]/.test(ch)) {
11869
- cjkRun.push(ch);
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
- results.push({ entry, score });
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
- return results.slice(0, limit);
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
- async function getRecentSkillChanges(repoPath) {
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
- "skills/*/SKILL.md"
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(/^skills\/([^/]+)\/SKILL\.md$/);
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
- "skills/*/SKILL.md"
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(/^skills\/([^/]+)\/SKILL\.md$/);
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(repoPath);
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
- const votesDir = path48.join(localConfig.repo.localPath, "votes");
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
- const statsDir = path48.join(localConfig.repo.localPath, "stats");
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 hooksDir = path56.join(baseDir, `.${tool}`, "hooks");
17233
- if (await pathExists(path56.join(hooksDir, OPENCLAW_HOOK_DIR))) {
17234
- res.openclawHookDirs.push({ hooksDir, tool });
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 os4 from "os";
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(os4.homedir(), ".teamai", "cache", "repos");
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 targetParts = imp.name.split("/");
25929
- if (targetParts[0] && targetParts[0] !== name) {
25930
- targetModules.add(targetParts[0]);
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
- callChains = [];
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 creds = tryGetTGitToken();
26703
- cloneUrl = url.replace(/^http:\/\//, "https://");
26704
- if (creds) {
26705
- cloneUrl = cloneUrl.replace("https://", `https://${tgitGitUser(creds.scheme)}:${creds.token}@`);
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 os5 from "os";
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(os5.homedir(), ".teamai", "cache", "repos");
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 os6 from "os";
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(os6.tmpdir(), "teamai-extract-"));
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({