@openturtle/cli 0.3.2 → 0.3.3

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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ OpenTurtle CLI 是一个本地命令行工具。它让你不安装 Desktop,也
12
12
  npm install --global @openturtle/cli
13
13
  ```
14
14
 
15
- CLI 启动时会在后台检查 npm 新版本,最多每 24 小时检查一次。检查不会阻塞当前命令;发现新版本后会通过 npm 自动完成全局升级。
15
+ CLI 启动时会在后台检查 npm 新版本,最多每 24 小时检查一次。检查不会阻塞当前命令;发现新版本后会通过 npm 自动完成全局升级,并刷新此前通过 `ot install` 安装的 Skill、Rule 和 Steering。
16
16
 
17
17
  也可以手动检查或立即升级:
18
18
 
@@ -27,7 +27,7 @@ ot update
27
27
  export OPENTURTLE_DISABLE_AUTO_UPDATE=1
28
28
  ```
29
29
 
30
- 源码运行、`pnpm link` 等开发安装不会被自动覆盖。自动更新状态记录在 `~/.openturtle/cli/update.json`。
30
+ 源码运行、`pnpm link` 等开发安装不会被自动覆盖。自动更新状态记录在 `~/.openturtle/cli/update.json`,Agent 安装记录保存在 `~/.openturtle/cli/installations.json`。
31
31
 
32
32
  临时使用、不全局安装时:
33
33
 
@@ -58,13 +58,7 @@ ot worklogs record --summary "完成联调" --type progress
58
58
  ot report draft --today
59
59
  ```
60
60
 
61
- 如果你希望 AI 工具自动帮你记录开发过程,再执行:
62
-
63
- ```bash
64
- ot install --target all --with-agent-hooks
65
- ```
66
-
67
- hooks 只负责记录,不会阻止提交代码,也不会改变 AI 工具原本的行为。
61
+ `ot install` 只安装 CLI 使用指引。Agent 直接执行 `ot ... --json`,不需要 MCP 或 Hook。
68
62
 
69
63
  ## 常用场景
70
64
 
@@ -83,7 +77,7 @@ ot todos progress <todo-id> --message "完成接口联调,等待验收"
83
77
  ### 记录一条工作日志
84
78
 
85
79
  ```bash
86
- ot worklogs record --summary "完成 Qoder hook 接入验证" --type progress
80
+ ot worklogs record --summary "完成 Qoder CLI 接入验证" --type progress
87
81
  ```
88
82
 
89
83
  ### 搜索知识
@@ -182,7 +176,6 @@ ot auth logout
182
176
  | ----------------- | -------------------- |
183
177
  | CLI command | `ot` |
184
178
  | Package | `@openturtle/cli` |
185
- | MCP server name | `ot-cli` |
186
179
  | Project state dir | `.openturtle-cli/` |
187
180
  | Global state dir | `~/.openturtle/cli/` |
188
181
 
@@ -239,7 +232,6 @@ ot init --update-gitignore
239
232
  .openturtle-cli/
240
233
  ├── config.json
241
234
  ├── state.db
242
- ├── hooks/
243
235
  ├── drafts/
244
236
  └── cache/
245
237
  ```
@@ -289,22 +281,15 @@ ot auth status
289
281
 
290
282
  ### 5. 安装 Agent 接入
291
283
 
292
- 默认安装 MCP、rules/skills Git hook,不安装 Agent hooks
284
+ 安装 Agent CLI Skill、Rule Steering
293
285
 
294
286
  ```bash
295
287
  ot install --target all
296
288
  ```
297
289
 
298
- 安装 Agent hooks:
299
-
300
- ```bash
301
- ot install --target all --with-agent-hooks
302
- ```
303
-
304
290
  只安装某个目标:
305
291
 
306
292
  ```bash
307
- ot install --target git
308
293
  ot install --target claude
309
294
  ot install --target codex
310
295
  ot install --target cursor
@@ -336,92 +321,25 @@ ot uninstall --target kiro
336
321
  ot uninstall --target all
337
322
  ```
338
323
 
