@teammates/cli 0.3.4 → 0.4.1

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.
@@ -8,7 +8,8 @@
8
8
  export declare function buildTitle(word: string): [string, string];
9
9
  export interface StartupInfo {
10
10
  version: string;
11
- adapterName: string;
11
+ /** Display name shown in the banner (user alias or adapter name). */
12
+ displayName: string;
12
13
  teammateCount: number;
13
14
  cwd: string;
14
15
  recallInstalled: boolean;
@@ -80,7 +80,7 @@ export async function playStartup(info) {
80
80
  const tmWidth = tmTop.length; // "▀█▀ █▀▄▀█" = 9 chars
81
81
  const gap = " ";
82
82
  // Build info lines to sit to the right of TM
83
- const rightLine1 = chalk.white(info.adapterName) +
83
+ const rightLine1 = chalk.white(info.displayName) +
84
84
  chalk.gray(` · ${info.teammateCount} teammate${info.teammateCount === 1 ? "" : "s"}`) +
85
85
  chalk.gray(` · v${info.version}`);
86
86
  const rightLine2 = chalk.gray(info.cwd);
package/dist/index.d.ts CHANGED
@@ -9,4 +9,4 @@ export { findTeammatesDir, PKG_VERSION, parseCliArgs } from "./cli-args.js";
9
9
  export { Orchestrator, type OrchestratorConfig, type TeammateStatus, } from "./orchestrator.js";
10
10
  export { Registry } from "./registry.js";
11
11
  export { tp } from "./theme.js";
12
- export type { DailyLog, HandoffEnvelope, OrchestratorEvent, OwnershipRules, QueueEntry, SandboxLevel, SlashCommand, TaskAssignment, TaskResult, TeammateConfig, } from "./types.js";
12
+ export type { DailyLog, HandoffEnvelope, OrchestratorEvent, OwnershipRules, PresenceState, QueueEntry, SandboxLevel, SlashCommand, TaskAssignment, TaskResult, TeammateConfig, TeammateType, } from "./types.js";
@@ -19,6 +19,8 @@ export interface OrchestratorConfig {
19
19
  }
20
20
  export interface TeammateStatus {
21
21
  state: "idle" | "working";
22
+ /** Presence for display: online (green), offline (red), reachable (yellow) */
23
+ presence: import("./types.js").PresenceState;
22
24
  lastSummary?: string;
23
25
  lastChangedFiles?: string[];
24
26
  lastTimestamp?: Date;
@@ -20,7 +20,10 @@ export class Orchestrator {
20
20
  async init() {
21
21
  await this.registry.loadAll();
22
22
  for (const name of this.registry.list()) {
23
- this.statuses.set(name, { state: "idle" });
23
+ const config = this.registry.get(name);
24
+ // AI teammates are always online; humans start as offline
25
+ const presence = config?.type === "human" ? "offline" : "online";
26
+ this.statuses.set(name, { state: "idle", presence });
24
27
  }
25
28
  }
26
29
  /** Get status for a teammate */
@@ -59,7 +62,8 @@ export class Orchestrator {
59
62
  };
60
63
  }
61
64
  this.onEvent({ type: "task_assigned", assignment });
62
- this.statuses.set(assignment.teammate, { state: "working" });
65
+ const prevPresence = this.statuses.get(assignment.teammate)?.presence ?? "online";
66
+ this.statuses.set(assignment.teammate, { state: "working", presence: prevPresence });
63
67
  // Get or create session
64
68
  let sessionId = this.sessions.get(assignment.teammate);
65
69
  if (!sessionId) {
@@ -74,9 +78,11 @@ export class Orchestrator {
74
78
  // Execute
75
79
  const result = await this.adapter.executeTask(sessionId, teammate, prompt);
76
80
  this.onEvent({ type: "task_completed", result });
77
- // Update status
81
+ // Update status (preserve presence)
82
+ const postPresence = this.statuses.get(assignment.teammate)?.presence ?? "online";
78
83
  this.statuses.set(assignment.teammate, {
79
84
  state: "idle",
85
+ presence: postPresence,
80
86
  lastSummary: result.summary,
81
87
  lastChangedFiles: result.changedFiles,
82
88
  lastTimestamp: new Date(),
@@ -128,7 +134,7 @@ export class Orchestrator {
128
134
  }
129
135
  }
130
136
  // Require a meaningful match — weak/ambiguous scores fall through
131
- // so the caller can default to the base coding agent
137
+ // so the caller can default to the user's agent
132
138
  if (bestScore < 2)
133
139
  return null;
134
140
  return bestMatch;
@@ -144,12 +150,6 @@ export class Orchestrator {
144
150
  for (const [name, config] of this.registry.all()) {
145
151
  roster.push({ name, role: config.role, ownership: config.ownership });
146
152
  }
147
- // Include the base agent as an option
148
- roster.push({
149
- name: this.adapter.name,
150
- role: "General-purpose coding agent",
151
- ownership: { primary: [], secondary: [] },
152
- });
153
153
  return this.adapter.routeTask(task, roster);
154
154
  }
155
155
  /**
@@ -162,7 +162,9 @@ export class Orchestrator {
162
162
  const added = [];
163
163
  for (const name of this.registry.list()) {
164
164
  if (!before.has(name)) {
165
- this.statuses.set(name, { state: "idle" });
165
+ const config = this.registry.get(name);
166
+ const presence = config?.type === "human" ? "offline" : "online";
167
+ this.statuses.set(name, { state: "idle", presence });
166
168
  added.push(name);
167
169
  }
168
170
  }
@@ -177,7 +179,8 @@ export class Orchestrator {
177
179
  }
178
180
  this.sessions.clear();
179
181
  for (const name of this.registry.list()) {
180
- this.statuses.set(name, { state: "idle" });
182
+ const prevPresence = this.statuses.get(name)?.presence ?? "online";
183
+ this.statuses.set(name, { state: "idle", presence: prevPresence });
181
184
  }
182
185
  }
183
186
  /** Destroy all sessions */
@@ -3,6 +3,7 @@ import { Orchestrator } from "./orchestrator.js";
3
3
  function makeTeammate(name, role = "Test role.", primary = []) {
4
4
  return {
5
5
  name,
6
+ type: "ai",
6
7
  role,
7
8
  soul: `# ${name}\n\n${role}`,
8
9
  wisdom: "",
@@ -45,7 +46,7 @@ function createOrchestrator(teammates, adapter, onEvent) {
45
46
  }
46
47
  // Initialize statuses
47
48
  for (const t of teammates) {
48
- orch.getAllStatuses().set(t.name, { state: "idle" });
49
+ orch.getAllStatuses().set(t.name, { state: "idle", presence: "online" });
49
50
  }
50
51
  return { orch, adapter: mockAdapter };
51
52
  }
package/dist/registry.js CHANGED
@@ -46,8 +46,10 @@ export class Registry {
46
46
  const ownership = parseOwnership(soul);
47
47
  const role = parseRole(soul);
48
48
  const routingKeywords = parseRoutingKeywords(soul);
49
+ const type = parseType(soul);
49
50
  const config = {
50
51
  name,
52
+ type,
51
53
  role,
52
54
  soul,
53
55
  wisdom,
@@ -183,6 +185,11 @@ function parseRoutingKeywords(soul) {
183
185
  return [];
184
186
  return extractPatterns(routingMatch[0]);
185
187
  }
188
+ /** Parse type (human or ai) from SOUL.md — looks for **Type:** human */
189
+ function parseType(soul) {
190
+ const match = soul.match(/\*\*Type:\*\*\s*(human|ai)/i);
191
+ return match && match[1].toLowerCase() === "human" ? "human" : "ai";
192
+ }
186
193
  /** Extract file patterns (backtick-wrapped) from a markdown section */
187
194
  function extractPatterns(section) {
188
195
  const patterns = [];
@@ -208,6 +208,7 @@ describe("Registry.register", () => {
208
208
  const registry = new Registry(tempDir);
209
209
  registry.register({
210
210
  name: "test",
211
+ type: "ai",
211
212
  role: "Test role.",
212
213
  soul: "# Test",
213
214
  wisdom: "",
package/dist/types.d.ts CHANGED
@@ -3,10 +3,16 @@
3
3
  */
4
4
  /** Sandbox level controlling what a teammate can do */
5
5
  export type SandboxLevel = "read-only" | "workspace-write" | "danger-full-access";
6
+ /** Whether this teammate is a human avatar or an AI agent */
7
+ export type TeammateType = "human" | "ai";
8
+ /** Presence state for /status display */
9
+ export type PresenceState = "online" | "offline" | "reachable";
6
10
  /** A teammate's loaded configuration */
7
11
  export interface TeammateConfig {
8
12
  /** Teammate name (folder name under .teammates/) */
9
13
  name: string;
14
+ /** Whether this is a human avatar or AI teammate */
15
+ type: TeammateType;
10
16
  /** Role description from SOUL.md */
11
17
  role: string;
12
18
  /** Full SOUL.md content */
@@ -124,6 +130,10 @@ export type QueueEntry = {
124
130
  type: "debug";
125
131
  teammate: string;
126
132
  task: string;
133
+ } | {
134
+ type: "summarize";
135
+ teammate: string;
136
+ task: string;
127
137
  };
128
138
  /** A registered slash command. */
129
139
  export interface SlashCommand {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teammates/cli",
3
- "version": "0.3.4",
3
+ "version": "0.4.1",
4
4
  "description": "Agent-agnostic CLI for teammates. Routes tasks, manages handoffs, and plugs into any coding agent backend.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -33,8 +33,8 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "@github/copilot-sdk": "^0.1.32",
36
- "@teammates/consolonia": "0.3.4",
37
- "@teammates/recall": "0.3.4",
36
+ "@teammates/consolonia": "0.4.0",
37
+ "@teammates/recall": "0.4.0",
38
38
  "chalk": "^5.6.2",
39
39
  "ora": "^9.3.0"
40
40
  },