teamai-cli 0.17.3 → 0.17.4
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/README.md +85 -488
- package/README.zh-CN.md +88 -491
- package/agents/teamai-recall.md +1 -0
- package/dist/index.js +872 -135
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -139,13 +139,19 @@ var init_logger = __esm({
|
|
|
139
139
|
var types_exports = {};
|
|
140
140
|
__export(types_exports, {
|
|
141
141
|
CONTRIBUTE_BASE_THRESHOLD: () => CONTRIBUTE_BASE_THRESHOLD,
|
|
142
|
+
CONTRIBUTE_CORRECTION_WEIGHT: () => CONTRIBUTE_CORRECTION_WEIGHT,
|
|
143
|
+
CONTRIBUTE_DIVERSITY_BONUS_MAX: () => CONTRIBUTE_DIVERSITY_BONUS_MAX,
|
|
142
144
|
CONTRIBUTE_FASTPATH_TTL_MS: () => CONTRIBUTE_FASTPATH_TTL_MS,
|
|
143
145
|
CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT: () => CONTRIBUTE_GIT_COMMIT_DOWNWEIGHT,
|
|
146
|
+
CONTRIBUTE_INTERRUPT_WEIGHT: () => CONTRIBUTE_INTERRUPT_WEIGHT,
|
|
144
147
|
CONTRIBUTE_KNOWLEDGE_GAP_BONUS: () => CONTRIBUTE_KNOWLEDGE_GAP_BONUS,
|
|
145
148
|
CONTRIBUTE_LOW_QUALITY_BONUS: () => CONTRIBUTE_LOW_QUALITY_BONUS,
|
|
146
149
|
CONTRIBUTE_LOW_QUALITY_THRESHOLD: () => CONTRIBUTE_LOW_QUALITY_THRESHOLD,
|
|
150
|
+
CONTRIBUTE_REJECT_WEIGHT: () => CONTRIBUTE_REJECT_WEIGHT,
|
|
147
151
|
CONTRIBUTE_SESSIONS_DIR: () => CONTRIBUTE_SESSIONS_DIR,
|
|
152
|
+
CONTRIBUTE_SKILL_BONUS: () => CONTRIBUTE_SKILL_BONUS,
|
|
148
153
|
CONTRIBUTE_SMART_THRESHOLD: () => CONTRIBUTE_SMART_THRESHOLD,
|
|
154
|
+
CONTRIBUTE_TOOLERROR_TIERS: () => CONTRIBUTE_TOOLERROR_TIERS,
|
|
149
155
|
CORRECTION_KEYWORDS: () => CORRECTION_KEYWORDS,
|
|
150
156
|
CORRECTION_WINDOW_MS: () => CORRECTION_WINDOW_MS,
|
|
151
157
|
CultureCompanySchema: () => CultureCompanySchema,
|
|
@@ -283,7 +289,7 @@ function getPushignorePath() {
|
|
|
283
289
|
function areTeamHooksDisabled() {
|
|
284
290
|
return process.env.TEAMAI_HOOKS_DISABLED === "1" || process.env.TEAMAI_HOOKS_DISABLED === "true";
|
|
285
291
|
}
|
|
286
|
-
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_FASTPATH_TTL_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;
|
|
292
|
+
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_INTERRUPT_WEIGHT, CONTRIBUTE_REJECT_WEIGHT, CONTRIBUTE_CORRECTION_WEIGHT, CONTRIBUTE_TOOLERROR_TIERS, CONTRIBUTE_SKILL_BONUS, CONTRIBUTE_DIVERSITY_BONUS_MAX, CONTRIBUTE_FASTPATH_TTL_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;
|
|
287
293
|
var init_types = __esm({
|
|
288
294
|
"src/types.ts"() {
|
|
289
295
|
"use strict";
|
|
@@ -386,6 +392,8 @@ var init_types = __esm({
|
|
|
386
392
|
projectRoot: z.string().optional(),
|
|
387
393
|
/** Tags the user has subscribed to. If empty/undefined, pull all resources. */
|
|
388
394
|
subscribedTags: z.array(z.string()).optional(),
|
|
395
|
+
/** Skills to exclude from local sync (per-user, does not affect team repo). */
|
|
396
|
+
excludedSkills: z.array(z.string()).optional(),
|
|
389
397
|
/** User-level override for recall feature. When set, takes precedence over team config. */
|
|
390
398
|
recallEnabled: z.boolean().optional(),
|
|
391
399
|
/** When set, only inject hooks into these agents. Additive across multiple init --agent runs. */
|
|
@@ -470,7 +478,17 @@ var init_types = __esm({
|
|
|
470
478
|
"doesn't want to proceed with this tool use"
|
|
471
479
|
];
|
|
472
480
|
CONTRIBUTE_BASE_THRESHOLD = 15;
|
|
473
|
-
CONTRIBUTE_SMART_THRESHOLD =
|
|
481
|
+
CONTRIBUTE_SMART_THRESHOLD = 20;
|
|
482
|
+
CONTRIBUTE_INTERRUPT_WEIGHT = 20;
|
|
483
|
+
CONTRIBUTE_REJECT_WEIGHT = 20;
|
|
484
|
+
CONTRIBUTE_CORRECTION_WEIGHT = 20;
|
|
485
|
+
CONTRIBUTE_TOOLERROR_TIERS = [
|
|
486
|
+
{ min: 8, points: 25 },
|
|
487
|
+
{ min: 5, points: 18 },
|
|
488
|
+
{ min: 3, points: 10 }
|
|
489
|
+
];
|
|
490
|
+
CONTRIBUTE_SKILL_BONUS = 5;
|
|
491
|
+
CONTRIBUTE_DIVERSITY_BONUS_MAX = 5;
|
|
474
492
|
CONTRIBUTE_FASTPATH_TTL_MS = 5 * 60 * 1e3;
|
|
475
493
|
CONTRIBUTE_KNOWLEDGE_GAP_BONUS = 20;
|
|
476
494
|
CONTRIBUTE_LOW_QUALITY_BONUS = 10;
|
|
@@ -1908,15 +1926,18 @@ teamai recall "<\u5173\u952E\u8BCD1> <\u5173\u952E\u8BCD2> ..."
|
|
|
1908
1926
|
|
|
1909
1927
|
\u4ECE\u4EFB\u52A1\u63CF\u8FF0\u4E2D\u63D0\u53D6 3-6 \u4E2A\u9AD8\u4FE1\u53F7\u5173\u952E\u8BCD\u8FDB\u884C\u68C0\u7D22\u3002
|
|
1910
1928
|
|
|
1911
|
-
### \u68C0\u7D22\u540E\u8981\u6C42
|
|
1929
|
+
### \u68C0\u7D22\u540E\u8981\u6C42\uFF08\u7528\u4E8E\u8BC4\u4F30\u77E5\u8BC6\u88AB\u91C7\u7EB3\u60C5\u51B5\uFF0C\u8BF7\u52A1\u5FC5\u6267\u884C\uFF09
|
|
1912
1930
|
|
|
1913
|
-
\
|
|
1931
|
+
teamai-recall subagent \u7684\u8FD4\u56DE\u91CC\u5DF2\u5217\u51FA\u672C\u6B21\u68C0\u7D22\u5230\u7684\u5019\u9009 doc-id\uFF08\u89C1\u5176 recalled-doc-ids \u5217\u8868\uFF09\u3002
|
|
1932
|
+
\u4F60**\u4E0D\u9700\u8981\u51ED\u8BB0\u5FC6\u91CD\u5EFA\u5217\u8868**\u2014\u2014\u53EA\u9700\u5728**\u6700\u7EC8\u56DE\u590D\u672B\u5C3E**\u8FFD\u52A0\u4E0B\u9762\u8FD9\u884C\uFF0C
|
|
1933
|
+
\u4ECE\u5019\u9009\u91CC**\u4FDD\u7559\u4F60\u5B9E\u9645\u7528\u5230\u7684**\uFF08\u7167\u6284\u4E86\u547D\u4EE4\u3001\u6216\u6982\u5FF5\u4E0A\u53C2\u8003\u4E86\uFF0C\u90FD\u7B97\u7528\u5230\uFF09\uFF0C\u5220\u6389\u6CA1\u7528\u5230\u7684\uFF1A
|
|
1914
1934
|
|
|
1915
1935
|
\`\`\`
|
|
1916
|
-
<!-- teamai:referenced-doc-ids: [
|
|
1936
|
+
<!-- teamai:referenced-doc-ids: [\u4F60\u5B9E\u9645\u7528\u5230\u7684-doc-id] -->
|
|
1917
1937
|
\`\`\`
|
|
1918
1938
|
|
|
1919
|
-
\
|
|
1939
|
+
\u4E00\u4E2A\u90FD\u6CA1\u7528\u5230\u5C31\u7559\u7A7A\uFF1A\`<!-- teamai:referenced-doc-ids: [] -->\`\u3002
|
|
1940
|
+
\u82E5\u76F4\u63A5\u7528 \`teamai recall\` \u547D\u4EE4\uFF08\u672A\u8D70 subagent\uFF09\uFF0C\u4ECE\u53EC\u56DE\u7ED3\u679C\u7684 File \u8DEF\u5F84\u63A8\u51FA doc-id \u81EA\u884C\u586B\u5165\u3002
|
|
1920
1941
|
`;
|
|
1921
1942
|
}
|
|
1922
1943
|
});
|
|
@@ -3393,7 +3414,10 @@ var init_pid_monitor = __esm({
|
|
|
3393
3414
|
function normalizeToolName(name) {
|
|
3394
3415
|
return IDE_TO_CLI[name] ?? name;
|
|
3395
3416
|
}
|
|
3396
|
-
|
|
3417
|
+
function normalizeAgentType(name) {
|
|
3418
|
+
return AGENT_TYPE_ALIASES[name] ?? name;
|
|
3419
|
+
}
|
|
3420
|
+
var IDE_TO_CLI, AGENT_TYPE_ALIASES;
|
|
3397
3421
|
var init_tool_names = __esm({
|
|
3398
3422
|
"src/utils/tool-names.ts"() {
|
|
3399
3423
|
"use strict";
|
|
@@ -3408,6 +3432,79 @@ var init_tool_names = __esm({
|
|
|
3408
3432
|
read_file: "Read",
|
|
3409
3433
|
task: "Task"
|
|
3410
3434
|
};
|
|
3435
|
+
AGENT_TYPE_ALIASES = {
|
|
3436
|
+
tcodex: "codex",
|
|
3437
|
+
"codex-internal": "codex",
|
|
3438
|
+
tclaude: "claude",
|
|
3439
|
+
"claude-internal": "claude"
|
|
3440
|
+
};
|
|
3441
|
+
}
|
|
3442
|
+
});
|
|
3443
|
+
|
|
3444
|
+
// src/utils/redact.ts
|
|
3445
|
+
function collectEnvSecrets(env = process.env) {
|
|
3446
|
+
const secrets = {};
|
|
3447
|
+
for (const [name, value] of Object.entries(env)) {
|
|
3448
|
+
if (typeof value !== "string" || value.length < MIN_ENV_VALUE_LENGTH) continue;
|
|
3449
|
+
const upper = name.toUpperCase();
|
|
3450
|
+
if (SECRET_KEY_HINTS.some((hint) => upper.includes(hint))) {
|
|
3451
|
+
secrets[name] = value;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
return secrets;
|
|
3455
|
+
}
|
|
3456
|
+
function redact(text, options = {}) {
|
|
3457
|
+
if (!text) return text;
|
|
3458
|
+
let out = text;
|
|
3459
|
+
const envSecrets = options.envSecrets ?? {};
|
|
3460
|
+
const entries = Object.entries(envSecrets).sort((a, b) => b[1].length - a[1].length);
|
|
3461
|
+
for (const [label, value] of entries) {
|
|
3462
|
+
if (value && out.includes(value)) {
|
|
3463
|
+
out = out.split(value).join(PLACEHOLDER(label));
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
if (options.patterns !== false) {
|
|
3467
|
+
for (const [label, pattern] of SECRET_PATTERNS) {
|
|
3468
|
+
out = out.replace(pattern, () => PLACEHOLDER(label));
|
|
3469
|
+
}
|
|
3470
|
+
out = out.replace(KV_PATTERN, (_m, key, sep) => key + sep + PLACEHOLDER("kv"));
|
|
3471
|
+
out = out.replace(BEARER_PATTERN, (_m, scheme) => scheme + PLACEHOLDER("authz"));
|
|
3472
|
+
out = out.replace(
|
|
3473
|
+
CONNECTION_STRING_PATTERN,
|
|
3474
|
+
(_m, prefix, _pw, at) => prefix + PLACEHOLDER("conn") + at
|
|
3475
|
+
);
|
|
3476
|
+
}
|
|
3477
|
+
return out;
|
|
3478
|
+
}
|
|
3479
|
+
function redactWithEnv(text, env = process.env) {
|
|
3480
|
+
return redact(text, { envSecrets: collectEnvSecrets(env) });
|
|
3481
|
+
}
|
|
3482
|
+
var PLACEHOLDER, SECRET_KEY_HINTS, MIN_ENV_VALUE_LENGTH, SECRET_PATTERNS, KV_PATTERN, BEARER_PATTERN, CONNECTION_STRING_PATTERN;
|
|
3483
|
+
var init_redact = __esm({
|
|
3484
|
+
"src/utils/redact.ts"() {
|
|
3485
|
+
"use strict";
|
|
3486
|
+
PLACEHOLDER = (label) => `<REDACTED:${label}>`;
|
|
3487
|
+
SECRET_KEY_HINTS = ["TOKEN", "SECRET", "KEY", "PASSWORD", "PASSWD", "_PAT", "CREDENTIAL"];
|
|
3488
|
+
MIN_ENV_VALUE_LENGTH = 8;
|
|
3489
|
+
SECRET_PATTERNS = [
|
|
3490
|
+
["pem", /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g],
|
|
3491
|
+
["anthropic", /\bsk-ant-[A-Za-z0-9_-]{20,}/g],
|
|
3492
|
+
["openai", /\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}/g],
|
|
3493
|
+
["gh_pat", /\bgithub_pat_[A-Za-z0-9_]{22,}/g],
|
|
3494
|
+
["gh_tok", /\bgh[pousr]_[A-Za-z0-9]{20,}/g],
|
|
3495
|
+
["aws", /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA)[0-9A-Z]{16}\b/g],
|
|
3496
|
+
["slack", /\bxox[baprs]-[A-Za-z0-9-]{10,}/g],
|
|
3497
|
+
["slackhook", /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/]{20,}/g],
|
|
3498
|
+
["google", /\bAIza[0-9A-Za-z_-]{35}\b/g],
|
|
3499
|
+
["gcp_oauth", /\bya29\.[0-9A-Za-z_-]{20,}/g],
|
|
3500
|
+
["stripe", /\b[rs]k_(?:live|test)_[A-Za-z0-9]{16,}/g],
|
|
3501
|
+
["jwt", /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g],
|
|
3502
|
+
["sendgrid", /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g],
|
|
3503
|
+
["provider", /\b(?:xai-|gsk_|pplx-|sk-or-v1-|hf_|dop_v1_|glpat-|npm_|shpat_|tfp_|nvapi-|r8_)[A-Za-z0-9_-]{16,}/g]
|
|
3504
|
+
];
|
|
3505
|
+
KV_PATTERN = /(secret|token|api[_-]?key|access[_-]?key|client[_-]?secret|password|passwd|auth[_-]?token|private[_-]?key)(["']?\s*[:=]\s*["']?)([A-Za-z0-9._\-/+=]{16,})/gi;
|
|
3506
|
+
BEARER_PATTERN = /\b(bearer\s+)([A-Za-z0-9._-]{16,})/gi;
|
|
3507
|
+
CONNECTION_STRING_PATTERN = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s:/@]+:)([^\s/@]{6,})(@)/g;
|
|
3411
3508
|
}
|
|
3412
3509
|
});
|
|
3413
3510
|
|
|
@@ -3463,7 +3560,7 @@ async function readLastAssistantOutput(transcriptPath) {
|
|
|
3463
3560
|
} catch {
|
|
3464
3561
|
}
|
|
3465
3562
|
}
|
|
3466
|
-
return lastAssistantText.slice(0, STOPPED_OUTPUT_MAX_CHARS);
|
|
3563
|
+
return redactWithEnv(lastAssistantText).slice(0, STOPPED_OUTPUT_MAX_CHARS);
|
|
3467
3564
|
} finally {
|
|
3468
3565
|
await fh.close();
|
|
3469
3566
|
}
|
|
@@ -3475,6 +3572,7 @@ async function readLastAssistantOutput(transcriptPath) {
|
|
|
3475
3572
|
async function scanTranscriptStop(transcriptPath) {
|
|
3476
3573
|
let interrupt = 0;
|
|
3477
3574
|
let toolReject = 0;
|
|
3575
|
+
let toolError = 0;
|
|
3478
3576
|
let prompts = 0;
|
|
3479
3577
|
const tokens = emptyTokenUsage();
|
|
3480
3578
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
@@ -3484,10 +3582,10 @@ async function scanTranscriptStop(transcriptPath) {
|
|
|
3484
3582
|
}
|
|
3485
3583
|
try {
|
|
3486
3584
|
const stat6 = await fs7.promises.stat(transcriptPath);
|
|
3487
|
-
if (stat6.size === 0) return { interrupt, toolReject, tokens, prompts };
|
|
3585
|
+
if (stat6.size === 0) return { interrupt, toolReject, toolError, tokens, prompts };
|
|
3488
3586
|
if (stat6.size > INTERVENTION_SCAN_MAX_BYTES) {
|
|
3489
3587
|
log.warn(`dashboard: transcript too large to scan (${stat6.size} bytes)`);
|
|
3490
|
-
return { interrupt, toolReject, tokens, prompts };
|
|
3588
|
+
return { interrupt, toolReject, toolError, tokens, prompts };
|
|
3491
3589
|
}
|
|
3492
3590
|
const rl = readline.createInterface({
|
|
3493
3591
|
input: fs7.createReadStream(transcriptPath, { encoding: "utf-8" }),
|
|
@@ -3538,6 +3636,8 @@ async function scanTranscriptStop(transcriptPath) {
|
|
|
3538
3636
|
const text = typeof item.content === "string" ? item.content : Array.isArray(item.content) ? item.content.map((c) => typeof c?.text === "string" ? c.text : "").join(" ") : "";
|
|
3539
3637
|
if (TRANSCRIPT_REJECT_MARKERS.some((m) => text.includes(m))) {
|
|
3540
3638
|
toolReject++;
|
|
3639
|
+
} else {
|
|
3640
|
+
toolError++;
|
|
3541
3641
|
}
|
|
3542
3642
|
}
|
|
3543
3643
|
}
|
|
@@ -3546,7 +3646,7 @@ async function scanTranscriptStop(transcriptPath) {
|
|
|
3546
3646
|
} catch (e) {
|
|
3547
3647
|
log.warn(`dashboard: failed to scan transcript: ${e.message}`);
|
|
3548
3648
|
}
|
|
3549
|
-
return { interrupt, toolReject, tokens, prompts };
|
|
3649
|
+
return { interrupt, toolReject, toolError, tokens, prompts };
|
|
3550
3650
|
}
|
|
3551
3651
|
async function readCodebuddyIndexOnce(transcriptPath) {
|
|
3552
3652
|
try {
|
|
@@ -3563,7 +3663,7 @@ async function readCodebuddyIndexOnce(transcriptPath) {
|
|
|
3563
3663
|
tokens.output += toNum(usage.outputTokens);
|
|
3564
3664
|
}
|
|
3565
3665
|
const prompts = Array.isArray(data.messages) ? data.messages.filter((m) => m?.role === "user").length : 0;
|
|
3566
|
-
return { interrupt: 0, toolReject: 0, tokens, prompts };
|
|
3666
|
+
return { interrupt: 0, toolReject: 0, toolError: 0, tokens, prompts };
|
|
3567
3667
|
} catch (e) {
|
|
3568
3668
|
log.warn(`dashboard: failed to scan CodeBuddy index: ${e.message}`);
|
|
3569
3669
|
return null;
|
|
@@ -3590,8 +3690,8 @@ function toNum(v) {
|
|
|
3590
3690
|
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
3591
3691
|
}
|
|
3592
3692
|
async function countInterventions(transcriptPath) {
|
|
3593
|
-
const { interrupt, toolReject } = await scanTranscriptStop(transcriptPath);
|
|
3594
|
-
return { interrupt, toolReject };
|
|
3693
|
+
const { interrupt, toolReject, toolError } = await scanTranscriptStop(transcriptPath);
|
|
3694
|
+
return { interrupt, toolReject, toolError };
|
|
3595
3695
|
}
|
|
3596
3696
|
function isCorrectionPrompt(text) {
|
|
3597
3697
|
if (!text) return false;
|
|
@@ -3664,8 +3764,12 @@ async function parseHookEvent(raw, tool) {
|
|
|
3664
3764
|
event.stoppedOutput = output;
|
|
3665
3765
|
}
|
|
3666
3766
|
const scan = await scanTranscriptStop(hookData.transcript_path);
|
|
3667
|
-
if (scan.interrupt > 0 || scan.toolReject > 0) {
|
|
3668
|
-
event.interventions = {
|
|
3767
|
+
if (scan.interrupt > 0 || scan.toolReject > 0 || scan.toolError > 0) {
|
|
3768
|
+
event.interventions = {
|
|
3769
|
+
interrupt: scan.interrupt,
|
|
3770
|
+
toolReject: scan.toolReject,
|
|
3771
|
+
toolError: scan.toolError
|
|
3772
|
+
};
|
|
3669
3773
|
}
|
|
3670
3774
|
if (scan.tokens.input > 0 || scan.tokens.output > 0 || scan.tokens.cacheRead > 0 || scan.tokens.cacheCreation > 0) {
|
|
3671
3775
|
event.tokens = scan.tokens;
|
|
@@ -3896,6 +4000,7 @@ var init_dashboard_collector = __esm({
|
|
|
3896
4000
|
init_fs();
|
|
3897
4001
|
init_pid_monitor();
|
|
3898
4002
|
init_tool_names();
|
|
4003
|
+
init_redact();
|
|
3899
4004
|
init_types();
|
|
3900
4005
|
TRANSCRIPT_TAIL_BYTES = 10240;
|
|
3901
4006
|
STOPPED_OUTPUT_MAX_CHARS = 500;
|
|
@@ -4159,6 +4264,145 @@ var init_http_log = __esm({
|
|
|
4159
4264
|
}
|
|
4160
4265
|
});
|
|
4161
4266
|
|
|
4267
|
+
// src/plugin-lifecycle.ts
|
|
4268
|
+
function makeReadyState(d, deps) {
|
|
4269
|
+
return {
|
|
4270
|
+
slug: d.slug,
|
|
4271
|
+
version: d.version,
|
|
4272
|
+
ready: true,
|
|
4273
|
+
installedAt: new Date(deps.now()).toISOString(),
|
|
4274
|
+
uninstallCmd: d.uninstallCmd,
|
|
4275
|
+
lastAttemptAt: void 0,
|
|
4276
|
+
lastError: void 0
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
async function reconcilePlugins(desired, deps) {
|
|
4280
|
+
const local = await deps.readPlugins();
|
|
4281
|
+
const desiredSlugs = new Set(desired.map((d) => d.slug));
|
|
4282
|
+
for (const d of desired) {
|
|
4283
|
+
const e = local[d.slug];
|
|
4284
|
+
try {
|
|
4285
|
+
if (!e || !e.ready) {
|
|
4286
|
+
if (e?.lastAttemptAt && deps.now() - Date.parse(e.lastAttemptAt) < FAILURE_COOLDOWN_MS) {
|
|
4287
|
+
deps.log.debug(`plugin ${d.slug}: in failure cooldown, skip`);
|
|
4288
|
+
continue;
|
|
4289
|
+
}
|
|
4290
|
+
deps.log.debug(`plugin ${d.slug}: installing${d.version ? " version " + d.version : ""}`);
|
|
4291
|
+
await deps.execCommand(d.installCmd, INSTALL_TIMEOUT_MS);
|
|
4292
|
+
await deps.execCommand(d.runCmd, RUN_TIMEOUT_MS);
|
|
4293
|
+
await deps.mutatePlugins((m) => {
|
|
4294
|
+
m[d.slug] = makeReadyState(d, deps);
|
|
4295
|
+
});
|
|
4296
|
+
deps.log.debug(`plugin ${d.slug}: installed and setup complete`);
|
|
4297
|
+
} else if (d.version !== void 0 && e.version !== d.version) {
|
|
4298
|
+
deps.log.debug(`plugin ${d.slug}: updating ${e.version ?? "(unknown)"} -> ${d.version}`);
|
|
4299
|
+
await deps.execCommand(d.updateCmd ?? d.installCmd, UPDATE_TIMEOUT_MS);
|
|
4300
|
+
await deps.execCommand(d.runCmd, RUN_TIMEOUT_MS);
|
|
4301
|
+
await deps.mutatePlugins((m) => {
|
|
4302
|
+
m[d.slug] = makeReadyState(d, deps);
|
|
4303
|
+
});
|
|
4304
|
+
deps.log.debug(`plugin ${d.slug}: updated to version ${d.version}`);
|
|
4305
|
+
}
|
|
4306
|
+
} catch (err) {
|
|
4307
|
+
const msg = err.message;
|
|
4308
|
+
deps.log.warn(`plugin ${d.slug} reconcile failed: ${msg}`);
|
|
4309
|
+
await deps.mutatePlugins((m) => {
|
|
4310
|
+
const prev = m[d.slug];
|
|
4311
|
+
const base = prev ?? {
|
|
4312
|
+
slug: d.slug,
|
|
4313
|
+
version: d.version,
|
|
4314
|
+
ready: false,
|
|
4315
|
+
uninstallCmd: d.uninstallCmd
|
|
4316
|
+
};
|
|
4317
|
+
if (!prev || !prev.ready) {
|
|
4318
|
+
base.ready = false;
|
|
4319
|
+
}
|
|
4320
|
+
base.lastAttemptAt = new Date(deps.now()).toISOString();
|
|
4321
|
+
base.lastError = msg;
|
|
4322
|
+
m[d.slug] = base;
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
for (const slug of Object.keys(local)) {
|
|
4327
|
+
if (desiredSlugs.has(slug)) continue;
|
|
4328
|
+
const e = local[slug];
|
|
4329
|
+
try {
|
|
4330
|
+
deps.log.debug(`plugin ${slug}: uninstalling (removed from desired list)`);
|
|
4331
|
+
await deps.execCommand(e.uninstallCmd, UNINSTALL_TIMEOUT_MS);
|
|
4332
|
+
await deps.mutatePlugins((m) => {
|
|
4333
|
+
delete m[slug];
|
|
4334
|
+
});
|
|
4335
|
+
deps.log.debug(`plugin ${slug}: uninstalled`);
|
|
4336
|
+
} catch (err) {
|
|
4337
|
+
deps.log.warn(`plugin ${slug} uninstall failed: ${err.message}`);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
async function teardownAllPlugins(deps) {
|
|
4342
|
+
const plugins = await deps.readPlugins();
|
|
4343
|
+
for (const [slug, state] of Object.entries(plugins)) {
|
|
4344
|
+
try {
|
|
4345
|
+
deps.log.debug(`plugin ${slug}: tearing down`);
|
|
4346
|
+
await deps.execCommand(state.uninstallCmd, UNINSTALL_TIMEOUT_MS);
|
|
4347
|
+
await deps.mutatePlugins((m) => {
|
|
4348
|
+
delete m[slug];
|
|
4349
|
+
});
|
|
4350
|
+
deps.log.debug(`plugin ${slug}: torn down`);
|
|
4351
|
+
} catch (err) {
|
|
4352
|
+
deps.log.warn(`plugin ${slug} teardown failed: ${err.message}`);
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
function parseGetConfig(resp) {
|
|
4357
|
+
const vars = {};
|
|
4358
|
+
const plugins = [];
|
|
4359
|
+
if (!resp || typeof resp !== "object") return { vars, plugins };
|
|
4360
|
+
for (const [key, section] of Object.entries(resp)) {
|
|
4361
|
+
if (!section || typeof section !== "object") continue;
|
|
4362
|
+
const s = section;
|
|
4363
|
+
const isStr = (v) => typeof v === "string" && v.length > 0;
|
|
4364
|
+
if (!isStr(s["install_cmd"]) || !isStr(s["run_cmd"]) || !isStr(s["uninstall_cmd"])) continue;
|
|
4365
|
+
for (const f of ["endpoint", "topic_id", "secret_id", "secret_key", "user_name"]) {
|
|
4366
|
+
if (isStr(s[f])) vars[f] = s[f];
|
|
4367
|
+
}
|
|
4368
|
+
if (typeof s["user_id"] === "number") vars["user_id"] = String(s["user_id"]);
|
|
4369
|
+
else if (isStr(s["user_id"])) vars["user_id"] = s["user_id"];
|
|
4370
|
+
plugins.push({
|
|
4371
|
+
slug: key,
|
|
4372
|
+
version: isStr(s["version"]) ? s["version"] : void 0,
|
|
4373
|
+
installCmd: s["install_cmd"],
|
|
4374
|
+
updateCmd: isStr(s["update_cmd"]) ? s["update_cmd"] : void 0,
|
|
4375
|
+
uninstallCmd: s["uninstall_cmd"],
|
|
4376
|
+
runCmd: s["run_cmd"]
|
|
4377
|
+
});
|
|
4378
|
+
}
|
|
4379
|
+
return { vars, plugins };
|
|
4380
|
+
}
|
|
4381
|
+
function substituteVars(cmd, vars) {
|
|
4382
|
+
return cmd.replace(
|
|
4383
|
+
/\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g,
|
|
4384
|
+
(match, key) => key in vars ? vars[key] : match
|
|
4385
|
+
);
|
|
4386
|
+
}
|
|
4387
|
+
function unresolvedPlaceholders(cmd) {
|
|
4388
|
+
const found = /* @__PURE__ */ new Set();
|
|
4389
|
+
for (const match of cmd.matchAll(/\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g)) {
|
|
4390
|
+
found.add(match[1]);
|
|
4391
|
+
}
|
|
4392
|
+
return [...found];
|
|
4393
|
+
}
|
|
4394
|
+
var INSTALL_TIMEOUT_MS, UPDATE_TIMEOUT_MS, UNINSTALL_TIMEOUT_MS, RUN_TIMEOUT_MS, FAILURE_COOLDOWN_MS;
|
|
4395
|
+
var init_plugin_lifecycle = __esm({
|
|
4396
|
+
"src/plugin-lifecycle.ts"() {
|
|
4397
|
+
"use strict";
|
|
4398
|
+
INSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4399
|
+
UPDATE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4400
|
+
UNINSTALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4401
|
+
RUN_TIMEOUT_MS = 60 * 1e3;
|
|
4402
|
+
FAILURE_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
4403
|
+
}
|
|
4404
|
+
});
|
|
4405
|
+
|
|
4162
4406
|
// src/utils/prompt.ts
|
|
4163
4407
|
var prompt_exports = {};
|
|
4164
4408
|
__export(prompt_exports, {
|
|
@@ -4284,17 +4528,21 @@ var init_claudemd = __esm({
|
|
|
4284
4528
|
// src/local-agent.ts
|
|
4285
4529
|
var local_agent_exports = {};
|
|
4286
4530
|
__export(local_agent_exports, {
|
|
4531
|
+
DEFAULT_ROUTES: () => DEFAULT_ROUTES,
|
|
4287
4532
|
bindCurrentProject: () => bindCurrentProject,
|
|
4288
|
-
|
|
4533
|
+
bindWorkspaceToProject: () => bindWorkspaceToProject,
|
|
4289
4534
|
buildReportPayload: () => buildReportPayload,
|
|
4290
4535
|
describeLocalAgent: () => describeLocalAgent,
|
|
4291
|
-
|
|
4536
|
+
fetchUserProjects: () => fetchUserProjects,
|
|
4292
4537
|
initLocalAgentHttp: () => initLocalAgentHttp,
|
|
4293
4538
|
loadLocalAgentConfig: () => loadLocalAgentConfig,
|
|
4294
4539
|
pullLocalAgentForCwd: () => pullLocalAgentForCwd,
|
|
4295
4540
|
removeLocalAgentHttp: () => removeLocalAgentHttp,
|
|
4296
4541
|
reportAndSyncFromHook: () => reportAndSyncFromHook,
|
|
4297
4542
|
reportAndSyncLocalAgent: () => reportAndSyncLocalAgent,
|
|
4543
|
+
resolveRoute: () => resolveRoute,
|
|
4544
|
+
runPluginReconcileWorker: () => runPluginReconcileWorker,
|
|
4545
|
+
teardownLocalAgentPlugins: () => teardownLocalAgentPlugins,
|
|
4298
4546
|
writeTokenFile: () => writeTokenFile
|
|
4299
4547
|
});
|
|
4300
4548
|
import fs10 from "fs";
|
|
@@ -4335,6 +4583,15 @@ function compileClaudemdBlock(contents) {
|
|
|
4335
4583
|
function normalizeEndpoint(endpoint) {
|
|
4336
4584
|
return endpoint.trim().replace(/\/+$/, "");
|
|
4337
4585
|
}
|
|
4586
|
+
function normalizeRoute(route) {
|
|
4587
|
+
const trimmed = route.trim();
|
|
4588
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
4589
|
+
}
|
|
4590
|
+
function resolveRoute(config, name) {
|
|
4591
|
+
const override = config.routes?.[name];
|
|
4592
|
+
if (override && override.trim()) return normalizeRoute(override);
|
|
4593
|
+
return DEFAULT_ROUTES[name];
|
|
4594
|
+
}
|
|
4338
4595
|
function resolveAgentInstallPath(agentType) {
|
|
4339
4596
|
const home = process.env.HOME ?? "";
|
|
4340
4597
|
const skillsRel = createLocalAgentTeamConfig("").toolPaths[agentType]?.skills;
|
|
@@ -4364,6 +4621,46 @@ async function loadManifest() {
|
|
|
4364
4621
|
async function saveManifest(manifest) {
|
|
4365
4622
|
await writeJson(getManifestPath(), manifest);
|
|
4366
4623
|
}
|
|
4624
|
+
function getPluginStatePath() {
|
|
4625
|
+
return path18.join(getLocalAgentHome(), "plugins.json");
|
|
4626
|
+
}
|
|
4627
|
+
async function readPluginState() {
|
|
4628
|
+
return await readJson(getPluginStatePath()) ?? {};
|
|
4629
|
+
}
|
|
4630
|
+
async function withPluginStateLock(mutate) {
|
|
4631
|
+
const statePath = getPluginStatePath();
|
|
4632
|
+
const lockPath = `${statePath}.lock`;
|
|
4633
|
+
await ensureDir(path18.dirname(lockPath));
|
|
4634
|
+
const deadline = Date.now() + 5e3;
|
|
4635
|
+
let acquired = false;
|
|
4636
|
+
while (Date.now() <= deadline) {
|
|
4637
|
+
try {
|
|
4638
|
+
const fd = await fs10.promises.open(lockPath, "wx");
|
|
4639
|
+
await fd.close();
|
|
4640
|
+
acquired = true;
|
|
4641
|
+
break;
|
|
4642
|
+
} catch (e) {
|
|
4643
|
+
if (e.code !== "EEXIST") throw e;
|
|
4644
|
+
try {
|
|
4645
|
+
const st = await fs10.promises.stat(lockPath);
|
|
4646
|
+
if (Date.now() - st.mtimeMs > 3e4) {
|
|
4647
|
+
await fs10.promises.rm(lockPath, { force: true });
|
|
4648
|
+
continue;
|
|
4649
|
+
}
|
|
4650
|
+
} catch {
|
|
4651
|
+
}
|
|
4652
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
if (!acquired) throw new Error("could not acquire plugin-state lock");
|
|
4656
|
+
try {
|
|
4657
|
+
const m = await readPluginState();
|
|
4658
|
+
mutate(m);
|
|
4659
|
+
await writeJson(statePath, m);
|
|
4660
|
+
} finally {
|
|
4661
|
+
await fs10.promises.rm(lockPath, { force: true });
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4367
4664
|
function getManifestScope(manifest, scope, workspacePath) {
|
|
4368
4665
|
const key = scopeKey(scope, workspacePath);
|
|
4369
4666
|
manifest.scopes[key] ??= emptyManifestScope();
|
|
@@ -4372,11 +4669,17 @@ function getManifestScope(manifest, scope, workspacePath) {
|
|
|
4372
4669
|
async function loadLocalAgentConfig() {
|
|
4373
4670
|
const fileConfig = await readJson(getConfigPath2());
|
|
4374
4671
|
if (fileConfig?.endpoint) {
|
|
4375
|
-
|
|
4672
|
+
const config = {
|
|
4376
4673
|
...fileConfig,
|
|
4377
4674
|
endpoint: normalizeEndpoint(fileConfig.endpoint),
|
|
4378
4675
|
workspaceBindings: fileConfig.workspaceBindings ?? {}
|
|
4379
4676
|
};
|
|
4677
|
+
for (const [wsPath, binding] of Object.entries(config.workspaceBindings)) {
|
|
4678
|
+
if ("groupId" in binding && !("projectId" in binding)) {
|
|
4679
|
+
delete config.workspaceBindings[wsPath];
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
return config;
|
|
4380
4683
|
}
|
|
4381
4684
|
const envEndpoint = process.env.TEAMAI_HTTP_ENDPOINT ?? process.env.TEAMAI_ENDPOINT ?? process.env.TEAMAI_API_BASE_URL;
|
|
4382
4685
|
if (!envEndpoint) return null;
|
|
@@ -4439,9 +4742,9 @@ function authHeaders(config, json = true) {
|
|
|
4439
4742
|
}
|
|
4440
4743
|
return headers;
|
|
4441
4744
|
}
|
|
4442
|
-
async function localAgentFetch(config, tag, route, init2) {
|
|
4745
|
+
async function localAgentFetch(config, tag, route, init2, opts) {
|
|
4443
4746
|
const method = init2?.method ?? "GET";
|
|
4444
|
-
const url = `${config.endpoint}${route}`;
|
|
4747
|
+
const url = `${config.endpoint}${resolveRoute(config, route)}`;
|
|
4445
4748
|
const headers = {
|
|
4446
4749
|
...authHeaders(config, init2?.body !== void 0),
|
|
4447
4750
|
...init2?.headers ?? {}
|
|
@@ -4457,7 +4760,7 @@ async function localAgentFetch(config, tag, route, init2) {
|
|
|
4457
4760
|
body = text;
|
|
4458
4761
|
}
|
|
4459
4762
|
}
|
|
4460
|
-
logHttpResponse(tag, method, url, response.status, response.statusText, body);
|
|
4763
|
+
logHttpResponse(tag, method, url, response.status, response.statusText, opts?.redactResponseLog ? "<redacted>" : body);
|
|
4461
4764
|
if (!response.ok) {
|
|
4462
4765
|
const message = typeof body === "object" && body && "error" in body ? String(body.error) : text || `${response.status} ${response.statusText}`;
|
|
4463
4766
|
throw new Error(message);
|
|
@@ -4475,14 +4778,173 @@ async function appendErrorLog(entry) {
|
|
|
4475
4778
|
} catch {
|
|
4476
4779
|
}
|
|
4477
4780
|
}
|
|
4478
|
-
async function
|
|
4781
|
+
async function fetchUserProjects(config) {
|
|
4479
4782
|
const response = await localAgentFetch(
|
|
4480
4783
|
config,
|
|
4481
4784
|
localAgentTag({}),
|
|
4482
|
-
"
|
|
4785
|
+
"projects",
|
|
4483
4786
|
{ method: "GET" }
|
|
4484
4787
|
);
|
|
4485
|
-
return response.
|
|
4788
|
+
return response.projects ?? [];
|
|
4789
|
+
}
|
|
4790
|
+
function redactSecrets(s) {
|
|
4791
|
+
const names = "secret[_-]?(?:key|id)|api[_-]?key|access[_-]?token|token|password|passwd|pwd";
|
|
4792
|
+
return s.replace(new RegExp(`(--(?:${names})[= ]+)\\S+`, "gi"), "$1***").replace(new RegExp(`((?:${names})"?\\s*[:=]\\s*"?)[^"\\s,}]+`, "gi"), "$1***").replace(/(bearer\s+)[\w.\-]+/gi, "$1***");
|
|
4793
|
+
}
|
|
4794
|
+
async function execPluginCommand(cmd, timeoutMs) {
|
|
4795
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
4796
|
+
await new Promise((resolve, reject) => {
|
|
4797
|
+
const child = process.platform === "win32" ? spawn3("cmd", ["/c", cmd], { windowsHide: true, stdio: ["ignore", "ignore", "pipe"] }) : spawn3("/bin/sh", ["-lc", cmd], { stdio: ["ignore", "ignore", "pipe"] });
|
|
4798
|
+
let stderr = "";
|
|
4799
|
+
let settled = false;
|
|
4800
|
+
let timer;
|
|
4801
|
+
child.stderr?.on("data", (d) => {
|
|
4802
|
+
stderr += d.toString();
|
|
4803
|
+
if (stderr.length > 8192) stderr = stderr.slice(-8192);
|
|
4804
|
+
});
|
|
4805
|
+
const detachStderr = () => {
|
|
4806
|
+
child.stderr?.removeAllListeners("data");
|
|
4807
|
+
child.stderr?.resume();
|
|
4808
|
+
child.stderr?.unref?.();
|
|
4809
|
+
};
|
|
4810
|
+
const finish = (fn) => {
|
|
4811
|
+
if (settled) return;
|
|
4812
|
+
settled = true;
|
|
4813
|
+
clearTimeout(timer);
|
|
4814
|
+
detachStderr();
|
|
4815
|
+
child.unref();
|
|
4816
|
+
fn();
|
|
4817
|
+
};
|
|
4818
|
+
timer = setTimeout(() => {
|
|
4819
|
+
child.kill("SIGKILL");
|
|
4820
|
+
finish(() => reject(new Error(`command timed out after ${timeoutMs}ms`)));
|
|
4821
|
+
}, timeoutMs);
|
|
4822
|
+
child.on("error", (e) => finish(() => reject(e)));
|
|
4823
|
+
child.on(
|
|
4824
|
+
"exit",
|
|
4825
|
+
(code, signal) => finish(() => {
|
|
4826
|
+
if (signal) return reject(new Error(`command killed by ${signal}`));
|
|
4827
|
+
if (code === 0) return resolve();
|
|
4828
|
+
const tail = stderr ? " :: " + redactSecrets(stderr.slice(0, 200).trim()) : "";
|
|
4829
|
+
reject(new Error(`command failed (exit ${code})${tail}`));
|
|
4830
|
+
})
|
|
4831
|
+
);
|
|
4832
|
+
});
|
|
4833
|
+
}
|
|
4834
|
+
async function fetchPluginConfig(config, tag) {
|
|
4835
|
+
return localAgentFetch(config, tag, "getConfig", { method: "GET" }, { redactResponseLog: true });
|
|
4836
|
+
}
|
|
4837
|
+
function getPluginPullStatePath() {
|
|
4838
|
+
return path18.join(getLocalAgentHome(), "plugin-pull.json");
|
|
4839
|
+
}
|
|
4840
|
+
function buildReconcileDeps(config, tag) {
|
|
4841
|
+
return {
|
|
4842
|
+
readPlugins: () => readPluginState(),
|
|
4843
|
+
mutatePlugins: (fn) => withPluginStateLock(fn),
|
|
4844
|
+
execCommand: (cmd, t) => execPluginCommand(cmd, t),
|
|
4845
|
+
now: () => Date.now(),
|
|
4846
|
+
log: {
|
|
4847
|
+
debug: (msg) => log.debug(`${tag} ${msg}`),
|
|
4848
|
+
// The reconcile worker runs detached (stdio: 'ignore'), so console-only log.warn
|
|
4849
|
+
// output is discarded. Mirror warnings to debug.log so failures are traceable.
|
|
4850
|
+
warn: (msg) => {
|
|
4851
|
+
log.warn(`${tag} ${msg}`);
|
|
4852
|
+
log.debug(`${tag} WARN: ${msg}`);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
};
|
|
4856
|
+
}
|
|
4857
|
+
async function maybeReconcilePlugins(context) {
|
|
4858
|
+
try {
|
|
4859
|
+
const state = await readJson(getPluginPullStatePath()) ?? {};
|
|
4860
|
+
const now = Date.now();
|
|
4861
|
+
if (state.lastPullAt && now - state.lastPullAt < PLUGIN_PULL_INTERVAL_MS) return;
|
|
4862
|
+
if (state.lastFailAt && now - state.lastFailAt < PLUGIN_FAIL_BACKOFF_MS) return;
|
|
4863
|
+
const tool = context.tool ?? "workbuddy";
|
|
4864
|
+
const localAgentId = `${tool}-${resolveLocalAgentId(context)}`;
|
|
4865
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
4866
|
+
if (!process.argv[1]) {
|
|
4867
|
+
log.debug("[local-agent] plugin reconcile: no CLI entrypoint (argv[1]), skipping");
|
|
4868
|
+
return;
|
|
4869
|
+
}
|
|
4870
|
+
const child = spawn3(
|
|
4871
|
+
process.execPath,
|
|
4872
|
+
[process.argv[1], "source", "reconcile-plugins"],
|
|
4873
|
+
{ detached: true, stdio: "ignore", env: { ...process.env, TEAMAI_PLUGIN_LOCAL_AGENT_ID: localAgentId } }
|
|
4874
|
+
);
|
|
4875
|
+
child.unref();
|
|
4876
|
+
} catch (e) {
|
|
4877
|
+
log.debug(`[local-agent] plugin reconcile spawn skipped: ${e.message}`);
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
async function runPluginReconcileWorker() {
|
|
4881
|
+
const config = await loadLocalAgentConfig();
|
|
4882
|
+
if (!config) return;
|
|
4883
|
+
const lockPath = path18.join(getLocalAgentHome(), "plugin-reconcile.lock");
|
|
4884
|
+
await ensureDir(path18.dirname(lockPath));
|
|
4885
|
+
let acquired = false;
|
|
4886
|
+
try {
|
|
4887
|
+
try {
|
|
4888
|
+
const fd = await fs10.promises.open(lockPath, "wx");
|
|
4889
|
+
await fd.close();
|
|
4890
|
+
acquired = true;
|
|
4891
|
+
} catch (e) {
|
|
4892
|
+
if (e.code !== "EEXIST") throw e;
|
|
4893
|
+
try {
|
|
4894
|
+
const st = await fs10.promises.stat(lockPath);
|
|
4895
|
+
if (Date.now() - st.mtimeMs > 30 * 60 * 1e3) {
|
|
4896
|
+
await fs10.promises.rm(lockPath, { force: true });
|
|
4897
|
+
const fd = await fs10.promises.open(lockPath, "wx");
|
|
4898
|
+
await fd.close();
|
|
4899
|
+
acquired = true;
|
|
4900
|
+
}
|
|
4901
|
+
} catch {
|
|
4902
|
+
}
|
|
4903
|
+
if (!acquired) return;
|
|
4904
|
+
}
|
|
4905
|
+
const tag = "[local-agent] [plugin-reconcile]";
|
|
4906
|
+
const statePath = getPluginPullStatePath();
|
|
4907
|
+
try {
|
|
4908
|
+
const resp = await fetchPluginConfig(config, tag);
|
|
4909
|
+
const { vars, plugins } = parseGetConfig(resp);
|
|
4910
|
+
const declaredSlugs = plugins.length ? ` [${plugins.map((p) => p.slug).join(", ")}]` : "";
|
|
4911
|
+
log.debug(`${tag} get-config: ${plugins.length} plugin(s) declared${declaredSlugs}`);
|
|
4912
|
+
const laid = process.env.TEAMAI_PLUGIN_LOCAL_AGENT_ID;
|
|
4913
|
+
if (!laid) log.debug(tag + " no local_agent_id in env; plugins needing it will be skipped");
|
|
4914
|
+
const allVars = { ...vars, ...laid ? { local_agent_id: laid } : {} };
|
|
4915
|
+
const resolved = [];
|
|
4916
|
+
for (const p of plugins) {
|
|
4917
|
+
const rp = {
|
|
4918
|
+
...p,
|
|
4919
|
+
installCmd: substituteVars(p.installCmd, allVars),
|
|
4920
|
+
updateCmd: p.updateCmd ? substituteVars(p.updateCmd, allVars) : void 0,
|
|
4921
|
+
uninstallCmd: substituteVars(p.uninstallCmd, allVars),
|
|
4922
|
+
runCmd: substituteVars(p.runCmd, allVars)
|
|
4923
|
+
};
|
|
4924
|
+
const missing = [.../* @__PURE__ */ new Set([
|
|
4925
|
+
...unresolvedPlaceholders(rp.installCmd),
|
|
4926
|
+
...unresolvedPlaceholders(rp.runCmd),
|
|
4927
|
+
...unresolvedPlaceholders(rp.uninstallCmd),
|
|
4928
|
+
...rp.updateCmd ? unresolvedPlaceholders(rp.updateCmd) : []
|
|
4929
|
+
])];
|
|
4930
|
+
if (missing.length) {
|
|
4931
|
+
log.warn(`${tag} plugin ${p.slug}: unresolved placeholders [${missing.join(",")}], skipping`);
|
|
4932
|
+
log.debug(`${tag} WARN: plugin ${p.slug}: unresolved placeholders [${missing.join(",")}], skipping`);
|
|
4933
|
+
continue;
|
|
4934
|
+
}
|
|
4935
|
+
resolved.push(rp);
|
|
4936
|
+
}
|
|
4937
|
+
await reconcilePlugins(resolved, buildReconcileDeps(config, tag));
|
|
4938
|
+
log.debug(`${tag} reconcile complete (${resolved.length} plugin(s) processed)`);
|
|
4939
|
+
await writeJson(statePath, { lastPullAt: Date.now() });
|
|
4940
|
+
} catch (e) {
|
|
4941
|
+
const prev = await readJson(statePath) ?? {};
|
|
4942
|
+
await writeJson(statePath, { ...prev, lastFailAt: Date.now() });
|
|
4943
|
+
log.debug(`${tag} reconcile failed: ${e.message}`);
|
|
4944
|
+
}
|
|
4945
|
+
} finally {
|
|
4946
|
+
if (acquired) await fs10.promises.rm(lockPath, { force: true });
|
|
4947
|
+
}
|
|
4486
4948
|
}
|
|
4487
4949
|
async function askViaTty(prompt) {
|
|
4488
4950
|
if (process.stdin.isTTY) {
|
|
@@ -4514,69 +4976,67 @@ async function askViaTty(prompt) {
|
|
|
4514
4976
|
}
|
|
4515
4977
|
}
|
|
4516
4978
|
}
|
|
4517
|
-
async function
|
|
4518
|
-
if (
|
|
4979
|
+
async function promptForProjectBinding(workspacePath, projects) {
|
|
4980
|
+
if (projects.length === 0) return null;
|
|
4519
4981
|
log.debug(`local-agent: workspace not bound: ${workspacePath}`);
|
|
4520
|
-
const answer = await askViaTty("\u662F\u5426\u7ED1\u5B9A\u5230\u4E00\u4E2A\
|
|
4982
|
+
const answer = await askViaTty("\u662F\u5426\u7ED1\u5B9A\u5230\u4E00\u4E2A\u9879\u76EE\uFF1F[y/N] ");
|
|
4521
4983
|
if (!answer || answer.toLowerCase() !== "y") return null;
|
|
4522
|
-
if (
|
|
4523
|
-
log.info("\u53EF\u7528\
|
|
4524
|
-
|
|
4525
|
-
const
|
|
4526
|
-
log.info(` ${index2 + 1}. ${
|
|
4984
|
+
if (projects.length === 1) return projects[0];
|
|
4985
|
+
log.info("\u53EF\u7528\u9879\u76EE:");
|
|
4986
|
+
projects.forEach((project, index2) => {
|
|
4987
|
+
const desc = project.description ? ` - ${project.description}` : "";
|
|
4988
|
+
log.info(` ${index2 + 1}. ${project.name}${desc} [id=${project.id}]`);
|
|
4527
4989
|
});
|
|
4528
|
-
const
|
|
4529
|
-
const defaultIndex = groups.indexOf(primary) + 1;
|
|
4530
|
-
const selection = await askViaTty(`\u9009\u62E9\u7EC4\u7EC7\u7F16\u53F7\uFF08\u9ED8\u8BA4 ${defaultIndex}\uFF0C0 \u8DF3\u8FC7\uFF09: `);
|
|
4990
|
+
const selection = await askViaTty(`\u9009\u62E9\u9879\u76EE\u7F16\u53F7\uFF081-${projects.length}\uFF0C0 \u8DF3\u8FC7\uFF09: `);
|
|
4531
4991
|
if (selection === null || selection === "0") return null;
|
|
4532
|
-
const index = selection ? Number.parseInt(selection, 10) :
|
|
4533
|
-
if (Number.isNaN(index) || index < 1 || index >
|
|
4534
|
-
return
|
|
4992
|
+
const index = selection ? Number.parseInt(selection, 10) : 0;
|
|
4993
|
+
if (Number.isNaN(index) || index < 1 || index > projects.length) return null;
|
|
4994
|
+
return projects[index - 1];
|
|
4535
4995
|
}
|
|
4536
|
-
async function
|
|
4996
|
+
async function bindWorkspaceToProject(workspacePath, projectId) {
|
|
4537
4997
|
const config = await loadLocalAgentConfig();
|
|
4538
4998
|
if (!config) {
|
|
4539
4999
|
throw new Error("HTTP local agent is not initialized. Run `teamai init --http <ENDPOINT> --token <API_TOKEN>` first.");
|
|
4540
5000
|
}
|
|
4541
|
-
const
|
|
4542
|
-
const
|
|
4543
|
-
if (!
|
|
5001
|
+
const projects = await fetchUserProjects(config);
|
|
5002
|
+
const project = projectId ? projects.find((item) => item.id === projectId) : await promptForProjectBinding(workspacePath, projects);
|
|
5003
|
+
if (!project) return null;
|
|
4544
5004
|
const binding = {
|
|
4545
|
-
|
|
4546
|
-
|
|
5005
|
+
projectId: project.id,
|
|
5006
|
+
projectName: project.name,
|
|
4547
5007
|
boundAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4548
5008
|
};
|
|
4549
5009
|
config.workspaceBindings[workspacePath] = binding;
|
|
4550
5010
|
await saveLocalAgentConfig(config);
|
|
4551
|
-
log.success(`\u5DF2\u5C06\
|
|
5011
|
+
log.success(`\u5DF2\u5C06\u5DE5\u4F5C\u533A\u7ED1\u5B9A\u5230\u9879\u76EE\uFF1A${project.name} [id=${project.id}]`);
|
|
4552
5012
|
return binding;
|
|
4553
5013
|
}
|
|
4554
5014
|
async function ensureWorkspaceBinding(config, workspacePath) {
|
|
4555
5015
|
if (config.workspaceBindings[workspacePath]) return;
|
|
4556
|
-
let
|
|
5016
|
+
let projects;
|
|
4557
5017
|
try {
|
|
4558
|
-
|
|
5018
|
+
projects = await fetchUserProjects(config);
|
|
4559
5019
|
} catch (e) {
|
|
4560
|
-
log.debug(`local-agent: failed to load user
|
|
5020
|
+
log.debug(`local-agent: failed to load user projects: ${e.message}`);
|
|
4561
5021
|
return;
|
|
4562
5022
|
}
|
|
4563
|
-
if (
|
|
4564
|
-
const
|
|
4565
|
-
if (
|
|
5023
|
+
if (projects.length === 0) return;
|
|
5024
|
+
const project = await promptForProjectBinding(workspacePath, projects);
|
|
5025
|
+
if (project) {
|
|
4566
5026
|
config.workspaceBindings[workspacePath] = {
|
|
4567
|
-
|
|
4568
|
-
|
|
5027
|
+
projectId: project.id,
|
|
5028
|
+
projectName: project.name,
|
|
4569
5029
|
boundAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4570
5030
|
};
|
|
4571
5031
|
await saveLocalAgentConfig(config);
|
|
4572
5032
|
return;
|
|
4573
5033
|
}
|
|
4574
|
-
const
|
|
5034
|
+
const projectList = projects.map((p) => `${p.name} (id=${p.id})`).join(", ");
|
|
4575
5035
|
const context = [
|
|
4576
|
-
"[teamai] \u5F53\u524D\
|
|
4577
|
-
`\u53EF\u7528\
|
|
5036
|
+
"[teamai] \u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u7ED1\u5B9A\u9879\u76EE\uFF0C\u65E0\u6CD5\u63A5\u6536\u9879\u76EE\u4E0B\u53D1\u7684 skills/rules/claudemd \u8D44\u6E90\u3002",
|
|
5037
|
+
`\u53EF\u7528\u9879\u76EE: ${projectList}`,
|
|
4578
5038
|
`\u8BF7\u63D0\u793A\u7528\u6237\u8FD0\u884C: teamai bind-project`,
|
|
4579
|
-
`\u6216\u6307\u5B9A\
|
|
5039
|
+
`\u6216\u6307\u5B9A\u9879\u76EE: teamai bind-project --project-id <id>`
|
|
4580
5040
|
].join("\n");
|
|
4581
5041
|
const hookOutput = JSON.stringify({
|
|
4582
5042
|
hookSpecificOutput: {
|
|
@@ -4598,21 +5058,21 @@ async function emitBindingHint(config, workspacePath) {
|
|
|
4598
5058
|
fs10.writeFileSync(hintMarker, "");
|
|
4599
5059
|
} catch {
|
|
4600
5060
|
}
|
|
4601
|
-
let
|
|
5061
|
+
let projects;
|
|
4602
5062
|
try {
|
|
4603
|
-
|
|
5063
|
+
projects = await fetchUserProjects(config);
|
|
4604
5064
|
} catch {
|
|
4605
5065
|
return;
|
|
4606
5066
|
}
|
|
4607
|
-
if (
|
|
4608
|
-
const skipNum =
|
|
4609
|
-
const choices =
|
|
4610
|
-
const actions =
|
|
5067
|
+
if (projects.length === 0) return;
|
|
5068
|
+
const skipNum = projects.length + 1;
|
|
5069
|
+
const choices = projects.map((p, i) => `${i + 1}. \u7ED1\u5B9A\u5230\u300C${p.name}\u300D\u9879\u76EE`);
|
|
5070
|
+
const actions = projects.map((p, i) => `- \u7528\u6237\u56DE\u590D ${i + 1} \u2192 \u6267\u884C \`teamai bind-project --project-id ${p.id}\`\uFF0C\u5B8C\u6210\u540E\u56DE\u590D"\u7ED1\u5B9A\u5B8C\u6210\uFF0C\u8BF7\u5F00\u65B0\u4F1A\u8BDD\u751F\u6548"`);
|
|
4611
5071
|
const context = [
|
|
4612
|
-
"[ClawPro\
|
|
5072
|
+
"[ClawPro\u9879\u76EE \u7ED1\u5B9A\u63D0\u793A] \u4F60\u5FC5\u987B\u539F\u6837\u5C55\u793A\u4E0B\u9762\u7684\u9009\u9879\u5217\u8868\uFF0C\u4E0D\u5F97\u4FEE\u6539\u7F16\u53F7\u6216\u5185\u5BB9\u3002\u7B49\u7528\u6237\u56DE\u590D\u6570\u5B57\u540E\u6267\u884C\u5BF9\u5E94\u547D\u4EE4\u3002",
|
|
4613
5073
|
"",
|
|
4614
5074
|
"---",
|
|
4615
|
-
"\u5F53\u524D\
|
|
5075
|
+
"\u5F53\u524D\u5DE5\u4F5C\u533A\u672A\u7ED1\u5B9AClawPro\u9879\u76EE\u3002\u7ED1\u5B9A\u540E\u53EF\u540C\u6B65\u56E2\u961F skills/rules\u3002\u8BF7\u56DE\u590D\u6570\u5B57\u9009\u62E9\uFF1A",
|
|
4616
5076
|
"",
|
|
4617
5077
|
...choices,
|
|
4618
5078
|
`${skipNum}. \u4E0D\u7ED1\u5B9A\uFF0C\u4EE5\u540E\u4E5F\u4E0D\u518D\u63D0\u793A`,
|
|
@@ -4706,7 +5166,7 @@ async function buildReportPayload(config, context) {
|
|
|
4706
5166
|
};
|
|
4707
5167
|
const userScope = await scanScope(process.env.HOME ?? "");
|
|
4708
5168
|
const payload = {
|
|
4709
|
-
agent_type: tool,
|
|
5169
|
+
agent_type: normalizeAgentType(tool),
|
|
4710
5170
|
agent_version: await getAgentVersion(tool),
|
|
4711
5171
|
local_agent_id: resolveLocalAgentId(context),
|
|
4712
5172
|
host_name: os3.hostname(),
|
|
@@ -4729,8 +5189,8 @@ async function buildReportPayload(config, context) {
|
|
|
4729
5189
|
{
|
|
4730
5190
|
path: workspacePath,
|
|
4731
5191
|
name: path18.basename(workspacePath),
|
|
4732
|
-
ide_type: tool,
|
|
4733
|
-
|
|
5192
|
+
ide_type: normalizeAgentType(tool),
|
|
5193
|
+
project_id: binding?.projectId,
|
|
4734
5194
|
skills: wsScope.skills,
|
|
4735
5195
|
rules: wsScope.rules
|
|
4736
5196
|
}
|
|
@@ -4742,7 +5202,7 @@ async function buildSyncPayload(config, context) {
|
|
|
4742
5202
|
const workspacePath = await resolveWorkspacePath(context.cwd);
|
|
4743
5203
|
const binding = workspacePath ? config.workspaceBindings[workspacePath] : void 0;
|
|
4744
5204
|
const payload = {
|
|
4745
|
-
agent_type: context.tool ?? "workbuddy",
|
|
5205
|
+
agent_type: normalizeAgentType(context.tool ?? "workbuddy"),
|
|
4746
5206
|
local_agent_id: resolveLocalAgentId(context),
|
|
4747
5207
|
status: context.status ?? "running"
|
|
4748
5208
|
};
|
|
@@ -4751,8 +5211,8 @@ async function buildSyncPayload(config, context) {
|
|
|
4751
5211
|
{
|
|
4752
5212
|
path: workspacePath,
|
|
4753
5213
|
name: path18.basename(workspacePath),
|
|
4754
|
-
ide_type: context.tool ?? "workbuddy",
|
|
4755
|
-
|
|
5214
|
+
ide_type: normalizeAgentType(context.tool ?? "workbuddy"),
|
|
5215
|
+
project_id: binding?.projectId
|
|
4756
5216
|
}
|
|
4757
5217
|
];
|
|
4758
5218
|
}
|
|
@@ -5036,7 +5496,7 @@ async function removeClaudeMdSection(filePath, startMarker, endMarker) {
|
|
|
5036
5496
|
await writeFile(filePath, (before + after).trimEnd() + "\n");
|
|
5037
5497
|
}
|
|
5038
5498
|
async function ackCommand(config, tag, command, status2, version2, error) {
|
|
5039
|
-
await localAgentFetch(config, tag, "
|
|
5499
|
+
await localAgentFetch(config, tag, "ack", {
|
|
5040
5500
|
method: "POST",
|
|
5041
5501
|
body: JSON.stringify({
|
|
5042
5502
|
id: command.id,
|
|
@@ -5098,9 +5558,12 @@ async function reportAndSyncLocalAgent(context) {
|
|
|
5098
5558
|
}
|
|
5099
5559
|
const tag = localAgentTag(context);
|
|
5100
5560
|
log.debug(`${tag} run: endpoint=${config.endpoint}`);
|
|
5561
|
+
if (context.event?.type === "session_start") {
|
|
5562
|
+
await maybeReconcilePlugins(context);
|
|
5563
|
+
}
|
|
5101
5564
|
try {
|
|
5102
5565
|
const reportPayload = await buildReportPayload(config, context);
|
|
5103
|
-
await localAgentFetch(config, tag, "
|
|
5566
|
+
await localAgentFetch(config, tag, "report", {
|
|
5104
5567
|
method: "POST",
|
|
5105
5568
|
body: JSON.stringify(reportPayload)
|
|
5106
5569
|
});
|
|
@@ -5109,7 +5572,7 @@ async function reportAndSyncLocalAgent(context) {
|
|
|
5109
5572
|
const syncResponse = await localAgentFetch(
|
|
5110
5573
|
config,
|
|
5111
5574
|
tag,
|
|
5112
|
-
"
|
|
5575
|
+
"sync",
|
|
5113
5576
|
{ method: "POST", body: JSON.stringify(syncPayload) }
|
|
5114
5577
|
);
|
|
5115
5578
|
const commands = syncResponse.commands ?? [];
|
|
@@ -5183,7 +5646,7 @@ async function pullLocalAgentForCwd(context) {
|
|
|
5183
5646
|
async function describeLocalAgent() {
|
|
5184
5647
|
const config = await loadLocalAgentConfig();
|
|
5185
5648
|
if (!config) return null;
|
|
5186
|
-
const
|
|
5649
|
+
const boundProjects = Object.entries(config.workspaceBindings).filter(([, b]) => b.projectId !== 0).map(([workspacePath, b]) => ({ path: workspacePath, projectName: b.projectName, projectId: b.projectId }));
|
|
5187
5650
|
const manifest = await loadManifest();
|
|
5188
5651
|
const counts = { skills: 0, rules: 0, claudemd: 0 };
|
|
5189
5652
|
for (const scope of Object.values(manifest.scopes)) {
|
|
@@ -5191,7 +5654,7 @@ async function describeLocalAgent() {
|
|
|
5191
5654
|
counts.rules += Object.keys(scope.rules ?? {}).length;
|
|
5192
5655
|
counts.claudemd += Object.keys(scope.claudemd ?? {}).length;
|
|
5193
5656
|
}
|
|
5194
|
-
return { endpoint: config.endpoint,
|
|
5657
|
+
return { endpoint: config.endpoint, boundProjects, resourceCounts: counts };
|
|
5195
5658
|
}
|
|
5196
5659
|
function parseScopeKey(key) {
|
|
5197
5660
|
if (key.startsWith("project:")) {
|
|
@@ -5199,12 +5662,26 @@ function parseScopeKey(key) {
|
|
|
5199
5662
|
}
|
|
5200
5663
|
return { scope: key === "instance" ? "instance" : "user" };
|
|
5201
5664
|
}
|
|
5665
|
+
async function teardownLocalAgentPlugins() {
|
|
5666
|
+
try {
|
|
5667
|
+
const config = await loadLocalAgentConfig();
|
|
5668
|
+
if (!config) return;
|
|
5669
|
+
await teardownAllPlugins(buildReconcileDeps(config, "[local-agent] [uninstall]"));
|
|
5670
|
+
} catch (e) {
|
|
5671
|
+
log.warn(`[local-agent] plugin teardown failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
5202
5674
|
async function removeLocalAgentHttp() {
|
|
5203
5675
|
const config = await loadLocalAgentConfig();
|
|
5204
5676
|
if (!config) {
|
|
5205
5677
|
log.info("No HTTP source configured \u2014 nothing to remove.");
|
|
5206
5678
|
return;
|
|
5207
5679
|
}
|
|
5680
|
+
try {
|
|
5681
|
+
await teardownAllPlugins(buildReconcileDeps(config, "[local-agent] [uninstall]"));
|
|
5682
|
+
} catch (e) {
|
|
5683
|
+
log.warn(`[local-agent] plugin teardown failed: ${e.message}`);
|
|
5684
|
+
}
|
|
5208
5685
|
const kinds = ["skill", "rule", "claudemd"];
|
|
5209
5686
|
const manifest = await loadManifest();
|
|
5210
5687
|
for (const [key, scopeManifest] of Object.entries(manifest.scopes)) {
|
|
@@ -5232,17 +5709,17 @@ async function bindCurrentProject(options) {
|
|
|
5232
5709
|
if (!config) {
|
|
5233
5710
|
throw new Error("Local agent not initialized. Run `teamai init --http` first.");
|
|
5234
5711
|
}
|
|
5235
|
-
config.workspaceBindings[workspacePath] = {
|
|
5712
|
+
config.workspaceBindings[workspacePath] = { projectId: 0, projectName: "__skipped__", boundAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5236
5713
|
await saveLocalAgentConfig(config);
|
|
5237
|
-
log.info(`\u5DF2\u8DF3\u8FC7\u7ED1\u5B9A\uFF0C\u4EE5\u540E\u4E0D\u518D\u63D0\u793A\u6B64\
|
|
5714
|
+
log.info(`\u5DF2\u8DF3\u8FC7\u7ED1\u5B9A\uFF0C\u4EE5\u540E\u4E0D\u518D\u63D0\u793A\u6B64\u5DE5\u4F5C\u533A\u3002`);
|
|
5238
5715
|
return;
|
|
5239
5716
|
}
|
|
5240
|
-
const binding = await
|
|
5717
|
+
const binding = await bindWorkspaceToProject(workspacePath, options?.projectId);
|
|
5241
5718
|
if (!binding) {
|
|
5242
5719
|
log.info("\u672A\u7ED1\u5B9A\u9879\u76EE\u3002");
|
|
5243
5720
|
}
|
|
5244
5721
|
}
|
|
5245
|
-
var execFileAsync, LOCAL_AGENT_DIR, CONFIG_FILE, MANIFEST_FILE, REPORTER_ERROR_LOG, ZIP_MAGIC;
|
|
5722
|
+
var execFileAsync, LOCAL_AGENT_DIR, CONFIG_FILE, MANIFEST_FILE, REPORTER_ERROR_LOG, DEFAULT_ROUTES, PLUGIN_PULL_INTERVAL_MS, PLUGIN_FAIL_BACKOFF_MS, ZIP_MAGIC;
|
|
5246
5723
|
var init_local_agent = __esm({
|
|
5247
5724
|
"src/local-agent.ts"() {
|
|
5248
5725
|
"use strict";
|
|
@@ -5256,13 +5733,24 @@ var init_local_agent = __esm({
|
|
|
5256
5733
|
init_machine_id();
|
|
5257
5734
|
init_builtin_rules();
|
|
5258
5735
|
init_path_safety();
|
|
5736
|
+
init_tool_names();
|
|
5259
5737
|
init_http_log();
|
|
5738
|
+
init_plugin_lifecycle();
|
|
5260
5739
|
init_types();
|
|
5261
5740
|
execFileAsync = promisify(execFile2);
|
|
5262
5741
|
LOCAL_AGENT_DIR = "local-agent";
|
|
5263
5742
|
CONFIG_FILE = "config.json";
|
|
5264
5743
|
MANIFEST_FILE = "manifest.json";
|
|
5265
5744
|
REPORTER_ERROR_LOG = "reporter/errors.jsonl";
|
|
5745
|
+
DEFAULT_ROUTES = {
|
|
5746
|
+
projects: "/api/projects/mine",
|
|
5747
|
+
report: "/api/local-agent/report",
|
|
5748
|
+
sync: "/api/local-agent/sync",
|
|
5749
|
+
ack: "/api/local-agent/commands/ack",
|
|
5750
|
+
getConfig: "/api/local-agent/get-config"
|
|
5751
|
+
};
|
|
5752
|
+
PLUGIN_PULL_INTERVAL_MS = 12 * 60 * 60 * 1e3;
|
|
5753
|
+
PLUGIN_FAIL_BACKOFF_MS = 60 * 60 * 1e3;
|
|
5266
5754
|
ZIP_MAGIC = Buffer.from([80, 75, 3, 4]);
|
|
5267
5755
|
}
|
|
5268
5756
|
});
|
|
@@ -5436,8 +5924,8 @@ async function sourceList() {
|
|
|
5436
5924
|
log.info("HTTP source (report/sync/ack):");
|
|
5437
5925
|
log.info(` ${httpSource.endpoint}`);
|
|
5438
5926
|
log.dim(` ${skills} skill(s), ${rules} rule(s), ${claudemd} claude.md`);
|
|
5439
|
-
for (const
|
|
5440
|
-
log.dim(` bound: ${
|
|
5927
|
+
for (const p of httpSource.boundProjects) {
|
|
5928
|
+
log.dim(` bound: ${p.projectName ?? p.projectId} \u2014 ${p.path}`);
|
|
5441
5929
|
}
|
|
5442
5930
|
}
|
|
5443
5931
|
}
|
|
@@ -7737,6 +8225,7 @@ __export(git_exports, {
|
|
|
7737
8225
|
getHeadRev: () => getHeadRev,
|
|
7738
8226
|
getRepoStatus: () => getRepoStatus,
|
|
7739
8227
|
initRepo: () => initRepo,
|
|
8228
|
+
isMetadataOnlyDiff: () => isMetadataOnlyDiff,
|
|
7740
8229
|
pullRepo: () => pullRepo,
|
|
7741
8230
|
pushRepoBranch: () => pushRepoBranch,
|
|
7742
8231
|
pushRepoDirectly: () => pushRepoDirectly,
|
|
@@ -7862,6 +8351,26 @@ async function autoPushViaMR(repoPath, message, files, teamConfig, localConfig)
|
|
|
7862
8351
|
return null;
|
|
7863
8352
|
}
|
|
7864
8353
|
}
|
|
8354
|
+
function isMetadataOnlyDiff(diff) {
|
|
8355
|
+
if (!diff.trim()) return true;
|
|
8356
|
+
const METADATA_PATTERNS = [
|
|
8357
|
+
/^\s*"?lastUpdated"?\s*[:=]/i,
|
|
8358
|
+
/^\s*"?lastScan"?\s*[:=]/i,
|
|
8359
|
+
/^\s*"?syncedAt"?\s*[:=]/i,
|
|
8360
|
+
/^\s*"?generatedAt"?\s*[:=]/i,
|
|
8361
|
+
/^\s*"?updatedAt"?\s*[:=]/i
|
|
8362
|
+
];
|
|
8363
|
+
const lines = diff.split("\n");
|
|
8364
|
+
for (const line of lines) {
|
|
8365
|
+
if (!line.startsWith("+") && !line.startsWith("-")) continue;
|
|
8366
|
+
if (line.startsWith("---") || line.startsWith("+++")) continue;
|
|
8367
|
+
const content = line.slice(1);
|
|
8368
|
+
if (!content.trim()) continue;
|
|
8369
|
+
const isMetadata = METADATA_PATTERNS.some((pat) => pat.test(content));
|
|
8370
|
+
if (!isMetadata) return false;
|
|
8371
|
+
}
|
|
8372
|
+
return true;
|
|
8373
|
+
}
|
|
7865
8374
|
async function pushRepoBranch(localPath, message, files, branchName) {
|
|
7866
8375
|
const git = createGit2(localPath);
|
|
7867
8376
|
await git.checkoutLocalBranch(branchName);
|
|
@@ -7874,6 +8383,14 @@ async function pushRepoBranch(localPath, message, files, branchName) {
|
|
|
7874
8383
|
await git.deleteLocalBranch(branchName, true);
|
|
7875
8384
|
return false;
|
|
7876
8385
|
}
|
|
8386
|
+
const diffOutput = await git.diff(["--cached", "--unified=0"]);
|
|
8387
|
+
if (isMetadataOnlyDiff(diffOutput)) {
|
|
8388
|
+
const defaultBranch = await getDefaultBranch(localPath);
|
|
8389
|
+
log.debug(`Only metadata/timestamp changes detected, switching back to ${defaultBranch}`);
|
|
8390
|
+
await git.checkout(defaultBranch);
|
|
8391
|
+
await git.deleteLocalBranch(branchName, true);
|
|
8392
|
+
return false;
|
|
8393
|
+
}
|
|
7877
8394
|
await git.commit(message);
|
|
7878
8395
|
await git.push(["-u", "origin", branchName]);
|
|
7879
8396
|
return true;
|
|
@@ -10754,6 +11271,7 @@ async function pullForScope(localConfig, options) {
|
|
|
10754
11271
|
}
|
|
10755
11272
|
const tagsConfig = await loadTagsConfig(localConfig.repo.localPath);
|
|
10756
11273
|
const subscribedTags = localConfig.subscribedTags;
|
|
11274
|
+
const excludedSkills = new Set(localConfig.excludedSkills ?? []);
|
|
10757
11275
|
const resourceTypes = ["skills", "rules", "docs", "env", "agents"];
|
|
10758
11276
|
let totalSynced = 0;
|
|
10759
11277
|
let desiredSkillNames = null;
|
|
@@ -10795,6 +11313,9 @@ async function pullForScope(localConfig, options) {
|
|
|
10795
11313
|
if (!merged.has(item.name)) merged.set(item.name, item);
|
|
10796
11314
|
}
|
|
10797
11315
|
items = [...merged.values()];
|
|
11316
|
+
if (excludedSkills.size > 0) {
|
|
11317
|
+
items = items.filter((item) => !excludedSkills.has(item.name));
|
|
11318
|
+
}
|
|
10798
11319
|
desiredSkillNames = new Set(items.map((i) => i.name));
|
|
10799
11320
|
knownRepoSkillNames = new Set(allTeamSkills.map((i) => i.name));
|
|
10800
11321
|
} else {
|
|
@@ -10903,6 +11424,19 @@ async function pullForScope(localConfig, options) {
|
|
|
10903
11424
|
await remove(skillDir);
|
|
10904
11425
|
log.debug(`Removed excluded skill ${dir} from ${tool}`);
|
|
10905
11426
|
}
|
|
11427
|
+
if (excludedSkills.size > 0) {
|
|
11428
|
+
for (const namespace of localDirs) {
|
|
11429
|
+
const namespaceDir = path39.join(skillsDir, namespace);
|
|
11430
|
+
if (await pathExists(path39.join(namespaceDir, "SKILL.md"))) continue;
|
|
11431
|
+
for (const skillName of await listDirs(namespaceDir)) {
|
|
11432
|
+
if (!excludedSkills.has(skillName) || BUILTIN_SKILL_NAMES.has(skillName)) continue;
|
|
11433
|
+
const nestedSkillDir = path39.join(namespaceDir, skillName);
|
|
11434
|
+
if (!await pathExists(path39.join(nestedSkillDir, "SKILL.md"))) continue;
|
|
11435
|
+
await remove(nestedSkillDir);
|
|
11436
|
+
log.debug(`Removed excluded skill ${namespace}/${skillName} from ${tool}`);
|
|
11437
|
+
}
|
|
11438
|
+
}
|
|
11439
|
+
}
|
|
10906
11440
|
}
|
|
10907
11441
|
}
|
|
10908
11442
|
if (totalSynced === 0) {
|
|
@@ -11978,6 +12512,79 @@ var init_skill_cmd = __esm({
|
|
|
11978
12512
|
}
|
|
11979
12513
|
});
|
|
11980
12514
|
|
|
12515
|
+
// src/exclude.ts
|
|
12516
|
+
var exclude_exports = {};
|
|
12517
|
+
__export(exclude_exports, {
|
|
12518
|
+
excludeAdd: () => excludeAdd,
|
|
12519
|
+
excludeList: () => excludeList,
|
|
12520
|
+
excludeRemove: () => excludeRemove
|
|
12521
|
+
});
|
|
12522
|
+
async function resolveExcludeScope() {
|
|
12523
|
+
const projectConfig = await detectProjectConfig();
|
|
12524
|
+
return projectConfig ?? (await requireInit()).localConfig;
|
|
12525
|
+
}
|
|
12526
|
+
async function saveExcludeScopeConfig(localConfig) {
|
|
12527
|
+
if (localConfig.scope === "project") {
|
|
12528
|
+
await saveLocalConfigForScope(localConfig, "project", localConfig.projectRoot);
|
|
12529
|
+
} else {
|
|
12530
|
+
await saveLocalConfig(localConfig);
|
|
12531
|
+
}
|
|
12532
|
+
try {
|
|
12533
|
+
const state = await loadStateForScope(localConfig.scope, localConfig.projectRoot);
|
|
12534
|
+
state.lastPullRev = null;
|
|
12535
|
+
await saveStateForScope(state, localConfig.scope, localConfig.projectRoot);
|
|
12536
|
+
} catch {
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
async function excludeList(_options) {
|
|
12540
|
+
const config = await resolveExcludeScope();
|
|
12541
|
+
const excludedSkills = config.excludedSkills ?? [];
|
|
12542
|
+
if (excludedSkills.length === 0) {
|
|
12543
|
+
log.info("No excluded skills. (pull syncs all role skills)");
|
|
12544
|
+
return;
|
|
12545
|
+
}
|
|
12546
|
+
log.info(`Excluded skills (${excludedSkills.length}):`);
|
|
12547
|
+
for (const skill of excludedSkills) log.dim(` ${skill}`);
|
|
12548
|
+
}
|
|
12549
|
+
async function excludeAdd(skills, _options) {
|
|
12550
|
+
const config = await resolveExcludeScope();
|
|
12551
|
+
const existing = new Set(config.excludedSkills ?? []);
|
|
12552
|
+
const added = skills.filter((skill) => {
|
|
12553
|
+
if (existing.has(skill)) return false;
|
|
12554
|
+
existing.add(skill);
|
|
12555
|
+
return true;
|
|
12556
|
+
});
|
|
12557
|
+
if (added.length === 0) {
|
|
12558
|
+
log.info("Already excluded.");
|
|
12559
|
+
return;
|
|
12560
|
+
}
|
|
12561
|
+
await saveExcludeScopeConfig({ ...config, excludedSkills: [...existing].sort() });
|
|
12562
|
+
log.success(`Excluded: ${added.join(", ")}`);
|
|
12563
|
+
log.dim("Run `teamai pull` to remove them from local AI tools.");
|
|
12564
|
+
}
|
|
12565
|
+
async function excludeRemove(skills, _options) {
|
|
12566
|
+
const config = await resolveExcludeScope();
|
|
12567
|
+
const existing = new Set(config.excludedSkills ?? []);
|
|
12568
|
+
const removed = skills.filter((skill) => existing.delete(skill));
|
|
12569
|
+
if (removed.length === 0) {
|
|
12570
|
+
log.info("None of those skills were excluded.");
|
|
12571
|
+
return;
|
|
12572
|
+
}
|
|
12573
|
+
await saveExcludeScopeConfig({
|
|
12574
|
+
...config,
|
|
12575
|
+
excludedSkills: existing.size > 0 ? [...existing].sort() : void 0
|
|
12576
|
+
});
|
|
12577
|
+
log.success(`Removed from exclude list: ${removed.join(", ")}`);
|
|
12578
|
+
log.dim("Run `teamai pull` to sync them again.");
|
|
12579
|
+
}
|
|
12580
|
+
var init_exclude = __esm({
|
|
12581
|
+
"src/exclude.ts"() {
|
|
12582
|
+
"use strict";
|
|
12583
|
+
init_config();
|
|
12584
|
+
init_logger();
|
|
12585
|
+
}
|
|
12586
|
+
});
|
|
12587
|
+
|
|
11981
12588
|
// src/members.ts
|
|
11982
12589
|
var members_exports = {};
|
|
11983
12590
|
__export(members_exports, {
|
|
@@ -13183,6 +13790,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
|
|
|
13183
13790
|
claudeMdFiles: [],
|
|
13184
13791
|
skillDirs: [],
|
|
13185
13792
|
ruleFiles: [],
|
|
13793
|
+
agentFiles: [],
|
|
13186
13794
|
shellProfile: null,
|
|
13187
13795
|
docsDir: null,
|
|
13188
13796
|
teamaiHome,
|
|
@@ -13192,7 +13800,9 @@ async function buildRemovalPlan(localConfig, teamConfig) {
|
|
|
13192
13800
|
};
|
|
13193
13801
|
const repoPath = localConfig.repo.localPath;
|
|
13194
13802
|
const teamSkillNames = await collectTeamSkillNames(repoPath);
|
|
13803
|
+
for (const name of BUILTIN_SKILL_NAMES) teamSkillNames.add(name);
|
|
13195
13804
|
const teamRuleNames = await collectTeamRuleNames(repoPath);
|
|
13805
|
+
for (const name of BUILTIN_RULE_NAMES) teamRuleNames.add(name);
|
|
13196
13806
|
const localAgentManifestPath = path47.join(
|
|
13197
13807
|
process.env.HOME ?? "",
|
|
13198
13808
|
".teamai",
|
|
@@ -13249,13 +13859,23 @@ async function buildRemovalPlan(localConfig, teamConfig) {
|
|
|
13249
13859
|
for (const file of files) {
|
|
13250
13860
|
if (!file.endsWith(".md")) continue;
|
|
13251
13861
|
const ruleName = file.replace(/\.md$/, "");
|
|
13252
|
-
if (EXCLUDED_RULE_NAMES.has(ruleName)) continue;
|
|
13253
13862
|
if (teamRuleNames.has(ruleName)) {
|
|
13254
13863
|
plan.ruleFiles.push(path47.join(rulesDir, file));
|
|
13255
13864
|
}
|
|
13256
13865
|
}
|
|
13257
13866
|
}
|
|
13258
13867
|
}
|
|
13868
|
+
if (toolPath.agents) {
|
|
13869
|
+
const agentsDir = path47.join(baseDir, toolPath.agents);
|
|
13870
|
+
if (await pathExists(agentsDir)) {
|
|
13871
|
+
for (const name of BUILTIN_AGENT_NAMES) {
|
|
13872
|
+
const agentFile = path47.join(agentsDir, `${name}.md`);
|
|
13873
|
+
if (await pathExists(agentFile)) {
|
|
13874
|
+
plan.agentFiles.push(agentFile);
|
|
13875
|
+
}
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
}
|
|
13259
13879
|
}
|
|
13260
13880
|
const shellProfilePath = teamConfig.sharing.env.shellProfilePath ? expandHome(teamConfig.sharing.env.shellProfilePath) : detectShellProfile();
|
|
13261
13881
|
if (shellProfilePath) {
|
|
@@ -13277,7 +13897,7 @@ async function buildRemovalPlan(localConfig, teamConfig) {
|
|
|
13277
13897
|
return plan;
|
|
13278
13898
|
}
|
|
13279
13899
|
function isPlanEmpty(plan) {
|
|
13280
|
-
return plan.hookFiles.length === 0 && plan.openclawHookDirs.length === 0 && plan.claudeMdFiles.length === 0 && plan.skillDirs.length === 0 && plan.ruleFiles.length === 0 && plan.shellProfile === null && plan.docsDir === null && !plan.teamaiHomeExists;
|
|
13900
|
+
return plan.hookFiles.length === 0 && plan.openclawHookDirs.length === 0 && plan.claudeMdFiles.length === 0 && plan.skillDirs.length === 0 && plan.ruleFiles.length === 0 && plan.agentFiles.length === 0 && plan.shellProfile === null && plan.docsDir === null && !plan.teamaiHomeExists;
|
|
13281
13901
|
}
|
|
13282
13902
|
function printSummary(plan) {
|
|
13283
13903
|
const cn = plan.scope === "project" ? "\u9879\u76EE\u7EA7" : "\u7528\u6237\u7EA7";
|
|
@@ -13314,6 +13934,10 @@ function printSummary(plan) {
|
|
|
13314
13934
|
console.log(` Rules (${plan.ruleFiles.length} \u4E2A\u6587\u4EF6)`);
|
|
13315
13935
|
console.log("");
|
|
13316
13936
|
}
|
|
13937
|
+
if (plan.agentFiles.length > 0) {
|
|
13938
|
+
console.log(` Agents (${plan.agentFiles.length} \u4E2A\u6587\u4EF6)`);
|
|
13939
|
+
console.log("");
|
|
13940
|
+
}
|
|
13317
13941
|
if (plan.shellProfile) {
|
|
13318
13942
|
console.log(" Shell profile \u73AF\u5883\u53D8\u91CF\u5757:");
|
|
13319
13943
|
console.log(` ${plan.shellProfile}`);
|
|
@@ -13330,6 +13954,14 @@ function printSummary(plan) {
|
|
|
13330
13954
|
console.log("");
|
|
13331
13955
|
}
|
|
13332
13956
|
}
|
|
13957
|
+
async function teardownPlugins() {
|
|
13958
|
+
try {
|
|
13959
|
+
const { teardownLocalAgentPlugins: teardownLocalAgentPlugins2 } = await Promise.resolve().then(() => (init_local_agent(), local_agent_exports));
|
|
13960
|
+
await teardownLocalAgentPlugins2();
|
|
13961
|
+
} catch (e) {
|
|
13962
|
+
log.warn(`plugin teardown failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
13963
|
+
}
|
|
13964
|
+
}
|
|
13333
13965
|
async function executeRemoval(plan) {
|
|
13334
13966
|
for (const { path: settingsPath, tool } of plan.hookFiles) {
|
|
13335
13967
|
try {
|
|
@@ -13388,6 +14020,16 @@ async function executeRemoval(plan) {
|
|
|
13388
14020
|
if (plan.ruleFiles.length > 0) {
|
|
13389
14021
|
log.success(`\u79FB\u9664\u4E86 ${plan.ruleFiles.length} \u4E2A rule \u6587\u4EF6`);
|
|
13390
14022
|
}
|
|
14023
|
+
for (const agentFile of plan.agentFiles) {
|
|
14024
|
+
try {
|
|
14025
|
+
await remove(agentFile);
|
|
14026
|
+
} catch (e) {
|
|
14027
|
+
log.warn(`\u79FB\u9664 agent \u5931\u8D25 ${agentFile}: ${e.message}`);
|
|
14028
|
+
}
|
|
14029
|
+
}
|
|
14030
|
+
if (plan.agentFiles.length > 0) {
|
|
14031
|
+
log.success(`\u79FB\u9664\u4E86 ${plan.agentFiles.length} \u4E2A agent \u6587\u4EF6`);
|
|
14032
|
+
}
|
|
13391
14033
|
if (plan.shellProfile) {
|
|
13392
14034
|
try {
|
|
13393
14035
|
const content = await readFileSafe(plan.shellProfile);
|
|
@@ -13414,6 +14056,7 @@ async function executeRemoval(plan) {
|
|
|
13414
14056
|
}
|
|
13415
14057
|
}
|
|
13416
14058
|
if (plan.teamaiHomeExists) {
|
|
14059
|
+
await teardownPlugins();
|
|
13417
14060
|
try {
|
|
13418
14061
|
await remove(plan.teamaiHome);
|
|
13419
14062
|
log.success(`\u79FB\u9664 ${plan.teamaiHome}/`);
|
|
@@ -13480,6 +14123,7 @@ async function uninstall(opts) {
|
|
|
13480
14123
|
}
|
|
13481
14124
|
}
|
|
13482
14125
|
try {
|
|
14126
|
+
await teardownPlugins();
|
|
13483
14127
|
await remove(home);
|
|
13484
14128
|
log.success(`\u79FB\u9664 ${home}/`);
|
|
13485
14129
|
log.success("teamai \u5378\u8F7D\u5B8C\u6210");
|
|
@@ -13497,6 +14141,8 @@ var init_uninstall = __esm({
|
|
|
13497
14141
|
init_openclaw_hooks();
|
|
13498
14142
|
init_types();
|
|
13499
14143
|
init_builtin_rules();
|
|
14144
|
+
init_builtin_agents();
|
|
14145
|
+
init_builtin_skills();
|
|
13500
14146
|
init_fs();
|
|
13501
14147
|
init_logger();
|
|
13502
14148
|
init_prompt();
|
|
@@ -14887,11 +15533,16 @@ async function cleanupStaleSessions(dir, currentSessionId) {
|
|
|
14887
15533
|
}
|
|
14888
15534
|
}
|
|
14889
15535
|
}
|
|
14890
|
-
function
|
|
15536
|
+
function toolErrorPoints(toolError) {
|
|
15537
|
+
for (const tier of CONTRIBUTE_TOOLERROR_TIERS) {
|
|
15538
|
+
if (toolError >= tier.min) return tier.points;
|
|
15539
|
+
}
|
|
15540
|
+
return 0;
|
|
15541
|
+
}
|
|
15542
|
+
function computeSmartScore(events, friction) {
|
|
14891
15543
|
if (events.length === 0) return 0;
|
|
14892
15544
|
const toolNames = /* @__PURE__ */ new Set();
|
|
14893
15545
|
let hasSkills = false;
|
|
14894
|
-
let hasErrors = false;
|
|
14895
15546
|
let totalToolCalls = 0;
|
|
14896
15547
|
for (const event of events) {
|
|
14897
15548
|
if (event.type === "tool_use" && event.toolName) {
|
|
@@ -14901,36 +15552,20 @@ function computeSmartScore(events) {
|
|
|
14901
15552
|
hasSkills = true;
|
|
14902
15553
|
}
|
|
14903
15554
|
}
|
|
14904
|
-
if (event.type === "prompt_submit" && event.promptSummary) {
|
|
14905
|
-
const lower = event.promptSummary.toLowerCase();
|
|
14906
|
-
if (lower.includes("error") || lower.includes("fix") || lower.includes("bug") || lower.includes("fail") || lower.includes("retry")) {
|
|
14907
|
-
hasErrors = true;
|
|
14908
|
-
}
|
|
14909
|
-
}
|
|
14910
15555
|
}
|
|
14911
15556
|
let score = 0;
|
|
14912
|
-
if (
|
|
14913
|
-
score +=
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
score += Math.min(Math.round(diversity * 20), 20);
|
|
15557
|
+
if (friction) {
|
|
15558
|
+
score += friction.interrupt * CONTRIBUTE_INTERRUPT_WEIGHT;
|
|
15559
|
+
score += friction.toolReject * CONTRIBUTE_REJECT_WEIGHT;
|
|
15560
|
+
score += friction.correction * CONTRIBUTE_CORRECTION_WEIGHT;
|
|
15561
|
+
score += toolErrorPoints(friction.toolError);
|
|
14918
15562
|
}
|
|
14919
15563
|
if (hasSkills) {
|
|
14920
|
-
score +=
|
|
14921
|
-
}
|
|
14922
|
-
if (hasErrors) {
|
|
14923
|
-
score += 10;
|
|
15564
|
+
score += CONTRIBUTE_SKILL_BONUS;
|
|
14924
15565
|
}
|
|
14925
|
-
if (
|
|
14926
|
-
const
|
|
14927
|
-
|
|
14928
|
-
const durationMin = (last - first) / (1e3 * 60);
|
|
14929
|
-
if (durationMin > 30) {
|
|
14930
|
-
score += 20;
|
|
14931
|
-
} else if (durationMin > 15) {
|
|
14932
|
-
score += 10;
|
|
14933
|
-
}
|
|
15566
|
+
if (totalToolCalls > 0) {
|
|
15567
|
+
const diversity = toolNames.size / Math.min(totalToolCalls, 10);
|
|
15568
|
+
score += Math.min(Math.round(diversity * CONTRIBUTE_DIVERSITY_BONUS_MAX), CONTRIBUTE_DIVERSITY_BONUS_MAX);
|
|
14934
15569
|
}
|
|
14935
15570
|
return score;
|
|
14936
15571
|
}
|
|
@@ -14995,19 +15630,33 @@ function countToolUseEvents(events) {
|
|
|
14995
15630
|
function countUniqueTools(events) {
|
|
14996
15631
|
return new Set(events.filter((e) => e.toolName).map((e) => e.toolName)).size;
|
|
14997
15632
|
}
|
|
14998
|
-
function
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15633
|
+
function extractFriction(events, sessionId) {
|
|
15634
|
+
let interrupt = 0;
|
|
15635
|
+
let toolReject = 0;
|
|
15636
|
+
let toolError = 0;
|
|
15637
|
+
for (const e of events) {
|
|
15638
|
+
if (e.type === "stop" && e.interventions) {
|
|
15639
|
+
interrupt = e.interventions.interrupt;
|
|
15640
|
+
toolReject = e.interventions.toolReject;
|
|
15641
|
+
toolError = e.interventions.toolError ?? 0;
|
|
15642
|
+
}
|
|
15005
15643
|
}
|
|
15006
|
-
|
|
15007
|
-
|
|
15644
|
+
const correction = aggregateSessionMetrics(events).get(sessionId)?.correction ?? 0;
|
|
15645
|
+
return { interrupt, toolReject, correction, toolError };
|
|
15646
|
+
}
|
|
15647
|
+
function buildHint(totalToolCalls, uniqueTools, isKnowledgeGap) {
|
|
15648
|
+
const body = isKnowledgeGap ? [
|
|
15649
|
+
`[teamai] \u672C\u6B21 session \u6D89\u53CA\u77E5\u8BC6\u5E93\u5C1A\u672A\u8986\u76D6\u7684\u9886\u57DF\uFF0C\u4E14\u8FC7\u7A0B\u4E2D\u7ECF\u5386\u4E86\u591A\u6B21\u8C03\u6574\uFF08${totalToolCalls} \u6B21\u5DE5\u5177\u8C03\u7528\uFF0C${uniqueTools} \u79CD\u4E0D\u540C\u5DE5\u5177\uFF09\u3002`,
|
|
15650
|
+
`\u5EFA\u8BAE\u8FD0\u884C /teamai-share-learnings \u5C06\u672C\u6B21\u7ECF\u9A8C\u603B\u7ED3\u5206\u4EAB\u7ED9\u56E2\u961F\uFF0C\u5E2E\u52A9\u586B\u8865\u77E5\u8BC6\u5E93\u7A7A\u767D\u3002`,
|
|
15651
|
+
`\u4E0B\u6B21\u9047\u5230\u7C7B\u4F3C\u4EFB\u52A1\u65F6\uFF0C\u56E2\u961F\u6210\u5458\u5C06\u76F4\u63A5\u53D7\u76CA\u4E8E\u60A8\u7684\u7ECF\u9A8C\u3002`
|
|
15652
|
+
].join("") : [
|
|
15653
|
+
`[teamai] \u672C\u6B21 session \u8FC7\u7A0B\u4E2D\u51FA\u73B0\u4E86\u8F83\u591A\u7EA0\u504F\u6216\u5DE5\u5177\u91CD\u8BD5\uFF0C\u5F88\u53EF\u80FD\u8E29\u5230\u4E86\u503C\u5F97\u8BB0\u5F55\u7684\u5751\uFF08${totalToolCalls} \u6B21\u5DE5\u5177\u8C03\u7528\uFF0C${uniqueTools} \u79CD\u4E0D\u540C\u5DE5\u5177\uFF09\u3002`,
|
|
15008
15654
|
`\u5EFA\u8BAE\u8FD0\u884C /teamai-share-learnings \u603B\u7ED3\u672C\u6B21 session \u7684\u7ECF\u9A8C\u5E76\u5206\u4EAB\u7ED9\u56E2\u961F\u3002`,
|
|
15009
15655
|
`\u603B\u7ED3\u6587\u6863\u5C06\u4FDD\u5B58\u5230\u56E2\u961F\u4ED3\u5E93\u7684 learnings/ \u76EE\u5F55\u3002`
|
|
15010
15656
|
].join("");
|
|
15657
|
+
return `Print the following message verbatim to the user (do NOT paraphrase, summarize, or explain its origin):
|
|
15658
|
+
|
|
15659
|
+
${body}`;
|
|
15011
15660
|
}
|
|
15012
15661
|
async function contributeCheckForSession(sessionId, cwd) {
|
|
15013
15662
|
const state = await readContributeState(sessionId);
|
|
@@ -15040,14 +15689,17 @@ async function contributeCheckForSession(sessionId, cwd) {
|
|
|
15040
15689
|
} else {
|
|
15041
15690
|
const allEvents = await readEvents();
|
|
15042
15691
|
const sessionEvents = allEvents.filter((e) => e.sessionId === sessionId);
|
|
15043
|
-
|
|
15692
|
+
const friction = extractFriction(sessionEvents, sessionId);
|
|
15693
|
+
score = computeSmartScore(sessionEvents, friction);
|
|
15044
15694
|
toolCount = countToolUseEvents(sessionEvents);
|
|
15045
15695
|
uniqueTools = countUniqueTools(sessionEvents);
|
|
15046
15696
|
needsPersist = true;
|
|
15047
15697
|
if (sessionEvents.length > 0) {
|
|
15048
15698
|
sessionStartIso = sessionEvents[0].timestamp;
|
|
15049
15699
|
}
|
|
15050
|
-
log.debug(
|
|
15700
|
+
log.debug(
|
|
15701
|
+
`contribute-check: session ${sessionId.slice(0, 16)} friction score = ${score} (interrupt=${friction.interrupt}, reject=${friction.toolReject}, correction=${friction.correction}, toolError=${friction.toolError}, threshold=${CONTRIBUTE_SMART_THRESHOLD})`
|
|
15702
|
+
);
|
|
15051
15703
|
}
|
|
15052
15704
|
const phase2 = applyPhase2Adjustments(score, sessionId, cwd, sessionStartIso);
|
|
15053
15705
|
score = phase2.score;
|
|
@@ -15058,7 +15710,7 @@ async function contributeCheckForSession(sessionId, cwd) {
|
|
|
15058
15710
|
`contribute-check: phase2 adjustments applied (gap=${isKnowledgeGap}, commit=${hasGitCommit}, adjusted=${score})`
|
|
15059
15711
|
);
|
|
15060
15712
|
}
|
|
15061
|
-
const willHint = score >= CONTRIBUTE_SMART_THRESHOLD;
|
|
15713
|
+
const willHint = score >= CONTRIBUTE_SMART_THRESHOLD && toolCount >= CONTRIBUTE_BASE_THRESHOLD;
|
|
15062
15714
|
if (needsPersist || willHint) {
|
|
15063
15715
|
const latest = await readContributeState(sessionId);
|
|
15064
15716
|
const updated = {
|
|
@@ -15138,7 +15790,9 @@ async function parseTranscriptForVotes(transcriptPath) {
|
|
|
15138
15790
|
});
|
|
15139
15791
|
for await (const line of rl) {
|
|
15140
15792
|
const trimmed = line.trim();
|
|
15141
|
-
if (!trimmed
|
|
15793
|
+
if (!trimmed) continue;
|
|
15794
|
+
extractRecalledDocIdsFromComment(trimmed, recalledSet);
|
|
15795
|
+
if (!trimmed.includes('"assistant"')) continue;
|
|
15142
15796
|
let entry;
|
|
15143
15797
|
try {
|
|
15144
15798
|
entry = JSON.parse(trimmed);
|
|
@@ -15161,6 +15815,19 @@ async function parseTranscriptForVotes(transcriptPath) {
|
|
|
15161
15815
|
referencedDocIds: [...referencedSet]
|
|
15162
15816
|
};
|
|
15163
15817
|
}
|
|
15818
|
+
function isValidDocId(docId) {
|
|
15819
|
+
return docId.length > 0 && !/[<>]/.test(docId) && docId !== "...";
|
|
15820
|
+
}
|
|
15821
|
+
function extractRecalledDocIdsFromComment(text, out) {
|
|
15822
|
+
const pattern = /<!--\s*teamai:recalled-doc-ids:\s*\[([^\]]*)\]\s*-->/g;
|
|
15823
|
+
let match;
|
|
15824
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
15825
|
+
for (const item of match[1].split(",")) {
|
|
15826
|
+
const docId = item.trim().replace(/^['"]|['"]$/g, "");
|
|
15827
|
+
if (isValidDocId(docId)) out.add(docId);
|
|
15828
|
+
}
|
|
15829
|
+
}
|
|
15830
|
+
}
|
|
15164
15831
|
function extractRecalledDocIds(text, out) {
|
|
15165
15832
|
const START = "--- [teamai:recall:start] ---";
|
|
15166
15833
|
const END = "--- [teamai:recall:end] ---";
|
|
@@ -15177,7 +15844,7 @@ function extractRecalledDocIds(text, out) {
|
|
|
15177
15844
|
while ((match = filePattern.exec(region)) !== null) {
|
|
15178
15845
|
const filePath = match[1].trim();
|
|
15179
15846
|
const docId = path53.basename(filePath).replace(/\.md$/i, "");
|
|
15180
|
-
out.add(docId);
|
|
15847
|
+
if (isValidDocId(docId)) out.add(docId);
|
|
15181
15848
|
}
|
|
15182
15849
|
searchFrom = endIdx + END.length;
|
|
15183
15850
|
}
|
|
@@ -15189,7 +15856,7 @@ function extractReferencedDocIds(text, out) {
|
|
|
15189
15856
|
const raw = match[1];
|
|
15190
15857
|
for (const item of raw.split(",")) {
|
|
15191
15858
|
const docId = item.trim().replace(/^['"]|['"]$/g, "");
|
|
15192
|
-
if (docId) out.add(docId);
|
|
15859
|
+
if (isValidDocId(docId)) out.add(docId);
|
|
15193
15860
|
}
|
|
15194
15861
|
}
|
|
15195
15862
|
}
|
|
@@ -15568,9 +16235,9 @@ function buildHandlerRegistry() {
|
|
|
15568
16235
|
{ event: "session-start", matcher: "*", handler: mrHintHandler, timeoutMs: MR_HINT_TIMEOUT_MS },
|
|
15569
16236
|
{ event: "session-start", matcher: "*", handler: localAgentHandler, timeoutMs: LOCAL_AGENT_TIMEOUT_MS },
|
|
15570
16237
|
// ─── Stop ─────────────────────────────────────────
|
|
15571
|
-
{ event: "stop", matcher: "*", handler: updateHandler, timeoutMs:
|
|
15572
|
-
{ event: "stop", matcher: "*", handler: contributeCheckHandler, timeoutMs: CONTRIBUTE_CHECK_TIMEOUT_MS },
|
|
16238
|
+
{ event: "stop", matcher: "*", handler: updateHandler, timeoutMs: UPDATE_TIMEOUT_MS2 },
|
|
15573
16239
|
{ event: "stop", matcher: "*", handler: votesSyncHandler, timeoutMs: VOTES_SYNC_TIMEOUT_MS },
|
|
16240
|
+
{ event: "stop", matcher: "*", handler: contributeCheckHandler, timeoutMs: CONTRIBUTE_CHECK_TIMEOUT_MS },
|
|
15574
16241
|
{ event: "stop", matcher: "*", handler: dashboardReportHandler, timeoutMs: DASHBOARD_TIMEOUT_MS },
|
|
15575
16242
|
{ event: "stop", matcher: "*", handler: localAgentHandler, timeoutMs: LOCAL_AGENT_TIMEOUT_MS },
|
|
15576
16243
|
// ─── PostToolUse ──────────────────────────────────
|
|
@@ -15584,14 +16251,14 @@ function buildHandlerRegistry() {
|
|
|
15584
16251
|
{ event: "prompt-submit", matcher: "*", handler: localAgentHandler, timeoutMs: LOCAL_AGENT_TIMEOUT_MS }
|
|
15585
16252
|
];
|
|
15586
16253
|
}
|
|
15587
|
-
var PULL_TIMEOUT_MS,
|
|
16254
|
+
var PULL_TIMEOUT_MS, UPDATE_TIMEOUT_MS2, TRACK_TIMEOUT_MS, DASHBOARD_TIMEOUT_MS, CONTRIBUTE_CHECK_TIMEOUT_MS, VOTES_SYNC_TIMEOUT_MS, TODOWRITE_HINT_TIMEOUT_MS, MR_HINT_TIMEOUT_MS, LOCAL_AGENT_TIMEOUT_MS, pullHandler, updateHandler, dashboardReportHandler, trackHandler, trackSlashHandler, contributeCheckHandler, votesSyncHandler, todowriteHintHandler, mrHintHandler, localAgentHandler;
|
|
15588
16255
|
var init_hook_handlers = __esm({
|
|
15589
16256
|
"src/hook-handlers.ts"() {
|
|
15590
16257
|
"use strict";
|
|
15591
16258
|
init_session_id();
|
|
15592
16259
|
init_tool_names();
|
|
15593
16260
|
PULL_TIMEOUT_MS = 6e4;
|
|
15594
|
-
|
|
16261
|
+
UPDATE_TIMEOUT_MS2 = 1e4;
|
|
15595
16262
|
TRACK_TIMEOUT_MS = 5e3;
|
|
15596
16263
|
DASHBOARD_TIMEOUT_MS = 5e3;
|
|
15597
16264
|
CONTRIBUTE_CHECK_TIMEOUT_MS = 1e4;
|
|
@@ -15689,7 +16356,7 @@ var init_hook_handlers = __esm({
|
|
|
15689
16356
|
};
|
|
15690
16357
|
votesSyncHandler = {
|
|
15691
16358
|
name: "votes-sync",
|
|
15692
|
-
async execute(stdin) {
|
|
16359
|
+
async execute(stdin, tool) {
|
|
15693
16360
|
if (process.env.TEAMAI_RECALL_DISABLED === "1") return null;
|
|
15694
16361
|
const transcriptPath = typeof stdin.transcript_path === "string" ? stdin.transcript_path : null;
|
|
15695
16362
|
if (!transcriptPath) return null;
|
|
@@ -15699,7 +16366,7 @@ var init_hook_handlers = __esm({
|
|
|
15699
16366
|
const { requireInit: requireInit3 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
15700
16367
|
const voteData = await parseTranscriptForVotes2(transcriptPath);
|
|
15701
16368
|
const { localConfig } = await requireInit3();
|
|
15702
|
-
const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
|
|
16369
|
+
const { VOTES_LOCAL_DIR: VOTES_LOCAL_DIR2, TEAMAI_SESSIONS_DIR: TEAMAI_SESSIONS_DIR2 } = await Promise.resolve().then(() => (init_types(), types_exports));
|
|
15703
16370
|
const votesDir = VOTES_LOCAL_DIR2;
|
|
15704
16371
|
const votePath = path56.join(votesDir, `${localConfig.username}.yaml`);
|
|
15705
16372
|
if (voteData.referencedDocIds.length > 0) {
|
|
@@ -15707,6 +16374,52 @@ var init_hook_handlers = __esm({
|
|
|
15707
16374
|
}
|
|
15708
16375
|
await syncVotesToTeam2(localConfig.repo.localPath, localConfig.username, votesDir).catch(() => {
|
|
15709
16376
|
});
|
|
16377
|
+
const sessionId = deriveSessionId(stdin, { includeCwd: true });
|
|
16378
|
+
const recalled = voteData.recalledDocIds;
|
|
16379
|
+
const declared = voteData.referencedDocIds;
|
|
16380
|
+
let nudged = false;
|
|
16381
|
+
if (recalled.length > 0 && declared.length === 0) {
|
|
16382
|
+
const fsp = await import("fs/promises");
|
|
16383
|
+
const safeId = sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
16384
|
+
const marker = path56.join(TEAMAI_SESSIONS_DIR2, `${safeId}-adoption-nudged`);
|
|
16385
|
+
let already = false;
|
|
16386
|
+
try {
|
|
16387
|
+
await fsp.access(marker);
|
|
16388
|
+
already = true;
|
|
16389
|
+
} catch {
|
|
16390
|
+
already = false;
|
|
16391
|
+
}
|
|
16392
|
+
if (!already) {
|
|
16393
|
+
try {
|
|
16394
|
+
const { ensureDir: ensureDir3 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
16395
|
+
await ensureDir3(TEAMAI_SESSIONS_DIR2);
|
|
16396
|
+
await fsp.writeFile(marker, "");
|
|
16397
|
+
nudged = true;
|
|
16398
|
+
} catch {
|
|
16399
|
+
}
|
|
16400
|
+
}
|
|
16401
|
+
}
|
|
16402
|
+
if (process.env.TEAMAI_ADOPTION_EVAL_LOG) {
|
|
16403
|
+
try {
|
|
16404
|
+
const { appendFile } = await import("fs/promises");
|
|
16405
|
+
await appendFile(
|
|
16406
|
+
process.env.TEAMAI_ADOPTION_EVAL_LOG,
|
|
16407
|
+
JSON.stringify({
|
|
16408
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16409
|
+
sessionId,
|
|
16410
|
+
recalled: recalled.length,
|
|
16411
|
+
declared: declared.length,
|
|
16412
|
+
nudged
|
|
16413
|
+
}) + "\n"
|
|
16414
|
+
);
|
|
16415
|
+
} catch {
|
|
16416
|
+
}
|
|
16417
|
+
}
|
|
16418
|
+
if (nudged) {
|
|
16419
|
+
const { formatStopHookOutput: formatStopHookOutput2 } = await Promise.resolve().then(() => (init_hook_output(), hook_output_exports));
|
|
16420
|
+
const msg = `\u4F60\u672C\u6B21\u901A\u8FC7 teamai \u53EC\u56DE\u4E86\u56E2\u961F\u77E5\u8BC6\uFF08\u5019\u9009 doc-id\uFF1A${recalled.join(", ")}\uFF09\u3002\u7ED3\u675F\u524D\u8BF7\u5728\u56DE\u590D\u672B\u5C3E\u58F0\u660E\u4F60\u5B9E\u9645\u7528\u5230\u7684\u6761\u76EE\uFF1A<!-- teamai:referenced-doc-ids: [\u7528\u5230\u7684doc-id] -->\uFF1B\u6CA1\u7528\u5230\u5C31\u7559\u7A7A []\u3002`;
|
|
16421
|
+
return formatStopHookOutput2(msg, tool ?? "claude");
|
|
16422
|
+
}
|
|
15710
16423
|
} catch {
|
|
15711
16424
|
}
|
|
15712
16425
|
return null;
|
|
@@ -27579,6 +28292,26 @@ skillCmd.command("show <name>").description("Show skill metadata: source / contr
|
|
|
27579
28292
|
const { skillShow: skillShow2 } = await Promise.resolve().then(() => (init_skill_cmd(), skill_cmd_exports));
|
|
27580
28293
|
await skillShow2(name, { ...globalOpts, ...cmdOpts });
|
|
27581
28294
|
});
|
|
28295
|
+
var excludeCmd = skillCmd.command("exclude").description("Manage per-user skill exclusion (skip sync without affecting team repo)").action(async () => {
|
|
28296
|
+
const globalOpts = program.opts();
|
|
28297
|
+
const { excludeList: excludeList2 } = await Promise.resolve().then(() => (init_exclude(), exclude_exports));
|
|
28298
|
+
await excludeList2(globalOpts);
|
|
28299
|
+
});
|
|
28300
|
+
excludeCmd.command("list").description("List excluded skills").action(async () => {
|
|
28301
|
+
const globalOpts = program.opts();
|
|
28302
|
+
const { excludeList: excludeList2 } = await Promise.resolve().then(() => (init_exclude(), exclude_exports));
|
|
28303
|
+
await excludeList2(globalOpts);
|
|
28304
|
+
});
|
|
28305
|
+
excludeCmd.command("add <skills...>").description("Add skill(s) to the exclude list").action(async (skills) => {
|
|
28306
|
+
const globalOpts = program.opts();
|
|
28307
|
+
const { excludeAdd: excludeAdd2 } = await Promise.resolve().then(() => (init_exclude(), exclude_exports));
|
|
28308
|
+
await excludeAdd2(skills, globalOpts);
|
|
28309
|
+
});
|
|
28310
|
+
excludeCmd.command("remove <skills...>").description("Remove skill(s) from the exclude list").action(async (skills) => {
|
|
28311
|
+
const globalOpts = program.opts();
|
|
28312
|
+
const { excludeRemove: excludeRemove2 } = await Promise.resolve().then(() => (init_exclude(), exclude_exports));
|
|
28313
|
+
await excludeRemove2(skills, globalOpts);
|
|
28314
|
+
});
|
|
27582
28315
|
var membersCmd = program.command("members").description("Manage team members").action(async () => {
|
|
27583
28316
|
const globalOpts = program.opts();
|
|
27584
28317
|
const { listMembers: listMembers2 } = await Promise.resolve().then(() => (init_members(), members_exports));
|
|
@@ -27701,6 +28434,10 @@ sourceCmd.command("remove-http").description("Remove the HTTP source and clean u
|
|
|
27701
28434
|
const { sourceRemoveHttp: sourceRemoveHttp2 } = await Promise.resolve().then(() => (init_source(), source_exports));
|
|
27702
28435
|
await sourceRemoveHttp2(globalOpts);
|
|
27703
28436
|
});
|
|
28437
|
+
sourceCmd.command("reconcile-plugins", { hidden: true }).description("Run plugin reconcile worker (called internally by session_start hook)").action(async () => {
|
|
28438
|
+
const { runPluginReconcileWorker: runPluginReconcileWorker2 } = await Promise.resolve().then(() => (init_local_agent(), local_agent_exports));
|
|
28439
|
+
await runPluginReconcileWorker2();
|
|
28440
|
+
});
|
|
27704
28441
|
sourceCmd.command("list").description("List all configured sources").action(async () => {
|
|
27705
28442
|
const { sourceList: sourceList2 } = await Promise.resolve().then(() => (init_source(), source_exports));
|
|
27706
28443
|
await sourceList2();
|
|
@@ -27795,10 +28532,10 @@ program.command("hook-dispatch <event>").description("Unified hook dispatcher \u
|
|
|
27795
28532
|
const { hookDispatchCli: hookDispatchCli2 } = await Promise.resolve().then(() => (init_hook_dispatch_cli(), hook_dispatch_cli_exports));
|
|
27796
28533
|
await hookDispatchCli2(event, cmdOpts.tool ?? "claude", cmdOpts.matcher ?? "*");
|
|
27797
28534
|
});
|
|
27798
|
-
program.command("bind-project").description("Bind the current
|
|
28535
|
+
program.command("bind-project").description("Bind the current workspace to a ClawPro project for HTTP local-agent sync").option("--project-id <id>", "Project ID from /projects/mine").option("--skip", "Mark current workspace as skipped (never prompt again)").action(async (cmdOpts) => {
|
|
27799
28536
|
const { bindCurrentProject: bindCurrentProject2 } = await Promise.resolve().then(() => (init_local_agent(), local_agent_exports));
|
|
27800
28537
|
await bindCurrentProject2({
|
|
27801
|
-
|
|
28538
|
+
projectId: cmdOpts.projectId ? Number.parseInt(cmdOpts.projectId, 10) : void 0,
|
|
27802
28539
|
skip: !!cmdOpts.skip
|
|
27803
28540
|
});
|
|
27804
28541
|
});
|