339
- 卸载只会删除 `ot-cli` MCP server、`OPENTURTLE-CLI` 托管 hook、CLI 写入的 rule/skill/steering/agent 文件,以及 `.openturtle-cli/` 本地状态目录。它会保留非 OpenTurtle 配置、用户自己的 hook、Desktop/旧版 MCP server,例如 `openturtle-knowledge`、`openturtle-automations`、`openturtle-todo`。
324
+ 卸载只会删除 CLI 写入的 rule/skill/steering/managed note,以及 `.openturtle-cli/` 本地状态目录。它会保留用户自己的 Agent 配置。
340
325
 
341
- 如果 Git 原来已有 `post-commit` hook,卸载会恢复原 hook,然后删除 OpenTurtle CLI 的备份文件。
326
+ 安装时会迁移旧版本:删除旧 `ot-cli` MCP `OPENTURTLE-CLI` 托管 Hook 条目,同时保留用户自己的 MCP server 和 Hook。`ot uninstall --target git` 仅作为旧版 Git Hook 的清理入口保留,已经不能再安装 Git Hook。
342
327
 
343
328
  ### 6. Target 安装矩阵
344
329
 
345
- | Target | 默认安装 | `--with-agent-hooks` |
346
- | ------ | --------------------------------------------- | ----------------------------------------------------------------------------- |
347
- | Git | `.git/hooks/post-commit` | 同默认 |
348
- | Claude | `ot-cli` MCP、Skill | `UserPromptSubmit`、`PreToolUse`、`PostToolUse`、`Stop` |
349
- | Codex | `ot-cli` MCP、Skill、`AGENTS.md` managed note | `PreToolUse`、`PostToolUse`、`Stop` |
350
- | Cursor | project `.mcp.json`、Cursor Rule | `preToolUse`、`postToolUse` |
351
- | Qoder | MCP、Skill | `UserPromptSubmit`、`PreToolUse`、`PostToolUse`、`PostToolUseFailure`、`Stop` |
352
- | Kiro | MCP、Steering | `agentSpawn`、`userPromptSubmit`、`preToolUse`、`postToolUse`、`stop` |
353
-
354
- Qoder 和 Kiro 支持 native hook。CLI 仍保留 `import-history` 和 `watch`,用于补齐旧会话或未启用 hook 的环境。
355
-
356
- ### 7. Hooks 的作用
357
-
358
- hooks 是本地记录入口。它们把开发过程写入 `.openturtle-cli/state.db`,用于工作日志、待办进展证据和日报草稿。
359
-
360
- 会记录:
361
-
362
- - Git commit
363
- - 用户提交给 Agent 的 prompt
364
- - Agent tool call 前后
365
- - Agent tool call 失败
366
- - Agent session stop
367
- - history import 记录
368
-
369
- 不会做:
370
-
371
- - 不阻止 Git commit
372
- - 不阻止 Agent tool call
373
- - 不替用户批准危险操作
374
- - 不上传私有文件内容
375
- - 不修改 Desktop MCP server
376
-
377
- Git hook 会保留已有 hook。原 hook 的退出码会保留,OpenTurtle 采集失败不会改变 commit 结果。
378
-
379
- ### 8. MCP server
380
-
381
- MCP server 名固定为:
382
-
383
- ```text
384
- ot-cli
385
- ```
386
-
387
- 启动:
388
-
389
- ```bash
390
- ot mcp serve
391
- ```
392
-
393
- tools:
394
-
395
- ```text
396
- list_my_todos
397
- list_goals
398
- list_workspace_knowledge
399
- search_workspace_knowledge
400
- list_knowledge_source_files
401
- read_knowledge_source_file
402
- record_worklog
403
- list_worklogs
404
- draft_daily_report
405
- update_todo_progress
406
- update_todo_status
407
- ```
408
-
409
- `list_workspace_knowledge` / `search_workspace_knowledge` 查询可复用知识对象。`list_knowledge_source_files` / `read_knowledge_source_file` 只查看知识背后的来源文件或原文文件,不把来源文件当成第二套知识库。
410
-
411
- MCP tools 优先访问 OpenTurtle API。云端不可用时,尽量返回本地缓存或本地草稿信息,并标记 `source: "local_cache"`。
330
+ | Target | 安装内容 |
331
+ | ------ | --------------------------------- |
332
+ | Claude | Skill |
333
+ | Codex | Skill、`AGENTS.md` managed note |
334
+ | Cursor | Cursor Rule |
335
+ | Qoder | Skill |
336
+ | Kiro | Steering |
412
337
 
