teamai-cli 0.17.0 → 0.17.1

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.
Files changed (2) hide show
  1. package/dist/index.js +1657 -589
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,6 +10,16 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/utils/logger.ts
13
+ var logger_exports = {};
14
+ __export(logger_exports, {
15
+ MAX_LOG_BYTES: () => MAX_LOG_BYTES,
16
+ _resetState: () => _resetState,
17
+ _setLogFilePath: () => _setLogFilePath,
18
+ log: () => log,
19
+ setSilent: () => setSilent,
20
+ setVerbose: () => setVerbose,
21
+ spinner: () => spinner
22
+ });
13
23
  import fs from "fs";
14
24
  import path from "path";
15
25
  import chalk from "chalk";
@@ -52,6 +62,15 @@ function writeToFile(level, msg) {
52
62
  _writing = false;
53
63
  }
54
64
  }
65
+ function _setLogFilePath(p) {
66
+ _logFilePath = p;
67
+ _dirEnsured = false;
68
+ }
69
+ function _resetState() {
70
+ _logFilePath = null;
71
+ _dirEnsured = false;
72
+ _writing = false;
73
+ }
55
74
  function setVerbose(v) {
56
75
  verboseEnabled = v;
57
76
  }
@@ -105,6 +124,85 @@ var init_logger = __esm({
105
124
  });
106
125
 
107
126
  // src/types.ts
127
+ var types_exports = {};
128
+ __export(types_exports, {
129
+ CONTRIBUTE_BASE_THRESHOLD: () => CONTRIBUTE_BASE_THRESHOLD,
130
+ CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT: () => CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT,
131
+ CONTRIBUTE_KNOWLEDGE_GAP_BONUS: () => CONTRIBUTE_KNOWLEDGE_GAP_BONUS,
132
+ CONTRIBUTE_LOW_QUALITY_BONUS: () => CONTRIBUTE_LOW_QUALITY_BONUS,
133
+ CONTRIBUTE_LOW_QUALITY_THRESHOLD: () => CONTRIBUTE_LOW_QUALITY_THRESHOLD,
134
+ CONTRIBUTE_SCORE_CACHE_MS: () => CONTRIBUTE_SCORE_CACHE_MS,
135
+ CONTRIBUTE_SESSIONS_DIR: () => CONTRIBUTE_SESSIONS_DIR,
136
+ CONTRIBUTE_SMART_THRESHOLD: () => CONTRIBUTE_SMART_THRESHOLD,
137
+ CORRECTION_KEYWORDS: () => CORRECTION_KEYWORDS,
138
+ CORRECTION_WINDOW_MS: () => CORRECTION_WINDOW_MS,
139
+ CultureCompanySchema: () => CultureCompanySchema,
140
+ CultureFrontmatterSchema: () => CultureFrontmatterSchema,
141
+ CultureTeamSchema: () => CultureTeamSchema,
142
+ DASHBOARD_COMPACTION_THRESHOLD: () => DASHBOARD_COMPACTION_THRESHOLD,
143
+ DASHBOARD_DEFAULT_PORT: () => DASHBOARD_DEFAULT_PORT,
144
+ DASHBOARD_EVENTS_DIR: () => DASHBOARD_EVENTS_DIR,
145
+ DASHBOARD_EVENTS_PATH: () => DASHBOARD_EVENTS_PATH,
146
+ DASHBOARD_IDLE_TIMEOUT_MS: () => DASHBOARD_IDLE_TIMEOUT_MS,
147
+ DASHBOARD_PID_CHECK_INTERVAL_MS: () => DASHBOARD_PID_CHECK_INTERVAL_MS,
148
+ DASHBOARD_STALE_TIMEOUT_MS: () => DASHBOARD_STALE_TIMEOUT_MS,
149
+ DASHBOARD_STOPPED_DISPLAY_MS: () => DASHBOARD_STOPPED_DISPLAY_MS,
150
+ INTERVENTION_SCAN_MAX_BYTES: () => INTERVENTION_SCAN_MAX_BYTES,
151
+ LEARNINGS_LOCAL_DIR: () => LEARNINGS_LOCAL_DIR,
152
+ LocalConfigSchema: () => LocalConfigSchema,
153
+ MemberConfigSchema: () => MemberConfigSchema,
154
+ RESOURCE_TYPES: () => RESOURCE_TYPES,
155
+ SEARCH_INDEX_PATH: () => SEARCH_INDEX_PATH,
156
+ SEARCH_INDEX_VERSION: () => SEARCH_INDEX_VERSION,
157
+ SKILL_NAME_REGEX: () => SKILL_NAME_REGEX,
158
+ SOURCE_PULL_TTL_MS: () => SOURCE_PULL_TTL_MS,
159
+ ScopeEnum: () => ScopeEnum,
160
+ SharingConfigSchema: () => SharingConfigSchema,
161
+ SourceConfigSchema: () => SourceConfigSchema,
162
+ StateSchema: () => StateSchema,
163
+ TEAMAI_CLAUDEMD_END: () => TEAMAI_CLAUDEMD_END,
164
+ TEAMAI_CLAUDEMD_START: () => TEAMAI_CLAUDEMD_START,
165
+ TEAMAI_CONFIG_PATH: () => TEAMAI_CONFIG_PATH,
166
+ TEAMAI_CULTURE_END: () => TEAMAI_CULTURE_END,
167
+ TEAMAI_CULTURE_START: () => TEAMAI_CULTURE_START,
168
+ TEAMAI_CUSTOM_HOOK_PREFIX: () => TEAMAI_CUSTOM_HOOK_PREFIX,
169
+ TEAMAI_ENV_END: () => TEAMAI_ENV_END,
170
+ TEAMAI_ENV_START: () => TEAMAI_ENV_START,
171
+ TEAMAI_HOME: () => TEAMAI_HOME,
172
+ TEAMAI_HOOK_DESCRIPTION_PREFIX: () => TEAMAI_HOOK_DESCRIPTION_PREFIX,
173
+ TEAMAI_KNOWN_SKILLS_PATH: () => TEAMAI_KNOWN_SKILLS_PATH,
174
+ TEAMAI_PUSHIGNORE_PATH: () => TEAMAI_PUSHIGNORE_PATH,
175
+ TEAMAI_RECALL_RULES_END: () => TEAMAI_RECALL_RULES_END,
176
+ TEAMAI_RECALL_RULES_START: () => TEAMAI_RECALL_RULES_START,
177
+ TEAMAI_RULES_END: () => TEAMAI_RULES_END,
178
+ TEAMAI_RULES_START: () => TEAMAI_RULES_START,
179
+ TEAMAI_SESSIONS_DIR: () => TEAMAI_SESSIONS_DIR,
180
+ TEAMAI_SOURCES_DIR: () => TEAMAI_SOURCES_DIR,
181
+ TEAMAI_STATE_PATH: () => TEAMAI_STATE_PATH,
182
+ TEAMAI_TOKEN_PATH: () => TEAMAI_TOKEN_PATH,
183
+ TEAMAI_UPDATE_LOCK_PATH: () => TEAMAI_UPDATE_LOCK_PATH,
184
+ TEAMAI_USAGE_PATH: () => TEAMAI_USAGE_PATH,
185
+ TRANSCRIPT_INTERRUPT_PREFIX: () => TRANSCRIPT_INTERRUPT_PREFIX,
186
+ TRANSCRIPT_REJECT_MARKERS: () => TRANSCRIPT_REJECT_MARKERS,
187
+ TRANSCRIPT_SYSTEM_PREFIXES: () => TRANSCRIPT_SYSTEM_PREFIXES,
188
+ TeamaiConfigSchema: () => TeamaiConfigSchema,
189
+ ToolPathsSchema: () => ToolPathsSchema,
190
+ UsageEventSchema: () => UsageEventSchema,
191
+ VOTES_LOCAL_DIR: () => VOTES_LOCAL_DIR,
192
+ addTokenUsage: () => addTokenUsage,
193
+ areTeamHooksDisabled: () => areTeamHooksDisabled,
194
+ emptyTokenUsage: () => emptyTokenUsage,
195
+ getConfigPath: () => getConfigPath,
196
+ getHooksSharing: () => getHooksSharing,
197
+ getManagedHooksPath: () => getManagedHooksPath,
198
+ getPushignorePath: () => getPushignorePath,
199
+ getRecallSharing: () => getRecallSharing,
200
+ getStatePath: () => getStatePath,
201
+ getTeamaiHome: () => getTeamaiHome,
202
+ isRecallEnabled: () => isRecallEnabled,
203
+ resolveBaseDir: () => resolveBaseDir,
204
+ totalTokens: () => totalTokens
205
+ });
108
206
  import { z } from "zod";
109
207
  import path2 from "path";
