@team-ai/teamai-cli 0.30.2 → 0.30.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.
Files changed (2) hide show
  1. package/dist/cli.js +47 -100
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -61809,49 +61809,26 @@ async function canonicalizeWorkspacePath(value) {
61809
61809
  }
61810
61810
  function mergeWorkspaceBindings(existing2, incoming, canonicalKey) {
61811
61811
  if (!existing2) return incoming;
61812
- const existingReal = existing2.projectId !== 0;
61813
- const incomingReal = incoming.projectId !== 0;
61812
+ const rank2 = (binding) => binding.projectId > 0 ? 2 : binding.projectName === "__skipped__" ? 1 : 0;
61813
+ const existingRank = rank2(existing2);
61814
+ const incomingRank = rank2(incoming);
61815
+ const existingReal = existingRank === 2;
61816
+ const incomingReal = incomingRank === 2;
61814
61817
  if (existingReal && incomingReal && existing2.projectId !== incoming.projectId) {
61815
61818
  log.warn(
61816
61819
  `local-agent: workspace ${canonicalKey} has conflicting project bindings (${existing2.projectId} vs ${incoming.projectId}); keeping ${existing2.projectId}`
61817
61820
  );
61818
61821
  }
61819
- const base = existingReal || !incomingReal ? { ...existing2 } : { ...incoming };
61822
+ const base = existingRank >= incomingRank ? { ...existing2 } : { ...incoming };
61823
+ base.enrolledAt ??= existing2.enrolledAt ?? incoming.enrolledAt;
61820
61824
  if (!base.ideType) base.ideType = existing2.ideType ?? incoming.ideType;
61821
61825
  return base;
61822
61826
  }
