@openspecui/core 0.9.0 → 1.0.0
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/dist/index.d.mts +966 -54
- package/dist/index.mjs +897 -122
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -2,14 +2,14 @@ import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
import { readFile as readFile$1, readdir, stat } from "node:fs/promises";
|
|
5
|
-
import { dirname, join as join$1, resolve } from "node:path";
|
|
5
|
+
import { dirname, join as join$1, matchesGlob, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { existsSync, realpathSync, utimesSync } from "node:fs";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { watch } from "fs";
|
|
9
9
|
import { EventEmitter } from "events";
|
|
10
10
|
import { exec, spawn } from "child_process";
|
|
11
11
|
import { promisify } from "util";
|
|
12
|
-
import {
|
|
12
|
+
import { parse } from "yaml";
|
|
13
13
|
|
|
14
14
|
//#region src/parser.ts
|
|
15
15
|
/**
|
|
@@ -2201,6 +2201,31 @@ var CliExecutor = class {
|
|
|
2201
2201
|
return this.execute(args);
|
|
2202
2202
|
}
|
|
2203
2203
|
/**
|
|
2204
|
+
* 执行 openspec schemas --json
|
|
2205
|
+
*/
|
|
2206
|
+
async schemas() {
|
|
2207
|
+
return this.execute(["schemas", "--json"]);
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* 执行 openspec schema which <name> --json
|
|
2211
|
+
*/
|
|
2212
|
+
async schemaWhich(name) {
|
|
2213
|
+
return this.execute([
|
|
2214
|
+
"schema",
|
|
2215
|
+
"which",
|
|
2216
|
+
name,
|
|
2217
|
+
"--json"
|
|
2218
|
+
]);
|
|
2219
|
+
}
|
|
2220
|
+
/**
|
|
2221
|
+
* 执行 openspec templates --json [--schema <name>]
|
|
2222
|
+
*/
|
|
2223
|
+
async templates(schema) {
|
|
2224
|
+
const args = ["templates", "--json"];
|
|
2225
|
+
if (schema) args.push("--schema", schema);
|
|
2226
|
+
return this.execute(args);
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2204
2229
|
* 流式执行 openspec validate
|
|
2205
2230
|
*/
|
|
2206
2231
|
validateStream(type, id, onEvent) {
|
|
@@ -2366,32 +2391,27 @@ var CliExecutor = class {
|
|
|
2366
2391
|
/**
|
|
2367
2392
|
* 工具配置检测模块
|
|
2368
2393
|
*
|
|
2369
|
-
*
|
|
2370
|
-
*
|
|
2371
|
-
*
|
|
2372
|
-
* 重要:使用响应式文件系统实现,监听配置目录,
|
|
2373
|
-
* 当配置文件变化时会自动触发更新。
|
|
2374
|
-
*
|
|
2375
|
-
* @see references/openspec/src/core/config.ts (AI_TOOLS)
|
|
2376
|
-
* @see references/openspec/src/core/configurators/slash/
|
|
2377
|
-
* @see references/openspec/src/core/init.ts (isToolConfigured)
|
|
2394
|
+
* 对齐 @fission-ai/openspec 的 skills 体系,
|
|
2395
|
+
* 通过 `skills/<skill>/SKILL.md` 判断工具是否已配置。
|
|
2378
2396
|
*/
|
|
2379
2397
|
/**
|
|
2380
|
-
*
|
|
2381
|
-
*
|
|
2382
|
-
* @see references/openspec/src/core/configurators/slash/codex.ts
|
|
2398
|
+
* OpenSpec 初始化生成的技能目录名称
|
|
2399
|
+
* @see references/openspec/src/core/shared/tool-detection.ts
|
|
2383
2400
|
*/
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2401
|
+
const SKILL_NAMES = [
|
|
2402
|
+
"openspec-explore",
|
|
2403
|
+
"openspec-new-change",
|
|
2404
|
+
"openspec-continue-change",
|
|
2405
|
+
"openspec-apply-change",
|
|
2406
|
+
"openspec-ff-change",
|
|
2407
|
+
"openspec-sync-specs",
|
|
2408
|
+
"openspec-archive-change",
|
|
2409
|
+
"openspec-bulk-archive-change",
|
|
2410
|
+
"openspec-verify-change"
|
|
2411
|
+
];
|
|
2387
2412
|
/**
|
|
2388
2413
|
* 所有支持的 AI 工具配置
|
|
2389
|
-
*
|
|
2390
2414
|
* 完全对齐官方 OpenSpec CLI 的 AI_TOOLS
|
|
2391
|
-
* 按字母顺序排序(与官方一致)
|
|
2392
|
-
*
|
|
2393
|
-
* @see references/openspec/src/core/config.ts
|
|
2394
|
-
* @see references/openspec/src/core/configurators/slash/registry.ts
|
|
2395
2415
|
*/
|
|
2396
2416
|
const AI_TOOLS = [
|
|
2397
2417
|
{
|
|
@@ -2399,168 +2419,160 @@ const AI_TOOLS = [
|
|
|
2399
2419
|
value: "amazon-q",
|
|
2400
2420
|
available: true,
|
|
2401
2421
|
successLabel: "Amazon Q Developer",
|
|
2402
|
-
|
|
2403
|
-
detectionPath: ".amazonq/prompts/openspec-proposal.md"
|
|
2422
|
+
skillsDir: ".amazonq"
|
|
2404
2423
|
},
|
|
2405
2424
|
{
|
|
2406
2425
|
name: "Antigravity",
|
|
2407
2426
|
value: "antigravity",
|
|
2408
2427
|
available: true,
|
|
2409
2428
|
successLabel: "Antigravity",
|
|
2410
|
-
|
|
2411
|
-
detectionPath: ".agent/workflows/openspec-proposal.md"
|
|
2429
|
+
skillsDir: ".agent"
|
|
2412
2430
|
},
|
|
2413
2431
|
{
|
|
2414
2432
|
name: "Auggie (Augment CLI)",
|
|
2415
2433
|
value: "auggie",
|
|
2416
2434
|
available: true,
|
|
2417
2435
|
successLabel: "Auggie",
|
|
2418
|
-
|
|
2419
|
-
detectionPath: ".augment/commands/openspec-proposal.md"
|
|
2436
|
+
skillsDir: ".augment"
|
|
2420
2437
|
},
|
|
2421
2438
|
{
|
|
2422
2439
|
name: "Claude Code",
|
|
2423
2440
|
value: "claude",
|
|
2424
2441
|
available: true,
|
|
2425
2442
|
successLabel: "Claude Code",
|
|
2426
|
-
|
|
2427
|
-
detectionPath: ".claude/commands/openspec/proposal.md"
|
|
2443
|
+
skillsDir: ".claude"
|
|
2428
2444
|
},
|
|
2429
2445
|
{
|
|
2430
2446
|
name: "Cline",
|
|
2431
2447
|
value: "cline",
|
|
2432
2448
|
available: true,
|
|
2433
2449
|
successLabel: "Cline",
|
|
2434
|
-
|
|
2435
|
-
detectionPath: ".clinerules/workflows/openspec-proposal.md"
|
|
2450
|
+
skillsDir: ".cline"
|
|
2436
2451
|
},
|
|
2437
2452
|
{
|
|
2438
2453
|
name: "Codex",
|
|
2439
2454
|
value: "codex",
|
|
2440
2455
|
available: true,
|
|
2441
2456
|
successLabel: "Codex",
|
|
2442
|
-
|
|
2443
|
-
detectionPath: () => join$1(getCodexGlobalPromptsDir(), "openspec-proposal.md")
|
|
2457
|
+
skillsDir: ".codex"
|
|
2444
2458
|
},
|
|
2445
2459
|
{
|
|
2446
2460
|
name: "CodeBuddy Code (CLI)",
|
|
2447
2461
|
value: "codebuddy",
|
|
2448
2462
|
available: true,
|
|
2449
2463
|
successLabel: "CodeBuddy Code",
|
|
2450
|
-
|
|
2451
|
-
|
|
2464
|
+
skillsDir: ".codebuddy"
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
name: "Continue",
|
|
2468
|
+
value: "continue",
|
|
2469
|
+
available: true,
|
|
2470
|
+
successLabel: "Continue (VS Code / JetBrains / Cli)",
|
|
2471
|
+
skillsDir: ".continue"
|
|
2452
2472
|
},
|
|
2453
2473
|
{
|
|
2454
2474
|
name: "CoStrict",
|
|
2455
2475
|
value: "costrict",
|
|
2456
2476
|
available: true,
|
|
2457
2477
|
successLabel: "CoStrict",
|
|
2458
|
-
|
|
2459
|
-
detectionPath: ".cospec/openspec/commands/openspec-proposal.md"
|
|
2478
|
+
skillsDir: ".cospec"
|
|
2460
2479
|
},
|
|
2461
2480
|
{
|
|
2462
2481
|
name: "Crush",
|
|
2463
2482
|
value: "crush",
|
|
2464
2483
|
available: true,
|
|
2465
2484
|
successLabel: "Crush",
|
|
2466
|
-
|
|
2467
|
-
detectionPath: ".crush/commands/openspec/proposal.md"
|
|
2485
|
+
skillsDir: ".crush"
|
|
2468
2486
|
},
|
|
2469
2487
|
{
|
|
2470
2488
|
name: "Cursor",
|
|
2471
2489
|
value: "cursor",
|
|
2472
2490
|
available: true,
|
|
2473
2491
|
successLabel: "Cursor",
|
|
2474
|
-
|
|
2475
|
-
detectionPath: ".cursor/commands/openspec-proposal.md"
|
|
2492
|
+
skillsDir: ".cursor"
|
|
2476
2493
|
},
|
|
2477
2494
|
{
|
|
2478
2495
|
name: "Factory Droid",
|
|
2479
2496
|
value: "factory",
|
|
2480
2497
|
available: true,
|
|
2481
2498
|
successLabel: "Factory Droid",
|
|
2482
|
-
|
|
2483
|
-
detectionPath: ".factory/commands/openspec-proposal.md"
|
|
2499
|
+
skillsDir: ".factory"
|
|
2484
2500
|
},
|
|
2485
2501
|
{
|
|
2486
2502
|
name: "Gemini CLI",
|
|
2487
2503
|
value: "gemini",
|
|
2488
2504
|
available: true,
|
|
2489
2505
|
successLabel: "Gemini CLI",
|
|
2490
|
-
|
|
2491
|
-
detectionPath: ".gemini/commands/openspec/proposal.toml"
|
|
2506
|
+
skillsDir: ".gemini"
|
|
2492
2507
|
},
|
|
2493
2508
|
{
|
|
2494
2509
|
name: "GitHub Copilot",
|
|
2495
2510
|
value: "github-copilot",
|
|
2496
2511
|
available: true,
|
|
2497
2512
|
successLabel: "GitHub Copilot",
|
|
2498
|
-
|
|
2499
|
-
detectionPath: ".github/prompts/openspec-proposal.prompt.md"
|
|
2513
|
+
skillsDir: ".github"
|
|
2500
2514
|
},
|
|
2501
2515
|
{
|
|
2502
2516
|
name: "iFlow",
|
|
2503
2517
|
value: "iflow",
|
|
2504
2518
|
available: true,
|
|
2505
2519
|
successLabel: "iFlow",
|
|
2506
|
-
|
|
2507
|
-
detectionPath: ".iflow/commands/openspec-proposal.md"
|
|
2520
|
+
skillsDir: ".iflow"
|
|
2508
2521
|
},
|
|
2509
2522
|
{
|
|
2510
2523
|
name: "Kilo Code",
|
|
2511
2524
|
value: "kilocode",
|
|
2512
2525
|
available: true,
|
|
2513
2526
|
successLabel: "Kilo Code",
|
|
2514
|
-
|
|
2515
|
-
detectionPath: ".kilocode/workflows/openspec-proposal.md"
|
|
2527
|
+
skillsDir: ".kilocode"
|
|
2516
2528
|
},
|
|
2517
2529
|
{
|
|
2518
2530
|
name: "OpenCode",
|
|
2519
2531
|
value: "opencode",
|
|
2520
2532
|
available: true,
|
|
2521
2533
|
successLabel: "OpenCode",
|
|
2522
|
-
|
|
2523
|
-
detectionPath: ".opencode/command/openspec-proposal.md"
|
|
2534
|
+
skillsDir: ".opencode"
|
|
2524
2535
|
},
|
|
2525
2536
|
{
|
|
2526
|
-
name: "Qoder
|
|
2537
|
+
name: "Qoder",
|
|
2527
2538
|
value: "qoder",
|
|
2528
2539
|
available: true,
|
|
2529
2540
|
successLabel: "Qoder",
|
|
2530
|
-
|
|
2531
|
-
detectionPath: ".qoder/commands/openspec/proposal.md"
|
|
2541
|
+
skillsDir: ".qoder"
|
|
2532
2542
|
},
|
|
2533
2543
|
{
|
|
2534
2544
|
name: "Qwen Code",
|
|
2535
2545
|
value: "qwen",
|
|
2536
2546
|
available: true,
|
|
2537
2547
|
successLabel: "Qwen Code",
|
|
2538
|
-
|
|
2539
|
-
detectionPath: ".qwen/commands/openspec-proposal.toml"
|
|
2548
|
+
skillsDir: ".qwen"
|
|
2540
2549
|
},
|
|
2541
2550
|
{
|
|
2542
2551
|
name: "RooCode",
|
|
2543
2552
|
value: "roocode",
|
|
2544
2553
|
available: true,
|
|
2545
2554
|
successLabel: "RooCode",
|
|
2546
|
-
|
|
2547
|
-
|
|
2555
|
+
skillsDir: ".roo"
|
|
2556
|
+
},
|
|
2557
|
+
{
|
|
2558
|
+
name: "Trae",
|
|
2559
|
+
value: "trae",
|
|
2560
|
+
available: true,
|
|
2561
|
+
successLabel: "Trae",
|
|
2562
|
+
skillsDir: ".trae"
|
|
2548
2563
|
},
|
|
2549
2564
|
{
|
|
2550
2565
|
name: "Windsurf",
|
|
2551
2566
|
value: "windsurf",
|
|
2552
2567
|
available: true,
|
|
2553
2568
|
successLabel: "Windsurf",
|
|
2554
|
-
|
|
2555
|
-
detectionPath: ".windsurf/workflows/openspec-proposal.md"
|
|
2569
|
+
skillsDir: ".windsurf"
|
|
2556
2570
|
},
|
|
2557
2571
|
{
|
|
2558
2572
|
name: "AGENTS.md (works with Amp, VS Code, …)",
|
|
2559
2573
|
value: "agents",
|
|
2560
2574
|
available: false,
|
|
2561
|
-
successLabel: "your AGENTS.md-compatible assistant"
|
|
2562
|
-
scope: "project",
|
|
2563
|
-
detectionPath: "AGENTS.md"
|
|
2575
|
+
successLabel: "your AGENTS.md-compatible assistant"
|
|
2564
2576
|
}
|
|
2565
2577
|
];
|
|
2566
2578
|
/**
|
|
@@ -2597,71 +2609,39 @@ function getToolById(toolId) {
|
|
|
2597
2609
|
const stateCache = /* @__PURE__ */ new Map();
|
|
2598
2610
|
/** 监听器释放函数缓存 */
|
|
2599
2611
|
const releaseCache = /* @__PURE__ */ new Map();
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
async function fileExists(filePath) {
|
|
2604
|
-
try {
|
|
2605
|
-
await stat(filePath);
|
|
2606
|
-
return true;
|
|
2607
|
-
} catch {
|
|
2608
|
-
return false;
|
|
2609
|
-
}
|
|
2612
|
+
function getSkillsDir(projectDir, tool) {
|
|
2613
|
+
if (!tool.skillsDir) return null;
|
|
2614
|
+
return join$1(projectDir, tool.skillsDir, "skills");
|
|
2610
2615
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
function resolveDetectionPath(config, projectDir) {
|
|
2618
|
-
if (config.scope === "none" || !config.detectionPath) return;
|
|
2619
|
-
if (config.scope === "global") return config.detectionPath();
|
|
2620
|
-
return join$1(projectDir, config.detectionPath);
|
|
2616
|
+
async function getSkillCount(projectDir, tool) {
|
|
2617
|
+
const skillsDir = getSkillsDir(projectDir, tool);
|
|
2618
|
+
if (!skillsDir) return 0;
|
|
2619
|
+
let count = 0;
|
|
2620
|
+
for (const skillName of SKILL_NAMES) if (await reactiveExists(join$1(skillsDir, skillName, "SKILL.md"))) count++;
|
|
2621
|
+
return count;
|
|
2621
2622
|
}
|
|
2622
2623
|
/**
|
|
2623
2624
|
* 扫描已配置的工具(并行检查)
|
|
2624
2625
|
*/
|
|
2625
2626
|
async function scanConfiguredTools(projectDir) {
|
|
2626
2627
|
return (await Promise.all(AI_TOOLS.map(async (config) => {
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
return await fileExists(filePath) ? config.value : null;
|
|
2628
|
+
if (!config.skillsDir) return null;
|
|
2629
|
+
return await getSkillCount(projectDir, config) > 0 ? config.value : null;
|
|
2630
2630
|
}))).filter((id) => id !== null);
|
|
2631
2631
|
}
|
|
2632
2632
|
/**
|
|
2633
2633
|
* 获取需要监听的项目级目录列表
|
|
2634
|
-
* 只监听包含工具配置的一级隐藏目录
|
|
2635
2634
|
*/
|
|
2636
2635
|
function getProjectWatchDirs(projectDir) {
|
|
2637
2636
|
const dirs = /* @__PURE__ */ new Set();
|
|
2638
|
-
for (const config of AI_TOOLS)
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
}
|
|
2642
|
-
return Array.from(dirs);
|
|
2643
|
-
}
|
|
2644
|
-
/**
|
|
2645
|
-
* 获取需要监听的全局目录列表
|
|
2646
|
-
* 如 Codex 的 ~/.codex/prompts/
|
|
2647
|
-
*/
|
|
2648
|
-
function getGlobalWatchDirs() {
|
|
2649
|
-
const dirs = /* @__PURE__ */ new Set();
|
|
2650
|
-
for (const config of AI_TOOLS) if (config.scope === "global" && config.detectionPath) {
|
|
2651
|
-
const filePath = config.detectionPath();
|
|
2652
|
-
dirs.add(dirname(filePath));
|
|
2637
|
+
for (const config of AI_TOOLS) {
|
|
2638
|
+
if (!config.skillsDir) continue;
|
|
2639
|
+
dirs.add(join$1(projectDir, config.skillsDir));
|
|
2653
2640
|
}
|
|
2654
2641
|
return Array.from(dirs);
|
|
2655
2642
|
}
|
|
2656
2643
|
/**
|
|
2657
2644
|
* 检测项目中已配置的工具(响应式)
|
|
2658
|
-
*
|
|
2659
|
-
* 监听两类目录:
|
|
2660
|
-
* 1. 项目级配置目录(如 .claude, .cursor 等)
|
|
2661
|
-
* 2. 全局配置目录(如 ~/.codex/prompts/)
|
|
2662
|
-
*
|
|
2663
|
-
* @param projectDir 项目根目录
|
|
2664
|
-
* @returns 已配置的工具 ID 列表
|
|
2665
2645
|
*/
|
|
2666
2646
|
async function getConfiguredTools(projectDir) {
|
|
2667
2647
|
const normalizedPath = resolve(projectDir);
|
|
@@ -2680,11 +2660,6 @@ async function getConfiguredTools(projectDir) {
|
|
|
2680
2660
|
const release = acquireWatcher(dir, onUpdate, { recursive: true });
|
|
2681
2661
|
releases.push(release);
|
|
2682
2662
|
}
|
|
2683
|
-
const globalWatchDirs = getGlobalWatchDirs();
|
|
2684
|
-
for (const dir of globalWatchDirs) {
|
|
2685
|
-
const release = acquireWatcher(dir, onUpdate, { recursive: false });
|
|
2686
|
-
releases.push(release);
|
|
2687
|
-
}
|
|
2688
2663
|
const rootRelease = acquireWatcher(normalizedPath, onUpdate, { recursive: false });
|
|
2689
2664
|
releases.push(rootRelease);
|
|
2690
2665
|
releaseCache.set(key, () => releases.forEach((r) => r()));
|
|
@@ -2693,14 +2668,814 @@ async function getConfiguredTools(projectDir) {
|
|
|
2693
2668
|
}
|
|
2694
2669
|
/**
|
|
2695
2670
|
* 检查特定工具是否已配置
|
|
2696
|
-
*
|
|
2697
|
-
* @param projectDir 项目根目录
|
|
2698
|
-
* @param toolId 工具 ID
|
|
2699
|
-
* @returns 是否已配置
|
|
2700
2671
|
*/
|
|
2701
2672
|
async function isToolConfigured(projectDir, toolId) {
|
|
2702
2673
|
return (await getConfiguredTools(projectDir)).includes(toolId);
|
|
2703
2674
|
}
|
|
2704
2675
|
|
|
2705
2676
|
//#endregion
|
|
2706
|
-
|
|
2677
|
+
//#region src/opsx-types.ts
|
|
2678
|
+
/** Check if an outputPath contains glob pattern characters */
|
|
2679
|
+
function isGlobPattern(pattern) {
|
|
2680
|
+
return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
|
|
2681
|
+
}
|
|
2682
|
+
const ArtifactStatusSchema = z.object({
|
|
2683
|
+
id: z.string(),
|
|
2684
|
+
outputPath: z.string(),
|
|
2685
|
+
status: z.enum([
|
|
2686
|
+
"done",
|
|
2687
|
+
"ready",
|
|
2688
|
+
"blocked"
|
|
2689
|
+
]),
|
|
2690
|
+
missingDeps: z.array(z.string()).optional(),
|
|
2691
|
+
relativePath: z.string().optional()
|
|
2692
|
+
});
|
|
2693
|
+
const ChangeStatusSchema = z.object({
|
|
2694
|
+
changeName: z.string(),
|
|
2695
|
+
schemaName: z.string(),
|
|
2696
|
+
isComplete: z.boolean(),
|
|
2697
|
+
applyRequires: z.array(z.string()),
|
|
2698
|
+
artifacts: z.array(ArtifactStatusSchema)
|
|
2699
|
+
});
|
|
2700
|
+
const DependencyInfoSchema = z.object({
|
|
2701
|
+
id: z.string(),
|
|
2702
|
+
done: z.boolean(),
|
|
2703
|
+
path: z.string(),
|
|
2704
|
+
description: z.string()
|
|
2705
|
+
});
|
|
2706
|
+
const ApplyTaskSchema = z.object({
|
|
2707
|
+
id: z.string(),
|
|
2708
|
+
description: z.string(),
|
|
2709
|
+
done: z.boolean()
|
|
2710
|
+
});
|
|
2711
|
+
const ApplyInstructionsSchema = z.object({
|
|
2712
|
+
changeName: z.string(),
|
|
2713
|
+
changeDir: z.string(),
|
|
2714
|
+
schemaName: z.string(),
|
|
2715
|
+
contextFiles: z.record(z.string()),
|
|
2716
|
+
progress: z.object({
|
|
2717
|
+
total: z.number(),
|
|
2718
|
+
complete: z.number(),
|
|
2719
|
+
remaining: z.number()
|
|
2720
|
+
}),
|
|
2721
|
+
tasks: z.array(ApplyTaskSchema),
|
|
2722
|
+
state: z.enum([
|
|
2723
|
+
"blocked",
|
|
2724
|
+
"all_done",
|
|
2725
|
+
"ready"
|
|
2726
|
+
]),
|
|
2727
|
+
missingArtifacts: z.array(z.string()).optional(),
|
|
2728
|
+
instruction: z.string()
|
|
2729
|
+
});
|
|
2730
|
+
const NullableString = z.string().nullable().optional();
|
|
2731
|
+
const ArtifactInstructionsSchema = z.object({
|
|
2732
|
+
changeName: z.string(),
|
|
2733
|
+
artifactId: z.string(),
|
|
2734
|
+
schemaName: z.string(),
|
|
2735
|
+
changeDir: z.string(),
|
|
2736
|
+
outputPath: z.string(),
|
|
2737
|
+
description: z.string(),
|
|
2738
|
+
instruction: NullableString,
|
|
2739
|
+
context: NullableString,
|
|
2740
|
+
rules: z.array(z.string()).optional().nullable(),
|
|
2741
|
+
template: z.string(),
|
|
2742
|
+
dependencies: z.array(DependencyInfoSchema),
|
|
2743
|
+
unlocks: z.array(z.string())
|
|
2744
|
+
});
|
|
2745
|
+
const SchemaInfoSchema = z.object({
|
|
2746
|
+
name: z.string(),
|
|
2747
|
+
description: z.string().optional(),
|
|
2748
|
+
artifacts: z.array(z.string()),
|
|
2749
|
+
source: z.enum([
|
|
2750
|
+
"project",
|
|
2751
|
+
"user",
|
|
2752
|
+
"package"
|
|
2753
|
+
])
|
|
2754
|
+
});
|
|
2755
|
+
const SchemaResolutionSchema = z.object({
|
|
2756
|
+
name: z.string(),
|
|
2757
|
+
source: z.enum([
|
|
2758
|
+
"project",
|
|
2759
|
+
"user",
|
|
2760
|
+
"package"
|
|
2761
|
+
]),
|
|
2762
|
+
path: z.string(),
|
|
2763
|
+
shadows: z.array(z.object({
|
|
2764
|
+
source: z.enum([
|
|
2765
|
+
"project",
|
|
2766
|
+
"user",
|
|
2767
|
+
"package"
|
|
2768
|
+
]),
|
|
2769
|
+
path: z.string()
|
|
2770
|
+
}))
|
|
2771
|
+
});
|
|
2772
|
+
const TemplatesSchema = z.record(z.object({
|
|
2773
|
+
path: z.string(),
|
|
2774
|
+
source: z.enum([
|
|
2775
|
+
"project",
|
|
2776
|
+
"user",
|
|
2777
|
+
"package"
|
|
2778
|
+
])
|
|
2779
|
+
}));
|
|
2780
|
+
const SchemaArtifactSchema = z.object({
|
|
2781
|
+
id: z.string(),
|
|
2782
|
+
outputPath: z.string(),
|
|
2783
|
+
description: z.string().optional(),
|
|
2784
|
+
template: z.string().optional(),
|
|
2785
|
+
instruction: z.string().optional(),
|
|
2786
|
+
requires: z.array(z.string())
|
|
2787
|
+
});
|
|
2788
|
+
const SchemaDetailSchema = z.object({
|
|
2789
|
+
name: z.string(),
|
|
2790
|
+
description: z.string().optional(),
|
|
2791
|
+
version: z.union([z.string(), z.number()]).optional(),
|
|
2792
|
+
artifacts: z.array(SchemaArtifactSchema),
|
|
2793
|
+
applyRequires: z.array(z.string()),
|
|
2794
|
+
applyTracks: z.string().optional(),
|
|
2795
|
+
applyInstruction: z.string().optional()
|
|
2796
|
+
});
|
|
2797
|
+
|
|
2798
|
+
//#endregion
|
|
2799
|
+
//#region src/opsx-kernel.ts
|
|
2800
|
+
function parseCliJson(raw, schema, label) {
|
|
2801
|
+
const trimmed = raw.trim();
|
|
2802
|
+
if (!trimmed) throw new Error(`${label} returned empty output`);
|
|
2803
|
+
let parsed;
|
|
2804
|
+
try {
|
|
2805
|
+
parsed = JSON.parse(trimmed);
|
|
2806
|
+
} catch (err) {
|
|
2807
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2808
|
+
throw new Error(`${label} returned invalid JSON: ${message}`);
|
|
2809
|
+
}
|
|
2810
|
+
const result = schema.safeParse(parsed);
|
|
2811
|
+
if (!result.success) throw new Error(`${label} returned unexpected JSON: ${result.error.message}`);
|
|
2812
|
+
return result.data;
|
|
2813
|
+
}
|
|
2814
|
+
function toRelativePath(root, absolutePath) {
|
|
2815
|
+
return relative(root, absolutePath).split(sep).join("/");
|
|
2816
|
+
}
|
|
2817
|
+
async function readEntriesUnderRoot(root) {
|
|
2818
|
+
if (!(await reactiveStat(root))?.isDirectory) return [];
|
|
2819
|
+
const collectEntries = async (dir) => {
|
|
2820
|
+
const names = await reactiveReadDir(dir, { includeHidden: false });
|
|
2821
|
+
const entries = [];
|
|
2822
|
+
for (const name of names) {
|
|
2823
|
+
const fullPath = join$1(dir, name);
|
|
2824
|
+
const statInfo = await reactiveStat(fullPath);
|
|
2825
|
+
if (!statInfo) continue;
|
|
2826
|
+
const relativePath = toRelativePath(root, fullPath);
|
|
2827
|
+
if (statInfo.isDirectory) {
|
|
2828
|
+
entries.push({
|
|
2829
|
+
path: relativePath,
|
|
2830
|
+
type: "directory"
|
|
2831
|
+
});
|
|
2832
|
+
entries.push(...await collectEntries(fullPath));
|
|
2833
|
+
} else {
|
|
2834
|
+
const content = await reactiveReadFile(fullPath);
|
|
2835
|
+
const size = content ? Buffer.byteLength(content, "utf-8") : void 0;
|
|
2836
|
+
entries.push({
|
|
2837
|
+
path: relativePath,
|
|
2838
|
+
type: "file",
|
|
2839
|
+
content: content ?? void 0,
|
|
2840
|
+
size
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return entries;
|
|
2845
|
+
};
|
|
2846
|
+
return collectEntries(root);
|
|
2847
|
+
}
|
|
2848
|
+
async function readGlobArtifactFiles(projectDir, changeId, outputPath) {
|
|
2849
|
+
return (await readEntriesUnderRoot(join$1(projectDir, "openspec", "changes", changeId))).filter((entry) => entry.type === "file" && matchesGlob(entry.path, outputPath)).map((entry) => ({
|
|
2850
|
+
path: entry.path,
|
|
2851
|
+
type: "file",
|
|
2852
|
+
content: entry.content ?? ""
|
|
2853
|
+
}));
|
|
2854
|
+
}
|
|
2855
|
+
async function touchOpsxProjectDeps(projectDir) {
|
|
2856
|
+
const openspecDir = join$1(projectDir, "openspec");
|
|
2857
|
+
await reactiveReadFile(join$1(openspecDir, "config.yaml"));
|
|
2858
|
+
const schemaRoot = join$1(openspecDir, "schemas");
|
|
2859
|
+
const schemaDirs = await reactiveReadDir(schemaRoot, {
|
|
2860
|
+
directoriesOnly: true,
|
|
2861
|
+
includeHidden: true
|
|
2862
|
+
});
|
|
2863
|
+
await Promise.all(schemaDirs.map((name) => reactiveReadFile(join$1(schemaRoot, name, "schema.yaml"))));
|
|
2864
|
+
await reactiveReadDir(join$1(openspecDir, "changes"), {
|
|
2865
|
+
directoriesOnly: true,
|
|
2866
|
+
includeHidden: true,
|
|
2867
|
+
exclude: ["archive"]
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
async function touchOpsxChangeDeps(projectDir, changeId) {
|
|
2871
|
+
const changeDir = join$1(projectDir, "openspec", "changes", changeId);
|
|
2872
|
+
await reactiveReadDir(changeDir, { includeHidden: true });
|
|
2873
|
+
await reactiveReadFile(join$1(changeDir, ".openspec.yaml"));
|
|
2874
|
+
}
|
|
2875
|
+
var OpsxKernel = class {
|
|
2876
|
+
projectDir;
|
|
2877
|
+
cliExecutor;
|
|
2878
|
+
controller = new AbortController();
|
|
2879
|
+
_statusList = new ReactiveState([]);
|
|
2880
|
+
_schemas = new ReactiveState([]);
|
|
2881
|
+
_changeIds = new ReactiveState([]);
|
|
2882
|
+
_projectConfig = new ReactiveState(null);
|
|
2883
|
+
_schemaResolutions = /* @__PURE__ */ new Map();
|
|
2884
|
+
_schemaDetails = /* @__PURE__ */ new Map();
|
|
2885
|
+
_schemaFiles = /* @__PURE__ */ new Map();
|
|
2886
|
+
_schemaYamls = /* @__PURE__ */ new Map();
|
|
2887
|
+
_templates = /* @__PURE__ */ new Map();
|
|
2888
|
+
_templateContents = /* @__PURE__ */ new Map();
|
|
2889
|
+
_statuses = /* @__PURE__ */ new Map();
|
|
2890
|
+
_instructions = /* @__PURE__ */ new Map();
|
|
2891
|
+
_applyInstructions = /* @__PURE__ */ new Map();
|
|
2892
|
+
_changeMetadata = /* @__PURE__ */ new Map();
|
|
2893
|
+
_artifactOutputs = /* @__PURE__ */ new Map();
|
|
2894
|
+
_globArtifactFiles = /* @__PURE__ */ new Map();
|
|
2895
|
+
_entityControllers = /* @__PURE__ */ new Map();
|
|
2896
|
+
constructor(projectDir, cliExecutor) {
|
|
2897
|
+
this.projectDir = projectDir;
|
|
2898
|
+
this.cliExecutor = cliExecutor;
|
|
2899
|
+
}
|
|
2900
|
+
async warmup() {
|
|
2901
|
+
const signal = this.controller.signal;
|
|
2902
|
+
await Promise.all([
|
|
2903
|
+
this.startStream(this._schemas, () => this.fetchSchemas(), signal),
|
|
2904
|
+
this.startStream(this._changeIds, () => this.fetchChangeIds(), signal),
|
|
2905
|
+
this.startStream(this._projectConfig, () => this.fetchProjectConfig(), signal)
|
|
2906
|
+
]);
|
|
2907
|
+
const schemas = this._schemas.get();
|
|
2908
|
+
await Promise.all(schemas.map((s) => this.warmupSchema(s.name, signal)));
|
|
2909
|
+
const changeIds = this._changeIds.get();
|
|
2910
|
+
await Promise.all(changeIds.map((id) => this.warmupChange(id, signal)));
|
|
2911
|
+
await this.startStream(this._statusList, () => this.fetchStatusList(), signal);
|
|
2912
|
+
this.watchSchemaChanges(signal);
|
|
2913
|
+
this.watchChangeIdChanges(signal);
|
|
2914
|
+
}
|
|
2915
|
+
dispose() {
|
|
2916
|
+
this.controller.abort();
|
|
2917
|
+
for (const ctrl of this._entityControllers.values()) ctrl.abort();
|
|
2918
|
+
this._entityControllers.clear();
|
|
2919
|
+
}
|
|
2920
|
+
getStatusList() {
|
|
2921
|
+
return this._statusList.get();
|
|
2922
|
+
}
|
|
2923
|
+
getSchemas() {
|
|
2924
|
+
return this._schemas.get();
|
|
2925
|
+
}
|
|
2926
|
+
getChangeIds() {
|
|
2927
|
+
return this._changeIds.get();
|
|
2928
|
+
}
|
|
2929
|
+
getProjectConfig() {
|
|
2930
|
+
return this._projectConfig.get();
|
|
2931
|
+
}
|
|
2932
|
+
getTemplates(schema) {
|
|
2933
|
+
const key = schema ?? "";
|
|
2934
|
+
const state = this._templates.get(key);
|
|
2935
|
+
return state ? state.get() : {};
|
|
2936
|
+
}
|
|
2937
|
+
getTemplateContents(schema) {
|
|
2938
|
+
const key = schema ?? "";
|
|
2939
|
+
const state = this._templateContents.get(key);
|
|
2940
|
+
return state ? state.get() : {};
|
|
2941
|
+
}
|
|
2942
|
+
getStatus(changeId, schema) {
|
|
2943
|
+
const key = `${changeId}:${schema ?? ""}`;
|
|
2944
|
+
const state = this._statuses.get(key);
|
|
2945
|
+
if (!state) throw new Error(`Status not found for change "${changeId}"`);
|
|
2946
|
+
return state.get();
|
|
2947
|
+
}
|
|
2948
|
+
getInstructions(changeId, artifact, schema) {
|
|
2949
|
+
const key = `${changeId}:${artifact}:${schema ?? ""}`;
|
|
2950
|
+
const state = this._instructions.get(key);
|
|
2951
|
+
if (!state) throw new Error(`Instructions not found for change "${changeId}" artifact "${artifact}"`);
|
|
2952
|
+
return state.get();
|
|
2953
|
+
}
|
|
2954
|
+
getApplyInstructions(changeId, schema) {
|
|
2955
|
+
const key = `${changeId}:${schema ?? ""}`;
|
|
2956
|
+
const state = this._applyInstructions.get(key);
|
|
2957
|
+
if (!state) throw new Error(`Apply instructions not found for change "${changeId}"`);
|
|
2958
|
+
return state.get();
|
|
2959
|
+
}
|
|
2960
|
+
getSchemaResolution(name) {
|
|
2961
|
+
const state = this._schemaResolutions.get(name);
|
|
2962
|
+
if (!state) throw new Error(`Schema resolution not found for "${name}"`);
|
|
2963
|
+
return state.get();
|
|
2964
|
+
}
|
|
2965
|
+
getSchemaDetail(name) {
|
|
2966
|
+
const state = this._schemaDetails.get(name);
|
|
2967
|
+
if (!state) throw new Error(`Schema detail not found for "${name}"`);
|
|
2968
|
+
return state.get();
|
|
2969
|
+
}
|
|
2970
|
+
getSchemaFiles(name) {
|
|
2971
|
+
const state = this._schemaFiles.get(name);
|
|
2972
|
+
if (!state) throw new Error(`Schema files not found for "${name}"`);
|
|
2973
|
+
return state.get();
|
|
2974
|
+
}
|
|
2975
|
+
getSchemaYaml(name) {
|
|
2976
|
+
const state = this._schemaYamls.get(name);
|
|
2977
|
+
if (!state) throw new Error(`Schema yaml not found for "${name}"`);
|
|
2978
|
+
return state.get();
|
|
2979
|
+
}
|
|
2980
|
+
getChangeMetadata(changeId) {
|
|
2981
|
+
const state = this._changeMetadata.get(changeId);
|
|
2982
|
+
if (!state) return null;
|
|
2983
|
+
return state.get();
|
|
2984
|
+
}
|
|
2985
|
+
getArtifactOutput(changeId, outputPath) {
|
|
2986
|
+
const key = `${changeId}:${outputPath}`;
|
|
2987
|
+
const state = this._artifactOutputs.get(key);
|
|
2988
|
+
if (!state) return null;
|
|
2989
|
+
return state.get();
|
|
2990
|
+
}
|
|
2991
|
+
getGlobArtifactFiles(changeId, outputPath) {
|
|
2992
|
+
const key = `${changeId}:${outputPath}`;
|
|
2993
|
+
const state = this._globArtifactFiles.get(key);
|
|
2994
|
+
if (!state) return [];
|
|
2995
|
+
return state.get();
|
|
2996
|
+
}
|
|
2997
|
+
startStream(state, task, signal) {
|
|
2998
|
+
return new Promise((resolve$1) => {
|
|
2999
|
+
const context = new ReactiveContext();
|
|
3000
|
+
let first = true;
|
|
3001
|
+
(async () => {
|
|
3002
|
+
try {
|
|
3003
|
+
for await (const data of context.stream(task, signal)) {
|
|
3004
|
+
state.set(data);
|
|
3005
|
+
if (first) {
|
|
3006
|
+
first = false;
|
|
3007
|
+
resolve$1();
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
} catch {}
|
|
3011
|
+
if (first) resolve$1();
|
|
3012
|
+
})();
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
3015
|
+
async warmupSchema(name, parentSignal) {
|
|
3016
|
+
const entityCtrl = new AbortController();
|
|
3017
|
+
this._entityControllers.set(`schema:${name}`, entityCtrl);
|
|
3018
|
+
const signal = this.combineSignals(parentSignal, entityCtrl.signal);
|
|
3019
|
+
if (!this._schemaResolutions.has(name)) this._schemaResolutions.set(name, new ReactiveState(null));
|
|
3020
|
+
if (!this._schemaDetails.has(name)) this._schemaDetails.set(name, new ReactiveState(null));
|
|
3021
|
+
if (!this._schemaFiles.has(name)) this._schemaFiles.set(name, new ReactiveState([]));
|
|
3022
|
+
if (!this._schemaYamls.has(name)) this._schemaYamls.set(name, new ReactiveState(null));
|
|
3023
|
+
if (!this._templates.has(name)) this._templates.set(name, new ReactiveState({}));
|
|
3024
|
+
if (!this._templateContents.has(name)) this._templateContents.set(name, new ReactiveState({}));
|
|
3025
|
+
if (!this._templates.has("")) this._templates.set("", new ReactiveState({}));
|
|
3026
|
+
if (!this._templateContents.has("")) this._templateContents.set("", new ReactiveState({}));
|
|
3027
|
+
await Promise.all([
|
|
3028
|
+
this.startStream(this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), signal),
|
|
3029
|
+
this.startStream(this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), signal),
|
|
3030
|
+
this.startStream(this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), signal),
|
|
3031
|
+
this.startStream(this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), signal),
|
|
3032
|
+
this.startStream(this._templates.get(name), () => this.fetchTemplates(name), signal),
|
|
3033
|
+
this.startStream(this._templateContents.get(name), () => this.fetchTemplateContents(name), signal),
|
|
3034
|
+
this.startStream(this._templates.get(""), () => this.fetchTemplates(void 0), signal),
|
|
3035
|
+
this.startStream(this._templateContents.get(""), () => this.fetchTemplateContents(void 0), signal)
|
|
3036
|
+
]);
|
|
3037
|
+
}
|
|
3038
|
+
async warmupChange(changeId, parentSignal) {
|
|
3039
|
+
const entityCtrl = new AbortController();
|
|
3040
|
+
this._entityControllers.set(`change:${changeId}`, entityCtrl);
|
|
3041
|
+
const signal = this.combineSignals(parentSignal, entityCtrl.signal);
|
|
3042
|
+
const statusKey = `${changeId}:`;
|
|
3043
|
+
if (!this._statuses.has(statusKey)) this._statuses.set(statusKey, new ReactiveState(null));
|
|
3044
|
+
if (!this._changeMetadata.has(changeId)) this._changeMetadata.set(changeId, new ReactiveState(null));
|
|
3045
|
+
const applyKey = `${changeId}:`;
|
|
3046
|
+
if (!this._applyInstructions.has(applyKey)) this._applyInstructions.set(applyKey, new ReactiveState(null));
|
|
3047
|
+
await Promise.all([
|
|
3048
|
+
this.startStream(this._statuses.get(statusKey), () => this.fetchStatus(changeId, void 0), signal),
|
|
3049
|
+
this.startStream(this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), signal),
|
|
3050
|
+
this.startStream(this._applyInstructions.get(applyKey), () => this.fetchApplyInstructions(changeId, void 0), signal)
|
|
3051
|
+
]);
|
|
3052
|
+
const status = this._statuses.get(statusKey)?.get();
|
|
3053
|
+
if (status?.artifacts) await Promise.all(status.artifacts.map(async (artifact) => {
|
|
3054
|
+
const instrKey = `${changeId}:${artifact.id}:`;
|
|
3055
|
+
if (!this._instructions.has(instrKey)) this._instructions.set(instrKey, new ReactiveState(null));
|
|
3056
|
+
await this.startStream(this._instructions.get(instrKey), () => this.fetchInstructions(changeId, artifact.id, void 0), signal);
|
|
3057
|
+
const outputKey = `${changeId}:${artifact.outputPath}`;
|
|
3058
|
+
if (!this._artifactOutputs.has(outputKey)) this._artifactOutputs.set(outputKey, new ReactiveState(null));
|
|
3059
|
+
await this.startStream(this._artifactOutputs.get(outputKey), () => this.fetchArtifactOutput(changeId, artifact.outputPath), signal);
|
|
3060
|
+
if (artifact.outputPath.includes("*") || artifact.outputPath.includes("?") || artifact.outputPath.includes("[")) {
|
|
3061
|
+
const globKey = `${changeId}:${artifact.outputPath}`;
|
|
3062
|
+
if (!this._globArtifactFiles.has(globKey)) this._globArtifactFiles.set(globKey, new ReactiveState([]));
|
|
3063
|
+
await this.startStream(this._globArtifactFiles.get(globKey), () => readGlobArtifactFiles(this.projectDir, changeId, artifact.outputPath), signal);
|
|
3064
|
+
}
|
|
3065
|
+
}));
|
|
3066
|
+
}
|
|
3067
|
+
watchSchemaChanges(signal) {
|
|
3068
|
+
const context = new ReactiveContext();
|
|
3069
|
+
(async () => {
|
|
3070
|
+
let prevNames = new Set(this._schemas.get().map((s) => s.name));
|
|
3071
|
+
try {
|
|
3072
|
+
for await (const schemas of context.stream(() => Promise.resolve(this._schemas.get()), signal)) {
|
|
3073
|
+
const newNames = new Set(schemas.map((s) => s.name));
|
|
3074
|
+
for (const name of newNames) if (!prevNames.has(name)) this.warmupSchema(name, signal);
|
|
3075
|
+
for (const name of prevNames) if (!newNames.has(name)) {
|
|
3076
|
+
this.teardownEntity(`schema:${name}`);
|
|
3077
|
+
this._schemaResolutions.delete(name);
|
|
3078
|
+
this._schemaDetails.delete(name);
|
|
3079
|
+
this._schemaFiles.delete(name);
|
|
3080
|
+
this._schemaYamls.delete(name);
|
|
3081
|
+
this._templates.delete(name);
|
|
3082
|
+
this._templateContents.delete(name);
|
|
3083
|
+
}
|
|
3084
|
+
prevNames = newNames;
|
|
3085
|
+
}
|
|
3086
|
+
} catch {}
|
|
3087
|
+
})();
|
|
3088
|
+
}
|
|
3089
|
+
watchChangeIdChanges(signal) {
|
|
3090
|
+
const context = new ReactiveContext();
|
|
3091
|
+
(async () => {
|
|
3092
|
+
let prevIds = new Set(this._changeIds.get());
|
|
3093
|
+
try {
|
|
3094
|
+
for await (const ids of context.stream(() => Promise.resolve(this._changeIds.get()), signal)) {
|
|
3095
|
+
const newIds = new Set(ids);
|
|
3096
|
+
for (const id of newIds) if (!prevIds.has(id)) this.warmupChange(id, signal);
|
|
3097
|
+
for (const id of prevIds) if (!newIds.has(id)) {
|
|
3098
|
+
this.teardownEntity(`change:${id}`);
|
|
3099
|
+
for (const key of this._statuses.keys()) if (key.startsWith(`${id}:`)) this._statuses.delete(key);
|
|
3100
|
+
for (const key of this._instructions.keys()) if (key.startsWith(`${id}:`)) this._instructions.delete(key);
|
|
3101
|
+
for (const key of this._applyInstructions.keys()) if (key.startsWith(`${id}:`)) this._applyInstructions.delete(key);
|
|
3102
|
+
this._changeMetadata.delete(id);
|
|
3103
|
+
for (const key of this._artifactOutputs.keys()) if (key.startsWith(`${id}:`)) this._artifactOutputs.delete(key);
|
|
3104
|
+
for (const key of this._globArtifactFiles.keys()) if (key.startsWith(`${id}:`)) this._globArtifactFiles.delete(key);
|
|
3105
|
+
}
|
|
3106
|
+
prevIds = newIds;
|
|
3107
|
+
}
|
|
3108
|
+
} catch {}
|
|
3109
|
+
})();
|
|
3110
|
+
}
|
|
3111
|
+
teardownEntity(key) {
|
|
3112
|
+
const ctrl = this._entityControllers.get(key);
|
|
3113
|
+
if (ctrl) {
|
|
3114
|
+
ctrl.abort();
|
|
3115
|
+
this._entityControllers.delete(key);
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
async fetchSchemas() {
|
|
3119
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3120
|
+
const result = await this.cliExecutor.schemas();
|
|
3121
|
+
if (!result.success) throw new Error(result.stderr || `openspec schemas failed (exit ${result.exitCode ?? "null"})`);
|
|
3122
|
+
return parseCliJson(result.stdout, z.array(SchemaInfoSchema), "openspec schemas");
|
|
3123
|
+
}
|
|
3124
|
+
async fetchChangeIds() {
|
|
3125
|
+
return reactiveReadDir(join$1(this.projectDir, "openspec", "changes"), {
|
|
3126
|
+
directoriesOnly: true,
|
|
3127
|
+
includeHidden: false,
|
|
3128
|
+
exclude: ["archive"]
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
async fetchProjectConfig() {
|
|
3132
|
+
return reactiveReadFile(join$1(this.projectDir, "openspec", "config.yaml"));
|
|
3133
|
+
}
|
|
3134
|
+
async fetchStatus(changeId, schema) {
|
|
3135
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3136
|
+
await touchOpsxChangeDeps(this.projectDir, changeId);
|
|
3137
|
+
const args = [
|
|
3138
|
+
"status",
|
|
3139
|
+
"--json",
|
|
3140
|
+
"--change",
|
|
3141
|
+
changeId
|
|
3142
|
+
];
|
|
3143
|
+
if (schema) args.push("--schema", schema);
|
|
3144
|
+
const result = await this.cliExecutor.execute(args);
|
|
3145
|
+
if (!result.success) throw new Error(result.stderr || `openspec status failed (exit ${result.exitCode ?? "null"})`);
|
|
3146
|
+
const status = parseCliJson(result.stdout, ChangeStatusSchema, "openspec status");
|
|
3147
|
+
const changeRelDir = `openspec/changes/${changeId}`;
|
|
3148
|
+
for (const artifact of status.artifacts) artifact.relativePath = `${changeRelDir}/${artifact.outputPath}`;
|
|
3149
|
+
return status;
|
|
3150
|
+
}
|
|
3151
|
+
async fetchStatusList() {
|
|
3152
|
+
const changeIds = this._changeIds.get();
|
|
3153
|
+
return Promise.all(changeIds.map((id) => {
|
|
3154
|
+
const key = `${id}:`;
|
|
3155
|
+
const state = this._statuses.get(key);
|
|
3156
|
+
if (state) return Promise.resolve(state.get());
|
|
3157
|
+
return this.fetchStatus(id);
|
|
3158
|
+
}));
|
|
3159
|
+
}
|
|
3160
|
+
async fetchInstructions(changeId, artifact, schema) {
|
|
3161
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3162
|
+
await touchOpsxChangeDeps(this.projectDir, changeId);
|
|
3163
|
+
const args = [
|
|
3164
|
+
"instructions",
|
|
3165
|
+
artifact,
|
|
3166
|
+
"--json",
|
|
3167
|
+
"--change",
|
|
3168
|
+
changeId
|
|
3169
|
+
];
|
|
3170
|
+
if (schema) args.push("--schema", schema);
|
|
3171
|
+
const result = await this.cliExecutor.execute(args);
|
|
3172
|
+
if (!result.success) throw new Error(result.stderr || `openspec instructions failed (exit ${result.exitCode ?? "null"})`);
|
|
3173
|
+
return parseCliJson(result.stdout, ArtifactInstructionsSchema, "openspec instructions");
|
|
3174
|
+
}
|
|
3175
|
+
async fetchApplyInstructions(changeId, schema) {
|
|
3176
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3177
|
+
await touchOpsxChangeDeps(this.projectDir, changeId);
|
|
3178
|
+
const args = [
|
|
3179
|
+
"instructions",
|
|
3180
|
+
"apply",
|
|
3181
|
+
"--json",
|
|
3182
|
+
"--change",
|
|
3183
|
+
changeId
|
|
3184
|
+
];
|
|
3185
|
+
if (schema) args.push("--schema", schema);
|
|
3186
|
+
const result = await this.cliExecutor.execute(args);
|
|
3187
|
+
if (!result.success) throw new Error(result.stderr || `openspec instructions apply failed (exit ${result.exitCode ?? "null"})`);
|
|
3188
|
+
return parseCliJson(result.stdout, ApplyInstructionsSchema, "openspec instructions apply");
|
|
3189
|
+
}
|
|
3190
|
+
async fetchSchemaResolution(name) {
|
|
3191
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3192
|
+
const result = await this.cliExecutor.schemaWhich(name);
|
|
3193
|
+
if (!result.success) throw new Error(result.stderr || `openspec schema which failed (exit ${result.exitCode ?? "null"})`);
|
|
3194
|
+
return parseCliJson(result.stdout, SchemaResolutionSchema, "openspec schema which");
|
|
3195
|
+
}
|
|
3196
|
+
async fetchSchemaDetail(name) {
|
|
3197
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3198
|
+
const schemaPath = join$1((await this.fetchSchemaResolution(name)).path, "schema.yaml");
|
|
3199
|
+
const content = await reactiveReadFile(schemaPath);
|
|
3200
|
+
if (!content) throw new Error(`schema.yaml not found at ${schemaPath}`);
|
|
3201
|
+
return parseSchemaYamlInline(content);
|
|
3202
|
+
}
|
|
3203
|
+
async fetchSchemaFiles(name) {
|
|
3204
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3205
|
+
return readEntriesUnderRoot((await this.fetchSchemaResolution(name)).path);
|
|
3206
|
+
}
|
|
3207
|
+
async fetchSchemaYaml(name) {
|
|
3208
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3209
|
+
return reactiveReadFile(join$1((await this.fetchSchemaResolution(name)).path, "schema.yaml"));
|
|
3210
|
+
}
|
|
3211
|
+
async fetchTemplates(schema) {
|
|
3212
|
+
await touchOpsxProjectDeps(this.projectDir);
|
|
3213
|
+
const result = await this.cliExecutor.templates(schema);
|
|
3214
|
+
if (!result.success) throw new Error(result.stderr || `openspec templates failed (exit ${result.exitCode ?? "null"})`);
|
|
3215
|
+
return parseCliJson(result.stdout, TemplatesSchema, "openspec templates");
|
|
3216
|
+
}
|
|
3217
|
+
async fetchTemplateContents(schema) {
|
|
3218
|
+
const templates = await this.fetchTemplates(schema);
|
|
3219
|
+
const entries = await Promise.all(Object.entries(templates).map(async ([artifactId, info]) => {
|
|
3220
|
+
return [artifactId, {
|
|
3221
|
+
content: await reactiveReadFile(info.path),
|
|
3222
|
+
path: info.path,
|
|
3223
|
+
source: info.source
|
|
3224
|
+
}];
|
|
3225
|
+
}));
|
|
3226
|
+
return Object.fromEntries(entries);
|
|
3227
|
+
}
|
|
3228
|
+
async fetchChangeMetadata(changeId) {
|
|
3229
|
+
return reactiveReadFile(join$1(this.projectDir, "openspec", "changes", changeId, ".openspec.yaml"));
|
|
3230
|
+
}
|
|
3231
|
+
async fetchArtifactOutput(changeId, outputPath) {
|
|
3232
|
+
return reactiveReadFile(join$1(this.projectDir, "openspec", "changes", changeId, outputPath));
|
|
3233
|
+
}
|
|
3234
|
+
/**
|
|
3235
|
+
* Ensure a per-change status stream exists. If not yet warmed up,
|
|
3236
|
+
* creates the state and starts a stream lazily.
|
|
3237
|
+
*/
|
|
3238
|
+
ensureStatus(changeId, schema) {
|
|
3239
|
+
const key = `${changeId}:${schema ?? ""}`;
|
|
3240
|
+
if (!this._statuses.has(key)) {
|
|
3241
|
+
this._statuses.set(key, new ReactiveState(null));
|
|
3242
|
+
this.startStream(this._statuses.get(key), () => this.fetchStatus(changeId, schema), this.controller.signal);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
ensureInstructions(changeId, artifact, schema) {
|
|
3246
|
+
const key = `${changeId}:${artifact}:${schema ?? ""}`;
|
|
3247
|
+
if (!this._instructions.has(key)) {
|
|
3248
|
+
this._instructions.set(key, new ReactiveState(null));
|
|
3249
|
+
this.startStream(this._instructions.get(key), () => this.fetchInstructions(changeId, artifact, schema), this.controller.signal);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
ensureApplyInstructions(changeId, schema) {
|
|
3253
|
+
const key = `${changeId}:${schema ?? ""}`;
|
|
3254
|
+
if (!this._applyInstructions.has(key)) {
|
|
3255
|
+
this._applyInstructions.set(key, new ReactiveState(null));
|
|
3256
|
+
this.startStream(this._applyInstructions.get(key), () => this.fetchApplyInstructions(changeId, schema), this.controller.signal);
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
ensureArtifactOutput(changeId, outputPath) {
|
|
3260
|
+
const key = `${changeId}:${outputPath}`;
|
|
3261
|
+
if (!this._artifactOutputs.has(key)) {
|
|
3262
|
+
this._artifactOutputs.set(key, new ReactiveState(null));
|
|
3263
|
+
this.startStream(this._artifactOutputs.get(key), () => this.fetchArtifactOutput(changeId, outputPath), this.controller.signal);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
ensureGlobArtifactFiles(changeId, outputPath) {
|
|
3267
|
+
const key = `${changeId}:${outputPath}`;
|
|
3268
|
+
if (!this._globArtifactFiles.has(key)) {
|
|
3269
|
+
this._globArtifactFiles.set(key, new ReactiveState([]));
|
|
3270
|
+
this.startStream(this._globArtifactFiles.get(key), () => readGlobArtifactFiles(this.projectDir, changeId, outputPath), this.controller.signal);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
ensureSchemaResolution(name) {
|
|
3274
|
+
if (!this._schemaResolutions.has(name)) {
|
|
3275
|
+
this._schemaResolutions.set(name, new ReactiveState(null));
|
|
3276
|
+
this.startStream(this._schemaResolutions.get(name), () => this.fetchSchemaResolution(name), this.controller.signal);
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
ensureSchemaDetail(name) {
|
|
3280
|
+
if (!this._schemaDetails.has(name)) {
|
|
3281
|
+
this._schemaDetails.set(name, new ReactiveState(null));
|
|
3282
|
+
this.startStream(this._schemaDetails.get(name), () => this.fetchSchemaDetail(name), this.controller.signal);
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
ensureSchemaFiles(name) {
|
|
3286
|
+
if (!this._schemaFiles.has(name)) {
|
|
3287
|
+
this._schemaFiles.set(name, new ReactiveState([]));
|
|
3288
|
+
this.startStream(this._schemaFiles.get(name), () => this.fetchSchemaFiles(name), this.controller.signal);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
ensureSchemaYaml(name) {
|
|
3292
|
+
if (!this._schemaYamls.has(name)) {
|
|
3293
|
+
this._schemaYamls.set(name, new ReactiveState(null));
|
|
3294
|
+
this.startStream(this._schemaYamls.get(name), () => this.fetchSchemaYaml(name), this.controller.signal);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
ensureTemplates(schema) {
|
|
3298
|
+
const key = schema ?? "";
|
|
3299
|
+
if (!this._templates.has(key)) {
|
|
3300
|
+
this._templates.set(key, new ReactiveState({}));
|
|
3301
|
+
this.startStream(this._templates.get(key), () => this.fetchTemplates(schema), this.controller.signal);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
ensureTemplateContents(schema) {
|
|
3305
|
+
const key = schema ?? "";
|
|
3306
|
+
if (!this._templateContents.has(key)) {
|
|
3307
|
+
this._templateContents.set(key, new ReactiveState({}));
|
|
3308
|
+
this.startStream(this._templateContents.get(key), () => this.fetchTemplateContents(schema), this.controller.signal);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
ensureChangeMetadata(changeId) {
|
|
3312
|
+
if (!this._changeMetadata.has(changeId)) {
|
|
3313
|
+
this._changeMetadata.set(changeId, new ReactiveState(null));
|
|
3314
|
+
this.startStream(this._changeMetadata.get(changeId), () => this.fetchChangeMetadata(changeId), this.controller.signal);
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
combineSignals(a, b) {
|
|
3318
|
+
const ctrl = new AbortController();
|
|
3319
|
+
const abort = () => ctrl.abort();
|
|
3320
|
+
if (a.aborted || b.aborted) {
|
|
3321
|
+
ctrl.abort();
|
|
3322
|
+
return ctrl.signal;
|
|
3323
|
+
}
|
|
3324
|
+
a.addEventListener("abort", abort, { once: true });
|
|
3325
|
+
b.addEventListener("abort", abort, { once: true });
|
|
3326
|
+
return ctrl.signal;
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
const SchemaYamlArtifactSchema = z.object({
|
|
3330
|
+
id: z.string(),
|
|
3331
|
+
generates: z.string(),
|
|
3332
|
+
description: z.string().optional(),
|
|
3333
|
+
template: z.string().optional(),
|
|
3334
|
+
instruction: z.string().optional(),
|
|
3335
|
+
requires: z.array(z.string()).optional()
|
|
3336
|
+
});
|
|
3337
|
+
const SchemaYamlSchema = z.object({
|
|
3338
|
+
name: z.string(),
|
|
3339
|
+
version: z.union([z.string(), z.number()]).optional(),
|
|
3340
|
+
description: z.string().optional(),
|
|
3341
|
+
artifacts: z.array(SchemaYamlArtifactSchema),
|
|
3342
|
+
apply: z.object({
|
|
3343
|
+
requires: z.array(z.string()).optional(),
|
|
3344
|
+
tracks: z.string().optional(),
|
|
3345
|
+
instruction: z.string().optional()
|
|
3346
|
+
}).optional()
|
|
3347
|
+
});
|
|
3348
|
+
function parseSchemaYamlInline(content) {
|
|
3349
|
+
const raw = parse(content);
|
|
3350
|
+
const parsed = SchemaYamlSchema.safeParse(raw);
|
|
3351
|
+
if (!parsed.success) throw new Error(`Invalid schema.yaml: ${parsed.error.message}`);
|
|
3352
|
+
const { artifacts, apply, name, description, version } = parsed.data;
|
|
3353
|
+
const detail = {
|
|
3354
|
+
name,
|
|
3355
|
+
description,
|
|
3356
|
+
version,
|
|
3357
|
+
artifacts: artifacts.map((artifact) => ({
|
|
3358
|
+
id: artifact.id,
|
|
3359
|
+
outputPath: artifact.generates,
|
|
3360
|
+
description: artifact.description,
|
|
3361
|
+
template: artifact.template,
|
|
3362
|
+
instruction: artifact.instruction,
|
|
3363
|
+
requires: artifact.requires ?? []
|
|
3364
|
+
})),
|
|
3365
|
+
applyRequires: apply?.requires ?? [],
|
|
3366
|
+
applyTracks: apply?.tracks,
|
|
3367
|
+
applyInstruction: apply?.instruction
|
|
3368
|
+
};
|
|
3369
|
+
const validated = SchemaDetailSchema.safeParse(detail);
|
|
3370
|
+
if (!validated.success) throw new Error(`Invalid schema detail: ${validated.error.message}`);
|
|
3371
|
+
return validated.data;
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
//#endregion
|
|
3375
|
+
//#region src/pty-protocol.ts
|
|
3376
|
+
const PositiveInt = z.number().int().positive();
|
|
3377
|
+
const PtyPlatformSchema = z.enum([
|
|
3378
|
+
"windows",
|
|
3379
|
+
"macos",
|
|
3380
|
+
"common"
|
|
3381
|
+
]);
|
|
3382
|
+
const PtySessionInfoSchema = z.object({
|
|
3383
|
+
id: z.string().min(1),
|
|
3384
|
+
title: z.string(),
|
|
3385
|
+
command: z.string(),
|
|
3386
|
+
args: z.array(z.string()),
|
|
3387
|
+
platform: PtyPlatformSchema,
|
|
3388
|
+
isExited: z.boolean(),
|
|
3389
|
+
exitCode: z.number().int().nullable()
|
|
3390
|
+
});
|
|
3391
|
+
const PtyCreateMessageSchema = z.object({
|
|
3392
|
+
type: z.literal("create"),
|
|
3393
|
+
requestId: z.string().min(1),
|
|
3394
|
+
cols: PositiveInt.optional(),
|
|
3395
|
+
rows: PositiveInt.optional(),
|
|
3396
|
+
command: z.string().min(1).optional(),
|
|
3397
|
+
args: z.array(z.string()).optional()
|
|
3398
|
+
});
|
|
3399
|
+
const PtyInputMessageSchema = z.object({
|
|
3400
|
+
type: z.literal("input"),
|
|
3401
|
+
sessionId: z.string().min(1),
|
|
3402
|
+
data: z.string()
|
|
3403
|
+
});
|
|
3404
|
+
const PtyResizeMessageSchema = z.object({
|
|
3405
|
+
type: z.literal("resize"),
|
|
3406
|
+
sessionId: z.string().min(1),
|
|
3407
|
+
cols: PositiveInt,
|
|
3408
|
+
rows: PositiveInt
|
|
3409
|
+
});
|
|
3410
|
+
const PtyCloseMessageSchema = z.object({
|
|
3411
|
+
type: z.literal("close"),
|
|
3412
|
+
sessionId: z.string().min(1)
|
|
3413
|
+
});
|
|
3414
|
+
const PtyAttachMessageSchema = z.object({
|
|
3415
|
+
type: z.literal("attach"),
|
|
3416
|
+
sessionId: z.string().min(1),
|
|
3417
|
+
cols: PositiveInt.optional(),
|
|
3418
|
+
rows: PositiveInt.optional()
|
|
3419
|
+
});
|
|
3420
|
+
const PtyListMessageSchema = z.object({ type: z.literal("list") });
|
|
3421
|
+
const PtyClientMessageSchema = z.discriminatedUnion("type", [
|
|
3422
|
+
PtyCreateMessageSchema,
|
|
3423
|
+
PtyInputMessageSchema,
|
|
3424
|
+
PtyResizeMessageSchema,
|
|
3425
|
+
PtyCloseMessageSchema,
|
|
3426
|
+
PtyAttachMessageSchema,
|
|
3427
|
+
PtyListMessageSchema
|
|
3428
|
+
]);
|
|
3429
|
+
const PtyCreatedResponseSchema = z.object({
|
|
3430
|
+
type: z.literal("created"),
|
|
3431
|
+
requestId: z.string().min(1),
|
|
3432
|
+
sessionId: z.string().min(1),
|
|
3433
|
+
platform: PtyPlatformSchema
|
|
3434
|
+
});
|
|
3435
|
+
const PtyOutputResponseSchema = z.object({
|
|
3436
|
+
type: z.literal("output"),
|
|
3437
|
+
sessionId: z.string().min(1),
|
|
3438
|
+
data: z.string()
|
|
3439
|
+
});
|
|
3440
|
+
const PtyExitResponseSchema = z.object({
|
|
3441
|
+
type: z.literal("exit"),
|
|
3442
|
+
sessionId: z.string().min(1),
|
|
3443
|
+
exitCode: z.number().int()
|
|
3444
|
+
});
|
|
3445
|
+
const PtyTitleResponseSchema = z.object({
|
|
3446
|
+
type: z.literal("title"),
|
|
3447
|
+
sessionId: z.string().min(1),
|
|
3448
|
+
title: z.string()
|
|
3449
|
+
});
|
|
3450
|
+
const PtyBufferResponseSchema = z.object({
|
|
3451
|
+
type: z.literal("buffer"),
|
|
3452
|
+
sessionId: z.string().min(1),
|
|
3453
|
+
data: z.string()
|
|
3454
|
+
});
|
|
3455
|
+
const PtyListResponseSchema = z.object({
|
|
3456
|
+
type: z.literal("list"),
|
|
3457
|
+
sessions: z.array(PtySessionInfoSchema)
|
|
3458
|
+
});
|
|
3459
|
+
const PtyErrorCodeSchema = z.enum([
|
|
3460
|
+
"INVALID_JSON",
|
|
3461
|
+
"INVALID_MESSAGE",
|
|
3462
|
+
"SESSION_NOT_FOUND"
|
|
3463
|
+
]);
|
|
3464
|
+
const PtyErrorResponseSchema = z.object({
|
|
3465
|
+
type: z.literal("error"),
|
|
3466
|
+
code: PtyErrorCodeSchema,
|
|
3467
|
+
message: z.string().min(1),
|
|
3468
|
+
sessionId: z.string().min(1).optional()
|
|
3469
|
+
});
|
|
3470
|
+
const PtyServerMessageSchema = z.discriminatedUnion("type", [
|
|
3471
|
+
PtyCreatedResponseSchema,
|
|
3472
|
+
PtyOutputResponseSchema,
|
|
3473
|
+
PtyExitResponseSchema,
|
|
3474
|
+
PtyTitleResponseSchema,
|
|
3475
|
+
PtyBufferResponseSchema,
|
|
3476
|
+
PtyListResponseSchema,
|
|
3477
|
+
PtyErrorResponseSchema
|
|
3478
|
+
]);
|
|
3479
|
+
|
|
3480
|
+
//#endregion
|
|
3481
|
+
export { AI_TOOLS, ApplyInstructionsSchema, ApplyTaskSchema, ArtifactInstructionsSchema, ArtifactStatusSchema, ChangeFileSchema, ChangeSchema, ChangeStatusSchema, CliExecutor, ConfigManager, DEFAULT_CONFIG, DeltaOperationType, DeltaSchema, DeltaSpecSchema, DependencyInfoSchema, MarkdownParser, OpenSpecAdapter, OpenSpecUIConfigSchema, OpenSpecWatcher, OpsxKernel, ProjectWatcher, PtyAttachMessageSchema, PtyBufferResponseSchema, PtyClientMessageSchema, PtyCloseMessageSchema, PtyCreateMessageSchema, PtyCreatedResponseSchema, PtyErrorCodeSchema, PtyErrorResponseSchema, PtyExitResponseSchema, PtyInputMessageSchema, PtyListMessageSchema, PtyListResponseSchema, PtyOutputResponseSchema, PtyPlatformSchema, PtyResizeMessageSchema, PtyServerMessageSchema, PtyTitleResponseSchema, ReactiveContext, ReactiveState, RequirementSchema, SchemaArtifactSchema, SchemaDetailSchema, SchemaInfoSchema, SchemaResolutionSchema, SpecSchema, TaskSchema, TemplatesSchema, Validator, acquireWatcher, clearCache, closeAllProjectWatchers, closeAllWatchers, contextStorage, createFileChangeObservable, getActiveWatcherCount, getAllToolIds, getAllTools, getAvailableToolIds, getAvailableTools, getCacheSize, getConfiguredTools, getDefaultCliCommand, getDefaultCliCommandString, getProjectWatcher, getToolById, getWatchedProjectDir, initWatcherPool, isGlobPattern, isToolConfigured, isWatcherPoolInitialized, parseCliCommand, reactiveExists, reactiveReadDir, reactiveReadFile, reactiveStat, sniffGlobalCli };
|