110
208
  function getHooksSharing(config) {
@@ -173,7 +271,7 @@ function getPushignorePath() {
173
271
  function areTeamHooksDisabled() {
174
272
  return process.env.TEAMAI_HOOKS_DISABLED === "1" || process.env.TEAMAI_HOOKS_DISABLED === "true";
175
273
  }
176
- var ToolPathsSchema, ScopeEnum, SharingConfigSchema, SourceConfigSchema, SOURCE_PULL_TTL_MS, TEAMAI_SOURCES_DIR, TeamaiConfigSchema, MemberConfigSchema, LocalConfigSchema, StateSchema, TEAMAI_HOME, TEAMAI_CONFIG_PATH, TEAMAI_STATE_PATH, TEAMAI_TOKEN_PATH, TEAMAI_UPDATE_LOCK_PATH, TEAMAI_RULES_START, TEAMAI_RULES_END, TEAMAI_HOOK_DESCRIPTION_PREFIX, TEAMAI_CUSTOM_HOOK_PREFIX, TEAMAI_ENV_START, TEAMAI_ENV_END, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, TEAMAI_RECALL_RULES_START, TEAMAI_RECALL_RULES_END, SKILL_NAME_REGEX, TEAMAI_USAGE_PATH, TEAMAI_KNOWN_SKILLS_PATH, TEAMAI_PUSHIGNORE_PATH, TEAMAI_SESSIONS_DIR, UsageEventSchema, DASHBOARD_EVENTS_DIR, DASHBOARD_EVENTS_PATH, DASHBOARD_DEFAULT_PORT, DASHBOARD_IDLE_TIMEOUT_MS, DASHBOARD_STALE_TIMEOUT_MS, DASHBOARD_COMPACTION_THRESHOLD, DASHBOARD_STOPPED_DISPLAY_MS, DASHBOARD_PID_CHECK_INTERVAL_MS, CORRECTION_WINDOW_MS, CORRECTION_KEYWORDS, INTERVENTION_SCAN_MAX_BYTES, TRANSCRIPT_INTERRUPT_PREFIX, TRANSCRIPT_SYSTEM_PREFIXES, TRANSCRIPT_REJECT_MARKERS, CONTRIBUTE_BASE_THRESHOLD, CONTRIBUTE_SMART_THRESHOLD, CONTRIBUTE_SCORE_CACHE_MS, CONTRIBUTE_KNOWLEDGE_GAP_BONUS, CONTRIBUTE_LOW_QUALITY_BONUS, CONTRIBUTE_LOW_QUALITY_THRESHOLD, CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT, CONTRIBUTE_SESSIONS_DIR, SEARCH_INDEX_VERSION, LEARNINGS_LOCAL_DIR, SEARCH_INDEX_PATH, VOTES_LOCAL_DIR, CultureCompanySchema, CultureTeamSchema, CultureFrontmatterSchema;
274
+ var ToolPathsSchema, ScopeEnum, SharingConfigSchema, SourceConfigSchema, SOURCE_PULL_TTL_MS, TEAMAI_SOURCES_DIR, TeamaiConfigSchema, MemberConfigSchema, LocalConfigSchema, StateSchema, TEAMAI_HOME, TEAMAI_CONFIG_PATH, TEAMAI_STATE_PATH, TEAMAI_TOKEN_PATH, TEAMAI_UPDATE_LOCK_PATH, RESOURCE_TYPES, TEAMAI_RULES_START, TEAMAI_RULES_END, TEAMAI_HOOK_DESCRIPTION_PREFIX, TEAMAI_CUSTOM_HOOK_PREFIX, TEAMAI_ENV_START, TEAMAI_ENV_END, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, TEAMAI_RECALL_RULES_START, TEAMAI_RECALL_RULES_END, SKILL_NAME_REGEX, TEAMAI_USAGE_PATH, TEAMAI_KNOWN_SKILLS_PATH, TEAMAI_PUSHIGNORE_PATH, TEAMAI_SESSIONS_DIR, UsageEventSchema, DASHBOARD_EVENTS_DIR, DASHBOARD_EVENTS_PATH, DASHBOARD_DEFAULT_PORT, DASHBOARD_IDLE_TIMEOUT_MS, DASHBOARD_STALE_TIMEOUT_MS, DASHBOARD_COMPACTION_THRESHOLD, DASHBOARD_STOPPED_DISPLAY_MS, DASHBOARD_PID_CHECK_INTERVAL_MS, CORRECTION_WINDOW_MS, CORRECTION_KEYWORDS, INTERVENTION_SCAN_MAX_BYTES, TRANSCRIPT_INTERRUPT_PREFIX, TRANSCRIPT_SYSTEM_PREFIXES, TRANSCRIPT_REJECT_MARKERS, CONTRIBUTE_BASE_THRESHOLD, CONTRIBUTE_SMART_THRESHOLD, CONTRIBUTE_SCORE_CACHE_MS, CONTRIBUTE_KNOWLEDGE_GAP_BONUS, CONTRIBUTE_LOW_QUALITY_BONUS, CONTRIBUTE_LOW_QUALITY_THRESHOLD, CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT, CONTRIBUTE_SESSIONS_DIR, SEARCH_INDEX_VERSION, LEARNINGS_LOCAL_DIR, SEARCH_INDEX_PATH, VOTES_LOCAL_DIR, CultureCompanySchema, CultureTeamSchema, CultureFrontmatterSchema;
177
275
  var init_types = __esm({
178
276
  "src/types.ts"() {
179
277
  "use strict";
@@ -281,7 +379,9 @@ var init_types = __esm({
281
379
  /** Tags the user has subscribed to. If empty/undefined, pull all resources. */
282
380
  subscribedTags: z.array(z.string()).optional(),
283
381
  /** User-level override for recall feature. When set, takes precedence over team config. */
284
- recallEnabled: z.boolean().optional()
382
+ recallEnabled: z.boolean().optional(),
383
+ /** When set, only inject hooks into these agents. Additive across multiple init --agent runs. */
384
+ enabledAgents: z.array(z.string()).optional()
285
385
  });
286
386
  StateSchema = z.object({
287
387
  lastPush: z.string().nullable().default(null),
@@ -299,6 +399,7 @@ var init_types = __esm({
299
399
  TEAMAI_STATE_PATH = `${TEAMAI_HOME}/state.json`;
300
400
  TEAMAI_TOKEN_PATH = `${TEAMAI_HOME}/token`;
301
401
  TEAMAI_UPDATE_LOCK_PATH = `${TEAMAI_HOME}/.update-lock`;
402
+ RESOURCE_TYPES = ["skills", "rules", "docs", "env", "agents", "hooks"];
302
403
  TEAMAI_RULES_START = "<!-- [teamai:rules:start] -->";
303
404
  TEAMAI_RULES_END = "<!-- [teamai:rules:end] -->";
304
405
  TEAMAI_HOOK_DESCRIPTION_PREFIX = "[teamai]";
@@ -368,7 +469,7 @@ var init_types = __esm({
368
469
  CONTRIBUTE_LOW_QUALITY_THRESHOLD = 5;
369
470
  CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT = 0;
370
471
  CONTRIBUTE_SESSIONS_DIR = `${TEAMAI_HOME}/sessions`;
371
- SEARCH_INDEX_VERSION = 5;
472
+ SEARCH_INDEX_VERSION = 6;
372
473
  LEARNINGS_LOCAL_DIR = `${TEAMAI_HOME}/learnings`;
373
474
  SEARCH_INDEX_PATH = `${TEAMAI_HOME}/search-index.json`;
374
475
  VOTES_LOCAL_DIR = `${TEAMAI_HOME}/votes`;
@@ -1503,9 +1604,10 @@ async function getHookStatus(settingsPath, tool) {
1503
1604
  });
1504
1605
  return present ? "installed" : "missing";
1505
1606
  }
1506
- async function injectHooksToAllTools(toolPaths, baseDir) {
1607
+ async function injectHooksToAllTools(toolPaths, baseDir, filterAgents2) {
1507
1608
  const resolvedBaseDir = baseDir ?? (process.env.HOME ?? "");
1508
1609
  for (const [tool, paths] of Object.entries(toolPaths)) {
1610
+ if (filterAgents2 && !filterAgents2.includes(tool)) continue;
1509
1611
  if (paths.settings) {
1510
1612
  const toolRoot = path9.join(resolvedBaseDir, paths.settings.split("/")[0]);
1511
1613
  if (!await pathExists(toolRoot)) continue;
@@ -1530,6 +1632,7 @@ async function injectHooksToAllTools(toolPaths, baseDir) {
1530
1632
  }
1531
1633
  async function reconcileHooksToAllTools(toolPaths, baseDir, teamDefs, manifestPath, opts = {}) {
1532
1634
  for (const [tool, paths] of Object.entries(toolPaths)) {
1635
+ if (opts.filterAgents && !opts.filterAgents.includes(tool)) continue;
1533
1636
  if (!paths.settings) continue;
1534
1637
  if (!opts.force) {
1535
1638
  const toolRoot = path9.join(baseDir, paths.settings.split("/")[0]);
@@ -1551,9 +1654,11 @@ async function reconcileTeamHooksForConfig(teamConfig, localConfig, opts = {}) {
1551
1654
  const { defs: teamDefs, builtin } = opts.removeAll ? { defs: [], builtin: void 0 } : await resolveTeamHooks(teamConfig, localConfig.repo.localPath, { auto: opts.auto, silent: opts.silent });
1552
1655
  const baseDir = resolveBaseDir(localConfig);
1553
1656
  const manifestPath = getManagedHooksPath(localConfig.scope, localConfig.projectRoot);
1657
+ const filterAgents2 = opts.filterAgents ?? localConfig.enabledAgents;
1554
1658
  await reconcileHooksToAllTools(teamConfig.toolPaths, baseDir, teamDefs, manifestPath, {
1555
1659
  removeAll: opts.removeAll,
1556
- builtinOverride: builtin
1660
+ builtinOverride: builtin,
1661
+ filterAgents: filterAgents2
1557
1662
  });
1558
1663
  return teamDefs;
1559
1664
  }
@@ -3056,7 +3161,7 @@ var init_builtin_skills = __esm({
3056
3161
  init_types();
3057
3162
  init_base();
3058
3163
  init_skills();
3059
- BUILTIN_SKILL_NAMES = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase"]);
3164
+ BUILTIN_SKILL_NAMES = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase", "teamai-workflow", "teamai-import"]);
3060
3165
  RECALL_DEPENDENT_SKILLS = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase"]);
3061
3166
  }
3062
3167
  });
@@ -6283,6 +6388,11 @@ async function initHttp(url, options) {
6283
6388
  log.debug(`Role selection skipped: ${msg}`);
6284
6389
  }
6285
6390
  }
6391
+ if (options.agent) {
6392
+ const existing = await loadLocalConfigForScope(scope, projectRoot);
6393
+ const prev = existing?.enabledAgents ?? [];
6394
+ localConfig.enabledAgents = [.../* @__PURE__ */ new Set([...prev, options.agent])];
6395
+ }
6286
6396
  await ensureDir(teamaiHome);
6287
6397
  if (scope === "project") {
6288
6398
  await saveLocalConfigForScope(localConfig, scope, projectRoot);
@@ -6297,7 +6407,8 @@ async function initHttp(url, options) {
6297
6407
  await saveStateForScope(state, scope, projectRoot);
6298
6408
  } catch {
6299
6409
  }
6300
- await reconcileTeamHooksForConfig(teamConfig, localConfig);
6410
+ const filterAgents2 = options.agent ? [options.agent] : void 0;
6411
+ await reconcileTeamHooksForConfig(teamConfig, localConfig, { filterAgents: filterAgents2 });
6301
6412
  if (reportingOnly) {
6302
6413
  log.success("teamai initialized (HTTP, reporting-only \u2014 /repo not live yet)!");
6303
6414
  log.info("Status reporting is active now; skills/rules will sync automatically once /repo is available.");
@@ -6544,6 +6655,11 @@ async function init(options) {
6544
6655
  process.exit(1);
6545
6656
  }
6546
6657
  }
6658
+ if (options.agent) {
6659
+ const existing = await loadLocalConfigForScope(scope, projectRoot);
6660
+ const prev = existing?.enabledAgents ?? [];
6661
+ localConfig.enabledAgents = [.../* @__PURE__ */ new Set([...prev, options.agent])];
6662
+ }
6547
6663
  await ensureDir(teamaiHome);
6548
6664
  if (scope === "project") {
6549
6665
  await saveLocalConfigForScope(localConfig, scope, projectRoot);
@@ -6583,7 +6699,8 @@ async function init(options) {
6583
6699
  }
6584
6700
  const reloadedTeamConfig = await loadTeamConfig(localPath);
6585
6701
  if (reloadedTeamConfig) {
6586
- await reconcileTeamHooksForConfig(reloadedTeamConfig, localConfig);
6702
+ const filterAgents2 = options.agent ? [options.agent] : void 0;
6703
+ await reconcileTeamHooksForConfig(reloadedTeamConfig, localConfig, { filterAgents: filterAgents2 });
6587
6704
  }
6588
6705
  log.success("teamai initialized successfully!");
6589
6706
  log.info("Skills, rules, env and docs will auto-sync on each session start (via hooks).");
@@ -6782,6 +6899,300 @@ var init_tokenizer = __esm({
6782
6899
  }
6783
6900
  });
6784
6901
 
6902
+ // src/votes.ts
6903
+ var votes_exports = {};
6904
+ __export(votes_exports, {
6905
+ incrementRecalled: () => incrementRecalled,
6906
+ incrementUpvoted: () => incrementUpvoted,
6907
+ loadUserVotes: () => loadUserVotes,
6908
+ mergeDeltas: () => mergeDeltas,
6909
+ migrateV1ToV2: () => migrateV1ToV2,
6910
+ recallFeedback: () => recallFeedback,
6911
+ saveUserVotes: () => saveUserVotes,
6912
+ syncVotesToTeam: () => syncVotesToTeam
6913
+ });
6914
+ import path30 from "path";
6915
+ import YAML9 from "yaml";
6916
+ function migrateV1ToV2(v1) {
6917
+ const votes = {};
6918
+ for (const [docId, entry] of Object.entries(v1.votes)) {
6919
+ votes[docId] = {
6920
+ recalled_count: 1,
6921
+ upvoted_count: 0,
6922
+ last_recalled_at: entry.at
6923
+ };
6924
+ }
6925
+ return { version: 2, votes, deltas: {} };
6926
+ }
6927
+ async function loadUserVotes(votePath) {
6928
+ const content = await readFileSafe(votePath);
6929
+ if (!content) return { version: 2, votes: {}, deltas: {} };
6930
+ let parsed;
6931
+ try {
6932
+ parsed = YAML9.parse(content);
6933
+ } catch {
6934
+ return { version: 2, votes: {}, deltas: {} };
6935
+ }
6936
+ if (!parsed || typeof parsed !== "object") {
6937
+ return { version: 2, votes: {}, deltas: {} };
6938
+ }
6939
+ const obj = parsed;
6940
+ if (obj["version"] === 2) {
6941
+ const v2 = obj;
6942
+ if (!v2.deltas) v2.deltas = {};
6943
+ return v2;
6944
+ }
6945
+ if (obj["votes"] !== void 0) {
6946
+ const migrated = migrateV1ToV2(obj);
6947
+ await saveUserVotes(votePath, migrated);
6948
+ return migrated;
6949
+ }
6950
+ return { version: 2, votes: {}, deltas: {} };
6951
+ }
6952
+ async function saveUserVotes(votePath, votes) {
6953
+ await ensureDir(path30.dirname(votePath));
6954
+ await writeFile(votePath, YAML9.stringify(votes));
6955
+ }
6956
+ async function incrementRecalled(votePath, docIds) {
6957
+ if (docIds.length === 0) return;
6958
+ const data = await loadUserVotes(votePath);
6959
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6960
+ for (const docId of docIds) {
6961
+ if (!data.votes[docId]) {
6962
+ data.votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
6963
+ }
6964
+ data.votes[docId].recalled_count++;
6965
+ data.votes[docId].last_recalled_at = now;
6966
+ if (!data.deltas[docId]) {
6967
+ data.deltas[docId] = { recalled_delta: 0, upvoted_delta: 0 };
6968
+ }
6969
+ data.deltas[docId].recalled_delta++;
6970
+ }
6971
+ await saveUserVotes(votePath, data);
6972
+ }
6973
+ async function incrementUpvoted(votePath, docIds) {
6974
+ if (docIds.length === 0) return;
6975
+ const data = await loadUserVotes(votePath);
6976
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6977
+ for (const docId of docIds) {
6978
+ if (!data.votes[docId]) {
6979
+ data.votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
6980
+ }
6981
+ data.votes[docId].upvoted_count++;
6982
+ data.votes[docId].last_upvoted_at = now;
6983
+ if (!data.deltas[docId]) {
6984
+ data.deltas[docId] = { recalled_delta: 0, upvoted_delta: 0 };
6985
+ }
6986
+ data.deltas[docId].upvoted_delta++;
6987
+ }
6988
+ await saveUserVotes(votePath, data);
6989
+ }
6990
+ function mergeDeltas(local, remote) {
6991
+ const votes = {};
6992
+ for (const [docId, entry] of Object.entries(remote.votes)) {
6993
+ votes[docId] = { ...entry };
6994
+ }
6995
+ for (const [docId, delta] of Object.entries(local.deltas)) {
6996
+ if (!votes[docId]) {
6997
+ votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
6998
+ }
6999
+ votes[docId].recalled_count = Math.max(0, votes[docId].recalled_count + delta.recalled_delta);
7000
+ votes[docId].upvoted_count = Math.max(0, votes[docId].upvoted_count + delta.upvoted_delta);
7001
+ const localEntry = local.votes[docId];
7002
+ if (localEntry) {
7003
+ if (localEntry.last_recalled_at > (votes[docId].last_recalled_at ?? "")) {
7004
+ votes[docId].last_recalled_at = localEntry.last_recalled_at;
7005
+ }
7006
+ if (localEntry.last_upvoted_at !== void 0 && localEntry.last_upvoted_at > (votes[docId].last_upvoted_at ?? "")) {
7007
+ votes[docId].last_upvoted_at = localEntry.last_upvoted_at;
7008
+ }
7009
+ }
7010
+ }
7011
+ return { version: 2, votes, deltas: {} };
7012
+ }
7013
+ async function syncVotesToTeam(repoPath, username, localVotesDir) {
7014
+ const localVotePath = path30.join(localVotesDir, `${username}.yaml`);
7015
+ const remoteVotePath = path30.join(repoPath, "votes", `${username}.yaml`);
7016
+ const local = await loadUserVotes(localVotePath);
7017
+ if (Object.keys(local.deltas).length === 0) {
7018
+ return false;
7019
+ }
7020
+ const remote = await loadUserVotes(remoteVotePath);
7021
+ const merged = mergeDeltas(local, remote);
7022
+ await saveUserVotes(remoteVotePath, merged);
7023
+ await saveUserVotes(localVotePath, { ...local, votes: merged.votes, deltas: {} });
7024
+ return true;
7025
+ }
7026
+ async function recallFeedback(opts) {
7027
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
7028
+ const { localConfig } = await requireInit3();
7029
+ const { username } = localConfig;
7030
+ const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
7031
+ const votePath = path30.join(VOTES_LOCAL_DIR2, `${username}.yaml`);
7032
+ if (opts.positive) {
7033
+ await incrementUpvoted(votePath, [opts.positive]);
7034
+ log.success(`Upvoted: ${opts.positive}`);
7035
+ return;
7036
+ }
7037
+ if (opts.negative) {
7038
+ const data = await loadUserVotes(votePath);
7039
+ if (!data.votes[opts.negative]) {
7040
+ log.warn(`Document not found in votes: ${opts.negative}`);
7041
+ return;
7042
+ }
7043
+ const entry = data.votes[opts.negative];
7044
+ if (entry.upvoted_count <= 0) {
7045
+ log.warn(`No upvotes to decrement for: ${opts.negative}`);
7046
+ return;
7047
+ }
7048
+ const existingDelta = data.deltas[opts.negative] ?? { recalled_delta: 0, upvoted_delta: 0 };
7049
+ const updated = {
7050
+ ...data,
7051
+ votes: {
7052
+ ...data.votes,
7053
+ [opts.negative]: { ...entry, upvoted_count: entry.upvoted_count - 1 }
7054
+ },
7055
+ deltas: {
7056
+ ...data.deltas,
7057
+ [opts.negative]: { ...existingDelta, upvoted_delta: existingDelta.upvoted_delta - 1 }
7058
+ }
7059
+ };
7060
+ await saveUserVotes(votePath, updated);
7061
+ log.success(`Negative signal recorded for: ${opts.negative}`);
7062
+ return;
7063
+ }
7064
+ log.error("Usage: teamai recall feedback --positive <docId> | --negative <docId>");
7065
+ }
7066
+ var init_votes = __esm({
7067
+ "src/votes.ts"() {
7068
+ "use strict";
7069
+ init_fs();
7070
+ init_logger();
7071
+ }
7072
+ });
7073
+
7074
+ // src/maintenance/confidence.ts
7075
+ var confidence_exports = {};
7076
+ __export(confidence_exports, {
7077
+ computeAllConfidence: () => computeAllConfidence,
7078
+ computeConfidence: () => computeConfidence,
7079
+ writeBackConfidence: () => writeBackConfidence
7080
+ });
7081
+ import path31 from "path";
7082
+ import matter2 from "gray-matter";
7083
+ function computeConfidence(factors) {
7084
+ const { recalledCount, upvotedCount, lastRecalledAt } = factors;
7085
+ const base = Math.min(1, recalledCount * 0.1 + upvotedCount * 0.3);
7086
+ let daysSinceRecall = 180;
7087
+ if (lastRecalledAt) {
7088
+ const elapsed = Date.now() - new Date(lastRecalledAt).getTime();
7089
+ daysSinceRecall = Math.max(0, elapsed / (1e3 * 60 * 60 * 24));
7090
+ }
7091
+ const recency = Math.max(0, 1 - daysSinceRecall / 180);
7092
+ const ratio = upvotedCount / Math.max(1, recalledCount);
7093
+ const score = base * 0.4 + recency * 0.3 + ratio * 0.3;
7094
+ return Math.round(score * 100) / 100;
7095
+ }
7096
+ async function computeAllConfidence(votesDir) {
7097
+ const result = /* @__PURE__ */ new Map();
7098
+ const { loadUserVotes: loadUserVotes2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
7099
+ const files = await listFiles(votesDir);
7100
+ const aggregated = /* @__PURE__ */ new Map();
7101
+ for (const file of files) {
7102
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
7103
+ try {
7104
+ const data = await loadUserVotes2(path31.join(votesDir, file));
7105
+ for (const [docId, entry] of Object.entries(data.votes)) {
7106
+ const existing = aggregated.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7107
+ existing.recalled += entry.recalled_count ?? 0;
7108
+ existing.upvoted += entry.upvoted_count ?? 0;
7109
+ if (entry.last_recalled_at > existing.lastRecalled) {
7110
+ existing.lastRecalled = entry.last_recalled_at;
7111
+ }
7112
+ if (entry.last_upvoted_at && (!existing.lastUpvoted || entry.last_upvoted_at > existing.lastUpvoted)) {
7113
+ existing.lastUpvoted = entry.last_upvoted_at;
7114
+ }
7115
+ aggregated.set(docId, existing);
7116
+ }
7117
+ } catch {
7118
+ log.debug(`confidence: failed to parse votes file: ${file}`);
7119
+ }
7120
+ }
7121
+ for (const [docId, data] of aggregated) {
7122
+ const factors = {
7123
+ recalledCount: data.recalled,
7124
+ upvotedCount: data.upvoted,
7125
+ lastRecalledAt: data.lastRecalled,
7126
+ lastUpvotedAt: data.lastUpvoted
7127
+ };
7128
+ result.set(docId, computeConfidence(factors));
7129
+ }
7130
+ return result;
7131
+ }
7132
+ async function writeBackConfidence(learningsDir, confidenceMap) {
7133
+ let updated = 0;
7134
+ const files = await listFiles(learningsDir);
7135
+ for (const file of files) {
7136
+ if (!file.endsWith(".md")) continue;
7137
+ const docId = file.replace(/\.md$/i, "");
7138
+ const newConf = confidenceMap.get(docId);
7139
+ if (newConf === void 0) continue;
7140
+ const absPath = path31.join(learningsDir, file);
7141
+ const content = await readFileSafe(absPath);
7142
+ if (!content) continue;
7143
+ try {
7144
+ const { data, content: body } = matter2(content);
7145
+ const existingConf = typeof data.confidence === "number" ? data.confidence : void 0;
7146
+ if (existingConf !== void 0 && Math.abs(existingConf - newConf) <= 0.05) continue;
7147
+ data.confidence = newConf;
7148
+ const newContent = matter2.stringify(body, data);
7149
+ await writeFile(absPath, newContent);
7150
+ updated++;
7151
+ } catch {
7152
+ log.debug(`confidence: failed to update frontmatter for: ${file}`);
7153
+ }
7154
+ }
7155
+ if (updated > 0) {
7156
+ log.info(`Updated confidence scores for ${updated} learning(s)`);
7157
+ }
7158
+ return updated;
7159
+ }
7160
+ var init_confidence = __esm({
7161
+ "src/maintenance/confidence.ts"() {
7162
+ "use strict";
7163
+ init_fs();
7164
+ init_logger();
7165
+ }
7166
+ });
7167
+
7168
+ // src/maintenance/hot-cold.ts
7169
+ var hot_cold_exports = {};
7170
+ __export(hot_cold_exports, {
7171
+ COLD_PENALTY: () => COLD_PENALTY,
7172
+ HOT_THRESHOLD: () => HOT_THRESHOLD,
7173
+ annotateHotness: () => annotateHotness
7174
+ });
7175
+ function annotateHotness(entries, confidenceMap) {
7176
+ for (const entry of entries) {
7177
+ const docId = entry.filename.replace(/\.md$/i, "");
7178
+ const confidence = confidenceMap.get(docId);
7179
+ entry.confidence = confidence;
7180
+ if (confidence === void 0) {
7181
+ entry.hotness = 1;
7182
+ } else {
7183
+ entry.hotness = confidence >= HOT_THRESHOLD ? 1 : COLD_PENALTY;
7184
+ }
7185
+ }
7186
+ }
7187
+ var HOT_THRESHOLD, COLD_PENALTY;
7188
+ var init_hot_cold = __esm({
7189
+ "src/maintenance/hot-cold.ts"() {
7190
+ "use strict";
7191
+ HOT_THRESHOLD = 0.5;
7192
+ COLD_PENALTY = 0.3;
7193
+ }
7194
+ });
7195
+
6785
7196
  // src/utils/search-index.ts
6786
7197
  var search_index_exports = {};
6787
7198
  __export(search_index_exports, {
@@ -6795,8 +7206,8 @@ __export(search_index_exports, {
6795
7206
  titleFromFilename: () => titleFromFilename,
6796
7207
  tokenize: () => tokenize
6797
7208
  });
6798
- import path30 from "path";
6799
- import matter2 from "gray-matter";
7209
+ import path32 from "path";
7210
+ import matter3 from "gray-matter";
6800
7211
  function getSearchIndexPath() {
6801
7212
  return `${process.env.HOME ?? ""}/.teamai/search-index.json`;
6802
7213
  }
@@ -6850,7 +7261,7 @@ function inferDomain(frontmatterDomain, tags, filePath, type) {
6850
7261
  function parseLearningDoc(content, filename) {
6851
7262
  if (!content.trim()) return null;
6852
7263
  try {
6853
- const { data, content: body } = matter2(content);
7264
+ const { data, content: body } = matter3(content);
6854
7265
  const meta = {
6855
7266
  title: typeof data.title === "string" ? data.title : void 0,
6856
7267
  author: typeof data.author === "string" ? data.author : void 0,
@@ -6871,31 +7282,61 @@ function titleFromFilename(filename) {
6871
7282
  return filename.replace(/\.md$/i, "").replace(/-\d{4}-\d{2}-\d{2}.*$/, "").replace(/[-_]/g, " ").trim();
6872
7283
  }
6873
7284
  async function aggregateVotes(votesDir) {
6874
- const counts = /* @__PURE__ */ new Map();
7285
+ const scores = /* @__PURE__ */ new Map();
7286
+ const agg = /* @__PURE__ */ new Map();
6875
7287
  const files = await listFiles(votesDir);
6876
7288
  for (const file of files) {
6877
7289
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
6878
- const content = await readFileSafe(path30.join(votesDir, file));
7290
+ const content = await readFileSafe(path32.join(votesDir, file));
6879
7291
  if (!content) continue;
6880
7292
  try {
6881
7293
  const YAML18 = (await import("yaml")).default;
6882
7294
  const parsed = YAML18.parse(content);
6883
- if (parsed?.votes) {
6884
- for (const docId of Object.keys(parsed.votes)) {
6885
- counts.set(docId, (counts.get(docId) ?? 0) + 1);
7295
+ if (!parsed?.votes) continue;
7296
+ if (parsed.version === 2) {
7297
+ const votes = parsed.votes;
7298
+ for (const [docId, entry] of Object.entries(votes)) {
7299
+ const recalled = entry.recalled_count ?? 0;
7300
+ const upvoted = entry.upvoted_count ?? 0;
7301
+ scores.set(docId, (scores.get(docId) ?? 0) + recalled * 0.3 + upvoted * 1);
7302
+ const existing = agg.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7303
+ existing.recalled += recalled;
7304
+ existing.upvoted += upvoted;
7305
+ if (entry.last_recalled_at > existing.lastRecalled) {
7306
+ existing.lastRecalled = entry.last_recalled_at;
7307
+ }
7308
+ agg.set(docId, existing);
7309
+ }
7310
+ } else {
7311
+ const votes = parsed.votes;
7312
+ for (const [docId, entry] of Object.entries(votes)) {
7313
+ scores.set(docId, (scores.get(docId) ?? 0) + 1);
7314
+ const existing = agg.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7315
+ existing.recalled += 1;
7316
+ if (entry.at > existing.lastRecalled) existing.lastRecalled = entry.at;
7317
+ agg.set(docId, existing);
6886
7318
  }
6887
7319
  }
6888
7320
  } catch {
6889
7321
  log.error(`Failed to parse votes file: ${file}`);
6890
7322
  }
6891
7323
  }
6892
- return counts;
7324
+ const { computeConfidence: computeConfidence2 } = await Promise.resolve().then(() => (init_confidence(), confidence_exports));
7325
+ const confidenceMap = /* @__PURE__ */ new Map();
7326
+ for (const [docId, data] of agg) {
7327
+ confidenceMap.set(docId, computeConfidence2({
7328
+ recalledCount: data.recalled,
7329
+ upvotedCount: data.upvoted,
7330
+ lastRecalledAt: data.lastRecalled
7331
+ }));
7332
+ }
7333
+ return { scores, confidenceMap };
6893
7334
  }
6894
7335
  async function entryFromMdFile(absPath, filenameForId, type, voteCounts) {
6895
- const basename = path30.basename(absPath);
7336
+ const basename = path32.basename(absPath);
6896
7337
  if (basename === CODEBASE_FULL_FILENAME) {
6897
- const dir = path30.dirname(absPath);
6898
- const indexPath = path30.join(dir, CODEBASE_INDEX_FILENAME);
7338
+ const dir = path32.dirname(absPath);
7339
+ const indexPath = path32.join(dir, CODEBASE_INDEX_FILENAME);
6899
7340
  if (await pathExists(indexPath)) {
6900
7341
  log.debug(`Skipping ${absPath}: codebase-index.md exists in same directory`);
6901
7342
  return null;
@@ -6914,7 +7355,7 @@ async function entryFromMdFile(absPath, filenameForId, type, voteCounts) {
6914
7355
  const tags = meta.tags ?? [];
6915
7356
  const rawFrontmatterDomain = (() => {
6916
7357
  try {
6917
- return matter2(content).data["domain"];
7358
+ return matter3(content).data["domain"];
6918
7359
  } catch {
6919
7360
  return void 0;
6920
7361
  }
@@ -6952,7 +7393,7 @@ async function collectFlatMdEntries(dir, type, voteCounts) {
6952
7393
  const out = [];
6953
7394
  for (const filename of files) {
6954
7395
  if (!filename.endsWith(".md")) continue;
6955
- const e = await entryFromMdFile(path30.join(dir, filename), filename, type, voteCounts);
7396
+ const e = await entryFromMdFile(path32.join(dir, filename), filename, type, voteCounts);
6956
7397
  if (e) out.push(e);
6957
7398
  }
6958
7399
  return out;
@@ -6963,7 +7404,7 @@ async function collectRecursiveMdEntries(dir, type, voteCounts) {
6963
7404
  const out = [];
6964
7405
  for (const rel of files) {
6965
7406
  if (!rel.endsWith(".md")) continue;
6966
- const e = await entryFromMdFile(path30.join(dir, rel), rel, type, voteCounts);
7407
+ const e = await entryFromMdFile(path32.join(dir, rel), rel, type, voteCounts);
6967
7408
  if (e) out.push(e);
6968
7409
  }
6969
7410
  return out;
@@ -6975,8 +7416,8 @@ async function collectSkillEntries(dir, voteCounts) {
6975
7416
  const subdirs = await listDirs(current);
6976
7417
  for (const sub of subdirs) {
6977
7418
  if (sub.startsWith(".")) continue;
6978
- const subPath = path30.join(current, sub);
6979
- const skillMd = path30.join(subPath, "SKILL.md");
7419
+ const subPath = path32.join(current, sub);
7420
+ const skillMd = path32.join(subPath, "SKILL.md");
6980
7421
  if (await pathExists(skillMd)) {
6981
7422
  const e = await entryFromMdFile(skillMd, `${sub}.md`, "skills", voteCounts);
6982
7423
  if (e) out.push(e);
@@ -6991,7 +7432,8 @@ async function collectSkillEntries(dir, voteCounts) {
6991
7432
  async function buildIndex(optionsOrLearningsDir, votesDir, indexPath) {
6992
7433
  const start = Date.now();
6993
7434
  const opts = typeof optionsOrLearningsDir === "string" ? { learningsDir: optionsOrLearningsDir, votesDir, indexPath } : optionsOrLearningsDir;
6994
- const voteCounts = opts.votesDir ? await aggregateVotes(opts.votesDir) : /* @__PURE__ */ new Map();
7435
+ const voteAgg = opts.votesDir ? await aggregateVotes(opts.votesDir) : { scores: /* @__PURE__ */ new Map(), confidenceMap: /* @__PURE__ */ new Map() };
7436
+ const voteCounts = voteAgg.scores;
6995
7437
  const entries = [];
6996
7438
  if (opts.learningsDir) {
6997
7439
  entries.push(...await collectFlatMdEntries(opts.learningsDir, "learnings", voteCounts));
@@ -7008,6 +7450,13 @@ async function buildIndex(optionsOrLearningsDir, votesDir, indexPath) {
7008
7450
  if (opts.codebaseDir) {
7009
7451
  entries.push(...await collectRecursiveMdEntries(opts.codebaseDir, "docs", voteCounts));
7010
7452
  }
7453
+ if (voteAgg.confidenceMap.size > 0) {
7454
+ try {
7455
+ const { annotateHotness: annotateHotness2 } = await Promise.resolve().then(() => (init_hot_cold(), hot_cold_exports));
7456
+ annotateHotness2(entries, voteAgg.confidenceMap);
7457
+ } catch {
7458
+ }
7459
+ }
7011
7460
  const df = {};
7012
7461
  for (const entry of entries) {
7013
7462
  for (const token of new Set(entry.tokens)) {
@@ -7085,9 +7534,12 @@ function search(query, index, limit = 5) {
7085
7534
  const domainMultiplier = domainWeightRow[entry.domain ?? "neutral"];
7086
7535
  const typeMultiplier = TYPE_BONUS[entry.type];
7087
7536
  score *= domainMultiplier * typeMultiplier;
7088
- if (path30.basename(entry.path ?? "") === CODEBASE_INDEX_FILENAME) {
7537
+ if (path32.basename(entry.path ?? "") === CODEBASE_INDEX_FILENAME) {
7089
7538
  score *= CODEBASE_INDEX_WEIGHT_BOOST;
7090
7539
  }
7540
+ if (entry.hotness !== void 0 && entry.hotness < 1) {
7541
+ score *= entry.hotness;
7542
+ }
7091
7543
  results.push({ entry, score });
7092
7544
  }
7093
7545
  }
@@ -7278,12 +7730,12 @@ __export(usage_tracker_exports, {
7278
7730
  updateKnownSkills: () => updateKnownSkills
7279
7731
  });
7280
7732
  import fs10 from "fs";
7281
- import path31 from "path";
7733
+ import path33 from "path";
7282
7734
  function getUsagePath() {
7283
- return path31.join(process.env.HOME ?? "", ".teamai", "usage.jsonl");
7735
+ return path33.join(process.env.HOME ?? "", ".teamai", "usage.jsonl");
7284
7736
  }
7285
7737
  function getKnownSkillsPath() {
7286
- return path31.join(process.env.HOME ?? "", ".teamai", "known-skills.json");
7738
+ return path33.join(process.env.HOME ?? "", ".teamai", "known-skills.json");
7287
7739
  }
7288
7740
  function extractSkillName(toolInput) {
7289
7741
  try {
@@ -7309,13 +7761,13 @@ function isValidSkillName(name) {
7309
7761
  async function skillExistsOnDisk(skillName) {
7310
7762
  const home = process.env.HOME ?? "";
7311
7763
  for (const dir of SKILL_DIRS) {
7312
- const skillMd = path31.join(home, dir, skillName, "SKILL.md");
7764
+ const skillMd = path33.join(home, dir, skillName, "SKILL.md");
7313
7765
  if (await pathExists(skillMd)) return true;
7314
7766
  }
7315
7767
  const cwd = process.cwd();
7316
- if (path31.resolve(cwd) !== path31.resolve(home)) {
7768
+ if (path33.resolve(cwd) !== path33.resolve(home)) {
7317
7769
  for (const dir of SKILL_DIRS) {
7318
- const skillMd = path31.join(cwd, dir, skillName, "SKILL.md");
7770
+ const skillMd = path33.join(cwd, dir, skillName, "SKILL.md");
7319
7771
  if (await pathExists(skillMd)) return true;
7320
7772
  }
7321
7773
  }
@@ -7323,7 +7775,7 @@ async function skillExistsOnDisk(skillName) {
7323
7775
  }
7324
7776
  async function appendUsageEvent(event) {
7325
7777
  try {
7326
- await ensureDir(path31.dirname(getUsagePath()));
7778
+ await ensureDir(path33.dirname(getUsagePath()));
7327
7779
  const line = JSON.stringify(event) + "\n";
7328
7780
  await fs10.promises.appendFile(getUsagePath(), line, "utf-8");
7329
7781
  log.debug(`Tracked skill: ${event.skill}`);
@@ -7717,7 +8169,7 @@ __export(dashboard_collector_exports, {
7717
8169
  scanTranscriptStop: () => scanTranscriptStop
7718
8170
  });
7719
8171
  import fs12 from "fs";
7720
- import path32 from "path";
8172
+ import path34 from "path";
7721
8173
  import readline2 from "readline";
7722
8174
  async function readStdin2() {
7723
8175
  if (process.stdin.isTTY) return "";
@@ -7768,7 +8220,7 @@ async function scanTranscriptStop(transcriptPath) {
7768
8220
  let prompts = 0;
7769
8221
  const tokens = emptyTokenUsage();
7770
8222
  const countedUsageKeys = /* @__PURE__ */ new Set();
7771
- if (path32.basename(transcriptPath) === "index.json") {
8223
+ if (path34.basename(transcriptPath) === "index.json") {
7772
8224
  const cb = await scanCodebuddyIndex(transcriptPath);
7773
8225
  if (cb) return cb;
7774
8226
  }
@@ -7967,12 +8419,12 @@ async function parseHookEvent(raw, tool) {
7967
8419
  return event;
7968
8420
  }
7969
8421
  function getEventsPath() {
7970
- return path32.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
8422
+ return path34.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
7971
8423
  }
7972
8424
  async function appendEvent(event) {
7973
8425
  try {
7974
8426
  const eventsPath = getEventsPath();
7975
- await ensureDir(path32.dirname(eventsPath));
8427
+ await ensureDir(path34.dirname(eventsPath));
7976
8428
  const line = JSON.stringify(event) + "\n";
7977
8429
  await fs12.promises.appendFile(eventsPath, line, "utf-8");
7978
8430
  const detail = event.toolName ? ` [tool=${event.toolName}]` : event.promptSummary ? ` [prompt=${event.promptSummary.slice(0, 60)}]` : "";
@@ -8254,20 +8706,20 @@ __export(digest_exports, {
8254
8706
  summarizeConversation: () => summarizeConversation,
8255
8707
  summarizeInterventions: () => summarizeInterventions
8256
8708
  });
8257
- import YAML9 from "yaml";
8258
- import path33 from "path";
8709
+ import YAML10 from "yaml";
8710
+ import path35 from "path";
8259
8711
  import fs13 from "fs";
8260
8712
  async function loadTeamStats(repoPath) {
8261
- const statsDir = path33.join(repoPath, "stats");
8713
+ const statsDir = path35.join(repoPath, "stats");
8262
8714
  const stats = [];
8263
8715
  try {
8264
8716
  const files = await listFiles(statsDir);
8265
8717
  for (const file of files) {
8266
8718
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
8267
- const content = await readFileSafe(path33.join(statsDir, file));
8719
+ const content = await readFileSafe(path35.join(statsDir, file));
8268
8720
  if (!content) continue;
8269
8721
  try {
8270
- const parsed = YAML9.parse(content);
8722
+ const parsed = YAML10.parse(content);
8271
8723
  if (parsed?.username && parsed?.skills) {
8272
8724
  stats.push(parsed);
8273
8725
  }
@@ -8361,17 +8813,17 @@ async function getRecentSkillChanges(repoPath) {
8361
8813
  return changes;
8362
8814
  }
8363
8815
  async function getRecentSessions(repoPath) {
8364
- const sessionsDir = path33.join(repoPath, "sessions");
8816
+ const sessionsDir = path35.join(repoPath, "sessions");
8365
8817
  const summaries = [];
8366
8818
  try {
8367
8819
  const userDirs = await fs13.promises.readdir(sessionsDir, { withFileTypes: true });
8368
8820
  for (const userDir of userDirs) {
8369
8821
  if (!userDir.isDirectory()) continue;
8370
- const userSessionsDir = path33.join(sessionsDir, userDir.name);
8822
+ const userSessionsDir = path35.join(sessionsDir, userDir.name);
8371
8823
  const files = await listFiles(userSessionsDir);
8372
8824
  for (const file of files) {
8373
8825
  if (!file.endsWith(".md")) continue;
8374
- const content = await readFileSafe(path33.join(userSessionsDir, file));
8826
+ const content = await readFileSafe(path35.join(userSessionsDir, file));
8375
8827
  if (content) {
8376
8828
  summaries.push(`[${userDir.name}] ${file}:
8377
8829
  ${content.slice(0, 500)}`);
@@ -8383,7 +8835,7 @@ ${content.slice(0, 500)}`);
8383
8835
  return summaries;
8384
8836
  }
8385
8837
  async function getRecentLearnings(repoPath) {
8386
- const learningsDir = path33.join(repoPath, "learnings");
8838
+ const learningsDir = path35.join(repoPath, "learnings");
8387
8839
  const recent = [];
8388
8840
  let total = 0;
8389
8841
  try {
@@ -8396,7 +8848,7 @@ async function getRecentLearnings(repoPath) {
8396
8848
  if (!dateMatch) continue;
8397
8849
  const fileDate = dateMatch[1];
8398
8850
  if (fileDate < cutoff) continue;
8399
- const content = await readFileSafe(path33.join(learningsDir, filename));
8851
+ const content = await readFileSafe(path35.join(learningsDir, filename));
8400
8852
  if (!content) continue;
8401
8853
  const parsed = parseLearningDoc(content, filename);
8402
8854
  const title = parsed?.meta.title ?? titleFromFilename(filename);
@@ -8580,8 +9032,8 @@ __export(stats_exports, {
8580
9032
  aggregateUsage: () => aggregateUsage,
8581
9033
  showStats: () => showStats
8582
9034
  });
8583
- import YAML10 from "yaml";
8584
- import path34 from "path";
9035
+ import YAML11 from "yaml";
9036
+ import path36 from "path";
8585
9037
  function aggregateUsage(events) {
8586
9038
  const map = /* @__PURE__ */ new Map();
8587
9039
  for (const event of events) {
@@ -8606,10 +9058,10 @@ async function loadReportedStats() {
8606
9058
  try {
8607
9059
  const config = await detectProjectConfig() ?? await loadLocalConfig();
8608
9060
  if (!config) return null;
8609
- const statsPath = path34.join(config.repo.localPath, "stats", `${config.username}.yaml`);
9061
+ const statsPath = path36.join(config.repo.localPath, "stats", `${config.username}.yaml`);
8610
9062
  const content = await readFileSafe(statsPath);
8611
9063
  if (!content) return null;
8612
- const parsed = YAML10.parse(content);
9064
+ const parsed = YAML11.parse(content);
8613
9065
  if (parsed?.username && parsed?.skills) return parsed;
8614
9066
  return null;
8615
9067
  } catch {
@@ -8762,13 +9214,13 @@ __export(team_push_exports, {
8762
9214
  mergeStats: () => mergeStats,
8763
9215
  reportUsageToTeam: () => reportUsageToTeam
8764
9216
  });
8765
- import YAML11 from "yaml";
8766
- import path35 from "path";
9217
+ import YAML12 from "yaml";
9218
+ import path37 from "path";
8767
9219
  async function readExistingStats(statsPath) {
8768
9220
  try {
8769
9221
  const content = await readFileSafe(statsPath);
8770
9222
  if (!content) return null;
8771
- const parsed = YAML11.parse(content);
9223
+ const parsed = YAML12.parse(content);
8772
9224
  if (parsed?.username && parsed?.skills) return parsed;
8773
9225
  return null;
8774
9226
  } catch {
@@ -8801,7 +9253,7 @@ function mergeStats(existing, username, newEvents) {
8801
9253
  };
8802
9254
  }
8803
9255
  function getReportedInterventionsPath() {
8804
- return path35.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-interventions.json");
9256
+ return path37.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-interventions.json");
8805
9257
  }
8806
9258
  async function readReportedInterventions() {
8807
9259
  const parsed = await readJson(getReportedInterventionsPath());
@@ -8839,7 +9291,7 @@ function hasInterventionDelta(d) {
8839
9291
  return d.sessions > 0 || d.interrupt > 0 || d.toolReject > 0 || d.correction > 0;
8840
9292
  }
8841
9293
  function getReportedPromptTokensPath() {
8842
- return path35.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-prompt-tokens.json");
9294
+ return path37.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-prompt-tokens.json");
8843
9295
  }
8844
9296
  async function readReportedPromptTokens() {
8845
9297
  try {
@@ -8854,7 +9306,7 @@ async function readReportedPromptTokens() {
8854
9306
  async function writeReportedPromptTokens(data) {
8855
9307
  try {
8856
9308
  const p = getReportedPromptTokensPath();
8857
- await ensureDir(path35.dirname(p));
9309
+ await ensureDir(path37.dirname(p));
8858
9310
  await writeFile(p, JSON.stringify(data));
8859
9311
  } catch (e) {
8860
9312
  log.error(`Failed to persist reported prompt/token snapshot: ${e.message}`);
@@ -8932,9 +9384,9 @@ async function reportUsageToTeam(repoPath, username, options) {
8932
9384
  await resetToCleanMaster(git, repoPath);
8933
9385
  await pullRepo(repoPath);
8934
9386
  if (hasUsage || hasInterventions || hasPromptTokens) {
8935
- const statsDir = path35.join(repoPath, "stats");
9387
+ const statsDir = path37.join(repoPath, "stats");
8936
9388
  await ensureDir(statsDir);
8937
- const statsPath = path35.join(statsDir, `${username}.yaml`);
9389
+ const statsPath = path37.join(statsDir, `${username}.yaml`);
8938
9390
  const existing = await readExistingStats(statsPath);
8939
9391
  const newStats = hasUsage ? aggregateUsage(events) : [];
8940
9392
  const merged = mergeStats(existing, username, newStats);
@@ -8946,22 +9398,15 @@ async function reportUsageToTeam(repoPath, username, options) {
8946
9398
  merged.prompts = pt.prompts;
8947
9399
  merged.tokens = pt.tokens;
8948
9400
  }
8949
- await writeFile(statsPath, YAML11.stringify(merged));
9401
+ await writeFile(statsPath, YAML12.stringify(merged));
8950
9402
  filesToPush.push(`stats/${username}.yaml`);
8951
9403
  }
8952
9404
  try {
8953
9405
  if (await pathExists(VOTES_LOCAL_DIR)) {
8954
- const voteFiles = await listFiles(VOTES_LOCAL_DIR);
8955
- for (const vf of voteFiles) {
8956
- if (!vf.endsWith(".yaml") && !vf.endsWith(".yml")) continue;
8957
- const localVotePath = path35.join(VOTES_LOCAL_DIR, vf);
8958
- const repoVotePath = path35.join(repoPath, "votes", vf);
8959
- const content = await readFileSafe(localVotePath);
8960
- if (content) {
8961
- await ensureDir(path35.join(repoPath, "votes"));
8962
- await writeFile(repoVotePath, content);
8963
- filesToPush.push(`votes/${vf}`);
8964
- }
9406
+ const { syncVotesToTeam: syncVotesToTeam2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
9407
+ const synced = await syncVotesToTeam2(repoPath, username, VOTES_LOCAL_DIR);
9408
+ if (synced) {
9409
+ filesToPush.push(`votes/${username}.yaml`);
8965
9410
  }
8966
9411
  }
8967
9412
  } catch (e) {
@@ -9024,9 +9469,9 @@ __export(pull_exports, {
9024
9469
  pull: () => pull,
9025
9470
  scanRoleAwareSkills: () => scanRoleAwareSkills
9026
9471
  });
9027
- import path36 from "path";
9472
+ import path38 from "path";
9028
9473
  import fse6 from "fs-extra";
9029
- import matter3 from "gray-matter";
9474
+ import matter4 from "gray-matter";
9030
9475
  async function refreshTeamRepo(localConfig) {
9031
9476
  if (localConfig.repo.kind === "http") {
9032
9477
  const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
@@ -9088,14 +9533,14 @@ async function buildRolePullContext(localConfig) {
9088
9533
  const activeSkillNames = /* @__PURE__ */ new Set();
9089
9534
  const inactiveSkillNames = /* @__PURE__ */ new Set();
9090
9535
  for (const namespace of activeNamespaces.skills) {
9091
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9536
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9092
9537
  const names = await listDirs(namespaceDir);
9093
9538
  for (const name of names) {
9094
9539
  activeSkillNames.add(name);
9095
9540
  }
9096
9541
  }
9097
9542
  for (const namespace of inactiveSkillNamespaces) {
9098
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9543
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9099
9544
  const names = await listDirs(namespaceDir);
9100
9545
  for (const name of names) {
9101
9546
  inactiveSkillNames.add(name);
@@ -9115,7 +9560,7 @@ function filterRulesByKnowledgeNamespaces(rules, knowledgeNamespaces) {
9115
9560
  async function scanRoleAwareSkills(localConfig, namespaces) {
9116
9561
  const items = /* @__PURE__ */ new Map();
9117
9562
  for (const namespace of namespaces.skills) {
9118
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9563
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9119
9564
  const dirs = await listDirs(namespaceDir);
9120
9565
  for (const dir of dirs) {
9121
9566
  const existing = items.get(dir);
@@ -9125,7 +9570,7 @@ async function scanRoleAwareSkills(localConfig, namespaces) {
9125
9570
  items.set(dir, {
9126
9571
  name: dir,
9127
9572
  type: "skills",
9128
- sourcePath: path36.join(namespaceDir, dir),
9573
+ sourcePath: path38.join(namespaceDir, dir),
9129
9574
  relativePath: `skills/${namespace}/${dir}`,
9130
9575
  namespace
9131
9576
  });
@@ -9138,13 +9583,13 @@ async function cleanupInactiveNamespaceSkills(teamConfig, localConfig, activeSki
9138
9583
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
9139
9584
  if (!toolPath.skills) continue;
9140
9585
  if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) continue;
9141
- if (!await pathExists(path36.join(baseDir, toolPath.skills))) continue;
9142
- const localSkillNames = await listDirs(path36.join(baseDir, toolPath.skills));
9586
+ if (!await pathExists(path38.join(baseDir, toolPath.skills))) continue;
9587
+ const localSkillNames = await listDirs(path38.join(baseDir, toolPath.skills));
9143
9588
  for (const skillName of localSkillNames) {
9144
9589
  if (BUILTIN_SKILL_NAMES.has(skillName)) continue;
9145
9590
  if (activeSkillNames.has(skillName)) continue;
9146
9591
  if (!inactiveSkillNames.has(skillName)) continue;
9147
- const localSkillDir = path36.join(baseDir, toolPath.skills, skillName);
9592
+ const localSkillDir = path38.join(baseDir, toolPath.skills, skillName);
9148
9593
  await remove(localSkillDir);
9149
9594
  log.debug(`[${localConfig.scope}] Removed inactive role-scoped skill ${skillName} from ${tool}`);
9150
9595
  }
@@ -9156,10 +9601,10 @@ async function getExistingLocalNames(type, items, teamConfig, localConfig) {
9156
9601
  if (type === "skills") {
9157
9602
  for (const [_tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
9158
9603
  if (!toolPath.skills) continue;
9159
- const skillsDir = path36.join(baseDir, toolPath.skills);
9604
+ const skillsDir = path38.join(baseDir, toolPath.skills);
9160
9605
  if (!await pathExists(skillsDir)) continue;
9161
9606
  for (const item of items) {
9162
- const skillDir = path36.join(skillsDir, item.name);
9607
+ const skillDir = path38.join(skillsDir, item.name);
9163
9608
  if (await pathExists(skillDir)) {
9164
9609
  existing.add(item.name);
9165
9610
  }
@@ -9369,7 +9814,7 @@ async function pullForScope(localConfig, options) {
9369
9814
  if (!dir) continue;
9370
9815
  if (!await ResourceHandler.isToolInstalled(dir, baseDir)) continue;
9371
9816
  for (const name of tombstones) {
9372
- const localPath = path36.join(baseDir, dir, ext ? `${name}${ext}` : name);
9817
+ const localPath = path38.join(baseDir, dir, ext ? `${name}${ext}` : name);
9373
9818
  if (await pathExists(localPath)) {
9374
9819
  await remove(localPath);
9375
9820
  log.debug(`[${scopeLabel}] Cleaned up tombstoned ${type} ${name} from ${dir}`);
@@ -9391,14 +9836,14 @@ async function pullForScope(localConfig, options) {
9391
9836
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9392
9837
  if (!toolPath.skills) continue;
9393
9838
  if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) continue;
9394
- const skillsDir = path36.join(baseDir, toolPath.skills);
9839
+ const skillsDir = path38.join(baseDir, toolPath.skills);
9395
9840
  if (!await pathExists(skillsDir)) continue;
9396
9841
  const localDirs = await listDirs(skillsDir);
9397
9842
  for (const dir of localDirs) {
9398
9843
  if (BUILTIN_SKILL_NAMES.has(dir)) continue;
9399
9844
  if (desiredSkillNames.has(dir)) continue;
9400
9845
  if (!knownRepoSkillNames.has(dir)) continue;
9401
- const skillDir = path36.join(skillsDir, dir);
9846
+ const skillDir = path38.join(skillsDir, dir);
9402
9847
  await remove(skillDir);
9403
9848
  log.debug(`Removed excluded skill ${dir} from ${tool}`);
9404
9849
  }
@@ -9422,18 +9867,18 @@ async function pullForScope(localConfig, options) {
9422
9867
  }
9423
9868
  if (!options.dryRun) {
9424
9869
  try {
9425
- const learningsRepoDir = path36.join(localConfig.repo.localPath, "learnings");
9426
- const docsRepoDir = path36.join(localConfig.repo.localPath, "docs");
9427
- const rulesRepoDir = path36.join(localConfig.repo.localPath, "rules");
9428
- const skillsRepoDir = path36.join(localConfig.repo.localPath, "skills");
9429
- const votesDir = path36.join(localConfig.repo.localPath, "votes");
9870
+ const learningsRepoDir = path38.join(localConfig.repo.localPath, "learnings");
9871
+ const docsRepoDir = path38.join(localConfig.repo.localPath, "docs");
9872
+ const rulesRepoDir = path38.join(localConfig.repo.localPath, "rules");
9873
+ const skillsRepoDir = path38.join(localConfig.repo.localPath, "skills");
9874
+ const votesDir = path38.join(localConfig.repo.localPath, "votes");
9430
9875
  let learningsCount = 0;
9431
9876
  let effectiveLearningsDir;
9432
9877
  if (localConfig.scope === "user") {
9433
9878
  if (await pathExists(learningsRepoDir)) {
9434
9879
  await fse6.copy(learningsRepoDir, LEARNINGS_LOCAL_DIR, {
9435
9880
  overwrite: true,
9436
- filter: (src) => !path36.basename(src).startsWith(".")
9881
+ filter: (src) => !path38.basename(src).startsWith(".")
9437
9882
  });
9438
9883
  const allFiles = await listFiles(learningsRepoDir);
9439
9884
  learningsCount = allFiles.filter((f) => f.endsWith(".md")).length;
@@ -9447,12 +9892,12 @@ async function pullForScope(localConfig, options) {
9447
9892
  }
9448
9893
  }
9449
9894
  const hasAnySource = effectiveLearningsDir || await pathExists(docsRepoDir) || await pathExists(rulesRepoDir) || await pathExists(skillsRepoDir);
9450
- const repoCodebaseDir = path36.join(localConfig.repo.localPath, "docs", "team-codebase");
9895
+ const repoCodebaseDir = path38.join(localConfig.repo.localPath, "docs", "team-codebase");
9451
9896
  const effectiveCodebaseDir = await pathExists(repoCodebaseDir) ? repoCodebaseDir : void 0;
9452
9897
  if (hasAnySource || effectiveCodebaseDir) {
9453
9898
  const votesExist = await pathExists(votesDir);
9454
9899
  const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
9455
- const indexPath = path36.join(teamaiHome, "search-index.json");
9900
+ const indexPath = path38.join(teamaiHome, "search-index.json");
9456
9901
  const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
9457
9902
  const elapsed = await buildIndex2({
9458
9903
  learningsDir: effectiveLearningsDir,
@@ -9476,18 +9921,18 @@ async function pullForScope(localConfig, options) {
9476
9921
  }
9477
9922
  if (!options.dryRun) {
9478
9923
  try {
9479
- const teamDomainsPath = path36.join(localConfig.repo.localPath, ".teamai", "domains.yaml");
9924
+ const teamDomainsPath = path38.join(localConfig.repo.localPath, ".teamai", "domains.yaml");
9480
9925
  if (await pathExists(teamDomainsPath)) {
9481
- const localDomainsDir = path36.join(process.cwd(), ".teamai");
9926
+ const localDomainsDir = path38.join(process.cwd(), ".teamai");
9482
9927
  await fse6.ensureDir(localDomainsDir);
9483
- await fse6.copy(teamDomainsPath, path36.join(localDomainsDir, "domains.yaml"), { overwrite: true });
9928
+ await fse6.copy(teamDomainsPath, path38.join(localDomainsDir, "domains.yaml"), { overwrite: true });
9484
9929
  }
9485
9930
  } catch {
9486
9931
  }
9487
9932
  }
9488
9933
  if (!options.dryRun) {
9489
9934
  try {
9490
- const culturePath = path36.join(localConfig.repo.localPath, "culture.md");
9935
+ const culturePath = path38.join(localConfig.repo.localPath, "culture.md");
9491
9936
  if (await pathExists(culturePath)) {
9492
9937
  const cultureContent = await readFileSafe(culturePath);
9493
9938
  if (cultureContent) {
@@ -9497,7 +9942,7 @@ async function pullForScope(localConfig, options) {
9497
9942
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9498
9943
  if (!toolPath.claudemd) continue;
9499
9944
  if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
9500
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
9945
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9501
9946
  try {
9502
9947
  await injectClaudeMdSection(claudeMdPath, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, compiled);
9503
9948
  log.debug(`Injected culture into ${tool} CLAUDE.md`);
@@ -9526,7 +9971,7 @@ async function pullForScope(localConfig, options) {
9526
9971
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9527
9972
  if (!toolPath.claudemd) continue;
9528
9973
  if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
9529
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
9974
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9530
9975
  try {
9531
9976
  await injectClaudeMdSection(claudeMdPath, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, compiled);
9532
9977
  log.debug(`Injected shared instructions into ${tool} CLAUDE.md`);
@@ -9549,7 +9994,7 @@ async function pullForScope(localConfig, options) {
9549
9994
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9550
9995
  if (!toolPath.claudemd || !toolPath.agents) continue;
9551
9996
  if (!await ResourceHandler.isToolInstalled(toolPath.agents, baseDir)) continue;
9552
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
9997
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9553
9998
  try {
9554
9999
  await injectClaudeMdSection(
9555
10000
  claudeMdPath,
@@ -9609,14 +10054,14 @@ async function pullForScope(localConfig, options) {
9609
10054
  if (!options.silent && !options.dryRun) {
9610
10055
  try {
9611
10056
  const YAML18 = (await import("yaml")).default;
9612
- const { listFiles: listFiles2, readFileSafe: readFileSafe4 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
10057
+ const { listFiles: listFiles2, readFileSafe: readFileSafe5 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
9613
10058
  const { getRecommendations: getRecommendations2, displayRecommendations: displayRecommendations2 } = await Promise.resolve().then(() => (init_skill_recommend(), skill_recommend_exports));
9614
- const statsDir = path36.join(localConfig.repo.localPath, "stats");
10059
+ const statsDir = path38.join(localConfig.repo.localPath, "stats");
9615
10060
  const files = await listFiles2(statsDir);
9616
10061
  const teamStats = [];
9617
10062
  for (const file of files) {
9618
10063
  if (!file.endsWith(".yaml")) continue;
9619
- const content = await readFileSafe4(path36.join(statsDir, file));
10064
+ const content = await readFileSafe5(path38.join(statsDir, file));
9620
10065
  if (!content) continue;
9621
10066
  try {
9622
10067
  const parsed = YAML18.parse(content);
@@ -9635,7 +10080,7 @@ async function pullForScope(localConfig, options) {
9635
10080
  function compileCulture(raw) {
9636
10081
  let parsed;
9637
10082
  try {
9638
- parsed = matter3(raw);
10083
+ parsed = matter4(raw);
9639
10084
  } catch {
9640
10085
  return null;
9641
10086
  }
@@ -9740,7 +10185,7 @@ function compileRecallRulesBlock() {
9740
10185
  return lines.join("\n");
9741
10186
  }
9742
10187
  async function collectClaudemdFiles(repoPath, roleContext) {
9743
- const claudemdDir = path36.join(repoPath, "claudemd");
10188
+ const claudemdDir = path38.join(repoPath, "claudemd");
9744
10189
  if (!await pathExists(claudemdDir)) return [];
9745
10190
  let namespaceDirs;
9746
10191
  if (roleContext) {
@@ -9750,11 +10195,11 @@ async function collectClaudemdFiles(repoPath, roleContext) {
9750
10195
  }
9751
10196
  const contents = [];
9752
10197
  for (const ns of namespaceDirs) {
9753
- const nsDir = path36.join(claudemdDir, ns);
10198
+ const nsDir = path38.join(claudemdDir, ns);
9754
10199
  if (!await pathExists(nsDir)) continue;
9755
10200
  const files = (await listFiles(nsDir)).filter((f) => f.endsWith(".md")).sort();
9756
10201
  for (const file of files) {
9757
- const content = await readFileSafe(path36.join(nsDir, file));
10202
+ const content = await readFileSafe(path38.join(nsDir, file));
9758
10203
  if (content) contents.push(content);
9759
10204
  }
9760
10205
  }
@@ -9762,7 +10207,7 @@ async function collectClaudemdFiles(repoPath, roleContext) {
9762
10207
  }
9763
10208
  async function autoMigrateHooksIfNeeded() {
9764
10209
  const home = process.env.HOME ?? "";
9765
- const primarySettings = path36.join(home, ".claude", "settings.json");
10210
+ const primarySettings = path38.join(home, ".claude", "settings.json");
9766
10211
  if (!await pathExists(primarySettings)) return;
9767
10212
  const content = await readFileSafe(primarySettings);
9768
10213
  if (!content) return;
@@ -9773,7 +10218,7 @@ async function autoMigrateHooksIfNeeded() {
9773
10218
  const { injectHooksToAllTools: injectHooksToAllTools2 } = await Promise.resolve().then(() => (init_hooks2(), hooks_exports));
9774
10219
  const { localConfig, teamConfig } = await autoDetectInit2();
9775
10220
  const baseDir = resolveBaseDir(localConfig);
9776
- await injectHooksToAllTools2(teamConfig.toolPaths, baseDir);
10221
+ await injectHooksToAllTools2(teamConfig.toolPaths, baseDir, localConfig.enabledAgents);
9777
10222
  log.debug("Hooks migrated to dispatch format");
9778
10223
  }
9779
10224
  async function pull(options) {
@@ -9865,7 +10310,8 @@ async function reconcileHooksAllScopes(userConfig, projectConfig, options) {
9865
10310
  const { reconcileTeamHooksForConfig: reconcileTeamHooksForConfig2 } = await Promise.resolve().then(() => (init_hooks2(), hooks_exports));
9866
10311
  const teamDefs = await reconcileTeamHooksForConfig2(teamConfig, localConfig, {
9867
10312
  auto: true,
9868
- silent: options.silent
10313
+ silent: options.silent,
10314
+ filterAgents: localConfig.enabledAgents
9869
10315
  });
9870
10316
  if (teamDefs.length > 0) {
9871
10317
  log.debug(`[${localConfig.scope}] Reconciled ${teamDefs.length} team hook(s)`);
@@ -9983,17 +10429,17 @@ var init_known_agents = __esm({
9983
10429
  });
9984
10430
 
9985
10431
  // src/agent-skills.ts
9986
- import path37 from "path";
9987
- import YAML12 from "yaml";
10432
+ import path39 from "path";
10433
+ import YAML13 from "yaml";
9988
10434
  async function buildClassifyContext(localConfig) {
9989
10435
  const teamSkills = await collectTeamRepoSkills(localConfig.repo.localPath);
9990
10436
  const sourceSkills = /* @__PURE__ */ new Map();
9991
10437
  try {
9992
- const sourcesDir = path37.join(process.env.HOME ?? "", ".teamai", "sources");
10438
+ const sourcesDir = path39.join(process.env.HOME ?? "", ".teamai", "sources");
9993
10439
  if (await pathExists(sourcesDir)) {
9994
10440
  const sourceNames = await listDirs(sourcesDir);
9995
10441
  for (const sourceName of sourceNames) {
9996
- const manifestPath = path37.join(sourcesDir, sourceName, "installed.json");
10442
+ const manifestPath = path39.join(sourcesDir, sourceName, "installed.json");
9997
10443
  const raw = await readFileSafe(manifestPath);
9998
10444
  if (!raw) continue;
9999
10445
  try {
@@ -10010,13 +10456,13 @@ async function buildClassifyContext(localConfig) {
10010
10456
  return { teamSkills, sourceSkills };
10011
10457
  }
10012
10458
  async function collectTeamRepoSkills(repoPath) {
10013
- const teamSkillsDir = path37.join(repoPath, "skills");
10459
+ const teamSkillsDir = path39.join(repoPath, "skills");
10014
10460
  const result = /* @__PURE__ */ new Map();
10015
10461
  if (!await pathExists(teamSkillsDir)) return result;
10016
10462
  const topDirs = await listDirs(teamSkillsDir);
10017
10463
  for (const dir of topDirs) {
10018
- const dirPath = path37.join(teamSkillsDir, dir);
10019
- const hasSkillMd = await pathExists(path37.join(dirPath, "SKILL.md"));
10464
+ const dirPath = path39.join(teamSkillsDir, dir);
10465
+ const hasSkillMd = await pathExists(path39.join(dirPath, "SKILL.md"));
10020
10466
  if (hasSkillMd) {
10021
10467
  result.set(dir, {});
10022
10468
  } else {
@@ -10063,8 +10509,8 @@ async function scanAgentSkills(agent, ctx) {
10063
10509
  const dirs = await listDirs(agent.absoluteSkillsPath);
10064
10510
  for (const name of dirs) {
10065
10511
  if (name.startsWith(".") || name.endsWith("-workspace")) continue;
10066
- const skillDir = path37.join(agent.absoluteSkillsPath, name);
10067
- const skillMd = path37.join(skillDir, "SKILL.md");
10512
+ const skillDir = path39.join(agent.absoluteSkillsPath, name);
10513
+ const skillMd = path39.join(skillDir, "SKILL.md");
10068
10514
  if (!await pathExists(skillMd)) continue;
10069
10515
  const description = await readSkillDescription(skillMd);
10070
10516
  skills.push({
@@ -10084,7 +10530,7 @@ async function readSkillDescription(skillMdPath) {
10084
10530
  if (!fm) return "";
10085
10531
  let parsed;
10086
10532
  try {
10087
- parsed = YAML12.parse(fm[1]);
10533
+ parsed = YAML13.parse(fm[1]);
10088
10534
  } catch {
10089
10535
  return "";
10090
10536
  }
@@ -10114,8 +10560,8 @@ __export(status_exports, {
10114
10560
  list: () => list,
10115
10561
  status: () => status
10116
10562
  });
10117
- import path38 from "path";
10118
- import YAML13 from "yaml";
10563
+ import path40 from "path";
10564
+ import YAML14 from "yaml";
10119
10565
  async function status(options) {
10120
10566
  const { localConfig, teamConfig } = await autoDetectInit();
10121
10567
  const scopeLabel = localConfig.scope;
@@ -10147,20 +10593,20 @@ async function status(options) {
10147
10593
  log.info("Team resources:");
10148
10594
  const repoPath = localConfig.repo.localPath;
10149
10595
  const counts = {};
10150
- const skillsDirs = await listDirs(path38.join(repoPath, "skills"));
10596
+ const skillsDirs = await listDirs(path40.join(repoPath, "skills"));
10151
10597
  counts.skills = skillsDirs.length;
10152
- const rulesFiles = (await listFiles(path38.join(repoPath, "rules"))).filter((f) => f.endsWith(".md"));
10598
+ const rulesFiles = (await listFiles(path40.join(repoPath, "rules"))).filter((f) => f.endsWith(".md"));
10153
10599
  counts.rules = rulesFiles.length;
10154
- const docsExists = await pathExists(path38.join(repoPath, "docs"));
10155
- const docFiles = docsExists ? (await listFiles(path38.join(repoPath, "docs"))).filter((f) => !f.startsWith(".")) : [];
10600
+ const docsExists = await pathExists(path40.join(repoPath, "docs"));
10601
+ const docFiles = docsExists ? (await listFiles(path40.join(repoPath, "docs"))).filter((f) => !f.startsWith(".")) : [];
10156
10602
  counts.docs = docFiles.length;
10157
- const envYamlPath = path38.join(repoPath, "env", "env.yaml");
10603
+ const envYamlPath = path40.join(repoPath, "env", "env.yaml");
10158
10604
  let envCount = 0;
10159
10605
  if (await pathExists(envYamlPath)) {
10160
10606
  const envContent = await readFileSafe(envYamlPath);
10161
10607
  if (envContent) {
10162
10608
  try {
10163
- const envData = YAML13.parse(envContent);
10609
+ const envData = YAML14.parse(envContent);
10164
10610
  envCount = Array.isArray(envData?.variables) ? envData.variables.length : 0;
10165
10611
  } catch {
10166
10612
  }
@@ -10234,12 +10680,12 @@ async function printRepoSection(t, options, ctx) {
10234
10680
  console.log("");
10235
10681
  console.log(`=== REPO ${t.toUpperCase()} ===`);
10236
10682
  if (t === "env") {
10237
- const envYamlPath = path38.join(repoPath, "env", "env.yaml");
10683
+ const envYamlPath = path40.join(repoPath, "env", "env.yaml");
10238
10684
  if (await pathExists(envYamlPath)) {
10239
10685
  const envContent = await readFileSafe(envYamlPath);
10240
10686
  if (envContent) {
10241
10687
  try {
10242
- const envData = YAML13.parse(envContent);
10688
+ const envData = YAML14.parse(envContent);
10243
10689
  if (envData?.variables && envData.variables.length > 0) {
10244
10690
  for (const v of envData.variables) {
10245
10691
  console.log(` ${v.key}=${v.value}`);
@@ -10356,7 +10802,7 @@ var skill_cmd_exports = {};
10356
10802
  __export(skill_cmd_exports, {
10357
10803
  skillShow: () => skillShow
10358
10804
  });
10359
- import path39 from "path";
10805
+ import path41 from "path";
10360
10806
  async function skillShow(name, options) {
10361
10807
  const { localConfig, teamConfig } = await autoDetectInit();
10362
10808
  const agents = await detectInstalledAgents(localConfig, teamConfig);
@@ -10369,7 +10815,7 @@ async function skillShow(name, options) {
10369
10815
  }
10370
10816
  const ctx = await buildClassifyContext(localConfig);
10371
10817
  const source = classifySkill(name, ctx);
10372
- const description = truncate(await readSkillDescription(path39.join(resolved.primaryPath, "SKILL.md")), DESCRIPTION_MAX);
10818
+ const description = truncate(await readSkillDescription(path41.join(resolved.primaryPath, "SKILL.md")), DESCRIPTION_MAX);
10373
10819
  const contributors = await SkillsHandler.readContributors(resolved.primaryPath);
10374
10820
  const tagsConfig = await loadTagsConfig(localConfig.repo.localPath);
10375
10821
  const tags = tagsConfig?.skills?.[name] ?? [];
@@ -10387,28 +10833,28 @@ async function skillShow(name, options) {
10387
10833
  });
10388
10834
  if (options.verbose) {
10389
10835
  console.log("");
10390
- console.log(` Verbose: SKILL.md path is ${path39.join(resolved.primaryPath, "SKILL.md")}`);
10836
+ console.log(` Verbose: SKILL.md path is ${path41.join(resolved.primaryPath, "SKILL.md")}`);
10391
10837
  }
10392
10838
  }
10393
10839
  async function locateSkill(name, localConfig, agents) {
10394
- const teamSkillsDir = path39.join(localConfig.repo.localPath, "skills");
10395
- const flat = path39.join(teamSkillsDir, name);
10396
- if (await pathExists(path39.join(flat, "SKILL.md"))) {
10840
+ const teamSkillsDir = path41.join(localConfig.repo.localPath, "skills");
10841
+ const flat = path41.join(teamSkillsDir, name);
10842
+ if (await pathExists(path41.join(flat, "SKILL.md"))) {
10397
10843
  return { name, primaryPath: flat, primaryOrigin: "team" };
10398
10844
  }
10399
10845
  if (await pathExists(teamSkillsDir)) {
10400
10846
  const namespaces = await listDirs(teamSkillsDir);
10401
10847
  for (const ns of namespaces) {
10402
- const candidate = path39.join(teamSkillsDir, ns, name);
10403
- if (await pathExists(path39.join(candidate, "SKILL.md"))) {
10848
+ const candidate = path41.join(teamSkillsDir, ns, name);
10849
+ if (await pathExists(path41.join(candidate, "SKILL.md"))) {
10404
10850
  return { name, primaryPath: candidate, primaryOrigin: "team", namespace: ns };
10405
10851
  }
10406
10852
  }
10407
10853
  }
10408
10854
  for (const agent of agents) {
10409
10855
  if (!agent.installed) continue;
10410
- const candidate = path39.join(agent.absoluteSkillsPath, name);
10411
- if (await pathExists(path39.join(candidate, "SKILL.md"))) {
10856
+ const candidate = path41.join(agent.absoluteSkillsPath, name);
10857
+ if (await pathExists(path41.join(candidate, "SKILL.md"))) {
10412
10858
  return { name, primaryPath: candidate, primaryOrigin: "agent" };
10413
10859
  }
10414
10860
  }
@@ -10418,8 +10864,8 @@ async function collectInstalledAgents(name, agents) {
10418
10864
  const matches = [];
10419
10865
  for (const agent of agents) {
10420
10866
  if (!agent.installed) continue;
10421
- const skillDir = path39.join(agent.absoluteSkillsPath, name);
10422
- if (await pathExists(path39.join(skillDir, "SKILL.md"))) {
10867
+ const skillDir = path41.join(agent.absoluteSkillsPath, name);
10868
+ if (await pathExists(path41.join(skillDir, "SKILL.md"))) {
10423
10869
  matches.push({ agent, path: skillDir });
10424
10870
  }
10425
10871
  }
@@ -10473,14 +10919,14 @@ __export(members_exports, {
10473
10919
  getMemberConfig: () => getMemberConfig,
10474
10920
  listMembers: () => listMembers
10475
10921
  });
10476
- import YAML14 from "yaml";
10477
- import path40 from "path";
10922
+ import YAML15 from "yaml";
10923
+ import path42 from "path";
10478
10924
  async function getMemberConfig(repoPath, username) {
10479
- const memberPath = path40.join(repoPath, "members", `${username}.yaml`);
10925
+ const memberPath = path42.join(repoPath, "members", `${username}.yaml`);
10480
10926
  const content = await readFileSafe(memberPath);
10481
10927
  if (!content) return null;
10482
10928
  try {
10483
- const raw = YAML14.parse(content);
10929
+ const raw = YAML15.parse(content);
10484
10930
  return MemberConfigSchema.parse(raw);
10485
10931
  } catch {
10486
10932
  return null;
@@ -10491,7 +10937,7 @@ async function listMembers(options) {
10491
10937
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
10492
10938
  const repoPath = localConfig.repo.localPath;
10493
10939
  await pullRepo(repoPath);
10494
- const membersDir = path40.join(repoPath, "members");
10940
+ const membersDir = path42.join(repoPath, "members");
10495
10941
  const files = await listFiles(membersDir);
10496
10942
  const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
10497
10943
  if (yamlFiles.length === 0) {
@@ -10502,10 +10948,10 @@ async function listMembers(options) {
10502
10948
  console.log(`Team members (${yamlFiles.length}):`);
10503
10949
  console.log("");
10504
10950
  for (const file of yamlFiles) {
10505
- const content = await readFileSafe(path40.join(membersDir, file));
10951
+ const content = await readFileSafe(path42.join(membersDir, file));
10506
10952
  if (!content) continue;
10507
10953
  try {
10508
- const raw = YAML14.parse(content);
10954
+ const raw = YAML15.parse(content);
10509
10955
  const member = MemberConfigSchema.parse(raw);
10510
10956
  const isSelf = member.username === localConfig.username;
10511
10957
  const marker = isSelf ? " (you)" : "";
@@ -10674,13 +11120,13 @@ var doctor_exports = {};
10674
11120
  __export(doctor_exports, {
10675
11121
  doctor: () => doctor
10676
11122
  });
10677
- import path41 from "path";
11123
+ import path43 from "path";
10678
11124
  async function buildHookChecks(toolPaths, baseDir) {
10679
11125
  const checks = [];
10680
11126
  for (const [tool, paths] of Object.entries(toolPaths)) {
10681
11127
  if (!paths.settings) continue;
10682
- const settingsPath = path41.join(baseDir, paths.settings);
10683
- const parentDir = path41.dirname(settingsPath);
11128
+ const settingsPath = path43.join(baseDir, paths.settings);
11129
+ const parentDir = path43.dirname(settingsPath);
10684
11130
  if (!await pathExists(parentDir)) continue;
10685
11131
  checks.push({
10686
11132
  name: `teamai hooks in ${tool} settings`,
@@ -10772,13 +11218,13 @@ async function doctor(options) {
10772
11218
  check: async () => {
10773
11219
  if (teamConfig?.sharing?.env?.injectShellProfile === false) return true;
10774
11220
  if (!localConfig) return true;
10775
- const envYamlPath = path41.join(localConfig.repo.localPath, "env", "env.yaml");
11221
+ const envYamlPath = path43.join(localConfig.repo.localPath, "env", "env.yaml");
10776
11222
  if (!await pathExists(envYamlPath)) return true;
10777
11223
  const home = process.env.HOME ?? "";
10778
- const envShPath = path41.join(home, ".teamai", "env.sh");
11224
+ const envShPath = path43.join(home, ".teamai", "env.sh");
10779
11225
  if (!await pathExists(envShPath)) return false;
10780
11226
  const shell = process.env.SHELL ?? "";
10781
- const profilePath = shell.includes("zsh") ? path41.join(home, ".zshrc") : path41.join(home, ".bashrc");
11227
+ const profilePath = shell.includes("zsh") ? path43.join(home, ".zshrc") : path43.join(home, ".bashrc");
10782
11228
  if (!await pathExists(profilePath)) return false;
10783
11229
  const content = await readFileSafe(profilePath);
10784
11230
  return content?.includes(TEAMAI_ENV_START) ?? false;
@@ -10825,8 +11271,8 @@ __export(roles_cmd_exports, {
10825
11271
  rolesSet: () => rolesSet,
10826
11272
  rolesUpdate: () => rolesUpdate
10827
11273
  });
10828
- import path42 from "path";
10829
- import YAML15 from "yaml";
11274
+ import path44 from "path";
11275
+ import YAML16 from "yaml";
10830
11276
  function parseNamespaces(input) {
10831
11277
  return input.split(",").map((s) => s.trim()).filter(Boolean);
10832
11278
  }
@@ -10870,7 +11316,7 @@ async function rolesInit(options) {
10870
11316
  const { localConfig, teamConfig } = await autoDetectInit();
10871
11317
  const repoPath = localConfig.repo.localPath;
10872
11318
  await pullLatest(repoPath);
10873
- const manifestPath = path42.join(repoPath, "manifest", "roles.yaml");
11319
+ const manifestPath = path44.join(repoPath, "manifest", "roles.yaml");
10874
11320
  if (await pathExists(manifestPath)) {
10875
11321
  log.warn(`Roles manifest already exists at ${manifestPath}`);
10876
11322
  const overwrite = await askConfirmation("Overwrite existing manifest? [y/N] ");
@@ -10926,7 +11372,7 @@ async function rolesInit(options) {
10926
11372
  };
10927
11373
  if (options.dryRun) {
10928
11374
  log.info("[dry-run] Would write manifest:");
10929
- console.log(YAML15.stringify(manifest));
11375
+ console.log(YAML16.stringify(manifest));
10930
11376
  return;
10931
11377
  }
10932
11378
  await saveRolesManifest(repoPath, manifest);
@@ -11213,7 +11659,7 @@ __export(tags_exports, {
11213
11659
  tagsSubscribe: () => tagsSubscribe,
11214
11660
  tagsUnsubscribe: () => tagsUnsubscribe
11215
11661
  });
11216
- import path43 from "path";
11662
+ import path45 from "path";
11217
11663
  async function resolveTagsScope() {
11218
11664
  const projectConfig = await detectProjectConfig();
11219
11665
  return projectConfig ?? (await requireInit()).localConfig;
@@ -11378,7 +11824,7 @@ async function tagsRemove(resourceType, name, tags, options) {
11378
11824
  async function getTeamSkillCount(repoPath) {
11379
11825
  try {
11380
11826
  const { listDirs: listDirs2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
11381
- const skillsDir = path43.join(repoPath, "skills");
11827
+ const skillsDir = path45.join(repoPath, "skills");
11382
11828
  const dirs = await listDirs2(skillsDir);
11383
11829
  return dirs.length;
11384
11830
  } catch {
@@ -11626,24 +12072,24 @@ var uninstall_exports = {};
11626
12072
  __export(uninstall_exports, {
11627
12073
  uninstall: () => uninstall
11628
12074
  });
11629
- import path44 from "path";
12075
+ import path46 from "path";
11630
12076
  function detectShellProfile() {
11631
12077
  const home = process.env.HOME;
11632
12078
  if (!home) return null;
11633
12079
  const shell = process.env.SHELL ?? "";
11634
12080
  if (shell.includes("zsh")) {
11635
- return path44.join(home, ".zshrc");
12081
+ return path46.join(home, ".zshrc");
11636
12082
  }
11637
- return path44.join(home, ".bashrc");
12083
+ return path46.join(home, ".bashrc");
11638
12084
  }
11639
12085
  async function collectTeamSkillNames(repoPath) {
11640
- const teamSkillsDir = path44.join(repoPath, "skills");
12086
+ const teamSkillsDir = path46.join(repoPath, "skills");
11641
12087
  if (!await pathExists(teamSkillsDir)) return /* @__PURE__ */ new Set();
11642
12088
  const names = /* @__PURE__ */ new Set();
11643
12089
  const topDirs = await listDirs(teamSkillsDir);
11644
12090
  for (const dir of topDirs) {
11645
- const dirPath = path44.join(teamSkillsDir, dir);
11646
- const hasSkillMd = await pathExists(path44.join(dirPath, "SKILL.md"));
12091
+ const dirPath = path46.join(teamSkillsDir, dir);
12092
+ const hasSkillMd = await pathExists(path46.join(dirPath, "SKILL.md"));
11647
12093
  if (hasSkillMd) {
11648
12094
  names.add(dir);
11649
12095
  } else {
@@ -11656,7 +12102,7 @@ async function collectTeamSkillNames(repoPath) {
11656
12102
  return names;
11657
12103
  }
11658
12104
  async function collectTeamRuleNames(repoPath) {
11659
- const teamRulesDir = path44.join(repoPath, "rules");
12105
+ const teamRulesDir = path46.join(repoPath, "rules");
11660
12106
  if (!await pathExists(teamRulesDir)) return /* @__PURE__ */ new Set();
11661
12107
  const files = await listFilesRecursive(teamRulesDir);
11662
12108
  return new Set(
@@ -11684,36 +12130,36 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11684
12130
  const teamRuleNames = await collectTeamRuleNames(repoPath);
11685
12131
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
11686
12132
  if (toolPath.settings) {
11687
- const settingsPath = path44.join(baseDir, toolPath.settings);
12133
+ const settingsPath = path46.join(baseDir, toolPath.settings);
11688
12134
  if (await pathExists(settingsPath)) {
11689
12135
  plan.hookFiles.push({ path: settingsPath, tool });
11690
12136
  }
11691
12137
  } else {
11692
- const hooksDir = path44.join(baseDir, `.${tool}`, "hooks");
11693
- if (await pathExists(path44.join(hooksDir, OPENCLAW_HOOK_DIR))) {
12138
+ const hooksDir = path46.join(baseDir, `.${tool}`, "hooks");
12139
+ if (await pathExists(path46.join(hooksDir, OPENCLAW_HOOK_DIR))) {
11694
12140
  plan.openclawHookDirs.push({ hooksDir, tool });
11695
12141
  }
11696
12142
  }
11697
12143
  if (toolPath.claudemd) {
11698
- const claudeMdPath = path44.join(baseDir, toolPath.claudemd);
12144
+ const claudeMdPath = path46.join(baseDir, toolPath.claudemd);
11699
12145
  const content = await readFileSafe(claudeMdPath);
11700
12146
  if (content && CLAUDEMD_MARKER_PAIRS.some(([start]) => content.includes(start))) {
11701
12147
  plan.claudeMdFiles.push(claudeMdPath);
11702
12148
  }
11703
12149
  }
11704
12150
  if (toolPath.skills) {
11705
- const skillsDir = path44.join(baseDir, toolPath.skills);
12151
+ const skillsDir = path46.join(baseDir, toolPath.skills);
11706
12152
  if (await pathExists(skillsDir)) {
11707
12153
  const dirs = await listDirs(skillsDir);
11708
12154
  for (const dir of dirs) {
11709
12155
  if (teamSkillNames.has(dir)) {
11710
- plan.skillDirs.push(path44.join(skillsDir, dir));
12156
+ plan.skillDirs.push(path46.join(skillsDir, dir));
11711
12157
  }
11712
12158
  }
11713
12159
  }
11714
12160
  }
11715
12161
  if (toolPath.rules) {
11716
- const rulesDir = path44.join(baseDir, toolPath.rules);
12162
+ const rulesDir = path46.join(baseDir, toolPath.rules);
11717
12163
  if (await pathExists(rulesDir)) {
11718
12164
  const files = await listFilesRecursive(rulesDir);
11719
12165
  for (const file of files) {
@@ -11721,7 +12167,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11721
12167
  const ruleName = file.replace(/\.md$/, "");
11722
12168
  if (EXCLUDED_RULE_NAMES.has(ruleName)) continue;
11723
12169
  if (teamRuleNames.has(ruleName)) {
11724
- plan.ruleFiles.push(path44.join(rulesDir, file));
12170
+ plan.ruleFiles.push(path46.join(rulesDir, file));
11725
12171
  }
11726
12172
  }
11727
12173
  }
@@ -11737,7 +12183,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11737
12183
  const docsLocalDir = teamConfig.sharing.docs.localDir;
11738
12184
  let docsDir;
11739
12185
  if (localConfig.scope === "project" && localConfig.projectRoot) {
11740
- docsDir = docsLocalDir.startsWith("~/") ? path44.join(localConfig.projectRoot, docsLocalDir.substring(2)) : expandHome(docsLocalDir);
12186
+ docsDir = docsLocalDir.startsWith("~/") ? path46.join(localConfig.projectRoot, docsLocalDir.substring(2)) : expandHome(docsLocalDir);
11741
12187
  } else {
11742
12188
  docsDir = expandHome(docsLocalDir);
11743
12189
  }
@@ -11765,7 +12211,7 @@ function printSummary(plan) {
11765
12211
  if (plan.openclawHookDirs.length > 0) {
11766
12212
  console.log(` OpenClaw Hooks (${plan.openclawHookDirs.length} \u4E2A\u76EE\u5F55):`);
11767
12213
  for (const { hooksDir } of plan.openclawHookDirs) {
11768
- console.log(` ${path44.join(hooksDir, OPENCLAW_HOOK_DIR)}/`);
12214
+ console.log(` ${path46.join(hooksDir, OPENCLAW_HOOK_DIR)}/`);
11769
12215
  }
11770
12216
  console.log("");
11771
12217
  }
@@ -11928,7 +12374,7 @@ async function uninstall(opts) {
11928
12374
  log.error("\u65E0\u6CD5\u786E\u5B9A\u7528\u6237\u4E3B\u76EE\u5F55\uFF08HOME \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E\uFF09");
11929
12375
  return;
11930
12376
  }
11931
- const home = path44.join(homeDir, ".teamai");
12377
+ const home = path46.join(homeDir, ".teamai");
11932
12378
  if (!await pathExists(home)) {
11933
12379
  log.info("\u6CA1\u6709\u9700\u8981\u5378\u8F7D\u7684\u5185\u5BB9");
11934
12380
  return;
@@ -11986,7 +12432,7 @@ __export(env_commands_exports, {
11986
12432
  envList: () => envList,
11987
12433
  envRemove: () => envRemove
11988
12434
  });
11989
- import path45 from "path";
12435
+ import path47 from "path";
11990
12436
  function maskValue(value) {
11991
12437
  if (value.length < 4) return "****";
11992
12438
  return `${value.slice(0, 2)}****`;
@@ -11994,7 +12440,7 @@ function maskValue(value) {
11994
12440
  async function envList(options) {
11995
12441
  const projectConfig = await detectProjectConfig();
11996
12442
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
11997
- const envYamlPath = path45.join(localConfig.repo.localPath, "env", "env.yaml");
12443
+ const envYamlPath = path47.join(localConfig.repo.localPath, "env", "env.yaml");
11998
12444
  if (!await pathExists(envYamlPath)) {
11999
12445
  log.info("No env variables defined (env/env.yaml not found)");
12000
12446
  return;
@@ -12023,7 +12469,7 @@ async function envAdd(key, value, options) {
12023
12469
  const projectConfig = await detectProjectConfig();
12024
12470
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
12025
12471
  const repoPath = localConfig.repo.localPath;
12026
- const envYamlPath = path45.join(repoPath, "env", "env.yaml");
12472
+ const envYamlPath = path47.join(repoPath, "env", "env.yaml");
12027
12473
  const pullSpin = spinner("Pulling latest...").start();
12028
12474
  try {
12029
12475
  await pullRepo(repoPath);
@@ -12050,7 +12496,7 @@ async function envAdd(key, value, options) {
12050
12496
  log.info(`[dry-run] Would ${isUpdate ? "update" : "add"} env variable: ${key}=${value}`);
12051
12497
  return;
12052
12498
  }
12053
- await ensureDir(path45.join(repoPath, "env"));
12499
+ await ensureDir(path47.join(repoPath, "env"));
12054
12500
  await envHandler.writeEnvYaml(envYamlPath, envConfig);
12055
12501
  const action = isUpdate ? "Updated" : "Added";
12056
12502
  log.success(`${action} env variable: ${key}=${value}`);
@@ -12060,7 +12506,7 @@ async function envRemove(key, options) {
12060
12506
  const projectConfig = await detectProjectConfig();
12061
12507
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
12062
12508
  const repoPath = localConfig.repo.localPath;
12063
- const envYamlPath = path45.join(repoPath, "env", "env.yaml");
12509
+ const envYamlPath = path47.join(repoPath, "env", "env.yaml");
12064
12510
  const pullSpin = spinner("Pulling latest...").start();
12065
12511
  try {
12066
12512
  await pullRepo(repoPath);
@@ -12107,7 +12553,7 @@ __export(hooks_cmd_exports, {
12107
12553
  hooksList: () => hooksList,
12108
12554
  hooksRemove: () => hooksRemove
12109
12555
  });
12110
- import path46 from "path";
12556
+ import path48 from "path";
12111
12557
  function resolveHookScopeTargets(localConfig) {
12112
12558
  const baseDir = resolveBaseDir(localConfig) ?? "";
12113
12559
  const manifestPath = getManagedHooksPath(localConfig.scope, localConfig.projectRoot);
@@ -12127,7 +12573,7 @@ function formatDisplayPath(settingsPath) {
12127
12573
  const home = process.env.HOME;
12128
12574
  if (!home) return settingsPath;
12129
12575
  if (settingsPath === home) return "~";
12130
- if (settingsPath.startsWith(home + path46.sep) || settingsPath.startsWith(home + "/")) {
12576
+ if (settingsPath.startsWith(home + path48.sep) || settingsPath.startsWith(home + "/")) {
12131
12577
  return `~${settingsPath.slice(home.length)}`;
12132
12578
  }
12133
12579
  return settingsPath;
@@ -12169,7 +12615,7 @@ async function hooksList(_options) {
12169
12615
  continue;
12170
12616
  }
12171
12617
  for (const baseDir of baseDirs) {
12172
- const settingsPath = path46.join(baseDir, paths.settings);
12618
+ const settingsPath = path48.join(baseDir, paths.settings);
12173
12619
  rows.push({
12174
12620
  tool,
12175
12621
  status: await getHookStatus(settingsPath, tool),
@@ -12965,11 +13411,11 @@ __export(dashboard_exports, {
12965
13411
  });
12966
13412
  import http from "http";
12967
13413
  import fs14 from "fs";
12968
- import path47 from "path";
13414
+ import path49 from "path";
12969
13415
  async function startDashboard(port) {
12970
13416
  const serverPort = port ?? DASHBOARD_DEFAULT_PORT;
12971
- const eventsPath = path47.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
12972
- await ensureDir(path47.dirname(eventsPath));
13417
+ const eventsPath = path49.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
13418
+ await ensureDir(path49.dirname(eventsPath));
12973
13419
  try {
12974
13420
  await fs14.promises.access(eventsPath);
12975
13421
  } catch {
@@ -13107,14 +13553,14 @@ var init_dashboard = __esm({
13107
13553
  });
13108
13554
 
13109
13555
  // src/recall-quality.ts
13110
- import path48 from "path";
13556
+ import path50 from "path";
13111
13557
  import fs15 from "fs";
13112
13558
  function sanitizeSessionId(sessionId) {
13113
13559
  return sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
13114
13560
  }
13115
13561
  function getCachePath(sessionId) {
13116
13562
  const safeName = sanitizeSessionId(sessionId);
13117
- return path48.join(
13563
+ return path50.join(
13118
13564
  process.env.HOME ?? "",
13119
13565
  ".teamai",
13120
13566
  "sessions",
@@ -13145,7 +13591,7 @@ function readCache(sessionId) {
13145
13591
  function writeCache(sessionId, cache) {
13146
13592
  try {
13147
13593
  const cachePath = getCachePath(sessionId);
13148
- const dir = path48.dirname(cachePath);
13594
+ const dir = path50.dirname(cachePath);
13149
13595
  if (!fs15.existsSync(dir)) {
13150
13596
  fs15.mkdirSync(dir, { recursive: true });
13151
13597
  }
@@ -13208,13 +13654,13 @@ __export(contribute_check_exports, {
13208
13654
  writeContributeState: () => writeContributeState
13209
13655
  });
13210
13656
  import fs16 from "fs";
13211
- import path49 from "path";
13657
+ import path51 from "path";
13212
13658
  import { execFileSync } from "child_process";
13213
13659
  function sanitizeSessionId2(sessionId) {
13214
13660
  return sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
13215
13661
  }
13216
13662
  function getSessionPath(sessionId) {
13217
- return path49.join(
13663
+ return path51.join(
13218
13664
  process.env.HOME ?? "",
13219
13665
  ".teamai",
13220
13666
  "sessions",
@@ -13250,9 +13696,9 @@ async function readContributeState(sessionId) {
13250
13696
  async function writeContributeState(sessionId, state) {
13251
13697
  try {
13252
13698
  const filePath = getSessionPath(sessionId);
13253
- await ensureDir(path49.dirname(filePath));
13699
+ await ensureDir(path51.dirname(filePath));
13254
13700
  await writeJson(filePath, state);
13255
- await cleanupStaleSessions(path49.dirname(filePath), sessionId);
13701
+ await cleanupStaleSessions(path51.dirname(filePath), sessionId);
13256
13702
  } catch (e) {
13257
13703
  log.error(`Failed to write contribute state: ${e.message}`);
13258
13704
  }
@@ -13265,7 +13711,7 @@ async function cleanupStaleSessions(dir, currentSessionId) {
13265
13711
  if (!entry.endsWith(".json")) continue;
13266
13712
  const name = entry.replace(".json", "");
13267
13713
  if (name === currentBasename) continue;
13268
- const filePath = path49.join(dir, entry);
13714
+ const filePath = path51.join(dir, entry);
13269
13715
  try {
13270
13716
  const stat6 = await fs16.promises.stat(filePath);
13271
13717
  if (now - stat6.mtimeMs > STALE_SESSION_MS) {
@@ -13506,21 +13952,21 @@ __export(contribute_exports, {
13506
13952
  contribute: () => contribute
13507
13953
  });
13508
13954
  import fs17 from "fs";
13509
- import path50 from "path";
13955
+ import path52 from "path";
13510
13956
  import fse8 from "fs-extra";
13511
13957
  async function rebuildIndexAfterContribute(localConfig) {
13512
13958
  const repoPath = localConfig.repo.localPath;
13513
- const learningsRepoDir = path50.join(repoPath, "learnings");
13514
- const docsRepoDir = path50.join(repoPath, "docs");
13515
- const rulesRepoDir = path50.join(repoPath, "rules");
13516
- const skillsRepoDir = path50.join(repoPath, "skills");
13517
- const votesDir = path50.join(repoPath, "votes");
13959
+ const learningsRepoDir = path52.join(repoPath, "learnings");
13960
+ const docsRepoDir = path52.join(repoPath, "docs");
13961
+ const rulesRepoDir = path52.join(repoPath, "rules");
13962
+ const skillsRepoDir = path52.join(repoPath, "skills");
13963
+ const votesDir = path52.join(repoPath, "votes");
13518
13964
  let effectiveLearningsDir;
13519
13965
  if (localConfig.scope === "user") {
13520
13966
  if (await pathExists(learningsRepoDir)) {
13521
13967
  await fse8.copy(learningsRepoDir, LEARNINGS_LOCAL_DIR, {
13522
13968
  overwrite: true,
13523
- filter: (src) => !path50.basename(src).startsWith(".")
13969
+ filter: (src) => !path52.basename(src).startsWith(".")
13524
13970
  });
13525
13971
  }
13526
13972
  effectiveLearningsDir = await pathExists(LEARNINGS_LOCAL_DIR) ? LEARNINGS_LOCAL_DIR : void 0;
@@ -13528,7 +13974,7 @@ async function rebuildIndexAfterContribute(localConfig) {
13528
13974
  effectiveLearningsDir = await pathExists(learningsRepoDir) ? learningsRepoDir : void 0;
13529
13975
  }
13530
13976
  const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
13531
- const indexPath = path50.join(teamaiHome, "search-index.json");
13977
+ const indexPath = path52.join(teamaiHome, "search-index.json");
13532
13978
  const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
13533
13979
  await buildIndex2({
13534
13980
  learningsDir: effectiveLearningsDir,
@@ -13587,9 +14033,9 @@ async function contribute(options) {
13587
14033
  const pushSpin = spinner("Contributing session knowledge...").start();
13588
14034
  const filename = generateFilename(options.title);
13589
14035
  try {
13590
- const aiDocsDir = path50.join(repoPath, "learnings");
14036
+ const aiDocsDir = path52.join(repoPath, "learnings");
13591
14037
  await ensureDir(aiDocsDir);
13592
- const destPath = path50.join(aiDocsDir, filename);
14038
+ const destPath = path52.join(aiDocsDir, filename);
13593
14039
  await fs17.promises.writeFile(destPath, content, "utf-8");
13594
14040
  try {
13595
14041
  await pullRepo(repoPath);
@@ -13644,7 +14090,7 @@ var init_contribute = __esm({
13644
14090
  });
13645
14091
 
13646
14092
  // src/wiki-engine/core/wiki-protocol.ts
13647
- import path51 from "path";
14093
+ import path53 from "path";
13648
14094
  function safeIgnore(filePath) {
13649
14095
  const normalized = toPosix(filePath);
13650
14096
  const parts = normalized.split("/").filter(Boolean);
@@ -13658,7 +14104,7 @@ function safeIgnore(filePath) {
13658
14104
  return /\.(pem|key|p12|pfx)$/i.test(base);
13659
14105
  }
13660
14106
  function toPosix(value) {
13661
- return value.split(path51.sep).join("/");
14107
+ return value.split(path53.sep).join("/");
13662
14108
  }
13663
14109
  var CONFIDENCE_SCORE_DEFAULTS, SAFE_IGNORE_SEGMENTS, SENSITIVE_FILE_NAMES;
13664
14110
  var init_wiki_protocol = __esm({
@@ -13705,7 +14151,7 @@ __export(graph_index_schema_exports, {
13705
14151
  validateGraph: () => validateGraph
13706
14152
  });
13707
14153
  import { readFile, writeFile as writeFile4, mkdir } from "fs/promises";
13708
- import path52 from "path";
14154
+ import path54 from "path";
13709
14155
  function toPageSlug(relativePath) {
13710
14156
  return relativePath.replace(/\.md$/u, "").replace(/\\/g, "/");
13711
14157
  }
@@ -13876,7 +14322,7 @@ function computeGraphHealth(graph) {
13876
14322
  };
13877
14323
  }
13878
14324
  async function loadGraphIndex(wikiRoot) {
13879
- const graphPath = path52.join(wikiRoot, ".indices", "graph-index.json");
14325
+ const graphPath = path54.join(wikiRoot, ".indices", "graph-index.json");
13880
14326
  try {
13881
14327
  const raw = await readFile(graphPath, "utf8");
13882
14328
  const parsed = JSON.parse(raw);
@@ -13889,9 +14335,9 @@ async function loadGraphIndex(wikiRoot) {
13889
14335
  }
13890
14336
  }
13891
14337
  async function saveGraphIndex(wikiRoot, graph) {
13892
- const dir = path52.join(wikiRoot, ".indices");
14338
+ const dir = path54.join(wikiRoot, ".indices");
13893
14339
  await mkdir(dir, { recursive: true });
13894
- const outPath = path52.join(dir, "graph-index.json");
14340
+ const outPath = path54.join(dir, "graph-index.json");
13895
14341
  await writeFile4(outPath, JSON.stringify(graph, null, 2), "utf8");
13896
14342
  return outPath;
13897
14343
  }
@@ -13944,7 +14390,7 @@ var init_graph_index_schema = __esm({
13944
14390
 
13945
14391
  // src/code-knowledge-recall.ts
13946
14392
  import { readFile as readFile2, readdir } from "fs/promises";
13947
- import path53 from "path";
14393
+ import path55 from "path";
13948
14394
  function countOccurrences(text, token) {
13949
14395
  let count = 0;
13950
14396
  let idx = 0;
@@ -14078,7 +14524,7 @@ function extractSnippet(content, queryTokens, maxLen = 300) {
14078
14524
  async function loadWikiPages(wikiRoot, depth) {
14079
14525
  const pages = [];
14080
14526
  if (depth === "route") {
14081
- const routerPath = path53.join(wikiRoot, "router.md");
14527
+ const routerPath = path55.join(wikiRoot, "router.md");
14082
14528
  try {
14083
14529
  const content = await readFile2(routerPath, "utf-8");
14084
14530
  const titleMatch = content.match(/^title:\s*(.+)$/m);
@@ -14094,7 +14540,7 @@ async function loadWikiPages(wikiRoot, depth) {
14094
14540
  }
14095
14541
  return pages;
14096
14542
  }
14097
- const evidenceDir = path53.join(wikiRoot, "evidence", "code");
14543
+ const evidenceDir = path55.join(wikiRoot, "evidence", "code");
14098
14544
  let projectDirs;
14099
14545
  try {
14100
14546
  const entries = await readdir(evidenceDir, { withFileTypes: true });
@@ -14103,7 +14549,7 @@ async function loadWikiPages(wikiRoot, depth) {
14103
14549
  return pages;
14104
14550
  }
14105
14551
  for (const project of projectDirs) {
14106
- const projectDir = path53.join(evidenceDir, project);
14552
+ const projectDir = path55.join(evidenceDir, project);
14107
14553
  await loadPagesRecursive(projectDir, `evidence/code/${project}`, pages, depth);
14108
14554
  }
14109
14555
  return pages;
@@ -14112,7 +14558,7 @@ async function loadPagesRecursive(dir, relativePath, pages, depth, currentDepth
14112
14558
  if (currentDepth >= MAX_RECURSION_DEPTH) return;
14113
14559
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
14114
14560
  for (const entry of entries) {
14115
- const fullPath = path53.join(dir, entry.name);
14561
+ const fullPath = path55.join(dir, entry.name);
14116
14562
  if (entry.isDirectory()) {
14117
14563
  await loadPagesRecursive(
14118
14564
  fullPath,
@@ -14234,8 +14680,7 @@ __export(recall_exports, {
14234
14680
  formatResults: () => formatResults,
14235
14681
  recall: () => recall
14236
14682
  });
14237
- import path54 from "path";
14238
- import YAML16 from "yaml";
14683
+ import path56 from "path";
14239
14684
  function getVotesLocalDir() {
14240
14685
  return `${process.env.HOME ?? ""}/.teamai/votes`;
14241
14686
  }
@@ -14265,49 +14710,25 @@ function formatResults(results) {
14265
14710
  lines.push("\u4EE5\u4E0A\u5185\u5BB9\u6765\u81EA\u56E2\u961F\u77E5\u8BC6\u5E93\uFF0C\u4EC5\u4F9B\u53C2\u8003\u3002\u5982\u9700\u8BE6\u7EC6\u4FE1\u606F\uFF0C\u8BF7\u7528 Read \u5DE5\u5177\u8BFB\u53D6\u5BF9\u5E94\u6587\u4EF6\u3002");
14266
14711
  return lines.join("\n");
14267
14712
  }
14268
- async function autoUpvote(results, username, repoPath) {
14713
+ async function autoUpvote(results, username, _repoPath) {
14269
14714
  if (results.length === 0) return;
14270
14715
  try {
14716
+ const { incrementRecalled: incrementRecalled2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
14271
14717
  const votesDir = getVotesLocalDir();
14272
- const localVotePath = path54.join(votesDir, `${username}.yaml`);
14718
+ const localVotePath = path56.join(votesDir, `${username}.yaml`);
14273
14719
  await ensureDir(votesDir);
14274
- let userVotes = { votes: {} };
14275
- const existingContent = await readFileSafe(localVotePath);
14276
- if (existingContent) {
14277
- try {
14278
- const parsed = YAML16.parse(existingContent);
14279
- if (parsed?.votes) {
14280
- userVotes = parsed;
14281
- }
14282
- } catch {
14283
- log.debug("Corrupt local votes file, resetting");
14284
- }
14285
- }
14286
- const now = (/* @__PURE__ */ new Date()).toISOString();
14287
- let newVotes = 0;
14288
- for (const result of results) {
14289
- const docId = result.entry.filename.replace(/\.md$/i, "");
14290
- if (!userVotes.votes[docId]) {
14291
- userVotes.votes[docId] = { at: now };
14292
- newVotes++;
14293
- }
14294
- }
14295
- if (newVotes === 0) {
14296
- log.debug("autoUpvote: all docs already voted, skipping write");
14297
- return;
14298
- }
14299
- const yamlContent = YAML16.stringify(userVotes);
14300
- await writeFile(localVotePath, yamlContent);
14301
- log.debug(`autoUpvote: recorded ${newVotes} new vote(s) for ${username}`);
14720
+ const docIds = results.map((r) => r.entry.filename.replace(/\.md$/i, ""));
14721
+ await incrementRecalled2(localVotePath, docIds);
14722
+ log.debug(`autoUpvote: incremented recalled_count for ${docIds.length} doc(s)`);
14302
14723
  } catch (e) {
14303
14724
  log.error(`autoUpvote failed: ${e.message}`);
14304
14725
  }
14305
14726
  }
14306
14727
  async function loadOrBuildScopeIndex(localConfig, scopeLabel) {
14307
14728
  const teamaiHome = localConfig.scope === "project" && localConfig.projectRoot ? getTeamaiHome("project", localConfig.projectRoot) : getTeamaiHome("user");
14308
- const indexPath = path54.join(teamaiHome, "search-index.json");
14309
- const localLearningsDir = path54.join(teamaiHome, "learnings");
14310
- const repoLearningsDir = path54.join(localConfig.repo.localPath, "learnings");
14729
+ const indexPath = path56.join(teamaiHome, "search-index.json");
14730
+ const localLearningsDir = path56.join(teamaiHome, "learnings");
14731
+ const repoLearningsDir = path56.join(localConfig.repo.localPath, "learnings");
14311
14732
  let effectiveLearningsDir = null;
14312
14733
  if (scopeLabel === "user" && await pathExists(localLearningsDir)) {
14313
14734
  effectiveLearningsDir = localLearningsDir;
@@ -14316,13 +14737,13 @@ async function loadOrBuildScopeIndex(localConfig, scopeLabel) {
14316
14737
  }
14317
14738
  let index = await loadIndex(indexPath);
14318
14739
  const needsRebuild = !index || isLegacyIndex(index);
14319
- if (needsRebuild && (effectiveLearningsDir || await pathExists(path54.join(localConfig.repo.localPath, "docs")) || await pathExists(path54.join(localConfig.repo.localPath, "rules")) || await pathExists(path54.join(localConfig.repo.localPath, "skills")))) {
14320
- const votesDir = path54.join(localConfig.repo.localPath, "votes");
14740
+ if (needsRebuild && (effectiveLearningsDir || await pathExists(path56.join(localConfig.repo.localPath, "docs")) || await pathExists(path56.join(localConfig.repo.localPath, "rules")) || await pathExists(path56.join(localConfig.repo.localPath, "skills")))) {
14741
+ const votesDir = path56.join(localConfig.repo.localPath, "votes");
14321
14742
  const votesExist = await pathExists(votesDir);
14322
- const docsDir = path54.join(localConfig.repo.localPath, "docs");
14323
- const rulesDir = path54.join(localConfig.repo.localPath, "rules");
14324
- const skillsDir = path54.join(localConfig.repo.localPath, "skills");
14325
- const repoCodebaseDir = path54.join(localConfig.repo.localPath, "docs", "team-codebase");
14743
+ const docsDir = path56.join(localConfig.repo.localPath, "docs");
14744
+ const rulesDir = path56.join(localConfig.repo.localPath, "rules");
14745
+ const skillsDir = path56.join(localConfig.repo.localPath, "skills");
14746
+ const repoCodebaseDir = path56.join(localConfig.repo.localPath, "docs", "team-codebase");
14326
14747
  const codebaseDir = await pathExists(repoCodebaseDir) ? repoCodebaseDir : void 0;
14327
14748
  try {
14328
14749
  await buildIndex({
@@ -14382,7 +14803,7 @@ async function recall(query, options) {
14382
14803
  }
14383
14804
  }
14384
14805
  const wikiConfig = scopeIndexes[0]?.config;
14385
- const wikiRoot = wikiConfig ? path54.join(wikiConfig.repo.localPath, "teamwiki") : path54.join(process.cwd(), ".teamai", "team-repo", "teamwiki");
14806
+ const wikiRoot = wikiConfig ? path56.join(wikiConfig.repo.localPath, "teamwiki") : path56.join(process.cwd(), ".teamai", "team-repo", "teamwiki");
14386
14807
  const hasWiki = await pathExists(wikiRoot);
14387
14808
  if (scopeIndexes.length === 0 && !hasWiki) {
14388
14809
  log.info("No learnings available. Run `teamai pull` first to sync team knowledge.");
@@ -14413,7 +14834,7 @@ async function recall(query, options) {
14413
14834
  votes: 0,
14414
14835
  type: "docs",
14415
14836
  domain: "technical",
14416
- path: path54.join(wikiRoot, cr.page),
14837
+ path: path56.join(wikiRoot, cr.page),
14417
14838
  snippet: cr.snippet
14418
14839
  },
14419
14840
  score: Math.min(10, Math.log2(cr.score + 1) * 2),
@@ -14472,19 +14893,19 @@ __export(recall_toggle_exports, {
14472
14893
  recallEnable: () => recallEnable,
14473
14894
  recallStatus: () => recallStatus
14474
14895
  });
14475
- import path55 from "path";
14896
+ import path57 from "path";
14476
14897
  async function removeRecallArtifacts(teamConfig, localConfig) {
14477
14898
  const baseDir = resolveBaseDir(localConfig);
14478
14899
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
14479
14900
  if (toolPath.rules) {
14480
- const ruleFile = path55.join(baseDir, toolPath.rules, "teamai-recall.md");
14901
+ const ruleFile = path57.join(baseDir, toolPath.rules, "teamai-recall.md");
14481
14902
  if (await pathExists(ruleFile)) {
14482
14903
  await remove(ruleFile);
14483
14904
  log.debug(`Removed recall rule from ${tool}`);
14484
14905
  }
14485
14906
  }
14486
14907
  if (toolPath.agents) {
14487
- const agentFile = path55.join(baseDir, toolPath.agents, "teamai-recall.md");
14908
+ const agentFile = path57.join(baseDir, toolPath.agents, "teamai-recall.md");
14488
14909
  if (await pathExists(agentFile)) {
14489
14910
  await remove(agentFile);
14490
14911
  log.debug(`Removed recall agent from ${tool}`);
@@ -14492,7 +14913,7 @@ async function removeRecallArtifacts(teamConfig, localConfig) {
14492
14913
  }
14493
14914
  if (toolPath.skills) {
14494
14915
  for (const skillName of RECALL_DEPENDENT_SKILLS) {
14495
- const skillDir = path55.join(baseDir, toolPath.skills, skillName);
14916
+ const skillDir = path57.join(baseDir, toolPath.skills, skillName);
14496
14917
  if (await pathExists(skillDir)) {
14497
14918
  await remove(skillDir);
14498
14919
  log.debug(`Removed recall skill ${skillName} from ${tool}`);
@@ -14500,7 +14921,7 @@ async function removeRecallArtifacts(teamConfig, localConfig) {
14500
14921
  }
14501
14922
  }
14502
14923
  if (toolPath.claudemd) {
14503
- const claudeMdPath = path55.join(baseDir, toolPath.claudemd);
14924
+ const claudeMdPath = path57.join(baseDir, toolPath.claudemd);
14504
14925
  const content = await readFileSafe(claudeMdPath);
14505
14926
  if (content && content.includes(TEAMAI_RECALL_RULES_START)) {
14506
14927
  const startIdx = content.indexOf(TEAMAI_RECALL_RULES_START);
@@ -14534,7 +14955,7 @@ async function deployRecallArtifacts(teamConfig, localConfig) {
14534
14955
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
14535
14956
  if (!toolPath.claudemd || !toolPath.agents) continue;
14536
14957
  if (!await ResourceHandler.isToolInstalled(toolPath.agents, baseDir)) continue;
14537
- const claudeMdPath = path55.join(baseDir, toolPath.claudemd);
14958
+ const claudeMdPath = path57.join(baseDir, toolPath.claudemd);
14538
14959
  try {
14539
14960
  await injectClaudeMdSection2(
14540
14961
  claudeMdPath,
@@ -14594,10 +15015,10 @@ __export(todowrite_hint_exports, {
14594
15015
  shouldSkipTodoWriteHint: () => shouldSkipTodoWriteHint,
14595
15016
  todoWriteHint: () => todoWriteHint
14596
15017
  });
14597
- import path56 from "path";
15018
+ import path58 from "path";
14598
15019
  import fs18 from "fs";
14599
15020
  function getTodoWriteHintCachePath(sessionId) {
14600
- return path56.join(
15021
+ return path58.join(
14601
15022
  process.env.HOME ?? "",
14602
15023
  ".teamai",
14603
15024
  "sessions",
@@ -14620,7 +15041,7 @@ function readCache2(sessionId) {
14620
15041
  function writeCache2(sessionId, cache) {
14621
15042
  try {
14622
15043
  const cachePath = getTodoWriteHintCachePath(sessionId);
14623
- const dir = path56.dirname(cachePath);
15044
+ const dir = path58.dirname(cachePath);
14624
15045
  if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
14625
15046
  fs18.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
14626
15047
  } catch {
@@ -14691,20 +15112,20 @@ var init_todowrite_hint = __esm({
14691
15112
  });
14692
15113
 
14693
15114
  // src/utils/cache-index.ts
14694
- import path57 from "path";
15115
+ import path59 from "path";
14695
15116
  import os3 from "os";
14696
15117
  import fs19 from "fs-extra";
14697
15118
  function getCacheRoot() {
14698
- return process.env.TEAMAI_CACHE_DIR ?? path57.join(os3.homedir(), ".teamai", "cache", "repos");
15119
+ return process.env.TEAMAI_CACHE_DIR ?? path59.join(os3.homedir(), ".teamai", "cache", "repos");
14699
15120
  }
14700
15121
  function buildKey(provider, owner, repo) {
14701
15122
  return `${provider}/${owner}/${repo}`;
14702
15123
  }
14703
15124
  function keyToAbsPath(key) {
14704
- return path57.join(getCacheRoot(), key);
15125
+ return path59.join(getCacheRoot(), key);
14705
15126
  }
14706
15127
  async function loadCacheIndex() {
14707
- const indexPath = path57.join(getCacheRoot(), INDEX_FILENAME);
15128
+ const indexPath = path59.join(getCacheRoot(), INDEX_FILENAME);
14708
15129
  try {
14709
15130
  const stat6 = await fs19.stat(indexPath);
14710
15131
  if (stat6.size > MAX_CONFIG_FILE_BYTES) {
@@ -14727,7 +15148,7 @@ async function loadCacheIndex() {
14727
15148
  async function saveCacheIndex(idx) {
14728
15149
  const root = getCacheRoot();
14729
15150
  await fs19.ensureDir(root);
14730
- const indexPath = path57.join(root, INDEX_FILENAME);
15151
+ const indexPath = path59.join(root, INDEX_FILENAME);
14731
15152
  const updated = { ...idx, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
14732
15153
  await fs19.writeFile(indexPath, JSON.stringify(updated, null, 2), "utf8");
14733
15154
  }
@@ -14760,7 +15181,7 @@ async function statDirSize(absPath) {
14760
15181
  return 0;
14761
15182
  }
14762
15183
  for (const entry of entries) {
14763
- const childPath = path57.join(absPath, entry.name);
15184
+ const childPath = path59.join(absPath, entry.name);
14764
15185
  if (entry.isSymbolicLink()) {
14765
15186
  continue;
14766
15187
  }
@@ -15073,6 +15494,12 @@ var init_cache_cmd = __esm({
15073
15494
  });
15074
15495
 
15075
15496
  // src/utils/ai-client.ts
15497
+ var ai_client_exports = {};
15498
+ __export(ai_client_exports, {
15499
+ callClaude: () => callClaude,
15500
+ callClaudeParallel: () => callClaudeParallel,
15501
+ getAICliName: () => getAICliName
15502
+ });
15076
15503
  import { spawn, execFileSync as execFileSync2 } from "child_process";
15077
15504
  import { existsSync } from "fs";
15078
15505
  function detectClaudeCli() {
@@ -15242,7 +15669,7 @@ var init_ai_client = __esm({
15242
15669
 
15243
15670
  // src/import-local.ts
15244
15671
  import fs20 from "fs";
15245
- import path58 from "path";
15672
+ import path60 from "path";
15246
15673
  import readline3 from "readline";
15247
15674
  function toSlug(title) {
15248
15675
  return title.toLowerCase().replace(/[^a-z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
@@ -15278,7 +15705,7 @@ function parseClassifyOutput(sourcePath, rawContent, output) {
15278
15705
  sourcePath,
15279
15706
  rawContent,
15280
15707
  type: knownType,
15281
- title: typeof parsed.title === "string" ? parsed.title : path58.basename(sourcePath),
15708
+ title: typeof parsed.title === "string" ? parsed.title : path60.basename(sourcePath),
15282
15709
  summary: typeof parsed.summary === "string" ? parsed.summary : "",
15283
15710
  tags: Array.isArray(parsed.tags) ? parsed.tags.filter((t) => typeof t === "string") : [],
15284
15711
  confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0,
@@ -15290,7 +15717,7 @@ function parseClassifyOutput(sourcePath, rawContent, output) {
15290
15717
  sourcePath,
15291
15718
  rawContent,
15292
15719
  type: "learning",
15293
- title: path58.basename(sourcePath),
15720
+ title: path60.basename(sourcePath),
15294
15721
  summary: "",
15295
15722
  tags: [],
15296
15723
  confidence: 0,
@@ -15336,9 +15763,9 @@ async function scanCandidates(opts) {
15336
15763
  const relPaths = await listFilesRecursive(expandedDir);
15337
15764
  for (const relPath of relPaths) {
15338
15765
  if (relPath.split("/").some((seg) => seg.startsWith("."))) continue;
15339
- const ext = path58.extname(relPath).toLowerCase();
15766
+ const ext = path60.extname(relPath).toLowerCase();
15340
15767
  if (ext !== ".md" && ext !== ".txt") continue;
15341
- const absPath = path58.join(expandedDir, relPath);
15768
+ const absPath = path60.join(expandedDir, relPath);
15342
15769
  try {
15343
15770
  const stat6 = fs20.statSync(absPath);
15344
15771
  if (stat6.size > MAX_FILE_SIZE_BYTES) continue;
@@ -15360,8 +15787,8 @@ async function scanCandidates(opts) {
15360
15787
  if (!fs20.existsSync(baseDir)) continue;
15361
15788
  const relPaths = await listFilesRecursive(baseDir);
15362
15789
  for (const relPath of relPaths) {
15363
- if (path58.extname(relPath).toLowerCase() !== ".md") continue;
15364
- const absPath = path58.join(baseDir, relPath);
15790
+ if (path60.extname(relPath).toLowerCase() !== ".md") continue;
15791
+ const absPath = path60.join(baseDir, relPath);
15365
15792
  try {
15366
15793
  const stat6 = fs20.statSync(absPath);
15367
15794
  if (stat6.size > MAX_FILE_SIZE_BYTES) continue;
@@ -15392,7 +15819,7 @@ async function classifyWithAI(candidates) {
15392
15819
  sourcePath: c.path,
15393
15820
  rawContent: c.rawContent,
15394
15821
  type: "learning",
15395
- title: path58.basename(c.path),
15822
+ title: path60.basename(c.path),
15396
15823
  summary: "",
15397
15824
  tags: [],
15398
15825
  confidence: 0,
@@ -15472,7 +15899,7 @@ async function interactiveReview(items, opts) {
15472
15899
  for (const sessionItem of pendingItems) {
15473
15900
  const currentIndex = session.items.indexOf(sessionItem) + 1;
15474
15901
  const classified = classifiedMap.get(sessionItem.sourcePath ?? "");
15475
- const title = sessionItem.learningDraft?.title ?? classified?.title ?? path58.basename(sessionItem.sourcePath ?? "");
15902
+ const title = sessionItem.learningDraft?.title ?? classified?.title ?? path60.basename(sessionItem.sourcePath ?? "");
15476
15903
  const itemType = classified?.type ?? "learning";
15477
15904
  const summary = classified?.summary ?? "";
15478
15905
  const tags = classified?.tags ?? [];
@@ -15539,9 +15966,9 @@ async function pushAccepted(session, repoPath, opts) {
15539
15966
  } else {
15540
15967
  const typeInContent = detectTypeFromContent(draft.content);
15541
15968
  const subDir = typeInContent === "rule" ? "rules" : typeInContent === "doc" ? "docs" : "learnings";
15542
- destDir = path58.join(expandHome(repoPath), subDir);
15969
+ destDir = path60.join(expandHome(repoPath), subDir);
15543
15970
  }
15544
- const destPath = path58.join(destDir, filename);
15971
+ const destPath = path60.join(destDir, filename);
15545
15972
  if (opts.dryRun) {
15546
15973
  log.info(`[dry-run] would write: ${destPath}`);
15547
15974
  pushed++;
@@ -15806,7 +16233,7 @@ var init_iwiki_client = __esm({
15806
16233
  });
15807
16234
 
15808
16235
  // src/import-iwiki.ts
15809
- import path59 from "path";
16236
+ import path61 from "path";
15810
16237
  import { readFile as readFile3, writeFile as writeFile5 } from "fs/promises";
15811
16238
  function parseIWikiInput(input) {
15812
16239
  const trimmed = input.trim();
@@ -15895,8 +16322,8 @@ async function importFromIWiki(opts) {
15895
16322
  dryRun: opts.dryRun,
15896
16323
  outputDir: opts.outputDir
15897
16324
  });
15898
- const teamwikiRoot = path59.join(repoPath, "teamwiki");
15899
- if (await pathExists(path59.join(teamwikiRoot, ".indices", "graph-index.json"))) {
16325
+ const teamwikiRoot = path61.join(repoPath, "teamwiki");
16326
+ if (await pathExists(path61.join(teamwikiRoot, ".indices", "graph-index.json"))) {
15900
16327
  try {
15901
16328
  const mapsToEdges = await reconcileIwikiWithCodebase(documents, teamwikiRoot);
15902
16329
  if (mapsToEdges.length > 0) {
@@ -15915,7 +16342,7 @@ async function importFromIWiki(opts) {
15915
16342
  log.success("iWiki import complete");
15916
16343
  }
15917
16344
  async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
15918
- const graphPath = path59.join(teamwikiRoot, ".indices", "graph-index.json");
16345
+ const graphPath = path61.join(teamwikiRoot, ".indices", "graph-index.json");
15919
16346
  const graphRaw = await readFile3(graphPath, "utf-8");
15920
16347
  const graph = JSON.parse(graphRaw);
15921
16348
  const codeLabels = /* @__PURE__ */ new Map();
@@ -15924,17 +16351,17 @@ async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
15924
16351
  const words = node.label.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
15925
16352
  codeLabels.set(words, node.id);
15926
16353
  }
15927
- const evidenceDir = path59.join(teamwikiRoot, "evidence", "code");
16354
+ const evidenceDir = path61.join(teamwikiRoot, "evidence", "code");
15928
16355
  const codePageContents = /* @__PURE__ */ new Map();
15929
16356
  if (await pathExists(evidenceDir)) {
15930
16357
  const { readdir: readdir9 } = await import("fs/promises");
15931
16358
  const projects = await readdir9(evidenceDir);
15932
16359
  for (const project of projects) {
15933
- const projectDir = path59.join(evidenceDir, project);
16360
+ const projectDir = path61.join(evidenceDir, project);
15934
16361
  const files = await readdir9(projectDir).catch(() => []);
15935
16362
  for (const file of files) {
15936
16363
  if (!file.endsWith(".md")) continue;
15937
- const content = await readFile3(path59.join(projectDir, file), "utf-8").catch(() => "");
16364
+ const content = await readFile3(path61.join(projectDir, file), "utf-8").catch(() => "");
15938
16365
  codePageContents.set(`evidence/code/${project}/${file}`, content);
15939
16366
  }
15940
16367
  }
@@ -16023,7 +16450,7 @@ function parseGitHubPRUrl(url) {
16023
16450
  }
16024
16451
  return { owner: match[1], repo: match[2], number: match[3] };
16025
16452
  }
16026
- async function githubApiGet(path92) {
16453
+ async function githubApiGet(path99) {
16027
16454
  return new Promise((resolve, reject) => {
16028
16455
  const token = process.env["GITHUB_TOKEN"];
16029
16456
  const headers = {
@@ -16032,7 +16459,7 @@ async function githubApiGet(path92) {
16032
16459
  };
16033
16460
  if (token) headers["Authorization"] = `Bearer ${token}`;
16034
16461
  const req = https2.request(
16035
- { hostname: "api.github.com", path: path92, headers },
16462
+ { hostname: "api.github.com", path: path99, headers },
16036
16463
  (res) => {
16037
16464
  const chunks = [];
16038
16465
  res.on("data", (c) => chunks.push(c));
@@ -16228,8 +16655,8 @@ var init_mr_fetch2 = __esm({
16228
16655
 
16229
16656
  // src/utils/dedup.ts
16230
16657
  import fs21 from "fs/promises";
16231
- import path60 from "path";
16232
- import matter4 from "gray-matter";
16658
+ import path62 from "path";
16659
+ import matter5 from "gray-matter";
16233
16660
  function extractKeywords(text) {
16234
16661
  const keywords = /* @__PURE__ */ new Set();
16235
16662
  const enWords = text.match(/[a-zA-Z]+/g) ?? [];
@@ -16286,14 +16713,14 @@ async function findSupersededLearnings(draftKeywords, learningsDir, withinDays =
16286
16713
  const cutoffDate = new Date(Date.now() - withinDays * 24 * 60 * 60 * 1e3);
16287
16714
  const results = [];
16288
16715
  for (const filename of mdFiles) {
16289
- const filePath = path60.join(learningsDir, filename);
16716
+ const filePath = path62.join(learningsDir, filename);
16290
16717
  try {
16291
16718
  const docDate = await resolveDocDate(filePath, filename);
16292
16719
  if (docDate < cutoffDate) {
16293
16720
  continue;
16294
16721
  }
16295
16722
  const raw = await fs21.readFile(filePath, "utf8");
16296
- const { content: body } = matter4(raw);
16723
+ const { content: body } = matter5(raw);
16297
16724
  const fileKeywords = extractKeywords(body);
16298
16725
  const ratio = overlapRatio(draftKeywords, fileKeywords);
16299
16726
  if (ratio >= 0.6) {
@@ -16368,9 +16795,9 @@ var init_dedup = __esm({
16368
16795
 
16369
16796
  // src/import-mr.ts
16370
16797
  import fs22 from "fs/promises";
16371
- import path61 from "path";
16798
+ import path63 from "path";
16372
16799
  import readline4 from "readline/promises";
16373
- import matter5 from "gray-matter";
16800
+ import matter6 from "gray-matter";
16374
16801
  async function fetchMR(url) {
16375
16802
  if (url.includes("github.com")) {
16376
16803
  return fetchGitHubPR(url);
@@ -16472,7 +16899,7 @@ async function importFromMR(opts) {
16472
16899
  if (frontmatterStart > 0) {
16473
16900
  learningContent = learningContent.slice(frontmatterStart);
16474
16901
  }
16475
- const parsed = matter5(learningContent);
16902
+ const parsed = matter6(learningContent);
16476
16903
  const learningTitle = parsed.data["title"] ?? mr.title;
16477
16904
  const draftKeywords = extractKeywords(learningContent);
16478
16905
  const supersededEntries = await findSupersededLearnings(draftKeywords, learningsDir);
@@ -16506,18 +16933,18 @@ async function importFromMR(opts) {
16506
16933
  async function writeLearning(draft, outputDir, repoPath) {
16507
16934
  if (outputDir) {
16508
16935
  await fs22.mkdir(outputDir, { recursive: true });
16509
- const filePath = path61.join(outputDir, "learning.md");
16936
+ const filePath = path63.join(outputDir, "learning.md");
16510
16937
  await fs22.writeFile(filePath, draft.content, "utf-8");
16511
16938
  log.info(`Learning written: ${filePath}`);
16512
16939
  return;
16513
16940
  }
16514
16941
  if (repoPath) {
16515
- const learningsDir = path61.join(repoPath, "learnings");
16942
+ const learningsDir = path63.join(repoPath, "learnings");
16516
16943
  await fs22.mkdir(learningsDir, { recursive: true });
16517
16944
  const datePrefix = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
16518
16945
  const safeTitle = draft.title.slice(0, 40).replace(/[^a-zA-Z0-9一-鿿_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
16519
16946
  const filename = `${datePrefix}-${safeTitle}.md`;
16520
- const filePath = path61.join(learningsDir, filename);
16947
+ const filePath = path63.join(learningsDir, filename);
16521
16948
  await fs22.writeFile(filePath, draft.content, "utf-8");
16522
16949
  log.info(`Learning written: ${filePath}`);
16523
16950
  return;
@@ -16533,7 +16960,7 @@ var init_import_mr = __esm({
16533
16960
  init_ai_client();
16534
16961
  init_dedup();
16535
16962
  init_logger();
16536
- DEFAULT_LEARNINGS_DIR = path61.join(process.env.HOME ?? "/tmp", ".teamai", "learnings");
16963
+ DEFAULT_LEARNINGS_DIR = path63.join(process.env.HOME ?? "/tmp", ".teamai", "learnings");
16537
16964
  SUPERSEDE_THRESHOLD = 0.6;
16538
16965
  }
16539
16966
  });
@@ -16541,8 +16968,8 @@ var init_import_mr = __esm({
16541
16968
  // src/codebase.ts
16542
16969
  import { execSync as execSync6 } from "child_process";
16543
16970
  import fs23 from "fs";
16544
- import path62 from "path";
16545
- import matter6 from "gray-matter";
16971
+ import path64 from "path";
16972
+ import matter7 from "gray-matter";
16546
16973
  async function gatherRepoContext(repoPath) {
16547
16974
  const parts = [];
16548
16975
  try {
@@ -16565,7 +16992,7 @@ ${truncated}`);
16565
16992
  } catch (err) {
16566
16993
  log.debug(`gatherRepoContext: find \u5931\u8D25 \u2014 ${String(err)}`);
16567
16994
  }
16568
- const pkgPath = path62.join(repoPath, "package.json");
16995
+ const pkgPath = path64.join(repoPath, "package.json");
16569
16996
  if (fs23.existsSync(pkgPath)) {
16570
16997
  try {
16571
16998
  const raw = fs23.readFileSync(pkgPath, "utf-8");
@@ -16579,7 +17006,7 @@ ${excerpt}
16579
17006
  }
16580
17007
  }
16581
17008
  for (const candidate of ["src/index.ts", "src/main.ts", "index.ts", "main.py"]) {
16582
- const entryPath = path62.join(repoPath, candidate);
17009
+ const entryPath = path64.join(repoPath, candidate);
16583
17010
  if (fs23.existsSync(entryPath)) {
16584
17011
  try {
16585
17012
  const raw = fs23.readFileSync(entryPath, "utf-8");
@@ -16595,7 +17022,7 @@ ${excerpt}
16595
17022
  }
16596
17023
  }
16597
17024
  for (const candidate of ["src/types.ts", "src/types/index.ts", "types.py"]) {
16598
- const typesPath = path62.join(repoPath, candidate);
17025
+ const typesPath = path64.join(repoPath, candidate);
16599
17026
  if (fs23.existsSync(typesPath)) {
16600
17027
  try {
16601
17028
  const raw = fs23.readFileSync(typesPath, "utf-8");
@@ -16611,10 +17038,10 @@ ${excerpt}
16611
17038
  }
16612
17039
  }
16613
17040
  const docCandidates = [
16614
- path62.join(repoPath, "README.md"),
16615
- path62.join(repoPath, "ARCHITECTURE.md")
17041
+ path64.join(repoPath, "README.md"),
17042
+ path64.join(repoPath, "ARCHITECTURE.md")
16616
17043
  ];
16617
- const docsDir = path62.join(repoPath, "docs");
17044
+ const docsDir = path64.join(repoPath, "docs");
16618
17045
  if (fs23.existsSync(docsDir)) {
16619
17046
  try {
16620
17047
  const entries = fs23.readdirSync(docsDir);
@@ -16622,7 +17049,7 @@ ${excerpt}
16622
17049
  for (const entry of entries) {
16623
17050
  if (count >= DOCS_MAX_FILES) break;
16624
17051
  if (entry.endsWith(".md")) {
16625
- docCandidates.push(path62.join(docsDir, entry));
17052
+ docCandidates.push(path64.join(docsDir, entry));
16626
17053
  count++;
16627
17054
  }
16628
17055
  }
@@ -16635,7 +17062,7 @@ ${excerpt}
16635
17062
  try {
16636
17063
  const raw = fs23.readFileSync(docPath, "utf-8");
16637
17064
  const excerpt = raw.length > DOC_MAX_CHARS ? raw.slice(0, DOC_MAX_CHARS) + "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : raw;
16638
- const relPath = path62.relative(repoPath, docPath);
17065
+ const relPath = path64.relative(repoPath, docPath);
16639
17066
  parts.push(`## \u6587\u6863\u6458\u8981\uFF1A${relPath}
16640
17067
  ${excerpt}`);
16641
17068
  } catch (err) {
@@ -16666,9 +17093,9 @@ ${lines.join("\n")}`);
16666
17093
  if (fileCount >= LEARNINGS_MAX_FILES) break;
16667
17094
  if (!entry.endsWith(".md")) continue;
16668
17095
  try {
16669
- const filePath = path62.join(learningsDir, entry);
17096
+ const filePath = path64.join(learningsDir, entry);
16670
17097
  const raw = fs23.readFileSync(filePath, "utf-8");
16671
- const parsed = matter6(raw);
17098
+ const parsed = matter7(raw);
16672
17099
  const tags = parsed.data["tags"];
16673
17100
  if (Array.isArray(tags)) {
16674
17101
  for (const tag of tags) {
@@ -16861,7 +17288,7 @@ var init_codebase = __esm({
16861
17288
  import { createHash } from "crypto";
16862
17289
  import { execFile } from "child_process";
16863
17290
  import { readFile as readFile4, readdir as readdir2, stat } from "fs/promises";
16864
- import path63 from "path";
17291
+ import path65 from "path";
16865
17292
  import { promisify } from "util";
16866
17293
  function isKeyFile(relativePath, language) {
16867
17294
  const patterns = KEY_FILE_PATTERNS[language];
@@ -16869,12 +17296,12 @@ function isKeyFile(relativePath, language) {
16869
17296
  return patterns.some((pattern) => pattern.test(relativePath));
16870
17297
  }
16871
17298
  async function collectCode(options) {
16872
- const root = path63.resolve(options.root);
17299
+ const root = path65.resolve(options.root);
16873
17300
  const filePaths = [];
16874
17301
  await walk(root, filePaths, options.includeTests ?? false);
16875
17302
  let filtered = filePaths.sort((a, b) => {
16876
- const relA = toPosix(path63.relative(root, a));
16877
- const relB = toPosix(path63.relative(root, b));
17303
+ const relA = toPosix(path65.relative(root, a));
17304
+ const relB = toPosix(path65.relative(root, b));
16878
17305
  const langA = languageFor(a);
16879
17306
  const langB = languageFor(b);
16880
17307
  const keyA = isKeyFile(relA, langA) ? 0 : 1;
@@ -16888,7 +17315,7 @@ async function collectCode(options) {
16888
17315
  if (options.changedFiles && options.changedFiles.length > 0) {
16889
17316
  const changedSet = new Set(options.changedFiles.map((f) => toPosix(f)));
16890
17317
  filtered = filtered.filter((fp) => {
16891
- const relativePath = toPosix(path63.relative(root, fp));
17318
+ const relativePath = toPosix(path65.relative(root, fp));
16892
17319
  return changedSet.has(relativePath);
16893
17320
  });
16894
17321
  }
@@ -16896,7 +17323,7 @@ async function collectCode(options) {
16896
17323
  const files = [];
16897
17324
  for (const filePath of limited) {
16898
17325
  const content = await readFile4(filePath, "utf8");
16899
- const relativePath = toPosix(path63.relative(root, filePath));
17326
+ const relativePath = toPosix(path65.relative(root, filePath));
16900
17327
  const language = languageFor(filePath);
16901
17328
  files.push({
16902
17329
  path: filePath,
@@ -16923,7 +17350,7 @@ async function walk(directory, results, includeTests) {
16923
17350
  return;
16924
17351
  }
16925
17352
  for (const entry of await readdir2(directory, { withFileTypes: true })) {
16926
- const fullPath = path63.join(directory, entry.name);
17353
+ const fullPath = path65.join(directory, entry.name);
16927
17354
  if (safeIgnore(fullPath) || !includeTests && isTestPath(fullPath)) {
16928
17355
  continue;
16929
17356
  }
@@ -16936,14 +17363,14 @@ async function walk(directory, results, includeTests) {
16936
17363
  }
16937
17364
  function isCodeFile(filePath) {
16938
17365
  return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".json", ".yaml", ".yml", ".toml", ".sql", ".conf", ".ini"].includes(
16939
- path63.extname(filePath).toLowerCase()
17366
+ path65.extname(filePath).toLowerCase()
16940
17367
  );
16941
17368
  }
16942
17369
  function isTestPath(filePath) {
16943
17370
  return /(^|\/|\\)(test|tests|__tests__|fixtures)(\/|\\)|\.test\.|\.spec\./u.test(filePath);
16944
17371
  }
16945
17372
  function languageFor(filePath) {
16946
- const ext = path63.extname(filePath).toLowerCase();
17373
+ const ext = path65.extname(filePath).toLowerCase();
16947
17374
  const map = {
16948
17375
  ".ts": "typescript",
16949
17376
  ".tsx": "typescript",
@@ -17587,14 +18014,14 @@ var init_code_extractors = __esm({
17587
18014
  });
17588
18015
 
17589
18016
  // src/wiki-engine/code-knowledge/code-graph.ts
17590
- import path64 from "path";
18017
+ import path66 from "path";
17591
18018
  function buildCodeGraph(facts) {
17592
18019
  const nodes = facts.filter((fact) => fact.kind !== "relation").map((fact) => ({
17593
18020
  slug: `${fact.kind}/${fact.name}`,
17594
18021
  type: mapFactKindToCategory(fact.kind),
17595
18022
  confidence: fact.confidence === "EXTRACTED" ? "EXTRACTED" : "INFERRED",
17596
18023
  title: fact.name,
17597
- domain: path64.dirname(fact.file).split("/")[0] || void 0
18024
+ domain: path66.dirname(fact.file).split("/")[0] || void 0
17598
18025
  }));
17599
18026
  const nodeFiles = new Set(facts.filter((f) => f.kind !== "relation").map((f) => f.file));
17600
18027
  const edges = facts.filter((fact) => fact.kind === "relation").flatMap((fact) => {
@@ -17637,7 +18064,7 @@ var init_code_graph = __esm({
17637
18064
 
17638
18065
  // src/wiki-engine/code-knowledge/code-incremental.ts
17639
18066
  import { readFile as readFile5, writeFile as writeFile6, stat as stat2, mkdir as mkdir3 } from "fs/promises";
17640
- import path65 from "path";
18067
+ import path67 from "path";
17641
18068
  async function detectCodeIncrementalChanges(root, manifestPath, project) {
17642
18069
  const previous = await exists(manifestPath) ? JSON.parse(await readFile5(manifestPath, "utf8")) : { files: [] };
17643
18070
  const current = await collectCode({ root });
@@ -17663,14 +18090,14 @@ function affectedPages(project, files) {
17663
18090
  }
17664
18091
  async function exists(filePath) {
17665
18092
  try {
17666
- await stat2(path65.resolve(filePath));
18093
+ await stat2(path67.resolve(filePath));
17667
18094
  return true;
17668
18095
  } catch {
17669
18096
  return false;
17670
18097
  }
17671
18098
  }
17672
18099
  async function loadFactsCache(indicesDir) {
17673
- const cachePath = path65.join(indicesDir, FACTS_CACHE_FILENAME);
18100
+ const cachePath = path67.join(indicesDir, FACTS_CACHE_FILENAME);
17674
18101
  try {
17675
18102
  const raw = await readFile5(cachePath, "utf-8");
17676
18103
  const parsed = JSON.parse(raw);
@@ -17681,10 +18108,10 @@ async function loadFactsCache(indicesDir) {
17681
18108
  }
17682
18109
  async function saveFactsCache(indicesDir, facts) {
17683
18110
  await mkdir3(indicesDir, { recursive: true });
17684
- await writeFile6(path65.join(indicesDir, FACTS_CACHE_FILENAME), JSON.stringify(facts), "utf-8");
18111
+ await writeFile6(path67.join(indicesDir, FACTS_CACHE_FILENAME), JSON.stringify(facts), "utf-8");
17685
18112
  }
17686
18113
  async function loadInterfacesCache(indicesDir) {
17687
- const cachePath = path65.join(indicesDir, INTERFACES_CACHE_FILENAME);
18114
+ const cachePath = path67.join(indicesDir, INTERFACES_CACHE_FILENAME);
17688
18115
  try {
17689
18116
  const raw = await readFile5(cachePath, "utf-8");
17690
18117
  const parsed = JSON.parse(raw);
@@ -17696,7 +18123,7 @@ async function loadInterfacesCache(indicesDir) {
17696
18123
  async function saveInterfacesCache(indicesDir, inventory) {
17697
18124
  await mkdir3(indicesDir, { recursive: true });
17698
18125
  await writeFile6(
17699
- path65.join(indicesDir, INTERFACES_CACHE_FILENAME),
18126
+ path67.join(indicesDir, INTERFACES_CACHE_FILENAME),
17700
18127
  JSON.stringify(inventory, null, 2),
17701
18128
  "utf-8"
17702
18129
  );
@@ -17722,7 +18149,7 @@ var init_code_incremental = __esm({
17722
18149
  });
17723
18150
 
17724
18151
  // src/wiki-engine/interface-scanner.ts
17725
- import path66 from "path";
18152
+ import path68 from "path";
17726
18153
  async function scanInterfaces(files) {
17727
18154
  const componentMap = groupByComponent(files);
17728
18155
  const entries = [];
@@ -17790,7 +18217,7 @@ function groupByComponent(files) {
17790
18217
  if (file.repo) {
17791
18218
  component = parts.length > 1 ? `${file.repo}/${parts[0]}` : file.repo;
17792
18219
  } else {
17793
- component = parts.length > 1 ? parts[0] : path66.basename(path66.dirname(file.path));
18220
+ component = parts.length > 1 ? parts[0] : path68.basename(path68.dirname(file.path));
17794
18221
  }
17795
18222
  const group = map.get(component) ?? [];
17796
18223
  group.push(file);
@@ -18076,7 +18503,7 @@ var init_reconciler_v2_types = __esm({
18076
18503
 
18077
18504
  // src/wiki-engine/knowledge-reconciler.ts
18078
18505
  import { readFile as readFile6, readdir as readdir3, stat as stat3 } from "fs/promises";
18079
- import path67 from "path";
18506
+ import path69 from "path";
18080
18507
  async function exists2(p) {
18081
18508
  return stat3(p).then(() => true).catch(() => false);
18082
18509
  }
@@ -18085,7 +18512,7 @@ async function readPages(dirPath) {
18085
18512
  const entries = await readdir3(dirPath, { withFileTypes: true });
18086
18513
  const pages = [];
18087
18514
  for (const entry of entries) {
18088
- const full = path67.join(dirPath, entry.name);
18515
+ const full = path69.join(dirPath, entry.name);
18089
18516
  if (entry.isDirectory()) {
18090
18517
  pages.push(...await readPages(full));
18091
18518
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -18178,17 +18605,17 @@ async function reconcileKnowledge(options) {
18178
18605
  const productDirNames = options.productDirs ?? ["product", "docs"];
18179
18606
  const codeDirNames = options.codeDirs ?? ["evidence/code"];
18180
18607
  for (const dir of [...productDirNames, ...codeDirNames]) {
18181
- if (dir.includes("..") || path67.isAbsolute(dir)) {
18608
+ if (dir.includes("..") || path69.isAbsolute(dir)) {
18182
18609
  throw new Error(`Unsafe directory path rejected: ${dir}`);
18183
18610
  }
18184
18611
  }
18185
18612
  const productPages = [];
18186
18613
  for (const dir of productDirNames) {
18187
- productPages.push(...await readPages(path67.join(wikiRoot, dir)));
18614
+ productPages.push(...await readPages(path69.join(wikiRoot, dir)));
18188
18615
  }
18189
18616
  const codePages = [];
18190
18617
  for (const dir of codeDirNames) {
18191
- codePages.push(...await readPages(path67.join(wikiRoot, dir)));
18618
+ codePages.push(...await readPages(path69.join(wikiRoot, dir)));
18192
18619
  }
18193
18620
  const graphEdges = [];
18194
18621
  const gaps = [];
@@ -18215,8 +18642,8 @@ async function reconcileKnowledge(options) {
18215
18642
  ];
18216
18643
  const nc = buildConfidence(factors);
18217
18644
  graphEdges.push({
18218
- from: toPageSlug(path67.relative(wikiRoot, productPage.path)),
18219
- to: toPageSlug(path67.relative(wikiRoot, codePage.path)),
18645
+ from: toPageSlug(path69.relative(wikiRoot, productPage.path)),
18646
+ to: toPageSlug(path69.relative(wikiRoot, codePage.path)),
18220
18647
  relation: "MAPS_TO",
18221
18648
  term,
18222
18649
  confidence: nc.label,
@@ -18297,10 +18724,10 @@ async function reconcileKnowledge(options) {
18297
18724
  const MS_PER_DAY = 864e5;
18298
18725
  for (const edge of graphEdges) {
18299
18726
  const fromPage = productPages.find(
18300
- (p) => toPageSlug(path67.relative(wikiRoot, p.path)) === edge.from
18727
+ (p) => toPageSlug(path69.relative(wikiRoot, p.path)) === edge.from
18301
18728
  );
18302
18729
  const toPage = codePages.find(
18303
- (p) => toPageSlug(path67.relative(wikiRoot, p.path)) === edge.to
18730
+ (p) => toPageSlug(path69.relative(wikiRoot, p.path)) === edge.to
18304
18731
  );
18305
18732
  if (!fromPage?.updated || !toPage?.updated) continue;
18306
18733
  const fromMs = new Date(fromPage.updated).getTime();
@@ -18559,7 +18986,7 @@ __export(enrich_with_ai_exports, {
18559
18986
  enrichWithAI: () => enrichWithAI,
18560
18987
  writeManifest: () => writeManifest
18561
18988
  });
18562
- import path68 from "path";
18989
+ import path70 from "path";
18563
18990
  import { writeFile as writeFile8, mkdir as mkdir5 } from "fs/promises";
18564
18991
  function sanitizeForPrompt(text) {
18565
18992
  return text.replace(/[\n\r]/g, " ").replace(/[<>]/g, "").slice(0, 200);
@@ -18700,7 +19127,7 @@ async function enrichWithAI(ctx) {
18700
19127
  }
18701
19128
  async function writeManifest(manifest, outputDir) {
18702
19129
  await mkdir5(outputDir, { recursive: true });
18703
- const manifestPath = path68.join(outputDir, "_manifest.json");
19130
+ const manifestPath = path70.join(outputDir, "_manifest.json");
18704
19131
  await writeFile8(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
18705
19132
  return manifestPath;
18706
19133
  }
@@ -18718,7 +19145,7 @@ __export(codebase_extract_exports, {
18718
19145
  extractCodebase: () => extractCodebase
18719
19146
  });
18720
19147
  import { mkdir as mkdir6, writeFile as writeFile9, readFile as readFile7 } from "fs/promises";
18721
- import path69 from "path";
19148
+ import path71 from "path";
18722
19149
  import chalk3 from "chalk";
18723
19150
  function detectKnowledgeGaps(facts, graph, files) {
18724
19151
  const gaps = [];
@@ -18735,7 +19162,7 @@ function detectKnowledgeGaps(facts, graph, files) {
18735
19162
  const target = rel.name;
18736
19163
  if (target.startsWith(".")) continue;
18737
19164
  if (target.startsWith("node:")) continue;
18738
- const matchesAnyFile = [...scannedFiles].some((f) => f.includes(target.replace(/\//g, path69.sep)));
19165
+ const matchesAnyFile = [...scannedFiles].some((f) => f.includes(target.replace(/\//g, path71.sep)));
18739
19166
  if (!matchesAnyFile) {
18740
19167
  unresolvedImports.add(target);
18741
19168
  }
@@ -19079,21 +19506,21 @@ function buildOverview(facts, graph, project, interfaceInventory, callChains) {
19079
19506
  lines.push("## Key Dependency Paths");
19080
19507
  lines.push("");
19081
19508
  for (const chain of callChains.slice(0, 5)) {
19082
- const path92 = chain.steps.map((s) => s.symbol).join(" \u2192 ");
19083
- lines.push(`- ${chain.entryPoint}: ${path92}`);
19509
+ const path99 = chain.steps.map((s) => s.symbol).join(" \u2192 ");
19510
+ lines.push(`- ${chain.entryPoint}: ${path99}`);
19084
19511
  }
19085
19512
  }
19086
19513
  lines.push("");
19087
19514
  return lines.join("\n");
19088
19515
  }
19089
19516
  async function extractCodebase(opts) {
19090
- const root = path69.resolve(opts.path || ".");
19091
- const project = opts.project || path69.basename(root);
19517
+ const root = path71.resolve(opts.path || ".");
19518
+ const project = opts.project || path71.basename(root);
19092
19519
  const maxFiles = opts.maxFiles || 200;
19093
- const outputBase = opts.outputRoot ? path69.resolve(opts.outputRoot) : root;
19094
- const wikiRoot = path69.join(outputBase, "teamwiki");
19095
- const evidenceDir = path69.join(wikiRoot, "evidence", "code", project);
19096
- const manifestPath = path69.join(wikiRoot, "source-manifest.json");
19520
+ const outputBase = opts.outputRoot ? path71.resolve(opts.outputRoot) : root;
19521
+ const wikiRoot = path71.join(outputBase, "teamwiki");
19522
+ const evidenceDir = path71.join(wikiRoot, "evidence", "code", project);
19523
+ const manifestPath = path71.join(wikiRoot, "source-manifest.json");
19097
19524
  let changedFiles;
19098
19525
  let deletedFiles = [];
19099
19526
  if (opts.incremental) {
@@ -19130,7 +19557,7 @@ async function extractCodebase(opts) {
19130
19557
  const newFacts = files.length > 0 ? extractCodeFacts(files) : [];
19131
19558
  let facts;
19132
19559
  let interfaceInventory;
19133
- const indicesDir = path69.join(wikiRoot, ".indices");
19560
+ const indicesDir = path71.join(wikiRoot, ".indices");
19134
19561
  if (changedFiles !== void 0) {
19135
19562
  const oldFacts = await loadFactsCache(indicesDir);
19136
19563
  const oldInterfaces = await loadInterfacesCache(indicesDir);
@@ -19158,7 +19585,7 @@ async function extractCodebase(opts) {
19158
19585
  }
19159
19586
  const graph = buildCodeGraph(facts);
19160
19587
  let callChains;
19161
- const depPathsFile = path69.join(evidenceDir, "dependency-paths.md");
19588
+ const depPathsFile = path71.join(evidenceDir, "dependency-paths.md");
19162
19589
  if (changedFiles) {
19163
19590
  callChains = [];
19164
19591
  } else {
@@ -19174,7 +19601,7 @@ async function extractCodebase(opts) {
19174
19601
  }
19175
19602
  }
19176
19603
  for (const [filename, content] of pages) {
19177
- await writeIfChanged(path69.join(evidenceDir, filename), content);
19604
+ await writeIfChanged(path71.join(evidenceDir, filename), content);
19178
19605
  }
19179
19606
  const pageSlugs = [...pages.keys()].map((p) => `evidence/code/${project}/${p.replace(".md", "")}`);
19180
19607
  const overlay = buildIndexHubOverlay(project, "evidence/code", pageSlugs);
@@ -19203,7 +19630,7 @@ async function extractCodebase(opts) {
19203
19630
  keywords: enrichResult.repoKeywords || [],
19204
19631
  components: enrichResult.domains[0]?.components ?? []
19205
19632
  };
19206
- await writeFile9(path69.join(evidenceDir, "_domains.json"), JSON.stringify(domainMeta, null, 2), "utf-8");
19633
+ await writeFile9(path71.join(evidenceDir, "_domains.json"), JSON.stringify(domainMeta, null, 2), "utf-8");
19207
19634
  if (!opts.json) {
19208
19635
  const domainLabel = domainMeta.domain || "uncategorized";
19209
19636
  console.log(` AI enrich: ${enrichResult.manifest.components.length} modules, domain=${domainLabel}`);
@@ -19216,14 +19643,14 @@ async function extractCodebase(opts) {
19216
19643
  }
19217
19644
  const moduleSummaries = buildModuleSummaries(facts, graph, project);
19218
19645
  if (moduleSummaries.size > 0) {
19219
- const modulesDir = path69.join(evidenceDir, "modules");
19646
+ const modulesDir = path71.join(evidenceDir, "modules");
19220
19647
  await mkdir6(modulesDir, { recursive: true });
19221
19648
  for (const [filename, content] of moduleSummaries) {
19222
- await writeIfChanged(path69.join(modulesDir, filename), content);
19649
+ await writeIfChanged(path71.join(modulesDir, filename), content);
19223
19650
  }
19224
19651
  }
19225
19652
  const overview = buildOverview(facts, repoGraph, project, interfaceInventory, callChains);
19226
- await writeIfChanged(path69.join(evidenceDir, "overview.md"), overview);
19653
+ await writeIfChanged(path71.join(evidenceDir, "overview.md"), overview);
19227
19654
  const proj = [{ slug: project, label: project }];
19228
19655
  const ifByType = {};
19229
19656
  for (const e of interfaceInventory.entries) {
@@ -19236,11 +19663,11 @@ async function extractCodebase(opts) {
19236
19663
  interfaces: Object.keys(ifByType).length > 0 ? ifByType : void 0,
19237
19664
  callChains: callChains.length > 0 ? callChains.length : void 0
19238
19665
  };
19239
- await writeIfChanged(path69.join(wikiRoot, "router.md"), routerTemplate(proj, aiDomains.length > 0 ? aiDomains : void 0));
19240
- await writeIfChanged(path69.join(wikiRoot, "hot.md"), HOT_TEMPLATE);
19241
- await writeIfChanged(path69.join(wikiRoot, "index.md"), indexTemplate(proj, indexStats));
19666
+ await writeIfChanged(path71.join(wikiRoot, "router.md"), routerTemplate(proj, aiDomains.length > 0 ? aiDomains : void 0));
19667
+ await writeIfChanged(path71.join(wikiRoot, "hot.md"), HOT_TEMPLATE);
19668
+ await writeIfChanged(path71.join(wikiRoot, "index.md"), indexTemplate(proj, indexStats));
19242
19669
  const gaps = detectKnowledgeGaps(facts, graph, files);
19243
- const gapsDir = path69.join(wikiRoot, "gaps");
19670
+ const gapsDir = path71.join(wikiRoot, "gaps");
19244
19671
  await mkdir6(gapsDir, { recursive: true });
19245
19672
  const gapLines = [
19246
19673
  "---",
@@ -19263,7 +19690,7 @@ async function extractCodebase(opts) {
19263
19690
  gapLines.push("| \u2014 | \u2014 | \u2014 | \u672A\u53D1\u73B0\u660E\u663E\u77E5\u8BC6\u7F3A\u53E3 | \u2014 |");
19264
19691
  }
19265
19692
  gapLines.push("");
19266
- await writeIfChanged(path69.join(gapsDir, "detected.md"), gapLines.join("\n"));
19693
+ await writeIfChanged(path71.join(gapsDir, "detected.md"), gapLines.join("\n"));
19267
19694
  await saveFactsCache(indicesDir, facts);
19268
19695
  await saveInterfacesCache(indicesDir, interfaceInventory);
19269
19696
  let allManifestFiles = collectionManifest.files.map((f) => ({
@@ -19493,10 +19920,10 @@ var init_clone = __esm({
19493
19920
 
19494
19921
  // src/review-store.ts
19495
19922
  import crypto2 from "crypto";
19496
- import path70 from "path";
19923
+ import path72 from "path";
19497
19924
  import fs25 from "fs-extra";
19498
19925
  function getPendingReviewPath(cwd) {
19499
- return path70.join(cwd, PENDING_REVIEW_PATH);
19926
+ return path72.join(cwd, PENDING_REVIEW_PATH);
19500
19927
  }
19501
19928
  function computeReviewId(file, section, ts) {
19502
19929
  return crypto2.createHash("sha1").update(`${file}|${section ?? ""}|${ts}`).digest("hex").slice(0, 12);
@@ -19572,7 +19999,7 @@ async function loadPendingReview(cwd) {
19572
19999
  async function savePendingReview(cwd, items) {
19573
20000
  const filePath = getPendingReviewPath(cwd);
19574
20001
  const tmpPath = `${filePath}.tmp`;
19575
- await fs25.ensureDir(path70.dirname(filePath));
20002
+ await fs25.ensureDir(path72.dirname(filePath));
19576
20003
  const content = items.map((item) => JSON.stringify(item)).join("\n") + (items.length > 0 ? "\n" : "");
19577
20004
  await fs25.writeFile(tmpPath, content, "utf8");
19578
20005
  await fs25.rename(tmpPath, filePath);
@@ -19592,7 +20019,7 @@ async function appendPendingReview(cwd, partial) {
19592
20019
  risk
19593
20020
  };
19594
20021
  const filePath = getPendingReviewPath(cwd);
19595
- await fs25.ensureDir(path70.dirname(filePath));
20022
+ await fs25.ensureDir(path72.dirname(filePath));
19596
20023
  await fs25.appendFile(filePath, JSON.stringify(item) + "\n", "utf8");
19597
20024
  return item;
19598
20025
  }
@@ -19635,14 +20062,14 @@ __export(repo_cache_exports, {
19635
20062
  readLastSync: () => readLastSync,
19636
20063
  writeLastSync: () => writeLastSync
19637
20064
  });
19638
- import path71 from "path";
20065
+ import path73 from "path";
19639
20066
  import os4 from "os";
19640
20067
  import fs26 from "fs-extra";
19641
20068
  function getCacheRoot2() {
19642
- return process.env.TEAMAI_CACHE_DIR ?? path71.join(os4.homedir(), ".teamai", "cache", "repos");
20069
+ return process.env.TEAMAI_CACHE_DIR ?? path73.join(os4.homedir(), ".teamai", "cache", "repos");
19643
20070
  }
19644
20071
  function getRepoCacheDir(provider, owner, repo) {
19645
- return path71.join(getCacheRoot2(), provider, owner, repo);
20072
+ return path73.join(getCacheRoot2(), provider, owner, repo);
19646
20073
  }
19647
20074
  function getRepoSlug(provider, owner, repo) {
19648
20075
  const safeOwner = owner.replace(/\//g, "-");
@@ -19653,10 +20080,10 @@ async function writeLastSync(cacheDir, sha) {
19653
20080
  const content = `${sha}
19654
20081
  ${isoTs}
19655
20082
  `;
19656
- await fs26.writeFile(path71.join(cacheDir, LAST_SYNC_FILE), content, "utf8");
20083
+ await fs26.writeFile(path73.join(cacheDir, LAST_SYNC_FILE), content, "utf8");
19657
20084
  }
19658
20085
  async function readLastSync(cacheDir) {
19659
- const filePath = path71.join(cacheDir, LAST_SYNC_FILE);
20086
+ const filePath = path73.join(cacheDir, LAST_SYNC_FILE);
19660
20087
  const exists3 = await fs26.pathExists(filePath);
19661
20088
  if (!exists3) {
19662
20089
  return null;
@@ -19721,7 +20148,7 @@ var init_schema = __esm({
19721
20148
  });
19722
20149
 
19723
20150
  // src/domains/store.ts
19724
- import path72 from "path";
20151
+ import path74 from "path";
19725
20152
  import fs27 from "fs-extra";
19726
20153
  import { parse as yamlParse, stringify as yamlStringify } from "yaml";
19727
20154
  function parseAndValidate(content, filePath) {
@@ -19734,7 +20161,7 @@ function parseAndValidate(content, filePath) {
19734
20161
  return result.data;
19735
20162
  }
19736
20163
  async function loadDomains(cwd) {
19737
- const filePath = path72.join(cwd, DOMAINS_PATH);
20164
+ const filePath = path74.join(cwd, DOMAINS_PATH);
19738
20165
  const exists3 = await fs27.pathExists(filePath);
19739
20166
  if (!exists3) {
19740
20167
  return DomainsFileSchema.parse({});
@@ -19747,14 +20174,14 @@ async function loadDomains(cwd) {
19747
20174
  return parseAndValidate(content, filePath);
19748
20175
  }
19749
20176
  async function saveDomains(cwd, data) {
19750
- const filePath = path72.join(cwd, DOMAINS_PATH);
19751
- await fs27.ensureDir(path72.dirname(filePath));
20177
+ const filePath = path74.join(cwd, DOMAINS_PATH);
20178
+ await fs27.ensureDir(path74.dirname(filePath));
19752
20179
  await fs27.writeFile(filePath, yamlStringify(data), "utf8");
19753
20180
  }
19754
20181
  async function appendHistory(cwd, event) {
19755
20182
  const validated = HistoryEventSchema.parse(event);
19756
- const filePath = path72.join(cwd, HISTORY_PATH);
19757
- await fs27.ensureDir(path72.dirname(filePath));
20183
+ const filePath = path74.join(cwd, HISTORY_PATH);
20184
+ await fs27.ensureDir(path74.dirname(filePath));
19758
20185
  await fs27.appendFile(filePath, JSON.stringify(validated) + "\n", "utf8");
19759
20186
  }
19760
20187
  var DOMAINS_PATH, HISTORY_PATH, MAX_CONFIG_FILE_BYTES3;
@@ -19901,8 +20328,8 @@ __export(deep_enrich_exports, {
19901
20328
  deepEnrich: () => deepEnrich
19902
20329
  });
19903
20330
  import { readFile as readFile8, writeFile as writeFile10, readdir as readdir4, mkdir as mkdir7 } from "fs/promises";
19904
- import path73 from "path";
19905
- async function readFileSafe3(filePath) {
20331
+ import path75 from "path";
20332
+ async function readFileSafe4(filePath) {
19906
20333
  try {
19907
20334
  return await readFile8(filePath, "utf-8");
19908
20335
  } catch {
@@ -19910,7 +20337,7 @@ async function readFileSafe3(filePath) {
19910
20337
  }
19911
20338
  }
19912
20339
  async function loadContext(evidenceDir) {
19913
- const manifestRaw = await readFileSafe3(path73.join(evidenceDir, "_manifest.json"));
20340
+ const manifestRaw = await readFileSafe4(path75.join(evidenceDir, "_manifest.json"));
19914
20341
  let manifest = {};
19915
20342
  try {
19916
20343
  manifest = JSON.parse(manifestRaw);
@@ -19918,18 +20345,18 @@ async function loadContext(evidenceDir) {
19918
20345
  log.debug("deep-enrich: failed to parse _manifest.json");
19919
20346
  }
19920
20347
  const [indexMd, callChains, overview] = await Promise.all([
19921
- readFileSafe3(path73.join(evidenceDir, "index.md")),
19922
- readFileSafe3(path73.join(evidenceDir, "dependency-paths.md")),
19923
- readFileSafe3(path73.join(evidenceDir, "overview.md"))
20348
+ readFileSafe4(path75.join(evidenceDir, "index.md")),
20349
+ readFileSafe4(path75.join(evidenceDir, "dependency-paths.md")),
20350
+ readFileSafe4(path75.join(evidenceDir, "overview.md"))
19924
20351
  ]);
19925
- const modulesDir = path73.join(evidenceDir, "modules");
20352
+ const modulesDir = path75.join(evidenceDir, "modules");
19926
20353
  const moduleDocs = /* @__PURE__ */ new Map();
19927
20354
  if (await pathExists(modulesDir)) {
19928
20355
  try {
19929
20356
  const entries = await readdir4(modulesDir);
19930
20357
  await Promise.all(
19931
20358
  entries.filter((e) => e.endsWith(".md")).map(async (e) => {
19932
- const content = await readFileSafe3(path73.join(modulesDir, e));
20359
+ const content = await readFileSafe4(path75.join(modulesDir, e));
19933
20360
  moduleDocs.set(e.replace(/\.md$/, ""), content);
19934
20361
  })
19935
20362
  );
@@ -19940,7 +20367,7 @@ async function loadContext(evidenceDir) {
19940
20367
  return { manifest, indexMd, callChains, overview, moduleDocs };
19941
20368
  }
19942
20369
  function progressPath(evidenceDir) {
19943
- return path73.join(evidenceDir, PROGRESS_PATH_SUBDIR, PROGRESS_FILENAME);
20370
+ return path75.join(evidenceDir, PROGRESS_PATH_SUBDIR, PROGRESS_FILENAME);
19944
20371
  }
19945
20372
  function isValidProgressState(v, project) {
19946
20373
  if (typeof v !== "object" || v === null) return false;
@@ -19966,7 +20393,7 @@ async function loadProgress(evidenceDir, project, allComponents) {
19966
20393
  }
19967
20394
  async function saveProgress(evidenceDir, state) {
19968
20395
  const p = progressPath(evidenceDir);
19969
- await mkdir7(path73.dirname(p), { recursive: true });
20396
+ await mkdir7(path75.dirname(p), { recursive: true });
19970
20397
  const updated = { ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
19971
20398
  await writeFile10(p, JSON.stringify(updated, null, 2), "utf-8");
19972
20399
  }
@@ -20205,7 +20632,7 @@ async function runPhaseComponents(opts, ctx, progress, docsDir) {
20205
20632
  log.warn(`deep-enrich[${project}]: Skipping unsafe component slug "${comp.slug}": ${e.message}`);
20206
20633
  continue;
20207
20634
  }
20208
- const outPath = path73.join(docsDir, `${comp.slug}.md`);
20635
+ const outPath = path75.join(docsDir, `${comp.slug}.md`);
20209
20636
  await mkdir7(docsDir, { recursive: true });
20210
20637
  await writeFile10(outPath, content, "utf-8");
20211
20638
  progress.componentsDone.push(comp.slug);
@@ -20233,7 +20660,7 @@ async function runPhaseArchitecture(opts, ctx, docsDir) {
20233
20660
  log.warn(`deep-enrich[${project}]: Architecture overview: AI returned empty, skipping write`);
20234
20661
  return;
20235
20662
  }
20236
- const outPath = path73.join(docsDir, "architecture.md");
20663
+ const outPath = path75.join(docsDir, "architecture.md");
20237
20664
  await mkdir7(docsDir, { recursive: true });
20238
20665
  await writeFile10(outPath, content, "utf-8");
20239
20666
  log.debug(`deep-enrich[${project}]: Architecture overview written: ${outPath}`);
@@ -20241,15 +20668,15 @@ async function runPhaseArchitecture(opts, ctx, docsDir) {
20241
20668
  async function runPhaseGraph(opts, ctx, docsDir) {
20242
20669
  const { project, evidenceDir } = opts;
20243
20670
  log.info(`deep-enrich[${project}]: Phase 3 \u2014 Generating deterministic graph docs`);
20244
- const interfacesMd = await readFileSafe3(path73.join(evidenceDir, "interfaces.md"));
20671
+ const interfacesMd = await readFileSafe4(path75.join(evidenceDir, "interfaces.md"));
20245
20672
  const g1 = buildG1RelationsDoc(ctx.manifest);
20246
20673
  const g2 = buildG2DataflowDoc(ctx.callChains);
20247
20674
  const g3 = buildG3InterfacesDoc(interfacesMd);
20248
20675
  await mkdir7(docsDir, { recursive: true });
20249
20676
  await Promise.all([
20250
- writeFile10(path73.join(docsDir, "graph-g1-relations.md"), g1, "utf-8"),
20251
- writeFile10(path73.join(docsDir, "graph-g2-dataflow.md"), g2, "utf-8"),
20252
- writeFile10(path73.join(docsDir, "graph-g3-interfaces.md"), g3, "utf-8")
20677
+ writeFile10(path75.join(docsDir, "graph-g1-relations.md"), g1, "utf-8"),
20678
+ writeFile10(path75.join(docsDir, "graph-g2-dataflow.md"), g2, "utf-8"),
20679
+ writeFile10(path75.join(docsDir, "graph-g3-interfaces.md"), g3, "utf-8")
20253
20680
  ]);
20254
20681
  log.debug(`deep-enrich[${project}]: Graph docs written: ${docsDir}`);
20255
20682
  }
@@ -20355,14 +20782,14 @@ async function runPhaseAiGraph(opts, ctx, docsDir) {
20355
20782
  await mkdir7(docsDir, { recursive: true });
20356
20783
  const g6HasEdges = (ctx.manifest.edges ?? []).length > 0;
20357
20784
  const g6 = buildG6Content(project, ctx.manifest);
20358
- await writeFile10(path73.join(docsDir, "graph-g6-multihop.md"), g6, "utf-8");
20785
+ await writeFile10(path75.join(docsDir, "graph-g6-multihop.md"), g6, "utf-8");
20359
20786
  log.debug(`deep-enrich[${project}]: G6 multi-hop analysis written`);
20360
20787
  let g5Generated = false;
20361
20788
  if (ctx.moduleDocs.size < 2) {
20362
20789
  log.warn(`deep-enrich[${project}]: Insufficient modules (${ctx.moduleDocs.size} < 2), skipping G5`);
20363
20790
  return { g5Generated, g6Generated: g6HasEdges };
20364
20791
  }
20365
- const architectureMd = await readFileSafe3(path73.join(docsDir, "architecture.md"));
20792
+ const architectureMd = await readFileSafe4(path75.join(docsDir, "architecture.md"));
20366
20793
  if (!architectureMd.trim()) {
20367
20794
  log.warn(`deep-enrich[${project}]: No architecture doc, skipping G5 scenarios`);
20368
20795
  return { g5Generated, g6Generated: g6HasEdges };
@@ -20372,7 +20799,7 @@ async function runPhaseAiGraph(opts, ctx, docsDir) {
20372
20799
  try {
20373
20800
  const g5Content = await callClaude(prompt);
20374
20801
  if (g5Content.trim()) {
20375
- await writeFile10(path73.join(docsDir, "graph-g5-scenarios.md"), g5Content, "utf-8");
20802
+ await writeFile10(path75.join(docsDir, "graph-g5-scenarios.md"), g5Content, "utf-8");
20376
20803
  log.debug(`deep-enrich[${project}]: G5 scenario diagrams written`);
20377
20804
  g5Generated = true;
20378
20805
  }
@@ -20390,10 +20817,10 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20390
20817
  hasG5: graphFlags?.g5Generated ?? false,
20391
20818
  hasG6: graphFlags?.g6Generated ?? true
20392
20819
  });
20393
- await writeFile10(path73.join(docsDir, "README.md"), graphReadme, "utf-8");
20820
+ await writeFile10(path75.join(docsDir, "README.md"), graphReadme, "utf-8");
20394
20821
  log.debug(`deep-enrich[${project}]: graph/README.md routing table written`);
20395
20822
  const { wikiRoot } = opts;
20396
- const domainsJson = await readFileSafe3(path73.join(evidenceDir, "_domains.json"));
20823
+ const domainsJson = await readFileSafe4(path75.join(evidenceDir, "_domains.json"));
20397
20824
  let keywords = [];
20398
20825
  let description = "";
20399
20826
  try {
@@ -20402,8 +20829,8 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20402
20829
  description = domains.description ?? "";
20403
20830
  } catch {
20404
20831
  }
20405
- const routerPath = path73.join(wikiRoot, "router.md");
20406
- const routerContent = await readFileSafe3(routerPath);
20832
+ const routerPath = path75.join(wikiRoot, "router.md");
20833
+ const routerContent = await readFileSafe4(routerPath);
20407
20834
  const projectLink = `[[evidence/code/${project}/index]]`;
20408
20835
  if (routerContent && !routerContent.includes(projectLink)) {
20409
20836
  const kw = keywords.length > 0 ? ` [${keywords.slice(0, 5).join(", ")}]` : "";
@@ -20412,8 +20839,8 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20412
20839
  `;
20413
20840
  await writeFile10(routerPath, routerContent.trimEnd() + "\n" + line, "utf-8");
20414
20841
  }
20415
- const indexPath = path73.join(wikiRoot, "index.md");
20416
- const indexContent = await readFileSafe3(indexPath);
20842
+ const indexPath = path75.join(wikiRoot, "index.md");
20843
+ const indexContent = await readFileSafe4(indexPath);
20417
20844
  if (indexContent && !indexContent.includes(`evidence/code/${project}/`)) {
20418
20845
  const navBlock = [
20419
20846
  `### ${project}`,
@@ -20437,7 +20864,7 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20437
20864
  }
20438
20865
  async function deepEnrich(opts) {
20439
20866
  const { project, evidenceDir } = opts;
20440
- const docsDir = path73.join(evidenceDir, "docs");
20867
+ const docsDir = path75.join(evidenceDir, "docs");
20441
20868
  log.info(`deep-enrich[${project}]: Starting deep knowledge generation, evidenceDir=${evidenceDir}`);
20442
20869
  const ctx = await loadContext(evidenceDir);
20443
20870
  let components = ctx.manifest.components ?? [];
@@ -20526,11 +20953,11 @@ var graph_aggregate_exports = {};
20526
20953
  __export(graph_aggregate_exports, {
20527
20954
  aggregateGlobalGraph: () => aggregateGlobalGraph
20528
20955
  });
20529
- import path74 from "path";
20956
+ import path76 from "path";
20530
20957
  import { readdir as readdir5 } from "fs/promises";
20531
20958
  import fs28 from "fs-extra";
20532
20959
  async function aggregateGlobalGraph(teamwikiRoot) {
20533
- const evidenceBase = path74.join(teamwikiRoot, "evidence", "code");
20960
+ const evidenceBase = path76.join(teamwikiRoot, "evidence", "code");
20534
20961
  if (!await fs28.pathExists(evidenceBase)) return null;
20535
20962
  const { mergeGraphs: mergeGraphs2 } = await Promise.resolve().then(() => (init_adapters(), adapters_exports));
20536
20963
  const { detectCrossRepoEdges: detectCrossRepoEdges2 } = await Promise.resolve().then(() => (init_import_repo(), import_repo_exports));
@@ -20538,7 +20965,7 @@ async function aggregateGlobalGraph(teamwikiRoot) {
20538
20965
  const projectDirs = await readdir5(evidenceBase, { withFileTypes: true });
20539
20966
  for (const dir of projectDirs) {
20540
20967
  if (!dir.isDirectory()) continue;
20541
- const graphPath = path74.join(evidenceBase, dir.name, ".indices", "graph-index.json");
20968
+ const graphPath = path76.join(evidenceBase, dir.name, ".indices", "graph-index.json");
20542
20969
  if (!await fs28.pathExists(graphPath)) continue;
20543
20970
  try {
20544
20971
  const overlay = JSON.parse(await fs28.readFile(graphPath, "utf8"));
@@ -20556,8 +20983,8 @@ async function aggregateGlobalGraph(teamwikiRoot) {
20556
20983
  }
20557
20984
  }
20558
20985
  if (globalGraph) {
20559
- const destPath = path74.join(teamwikiRoot, ".indices", "graph-index.json");
20560
- await fs28.ensureDir(path74.dirname(destPath));
20986
+ const destPath = path76.join(teamwikiRoot, ".indices", "graph-index.json");
20987
+ await fs28.ensureDir(path76.dirname(destPath));
20561
20988
  await fs28.writeFile(destPath, JSON.stringify(globalGraph, null, 2), "utf8");
20562
20989
  log.info(`global graph-index.json aggregated (${globalGraph.nodes.length} nodes, ${globalGraph.edges.length} edges)`);
20563
20990
  return { nodes: globalGraph.nodes.length, edges: globalGraph.edges.length };
@@ -20579,7 +21006,7 @@ __export(import_repo_exports, {
20579
21006
  detectDomainDrift: () => detectDomainDrift,
20580
21007
  importFromRepo: () => importFromRepo
20581
21008
  });
20582
- import path75 from "path";
21009
+ import path77 from "path";
20583
21010
  import fs29 from "fs-extra";
20584
21011
  import chalk5 from "chalk";
20585
21012
  function detectCrossRepoEdges(overlay, existing) {
@@ -20694,9 +21121,9 @@ async function detectPrimaryLanguage(repoPath, maxDepth = 3) {
20694
21121
  if (["node_modules", ".git", "dist", "build", "__pycache__", ".venv"].includes(name)) {
20695
21122
  continue;
20696
21123
  }
20697
- await walk2(path75.join(dir, name), depth + 1);
21124
+ await walk2(path77.join(dir, name), depth + 1);
20698
21125
  } else if (entry.isFile()) {
20699
- const ext = path75.extname(name).toLowerCase();
21126
+ const ext = path77.extname(name).toLowerCase();
20700
21127
  const lang = langExtMap[ext];
20701
21128
  if (lang) {
20702
21129
  counts.set(lang, (counts.get(lang) ?? 0) + 1);
@@ -20720,7 +21147,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20720
21147
  const meta = { url, name };
20721
21148
  const readmeCandidates = ["README.md", "readme.md", "README.zh-CN.md", "README.zh.md"];
20722
21149
  for (const candidate of readmeCandidates) {
20723
- const filePath = path75.join(repoPath, candidate);
21150
+ const filePath = path77.join(repoPath, candidate);
20724
21151
  if (await fs29.pathExists(filePath)) {
20725
21152
  try {
20726
21153
  const content = await fs29.readFile(filePath, "utf8");
@@ -20731,7 +21158,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20731
21158
  }
20732
21159
  }
20733
21160
  }
20734
- const pkgPath = path75.join(repoPath, "package.json");
21161
+ const pkgPath = path77.join(repoPath, "package.json");
20735
21162
  if (await fs29.pathExists(pkgPath)) {
20736
21163
  try {
20737
21164
  const pkgRaw = await fs29.readFile(pkgPath, "utf8");
@@ -20746,7 +21173,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20746
21173
  }
20747
21174
  }
20748
21175
  if (!meta.description) {
20749
- const setupPath = path75.join(repoPath, "setup.py");
21176
+ const setupPath = path77.join(repoPath, "setup.py");
20750
21177
  if (await fs29.pathExists(setupPath)) {
20751
21178
  try {
20752
21179
  const setupContent = await fs29.readFile(setupPath, "utf8");
@@ -20877,7 +21304,7 @@ async function importFromRepo(opts) {
20877
21304
  const cacheDir = getRepoCacheDir(providerName, owner, repoName);
20878
21305
  const slug = getRepoSlug(providerName, owner, repoName);
20879
21306
  const lastSync = await readLastSync(cacheDir);
20880
- const cacheExists = await fs29.pathExists(path75.join(cacheDir, ".git"));
21307
+ const cacheExists = await fs29.pathExists(path77.join(cacheDir, ".git"));
20881
21308
  const useIncremental = incremental && cacheExists && lastSync !== null;
20882
21309
  let cloneSha;
20883
21310
  let cloneBranch;
@@ -20955,42 +21382,42 @@ async function importFromRepo(opts) {
20955
21382
  mrTeamConfig = { repo: tc.repo, provider: tc.provider, reviewers: tc.reviewers };
20956
21383
  mrLocalConfig = { repo: lc.repo, username: lc.username };
20957
21384
  } catch {
20958
- teamRepoDir = path75.join(process.cwd(), ".teamai", "team-repo");
21385
+ teamRepoDir = path77.join(process.cwd(), ".teamai", "team-repo");
20959
21386
  }
20960
- const teamwikiRoot = output ? path75.resolve(output, "..", "teamwiki") : path75.join(teamRepoDir, "teamwiki");
21387
+ const teamwikiRoot = output ? path77.resolve(output, "..", "teamwiki") : path77.join(teamRepoDir, "teamwiki");
20961
21388
  if (!dryRun) {
20962
- const cacheWiki = path75.join(cacheDir, "teamwiki");
21389
+ const cacheWiki = path77.join(cacheDir, "teamwiki");
20963
21390
  try {
20964
21391
  if (incremental) {
20965
- const destIndices = path75.join(teamwikiRoot, ".indices");
20966
- const cacheIndices = path75.join(cacheDir, "teamwiki", ".indices");
21392
+ const destIndices = path77.join(teamwikiRoot, ".indices");
21393
+ const cacheIndices = path77.join(cacheDir, "teamwiki", ".indices");
20967
21394
  await fs29.ensureDir(cacheIndices);
20968
21395
  for (const f of ["facts-cache.json", "interfaces-cache.json"]) {
20969
- const src = path75.join(destIndices, f);
21396
+ const src = path77.join(destIndices, f);
20970
21397
  if (await fs29.pathExists(src)) {
20971
- await fs29.copy(src, path75.join(cacheIndices, f));
21398
+ await fs29.copy(src, path77.join(cacheIndices, f));
20972
21399
  }
20973
21400
  }
20974
- const existingManifest = path75.join(teamwikiRoot, "source-manifest.json");
21401
+ const existingManifest = path77.join(teamwikiRoot, "source-manifest.json");
20975
21402
  if (await fs29.pathExists(existingManifest)) {
20976
- await fs29.copy(existingManifest, path75.join(cacheDir, "teamwiki", "source-manifest.json"));
21403
+ await fs29.copy(existingManifest, path77.join(cacheDir, "teamwiki", "source-manifest.json"));
20977
21404
  }
20978
21405
  }
20979
21406
  await extractCodebase({ path: cacheDir, project: slug, json: false, skipEnrich, incremental });
20980
21407
  if (await fs29.pathExists(cacheWiki)) {
20981
- const evidenceSrc = path75.join(cacheWiki, "evidence", "code", slug);
20982
- const evidenceDest = path75.join(teamwikiRoot, "evidence", "code", slug);
21408
+ const evidenceSrc = path77.join(cacheWiki, "evidence", "code", slug);
21409
+ const evidenceDest = path77.join(teamwikiRoot, "evidence", "code", slug);
20983
21410
  if (await fs29.pathExists(evidenceDest)) {
20984
21411
  const entries = await fs29.readdir(evidenceDest);
20985
21412
  for (const entry of entries) {
20986
21413
  if (entry === ".indices") continue;
20987
- await fs29.remove(path75.join(evidenceDest, entry));
21414
+ await fs29.remove(path77.join(evidenceDest, entry));
20988
21415
  }
20989
21416
  }
20990
21417
  await fs29.ensureDir(evidenceDest);
20991
21418
  await fs29.copy(evidenceSrc, evidenceDest, { overwrite: true });
20992
21419
  if (codebaseMd) {
20993
- const overviewPath = path75.join(evidenceDest, "overview.md");
21420
+ const overviewPath = path77.join(evidenceDest, "overview.md");
20994
21421
  const existing = await fs29.readFile(overviewPath, "utf8").catch(() => "");
20995
21422
  const aiNarrative = codebaseMd.replace(/^---[\s\S]*?---\n*/m, "");
20996
21423
  const marker = "## AI Architecture Narrative";
@@ -21013,31 +21440,31 @@ ${aiNarrative}`;
21013
21440
  }
21014
21441
  await fs29.writeFile(overviewPath, combined, "utf8");
21015
21442
  }
21016
- const srcGraph = path75.join(cacheWiki, ".indices", "graph-index.json");
21443
+ const srcGraph = path77.join(cacheWiki, ".indices", "graph-index.json");
21017
21444
  if (await fs29.pathExists(srcGraph)) {
21018
- const evidenceGraphDir = path75.join(teamwikiRoot, "evidence", "code", slug, ".indices");
21445
+ const evidenceGraphDir = path77.join(teamwikiRoot, "evidence", "code", slug, ".indices");
21019
21446
  await fs29.ensureDir(evidenceGraphDir);
21020
- await fs29.copy(srcGraph, path75.join(evidenceGraphDir, "graph-index.json"));
21447
+ await fs29.copy(srcGraph, path77.join(evidenceGraphDir, "graph-index.json"));
21021
21448
  } else {
21022
21449
  log.debug(`[graph] per-repo graph-index.json not found, skipping copy`);
21023
21450
  }
21024
- const cacheIndices = path75.join(cacheWiki, ".indices");
21025
- const destIndices = path75.join(teamwikiRoot, ".indices");
21451
+ const cacheIndices = path77.join(cacheWiki, ".indices");
21452
+ const destIndices = path77.join(teamwikiRoot, ".indices");
21026
21453
  for (const cacheFile of ["facts-cache.json", "interfaces-cache.json"]) {
21027
- const src = path75.join(cacheIndices, cacheFile);
21454
+ const src = path77.join(cacheIndices, cacheFile);
21028
21455
  if (await fs29.pathExists(src)) {
21029
21456
  await fs29.ensureDir(destIndices);
21030
- await fs29.copy(src, path75.join(destIndices, cacheFile), { overwrite: true });
21457
+ await fs29.copy(src, path77.join(destIndices, cacheFile), { overwrite: true });
21031
21458
  }
21032
21459
  }
21033
- const srcManifest = path75.join(cacheWiki, "source-manifest.json");
21460
+ const srcManifest = path77.join(cacheWiki, "source-manifest.json");
21034
21461
  if (await fs29.pathExists(srcManifest)) {
21035
- await fs29.copy(srcManifest, path75.join(teamwikiRoot, "source-manifest.json"), { overwrite: true });
21462
+ await fs29.copy(srcManifest, path77.join(teamwikiRoot, "source-manifest.json"), { overwrite: true });
21036
21463
  }
21037
21464
  await fs29.remove(cacheWiki);
21038
21465
  }
21039
21466
  if (explicitDomain) {
21040
- const domainsJsonPath = path75.join(teamwikiRoot, "evidence", "code", slug, "_domains.json");
21467
+ const domainsJsonPath = path77.join(teamwikiRoot, "evidence", "code", slug, "_domains.json");
21041
21468
  if (await fs29.pathExists(domainsJsonPath)) {
21042
21469
  try {
21043
21470
  const existing = JSON.parse(await fs29.readFile(domainsJsonPath, "utf8"));
@@ -21050,8 +21477,8 @@ ${aiNarrative}`;
21050
21477
  }
21051
21478
  }
21052
21479
  const { routerTemplate: routerTemplate2, indexTemplate: indexTemplate2, HOT_TEMPLATE: HOT_TEMPLATE2 } = await Promise.resolve().then(() => (init_templates(), templates_exports));
21053
- const routerPath = path75.join(teamwikiRoot, "router.md");
21054
- const indexPath = path75.join(teamwikiRoot, "index.md");
21480
+ const routerPath = path77.join(teamwikiRoot, "router.md");
21481
+ const indexPath = path77.join(teamwikiRoot, "index.md");
21055
21482
  const projectLink = `[[evidence/code/${slug}/index]]`;
21056
21483
  if (await fs29.pathExists(routerPath)) {
21057
21484
  const router = await fs29.readFile(routerPath, "utf8");
@@ -21077,8 +21504,8 @@ ${aiNarrative}`;
21077
21504
  } else {
21078
21505
  await fs29.writeFile(indexPath, indexTemplate2([{ slug, label: slug }]), "utf8");
21079
21506
  }
21080
- if (!await fs29.pathExists(path75.join(teamwikiRoot, "hot.md"))) {
21081
- await fs29.writeFile(path75.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE2, "utf8");
21507
+ if (!await fs29.pathExists(path77.join(teamwikiRoot, "hot.md"))) {
21508
+ await fs29.writeFile(path77.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE2, "utf8");
21082
21509
  }
21083
21510
  log.info(chalk5.green(`\u2713 teamwiki/ knowledge graph updated: ${slug}`));
21084
21511
  } catch (err) {
@@ -21100,8 +21527,8 @@ ${aiNarrative}`;
21100
21527
  }
21101
21528
  }
21102
21529
  if (!dryRun && !skipEnrich && teamwikiRoot) {
21103
- const evidenceDir = path75.join(teamwikiRoot, "evidence", "code", slug);
21104
- if (await fs29.pathExists(path75.join(evidenceDir, "_manifest.json"))) {
21530
+ const evidenceDir = path77.join(teamwikiRoot, "evidence", "code", slug);
21531
+ if (await fs29.pathExists(path77.join(evidenceDir, "_manifest.json"))) {
21105
21532
  try {
21106
21533
  const { deepEnrich: deepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
21107
21534
  await deepEnrich2({ project: slug, evidenceDir, wikiRoot: teamwikiRoot, cacheDir });
@@ -21220,14 +21647,14 @@ var init_store2 = __esm({
21220
21647
  });
21221
21648
 
21222
21649
  // src/utils/team-codebase-paths.ts
21223
- import path76 from "path";
21650
+ import path78 from "path";
21224
21651
  function getTeamCodebasePaths(cwd, output) {
21225
- const root = output ?? path76.join(cwd, "docs", TEAM_CODEBASE_DIR);
21652
+ const root = output ?? path78.join(cwd, "docs", TEAM_CODEBASE_DIR);
21226
21653
  return {
21227
21654
  root,
21228
- index: path76.join(root, "index.md"),
21229
- domainsDir: path76.join(root, "domains"),
21230
- reposDir: path76.join(root, "repos")
21655
+ index: path78.join(root, "index.md"),
21656
+ domainsDir: path78.join(root, "domains"),
21657
+ reposDir: path78.join(root, "repos")
21231
21658
  };
21232
21659
  }
21233
21660
  function safeDomainSlug(name) {
@@ -21246,12 +21673,12 @@ var init_team_codebase_paths = __esm({
21246
21673
  });
21247
21674
 
21248
21675
  // src/aggregate.ts
21249
- import path77 from "path";
21676
+ import path79 from "path";
21250
21677
  import fs31 from "fs-extra";
21251
- import matter7 from "gray-matter";
21678
+ import matter8 from "gray-matter";
21252
21679
  async function parseRepoMd(filePath, slug) {
21253
21680
  const raw = await fs31.readFile(filePath, "utf8");
21254
- const { data, content } = matter7(raw);
21681
+ const { data, content } = matter8(raw);
21255
21682
  const fm = data;
21256
21683
  const url = typeof fm.repo_url === "string" ? fm.repo_url : typeof fm.url === "string" ? fm.url : "";
21257
21684
  let name = typeof fm.repo_name === "string" ? fm.repo_name : "";
@@ -21293,7 +21720,7 @@ async function regenerateAggregate(opts) {
21293
21720
  for (const file of repoFiles) {
21294
21721
  const slug = file.replace(/\.md$/, "");
21295
21722
  try {
21296
- const summary = await parseRepoMd(path77.join(paths.reposDir, file), slug);
21723
+ const summary = await parseRepoMd(path79.join(paths.reposDir, file), slug);
21297
21724
  repoMap.set(slug, summary);
21298
21725
  } catch {
21299
21726
  }
@@ -21330,7 +21757,7 @@ async function regenerateAggregate(opts) {
21330
21757
  );
21331
21758
  for (const oldFile of existingDomainFiles) {
21332
21759
  if (!newDomainFileNames.has(oldFile)) {
21333
- await fs31.remove(path77.join(paths.domainsDir, oldFile));
21760
+ await fs31.remove(path79.join(paths.domainsDir, oldFile));
21334
21761
  }
21335
21762
  }
21336
21763
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -21339,7 +21766,7 @@ async function regenerateAggregate(opts) {
21339
21766
  const domainEntry = domains.domains.find((d) => d.name === domainName);
21340
21767
  const description = domainEntry?.description ?? "";
21341
21768
  const safeSlug = safeDomainSlug(domainName);
21342
- const outputPath = path77.join(paths.domainsDir, `domain-${safeSlug}.md`);
21769
+ const outputPath = path79.join(paths.domainsDir, `domain-${safeSlug}.md`);
21343
21770
  const tableRows = slugs.map((slug) => {
21344
21771
  const s = repoMap.get(slug);
21345
21772
  if (!s) return "";
@@ -21443,7 +21870,7 @@ var init_aggregate = __esm({
21443
21870
  });
21444
21871
 
21445
21872
  // src/import-repo-list.ts
21446
- import path78 from "path";
21873
+ import path80 from "path";
21447
21874
  function sortByPriority(entries) {
21448
21875
  const order = { high: 0, normal: 1, low: 2 };
21449
21876
  return [...entries].sort((a, b) => {
@@ -21521,7 +21948,7 @@ async function importFromRepoList(opts) {
21521
21948
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21522
21949
  const { localConfig: lc } = await autoDetectInit2();
21523
21950
  const teamRepoPath = lc.repo.localPath;
21524
- const teamwikiRoot = path78.join(teamRepoPath, "teamwiki");
21951
+ const teamwikiRoot = path80.join(teamRepoPath, "teamwiki");
21525
21952
  const { aggregateGlobalGraph: aggregateGlobalGraph2 } = await Promise.resolve().then(() => (init_graph_aggregate(), graph_aggregate_exports));
21526
21953
  await aggregateGlobalGraph2(teamwikiRoot);
21527
21954
  } catch (e) {
@@ -21538,7 +21965,7 @@ async function importFromRepoList(opts) {
21538
21965
  try {
21539
21966
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21540
21967
  const { localConfig: lc } = await autoDetectInit2();
21541
- resolvedOutput = path78.join(lc.repo.localPath, "docs", "team-codebase");
21968
+ resolvedOutput = path80.join(lc.repo.localPath, "docs", "team-codebase");
21542
21969
  domainsBase = lc.repo.localPath;
21543
21970
  } catch {
21544
21971
  }
@@ -21600,9 +22027,9 @@ __export(rebuild_wiki_index_exports, {
21600
22027
  rebuildWikiIndex: () => rebuildWikiIndex
21601
22028
  });
21602
22029
  import { readFile as readFile9, readdir as readdir6, stat as stat4, writeFile as writeFile11 } from "fs/promises";
21603
- import path79 from "path";
22030
+ import path81 from "path";
21604
22031
  async function rebuildWikiIndex(teamwikiRoot) {
21605
- const evidenceCodeDir = path79.join(teamwikiRoot, "evidence", "code");
22032
+ const evidenceCodeDir = path81.join(teamwikiRoot, "evidence", "code");
21606
22033
  if (!await pathExists(evidenceCodeDir)) return;
21607
22034
  const projects = [];
21608
22035
  let totalFacts = 0, totalNodes = 0, totalEdges = 0;
@@ -21610,7 +22037,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21610
22037
  let totalCallChains = 0;
21611
22038
  const dirs = await readdir6(evidenceCodeDir);
21612
22039
  for (const dir of dirs) {
21613
- const dirPath = path79.join(evidenceCodeDir, dir);
22040
+ const dirPath = path81.join(evidenceCodeDir, dir);
21614
22041
  const dirStat = await stat4(dirPath).catch(() => null);
21615
22042
  if (!dirStat?.isDirectory()) continue;
21616
22043
  const info = {
@@ -21623,7 +22050,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21623
22050
  keywords: [],
21624
22051
  domain: ""
21625
22052
  };
21626
- const overviewPath = path79.join(dirPath, "overview.md");
22053
+ const overviewPath = path81.join(dirPath, "overview.md");
21627
22054
  if (await pathExists(overviewPath)) {
21628
22055
  const content = await readFile9(overviewPath, "utf-8");
21629
22056
  const bodyStart = content.indexOf("\n\n", content.indexOf("---", 3));
@@ -21636,7 +22063,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21636
22063
  }
21637
22064
  }
21638
22065
  }
21639
- const projectIndex = path79.join(dirPath, "index.md");
22066
+ const projectIndex = path81.join(dirPath, "index.md");
21640
22067
  if (await pathExists(projectIndex)) {
21641
22068
  const content = await readFile9(projectIndex, "utf-8");
21642
22069
  const factsMatch = content.match(/Facts:\s*(\d+)/);
@@ -21646,7 +22073,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21646
22073
  info.interfaces[m[1]] = (info.interfaces[m[1]] ?? 0) + parseInt(m[2], 10);
21647
22074
  }
21648
22075
  }
21649
- const manifestPath = path79.join(dirPath, "_manifest.json");
22076
+ const manifestPath = path81.join(dirPath, "_manifest.json");
21650
22077
  if (await pathExists(manifestPath)) {
21651
22078
  try {
21652
22079
  const raw = await readFile9(manifestPath, "utf-8");
@@ -21658,7 +22085,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21658
22085
  } catch {
21659
22086
  }
21660
22087
  }
21661
- const domainsPath = path79.join(dirPath, "_domains.json");
22088
+ const domainsPath = path81.join(dirPath, "_domains.json");
21662
22089
  if (await pathExists(domainsPath)) {
21663
22090
  try {
21664
22091
  const raw = await readFile9(domainsPath, "utf-8");
@@ -21675,7 +22102,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21675
22102
  } catch {
21676
22103
  }
21677
22104
  }
21678
- const chainsPath = path79.join(dirPath, "dependency-paths.md");
22105
+ const chainsPath = path81.join(dirPath, "dependency-paths.md");
21679
22106
  if (await pathExists(chainsPath)) {
21680
22107
  const content = await readFile9(chainsPath, "utf-8");
21681
22108
  const chainMatch = content.match(/(\d+)\s*call chain/);
@@ -21691,7 +22118,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21691
22118
  }
21692
22119
  projects.push(info);
21693
22120
  }
21694
- const graphPath = path79.join(teamwikiRoot, ".indices", "graph-index.json");
22121
+ const graphPath = path81.join(teamwikiRoot, ".indices", "graph-index.json");
21695
22122
  if (await pathExists(graphPath)) {
21696
22123
  try {
21697
22124
  const raw = await readFile9(graphPath, "utf-8");
@@ -21732,7 +22159,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21732
22159
  routerLines.push("4. **\u8C03\u7528\u94FE/\u6392\u969C** \u2192 \u67E5\u5BF9\u5E94\u4ED3\u5E93\u7684 dependency-paths.md");
21733
22160
  routerLines.push("5. **\u6A21\u5757\u804C\u8D23\u6982\u8FF0** \u2192 \u67E5 overview.md \u6216 modules/*.md");
21734
22161
  routerLines.push("");
21735
- await writeFile11(path79.join(teamwikiRoot, "router.md"), routerLines.join("\n"), "utf-8");
22162
+ await writeFile11(path81.join(teamwikiRoot, "router.md"), routerLines.join("\n"), "utf-8");
21736
22163
  const indexLines = [
21737
22164
  "# Team Wiki Index",
21738
22165
  "",
@@ -21768,9 +22195,9 @@ async function rebuildWikiIndex(teamwikiRoot) {
21768
22195
  indexLines.push("- [router.md](./router.md) \u2014 \u4EA7\u54C1\u57DF\u8DEF\u7531\uFF08\u8868\u683C + \u8DEF\u7531\u89C4\u5219\uFF09");
21769
22196
  indexLines.push("- [hot.md](./hot.md) \u2014 \u6D3B\u8DC3\u5DE5\u4F5C\u8BB0\u5FC6");
21770
22197
  indexLines.push("");
21771
- await writeFile11(path79.join(teamwikiRoot, "index.md"), indexLines.join("\n"), "utf-8");
21772
- if (!await pathExists(path79.join(teamwikiRoot, "hot.md"))) {
21773
- await writeFile11(path79.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE, "utf-8");
22198
+ await writeFile11(path81.join(teamwikiRoot, "index.md"), indexLines.join("\n"), "utf-8");
22199
+ if (!await pathExists(path81.join(teamwikiRoot, "hot.md"))) {
22200
+ await writeFile11(path81.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE, "utf-8");
21774
22201
  }
21775
22202
  log.debug(`rebuildWikiIndex: ${projects.length} projects, ${totalNodes} nodes, ${totalEdges} edges`);
21776
22203
  }
@@ -21809,7 +22236,7 @@ var init_rebuild_wiki_index = __esm({
21809
22236
  });
21810
22237
 
21811
22238
  // src/import-org.ts
21812
- import path80 from "path";
22239
+ import path82 from "path";
21813
22240
  import fs32 from "fs-extra";
21814
22241
  import { stringify as yamlStringify2 } from "yaml";
21815
22242
  function parseOrgInput(org) {
@@ -21888,9 +22315,9 @@ async function importFromOrg(opts) {
21888
22315
  return;
21889
22316
  }
21890
22317
  log.info(`${filteredRepos.length} repos after filtering, generating whitelist...`);
21891
- const whitelistDraftPath = path80.join(cwd, WHITELIST_DRAFT_PATH);
22318
+ const whitelistDraftPath = path82.join(cwd, WHITELIST_DRAFT_PATH);
21892
22319
  if (!opts.dryRun) {
21893
- await fs32.ensureDir(path80.dirname(whitelistDraftPath));
22320
+ await fs32.ensureDir(path82.dirname(whitelistDraftPath));
21894
22321
  const lines = ["version: 1", "repos:"];
21895
22322
  for (const repo of filteredRepos) {
21896
22323
  lines.push(` - url: ${repo.url}`);
@@ -21920,8 +22347,8 @@ async function importFromOrg(opts) {
21920
22347
  );
21921
22348
  try {
21922
22349
  const { rebuildWikiIndex: rebuildWikiIndex2 } = await Promise.resolve().then(() => (init_rebuild_wiki_index(), rebuild_wiki_index_exports));
21923
- const teamRepoPath = path80.join(cwd, ".teamai", "team-repo");
21924
- const teamRepoWiki = path80.join(teamRepoPath, "teamwiki");
22350
+ const teamRepoPath = path82.join(cwd, ".teamai", "team-repo");
22351
+ const teamRepoWiki = path82.join(teamRepoPath, "teamwiki");
21925
22352
  if (await fs32.pathExists(teamRepoWiki)) {
21926
22353
  await rebuildWikiIndex2(teamRepoWiki);
21927
22354
  log.info("teamwiki router.md / index.md rebuilt");
@@ -21963,7 +22390,7 @@ var init_import_org = __esm({
21963
22390
  });
21964
22391
 
21965
22392
  // src/iwiki-dual.ts
21966
- import path81 from "path";
22393
+ import path83 from "path";
21967
22394
  import fs33 from "fs-extra";
21968
22395
  function parseIWikiInput2(input) {
21969
22396
  const trimmed = input.trim();
@@ -22128,10 +22555,10 @@ async function importFromIWikiDual(opts) {
22128
22555
  return { sectionsUpdated: [], pendingReview: false };
22129
22556
  }
22130
22557
  const paths = getTeamCodebasePaths(cwd, opts.output);
22131
- const filePath = path81.join(paths.root, "external-knowledge.md");
22558
+ const filePath = path83.join(paths.root, "external-knowledge.md");
22132
22559
  if (opts.requireReview) {
22133
22560
  if (!opts.dryRun) {
22134
- const relativeFilePath = path81.relative(cwd, filePath);
22561
+ const relativeFilePath = path83.relative(cwd, filePath);
22135
22562
  for (const sectionKey of sections) {
22136
22563
  const body = aiOutput[sectionKey] ?? "";
22137
22564
  if (!body) continue;
@@ -22198,7 +22625,7 @@ var import_exports = {};
22198
22625
  __export(import_exports, {
22199
22626
  importCmd: () => importCmd
22200
22627
  });
22201
- import path82 from "path";
22628
+ import path84 from "path";
22202
22629
  import os5 from "os";
22203
22630
  import fs34 from "fs-extra";
22204
22631
  import { Listr, PRESET_TIMER } from "listr2";
@@ -22316,7 +22743,7 @@ async function importCmd(opts) {
22316
22743
  task: async (ctx) => {
22317
22744
  const { learning, repoUrl } = await importFromMR({
22318
22745
  url: opts.fromMr,
22319
- learningsDir: path82.join(localConfig.repo.localPath, "learnings"),
22746
+ learningsDir: path84.join(localConfig.repo.localPath, "learnings"),
22320
22747
  all: opts.all,
22321
22748
  outputDir: opts.output,
22322
22749
  repoPath: opts.dryRun ? void 0 : localConfig.repo.localPath,
@@ -22330,7 +22757,7 @@ async function importCmd(opts) {
22330
22757
  title: "Incremental teamwiki update",
22331
22758
  skip: (ctx) => !ctx.repoUrl || !!opts.dryRun || !!opts.output,
22332
22759
  task: async (ctx, task) => {
22333
- const teamwikiRoot = path82.join(localConfig.repo.localPath, "teamwiki");
22760
+ const teamwikiRoot = path84.join(localConfig.repo.localPath, "teamwiki");
22334
22761
  try {
22335
22762
  const { detectProvider: detectProvider2, getProvider: getProvider2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
22336
22763
  const { getRepoSlug: getRepoSlug2 } = await Promise.resolve().then(() => (init_repo_cache(), repo_cache_exports));
@@ -22338,7 +22765,7 @@ async function importCmd(opts) {
22338
22765
  const provider = getProvider2(providerName);
22339
22766
  const repoInfo = provider.parseRepoInput(ctx.repoUrl);
22340
22767
  const slug = getRepoSlug2(providerName, repoInfo.owner, repoInfo.repo);
22341
- const evidenceDir = path82.join(teamwikiRoot, "evidence", "code", slug);
22768
+ const evidenceDir = path84.join(teamwikiRoot, "evidence", "code", slug);
22342
22769
  if (await fs34.pathExists(evidenceDir)) {
22343
22770
  task.output = `Updating ${slug}...`;
22344
22771
  await importFromRepo({
@@ -22383,18 +22810,18 @@ async function importCmd(opts) {
22383
22810
  setSilent(false);
22384
22811
  }
22385
22812
  } else if (opts.dir) {
22386
- const dirPath = path82.resolve(opts.dir);
22813
+ const dirPath = path84.resolve(opts.dir);
22387
22814
  if (!await fs34.pathExists(dirPath)) {
22388
22815
  throw new Error(`Directory not found: ${dirPath}`);
22389
22816
  }
22390
- const slug = path82.basename(dirPath);
22817
+ const slug = path84.basename(dirPath);
22391
22818
  log.info(`Scanning local directory: ${dirPath} (project: ${slug})`);
22392
22819
  if (opts.dryRun) {
22393
22820
  log.info(`[dry-run] skipping code extraction, no action taken`);
22394
22821
  log.success(`Local directory ${slug} import complete (dry-run)`);
22395
22822
  return;
22396
22823
  }
22397
- const tmpExtractDir = await fs34.mkdtemp(path82.join(os5.tmpdir(), "teamai-extract-"));
22824
+ const tmpExtractDir = await fs34.mkdtemp(path84.join(os5.tmpdir(), "teamai-extract-"));
22398
22825
  try {
22399
22826
  const { extractCodebase: extractCodebase2 } = await Promise.resolve().then(() => (init_codebase_extract(), codebase_extract_exports));
22400
22827
  await extractCodebase2({
@@ -22404,9 +22831,9 @@ async function importCmd(opts) {
22404
22831
  skipEnrich: opts.skipEnrich ?? false,
22405
22832
  outputRoot: tmpExtractDir
22406
22833
  });
22407
- const srcWiki = path82.join(tmpExtractDir, "teamwiki");
22834
+ const srcWiki = path84.join(tmpExtractDir, "teamwiki");
22408
22835
  if (opts.output) {
22409
- const outputWiki = path82.join(opts.output, "teamwiki");
22836
+ const outputWiki = path84.join(opts.output, "teamwiki");
22410
22837
  if (await fs34.pathExists(srcWiki)) {
22411
22838
  await fs34.copy(srcWiki, outputWiki, { overwrite: true });
22412
22839
  log.info(`Output written: ${outputWiki}`);
@@ -22414,19 +22841,19 @@ async function importCmd(opts) {
22414
22841
  } else {
22415
22842
  const { localConfig } = await autoDetectInit();
22416
22843
  const teamRepoPath = localConfig.repo.localPath;
22417
- const teamwikiRoot = path82.join(teamRepoPath, "teamwiki");
22844
+ const teamwikiRoot = path84.join(teamRepoPath, "teamwiki");
22418
22845
  if (await fs34.pathExists(srcWiki)) {
22419
- const evidenceSrc = path82.join(srcWiki, "evidence", "code", slug);
22420
- const evidenceDest = path82.join(teamwikiRoot, "evidence", "code", slug);
22846
+ const evidenceSrc = path84.join(srcWiki, "evidence", "code", slug);
22847
+ const evidenceDest = path84.join(teamwikiRoot, "evidence", "code", slug);
22421
22848
  if (await fs34.pathExists(evidenceSrc)) {
22422
- await fs34.ensureDir(path82.dirname(evidenceDest));
22849
+ await fs34.ensureDir(path84.dirname(evidenceDest));
22423
22850
  await fs34.copy(evidenceSrc, evidenceDest, { overwrite: true });
22424
22851
  }
22425
- const srcGraph = path82.join(srcWiki, ".indices", "graph-index.json");
22852
+ const srcGraph = path84.join(srcWiki, ".indices", "graph-index.json");
22426
22853
  if (await fs34.pathExists(srcGraph)) {
22427
- const destGraphDir = path82.join(evidenceDest, ".indices");
22854
+ const destGraphDir = path84.join(evidenceDest, ".indices");
22428
22855
  await fs34.ensureDir(destGraphDir);
22429
- await fs34.copy(srcGraph, path82.join(destGraphDir, "graph-index.json"), { overwrite: true });
22856
+ await fs34.copy(srcGraph, path84.join(destGraphDir, "graph-index.json"), { overwrite: true });
22430
22857
  }
22431
22858
  log.info(`teamwiki/ knowledge graph updated: ${slug}`);
22432
22859
  }
@@ -22492,12 +22919,12 @@ __export(mr_hint_exports, {
22492
22919
  });
22493
22920
  import { spawnSync as spawnSync3 } from "child_process";
22494
22921
  import fs35 from "fs";
22495
- import path83 from "path";
22922
+ import path85 from "path";
22496
22923
  function repoSlug(owner, repo) {
22497
22924
  return `${owner}/${repo}`.replace(/[^a-zA-Z0-9_-]/g, "_");
22498
22925
  }
22499
22926
  function getCachePath2(owner, repo) {
22500
- return path83.join(
22927
+ return path85.join(
22501
22928
  process.env.HOME ?? "",
22502
22929
  ".teamai",
22503
22930
  "sessions",
@@ -22520,7 +22947,7 @@ function loadCache(owner, repo) {
22520
22947
  function saveCache(owner, repo, cache) {
22521
22948
  try {
22522
22949
  const cachePath = getCachePath2(owner, repo);
22523
- const dir = path83.dirname(cachePath);
22950
+ const dir = path85.dirname(cachePath);
22524
22951
  if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
22525
22952
  fs35.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
22526
22953
  } catch {
@@ -22667,7 +23094,7 @@ function buildHintMessage2(mrs) {
22667
23094
  async function computeMrHintOutput() {
22668
23095
  if (process.env.TEAMAI_MR_HINT_DISABLED === "1") return null;
22669
23096
  const rawCwd = process.env.TEAMAI_MR_HINT_CWD ?? process.cwd();
22670
- const cwd = path83.resolve(rawCwd);
23097
+ const cwd = path85.resolve(rawCwd);
22671
23098
  try {
22672
23099
  if (!fs35.statSync(cwd).isDirectory()) {
22673
23100
  return null;
@@ -22735,10 +23162,10 @@ var init_mr_hint = __esm({
22735
23162
  });
22736
23163
 
22737
23164
  // src/codebase-lint.ts
22738
- import path84 from "path";
23165
+ import path86 from "path";
22739
23166
  import os6 from "os";
22740
23167
  import fs36 from "fs-extra";
22741
- import matter8 from "gray-matter";
23168
+ import matter9 from "gray-matter";
22742
23169
  import chalk6 from "chalk";
22743
23170
  function severityAtLeast(issue, threshold) {
22744
23171
  return SEVERITY_ORDER[issue] >= SEVERITY_ORDER[threshold];
@@ -22946,7 +23373,7 @@ async function lintTeamCodebase(opts) {
22946
23373
  fixable: false
22947
23374
  });
22948
23375
  }
22949
- const whitelistPath = path84.join(opts.cwd, ".teamai", "repo-whitelist.yaml");
23376
+ const whitelistPath = path86.join(opts.cwd, ".teamai", "repo-whitelist.yaml");
22950
23377
  let whitelistData = null;
22951
23378
  try {
22952
23379
  whitelistData = await loadRepoList(whitelistPath);
@@ -22983,7 +23410,7 @@ async function lintTeamCodebase(opts) {
22983
23410
  for (const url of allDomainUrls) {
22984
23411
  const slug = urlToSlugMap.get(url);
22985
23412
  if (!slug) continue;
22986
- const mdPath = path84.join(paths.reposDir, `${slug}.md`);
23413
+ const mdPath = path86.join(paths.reposDir, `${slug}.md`);
22987
23414
  if (!await fs36.pathExists(mdPath)) {
22988
23415
  issues.push({
22989
23416
  severity: "high",
@@ -23011,13 +23438,13 @@ async function lintTeamCodebase(opts) {
23011
23438
  }
23012
23439
  }
23013
23440
  }
23014
- const cacheRoot = path84.join(os6.homedir(), ".teamai", "cache", "repos");
23441
+ const cacheRoot = path86.join(os6.homedir(), ".teamai", "cache", "repos");
23015
23442
  const cacheExists = await fs36.pathExists(cacheRoot);
23016
23443
  for (const mdFile of repoMdFiles) {
23017
- const mdPath = path84.join(paths.reposDir, mdFile);
23444
+ const mdPath = path86.join(paths.reposDir, mdFile);
23018
23445
  try {
23019
23446
  const content = await fs36.readFile(mdPath, "utf8");
23020
- const parsed = matter8(content);
23447
+ const parsed = matter9(content);
23021
23448
  const fm = parsed.data;
23022
23449
  issues.push(...checkRepoFrontmatterRequired(fm, `docs/team-codebase/repos/${mdFile}`));
23023
23450
  const source = fm["source"];
@@ -23071,10 +23498,10 @@ async function lintTeamCodebase(opts) {
23071
23498
  }
23072
23499
  }
23073
23500
  for (const domFile of domainMdFiles) {
23074
- const domPath = path84.join(paths.domainsDir, domFile);
23501
+ const domPath = path86.join(paths.domainsDir, domFile);
23075
23502
  try {
23076
23503
  const content = await fs36.readFile(domPath, "utf8");
23077
- const parsed = matter8(content);
23504
+ const parsed = matter9(content);
23078
23505
  const fm = parsed.data;
23079
23506
  issues.push(
23080
23507
  ...checkDomainFrontmatterRequired(fm, `docs/team-codebase/domains/${domFile}`)
@@ -23128,7 +23555,7 @@ async function lintTeamCodebase(opts) {
23128
23555
  scanned.indexFile = true;
23129
23556
  try {
23130
23557
  const content = await fs36.readFile(paths.index, "utf8");
23131
- const parsed = matter8(content);
23558
+ const parsed = matter9(content);
23132
23559
  const fm = parsed.data;
23133
23560
  issues.push(...checkIndexFrontmatterRequired(fm, "docs/team-codebase/index.md"));
23134
23561
  const fmRepoCnt = Number(fm["repo_count"] ?? -1);
@@ -23165,7 +23592,7 @@ async function lintTeamCodebase(opts) {
23165
23592
  });
23166
23593
  }
23167
23594
  }
23168
- const extKnowledgePath = path84.join(paths.root, "external-knowledge.md");
23595
+ const extKnowledgePath = path86.join(paths.root, "external-knowledge.md");
23169
23596
  if (await fs36.pathExists(extKnowledgePath)) {
23170
23597
  scanned.externalKnowledgeFile = true;
23171
23598
  try {
@@ -23220,7 +23647,7 @@ async function lintTeamCodebase(opts) {
23220
23647
  }
23221
23648
  }
23222
23649
  }
23223
- const pendingPath = path84.join(opts.cwd, ".teamai", "pending-review.jsonl");
23650
+ const pendingPath = path86.join(opts.cwd, ".teamai", "pending-review.jsonl");
23224
23651
  if (await fs36.pathExists(pendingPath)) {
23225
23652
  try {
23226
23653
  const content = await fs36.readFile(pendingPath, "utf8");
@@ -23238,7 +23665,7 @@ async function lintTeamCodebase(opts) {
23238
23665
  } catch {
23239
23666
  }
23240
23667
  }
23241
- const sourceMarksPath = path84.join(opts.cwd, ".teamai", "source-marks.jsonl");
23668
+ const sourceMarksPath = path86.join(opts.cwd, ".teamai", "source-marks.jsonl");
23242
23669
  if (await fs36.pathExists(sourceMarksPath)) {
23243
23670
  try {
23244
23671
  const content = await fs36.readFile(sourceMarksPath, "utf8");
@@ -23316,10 +23743,10 @@ async function fixTeamCodebase(opts) {
23316
23743
  continue;
23317
23744
  }
23318
23745
  if (issue.category === "orphan-md") {
23319
- const mdFile = path84.basename(issue.location);
23320
- const srcPath = path84.join(paths.reposDir, mdFile);
23321
- const archivedDir = path84.join(paths.reposDir, ".archived");
23322
- const dstPath = path84.join(archivedDir, mdFile);
23746
+ const mdFile = path86.basename(issue.location);
23747
+ const srcPath = path86.join(paths.reposDir, mdFile);
23748
+ const archivedDir = path86.join(paths.reposDir, ".archived");
23749
+ const dstPath = path86.join(archivedDir, mdFile);
23323
23750
  if (opts.dryRun) {
23324
23751
  applied.push({
23325
23752
  category: issue.category,
@@ -23345,7 +23772,7 @@ async function fixTeamCodebase(opts) {
23345
23772
  }
23346
23773
  } else if (issue.category === "frontmatter-missing") {
23347
23774
  const relPath = issue.location;
23348
- const absPath = path84.join(opts.cwd, relPath);
23775
+ const absPath = path86.join(opts.cwd, relPath);
23349
23776
  if (opts.dryRun) {
23350
23777
  applied.push({
23351
23778
  category: issue.category,
@@ -23355,10 +23782,10 @@ async function fixTeamCodebase(opts) {
23355
23782
  } else {
23356
23783
  try {
23357
23784
  const content = await fs36.readFile(absPath, "utf8");
23358
- const parsed = matter8(content);
23785
+ const parsed = matter9(content);
23359
23786
  if (!parsed.data["schemaVersion"]) {
23360
23787
  parsed.data["schemaVersion"] = 1;
23361
- const newContent = matter8.stringify(parsed.content, parsed.data);
23788
+ const newContent = matter9.stringify(parsed.content, parsed.data);
23362
23789
  await fs36.writeFile(absPath, newContent, "utf8");
23363
23790
  applied.push({
23364
23791
  category: issue.category,
@@ -23394,14 +23821,14 @@ async function fixTeamCodebase(opts) {
23394
23821
  } else {
23395
23822
  try {
23396
23823
  const indexContent = await fs36.readFile(paths.index, "utf8");
23397
- const parsed = matter8(indexContent);
23824
+ const parsed = matter9(indexContent);
23398
23825
  const repoFiles = await fs36.readdir(paths.reposDir).catch(() => []);
23399
23826
  const domainFiles = await fs36.readdir(paths.domainsDir).catch(() => []);
23400
23827
  const repoCnt = repoFiles.filter((f) => f.endsWith(".md")).length;
23401
23828
  const domainCnt = domainFiles.filter((f) => f.endsWith(".md")).length;
23402
23829
  parsed.data["repo_count"] = repoCnt;
23403
23830
  parsed.data["domain_count"] = domainCnt;
23404
- const newContent = matter8.stringify(parsed.content, parsed.data);
23831
+ const newContent = matter9.stringify(parsed.content, parsed.data);
23405
23832
  await fs36.writeFile(paths.index, newContent, "utf8");
23406
23833
  applied.push({
23407
23834
  category: issue.category,
@@ -23444,11 +23871,11 @@ __export(codebase_upgrade_wiki_exports, {
23444
23871
  upgradeCodebaseWiki: () => upgradeCodebaseWiki
23445
23872
  });
23446
23873
  import { readdir as readdir7, readFile as readFile10 } from "fs/promises";
23447
- import path85 from "path";
23874
+ import path87 from "path";
23448
23875
  import chalk7 from "chalk";
23449
- import matter9 from "gray-matter";
23876
+ import matter10 from "gray-matter";
23450
23877
  async function upgradeCodebaseWiki(opts) {
23451
- const teamCodebaseDir = path85.join(opts.cwd, "docs", "team-codebase", "repos");
23878
+ const teamCodebaseDir = path87.join(opts.cwd, "docs", "team-codebase", "repos");
23452
23879
  if (!await pathExists(teamCodebaseDir)) {
23453
23880
  if (opts.json) {
23454
23881
  console.log(JSON.stringify({ status: "nothing-to-migrate", reason: "docs/team-codebase/repos/ not found" }));
@@ -23473,10 +23900,10 @@ async function upgradeCodebaseWiki(opts) {
23473
23900
  const result = { migrated: [], skipped: [], errors: [] };
23474
23901
  for (const file of mdFiles) {
23475
23902
  const slug = file.replace(".md", "");
23476
- const filePath = path85.join(teamCodebaseDir, file);
23903
+ const filePath = path87.join(teamCodebaseDir, file);
23477
23904
  try {
23478
23905
  const content = await readFile10(filePath, "utf-8");
23479
- const parsed = matter9(content);
23906
+ const parsed = matter10(content);
23480
23907
  const source = parsed.data["source"] ?? parsed.data["repo_url"];
23481
23908
  if (!source) {
23482
23909
  result.skipped.push(`${slug}: \u65E0 source/repo_url \u5B57\u6BB5`);
@@ -23486,9 +23913,9 @@ async function upgradeCodebaseWiki(opts) {
23486
23913
  result.migrated.push(`${slug} \u2192 teamwiki/evidence/code/${slug}/`);
23487
23914
  continue;
23488
23915
  }
23489
- const cacheBase = path85.join(process.env["HOME"] ?? "", ".teamai", "cache", "repos");
23916
+ const cacheBase = path87.join(process.env["HOME"] ?? "", ".teamai", "cache", "repos");
23490
23917
  const urlParts = String(source).replace(/^https?:\/\//, "").replace(/@.*$/, "").split("/");
23491
- const cachePath = path85.join(cacheBase, ...urlParts.slice(0, 3));
23918
+ const cachePath = path87.join(cacheBase, ...urlParts.slice(0, 3));
23492
23919
  if (await pathExists(cachePath)) {
23493
23920
  await extractCodebase({ path: cachePath, project: slug });
23494
23921
  result.migrated.push(slug);
@@ -23543,10 +23970,10 @@ __export(codebase_wiki_lint_exports, {
23543
23970
  lintTeamwiki: () => lintTeamwiki
23544
23971
  });
23545
23972
  import { readFile as readFile11, readdir as readdir8, stat as stat5 } from "fs/promises";
23546
- import path86 from "path";
23973
+ import path88 from "path";
23547
23974
  import chalk8 from "chalk";
23548
23975
  async function lintTeamwiki(opts) {
23549
- const wikiRoot = opts.wikiRoot ?? path86.join(opts.cwd ?? process.cwd(), "teamwiki");
23976
+ const wikiRoot = opts.wikiRoot ?? path88.join(opts.cwd ?? process.cwd(), "teamwiki");
23550
23977
  const issues = [];
23551
23978
  const minSeverity = opts.severity ?? "info";
23552
23979
  const severityOrder = ["info", "low", "medium", "high"];
@@ -23556,7 +23983,7 @@ async function lintTeamwiki(opts) {
23556
23983
  issues.push(issue);
23557
23984
  }
23558
23985
  }
23559
- const graphPath = path86.join(wikiRoot, ".indices", "graph-index.json");
23986
+ const graphPath = path88.join(wikiRoot, ".indices", "graph-index.json");
23560
23987
  let graph = null;
23561
23988
  if (!await pathExists(graphPath)) {
23562
23989
  addIssue({
@@ -23578,7 +24005,7 @@ async function lintTeamwiki(opts) {
23578
24005
  });
23579
24006
  }
23580
24007
  }
23581
- const evidenceDir = path86.join(wikiRoot, "evidence", "code");
24008
+ const evidenceDir = path88.join(wikiRoot, "evidence", "code");
23582
24009
  if (!await pathExists(evidenceDir)) {
23583
24010
  addIssue({
23584
24011
  severity: "high",
@@ -23597,7 +24024,7 @@ async function lintTeamwiki(opts) {
23597
24024
  });
23598
24025
  }
23599
24026
  for (const project of projects) {
23600
- const projectDir = path86.join(evidenceDir, project);
24027
+ const projectDir = path88.join(evidenceDir, project);
23601
24028
  const pStat = await stat5(projectDir).catch(() => null);
23602
24029
  if (!pStat?.isDirectory()) {
23603
24030
  if (!pStat) {
@@ -23617,7 +24044,7 @@ async function lintTeamwiki(opts) {
23617
24044
  }
23618
24045
  }
23619
24046
  for (const navFile of ["router.md", "index.md", "hot.md"]) {
23620
- if (!await pathExists(path86.join(wikiRoot, navFile))) {
24047
+ if (!await pathExists(path88.join(wikiRoot, navFile))) {
23621
24048
  addIssue({
23622
24049
  severity: "low",
23623
24050
  category: "nav-missing",
@@ -23626,7 +24053,7 @@ async function lintTeamwiki(opts) {
23626
24053
  });
23627
24054
  }
23628
24055
  }
23629
- const manifestPath = path86.join(wikiRoot, "source-manifest.json");
24056
+ const manifestPath = path88.join(wikiRoot, "source-manifest.json");
23630
24057
  if (!await pathExists(manifestPath)) {
23631
24058
  addIssue({
23632
24059
  severity: "low",
@@ -23742,7 +24169,7 @@ var codebase_cmd_exports = {};
23742
24169
  __export(codebase_cmd_exports, {
23743
24170
  codebaseCmd: () => codebaseCmd
23744
24171
  });
23745
- import path87 from "path";
24172
+ import path89 from "path";
23746
24173
  import chalk9 from "chalk";
23747
24174
  function formatFixResult(result) {
23748
24175
  const lines = [];
@@ -23801,9 +24228,9 @@ async function codebaseCmd(opts) {
23801
24228
  try {
23802
24229
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
23803
24230
  const { localConfig: lc } = await autoDetectInit2();
23804
- teamwikiDir = path87.join(lc.repo.localPath, "teamwiki");
24231
+ teamwikiDir = path89.join(lc.repo.localPath, "teamwiki");
23805
24232
  } catch {
23806
- teamwikiDir = path87.join(cwd, ".teamai", "team-repo", "teamwiki");
24233
+ teamwikiDir = path89.join(cwd, ".teamai", "team-repo", "teamwiki");
23807
24234
  }
23808
24235
  if (await pathExists3(teamwikiDir)) {
23809
24236
  const { lintTeamwiki: lintTeamwiki2, formatWikiLintReport: formatWikiLintReport2 } = await Promise.resolve().then(() => (init_codebase_wiki_lint(), codebase_wiki_lint_exports));
@@ -23940,7 +24367,7 @@ var review_cmd_exports = {};
23940
24367
  __export(review_cmd_exports, {
23941
24368
  reviewCmd: () => reviewCmd
23942
24369
  });
23943
- import path88 from "path";
24370
+ import path90 from "path";
23944
24371
  import chalk10 from "chalk";
23945
24372
  import fs37 from "fs-extra";
23946
24373
  function riskAtMost(itemRisk, ceiling) {
@@ -24016,7 +24443,7 @@ async function applyOne(cwd, item) {
24016
24443
  if (!section) {
24017
24444
  return { ok: false, reason: "target.section \u7F3A\u5931" };
24018
24445
  }
24019
- const filePath = path88.isAbsolute(file) ? file : path88.join(cwd, file);
24446
+ const filePath = path90.isAbsolute(file) ? file : path90.join(cwd, file);
24020
24447
  if (!await fs37.pathExists(filePath)) {
24021
24448
  return { ok: false, reason: `\u76EE\u6807\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${filePath}` };
24022
24449
  }
@@ -24457,9 +24884,93 @@ var init_hook_output = __esm({
24457
24884
  }
24458
24885
  });
24459
24886
 
24887
+ // src/transcript-parser.ts
24888
+ var transcript_parser_exports = {};
24889
+ __export(transcript_parser_exports, {
24890
+ parseTranscriptForVotes: () => parseTranscriptForVotes
24891
+ });
24892
+ import fs38 from "fs";
24893
+ import path91 from "path";
24894
+ import readline5 from "readline";
24895
+ async function parseTranscriptForVotes(transcriptPath) {
24896
+ const recalledSet = /* @__PURE__ */ new Set();
24897
+ const referencedSet = /* @__PURE__ */ new Set();
24898
+ try {
24899
+ const stat6 = await fs38.promises.stat(transcriptPath);
24900
+ if (stat6.size === 0) return { recalledDocIds: [], referencedDocIds: [] };
24901
+ } catch {
24902
+ return { recalledDocIds: [], referencedDocIds: [] };
24903
+ }
24904
+ const rl = readline5.createInterface({
24905
+ input: fs38.createReadStream(transcriptPath, { encoding: "utf-8" }),
24906
+ crlfDelay: Infinity
24907
+ });
24908
+ for await (const line of rl) {
24909
+ const trimmed = line.trim();
24910
+ if (!trimmed || !trimmed.includes('"assistant"')) continue;
24911
+ let entry;
24912
+ try {
24913
+ entry = JSON.parse(trimmed);
24914
+ } catch {
24915
+ continue;
24916
+ }
24917
+ if (entry["type"] !== "assistant") continue;
24918
+ const message = entry["message"];
24919
+ if (!message || !Array.isArray(message["content"])) continue;
24920
+ for (const block of message["content"]) {
24921
+ if (block["type"] !== "text") continue;
24922
+ const text = block["text"];
24923
+ if (typeof text !== "string") continue;
24924
+ extractRecalledDocIds(text, recalledSet);
24925
+ extractReferencedDocIds(text, referencedSet);
24926
+ }
24927
+ }
24928
+ return {
24929
+ recalledDocIds: [...recalledSet],
24930
+ referencedDocIds: [...referencedSet]
24931
+ };
24932
+ }
24933
+ function extractRecalledDocIds(text, out) {
24934
+ const START = "--- [teamai:recall:start] ---";
24935
+ const END = "--- [teamai:recall:end] ---";
24936
+ const filePattern = /^File:\s*(.+)$/gm;
24937
+ let searchFrom = 0;
24938
+ while (true) {
24939
+ const startIdx = text.indexOf(START, searchFrom);
24940
+ if (startIdx === -1) break;
24941
+ const endIdx = text.indexOf(END, startIdx + START.length);
24942
+ if (endIdx === -1) break;
24943
+ const region = text.slice(startIdx + START.length, endIdx);
24944
+ filePattern.lastIndex = 0;
24945
+ let match;
24946
+ while ((match = filePattern.exec(region)) !== null) {
24947
+ const filePath = match[1].trim();
24948
+ const docId = path91.basename(filePath).replace(/\.md$/i, "");
24949
+ out.add(docId);
24950
+ }
24951
+ searchFrom = endIdx + END.length;
24952
+ }
24953
+ }
24954
+ function extractReferencedDocIds(text, out) {
24955
+ const pattern = /<!--\s*teamai:referenced-doc-ids:\s*\[([^\]]*)\]\s*-->/g;
24956
+ let match;
24957
+ while ((match = pattern.exec(text)) !== null) {
24958
+ const raw = match[1];
24959
+ for (const item of raw.split(",")) {
24960
+ const docId = item.trim().replace(/^['"]|['"]$/g, "");
24961
+ if (docId) out.add(docId);
24962
+ }
24963
+ }
24964
+ }
24965
+ var init_transcript_parser = __esm({
24966
+ "src/transcript-parser.ts"() {
24967
+ "use strict";
24968
+ }
24969
+ });
24970
+
24460
24971
  // src/machine-id.ts
24461
24972
  import crypto4 from "crypto";
24462
- import fs38 from "fs";
24973
+ import fs39 from "fs";
24463
24974
  import { execFileSync as execFileSync4 } from "child_process";
24464
24975
  function getMachineId() {
24465
24976
  if (cachedMachineId !== null) return cachedMachineId;
@@ -24500,7 +25011,7 @@ function readWindowsMachineId() {
24500
25011
  function readLinuxMachineId() {
24501
25012
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
24502
25013
  try {
24503
- const content = fs38.readFileSync(p, "utf-8").trim();
25014
+ const content = fs39.readFileSync(p, "utf-8").trim();
24504
25015
  if (content) return content;
24505
25016
  } catch {
24506
25017
  }
@@ -24525,7 +25036,7 @@ var init_machine_id = __esm({
24525
25036
  // src/agent-version.ts
24526
25037
  import { execFile as execFile2 } from "child_process";
24527
25038
  import { readFile as readFile12 } from "fs/promises";
24528
- import path89 from "path";
25039
+ import path92 from "path";
24529
25040
  async function execVersion(bin, args = ["--version"]) {
24530
25041
  return new Promise((resolve) => {
24531
25042
  execFile2(bin, args, { timeout: 5e3 }, (err, stdout) => {
@@ -24538,7 +25049,7 @@ async function execVersion(bin, args = ["--version"]) {
24538
25049
  });
24539
25050
  }
24540
25051
  async function readPlistVersion(appPath) {
24541
- const plistPath = path89.join(appPath, "Contents", "Info.plist");
25052
+ const plistPath = path92.join(appPath, "Contents", "Info.plist");
24542
25053
  try {
24543
25054
  const content = await readFile12(plistPath, "utf-8");
24544
25055
  const match = content.match(
@@ -24622,7 +25133,7 @@ __export(status_report_exports, {
24622
25133
  scanReportableSkills: () => scanReportableSkills
24623
25134
  });
24624
25135
  import os7 from "os";
24625
- import path90 from "path";
25136
+ import path93 from "path";
24626
25137
  import YAML17 from "yaml";
24627
25138
  function resolveEndpoints() {
24628
25139
  const raw = process.env.TEAMAI_REPORT_PATHS;
@@ -24668,7 +25179,7 @@ async function scanReportableSkills(skillsDir) {
24668
25179
  const dirs = (await listDirs(skillsDir)).filter((slug) => !slug.startsWith("."));
24669
25180
  const skills = await Promise.all(
24670
25181
  dirs.map(async (slug) => {
24671
- const skillMd = path90.join(skillsDir, slug, "SKILL.md");
25182
+ const skillMd = path93.join(skillsDir, slug, "SKILL.md");
24672
25183
  if (!await pathExists(skillMd)) return null;
24673
25184
  const meta = await readSkillMeta(skillMd);
24674
25185
  return { slug, version: meta.version, display_name: meta.name || slug };
@@ -24677,11 +25188,11 @@ async function scanReportableSkills(skillsDir) {
24677
25188
  return skills.filter((s) => s !== null).sort((a, b) => a.slug.localeCompare(b.slug));
24678
25189
  }
24679
25190
  function queuePath() {
24680
- return path90.join(process.env.HOME ?? "", ".teamai", "reporter", "queue.jsonl");
25191
+ return path93.join(process.env.HOME ?? "", ".teamai", "reporter", "queue.jsonl");
24681
25192
  }
24682
25193
  async function enqueue(req) {
24683
25194
  const p = queuePath();
24684
- await ensureDir(path90.dirname(p));
25195
+ await ensureDir(path93.dirname(p));
24685
25196
  const fse9 = await import("fs-extra");
24686
25197
  await fse9.default.appendFile(p, JSON.stringify(req) + "\n", "utf-8");
24687
25198
  }
@@ -24755,8 +25266,8 @@ async function runStatusReportInner(opts) {
24755
25266
  return;
24756
25267
  }
24757
25268
  const baseDir = resolveBaseDir(localConfig);
24758
- const skillsDir = path90.join(baseDir, skillsRel);
24759
- const installPath = path90.join(baseDir, path90.dirname(skillsRel));
25269
+ const skillsDir = path93.join(baseDir, skillsRel);
25270
+ const installPath = path93.join(baseDir, path93.dirname(skillsRel));
24760
25271
  const machineId = getMachineId();
24761
25272
  const localAgentId = deriveLocalAgentId(agentType, machineId, installPath);
24762
25273
  const endpoints = resolveEndpoints();
@@ -24863,6 +25374,7 @@ var init_status_report = __esm({
24863
25374
  });
24864
25375
 
24865
25376
  // src/hook-handlers.ts
25377
+ import path94 from "path";
24866
25378
  function buildHandlerRegistry() {
24867
25379
  return [
24868
25380
  // ─── SessionStart ─────────────────────────────────
@@ -24873,6 +25385,7 @@ function buildHandlerRegistry() {
24873
25385
  // ─── Stop ─────────────────────────────────────────
24874
25386
  { event: "stop", matcher: "*", handler: updateHandler, timeoutMs: UPDATE_TIMEOUT_MS },
24875
25387
  { event: "stop", matcher: "*", handler: contributeCheckHandler, timeoutMs: CONTRIBUTE_CHECK_TIMEOUT_MS },
25388
+ { event: "stop", matcher: "*", handler: votesSyncHandler, timeoutMs: VOTES_SYNC_TIMEOUT_MS },
24876
25389
  { event: "stop", matcher: "*", handler: dashboardReportHandler, timeoutMs: DASHBOARD_TIMEOUT_MS },
24877
25390
  // ─── PostToolUse ──────────────────────────────────
24878
25391
  { event: "post-tool-use", matcher: "*", handler: dashboardReportHandler, timeoutMs: DASHBOARD_TIMEOUT_MS },
@@ -24884,7 +25397,7 @@ function buildHandlerRegistry() {
24884
25397
  { event: "prompt-submit", matcher: "*", handler: statusReportMessageHandler, timeoutMs: STATUS_REPORT_TIMEOUT_MS }
24885
25398
  ];
24886
25399
  }
24887
- var PULL_TIMEOUT_MS, UPDATE_TIMEOUT_MS, TRACK_TIMEOUT_MS, DASHBOARD_TIMEOUT_MS, CONTRIBUTE_CHECK_TIMEOUT_MS, TODOWRITE_HINT_TIMEOUT_MS, MR_HINT_TIMEOUT_MS, STATUS_REPORT_TIMEOUT_MS, pullHandler, updateHandler, dashboardReportHandler, trackHandler, trackSlashHandler, contributeCheckHandler, todowriteHintHandler, mrHintHandler, statusReportSessionHandler, statusReportMessageHandler;
25400
+ var PULL_TIMEOUT_MS, UPDATE_TIMEOUT_MS, TRACK_TIMEOUT_MS, DASHBOARD_TIMEOUT_MS, CONTRIBUTE_CHECK_TIMEOUT_MS, VOTES_SYNC_TIMEOUT_MS, TODOWRITE_HINT_TIMEOUT_MS, MR_HINT_TIMEOUT_MS, STATUS_REPORT_TIMEOUT_MS, pullHandler, updateHandler, dashboardReportHandler, trackHandler, trackSlashHandler, contributeCheckHandler, votesSyncHandler, todowriteHintHandler, mrHintHandler, statusReportSessionHandler, statusReportMessageHandler;
24888
25401
  var init_hook_handlers = __esm({
24889
25402
  "src/hook-handlers.ts"() {
24890
25403
  "use strict";
@@ -24895,6 +25408,7 @@ var init_hook_handlers = __esm({
24895
25408
  TRACK_TIMEOUT_MS = 5e3;
24896
25409
  DASHBOARD_TIMEOUT_MS = 5e3;
24897
25410
  CONTRIBUTE_CHECK_TIMEOUT_MS = 1e4;
25411
+ VOTES_SYNC_TIMEOUT_MS = 8e3;
24898
25412
  TODOWRITE_HINT_TIMEOUT_MS = 5e3;
24899
25413
  MR_HINT_TIMEOUT_MS = 1e4;
24900
25414
  STATUS_REPORT_TIMEOUT_MS = 3e4;
@@ -24987,6 +25501,31 @@ var init_hook_handlers = __esm({
24987
25501
  return null;
24988
25502
  }
24989
25503
  };
25504
+ votesSyncHandler = {
25505
+ name: "votes-sync",
25506
+ async execute(stdin) {
25507
+ if (process.env.TEAMAI_RECALL_DISABLED === "1") return null;
25508
+ const transcriptPath = typeof stdin.transcript_path === "string" ? stdin.transcript_path : null;
25509
+ if (!transcriptPath) return null;
25510
+ try {
25511
+ const { parseTranscriptForVotes: parseTranscriptForVotes2 } = await Promise.resolve().then(() => (init_transcript_parser(), transcript_parser_exports));
25512
+ const { incrementUpvoted: incrementUpvoted2, syncVotesToTeam: syncVotesToTeam2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
25513
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
25514
+ const voteData = await parseTranscriptForVotes2(transcriptPath);
25515
+ const { localConfig } = await requireInit3();
25516
+ const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
25517
+ const votesDir = VOTES_LOCAL_DIR2;
25518
+ const votePath = path94.join(votesDir, `${localConfig.username}.yaml`);
25519
+ if (voteData.referencedDocIds.length > 0) {
25520
+ await incrementUpvoted2(votePath, voteData.referencedDocIds);
25521
+ }
25522
+ await syncVotesToTeam2(localConfig.repo.localPath, localConfig.username, votesDir).catch(() => {
25523
+ });
25524
+ } catch {
25525
+ }
25526
+ return null;
25527
+ }
25528
+ };
24990
25529
  todowriteHintHandler = {
24991
25530
  name: "todowrite-hint",
24992
25531
  async execute(stdin, _tool) {
@@ -25113,10 +25652,10 @@ function formatComment(learning, suggestions, marker) {
25113
25652
  lines.push("> _Auto-generated by `teamai ci extract-mr`_");
25114
25653
  return lines.join("\n");
25115
25654
  }
25116
- async function githubRequest(path92, method, body) {
25655
+ async function githubRequest(path99, method, body) {
25117
25656
  const token = process.env["GITHUB_TOKEN"];
25118
25657
  if (!token) throw new Error("\u672A\u8BBE\u7F6E GITHUB_TOKEN \u73AF\u5883\u53D8\u91CF");
25119
- const url = `https://api.github.com${path92}`;
25658
+ const url = `https://api.github.com${path99}`;
25120
25659
  const headers = {
25121
25660
  Authorization: `Bearer ${token}`,
25122
25661
  Accept: "application/vnd.github+json",
@@ -25172,9 +25711,9 @@ function getTGitToken() {
25172
25711
  if (oauthToken) return oauthToken;
25173
25712
  throw new Error("\u672A\u8BBE\u7F6E TAI_PAT_TOKEN \u73AF\u5883\u53D8\u91CF\uFF0C\u4E14\u65E0\u6CD5\u4ECE gf credential \u83B7\u53D6 token");
25174
25713
  }
25175
- async function tgitRequest(path92, method, body) {
25714
+ async function tgitRequest(path99, method, body) {
25176
25715
  const token = getTGitToken();
25177
- const url = `https://git.woa.com/api/v3${path92}`;
25716
+ const url = `https://git.woa.com/api/v3${path99}`;
25178
25717
  const headers = {
25179
25718
  Authorization: `Bearer ${token}`,
25180
25719
  "Content-Type": "application/json"
@@ -25464,10 +26003,10 @@ function extractMarkerId(body) {
25464
26003
  const match = body.match(MARKER_REGEX);
25465
26004
  return match ? match[1] : null;
25466
26005
  }
25467
- async function githubRequest2(path92) {
26006
+ async function githubRequest2(path99) {
25468
26007
  const token = process.env["GITHUB_TOKEN"];
25469
26008
  if (!token) throw new Error("\u672A\u8BBE\u7F6E GITHUB_TOKEN");
25470
- return fetch(`https://api.github.com${path92}`, {
26009
+ return fetch(`https://api.github.com${path99}`, {
25471
26010
  headers: {
25472
26011
  Authorization: `Bearer ${token}`,
25473
26012
  Accept: "application/vnd.github+json",
@@ -25509,9 +26048,9 @@ function getTGitToken2() {
25509
26048
  if (oauthToken) return oauthToken;
25510
26049
  throw new Error("\u672A\u8BBE\u7F6E TAI_PAT_TOKEN \u4E14\u65E0\u6CD5\u83B7\u53D6 OAuth token");
25511
26050
  }
25512
- async function tgitRequest2(path92) {
26051
+ async function tgitRequest2(path99) {
25513
26052
  const token = getTGitToken2();
25514
- return fetch(`https://git.woa.com/api/v3${path92}`, {
26053
+ return fetch(`https://git.woa.com/api/v3${path99}`, {
25515
26054
  headers: {
25516
26055
  Authorization: `Bearer ${token}`,
25517
26056
  "Content-Type": "application/json"
@@ -25577,8 +26116,8 @@ var extract_mr_exports = {};
25577
26116
  __export(extract_mr_exports, {
25578
26117
  ciExtractMr: () => ciExtractMr
25579
26118
  });
25580
- import fs39 from "fs/promises";
25581
- import path91 from "path";
26119
+ import fs40 from "fs/promises";
26120
+ import path95 from "path";
25582
26121
  async function configureGitUser2(repoPath, provider) {
25583
26122
  const { execFileSync: execFileSync5 } = await import("child_process");
25584
26123
  let name = "teamai-ci";
@@ -25631,11 +26170,11 @@ async function writeKnowledgeToRepo(teamRepo, learning, suggestions, writeMode,
25631
26170
  const safeTitle = learning.title.replace(/[^a-zA-Z0-9一-鿿_-]/g, "-").replace(/-+/g, "-").slice(0, 50);
25632
26171
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
25633
26172
  const filename = `${dateStr}-${safeTitle}.md`;
25634
- const learningsDir = path91.join(teamRepo, "learnings");
25635
- const learningPath = path91.join(learningsDir, filename);
26173
+ const learningsDir = path95.join(teamRepo, "learnings");
26174
+ const learningPath = path95.join(learningsDir, filename);
25636
26175
  if (!dryRun) {
25637
- await fs39.mkdir(learningsDir, { recursive: true });
25638
- await fs39.writeFile(learningPath, learning.content, "utf-8");
26176
+ await fs40.mkdir(learningsDir, { recursive: true });
26177
+ await fs40.writeFile(learningPath, learning.content, "utf-8");
25639
26178
  }
25640
26179
  log.success(`Learning \u5199\u5165: learnings/${filename}`);
25641
26180
  changedFiles.push(`learnings/${filename}`);
@@ -25670,13 +26209,13 @@ async function writeKnowledgeToRepo(teamRepo, learning, suggestions, writeMode,
25670
26209
  }
25671
26210
  }
25672
26211
  async function writeArtifacts(outputDir, learning, suggestions) {
25673
- await fs39.mkdir(outputDir, { recursive: true });
26212
+ await fs40.mkdir(outputDir, { recursive: true });
25674
26213
  if (learning) {
25675
- await fs39.writeFile(path91.join(outputDir, "learning.md"), learning.content, "utf-8");
26214
+ await fs40.writeFile(path95.join(outputDir, "learning.md"), learning.content, "utf-8");
25676
26215
  }
25677
26216
  if (suggestions && suggestions.length > 0) {
25678
- await fs39.writeFile(
25679
- path91.join(outputDir, "codebase-suggestions.json"),
26217
+ await fs40.writeFile(
26218
+ path95.join(outputDir, "codebase-suggestions.json"),
25680
26219
  JSON.stringify(suggestions, null, 2),
25681
26220
  "utf-8"
25682
26221
  );
@@ -25690,7 +26229,7 @@ async function ciExtractMr(opts) {
25690
26229
  const result = await importFromMR({
25691
26230
  url: opts.url,
25692
26231
  all: true,
25693
- learningsDir: opts.teamRepo ? path91.join(opts.teamRepo, "learnings") : void 0,
26232
+ learningsDir: opts.teamRepo ? path95.join(opts.teamRepo, "learnings") : void 0,
25694
26233
  dryRun: true
25695
26234
  // 不让 importFromMR 自己写文件,我们自己控制写入
25696
26235
  });
@@ -25799,21 +26338,21 @@ ${affectedModules.map((m) => `- \`${m}\` (evidence + G-document)`).join("\n")}`
25799
26338
  const projectName = parsed.repo;
25800
26339
  await extractCodebase2({ path: businessRepo, project: projectName });
25801
26340
  const fse9 = await import("fs-extra");
25802
- const srcWiki = path91.join(businessRepo, "teamwiki");
25803
- const teamWikiRoot = path91.join(path91.resolve(opts.teamRepo), "teamwiki");
26341
+ const srcWiki = path95.join(businessRepo, "teamwiki");
26342
+ const teamWikiRoot = path95.join(path95.resolve(opts.teamRepo), "teamwiki");
25804
26343
  try {
25805
26344
  if (await fse9.pathExists(srcWiki)) {
25806
- const evidenceSrc = path91.join(srcWiki, "evidence", "code", projectName);
25807
- const evidenceDest = path91.join(teamWikiRoot, "evidence", "code", projectName);
26345
+ const evidenceSrc = path95.join(srcWiki, "evidence", "code", projectName);
26346
+ const evidenceDest = path95.join(teamWikiRoot, "evidence", "code", projectName);
25808
26347
  if (await fse9.pathExists(evidenceSrc)) {
25809
26348
  await fse9.ensureDir(evidenceDest);
25810
26349
  await fse9.copy(evidenceSrc, evidenceDest, { overwrite: true });
25811
26350
  }
25812
- const srcGraph = path91.join(srcWiki, ".indices", "graph-index.json");
26351
+ const srcGraph = path95.join(srcWiki, ".indices", "graph-index.json");
25813
26352
  if (await fse9.pathExists(srcGraph)) {
25814
- const destGraphDir = path91.join(evidenceDest, ".indices");
26353
+ const destGraphDir = path95.join(evidenceDest, ".indices");
25815
26354
  await fse9.ensureDir(destGraphDir);
25816
- await fse9.copy(srcGraph, path91.join(destGraphDir, "graph-index.json"));
26355
+ await fse9.copy(srcGraph, path95.join(destGraphDir, "graph-index.json"));
25817
26356
  }
25818
26357
  const { aggregateGlobalGraph: aggregateGlobalGraph2 } = await Promise.resolve().then(() => (init_graph_aggregate(), graph_aggregate_exports));
25819
26358
  await aggregateGlobalGraph2(teamWikiRoot);
@@ -25868,6 +26407,440 @@ var init_extract_mr = __esm({
25868
26407
  }
25869
26408
  });
25870
26409
 
26410
+ // src/maintenance/prune.ts
26411
+ import path96 from "path";
26412
+ import matter11 from "gray-matter";
26413
+ async function findPruneCandidates(learningsDir, votesDir, options = {}) {
26414
+ const threshold = options.threshold ?? DEFAULT_THRESHOLD;
26415
+ const confidenceMap = await computeAllConfidence(votesDir);
26416
+ const candidates = [];
26417
+ const files = await listFiles(learningsDir);
26418
+ const now = Date.now();
26419
+ for (const file of files) {
26420
+ if (!file.endsWith(".md")) continue;
26421
+ const docId = file.replace(/\.md$/i, "");
26422
+ const absPath = path96.join(learningsDir, file);
26423
+ const content = await readFileSafe(absPath);
26424
+ if (!content) continue;
26425
+ let date = "";
26426
+ try {
26427
+ const { data } = matter11(content);
26428
+ date = typeof data.date === "string" ? data.date : "";
26429
+ } catch {
26430
+ continue;
26431
+ }
26432
+ const confidence = confidenceMap.get(docId);
26433
+ if (confidence === void 0) continue;
26434
+ const daysSinceCreate = date ? (now - new Date(date).getTime()) / (1e3 * 60 * 60 * 24) : Infinity;
26435
+ if (confidence < threshold) {
26436
+ candidates.push({
26437
+ filename: file,
26438
+ path: absPath,
26439
+ confidence,
26440
+ lastActivity: date,
26441
+ reason: `confidence ${confidence.toFixed(2)} < ${threshold}`
26442
+ });
26443
+ } else if (daysSinceCreate > STALE_DAYS && confidence < 0.3) {
26444
+ candidates.push({
26445
+ filename: file,
26446
+ path: absPath,
26447
+ confidence,
26448
+ lastActivity: date,
26449
+ reason: `inactive ${isFinite(daysSinceCreate) ? Math.round(daysSinceCreate) + "d" : "no-date"}, confidence ${confidence.toFixed(2)}`
26450
+ });
26451
+ }
26452
+ }
26453
+ return candidates.sort((a, b) => a.confidence - b.confidence);
26454
+ }
26455
+ async function executePrune(repoPath, candidates, options = {}) {
26456
+ let archived = 0;
26457
+ let removed = 0;
26458
+ if (options.dryRun) {
26459
+ log.info(`[dry-run] Would ${options.archive ? "archive" : "remove"} ${candidates.length} file(s)`);
26460
+ return { archived: 0, removed: 0 };
26461
+ }
26462
+ for (const candidate of candidates) {
26463
+ if (options.archive) {
26464
+ const archiveDir = path96.join(repoPath, "learnings", "_archive");
26465
+ await ensureDir(archiveDir);
26466
+ await copyFile(candidate.path, path96.join(archiveDir, candidate.filename));
26467
+ await remove(candidate.path);
26468
+ archived++;
26469
+ } else {
26470
+ await remove(candidate.path);
26471
+ removed++;
26472
+ }
26473
+ }
26474
+ if (archived > 0) log.success(`Archived ${archived} learning(s)`);
26475
+ if (removed > 0) log.success(`Removed ${removed} learning(s)`);
26476
+ return { archived, removed };
26477
+ }
26478
+ var DEFAULT_THRESHOLD, STALE_DAYS;
26479
+ var init_prune = __esm({
26480
+ "src/maintenance/prune.ts"() {
26481
+ "use strict";
26482
+ init_fs();
26483
+ init_logger();
26484
+ init_confidence();
26485
+ DEFAULT_THRESHOLD = 0.15;
26486
+ STALE_DAYS = 180;
26487
+ }
26488
+ });
26489
+
26490
+ // src/maintenance/quality-update.ts
26491
+ import path97 from "path";
26492
+ async function findStaleEntries(votesDir, knowledgeDirs, options = {}) {
26493
+ const minRecalled = options.minRecalled ?? DEFAULT_MIN_RECALLED;
26494
+ const maxUpvoted = options.maxUpvoted ?? DEFAULT_MAX_UPVOTED;
26495
+ const minUsers = options.minUsers ?? DEFAULT_MIN_USERS;
26496
+ const perDoc = /* @__PURE__ */ new Map();
26497
+ const voteFiles = await listFiles(votesDir);
26498
+ for (const file of voteFiles) {
26499
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26500
+ const username = file.replace(/\.(yaml|yml)$/, "");
26501
+ const filePath = path97.join(votesDir, file);
26502
+ try {
26503
+ const data = await loadUserVotes(filePath);
26504
+ for (const [docId, entry] of Object.entries(data.votes)) {
26505
+ const existing = perDoc.get(docId) ?? { recalled: 0, upvoted: 0, users: /* @__PURE__ */ new Set() };
26506
+ existing.recalled += entry.recalled_count ?? 0;
26507
+ existing.upvoted += entry.upvoted_count ?? 0;
26508
+ if ((entry.recalled_count ?? 0) > 0) existing.users.add(username);
26509
+ perDoc.set(docId, existing);
26510
+ }
26511
+ } catch {
26512
+ continue;
26513
+ }
26514
+ }
26515
+ const candidates = [];
26516
+ for (const [docId, data] of perDoc) {
26517
+ if (data.recalled < minRecalled) continue;
26518
+ if (data.upvoted > maxUpvoted) continue;
26519
+ if (data.users.size < minUsers) continue;
26520
+ const entryPath = await resolveDocPath(docId, knowledgeDirs);
26521
+ if (!entryPath) continue;
26522
+ const type = entryPath.includes("/docs/") ? "docs" : entryPath.includes("/rules/") ? "rules" : entryPath.includes("/skills/") ? "skills" : "unknown";
26523
+ candidates.push({
26524
+ docId,
26525
+ path: entryPath,
26526
+ recalledCount: data.recalled,
26527
+ upvotedCount: data.upvoted,
26528
+ userCount: data.users.size,
26529
+ type
26530
+ });
26531
+ }
26532
+ return candidates.sort((a, b) => b.recalledCount - a.recalledCount);
26533
+ }
26534
+ async function resolveDocPath(docId, dirs) {
26535
+ const filename = docId.endsWith(".md") ? docId : `${docId}.md`;
26536
+ for (const dir of [dirs.docs, dirs.rules, dirs.skills]) {
26537
+ if (!dir) continue;
26538
+ const candidate = path97.join(dir, filename);
26539
+ if (await pathExists(candidate)) return candidate;
26540
+ }
26541
+ return null;
26542
+ }
26543
+ function reportStaleEntries(entries) {
26544
+ if (entries.length === 0) {
26545
+ log.info("No stale entries found that need quality updates.");
26546
+ return;
26547
+ }
26548
+ log.info(`Found ${entries.length} entry(ies) recalled often but rarely adopted:`);
26549
+ for (const entry of entries) {
26550
+ log.info(
26551
+ ` - [${entry.type}] ${entry.docId}: recalled ${entry.recalledCount}x by ${entry.userCount} users, adopted ${entry.upvotedCount}x`
26552
+ );
26553
+ }
26554
+ }
26555
+ async function findRelatedAdoptedLearnings(staleEntry, votesDir, learningsDir, limit = 5) {
26556
+ const perDoc = /* @__PURE__ */ new Map();
26557
+ const voteFiles = await listFiles(votesDir);
26558
+ for (const file of voteFiles) {
26559
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26560
+ try {
26561
+ const data = await loadUserVotes(path97.join(votesDir, file));
26562
+ for (const [docId, entry] of Object.entries(data.votes)) {
26563
+ if (docId === staleEntry.docId) continue;
26564
+ if ((entry.upvoted_count ?? 0) > 0) {
26565
+ perDoc.set(docId, (perDoc.get(docId) ?? 0) + (entry.upvoted_count ?? 0));
26566
+ }
26567
+ }
26568
+ } catch {
26569
+ continue;
26570
+ }
26571
+ }
26572
+ const sorted = [...perDoc.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit);
26573
+ const contents = [];
26574
+ for (const [docId] of sorted) {
26575
+ const filename = docId.endsWith(".md") ? docId : `${docId}.md`;
26576
+ const filePath = path97.join(learningsDir, filename);
26577
+ const content = await readFileSafe(filePath);
26578
+ if (content) contents.push(content);
26579
+ }
26580
+ return contents;
26581
+ }
26582
+ async function generateUpdateDraft(staleEntry, relatedLearnings) {
26583
+ const currentContent = await readFileSafe(staleEntry.path);
26584
+ if (!currentContent) return null;
26585
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26586
+ const learningContext = relatedLearnings.length > 0 ? `
26587
+
26588
+ The following learnings were actually adopted by team members (these represent what the team found more useful):
26589
+
26590
+ ${relatedLearnings.map((l, i) => `--- Learning ${i + 1} ---
26591
+ ${l}`).join("\n\n")}` : "";
26592
+ const prompt = `You are a technical writer updating a team knowledge base entry.
26593
+
26594
+ The following ${staleEntry.type} entry has been recalled ${staleEntry.recalledCount} times by ${staleEntry.userCount} team members but was adopted only ${staleEntry.upvotedCount} time(s). This indicates the content is relevant to common queries but not actionable enough to be directly useful.
26595
+
26596
+ Current content:
26597
+ ---
26598
+ ${currentContent}
26599
+ ---
26600
+ ${learningContext}
26601
+
26602
+ Please rewrite this entry to be more actionable and directly useful. Keep the same topic but:
26603
+ 1. Add concrete examples or commands where applicable
26604
+ 2. Remove outdated or vague information
26605
+ 3. Incorporate relevant insights from the adopted learnings above
26606
+ 4. Keep the same YAML frontmatter format (update the date field to today)
26607
+ 5. Be concise \u2014 aim for the same or shorter length
26608
+
26609
+ Output ONLY the updated markdown file content (including frontmatter).`;
26610
+ try {
26611
+ const draft = await callClaude2(prompt);
26612
+ return draft.trim();
26613
+ } catch (e) {
26614
+ log.error(`AI update generation failed: ${e.message}`);
26615
+ return null;
26616
+ }
26617
+ }
26618
+ var DEFAULT_MIN_RECALLED, DEFAULT_MAX_UPVOTED, DEFAULT_MIN_USERS;
26619
+ var init_quality_update = __esm({
26620
+ "src/maintenance/quality-update.ts"() {
26621
+ "use strict";
26622
+ init_fs();
26623
+ init_votes();
26624
+ init_logger();
26625
+ DEFAULT_MIN_RECALLED = 5;
26626
+ DEFAULT_MAX_UPVOTED = 1;
26627
+ DEFAULT_MIN_USERS = 2;
26628
+ }
26629
+ });
26630
+
26631
+ // src/maintenance/promote.ts
26632
+ import path98 from "path";
26633
+ import matter12 from "gray-matter";
26634
+ async function findPromotionCandidates(learningsDir, votesDir) {
26635
+ const confidenceMap = await computeAllConfidence(votesDir);
26636
+ const candidates = [];
26637
+ const perDoc = await aggregatePerDocVotes(votesDir);
26638
+ const files = await listFiles(learningsDir);
26639
+ const now = Date.now();
26640
+ for (const file of files) {
26641
+ if (!file.endsWith(".md")) continue;
26642
+ const docId = file.replace(/\.md$/i, "");
26643
+ const confidence = confidenceMap.get(docId) ?? 0;
26644
+ if (confidence < MIN_CONFIDENCE) continue;
26645
+ const docVotes = perDoc.get(docId);
26646
+ if (!docVotes) continue;
26647
+ if (docVotes.upvoted < MIN_UPVOTED) continue;
26648
+ if (docVotes.users.size < MIN_USERS) continue;
26649
+ const absPath = path98.join(learningsDir, file);
26650
+ const content = await readFileSafe(absPath);
26651
+ if (!content) continue;
26652
+ let title = docId;
26653
+ let date = "";
26654
+ try {
26655
+ const { data } = matter12(content);
26656
+ title = typeof data.title === "string" ? data.title : docId;
26657
+ date = typeof data.date === "string" ? data.date : "";
26658
+ if (data.promoted_to) continue;
26659
+ } catch {
26660
+ continue;
26661
+ }
26662
+ if (date) {
26663
+ const ageInDays = (now - new Date(date).getTime()) / (1e3 * 60 * 60 * 24);
26664
+ if (ageInDays < MIN_AGE_DAYS) continue;
26665
+ }
26666
+ const suggestedCategory = await inferCategory(content, title);
26667
+ candidates.push({
26668
+ docId,
26669
+ filename: file,
26670
+ path: absPath,
26671
+ confidence,
26672
+ upvotedCount: docVotes.upvoted,
26673
+ userCount: docVotes.users.size,
26674
+ title,
26675
+ suggestedCategory
26676
+ });
26677
+ }
26678
+ return candidates.sort((a, b) => b.confidence - a.confidence);
26679
+ }
26680
+ async function generatePromotedContent(originalContent, category, title) {
26681
+ const categoryGuide = {
26682
+ skills: `a reusable skill/SOP. Format it with:
26683
+ - A clear "when to use" trigger description
26684
+ - Step-by-step numbered procedure
26685
+ - Expected outcome
26686
+ - Common pitfalls to avoid
26687
+ Remove any references to specific dates, people, or one-off incidents. Make it timeless and reusable.`,
26688
+ rules: `a team rule/constraint. Format it with:
26689
+ - A one-line rule statement (imperative: "Always X" / "Never Y")
26690
+ - Rationale: why this matters (1-2 sentences)
26691
+ - Examples of correct and incorrect usage
26692
+ Remove anecdotal context. State the constraint as an absolute standard.`,
26693
+ docs: `a reference document. Format it with:
26694
+ - Context: what system/component this describes
26695
+ - Key concepts and their relationships
26696
+ - Architecture decisions and their rationale
26697
+ - Current state (as of promotion date)
26698
+ Remove personal narrative. Write as objective technical documentation.`
26699
+ };
26700
+ try {
26701
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26702
+ const prompt = `You are a technical knowledge engineer promoting a team learning into formal knowledge.
26703
+
26704
+ The following "learning" has been repeatedly validated by the team (high confidence, adopted by multiple members). Transform it into ${categoryGuide[category]}
26705
+
26706
+ Original learning (title: "${title}"):
26707
+ ---
26708
+ ${originalContent}
26709
+ ---
26710
+
26711
+ Output ONLY the transformed markdown content (including YAML frontmatter with title, tags, date set to today). Do NOT include the original learning's confidence or vote-related fields in the frontmatter.`;
26712
+ const result = await callClaude2(prompt, { timeout: 6e4 });
26713
+ return result.trim();
26714
+ } catch (e) {
26715
+ log.warn(`AI content generation failed, using original content: ${e.message}`);
26716
+ return originalContent;
26717
+ }
26718
+ }
26719
+ async function executePromotion(candidate, repoPath, options = {}) {
26720
+ const category = options.category ?? candidate.suggestedCategory;
26721
+ const targetDir = path98.join(repoPath, category);
26722
+ await ensureDir(targetDir);
26723
+ const targetPath = path98.join(targetDir, candidate.filename);
26724
+ if (options.dryRun) {
26725
+ log.info(`[dry-run] Would promote ${candidate.docId} -> ${category}/${candidate.filename}`);
26726
+ return targetPath;
26727
+ }
26728
+ const originalContent = await readFileSafe(candidate.path);
26729
+ if (!originalContent) {
26730
+ log.error(`Cannot read source file: ${candidate.path}`);
26731
+ return targetPath;
26732
+ }
26733
+ const promotedContent = await generatePromotedContent(originalContent, category, candidate.title);
26734
+ await writeFile(targetPath, promotedContent);
26735
+ const { data, content: body } = matter12(originalContent);
26736
+ data.promoted_to = `${category}/${candidate.filename}`;
26737
+ const updated = matter12.stringify(body, data);
26738
+ await writeFile(candidate.path, updated);
26739
+ log.success(`Promoted: ${candidate.docId} -> ${category}/${candidate.filename}`);
26740
+ return targetPath;
26741
+ }
26742
+ function inferCategoryByKeywords(content, title) {
26743
+ const lower = (content + " " + title).toLowerCase();
26744
+ const skillSignals = ["command", "cli", "workflow", "step-by-step", "procedure", "how to", "recipe"];
26745
+ const ruleSignals = ["must", "never", "always", "constraint", "convention", "standard", "rule"];
26746
+ const docSignals = ["architecture", "design", "overview", "background", "decision", "context"];
26747
+ const skillScore = skillSignals.filter((s) => lower.includes(s)).length;
26748
+ const ruleScore = ruleSignals.filter((s) => lower.includes(s)).length;
26749
+ const docScore = docSignals.filter((s) => lower.includes(s)).length;
26750
+ if (ruleScore >= skillScore && ruleScore >= docScore) return "rules";
26751
+ if (skillScore >= docScore) return "skills";
26752
+ return "docs";
26753
+ }
26754
+ async function inferCategory(content, title) {
26755
+ try {
26756
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26757
+ const prompt = `Classify the following team knowledge entry into exactly ONE category. Reply with ONLY the category name (no explanation):
26758
+
26759
+ - "skills" \u2014 reusable workflows, SOPs, step-by-step procedures, CLI recipes, operational playbooks
26760
+ - "rules" \u2014 mandatory constraints, coding standards, conventions, best practices that MUST be followed
26761
+ - "docs" \u2014 architecture decisions, system design, background context, reference documentation
26762
+
26763
+ Title: ${title}
26764
+
26765
+ Content:
26766
+ ${content.slice(0, 2e3)}
26767
+
26768
+ Category:`;
26769
+ const result = await callClaude2(prompt, { timeout: 3e4 });
26770
+ const category = result.trim().toLowerCase().replace(/[^a-z]/g, "");
26771
+ if (category === "skills" || category === "rules" || category === "docs") {
26772
+ return category;
26773
+ }
26774
+ } catch {
26775
+ }
26776
+ return inferCategoryByKeywords(content, title);
26777
+ }
26778
+ async function aggregatePerDocVotes(votesDir) {
26779
+ const perDoc = /* @__PURE__ */ new Map();
26780
+ const { loadUserVotes: loadUserVotes2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
26781
+ const voteFiles = await listFiles(votesDir);
26782
+ for (const file of voteFiles) {
26783
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26784
+ const username = file.replace(/\.(yaml|yml)$/, "");
26785
+ const filePath = path98.join(votesDir, file);
26786
+ try {
26787
+ const data = await loadUserVotes2(filePath);
26788
+ for (const [docId, entry] of Object.entries(data.votes)) {
26789
+ const existing = perDoc.get(docId) ?? { recalled: 0, upvoted: 0, users: /* @__PURE__ */ new Set() };
26790
+ existing.recalled += entry.recalled_count ?? 0;
26791
+ existing.upvoted += entry.upvoted_count ?? 0;
26792
+ if ((entry.upvoted_count ?? 0) > 0) existing.users.add(username);
26793
+ perDoc.set(docId, existing);
26794
+ }
26795
+ } catch {
26796
+ continue;
26797
+ }
26798
+ }
26799
+ return perDoc;
26800
+ }
26801
+ var MIN_CONFIDENCE, MIN_UPVOTED, MIN_USERS, MIN_AGE_DAYS;
26802
+ var init_promote = __esm({
26803
+ "src/maintenance/promote.ts"() {
26804
+ "use strict";
26805
+ init_fs();
26806
+ init_logger();
26807
+ init_confidence();
26808
+ MIN_CONFIDENCE = 0.9;
26809
+ MIN_UPVOTED = 5;
26810
+ MIN_USERS = 2;
26811
+ MIN_AGE_DAYS = 14;
26812
+ }
26813
+ });
26814
+
26815
+ // src/maintenance/index.ts
26816
+ var maintenance_exports = {};
26817
+ __export(maintenance_exports, {
26818
+ COLD_PENALTY: () => COLD_PENALTY,
26819
+ HOT_THRESHOLD: () => HOT_THRESHOLD,
26820
+ annotateHotness: () => annotateHotness,
26821
+ computeAllConfidence: () => computeAllConfidence,
26822
+ computeConfidence: () => computeConfidence,
26823
+ executePromotion: () => executePromotion,
26824
+ executePrune: () => executePrune,
26825
+ findPromotionCandidates: () => findPromotionCandidates,
26826
+ findPruneCandidates: () => findPruneCandidates,
26827
+ findRelatedAdoptedLearnings: () => findRelatedAdoptedLearnings,
26828
+ findStaleEntries: () => findStaleEntries,
26829
+ generateUpdateDraft: () => generateUpdateDraft,
26830
+ reportStaleEntries: () => reportStaleEntries,
26831
+ writeBackConfidence: () => writeBackConfidence
26832
+ });
26833
+ var init_maintenance = __esm({
26834
+ "src/maintenance/index.ts"() {
26835
+ "use strict";
26836
+ init_confidence();
26837
+ init_prune();
26838
+ init_hot_cold();
26839
+ init_quality_update();
26840
+ init_promote();
26841
+ }
26842
+ });
26843
+
25871
26844
  // src/index.ts
25872
26845
  init_logger();
25873
26846
  import { createRequire as createRequire2 } from "module";
@@ -25879,7 +26852,7 @@ program.name("teamai").description("TeamAI \u2014 The team harness for AI agents
25879
26852
  const opts = thisCommand.opts();
25880
26853
  if (opts.verbose) setVerbose(true);
25881
26854
  });
25882
- program.command("init").description("Initialize teamai (configure TGit, clone repo, register member)").option("--repo <repo>", "Team repo (owner/repo or full URL)").option("--http <url>", "Git-free HTTP team repo (read-only consumer; only needs an API key)").option("--token <key>", "API key for HTTP team repo / status reporting (stored 0600, never committed). Also reads TEAMAI_API_TOKEN.").option("--scope <scope>", "Scope: user (default) or project").option("--role <id>", "Primary role ID (e.g. hai_dev) for non-interactive setup").option("--force", "Overwrite existing config without confirmation").action(async (cmdOpts) => {
26855
+ program.command("init").description("Initialize teamai (configure TGit, clone repo, register member)").option("--repo <repo>", "Team repo (owner/repo or full URL)").option("--http <url>", "Git-free HTTP team repo (read-only consumer; only needs an API key)").option("--token <key>", "API key for HTTP team repo / status reporting (stored 0600, never committed). Also reads TEAMAI_API_TOKEN.").option("--scope <scope>", "Scope: user (default) or project").option("--role <id>", "Primary role ID (e.g. hai_dev) for non-interactive setup").option("--agent <name>", "Only inject hooks into this agent (e.g. claude, codebuddy, workbuddy). Additive on repeated runs.").option("--force", "Overwrite existing config without confirmation").action(async (cmdOpts) => {
25883
26856
  const globalOpts = program.opts();
25884
26857
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
25885
26858
  await init2({ ...globalOpts, ...cmdOpts });
@@ -26220,5 +27193,100 @@ program.command("deep-enrich", { hidden: true }).description("Run deep AI knowle
26220
27193
  const { deepEnrich: deepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
26221
27194
  await deepEnrich2({ project: cmdOpts.project, evidenceDir, wikiRoot, maxModules: cmdOpts.maxModules });
26222
27195
  });
27196
+ recallCmd.command("feedback").description("Record manual feedback for a recalled document").option("--positive <docId>", "Upvote a document (marks as actually useful)").option("--negative <docId>", "Record negative signal for a document").action(async (cmdOpts) => {
27197
+ const { recallFeedback: recallFeedback2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
27198
+ await recallFeedback2({ positive: cmdOpts.positive, negative: cmdOpts.negative });
27199
+ });
27200
+ recallCmd.command("maintenance").description("Automatic maintenance of team knowledge base").option("--prune", "Remove low-confidence learnings").option("--threshold <n>", "Confidence threshold for pruning (default 0.15)", parseFloat).option("--archive", "Move to archive/ instead of deleting").option("--confidence-writeback", "Update frontmatter confidence scores").option("--update-quality", "Find stale docs/rules/skills and suggest updates").option("--dry-run", "Show what would be done without making changes").action(async (cmdOpts) => {
27201
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
27202
+ const { localConfig } = await requireInit3();
27203
+ const repoPath = localConfig.repo.localPath;
27204
+ const votesDir = `${repoPath}/votes`;
27205
+ const learningsDir = `${repoPath}/learnings`;
27206
+ if (cmdOpts.confidenceWriteback) {
27207
+ const { computeAllConfidence: computeAllConfidence2, writeBackConfidence: writeBackConfidence2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27208
+ const map = await computeAllConfidence2(votesDir);
27209
+ await writeBackConfidence2(learningsDir, map);
27210
+ return;
27211
+ }
27212
+ if (cmdOpts.prune) {
27213
+ const { findPruneCandidates: findPruneCandidates2, executePrune: executePrune2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27214
+ const candidates = await findPruneCandidates2(learningsDir, votesDir, {
27215
+ threshold: cmdOpts.threshold
27216
+ });
27217
+ if (candidates.length === 0) {
27218
+ const { log: log4 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27219
+ log4.info("No learnings below threshold. Knowledge base is healthy.");
27220
+ return;
27221
+ }
27222
+ const { log: log3 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27223
+ log3.info(`Found ${candidates.length} candidate(s) for pruning:`);
27224
+ for (const c of candidates) {
27225
+ log3.info(` - ${c.filename} (confidence: ${c.confidence.toFixed(2)}, reason: ${c.reason})`);
27226
+ }
27227
+ await executePrune2(repoPath, candidates, {
27228
+ dryRun: cmdOpts.dryRun,
27229
+ archive: cmdOpts.archive
27230
+ });
27231
+ return;
27232
+ }
27233
+ if (cmdOpts.updateQuality) {
27234
+ const { findStaleEntries: findStaleEntries2, reportStaleEntries: reportStaleEntries2, findRelatedAdoptedLearnings: findRelatedAdoptedLearnings2, generateUpdateDraft: generateUpdateDraft2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27235
+ const { writeFile: writeFile12 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
27236
+ const { log: log3 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27237
+ const entries = await findStaleEntries2(votesDir, {
27238
+ docs: `${repoPath}/docs`,
27239
+ rules: `${repoPath}/rules`,
27240
+ skills: `${repoPath}/skills`
27241
+ });
27242
+ reportStaleEntries2(entries);
27243
+ if (entries.length === 0 || cmdOpts.dryRun) return;
27244
+ log3.info("\nGenerating AI-powered update drafts...");
27245
+ for (const entry of entries) {
27246
+ const related = await findRelatedAdoptedLearnings2(entry, votesDir, learningsDir);
27247
+ const draft = await generateUpdateDraft2(entry, related);
27248
+ if (draft) {
27249
+ const draftPath = `${entry.path}.draft.md`;
27250
+ await writeFile12(draftPath, draft);
27251
+ log3.success(` Draft written: ${draftPath}`);
27252
+ }
27253
+ }
27254
+ log3.info("\nReview drafts, then rename .draft.md -> .md to apply updates.");
27255
+ return;
27256
+ }
27257
+ const { log: log2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27258
+ log2.info("Usage: teamai recall maintenance --prune | --confidence-writeback | --update-quality");
27259
+ });
27260
+ recallCmd.command("promote [learningId]").description("Promote a high-confidence learning to formal knowledge (docs/skills/rules)").option("--category <cat>", "Target category: skills | rules | docs").option("--dry-run", "Show what would be done without making changes").action(async (learningId, cmdOpts) => {
27261
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
27262
+ const { localConfig } = await requireInit3();
27263
+ const repoPath = localConfig.repo.localPath;
27264
+ const votesDir = `${repoPath}/votes`;
27265
+ const learningsDir = `${repoPath}/learnings`;
27266
+ const { findPromotionCandidates: findPromotionCandidates2, executePromotion: executePromotion2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27267
+ const { log: log2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27268
+ const candidates = await findPromotionCandidates2(learningsDir, votesDir);
27269
+ if (candidates.length === 0) {
27270
+ log2.info("No learnings eligible for promotion yet.");
27271
+ return;
27272
+ }
27273
+ if (!learningId) {
27274
+ log2.info(`${candidates.length} learning(s) eligible for promotion:`);
27275
+ for (const c of candidates) {
27276
+ log2.info(` - ${c.docId} (confidence: ${c.confidence.toFixed(2)}, suggested: ${c.suggestedCategory})`);
27277
+ }
27278
+ log2.info("\nRun: teamai recall promote <learning-id> [--category <cat>]");
27279
+ return;
27280
+ }
27281
+ const candidate = candidates.find((c) => c.docId === learningId);
27282
+ if (!candidate) {
27283
+ log2.error(`Learning "${learningId}" not found or not eligible for promotion.`);
27284
+ return;
27285
+ }
27286
+ await executePromotion2(candidate, repoPath, {
27287
+ category: cmdOpts.category,
27288
+ dryRun: cmdOpts.dryRun
27289
+ });
27290
+ });
26223
27291
  program.parse();
26224
27292
  //# sourceMappingURL=index.js.map