refactor: remove legacy teamctl CLI support and update package description

- Removed the legacy teamctl CLI integration, including the associated source code and references in the main module.
- Updated the package description to reflect the current functionality without legacy support.
- Cleaned up build scripts by removing unnecessary executable permissions and legacy file handling.
- Adjusted tests and documentation to remove references to the deprecated CLI.
This commit is contained in:
iliya 2026-03-08 22:50:57 +02:00
parent df962a21d3
commit 7a7e2e1f12
12 changed files with 19 additions and 1629 deletions

View file

@ -2,12 +2,9 @@
"name": "agent-teams-controller",
"version": "1.0.0",
"private": true,
"description": "Controller package for Claude agent teams operations and legacy teamctl CLI compatibility",
"description": "Controller package for Claude agent teams operations",
"type": "commonjs",
"main": "src/index.js",
"bin": {
"teamctl": "src/cli.js"
},
"files": [
"dist"
],

View file

@ -1,4 +1,4 @@
import { chmod, copyFile, mkdir, readdir, rm, stat } from 'node:fs/promises';
import { copyFile, mkdir, readdir, rm } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@ -29,9 +29,3 @@ async function copyRecursive(sourceDir, targetDir) {
await rm(distDir, { recursive: true, force: true });
await mkdir(distDir, { recursive: true });
await copyRecursive(srcDir, distDir);
for (const executablePath of ['cli.js', path.join('legacy', 'teamctl.cli.js')]) {
const absPath = path.join(distDir, executablePath);
const info = await stat(absPath);
await chmod(absPath, info.mode | 0o111);
}

View file

@ -1,4 +0,0 @@
#!/usr/bin/env node
'use strict';
require('./legacy/teamctl.cli.js');

View file

@ -1,17 +1,5 @@
const fs = require('fs');
const path = require('path');
const controller = require('./controller.js');
function getLegacyTeamctlCliPath() {
return path.join(__dirname, 'legacy', 'teamctl.cli.js');
}
function readLegacyTeamctlCliSource() {
return fs.readFileSync(getLegacyTeamctlCliPath(), 'utf8');
}
module.exports = {
...controller,
getLegacyTeamctlCliPath,
readLegacyTeamctlCliSource,
};

File diff suppressed because it is too large Load diff

View file

@ -1,13 +0,0 @@
const { readLegacyTeamctlCliSource } = require('../src/index.js');
describe('agent-teams-controller legacy teamctl source', () => {
it('exposes the extracted CLI source', () => {
const source = readLegacyTeamctlCliSource();
expect(source.startsWith('#!/usr/bin/env node')).toBe(true);
expect(source).toContain("if (domain === 'task')");
expect(source).toContain("if (domain === 'process')");
expect(source).toContain('task comment-attach');
expect(source).toContain('process unregister --id <uuid>');
});
});

View file

@ -135,7 +135,7 @@ export class ChangeExtractorService {
2. Парсить файлы, ища маркеры `TaskUpdate` tool_use:
- `input.taskId === taskId && input.status === 'in_progress'` → начало
- `input.taskId === taskId && input.status === 'completed'` → конец
3. Альтернативно: Bash teamctl `task start|complete <id>` (regex)
3. Альтернативно: исторические Bash teamctl логи `task start|complete <id>` (regex)
4. Все tool_use Edit/Write между start и end маркерами = изменения задачи
5. Если 86% кейс (1 задача в сессии): вся сессия = задача

View file

@ -22,7 +22,7 @@ export interface TaskBoundary {
/** ISO timestamp из JSONL entry */
timestamp: string;
/** Каким механизмом обнаружено */
mechanism: 'TaskUpdate' | 'teamctl';
mechanism: 'TaskUpdate' | 'teamctl'; // historical legacy mechanism
/** tool_use id (для link к конкретному блоку) */
toolUseId?: string;
}
@ -61,7 +61,7 @@ export interface TaskBoundariesResult {
/** True если сессия работала только с одной задачей */
isSingleTaskSession: boolean;
/** Механизм обнаружения (один на сессию — никогда не смешиваются!) */
detectedMechanism: 'TaskUpdate' | 'teamctl' | 'none';
detectedMechanism: 'TaskUpdate' | 'teamctl' | 'none'; // historical legacy mechanism in this design note
}
/** Расширенный TaskChangeSet с confidence деталями.
@ -77,7 +77,7 @@ export interface TaskChangeSetV2 extends TaskChangeSet {
### 2. Сервис: `src/main/services/team/TaskBoundaryParser.ts` (NEW)
**Задача**: Парсить JSONL файлы субагентов для извлечения `TaskUpdate` и `teamctl` маркеров задач.
**Задача**: Парсить JSONL файлы субагентов для извлечения `TaskUpdate` и исторических `teamctl` маркеров задач.
**Ключевой факт**: Механизмы НИКОГДА не смешиваются в одной сессии (0 из 351 проверенных). Это означает один pass по JSONL для определения механизма + extraction.
@ -90,7 +90,7 @@ export class TaskBoundaryParser {
private readonly CACHE_TTL = 60 * 1000; // 1 мин (не 3 — JSONL файлы меняются часто при активной работе)
/**
* Парсит JSONL файл и извлекает все TaskUpdate/teamctl маркеры.
* Парсит JSONL файл и извлекает все TaskUpdate/исторические teamctl маркеры.
*
* Один проход по файлу, O(n) по количеству строк.
*/
@ -101,8 +101,8 @@ export class TaskBoundaryParser {
*
* Алгоритм:
* 1. Найти все TaskBoundary для taskId
* 2. Start boundary = TaskUpdate(in_progress) или teamctl(start)
* 3. End boundary = TaskUpdate(completed) или teamctl(complete)
* 2. Start boundary = TaskUpdate(in_progress) или historical teamctl(start)
* 3. End boundary = TaskUpdate(completed) или historical teamctl(complete)
* 4. Scope = все tool_use между start.lineNumber и end.lineNumber
* 5. Если single-task session: scope = весь файл
*/

View file

@ -559,7 +559,7 @@ EditorView.theme({
- 100% парсируемо — `input.taskId` + `input.status`
- Tool result: `"Updated task #1 status"` (текст)
**Механизм B: Bash `teamctl.js`** (44 сессии)
**Механизм B: исторический Bash `teamctl.js`** (44 сессии, legacy)
```bash
node "$HOME/.claude/tools/teamctl.js" --team "<team>" task start|complete|set-status <id>
```
@ -600,7 +600,7 @@ parseTaskBoundaries(sessionJsonl) → Map<taskId, tool_use_ids[]>
- status == "in_progress" → TASK_START(taskId, line)
- status == "completed" → TASK_END(taskId, line)
2. Детектировать Bash teamctl:
2. Детектировать исторические Bash teamctl вызовы:
- "task start <id>" → TASK_START(taskId, line)
- "task complete <id>" → TASK_END(taskId, line)
@ -625,7 +625,7 @@ parseTaskBoundaries(sessionJsonl) → Map<taskId, tool_use_ids[]>
### Как достичь 95%+
1. **Добавить парсинг `TaskUpdate` tool_use** (name == "TaskUpdate", input.taskId, input.status)
2. **Сохранить Bash teamctl regex** для остальных 12.5%
2. **Сохранить regex для исторических Bash teamctl логов** для остальных 12.5%
3. Для single-task сессий (86%): вся сессия = задача (100%)
4. Для multi-task: маркеры start/end как границы сегментов
@ -644,7 +644,7 @@ parseTaskBoundaries(sessionJsonl) → Map<taskId, tool_use_ids[]>
- Решает проблему subagent'ов без `toolUseResult`
### Per-Task Scoping: Структурные маркеры = 95%+
- `TaskUpdate` tool_use + Bash teamctl = 100% парсируемые маркеры
- `TaskUpdate` tool_use + исторические Bash teamctl логи = 100% парсируемые маркеры
- 86% сессий = 1 задача → 100% надёжность
- Улучшение с ~85% (text search) до 95%+ (структурный парсинг)

View file

@ -96,8 +96,8 @@ From `MarkdownViewer.tsx` (lines 561-571):
### Identified scenarios
1. **Task descriptions from CLI tooling (teamctl.js / Claude agents)**
Task descriptions are set via `node teamctl.js task create` or `TaskCreate` tool calls. Agents typically write plain text descriptions, not markdown. The description content itself lacks formatting — MarkdownViewer renders it correctly, but there's nothing to format.
1. **Task descriptions from task tooling / Claude agents**
Historically this included `teamctl.js`; the current architecture uses controller/MCP-based task operations. In both cases, agents typically write plain text descriptions, not markdown. The description content itself lacks formatting — MarkdownViewer renders it correctly, but there's nothing to format.
2. **Task comments from agents**
Same issue — `task comment --text "..."` passes plain text. However, TaskCommentsSection.tsx (line 246) correctly uses `<MarkdownViewer content={displayText} bare />`.
@ -125,7 +125,7 @@ From `MarkdownViewer.tsx` (lines 561-571):
The pipeline is correct. All text display surfaces that show long-form content already use MarkdownViewer.
### Fix 2: If specific content appears unformatted, the fix is upstream
Ensure that agents/tooling that create tasks or comments use markdown formatting in their text. For example, the `teamctl.js` task create command could document that `--description` supports markdown.
Ensure that agents/tooling that create tasks or comments use markdown formatting in their text. In the current architecture, this guidance applies to controller/MCP-backed task creation rather than the removed `teamctl.js` CLI.
### Fix 3 (Optional): ReplyQuoteBlock markdown support
`ReplyQuoteBlock` renders the reply body. If it currently shows plain text, wrap body content in `<MarkdownViewer bare />` for consistency. (Needs verification — separate from this research scope.)

View file

@ -280,7 +280,7 @@ export const ActivityItem = ({
if (structured) return null;
const stripped = stripAgentBlocks(message.text).trim();
if (!stripped) return null; // All content was agent-only blocks → show summary instead
// Normalize literal \n from CLI tools (teamctl.js) to real newlines
// Normalize literal \n from historical CLI-produced text to real newlines
return stripped.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
}, [structured, message.text]);

View file

@ -27,8 +27,8 @@ import type { ResolvedTeamMember, TaskAttachmentMeta, TaskComment } from '@share
/**
* Convert literal backslash-n sequences to real newlines.
* CLI tools (teamctl.js) may store `\n` as literal text when
* shell double-quotes don't interpret escape sequences.
* Historical CLI-produced comments may store `\n` as literal text
* when shell double-quotes don't interpret escape sequences.
*/
function normalizeLiteralNewlines(text: string): string {
return text.replace(/\\n/g, '\n').replace(/\\t/g, '\t');