413
- MCP smoke test:
414
-
415
- ```bash
416
- printf '%s\n' \
417
- '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
418
- '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
419
- | ot mcp serve
420
- ```
338
+ ### 7. CLI 接入模型
421
339
 
422
- 期望 `serverInfo.name` `ot-cli`。
340
+ 所有 Agent 都直接执行 `ot ... --json`。读取命令直接运行;写命令先加 `--dry-run` 检查请求,再执行正式写入。CLI 本身负责认证、后端访问、稳定 JSON、自动更新,以及升级后刷新已安装的 Agent 指引。
423
341
 
424
- ### 9. 资源命令
342
+ ### 8. 资源命令
425
343
 
426
344
  项目、团队成员和项目上下文:
427
345
 
@@ -508,8 +426,8 @@ ot meetings updates ignore <meeting-id> <update-id> --dry-run
508
426
 
509
427
  ```bash
510
428
  ot worklogs list --today
511
- ot worklogs record --summary "完成 Qoder hook 接入验证" --type progress
512
- ot worklogs record --summary "Codex hook trust 需要用户确认" --type blocker
429
+ ot worklogs record --summary "完成 Qoder CLI 接入验证" --type progress
430
+ ot worklogs record --summary "Codex CLI 登录需要用户确认" --type blocker
513
431
  ot worklogs create --body-file worklog.json --dry-run
514
432
  ```
515
433
 
@@ -529,26 +447,7 @@ ot report draft --today
529
447
  ot report draft --today --output .openturtle-cli/drafts/today.md
530
448
  ```
531
449
 
532
- ### 10. 历史导入与监听
533
-
534
- 一次性导入:
535
-
536
- ```bash
537
- ot import-history --target qoder
538
- ot import-history --target kiro
539
- ot import-history --target all
540
- ```
541
-
542
- 查看 watcher 路径:
543
-
544
- ```bash
545
- ot watch --target qoder
546
- ot watch --target kiro
547
- ```
548
-
549
- v1 优先使用 native hook。`import-history` 和 `watch` 是兜底能力。
550
-
551
- ### 11. AI Agent 推荐流程
450
+ ### 9. AI Agent 推荐流程
552
451
 
553
452
  当你需要在一个新工作区启用 CLI:
554
453
 
@@ -560,12 +459,6 @@ ot install --target all
560
459
  ot --json doctor
561
460
  ```
562
461
 
563
- 只有用户明确要求自动记录 Agent 行为时,才执行:
564
-
565
- ```bash
566
- ot install --target all --with-agent-hooks
567
- ```
568
-
569
462
  然后优先使用高层命令:
570
463
 
