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.
Files changed (2) hide show
  1. package/dist/index.js +138 -10
  2. 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(_item, _teamConfig, _localConfig) {
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(path25.join(teamaiHome, "env"), backupLines.join("\n") + "\n");
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 dest = path28.join(localConfig.repo.localPath, "agents", `${item.name}.md`);
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 (legacy MD format)`);
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
- "env",
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("Push your business repo (e.g. `git push -u origin HEAD`) so teammates get the .teamai/ knowledge and are auto-initialized on clone.");
11326
- log.info("Add skills/rules later with `teamai push` \u2014 it opens a PR against your repo without touching your working tree.");
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 = path47.join(getTeamaiHome(localConfig.scope, localConfig.projectRoot), "env");
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamai-cli",
3
- "version": "0.20.0-beta.4",
3
+ "version": "0.20.0-beta.6",
4
4
  "description": "TeamAI — the team harness for AI agents (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {