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