571
464
  ```bash
@@ -580,7 +473,7 @@ ot report draft --today
580
473
 
581
474
  不要直接改 `.openturtle-cli/state.db`,除非用户明确要求底层排查。
582
475
 
583
- ### 12. AI Agent 禁止事项
476
+ ### 10. AI Agent 禁止事项
584
477
 
585
478
  - 不要写项目 `.openturtle/`
586
479
  - 不要覆盖 `openturtle-knowledge`
@@ -588,13 +481,12 @@ ot report draft --today
588
481
  - 不要覆盖旧 `openturtle-todo`
589
482
  - 不要删除用户已有 Agent 配置
590
483
  - 不要把 token 写入代码、README、commit 或日志
591
- - 不要让 hook 阻塞 Git commit 或 Agent tool call
592
- - 不要伪造 Codex hook trust state
484
+ - 不要安装或调用旧版 OpenTurtle MCP/Hook
593
485
  - 不要在会议 Knowledge 尚未发布时确认项目更新
594
486
  - 不要跳过 `meetings updates list` 直接批量确认会议草稿
595
487
  - 不要修改 Qoder/Kiro 私有未知格式文件;只写本文列出的配置面
596
488
 
597
- ### 13. JSON 输出约定
489
+ ### 11. JSON 输出约定
598
490
 
599
491
  CLI 默认输出 JSON,便于 AI Agent 读取。
600
492
 
@@ -624,7 +516,7 @@ CLI 默认输出 JSON,便于 AI Agent 读取。
624
516
 
625
517
  token 不应该被完整打印,`auth status` 只显示脱敏 token。
626
518
 
627
- ### 14. 开发验证
519
+ ### 12. 开发验证
628
520
 
629
521
  在 `frontend` 目录执行:
630
522
 
@@ -642,7 +534,7 @@ npm pack --dry-run
642
534
 
643
535
  期望包内只包含 `dist/`、`README.md` 和 `package.json` 等运行所需文件,不包含 `src/` 或 `test/`。
644
536
 
645
- ### 15. npm 自动发布
537
+ ### 13. npm 自动发布
646
538
 
647
539
  `.github/workflows/cli-npm-release.yml` 会在 `master` 的 CLI 文件发生变化时运行测试和构建,
648
540
  然后以仓库版本为最低版本、基于 npm 已发布版本自动递增 patch,发布公开包 `@openturtle/cli`。
@@ -659,28 +551,15 @@ npm pack --dry-run
659
551
  创建包并配置 Trusted Publisher 后,后续 `master` 发布只使用短期 OIDC 凭证。发布固定使用
660
552
  `https://registry.npmjs.org/`,不受开发机 `npmmirror` 配置影响。
661
553
 
662
- 构建后 MCP smoke test:
663
-
664
- ```bash
665
- printf '%s\n' \
666
- '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
667
- '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
668
- | node apps/cli/dist/index.js mcp serve
669
- ```
670
-
671
554
  ## 常见问题
672
555
 
673
556
  ### 我没有安装 Desktop,可以用吗?
674
557
 
675
558
  可以。CLI 的目标就是让没有 Desktop 的用户也能使用 OpenTurtle。
676
559
 
677
- ### 安装 hook 会不会影响我提交代码?
678
-
679
- 不会。它只记录信息,记录失败也不会改变提交结果。
680
-
681
- ### Qoder 和 Kiro 支持 hook 吗?
560
+ ### 为什么升级 CLI 后 Skill 也会更新?
682
561
 
683
- 支持。Qoder 使用 settings hook;Kiro 使用 hooks/agent 配置。
562
+ `ot install` 会记录安装过的 target、scope 和工作区。手动执行 `ot update` 或启动时自动更新 npm 包后,新版 CLI 会重新写入这些 Skill、Rule 和 Steering,并清理旧的 OpenTurtle MCP/Hook 配置。
684
563
 
685
564
  ### 为什么不用 `.openturtle/`?
686
565
 
@@ -103,16 +103,13 @@ export function doctor(workspace, target) {
103
103
  project_dir: store.dir,
104
104
  project_dir_exists: projectDirExists,
105
105
  global_dir: path.join(process.env.HOME || '', '.openturtle', 'cli'),
106
- mcp_server_name: 'ot-cli',
106
+ integration: 'cli-skill',
107
107
  },
108
108
  auth: {
109
109
  server: auth.server || null,
110
110
  token_configured: Boolean(auth.token),
111
111
  },
112
- coexistence: {
113
- avoids_project_dot_openturtle: true,
114
- desktop_mcp_names_reserved: ['openturtle-knowledge', 'openturtle-automations', 'openturtle-todo'],
115
- },
112
+ coexistence: { avoids_project_dot_openturtle: true },
116
113
  target: target || 'all',
117
114
  };
118
115
  }
@@ -64,7 +64,8 @@ export async function updateCli(currentVersion, options = {}, dependencies = {})
64
64
  };
65
65
  }
66
66
  await (dependencies.runNpmInstall || runNpmInstall)(npmUpdateArgs());
