teamai-cli 0.20.0-beta.4 → 0.20.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +138 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -218,6 +218,7 @@ __export(types_exports, {
|
|
|
218
218
|
areTeamHooksDisabled: () => areTeamHooksDisabled,
|
|
219
219
|
emptyTokenUsage: () => emptyTokenUsage,
|
|
220
220
|
getConfigPath: () => getConfigPath,
|
|
221
|
+
getEnvBackupPath: () => getEnvBackupPath,
|
|
221
222
|
getHooksSharing: () => getHooksSharing,
|
|
222
223
|
getKnowledgeDir: () => getKnowledgeDir,
|
|
223
224
|
getManagedHooksPath: () => getManagedHooksPath,
|
|
@@ -313,6 +314,10 @@ function getTeamaiHome(scope, projectRoot) {
|
|
|
313
314
|
}
|
|
314
315
|
return path2.join(process.env.HOME ?? "", ".teamai");
|
|
315
316
|
}
|
|
317
|
+
function getEnvBackupPath(localConfig) {
|
|
318
|
+
const home = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
|
|
319
|
+
return path2.join(home, isSelfMode(localConfig) ? "env.local" : "env");
|
|
320
|
+
}
|
|
316
321
|
function getConfigPath(scope, projectRoot) {
|
|
317
322
|
return path2.join(getTeamaiHome(scope, projectRoot), "config.yaml");
|
|
318
323
|
}
|
|
@@ -8004,6 +8009,20 @@ var init_env = __esm({
|
|
|
8004
8009
|
* Compares local env/env.yaml against the committed version.
|
|
8005
8010
|
*/
|
|
8006
8011
|
async scanLocalForPush(_teamConfig, localConfig) {
|
|
8012
|
+
if (isSelfMode(localConfig) && localConfig.projectRoot) {
|
|
8013
|
+
const activeEnv = path25.join(localConfig.projectRoot, ".teamai", "env", "env.yaml");
|
|
8014
|
+
if (!await pathExists(activeEnv)) return [];
|
|
8015
|
+
const baseEnv = path25.join(localConfig.repo.localPath, "env", "env.yaml");
|
|
8016
|
+
if (await pathExists(baseEnv) && await fileContentEqual(activeEnv, baseEnv)) {
|
|
8017
|
+
return [];
|
|
8018
|
+
}
|
|
8019
|
+
return [{
|
|
8020
|
+
name: "env.yaml",
|
|
8021
|
+
type: "env",
|
|
8022
|
+
sourcePath: activeEnv,
|
|
8023
|
+
relativePath: "env/env.yaml"
|
|
8024
|
+
}];
|
|
8025
|
+
}
|
|
8007
8026
|
const envYamlPath = path25.join(localConfig.repo.localPath, "env", "env.yaml");
|
|
8008
8027
|
if (!await pathExists(envYamlPath)) return [];
|
|
8009
8028
|
const { execFile: execFile5 } = await import("child_process");
|
|
@@ -8036,7 +8055,15 @@ var init_env = __esm({
|
|
|
8036
8055
|
relativePath: "env/env.yaml"
|
|
8037
8056
|
}];
|
|
8038
8057
|
}
|
|
8039
|
-
async pushItem(
|
|
8058
|
+
async pushItem(item, _teamConfig, localConfig) {
|
|
8059
|
+
if (isSelfMode(localConfig)) {
|
|
8060
|
+
const dest = path25.join(localConfig.repo.localPath, "env", "env.yaml");
|
|
8061
|
+
if (item.sourcePath !== dest) {
|
|
8062
|
+
await ensureDir(path25.dirname(dest));
|
|
8063
|
+
const content = await readFileSafe(item.sourcePath);
|
|
8064
|
+
if (content !== null) await writeFile(dest, content);
|
|
8065
|
+
}
|
|
8066
|
+
}
|
|
8040
8067
|
}
|
|
8041
8068
|
/**
|
|
8042
8069
|
* Pull env variables: parse env.yaml, write env.sh, inject source line into shell profile.
|
|
@@ -8056,7 +8083,7 @@ var init_env = __esm({
|
|
|
8056
8083
|
const teamaiHome = getTeamaiHome(localConfig.scope, localConfig.projectRoot);
|
|
8057
8084
|
const backupLines = envConfig.variables.map((v) => `${v.key}=${v.value}`);
|
|
8058
8085
|
await ensureDir(teamaiHome);
|
|
8059
|
-
await writeFile(
|
|
8086
|
+
await writeFile(getEnvBackupPath(localConfig), backupLines.join("\n") + "\n");
|
|
8060
8087
|
const envShContent = this.generateEnvFile(envConfig.variables);
|
|
8061
8088
|
await writeFile(path25.join(teamaiHome, "env.sh"), envShContent);
|
|
8062
8089
|
const inject = teamConfig.sharing.env.injectShellProfile !== false;
|
|
@@ -8568,6 +8595,34 @@ var init_agents = __esm({
|
|
|
8568
8595
|
const teamAgentsDir = path28.join(localConfig.repo.localPath, "agents");
|
|
8569
8596
|
const tombstones = await this.readTombstones(localConfig);
|
|
8570
8597
|
const baseDir = resolveBaseDir(localConfig);
|
|
8598
|
+
const directItems = [];
|
|
8599
|
+
const directStems = /* @__PURE__ */ new Set();
|
|
8600
|
+
if (isSelfMode(localConfig) && localConfig.projectRoot) {
|
|
8601
|
+
const activeAgentsDir = path28.join(localConfig.projectRoot, ".teamai", "agents");
|
|
8602
|
+
if (await pathExists(activeAgentsDir)) {
|
|
8603
|
+
for (const file of await listFiles(activeAgentsDir)) {
|
|
8604
|
+
const isYaml = file.endsWith(".yaml");
|
|
8605
|
+
const isMd = file.endsWith(".md");
|
|
8606
|
+
if (!isYaml && !isMd) continue;
|
|
8607
|
+
const stem = file.replace(/\.(yaml|md)$/, "");
|
|
8608
|
+
if (tombstones.has(stem)) continue;
|
|
8609
|
+
if (BUILTIN_AGENT_NAMES.has(stem)) continue;
|
|
8610
|
+
const activePath = path28.join(activeAgentsDir, file);
|
|
8611
|
+
const basePath = path28.join(teamAgentsDir, file);
|
|
8612
|
+
const baseExists = await pathExists(basePath);
|
|
8613
|
+
if (baseExists && await fileContentEqual(activePath, basePath)) continue;
|
|
8614
|
+
directItems.push({
|
|
8615
|
+
name: stem,
|
|
8616
|
+
type: "agents",
|
|
8617
|
+
sourcePath: activePath,
|
|
8618
|
+
relativePath: `agents/${file}`,
|
|
8619
|
+
status: baseExists ? "modified" : "new",
|
|
8620
|
+
legacy: isMd
|
|
8621
|
+
});
|
|
8622
|
+
directStems.add(stem);
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8571
8626
|
const grouped = /* @__PURE__ */ new Map();
|
|
8572
8627
|
for (const [tool, toolPath] of Object.entries(teamConfig.toolPaths)) {
|
|
8573
8628
|
if (!toolPath.agents) continue;
|
|
@@ -8579,6 +8634,7 @@ var init_agents = __esm({
|
|
|
8579
8634
|
if (stem === null) continue;
|
|
8580
8635
|
if (tombstones.has(stem)) continue;
|
|
8581
8636
|
if (BUILTIN_AGENT_NAMES.has(stem)) continue;
|
|
8637
|
+
if (directStems.has(stem)) continue;
|
|
8582
8638
|
const filePath = path28.join(agentsDir, file);
|
|
8583
8639
|
let toolGroup = grouped.get(stem);
|
|
8584
8640
|
if (!toolGroup) {
|
|
@@ -8590,7 +8646,7 @@ var init_agents = __esm({
|
|
|
8590
8646
|
}
|
|
8591
8647
|
}
|
|
8592
8648
|
}
|
|
8593
|
-
const items = [];
|
|
8649
|
+
const items = [...directItems];
|
|
8594
8650
|
for (const [stem, toolFiles] of grouped) {
|
|
8595
8651
|
const teamYamlPath = path28.join(teamAgentsDir, `${stem}.yaml`);
|
|
8596
8652
|
const teamMdPath = path28.join(teamAgentsDir, `${stem}.md`);
|
|
@@ -8722,12 +8778,13 @@ var init_agents = __esm({
|
|
|
8722
8778
|
log.debug(`Wrote agent ${item.name} \u2192 team repo (YAML format)`);
|
|
8723
8779
|
return;
|
|
8724
8780
|
}
|
|
8725
|
-
const
|
|
8781
|
+
const ext = item.sourcePath.endsWith(".yaml") ? ".yaml" : ".md";
|
|
8782
|
+
const dest = path28.join(localConfig.repo.localPath, "agents", `${item.name}${ext}`);
|
|
8726
8783
|
if (item.sourcePath !== dest) {
|
|
8727
8784
|
await ensureDir(path28.dirname(dest));
|
|
8728
8785
|
await copyFile(item.sourcePath, dest);
|
|
8729
8786
|
}
|
|
8730
|
-
log.debug(`Copied agent ${item.name} \u2192 team repo (
|
|
8787
|
+
log.debug(`Copied agent ${item.name} \u2192 team repo (${ext} verbatim)`);
|
|
8731
8788
|
}
|
|
8732
8789
|
/**
|
|
8733
8790
|
* Pull an agent to every installed tool's agents/ directory.
|
|
@@ -9619,6 +9676,11 @@ async function push(options) {
|
|
|
9619
9676
|
const { localConfig, teamConfig } = await autoDetectInit();
|
|
9620
9677
|
assertNotReadOnly(localConfig, "teamai push");
|
|
9621
9678
|
if (localConfig.repo.kind === "self") {
|
|
9679
|
+
try {
|
|
9680
|
+
const { migrateSelfModeGitignore: migrateSelfModeGitignore2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
9681
|
+
await migrateSelfModeGitignore2(localConfig);
|
|
9682
|
+
} catch {
|
|
9683
|
+
}
|
|
9622
9684
|
const { withKnowledgeWorktree: withKnowledgeWorktree2, EmptyRepoError: EmptyRepoError2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
9623
9685
|
try {
|
|
9624
9686
|
await withKnowledgeWorktree2(localConfig, (wtConfig) => pushCore(wtConfig, teamConfig, options));
|
|
@@ -10788,6 +10850,8 @@ __export(init_exports, {
|
|
|
10788
10850
|
init: () => init,
|
|
10789
10851
|
initHttp: () => initHttp,
|
|
10790
10852
|
initSelfRepo: () => initSelfRepo,
|
|
10853
|
+
migrateSelfModeGitignore: () => migrateSelfModeGitignore,
|
|
10854
|
+
migrateSelfModeGitignoreContent: () => migrateSelfModeGitignoreContent,
|
|
10791
10855
|
promptForSelfModeAgents: () => promptForSelfModeAgents,
|
|
10792
10856
|
resolveInheritUserScope: () => resolveInheritUserScope,
|
|
10793
10857
|
resolveInitRepo: () => resolveInitRepo,
|
|
@@ -11053,8 +11117,14 @@ function buildSelfModeGitignore() {
|
|
|
11053
11117
|
".update-lock",
|
|
11054
11118
|
".reports-lock",
|
|
11055
11119
|
".bootstrap-lock",
|
|
11056
|
-
|
|
11120
|
+
// NB: env/ is intentionally NOT ignored in single-repo mode — team env vars
|
|
11121
|
+
// (.teamai/env/env.yaml) are committed to main so `teamai push` can carry them
|
|
11122
|
+
// and teammates get them on clone. env.yaml holds plaintext key/value pairs, so
|
|
11123
|
+
// only put non-secret config there; keep real secrets out of the repo.
|
|
11057
11124
|
"env.sh",
|
|
11125
|
+
// env.local is the machine-local KEY=value backup pull writes for ${VAR}
|
|
11126
|
+
// resolution (self mode uses this name to avoid colliding with the env/ dir).
|
|
11127
|
+
"env.local",
|
|
11058
11128
|
"usage.jsonl",
|
|
11059
11129
|
"known-skills.json",
|
|
11060
11130
|
"search-index.json",
|
|
@@ -11073,6 +11143,45 @@ function buildSelfModeGitignore() {
|
|
|
11073
11143
|
""
|
|
11074
11144
|
].join("\n");
|
|
11075
11145
|
}
|
|
11146
|
+
function migrateSelfModeGitignoreContent(content) {
|
|
11147
|
+
const lines = content.split("\n");
|
|
11148
|
+
let changed = false;
|
|
11149
|
+
const filtered = lines.filter((line) => {
|
|
11150
|
+
if (line.trim() === "env") {
|
|
11151
|
+
changed = true;
|
|
11152
|
+
return false;
|
|
11153
|
+
}
|
|
11154
|
+
return true;
|
|
11155
|
+
});
|
|
11156
|
+
const hasEnvLocal = filtered.some((l) => l.trim() === "env.local");
|
|
11157
|
+
if (!hasEnvLocal) {
|
|
11158
|
+
const envShIdx = filtered.findIndex((l) => l.trim() === "env.sh");
|
|
11159
|
+
if (envShIdx >= 0) {
|
|
11160
|
+
filtered.splice(envShIdx + 1, 0, "env.local");
|
|
11161
|
+
} else {
|
|
11162
|
+
const lastNonEmpty = filtered.reduce((acc, l, i) => l.trim() ? i : acc, -1);
|
|
11163
|
+
filtered.splice(lastNonEmpty + 1, 0, "env.local");
|
|
11164
|
+
}
|
|
11165
|
+
changed = true;
|
|
11166
|
+
}
|
|
11167
|
+
return { changed, content: filtered.join("\n") };
|
|
11168
|
+
}
|
|
11169
|
+
async function migrateSelfModeGitignore(localConfig) {
|
|
11170
|
+
if (localConfig.repo.kind !== "self" || !localConfig.projectRoot) return;
|
|
11171
|
+
const gitignorePath = path38.join(localConfig.projectRoot, ".teamai", ".gitignore");
|
|
11172
|
+
try {
|
|
11173
|
+
const current = await readFileSafe(gitignorePath);
|
|
11174
|
+
if (current === null) return;
|
|
11175
|
+
const { changed, content } = migrateSelfModeGitignoreContent(current);
|
|
11176
|
+
if (!changed) return;
|
|
11177
|
+
await writeFile(gitignorePath, content);
|
|
11178
|
+
log.info(
|
|
11179
|
+
"Updated .teamai/.gitignore so team env vars (.teamai/env/env.yaml) can be shared \u2014 please `git add .teamai/.gitignore` and commit it."
|
|
11180
|
+
);
|
|
11181
|
+
} catch (e) {
|
|
11182
|
+
log.debug(`[self-mode] gitignore migration skipped: ${e.message}`);
|
|
11183
|
+
}
|
|
11184
|
+
}
|
|
11076
11185
|
function resolveSelfModeSelection(indices, detected) {
|
|
11077
11186
|
const out = [];
|
|
11078
11187
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11193,7 +11302,7 @@ async function initSelfRepo(options) {
|
|
|
11193
11302
|
return;
|
|
11194
11303
|
}
|
|
11195
11304
|
await ensureDir(localPath);
|
|
11196
|
-
for (const dir of ["skills", "rules", "docs", "learnings", "env"]) {
|
|
11305
|
+
for (const dir of ["skills", "rules", "docs", "learnings", "env", "agents", "hooks", "mcp"]) {
|
|
11197
11306
|
await ensureDir(path38.join(localPath, dir));
|
|
11198
11307
|
const gitkeep = path38.join(localPath, dir, ".gitkeep");
|
|
11199
11308
|
if (!await pathExists(gitkeep)) {
|
|
@@ -11270,6 +11379,10 @@ async function initSelfRepo(options) {
|
|
|
11270
11379
|
".teamai/rules",
|
|
11271
11380
|
".teamai/docs",
|
|
11272
11381
|
".teamai/learnings",
|
|
11382
|
+
".teamai/env",
|
|
11383
|
+
".teamai/agents",
|
|
11384
|
+
".teamai/hooks",
|
|
11385
|
+
".teamai/mcp",
|
|
11273
11386
|
".teamai/teamai.yaml",
|
|
11274
11387
|
".teamai/.gitignore"
|
|
11275
11388
|
];
|
|
@@ -11322,8 +11435,18 @@ async function initSelfRepo(options) {
|
|
|
11322
11435
|
} catch {
|
|
11323
11436
|
}
|
|
11324
11437
|
log.success("teamai initialized (single-repo mode)!");
|
|
11325
|
-
log.info("
|
|
11326
|
-
log.info("Add
|
|
11438
|
+
log.info("Next steps:");
|
|
11439
|
+
log.info(" 1. Add team resources by dropping them into .teamai/ (or author them in your AI tool as usual):");
|
|
11440
|
+
log.info(" .teamai/skills/ team skills");
|
|
11441
|
+
log.info(" .teamai/rules/ shared rules");
|
|
11442
|
+
log.info(" .teamai/agents/ subagent definitions (<name>.yaml)");
|
|
11443
|
+
log.info(" .teamai/env/env.yaml shared env vars \u2014 committed to main, so keep real secrets out");
|
|
11444
|
+
log.info(" 2. Run `teamai push` for the above \u2014 it scans .teamai/{skills,rules,agents,env} plus your AI tool dirs and opens a PR against your repo, without touching your working tree.");
|
|
11445
|
+
log.info(" 3. docs / hooks / mcp are edited directly and shipped with a normal commit \u2014 no push needed:");
|
|
11446
|
+
log.info(" .teamai/docs/ team docs");
|
|
11447
|
+
log.info(" .teamai/hooks/hooks.yaml team hooks");
|
|
11448
|
+
log.info(" .teamai/mcp/mcp.yaml shared MCP servers");
|
|
11449
|
+
log.info(" 4. Push your business repo (e.g. `git push -u origin HEAD`) so teammates get the .teamai/ knowledge and are auto-initialized on clone.");
|
|
11327
11450
|
closePrompt();
|
|
11328
11451
|
}
|
|
11329
11452
|
async function init(options) {
|
|
@@ -14256,7 +14379,7 @@ async function readManifest2(manifestPath) {
|
|
|
14256
14379
|
}
|
|
14257
14380
|
async function buildVarTable(localConfig) {
|
|
14258
14381
|
const table = {};
|
|
14259
|
-
const envFile =
|
|
14382
|
+
const envFile = getEnvBackupPath(localConfig);
|
|
14260
14383
|
const content = await readFileSafe(envFile);
|
|
14261
14384
|
if (content) {
|
|
14262
14385
|
for (const line of content.split("\n")) {
|
|
@@ -14571,6 +14694,11 @@ async function refreshTeamRepo(localConfig) {
|
|
|
14571
14694
|
return { label: "HTTP (report/sync delivery)", version: null, reportingOnly: true };
|
|
14572
14695
|
}
|
|
14573
14696
|
if (localConfig.repo.kind === "self") {
|
|
14697
|
+
try {
|
|
14698
|
+
const { migrateSelfModeGitignore: migrateSelfModeGitignore2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
14699
|
+
await migrateSelfModeGitignore2(localConfig);
|
|
14700
|
+
} catch {
|
|
14701
|
+
}
|
|
14574
14702
|
let version3 = null;
|
|
14575
14703
|
try {
|
|
14576
14704
|
version3 = await getHeadRev(localConfig.repo.localPath);
|