teamai-cli 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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
  }
@@ -1667,10 +1772,17 @@ var init_builtin_rules = __esm({
1667
1772
  ]);
1668
1773
  TEAMAI_RECALL_RULE_CONTENT = `# Team Knowledge Recall (teamai)
1669
1774
 
1670
- ## \u89C4\u5219\uFF1A\u4EFB\u52A1\u5F00\u59CB\u524D\u5FC5\u987B\u68C0\u7D22\u56E2\u961F\u77E5\u8BC6\u5E93
1775
+ ## \u89C4\u5219\uFF1A\u4EFB\u52A1\u5F00\u59CB\u524D\u68C0\u7D22\u56E2\u961F\u77E5\u8BC6\u5E93\uFF08\u63A8\u8350\uFF09
1671
1776
 
1672
- **Before** starting any task that involves code changes, debugging,
1673
- or design decisions, you **MUST** first search the team knowledge base.
1777
+ **Before** starting a task that involves code changes, debugging,
1778
+ or design decisions, you **SHOULD** search the team knowledge base
1779
+ \u2014 unless one of the skip conditions below applies.
1780
+
1781
+ ### \u8DF3\u8FC7\u6761\u4EF6\uFF08\u6EE1\u8DB3\u4EFB\u4E00\u5373\u53EF\u8DF3\u8FC7\u68C0\u7D22\uFF09
1782
+
1783
+ 1. **\u7528\u6237\u5DF2\u63D0\u4F9B\u4E0A\u4E0B\u6587** \u2014 \u7528\u6237\u5728\u6D88\u606F\u4E2D\u660E\u786E\u5F15\u7528\u4E86\u6587\u4EF6\u3001\u7ED9\u51FA\u4E86\u89E3\u51B3\u65B9\u6848\u3001\u6216\u8BF4\u660E"\u7B54\u6848\u5728\u5F53\u524D\u76EE\u5F55/\u8FD9\u4E2A\u6587\u4EF6\u91CC"
1784
+ 2. **\u672C\u5730\u6587\u4EF6\u5DF2\u6709\u7B54\u6848** \u2014 \u4EFB\u52A1\u4FE1\u606F\u53EF\u76F4\u63A5\u4ECE\u5F53\u524D\u5DE5\u4F5C\u533A\u6587\u4EF6\u83B7\u5F97\uFF08\u5982\u4FEE\u590D\u5F53\u524D\u6587\u4EF6\u4E2D\u660E\u663E\u7684 bug\uFF09
1785
+ 3. **\u7B80\u5355/\u5C40\u90E8\u6539\u52A8** \u2014 \u5BF9\u5DF2\u77E5\u6587\u4EF6\u505A\u5C0F\u8303\u56F4\u4FEE\u6539\uFF08\u4FEE typo\u3001\u8C03\u6574\u53C2\u6570\u503C\u3001\u6539\u683C\u5F0F\uFF09\uFF0C\u65E0\u9700\u989D\u5916\u77E5\u8BC6\u80CC\u666F
1674
1786
 
1675
1787
  ### \u65B9\u5F0F\u4E00\uFF1A\u901A\u8FC7 Subagent\uFF08\u63A8\u8350\uFF0C\u9002\u7528\u4E8E\u652F\u6301 Agent tool \u7684\u5DE5\u5177\uFF09
1676
1788
 
@@ -3056,7 +3168,7 @@ var init_builtin_skills = __esm({
3056
3168
  init_types();
3057
3169
  init_base();
3058
3170
  init_skills();
3059
- BUILTIN_SKILL_NAMES = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase"]);
3171
+ BUILTIN_SKILL_NAMES = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase", "teamai-workflow", "teamai-import"]);
3060
3172
  RECALL_DEPENDENT_SKILLS = /* @__PURE__ */ new Set(["teamai-share-learnings", "team-wiki-codebase"]);
3061
3173
  }
3062
3174
  });
@@ -6283,6 +6395,11 @@ async function initHttp(url, options) {
6283
6395
  log.debug(`Role selection skipped: ${msg}`);
6284
6396
  }
6285
6397
  }
6398
+ if (options.agent) {
6399
+ const existing = await loadLocalConfigForScope(scope, projectRoot);
6400
+ const prev = existing?.enabledAgents ?? [];
6401
+ localConfig.enabledAgents = [.../* @__PURE__ */ new Set([...prev, options.agent])];
6402
+ }
6286
6403
  await ensureDir(teamaiHome);
6287
6404
  if (scope === "project") {
6288
6405
  await saveLocalConfigForScope(localConfig, scope, projectRoot);
@@ -6297,7 +6414,8 @@ async function initHttp(url, options) {
6297
6414
  await saveStateForScope(state, scope, projectRoot);
6298
6415
  } catch {
6299
6416
  }
6300
- await reconcileTeamHooksForConfig(teamConfig, localConfig);
6417
+ const filterAgents2 = options.agent ? [options.agent] : void 0;
6418
+ await reconcileTeamHooksForConfig(teamConfig, localConfig, { filterAgents: filterAgents2 });
6301
6419
  if (reportingOnly) {
6302
6420
  log.success("teamai initialized (HTTP, reporting-only \u2014 /repo not live yet)!");
6303
6421
  log.info("Status reporting is active now; skills/rules will sync automatically once /repo is available.");
@@ -6544,6 +6662,11 @@ async function init(options) {
6544
6662
  process.exit(1);
6545
6663
  }
6546
6664
  }
6665
+ if (options.agent) {
6666
+ const existing = await loadLocalConfigForScope(scope, projectRoot);
6667
+ const prev = existing?.enabledAgents ?? [];
6668
+ localConfig.enabledAgents = [.../* @__PURE__ */ new Set([...prev, options.agent])];
6669
+ }
6547
6670
  await ensureDir(teamaiHome);
6548
6671
  if (scope === "project") {
6549
6672
  await saveLocalConfigForScope(localConfig, scope, projectRoot);
@@ -6583,7 +6706,8 @@ async function init(options) {
6583
6706
  }
6584
6707
  const reloadedTeamConfig = await loadTeamConfig(localPath);
6585
6708
  if (reloadedTeamConfig) {
6586
- await reconcileTeamHooksForConfig(reloadedTeamConfig, localConfig);
6709
+ const filterAgents2 = options.agent ? [options.agent] : void 0;
6710
+ await reconcileTeamHooksForConfig(reloadedTeamConfig, localConfig, { filterAgents: filterAgents2 });
6587
6711
  }
6588
6712
  log.success("teamai initialized successfully!");
6589
6713
  log.info("Skills, rules, env and docs will auto-sync on each session start (via hooks).");
@@ -6782,6 +6906,300 @@ var init_tokenizer = __esm({
6782
6906
  }
6783
6907
  });
6784
6908
 
6909
+ // src/votes.ts
6910
+ var votes_exports = {};
6911
+ __export(votes_exports, {
6912
+ incrementRecalled: () => incrementRecalled,
6913
+ incrementUpvoted: () => incrementUpvoted,
6914
+ loadUserVotes: () => loadUserVotes,
6915
+ mergeDeltas: () => mergeDeltas,
6916
+ migrateV1ToV2: () => migrateV1ToV2,
6917
+ recallFeedback: () => recallFeedback,
6918
+ saveUserVotes: () => saveUserVotes,
6919
+ syncVotesToTeam: () => syncVotesToTeam
6920
+ });
6921
+ import path30 from "path";
6922
+ import YAML9 from "yaml";
6923
+ function migrateV1ToV2(v1) {
6924
+ const votes = {};
6925
+ for (const [docId, entry] of Object.entries(v1.votes)) {
6926
+ votes[docId] = {
6927
+ recalled_count: 1,
6928
+ upvoted_count: 0,
6929
+ last_recalled_at: entry.at
6930
+ };
6931
+ }
6932
+ return { version: 2, votes, deltas: {} };
6933
+ }
6934
+ async function loadUserVotes(votePath) {
6935
+ const content = await readFileSafe(votePath);
6936
+ if (!content) return { version: 2, votes: {}, deltas: {} };
6937
+ let parsed;
6938
+ try {
6939
+ parsed = YAML9.parse(content);
6940
+ } catch {
6941
+ return { version: 2, votes: {}, deltas: {} };
6942
+ }
6943
+ if (!parsed || typeof parsed !== "object") {
6944
+ return { version: 2, votes: {}, deltas: {} };
6945
+ }
6946
+ const obj = parsed;
6947
+ if (obj["version"] === 2) {
6948
+ const v2 = obj;
6949
+ if (!v2.deltas) v2.deltas = {};
6950
+ return v2;
6951
+ }
6952
+ if (obj["votes"] !== void 0) {
6953
+ const migrated = migrateV1ToV2(obj);
6954
+ await saveUserVotes(votePath, migrated);
6955
+ return migrated;
6956
+ }
6957
+ return { version: 2, votes: {}, deltas: {} };
6958
+ }
6959
+ async function saveUserVotes(votePath, votes) {
6960
+ await ensureDir(path30.dirname(votePath));
6961
+ await writeFile(votePath, YAML9.stringify(votes));
6962
+ }
6963
+ async function incrementRecalled(votePath, docIds) {
6964
+ if (docIds.length === 0) return;
6965
+ const data = await loadUserVotes(votePath);
6966
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6967
+ for (const docId of docIds) {
6968
+ if (!data.votes[docId]) {
6969
+ data.votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
6970
+ }
6971
+ data.votes[docId].recalled_count++;
6972
+ data.votes[docId].last_recalled_at = now;
6973
+ if (!data.deltas[docId]) {
6974
+ data.deltas[docId] = { recalled_delta: 0, upvoted_delta: 0 };
6975
+ }
6976
+ data.deltas[docId].recalled_delta++;
6977
+ }
6978
+ await saveUserVotes(votePath, data);
6979
+ }
6980
+ async function incrementUpvoted(votePath, docIds) {
6981
+ if (docIds.length === 0) return;
6982
+ const data = await loadUserVotes(votePath);
6983
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6984
+ for (const docId of docIds) {
6985
+ if (!data.votes[docId]) {
6986
+ data.votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
6987
+ }
6988
+ data.votes[docId].upvoted_count++;
6989
+ data.votes[docId].last_upvoted_at = now;
6990
+ if (!data.deltas[docId]) {
6991
+ data.deltas[docId] = { recalled_delta: 0, upvoted_delta: 0 };
6992
+ }
6993
+ data.deltas[docId].upvoted_delta++;
6994
+ }
6995
+ await saveUserVotes(votePath, data);
6996
+ }
6997
+ function mergeDeltas(local, remote) {
6998
+ const votes = {};
6999
+ for (const [docId, entry] of Object.entries(remote.votes)) {
7000
+ votes[docId] = { ...entry };
7001
+ }
7002
+ for (const [docId, delta] of Object.entries(local.deltas)) {
7003
+ if (!votes[docId]) {
7004
+ votes[docId] = { recalled_count: 0, upvoted_count: 0, last_recalled_at: "" };
7005
+ }
7006
+ votes[docId].recalled_count = Math.max(0, votes[docId].recalled_count + delta.recalled_delta);
7007
+ votes[docId].upvoted_count = Math.max(0, votes[docId].upvoted_count + delta.upvoted_delta);
7008
+ const localEntry = local.votes[docId];
7009
+ if (localEntry) {
7010
+ if (localEntry.last_recalled_at > (votes[docId].last_recalled_at ?? "")) {
7011
+ votes[docId].last_recalled_at = localEntry.last_recalled_at;
7012
+ }
7013
+ if (localEntry.last_upvoted_at !== void 0 && localEntry.last_upvoted_at > (votes[docId].last_upvoted_at ?? "")) {
7014
+ votes[docId].last_upvoted_at = localEntry.last_upvoted_at;
7015
+ }
7016
+ }
7017
+ }
7018
+ return { version: 2, votes, deltas: {} };
7019
+ }
7020
+ async function syncVotesToTeam(repoPath, username, localVotesDir) {
7021
+ const localVotePath = path30.join(localVotesDir, `${username}.yaml`);
7022
+ const remoteVotePath = path30.join(repoPath, "votes", `${username}.yaml`);
7023
+ const local = await loadUserVotes(localVotePath);
7024
+ if (Object.keys(local.deltas).length === 0) {
7025
+ return false;
7026
+ }
7027
+ const remote = await loadUserVotes(remoteVotePath);
7028
+ const merged = mergeDeltas(local, remote);
7029
+ await saveUserVotes(remoteVotePath, merged);
7030
+ await saveUserVotes(localVotePath, { ...local, votes: merged.votes, deltas: {} });
7031
+ return true;
7032
+ }
7033
+ async function recallFeedback(opts) {
7034
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
7035
+ const { localConfig } = await requireInit3();
7036
+ const { username } = localConfig;
7037
+ const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
7038
+ const votePath = path30.join(VOTES_LOCAL_DIR2, `${username}.yaml`);
7039
+ if (opts.positive) {
7040
+ await incrementUpvoted(votePath, [opts.positive]);
7041
+ log.success(`Upvoted: ${opts.positive}`);
7042
+ return;
7043
+ }
7044
+ if (opts.negative) {
7045
+ const data = await loadUserVotes(votePath);
7046
+ if (!data.votes[opts.negative]) {
7047
+ log.warn(`Document not found in votes: ${opts.negative}`);
7048
+ return;
7049
+ }
7050
+ const entry = data.votes[opts.negative];
7051
+ if (entry.upvoted_count <= 0) {
7052
+ log.warn(`No upvotes to decrement for: ${opts.negative}`);
7053
+ return;
7054
+ }
7055
+ const existingDelta = data.deltas[opts.negative] ?? { recalled_delta: 0, upvoted_delta: 0 };
7056
+ const updated = {
7057
+ ...data,
7058
+ votes: {
7059
+ ...data.votes,
7060
+ [opts.negative]: { ...entry, upvoted_count: entry.upvoted_count - 1 }
7061
+ },
7062
+ deltas: {
7063
+ ...data.deltas,
7064
+ [opts.negative]: { ...existingDelta, upvoted_delta: existingDelta.upvoted_delta - 1 }
7065
+ }
7066
+ };
7067
+ await saveUserVotes(votePath, updated);
7068
+ log.success(`Negative signal recorded for: ${opts.negative}`);
7069
+ return;
7070
+ }
7071
+ log.error("Usage: teamai recall feedback --positive <docId> | --negative <docId>");
7072
+ }
7073
+ var init_votes = __esm({
7074
+ "src/votes.ts"() {
7075
+ "use strict";
7076
+ init_fs();
7077
+ init_logger();
7078
+ }
7079
+ });
7080
+
7081
+ // src/maintenance/confidence.ts
7082
+ var confidence_exports = {};
7083
+ __export(confidence_exports, {
7084
+ computeAllConfidence: () => computeAllConfidence,
7085
+ computeConfidence: () => computeConfidence,
7086
+ writeBackConfidence: () => writeBackConfidence
7087
+ });
7088
+ import path31 from "path";
7089
+ import matter2 from "gray-matter";
7090
+ function computeConfidence(factors) {
7091
+ const { recalledCount, upvotedCount, lastRecalledAt } = factors;
7092
+ const base = Math.min(1, recalledCount * 0.1 + upvotedCount * 0.3);
7093
+ let daysSinceRecall = 180;
7094
+ if (lastRecalledAt) {
7095
+ const elapsed = Date.now() - new Date(lastRecalledAt).getTime();
7096
+ daysSinceRecall = Math.max(0, elapsed / (1e3 * 60 * 60 * 24));
7097
+ }
7098
+ const recency = Math.max(0, 1 - daysSinceRecall / 180);
7099
+ const ratio = upvotedCount / Math.max(1, recalledCount);
7100
+ const score = base * 0.4 + recency * 0.3 + ratio * 0.3;
7101
+ return Math.round(score * 100) / 100;
7102
+ }
7103
+ async function computeAllConfidence(votesDir) {
7104
+ const result = /* @__PURE__ */ new Map();
7105
+ const { loadUserVotes: loadUserVotes2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
7106
+ const files = await listFiles(votesDir);
7107
+ const aggregated = /* @__PURE__ */ new Map();
7108
+ for (const file of files) {
7109
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
7110
+ try {
7111
+ const data = await loadUserVotes2(path31.join(votesDir, file));
7112
+ for (const [docId, entry] of Object.entries(data.votes)) {
7113
+ const existing = aggregated.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7114
+ existing.recalled += entry.recalled_count ?? 0;
7115
+ existing.upvoted += entry.upvoted_count ?? 0;
7116
+ if (entry.last_recalled_at > existing.lastRecalled) {
7117
+ existing.lastRecalled = entry.last_recalled_at;
7118
+ }
7119
+ if (entry.last_upvoted_at && (!existing.lastUpvoted || entry.last_upvoted_at > existing.lastUpvoted)) {
7120
+ existing.lastUpvoted = entry.last_upvoted_at;
7121
+ }
7122
+ aggregated.set(docId, existing);
7123
+ }
7124
+ } catch {
7125
+ log.debug(`confidence: failed to parse votes file: ${file}`);
7126
+ }
7127
+ }
7128
+ for (const [docId, data] of aggregated) {
7129
+ const factors = {
7130
+ recalledCount: data.recalled,
7131
+ upvotedCount: data.upvoted,
7132
+ lastRecalledAt: data.lastRecalled,
7133
+ lastUpvotedAt: data.lastUpvoted
7134
+ };
7135
+ result.set(docId, computeConfidence(factors));
7136
+ }
7137
+ return result;
7138
+ }
7139
+ async function writeBackConfidence(learningsDir, confidenceMap) {
7140
+ let updated = 0;
7141
+ const files = await listFiles(learningsDir);
7142
+ for (const file of files) {
7143
+ if (!file.endsWith(".md")) continue;
7144
+ const docId = file.replace(/\.md$/i, "");
7145
+ const newConf = confidenceMap.get(docId);
7146
+ if (newConf === void 0) continue;
7147
+ const absPath = path31.join(learningsDir, file);
7148
+ const content = await readFileSafe(absPath);
7149
+ if (!content) continue;
7150
+ try {
7151
+ const { data, content: body } = matter2(content);
7152
+ const existingConf = typeof data.confidence === "number" ? data.confidence : void 0;
7153
+ if (existingConf !== void 0 && Math.abs(existingConf - newConf) <= 0.05) continue;
7154
+ data.confidence = newConf;
7155
+ const newContent = matter2.stringify(body, data);
7156
+ await writeFile(absPath, newContent);
7157
+ updated++;
7158
+ } catch {
7159
+ log.debug(`confidence: failed to update frontmatter for: ${file}`);
7160
+ }
7161
+ }
7162
+ if (updated > 0) {
7163
+ log.info(`Updated confidence scores for ${updated} learning(s)`);
7164
+ }
7165
+ return updated;
7166
+ }
7167
+ var init_confidence = __esm({
7168
+ "src/maintenance/confidence.ts"() {
7169
+ "use strict";
7170
+ init_fs();
7171
+ init_logger();
7172
+ }
7173
+ });
7174
+
7175
+ // src/maintenance/hot-cold.ts
7176
+ var hot_cold_exports = {};
7177
+ __export(hot_cold_exports, {
7178
+ COLD_PENALTY: () => COLD_PENALTY,
7179
+ HOT_THRESHOLD: () => HOT_THRESHOLD,
7180
+ annotateHotness: () => annotateHotness
7181
+ });
7182
+ function annotateHotness(entries, confidenceMap) {
7183
+ for (const entry of entries) {
7184
+ const docId = entry.filename.replace(/\.md$/i, "");
7185
+ const confidence = confidenceMap.get(docId);
7186
+ entry.confidence = confidence;
7187
+ if (confidence === void 0) {
7188
+ entry.hotness = 1;
7189
+ } else {
7190
+ entry.hotness = confidence >= HOT_THRESHOLD ? 1 : COLD_PENALTY;
7191
+ }
7192
+ }
7193
+ }
7194
+ var HOT_THRESHOLD, COLD_PENALTY;
7195
+ var init_hot_cold = __esm({
7196
+ "src/maintenance/hot-cold.ts"() {
7197
+ "use strict";
7198
+ HOT_THRESHOLD = 0.5;
7199
+ COLD_PENALTY = 0.3;
7200
+ }
7201
+ });
7202
+
6785
7203
  // src/utils/search-index.ts
6786
7204
  var search_index_exports = {};
6787
7205
  __export(search_index_exports, {
@@ -6795,8 +7213,8 @@ __export(search_index_exports, {
6795
7213
  titleFromFilename: () => titleFromFilename,
6796
7214
  tokenize: () => tokenize
6797
7215
  });
6798
- import path30 from "path";
6799
- import matter2 from "gray-matter";
7216
+ import path32 from "path";
7217
+ import matter3 from "gray-matter";
6800
7218
  function getSearchIndexPath() {
6801
7219
  return `${process.env.HOME ?? ""}/.teamai/search-index.json`;
6802
7220
  }
@@ -6850,7 +7268,7 @@ function inferDomain(frontmatterDomain, tags, filePath, type) {
6850
7268
  function parseLearningDoc(content, filename) {
6851
7269
  if (!content.trim()) return null;
6852
7270
  try {
6853
- const { data, content: body } = matter2(content);
7271
+ const { data, content: body } = matter3(content);
6854
7272
  const meta = {
6855
7273
  title: typeof data.title === "string" ? data.title : void 0,
6856
7274
  author: typeof data.author === "string" ? data.author : void 0,
@@ -6871,31 +7289,61 @@ function titleFromFilename(filename) {
6871
7289
  return filename.replace(/\.md$/i, "").replace(/-\d{4}-\d{2}-\d{2}.*$/, "").replace(/[-_]/g, " ").trim();
6872
7290
  }
6873
7291
  async function aggregateVotes(votesDir) {
6874
- const counts = /* @__PURE__ */ new Map();
7292
+ const scores = /* @__PURE__ */ new Map();
7293
+ const agg = /* @__PURE__ */ new Map();
6875
7294
  const files = await listFiles(votesDir);
6876
7295
  for (const file of files) {
6877
7296
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
6878
- const content = await readFileSafe(path30.join(votesDir, file));
7297
+ const content = await readFileSafe(path32.join(votesDir, file));
6879
7298
  if (!content) continue;
6880
7299
  try {
6881
7300
  const YAML18 = (await import("yaml")).default;
6882
7301
  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);
7302
+ if (!parsed?.votes) continue;
7303
+ if (parsed.version === 2) {
7304
+ const votes = parsed.votes;
7305
+ for (const [docId, entry] of Object.entries(votes)) {
7306
+ const recalled = entry.recalled_count ?? 0;
7307
+ const upvoted = entry.upvoted_count ?? 0;
7308
+ scores.set(docId, (scores.get(docId) ?? 0) + recalled * 0.3 + upvoted * 1);
7309
+ const existing = agg.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7310
+ existing.recalled += recalled;
7311
+ existing.upvoted += upvoted;
7312
+ if (entry.last_recalled_at > existing.lastRecalled) {
7313
+ existing.lastRecalled = entry.last_recalled_at;
7314
+ }
7315
+ agg.set(docId, existing);
7316
+ }
7317
+ } else {
7318
+ const votes = parsed.votes;
7319
+ for (const [docId, entry] of Object.entries(votes)) {
7320
+ scores.set(docId, (scores.get(docId) ?? 0) + 1);
7321
+ const existing = agg.get(docId) ?? { recalled: 0, upvoted: 0, lastRecalled: "" };
7322
+ existing.recalled += 1;
7323
+ if (entry.at > existing.lastRecalled) existing.lastRecalled = entry.at;
7324
+ agg.set(docId, existing);
6886
7325
  }
6887
7326
  }
6888
7327
  } catch {
6889
7328
  log.error(`Failed to parse votes file: ${file}`);
6890
7329
  }
6891
7330
  }
6892
- return counts;
7331
+ const { computeConfidence: computeConfidence2 } = await Promise.resolve().then(() => (init_confidence(), confidence_exports));
7332
+ const confidenceMap = /* @__PURE__ */ new Map();
7333
+ for (const [docId, data] of agg) {
7334
+ confidenceMap.set(docId, computeConfidence2({
7335
+ recalledCount: data.recalled,
7336
+ upvotedCount: data.upvoted,
7337
+ lastRecalledAt: data.lastRecalled
7338
+ }));
7339
+ }
7340
+ return { scores, confidenceMap };
6893
7341
  }
6894
7342
  async function entryFromMdFile(absPath, filenameForId, type, voteCounts) {
6895
- const basename = path30.basename(absPath);
7343
+ const basename = path32.basename(absPath);
6896
7344
  if (basename === CODEBASE_FULL_FILENAME) {
6897
- const dir = path30.dirname(absPath);
6898
- const indexPath = path30.join(dir, CODEBASE_INDEX_FILENAME);
7345
+ const dir = path32.dirname(absPath);
7346
+ const indexPath = path32.join(dir, CODEBASE_INDEX_FILENAME);
6899
7347
  if (await pathExists(indexPath)) {
6900
7348
  log.debug(`Skipping ${absPath}: codebase-index.md exists in same directory`);
6901
7349
  return null;
@@ -6914,7 +7362,7 @@ async function entryFromMdFile(absPath, filenameForId, type, voteCounts) {
6914
7362
  const tags = meta.tags ?? [];
6915
7363
  const rawFrontmatterDomain = (() => {
6916
7364
  try {
6917
- return matter2(content).data["domain"];
7365
+ return matter3(content).data["domain"];
6918
7366
  } catch {
6919
7367
  return void 0;
6920
7368
  }
@@ -6952,7 +7400,7 @@ async function collectFlatMdEntries(dir, type, voteCounts) {
6952
7400
  const out = [];
6953
7401
  for (const filename of files) {
6954
7402
  if (!filename.endsWith(".md")) continue;
6955
- const e = await entryFromMdFile(path30.join(dir, filename), filename, type, voteCounts);
7403
+ const e = await entryFromMdFile(path32.join(dir, filename), filename, type, voteCounts);
6956
7404
  if (e) out.push(e);
6957
7405
  }
6958
7406
  return out;
@@ -6963,7 +7411,7 @@ async function collectRecursiveMdEntries(dir, type, voteCounts) {
6963
7411
  const out = [];
6964
7412
  for (const rel of files) {
6965
7413
  if (!rel.endsWith(".md")) continue;
6966
- const e = await entryFromMdFile(path30.join(dir, rel), rel, type, voteCounts);
7414
+ const e = await entryFromMdFile(path32.join(dir, rel), rel, type, voteCounts);
6967
7415
  if (e) out.push(e);
6968
7416
  }
6969
7417
  return out;
@@ -6975,8 +7423,8 @@ async function collectSkillEntries(dir, voteCounts) {
6975
7423
  const subdirs = await listDirs(current);
6976
7424
  for (const sub of subdirs) {
6977
7425
  if (sub.startsWith(".")) continue;
6978
- const subPath = path30.join(current, sub);
6979
- const skillMd = path30.join(subPath, "SKILL.md");
7426
+ const subPath = path32.join(current, sub);
7427
+ const skillMd = path32.join(subPath, "SKILL.md");
6980
7428
  if (await pathExists(skillMd)) {
6981
7429
  const e = await entryFromMdFile(skillMd, `${sub}.md`, "skills", voteCounts);
6982
7430
  if (e) out.push(e);
@@ -6991,7 +7439,8 @@ async function collectSkillEntries(dir, voteCounts) {
6991
7439
  async function buildIndex(optionsOrLearningsDir, votesDir, indexPath) {
6992
7440
  const start = Date.now();
6993
7441
  const opts = typeof optionsOrLearningsDir === "string" ? { learningsDir: optionsOrLearningsDir, votesDir, indexPath } : optionsOrLearningsDir;
6994
- const voteCounts = opts.votesDir ? await aggregateVotes(opts.votesDir) : /* @__PURE__ */ new Map();
7442
+ const voteAgg = opts.votesDir ? await aggregateVotes(opts.votesDir) : { scores: /* @__PURE__ */ new Map(), confidenceMap: /* @__PURE__ */ new Map() };
7443
+ const voteCounts = voteAgg.scores;
6995
7444
  const entries = [];
6996
7445
  if (opts.learningsDir) {
6997
7446
  entries.push(...await collectFlatMdEntries(opts.learningsDir, "learnings", voteCounts));
@@ -7008,6 +7457,13 @@ async function buildIndex(optionsOrLearningsDir, votesDir, indexPath) {
7008
7457
  if (opts.codebaseDir) {
7009
7458
  entries.push(...await collectRecursiveMdEntries(opts.codebaseDir, "docs", voteCounts));
7010
7459
  }
7460
+ if (voteAgg.confidenceMap.size > 0) {
7461
+ try {
7462
+ const { annotateHotness: annotateHotness2 } = await Promise.resolve().then(() => (init_hot_cold(), hot_cold_exports));
7463
+ annotateHotness2(entries, voteAgg.confidenceMap);
7464
+ } catch {
7465
+ }
7466
+ }
7011
7467
  const df = {};
7012
7468
  for (const entry of entries) {
7013
7469
  for (const token of new Set(entry.tokens)) {
@@ -7085,9 +7541,12 @@ function search(query, index, limit = 5) {
7085
7541
  const domainMultiplier = domainWeightRow[entry.domain ?? "neutral"];
7086
7542
  const typeMultiplier = TYPE_BONUS[entry.type];
7087
7543
  score *= domainMultiplier * typeMultiplier;
7088
- if (path30.basename(entry.path ?? "") === CODEBASE_INDEX_FILENAME) {
7544
+ if (path32.basename(entry.path ?? "") === CODEBASE_INDEX_FILENAME) {
7089
7545
  score *= CODEBASE_INDEX_WEIGHT_BOOST;
7090
7546
  }
7547
+ if (entry.hotness !== void 0 && entry.hotness < 1) {
7548
+ score *= entry.hotness;
7549
+ }
7091
7550
  results.push({ entry, score });
7092
7551
  }
7093
7552
  }
@@ -7278,12 +7737,12 @@ __export(usage_tracker_exports, {
7278
7737
  updateKnownSkills: () => updateKnownSkills
7279
7738
  });
7280
7739
  import fs10 from "fs";
7281
- import path31 from "path";
7740
+ import path33 from "path";
7282
7741
  function getUsagePath() {
7283
- return path31.join(process.env.HOME ?? "", ".teamai", "usage.jsonl");
7742
+ return path33.join(process.env.HOME ?? "", ".teamai", "usage.jsonl");
7284
7743
  }
7285
7744
  function getKnownSkillsPath() {
7286
- return path31.join(process.env.HOME ?? "", ".teamai", "known-skills.json");
7745
+ return path33.join(process.env.HOME ?? "", ".teamai", "known-skills.json");
7287
7746
  }
7288
7747
  function extractSkillName(toolInput) {
7289
7748
  try {
@@ -7309,13 +7768,13 @@ function isValidSkillName(name) {
7309
7768
  async function skillExistsOnDisk(skillName) {
7310
7769
  const home = process.env.HOME ?? "";
7311
7770
  for (const dir of SKILL_DIRS) {
7312
- const skillMd = path31.join(home, dir, skillName, "SKILL.md");
7771
+ const skillMd = path33.join(home, dir, skillName, "SKILL.md");
7313
7772
  if (await pathExists(skillMd)) return true;
7314
7773
  }
7315
7774
  const cwd = process.cwd();
7316
- if (path31.resolve(cwd) !== path31.resolve(home)) {
7775
+ if (path33.resolve(cwd) !== path33.resolve(home)) {
7317
7776
  for (const dir of SKILL_DIRS) {
7318
- const skillMd = path31.join(cwd, dir, skillName, "SKILL.md");
7777
+ const skillMd = path33.join(cwd, dir, skillName, "SKILL.md");
7319
7778
  if (await pathExists(skillMd)) return true;
7320
7779
  }
7321
7780
  }
@@ -7323,7 +7782,7 @@ async function skillExistsOnDisk(skillName) {
7323
7782
  }
7324
7783
  async function appendUsageEvent(event) {
7325
7784
  try {
7326
- await ensureDir(path31.dirname(getUsagePath()));
7785
+ await ensureDir(path33.dirname(getUsagePath()));
7327
7786
  const line = JSON.stringify(event) + "\n";
7328
7787
  await fs10.promises.appendFile(getUsagePath(), line, "utf-8");
7329
7788
  log.debug(`Tracked skill: ${event.skill}`);
@@ -7717,7 +8176,7 @@ __export(dashboard_collector_exports, {
7717
8176
  scanTranscriptStop: () => scanTranscriptStop
7718
8177
  });
7719
8178
  import fs12 from "fs";
7720
- import path32 from "path";
8179
+ import path34 from "path";
7721
8180
  import readline2 from "readline";
7722
8181
  async function readStdin2() {
7723
8182
  if (process.stdin.isTTY) return "";
@@ -7768,7 +8227,7 @@ async function scanTranscriptStop(transcriptPath) {
7768
8227
  let prompts = 0;
7769
8228
  const tokens = emptyTokenUsage();
7770
8229
  const countedUsageKeys = /* @__PURE__ */ new Set();
7771
- if (path32.basename(transcriptPath) === "index.json") {
8230
+ if (path34.basename(transcriptPath) === "index.json") {
7772
8231
  const cb = await scanCodebuddyIndex(transcriptPath);
7773
8232
  if (cb) return cb;
7774
8233
  }
@@ -7967,12 +8426,12 @@ async function parseHookEvent(raw, tool) {
7967
8426
  return event;
7968
8427
  }
7969
8428
  function getEventsPath() {
7970
- return path32.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
8429
+ return path34.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
7971
8430
  }
7972
8431
  async function appendEvent(event) {
7973
8432
  try {
7974
8433
  const eventsPath = getEventsPath();
7975
- await ensureDir(path32.dirname(eventsPath));
8434
+ await ensureDir(path34.dirname(eventsPath));
7976
8435
  const line = JSON.stringify(event) + "\n";
7977
8436
  await fs12.promises.appendFile(eventsPath, line, "utf-8");
7978
8437
  const detail = event.toolName ? ` [tool=${event.toolName}]` : event.promptSummary ? ` [prompt=${event.promptSummary.slice(0, 60)}]` : "";
@@ -8254,20 +8713,20 @@ __export(digest_exports, {
8254
8713
  summarizeConversation: () => summarizeConversation,
8255
8714
  summarizeInterventions: () => summarizeInterventions
8256
8715
  });
8257
- import YAML9 from "yaml";
8258
- import path33 from "path";
8716
+ import YAML10 from "yaml";
8717
+ import path35 from "path";
8259
8718
  import fs13 from "fs";
8260
8719
  async function loadTeamStats(repoPath) {
8261
- const statsDir = path33.join(repoPath, "stats");
8720
+ const statsDir = path35.join(repoPath, "stats");
8262
8721
  const stats = [];
8263
8722
  try {
8264
8723
  const files = await listFiles(statsDir);
8265
8724
  for (const file of files) {
8266
8725
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
8267
- const content = await readFileSafe(path33.join(statsDir, file));
8726
+ const content = await readFileSafe(path35.join(statsDir, file));
8268
8727
  if (!content) continue;
8269
8728
  try {
8270
- const parsed = YAML9.parse(content);
8729
+ const parsed = YAML10.parse(content);
8271
8730
  if (parsed?.username && parsed?.skills) {
8272
8731
  stats.push(parsed);
8273
8732
  }
@@ -8361,17 +8820,17 @@ async function getRecentSkillChanges(repoPath) {
8361
8820
  return changes;
8362
8821
  }
8363
8822
  async function getRecentSessions(repoPath) {
8364
- const sessionsDir = path33.join(repoPath, "sessions");
8823
+ const sessionsDir = path35.join(repoPath, "sessions");
8365
8824
  const summaries = [];
8366
8825
  try {
8367
8826
  const userDirs = await fs13.promises.readdir(sessionsDir, { withFileTypes: true });
8368
8827
  for (const userDir of userDirs) {
8369
8828
  if (!userDir.isDirectory()) continue;
8370
- const userSessionsDir = path33.join(sessionsDir, userDir.name);
8829
+ const userSessionsDir = path35.join(sessionsDir, userDir.name);
8371
8830
  const files = await listFiles(userSessionsDir);
8372
8831
  for (const file of files) {
8373
8832
  if (!file.endsWith(".md")) continue;
8374
- const content = await readFileSafe(path33.join(userSessionsDir, file));
8833
+ const content = await readFileSafe(path35.join(userSessionsDir, file));
8375
8834
  if (content) {
8376
8835
  summaries.push(`[${userDir.name}] ${file}:
