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:
parent
df962a21d3
commit
7a7e2e1f12
12 changed files with 19 additions and 1629 deletions
|
|
@ -2,12 +2,9 @@
|
||||||
"name": "agent-teams-controller",
|
"name": "agent-teams-controller",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"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",
|
"type": "commonjs",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
|
||||||
"teamctl": "src/cli.js"
|
|
||||||
},
|
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -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 path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
|
@ -29,9 +29,3 @@ async function copyRecursive(sourceDir, targetDir) {
|
||||||
await rm(distDir, { recursive: true, force: true });
|
await rm(distDir, { recursive: true, force: true });
|
||||||
await mkdir(distDir, { recursive: true });
|
await mkdir(distDir, { recursive: true });
|
||||||
await copyRecursive(srcDir, distDir);
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
require('./legacy/teamctl.cli.js');
|
|
||||||
|
|
@ -1,17 +1,5 @@
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const controller = require('./controller.js');
|
const controller = require('./controller.js');
|
||||||
|
|
||||||
function getLegacyTeamctlCliPath() {
|
|
||||||
return path.join(__dirname, 'legacy', 'teamctl.cli.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
function readLegacyTeamctlCliSource() {
|
|
||||||
return fs.readFileSync(getLegacyTeamctlCliPath(), 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...controller,
|
...controller,
|
||||||
getLegacyTeamctlCliPath,
|
|
||||||
readLegacyTeamctlCliSource,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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>');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -135,7 +135,7 @@ export class ChangeExtractorService {
|
||||||
2. Парсить файлы, ища маркеры `TaskUpdate` tool_use:
|
2. Парсить файлы, ища маркеры `TaskUpdate` tool_use:
|
||||||
- `input.taskId === taskId && input.status === 'in_progress'` → начало
|
- `input.taskId === taskId && input.status === 'in_progress'` → начало
|
||||||
- `input.taskId === taskId && input.status === 'completed'` → конец
|
- `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 маркерами = изменения задачи
|
4. Все tool_use Edit/Write между start и end маркерами = изменения задачи
|
||||||
5. Если 86% кейс (1 задача в сессии): вся сессия = задача
|
5. Если 86% кейс (1 задача в сессии): вся сессия = задача
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export interface TaskBoundary {
|
||||||
/** ISO timestamp из JSONL entry */
|
/** ISO timestamp из JSONL entry */
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
/** Каким механизмом обнаружено */
|
/** Каким механизмом обнаружено */
|
||||||
mechanism: 'TaskUpdate' | 'teamctl';
|
mechanism: 'TaskUpdate' | 'teamctl'; // historical legacy mechanism
|
||||||
/** tool_use id (для link к конкретному блоку) */
|
/** tool_use id (для link к конкретному блоку) */
|
||||||
toolUseId?: string;
|
toolUseId?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ export interface TaskBoundariesResult {
|
||||||
/** True если сессия работала только с одной задачей */
|
/** True если сессия работала только с одной задачей */
|
||||||
isSingleTaskSession: boolean;
|
isSingleTaskSession: boolean;
|
||||||
/** Механизм обнаружения (один на сессию — никогда не смешиваются!) */
|
/** Механизм обнаружения (один на сессию — никогда не смешиваются!) */
|
||||||
detectedMechanism: 'TaskUpdate' | 'teamctl' | 'none';
|
detectedMechanism: 'TaskUpdate' | 'teamctl' | 'none'; // historical legacy mechanism in this design note
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Расширенный TaskChangeSet с confidence деталями.
|
/** Расширенный TaskChangeSet с confidence деталями.
|
||||||
|
|
@ -77,7 +77,7 @@ export interface TaskChangeSetV2 extends TaskChangeSet {
|
||||||
|
|
||||||
### 2. Сервис: `src/main/services/team/TaskBoundaryParser.ts` (NEW)
|
### 2. Сервис: `src/main/services/team/TaskBoundaryParser.ts` (NEW)
|
||||||
|
|
||||||
**Задача**: Парсить JSONL файлы субагентов для извлечения `TaskUpdate` и `teamctl` маркеров задач.
|
**Задача**: Парсить JSONL файлы субагентов для извлечения `TaskUpdate` и исторических `teamctl` маркеров задач.
|
||||||
|
|
||||||
**Ключевой факт**: Механизмы НИКОГДА не смешиваются в одной сессии (0 из 351 проверенных). Это означает один pass по JSONL для определения механизма + extraction.
|
**Ключевой факт**: Механизмы НИКОГДА не смешиваются в одной сессии (0 из 351 проверенных). Это означает один pass по JSONL для определения механизма + extraction.
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ export class TaskBoundaryParser {
|
||||||
private readonly CACHE_TTL = 60 * 1000; // 1 мин (не 3 — JSONL файлы меняются часто при активной работе)
|
private readonly CACHE_TTL = 60 * 1000; // 1 мин (не 3 — JSONL файлы меняются часто при активной работе)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Парсит JSONL файл и извлекает все TaskUpdate/teamctl маркеры.
|
* Парсит JSONL файл и извлекает все TaskUpdate/исторические teamctl маркеры.
|
||||||
*
|
*
|
||||||
* Один проход по файлу, O(n) по количеству строк.
|
* Один проход по файлу, O(n) по количеству строк.
|
||||||
*/
|
*/
|
||||||
|
|
@ -101,8 +101,8 @@ export class TaskBoundaryParser {
|
||||||
*
|
*
|
||||||
* Алгоритм:
|
* Алгоритм:
|
||||||
* 1. Найти все TaskBoundary для taskId
|
* 1. Найти все TaskBoundary для taskId
|
||||||
* 2. Start boundary = TaskUpdate(in_progress) или teamctl(start)
|
* 2. Start boundary = TaskUpdate(in_progress) или historical teamctl(start)
|
||||||
* 3. End boundary = TaskUpdate(completed) или teamctl(complete)
|
* 3. End boundary = TaskUpdate(completed) или historical teamctl(complete)
|
||||||
* 4. Scope = все tool_use между start.lineNumber и end.lineNumber
|
* 4. Scope = все tool_use между start.lineNumber и end.lineNumber
|
||||||
* 5. Если single-task session: scope = весь файл
|
* 5. Если single-task session: scope = весь файл
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -559,7 +559,7 @@ EditorView.theme({
|
||||||
- 100% парсируемо — `input.taskId` + `input.status`
|
- 100% парсируемо — `input.taskId` + `input.status`
|
||||||
- Tool result: `"Updated task #1 status"` (текст)
|
- Tool result: `"Updated task #1 status"` (текст)
|
||||||
|
|
||||||
**Механизм B: Bash `teamctl.js`** (44 сессии)
|
**Механизм B: исторический Bash `teamctl.js`** (44 сессии, legacy)
|
||||||
```bash
|
```bash
|
||||||
node "$HOME/.claude/tools/teamctl.js" --team "<team>" task start|complete|set-status <id>
|
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 == "in_progress" → TASK_START(taskId, line)
|
||||||
- status == "completed" → TASK_END(taskId, line)
|
- status == "completed" → TASK_END(taskId, line)
|
||||||
|
|
||||||
2. Детектировать Bash teamctl:
|
2. Детектировать исторические Bash teamctl вызовы:
|
||||||
- "task start <id>" → TASK_START(taskId, line)
|
- "task start <id>" → TASK_START(taskId, line)
|
||||||
- "task complete <id>" → TASK_END(taskId, line)
|
- "task complete <id>" → TASK_END(taskId, line)
|
||||||
|
|
||||||
|
|
@ -625,7 +625,7 @@ parseTaskBoundaries(sessionJsonl) → Map<taskId, tool_use_ids[]>
|
||||||
|
|
||||||
### Как достичь 95%+
|
### Как достичь 95%+
|
||||||
1. **Добавить парсинг `TaskUpdate` tool_use** (name == "TaskUpdate", input.taskId, input.status)
|
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%)
|
3. Для single-task сессий (86%): вся сессия = задача (100%)
|
||||||
4. Для multi-task: маркеры start/end как границы сегментов
|
4. Для multi-task: маркеры start/end как границы сегментов
|
||||||
|
|
||||||
|
|
@ -644,7 +644,7 @@ parseTaskBoundaries(sessionJsonl) → Map<taskId, tool_use_ids[]>
|
||||||
- Решает проблему subagent'ов без `toolUseResult`
|
- Решает проблему subagent'ов без `toolUseResult`
|
||||||
|
|
||||||
### Per-Task Scoping: Структурные маркеры = 95%+
|
### Per-Task Scoping: Структурные маркеры = 95%+
|
||||||
- `TaskUpdate` tool_use + Bash teamctl = 100% парсируемые маркеры
|
- `TaskUpdate` tool_use + исторические Bash teamctl логи = 100% парсируемые маркеры
|
||||||
- 86% сессий = 1 задача → 100% надёжность
|
- 86% сессий = 1 задача → 100% надёжность
|
||||||
- Улучшение с ~85% (text search) до 95%+ (структурный парсинг)
|
- Улучшение с ~85% (text search) до 95%+ (структурный парсинг)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,8 @@ From `MarkdownViewer.tsx` (lines 561-571):
|
||||||
|
|
||||||
### Identified scenarios
|
### Identified scenarios
|
||||||
|
|
||||||
1. **Task descriptions from CLI tooling (teamctl.js / Claude agents)**
|
1. **Task descriptions from task tooling / 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.
|
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**
|
2. **Task comments from agents**
|
||||||
Same issue — `task comment --text "..."` passes plain text. However, TaskCommentsSection.tsx (line 246) correctly uses `<MarkdownViewer content={displayText} bare />`.
|
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.
|
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
|
### 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
|
### 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.)
|
`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.)
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ export const ActivityItem = ({
|
||||||
if (structured) return null;
|
if (structured) return null;
|
||||||
const stripped = stripAgentBlocks(message.text).trim();
|
const stripped = stripAgentBlocks(message.text).trim();
|
||||||
if (!stripped) return null; // All content was agent-only blocks → show summary instead
|
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');
|
return stripped.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
||||||
}, [structured, message.text]);
|
}, [structured, message.text]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ import type { ResolvedTeamMember, TaskAttachmentMeta, TaskComment } from '@share
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert literal backslash-n sequences to real newlines.
|
* Convert literal backslash-n sequences to real newlines.
|
||||||
* CLI tools (teamctl.js) may store `\n` as literal text when
|
* Historical CLI-produced comments may store `\n` as literal text
|
||||||
* shell double-quotes don't interpret escape sequences.
|
* when shell double-quotes don't interpret escape sequences.
|
||||||
*/
|
*/
|
||||||
function normalizeLiteralNewlines(text: string): string {
|
function normalizeLiteralNewlines(text: string): string {
|
||||||
return text.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
return text.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue