teamai-cli 0.20.0-beta.3 → 0.20.0-beta.5

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 +122 -18
  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.
@@ -10791,7 +10848,8 @@ __export(init_exports, {
10791
10848
  promptForSelfModeAgents: () => promptForSelfModeAgents,
10792
10849
  resolveInheritUserScope: () => resolveInheritUserScope,
10793
10850
  resolveInitRepo: () => resolveInitRepo,
10794
- resolveInitScope: () => resolveInitScope
10851
+ resolveInitScope: () => resolveInitScope,
10852
+ resolveSelfModeSelection: () => resolveSelfModeSelection
10795
10853
  });
10796
10854
  import YAML10 from "yaml";
10797
10855
  import fs14 from "fs";
@@ -11052,8 +11110,14 @@ function buildSelfModeGitignore() {
11052
11110
  ".update-lock",
11053
11111
  ".reports-lock",
11054
11112
  ".bootstrap-lock",
11055
- "env",
11113
+ // NB: env/ is intentionally NOT ignored in single-repo mode — team env vars
11114
+ // (.teamai/env/env.yaml) are committed to main so `teamai push` can carry them
11115
+ // and teammates get them on clone. env.yaml holds plaintext key/value pairs, so
11116
+ // only put non-secret config there; keep real secrets out of the repo.
11056
11117
  "env.sh",
11118
+ // env.local is the machine-local KEY=value backup pull writes for ${VAR}
11119
+ // resolution (self mode uses this name to avoid colliding with the env/ dir).
11120
+ "env.local",
11057
11121
  "usage.jsonl",
11058
11122
  "known-skills.json",
11059
11123
  "search-index.json",
@@ -11072,34 +11136,60 @@ function buildSelfModeGitignore() {
11072
11136
  ""
11073
11137
  ].join("\n");
11074
11138
  }
