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