@openspecui/core 2.1.2 → 2.1.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/dist/index.d.mts +58 -14
- package/dist/index.mjs +277 -70
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -696,18 +696,10 @@ declare class OpenSpecAdapter {
|
|
|
696
696
|
* Read project.md content (reactive)
|
|
697
697
|
*/
|
|
698
698
|
readProjectMd(): Promise<string | null>;
|
|
699
|
-
/**
|
|
700
|
-
* Read AGENTS.md content (reactive)
|
|
701
|
-
*/
|
|
702
|
-
readAgentsMd(): Promise<string | null>;
|
|
703
699
|
/**
|
|
704
700
|
* Write project.md content
|
|
705
701
|
*/
|
|
706
702
|
writeProjectMd(content: string): Promise<void>;
|
|
707
|
-
/**
|
|
708
|
-
* Write AGENTS.md content
|
|
709
|
-
*/
|
|
710
|
-
writeAgentsMd(content: string): Promise<void>;
|
|
711
703
|
readSpec(specId: string): Promise<Spec | null>;
|
|
712
704
|
readSpecRaw(specId: string): Promise<string | null>;
|
|
713
705
|
readChange(changeId: string): Promise<Change | null>;
|
|
@@ -1538,6 +1530,9 @@ declare const DEFAULT_CONFIG: OpenSpecUIConfig;
|
|
|
1538
1530
|
*
|
|
1539
1531
|
* 负责读写 openspec/.openspecui.json 配置文件。
|
|
1540
1532
|
* 读取操作使用 reactiveReadFile,支持响应式更新。
|
|
1533
|
+
*
|
|
1534
|
+
* `.openspecui.json` 是预期中的项目级 UI 配置文件,但只有显式偏离默认值的
|
|
1535
|
+
* override 才会落盘。仅启动 openspecui 或仅依赖默认配置时,不应触发文件写入。
|
|
1541
1536
|
*/
|
|
1542
1537
|
declare class ConfigManager {
|
|
1543
1538
|
private configPath;
|
|
@@ -1545,6 +1540,7 @@ declare class ConfigManager {
|
|
|
1545
1540
|
private resolvedRunner;
|
|
1546
1541
|
private resolvingRunnerPromise;
|
|
1547
1542
|
constructor(projectDir: string);
|
|
1543
|
+
private parseConfigContent;
|
|
1548
1544
|
/**
|
|
1549
1545
|
* 读取配置(响应式)
|
|
1550
1546
|
*
|
|
@@ -1558,6 +1554,8 @@ declare class ConfigManager {
|
|
|
1558
1554
|
* 会触发文件监听,自动更新订阅者。
|
|
1559
1555
|
*/
|
|
1560
1556
|
writeConfig(config: OpenSpecUIConfigUpdate): Promise<void>;
|
|
1557
|
+
private resolveDefaultCliCommandParts;
|
|
1558
|
+
private isDefaultCliCommand;
|
|
1561
1559
|
/**
|
|
1562
1560
|
* 解析并缓存可用 CLI runner。
|
|
1563
1561
|
*/
|
|
@@ -1691,6 +1689,9 @@ declare class CliExecutor {
|
|
|
1691
1689
|
}, onEvent: (event: CliStreamEvent) => void): Promise<() => void>;
|
|
1692
1690
|
/**
|
|
1693
1691
|
* 流式执行任意命令(数组形式)
|
|
1692
|
+
*
|
|
1693
|
+
* 字面量 `openspec` 会自动通过已解析的 CLI runner 执行,
|
|
1694
|
+
* 其它命令保持原始 spawn 行为。
|
|
1694
1695
|
*/
|
|
1695
1696
|
executeCommandStream(command: readonly string[], onEvent: (event: CliStreamEvent) => void): () => void;
|
|
1696
1697
|
}
|
|
@@ -1711,7 +1712,7 @@ interface AIToolOption {
|
|
|
1711
1712
|
name: string;
|
|
1712
1713
|
/** 工具 ID(用于 CLI 参数) */
|
|
1713
1714
|
value: string;
|
|
1714
|
-
/**
|
|
1715
|
+
/** 是否可由 openspec init 直接管理 */
|
|
1715
1716
|
available: boolean;
|
|
1716
1717
|
/** 成功消息中使用的标签 */
|
|
1717
1718
|
successLabel?: string;
|
|
@@ -1736,11 +1737,18 @@ declare function getAvailableTools(): ToolConfig[];
|
|
|
1736
1737
|
*/
|
|
1737
1738
|
declare function getAvailableToolIds(): string[];
|
|
1738
1739
|
/**
|
|
1739
|
-
*
|
|
1740
|
+
* 获取所有工具
|
|
1740
1741
|
*/
|
|
1741
1742
|
declare function getAllTools(): ToolConfig[];
|
|
1742
1743
|
/**
|
|
1743
|
-
*
|
|
1744
|
+
* 检测当前项目中已经存在的工具目录。
|
|
1745
|
+
*
|
|
1746
|
+
* 这里对齐 OpenSpec 官方 `getAvailableTools(projectPath)` 的语义:
|
|
1747
|
+
* 仅根据项目根目录下的工具目录是否存在来判断,不读取全局命令安装状态。
|
|
1748
|
+
*/
|
|
1749
|
+
declare function getDetectedProjectTools(projectDir: string): Promise<ToolConfig[]>;
|
|
1750
|
+
/**
|
|
1751
|
+
* 获取所有工具 ID 列表
|
|
1744
1752
|
*/
|
|
1745
1753
|
declare function getAllToolIds(): string[];
|
|
1746
1754
|
/**
|
|
@@ -1756,6 +1764,44 @@ declare function getConfiguredTools(projectDir: string): Promise<string[]>;
|
|
|
1756
1764
|
*/
|
|
1757
1765
|
declare function isToolConfigured(projectDir: string, toolId: string): Promise<boolean>;
|
|
1758
1766
|
//#endregion
|
|
1767
|
+
//#region src/tool-init-state.d.ts
|
|
1768
|
+
declare const TOOL_WORKFLOW_TO_SKILL_DIR: {
|
|
1769
|
+
readonly propose: "openspec-propose";
|
|
1770
|
+
readonly explore: "openspec-explore";
|
|
1771
|
+
readonly new: "openspec-new-change";
|
|
1772
|
+
readonly continue: "openspec-continue-change";
|
|
1773
|
+
readonly apply: "openspec-apply-change";
|
|
1774
|
+
readonly ff: "openspec-ff-change";
|
|
1775
|
+
readonly sync: "openspec-sync-specs";
|
|
1776
|
+
readonly archive: "openspec-archive-change";
|
|
1777
|
+
readonly 'bulk-archive': "openspec-bulk-archive-change";
|
|
1778
|
+
readonly verify: "openspec-verify-change";
|
|
1779
|
+
readonly onboard: "openspec-onboard";
|
|
1780
|
+
};
|
|
1781
|
+
type ToolWorkflowId = keyof typeof TOOL_WORKFLOW_TO_SKILL_DIR;
|
|
1782
|
+
type ToolInitDelivery = 'both' | 'skills' | 'commands';
|
|
1783
|
+
type ToolInitStatus = 'uninitialized' | 'partial' | 'initialized';
|
|
1784
|
+
interface ToolInitState {
|
|
1785
|
+
toolId: string;
|
|
1786
|
+
toolName: string;
|
|
1787
|
+
status: ToolInitStatus;
|
|
1788
|
+
hasAnyArtifacts: boolean;
|
|
1789
|
+
expectedSkillCount: number;
|
|
1790
|
+
presentExpectedSkillCount: number;
|
|
1791
|
+
detectedSkillCount: number;
|
|
1792
|
+
expectedCommandCount: number;
|
|
1793
|
+
presentExpectedCommandCount: number;
|
|
1794
|
+
detectedCommandCount: number;
|
|
1795
|
+
missingSkillWorkflows: ToolWorkflowId[];
|
|
1796
|
+
missingCommandWorkflows: ToolWorkflowId[];
|
|
1797
|
+
unexpectedSkillWorkflows: ToolWorkflowId[];
|
|
1798
|
+
unexpectedCommandWorkflows: ToolWorkflowId[];
|
|
1799
|
+
}
|
|
1800
|
+
declare function getToolInitStates(projectDir: string, options: {
|
|
1801
|
+
delivery: ToolInitDelivery;
|
|
1802
|
+
workflows: readonly string[];
|
|
1803
|
+
}): Promise<ToolInitState[]>;
|
|
1804
|
+
//#endregion
|
|
1759
1805
|
//#region src/dashboard-types.d.ts
|
|
1760
1806
|
declare const DASHBOARD_METRIC_KEYS: readonly ["specifications", "requirements", "activeChanges", "inProgressChanges", "completedChanges", "taskCompletionPercent"];
|
|
1761
1807
|
type DashboardMetricKey = (typeof DASHBOARD_METRIC_KEYS)[number];
|
|
@@ -2346,8 +2392,6 @@ interface ExportSnapshot {
|
|
|
2346
2392
|
}>;
|
|
2347
2393
|
/** Project.md content */
|
|
2348
2394
|
projectMd?: string;
|
|
2349
|
-
/** AGENTS.md content */
|
|
2350
|
-
agentsMd?: string;
|
|
2351
2395
|
/** OPSX configuration data (for Config view) */
|
|
2352
2396
|
opsx?: {
|
|
2353
2397
|
configYaml?: string;
|
|
@@ -2962,4 +3006,4 @@ type PtyServerMessage = z.infer<typeof PtyServerMessageSchema>;
|
|
|
2962
3006
|
type PtySessionInfo = z.infer<typeof PtySessionInfoSchema>;
|
|
2963
3007
|
type PtyPlatform = z.infer<typeof PtyPlatformSchema>;
|
|
2964
3008
|
//#endregion
|
|
2965
|
-
export { type AIToolOption, AI_TOOLS, type ApplyInstructions, ApplyInstructionsSchema, type ApplyTask, ApplyTaskSchema, type ArchiveMeta, type ArtifactInstructions, ArtifactInstructionsSchema, type ArtifactStatus, ArtifactStatusSchema, CODE_EDITOR_THEME_VALUES, type Change, type ChangeFile, ChangeFileSchema, type ChangeMeta, ChangeSchema, type ChangeStatus, ChangeStatusSchema, CliExecutor, type CliResult, type CliRunnerAttempt, type CliSniffResult, type CliStreamEvent, type CodeEditorTheme, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DEFAULT_CONFIG, type DashboardCardAvailability, type DashboardConfig, DashboardConfigSchema, type DashboardGitCommitEntry, type DashboardGitDiffStats, type DashboardGitEntry, type DashboardGitSnapshot, type DashboardGitUncommittedEntry, type DashboardGitWorktree, type DashboardMetricKey, type DashboardOverview, type DashboardSummary, type DashboardTrendKind, type DashboardTrendMeta, type DashboardTrendPoint, type DashboardTriColorTrendPoint, type Delta, type DeltaOperation, DeltaOperationType, DeltaSchema, type DeltaSpec, DeltaSpecSchema, type DependencyInfo, DependencyInfoSchema, type ExportSnapshot, type FileChangeEvent, type FileChangeType, type HostedAppChannelKind, type HostedAppChannelManifest, type HostedAppCompatibilityEntry, type HostedAppVersionManifest, type HostedBackendHealthResponse, MarkdownParser, OFFICIAL_APP_BASE_URL, OpenSpecAdapter, type OpenSpecUIConfig, OpenSpecUIConfigSchema, type OpenSpecUIConfigUpdate, OpenSpecWatcher, OpsxKernel, type PathCallback, ProjectWatcher, type ProjectWatcherReinitializeReason, type ProjectWatcherRuntimeStatus, PtyAttachMessageSchema, PtyBufferResponseSchema, type PtyClientMessage, PtyClientMessageSchema, PtyCloseMessageSchema, PtyCreateMessageSchema, PtyCreatedResponseSchema, PtyErrorCodeSchema, PtyErrorResponseSchema, PtyExitResponseSchema, PtyInputMessageSchema, PtyListMessageSchema, PtyListResponseSchema, PtyOutputResponseSchema, type PtyPlatform, PtyPlatformSchema, PtyResizeMessageSchema, type PtyServerMessage, PtyServerMessageSchema, type PtySessionInfo, PtyTitleResponseSchema, ReactiveContext, ReactiveState, type ReactiveStateOptions, type Requirement, RequirementSchema, type ResolvedCliRunner, type SchemaArtifact, SchemaArtifactSchema, type SchemaDetail, SchemaDetailSchema, type SchemaInfo, SchemaInfoSchema, type SchemaResolution, SchemaResolutionSchema, type Spec, type SpecMeta, SpecSchema, type Task, TaskSchema, type TemplateContentMap, type TemplatesMap, TemplatesSchema, type TerminalConfig, TerminalConfigSchema, type TerminalRendererEngine, TerminalRendererEngineSchema, type ToolConfig, VIRTUAL_PROJECT_DIRNAME, type ValidationIssue, type ValidationResult, Validator, type WatchEvent, type WatchEventType, type WatcherRuntimeStatus, acquireWatcher, buildCliRunnerCandidates, buildHostedLaunchUrl, buildHostedVersionManifestUrl, clearCache, closeAllProjectWatchers, closeAllWatchers, contextStorage, createCleanCliEnv, createFileChangeObservable, getActiveWatcherCount, getAllToolIds, getAllTools, getAvailableToolIds, getAvailableTools, getCacheSize, getConfiguredTools, getDefaultCliCommand, getDefaultCliCommandString, getProjectWatcher, getToolById, getWatchedProjectDir, getWatcherRuntimeStatus, initWatcherPool, isGlobPattern, isHostedAppVersionManifest, isHostedBackendHealthResponse, isTerminalRendererEngine, isToolConfigured, isWatcherPoolInitialized, normalizeHostedAppBaseUrl, parseCliCommand, reactiveExists, reactiveReadDir, reactiveReadFile, reactiveStat, resolveHostedAppBaseUrl, resolveHostedChannelForVersion, sniffGlobalCli, toOpsxDisplayPath };
|
|
3009
|
+
export { type AIToolOption, AI_TOOLS, type ApplyInstructions, ApplyInstructionsSchema, type ApplyTask, ApplyTaskSchema, type ArchiveMeta, type ArtifactInstructions, ArtifactInstructionsSchema, type ArtifactStatus, ArtifactStatusSchema, CODE_EDITOR_THEME_VALUES, type Change, type ChangeFile, ChangeFileSchema, type ChangeMeta, ChangeSchema, type ChangeStatus, ChangeStatusSchema, CliExecutor, type CliResult, type CliRunnerAttempt, type CliSniffResult, type CliStreamEvent, type CodeEditorTheme, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DEFAULT_CONFIG, type DashboardCardAvailability, type DashboardConfig, DashboardConfigSchema, type DashboardGitCommitEntry, type DashboardGitDiffStats, type DashboardGitEntry, type DashboardGitSnapshot, type DashboardGitUncommittedEntry, type DashboardGitWorktree, type DashboardMetricKey, type DashboardOverview, type DashboardSummary, type DashboardTrendKind, type DashboardTrendMeta, type DashboardTrendPoint, type DashboardTriColorTrendPoint, type Delta, type DeltaOperation, DeltaOperationType, DeltaSchema, type DeltaSpec, DeltaSpecSchema, type DependencyInfo, DependencyInfoSchema, type ExportSnapshot, type FileChangeEvent, type FileChangeType, type HostedAppChannelKind, type HostedAppChannelManifest, type HostedAppCompatibilityEntry, type HostedAppVersionManifest, type HostedBackendHealthResponse, MarkdownParser, OFFICIAL_APP_BASE_URL, OpenSpecAdapter, type OpenSpecUIConfig, OpenSpecUIConfigSchema, type OpenSpecUIConfigUpdate, OpenSpecWatcher, OpsxKernel, type PathCallback, ProjectWatcher, type ProjectWatcherReinitializeReason, type ProjectWatcherRuntimeStatus, PtyAttachMessageSchema, PtyBufferResponseSchema, type PtyClientMessage, PtyClientMessageSchema, PtyCloseMessageSchema, PtyCreateMessageSchema, PtyCreatedResponseSchema, PtyErrorCodeSchema, PtyErrorResponseSchema, PtyExitResponseSchema, PtyInputMessageSchema, PtyListMessageSchema, PtyListResponseSchema, PtyOutputResponseSchema, type PtyPlatform, PtyPlatformSchema, PtyResizeMessageSchema, type PtyServerMessage, PtyServerMessageSchema, type PtySessionInfo, PtyTitleResponseSchema, ReactiveContext, ReactiveState, type ReactiveStateOptions, type Requirement, RequirementSchema, type ResolvedCliRunner, type SchemaArtifact, SchemaArtifactSchema, type SchemaDetail, SchemaDetailSchema, type SchemaInfo, SchemaInfoSchema, type SchemaResolution, SchemaResolutionSchema, type Spec, type SpecMeta, SpecSchema, TOOL_WORKFLOW_TO_SKILL_DIR, type Task, TaskSchema, type TemplateContentMap, type TemplatesMap, TemplatesSchema, type TerminalConfig, TerminalConfigSchema, type TerminalRendererEngine, TerminalRendererEngineSchema, type ToolConfig, type ToolInitDelivery, type ToolInitState, type ToolInitStatus, type ToolWorkflowId, VIRTUAL_PROJECT_DIRNAME, type ValidationIssue, type ValidationResult, Validator, type WatchEvent, type WatchEventType, type WatcherRuntimeStatus, acquireWatcher, buildCliRunnerCandidates, buildHostedLaunchUrl, buildHostedVersionManifestUrl, clearCache, closeAllProjectWatchers, closeAllWatchers, contextStorage, createCleanCliEnv, createFileChangeObservable, getActiveWatcherCount, getAllToolIds, getAllTools, getAvailableToolIds, getAvailableTools, getCacheSize, getConfiguredTools, getDefaultCliCommand, getDefaultCliCommandString, getDetectedProjectTools, getProjectWatcher, getToolById, getToolInitStates, getWatchedProjectDir, getWatcherRuntimeStatus, initWatcherPool, isGlobPattern, isHostedAppVersionManifest, isHostedBackendHealthResponse, isTerminalRendererEngine, isToolConfigured, isWatcherPoolInitialized, normalizeHostedAppBaseUrl, parseCliCommand, reactiveExists, reactiveReadDir, reactiveReadFile, reactiveStat, resolveHostedAppBaseUrl, resolveHostedChannelForVersion, sniffGlobalCli, toOpsxDisplayPath };
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { EventEmitter } from "events";
|
|
|
11
11
|
import { watch } from "fs";
|
|
12
12
|
import { exec, spawn } from "child_process";
|
|
13
13
|
import { promisify } from "util";
|
|
14
|
+
import { homedir } from "node:os";
|
|
14
15
|
import { parse } from "yaml";
|
|
15
16
|
|
|
16
17
|
//#region src/parser.ts
|
|
@@ -1417,23 +1418,11 @@ var OpenSpecAdapter = class {
|
|
|
1417
1418
|
return reactiveReadFile(join(this.openspecDir, "project.md"));
|
|
1418
1419
|
}
|
|
1419
1420
|
/**
|
|
1420
|
-
* Read AGENTS.md content (reactive)
|
|
1421
|
-
*/
|
|
1422
|
-
async readAgentsMd() {
|
|
1423
|
-
return reactiveReadFile(join(this.openspecDir, "AGENTS.md"));
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
1421
|
* Write project.md content
|
|
1427
1422
|
*/
|
|
1428
1423
|
async writeProjectMd(content) {
|
|
1429
1424
|
await writeFile(join(this.openspecDir, "project.md"), content, "utf-8");
|
|
1430
1425
|
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Write AGENTS.md content
|
|
1433
|
-
*/
|
|
1434
|
-
async writeAgentsMd(content) {
|
|
1435
|
-
await writeFile(join(this.openspecDir, "AGENTS.md"), content, "utf-8");
|
|
1436
|
-
}
|
|
1437
1426
|
async readSpec(specId) {
|
|
1438
1427
|
try {
|
|
1439
1428
|
const content = await this.readSpecRaw(specId);
|
|
@@ -1604,24 +1593,6 @@ This project uses OpenSpec for spec-driven development.
|
|
|
1604
1593
|
- \`specs/\` - Source of truth specifications
|
|
1605
1594
|
- \`changes/\` - Active change proposals
|
|
1606
1595
|
- \`changes/archive/\` - Completed changes
|
|
1607
|
-
`, "utf-8");
|
|
1608
|
-
await writeFile(join(this.openspecDir, "AGENTS.md"), `# AI Agent Instructions
|
|
1609
|
-
|
|
1610
|
-
This project uses OpenSpec for spec-driven development.
|
|
1611
|
-
|
|
1612
|
-
## Available Commands
|
|
1613
|
-
- \`openspec list\` - List changes or specs
|
|
1614
|
-
- \`openspec view\` - Dashboard view
|
|
1615
|
-
- \`openspec show <name>\` - Show change or spec details
|
|
1616
|
-
- \`openspec validate <name>\` - Validate change or spec
|
|
1617
|
-
- \`openspec archive <change>\` - Archive completed change
|
|
1618
|
-
|
|
1619
|
-
## Workflow
|
|
1620
|
-
1. Create a change proposal in \`changes/<change-id>/proposal.md\`
|
|
1621
|
-
2. Define delta specs in \`changes/<change-id>/specs/\`
|
|
1622
|
-
3. Track tasks in \`changes/<change-id>/tasks.md\`
|
|
1623
|
-
4. Implement and mark tasks complete
|
|
1624
|
-
5. Archive when done: \`openspec archive <change-id>\`
|
|
1625
1596
|
`, "utf-8");
|
|
1626
1597
|
}
|
|
1627
1598
|
/**
|
|
@@ -1880,7 +1851,7 @@ var OpenSpecWatcher = class extends EventEmitter {
|
|
|
1880
1851
|
});
|
|
1881
1852
|
});
|
|
1882
1853
|
this.watchDir(this.openspecDir, (filename, eventType) => {
|
|
1883
|
-
if (filename === "project.md"
|
|
1854
|
+
if (filename === "project.md") this.emitDebounced(`project:${filename}`, {
|
|
1884
1855
|
type: "project",
|
|
1885
1856
|
action: eventType === "rename" ? "create" : "update",
|
|
1886
1857
|
path: join(this.openspecDir, filename),
|
|
@@ -2344,11 +2315,68 @@ const DEFAULT_CONFIG = {
|
|
|
2344
2315
|
terminal: TerminalConfigSchema.parse({}),
|
|
2345
2316
|
dashboard: DashboardConfigSchema.parse({})
|
|
2346
2317
|
};
|
|
2318
|
+
function areStringArraysEqual(left, right) {
|
|
2319
|
+
if (left === right) return true;
|
|
2320
|
+
if (!left || !right) return !left && !right;
|
|
2321
|
+
if (left.length !== right.length) return false;
|
|
2322
|
+
return left.every((value, index) => value === right[index]);
|
|
2323
|
+
}
|
|
2324
|
+
function pruneNullish(value) {
|
|
2325
|
+
if (value === null || value === void 0) return;
|
|
2326
|
+
if (Array.isArray(value)) return value.map((entry) => pruneNullish(entry)).filter((entry) => entry !== void 0);
|
|
2327
|
+
if (typeof value === "object") {
|
|
2328
|
+
const normalizedEntries = Object.entries(value).flatMap(([key, entryValue]) => {
|
|
2329
|
+
const nextValue = pruneNullish(entryValue);
|
|
2330
|
+
return nextValue === void 0 ? [] : [[key, nextValue]];
|
|
2331
|
+
});
|
|
2332
|
+
return Object.fromEntries(normalizedEntries);
|
|
2333
|
+
}
|
|
2334
|
+
return value;
|
|
2335
|
+
}
|
|
2336
|
+
function hasOwnEntries(value) {
|
|
2337
|
+
return Object.keys(value).length > 0;
|
|
2338
|
+
}
|
|
2339
|
+
function toPersistedConfig(config, options = {}) {
|
|
2340
|
+
const persisted = {};
|
|
2341
|
+
const command = config.cli.command?.trim();
|
|
2342
|
+
const args = (config.cli.args ?? []).map((arg) => arg.trim()).filter(Boolean);
|
|
2343
|
+
const cliCommandParts = command ? [command, ...args] : void 0;
|
|
2344
|
+
if (cliCommandParts && !areStringArraysEqual(cliCommandParts, options.defaultCliCommandParts)) {
|
|
2345
|
+
const persistedCommand = cliCommandParts[0];
|
|
2346
|
+
persisted.cli = args.length > 0 ? {
|
|
2347
|
+
command: persistedCommand,
|
|
2348
|
+
args
|
|
2349
|
+
} : { command: persistedCommand };
|
|
2350
|
+
}
|
|
2351
|
+
if (config.theme !== DEFAULT_CONFIG.theme) persisted.theme = config.theme;
|
|
2352
|
+
const codeEditor = {};
|
|
2353
|
+
if (config.codeEditor.theme !== DEFAULT_CONFIG.codeEditor.theme) codeEditor.theme = config.codeEditor.theme;
|
|
2354
|
+
if (hasOwnEntries(codeEditor)) persisted.codeEditor = codeEditor;
|
|
2355
|
+
if (config.appBaseUrl !== DEFAULT_CONFIG.appBaseUrl) persisted.appBaseUrl = config.appBaseUrl;
|
|
2356
|
+
const terminal = {};
|
|
2357
|
+
if (config.terminal.fontSize !== DEFAULT_CONFIG.terminal.fontSize) terminal.fontSize = config.terminal.fontSize;
|
|
2358
|
+
if (config.terminal.fontFamily !== DEFAULT_CONFIG.terminal.fontFamily) terminal.fontFamily = config.terminal.fontFamily;
|
|
2359
|
+
if (config.terminal.cursorBlink !== DEFAULT_CONFIG.terminal.cursorBlink) terminal.cursorBlink = config.terminal.cursorBlink;
|
|
2360
|
+
if (config.terminal.cursorStyle !== DEFAULT_CONFIG.terminal.cursorStyle) terminal.cursorStyle = config.terminal.cursorStyle;
|
|
2361
|
+
if (config.terminal.scrollback !== DEFAULT_CONFIG.terminal.scrollback) terminal.scrollback = config.terminal.scrollback;
|
|
2362
|
+
if (config.terminal.rendererEngine !== DEFAULT_CONFIG.terminal.rendererEngine) terminal.rendererEngine = config.terminal.rendererEngine;
|
|
2363
|
+
if (hasOwnEntries(terminal)) persisted.terminal = terminal;
|
|
2364
|
+
const dashboard = {};
|
|
2365
|
+
if (config.dashboard.trendPointLimit !== DEFAULT_CONFIG.dashboard.trendPointLimit) dashboard.trendPointLimit = config.dashboard.trendPointLimit;
|
|
2366
|
+
if (hasOwnEntries(dashboard)) persisted.dashboard = dashboard;
|
|
2367
|
+
return persisted;
|
|
2368
|
+
}
|
|
2369
|
+
function isPersistedConfigEmpty(config) {
|
|
2370
|
+
return !hasOwnEntries(config);
|
|
2371
|
+
}
|
|
2347
2372
|
/**
|
|
2348
2373
|
* 配置管理器
|
|
2349
2374
|
*
|
|
2350
2375
|
* 负责读写 openspec/.openspecui.json 配置文件。
|
|
2351
2376
|
* 读取操作使用 reactiveReadFile,支持响应式更新。
|
|
2377
|
+
*
|
|
2378
|
+
* `.openspecui.json` 是预期中的项目级 UI 配置文件,但只有显式偏离默认值的
|
|
2379
|
+
* override 才会落盘。仅启动 openspecui 或仅依赖默认配置时,不应触发文件写入。
|
|
2352
2380
|
*/
|
|
2353
2381
|
var ConfigManager = class {
|
|
2354
2382
|
configPath;
|
|
@@ -2359,18 +2387,11 @@ var ConfigManager = class {
|
|
|
2359
2387
|
this.projectDir = projectDir;
|
|
2360
2388
|
this.configPath = join(projectDir, "openspec", ".openspecui.json");
|
|
2361
2389
|
}
|
|
2362
|
-
|
|
2363
|
-
* 读取配置(响应式)
|
|
2364
|
-
*
|
|
2365
|
-
* 如果配置文件不存在,返回默认配置。
|
|
2366
|
-
* 如果配置文件格式错误,返回默认配置并打印警告。
|
|
2367
|
-
*/
|
|
2368
|
-
async readConfig() {
|
|
2369
|
-
const content = await reactiveReadFile(this.configPath);
|
|
2390
|
+
parseConfigContent(content) {
|
|
2370
2391
|
if (!content) return DEFAULT_CONFIG;
|
|
2371
2392
|
try {
|
|
2372
|
-
const
|
|
2373
|
-
const result = OpenSpecUIConfigSchema.safeParse(
|
|
2393
|
+
const normalized = pruneNullish(JSON.parse(content)) ?? {};
|
|
2394
|
+
const result = OpenSpecUIConfigSchema.safeParse(normalized);
|
|
2374
2395
|
if (result.success) return result.data;
|
|
2375
2396
|
console.warn("Invalid config format, using defaults:", result.error.message);
|
|
2376
2397
|
return DEFAULT_CONFIG;
|
|
@@ -2380,12 +2401,24 @@ var ConfigManager = class {
|
|
|
2380
2401
|
}
|
|
2381
2402
|
}
|
|
2382
2403
|
/**
|
|
2404
|
+
* 读取配置(响应式)
|
|
2405
|
+
*
|
|
2406
|
+
* 如果配置文件不存在,返回默认配置。
|
|
2407
|
+
* 如果配置文件格式错误,返回默认配置并打印警告。
|
|
2408
|
+
*/
|
|
2409
|
+
async readConfig() {
|
|
2410
|
+
const content = await reactiveReadFile(this.configPath);
|
|
2411
|
+
return this.parseConfigContent(content);
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2383
2414
|
* 写入配置
|
|
2384
2415
|
*
|
|
2385
2416
|
* 会触发文件监听,自动更新订阅者。
|
|
2386
2417
|
*/
|
|
2387
2418
|
async writeConfig(config) {
|
|
2388
|
-
const
|
|
2419
|
+
const currentContent = await reactiveReadFile(this.configPath);
|
|
2420
|
+
const fileExists = currentContent !== null;
|
|
2421
|
+
const current = this.parseConfigContent(currentContent);
|
|
2389
2422
|
const nextCli = { ...current.cli };
|
|
2390
2423
|
if (config.cli && Object.prototype.hasOwnProperty.call(config.cli, "command")) {
|
|
2391
2424
|
const trimmed = config.cli.command?.trim();
|
|
@@ -2401,7 +2434,7 @@ var ConfigManager = class {
|
|
|
2401
2434
|
else delete nextCli.args;
|
|
2402
2435
|
}
|
|
2403
2436
|
if (!nextCli.command) delete nextCli.args;
|
|
2404
|
-
const
|
|
2437
|
+
const persisted = toPersistedConfig({
|
|
2405
2438
|
...current,
|
|
2406
2439
|
cli: nextCli,
|
|
2407
2440
|
theme: config.theme ?? current.theme,
|
|
@@ -2418,13 +2451,25 @@ var ConfigManager = class {
|
|
|
2418
2451
|
...current.dashboard,
|
|
2419
2452
|
...config.dashboard
|
|
2420
2453
|
}
|
|
2421
|
-
};
|
|
2422
|
-
|
|
2454
|
+
});
|
|
2455
|
+
if (isPersistedConfigEmpty(persisted) && !fileExists) return;
|
|
2456
|
+
const serialized = isPersistedConfigEmpty(persisted) ? "{}" : JSON.stringify(persisted, null, 2);
|
|
2457
|
+
if (currentContent === serialized) return;
|
|
2423
2458
|
await mkdir(dirname(this.configPath), { recursive: true });
|
|
2424
2459
|
await writeFile(this.configPath, serialized, "utf-8");
|
|
2425
2460
|
updateReactiveFileCache(this.configPath, serialized);
|
|
2426
2461
|
this.invalidateResolvedCliRunner();
|
|
2427
2462
|
}
|
|
2463
|
+
async resolveDefaultCliCommandParts() {
|
|
2464
|
+
return (await resolveCliRunner(buildCliRunnerCandidates({ userAgent: process.env.npm_config_user_agent }).filter((candidate) => candidate.id !== "configured"), this.projectDir, createCleanCliEnv())).commandParts;
|
|
2465
|
+
}
|
|
2466
|
+
async isDefaultCliCommand(commandParts) {
|
|
2467
|
+
try {
|
|
2468
|
+
return areStringArraysEqual(commandParts, await this.resolveDefaultCliCommandParts());
|
|
2469
|
+
} catch {
|
|
2470
|
+
return false;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2428
2473
|
/**
|
|
2429
2474
|
* 解析并缓存可用 CLI runner。
|
|
2430
2475
|
*/
|
|
@@ -2442,8 +2487,7 @@ var ConfigManager = class {
|
|
|
2442
2487
|
async resolveCliRunnerUncached() {
|
|
2443
2488
|
const config = await this.readConfig();
|
|
2444
2489
|
const configuredCommandParts = this.getConfiguredCommandParts(config.cli);
|
|
2445
|
-
|
|
2446
|
-
const resolved = await resolveCliRunner(hasConfiguredCommand ? [{
|
|
2490
|
+
return await resolveCliRunner(configuredCommandParts.length > 0 ? [{
|
|
2447
2491
|
id: "configured",
|
|
2448
2492
|
source: "config.cli.command",
|
|
2449
2493
|
commandParts: configuredCommandParts
|
|
@@ -2451,20 +2495,6 @@ var ConfigManager = class {
|
|
|
2451
2495
|
configuredCommandParts,
|
|
2452
2496
|
userAgent: process.env.npm_config_user_agent
|
|
2453
2497
|
}), this.projectDir, createCleanCliEnv());
|
|
2454
|
-
if (!hasConfiguredCommand) {
|
|
2455
|
-
const [resolvedCommand, ...resolvedArgs] = resolved.commandParts;
|
|
2456
|
-
const currentCommand = config.cli.command?.trim();
|
|
2457
|
-
const currentArgs = config.cli.args ?? [];
|
|
2458
|
-
if (currentCommand !== resolvedCommand || currentArgs.length !== resolvedArgs.length || currentArgs.some((arg, index) => arg !== resolvedArgs[index])) try {
|
|
2459
|
-
await this.writeConfig({ cli: {
|
|
2460
|
-
command: resolvedCommand,
|
|
2461
|
-
args: resolvedArgs
|
|
2462
|
-
} });
|
|
2463
|
-
} catch (err) {
|
|
2464
|
-
console.warn("Failed to persist auto-detected CLI command:", err);
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
return resolved;
|
|
2468
2498
|
}
|
|
2469
2499
|
/**
|
|
2470
2500
|
* 获取 CLI 命令(数组形式)
|
|
@@ -2511,6 +2541,13 @@ var ConfigManager = class {
|
|
|
2511
2541
|
} });
|
|
2512
2542
|
return;
|
|
2513
2543
|
}
|
|
2544
|
+
if (await this.isDefaultCliCommand(commandParts)) {
|
|
2545
|
+
await this.writeConfig({ cli: {
|
|
2546
|
+
command: null,
|
|
2547
|
+
args: null
|
|
2548
|
+
} });
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2514
2551
|
const [resolvedCommand, ...resolvedArgs] = commandParts;
|
|
2515
2552
|
await this.writeConfig({ cli: {
|
|
2516
2553
|
command: resolvedCommand,
|
|
@@ -2823,9 +2860,36 @@ var CliExecutor = class {
|
|
|
2823
2860
|
}
|
|
2824
2861
|
/**
|
|
2825
2862
|
* 流式执行任意命令(数组形式)
|
|
2863
|
+
*
|
|
2864
|
+
* 字面量 `openspec` 会自动通过已解析的 CLI runner 执行,
|
|
2865
|
+
* 其它命令保持原始 spawn 行为。
|
|
2826
2866
|
*/
|
|
2827
2867
|
executeCommandStream(command, onEvent) {
|
|
2828
2868
|
const [cmd, ...cmdArgs] = command;
|
|
2869
|
+
if (cmd === "openspec") {
|
|
2870
|
+
let cancelResolved = null;
|
|
2871
|
+
let cancelled = false;
|
|
2872
|
+
this.executeStream([...cmdArgs], onEvent).then((cancel) => {
|
|
2873
|
+
if (cancelled) {
|
|
2874
|
+
cancel();
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
cancelResolved = cancel;
|
|
2878
|
+
}).catch((err) => {
|
|
2879
|
+
onEvent({
|
|
2880
|
+
type: "stderr",
|
|
2881
|
+
data: err instanceof Error ? err.message : String(err)
|
|
2882
|
+
});
|
|
2883
|
+
onEvent({
|
|
2884
|
+
type: "exit",
|
|
2885
|
+
exitCode: null
|
|
2886
|
+
});
|
|
2887
|
+
});
|
|
2888
|
+
return () => {
|
|
2889
|
+
cancelled = true;
|
|
2890
|
+
cancelResolved?.();
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2829
2893
|
onEvent({
|
|
2830
2894
|
type: "command",
|
|
2831
2895
|
data: command.join(" ")
|
|
@@ -3067,12 +3131,6 @@ const AI_TOOLS = [
|
|
|
3067
3131
|
available: true,
|
|
3068
3132
|
successLabel: "Windsurf",
|
|
3069
3133
|
skillsDir: ".windsurf"
|
|
3070
|
-
},
|
|
3071
|
-
{
|
|
3072
|
-
name: "AGENTS.md (works with Amp, VS Code, …)",
|
|
3073
|
-
value: "agents",
|
|
3074
|
-
available: false,
|
|
3075
|
-
successLabel: "your AGENTS.md-compatible assistant"
|
|
3076
3134
|
}
|
|
3077
3135
|
];
|
|
3078
3136
|
/**
|
|
@@ -3088,13 +3146,25 @@ function getAvailableToolIds() {
|
|
|
3088
3146
|
return getAvailableTools().map((tool) => tool.value);
|
|
3089
3147
|
}
|
|
3090
3148
|
/**
|
|
3091
|
-
*
|
|
3149
|
+
* 获取所有工具
|
|
3092
3150
|
*/
|
|
3093
3151
|
function getAllTools() {
|
|
3094
3152
|
return AI_TOOLS;
|
|
3095
3153
|
}
|
|
3096
3154
|
/**
|
|
3097
|
-
*
|
|
3155
|
+
* 检测当前项目中已经存在的工具目录。
|
|
3156
|
+
*
|
|
3157
|
+
* 这里对齐 OpenSpec 官方 `getAvailableTools(projectPath)` 的语义:
|
|
3158
|
+
* 仅根据项目根目录下的工具目录是否存在来判断,不读取全局命令安装状态。
|
|
3159
|
+
*/
|
|
3160
|
+
async function getDetectedProjectTools(projectDir) {
|
|
3161
|
+
return (await Promise.all(AI_TOOLS.map(async (tool) => {
|
|
3162
|
+
if (!tool.skillsDir) return null;
|
|
3163
|
+
return await reactiveExists(join$1(projectDir, tool.skillsDir)) ? tool : null;
|
|
3164
|
+
}))).filter((tool) => tool !== null);
|
|
3165
|
+
}
|
|
3166
|
+
/**
|
|
3167
|
+
* 获取所有工具 ID 列表
|
|
3098
3168
|
*/
|
|
3099
3169
|
function getAllToolIds() {
|
|
3100
3170
|
return AI_TOOLS.map((tool) => tool.value);
|
|
@@ -3173,6 +3243,143 @@ async function isToolConfigured(projectDir, toolId) {
|
|
|
3173
3243
|
return (await getConfiguredTools(projectDir)).includes(toolId);
|
|
3174
3244
|
}
|
|
3175
3245
|
|
|
3246
|
+
//#endregion
|
|
3247
|
+
//#region src/tool-init-state.ts
|
|
3248
|
+
const TOOL_WORKFLOW_TO_SKILL_DIR = {
|
|
3249
|
+
propose: "openspec-propose",
|
|
3250
|
+
explore: "openspec-explore",
|
|
3251
|
+
new: "openspec-new-change",
|
|
3252
|
+
continue: "openspec-continue-change",
|
|
3253
|
+
apply: "openspec-apply-change",
|
|
3254
|
+
ff: "openspec-ff-change",
|
|
3255
|
+
sync: "openspec-sync-specs",
|
|
3256
|
+
archive: "openspec-archive-change",
|
|
3257
|
+
"bulk-archive": "openspec-bulk-archive-change",
|
|
3258
|
+
verify: "openspec-verify-change",
|
|
3259
|
+
onboard: "openspec-onboard"
|
|
3260
|
+
};
|
|
3261
|
+
const ALL_TOOL_WORKFLOWS = Object.keys(TOOL_WORKFLOW_TO_SKILL_DIR);
|
|
3262
|
+
function toKnownWorkflows(workflows) {
|
|
3263
|
+
return workflows.filter((workflow) => workflow in TOOL_WORKFLOW_TO_SKILL_DIR);
|
|
3264
|
+
}
|
|
3265
|
+
function resolveCodexHome() {
|
|
3266
|
+
const configuredHome = process.env.CODEX_HOME?.trim();
|
|
3267
|
+
return resolve(configuredHome ? configuredHome : join$1(homedir(), ".codex"));
|
|
3268
|
+
}
|
|
3269
|
+
function resolveToolCommandPath(projectDir, toolId, workflow) {
|
|
3270
|
+
switch (toolId) {
|
|
3271
|
+
case "amazon-q": return resolve(projectDir, ".amazonq", "prompts", `opsx-${workflow}.md`);
|
|
3272
|
+
case "antigravity": return resolve(projectDir, ".agent", "workflows", `opsx-${workflow}.md`);
|
|
3273
|
+
case "auggie": return resolve(projectDir, ".augment", "commands", `opsx-${workflow}.md`);
|
|
3274
|
+
case "claude": return resolve(projectDir, ".claude", "commands", "opsx", `${workflow}.md`);
|
|
3275
|
+
case "cline": return resolve(projectDir, ".clinerules", "workflows", `opsx-${workflow}.md`);
|
|
3276
|
+
case "codebuddy": return resolve(projectDir, ".codebuddy", "commands", "opsx", `${workflow}.md`);
|
|
3277
|
+
case "codex": return resolve(resolveCodexHome(), "prompts", `opsx-${workflow}.md`);
|
|
3278
|
+
case "continue": return resolve(projectDir, ".continue", "prompts", `opsx-${workflow}.prompt`);
|
|
3279
|
+
case "costrict": return resolve(projectDir, ".cospec", "openspec", "commands", `opsx-${workflow}.md`);
|
|
3280
|
+
case "crush": return resolve(projectDir, ".crush", "commands", "opsx", `${workflow}.md`);
|
|
3281
|
+
case "cursor": return resolve(projectDir, ".cursor", "commands", `opsx-${workflow}.md`);
|
|
3282
|
+
case "factory": return resolve(projectDir, ".factory", "commands", `opsx-${workflow}.md`);
|
|
3283
|
+
case "gemini": return resolve(projectDir, ".gemini", "commands", "opsx", `${workflow}.toml`);
|
|
3284
|
+
case "github-copilot": return resolve(projectDir, ".github", "prompts", `opsx-${workflow}.prompt.md`);
|
|
3285
|
+
case "iflow": return resolve(projectDir, ".iflow", "commands", `opsx-${workflow}.md`);
|
|
3286
|
+
case "kilocode": return resolve(projectDir, ".kilocode", "workflows", `opsx-${workflow}.md`);
|
|
3287
|
+
case "kiro": return resolve(projectDir, ".kiro", "prompts", `opsx-${workflow}.prompt.md`);
|
|
3288
|
+
case "opencode": return resolve(projectDir, ".opencode", "command", `opsx-${workflow}.md`);
|
|
3289
|
+
case "pi": return resolve(projectDir, ".pi", "prompts", `opsx-${workflow}.md`);
|
|
3290
|
+
case "qoder": return resolve(projectDir, ".qoder", "commands", "opsx", `${workflow}.md`);
|
|
3291
|
+
case "qwen": return resolve(projectDir, ".qwen", "commands", `opsx-${workflow}.toml`);
|
|
3292
|
+
case "roocode": return resolve(projectDir, ".roo", "commands", `opsx-${workflow}.md`);
|
|
3293
|
+
case "windsurf": return resolve(projectDir, ".windsurf", "workflows", `opsx-${workflow}.md`);
|
|
3294
|
+
default: return null;
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
function getSkillArtifacts(projectDir, skillsDir) {
|
|
3298
|
+
return ALL_TOOL_WORKFLOWS.map((workflow) => ({
|
|
3299
|
+
workflow,
|
|
3300
|
+
path: resolve(projectDir, skillsDir, "skills", TOOL_WORKFLOW_TO_SKILL_DIR[workflow], "SKILL.md")
|
|
3301
|
+
}));
|
|
3302
|
+
}
|
|
3303
|
+
function getCommandArtifacts(projectDir, toolId) {
|
|
3304
|
+
return ALL_TOOL_WORKFLOWS.flatMap((workflow) => {
|
|
3305
|
+
const path = resolveToolCommandPath(projectDir, toolId, workflow);
|
|
3306
|
+
return path ? [{
|
|
3307
|
+
workflow,
|
|
3308
|
+
path
|
|
3309
|
+
}] : [];
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
function invalidateToolInitCaches(projectDir) {
|
|
3313
|
+
const cacheRoots = /* @__PURE__ */ new Set();
|
|
3314
|
+
for (const tool of AI_TOOLS) {
|
|
3315
|
+
if (tool.skillsDir) cacheRoots.add(resolve(projectDir, tool.skillsDir));
|
|
3316
|
+
for (const workflow of ALL_TOOL_WORKFLOWS) {
|
|
3317
|
+
const commandPath = resolveToolCommandPath(projectDir, tool.value, workflow);
|
|
3318
|
+
if (commandPath) cacheRoots.add(dirname$1(commandPath));
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
for (const root of cacheRoots) clearCache(root);
|
|
3322
|
+
}
|
|
3323
|
+
async function getExistingArtifactPaths(entries) {
|
|
3324
|
+
const presence = await Promise.all(entries.map(async (entry) => ({
|
|
3325
|
+
path: entry.path,
|
|
3326
|
+
exists: await reactiveExists(entry.path)
|
|
3327
|
+
})));
|
|
3328
|
+
return new Set(presence.filter((entry) => entry.exists).map((entry) => entry.path));
|
|
3329
|
+
}
|
|
3330
|
+
function countExisting(entries, existingPaths) {
|
|
3331
|
+
return entries.reduce((count, entry) => count + (existingPaths.has(entry.path) ? 1 : 0), 0);
|
|
3332
|
+
}
|
|
3333
|
+
function collectMissingWorkflows(entries, existingPaths) {
|
|
3334
|
+
return entries.filter((entry) => !existingPaths.has(entry.path)).map((entry) => entry.workflow);
|
|
3335
|
+
}
|
|
3336
|
+
function collectUnexpectedWorkflows(entries, desiredWorkflowSet, existingPaths) {
|
|
3337
|
+
return entries.filter((entry) => !desiredWorkflowSet.has(entry.workflow) && existingPaths.has(entry.path)).map((entry) => entry.workflow);
|
|
3338
|
+
}
|
|
3339
|
+
async function getToolInitStates(projectDir, options) {
|
|
3340
|
+
invalidateToolInitCaches(projectDir);
|
|
3341
|
+
const desiredWorkflows = toKnownWorkflows(options.workflows);
|
|
3342
|
+
const desiredWorkflowSet = new Set(desiredWorkflows);
|
|
3343
|
+
const shouldGenerateSkills = options.delivery !== "commands";
|
|
3344
|
+
const shouldGenerateCommands = options.delivery !== "skills";
|
|
3345
|
+
return Promise.all(AI_TOOLS.filter((tool) => tool.skillsDir).map(async (tool) => {
|
|
3346
|
+
const skillArtifacts = getSkillArtifacts(projectDir, tool.skillsDir);
|
|
3347
|
+
const commandArtifacts = getCommandArtifacts(projectDir, tool.value);
|
|
3348
|
+
const existingSkillPaths = await getExistingArtifactPaths(skillArtifacts);
|
|
3349
|
+
const existingCommandPaths = await getExistingArtifactPaths(commandArtifacts);
|
|
3350
|
+
const expectedSkillArtifacts = shouldGenerateSkills ? skillArtifacts.filter((entry) => desiredWorkflowSet.has(entry.workflow)) : [];
|
|
3351
|
+
const expectedCommandArtifacts = shouldGenerateCommands ? commandArtifacts.filter((entry) => desiredWorkflowSet.has(entry.workflow)) : [];
|
|
3352
|
+
const missingSkillWorkflows = collectMissingWorkflows(expectedSkillArtifacts, existingSkillPaths);
|
|
3353
|
+
const missingCommandWorkflows = collectMissingWorkflows(expectedCommandArtifacts, existingCommandPaths);
|
|
3354
|
+
const unexpectedSkillWorkflows = collectUnexpectedWorkflows(shouldGenerateSkills ? skillArtifacts : skillArtifacts, shouldGenerateSkills ? desiredWorkflowSet : /* @__PURE__ */ new Set(), existingSkillPaths);
|
|
3355
|
+
const unexpectedCommandWorkflows = collectUnexpectedWorkflows(shouldGenerateCommands ? commandArtifacts : commandArtifacts, shouldGenerateCommands ? desiredWorkflowSet : /* @__PURE__ */ new Set(), existingCommandPaths);
|
|
3356
|
+
const expectedSkillCount = expectedSkillArtifacts.length;
|
|
3357
|
+
const presentExpectedSkillCount = expectedSkillCount - missingSkillWorkflows.length;
|
|
3358
|
+
const detectedSkillCount = countExisting(skillArtifacts, existingSkillPaths);
|
|
3359
|
+
const expectedCommandCount = expectedCommandArtifacts.length;
|
|
3360
|
+
const presentExpectedCommandCount = expectedCommandCount - missingCommandWorkflows.length;
|
|
3361
|
+
const detectedCommandCount = countExisting(commandArtifacts, existingCommandPaths);
|
|
3362
|
+
const hasAnyArtifacts = detectedSkillCount + detectedCommandCount > 0;
|
|
3363
|
+
const isInitialized = missingSkillWorkflows.length === 0 && missingCommandWorkflows.length === 0 && unexpectedSkillWorkflows.length === 0 && unexpectedCommandWorkflows.length === 0;
|
|
3364
|
+
return {
|
|
3365
|
+
toolId: tool.value,
|
|
3366
|
+
toolName: tool.name,
|
|
3367
|
+
status: !hasAnyArtifacts ? "uninitialized" : isInitialized ? "initialized" : "partial",
|
|
3368
|
+
hasAnyArtifacts,
|
|
3369
|
+
expectedSkillCount,
|
|
3370
|
+
presentExpectedSkillCount,
|
|
3371
|
+
detectedSkillCount,
|
|
3372
|
+
expectedCommandCount,
|
|
3373
|
+
presentExpectedCommandCount,
|
|
3374
|
+
detectedCommandCount,
|
|
3375
|
+
missingSkillWorkflows,
|
|
3376
|
+
missingCommandWorkflows,
|
|
3377
|
+
unexpectedSkillWorkflows,
|
|
3378
|
+
unexpectedCommandWorkflows
|
|
3379
|
+
};
|
|
3380
|
+
}));
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3176
3383
|
//#endregion
|
|
3177
3384
|
//#region src/dashboard-types.ts
|
|
3178
3385
|
const DASHBOARD_METRIC_KEYS = [
|
|
@@ -4079,4 +4286,4 @@ const PtyServerMessageSchema = z.discriminatedUnion("type", [
|
|
|
4079
4286
|
]);
|
|
4080
4287
|
|
|
4081
4288
|
//#endregion
|
|
4082
|
-
export { AI_TOOLS, ApplyInstructionsSchema, ApplyTaskSchema, ArtifactInstructionsSchema, ArtifactStatusSchema, CODE_EDITOR_THEME_VALUES, ChangeFileSchema, ChangeSchema, ChangeStatusSchema, CliExecutor, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DEFAULT_CONFIG, DashboardConfigSchema, DeltaOperationType, DeltaSchema, DeltaSpecSchema, DependencyInfoSchema, MarkdownParser, OFFICIAL_APP_BASE_URL, 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, TerminalConfigSchema, TerminalRendererEngineSchema, VIRTUAL_PROJECT_DIRNAME, Validator, acquireWatcher, buildCliRunnerCandidates, buildHostedLaunchUrl, buildHostedVersionManifestUrl, clearCache, closeAllProjectWatchers, closeAllWatchers, contextStorage, createCleanCliEnv, createFileChangeObservable, getActiveWatcherCount, getAllToolIds, getAllTools, getAvailableToolIds, getAvailableTools, getCacheSize, getConfiguredTools, getDefaultCliCommand, getDefaultCliCommandString, getProjectWatcher, getToolById, getWatchedProjectDir, getWatcherRuntimeStatus, initWatcherPool, isGlobPattern, isHostedAppVersionManifest, isHostedBackendHealthResponse, isTerminalRendererEngine, isToolConfigured, isWatcherPoolInitialized, normalizeHostedAppBaseUrl, parseCliCommand, reactiveExists, reactiveReadDir, reactiveReadFile, reactiveStat, resolveHostedAppBaseUrl, resolveHostedChannelForVersion, sniffGlobalCli, toOpsxDisplayPath };
|
|
4289
|
+
export { AI_TOOLS, ApplyInstructionsSchema, ApplyTaskSchema, ArtifactInstructionsSchema, ArtifactStatusSchema, CODE_EDITOR_THEME_VALUES, ChangeFileSchema, ChangeSchema, ChangeStatusSchema, CliExecutor, CodeEditorThemeSchema, ConfigManager, DASHBOARD_METRIC_KEYS, DEFAULT_CONFIG, DashboardConfigSchema, DeltaOperationType, DeltaSchema, DeltaSpecSchema, DependencyInfoSchema, MarkdownParser, OFFICIAL_APP_BASE_URL, 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, TOOL_WORKFLOW_TO_SKILL_DIR, TaskSchema, TemplatesSchema, TerminalConfigSchema, TerminalRendererEngineSchema, VIRTUAL_PROJECT_DIRNAME, Validator, acquireWatcher, buildCliRunnerCandidates, buildHostedLaunchUrl, buildHostedVersionManifestUrl, clearCache, closeAllProjectWatchers, closeAllWatchers, contextStorage, createCleanCliEnv, createFileChangeObservable, getActiveWatcherCount, getAllToolIds, getAllTools, getAvailableToolIds, getAvailableTools, getCacheSize, getConfiguredTools, getDefaultCliCommand, getDefaultCliCommandString, getDetectedProjectTools, getProjectWatcher, getToolById, getToolInitStates, getWatchedProjectDir, getWatcherRuntimeStatus, initWatcherPool, isGlobPattern, isHostedAppVersionManifest, isHostedBackendHealthResponse, isTerminalRendererEngine, isToolConfigured, isWatcherPoolInitialized, normalizeHostedAppBaseUrl, parseCliCommand, reactiveExists, reactiveReadDir, reactiveReadFile, reactiveStat, resolveHostedAppBaseUrl, resolveHostedChannelForVersion, sniffGlobalCli, toOpsxDisplayPath };
|