11139
+ function resolveSelfModeSelection(indices, detected) {
11140
+ const out = [];
11141
+ const seen = /* @__PURE__ */ new Set();
11142
+ const add = (id) => {
11143
+ if (id && !seen.has(id)) {
11144
+ seen.add(id);
11145
+ out.push(id);
11146
+ }
11147
+ };
11148
+ const pickedAuto = indices.includes(0);
11149
+ if (pickedAuto) {
11150
+ if (detected.length > 0) detected.forEach(add);
11151
+ else add("claude");
11152
+ }
11153
+ for (const i of indices) {
11154
+ if (i === 0) continue;
11155
+ const id = SELF_MODE_AGENT_CHOICES[i - 1];
11156
+ if (id) add(id);
11157
+ }
11158
+ return out;
11159
+ }
11075
11160
  async function promptForSelfModeAgents(options) {
11076
11161
  const explicit = normalizeAgentList(options.agent);
11077
11162
  if (explicit.length > 0) return explicit;
11078
11163
  if (options.silent || options.force || !process.stdin.isTTY) {
11079
11164
  return detectHomeInstalledAgents();
11080
11165
  }
11081
- const choices = SELF_MODE_AGENT_CHOICES.map((id) => {
11166
+ const detected = await detectHomeInstalledAgents();
11167
+ const tools = SELF_MODE_AGENT_CHOICES.map((id) => {
11082
11168
  const meta = KNOWN_AGENTS.find((a) => a.id === id);
11083
11169
  const root = meta?.skillsPath.split("/")[0] ?? `.${id}`;
11084
11170
  return { id, label: meta?.displayName ?? id, root };
11085
11171
  });
11172
+ const detectedLabels = detected.map((id) => tools.find((t) => t.id === id)?.label ?? id).join(", ");
11173
+ const autoLabel = detected.length > 0 ? `Auto \u2014 the AI tools already installed here: ${detectedLabels}` : "Auto \u2014 none detected (will set up Claude Code)";
11086
11174
  console.log("");
11087
11175
  console.log("Which AI tools should teamai set up in this repo?");
11088
11176
  console.log("(creates the skills dir, injects hooks, commits settings to main)");
11089
11177
  console.log("");
11090
- choices.forEach((c, i) => {
11091
- console.log(` ${i + 1}. ${c.label} (${c.root})`);
11178
+ console.log(` 1. ${autoLabel}`);
11179
+ tools.forEach((t, i) => {
11180
+ console.log(` ${i + 2}. ${t.label} (${t.root})`);
11092
11181
  });
11093
11182
  console.log("");
11183
+ const optionCount = tools.length + 1;
11094
11184
  const indices = await askSelection(
11095
- `Select [1-${choices.length}, comma/range, or "all"] (default: 1 = ${choices[0].label}): `,
11096
- choices.length,
11185
+ `Select [1-${optionCount}, comma/range, or "all"] (default: 1 = Auto): `,
11186
+ optionCount,
11097
11187
  false
11098
11188
  );
11099
11189
  if (!indices || indices.length === 0) {
11100
- return ["claude"];
11190
+ return resolveSelfModeSelection([0], detected);
11101
11191
  }
11102
- return indices.map((i) => choices[i].id);
11192
+ return resolveSelfModeSelection(indices, detected);
11103
11193
  }
11104
11194
  async function initSelfRepo(options) {
11105
11195
  log.info("Initializing teamai (single-repo mode)...");
@@ -11166,7 +11256,7 @@ async function initSelfRepo(options) {
11166
11256
  return;
11167
11257
  }
11168
11258
  await ensureDir(localPath);
11169
- for (const dir of ["skills", "rules", "docs", "learnings", "env"]) {
11259
+ for (const dir of ["skills", "rules", "docs", "learnings", "env", "agents", "hooks", "mcp"]) {
11170
11260
  await ensureDir(path38.join(localPath, dir));
11171
11261
  const gitkeep = path38.join(localPath, dir, ".gitkeep");
11172
11262
  if (!await pathExists(gitkeep)) {
@@ -11243,6 +11333,10 @@ async function initSelfRepo(options) {
11243
11333
  ".teamai/rules",
11244
11334
  ".teamai/docs",
11245
11335
  ".teamai/learnings",
11336
+ ".teamai/env",
11337
+ ".teamai/agents",
11338
+ ".teamai/hooks",
11339
+ ".teamai/mcp",
11246
11340
  ".teamai/teamai.yaml",
11247
11341
  ".teamai/.gitignore"
11248
11342
  ];
@@ -11295,8 +11389,18 @@ async function initSelfRepo(options) {
11295
11389
  } catch {
11296
11390
  }
11297
11391
  log.success("teamai initialized (single-repo mode)!");
11298
- 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.");
11299
- log.info("Add skills/rules later with `teamai push` \u2014 it opens a PR against your repo without touching your working tree.");
11392
+ log.info("Next steps:");
11393
+ log.info(" 1. Add team resources by dropping them into .teamai/ (or author them in your AI tool as usual):");
11394
+ log.info(" .teamai/skills/ team skills");
11395
+ log.info(" .teamai/rules/ shared rules");
11396
+ log.info(" .teamai/agents/ subagent definitions (<name>.yaml)");
11397
+ log.info(" .teamai/env/env.yaml shared env vars \u2014 committed to main, so keep real secrets out");
11398
+ 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.");
11399
+ log.info(" 3. docs / hooks / mcp are edited directly and shipped with a normal commit \u2014 no push needed:");
11400
+ log.info(" .teamai/docs/ team docs");
11401
+ log.info(" .teamai/hooks/hooks.yaml team hooks");
11402
+ log.info(" .teamai/mcp/mcp.yaml shared MCP servers");
11403
+ 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.");
11300
11404
  closePrompt();
11301
11405
  }
11302
11406
  async function init(options) {
@@ -14229,7 +14333,7 @@ async function readManifest2(manifestPath) {
14229
14333
  }
14230
14334
  async function buildVarTable(localConfig) {
14231
14335
  const table = {};
14232
- const envFile = path47.join(getTeamaiHome(localConfig.scope, localConfig.projectRoot), "env");
14336
+ const envFile = getEnvBackupPath(localConfig);
14233
14337
  const content = await readFileSafe(envFile);
14234
14338
  if (content) {
14235
14339
  for (const line of content.split("\n")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamai-cli",
3
- "version": "0.20.0-beta.3",
3
+ "version": "0.20.0-beta.5",
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": {