8377
8836
  ${content.slice(0, 500)}`);
@@ -8383,7 +8842,7 @@ ${content.slice(0, 500)}`);
8383
8842
  return summaries;
8384
8843
  }
8385
8844
  async function getRecentLearnings(repoPath) {
8386
- const learningsDir = path33.join(repoPath, "learnings");
8845
+ const learningsDir = path35.join(repoPath, "learnings");
8387
8846
  const recent = [];
8388
8847
  let total = 0;
8389
8848
  try {
@@ -8396,7 +8855,7 @@ async function getRecentLearnings(repoPath) {
8396
8855
  if (!dateMatch) continue;
8397
8856
  const fileDate = dateMatch[1];
8398
8857
  if (fileDate < cutoff) continue;
8399
- const content = await readFileSafe(path33.join(learningsDir, filename));
8858
+ const content = await readFileSafe(path35.join(learningsDir, filename));
8400
8859
  if (!content) continue;
8401
8860
  const parsed = parseLearningDoc(content, filename);
8402
8861
  const title = parsed?.meta.title ?? titleFromFilename(filename);
@@ -8580,8 +9039,8 @@ __export(stats_exports, {
8580
9039
  aggregateUsage: () => aggregateUsage,
8581
9040
  showStats: () => showStats
8582
9041
  });
8583
- import YAML10 from "yaml";
8584
- import path34 from "path";
9042
+ import YAML11 from "yaml";
9043
+ import path36 from "path";
8585
9044
  function aggregateUsage(events) {
8586
9045
  const map = /* @__PURE__ */ new Map();
8587
9046
  for (const event of events) {
@@ -8606,10 +9065,10 @@ async function loadReportedStats() {
8606
9065
  try {
8607
9066
  const config = await detectProjectConfig() ?? await loadLocalConfig();
8608
9067
  if (!config) return null;
8609
- const statsPath = path34.join(config.repo.localPath, "stats", `${config.username}.yaml`);
9068
+ const statsPath = path36.join(config.repo.localPath, "stats", `${config.username}.yaml`);
8610
9069
  const content = await readFileSafe(statsPath);
8611
9070
  if (!content) return null;
8612
- const parsed = YAML10.parse(content);
9071
+ const parsed = YAML11.parse(content);
8613
9072
  if (parsed?.username && parsed?.skills) return parsed;
8614
9073
  return null;
8615
9074
  } catch {
@@ -8762,13 +9221,13 @@ __export(team_push_exports, {
8762
9221
  mergeStats: () => mergeStats,
8763
9222
  reportUsageToTeam: () => reportUsageToTeam
8764
9223
  });
8765
- import YAML11 from "yaml";
8766
- import path35 from "path";
9224
+ import YAML12 from "yaml";
9225
+ import path37 from "path";
8767
9226
  async function readExistingStats(statsPath) {
8768
9227
  try {
8769
9228
  const content = await readFileSafe(statsPath);
8770
9229
  if (!content) return null;
8771
- const parsed = YAML11.parse(content);
9230
+ const parsed = YAML12.parse(content);
8772
9231
  if (parsed?.username && parsed?.skills) return parsed;
8773
9232
  return null;
8774
9233
  } catch {
@@ -8801,7 +9260,7 @@ function mergeStats(existing, username, newEvents) {
8801
9260
  };
8802
9261
  }
8803
9262
  function getReportedInterventionsPath() {
8804
- return path35.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-interventions.json");
9263
+ return path37.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-interventions.json");
8805
9264
  }
8806
9265
  async function readReportedInterventions() {
8807
9266
  const parsed = await readJson(getReportedInterventionsPath());
@@ -8839,7 +9298,7 @@ function hasInterventionDelta(d) {
8839
9298
  return d.sessions > 0 || d.interrupt > 0 || d.toolReject > 0 || d.correction > 0;
8840
9299
  }
8841
9300
  function getReportedPromptTokensPath() {
8842
- return path35.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-prompt-tokens.json");
9301
+ return path37.join(process.env.HOME ?? "", ".teamai", "dashboard", "reported-prompt-tokens.json");
8843
9302
  }
8844
9303
  async function readReportedPromptTokens() {
8845
9304
  try {
@@ -8854,7 +9313,7 @@ async function readReportedPromptTokens() {
8854
9313
  async function writeReportedPromptTokens(data) {
8855
9314
  try {
8856
9315
  const p = getReportedPromptTokensPath();
8857
- await ensureDir(path35.dirname(p));
9316
+ await ensureDir(path37.dirname(p));
8858
9317
  await writeFile(p, JSON.stringify(data));
8859
9318
  } catch (e) {
8860
9319
  log.error(`Failed to persist reported prompt/token snapshot: ${e.message}`);
@@ -8932,9 +9391,9 @@ async function reportUsageToTeam(repoPath, username, options) {
8932
9391
  await resetToCleanMaster(git, repoPath);
8933
9392
  await pullRepo(repoPath);
8934
9393
  if (hasUsage || hasInterventions || hasPromptTokens) {
8935
- const statsDir = path35.join(repoPath, "stats");
9394
+ const statsDir = path37.join(repoPath, "stats");
8936
9395
  await ensureDir(statsDir);
8937
- const statsPath = path35.join(statsDir, `${username}.yaml`);
9396
+ const statsPath = path37.join(statsDir, `${username}.yaml`);
8938
9397
  const existing = await readExistingStats(statsPath);
8939
9398
  const newStats = hasUsage ? aggregateUsage(events) : [];
8940
9399
  const merged = mergeStats(existing, username, newStats);
@@ -8946,22 +9405,15 @@ async function reportUsageToTeam(repoPath, username, options) {
8946
9405
  merged.prompts = pt.prompts;
8947
9406
  merged.tokens = pt.tokens;
8948
9407
  }
8949
- await writeFile(statsPath, YAML11.stringify(merged));
9408
+ await writeFile(statsPath, YAML12.stringify(merged));
8950
9409
  filesToPush.push(`stats/${username}.yaml`);
8951
9410
  }
8952
9411
  try {
8953
9412
  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
- }
9413
+ const { syncVotesToTeam: syncVotesToTeam2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
9414
+ const synced = await syncVotesToTeam2(repoPath, username, VOTES_LOCAL_DIR);
9415
+ if (synced) {
9416
+ filesToPush.push(`votes/${username}.yaml`);
8965
9417
  }
8966
9418
  }
8967
9419
  } catch (e) {
@@ -9024,9 +9476,9 @@ __export(pull_exports, {
9024
9476
  pull: () => pull,
9025
9477
  scanRoleAwareSkills: () => scanRoleAwareSkills
9026
9478
  });
9027
- import path36 from "path";
9479
+ import path38 from "path";
9028
9480
  import fse6 from "fs-extra";
9029
- import matter3 from "gray-matter";
9481
+ import matter4 from "gray-matter";
9030
9482
  async function refreshTeamRepo(localConfig) {
9031
9483
  if (localConfig.repo.kind === "http") {
9032
9484
  const { resolveApiKey: resolveApiKey2 } = await Promise.resolve().then(() => (init_api_key(), api_key_exports));
@@ -9088,14 +9540,14 @@ async function buildRolePullContext(localConfig) {
9088
9540
  const activeSkillNames = /* @__PURE__ */ new Set();
9089
9541
  const inactiveSkillNames = /* @__PURE__ */ new Set();
9090
9542
  for (const namespace of activeNamespaces.skills) {
9091
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9543
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9092
9544
  const names = await listDirs(namespaceDir);
9093
9545
  for (const name of names) {
9094
9546
  activeSkillNames.add(name);
9095
9547
  }
9096
9548
  }
9097
9549
  for (const namespace of inactiveSkillNamespaces) {
9098
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9550
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9099
9551
  const names = await listDirs(namespaceDir);
9100
9552
  for (const name of names) {
9101
9553
  inactiveSkillNames.add(name);
@@ -9115,7 +9567,7 @@ function filterRulesByKnowledgeNamespaces(rules, knowledgeNamespaces) {
9115
9567
  async function scanRoleAwareSkills(localConfig, namespaces) {
9116
9568
  const items = /* @__PURE__ */ new Map();
9117
9569
  for (const namespace of namespaces.skills) {
9118
- const namespaceDir = path36.join(localConfig.repo.localPath, "skills", namespace);
9570
+ const namespaceDir = path38.join(localConfig.repo.localPath, "skills", namespace);
9119
9571
  const dirs = await listDirs(namespaceDir);
9120
9572
  for (const dir of dirs) {
9121
9573
  const existing = items.get(dir);
@@ -9125,7 +9577,7 @@ async function scanRoleAwareSkills(localConfig, namespaces) {
9125
9577
  items.set(dir, {
9126
9578
  name: dir,
9127
9579
  type: "skills",
9128
- sourcePath: path36.join(namespaceDir, dir),
9580
+ sourcePath: path38.join(namespaceDir, dir),
9129
9581
  relativePath: `skills/${namespace}/${dir}`,
9130
9582
  namespace
9131
9583
  });
@@ -9138,13 +9590,13 @@ async function cleanupInactiveNamespaceSkills(teamConfig, localConfig, activeSki
9138
9590
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
9139
9591
  if (!toolPath.skills) continue;
9140
9592
  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));
9593
+ if (!await pathExists(path38.join(baseDir, toolPath.skills))) continue;
9594
+ const localSkillNames = await listDirs(path38.join(baseDir, toolPath.skills));
9143
9595
  for (const skillName of localSkillNames) {
9144
9596
  if (BUILTIN_SKILL_NAMES.has(skillName)) continue;
9145
9597
  if (activeSkillNames.has(skillName)) continue;
9146
9598
  if (!inactiveSkillNames.has(skillName)) continue;
9147
- const localSkillDir = path36.join(baseDir, toolPath.skills, skillName);
9599
+ const localSkillDir = path38.join(baseDir, toolPath.skills, skillName);
9148
9600
  await remove(localSkillDir);
9149
9601
  log.debug(`[${localConfig.scope}] Removed inactive role-scoped skill ${skillName} from ${tool}`);
9150
9602
  }
@@ -9156,10 +9608,10 @@ async function getExistingLocalNames(type, items, teamConfig, localConfig) {
9156
9608
  if (type === "skills") {
9157
9609
  for (const [_tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
9158
9610
  if (!toolPath.skills) continue;
9159
- const skillsDir = path36.join(baseDir, toolPath.skills);
9611
+ const skillsDir = path38.join(baseDir, toolPath.skills);
9160
9612
  if (!await pathExists(skillsDir)) continue;
9161
9613
  for (const item of items) {
9162
- const skillDir = path36.join(skillsDir, item.name);
9614
+ const skillDir = path38.join(skillsDir, item.name);
9163
9615
  if (await pathExists(skillDir)) {
9164
9616
  existing.add(item.name);
9165
9617
  }
@@ -9369,7 +9821,7 @@ async function pullForScope(localConfig, options) {
9369
9821
  if (!dir) continue;
9370
9822
  if (!await ResourceHandler.isToolInstalled(dir, baseDir)) continue;
9371
9823
  for (const name of tombstones) {
9372
- const localPath = path36.join(baseDir, dir, ext ? `${name}${ext}` : name);
9824
+ const localPath = path38.join(baseDir, dir, ext ? `${name}${ext}` : name);
9373
9825
  if (await pathExists(localPath)) {
9374
9826
  await remove(localPath);
9375
9827
  log.debug(`[${scopeLabel}] Cleaned up tombstoned ${type} ${name} from ${dir}`);
@@ -9391,14 +9843,14 @@ async function pullForScope(localConfig, options) {
9391
9843
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9392
9844
  if (!toolPath.skills) continue;
9393
9845
  if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) continue;
9394
- const skillsDir = path36.join(baseDir, toolPath.skills);
9846
+ const skillsDir = path38.join(baseDir, toolPath.skills);
9395
9847
  if (!await pathExists(skillsDir)) continue;
9396
9848
  const localDirs = await listDirs(skillsDir);
9397
9849
  for (const dir of localDirs) {
9398
9850
  if (BUILTIN_SKILL_NAMES.has(dir)) continue;
9399
9851
  if (desiredSkillNames.has(dir)) continue;
9400
9852
  if (!knownRepoSkillNames.has(dir)) continue;
9401
- const skillDir = path36.join(skillsDir, dir);
9853
+ const skillDir = path38.join(skillsDir, dir);
9402
9854
  await remove(skillDir);
9403
9855
  log.debug(`Removed excluded skill ${dir} from ${tool}`);
9404
9856
  }
@@ -9422,18 +9874,18 @@ async function pullForScope(localConfig, options) {
9422
9874
  }
9423
9875
  if (!options.dryRun) {
9424
9876
  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");
9877
+ const learningsRepoDir = path38.join(localConfig.repo.localPath, "learnings");
9878
+ const docsRepoDir = path38.join(localConfig.repo.localPath, "docs");
9879
+ const rulesRepoDir = path38.join(localConfig.repo.localPath, "rules");
9880
+ const skillsRepoDir = path38.join(localConfig.repo.localPath, "skills");
9881
+ const votesDir = path38.join(localConfig.repo.localPath, "votes");
9430
9882
  let learningsCount = 0;
9431
9883
  let effectiveLearningsDir;
9432
9884
  if (localConfig.scope === "user") {
9433
9885
  if (await pathExists(learningsRepoDir)) {
9434
9886
  await fse6.copy(learningsRepoDir, LEARNINGS_LOCAL_DIR, {
9435
9887
  overwrite: true,
9436
- filter: (src) => !path36.basename(src).startsWith(".")
9888
+ filter: (src) => !path38.basename(src).startsWith(".")
9437
9889
  });
9438
9890
  const allFiles = await listFiles(learningsRepoDir);
9439
9891
  learningsCount = allFiles.filter((f) => f.endsWith(".md")).length;
@@ -9447,12 +9899,12 @@ async function pullForScope(localConfig, options) {
9447
9899
  }
9448
9900
  }
9449
9901
  const hasAnySource = effectiveLearningsDir || await pathExists(docsRepoDir) || await pathExists(rulesRepoDir) || await pathExists(skillsRepoDir);
9450
- const repoCodebaseDir = path36.join(localConfig.repo.localPath, "docs", "team-codebase");
9902
+ const repoCodebaseDir = path38.join(localConfig.repo.localPath, "docs", "team-codebase");
9451
9903
  const effectiveCodebaseDir = await pathExists(repoCodebaseDir) ? repoCodebaseDir : void 0;
9452
9904
  if (hasAnySource || effectiveCodebaseDir) {
9453
9905
  const votesExist = await pathExists(votesDir);
9454
9906
  const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
9455
- const indexPath = path36.join(teamaiHome, "search-index.json");
9907
+ const indexPath = path38.join(teamaiHome, "search-index.json");
9456
9908
  const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
9457
9909
  const elapsed = await buildIndex2({
9458
9910
  learningsDir: effectiveLearningsDir,
@@ -9476,18 +9928,18 @@ async function pullForScope(localConfig, options) {
9476
9928
  }
9477
9929
  if (!options.dryRun) {
9478
9930
  try {
9479
- const teamDomainsPath = path36.join(localConfig.repo.localPath, ".teamai", "domains.yaml");
9931
+ const teamDomainsPath = path38.join(localConfig.repo.localPath, ".teamai", "domains.yaml");
9480
9932
  if (await pathExists(teamDomainsPath)) {
9481
- const localDomainsDir = path36.join(process.cwd(), ".teamai");
9933
+ const localDomainsDir = path38.join(process.cwd(), ".teamai");
9482
9934
  await fse6.ensureDir(localDomainsDir);
9483
- await fse6.copy(teamDomainsPath, path36.join(localDomainsDir, "domains.yaml"), { overwrite: true });
9935
+ await fse6.copy(teamDomainsPath, path38.join(localDomainsDir, "domains.yaml"), { overwrite: true });
9484
9936
  }
9485
9937
  } catch {
9486
9938
  }
9487
9939
  }
9488
9940
  if (!options.dryRun) {
9489
9941
  try {
9490
- const culturePath = path36.join(localConfig.repo.localPath, "culture.md");
9942
+ const culturePath = path38.join(localConfig.repo.localPath, "culture.md");
9491
9943
  if (await pathExists(culturePath)) {
9492
9944
  const cultureContent = await readFileSafe(culturePath);
9493
9945
  if (cultureContent) {
@@ -9497,7 +9949,7 @@ async function pullForScope(localConfig, options) {
9497
9949
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9498
9950
  if (!toolPath.claudemd) continue;
9499
9951
  if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
9500
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
9952
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9501
9953
  try {
9502
9954
  await injectClaudeMdSection(claudeMdPath, TEAMAI_CULTURE_START, TEAMAI_CULTURE_END, compiled);
9503
9955
  log.debug(`Injected culture into ${tool} CLAUDE.md`);
@@ -9526,7 +9978,7 @@ async function pullForScope(localConfig, options) {
9526
9978
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9527
9979
  if (!toolPath.claudemd) continue;
9528
9980
  if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
9529
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
9981
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9530
9982
  try {
9531
9983
  await injectClaudeMdSection(claudeMdPath, TEAMAI_CLAUDEMD_START, TEAMAI_CLAUDEMD_END, compiled);
9532
9984
  log.debug(`Injected shared instructions into ${tool} CLAUDE.md`);
@@ -9549,7 +10001,7 @@ async function pullForScope(localConfig, options) {
9549
10001
  for (const [tool, toolPath] of Object.entries(freshConfig.toolPaths)) {
9550
10002
  if (!toolPath.claudemd || !toolPath.agents) continue;
9551
10003
  if (!await ResourceHandler.isToolInstalled(toolPath.agents, baseDir)) continue;
9552
- const claudeMdPath = path36.join(baseDir, toolPath.claudemd);
10004
+ const claudeMdPath = path38.join(baseDir, toolPath.claudemd);
9553
10005
  try {
9554
10006
  await injectClaudeMdSection(
9555
10007
  claudeMdPath,
@@ -9609,14 +10061,14 @@ async function pullForScope(localConfig, options) {
9609
10061
  if (!options.silent && !options.dryRun) {
9610
10062
  try {
9611
10063
  const YAML18 = (await import("yaml")).default;
9612
- const { listFiles: listFiles2, readFileSafe: readFileSafe4 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
10064
+ const { listFiles: listFiles2, readFileSafe: readFileSafe5 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
9613
10065
  const { getRecommendations: getRecommendations2, displayRecommendations: displayRecommendations2 } = await Promise.resolve().then(() => (init_skill_recommend(), skill_recommend_exports));
9614
- const statsDir = path36.join(localConfig.repo.localPath, "stats");
10066
+ const statsDir = path38.join(localConfig.repo.localPath, "stats");
9615
10067
  const files = await listFiles2(statsDir);
9616
10068
  const teamStats = [];
9617
10069
  for (const file of files) {
9618
10070
  if (!file.endsWith(".yaml")) continue;
9619
- const content = await readFileSafe4(path36.join(statsDir, file));
10071
+ const content = await readFileSafe5(path38.join(statsDir, file));
9620
10072
  if (!content) continue;
9621
10073
  try {
9622
10074
  const parsed = YAML18.parse(content);
@@ -9635,7 +10087,7 @@ async function pullForScope(localConfig, options) {
9635
10087
  function compileCulture(raw) {
9636
10088
  let parsed;
9637
10089
  try {
9638
- parsed = matter3(raw);
10090
+ parsed = matter4(raw);
9639
10091
  } catch {
9640
10092
  return null;
9641
10093
  }
@@ -9701,15 +10153,24 @@ function compileRecallRulesBlock() {
9701
10153
  "",
9702
10154
  "## Team Knowledge Recall (teamai)",
9703
10155
  "",
9704
- "**Before** starting any task that involves code changes, debugging,",
9705
- "or design decisions, you **MUST** first invoke the `teamai-recall`",
10156
+ "**Before** starting a task that involves code changes, debugging,",
10157
+ "or design decisions, you **SHOULD** invoke the `teamai-recall`",
9706
10158
  "subagent via the Agent tool with a concise natural-language",
9707
- "description of the task. The subagent will return a compact summary",
9708
- "of relevant team knowledge (skills, learnings, docs, rules) without",
9709
- "polluting this conversation with raw content.",
10159
+ "description of the task \u2014 unless one of these skip conditions applies:",
10160
+ "",
10161
+ "1. **User already provided context** \u2014 the user referenced specific files,",
10162
+ ' gave a solution, or said "the answer is in this directory/file".',
10163
+ "2. **Local files have the answer** \u2014 the task info is directly available",
10164
+ " from the current workspace (e.g. fixing an obvious bug in the current file).",
10165
+ "3. **Trivial/local change** \u2014 small modifications to known files (typo fix,",
10166
+ " parameter tweak, formatting) that need no additional knowledge.",
9710
10167
  "",
9711
- "**Important constraints on agent sequencing:**",
9712
- "1. Always invoke `teamai-recall` subagent **first and alone** \u2014 never",
10168
+ "The subagent will return a compact summary of relevant team knowledge",
10169
+ "(skills, learnings, docs, rules) without polluting this conversation",
10170
+ "with raw content.",
10171
+ "",
10172
+ "**Important constraints on agent sequencing (when recall is invoked):**",
10173
+ "1. Invoke `teamai-recall` subagent **first and alone** \u2014 never",
9713
10174
  " launch it in parallel with Explore or other research agents.",
9714
10175
  "2. After recall returns results, use Read to get full content of the",
9715
10176
  " returned files if you need more detail. Do NOT launch Explore agents",
@@ -9740,7 +10201,7 @@ function compileRecallRulesBlock() {
9740
10201
  return lines.join("\n");
9741
10202
  }
9742
10203
  async function collectClaudemdFiles(repoPath, roleContext) {
9743
- const claudemdDir = path36.join(repoPath, "claudemd");
10204
+ const claudemdDir = path38.join(repoPath, "claudemd");
9744
10205
  if (!await pathExists(claudemdDir)) return [];
9745
10206
  let namespaceDirs;
9746
10207
  if (roleContext) {
@@ -9750,11 +10211,11 @@ async function collectClaudemdFiles(repoPath, roleContext) {
9750
10211
  }
9751
10212
  const contents = [];
9752
10213
  for (const ns of namespaceDirs) {
9753
- const nsDir = path36.join(claudemdDir, ns);
10214
+ const nsDir = path38.join(claudemdDir, ns);
9754
10215
  if (!await pathExists(nsDir)) continue;
9755
10216
  const files = (await listFiles(nsDir)).filter((f) => f.endsWith(".md")).sort();
9756
10217
  for (const file of files) {
9757
- const content = await readFileSafe(path36.join(nsDir, file));
10218
+ const content = await readFileSafe(path38.join(nsDir, file));
9758
10219
  if (content) contents.push(content);
9759
10220
  }
9760
10221
  }
@@ -9762,7 +10223,7 @@ async function collectClaudemdFiles(repoPath, roleContext) {
9762
10223
  }
9763
10224
  async function autoMigrateHooksIfNeeded() {
9764
10225
  const home = process.env.HOME ?? "";
9765
- const primarySettings = path36.join(home, ".claude", "settings.json");
10226
+ const primarySettings = path38.join(home, ".claude", "settings.json");
9766
10227
  if (!await pathExists(primarySettings)) return;
9767
10228
  const content = await readFileSafe(primarySettings);
9768
10229
  if (!content) return;
@@ -9773,7 +10234,7 @@ async function autoMigrateHooksIfNeeded() {
9773
10234
  const { injectHooksToAllTools: injectHooksToAllTools2 } = await Promise.resolve().then(() => (init_hooks2(), hooks_exports));
9774
10235
  const { localConfig, teamConfig } = await autoDetectInit2();
9775
10236
  const baseDir = resolveBaseDir(localConfig);
9776
- await injectHooksToAllTools2(teamConfig.toolPaths, baseDir);
10237
+ await injectHooksToAllTools2(teamConfig.toolPaths, baseDir, localConfig.enabledAgents);
9777
10238
  log.debug("Hooks migrated to dispatch format");
9778
10239
  }
9779
10240
  async function pull(options) {
@@ -9865,7 +10326,8 @@ async function reconcileHooksAllScopes(userConfig, projectConfig, options) {
9865
10326
  const { reconcileTeamHooksForConfig: reconcileTeamHooksForConfig2 } = await Promise.resolve().then(() => (init_hooks2(), hooks_exports));
9866
10327
  const teamDefs = await reconcileTeamHooksForConfig2(teamConfig, localConfig, {
9867
10328
  auto: true,
9868
- silent: options.silent
10329
+ silent: options.silent,
10330
+ filterAgents: localConfig.enabledAgents
9869
10331
  });
9870
10332
  if (teamDefs.length > 0) {
9871
10333
  log.debug(`[${localConfig.scope}] Reconciled ${teamDefs.length} team hook(s)`);
@@ -9983,17 +10445,17 @@ var init_known_agents = __esm({
9983
10445
  });
9984
10446
 
9985
10447
  // src/agent-skills.ts
9986
- import path37 from "path";
9987
- import YAML12 from "yaml";
10448
+ import path39 from "path";
10449
+ import YAML13 from "yaml";
9988
10450
  async function buildClassifyContext(localConfig) {
9989
10451
  const teamSkills = await collectTeamRepoSkills(localConfig.repo.localPath);
9990
10452
  const sourceSkills = /* @__PURE__ */ new Map();
9991
10453
  try {
9992
- const sourcesDir = path37.join(process.env.HOME ?? "", ".teamai", "sources");
10454
+ const sourcesDir = path39.join(process.env.HOME ?? "", ".teamai", "sources");
9993
10455
  if (await pathExists(sourcesDir)) {
9994
10456
  const sourceNames = await listDirs(sourcesDir);
9995
10457
  for (const sourceName of sourceNames) {
9996
- const manifestPath = path37.join(sourcesDir, sourceName, "installed.json");
10458
+ const manifestPath = path39.join(sourcesDir, sourceName, "installed.json");
9997
10459
  const raw = await readFileSafe(manifestPath);
9998
10460
  if (!raw) continue;
9999
10461
  try {
@@ -10010,13 +10472,13 @@ async function buildClassifyContext(localConfig) {
10010
10472
  return { teamSkills, sourceSkills };
10011
10473
  }
10012
10474
  async function collectTeamRepoSkills(repoPath) {
10013
- const teamSkillsDir = path37.join(repoPath, "skills");
10475
+ const teamSkillsDir = path39.join(repoPath, "skills");
10014
10476
  const result = /* @__PURE__ */ new Map();
10015
10477
  if (!await pathExists(teamSkillsDir)) return result;
10016
10478
  const topDirs = await listDirs(teamSkillsDir);
10017
10479
  for (const dir of topDirs) {
10018
- const dirPath = path37.join(teamSkillsDir, dir);
10019
- const hasSkillMd = await pathExists(path37.join(dirPath, "SKILL.md"));
10480
+ const dirPath = path39.join(teamSkillsDir, dir);
10481
+ const hasSkillMd = await pathExists(path39.join(dirPath, "SKILL.md"));
10020
10482
  if (hasSkillMd) {
10021
10483
  result.set(dir, {});
10022
10484
  } else {
@@ -10063,8 +10525,8 @@ async function scanAgentSkills(agent, ctx) {
10063
10525
  const dirs = await listDirs(agent.absoluteSkillsPath);
10064
10526
  for (const name of dirs) {
10065
10527
  if (name.startsWith(".") || name.endsWith("-workspace")) continue;
10066
- const skillDir = path37.join(agent.absoluteSkillsPath, name);
10067
- const skillMd = path37.join(skillDir, "SKILL.md");
10528
+ const skillDir = path39.join(agent.absoluteSkillsPath, name);
10529
+ const skillMd = path39.join(skillDir, "SKILL.md");
10068
10530
  if (!await pathExists(skillMd)) continue;
10069
10531
  const description = await readSkillDescription(skillMd);
10070
10532
  skills.push({
@@ -10084,7 +10546,7 @@ async function readSkillDescription(skillMdPath) {
10084
10546
  if (!fm) return "";
10085
10547
  let parsed;
10086
10548
  try {
10087
- parsed = YAML12.parse(fm[1]);
10549
+ parsed = YAML13.parse(fm[1]);
10088
10550
  } catch {
10089
10551
  return "";
10090
10552
  }
@@ -10114,8 +10576,8 @@ __export(status_exports, {
10114
10576
  list: () => list,
10115
10577
  status: () => status
10116
10578
  });
10117
- import path38 from "path";
10118
- import YAML13 from "yaml";
10579
+ import path40 from "path";
10580
+ import YAML14 from "yaml";
10119
10581
  async function status(options) {
10120
10582
  const { localConfig, teamConfig } = await autoDetectInit();
10121
10583
  const scopeLabel = localConfig.scope;
@@ -10147,20 +10609,20 @@ async function status(options) {
10147
10609
  log.info("Team resources:");
10148
10610
  const repoPath = localConfig.repo.localPath;
10149
10611
  const counts = {};
10150
- const skillsDirs = await listDirs(path38.join(repoPath, "skills"));
10612
+ const skillsDirs = await listDirs(path40.join(repoPath, "skills"));
10151
10613
  counts.skills = skillsDirs.length;
10152
- const rulesFiles = (await listFiles(path38.join(repoPath, "rules"))).filter((f) => f.endsWith(".md"));
10614
+ const rulesFiles = (await listFiles(path40.join(repoPath, "rules"))).filter((f) => f.endsWith(".md"));
10153
10615
  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(".")) : [];
10616
+ const docsExists = await pathExists(path40.join(repoPath, "docs"));
10617
+ const docFiles = docsExists ? (await listFiles(path40.join(repoPath, "docs"))).filter((f) => !f.startsWith(".")) : [];
10156
10618
  counts.docs = docFiles.length;
10157
- const envYamlPath = path38.join(repoPath, "env", "env.yaml");
10619
+ const envYamlPath = path40.join(repoPath, "env", "env.yaml");
10158
10620
  let envCount = 0;
10159
10621
  if (await pathExists(envYamlPath)) {
10160
10622
  const envContent = await readFileSafe(envYamlPath);
10161
10623
  if (envContent) {
10162
10624
  try {
10163
- const envData = YAML13.parse(envContent);
10625
+ const envData = YAML14.parse(envContent);
10164
10626
  envCount = Array.isArray(envData?.variables) ? envData.variables.length : 0;
10165
10627
  } catch {
10166
10628
  }
@@ -10234,12 +10696,12 @@ async function printRepoSection(t, options, ctx) {
10234
10696
  console.log("");
10235
10697
  console.log(`=== REPO ${t.toUpperCase()} ===`);
10236
10698
  if (t === "env") {
10237
- const envYamlPath = path38.join(repoPath, "env", "env.yaml");
10699
+ const envYamlPath = path40.join(repoPath, "env", "env.yaml");
10238
10700
  if (await pathExists(envYamlPath)) {
10239
10701
  const envContent = await readFileSafe(envYamlPath);
10240
10702
  if (envContent) {
10241
10703
  try {
10242
- const envData = YAML13.parse(envContent);
10704
+ const envData = YAML14.parse(envContent);
10243
10705
  if (envData?.variables && envData.variables.length > 0) {
10244
10706
  for (const v of envData.variables) {
10245
10707
  console.log(` ${v.key}=${v.value}`);
@@ -10356,7 +10818,7 @@ var skill_cmd_exports = {};
10356
10818
  __export(skill_cmd_exports, {
10357
10819
  skillShow: () => skillShow
10358
10820
  });
10359
- import path39 from "path";
10821
+ import path41 from "path";
10360
10822
  async function skillShow(name, options) {
10361
10823
  const { localConfig, teamConfig } = await autoDetectInit();
10362
10824
  const agents = await detectInstalledAgents(localConfig, teamConfig);
@@ -10369,7 +10831,7 @@ async function skillShow(name, options) {
10369
10831
  }
10370
10832
  const ctx = await buildClassifyContext(localConfig);
10371
10833
  const source = classifySkill(name, ctx);
10372
- const description = truncate(await readSkillDescription(path39.join(resolved.primaryPath, "SKILL.md")), DESCRIPTION_MAX);
10834
+ const description = truncate(await readSkillDescription(path41.join(resolved.primaryPath, "SKILL.md")), DESCRIPTION_MAX);
10373
10835
  const contributors = await SkillsHandler.readContributors(resolved.primaryPath);
10374
10836
  const tagsConfig = await loadTagsConfig(localConfig.repo.localPath);
10375
10837
  const tags = tagsConfig?.skills?.[name] ?? [];
@@ -10387,28 +10849,28 @@ async function skillShow(name, options) {
10387
10849
  });
10388
10850
  if (options.verbose) {
10389
10851
  console.log("");
10390
- console.log(` Verbose: SKILL.md path is ${path39.join(resolved.primaryPath, "SKILL.md")}`);
10852
+ console.log(` Verbose: SKILL.md path is ${path41.join(resolved.primaryPath, "SKILL.md")}`);
10391
10853
  }
10392
10854
  }
10393
10855
  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"))) {
10856
+ const teamSkillsDir = path41.join(localConfig.repo.localPath, "skills");
10857
+ const flat = path41.join(teamSkillsDir, name);
10858
+ if (await pathExists(path41.join(flat, "SKILL.md"))) {
10397
10859
  return { name, primaryPath: flat, primaryOrigin: "team" };
10398
10860
  }
10399
10861
  if (await pathExists(teamSkillsDir)) {
10400
10862
  const namespaces = await listDirs(teamSkillsDir);
10401
10863
  for (const ns of namespaces) {
10402
- const candidate = path39.join(teamSkillsDir, ns, name);
10403
- if (await pathExists(path39.join(candidate, "SKILL.md"))) {
10864
+ const candidate = path41.join(teamSkillsDir, ns, name);
10865
+ if (await pathExists(path41.join(candidate, "SKILL.md"))) {
10404
10866
  return { name, primaryPath: candidate, primaryOrigin: "team", namespace: ns };
10405
10867
  }
10406
10868
  }
10407
10869
  }
10408
10870
  for (const agent of agents) {
10409
10871
  if (!agent.installed) continue;
10410
- const candidate = path39.join(agent.absoluteSkillsPath, name);
10411
- if (await pathExists(path39.join(candidate, "SKILL.md"))) {
10872
+ const candidate = path41.join(agent.absoluteSkillsPath, name);
10873
+ if (await pathExists(path41.join(candidate, "SKILL.md"))) {
10412
10874
  return { name, primaryPath: candidate, primaryOrigin: "agent" };
10413
10875
  }
10414
10876
  }
@@ -10418,8 +10880,8 @@ async function collectInstalledAgents(name, agents) {
10418
10880
  const matches = [];
10419
10881
  for (const agent of agents) {
10420
10882
  if (!agent.installed) continue;
10421
- const skillDir = path39.join(agent.absoluteSkillsPath, name);
10422
- if (await pathExists(path39.join(skillDir, "SKILL.md"))) {
10883
+ const skillDir = path41.join(agent.absoluteSkillsPath, name);
10884
+ if (await pathExists(path41.join(skillDir, "SKILL.md"))) {
10423
10885
  matches.push({ agent, path: skillDir });
10424
10886
  }
10425
10887
  }
@@ -10473,14 +10935,14 @@ __export(members_exports, {
10473
10935
  getMemberConfig: () => getMemberConfig,
10474
10936
  listMembers: () => listMembers
10475
10937
  });
10476
- import YAML14 from "yaml";
10477
- import path40 from "path";
10938
+ import YAML15 from "yaml";
10939
+ import path42 from "path";
10478
10940
  async function getMemberConfig(repoPath, username) {
10479
- const memberPath = path40.join(repoPath, "members", `${username}.yaml`);
10941
+ const memberPath = path42.join(repoPath, "members", `${username}.yaml`);
10480
10942
  const content = await readFileSafe(memberPath);
10481
10943
  if (!content) return null;
10482
10944
  try {
10483
- const raw = YAML14.parse(content);
10945
+ const raw = YAML15.parse(content);
10484
10946
  return MemberConfigSchema.parse(raw);
10485
10947
  } catch {
10486
10948
  return null;
@@ -10491,7 +10953,7 @@ async function listMembers(options) {
10491
10953
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
10492
10954
  const repoPath = localConfig.repo.localPath;
10493
10955
  await pullRepo(repoPath);
10494
- const membersDir = path40.join(repoPath, "members");
10956
+ const membersDir = path42.join(repoPath, "members");
10495
10957
  const files = await listFiles(membersDir);
10496
10958
  const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
10497
10959
  if (yamlFiles.length === 0) {
@@ -10502,10 +10964,10 @@ async function listMembers(options) {
10502
10964
  console.log(`Team members (${yamlFiles.length}):`);
10503
10965
  console.log("");
10504
10966
  for (const file of yamlFiles) {
10505
- const content = await readFileSafe(path40.join(membersDir, file));
10967
+ const content = await readFileSafe(path42.join(membersDir, file));
10506
10968
  if (!content) continue;
10507
10969
  try {
10508
- const raw = YAML14.parse(content);
10970
+ const raw = YAML15.parse(content);
10509
10971
  const member = MemberConfigSchema.parse(raw);
10510
10972
  const isSelf = member.username === localConfig.username;
10511
10973
  const marker = isSelf ? " (you)" : "";
@@ -10674,13 +11136,13 @@ var doctor_exports = {};
10674
11136
  __export(doctor_exports, {
10675
11137
  doctor: () => doctor
10676
11138
  });
10677
- import path41 from "path";
11139
+ import path43 from "path";
10678
11140
  async function buildHookChecks(toolPaths, baseDir) {
10679
11141
  const checks = [];
10680
11142
  for (const [tool, paths] of Object.entries(toolPaths)) {
10681
11143
  if (!paths.settings) continue;
10682
- const settingsPath = path41.join(baseDir, paths.settings);
10683
- const parentDir = path41.dirname(settingsPath);
11144
+ const settingsPath = path43.join(baseDir, paths.settings);
11145
+ const parentDir = path43.dirname(settingsPath);
10684
11146
  if (!await pathExists(parentDir)) continue;
10685
11147
  checks.push({
10686
11148
  name: `teamai hooks in ${tool} settings`,
@@ -10772,13 +11234,13 @@ async function doctor(options) {
10772
11234
  check: async () => {
10773
11235
  if (teamConfig?.sharing?.env?.injectShellProfile === false) return true;
10774
11236
  if (!localConfig) return true;
10775
- const envYamlPath = path41.join(localConfig.repo.localPath, "env", "env.yaml");
11237
+ const envYamlPath = path43.join(localConfig.repo.localPath, "env", "env.yaml");
10776
11238
  if (!await pathExists(envYamlPath)) return true;
10777
11239
  const home = process.env.HOME ?? "";
10778
- const envShPath = path41.join(home, ".teamai", "env.sh");
11240
+ const envShPath = path43.join(home, ".teamai", "env.sh");
10779
11241
  if (!await pathExists(envShPath)) return false;
10780
11242
  const shell = process.env.SHELL ?? "";
10781
- const profilePath = shell.includes("zsh") ? path41.join(home, ".zshrc") : path41.join(home, ".bashrc");
11243
+ const profilePath = shell.includes("zsh") ? path43.join(home, ".zshrc") : path43.join(home, ".bashrc");
10782
11244
  if (!await pathExists(profilePath)) return false;
10783
11245
  const content = await readFileSafe(profilePath);
10784
11246
  return content?.includes(TEAMAI_ENV_START) ?? false;
@@ -10825,8 +11287,8 @@ __export(roles_cmd_exports, {
10825
11287
  rolesSet: () => rolesSet,
10826
11288
  rolesUpdate: () => rolesUpdate
10827
11289
  });
10828
- import path42 from "path";
10829
- import YAML15 from "yaml";
11290
+ import path44 from "path";
11291
+ import YAML16 from "yaml";
10830
11292
  function parseNamespaces(input) {
10831
11293
  return input.split(",").map((s) => s.trim()).filter(Boolean);
10832
11294
  }
@@ -10870,7 +11332,7 @@ async function rolesInit(options) {
10870
11332
  const { localConfig, teamConfig } = await autoDetectInit();
10871
11333
  const repoPath = localConfig.repo.localPath;
10872
11334
  await pullLatest(repoPath);
10873
- const manifestPath = path42.join(repoPath, "manifest", "roles.yaml");
11335
+ const manifestPath = path44.join(repoPath, "manifest", "roles.yaml");
10874
11336
  if (await pathExists(manifestPath)) {
10875
11337
  log.warn(`Roles manifest already exists at ${manifestPath}`);
10876
11338
  const overwrite = await askConfirmation("Overwrite existing manifest? [y/N] ");
@@ -10926,7 +11388,7 @@ async function rolesInit(options) {
10926
11388
  };
10927
11389
  if (options.dryRun) {
10928
11390
  log.info("[dry-run] Would write manifest:");
10929
- console.log(YAML15.stringify(manifest));
11391
+ console.log(YAML16.stringify(manifest));
10930
11392
  return;
10931
11393
  }
10932
11394
  await saveRolesManifest(repoPath, manifest);
@@ -11213,7 +11675,7 @@ __export(tags_exports, {
11213
11675
  tagsSubscribe: () => tagsSubscribe,
11214
11676
  tagsUnsubscribe: () => tagsUnsubscribe
11215
11677
  });
11216
- import path43 from "path";
11678
+ import path45 from "path";
11217
11679
  async function resolveTagsScope() {
11218
11680
  const projectConfig = await detectProjectConfig();
11219
11681
  return projectConfig ?? (await requireInit()).localConfig;
@@ -11378,7 +11840,7 @@ async function tagsRemove(resourceType, name, tags, options) {
11378
11840
  async function getTeamSkillCount(repoPath) {
11379
11841
  try {
11380
11842
  const { listDirs: listDirs2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
11381
- const skillsDir = path43.join(repoPath, "skills");
11843
+ const skillsDir = path45.join(repoPath, "skills");
11382
11844
  const dirs = await listDirs2(skillsDir);
11383
11845
  return dirs.length;
11384
11846
  } catch {
@@ -11626,24 +12088,24 @@ var uninstall_exports = {};
11626
12088
  __export(uninstall_exports, {
11627
12089
  uninstall: () => uninstall
11628
12090
  });
11629
- import path44 from "path";
12091
+ import path46 from "path";
11630
12092
  function detectShellProfile() {
11631
12093
  const home = process.env.HOME;
11632
12094
  if (!home) return null;
11633
12095
  const shell = process.env.SHELL ?? "";
11634
12096
  if (shell.includes("zsh")) {
11635
- return path44.join(home, ".zshrc");
12097
+ return path46.join(home, ".zshrc");
11636
12098
  }
11637
- return path44.join(home, ".bashrc");
12099
+ return path46.join(home, ".bashrc");
11638
12100
  }
11639
12101
  async function collectTeamSkillNames(repoPath) {
11640
- const teamSkillsDir = path44.join(repoPath, "skills");
12102
+ const teamSkillsDir = path46.join(repoPath, "skills");
11641
12103
  if (!await pathExists(teamSkillsDir)) return /* @__PURE__ */ new Set();
11642
12104
  const names = /* @__PURE__ */ new Set();
11643
12105
  const topDirs = await listDirs(teamSkillsDir);
11644
12106
  for (const dir of topDirs) {
11645
- const dirPath = path44.join(teamSkillsDir, dir);
11646
- const hasSkillMd = await pathExists(path44.join(dirPath, "SKILL.md"));
12107
+ const dirPath = path46.join(teamSkillsDir, dir);
12108
+ const hasSkillMd = await pathExists(path46.join(dirPath, "SKILL.md"));
11647
12109
  if (hasSkillMd) {
11648
12110
  names.add(dir);
11649
12111
  } else {
@@ -11656,7 +12118,7 @@ async function collectTeamSkillNames(repoPath) {
11656
12118
  return names;
11657
12119
  }
11658
12120
  async function collectTeamRuleNames(repoPath) {
11659
- const teamRulesDir = path44.join(repoPath, "rules");
12121
+ const teamRulesDir = path46.join(repoPath, "rules");
11660
12122
  if (!await pathExists(teamRulesDir)) return /* @__PURE__ */ new Set();
11661
12123
  const files = await listFilesRecursive(teamRulesDir);
11662
12124
  return new Set(
@@ -11684,36 +12146,36 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11684
12146
  const teamRuleNames = await collectTeamRuleNames(repoPath);
11685
12147
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
11686
12148
  if (toolPath.settings) {
11687
- const settingsPath = path44.join(baseDir, toolPath.settings);
12149
+ const settingsPath = path46.join(baseDir, toolPath.settings);
11688
12150
  if (await pathExists(settingsPath)) {
11689
12151
  plan.hookFiles.push({ path: settingsPath, tool });
11690
12152
  }
11691
12153
  } else {
11692
- const hooksDir = path44.join(baseDir, `.${tool}`, "hooks");
11693
- if (await pathExists(path44.join(hooksDir, OPENCLAW_HOOK_DIR))) {
12154
+ const hooksDir = path46.join(baseDir, `.${tool}`, "hooks");
12155
+ if (await pathExists(path46.join(hooksDir, OPENCLAW_HOOK_DIR))) {
11694
12156
  plan.openclawHookDirs.push({ hooksDir, tool });
11695
12157
  }
11696
12158
  }
11697
12159
  if (toolPath.claudemd) {
11698
- const claudeMdPath = path44.join(baseDir, toolPath.claudemd);
12160
+ const claudeMdPath = path46.join(baseDir, toolPath.claudemd);
11699
12161
  const content = await readFileSafe(claudeMdPath);
11700
12162
  if (content && CLAUDEMD_MARKER_PAIRS.some(([start]) => content.includes(start))) {
11701
12163
  plan.claudeMdFiles.push(claudeMdPath);
11702
12164
  }
11703
12165
  }
11704
12166
  if (toolPath.skills) {
11705
- const skillsDir = path44.join(baseDir, toolPath.skills);
12167
+ const skillsDir = path46.join(baseDir, toolPath.skills);
11706
12168
  if (await pathExists(skillsDir)) {
11707
12169
  const dirs = await listDirs(skillsDir);
11708
12170
  for (const dir of dirs) {
11709
12171
  if (teamSkillNames.has(dir)) {
11710
- plan.skillDirs.push(path44.join(skillsDir, dir));
12172
+ plan.skillDirs.push(path46.join(skillsDir, dir));
11711
12173
  }
11712
12174
  }
11713
12175
  }
11714
12176
  }
11715
12177
  if (toolPath.rules) {
11716
- const rulesDir = path44.join(baseDir, toolPath.rules);
12178
+ const rulesDir = path46.join(baseDir, toolPath.rules);
11717
12179
  if (await pathExists(rulesDir)) {
11718
12180
  const files = await listFilesRecursive(rulesDir);
11719
12181
  for (const file of files) {
@@ -11721,7 +12183,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11721
12183
  const ruleName = file.replace(/\.md$/, "");
11722
12184
  if (EXCLUDED_RULE_NAMES.has(ruleName)) continue;
11723
12185
  if (teamRuleNames.has(ruleName)) {
11724
- plan.ruleFiles.push(path44.join(rulesDir, file));
12186
+ plan.ruleFiles.push(path46.join(rulesDir, file));
11725
12187
  }
11726
12188
  }
11727
12189
  }
@@ -11737,7 +12199,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
11737
12199
  const docsLocalDir = teamConfig.sharing.docs.localDir;
11738
12200
  let docsDir;
11739
12201
  if (localConfig.scope === "project" && localConfig.projectRoot) {
11740
- docsDir = docsLocalDir.startsWith("~/") ? path44.join(localConfig.projectRoot, docsLocalDir.substring(2)) : expandHome(docsLocalDir);
12202
+ docsDir = docsLocalDir.startsWith("~/") ? path46.join(localConfig.projectRoot, docsLocalDir.substring(2)) : expandHome(docsLocalDir);
11741
12203
  } else {
11742
12204
  docsDir = expandHome(docsLocalDir);
11743
12205
  }
@@ -11765,7 +12227,7 @@ function printSummary(plan) {
11765
12227
  if (plan.openclawHookDirs.length > 0) {
11766
12228
  console.log(` OpenClaw Hooks (${plan.openclawHookDirs.length} \u4E2A\u76EE\u5F55):`);
11767
12229
  for (const { hooksDir } of plan.openclawHookDirs) {
11768
- console.log(` ${path44.join(hooksDir, OPENCLAW_HOOK_DIR)}/`);
12230
+ console.log(` ${path46.join(hooksDir, OPENCLAW_HOOK_DIR)}/`);
11769
12231
  }
11770
12232
  console.log("");
11771
12233
  }
@@ -11928,7 +12390,7 @@ async function uninstall(opts) {
11928
12390
  log.error("\u65E0\u6CD5\u786E\u5B9A\u7528\u6237\u4E3B\u76EE\u5F55\uFF08HOME \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E\uFF09");
11929
12391
  return;
11930
12392
  }
11931
- const home = path44.join(homeDir, ".teamai");
12393
+ const home = path46.join(homeDir, ".teamai");
11932
12394
  if (!await pathExists(home)) {
11933
12395
  log.info("\u6CA1\u6709\u9700\u8981\u5378\u8F7D\u7684\u5185\u5BB9");
11934
12396
  return;
@@ -11986,7 +12448,7 @@ __export(env_commands_exports, {
11986
12448
  envList: () => envList,
11987
12449
  envRemove: () => envRemove
11988
12450
  });
11989
- import path45 from "path";
12451
+ import path47 from "path";
11990
12452
  function maskValue(value) {
11991
12453
  if (value.length < 4) return "****";
11992
12454
  return `${value.slice(0, 2)}****`;
@@ -11994,7 +12456,7 @@ function maskValue(value) {
11994
12456
  async function envList(options) {
11995
12457
  const projectConfig = await detectProjectConfig();
11996
12458
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
11997
- const envYamlPath = path45.join(localConfig.repo.localPath, "env", "env.yaml");
12459
+ const envYamlPath = path47.join(localConfig.repo.localPath, "env", "env.yaml");
11998
12460
  if (!await pathExists(envYamlPath)) {
11999
12461
  log.info("No env variables defined (env/env.yaml not found)");
12000
12462
  return;
@@ -12023,7 +12485,7 @@ async function envAdd(key, value, options) {
12023
12485
  const projectConfig = await detectProjectConfig();
12024
12486
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
12025
12487
  const repoPath = localConfig.repo.localPath;
12026
- const envYamlPath = path45.join(repoPath, "env", "env.yaml");
12488
+ const envYamlPath = path47.join(repoPath, "env", "env.yaml");
12027
12489
  const pullSpin = spinner("Pulling latest...").start();
12028
12490
  try {
12029
12491
  await pullRepo(repoPath);
@@ -12050,7 +12512,7 @@ async function envAdd(key, value, options) {
12050
12512
  log.info(`[dry-run] Would ${isUpdate ? "update" : "add"} env variable: ${key}=${value}`);
12051
12513
  return;
12052
12514
  }
12053
- await ensureDir(path45.join(repoPath, "env"));
12515
+ await ensureDir(path47.join(repoPath, "env"));
12054
12516
  await envHandler.writeEnvYaml(envYamlPath, envConfig);
12055
12517
  const action = isUpdate ? "Updated" : "Added";
12056
12518
  log.success(`${action} env variable: ${key}=${value}`);
@@ -12060,7 +12522,7 @@ async function envRemove(key, options) {
12060
12522
  const projectConfig = await detectProjectConfig();
12061
12523
  const localConfig = projectConfig ?? (await requireInit()).localConfig;
12062
12524
  const repoPath = localConfig.repo.localPath;
12063
- const envYamlPath = path45.join(repoPath, "env", "env.yaml");
12525
+ const envYamlPath = path47.join(repoPath, "env", "env.yaml");
12064
12526
  const pullSpin = spinner("Pulling latest...").start();
12065
12527
  try {
12066
12528
  await pullRepo(repoPath);
@@ -12107,7 +12569,7 @@ __export(hooks_cmd_exports, {
12107
12569
  hooksList: () => hooksList,
12108
12570
  hooksRemove: () => hooksRemove
12109
12571
  });
12110
- import path46 from "path";
12572
+ import path48 from "path";
12111
12573
  function resolveHookScopeTargets(localConfig) {
12112
12574
  const baseDir = resolveBaseDir(localConfig) ?? "";
12113
12575
  const manifestPath = getManagedHooksPath(localConfig.scope, localConfig.projectRoot);
@@ -12127,7 +12589,7 @@ function formatDisplayPath(settingsPath) {
12127
12589
  const home = process.env.HOME;
12128
12590
  if (!home) return settingsPath;
12129
12591
  if (settingsPath === home) return "~";
12130
- if (settingsPath.startsWith(home + path46.sep) || settingsPath.startsWith(home + "/")) {
12592
+ if (settingsPath.startsWith(home + path48.sep) || settingsPath.startsWith(home + "/")) {
12131
12593
  return `~${settingsPath.slice(home.length)}`;
12132
12594
  }
12133
12595
  return settingsPath;
@@ -12169,7 +12631,7 @@ async function hooksList(_options) {
12169
12631
  continue;
12170
12632
  }
12171
12633
  for (const baseDir of baseDirs) {
12172
- const settingsPath = path46.join(baseDir, paths.settings);
12634
+ const settingsPath = path48.join(baseDir, paths.settings);
12173
12635
  rows.push({
12174
12636
  tool,
12175
12637
  status: await getHookStatus(settingsPath, tool),
@@ -12965,11 +13427,11 @@ __export(dashboard_exports, {
12965
13427
  });
12966
13428
  import http from "http";
12967
13429
  import fs14 from "fs";
12968
- import path47 from "path";
13430
+ import path49 from "path";
12969
13431
  async function startDashboard(port) {
12970
13432
  const serverPort = port ?? DASHBOARD_DEFAULT_PORT;
12971
- const eventsPath = path47.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
12972
- await ensureDir(path47.dirname(eventsPath));
13433
+ const eventsPath = path49.join(process.env.HOME ?? "", ".teamai", "dashboard", "events.jsonl");
13434
+ await ensureDir(path49.dirname(eventsPath));
12973
13435
  try {
12974
13436
  await fs14.promises.access(eventsPath);
12975
13437
  } catch {
@@ -13107,14 +13569,14 @@ var init_dashboard = __esm({
13107
13569
  });
13108
13570
 
13109
13571
  // src/recall-quality.ts
13110
- import path48 from "path";
13572
+ import path50 from "path";
13111
13573
  import fs15 from "fs";
13112
13574
  function sanitizeSessionId(sessionId) {
13113
13575
  return sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
13114
13576
  }
13115
13577
  function getCachePath(sessionId) {
13116
13578
  const safeName = sanitizeSessionId(sessionId);
13117
- return path48.join(
13579
+ return path50.join(
13118
13580
  process.env.HOME ?? "",
13119
13581
  ".teamai",
13120
13582
  "sessions",
@@ -13145,7 +13607,7 @@ function readCache(sessionId) {
13145
13607
  function writeCache(sessionId, cache) {
13146
13608
  try {
13147
13609
  const cachePath = getCachePath(sessionId);
13148
- const dir = path48.dirname(cachePath);
13610
+ const dir = path50.dirname(cachePath);
13149
13611
  if (!fs15.existsSync(dir)) {
13150
13612
  fs15.mkdirSync(dir, { recursive: true });
13151
13613
  }
@@ -13208,13 +13670,13 @@ __export(contribute_check_exports, {
13208
13670
  writeContributeState: () => writeContributeState
13209
13671
  });
13210
13672
  import fs16 from "fs";
13211
- import path49 from "path";
13673
+ import path51 from "path";
13212
13674
  import { execFileSync } from "child_process";
13213
13675
  function sanitizeSessionId2(sessionId) {
13214
13676
  return sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
13215
13677
  }
13216
13678
  function getSessionPath(sessionId) {
13217
- return path49.join(
13679
+ return path51.join(
13218
13680
  process.env.HOME ?? "",
13219
13681
  ".teamai",
13220
13682
  "sessions",
@@ -13250,9 +13712,9 @@ async function readContributeState(sessionId) {
13250
13712
  async function writeContributeState(sessionId, state) {
13251
13713
  try {
13252
13714
  const filePath = getSessionPath(sessionId);
13253
- await ensureDir(path49.dirname(filePath));
13715
+ await ensureDir(path51.dirname(filePath));
13254
13716
  await writeJson(filePath, state);
13255
- await cleanupStaleSessions(path49.dirname(filePath), sessionId);
13717
+ await cleanupStaleSessions(path51.dirname(filePath), sessionId);
13256
13718
  } catch (e) {
13257
13719
  log.error(`Failed to write contribute state: ${e.message}`);
13258
13720
  }
@@ -13265,7 +13727,7 @@ async function cleanupStaleSessions(dir, currentSessionId) {
13265
13727
  if (!entry.endsWith(".json")) continue;
13266
13728
  const name = entry.replace(".json", "");
13267
13729
  if (name === currentBasename) continue;
13268
- const filePath = path49.join(dir, entry);
13730
+ const filePath = path51.join(dir, entry);
13269
13731
  try {
13270
13732
  const stat6 = await fs16.promises.stat(filePath);
13271
13733
  if (now - stat6.mtimeMs > STALE_SESSION_MS) {
@@ -13506,21 +13968,21 @@ __export(contribute_exports, {
13506
13968
  contribute: () => contribute
13507
13969
  });
13508
13970
  import fs17 from "fs";
13509
- import path50 from "path";
13971
+ import path52 from "path";
13510
13972
  import fse8 from "fs-extra";
13511
13973
  async function rebuildIndexAfterContribute(localConfig) {
13512
13974
  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");
13975
+ const learningsRepoDir = path52.join(repoPath, "learnings");
13976
+ const docsRepoDir = path52.join(repoPath, "docs");
13977
+ const rulesRepoDir = path52.join(repoPath, "rules");
13978
+ const skillsRepoDir = path52.join(repoPath, "skills");
13979
+ const votesDir = path52.join(repoPath, "votes");
13518
13980
  let effectiveLearningsDir;
13519
13981
  if (localConfig.scope === "user") {
13520
13982
  if (await pathExists(learningsRepoDir)) {
13521
13983
  await fse8.copy(learningsRepoDir, LEARNINGS_LOCAL_DIR, {
13522
13984
  overwrite: true,
13523
- filter: (src) => !path50.basename(src).startsWith(".")
13985
+ filter: (src) => !path52.basename(src).startsWith(".")
13524
13986
  });
13525
13987
  }
13526
13988
  effectiveLearningsDir = await pathExists(LEARNINGS_LOCAL_DIR) ? LEARNINGS_LOCAL_DIR : void 0;
@@ -13528,7 +13990,7 @@ async function rebuildIndexAfterContribute(localConfig) {
13528
13990
  effectiveLearningsDir = await pathExists(learningsRepoDir) ? learningsRepoDir : void 0;
13529
13991
  }
13530
13992
  const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
13531
- const indexPath = path50.join(teamaiHome, "search-index.json");
13993
+ const indexPath = path52.join(teamaiHome, "search-index.json");
13532
13994
  const { buildIndex: buildIndex2 } = await Promise.resolve().then(() => (init_search_index(), search_index_exports));
13533
13995
  await buildIndex2({
13534
13996
  learningsDir: effectiveLearningsDir,
@@ -13587,9 +14049,9 @@ async function contribute(options) {
13587
14049
  const pushSpin = spinner("Contributing session knowledge...").start();
13588
14050
  const filename = generateFilename(options.title);
13589
14051
  try {
13590
- const aiDocsDir = path50.join(repoPath, "learnings");
14052
+ const aiDocsDir = path52.join(repoPath, "learnings");
13591
14053
  await ensureDir(aiDocsDir);
13592
- const destPath = path50.join(aiDocsDir, filename);
14054
+ const destPath = path52.join(aiDocsDir, filename);
13593
14055
  await fs17.promises.writeFile(destPath, content, "utf-8");
13594
14056
  try {
13595
14057
  await pullRepo(repoPath);
@@ -13644,7 +14106,7 @@ var init_contribute = __esm({
13644
14106
  });
13645
14107
 
13646
14108
  // src/wiki-engine/core/wiki-protocol.ts
13647
- import path51 from "path";
14109
+ import path53 from "path";
13648
14110
  function safeIgnore(filePath) {
13649
14111
  const normalized = toPosix(filePath);
13650
14112
  const parts = normalized.split("/").filter(Boolean);
@@ -13658,7 +14120,7 @@ function safeIgnore(filePath) {
13658
14120
  return /\.(pem|key|p12|pfx)$/i.test(base);
13659
14121
  }
13660
14122
  function toPosix(value) {
13661
- return value.split(path51.sep).join("/");
14123
+ return value.split(path53.sep).join("/");
13662
14124
  }
13663
14125
  var CONFIDENCE_SCORE_DEFAULTS, SAFE_IGNORE_SEGMENTS, SENSITIVE_FILE_NAMES;
13664
14126
  var init_wiki_protocol = __esm({
@@ -13705,7 +14167,7 @@ __export(graph_index_schema_exports, {
13705
14167
  validateGraph: () => validateGraph
13706
14168
  });
13707
14169
  import { readFile, writeFile as writeFile4, mkdir } from "fs/promises";
13708
- import path52 from "path";
14170
+ import path54 from "path";
13709
14171
  function toPageSlug(relativePath) {
13710
14172
  return relativePath.replace(/\.md$/u, "").replace(/\\/g, "/");
13711
14173
  }
@@ -13876,7 +14338,7 @@ function computeGraphHealth(graph) {
13876
14338
  };
13877
14339
  }
13878
14340
  async function loadGraphIndex(wikiRoot) {
13879
- const graphPath = path52.join(wikiRoot, ".indices", "graph-index.json");
14341
+ const graphPath = path54.join(wikiRoot, ".indices", "graph-index.json");
13880
14342
  try {
13881
14343
  const raw = await readFile(graphPath, "utf8");
13882
14344
  const parsed = JSON.parse(raw);
@@ -13889,9 +14351,9 @@ async function loadGraphIndex(wikiRoot) {
13889
14351
  }
13890
14352
  }
13891
14353
  async function saveGraphIndex(wikiRoot, graph) {
13892
- const dir = path52.join(wikiRoot, ".indices");
14354
+ const dir = path54.join(wikiRoot, ".indices");
13893
14355
  await mkdir(dir, { recursive: true });
13894
- const outPath = path52.join(dir, "graph-index.json");
14356
+ const outPath = path54.join(dir, "graph-index.json");
13895
14357
  await writeFile4(outPath, JSON.stringify(graph, null, 2), "utf8");
13896
14358
  return outPath;
13897
14359
  }
@@ -13944,7 +14406,7 @@ var init_graph_index_schema = __esm({
13944
14406
 
13945
14407
  // src/code-knowledge-recall.ts
13946
14408
  import { readFile as readFile2, readdir } from "fs/promises";
13947
- import path53 from "path";
14409
+ import path55 from "path";
13948
14410
  function countOccurrences(text, token) {
13949
14411
  let count = 0;
13950
14412
  let idx = 0;
@@ -14078,7 +14540,7 @@ function extractSnippet(content, queryTokens, maxLen = 300) {
14078
14540
  async function loadWikiPages(wikiRoot, depth) {
14079
14541
  const pages = [];
14080
14542
  if (depth === "route") {
14081
- const routerPath = path53.join(wikiRoot, "router.md");
14543
+ const routerPath = path55.join(wikiRoot, "router.md");
14082
14544
  try {
14083
14545
  const content = await readFile2(routerPath, "utf-8");
14084
14546
  const titleMatch = content.match(/^title:\s*(.+)$/m);
@@ -14094,7 +14556,7 @@ async function loadWikiPages(wikiRoot, depth) {
14094
14556
  }
14095
14557
  return pages;
14096
14558
  }
14097
- const evidenceDir = path53.join(wikiRoot, "evidence", "code");
14559
+ const evidenceDir = path55.join(wikiRoot, "evidence", "code");
14098
14560
  let projectDirs;
14099
14561
  try {
14100
14562
  const entries = await readdir(evidenceDir, { withFileTypes: true });
@@ -14103,7 +14565,7 @@ async function loadWikiPages(wikiRoot, depth) {
14103
14565
  return pages;
14104
14566
  }
14105
14567
  for (const project of projectDirs) {
14106
- const projectDir = path53.join(evidenceDir, project);
14568
+ const projectDir = path55.join(evidenceDir, project);
14107
14569
  await loadPagesRecursive(projectDir, `evidence/code/${project}`, pages, depth);
14108
14570
  }
14109
14571
  return pages;
@@ -14112,7 +14574,7 @@ async function loadPagesRecursive(dir, relativePath, pages, depth, currentDepth
14112
14574
  if (currentDepth >= MAX_RECURSION_DEPTH) return;
14113
14575
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
14114
14576
  for (const entry of entries) {
14115
- const fullPath = path53.join(dir, entry.name);
14577
+ const fullPath = path55.join(dir, entry.name);
14116
14578
  if (entry.isDirectory()) {
14117
14579
  await loadPagesRecursive(
14118
14580
  fullPath,
@@ -14234,8 +14696,7 @@ __export(recall_exports, {
14234
14696
  formatResults: () => formatResults,
14235
14697
  recall: () => recall
14236
14698
  });
14237
- import path54 from "path";
14238
- import YAML16 from "yaml";
14699
+ import path56 from "path";
14239
14700
  function getVotesLocalDir() {
14240
14701
  return `${process.env.HOME ?? ""}/.teamai/votes`;
14241
14702
  }
@@ -14265,49 +14726,25 @@ function formatResults(results) {
14265
14726
  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
14727
  return lines.join("\n");
14267
14728
  }
14268
- async function autoUpvote(results, username, repoPath) {
14729
+ async function autoUpvote(results, username, _repoPath) {
14269
14730
  if (results.length === 0) return;
14270
14731
  try {
14732
+ const { incrementRecalled: incrementRecalled2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
14271
14733
  const votesDir = getVotesLocalDir();
14272
- const localVotePath = path54.join(votesDir, `${username}.yaml`);
14734
+ const localVotePath = path56.join(votesDir, `${username}.yaml`);
14273
14735
  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}`);
14736
+ const docIds = results.map((r) => r.entry.filename.replace(/\.md$/i, ""));
14737
+ await incrementRecalled2(localVotePath, docIds);
14738
+ log.debug(`autoUpvote: incremented recalled_count for ${docIds.length} doc(s)`);
14302
14739
  } catch (e) {
14303
14740
  log.error(`autoUpvote failed: ${e.message}`);
14304
14741
  }
14305
14742
  }
14306
14743
  async function loadOrBuildScopeIndex(localConfig, scopeLabel) {
14307
14744
  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");
14745
+ const indexPath = path56.join(teamaiHome, "search-index.json");
14746
+ const localLearningsDir = path56.join(teamaiHome, "learnings");
14747
+ const repoLearningsDir = path56.join(localConfig.repo.localPath, "learnings");
14311
14748
  let effectiveLearningsDir = null;
14312
14749
  if (scopeLabel === "user" && await pathExists(localLearningsDir)) {
14313
14750
  effectiveLearningsDir = localLearningsDir;
@@ -14316,13 +14753,13 @@ async function loadOrBuildScopeIndex(localConfig, scopeLabel) {
14316
14753
  }
14317
14754
  let index = await loadIndex(indexPath);
14318
14755
  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");
14756
+ 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")))) {
14757
+ const votesDir = path56.join(localConfig.repo.localPath, "votes");
14321
14758
  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");
14759
+ const docsDir = path56.join(localConfig.repo.localPath, "docs");
14760
+ const rulesDir = path56.join(localConfig.repo.localPath, "rules");
14761
+ const skillsDir = path56.join(localConfig.repo.localPath, "skills");
14762
+ const repoCodebaseDir = path56.join(localConfig.repo.localPath, "docs", "team-codebase");
14326
14763
  const codebaseDir = await pathExists(repoCodebaseDir) ? repoCodebaseDir : void 0;
14327
14764
  try {
14328
14765
  await buildIndex({
@@ -14382,7 +14819,7 @@ async function recall(query, options) {
14382
14819
  }
14383
14820
  }
14384
14821
  const wikiConfig = scopeIndexes[0]?.config;
14385
- const wikiRoot = wikiConfig ? path54.join(wikiConfig.repo.localPath, "teamwiki") : path54.join(process.cwd(), ".teamai", "team-repo", "teamwiki");
14822
+ const wikiRoot = wikiConfig ? path56.join(wikiConfig.repo.localPath, "teamwiki") : path56.join(process.cwd(), ".teamai", "team-repo", "teamwiki");
14386
14823
  const hasWiki = await pathExists(wikiRoot);
14387
14824
  if (scopeIndexes.length === 0 && !hasWiki) {
14388
14825
  log.info("No learnings available. Run `teamai pull` first to sync team knowledge.");
@@ -14413,7 +14850,7 @@ async function recall(query, options) {
14413
14850
  votes: 0,
14414
14851
  type: "docs",
14415
14852
  domain: "technical",
14416
- path: path54.join(wikiRoot, cr.page),
14853
+ path: path56.join(wikiRoot, cr.page),
14417
14854
  snippet: cr.snippet
14418
14855
  },
14419
14856
  score: Math.min(10, Math.log2(cr.score + 1) * 2),
@@ -14472,19 +14909,19 @@ __export(recall_toggle_exports, {
14472
14909
  recallEnable: () => recallEnable,
14473
14910
  recallStatus: () => recallStatus
14474
14911
  });
14475
- import path55 from "path";
14912
+ import path57 from "path";
14476
14913
  async function removeRecallArtifacts(teamConfig, localConfig) {
14477
14914
  const baseDir = resolveBaseDir(localConfig);
14478
14915
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
14479
14916
  if (toolPath.rules) {
14480
- const ruleFile = path55.join(baseDir, toolPath.rules, "teamai-recall.md");
14917
+ const ruleFile = path57.join(baseDir, toolPath.rules, "teamai-recall.md");
14481
14918
  if (await pathExists(ruleFile)) {
14482
14919
  await remove(ruleFile);
14483
14920
  log.debug(`Removed recall rule from ${tool}`);
14484
14921
  }
14485
14922
  }
14486
14923
  if (toolPath.agents) {
14487
- const agentFile = path55.join(baseDir, toolPath.agents, "teamai-recall.md");
14924
+ const agentFile = path57.join(baseDir, toolPath.agents, "teamai-recall.md");
14488
14925
  if (await pathExists(agentFile)) {
14489
14926
  await remove(agentFile);
14490
14927
  log.debug(`Removed recall agent from ${tool}`);
@@ -14492,7 +14929,7 @@ async function removeRecallArtifacts(teamConfig, localConfig) {
14492
14929
  }
14493
14930
  if (toolPath.skills) {
14494
14931
  for (const skillName of RECALL_DEPENDENT_SKILLS) {
14495
- const skillDir = path55.join(baseDir, toolPath.skills, skillName);
14932
+ const skillDir = path57.join(baseDir, toolPath.skills, skillName);
14496
14933
  if (await pathExists(skillDir)) {
14497
14934
  await remove(skillDir);
14498
14935
  log.debug(`Removed recall skill ${skillName} from ${tool}`);
@@ -14500,7 +14937,7 @@ async function removeRecallArtifacts(teamConfig, localConfig) {
14500
14937
  }
14501
14938
  }
14502
14939
  if (toolPath.claudemd) {
14503
- const claudeMdPath = path55.join(baseDir, toolPath.claudemd);
14940
+ const claudeMdPath = path57.join(baseDir, toolPath.claudemd);
14504
14941
  const content = await readFileSafe(claudeMdPath);
14505
14942
  if (content && content.includes(TEAMAI_RECALL_RULES_START)) {
14506
14943
  const startIdx = content.indexOf(TEAMAI_RECALL_RULES_START);
@@ -14534,7 +14971,7 @@ async function deployRecallArtifacts(teamConfig, localConfig) {
14534
14971
  for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
14535
14972
  if (!toolPath.claudemd || !toolPath.agents) continue;
14536
14973
  if (!await ResourceHandler.isToolInstalled(toolPath.agents, baseDir)) continue;
14537
- const claudeMdPath = path55.join(baseDir, toolPath.claudemd);
14974
+ const claudeMdPath = path57.join(baseDir, toolPath.claudemd);
14538
14975
  try {
14539
14976
  await injectClaudeMdSection2(
14540
14977
  claudeMdPath,
@@ -14594,10 +15031,10 @@ __export(todowrite_hint_exports, {
14594
15031
  shouldSkipTodoWriteHint: () => shouldSkipTodoWriteHint,
14595
15032
  todoWriteHint: () => todoWriteHint
14596
15033
  });
14597
- import path56 from "path";
15034
+ import path58 from "path";
14598
15035
  import fs18 from "fs";
14599
15036
  function getTodoWriteHintCachePath(sessionId) {
14600
- return path56.join(
15037
+ return path58.join(
14601
15038
  process.env.HOME ?? "",
14602
15039
  ".teamai",
14603
15040
  "sessions",
@@ -14620,7 +15057,7 @@ function readCache2(sessionId) {
14620
15057
  function writeCache2(sessionId, cache) {
14621
15058
  try {
14622
15059
  const cachePath = getTodoWriteHintCachePath(sessionId);
14623
- const dir = path56.dirname(cachePath);
15060
+ const dir = path58.dirname(cachePath);
14624
15061
  if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
14625
15062
  fs18.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
14626
15063
  } catch {
@@ -14652,12 +15089,13 @@ function buildHintMessage() {
14652
15089
  return [
14653
15090
  "[teamai:todowrite-hint] \u4EFB\u52A1\u5DF2\u89C4\u5212\u3002",
14654
15091
  "",
14655
- "\u8BF7\u786E\u8BA4\u672C\u6B21\u4EFB\u52A1\u5F00\u59CB\u524D\u5DF2\u901A\u8FC7 Agent tool \u8C03\u7528 teamai-recall subagent \u5B8C\u6210\u77E5\u8BC6\u5E93\u68C0\u7D22\uFF1B",
14656
- "\u5982\u672A\u68C0\u7D22\uFF0C\u8BF7\u7ACB\u5373\u8C03\u7528 teamai-recall\uFF08\u4E00\u6B21\u5373\u53EF\uFF09\uFF0C\u5B8C\u6210\u540E\u518D\u7EE7\u7EED\u540E\u7EED Todo\u3002",
15092
+ "\u5982\u672C\u6B21\u4EFB\u52A1\u53EF\u80FD\u53D7\u76CA\u4E8E\u56E2\u961F\u77E5\u8BC6\u5E93\uFF08\u975E\u7B80\u5355\u5C40\u90E8\u6539\u52A8\u3001\u7528\u6237\u672A\u63D0\u4F9B\u76F8\u5173\u4E0A\u4E0B\u6587\uFF09\uFF0C",
15093
+ "\u5EFA\u8BAE\u901A\u8FC7 Agent tool \u8C03\u7528 teamai-recall subagent \u68C0\u7D22\u4E00\u6B21\u3002",
15094
+ "\u82E5\u7528\u6237\u5DF2\u7ED9\u51FA\u5145\u5206\u4E0A\u4E0B\u6587\u6216\u4EC5\u4E3A\u7B80\u5355\u4FEE\u6539\uFF0C\u53EF\u8DF3\u8FC7\u3002",
14657
15095
  "",
14658
- "Task plan detected \u2014 confirm you have already invoked the `teamai-recall`",
14659
- "subagent for relevant team knowledge before executing the todo list.",
14660
- "If not, invoke it once now."
15096
+ "Task plan detected \u2014 if this task may benefit from team knowledge",
15097
+ "(not a trivial local change, user has not provided relevant context),",
15098
+ "consider invoking the `teamai-recall` subagent once before proceeding."
14661
15099
  ].join("\n");
14662
15100
  }
14663
15101
  async function todoWriteHint() {
@@ -14691,20 +15129,20 @@ var init_todowrite_hint = __esm({
14691
15129
  });
14692
15130
 
14693
15131
  // src/utils/cache-index.ts
14694
- import path57 from "path";
15132
+ import path59 from "path";
14695
15133
  import os3 from "os";
14696
15134
  import fs19 from "fs-extra";
14697
15135
  function getCacheRoot() {
14698
- return process.env.TEAMAI_CACHE_DIR ?? path57.join(os3.homedir(), ".teamai", "cache", "repos");
15136
+ return process.env.TEAMAI_CACHE_DIR ?? path59.join(os3.homedir(), ".teamai", "cache", "repos");
14699
15137
  }
14700
15138
  function buildKey(provider, owner, repo) {
14701
15139
  return `${provider}/${owner}/${repo}`;
14702
15140
  }
14703
15141
  function keyToAbsPath(key) {
14704
- return path57.join(getCacheRoot(), key);
15142
+ return path59.join(getCacheRoot(), key);
14705
15143
  }
14706
15144
  async function loadCacheIndex() {
14707
- const indexPath = path57.join(getCacheRoot(), INDEX_FILENAME);
15145
+ const indexPath = path59.join(getCacheRoot(), INDEX_FILENAME);
14708
15146
  try {
14709
15147
  const stat6 = await fs19.stat(indexPath);
14710
15148
  if (stat6.size > MAX_CONFIG_FILE_BYTES) {
@@ -14727,7 +15165,7 @@ async function loadCacheIndex() {
14727
15165
  async function saveCacheIndex(idx) {
14728
15166
  const root = getCacheRoot();
14729
15167
  await fs19.ensureDir(root);
14730
- const indexPath = path57.join(root, INDEX_FILENAME);
15168
+ const indexPath = path59.join(root, INDEX_FILENAME);
14731
15169
  const updated = { ...idx, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
14732
15170
  await fs19.writeFile(indexPath, JSON.stringify(updated, null, 2), "utf8");
14733
15171
  }
@@ -14760,7 +15198,7 @@ async function statDirSize(absPath) {
14760
15198
  return 0;
14761
15199
  }
14762
15200
  for (const entry of entries) {
14763
- const childPath = path57.join(absPath, entry.name);
15201
+ const childPath = path59.join(absPath, entry.name);
14764
15202
  if (entry.isSymbolicLink()) {
14765
15203
  continue;
14766
15204
  }
@@ -15073,6 +15511,12 @@ var init_cache_cmd = __esm({
15073
15511
  });
15074
15512
 
15075
15513
  // src/utils/ai-client.ts
15514
+ var ai_client_exports = {};
15515
+ __export(ai_client_exports, {
15516
+ callClaude: () => callClaude,
15517
+ callClaudeParallel: () => callClaudeParallel,
15518
+ getAICliName: () => getAICliName
15519
+ });
15076
15520
  import { spawn, execFileSync as execFileSync2 } from "child_process";
15077
15521
  import { existsSync } from "fs";
15078
15522
  function detectClaudeCli() {
@@ -15242,7 +15686,7 @@ var init_ai_client = __esm({
15242
15686
 
15243
15687
  // src/import-local.ts
15244
15688
  import fs20 from "fs";
15245
- import path58 from "path";
15689
+ import path60 from "path";
15246
15690
  import readline3 from "readline";
15247
15691
  function toSlug(title) {
15248
15692
  return title.toLowerCase().replace(/[^a-z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
@@ -15278,7 +15722,7 @@ function parseClassifyOutput(sourcePath, rawContent, output) {
15278
15722
  sourcePath,
15279
15723
  rawContent,
15280
15724
  type: knownType,
15281
- title: typeof parsed.title === "string" ? parsed.title : path58.basename(sourcePath),
15725
+ title: typeof parsed.title === "string" ? parsed.title : path60.basename(sourcePath),
15282
15726
  summary: typeof parsed.summary === "string" ? parsed.summary : "",
15283
15727
  tags: Array.isArray(parsed.tags) ? parsed.tags.filter((t) => typeof t === "string") : [],
15284
15728
  confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0,
@@ -15290,7 +15734,7 @@ function parseClassifyOutput(sourcePath, rawContent, output) {
15290
15734
  sourcePath,
15291
15735
  rawContent,
15292
15736
  type: "learning",
15293
- title: path58.basename(sourcePath),
15737
+ title: path60.basename(sourcePath),
15294
15738
  summary: "",
15295
15739
  tags: [],
15296
15740
  confidence: 0,
@@ -15336,9 +15780,9 @@ async function scanCandidates(opts) {
15336
15780
  const relPaths = await listFilesRecursive(expandedDir);
15337
15781
  for (const relPath of relPaths) {
15338
15782
  if (relPath.split("/").some((seg) => seg.startsWith("."))) continue;
15339
- const ext = path58.extname(relPath).toLowerCase();
15783
+ const ext = path60.extname(relPath).toLowerCase();
15340
15784
  if (ext !== ".md" && ext !== ".txt") continue;
15341
- const absPath = path58.join(expandedDir, relPath);
15785
+ const absPath = path60.join(expandedDir, relPath);
15342
15786
  try {
15343
15787
  const stat6 = fs20.statSync(absPath);
15344
15788
  if (stat6.size > MAX_FILE_SIZE_BYTES) continue;
@@ -15360,8 +15804,8 @@ async function scanCandidates(opts) {
15360
15804
  if (!fs20.existsSync(baseDir)) continue;
15361
15805
  const relPaths = await listFilesRecursive(baseDir);
15362
15806
  for (const relPath of relPaths) {
15363
- if (path58.extname(relPath).toLowerCase() !== ".md") continue;
15364
- const absPath = path58.join(baseDir, relPath);
15807
+ if (path60.extname(relPath).toLowerCase() !== ".md") continue;
15808
+ const absPath = path60.join(baseDir, relPath);
15365
15809
  try {
15366
15810
  const stat6 = fs20.statSync(absPath);
15367
15811
  if (stat6.size > MAX_FILE_SIZE_BYTES) continue;
@@ -15392,7 +15836,7 @@ async function classifyWithAI(candidates) {
15392
15836
  sourcePath: c.path,
15393
15837
  rawContent: c.rawContent,
15394
15838
  type: "learning",
15395
- title: path58.basename(c.path),
15839
+ title: path60.basename(c.path),
15396
15840
  summary: "",
15397
15841
  tags: [],
15398
15842
  confidence: 0,
@@ -15472,7 +15916,7 @@ async function interactiveReview(items, opts) {
15472
15916
  for (const sessionItem of pendingItems) {
15473
15917
  const currentIndex = session.items.indexOf(sessionItem) + 1;
15474
15918
  const classified = classifiedMap.get(sessionItem.sourcePath ?? "");
15475
- const title = sessionItem.learningDraft?.title ?? classified?.title ?? path58.basename(sessionItem.sourcePath ?? "");
15919
+ const title = sessionItem.learningDraft?.title ?? classified?.title ?? path60.basename(sessionItem.sourcePath ?? "");
15476
15920
  const itemType = classified?.type ?? "learning";
15477
15921
  const summary = classified?.summary ?? "";
15478
15922
  const tags = classified?.tags ?? [];
@@ -15539,9 +15983,9 @@ async function pushAccepted(session, repoPath, opts) {
15539
15983
  } else {
15540
15984
  const typeInContent = detectTypeFromContent(draft.content);
15541
15985
  const subDir = typeInContent === "rule" ? "rules" : typeInContent === "doc" ? "docs" : "learnings";
15542
- destDir = path58.join(expandHome(repoPath), subDir);
15986
+ destDir = path60.join(expandHome(repoPath), subDir);
15543
15987
  }
15544
- const destPath = path58.join(destDir, filename);
15988
+ const destPath = path60.join(destDir, filename);
15545
15989
  if (opts.dryRun) {
15546
15990
  log.info(`[dry-run] would write: ${destPath}`);
15547
15991
  pushed++;
@@ -15806,7 +16250,7 @@ var init_iwiki_client = __esm({
15806
16250
  });
15807
16251
 
15808
16252
  // src/import-iwiki.ts
15809
- import path59 from "path";
16253
+ import path61 from "path";
15810
16254
  import { readFile as readFile3, writeFile as writeFile5 } from "fs/promises";
15811
16255
  function parseIWikiInput(input) {
15812
16256
  const trimmed = input.trim();
@@ -15895,8 +16339,8 @@ async function importFromIWiki(opts) {
15895
16339
  dryRun: opts.dryRun,
15896
16340
  outputDir: opts.outputDir
15897
16341
  });
15898
- const teamwikiRoot = path59.join(repoPath, "teamwiki");
15899
- if (await pathExists(path59.join(teamwikiRoot, ".indices", "graph-index.json"))) {
16342
+ const teamwikiRoot = path61.join(repoPath, "teamwiki");
16343
+ if (await pathExists(path61.join(teamwikiRoot, ".indices", "graph-index.json"))) {
15900
16344
  try {
15901
16345
  const mapsToEdges = await reconcileIwikiWithCodebase(documents, teamwikiRoot);
15902
16346
  if (mapsToEdges.length > 0) {
@@ -15915,7 +16359,7 @@ async function importFromIWiki(opts) {
15915
16359
  log.success("iWiki import complete");
15916
16360
  }
15917
16361
  async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
15918
- const graphPath = path59.join(teamwikiRoot, ".indices", "graph-index.json");
16362
+ const graphPath = path61.join(teamwikiRoot, ".indices", "graph-index.json");
15919
16363
  const graphRaw = await readFile3(graphPath, "utf-8");
15920
16364
  const graph = JSON.parse(graphRaw);
15921
16365
  const codeLabels = /* @__PURE__ */ new Map();
@@ -15924,17 +16368,17 @@ async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
15924
16368
  const words = node.label.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
15925
16369
  codeLabels.set(words, node.id);
15926
16370
  }
15927
- const evidenceDir = path59.join(teamwikiRoot, "evidence", "code");
16371
+ const evidenceDir = path61.join(teamwikiRoot, "evidence", "code");
15928
16372
  const codePageContents = /* @__PURE__ */ new Map();
15929
16373
  if (await pathExists(evidenceDir)) {
15930
16374
  const { readdir: readdir9 } = await import("fs/promises");
15931
16375
  const projects = await readdir9(evidenceDir);
15932
16376
  for (const project of projects) {
15933
- const projectDir = path59.join(evidenceDir, project);
16377
+ const projectDir = path61.join(evidenceDir, project);
15934
16378
  const files = await readdir9(projectDir).catch(() => []);
15935
16379
  for (const file of files) {
15936
16380
  if (!file.endsWith(".md")) continue;
15937
- const content = await readFile3(path59.join(projectDir, file), "utf-8").catch(() => "");
16381
+ const content = await readFile3(path61.join(projectDir, file), "utf-8").catch(() => "");
15938
16382
  codePageContents.set(`evidence/code/${project}/${file}`, content);
15939
16383
  }
15940
16384
  }
@@ -16023,7 +16467,7 @@ function parseGitHubPRUrl(url) {
16023
16467
  }
16024
16468
  return { owner: match[1], repo: match[2], number: match[3] };
16025
16469
  }
16026
- async function githubApiGet(path92) {
16470
+ async function githubApiGet(path99) {
16027
16471
  return new Promise((resolve, reject) => {
16028
16472
  const token = process.env["GITHUB_TOKEN"];
16029
16473
  const headers = {
@@ -16032,7 +16476,7 @@ async function githubApiGet(path92) {
16032
16476
  };
16033
16477
  if (token) headers["Authorization"] = `Bearer ${token}`;
16034
16478
  const req = https2.request(
16035
- { hostname: "api.github.com", path: path92, headers },
16479
+ { hostname: "api.github.com", path: path99, headers },
16036
16480
  (res) => {
16037
16481
  const chunks = [];
16038
16482
  res.on("data", (c) => chunks.push(c));
@@ -16228,8 +16672,8 @@ var init_mr_fetch2 = __esm({
16228
16672
 
16229
16673
  // src/utils/dedup.ts
16230
16674
  import fs21 from "fs/promises";
16231
- import path60 from "path";
16232
- import matter4 from "gray-matter";
16675
+ import path62 from "path";
16676
+ import matter5 from "gray-matter";
16233
16677
  function extractKeywords(text) {
16234
16678
  const keywords = /* @__PURE__ */ new Set();
16235
16679
  const enWords = text.match(/[a-zA-Z]+/g) ?? [];
@@ -16286,14 +16730,14 @@ async function findSupersededLearnings(draftKeywords, learningsDir, withinDays =
16286
16730
  const cutoffDate = new Date(Date.now() - withinDays * 24 * 60 * 60 * 1e3);
16287
16731
  const results = [];
16288
16732
  for (const filename of mdFiles) {
16289
- const filePath = path60.join(learningsDir, filename);
16733
+ const filePath = path62.join(learningsDir, filename);
16290
16734
  try {
16291
16735
  const docDate = await resolveDocDate(filePath, filename);
16292
16736
  if (docDate < cutoffDate) {
16293
16737
  continue;
16294
16738
  }
16295
16739
  const raw = await fs21.readFile(filePath, "utf8");
16296
- const { content: body } = matter4(raw);
16740
+ const { content: body } = matter5(raw);
16297
16741
  const fileKeywords = extractKeywords(body);
16298
16742
  const ratio = overlapRatio(draftKeywords, fileKeywords);
16299
16743
  if (ratio >= 0.6) {
@@ -16368,9 +16812,9 @@ var init_dedup = __esm({
16368
16812
 
16369
16813
  // src/import-mr.ts
16370
16814
  import fs22 from "fs/promises";
16371
- import path61 from "path";
16815
+ import path63 from "path";
16372
16816
  import readline4 from "readline/promises";
16373
- import matter5 from "gray-matter";
16817
+ import matter6 from "gray-matter";
16374
16818
  async function fetchMR(url) {
16375
16819
  if (url.includes("github.com")) {
16376
16820
  return fetchGitHubPR(url);
@@ -16472,7 +16916,7 @@ async function importFromMR(opts) {
16472
16916
  if (frontmatterStart > 0) {
16473
16917
  learningContent = learningContent.slice(frontmatterStart);
16474
16918
  }
16475
- const parsed = matter5(learningContent);
16919
+ const parsed = matter6(learningContent);
16476
16920
  const learningTitle = parsed.data["title"] ?? mr.title;
16477
16921
  const draftKeywords = extractKeywords(learningContent);
16478
16922
  const supersededEntries = await findSupersededLearnings(draftKeywords, learningsDir);
@@ -16506,18 +16950,18 @@ async function importFromMR(opts) {
16506
16950
  async function writeLearning(draft, outputDir, repoPath) {
16507
16951
  if (outputDir) {
16508
16952
  await fs22.mkdir(outputDir, { recursive: true });
16509
- const filePath = path61.join(outputDir, "learning.md");
16953
+ const filePath = path63.join(outputDir, "learning.md");
16510
16954
  await fs22.writeFile(filePath, draft.content, "utf-8");
16511
16955
  log.info(`Learning written: ${filePath}`);
16512
16956
  return;
16513
16957
  }
16514
16958
  if (repoPath) {
16515
- const learningsDir = path61.join(repoPath, "learnings");
16959
+ const learningsDir = path63.join(repoPath, "learnings");
16516
16960
  await fs22.mkdir(learningsDir, { recursive: true });
16517
16961
  const datePrefix = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
16518
16962
  const safeTitle = draft.title.slice(0, 40).replace(/[^a-zA-Z0-9一-鿿_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
16519
16963
  const filename = `${datePrefix}-${safeTitle}.md`;
16520
- const filePath = path61.join(learningsDir, filename);
16964
+ const filePath = path63.join(learningsDir, filename);
16521
16965
  await fs22.writeFile(filePath, draft.content, "utf-8");
16522
16966
  log.info(`Learning written: ${filePath}`);
16523
16967
  return;
@@ -16533,7 +16977,7 @@ var init_import_mr = __esm({
16533
16977
  init_ai_client();
16534
16978
  init_dedup();
16535
16979
  init_logger();
16536
- DEFAULT_LEARNINGS_DIR = path61.join(process.env.HOME ?? "/tmp", ".teamai", "learnings");
16980
+ DEFAULT_LEARNINGS_DIR = path63.join(process.env.HOME ?? "/tmp", ".teamai", "learnings");
16537
16981
  SUPERSEDE_THRESHOLD = 0.6;
16538
16982
  }
16539
16983
  });
@@ -16541,8 +16985,8 @@ var init_import_mr = __esm({
16541
16985
  // src/codebase.ts
16542
16986
  import { execSync as execSync6 } from "child_process";
16543
16987
  import fs23 from "fs";
16544
- import path62 from "path";
16545
- import matter6 from "gray-matter";
16988
+ import path64 from "path";
16989
+ import matter7 from "gray-matter";
16546
16990
  async function gatherRepoContext(repoPath) {
16547
16991
  const parts = [];
16548
16992
  try {
@@ -16565,7 +17009,7 @@ ${truncated}`);
16565
17009
  } catch (err) {
16566
17010
  log.debug(`gatherRepoContext: find \u5931\u8D25 \u2014 ${String(err)}`);
16567
17011
  }
16568
- const pkgPath = path62.join(repoPath, "package.json");
17012
+ const pkgPath = path64.join(repoPath, "package.json");
16569
17013
  if (fs23.existsSync(pkgPath)) {
16570
17014
  try {
16571
17015
  const raw = fs23.readFileSync(pkgPath, "utf-8");
@@ -16579,7 +17023,7 @@ ${excerpt}
16579
17023
  }
16580
17024
  }
16581
17025
  for (const candidate of ["src/index.ts", "src/main.ts", "index.ts", "main.py"]) {
16582
- const entryPath = path62.join(repoPath, candidate);
17026
+ const entryPath = path64.join(repoPath, candidate);
16583
17027
  if (fs23.existsSync(entryPath)) {
16584
17028
  try {
16585
17029
  const raw = fs23.readFileSync(entryPath, "utf-8");
@@ -16595,7 +17039,7 @@ ${excerpt}
16595
17039
  }
16596
17040
  }
16597
17041
  for (const candidate of ["src/types.ts", "src/types/index.ts", "types.py"]) {
16598
- const typesPath = path62.join(repoPath, candidate);
17042
+ const typesPath = path64.join(repoPath, candidate);
16599
17043
  if (fs23.existsSync(typesPath)) {
16600
17044
  try {
16601
17045
  const raw = fs23.readFileSync(typesPath, "utf-8");
@@ -16611,10 +17055,10 @@ ${excerpt}
16611
17055
  }
16612
17056
  }
16613
17057
  const docCandidates = [
16614
- path62.join(repoPath, "README.md"),
16615
- path62.join(repoPath, "ARCHITECTURE.md")
17058
+ path64.join(repoPath, "README.md"),
17059
+ path64.join(repoPath, "ARCHITECTURE.md")
16616
17060
  ];
16617
- const docsDir = path62.join(repoPath, "docs");
17061
+ const docsDir = path64.join(repoPath, "docs");
16618
17062
  if (fs23.existsSync(docsDir)) {
16619
17063
  try {
16620
17064
  const entries = fs23.readdirSync(docsDir);
@@ -16622,7 +17066,7 @@ ${excerpt}
16622
17066
  for (const entry of entries) {
16623
17067
  if (count >= DOCS_MAX_FILES) break;
16624
17068
  if (entry.endsWith(".md")) {
16625
- docCandidates.push(path62.join(docsDir, entry));
17069
+ docCandidates.push(path64.join(docsDir, entry));
16626
17070
  count++;
16627
17071
  }
16628
17072
  }
@@ -16635,7 +17079,7 @@ ${excerpt}
16635
17079
  try {
16636
17080
  const raw = fs23.readFileSync(docPath, "utf-8");
16637
17081
  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);
17082
+ const relPath = path64.relative(repoPath, docPath);
16639
17083
  parts.push(`## \u6587\u6863\u6458\u8981\uFF1A${relPath}
16640
17084
  ${excerpt}`);
16641
17085
  } catch (err) {
@@ -16666,9 +17110,9 @@ ${lines.join("\n")}`);
16666
17110
  if (fileCount >= LEARNINGS_MAX_FILES) break;
16667
17111
  if (!entry.endsWith(".md")) continue;
16668
17112
  try {
16669
- const filePath = path62.join(learningsDir, entry);
17113
+ const filePath = path64.join(learningsDir, entry);
16670
17114
  const raw = fs23.readFileSync(filePath, "utf-8");
16671
- const parsed = matter6(raw);
17115
+ const parsed = matter7(raw);
16672
17116
  const tags = parsed.data["tags"];
16673
17117
  if (Array.isArray(tags)) {
16674
17118
  for (const tag of tags) {
@@ -16861,7 +17305,7 @@ var init_codebase = __esm({
16861
17305
  import { createHash } from "crypto";
16862
17306
  import { execFile } from "child_process";
16863
17307
  import { readFile as readFile4, readdir as readdir2, stat } from "fs/promises";
16864
- import path63 from "path";
17308
+ import path65 from "path";
16865
17309
  import { promisify } from "util";
16866
17310
  function isKeyFile(relativePath, language) {
16867
17311
  const patterns = KEY_FILE_PATTERNS[language];
@@ -16869,12 +17313,12 @@ function isKeyFile(relativePath, language) {
16869
17313
  return patterns.some((pattern) => pattern.test(relativePath));
16870
17314
  }
16871
17315
  async function collectCode(options) {
16872
- const root = path63.resolve(options.root);
17316
+ const root = path65.resolve(options.root);
16873
17317
  const filePaths = [];
16874
17318
  await walk(root, filePaths, options.includeTests ?? false);
16875
17319
  let filtered = filePaths.sort((a, b) => {
16876
- const relA = toPosix(path63.relative(root, a));
16877
- const relB = toPosix(path63.relative(root, b));
17320
+ const relA = toPosix(path65.relative(root, a));
17321
+ const relB = toPosix(path65.relative(root, b));
16878
17322
  const langA = languageFor(a);
16879
17323
  const langB = languageFor(b);
16880
17324
  const keyA = isKeyFile(relA, langA) ? 0 : 1;
@@ -16888,7 +17332,7 @@ async function collectCode(options) {
16888
17332
  if (options.changedFiles && options.changedFiles.length > 0) {
16889
17333
  const changedSet = new Set(options.changedFiles.map((f) => toPosix(f)));
16890
17334
  filtered = filtered.filter((fp) => {
16891
- const relativePath = toPosix(path63.relative(root, fp));
17335
+ const relativePath = toPosix(path65.relative(root, fp));
16892
17336
  return changedSet.has(relativePath);
16893
17337
  });
16894
17338
  }
@@ -16896,7 +17340,7 @@ async function collectCode(options) {
16896
17340
  const files = [];
16897
17341
  for (const filePath of limited) {
16898
17342
  const content = await readFile4(filePath, "utf8");
16899
- const relativePath = toPosix(path63.relative(root, filePath));
17343
+ const relativePath = toPosix(path65.relative(root, filePath));
16900
17344
  const language = languageFor(filePath);
16901
17345
  files.push({
16902
17346
  path: filePath,
@@ -16923,7 +17367,7 @@ async function walk(directory, results, includeTests) {
16923
17367
  return;
16924
17368
  }
16925
17369
  for (const entry of await readdir2(directory, { withFileTypes: true })) {
16926
- const fullPath = path63.join(directory, entry.name);
17370
+ const fullPath = path65.join(directory, entry.name);
16927
17371
  if (safeIgnore(fullPath) || !includeTests && isTestPath(fullPath)) {
16928
17372
  continue;
16929
17373
  }
@@ -16936,14 +17380,14 @@ async function walk(directory, results, includeTests) {
16936
17380
  }
16937
17381
  function isCodeFile(filePath) {
16938
17382
  return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".json", ".yaml", ".yml", ".toml", ".sql", ".conf", ".ini"].includes(
16939
- path63.extname(filePath).toLowerCase()
17383
+ path65.extname(filePath).toLowerCase()
16940
17384
  );
16941
17385
  }
16942
17386
  function isTestPath(filePath) {
16943
17387
  return /(^|\/|\\)(test|tests|__tests__|fixtures)(\/|\\)|\.test\.|\.spec\./u.test(filePath);
16944
17388
  }
16945
17389
  function languageFor(filePath) {
16946
- const ext = path63.extname(filePath).toLowerCase();
17390
+ const ext = path65.extname(filePath).toLowerCase();
16947
17391
  const map = {
16948
17392
  ".ts": "typescript",
16949
17393
  ".tsx": "typescript",
@@ -17587,14 +18031,14 @@ var init_code_extractors = __esm({
17587
18031
  });
17588
18032
 
17589
18033
  // src/wiki-engine/code-knowledge/code-graph.ts
17590
- import path64 from "path";
18034
+ import path66 from "path";
17591
18035
  function buildCodeGraph(facts) {
17592
18036
  const nodes = facts.filter((fact) => fact.kind !== "relation").map((fact) => ({
17593
18037
  slug: `${fact.kind}/${fact.name}`,
17594
18038
  type: mapFactKindToCategory(fact.kind),
17595
18039
  confidence: fact.confidence === "EXTRACTED" ? "EXTRACTED" : "INFERRED",
17596
18040
  title: fact.name,
17597
- domain: path64.dirname(fact.file).split("/")[0] || void 0
18041
+ domain: path66.dirname(fact.file).split("/")[0] || void 0
17598
18042
  }));
17599
18043
  const nodeFiles = new Set(facts.filter((f) => f.kind !== "relation").map((f) => f.file));
17600
18044
  const edges = facts.filter((fact) => fact.kind === "relation").flatMap((fact) => {
@@ -17637,7 +18081,7 @@ var init_code_graph = __esm({
17637
18081
 
17638
18082
  // src/wiki-engine/code-knowledge/code-incremental.ts
17639
18083
  import { readFile as readFile5, writeFile as writeFile6, stat as stat2, mkdir as mkdir3 } from "fs/promises";
17640
- import path65 from "path";
18084
+ import path67 from "path";
17641
18085
  async function detectCodeIncrementalChanges(root, manifestPath, project) {
17642
18086
  const previous = await exists(manifestPath) ? JSON.parse(await readFile5(manifestPath, "utf8")) : { files: [] };
17643
18087
  const current = await collectCode({ root });
@@ -17663,14 +18107,14 @@ function affectedPages(project, files) {
17663
18107
  }
17664
18108
  async function exists(filePath) {
17665
18109
  try {
17666
- await stat2(path65.resolve(filePath));
18110
+ await stat2(path67.resolve(filePath));
17667
18111
  return true;
17668
18112
  } catch {
17669
18113
  return false;
17670
18114
  }
17671
18115
  }
17672
18116
  async function loadFactsCache(indicesDir) {
17673
- const cachePath = path65.join(indicesDir, FACTS_CACHE_FILENAME);
18117
+ const cachePath = path67.join(indicesDir, FACTS_CACHE_FILENAME);
17674
18118
  try {
17675
18119
  const raw = await readFile5(cachePath, "utf-8");
17676
18120
  const parsed = JSON.parse(raw);
@@ -17681,10 +18125,10 @@ async function loadFactsCache(indicesDir) {
17681
18125
  }
17682
18126
  async function saveFactsCache(indicesDir, facts) {
17683
18127
  await mkdir3(indicesDir, { recursive: true });
17684
- await writeFile6(path65.join(indicesDir, FACTS_CACHE_FILENAME), JSON.stringify(facts), "utf-8");
18128
+ await writeFile6(path67.join(indicesDir, FACTS_CACHE_FILENAME), JSON.stringify(facts), "utf-8");
17685
18129
  }
17686
18130
  async function loadInterfacesCache(indicesDir) {
17687
- const cachePath = path65.join(indicesDir, INTERFACES_CACHE_FILENAME);
18131
+ const cachePath = path67.join(indicesDir, INTERFACES_CACHE_FILENAME);
17688
18132
  try {
17689
18133
  const raw = await readFile5(cachePath, "utf-8");
17690
18134
  const parsed = JSON.parse(raw);
@@ -17696,7 +18140,7 @@ async function loadInterfacesCache(indicesDir) {
17696
18140
  async function saveInterfacesCache(indicesDir, inventory) {
17697
18141
  await mkdir3(indicesDir, { recursive: true });
17698
18142
  await writeFile6(
17699
- path65.join(indicesDir, INTERFACES_CACHE_FILENAME),
18143
+ path67.join(indicesDir, INTERFACES_CACHE_FILENAME),
17700
18144
  JSON.stringify(inventory, null, 2),
17701
18145
  "utf-8"
17702
18146
  );
@@ -17722,7 +18166,7 @@ var init_code_incremental = __esm({
17722
18166
  });
17723
18167
 
17724
18168
  // src/wiki-engine/interface-scanner.ts
17725
- import path66 from "path";
18169
+ import path68 from "path";
17726
18170
  async function scanInterfaces(files) {
17727
18171
  const componentMap = groupByComponent(files);
17728
18172
  const entries = [];
@@ -17790,7 +18234,7 @@ function groupByComponent(files) {
17790
18234
  if (file.repo) {
17791
18235
  component = parts.length > 1 ? `${file.repo}/${parts[0]}` : file.repo;
17792
18236
  } else {
17793
- component = parts.length > 1 ? parts[0] : path66.basename(path66.dirname(file.path));
18237
+ component = parts.length > 1 ? parts[0] : path68.basename(path68.dirname(file.path));
17794
18238
  }
17795
18239
  const group = map.get(component) ?? [];
17796
18240
  group.push(file);
@@ -18076,7 +18520,7 @@ var init_reconciler_v2_types = __esm({
18076
18520
 
18077
18521
  // src/wiki-engine/knowledge-reconciler.ts
18078
18522
  import { readFile as readFile6, readdir as readdir3, stat as stat3 } from "fs/promises";
18079
- import path67 from "path";
18523
+ import path69 from "path";
18080
18524
  async function exists2(p) {
18081
18525
  return stat3(p).then(() => true).catch(() => false);
18082
18526
  }
@@ -18085,7 +18529,7 @@ async function readPages(dirPath) {
18085
18529
  const entries = await readdir3(dirPath, { withFileTypes: true });
18086
18530
  const pages = [];
18087
18531
  for (const entry of entries) {
18088
- const full = path67.join(dirPath, entry.name);
18532
+ const full = path69.join(dirPath, entry.name);
18089
18533
  if (entry.isDirectory()) {
18090
18534
  pages.push(...await readPages(full));
18091
18535
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -18178,17 +18622,17 @@ async function reconcileKnowledge(options) {
18178
18622
  const productDirNames = options.productDirs ?? ["product", "docs"];
18179
18623
  const codeDirNames = options.codeDirs ?? ["evidence/code"];
18180
18624
  for (const dir of [...productDirNames, ...codeDirNames]) {
18181
- if (dir.includes("..") || path67.isAbsolute(dir)) {
18625
+ if (dir.includes("..") || path69.isAbsolute(dir)) {
18182
18626
  throw new Error(`Unsafe directory path rejected: ${dir}`);
18183
18627
  }
18184
18628
  }
18185
18629
  const productPages = [];
18186
18630
  for (const dir of productDirNames) {
18187
- productPages.push(...await readPages(path67.join(wikiRoot, dir)));
18631
+ productPages.push(...await readPages(path69.join(wikiRoot, dir)));
18188
18632
  }
18189
18633
  const codePages = [];
18190
18634
  for (const dir of codeDirNames) {
18191
- codePages.push(...await readPages(path67.join(wikiRoot, dir)));
18635
+ codePages.push(...await readPages(path69.join(wikiRoot, dir)));
18192
18636
  }
18193
18637
  const graphEdges = [];
18194
18638
  const gaps = [];
@@ -18215,8 +18659,8 @@ async function reconcileKnowledge(options) {
18215
18659
  ];
18216
18660
  const nc = buildConfidence(factors);
18217
18661
  graphEdges.push({
18218
- from: toPageSlug(path67.relative(wikiRoot, productPage.path)),
18219
- to: toPageSlug(path67.relative(wikiRoot, codePage.path)),
18662
+ from: toPageSlug(path69.relative(wikiRoot, productPage.path)),
18663
+ to: toPageSlug(path69.relative(wikiRoot, codePage.path)),
18220
18664
  relation: "MAPS_TO",
18221
18665
  term,
18222
18666
  confidence: nc.label,
@@ -18297,10 +18741,10 @@ async function reconcileKnowledge(options) {
18297
18741
  const MS_PER_DAY = 864e5;
18298
18742
  for (const edge of graphEdges) {
18299
18743
  const fromPage = productPages.find(
18300
- (p) => toPageSlug(path67.relative(wikiRoot, p.path)) === edge.from
18744
+ (p) => toPageSlug(path69.relative(wikiRoot, p.path)) === edge.from
18301
18745
  );
18302
18746
  const toPage = codePages.find(
18303
- (p) => toPageSlug(path67.relative(wikiRoot, p.path)) === edge.to
18747
+ (p) => toPageSlug(path69.relative(wikiRoot, p.path)) === edge.to
18304
18748
  );
18305
18749
  if (!fromPage?.updated || !toPage?.updated) continue;
18306
18750
  const fromMs = new Date(fromPage.updated).getTime();
@@ -18559,7 +19003,7 @@ __export(enrich_with_ai_exports, {
18559
19003
  enrichWithAI: () => enrichWithAI,
18560
19004
  writeManifest: () => writeManifest
18561
19005
  });
18562
- import path68 from "path";
19006
+ import path70 from "path";
18563
19007
  import { writeFile as writeFile8, mkdir as mkdir5 } from "fs/promises";
18564
19008
  function sanitizeForPrompt(text) {
18565
19009
  return text.replace(/[\n\r]/g, " ").replace(/[<>]/g, "").slice(0, 200);
@@ -18700,7 +19144,7 @@ async function enrichWithAI(ctx) {
18700
19144
  }
18701
19145
  async function writeManifest(manifest, outputDir) {
18702
19146
  await mkdir5(outputDir, { recursive: true });
18703
- const manifestPath = path68.join(outputDir, "_manifest.json");
19147
+ const manifestPath = path70.join(outputDir, "_manifest.json");
18704
19148
  await writeFile8(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
18705
19149
  return manifestPath;
18706
19150
  }
@@ -18718,7 +19162,7 @@ __export(codebase_extract_exports, {
18718
19162
  extractCodebase: () => extractCodebase
18719
19163
  });
18720
19164
  import { mkdir as mkdir6, writeFile as writeFile9, readFile as readFile7 } from "fs/promises";
18721
- import path69 from "path";
19165
+ import path71 from "path";
18722
19166
  import chalk3 from "chalk";
18723
19167
  function detectKnowledgeGaps(facts, graph, files) {
18724
19168
  const gaps = [];
@@ -18735,7 +19179,7 @@ function detectKnowledgeGaps(facts, graph, files) {
18735
19179
  const target = rel.name;
18736
19180
  if (target.startsWith(".")) continue;
18737
19181
  if (target.startsWith("node:")) continue;
18738
- const matchesAnyFile = [...scannedFiles].some((f) => f.includes(target.replace(/\//g, path69.sep)));
19182
+ const matchesAnyFile = [...scannedFiles].some((f) => f.includes(target.replace(/\//g, path71.sep)));
18739
19183
  if (!matchesAnyFile) {
18740
19184
  unresolvedImports.add(target);
18741
19185
  }
@@ -19079,21 +19523,21 @@ function buildOverview(facts, graph, project, interfaceInventory, callChains) {
19079
19523
  lines.push("## Key Dependency Paths");
19080
19524
  lines.push("");
19081
19525
  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}`);
19526
+ const path99 = chain.steps.map((s) => s.symbol).join(" \u2192 ");
19527
+ lines.push(`- ${chain.entryPoint}: ${path99}`);
19084
19528
  }
19085
19529
  }
19086
19530
  lines.push("");
19087
19531
  return lines.join("\n");
19088
19532
  }
19089
19533
  async function extractCodebase(opts) {
19090
- const root = path69.resolve(opts.path || ".");
19091
- const project = opts.project || path69.basename(root);
19534
+ const root = path71.resolve(opts.path || ".");
19535
+ const project = opts.project || path71.basename(root);
19092
19536
  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");
19537
+ const outputBase = opts.outputRoot ? path71.resolve(opts.outputRoot) : root;
19538
+ const wikiRoot = path71.join(outputBase, "teamwiki");
19539
+ const evidenceDir = path71.join(wikiRoot, "evidence", "code", project);
19540
+ const manifestPath = path71.join(wikiRoot, "source-manifest.json");
19097
19541
  let changedFiles;
19098
19542
  let deletedFiles = [];
19099
19543
  if (opts.incremental) {
@@ -19130,7 +19574,7 @@ async function extractCodebase(opts) {
19130
19574
  const newFacts = files.length > 0 ? extractCodeFacts(files) : [];
19131
19575
  let facts;
19132
19576
  let interfaceInventory;
19133
- const indicesDir = path69.join(wikiRoot, ".indices");
19577
+ const indicesDir = path71.join(wikiRoot, ".indices");
19134
19578
  if (changedFiles !== void 0) {
19135
19579
  const oldFacts = await loadFactsCache(indicesDir);
19136
19580
  const oldInterfaces = await loadInterfacesCache(indicesDir);
@@ -19158,7 +19602,7 @@ async function extractCodebase(opts) {
19158
19602
  }
19159
19603
  const graph = buildCodeGraph(facts);
19160
19604
  let callChains;
19161
- const depPathsFile = path69.join(evidenceDir, "dependency-paths.md");
19605
+ const depPathsFile = path71.join(evidenceDir, "dependency-paths.md");
19162
19606
  if (changedFiles) {
19163
19607
  callChains = [];
19164
19608
  } else {
@@ -19174,7 +19618,7 @@ async function extractCodebase(opts) {
19174
19618
  }
19175
19619
  }
19176
19620
  for (const [filename, content] of pages) {
19177
- await writeIfChanged(path69.join(evidenceDir, filename), content);
19621
+ await writeIfChanged(path71.join(evidenceDir, filename), content);
19178
19622
  }
19179
19623
  const pageSlugs = [...pages.keys()].map((p) => `evidence/code/${project}/${p.replace(".md", "")}`);
19180
19624
  const overlay = buildIndexHubOverlay(project, "evidence/code", pageSlugs);
@@ -19203,7 +19647,7 @@ async function extractCodebase(opts) {
19203
19647
  keywords: enrichResult.repoKeywords || [],
19204
19648
  components: enrichResult.domains[0]?.components ?? []
19205
19649
  };
19206
- await writeFile9(path69.join(evidenceDir, "_domains.json"), JSON.stringify(domainMeta, null, 2), "utf-8");
19650
+ await writeFile9(path71.join(evidenceDir, "_domains.json"), JSON.stringify(domainMeta, null, 2), "utf-8");
19207
19651
  if (!opts.json) {
19208
19652
  const domainLabel = domainMeta.domain || "uncategorized";
19209
19653
  console.log(` AI enrich: ${enrichResult.manifest.components.length} modules, domain=${domainLabel}`);
@@ -19216,14 +19660,14 @@ async function extractCodebase(opts) {
19216
19660
  }
19217
19661
  const moduleSummaries = buildModuleSummaries(facts, graph, project);
19218
19662
  if (moduleSummaries.size > 0) {
19219
- const modulesDir = path69.join(evidenceDir, "modules");
19663
+ const modulesDir = path71.join(evidenceDir, "modules");
19220
19664
  await mkdir6(modulesDir, { recursive: true });
19221
19665
  for (const [filename, content] of moduleSummaries) {
19222
- await writeIfChanged(path69.join(modulesDir, filename), content);
19666
+ await writeIfChanged(path71.join(modulesDir, filename), content);
19223
19667
  }
19224
19668
  }
19225
19669
  const overview = buildOverview(facts, repoGraph, project, interfaceInventory, callChains);
19226
- await writeIfChanged(path69.join(evidenceDir, "overview.md"), overview);
19670
+ await writeIfChanged(path71.join(evidenceDir, "overview.md"), overview);
19227
19671
  const proj = [{ slug: project, label: project }];
19228
19672
  const ifByType = {};
19229
19673
  for (const e of interfaceInventory.entries) {
@@ -19236,11 +19680,11 @@ async function extractCodebase(opts) {
19236
19680
  interfaces: Object.keys(ifByType).length > 0 ? ifByType : void 0,
19237
19681
  callChains: callChains.length > 0 ? callChains.length : void 0
19238
19682
  };
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));
19683
+ await writeIfChanged(path71.join(wikiRoot, "router.md"), routerTemplate(proj, aiDomains.length > 0 ? aiDomains : void 0));
19684
+ await writeIfChanged(path71.join(wikiRoot, "hot.md"), HOT_TEMPLATE);
19685
+ await writeIfChanged(path71.join(wikiRoot, "index.md"), indexTemplate(proj, indexStats));
19242
19686
  const gaps = detectKnowledgeGaps(facts, graph, files);
19243
- const gapsDir = path69.join(wikiRoot, "gaps");
19687
+ const gapsDir = path71.join(wikiRoot, "gaps");
19244
19688
  await mkdir6(gapsDir, { recursive: true });
19245
19689
  const gapLines = [
19246
19690
  "---",
@@ -19263,7 +19707,7 @@ async function extractCodebase(opts) {
19263
19707
  gapLines.push("| \u2014 | \u2014 | \u2014 | \u672A\u53D1\u73B0\u660E\u663E\u77E5\u8BC6\u7F3A\u53E3 | \u2014 |");
19264
19708
  }
19265
19709
  gapLines.push("");
19266
- await writeIfChanged(path69.join(gapsDir, "detected.md"), gapLines.join("\n"));
19710
+ await writeIfChanged(path71.join(gapsDir, "detected.md"), gapLines.join("\n"));
19267
19711
  await saveFactsCache(indicesDir, facts);
19268
19712
  await saveInterfacesCache(indicesDir, interfaceInventory);
19269
19713
  let allManifestFiles = collectionManifest.files.map((f) => ({
@@ -19493,10 +19937,10 @@ var init_clone = __esm({
19493
19937
 
19494
19938
  // src/review-store.ts
19495
19939
  import crypto2 from "crypto";
19496
- import path70 from "path";
19940
+ import path72 from "path";
19497
19941
  import fs25 from "fs-extra";
19498
19942
  function getPendingReviewPath(cwd) {
19499
- return path70.join(cwd, PENDING_REVIEW_PATH);
19943
+ return path72.join(cwd, PENDING_REVIEW_PATH);
19500
19944
  }
19501
19945
  function computeReviewId(file, section, ts) {
19502
19946
  return crypto2.createHash("sha1").update(`${file}|${section ?? ""}|${ts}`).digest("hex").slice(0, 12);
@@ -19572,7 +20016,7 @@ async function loadPendingReview(cwd) {
19572
20016
  async function savePendingReview(cwd, items) {
19573
20017
  const filePath = getPendingReviewPath(cwd);
19574
20018
  const tmpPath = `${filePath}.tmp`;
19575
- await fs25.ensureDir(path70.dirname(filePath));
20019
+ await fs25.ensureDir(path72.dirname(filePath));
19576
20020
  const content = items.map((item) => JSON.stringify(item)).join("\n") + (items.length > 0 ? "\n" : "");
19577
20021
  await fs25.writeFile(tmpPath, content, "utf8");
19578
20022
  await fs25.rename(tmpPath, filePath);
@@ -19592,7 +20036,7 @@ async function appendPendingReview(cwd, partial) {
19592
20036
  risk
19593
20037
  };
19594
20038
  const filePath = getPendingReviewPath(cwd);
19595
- await fs25.ensureDir(path70.dirname(filePath));
20039
+ await fs25.ensureDir(path72.dirname(filePath));
19596
20040
  await fs25.appendFile(filePath, JSON.stringify(item) + "\n", "utf8");
19597
20041
  return item;
19598
20042
  }
@@ -19635,14 +20079,14 @@ __export(repo_cache_exports, {
19635
20079
  readLastSync: () => readLastSync,
19636
20080
  writeLastSync: () => writeLastSync
19637
20081
  });
19638
- import path71 from "path";
20082
+ import path73 from "path";
19639
20083
  import os4 from "os";
19640
20084
  import fs26 from "fs-extra";
19641
20085
  function getCacheRoot2() {
19642
- return process.env.TEAMAI_CACHE_DIR ?? path71.join(os4.homedir(), ".teamai", "cache", "repos");
20086
+ return process.env.TEAMAI_CACHE_DIR ?? path73.join(os4.homedir(), ".teamai", "cache", "repos");
19643
20087
  }
19644
20088
  function getRepoCacheDir(provider, owner, repo) {
19645
- return path71.join(getCacheRoot2(), provider, owner, repo);
20089
+ return path73.join(getCacheRoot2(), provider, owner, repo);
19646
20090
  }
19647
20091
  function getRepoSlug(provider, owner, repo) {
19648
20092
  const safeOwner = owner.replace(/\//g, "-");
@@ -19653,10 +20097,10 @@ async function writeLastSync(cacheDir, sha) {
19653
20097
  const content = `${sha}
19654
20098
  ${isoTs}
19655
20099
  `;
19656
- await fs26.writeFile(path71.join(cacheDir, LAST_SYNC_FILE), content, "utf8");
20100
+ await fs26.writeFile(path73.join(cacheDir, LAST_SYNC_FILE), content, "utf8");
19657
20101
  }
19658
20102
  async function readLastSync(cacheDir) {
19659
- const filePath = path71.join(cacheDir, LAST_SYNC_FILE);
20103
+ const filePath = path73.join(cacheDir, LAST_SYNC_FILE);
19660
20104
  const exists3 = await fs26.pathExists(filePath);
19661
20105
  if (!exists3) {
19662
20106
  return null;
@@ -19721,7 +20165,7 @@ var init_schema = __esm({
19721
20165
  });
19722
20166
 
19723
20167
  // src/domains/store.ts
19724
- import path72 from "path";
20168
+ import path74 from "path";
19725
20169
  import fs27 from "fs-extra";
19726
20170
  import { parse as yamlParse, stringify as yamlStringify } from "yaml";
19727
20171
  function parseAndValidate(content, filePath) {
@@ -19734,7 +20178,7 @@ function parseAndValidate(content, filePath) {
19734
20178
  return result.data;
19735
20179
  }
19736
20180
  async function loadDomains(cwd) {
19737
- const filePath = path72.join(cwd, DOMAINS_PATH);
20181
+ const filePath = path74.join(cwd, DOMAINS_PATH);
19738
20182
  const exists3 = await fs27.pathExists(filePath);
19739
20183
  if (!exists3) {
19740
20184
  return DomainsFileSchema.parse({});
@@ -19747,14 +20191,14 @@ async function loadDomains(cwd) {
19747
20191
  return parseAndValidate(content, filePath);
19748
20192
  }
19749
20193
  async function saveDomains(cwd, data) {
19750
- const filePath = path72.join(cwd, DOMAINS_PATH);
19751
- await fs27.ensureDir(path72.dirname(filePath));
20194
+ const filePath = path74.join(cwd, DOMAINS_PATH);
20195
+ await fs27.ensureDir(path74.dirname(filePath));
19752
20196
  await fs27.writeFile(filePath, yamlStringify(data), "utf8");
19753
20197
  }
19754
20198
  async function appendHistory(cwd, event) {
19755
20199
  const validated = HistoryEventSchema.parse(event);
19756
- const filePath = path72.join(cwd, HISTORY_PATH);
19757
- await fs27.ensureDir(path72.dirname(filePath));
20200
+ const filePath = path74.join(cwd, HISTORY_PATH);
20201
+ await fs27.ensureDir(path74.dirname(filePath));
19758
20202
  await fs27.appendFile(filePath, JSON.stringify(validated) + "\n", "utf8");
19759
20203
  }
19760
20204
  var DOMAINS_PATH, HISTORY_PATH, MAX_CONFIG_FILE_BYTES3;
@@ -19901,8 +20345,8 @@ __export(deep_enrich_exports, {
19901
20345
  deepEnrich: () => deepEnrich
19902
20346
  });
19903
20347
  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) {
20348
+ import path75 from "path";
20349
+ async function readFileSafe4(filePath) {
19906
20350
  try {
19907
20351
  return await readFile8(filePath, "utf-8");
19908
20352
  } catch {
@@ -19910,7 +20354,7 @@ async function readFileSafe3(filePath) {
19910
20354
  }
19911
20355
  }
19912
20356
  async function loadContext(evidenceDir) {
19913
- const manifestRaw = await readFileSafe3(path73.join(evidenceDir, "_manifest.json"));
20357
+ const manifestRaw = await readFileSafe4(path75.join(evidenceDir, "_manifest.json"));
19914
20358
  let manifest = {};
19915
20359
  try {
19916
20360
  manifest = JSON.parse(manifestRaw);
@@ -19918,18 +20362,18 @@ async function loadContext(evidenceDir) {
19918
20362
  log.debug("deep-enrich: failed to parse _manifest.json");
19919
20363
  }
19920
20364
  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"))
20365
+ readFileSafe4(path75.join(evidenceDir, "index.md")),
20366
+ readFileSafe4(path75.join(evidenceDir, "dependency-paths.md")),
20367
+ readFileSafe4(path75.join(evidenceDir, "overview.md"))
19924
20368
  ]);
19925
- const modulesDir = path73.join(evidenceDir, "modules");
20369
+ const modulesDir = path75.join(evidenceDir, "modules");
19926
20370
  const moduleDocs = /* @__PURE__ */ new Map();
19927
20371
  if (await pathExists(modulesDir)) {
19928
20372
  try {
19929
20373
  const entries = await readdir4(modulesDir);
19930
20374
  await Promise.all(
19931
20375
  entries.filter((e) => e.endsWith(".md")).map(async (e) => {
19932
- const content = await readFileSafe3(path73.join(modulesDir, e));
20376
+ const content = await readFileSafe4(path75.join(modulesDir, e));
19933
20377
  moduleDocs.set(e.replace(/\.md$/, ""), content);
19934
20378
  })
19935
20379
  );
@@ -19940,7 +20384,7 @@ async function loadContext(evidenceDir) {
19940
20384
  return { manifest, indexMd, callChains, overview, moduleDocs };
19941
20385
  }
19942
20386
  function progressPath(evidenceDir) {
19943
- return path73.join(evidenceDir, PROGRESS_PATH_SUBDIR, PROGRESS_FILENAME);
20387
+ return path75.join(evidenceDir, PROGRESS_PATH_SUBDIR, PROGRESS_FILENAME);
19944
20388
  }
19945
20389
  function isValidProgressState(v, project) {
19946
20390
  if (typeof v !== "object" || v === null) return false;
@@ -19966,7 +20410,7 @@ async function loadProgress(evidenceDir, project, allComponents) {
19966
20410
  }
19967
20411
  async function saveProgress(evidenceDir, state) {
19968
20412
  const p = progressPath(evidenceDir);
19969
- await mkdir7(path73.dirname(p), { recursive: true });
20413
+ await mkdir7(path75.dirname(p), { recursive: true });
19970
20414
  const updated = { ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
19971
20415
  await writeFile10(p, JSON.stringify(updated, null, 2), "utf-8");
19972
20416
  }
@@ -20205,7 +20649,7 @@ async function runPhaseComponents(opts, ctx, progress, docsDir) {
20205
20649
  log.warn(`deep-enrich[${project}]: Skipping unsafe component slug "${comp.slug}": ${e.message}`);
20206
20650
  continue;
20207
20651
  }
20208
- const outPath = path73.join(docsDir, `${comp.slug}.md`);
20652
+ const outPath = path75.join(docsDir, `${comp.slug}.md`);
20209
20653
  await mkdir7(docsDir, { recursive: true });
20210
20654
  await writeFile10(outPath, content, "utf-8");
20211
20655
  progress.componentsDone.push(comp.slug);
@@ -20233,7 +20677,7 @@ async function runPhaseArchitecture(opts, ctx, docsDir) {
20233
20677
  log.warn(`deep-enrich[${project}]: Architecture overview: AI returned empty, skipping write`);
20234
20678
  return;
20235
20679
  }
20236
- const outPath = path73.join(docsDir, "architecture.md");
20680
+ const outPath = path75.join(docsDir, "architecture.md");
20237
20681
  await mkdir7(docsDir, { recursive: true });
20238
20682
  await writeFile10(outPath, content, "utf-8");
20239
20683
  log.debug(`deep-enrich[${project}]: Architecture overview written: ${outPath}`);
@@ -20241,15 +20685,15 @@ async function runPhaseArchitecture(opts, ctx, docsDir) {
20241
20685
  async function runPhaseGraph(opts, ctx, docsDir) {
20242
20686
  const { project, evidenceDir } = opts;
20243
20687
  log.info(`deep-enrich[${project}]: Phase 3 \u2014 Generating deterministic graph docs`);
20244
- const interfacesMd = await readFileSafe3(path73.join(evidenceDir, "interfaces.md"));
20688
+ const interfacesMd = await readFileSafe4(path75.join(evidenceDir, "interfaces.md"));
20245
20689
  const g1 = buildG1RelationsDoc(ctx.manifest);
20246
20690
  const g2 = buildG2DataflowDoc(ctx.callChains);
20247
20691
  const g3 = buildG3InterfacesDoc(interfacesMd);
20248
20692
  await mkdir7(docsDir, { recursive: true });
20249
20693
  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")
20694
+ writeFile10(path75.join(docsDir, "graph-g1-relations.md"), g1, "utf-8"),
20695
+ writeFile10(path75.join(docsDir, "graph-g2-dataflow.md"), g2, "utf-8"),
20696
+ writeFile10(path75.join(docsDir, "graph-g3-interfaces.md"), g3, "utf-8")
20253
20697
  ]);
20254
20698
  log.debug(`deep-enrich[${project}]: Graph docs written: ${docsDir}`);
20255
20699
  }
@@ -20355,14 +20799,14 @@ async function runPhaseAiGraph(opts, ctx, docsDir) {
20355
20799
  await mkdir7(docsDir, { recursive: true });
20356
20800
  const g6HasEdges = (ctx.manifest.edges ?? []).length > 0;
20357
20801
  const g6 = buildG6Content(project, ctx.manifest);
20358
- await writeFile10(path73.join(docsDir, "graph-g6-multihop.md"), g6, "utf-8");
20802
+ await writeFile10(path75.join(docsDir, "graph-g6-multihop.md"), g6, "utf-8");
20359
20803
  log.debug(`deep-enrich[${project}]: G6 multi-hop analysis written`);
20360
20804
  let g5Generated = false;
20361
20805
  if (ctx.moduleDocs.size < 2) {
20362
20806
  log.warn(`deep-enrich[${project}]: Insufficient modules (${ctx.moduleDocs.size} < 2), skipping G5`);
20363
20807
  return { g5Generated, g6Generated: g6HasEdges };
20364
20808
  }
20365
- const architectureMd = await readFileSafe3(path73.join(docsDir, "architecture.md"));
20809
+ const architectureMd = await readFileSafe4(path75.join(docsDir, "architecture.md"));
20366
20810
  if (!architectureMd.trim()) {
20367
20811
  log.warn(`deep-enrich[${project}]: No architecture doc, skipping G5 scenarios`);
20368
20812
  return { g5Generated, g6Generated: g6HasEdges };
@@ -20372,7 +20816,7 @@ async function runPhaseAiGraph(opts, ctx, docsDir) {
20372
20816
  try {
20373
20817
  const g5Content = await callClaude(prompt);
20374
20818
  if (g5Content.trim()) {
20375
- await writeFile10(path73.join(docsDir, "graph-g5-scenarios.md"), g5Content, "utf-8");
20819
+ await writeFile10(path75.join(docsDir, "graph-g5-scenarios.md"), g5Content, "utf-8");
20376
20820
  log.debug(`deep-enrich[${project}]: G5 scenario diagrams written`);
20377
20821
  g5Generated = true;
20378
20822
  }
@@ -20390,10 +20834,10 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20390
20834
  hasG5: graphFlags?.g5Generated ?? false,
20391
20835
  hasG6: graphFlags?.g6Generated ?? true
20392
20836
  });
20393
- await writeFile10(path73.join(docsDir, "README.md"), graphReadme, "utf-8");
20837
+ await writeFile10(path75.join(docsDir, "README.md"), graphReadme, "utf-8");
20394
20838
  log.debug(`deep-enrich[${project}]: graph/README.md routing table written`);
20395
20839
  const { wikiRoot } = opts;
20396
- const domainsJson = await readFileSafe3(path73.join(evidenceDir, "_domains.json"));
20840
+ const domainsJson = await readFileSafe4(path75.join(evidenceDir, "_domains.json"));
20397
20841
  let keywords = [];
20398
20842
  let description = "";
20399
20843
  try {
@@ -20402,8 +20846,8 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20402
20846
  description = domains.description ?? "";
20403
20847
  } catch {
20404
20848
  }
20405
- const routerPath = path73.join(wikiRoot, "router.md");
20406
- const routerContent = await readFileSafe3(routerPath);
20849
+ const routerPath = path75.join(wikiRoot, "router.md");
20850
+ const routerContent = await readFileSafe4(routerPath);
20407
20851
  const projectLink = `[[evidence/code/${project}/index]]`;
20408
20852
  if (routerContent && !routerContent.includes(projectLink)) {
20409
20853
  const kw = keywords.length > 0 ? ` [${keywords.slice(0, 5).join(", ")}]` : "";
@@ -20412,8 +20856,8 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20412
20856
  `;
20413
20857
  await writeFile10(routerPath, routerContent.trimEnd() + "\n" + line, "utf-8");
20414
20858
  }
20415
- const indexPath = path73.join(wikiRoot, "index.md");
20416
- const indexContent = await readFileSafe3(indexPath);
20859
+ const indexPath = path75.join(wikiRoot, "index.md");
20860
+ const indexContent = await readFileSafe4(indexPath);
20417
20861
  if (indexContent && !indexContent.includes(`evidence/code/${project}/`)) {
20418
20862
  const navBlock = [
20419
20863
  `### ${project}`,
@@ -20437,7 +20881,7 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
20437
20881
  }
20438
20882
  async function deepEnrich(opts) {
20439
20883
  const { project, evidenceDir } = opts;
20440
- const docsDir = path73.join(evidenceDir, "docs");
20884
+ const docsDir = path75.join(evidenceDir, "docs");
20441
20885
  log.info(`deep-enrich[${project}]: Starting deep knowledge generation, evidenceDir=${evidenceDir}`);
20442
20886
  const ctx = await loadContext(evidenceDir);
20443
20887
  let components = ctx.manifest.components ?? [];
@@ -20526,11 +20970,11 @@ var graph_aggregate_exports = {};
20526
20970
  __export(graph_aggregate_exports, {
20527
20971
  aggregateGlobalGraph: () => aggregateGlobalGraph
20528
20972
  });
20529
- import path74 from "path";
20973
+ import path76 from "path";
20530
20974
  import { readdir as readdir5 } from "fs/promises";
20531
20975
  import fs28 from "fs-extra";
20532
20976
  async function aggregateGlobalGraph(teamwikiRoot) {
20533
- const evidenceBase = path74.join(teamwikiRoot, "evidence", "code");
20977
+ const evidenceBase = path76.join(teamwikiRoot, "evidence", "code");
20534
20978
  if (!await fs28.pathExists(evidenceBase)) return null;
20535
20979
  const { mergeGraphs: mergeGraphs2 } = await Promise.resolve().then(() => (init_adapters(), adapters_exports));
20536
20980
  const { detectCrossRepoEdges: detectCrossRepoEdges2 } = await Promise.resolve().then(() => (init_import_repo(), import_repo_exports));
@@ -20538,7 +20982,7 @@ async function aggregateGlobalGraph(teamwikiRoot) {
20538
20982
  const projectDirs = await readdir5(evidenceBase, { withFileTypes: true });
20539
20983
  for (const dir of projectDirs) {
20540
20984
  if (!dir.isDirectory()) continue;
20541
- const graphPath = path74.join(evidenceBase, dir.name, ".indices", "graph-index.json");
20985
+ const graphPath = path76.join(evidenceBase, dir.name, ".indices", "graph-index.json");
20542
20986
  if (!await fs28.pathExists(graphPath)) continue;
20543
20987
  try {
20544
20988
  const overlay = JSON.parse(await fs28.readFile(graphPath, "utf8"));
@@ -20556,8 +21000,8 @@ async function aggregateGlobalGraph(teamwikiRoot) {
20556
21000
  }
20557
21001
  }
20558
21002
  if (globalGraph) {
20559
- const destPath = path74.join(teamwikiRoot, ".indices", "graph-index.json");
20560
- await fs28.ensureDir(path74.dirname(destPath));
21003
+ const destPath = path76.join(teamwikiRoot, ".indices", "graph-index.json");
21004
+ await fs28.ensureDir(path76.dirname(destPath));
20561
21005
  await fs28.writeFile(destPath, JSON.stringify(globalGraph, null, 2), "utf8");
20562
21006
  log.info(`global graph-index.json aggregated (${globalGraph.nodes.length} nodes, ${globalGraph.edges.length} edges)`);
20563
21007
  return { nodes: globalGraph.nodes.length, edges: globalGraph.edges.length };
@@ -20579,7 +21023,7 @@ __export(import_repo_exports, {
20579
21023
  detectDomainDrift: () => detectDomainDrift,
20580
21024
  importFromRepo: () => importFromRepo
20581
21025
  });
20582
- import path75 from "path";
21026
+ import path77 from "path";
20583
21027
  import fs29 from "fs-extra";
20584
21028
  import chalk5 from "chalk";
20585
21029
  function detectCrossRepoEdges(overlay, existing) {
@@ -20694,9 +21138,9 @@ async function detectPrimaryLanguage(repoPath, maxDepth = 3) {
20694
21138
  if (["node_modules", ".git", "dist", "build", "__pycache__", ".venv"].includes(name)) {
20695
21139
  continue;
20696
21140
  }
20697
- await walk2(path75.join(dir, name), depth + 1);
21141
+ await walk2(path77.join(dir, name), depth + 1);
20698
21142
  } else if (entry.isFile()) {
20699
- const ext = path75.extname(name).toLowerCase();
21143
+ const ext = path77.extname(name).toLowerCase();
20700
21144
  const lang = langExtMap[ext];
20701
21145
  if (lang) {
20702
21146
  counts.set(lang, (counts.get(lang) ?? 0) + 1);
@@ -20720,7 +21164,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20720
21164
  const meta = { url, name };
20721
21165
  const readmeCandidates = ["README.md", "readme.md", "README.zh-CN.md", "README.zh.md"];
20722
21166
  for (const candidate of readmeCandidates) {
20723
- const filePath = path75.join(repoPath, candidate);
21167
+ const filePath = path77.join(repoPath, candidate);
20724
21168
  if (await fs29.pathExists(filePath)) {
20725
21169
  try {
20726
21170
  const content = await fs29.readFile(filePath, "utf8");
@@ -20731,7 +21175,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20731
21175
  }
20732
21176
  }
20733
21177
  }
20734
- const pkgPath = path75.join(repoPath, "package.json");
21178
+ const pkgPath = path77.join(repoPath, "package.json");
20735
21179
  if (await fs29.pathExists(pkgPath)) {
20736
21180
  try {
20737
21181
  const pkgRaw = await fs29.readFile(pkgPath, "utf8");
@@ -20746,7 +21190,7 @@ async function buildRepoMetaFromPath(repoPath, url, name) {
20746
21190
  }
20747
21191
  }
20748
21192
  if (!meta.description) {
20749
- const setupPath = path75.join(repoPath, "setup.py");
21193
+ const setupPath = path77.join(repoPath, "setup.py");
20750
21194
  if (await fs29.pathExists(setupPath)) {
20751
21195
  try {
20752
21196
  const setupContent = await fs29.readFile(setupPath, "utf8");
@@ -20877,7 +21321,7 @@ async function importFromRepo(opts) {
20877
21321
  const cacheDir = getRepoCacheDir(providerName, owner, repoName);
20878
21322
  const slug = getRepoSlug(providerName, owner, repoName);
20879
21323
  const lastSync = await readLastSync(cacheDir);
20880
- const cacheExists = await fs29.pathExists(path75.join(cacheDir, ".git"));
21324
+ const cacheExists = await fs29.pathExists(path77.join(cacheDir, ".git"));
20881
21325
  const useIncremental = incremental && cacheExists && lastSync !== null;
20882
21326
  let cloneSha;
20883
21327
  let cloneBranch;
@@ -20955,42 +21399,42 @@ async function importFromRepo(opts) {
20955
21399
  mrTeamConfig = { repo: tc.repo, provider: tc.provider, reviewers: tc.reviewers };
20956
21400
  mrLocalConfig = { repo: lc.repo, username: lc.username };
20957
21401
  } catch {
20958
- teamRepoDir = path75.join(process.cwd(), ".teamai", "team-repo");
21402
+ teamRepoDir = path77.join(process.cwd(), ".teamai", "team-repo");
20959
21403
  }
20960
- const teamwikiRoot = output ? path75.resolve(output, "..", "teamwiki") : path75.join(teamRepoDir, "teamwiki");
21404
+ const teamwikiRoot = output ? path77.resolve(output, "..", "teamwiki") : path77.join(teamRepoDir, "teamwiki");
20961
21405
  if (!dryRun) {
20962
- const cacheWiki = path75.join(cacheDir, "teamwiki");
21406
+ const cacheWiki = path77.join(cacheDir, "teamwiki");
20963
21407
  try {
20964
21408
  if (incremental) {
20965
- const destIndices = path75.join(teamwikiRoot, ".indices");
20966
- const cacheIndices = path75.join(cacheDir, "teamwiki", ".indices");
21409
+ const destIndices = path77.join(teamwikiRoot, ".indices");
21410
+ const cacheIndices = path77.join(cacheDir, "teamwiki", ".indices");
20967
21411
  await fs29.ensureDir(cacheIndices);
20968
21412
  for (const f of ["facts-cache.json", "interfaces-cache.json"]) {
20969
- const src = path75.join(destIndices, f);
21413
+ const src = path77.join(destIndices, f);
20970
21414
  if (await fs29.pathExists(src)) {
20971
- await fs29.copy(src, path75.join(cacheIndices, f));
21415
+ await fs29.copy(src, path77.join(cacheIndices, f));
20972
21416
  }
20973
21417
  }
20974
- const existingManifest = path75.join(teamwikiRoot, "source-manifest.json");
21418
+ const existingManifest = path77.join(teamwikiRoot, "source-manifest.json");
20975
21419
  if (await fs29.pathExists(existingManifest)) {
20976
- await fs29.copy(existingManifest, path75.join(cacheDir, "teamwiki", "source-manifest.json"));
21420
+ await fs29.copy(existingManifest, path77.join(cacheDir, "teamwiki", "source-manifest.json"));
20977
21421
  }
20978
21422
  }
20979
21423
  await extractCodebase({ path: cacheDir, project: slug, json: false, skipEnrich, incremental });
20980
21424
  if (await fs29.pathExists(cacheWiki)) {
20981
- const evidenceSrc = path75.join(cacheWiki, "evidence", "code", slug);
20982
- const evidenceDest = path75.join(teamwikiRoot, "evidence", "code", slug);
21425
+ const evidenceSrc = path77.join(cacheWiki, "evidence", "code", slug);
21426
+ const evidenceDest = path77.join(teamwikiRoot, "evidence", "code", slug);
20983
21427
  if (await fs29.pathExists(evidenceDest)) {
20984
21428
  const entries = await fs29.readdir(evidenceDest);
20985
21429
  for (const entry of entries) {
20986
21430
  if (entry === ".indices") continue;
20987
- await fs29.remove(path75.join(evidenceDest, entry));
21431
+ await fs29.remove(path77.join(evidenceDest, entry));
20988
21432
  }
20989
21433
  }
20990
21434
  await fs29.ensureDir(evidenceDest);
20991
21435
  await fs29.copy(evidenceSrc, evidenceDest, { overwrite: true });
20992
21436
  if (codebaseMd) {
20993
- const overviewPath = path75.join(evidenceDest, "overview.md");
21437
+ const overviewPath = path77.join(evidenceDest, "overview.md");
20994
21438
  const existing = await fs29.readFile(overviewPath, "utf8").catch(() => "");
20995
21439
  const aiNarrative = codebaseMd.replace(/^---[\s\S]*?---\n*/m, "");
20996
21440
  const marker = "## AI Architecture Narrative";
@@ -21013,31 +21457,31 @@ ${aiNarrative}`;
21013
21457
  }
21014
21458
  await fs29.writeFile(overviewPath, combined, "utf8");
21015
21459
  }
21016
- const srcGraph = path75.join(cacheWiki, ".indices", "graph-index.json");
21460
+ const srcGraph = path77.join(cacheWiki, ".indices", "graph-index.json");
21017
21461
  if (await fs29.pathExists(srcGraph)) {
21018
- const evidenceGraphDir = path75.join(teamwikiRoot, "evidence", "code", slug, ".indices");
21462
+ const evidenceGraphDir = path77.join(teamwikiRoot, "evidence", "code", slug, ".indices");
21019
21463
  await fs29.ensureDir(evidenceGraphDir);
21020
- await fs29.copy(srcGraph, path75.join(evidenceGraphDir, "graph-index.json"));
21464
+ await fs29.copy(srcGraph, path77.join(evidenceGraphDir, "graph-index.json"));
21021
21465
  } else {
21022
21466
  log.debug(`[graph] per-repo graph-index.json not found, skipping copy`);
21023
21467
  }
21024
- const cacheIndices = path75.join(cacheWiki, ".indices");
21025
- const destIndices = path75.join(teamwikiRoot, ".indices");
21468
+ const cacheIndices = path77.join(cacheWiki, ".indices");
21469
+ const destIndices = path77.join(teamwikiRoot, ".indices");
21026
21470
  for (const cacheFile of ["facts-cache.json", "interfaces-cache.json"]) {
21027
- const src = path75.join(cacheIndices, cacheFile);
21471
+ const src = path77.join(cacheIndices, cacheFile);
21028
21472
  if (await fs29.pathExists(src)) {
21029
21473
  await fs29.ensureDir(destIndices);
21030
- await fs29.copy(src, path75.join(destIndices, cacheFile), { overwrite: true });
21474
+ await fs29.copy(src, path77.join(destIndices, cacheFile), { overwrite: true });
21031
21475
  }
21032
21476
  }
21033
- const srcManifest = path75.join(cacheWiki, "source-manifest.json");
21477
+ const srcManifest = path77.join(cacheWiki, "source-manifest.json");
21034
21478
  if (await fs29.pathExists(srcManifest)) {
21035
- await fs29.copy(srcManifest, path75.join(teamwikiRoot, "source-manifest.json"), { overwrite: true });
21479
+ await fs29.copy(srcManifest, path77.join(teamwikiRoot, "source-manifest.json"), { overwrite: true });
21036
21480
  }
21037
21481
  await fs29.remove(cacheWiki);
21038
21482
  }
21039
21483
  if (explicitDomain) {
21040
- const domainsJsonPath = path75.join(teamwikiRoot, "evidence", "code", slug, "_domains.json");
21484
+ const domainsJsonPath = path77.join(teamwikiRoot, "evidence", "code", slug, "_domains.json");
21041
21485
  if (await fs29.pathExists(domainsJsonPath)) {
21042
21486
  try {
21043
21487
  const existing = JSON.parse(await fs29.readFile(domainsJsonPath, "utf8"));
@@ -21050,8 +21494,8 @@ ${aiNarrative}`;
21050
21494
  }
21051
21495
  }
21052
21496
  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");
21497
+ const routerPath = path77.join(teamwikiRoot, "router.md");
21498
+ const indexPath = path77.join(teamwikiRoot, "index.md");
21055
21499
  const projectLink = `[[evidence/code/${slug}/index]]`;
21056
21500
  if (await fs29.pathExists(routerPath)) {
21057
21501
  const router = await fs29.readFile(routerPath, "utf8");
@@ -21077,8 +21521,8 @@ ${aiNarrative}`;
21077
21521
  } else {
21078
21522
  await fs29.writeFile(indexPath, indexTemplate2([{ slug, label: slug }]), "utf8");
21079
21523
  }
21080
- if (!await fs29.pathExists(path75.join(teamwikiRoot, "hot.md"))) {
21081
- await fs29.writeFile(path75.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE2, "utf8");
21524
+ if (!await fs29.pathExists(path77.join(teamwikiRoot, "hot.md"))) {
21525
+ await fs29.writeFile(path77.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE2, "utf8");
21082
21526
  }
21083
21527
  log.info(chalk5.green(`\u2713 teamwiki/ knowledge graph updated: ${slug}`));
21084
21528
  } catch (err) {
@@ -21100,8 +21544,8 @@ ${aiNarrative}`;
21100
21544
  }
21101
21545
  }
21102
21546
  if (!dryRun && !skipEnrich && teamwikiRoot) {
21103
- const evidenceDir = path75.join(teamwikiRoot, "evidence", "code", slug);
21104
- if (await fs29.pathExists(path75.join(evidenceDir, "_manifest.json"))) {
21547
+ const evidenceDir = path77.join(teamwikiRoot, "evidence", "code", slug);
21548
+ if (await fs29.pathExists(path77.join(evidenceDir, "_manifest.json"))) {
21105
21549
  try {
21106
21550
  const { deepEnrich: deepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
21107
21551
  await deepEnrich2({ project: slug, evidenceDir, wikiRoot: teamwikiRoot, cacheDir });
@@ -21220,14 +21664,14 @@ var init_store2 = __esm({
21220
21664
  });
21221
21665
 
21222
21666
  // src/utils/team-codebase-paths.ts
21223
- import path76 from "path";
21667
+ import path78 from "path";
21224
21668
  function getTeamCodebasePaths(cwd, output) {
21225
- const root = output ?? path76.join(cwd, "docs", TEAM_CODEBASE_DIR);
21669
+ const root = output ?? path78.join(cwd, "docs", TEAM_CODEBASE_DIR);
21226
21670
  return {
21227
21671
  root,
21228
- index: path76.join(root, "index.md"),
21229
- domainsDir: path76.join(root, "domains"),
21230
- reposDir: path76.join(root, "repos")
21672
+ index: path78.join(root, "index.md"),
21673
+ domainsDir: path78.join(root, "domains"),
21674
+ reposDir: path78.join(root, "repos")
21231
21675
  };
21232
21676
  }
21233
21677
  function safeDomainSlug(name) {
@@ -21246,12 +21690,12 @@ var init_team_codebase_paths = __esm({
21246
21690
  });
21247
21691
 
21248
21692
  // src/aggregate.ts
21249
- import path77 from "path";
21693
+ import path79 from "path";
21250
21694
  import fs31 from "fs-extra";
21251
- import matter7 from "gray-matter";
21695
+ import matter8 from "gray-matter";
21252
21696
  async function parseRepoMd(filePath, slug) {
21253
21697
  const raw = await fs31.readFile(filePath, "utf8");
21254
- const { data, content } = matter7(raw);
21698
+ const { data, content } = matter8(raw);
21255
21699
  const fm = data;
21256
21700
  const url = typeof fm.repo_url === "string" ? fm.repo_url : typeof fm.url === "string" ? fm.url : "";
21257
21701
  let name = typeof fm.repo_name === "string" ? fm.repo_name : "";
@@ -21293,7 +21737,7 @@ async function regenerateAggregate(opts) {
21293
21737
  for (const file of repoFiles) {
21294
21738
  const slug = file.replace(/\.md$/, "");
21295
21739
  try {
21296
- const summary = await parseRepoMd(path77.join(paths.reposDir, file), slug);
21740
+ const summary = await parseRepoMd(path79.join(paths.reposDir, file), slug);
21297
21741
  repoMap.set(slug, summary);
21298
21742
  } catch {
21299
21743
  }
@@ -21330,7 +21774,7 @@ async function regenerateAggregate(opts) {
21330
21774
  );
21331
21775
  for (const oldFile of existingDomainFiles) {
21332
21776
  if (!newDomainFileNames.has(oldFile)) {
21333
- await fs31.remove(path77.join(paths.domainsDir, oldFile));
21777
+ await fs31.remove(path79.join(paths.domainsDir, oldFile));
21334
21778
  }
21335
21779
  }
21336
21780
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -21339,7 +21783,7 @@ async function regenerateAggregate(opts) {
21339
21783
  const domainEntry = domains.domains.find((d) => d.name === domainName);
21340
21784
  const description = domainEntry?.description ?? "";
21341
21785
  const safeSlug = safeDomainSlug(domainName);
21342
- const outputPath = path77.join(paths.domainsDir, `domain-${safeSlug}.md`);
21786
+ const outputPath = path79.join(paths.domainsDir, `domain-${safeSlug}.md`);
21343
21787
  const tableRows = slugs.map((slug) => {
21344
21788
  const s = repoMap.get(slug);
21345
21789
  if (!s) return "";
@@ -21443,7 +21887,7 @@ var init_aggregate = __esm({
21443
21887
  });
21444
21888
 
21445
21889
  // src/import-repo-list.ts
21446
- import path78 from "path";
21890
+ import path80 from "path";
21447
21891
  function sortByPriority(entries) {
21448
21892
  const order = { high: 0, normal: 1, low: 2 };
21449
21893
  return [...entries].sort((a, b) => {
@@ -21521,7 +21965,7 @@ async function importFromRepoList(opts) {
21521
21965
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21522
21966
  const { localConfig: lc } = await autoDetectInit2();
21523
21967
  const teamRepoPath = lc.repo.localPath;
21524
- const teamwikiRoot = path78.join(teamRepoPath, "teamwiki");
21968
+ const teamwikiRoot = path80.join(teamRepoPath, "teamwiki");
21525
21969
  const { aggregateGlobalGraph: aggregateGlobalGraph2 } = await Promise.resolve().then(() => (init_graph_aggregate(), graph_aggregate_exports));
21526
21970
  await aggregateGlobalGraph2(teamwikiRoot);
21527
21971
  } catch (e) {
@@ -21538,7 +21982,7 @@ async function importFromRepoList(opts) {
21538
21982
  try {
21539
21983
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21540
21984
  const { localConfig: lc } = await autoDetectInit2();
21541
- resolvedOutput = path78.join(lc.repo.localPath, "docs", "team-codebase");
21985
+ resolvedOutput = path80.join(lc.repo.localPath, "docs", "team-codebase");
21542
21986
  domainsBase = lc.repo.localPath;
21543
21987
  } catch {
21544
21988
  }
@@ -21600,9 +22044,9 @@ __export(rebuild_wiki_index_exports, {
21600
22044
  rebuildWikiIndex: () => rebuildWikiIndex
21601
22045
  });
21602
22046
  import { readFile as readFile9, readdir as readdir6, stat as stat4, writeFile as writeFile11 } from "fs/promises";
21603
- import path79 from "path";
22047
+ import path81 from "path";
21604
22048
  async function rebuildWikiIndex(teamwikiRoot) {
21605
- const evidenceCodeDir = path79.join(teamwikiRoot, "evidence", "code");
22049
+ const evidenceCodeDir = path81.join(teamwikiRoot, "evidence", "code");
21606
22050
  if (!await pathExists(evidenceCodeDir)) return;
21607
22051
  const projects = [];
21608
22052
  let totalFacts = 0, totalNodes = 0, totalEdges = 0;
@@ -21610,7 +22054,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21610
22054
  let totalCallChains = 0;
21611
22055
  const dirs = await readdir6(evidenceCodeDir);
21612
22056
  for (const dir of dirs) {
21613
- const dirPath = path79.join(evidenceCodeDir, dir);
22057
+ const dirPath = path81.join(evidenceCodeDir, dir);
21614
22058
  const dirStat = await stat4(dirPath).catch(() => null);
21615
22059
  if (!dirStat?.isDirectory()) continue;
21616
22060
  const info = {
@@ -21623,7 +22067,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21623
22067
  keywords: [],
21624
22068
  domain: ""
21625
22069
  };
21626
- const overviewPath = path79.join(dirPath, "overview.md");
22070
+ const overviewPath = path81.join(dirPath, "overview.md");
21627
22071
  if (await pathExists(overviewPath)) {
21628
22072
  const content = await readFile9(overviewPath, "utf-8");
21629
22073
  const bodyStart = content.indexOf("\n\n", content.indexOf("---", 3));
@@ -21636,7 +22080,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21636
22080
  }
21637
22081
  }
21638
22082
  }
21639
- const projectIndex = path79.join(dirPath, "index.md");
22083
+ const projectIndex = path81.join(dirPath, "index.md");
21640
22084
  if (await pathExists(projectIndex)) {
21641
22085
  const content = await readFile9(projectIndex, "utf-8");
21642
22086
  const factsMatch = content.match(/Facts:\s*(\d+)/);
@@ -21646,7 +22090,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21646
22090
  info.interfaces[m[1]] = (info.interfaces[m[1]] ?? 0) + parseInt(m[2], 10);
21647
22091
  }
21648
22092
  }
21649
- const manifestPath = path79.join(dirPath, "_manifest.json");
22093
+ const manifestPath = path81.join(dirPath, "_manifest.json");
21650
22094
  if (await pathExists(manifestPath)) {
21651
22095
  try {
21652
22096
  const raw = await readFile9(manifestPath, "utf-8");
@@ -21658,7 +22102,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21658
22102
  } catch {
21659
22103
  }
21660
22104
  }
21661
- const domainsPath = path79.join(dirPath, "_domains.json");
22105
+ const domainsPath = path81.join(dirPath, "_domains.json");
21662
22106
  if (await pathExists(domainsPath)) {
21663
22107
  try {
21664
22108
  const raw = await readFile9(domainsPath, "utf-8");
@@ -21675,7 +22119,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21675
22119
  } catch {
21676
22120
  }
21677
22121
  }
21678
- const chainsPath = path79.join(dirPath, "dependency-paths.md");
22122
+ const chainsPath = path81.join(dirPath, "dependency-paths.md");
21679
22123
  if (await pathExists(chainsPath)) {
21680
22124
  const content = await readFile9(chainsPath, "utf-8");
21681
22125
  const chainMatch = content.match(/(\d+)\s*call chain/);
@@ -21691,7 +22135,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21691
22135
  }
21692
22136
  projects.push(info);
21693
22137
  }
21694
- const graphPath = path79.join(teamwikiRoot, ".indices", "graph-index.json");
22138
+ const graphPath = path81.join(teamwikiRoot, ".indices", "graph-index.json");
21695
22139
  if (await pathExists(graphPath)) {
21696
22140
  try {
21697
22141
  const raw = await readFile9(graphPath, "utf-8");
@@ -21732,7 +22176,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
21732
22176
  routerLines.push("4. **\u8C03\u7528\u94FE/\u6392\u969C** \u2192 \u67E5\u5BF9\u5E94\u4ED3\u5E93\u7684 dependency-paths.md");
21733
22177
  routerLines.push("5. **\u6A21\u5757\u804C\u8D23\u6982\u8FF0** \u2192 \u67E5 overview.md \u6216 modules/*.md");
21734
22178
  routerLines.push("");
21735
- await writeFile11(path79.join(teamwikiRoot, "router.md"), routerLines.join("\n"), "utf-8");
22179
+ await writeFile11(path81.join(teamwikiRoot, "router.md"), routerLines.join("\n"), "utf-8");
21736
22180
  const indexLines = [
21737
22181
  "# Team Wiki Index",
21738
22182
  "",
@@ -21768,9 +22212,9 @@ async function rebuildWikiIndex(teamwikiRoot) {
21768
22212
  indexLines.push("- [router.md](./router.md) \u2014 \u4EA7\u54C1\u57DF\u8DEF\u7531\uFF08\u8868\u683C + \u8DEF\u7531\u89C4\u5219\uFF09");
21769
22213
  indexLines.push("- [hot.md](./hot.md) \u2014 \u6D3B\u8DC3\u5DE5\u4F5C\u8BB0\u5FC6");
21770
22214
  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");
22215
+ await writeFile11(path81.join(teamwikiRoot, "index.md"), indexLines.join("\n"), "utf-8");
22216
+ if (!await pathExists(path81.join(teamwikiRoot, "hot.md"))) {
22217
+ await writeFile11(path81.join(teamwikiRoot, "hot.md"), HOT_TEMPLATE, "utf-8");
21774
22218
  }
21775
22219
  log.debug(`rebuildWikiIndex: ${projects.length} projects, ${totalNodes} nodes, ${totalEdges} edges`);
21776
22220
  }
@@ -21809,7 +22253,7 @@ var init_rebuild_wiki_index = __esm({
21809
22253
  });
21810
22254
 
21811
22255
  // src/import-org.ts
21812
- import path80 from "path";
22256
+ import path82 from "path";
21813
22257
  import fs32 from "fs-extra";
21814
22258
  import { stringify as yamlStringify2 } from "yaml";
21815
22259
  function parseOrgInput(org) {
@@ -21888,9 +22332,9 @@ async function importFromOrg(opts) {
21888
22332
  return;
21889
22333
  }
21890
22334
  log.info(`${filteredRepos.length} repos after filtering, generating whitelist...`);
21891
- const whitelistDraftPath = path80.join(cwd, WHITELIST_DRAFT_PATH);
22335
+ const whitelistDraftPath = path82.join(cwd, WHITELIST_DRAFT_PATH);
21892
22336
  if (!opts.dryRun) {
21893
- await fs32.ensureDir(path80.dirname(whitelistDraftPath));
22337
+ await fs32.ensureDir(path82.dirname(whitelistDraftPath));
21894
22338
  const lines = ["version: 1", "repos:"];
21895
22339
  for (const repo of filteredRepos) {
21896
22340
  lines.push(` - url: ${repo.url}`);
@@ -21920,8 +22364,8 @@ async function importFromOrg(opts) {
21920
22364
  );
21921
22365
  try {
21922
22366
  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");
22367
+ const teamRepoPath = path82.join(cwd, ".teamai", "team-repo");
22368
+ const teamRepoWiki = path82.join(teamRepoPath, "teamwiki");
21925
22369
  if (await fs32.pathExists(teamRepoWiki)) {
21926
22370
  await rebuildWikiIndex2(teamRepoWiki);
21927
22371
  log.info("teamwiki router.md / index.md rebuilt");
@@ -21963,7 +22407,7 @@ var init_import_org = __esm({
21963
22407
  });
21964
22408
 
21965
22409
  // src/iwiki-dual.ts
21966
- import path81 from "path";
22410
+ import path83 from "path";
21967
22411
  import fs33 from "fs-extra";
21968
22412
  function parseIWikiInput2(input) {
21969
22413
  const trimmed = input.trim();
@@ -22128,10 +22572,10 @@ async function importFromIWikiDual(opts) {
22128
22572
  return { sectionsUpdated: [], pendingReview: false };
22129
22573
  }
22130
22574
  const paths = getTeamCodebasePaths(cwd, opts.output);
22131
- const filePath = path81.join(paths.root, "external-knowledge.md");
22575
+ const filePath = path83.join(paths.root, "external-knowledge.md");
22132
22576
  if (opts.requireReview) {
22133
22577
  if (!opts.dryRun) {
22134
- const relativeFilePath = path81.relative(cwd, filePath);
22578
+ const relativeFilePath = path83.relative(cwd, filePath);
22135
22579
  for (const sectionKey of sections) {
22136
22580
  const body = aiOutput[sectionKey] ?? "";
22137
22581
  if (!body) continue;
@@ -22198,7 +22642,7 @@ var import_exports = {};
22198
22642
  __export(import_exports, {
22199
22643
  importCmd: () => importCmd
22200
22644
  });
22201
- import path82 from "path";
22645
+ import path84 from "path";
22202
22646
  import os5 from "os";
22203
22647
  import fs34 from "fs-extra";
22204
22648
  import { Listr, PRESET_TIMER } from "listr2";
@@ -22316,7 +22760,7 @@ async function importCmd(opts) {
22316
22760
  task: async (ctx) => {
22317
22761
  const { learning, repoUrl } = await importFromMR({
22318
22762
  url: opts.fromMr,
22319
- learningsDir: path82.join(localConfig.repo.localPath, "learnings"),
22763
+ learningsDir: path84.join(localConfig.repo.localPath, "learnings"),
22320
22764
  all: opts.all,
22321
22765
  outputDir: opts.output,
22322
22766
  repoPath: opts.dryRun ? void 0 : localConfig.repo.localPath,
@@ -22330,7 +22774,7 @@ async function importCmd(opts) {
22330
22774
  title: "Incremental teamwiki update",
22331
22775
  skip: (ctx) => !ctx.repoUrl || !!opts.dryRun || !!opts.output,
22332
22776
  task: async (ctx, task) => {
22333
- const teamwikiRoot = path82.join(localConfig.repo.localPath, "teamwiki");
22777
+ const teamwikiRoot = path84.join(localConfig.repo.localPath, "teamwiki");
22334
22778
  try {
22335
22779
  const { detectProvider: detectProvider2, getProvider: getProvider2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
22336
22780
  const { getRepoSlug: getRepoSlug2 } = await Promise.resolve().then(() => (init_repo_cache(), repo_cache_exports));
@@ -22338,7 +22782,7 @@ async function importCmd(opts) {
22338
22782
  const provider = getProvider2(providerName);
22339
22783
  const repoInfo = provider.parseRepoInput(ctx.repoUrl);
22340
22784
  const slug = getRepoSlug2(providerName, repoInfo.owner, repoInfo.repo);
22341
- const evidenceDir = path82.join(teamwikiRoot, "evidence", "code", slug);
22785
+ const evidenceDir = path84.join(teamwikiRoot, "evidence", "code", slug);
22342
22786
  if (await fs34.pathExists(evidenceDir)) {
22343
22787
  task.output = `Updating ${slug}...`;
22344
22788
  await importFromRepo({
@@ -22383,18 +22827,18 @@ async function importCmd(opts) {
22383
22827
  setSilent(false);
22384
22828
  }
22385
22829
  } else if (opts.dir) {
22386
- const dirPath = path82.resolve(opts.dir);
22830
+ const dirPath = path84.resolve(opts.dir);
22387
22831
  if (!await fs34.pathExists(dirPath)) {
22388
22832
  throw new Error(`Directory not found: ${dirPath}`);
22389
22833
  }
22390
- const slug = path82.basename(dirPath);
22834
+ const slug = path84.basename(dirPath);
22391
22835
  log.info(`Scanning local directory: ${dirPath} (project: ${slug})`);
22392
22836
  if (opts.dryRun) {
22393
22837
  log.info(`[dry-run] skipping code extraction, no action taken`);
22394
22838
  log.success(`Local directory ${slug} import complete (dry-run)`);
22395
22839
  return;
22396
22840
  }
22397
- const tmpExtractDir = await fs34.mkdtemp(path82.join(os5.tmpdir(), "teamai-extract-"));
22841
+ const tmpExtractDir = await fs34.mkdtemp(path84.join(os5.tmpdir(), "teamai-extract-"));
22398
22842
  try {
22399
22843
  const { extractCodebase: extractCodebase2 } = await Promise.resolve().then(() => (init_codebase_extract(), codebase_extract_exports));
22400
22844
  await extractCodebase2({
@@ -22404,9 +22848,9 @@ async function importCmd(opts) {
22404
22848
  skipEnrich: opts.skipEnrich ?? false,
22405
22849
  outputRoot: tmpExtractDir
22406
22850
  });
22407
- const srcWiki = path82.join(tmpExtractDir, "teamwiki");
22851
+ const srcWiki = path84.join(tmpExtractDir, "teamwiki");
22408
22852
  if (opts.output) {
22409
- const outputWiki = path82.join(opts.output, "teamwiki");
22853
+ const outputWiki = path84.join(opts.output, "teamwiki");
22410
22854
  if (await fs34.pathExists(srcWiki)) {
22411
22855
  await fs34.copy(srcWiki, outputWiki, { overwrite: true });
22412
22856
  log.info(`Output written: ${outputWiki}`);
@@ -22414,19 +22858,19 @@ async function importCmd(opts) {
22414
22858
  } else {
22415
22859
  const { localConfig } = await autoDetectInit();
22416
22860
  const teamRepoPath = localConfig.repo.localPath;
22417
- const teamwikiRoot = path82.join(teamRepoPath, "teamwiki");
22861
+ const teamwikiRoot = path84.join(teamRepoPath, "teamwiki");
22418
22862
  if (await fs34.pathExists(srcWiki)) {
22419
- const evidenceSrc = path82.join(srcWiki, "evidence", "code", slug);
22420
- const evidenceDest = path82.join(teamwikiRoot, "evidence", "code", slug);
22863
+ const evidenceSrc = path84.join(srcWiki, "evidence", "code", slug);
22864
+ const evidenceDest = path84.join(teamwikiRoot, "evidence", "code", slug);
22421
22865
  if (await fs34.pathExists(evidenceSrc)) {
22422
- await fs34.ensureDir(path82.dirname(evidenceDest));
22866
+ await fs34.ensureDir(path84.dirname(evidenceDest));
22423
22867
  await fs34.copy(evidenceSrc, evidenceDest, { overwrite: true });
22424
22868
  }
22425
- const srcGraph = path82.join(srcWiki, ".indices", "graph-index.json");
22869
+ const srcGraph = path84.join(srcWiki, ".indices", "graph-index.json");
22426
22870
  if (await fs34.pathExists(srcGraph)) {
22427
- const destGraphDir = path82.join(evidenceDest, ".indices");
22871
+ const destGraphDir = path84.join(evidenceDest, ".indices");
22428
22872
  await fs34.ensureDir(destGraphDir);
22429
- await fs34.copy(srcGraph, path82.join(destGraphDir, "graph-index.json"), { overwrite: true });
22873
+ await fs34.copy(srcGraph, path84.join(destGraphDir, "graph-index.json"), { overwrite: true });
22430
22874
  }
22431
22875
  log.info(`teamwiki/ knowledge graph updated: ${slug}`);
22432
22876
  }
@@ -22492,12 +22936,12 @@ __export(mr_hint_exports, {
22492
22936
  });
22493
22937
  import { spawnSync as spawnSync3 } from "child_process";
22494
22938
  import fs35 from "fs";
22495
- import path83 from "path";
22939
+ import path85 from "path";
22496
22940
  function repoSlug(owner, repo) {
22497
22941
  return `${owner}/${repo}`.replace(/[^a-zA-Z0-9_-]/g, "_");
22498
22942
  }
22499
22943
  function getCachePath2(owner, repo) {
22500
- return path83.join(
22944
+ return path85.join(
22501
22945
  process.env.HOME ?? "",
22502
22946
  ".teamai",
22503
22947
  "sessions",
@@ -22520,7 +22964,7 @@ function loadCache(owner, repo) {
22520
22964
  function saveCache(owner, repo, cache) {
22521
22965
  try {
22522
22966
  const cachePath = getCachePath2(owner, repo);
22523
- const dir = path83.dirname(cachePath);
22967
+ const dir = path85.dirname(cachePath);
22524
22968
  if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
22525
22969
  fs35.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
22526
22970
  } catch {
@@ -22667,7 +23111,7 @@ function buildHintMessage2(mrs) {
22667
23111
  async function computeMrHintOutput() {
22668
23112
  if (process.env.TEAMAI_MR_HINT_DISABLED === "1") return null;
22669
23113
  const rawCwd = process.env.TEAMAI_MR_HINT_CWD ?? process.cwd();
22670
- const cwd = path83.resolve(rawCwd);
23114
+ const cwd = path85.resolve(rawCwd);
22671
23115
  try {
22672
23116
  if (!fs35.statSync(cwd).isDirectory()) {
22673
23117
  return null;
@@ -22735,10 +23179,10 @@ var init_mr_hint = __esm({
22735
23179
  });
22736
23180
 
22737
23181
  // src/codebase-lint.ts
22738
- import path84 from "path";
23182
+ import path86 from "path";
22739
23183
  import os6 from "os";
22740
23184
  import fs36 from "fs-extra";
22741
- import matter8 from "gray-matter";
23185
+ import matter9 from "gray-matter";
22742
23186
  import chalk6 from "chalk";
22743
23187
  function severityAtLeast(issue, threshold) {
22744
23188
  return SEVERITY_ORDER[issue] >= SEVERITY_ORDER[threshold];
@@ -22946,7 +23390,7 @@ async function lintTeamCodebase(opts) {
22946
23390
  fixable: false
22947
23391
  });
22948
23392
  }
22949
- const whitelistPath = path84.join(opts.cwd, ".teamai", "repo-whitelist.yaml");
23393
+ const whitelistPath = path86.join(opts.cwd, ".teamai", "repo-whitelist.yaml");
22950
23394
  let whitelistData = null;
22951
23395
  try {
22952
23396
  whitelistData = await loadRepoList(whitelistPath);
@@ -22983,7 +23427,7 @@ async function lintTeamCodebase(opts) {
22983
23427
  for (const url of allDomainUrls) {
22984
23428
  const slug = urlToSlugMap.get(url);
22985
23429
  if (!slug) continue;
22986
- const mdPath = path84.join(paths.reposDir, `${slug}.md`);
23430
+ const mdPath = path86.join(paths.reposDir, `${slug}.md`);
22987
23431
  if (!await fs36.pathExists(mdPath)) {
22988
23432
  issues.push({
22989
23433
  severity: "high",
@@ -23011,13 +23455,13 @@ async function lintTeamCodebase(opts) {
23011
23455
  }
23012
23456
  }
23013
23457
  }
23014
- const cacheRoot = path84.join(os6.homedir(), ".teamai", "cache", "repos");
23458
+ const cacheRoot = path86.join(os6.homedir(), ".teamai", "cache", "repos");
23015
23459
  const cacheExists = await fs36.pathExists(cacheRoot);
23016
23460
  for (const mdFile of repoMdFiles) {
23017
- const mdPath = path84.join(paths.reposDir, mdFile);
23461
+ const mdPath = path86.join(paths.reposDir, mdFile);
23018
23462
  try {
23019
23463
  const content = await fs36.readFile(mdPath, "utf8");
23020
- const parsed = matter8(content);
23464
+ const parsed = matter9(content);
23021
23465
  const fm = parsed.data;
23022
23466
  issues.push(...checkRepoFrontmatterRequired(fm, `docs/team-codebase/repos/${mdFile}`));
23023
23467
  const source = fm["source"];
@@ -23071,10 +23515,10 @@ async function lintTeamCodebase(opts) {
23071
23515
  }
23072
23516
  }
23073
23517
  for (const domFile of domainMdFiles) {
23074
- const domPath = path84.join(paths.domainsDir, domFile);
23518
+ const domPath = path86.join(paths.domainsDir, domFile);
23075
23519
  try {
23076
23520
  const content = await fs36.readFile(domPath, "utf8");
23077
- const parsed = matter8(content);
23521
+ const parsed = matter9(content);
23078
23522
  const fm = parsed.data;
23079
23523
  issues.push(
23080
23524
  ...checkDomainFrontmatterRequired(fm, `docs/team-codebase/domains/${domFile}`)
@@ -23128,7 +23572,7 @@ async function lintTeamCodebase(opts) {
23128
23572
  scanned.indexFile = true;
23129
23573
  try {
23130
23574
  const content = await fs36.readFile(paths.index, "utf8");
23131
- const parsed = matter8(content);
23575
+ const parsed = matter9(content);
23132
23576
  const fm = parsed.data;
23133
23577
  issues.push(...checkIndexFrontmatterRequired(fm, "docs/team-codebase/index.md"));
23134
23578
  const fmRepoCnt = Number(fm["repo_count"] ?? -1);
@@ -23165,7 +23609,7 @@ async function lintTeamCodebase(opts) {
23165
23609
  });
23166
23610
  }
23167
23611
  }
23168
- const extKnowledgePath = path84.join(paths.root, "external-knowledge.md");
23612
+ const extKnowledgePath = path86.join(paths.root, "external-knowledge.md");
23169
23613
  if (await fs36.pathExists(extKnowledgePath)) {
23170
23614
  scanned.externalKnowledgeFile = true;
23171
23615
  try {
@@ -23220,7 +23664,7 @@ async function lintTeamCodebase(opts) {
23220
23664
  }
23221
23665
  }
23222
23666
  }
23223
- const pendingPath = path84.join(opts.cwd, ".teamai", "pending-review.jsonl");
23667
+ const pendingPath = path86.join(opts.cwd, ".teamai", "pending-review.jsonl");
23224
23668
  if (await fs36.pathExists(pendingPath)) {
23225
23669
  try {
23226
23670
  const content = await fs36.readFile(pendingPath, "utf8");
@@ -23238,7 +23682,7 @@ async function lintTeamCodebase(opts) {
23238
23682
  } catch {
23239
23683
  }
23240
23684
  }
23241
- const sourceMarksPath = path84.join(opts.cwd, ".teamai", "source-marks.jsonl");
23685
+ const sourceMarksPath = path86.join(opts.cwd, ".teamai", "source-marks.jsonl");
23242
23686
  if (await fs36.pathExists(sourceMarksPath)) {
23243
23687
  try {
23244
23688
  const content = await fs36.readFile(sourceMarksPath, "utf8");
@@ -23316,10 +23760,10 @@ async function fixTeamCodebase(opts) {
23316
23760
  continue;
23317
23761
  }
23318
23762
  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);
23763
+ const mdFile = path86.basename(issue.location);
23764
+ const srcPath = path86.join(paths.reposDir, mdFile);
23765
+ const archivedDir = path86.join(paths.reposDir, ".archived");
23766
+ const dstPath = path86.join(archivedDir, mdFile);
23323
23767
  if (opts.dryRun) {
23324
23768
  applied.push({
23325
23769
  category: issue.category,
@@ -23345,7 +23789,7 @@ async function fixTeamCodebase(opts) {
23345
23789
  }
23346
23790
  } else if (issue.category === "frontmatter-missing") {
23347
23791
  const relPath = issue.location;
23348
- const absPath = path84.join(opts.cwd, relPath);
23792
+ const absPath = path86.join(opts.cwd, relPath);
23349
23793
  if (opts.dryRun) {
23350
23794
  applied.push({
23351
23795
  category: issue.category,
@@ -23355,10 +23799,10 @@ async function fixTeamCodebase(opts) {
23355
23799
  } else {
23356
23800
  try {
23357
23801
  const content = await fs36.readFile(absPath, "utf8");
23358
- const parsed = matter8(content);
23802
+ const parsed = matter9(content);
23359
23803
  if (!parsed.data["schemaVersion"]) {
23360
23804
  parsed.data["schemaVersion"] = 1;
23361
- const newContent = matter8.stringify(parsed.content, parsed.data);
23805
+ const newContent = matter9.stringify(parsed.content, parsed.data);
23362
23806
  await fs36.writeFile(absPath, newContent, "utf8");
23363
23807
  applied.push({
23364
23808
  category: issue.category,
@@ -23394,14 +23838,14 @@ async function fixTeamCodebase(opts) {
23394
23838
  } else {
23395
23839
  try {
23396
23840
  const indexContent = await fs36.readFile(paths.index, "utf8");
23397
- const parsed = matter8(indexContent);
23841
+ const parsed = matter9(indexContent);
23398
23842
  const repoFiles = await fs36.readdir(paths.reposDir).catch(() => []);
23399
23843
  const domainFiles = await fs36.readdir(paths.domainsDir).catch(() => []);
23400
23844
  const repoCnt = repoFiles.filter((f) => f.endsWith(".md")).length;
23401
23845
  const domainCnt = domainFiles.filter((f) => f.endsWith(".md")).length;
23402
23846
  parsed.data["repo_count"] = repoCnt;
23403
23847
  parsed.data["domain_count"] = domainCnt;
23404
- const newContent = matter8.stringify(parsed.content, parsed.data);
23848
+ const newContent = matter9.stringify(parsed.content, parsed.data);
23405
23849
  await fs36.writeFile(paths.index, newContent, "utf8");
23406
23850
  applied.push({
23407
23851
  category: issue.category,
@@ -23444,11 +23888,11 @@ __export(codebase_upgrade_wiki_exports, {
23444
23888
  upgradeCodebaseWiki: () => upgradeCodebaseWiki
23445
23889
  });
23446
23890
  import { readdir as readdir7, readFile as readFile10 } from "fs/promises";
23447
- import path85 from "path";
23891
+ import path87 from "path";
23448
23892
  import chalk7 from "chalk";
23449
- import matter9 from "gray-matter";
23893
+ import matter10 from "gray-matter";
23450
23894
  async function upgradeCodebaseWiki(opts) {
23451
- const teamCodebaseDir = path85.join(opts.cwd, "docs", "team-codebase", "repos");
23895
+ const teamCodebaseDir = path87.join(opts.cwd, "docs", "team-codebase", "repos");
23452
23896
  if (!await pathExists(teamCodebaseDir)) {
23453
23897
  if (opts.json) {
23454
23898
  console.log(JSON.stringify({ status: "nothing-to-migrate", reason: "docs/team-codebase/repos/ not found" }));
@@ -23473,10 +23917,10 @@ async function upgradeCodebaseWiki(opts) {
23473
23917
  const result = { migrated: [], skipped: [], errors: [] };
23474
23918
  for (const file of mdFiles) {
23475
23919
  const slug = file.replace(".md", "");
23476
- const filePath = path85.join(teamCodebaseDir, file);
23920
+ const filePath = path87.join(teamCodebaseDir, file);
23477
23921
  try {
23478
23922
  const content = await readFile10(filePath, "utf-8");
23479
- const parsed = matter9(content);
23923
+ const parsed = matter10(content);
23480
23924
  const source = parsed.data["source"] ?? parsed.data["repo_url"];
23481
23925
  if (!source) {
23482
23926
  result.skipped.push(`${slug}: \u65E0 source/repo_url \u5B57\u6BB5`);
@@ -23486,9 +23930,9 @@ async function upgradeCodebaseWiki(opts) {
23486
23930
  result.migrated.push(`${slug} \u2192 teamwiki/evidence/code/${slug}/`);
23487
23931
  continue;
23488
23932
  }
23489
- const cacheBase = path85.join(process.env["HOME"] ?? "", ".teamai", "cache", "repos");
23933
+ const cacheBase = path87.join(process.env["HOME"] ?? "", ".teamai", "cache", "repos");
23490
23934
  const urlParts = String(source).replace(/^https?:\/\//, "").replace(/@.*$/, "").split("/");
23491
- const cachePath = path85.join(cacheBase, ...urlParts.slice(0, 3));
23935
+ const cachePath = path87.join(cacheBase, ...urlParts.slice(0, 3));
23492
23936
  if (await pathExists(cachePath)) {
23493
23937
  await extractCodebase({ path: cachePath, project: slug });
23494
23938
  result.migrated.push(slug);
@@ -23543,10 +23987,10 @@ __export(codebase_wiki_lint_exports, {
23543
23987
  lintTeamwiki: () => lintTeamwiki
23544
23988
  });
23545
23989
  import { readFile as readFile11, readdir as readdir8, stat as stat5 } from "fs/promises";
23546
- import path86 from "path";
23990
+ import path88 from "path";
23547
23991
  import chalk8 from "chalk";
23548
23992
  async function lintTeamwiki(opts) {
23549
- const wikiRoot = opts.wikiRoot ?? path86.join(opts.cwd ?? process.cwd(), "teamwiki");
23993
+ const wikiRoot = opts.wikiRoot ?? path88.join(opts.cwd ?? process.cwd(), "teamwiki");
23550
23994
  const issues = [];
23551
23995
  const minSeverity = opts.severity ?? "info";
23552
23996
  const severityOrder = ["info", "low", "medium", "high"];
@@ -23556,7 +24000,7 @@ async function lintTeamwiki(opts) {
23556
24000
  issues.push(issue);
23557
24001
  }
23558
24002
  }
23559
- const graphPath = path86.join(wikiRoot, ".indices", "graph-index.json");
24003
+ const graphPath = path88.join(wikiRoot, ".indices", "graph-index.json");
23560
24004
  let graph = null;
23561
24005
  if (!await pathExists(graphPath)) {
23562
24006
  addIssue({
@@ -23578,7 +24022,7 @@ async function lintTeamwiki(opts) {
23578
24022
  });
23579
24023
  }
23580
24024
  }
23581
- const evidenceDir = path86.join(wikiRoot, "evidence", "code");
24025
+ const evidenceDir = path88.join(wikiRoot, "evidence", "code");
23582
24026
  if (!await pathExists(evidenceDir)) {
23583
24027
  addIssue({
23584
24028
  severity: "high",
@@ -23597,7 +24041,7 @@ async function lintTeamwiki(opts) {
23597
24041
  });
23598
24042
  }
23599
24043
  for (const project of projects) {
23600
- const projectDir = path86.join(evidenceDir, project);
24044
+ const projectDir = path88.join(evidenceDir, project);
23601
24045
  const pStat = await stat5(projectDir).catch(() => null);
23602
24046
  if (!pStat?.isDirectory()) {
23603
24047
  if (!pStat) {
@@ -23617,7 +24061,7 @@ async function lintTeamwiki(opts) {
23617
24061
  }
23618
24062
  }
23619
24063
  for (const navFile of ["router.md", "index.md", "hot.md"]) {
23620
- if (!await pathExists(path86.join(wikiRoot, navFile))) {
24064
+ if (!await pathExists(path88.join(wikiRoot, navFile))) {
23621
24065
  addIssue({
23622
24066
  severity: "low",
23623
24067
  category: "nav-missing",
@@ -23626,7 +24070,7 @@ async function lintTeamwiki(opts) {
23626
24070
  });
23627
24071
  }
23628
24072
  }
23629
- const manifestPath = path86.join(wikiRoot, "source-manifest.json");
24073
+ const manifestPath = path88.join(wikiRoot, "source-manifest.json");
23630
24074
  if (!await pathExists(manifestPath)) {
23631
24075
  addIssue({
23632
24076
  severity: "low",
@@ -23742,7 +24186,7 @@ var codebase_cmd_exports = {};
23742
24186
  __export(codebase_cmd_exports, {
23743
24187
  codebaseCmd: () => codebaseCmd
23744
24188
  });
23745
- import path87 from "path";
24189
+ import path89 from "path";
23746
24190
  import chalk9 from "chalk";
23747
24191
  function formatFixResult(result) {
23748
24192
  const lines = [];
@@ -23801,9 +24245,9 @@ async function codebaseCmd(opts) {
23801
24245
  try {
23802
24246
  const { autoDetectInit: autoDetectInit2 } = await Promise.resolve().then(() => (init_config(), config_exports));
23803
24247
  const { localConfig: lc } = await autoDetectInit2();
23804
- teamwikiDir = path87.join(lc.repo.localPath, "teamwiki");
24248
+ teamwikiDir = path89.join(lc.repo.localPath, "teamwiki");
23805
24249
  } catch {
23806
- teamwikiDir = path87.join(cwd, ".teamai", "team-repo", "teamwiki");
24250
+ teamwikiDir = path89.join(cwd, ".teamai", "team-repo", "teamwiki");
23807
24251
  }
23808
24252
  if (await pathExists3(teamwikiDir)) {
23809
24253
  const { lintTeamwiki: lintTeamwiki2, formatWikiLintReport: formatWikiLintReport2 } = await Promise.resolve().then(() => (init_codebase_wiki_lint(), codebase_wiki_lint_exports));
@@ -23940,7 +24384,7 @@ var review_cmd_exports = {};
23940
24384
  __export(review_cmd_exports, {
23941
24385
  reviewCmd: () => reviewCmd
23942
24386
  });
23943
- import path88 from "path";
24387
+ import path90 from "path";
23944
24388
  import chalk10 from "chalk";
23945
24389
  import fs37 from "fs-extra";
23946
24390
  function riskAtMost(itemRisk, ceiling) {
@@ -24016,7 +24460,7 @@ async function applyOne(cwd, item) {
24016
24460
  if (!section) {
24017
24461
  return { ok: false, reason: "target.section \u7F3A\u5931" };
24018
24462
  }
24019
- const filePath = path88.isAbsolute(file) ? file : path88.join(cwd, file);
24463
+ const filePath = path90.isAbsolute(file) ? file : path90.join(cwd, file);
24020
24464
  if (!await fs37.pathExists(filePath)) {
24021
24465
  return { ok: false, reason: `\u76EE\u6807\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${filePath}` };
24022
24466
  }
@@ -24457,9 +24901,93 @@ var init_hook_output = __esm({
24457
24901
  }
24458
24902
  });
24459
24903
 
24904
+ // src/transcript-parser.ts
24905
+ var transcript_parser_exports = {};
24906
+ __export(transcript_parser_exports, {
24907
+ parseTranscriptForVotes: () => parseTranscriptForVotes
24908
+ });
24909
+ import fs38 from "fs";
24910
+ import path91 from "path";
24911
+ import readline5 from "readline";
24912
+ async function parseTranscriptForVotes(transcriptPath) {
24913
+ const recalledSet = /* @__PURE__ */ new Set();
24914
+ const referencedSet = /* @__PURE__ */ new Set();
24915
+ try {
24916
+ const stat6 = await fs38.promises.stat(transcriptPath);
24917
+ if (stat6.size === 0) return { recalledDocIds: [], referencedDocIds: [] };
24918
+ } catch {
24919
+ return { recalledDocIds: [], referencedDocIds: [] };
24920
+ }
24921
+ const rl = readline5.createInterface({
24922
+ input: fs38.createReadStream(transcriptPath, { encoding: "utf-8" }),
24923
+ crlfDelay: Infinity
24924
+ });
24925
+ for await (const line of rl) {
24926
+ const trimmed = line.trim();
24927
+ if (!trimmed || !trimmed.includes('"assistant"')) continue;
24928
+ let entry;
24929
+ try {
24930
+ entry = JSON.parse(trimmed);
24931
+ } catch {
24932
+ continue;
24933
+ }
24934
+ if (entry["type"] !== "assistant") continue;
24935
+ const message = entry["message"];
24936
+ if (!message || !Array.isArray(message["content"])) continue;
24937
+ for (const block of message["content"]) {
24938
+ if (block["type"] !== "text") continue;
24939
+ const text = block["text"];
24940
+ if (typeof text !== "string") continue;
24941
+ extractRecalledDocIds(text, recalledSet);
24942
+ extractReferencedDocIds(text, referencedSet);
24943
+ }
24944
+ }
24945
+ return {
24946
+ recalledDocIds: [...recalledSet],
24947
+ referencedDocIds: [...referencedSet]
24948
+ };
24949
+ }
24950
+ function extractRecalledDocIds(text, out) {
24951
+ const START = "--- [teamai:recall:start] ---";
24952
+ const END = "--- [teamai:recall:end] ---";
24953
+ const filePattern = /^File:\s*(.+)$/gm;
24954
+ let searchFrom = 0;
24955
+ while (true) {
24956
+ const startIdx = text.indexOf(START, searchFrom);
24957
+ if (startIdx === -1) break;
24958
+ const endIdx = text.indexOf(END, startIdx + START.length);
24959
+ if (endIdx === -1) break;
24960
+ const region = text.slice(startIdx + START.length, endIdx);
24961
+ filePattern.lastIndex = 0;
24962
+ let match;
24963
+ while ((match = filePattern.exec(region)) !== null) {
24964
+ const filePath = match[1].trim();
24965
+ const docId = path91.basename(filePath).replace(/\.md$/i, "");
24966
+ out.add(docId);
24967
+ }
24968
+ searchFrom = endIdx + END.length;
24969
+ }
24970
+ }
24971
+ function extractReferencedDocIds(text, out) {
24972
+ const pattern = /<!--\s*teamai:referenced-doc-ids:\s*\[([^\]]*)\]\s*-->/g;
24973
+ let match;
24974
+ while ((match = pattern.exec(text)) !== null) {
24975
+ const raw = match[1];
24976
+ for (const item of raw.split(",")) {
24977
+ const docId = item.trim().replace(/^['"]|['"]$/g, "");
24978
+ if (docId) out.add(docId);
24979
+ }
24980
+ }
24981
+ }
24982
+ var init_transcript_parser = __esm({
24983
+ "src/transcript-parser.ts"() {
24984
+ "use strict";
24985
+ }
24986
+ });
24987
+
24460
24988
  // src/machine-id.ts
24461
24989
  import crypto4 from "crypto";
24462
- import fs38 from "fs";
24990
+ import fs39 from "fs";
24463
24991
  import { execFileSync as execFileSync4 } from "child_process";
24464
24992
  function getMachineId() {
24465
24993
  if (cachedMachineId !== null) return cachedMachineId;
@@ -24500,7 +25028,7 @@ function readWindowsMachineId() {
24500
25028
  function readLinuxMachineId() {
24501
25029
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
24502
25030
  try {
24503
- const content = fs38.readFileSync(p, "utf-8").trim();
25031
+ const content = fs39.readFileSync(p, "utf-8").trim();
24504
25032
  if (content) return content;
24505
25033
  } catch {
24506
25034
  }
@@ -24525,7 +25053,7 @@ var init_machine_id = __esm({
24525
25053
  // src/agent-version.ts
24526
25054
  import { execFile as execFile2 } from "child_process";
24527
25055
  import { readFile as readFile12 } from "fs/promises";
24528
- import path89 from "path";
25056
+ import path92 from "path";
24529
25057
  async function execVersion(bin, args = ["--version"]) {
24530
25058
  return new Promise((resolve) => {
24531
25059
  execFile2(bin, args, { timeout: 5e3 }, (err, stdout) => {
@@ -24538,7 +25066,7 @@ async function execVersion(bin, args = ["--version"]) {
24538
25066
  });
24539
25067
  }
24540
25068
  async function readPlistVersion(appPath) {
24541
- const plistPath = path89.join(appPath, "Contents", "Info.plist");
25069
+ const plistPath = path92.join(appPath, "Contents", "Info.plist");
24542
25070
  try {
24543
25071
  const content = await readFile12(plistPath, "utf-8");
24544
25072
  const match = content.match(
@@ -24622,7 +25150,7 @@ __export(status_report_exports, {
24622
25150
  scanReportableSkills: () => scanReportableSkills
24623
25151
  });
24624
25152
  import os7 from "os";
24625
- import path90 from "path";
25153
+ import path93 from "path";
24626
25154
  import YAML17 from "yaml";
24627
25155
  function resolveEndpoints() {
24628
25156
  const raw = process.env.TEAMAI_REPORT_PATHS;
@@ -24668,7 +25196,7 @@ async function scanReportableSkills(skillsDir) {
24668
25196
  const dirs = (await listDirs(skillsDir)).filter((slug) => !slug.startsWith("."));
24669
25197
  const skills = await Promise.all(
24670
25198
  dirs.map(async (slug) => {
24671
- const skillMd = path90.join(skillsDir, slug, "SKILL.md");
25199
+ const skillMd = path93.join(skillsDir, slug, "SKILL.md");
24672
25200
  if (!await pathExists(skillMd)) return null;
24673
25201
  const meta = await readSkillMeta(skillMd);
24674
25202
  return { slug, version: meta.version, display_name: meta.name || slug };
@@ -24677,11 +25205,11 @@ async function scanReportableSkills(skillsDir) {
24677
25205
  return skills.filter((s) => s !== null).sort((a, b) => a.slug.localeCompare(b.slug));
24678
25206
  }
24679
25207
  function queuePath() {
24680
- return path90.join(process.env.HOME ?? "", ".teamai", "reporter", "queue.jsonl");
25208
+ return path93.join(process.env.HOME ?? "", ".teamai", "reporter", "queue.jsonl");
24681
25209
  }
24682
25210
  async function enqueue(req) {
24683
25211
  const p = queuePath();
24684
- await ensureDir(path90.dirname(p));
25212
+ await ensureDir(path93.dirname(p));
24685
25213
  const fse9 = await import("fs-extra");
24686
25214
  await fse9.default.appendFile(p, JSON.stringify(req) + "\n", "utf-8");
24687
25215
  }
@@ -24755,8 +25283,8 @@ async function runStatusReportInner(opts) {
24755
25283
  return;
24756
25284
  }
24757
25285
  const baseDir = resolveBaseDir(localConfig);
24758
- const skillsDir = path90.join(baseDir, skillsRel);
24759
- const installPath = path90.join(baseDir, path90.dirname(skillsRel));
25286
+ const skillsDir = path93.join(baseDir, skillsRel);
25287
+ const installPath = path93.join(baseDir, path93.dirname(skillsRel));
24760
25288
  const machineId = getMachineId();
24761
25289
  const localAgentId = deriveLocalAgentId(agentType, machineId, installPath);
24762
25290
  const endpoints = resolveEndpoints();
@@ -24863,6 +25391,7 @@ var init_status_report = __esm({
24863
25391
  });
24864
25392
 
24865
25393
  // src/hook-handlers.ts
25394
+ import path94 from "path";
24866
25395
  function buildHandlerRegistry() {
24867
25396
  return [
24868
25397
  // ─── SessionStart ─────────────────────────────────
@@ -24873,6 +25402,7 @@ function buildHandlerRegistry() {
24873
25402
  // ─── Stop ─────────────────────────────────────────
24874
25403
  { event: "stop", matcher: "*", handler: updateHandler, timeoutMs: UPDATE_TIMEOUT_MS },
24875
25404
  { event: "stop", matcher: "*", handler: contributeCheckHandler, timeoutMs: CONTRIBUTE_CHECK_TIMEOUT_MS },
25405
+ { event: "stop", matcher: "*", handler: votesSyncHandler, timeoutMs: VOTES_SYNC_TIMEOUT_MS },
24876
25406
  { event: "stop", matcher: "*", handler: dashboardReportHandler, timeoutMs: DASHBOARD_TIMEOUT_MS },
24877
25407
  // ─── PostToolUse ──────────────────────────────────
24878
25408
  { event: "post-tool-use", matcher: "*", handler: dashboardReportHandler, timeoutMs: DASHBOARD_TIMEOUT_MS },
@@ -24884,7 +25414,7 @@ function buildHandlerRegistry() {
24884
25414
  { event: "prompt-submit", matcher: "*", handler: statusReportMessageHandler, timeoutMs: STATUS_REPORT_TIMEOUT_MS }
24885
25415
  ];
24886
25416
  }
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;
25417
+ 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
25418
  var init_hook_handlers = __esm({
24889
25419
  "src/hook-handlers.ts"() {
24890
25420
  "use strict";
@@ -24895,6 +25425,7 @@ var init_hook_handlers = __esm({
24895
25425
  TRACK_TIMEOUT_MS = 5e3;
24896
25426
  DASHBOARD_TIMEOUT_MS = 5e3;
24897
25427
  CONTRIBUTE_CHECK_TIMEOUT_MS = 1e4;
25428
+ VOTES_SYNC_TIMEOUT_MS = 8e3;
24898
25429
  TODOWRITE_HINT_TIMEOUT_MS = 5e3;
24899
25430
  MR_HINT_TIMEOUT_MS = 1e4;
24900
25431
  STATUS_REPORT_TIMEOUT_MS = 3e4;
@@ -24987,6 +25518,31 @@ var init_hook_handlers = __esm({
24987
25518
  return null;
24988
25519
  }
24989
25520
  };
25521
+ votesSyncHandler = {
25522
+ name: "votes-sync",
25523
+ async execute(stdin) {
25524
+ if (process.env.TEAMAI_RECALL_DISABLED === "1") return null;
25525
+ const transcriptPath = typeof stdin.transcript_path === "string" ? stdin.transcript_path : null;
25526
+ if (!transcriptPath) return null;
25527
+ try {
25528
+ const { parseTranscriptForVotes: parseTranscriptForVotes2 } = await Promise.resolve().then(() => (init_transcript_parser(), transcript_parser_exports));
25529
+ const { incrementUpvoted: incrementUpvoted2, syncVotesToTeam: syncVotesToTeam2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
25530
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
25531
+ const voteData = await parseTranscriptForVotes2(transcriptPath);
25532
+ const { localConfig } = await requireInit3();
25533
+ const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
25534
+ const votesDir = VOTES_LOCAL_DIR2;
25535
+ const votePath = path94.join(votesDir, `${localConfig.username}.yaml`);
25536
+ if (voteData.referencedDocIds.length > 0) {
25537
+ await incrementUpvoted2(votePath, voteData.referencedDocIds);
25538
+ }
25539
+ await syncVotesToTeam2(localConfig.repo.localPath, localConfig.username, votesDir).catch(() => {
25540
+ });
25541
+ } catch {
25542
+ }
25543
+ return null;
25544
+ }
25545
+ };
24990
25546
  todowriteHintHandler = {
24991
25547
  name: "todowrite-hint",
24992
25548
  async execute(stdin, _tool) {
@@ -25113,10 +25669,10 @@ function formatComment(learning, suggestions, marker) {
25113
25669
  lines.push("> _Auto-generated by `teamai ci extract-mr`_");
25114
25670
  return lines.join("\n");
25115
25671
  }
25116
- async function githubRequest(path92, method, body) {
25672
+ async function githubRequest(path99, method, body) {
25117
25673
  const token = process.env["GITHUB_TOKEN"];
25118
25674
  if (!token) throw new Error("\u672A\u8BBE\u7F6E GITHUB_TOKEN \u73AF\u5883\u53D8\u91CF");
25119
- const url = `https://api.github.com${path92}`;
25675
+ const url = `https://api.github.com${path99}`;
25120
25676
  const headers = {
25121
25677
  Authorization: `Bearer ${token}`,
25122
25678
  Accept: "application/vnd.github+json",
@@ -25172,9 +25728,9 @@ function getTGitToken() {
25172
25728
  if (oauthToken) return oauthToken;
25173
25729
  throw new Error("\u672A\u8BBE\u7F6E TAI_PAT_TOKEN \u73AF\u5883\u53D8\u91CF\uFF0C\u4E14\u65E0\u6CD5\u4ECE gf credential \u83B7\u53D6 token");
25174
25730
  }
25175
- async function tgitRequest(path92, method, body) {
25731
+ async function tgitRequest(path99, method, body) {
25176
25732
  const token = getTGitToken();
25177
- const url = `https://git.woa.com/api/v3${path92}`;
25733
+ const url = `https://git.woa.com/api/v3${path99}`;
25178
25734
  const headers = {
25179
25735
  Authorization: `Bearer ${token}`,
25180
25736
  "Content-Type": "application/json"
@@ -25464,10 +26020,10 @@ function extractMarkerId(body) {
25464
26020
  const match = body.match(MARKER_REGEX);
25465
26021
  return match ? match[1] : null;
25466
26022
  }
25467
- async function githubRequest2(path92) {
26023
+ async function githubRequest2(path99) {
25468
26024
  const token = process.env["GITHUB_TOKEN"];
25469
26025
  if (!token) throw new Error("\u672A\u8BBE\u7F6E GITHUB_TOKEN");
25470
- return fetch(`https://api.github.com${path92}`, {
26026
+ return fetch(`https://api.github.com${path99}`, {
25471
26027
  headers: {
25472
26028
  Authorization: `Bearer ${token}`,
25473
26029
  Accept: "application/vnd.github+json",
@@ -25509,9 +26065,9 @@ function getTGitToken2() {
25509
26065
  if (oauthToken) return oauthToken;
25510
26066
  throw new Error("\u672A\u8BBE\u7F6E TAI_PAT_TOKEN \u4E14\u65E0\u6CD5\u83B7\u53D6 OAuth token");
25511
26067
  }
25512
- async function tgitRequest2(path92) {
26068
+ async function tgitRequest2(path99) {
25513
26069
  const token = getTGitToken2();
25514
- return fetch(`https://git.woa.com/api/v3${path92}`, {
26070
+ return fetch(`https://git.woa.com/api/v3${path99}`, {
25515
26071
  headers: {
25516
26072
  Authorization: `Bearer ${token}`,
25517
26073
  "Content-Type": "application/json"
@@ -25577,8 +26133,8 @@ var extract_mr_exports = {};
25577
26133
  __export(extract_mr_exports, {
25578
26134
  ciExtractMr: () => ciExtractMr
25579
26135
  });
25580
- import fs39 from "fs/promises";
25581
- import path91 from "path";
26136
+ import fs40 from "fs/promises";
26137
+ import path95 from "path";
25582
26138
  async function configureGitUser2(repoPath, provider) {
25583
26139
  const { execFileSync: execFileSync5 } = await import("child_process");
25584
26140
  let name = "teamai-ci";
@@ -25631,11 +26187,11 @@ async function writeKnowledgeToRepo(teamRepo, learning, suggestions, writeMode,
25631
26187
  const safeTitle = learning.title.replace(/[^a-zA-Z0-9一-鿿_-]/g, "-").replace(/-+/g, "-").slice(0, 50);
25632
26188
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
25633
26189
  const filename = `${dateStr}-${safeTitle}.md`;
25634
- const learningsDir = path91.join(teamRepo, "learnings");
25635
- const learningPath = path91.join(learningsDir, filename);
26190
+ const learningsDir = path95.join(teamRepo, "learnings");
26191
+ const learningPath = path95.join(learningsDir, filename);
25636
26192
  if (!dryRun) {
25637
- await fs39.mkdir(learningsDir, { recursive: true });
25638
- await fs39.writeFile(learningPath, learning.content, "utf-8");
26193
+ await fs40.mkdir(learningsDir, { recursive: true });
26194
+ await fs40.writeFile(learningPath, learning.content, "utf-8");
25639
26195
  }
25640
26196
  log.success(`Learning \u5199\u5165: learnings/${filename}`);
25641
26197
  changedFiles.push(`learnings/${filename}`);
@@ -25670,13 +26226,13 @@ async function writeKnowledgeToRepo(teamRepo, learning, suggestions, writeMode,
25670
26226
  }
25671
26227
  }
25672
26228
  async function writeArtifacts(outputDir, learning, suggestions) {
25673
- await fs39.mkdir(outputDir, { recursive: true });
26229
+ await fs40.mkdir(outputDir, { recursive: true });
25674
26230
  if (learning) {
25675
- await fs39.writeFile(path91.join(outputDir, "learning.md"), learning.content, "utf-8");
26231
+ await fs40.writeFile(path95.join(outputDir, "learning.md"), learning.content, "utf-8");
25676
26232
  }
25677
26233
  if (suggestions && suggestions.length > 0) {
25678
- await fs39.writeFile(
25679
- path91.join(outputDir, "codebase-suggestions.json"),
26234
+ await fs40.writeFile(
26235
+ path95.join(outputDir, "codebase-suggestions.json"),
25680
26236
  JSON.stringify(suggestions, null, 2),
25681
26237
  "utf-8"
25682
26238
  );
@@ -25690,7 +26246,7 @@ async function ciExtractMr(opts) {
25690
26246
  const result = await importFromMR({
25691
26247
  url: opts.url,
25692
26248
  all: true,
25693
- learningsDir: opts.teamRepo ? path91.join(opts.teamRepo, "learnings") : void 0,
26249
+ learningsDir: opts.teamRepo ? path95.join(opts.teamRepo, "learnings") : void 0,
25694
26250
  dryRun: true
25695
26251
  // 不让 importFromMR 自己写文件,我们自己控制写入
25696
26252
  });
@@ -25799,21 +26355,21 @@ ${affectedModules.map((m) => `- \`${m}\` (evidence + G-document)`).join("\n")}`
25799
26355
  const projectName = parsed.repo;
25800
26356
  await extractCodebase2({ path: businessRepo, project: projectName });
25801
26357
  const fse9 = await import("fs-extra");
25802
- const srcWiki = path91.join(businessRepo, "teamwiki");
25803
- const teamWikiRoot = path91.join(path91.resolve(opts.teamRepo), "teamwiki");
26358
+ const srcWiki = path95.join(businessRepo, "teamwiki");
26359
+ const teamWikiRoot = path95.join(path95.resolve(opts.teamRepo), "teamwiki");
25804
26360
  try {
25805
26361
  if (await fse9.pathExists(srcWiki)) {
25806
- const evidenceSrc = path91.join(srcWiki, "evidence", "code", projectName);
25807
- const evidenceDest = path91.join(teamWikiRoot, "evidence", "code", projectName);
26362
+ const evidenceSrc = path95.join(srcWiki, "evidence", "code", projectName);
26363
+ const evidenceDest = path95.join(teamWikiRoot, "evidence", "code", projectName);
25808
26364
  if (await fse9.pathExists(evidenceSrc)) {
25809
26365
  await fse9.ensureDir(evidenceDest);
25810
26366
  await fse9.copy(evidenceSrc, evidenceDest, { overwrite: true });
25811
26367
  }
25812
- const srcGraph = path91.join(srcWiki, ".indices", "graph-index.json");
26368
+ const srcGraph = path95.join(srcWiki, ".indices", "graph-index.json");
25813
26369
  if (await fse9.pathExists(srcGraph)) {
25814
- const destGraphDir = path91.join(evidenceDest, ".indices");
26370
+ const destGraphDir = path95.join(evidenceDest, ".indices");
25815
26371
  await fse9.ensureDir(destGraphDir);
25816
- await fse9.copy(srcGraph, path91.join(destGraphDir, "graph-index.json"));
26372
+ await fse9.copy(srcGraph, path95.join(destGraphDir, "graph-index.json"));
25817
26373
  }
25818
26374
  const { aggregateGlobalGraph: aggregateGlobalGraph2 } = await Promise.resolve().then(() => (init_graph_aggregate(), graph_aggregate_exports));
25819
26375
  await aggregateGlobalGraph2(teamWikiRoot);
@@ -25868,6 +26424,440 @@ var init_extract_mr = __esm({
25868
26424
  }
25869
26425
  });
25870
26426
 
26427
+ // src/maintenance/prune.ts
26428
+ import path96 from "path";
26429
+ import matter11 from "gray-matter";
26430
+ async function findPruneCandidates(learningsDir, votesDir, options = {}) {
26431
+ const threshold = options.threshold ?? DEFAULT_THRESHOLD;
26432
+ const confidenceMap = await computeAllConfidence(votesDir);
26433
+ const candidates = [];
26434
+ const files = await listFiles(learningsDir);
26435
+ const now = Date.now();
26436
+ for (const file of files) {
26437
+ if (!file.endsWith(".md")) continue;
26438
+ const docId = file.replace(/\.md$/i, "");
26439
+ const absPath = path96.join(learningsDir, file);
26440
+ const content = await readFileSafe(absPath);
26441
+ if (!content) continue;
26442
+ let date = "";
26443
+ try {
26444
+ const { data } = matter11(content);
26445
+ date = typeof data.date === "string" ? data.date : "";
26446
+ } catch {
26447
+ continue;
26448
+ }
26449
+ const confidence = confidenceMap.get(docId);
26450
+ if (confidence === void 0) continue;
26451
+ const daysSinceCreate = date ? (now - new Date(date).getTime()) / (1e3 * 60 * 60 * 24) : Infinity;
26452
+ if (confidence < threshold) {
26453
+ candidates.push({
26454
+ filename: file,
26455
+ path: absPath,
26456
+ confidence,
26457
+ lastActivity: date,
26458
+ reason: `confidence ${confidence.toFixed(2)} < ${threshold}`
26459
+ });
26460
+ } else if (daysSinceCreate > STALE_DAYS && confidence < 0.3) {
26461
+ candidates.push({
26462
+ filename: file,
26463
+ path: absPath,
26464
+ confidence,
26465
+ lastActivity: date,
26466
+ reason: `inactive ${isFinite(daysSinceCreate) ? Math.round(daysSinceCreate) + "d" : "no-date"}, confidence ${confidence.toFixed(2)}`
26467
+ });
26468
+ }
26469
+ }
26470
+ return candidates.sort((a, b) => a.confidence - b.confidence);
26471
+ }
26472
+ async function executePrune(repoPath, candidates, options = {}) {
26473
+ let archived = 0;
26474
+ let removed = 0;
26475
+ if (options.dryRun) {
26476
+ log.info(`[dry-run] Would ${options.archive ? "archive" : "remove"} ${candidates.length} file(s)`);
26477
+ return { archived: 0, removed: 0 };
26478
+ }
26479
+ for (const candidate of candidates) {
26480
+ if (options.archive) {
26481
+ const archiveDir = path96.join(repoPath, "learnings", "_archive");
26482
+ await ensureDir(archiveDir);
26483
+ await copyFile(candidate.path, path96.join(archiveDir, candidate.filename));
26484
+ await remove(candidate.path);
26485
+ archived++;
26486
+ } else {
26487
+ await remove(candidate.path);
26488
+ removed++;
26489
+ }
26490
+ }
26491
+ if (archived > 0) log.success(`Archived ${archived} learning(s)`);
26492
+ if (removed > 0) log.success(`Removed ${removed} learning(s)`);
26493
+ return { archived, removed };
26494
+ }
26495
+ var DEFAULT_THRESHOLD, STALE_DAYS;
26496
+ var init_prune = __esm({
26497
+ "src/maintenance/prune.ts"() {
26498
+ "use strict";
26499
+ init_fs();
26500
+ init_logger();
26501
+ init_confidence();
26502
+ DEFAULT_THRESHOLD = 0.15;
26503
+ STALE_DAYS = 180;
26504
+ }
26505
+ });
26506
+
26507
+ // src/maintenance/quality-update.ts
26508
+ import path97 from "path";
26509
+ async function findStaleEntries(votesDir, knowledgeDirs, options = {}) {
26510
+ const minRecalled = options.minRecalled ?? DEFAULT_MIN_RECALLED;
26511
+ const maxUpvoted = options.maxUpvoted ?? DEFAULT_MAX_UPVOTED;
26512
+ const minUsers = options.minUsers ?? DEFAULT_MIN_USERS;
26513
+ const perDoc = /* @__PURE__ */ new Map();
26514
+ const voteFiles = await listFiles(votesDir);
26515
+ for (const file of voteFiles) {
26516
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26517
+ const username = file.replace(/\.(yaml|yml)$/, "");
26518
+ const filePath = path97.join(votesDir, file);
26519
+ try {
26520
+ const data = await loadUserVotes(filePath);
26521
+ for (const [docId, entry] of Object.entries(data.votes)) {
26522
+ const existing = perDoc.get(docId) ?? { recalled: 0, upvoted: 0, users: /* @__PURE__ */ new Set() };
26523
+ existing.recalled += entry.recalled_count ?? 0;
26524
+ existing.upvoted += entry.upvoted_count ?? 0;
26525
+ if ((entry.recalled_count ?? 0) > 0) existing.users.add(username);
26526
+ perDoc.set(docId, existing);
26527
+ }
26528
+ } catch {
26529
+ continue;
26530
+ }
26531
+ }
26532
+ const candidates = [];
26533
+ for (const [docId, data] of perDoc) {
26534
+ if (data.recalled < minRecalled) continue;
26535
+ if (data.upvoted > maxUpvoted) continue;
26536
+ if (data.users.size < minUsers) continue;
26537
+ const entryPath = await resolveDocPath(docId, knowledgeDirs);
26538
+ if (!entryPath) continue;
26539
+ const type = entryPath.includes("/docs/") ? "docs" : entryPath.includes("/rules/") ? "rules" : entryPath.includes("/skills/") ? "skills" : "unknown";
26540
+ candidates.push({
26541
+ docId,
26542
+ path: entryPath,
26543
+ recalledCount: data.recalled,
26544
+ upvotedCount: data.upvoted,
26545
+ userCount: data.users.size,
26546
+ type
26547
+ });
26548
+ }
26549
+ return candidates.sort((a, b) => b.recalledCount - a.recalledCount);
26550
+ }
26551
+ async function resolveDocPath(docId, dirs) {
26552
+ const filename = docId.endsWith(".md") ? docId : `${docId}.md`;
26553
+ for (const dir of [dirs.docs, dirs.rules, dirs.skills]) {
26554
+ if (!dir) continue;
26555
+ const candidate = path97.join(dir, filename);
26556
+ if (await pathExists(candidate)) return candidate;
26557
+ }
26558
+ return null;
26559
+ }
26560
+ function reportStaleEntries(entries) {
26561
+ if (entries.length === 0) {
26562
+ log.info("No stale entries found that need quality updates.");
26563
+ return;
26564
+ }
26565
+ log.info(`Found ${entries.length} entry(ies) recalled often but rarely adopted:`);
26566
+ for (const entry of entries) {
26567
+ log.info(
26568
+ ` - [${entry.type}] ${entry.docId}: recalled ${entry.recalledCount}x by ${entry.userCount} users, adopted ${entry.upvotedCount}x`
26569
+ );
26570
+ }
26571
+ }
26572
+ async function findRelatedAdoptedLearnings(staleEntry, votesDir, learningsDir, limit = 5) {
26573
+ const perDoc = /* @__PURE__ */ new Map();
26574
+ const voteFiles = await listFiles(votesDir);
26575
+ for (const file of voteFiles) {
26576
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26577
+ try {
26578
+ const data = await loadUserVotes(path97.join(votesDir, file));
26579
+ for (const [docId, entry] of Object.entries(data.votes)) {
26580
+ if (docId === staleEntry.docId) continue;
26581
+ if ((entry.upvoted_count ?? 0) > 0) {
26582
+ perDoc.set(docId, (perDoc.get(docId) ?? 0) + (entry.upvoted_count ?? 0));
26583
+ }
26584
+ }
26585
+ } catch {
26586
+ continue;
26587
+ }
26588
+ }
26589
+ const sorted = [...perDoc.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit);
26590
+ const contents = [];
26591
+ for (const [docId] of sorted) {
26592
+ const filename = docId.endsWith(".md") ? docId : `${docId}.md`;
26593
+ const filePath = path97.join(learningsDir, filename);
26594
+ const content = await readFileSafe(filePath);
26595
+ if (content) contents.push(content);
26596
+ }
26597
+ return contents;
26598
+ }
26599
+ async function generateUpdateDraft(staleEntry, relatedLearnings) {
26600
+ const currentContent = await readFileSafe(staleEntry.path);
26601
+ if (!currentContent) return null;
26602
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26603
+ const learningContext = relatedLearnings.length > 0 ? `
26604
+
26605
+ The following learnings were actually adopted by team members (these represent what the team found more useful):
26606
+
26607
+ ${relatedLearnings.map((l, i) => `--- Learning ${i + 1} ---
26608
+ ${l}`).join("\n\n")}` : "";
26609
+ const prompt = `You are a technical writer updating a team knowledge base entry.
26610
+
26611
+ 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.
26612
+
26613
+ Current content:
26614
+ ---
26615
+ ${currentContent}
26616
+ ---
26617
+ ${learningContext}
26618
+
26619
+ Please rewrite this entry to be more actionable and directly useful. Keep the same topic but:
26620
+ 1. Add concrete examples or commands where applicable
26621
+ 2. Remove outdated or vague information
26622
+ 3. Incorporate relevant insights from the adopted learnings above
26623
+ 4. Keep the same YAML frontmatter format (update the date field to today)
26624
+ 5. Be concise \u2014 aim for the same or shorter length
26625
+
26626
+ Output ONLY the updated markdown file content (including frontmatter).`;
26627
+ try {
26628
+ const draft = await callClaude2(prompt);
26629
+ return draft.trim();
26630
+ } catch (e) {
26631
+ log.error(`AI update generation failed: ${e.message}`);
26632
+ return null;
26633
+ }
26634
+ }
26635
+ var DEFAULT_MIN_RECALLED, DEFAULT_MAX_UPVOTED, DEFAULT_MIN_USERS;
26636
+ var init_quality_update = __esm({
26637
+ "src/maintenance/quality-update.ts"() {
26638
+ "use strict";
26639
+ init_fs();
26640
+ init_votes();
26641
+ init_logger();
26642
+ DEFAULT_MIN_RECALLED = 5;
26643
+ DEFAULT_MAX_UPVOTED = 1;
26644
+ DEFAULT_MIN_USERS = 2;
26645
+ }
26646
+ });
26647
+
26648
+ // src/maintenance/promote.ts
26649
+ import path98 from "path";
26650
+ import matter12 from "gray-matter";
26651
+ async function findPromotionCandidates(learningsDir, votesDir) {
26652
+ const confidenceMap = await computeAllConfidence(votesDir);
26653
+ const candidates = [];
26654
+ const perDoc = await aggregatePerDocVotes(votesDir);
26655
+ const files = await listFiles(learningsDir);
26656
+ const now = Date.now();
26657
+ for (const file of files) {
26658
+ if (!file.endsWith(".md")) continue;
26659
+ const docId = file.replace(/\.md$/i, "");
26660
+ const confidence = confidenceMap.get(docId) ?? 0;
26661
+ if (confidence < MIN_CONFIDENCE) continue;
26662
+ const docVotes = perDoc.get(docId);
26663
+ if (!docVotes) continue;
26664
+ if (docVotes.upvoted < MIN_UPVOTED) continue;
26665
+ if (docVotes.users.size < MIN_USERS) continue;
26666
+ const absPath = path98.join(learningsDir, file);
26667
+ const content = await readFileSafe(absPath);
26668
+ if (!content) continue;
26669
+ let title = docId;
26670
+ let date = "";
26671
+ try {
26672
+ const { data } = matter12(content);
26673
+ title = typeof data.title === "string" ? data.title : docId;
26674
+ date = typeof data.date === "string" ? data.date : "";
26675
+ if (data.promoted_to) continue;
26676
+ } catch {
26677
+ continue;
26678
+ }
26679
+ if (date) {
26680
+ const ageInDays = (now - new Date(date).getTime()) / (1e3 * 60 * 60 * 24);
26681
+ if (ageInDays < MIN_AGE_DAYS) continue;
26682
+ }
26683
+ const suggestedCategory = await inferCategory(content, title);
26684
+ candidates.push({
26685
+ docId,
26686
+ filename: file,
26687
+ path: absPath,
26688
+ confidence,
26689
+ upvotedCount: docVotes.upvoted,
26690
+ userCount: docVotes.users.size,
26691
+ title,
26692
+ suggestedCategory
26693
+ });
26694
+ }
26695
+ return candidates.sort((a, b) => b.confidence - a.confidence);
26696
+ }
26697
+ async function generatePromotedContent(originalContent, category, title) {
26698
+ const categoryGuide = {
26699
+ skills: `a reusable skill/SOP. Format it with:
26700
+ - A clear "when to use" trigger description
26701
+ - Step-by-step numbered procedure
26702
+ - Expected outcome
26703
+ - Common pitfalls to avoid
26704
+ Remove any references to specific dates, people, or one-off incidents. Make it timeless and reusable.`,
26705
+ rules: `a team rule/constraint. Format it with:
26706
+ - A one-line rule statement (imperative: "Always X" / "Never Y")
26707
+ - Rationale: why this matters (1-2 sentences)
26708
+ - Examples of correct and incorrect usage
26709
+ Remove anecdotal context. State the constraint as an absolute standard.`,
26710
+ docs: `a reference document. Format it with:
26711
+ - Context: what system/component this describes
26712
+ - Key concepts and their relationships
26713
+ - Architecture decisions and their rationale
26714
+ - Current state (as of promotion date)
26715
+ Remove personal narrative. Write as objective technical documentation.`
26716
+ };
26717
+ try {
26718
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26719
+ const prompt = `You are a technical knowledge engineer promoting a team learning into formal knowledge.
26720
+
26721
+ The following "learning" has been repeatedly validated by the team (high confidence, adopted by multiple members). Transform it into ${categoryGuide[category]}
26722
+
26723
+ Original learning (title: "${title}"):
26724
+ ---
26725
+ ${originalContent}
26726
+ ---
26727
+
26728
+ 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.`;
26729
+ const result = await callClaude2(prompt, { timeout: 6e4 });
26730
+ return result.trim();
26731
+ } catch (e) {
26732
+ log.warn(`AI content generation failed, using original content: ${e.message}`);
26733
+ return originalContent;
26734
+ }
26735
+ }
26736
+ async function executePromotion(candidate, repoPath, options = {}) {
26737
+ const category = options.category ?? candidate.suggestedCategory;
26738
+ const targetDir = path98.join(repoPath, category);
26739
+ await ensureDir(targetDir);
26740
+ const targetPath = path98.join(targetDir, candidate.filename);
26741
+ if (options.dryRun) {
26742
+ log.info(`[dry-run] Would promote ${candidate.docId} -> ${category}/${candidate.filename}`);
26743
+ return targetPath;
26744
+ }
26745
+ const originalContent = await readFileSafe(candidate.path);
26746
+ if (!originalContent) {
26747
+ log.error(`Cannot read source file: ${candidate.path}`);
26748
+ return targetPath;
26749
+ }
26750
+ const promotedContent = await generatePromotedContent(originalContent, category, candidate.title);
26751
+ await writeFile(targetPath, promotedContent);
26752
+ const { data, content: body } = matter12(originalContent);
26753
+ data.promoted_to = `${category}/${candidate.filename}`;
26754
+ const updated = matter12.stringify(body, data);
26755
+ await writeFile(candidate.path, updated);
26756
+ log.success(`Promoted: ${candidate.docId} -> ${category}/${candidate.filename}`);
26757
+ return targetPath;
26758
+ }
26759
+ function inferCategoryByKeywords(content, title) {
26760
+ const lower = (content + " " + title).toLowerCase();
26761
+ const skillSignals = ["command", "cli", "workflow", "step-by-step", "procedure", "how to", "recipe"];
26762
+ const ruleSignals = ["must", "never", "always", "constraint", "convention", "standard", "rule"];
26763
+ const docSignals = ["architecture", "design", "overview", "background", "decision", "context"];
26764
+ const skillScore = skillSignals.filter((s) => lower.includes(s)).length;
26765
+ const ruleScore = ruleSignals.filter((s) => lower.includes(s)).length;
26766
+ const docScore = docSignals.filter((s) => lower.includes(s)).length;
26767
+ if (ruleScore >= skillScore && ruleScore >= docScore) return "rules";
26768
+ if (skillScore >= docScore) return "skills";
26769
+ return "docs";
26770
+ }
26771
+ async function inferCategory(content, title) {
26772
+ try {
26773
+ const { callClaude: callClaude2 } = await Promise.resolve().then(() => (init_ai_client(), ai_client_exports));
26774
+ const prompt = `Classify the following team knowledge entry into exactly ONE category. Reply with ONLY the category name (no explanation):
26775
+
26776
+ - "skills" \u2014 reusable workflows, SOPs, step-by-step procedures, CLI recipes, operational playbooks
26777
+ - "rules" \u2014 mandatory constraints, coding standards, conventions, best practices that MUST be followed
26778
+ - "docs" \u2014 architecture decisions, system design, background context, reference documentation
26779
+
26780
+ Title: ${title}
26781
+
26782
+ Content:
26783
+ ${content.slice(0, 2e3)}
26784
+
26785
+ Category:`;
26786
+ const result = await callClaude2(prompt, { timeout: 3e4 });
26787
+ const category = result.trim().toLowerCase().replace(/[^a-z]/g, "");
26788
+ if (category === "skills" || category === "rules" || category === "docs") {
26789
+ return category;
26790
+ }
26791
+ } catch {
26792
+ }
26793
+ return inferCategoryByKeywords(content, title);
26794
+ }
26795
+ async function aggregatePerDocVotes(votesDir) {
26796
+ const perDoc = /* @__PURE__ */ new Map();
26797
+ const { loadUserVotes: loadUserVotes2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
26798
+ const voteFiles = await listFiles(votesDir);
26799
+ for (const file of voteFiles) {
26800
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
26801
+ const username = file.replace(/\.(yaml|yml)$/, "");
26802
+ const filePath = path98.join(votesDir, file);
26803
+ try {
26804
+ const data = await loadUserVotes2(filePath);
26805
+ for (const [docId, entry] of Object.entries(data.votes)) {
26806
+ const existing = perDoc.get(docId) ?? { recalled: 0, upvoted: 0, users: /* @__PURE__ */ new Set() };
26807
+ existing.recalled += entry.recalled_count ?? 0;
26808
+ existing.upvoted += entry.upvoted_count ?? 0;
26809
+ if ((entry.upvoted_count ?? 0) > 0) existing.users.add(username);
26810
+ perDoc.set(docId, existing);
26811
+ }
26812
+ } catch {
26813
+ continue;
26814
+ }
26815
+ }
26816
+ return perDoc;
26817
+ }
26818
+ var MIN_CONFIDENCE, MIN_UPVOTED, MIN_USERS, MIN_AGE_DAYS;
26819
+ var init_promote = __esm({
26820
+ "src/maintenance/promote.ts"() {
26821
+ "use strict";
26822
+ init_fs();
26823
+ init_logger();
26824
+ init_confidence();
26825
+ MIN_CONFIDENCE = 0.9;
26826
+ MIN_UPVOTED = 5;
26827
+ MIN_USERS = 2;
26828
+ MIN_AGE_DAYS = 14;
26829
+ }
26830
+ });
26831
+
26832
+ // src/maintenance/index.ts
26833
+ var maintenance_exports = {};
26834
+ __export(maintenance_exports, {
26835
+ COLD_PENALTY: () => COLD_PENALTY,
26836
+ HOT_THRESHOLD: () => HOT_THRESHOLD,
26837
+ annotateHotness: () => annotateHotness,
26838
+ computeAllConfidence: () => computeAllConfidence,
26839
+ computeConfidence: () => computeConfidence,
26840
+ executePromotion: () => executePromotion,
26841
+ executePrune: () => executePrune,
26842
+ findPromotionCandidates: () => findPromotionCandidates,
26843
+ findPruneCandidates: () => findPruneCandidates,
26844
+ findRelatedAdoptedLearnings: () => findRelatedAdoptedLearnings,
26845
+ findStaleEntries: () => findStaleEntries,
26846
+ generateUpdateDraft: () => generateUpdateDraft,
26847
+ reportStaleEntries: () => reportStaleEntries,
26848
+ writeBackConfidence: () => writeBackConfidence
26849
+ });
26850
+ var init_maintenance = __esm({
26851
+ "src/maintenance/index.ts"() {
26852
+ "use strict";
26853
+ init_confidence();
26854
+ init_prune();
26855
+ init_hot_cold();
26856
+ init_quality_update();
26857
+ init_promote();
26858
+ }
26859
+ });
26860
+
25871
26861
  // src/index.ts
25872
26862
  init_logger();
25873
26863
  import { createRequire as createRequire2 } from "module";
@@ -25879,7 +26869,7 @@ program.name("teamai").description("TeamAI \u2014 The team harness for AI agents
25879
26869
  const opts = thisCommand.opts();
25880
26870
  if (opts.verbose) setVerbose(true);
25881
26871
  });
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) => {
26872
+ 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
26873
  const globalOpts = program.opts();
25884
26874
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
25885
26875
  await init2({ ...globalOpts, ...cmdOpts });
@@ -26220,5 +27210,100 @@ program.command("deep-enrich", { hidden: true }).description("Run deep AI knowle
26220
27210
  const { deepEnrich: deepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
26221
27211
  await deepEnrich2({ project: cmdOpts.project, evidenceDir, wikiRoot, maxModules: cmdOpts.maxModules });
26222
27212
  });
27213
+ 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) => {
27214
+ const { recallFeedback: recallFeedback2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
27215
+ await recallFeedback2({ positive: cmdOpts.positive, negative: cmdOpts.negative });
27216
+ });
27217
+ 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) => {
27218
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
27219
+ const { localConfig } = await requireInit3();
27220
+ const repoPath = localConfig.repo.localPath;
27221
+ const votesDir = `${repoPath}/votes`;
27222
+ const learningsDir = `${repoPath}/learnings`;
27223
+ if (cmdOpts.confidenceWriteback) {
27224
+ const { computeAllConfidence: computeAllConfidence2, writeBackConfidence: writeBackConfidence2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27225
+ const map = await computeAllConfidence2(votesDir);
27226
+ await writeBackConfidence2(learningsDir, map);
27227
+ return;
27228
+ }
27229
+ if (cmdOpts.prune) {
27230
+ const { findPruneCandidates: findPruneCandidates2, executePrune: executePrune2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27231
+ const candidates = await findPruneCandidates2(learningsDir, votesDir, {
27232
+ threshold: cmdOpts.threshold
27233
+ });
27234
+ if (candidates.length === 0) {
27235
+ const { log: log4 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27236
+ log4.info("No learnings below threshold. Knowledge base is healthy.");
27237
+ return;
27238
+ }
27239
+ const { log: log3 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27240
+ log3.info(`Found ${candidates.length} candidate(s) for pruning:`);
27241
+ for (const c of candidates) {
27242
+ log3.info(` - ${c.filename} (confidence: ${c.confidence.toFixed(2)}, reason: ${c.reason})`);
27243
+ }
27244
+ await executePrune2(repoPath, candidates, {
27245
+ dryRun: cmdOpts.dryRun,
27246
+ archive: cmdOpts.archive
27247
+ });
27248
+ return;
27249
+ }
27250
+ if (cmdOpts.updateQuality) {
27251
+ const { findStaleEntries: findStaleEntries2, reportStaleEntries: reportStaleEntries2, findRelatedAdoptedLearnings: findRelatedAdoptedLearnings2, generateUpdateDraft: generateUpdateDraft2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27252
+ const { writeFile: writeFile12 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
27253
+ const { log: log3 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27254
+ const entries = await findStaleEntries2(votesDir, {
27255
+ docs: `${repoPath}/docs`,
27256
+ rules: `${repoPath}/rules`,
27257
+ skills: `${repoPath}/skills`
27258
+ });
27259
+ reportStaleEntries2(entries);
27260
+ if (entries.length === 0 || cmdOpts.dryRun) return;
27261
+ log3.info("\nGenerating AI-powered update drafts...");
27262
+ for (const entry of entries) {
27263
+ const related = await findRelatedAdoptedLearnings2(entry, votesDir, learningsDir);
27264
+ const draft = await generateUpdateDraft2(entry, related);
27265
+ if (draft) {
27266
+ const draftPath = `${entry.path}.draft.md`;
27267
+ await writeFile12(draftPath, draft);
27268
+ log3.success(` Draft written: ${draftPath}`);
27269
+ }
27270
+ }
27271
+ log3.info("\nReview drafts, then rename .draft.md -> .md to apply updates.");
27272
+ return;
27273
+ }
27274
+ const { log: log2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27275
+ log2.info("Usage: teamai recall maintenance --prune | --confidence-writeback | --update-quality");
27276
+ });
27277
+ 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) => {
27278
+ const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
27279
+ const { localConfig } = await requireInit3();
27280
+ const repoPath = localConfig.repo.localPath;
27281
+ const votesDir = `${repoPath}/votes`;
27282
+ const learningsDir = `${repoPath}/learnings`;
27283
+ const { findPromotionCandidates: findPromotionCandidates2, executePromotion: executePromotion2 } = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
27284
+ const { log: log2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
27285
+ const candidates = await findPromotionCandidates2(learningsDir, votesDir);
27286
+ if (candidates.length === 0) {
27287
+ log2.info("No learnings eligible for promotion yet.");
27288
+ return;
27289
+ }
27290
+ if (!learningId) {
27291
+ log2.info(`${candidates.length} learning(s) eligible for promotion:`);
27292
+ for (const c of candidates) {
27293
+ log2.info(` - ${c.docId} (confidence: ${c.confidence.toFixed(2)}, suggested: ${c.suggestedCategory})`);
27294
+ }
27295
+ log2.info("\nRun: teamai recall promote <learning-id> [--category <cat>]");
27296
+ return;
27297
+ }
27298
+ const candidate = candidates.find((c) => c.docId === learningId);
27299
+ if (!candidate) {
27300
+ log2.error(`Learning "${learningId}" not found or not eligible for promotion.`);
27301
+ return;
27302
+ }
27303
+ await executePromotion2(candidate, repoPath, {
27304
+ category: cmdOpts.category,
27305
+ dryRun: cmdOpts.dryRun
27306
+ });
27307
+ });
26223
27308
  program.parse();
26224
27309
  //# sourceMappingURL=index.js.map