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",
|
||||
"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"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
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
|
|
@ -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:
|
||||
- `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 задача в сессии): вся сессия = задача
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = весь файл
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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%+ (структурный парсинг)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Reference in a new issue