67
- return { current: currentVersion, latest, available: true, updated: true };
67
+ await (dependencies.refreshInstalledSkills || refreshInstalledSkills)();
68
+ return { current: currentVersion, latest, available: true, updated: true, skills_refreshed: true };
68
69
  }
69
70
  export function scheduleAutomaticUpdate(currentVersion, dependencies = {}) {
70
71
  try {
@@ -166,6 +167,22 @@ function runNpmInstall(args) {
166
167
  });
167
168
  });
168
169
  }
170
+ function refreshInstalledSkills() {
171
+ const entryPath = fileURLToPath(new URL('../index.js', import.meta.url));
172
+ return new Promise((resolve, reject) => {
173
+ const child = spawn(process.execPath, [entryPath, '_refresh-installs'], {
174
+ env: { ...process.env, OPENTURTLE_AUTO_UPDATE_WORKER: '1' },
175
+ stdio: 'ignore',
176
+ });
177
+ child.once('error', reject);
178
+ child.once('exit', (code, signal) => {
179
+ if (code === 0)
180
+ resolve();
181
+ else
182
+ reject(new Error(`Skill refresh failed (${signal || `exit ${code ?? 'unknown'}`})`));
183
+ });
184
+ });
185
+ }
169
186
  function spawnUpdateWorker(workerPath, env) {
170
187
  const child = spawn(process.execPath, [workerPath], {
171
188
  detached: true,
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { cliGlobalDir, ensureFileDir } from './paths.js';
4
+ export function installationsPath(homeDir) {
5
+ return path.join(cliGlobalDir(homeDir), 'installations.json');
6
+ }
7
+ export function readInstallations(filePath = installationsPath()) {
8
+ try {
9
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
10
+ return Array.isArray(parsed.installations) ? parsed.installations : [];
11
+ }
12
+ catch {
13
+ return [];
14
+ }
15
+ }
16
+ export function recordInstallation(installation, filePath = installationsPath()) {
17
+ const normalized = normalizeInstallation(installation);
18
+ const installations = readInstallations(filePath).filter((item) => installationKey(item) !== installationKey(normalized));
19
+ writeInstallations([...installations, normalized], filePath);
20
+ }
21
+ export function forgetInstallation(installation, filePath = installationsPath()) {
22
+ const key = installationKey(normalizeInstallation(installation));
23
+ writeInstallations(readInstallations(filePath).filter((item) => installationKey(item) !== key), filePath);
24
+ }
25
+ export function replaceInstallations(installations, filePath = installationsPath()) {
26
+ writeInstallations(installations.map(normalizeInstallation), filePath);
27
+ }
28
+ function normalizeInstallation(installation) {
29
+ return { ...installation, workspace: path.resolve(installation.workspace) };
30
+ }
31
+ function installationKey(installation) {
32
+ return `${installation.target}\0${installation.scope}\0${path.resolve(installation.workspace)}`;
33
+ }
34
+ function writeInstallations(installations, filePath) {
35
+ if (installations.length === 0) {
36
+ fs.rmSync(filePath, { force: true });
37
+ return;
38
+ }
39
+ ensureFileDir(filePath);
40
+ fs.writeFileSync(filePath, `${JSON.stringify({ installations }, null, 2)}\n`, { mode: 0o600 });
41
+ }
@@ -2,27 +2,15 @@ import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { readJsonFile, writeJsonFile } from './json.js';
5
- import { ensureFileDir } from './paths.js';
6
- export const MCP_SERVER_NAME = 'ot-cli';
7
- export function otMcpConfig() {
8
- return { command: 'ot', args: ['mcp', 'serve'], enabled: true };
9
- }
10
- export function mergeJsonMcpServer(filePath, name = MCP_SERVER_NAME) {
11
- const parsed = readJsonFile(filePath, {});
12
- const servers = parsed.mcpServers && typeof parsed.mcpServers === 'object'
13
- ? parsed.mcpServers
14
- : {};
15
- servers[name] = otMcpConfig();
16
- writeJsonFile(filePath, { ...parsed, mcpServers: servers });
17
- }
18
- export function removeJsonMcpServer(filePath, name = MCP_SERVER_NAME, pruneStopDir) {
5
+ const LEGACY_SERVER_NAME = 'ot-cli';
6
+ export function removeLegacyJsonServer(filePath, pruneStopDir) {
19
7
  if (!fs.existsSync(filePath))
20
8
  return;
21
9
  const parsed = readJsonFile(filePath, {});
22
10
  const servers = parsed.mcpServers && typeof parsed.mcpServers === 'object'
23
11
  ? { ...parsed.mcpServers }
24
12
  : {};
25
- delete servers[name];
13
+ delete servers[LEGACY_SERVER_NAME];
26
14
  const next = { ...parsed, mcpServers: servers };
27
15
  if (Object.keys(servers).length === 0)
28
16
  delete next.mcpServers;
@@ -33,7 +21,7 @@ export function removeJsonMcpServer(filePath, name = MCP_SERVER_NAME, pruneStopD
33
21
  }
34
22
  writeJsonFile(filePath, next);
35
23
  }
36
- export function claudeMcpPath(scope, workspace, homeDir = os.homedir()) {
24
+ export function claudeLegacyServerPath(scope, workspace, homeDir = os.homedir()) {
37
25
  return scope === 'user'
38
26
  ? path.join(homeDir, '.claude', 'mcp_servers.json')
39
27
  : path.join(workspace, '.claude', 'mcp_servers.json');
@@ -46,42 +34,23 @@ export function claudeSettingsPath(scope, workspace, homeDir = os.homedir()) {
46
34
  export function codexConfigPath(scope, workspace, homeDir = os.homedir()) {
47
35
  return scope === 'user' ? path.join(homeDir, '.codex', 'config.toml') : path.join(workspace, '.codex', 'config.toml');
48
36
  }
49
- export function cursorMcpPath(workspace) {
37
+ export function cursorLegacyServerPath(workspace) {
50
38
  return path.join(workspace, '.mcp.json');
51
39
  }
52
- export function qoderMcpPath(scope, workspace, homeDir = os.homedir()) {
40
+ export function qoderLegacyServerPath(scope, workspace, homeDir = os.homedir()) {
53
41
  return scope === 'user' ? path.join(homeDir, '.qoder.json') : path.join(workspace, '.mcp.json');
54
42
  }
55
- export function kiroMcpPath(scope, workspace, homeDir = os.homedir()) {
43
+ export function kiroLegacyServerPath(scope, workspace, homeDir = os.homedir()) {
56
44
  return scope === 'user'
57
45
  ? path.join(homeDir, '.kiro', 'settings', 'mcp.json')
58
46
  : path.join(workspace, '.kiro', 'settings', 'mcp.json');
59
47
  }
60
- export function mergeCodexMcpServer(filePath) {
61
- ensureFileDir(filePath);
62
- const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';
63
- const withoutServer = stripTomlTable(existing, `mcp_servers.${MCP_SERVER_NAME}`);
64
- const block = [
65
- `[mcp_servers.${MCP_SERVER_NAME}]`,
66
- `command = "ot"`,
67
- `args = ["mcp", "serve"]`,
68
- `enabled = true`,
69
- ].join('\n');
70
- fs.writeFileSync(filePath, `${[withoutServer.trimEnd(), block].filter(Boolean).join('\n\n')}\n`, 'utf-8');
71
- }
72
- export function appendCodexHookBlocks(filePath, hookBlocks) {
73
- ensureFileDir(filePath);
74
- const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';
75
- const cleaned = removeManagedTomlBlock(existing, 'hooks');
76
- const block = [`# >>> OPENTURTLE-CLI:hooks`, hookBlocks.trimEnd(), `# <<< OPENTURTLE-CLI:hooks`].join('\n');
77
- fs.writeFileSync(filePath, `${[cleaned.trimEnd(), block].filter(Boolean).join('\n\n')}\n`, 'utf-8');
78
- }
79
- export function removeCodexManagedConfig(filePath, pruneStopDir) {
48
+ export function removeLegacyCodexConfig(filePath, pruneStopDir) {
80
49
  if (!fs.existsSync(filePath))
81
50
  return;
82
51
  const existing = fs.readFileSync(filePath, 'utf-8');
83
52
  const withoutHooks = removeManagedTomlBlock(existing, 'hooks');
84
- const withoutServer = stripTomlTable(withoutHooks, `mcp_servers.${MCP_SERVER_NAME}`);
53
+ const withoutServer = stripTomlTable(withoutHooks, `mcp_servers.${LEGACY_SERVER_NAME}`);
85
54
  const next = withoutServer.trim();
86
55
  if (!next) {
87
56
  fs.rmSync(filePath, { force: true });
@@ -99,9 +68,8 @@ function stripTomlTable(content, tableName) {
99
68
  let skipping = false;
100
69
  const tablePattern = new RegExp(`^\\s*\\[${escapeRegExp(tableName)}\\]\\s*$`);
101
70
  for (const line of lines) {
102
- if (/^\s*\[/.test(line)) {
71
+ if (/^\s*\[/.test(line))
103
72
  skipping = tablePattern.test(line);
104
- }
105
73
  if (!skipping)
106
74
  kept.push(line);
107
75
  }
@@ -5,14 +5,13 @@ import { projectWorklogDir } from './paths.js';
5
5
  export function initProjectStore(workspacePath) {
6
6
  const workspace = path.resolve(workspacePath);
7
7
  const dir = projectWorklogDir(workspace);
8
- fs.mkdirSync(path.join(dir, 'hooks'), { recursive: true });
9
8
  fs.mkdirSync(path.join(dir, 'drafts'), { recursive: true });
10
9
  fs.mkdirSync(path.join(dir, 'cache'), { recursive: true });
11
10
  const configPath = path.join(dir, 'config.json');
12
11
  if (!fs.existsSync(configPath)) {
13
12
  fs.writeFileSync(configPath, `${JSON.stringify({
14
13
  version: 1,
15
- mcpServerName: 'ot-cli',
14
+ integration: 'cli-skill',
16
15
  createdAt: new Date().toISOString(),
17
16
  }, null, 2)}\n`, 'utf-8');
18
17
  }
@@ -37,17 +36,6 @@ function migrate(db) {
37
36
  dedupe_key TEXT NOT NULL UNIQUE
38
37
  );
39
38
 
40
- CREATE TABLE IF NOT EXISTS hook_runs (
41
- id INTEGER PRIMARY KEY AUTOINCREMENT,
42
- target TEXT NOT NULL,
43
- event_type TEXT NOT NULL,
44
- exit_code INTEGER NOT NULL,
45
- duration_ms INTEGER NOT NULL,
46
- raw_input_json TEXT,
47
- error TEXT,
48
- occurred_at TEXT NOT NULL
49
- );
50
-
51
39
  CREATE TABLE IF NOT EXISTS commits (
52
40
  hash TEXT PRIMARY KEY,
53
41
  message TEXT,
@@ -81,13 +69,6 @@ export function upsertEvent(store, event) {
81
69
  .run(event.type, event.source, event.sourceId || null, event.workspacePath || null, event.summary || null, event.rawJson == null ? null : JSON.stringify(event.rawJson), occurredAt, event.dedupeKey);
82
70
  return { inserted: result.changes > 0 };
83
71
  }
84
- export function recordHookRun(store, run) {
85
- store.db
86
- .prepare(`INSERT INTO hook_runs
87
- (target, event_type, exit_code, duration_ms, raw_input_json, error, occurred_at)
88
- VALUES (?, ?, ?, ?, ?, ?, ?)`)
89
- .run(run.target, run.eventType, run.exitCode, run.durationMs, run.rawInputJson || null, run.error || null, run.occurredAt || new Date().toISOString());
90
- }
91
72
  export function listEvents(store, limit = 100) {
92
73
  return store.db
93
74
  .prepare('SELECT * FROM events ORDER BY occurred_at ASC, id ASC LIMIT ?')