61823
- function workspaceEnrollmentsOf(config) {
61824
- return config.workspaceEnrollments ??= {};
61827
+ function isWorkspaceEnrolled(config, workspacePath) {
61828
+ return config.workspaceBindings[workspacePath]?.enrolledAt !== void 0;
61825
61829
  }
61826
- async function inferWorkspaceEnrollments(config) {
61827
- const inferred = {};
61828
- const add = async (workspacePath, enrolledAt = config.createdAt, ideType) => {
61829
- const canonical2 = await canonicalizeWorkspacePath(workspacePath);
61830
- inferred[canonical2] ??= { enrolledAt, ...ideType ? { ideType } : {} };
61831
- };
61832
- for (const [workspacePath, binding] of Object.entries(config.workspaceBindings)) {
61833
- if (binding.projectId > 0) await add(workspacePath, binding.boundAt, binding.ideType);
61834
- }
61835
- const manifest = await readJson(getManifestPath());
61836
- for (const key of Object.keys(manifest?.scopes ?? {})) {
61837
- const parsed = parseScopeKey(key);
61838
- if (parsed.scope === "project" && parsed.workspacePath) await add(parsed.workspacePath);
61839
- }
61840
- const models = await readJson(getModelManifestPath());
61841
- for (const workspacePath of Object.keys(models?.workspaceModels ?? {})) await add(workspacePath);
61842
- const projectHomes = await listDirs(path76.join(getTeamaiHomePath(), "projects")).catch(() => []);
61843
- const mcpOwnershipFiles = [
61844
- path76.join(getTeamaiHomePath(), "managed-local-mcp.json"),
61845
- ...projectHomes.map((dir) => path76.join(getTeamaiHomePath(), "projects", dir, "managed-local-mcp.json"))
61846
- ];
61847
- for (const file of mcpOwnershipFiles) {
61848
- const records2 = await readJson(file);
61849
- for (const [key, owned] of Object.entries(records2 ?? {})) {
61850
- const match = /^[^:]+:local:(.+)$/.exec(key);
61851
- if (match?.[1] && owned.length > 0) await add(match[1]);
61852
- }
61853
- }
61854
- return inferred;
61830
+ function hasWorkspaceBindingDecision(binding) {
61831
+ return Boolean(binding && (binding.projectId > 0 || binding.projectName === "__skipped__"));
61855
61832
  }
61856
61833
  async function loadLocalAgentConfig(options2 = {}) {
61857
61834
  const fileConfig = await readJson(getConfigPath());
@@ -61860,14 +61837,8 @@ async function loadLocalAgentConfig(options2 = {}) {
61860
61837
  const config = {
61861
61838
  ...fileConfig,
61862
61839
  endpoint: normalizeEndpoint(fileConfig.endpoint),
61863
- workspaceEnrollments: fileConfig.workspaceEnrollments ?? {},
61864
61840
  workspaceBindings: fileConfig.workspaceBindings ?? {}
61865
61841
  };
61866
- let enrollmentMigrationChanged = false;
61867
- if (fileConfig.workspaceEnrollments === void 0) {
61868
- config.workspaceEnrollments = await inferWorkspaceEnrollments(config);
61869
- enrollmentMigrationChanged = true;
61870
- }
61871
61842
  const removedLegacyPaths = [];
61872
61843
  for (const [wsPath, binding] of Object.entries(config.workspaceBindings)) {
61873
61844
  if ("groupId" in binding && !("projectId" in binding)) {
@@ -61875,20 +61846,15 @@ async function loadLocalAgentConfig(options2 = {}) {
61875
61846
  removedLegacyPaths.push(wsPath);
61876
61847
  }
61877
61848
  }
61849
+ let migrationChanged = removedLegacyPaths.length > 0;
61878
61850
  if (removedLegacyPaths.length > 0 && options2.dryRun) {
61879
61851
  log.info(`[dry-run] Would remove ${removedLegacyPaths.length} legacy group-based workspace binding(s).`);
61880
61852
  } else if (removedLegacyPaths.length > 0) {
61881
61853
  log.warn(
61882
61854
  `Removed ${removedLegacyPaths.length} legacy group-based workspace binding(s); you will be prompted to re-bind on the next session.`
61883
61855
  );
61884
- try {
61885
- await saveLocalAgentConfig(config);
61886
- } catch (e) {
61887
- log.debug(`local-agent: failed to persist binding cleanup: ${e.message}`);
61888
- }
61889
61856
  }
61890
61857
  const migrated = {};
61891
- let migrationChanged = false;
61892
61858
  for (const [wsPath, binding] of Object.entries(config.workspaceBindings)) {
61893
61859
  const canonicalKey = await canonicalizeWorkspacePath(wsPath);
61894
61860
  if (canonicalKey !== wsPath) migrationChanged = true;
@@ -61896,14 +61862,14 @@ async function loadLocalAgentConfig(options2 = {}) {
61896
61862
  migrated[canonicalKey] = mergeWorkspaceBindings(migrated[canonicalKey], binding, canonicalKey);
61897
61863
  }
61898
61864
  config.workspaceBindings = migrated;
61899
- const migratedEnrollments = {};
61900
- for (const [wsPath, enrollment] of Object.entries(config.workspaceEnrollments)) {
61901
- const canonicalKey = await canonicalizeWorkspacePath(wsPath);
61902
- if (canonicalKey !== wsPath || migratedEnrollments[canonicalKey]) migrationChanged = true;
61903
- migratedEnrollments[canonicalKey] ??= enrollment;
61865
+ if (fileConfig.workspaceStateVersion !== 1) {
61866
+ for (const binding of Object.values(config.workspaceBindings)) {
61867
+ if (binding.projectId > 0) binding.enrolledAt ??= binding.boundAt;
61868
+ }
61869
+ config.workspaceStateVersion = 1;
61870
+ migrationChanged = true;
61904
61871
  }
61905
- config.workspaceEnrollments = migratedEnrollments;
61906
- if ((migrationChanged || enrollmentMigrationChanged) && !options2.dryRun) {
61872
+ if (migrationChanged && !options2.dryRun) {
61907
61873
  await saveLocalAgentConfig(config);
61908
61874
  }
61909
61875
  return config;
@@ -61918,7 +61884,7 @@ async function loadLocalAgentConfig(options2 = {}) {
61918
61884
  endpoint,
61919
61885
  token,
61920
61886
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
61921
- workspaceEnrollments: {},
61887
+ workspaceStateVersion: 1,
61922
61888
  workspaceBindings: {}
61923
61889
  };
61924
61890
  if (options2.dryRun) return backfilled;
@@ -61936,7 +61902,7 @@ async function loadLocalAgentConfig(options2 = {}) {
61936
61902
  endpoint: normalizeEndpoint(envEndpoint),
61937
61903
  token: process.env.TEAMAI_API_TOKEN ?? process.env.TEAMAI_TOKEN,
61938
61904
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
61939
- workspaceEnrollments: {},
61905
+ workspaceStateVersion: 1,
61940
61906
  workspaceBindings: {}
61941
61907
  };
61942
61908
  }
@@ -61944,7 +61910,7 @@ async function saveLocalAgentConfig(config) {
61944
61910
  await writeJsonAtomic(getConfigPath(), {
61945
61911
  ...config,
61946
61912
  endpoint: normalizeEndpoint(config.endpoint),
61947
- workspaceEnrollments: config.workspaceEnrollments ?? {},
61913
+ workspaceStateVersion: 1,
61948
61914
  workspaceBindings: config.workspaceBindings ?? {}
61949
61915
  });
61950
61916
  }
@@ -62352,14 +62318,15 @@ async function persistWorkspaceBinding(config, cwd, resolvedPath, projectId, pro
62352
62318
  await saveLocalAgentConfig(config);
62353
62319
  }
62354
62320
  async function enrollWorkspace(config, resolvedPath) {
62355
- const enrollments = workspaceEnrollmentsOf(config);
62356
- enrollments[resolvedPath] ??= { enrolledAt: (/* @__PURE__ */ new Date()).toISOString() };
62321
+ const enrolledAt = (/* @__PURE__ */ new Date()).toISOString();
62322
+ const binding = config.workspaceBindings[resolvedPath];
62323
+ config.workspaceBindings[resolvedPath] = binding ? { ...binding, enrolledAt: binding.enrolledAt ?? enrolledAt } : { projectId: 0, boundAt: enrolledAt, enrolledAt };
62357
62324
  }
62358
62325
  async function inheritWorktreeBinding(config, cwd, resolvedPath) {
62359
62326
  const anchors = await resolveAnchors(cwd);
62360
62327
  if (!anchors || anchors.projectAnchor === anchors.workspaceRoot) return false;
62361
62328
  const anchorBinding = config.workspaceBindings[anchors.projectAnchor];
62362
- if (!anchorBinding) return false;
62329
+ if (!hasWorkspaceBindingDecision(anchorBinding)) return false;
62363
62330
  config.workspaceBindings[resolvedPath] = {
62364
62331
  ...config.workspaceBindings[resolvedPath],
62365
62332
  projectId: anchorBinding.projectId,
@@ -62374,7 +62341,7 @@ async function bindWorkspaceToProject(workspacePath, projectId) {
62374
62341
  if (!config) {
62375
62342
  throw new Error("HTTP local agent is not initialized. Run `teamai init --http <ENDPOINT> --token <API_TOKEN>` first.");
62376
62343
  }
62377
- if (!workspaceEnrollmentsOf(config)[workspacePath]) {
62344
+ if (!isWorkspaceEnrolled(config, workspacePath)) {
62378
62345
  throw new Error(
62379
62346
  "Current workspace is not enrolled. Run `teamai init --http <ENDPOINT> --token <API_TOKEN>` here first."
62380
62347
  );
@@ -62392,8 +62359,8 @@ async function bindWorkspaceToProject(workspacePath, projectId) {
62392
62359
  return binding;
62393
62360
  }
62394
62361
  async function ensureWorkspaceBinding(config, workspacePath, sessionId, cwd) {
62395
- if (!workspaceEnrollmentsOf(config)[workspacePath]) return;
62396
- if (config.workspaceBindings[workspacePath]) return;
62362
+ if (!isWorkspaceEnrolled(config, workspacePath)) return;
62363
+ if (hasWorkspaceBindingDecision(config.workspaceBindings[workspacePath])) return;
62397
62364
  if (await inheritWorktreeBinding(config, cwd, workspacePath)) return;
62398
62365
  const markerKey = sessionId || `ppid-${process.ppid}`;
62399
62366
  const hintMarker = path76.join(os9.tmpdir(), `teamai-bind-session-${markerKey}`);
@@ -62441,8 +62408,8 @@ function isBindPromptTool(tool) {
62441
62408
  return kind === "codebuddy" || kind === "workbuddy";
62442
62409
  }
62443
62410
  async function emitBindingHint(config, workspacePath, sessionId, cwd) {
62444
- if (!workspaceEnrollmentsOf(config)[workspacePath]) return;
62445
- if (config.workspaceBindings[workspacePath]) return;
62411
+ if (!isWorkspaceEnrolled(config, workspacePath)) return;
62412
+ if (hasWorkspaceBindingDecision(config.workspaceBindings[workspacePath])) return;
62446
62413
  if (await inheritWorktreeBinding(config, cwd, workspacePath)) return;
62447
62414
  const markerKey = sessionId || `ppid-${process.ppid}`;
62448
62415
  const hintMarker = path76.join(os9.tmpdir(), `teamai-bind-hint-${markerKey}`);
@@ -62641,14 +62608,9 @@ async function scanModelsFromDisk(tool, workspacePath) {
62641
62608
  }
62642
62609
  async function pruneDeadWorkspaceBindings(config) {
62643
62610
  let changed = false;
62644
- const allPaths = /* @__PURE__ */ new Set([
62645
- ...Object.keys(config.workspaceBindings),
62646
- ...Object.keys(workspaceEnrollmentsOf(config))
62647
- ]);
62648
- for (const workspacePath of allPaths) {
62611
+ for (const workspacePath of Object.keys(config.workspaceBindings)) {
62649
62612
  if (await isUserHomeDir(workspacePath)) {
62650
62613
  delete config.workspaceBindings[workspacePath];
62651
- delete workspaceEnrollmentsOf(config)[workspacePath];
62652
62614
  changed = true;
62653
62615
  continue;
62654
62616
  }
@@ -62657,7 +62619,6 @@ async function pruneDeadWorkspaceBindings(config) {
62657
62619
  } catch (error2) {
62658
62620
  if (error2.code === "ENOENT") {
62659
62621
  delete config.workspaceBindings[workspacePath];
62660
- delete workspaceEnrollmentsOf(config)[workspacePath];
62661
62622
  changed = true;
62662
62623
  } else {
62663
62624
  log.debug(
@@ -62670,26 +62631,18 @@ async function pruneDeadWorkspaceBindings(config) {
62670
62631
  }
62671
62632
  function stampWorkspaceTool(config, currentPath, tool) {
62672
62633
  if (!currentPath) return false;
62673
- const enrollment = workspaceEnrollmentsOf(config)[currentPath];
62674
- if (!enrollment) return false;
62675
62634
  const binding = config.workspaceBindings[currentPath];
62635
+ if (!binding?.enrolledAt) return false;
62676
62636
  const normalized = normalizeAgentType(tool);
62677
- let changed = false;
62678
- if (enrollment.ideType !== normalized) {
62679
- enrollment.ideType = normalized;
62680
- changed = true;
62681
- }
62682
- if (binding && binding.ideType !== normalized) {
62683
- binding.ideType = normalized;
62684
- changed = true;
62685
- }
62686
- return changed;
62637
+ if (binding.ideType === normalized) return false;
62638
+ binding.ideType = normalized;
62639
+ return true;
62687
62640
  }
62688
62641
  function selectToolWorkspaces(config, currentPath, currentTool) {
62689
- return Object.keys(workspaceEnrollmentsOf(config)).filter((wsPath) => {
62690
- const enrollment = workspaceEnrollmentsOf(config)[wsPath];
62642
+ return Object.keys(config.workspaceBindings).filter((wsPath) => {
62691
62643
  const b2 = config.workspaceBindings[wsPath];
62692
- const wsTool = enrollment.ideType ?? b2?.ideType ?? (wsPath === currentPath ? currentTool : void 0);
62644
+ if (!b2.enrolledAt) return false;
62645
+ const wsTool = b2.ideType ?? (wsPath === currentPath ? currentTool : void 0);
62693
62646
  return wsTool === currentTool;
62694
62647
  });
62695
62648
  }
@@ -62751,7 +62704,7 @@ async function buildReportPayload(config, context) {
62751
62704
  name: path76.basename(wsPath),
62752
62705
  ide_type: currentTool
62753
62706
  };
62754
- if (wsBinding && wsBinding.projectId > 0) workspace.project_id = wsBinding.projectId;
62707
+ if (wsBinding.projectId > 0) workspace.project_id = wsBinding.projectId;
62755
62708
  if (wsScope.skills.length > 0) workspace.skills = wsScope.skills;
62756
62709
  if (wsScope.rules.length > 0) workspace.rules = wsScope.rules;
62757
62710
  const wsMcps = await scanMcpFromManifest("project", tool, wsPath);
@@ -62780,7 +62733,7 @@ async function buildSyncPayload(config, context) {
62780
62733
  path: wsPath,
62781
62734
  name: path76.basename(wsPath),
62782
62735
  ide_type: currentTool,
62783
- ...wsBinding && wsBinding.projectId > 0 ? { project_id: wsBinding.projectId } : {}
62736
+ ...wsBinding.projectId > 0 ? { project_id: wsBinding.projectId } : {}
62784
62737
  };
62785
62738
  });
62786
62739
  }
@@ -63852,7 +63805,7 @@ async function applyModelConfig(config, command, context) {
63852
63805
  if (scope === "project" && !workspacePath) {
63853
63806
  throw new Error("apply_model_config: workspace command is missing workspace_path");
63854
63807
  }
63855
- if (workspacePath && workspaceEnrollmentsOf(config)[workspacePath] === void 0) {
63808
+ if (workspacePath && !isWorkspaceEnrolled(config, workspacePath)) {
63856
63809
  throw new Error(
63857
63810
  `apply_model_config: workspace "${path76.basename(workspacePath)}" is not enrolled`
63858
63811
  );
@@ -64377,7 +64330,7 @@ async function executeCommand(config, command, context) {
64377
64330
  if (normalizeScope(command.scope) === "project") {
64378
64331
  const workspacePath2 = await resolveWorkspacePath(command.workspace_path ?? context.cwd);
64379
64332
  if (!workspacePath2) throw new Error(`${command.type ?? "workspace command"} is missing workspace_path`);
64380
- if (!workspaceEnrollmentsOf(config)[workspacePath2]) {
64333
+ if (!isWorkspaceEnrolled(config, workspacePath2)) {
64381
64334
  throw new Error(`Workspace is not enrolled for TeamAI management: ${workspacePath2}`);
64382
64335
  }
64383
64336
  if (command.project_id !== void 0) {
@@ -64472,7 +64425,7 @@ async function syncLocalAgent(context) {
64472
64425
  if (!config) return false;
64473
64426
  const workspacePath = await resolveWorkspacePath(context.cwd);
64474
64427
  if (isBindPromptEnabled() && isBindPromptTool(context.tool)) {
64475
- if (workspacePath && workspaceEnrollmentsOf(config)[workspacePath]) {
64428
+ if (workspacePath && isWorkspaceEnrolled(config, workspacePath)) {
64476
64429
  const sid = context.event?.sessionId;
64477
64430
  if (context.event?.type === "session_start") {
64478
64431
  await ensureWorkspaceBinding(config, workspacePath, sid, context.cwd);
@@ -64674,7 +64627,7 @@ async function initLocalAgentHttp(options2) {
64674
64627
  endpoint,
64675
64628
  token: options2.token ?? existing2?.token,
64676
64629
  createdAt: existing2?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
64677
- workspaceEnrollments: existing2?.workspaceEnrollments ?? {},
64630
+ workspaceStateVersion: 1,
64678
64631
  workspaceBindings: existing2?.workspaceBindings ?? {},
64679
64632
  userGroupId: existing2?.userGroupId,
64680
64633
  userGroupName: existing2?.userGroupName
@@ -64922,18 +64875,12 @@ async function bindCurrentProject(options2) {
64922
64875
  if (!config) {
64923
64876
  throw new Error("Local agent not initialized. Run `teamai init --http` first.");
64924
64877
  }
64925
- if (!workspaceEnrollmentsOf(config)[workspacePath]) {
64878
+ if (!isWorkspaceEnrolled(config, workspacePath)) {
64926
64879
  throw new Error(
64927
64880
  "Current workspace is not enrolled. Run `teamai init --http <ENDPOINT> --token <API_TOKEN>` here first."
64928
64881
  );
64929
64882
  }
64930
- await persistWorkspaceBinding(
64931
- config,
64932
- options2?.cwd ?? process.cwd(),
64933
- workspacePath,
64934
- 0,
64935
- "__skipped__"
64936
- );
64883
+ await persistWorkspaceBinding(config, options2?.cwd ?? process.cwd(), workspacePath, 0, "__skipped__");
64937
64884
  log.info(`\u5DF2\u8DF3\u8FC7\u7ED1\u5B9A\uFF0C\u4EE5\u540E\u4E0D\u518D\u63D0\u793A\u6B64\u5DE5\u4F5C\u533A\u3002`);
64938
64885
  return;
64939
64886
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-ai/teamai-cli",
3
- "version": "0.30.2",
3
+ "version": "0.30.4",
4
4
  "description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {