peaks-cli 1.4.2 → 2.0.1

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.
Files changed (180) hide show
  1. package/.claude-plugin/marketplace.json +51 -0
  2. package/CHANGELOG.md +279 -0
  3. package/README-en.md +226 -0
  4. package/README.md +152 -122
  5. package/dist/src/cli/commands/agent-commands.d.ts +20 -0
  6. package/dist/src/cli/commands/agent-commands.js +48 -0
  7. package/dist/src/cli/commands/audit-commands.d.ts +18 -0
  8. package/dist/src/cli/commands/audit-commands.js +138 -0
  9. package/dist/src/cli/commands/capability-commands.js +2 -1
  10. package/dist/src/cli/commands/classify-classify-commands.d.ts +19 -0
  11. package/dist/src/cli/commands/classify-classify-commands.js +151 -0
  12. package/dist/src/cli/commands/code-review-commands.d.ts +34 -0
  13. package/dist/src/cli/commands/code-review-commands.js +83 -0
  14. package/dist/src/cli/commands/config-commands.js +90 -0
  15. package/dist/src/cli/commands/context-commands.d.ts +21 -0
  16. package/dist/src/cli/commands/context-commands.js +167 -0
  17. package/dist/src/cli/commands/core-artifact-commands.js +60 -2
  18. package/dist/src/cli/commands/hook-handle.js +50 -0
  19. package/dist/src/cli/commands/loop-commands.d.ts +21 -0
  20. package/dist/src/cli/commands/loop-commands.js +128 -0
  21. package/dist/src/cli/commands/openspec-commands.js +37 -0
  22. package/dist/src/cli/commands/preferences-commands.d.ts +2 -0
  23. package/dist/src/cli/commands/preferences-commands.js +147 -0
  24. package/dist/src/cli/commands/skill-conformance-commands.d.ts +9 -0
  25. package/dist/src/cli/commands/skill-conformance-commands.js +39 -0
  26. package/dist/src/cli/commands/understand-commands.js +34 -0
  27. package/dist/src/cli/commands/upgrade-commands.d.ts +23 -0
  28. package/dist/src/cli/commands/upgrade-commands.js +57 -0
  29. package/dist/src/cli/commands/workflow-commands.js +70 -0
  30. package/dist/src/cli/commands/workspace-commands.js +117 -2
  31. package/dist/src/cli/program.js +30 -0
  32. package/dist/src/lib/render/message-renderer.d.ts +20 -0
  33. package/dist/src/lib/render/message-renderer.js +80 -0
  34. package/dist/src/services/agent/ecc-agent-service.d.ts +47 -0
  35. package/dist/src/services/agent/ecc-agent-service.js +143 -0
  36. package/dist/src/services/artifacts/request-artifact-service.js +14 -0
  37. package/dist/src/services/audit/backing-detector.d.ts +24 -0
  38. package/dist/src/services/audit/backing-detector.js +59 -0
  39. package/dist/src/services/audit/classifier.d.ts +38 -0
  40. package/dist/src/services/audit/classifier.js +127 -0
  41. package/dist/src/services/audit/enforcers/active-skill-resolver.d.ts +29 -0
  42. package/dist/src/services/audit/enforcers/active-skill-resolver.js +71 -0
  43. package/dist/src/services/audit/enforcers/design-draft-confirm.d.ts +25 -0
  44. package/dist/src/services/audit/enforcers/design-draft-confirm.js +54 -0
  45. package/dist/src/services/audit/enforcers/lint-audit-regression.d.ts +21 -0
  46. package/dist/src/services/audit/enforcers/lint-audit-regression.js +86 -0
  47. package/dist/src/services/audit/enforcers/lint-catalog-governance.d.ts +27 -0
  48. package/dist/src/services/audit/enforcers/lint-catalog-governance.js +38 -0
  49. package/dist/src/services/audit/enforcers/lint-cli-back.d.ts +16 -0
  50. package/dist/src/services/audit/enforcers/lint-cli-back.js +35 -0
  51. package/dist/src/services/audit/enforcers/lint-output-style.d.ts +11 -0
  52. package/dist/src/services/audit/enforcers/lint-output-style.js +94 -0
  53. package/dist/src/services/audit/enforcers/lint-reference-integrity.d.ts +6 -0
  54. package/dist/src/services/audit/enforcers/lint-reference-integrity.js +83 -0
  55. package/dist/src/services/audit/enforcers/lint-reference-shape.d.ts +30 -0
  56. package/dist/src/services/audit/enforcers/lint-reference-shape.js +272 -0
  57. package/dist/src/services/audit/enforcers/lint-style.d.ts +49 -0
  58. package/dist/src/services/audit/enforcers/lint-style.js +173 -0
  59. package/dist/src/services/audit/enforcers/lint-workflow-shape.d.ts +5 -0
  60. package/dist/src/services/audit/enforcers/lint-workflow-shape.js +141 -0
  61. package/dist/src/services/audit/enforcers/login-gate.d.ts +23 -0
  62. package/dist/src/services/audit/enforcers/login-gate.js +40 -0
  63. package/dist/src/services/audit/enforcers/mock-placement.d.ts +25 -0
  64. package/dist/src/services/audit/enforcers/mock-placement.js +48 -0
  65. package/dist/src/services/audit/enforcers/no-root-pollution.d.ts +21 -0
  66. package/dist/src/services/audit/enforcers/no-root-pollution.js +56 -0
  67. package/dist/src/services/audit/enforcers/pre-rd-scan.d.ts +22 -0
  68. package/dist/src/services/audit/enforcers/pre-rd-scan.js +23 -0
  69. package/dist/src/services/audit/enforcers/prototype-fidelity.d.ts +25 -0
  70. package/dist/src/services/audit/enforcers/prototype-fidelity.js +75 -0
  71. package/dist/src/services/audit/enforcers/resume-detection.d.ts +21 -0
  72. package/dist/src/services/audit/enforcers/resume-detection.js +52 -0
  73. package/dist/src/services/audit/enforcers/solo-code-ban.d.ts +23 -0
  74. package/dist/src/services/audit/enforcers/solo-code-ban.js +27 -0
  75. package/dist/src/services/audit/enforcers/sub-agent-sid.d.ts +25 -0
  76. package/dist/src/services/audit/enforcers/sub-agent-sid.js +63 -0
  77. package/dist/src/services/audit/enforcers/tech-doc-presence.d.ts +28 -0
  78. package/dist/src/services/audit/enforcers/tech-doc-presence.js +35 -0
  79. package/dist/src/services/audit/red-line-catalog-p2-a.d.ts +21 -0
  80. package/dist/src/services/audit/red-line-catalog-p2-a.js +233 -0
  81. package/dist/src/services/audit/red-line-catalog-p2-b.d.ts +19 -0
  82. package/dist/src/services/audit/red-line-catalog-p2-b.js +225 -0
  83. package/dist/src/services/audit/red-line-catalog.d.ts +51 -0
  84. package/dist/src/services/audit/red-line-catalog.js +210 -0
  85. package/dist/src/services/audit/red-lines-service.d.ts +23 -0
  86. package/dist/src/services/audit/red-lines-service.js +486 -0
  87. package/dist/src/services/audit/scanners/openspec-scanner.d.ts +15 -0
  88. package/dist/src/services/audit/scanners/openspec-scanner.js +55 -0
  89. package/dist/src/services/audit/scanners/rules-tree-scanner.d.ts +16 -0
  90. package/dist/src/services/audit/scanners/rules-tree-scanner.js +56 -0
  91. package/dist/src/services/audit/scanners/skills-tree-scanner.d.ts +17 -0
  92. package/dist/src/services/audit/scanners/skills-tree-scanner.js +46 -0
  93. package/dist/src/services/audit/static-service.d.ts +57 -0
  94. package/dist/src/services/audit/static-service.js +125 -0
  95. package/dist/src/services/audit/types.d.ts +69 -0
  96. package/dist/src/services/audit/types.js +13 -0
  97. package/dist/src/services/classify/classify-service.d.ts +42 -0
  98. package/dist/src/services/classify/classify-service.js +122 -0
  99. package/dist/src/services/classify/classify-types.d.ts +79 -0
  100. package/dist/src/services/classify/classify-types.js +90 -0
  101. package/dist/src/services/code-review/ocr-service.d.ts +129 -0
  102. package/dist/src/services/code-review/ocr-service.js +362 -0
  103. package/dist/src/services/config/config-migration.d.ts +32 -0
  104. package/dist/src/services/config/config-migration.js +111 -0
  105. package/dist/src/services/config/config-restore.d.ts +10 -0
  106. package/dist/src/services/config/config-restore.js +47 -0
  107. package/dist/src/services/config/config-rollback.d.ts +13 -0
  108. package/dist/src/services/config/config-rollback.js +26 -0
  109. package/dist/src/services/config/config-service.d.ts +36 -2
  110. package/dist/src/services/config/config-service.js +105 -0
  111. package/dist/src/services/config/config-types.d.ts +73 -0
  112. package/dist/src/services/config/config-types.js +28 -13
  113. package/dist/src/services/config/model-routing.js +5 -3
  114. package/dist/src/services/doctor/doctor-service.js +96 -0
  115. package/dist/src/services/ide/adapters/hermes-adapter.d.ts +21 -0
  116. package/dist/src/services/ide/adapters/hermes-adapter.js +51 -0
  117. package/dist/src/services/ide/adapters/openclaw-adapter.d.ts +14 -0
  118. package/dist/src/services/ide/adapters/openclaw-adapter.js +42 -0
  119. package/dist/src/services/ide/ide-registry.js +7 -0
  120. package/dist/src/services/ide/ide-types.d.ts +1 -1
  121. package/dist/src/services/openspec/openspec-propose-from-doctor-service.d.ts +31 -0
  122. package/dist/src/services/openspec/openspec-propose-from-doctor-service.js +95 -0
  123. package/dist/src/services/preferences/preferences-service.d.ts +6 -0
  124. package/dist/src/services/preferences/preferences-service.js +43 -0
  125. package/dist/src/services/preferences/preferences-types.d.ts +90 -0
  126. package/dist/src/services/preferences/preferences-types.js +38 -0
  127. package/dist/src/services/rd/rd-service.js +29 -1
  128. package/dist/src/services/skills/skill-conformance-service.d.ts +40 -0
  129. package/dist/src/services/skills/skill-conformance-service.js +136 -0
  130. package/dist/src/services/skills/skill-runbook-service.js +44 -10
  131. package/dist/src/services/skills/sync-service.d.ts +86 -0
  132. package/dist/src/services/skills/sync-service.js +271 -0
  133. package/dist/src/services/slice/slice-check-service.js +166 -13
  134. package/dist/src/services/slice/slice-check-types.d.ts +1 -1
  135. package/dist/src/services/standards/migrate-claude-rules-service.d.ts +19 -0
  136. package/dist/src/services/standards/migrate-claude-rules-service.js +193 -0
  137. package/dist/src/services/understand/understand-scan-service.js +15 -2
  138. package/dist/src/services/understand/understand-types.d.ts +26 -0
  139. package/dist/src/services/upgrade/1x-detector-service.d.ts +7 -0
  140. package/dist/src/services/upgrade/1x-detector-service.js +94 -0
  141. package/dist/src/services/upgrade/gitignore-migrate-service.d.ts +56 -0
  142. package/dist/src/services/upgrade/gitignore-migrate-service.js +170 -0
  143. package/dist/src/services/upgrade/upgrade-service.d.ts +47 -0
  144. package/dist/src/services/upgrade/upgrade-service.js +381 -0
  145. package/dist/src/services/workflow/workflow-router-service.js +15 -4
  146. package/dist/src/services/workspace/claude-settings-template.d.ts +53 -0
  147. package/dist/src/services/workspace/claude-settings-template.js +133 -0
  148. package/dist/src/services/workspace/sid-naming-guard.d.ts +14 -0
  149. package/dist/src/services/workspace/sid-naming-guard.js +31 -0
  150. package/dist/src/services/workspace/workspace-archive-service.d.ts +19 -0
  151. package/dist/src/services/workspace/workspace-archive-service.js +32 -0
  152. package/dist/src/services/workspace/workspace-clean-service.d.ts +41 -0
  153. package/dist/src/services/workspace/workspace-clean-service.js +86 -0
  154. package/dist/src/services/workspace/workspace-service.d.ts +24 -0
  155. package/dist/src/services/workspace/workspace-service.js +124 -2
  156. package/dist/src/services/workspace/workspace-state-service.d.ts +7 -0
  157. package/dist/src/services/workspace/workspace-state-service.js +43 -0
  158. package/dist/src/shared/change-id.js +4 -1
  159. package/dist/src/shared/version.d.ts +1 -1
  160. package/dist/src/shared/version.js +1 -1
  161. package/package.json +8 -2
  162. package/schemas/doctor-report.schema.json +1 -1
  163. package/scripts/install-skills.mjs +296 -12
  164. package/skills/peaks-doctor/SKILL.md +59 -0
  165. package/skills/peaks-doctor/references/doctor-check-catalog.md +31 -0
  166. package/skills/peaks-doctor/references/from-doctor-flow.md +64 -0
  167. package/skills/peaks-doctor/test_prompts.json +17 -0
  168. package/skills/peaks-ide/SKILL.md +2 -0
  169. package/skills/peaks-qa/SKILL.md +9 -7
  170. package/skills/peaks-qa/references/artifact-per-request.md +19 -5
  171. package/skills/peaks-qa/references/qa-perf-test-plan.md +6 -6
  172. package/skills/peaks-qa/references/qa-runbook.md +1 -1
  173. package/skills/peaks-rd/SKILL.md +25 -10
  174. package/skills/peaks-rd/references/ocr-integration.md +214 -0
  175. package/skills/peaks-rd/references/rd-fanout-contracts.md +70 -0
  176. package/skills/peaks-rd/references/rd-runbook.md +1 -1
  177. package/skills/peaks-solo/SKILL.md +16 -4
  178. package/skills/peaks-solo/references/anchoring-and-session-info.md +9 -0
  179. package/skills/peaks-solo/references/step-0-55-1x-detection.md +82 -0
  180. package/skills/peaks-solo/references/workflow-gates-and-types.md +9 -0
@@ -0,0 +1,47 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { backupConfigPath } from './config-migration.js';
5
+ /**
6
+ * Spec §8.6 — restore a single archived field from `config.json.1.x.bak`.
7
+ * Does NOT modify `config.json` itself (which is now slim v2).
8
+ * Instead writes a sidecar `config.json.restore-<field>.json` so the user
9
+ * can review before adopting. Fields in the deferred-design set
10
+ * (workspaces, providers, proxy) throw RESTORE_GUARDED so the user has
11
+ * to acknowledge explicitly.
12
+ */
13
+ const GUARDED_FIELDS = new Set(['workspaces', 'providers', 'proxy']);
14
+ function readBakContent() {
15
+ const bak = backupConfigPath();
16
+ if (!existsSync(bak)) {
17
+ throw new Error('NO_BACKUP: ~/.peaks/config.json.1.x.bak not found');
18
+ }
19
+ return JSON.parse(readFileSync(bak, 'utf8'));
20
+ }
21
+ export function listAvailableFields() {
22
+ const bak = readBakContent();
23
+ return Object.keys(bak).filter((k) => k !== 'version');
24
+ }
25
+ export function restoreField(opts) {
26
+ const bak = readBakContent();
27
+ if (!(opts.field in bak)) {
28
+ throw new Error(`FIELD_NOT_FOUND: ${opts.field} is not in config.json.1.x.bak`);
29
+ }
30
+ if (GUARDED_FIELDS.has(opts.field)) {
31
+ throw new Error(`RESTORE_GUARDED: restoring "${opts.field}" is discouraged because the deferred design intentionally avoids it; add it to config.json manually if you really need it`);
32
+ }
33
+ const home = homedir();
34
+ const sidecar = join(home, '.peaks', `config.json.restore-${opts.field}.json`);
35
+ if (!opts.apply) {
36
+ return { field: opts.field, applied: false };
37
+ }
38
+ mkdirSync(join(home, '.peaks'), { recursive: true });
39
+ const payload = {
40
+ field: opts.field,
41
+ value: bak[opts.field],
42
+ source: 'config.json.1.x.bak',
43
+ restoredAt: new Date().toISOString(),
44
+ };
45
+ writeFileSync(sidecar, JSON.stringify(payload, null, 2) + '\n', 'utf8');
46
+ return { field: opts.field, applied: true, sidecarPath: sidecar };
47
+ }
@@ -0,0 +1,13 @@
1
+ export interface RollbackPlan {
2
+ available: boolean;
3
+ detectedVersion: string | null;
4
+ backupPath: string;
5
+ }
6
+ export interface RollbackResult extends RollbackPlan {
7
+ applied: boolean;
8
+ restoredConfigPath?: string;
9
+ }
10
+ export declare function planRollback(): RollbackPlan;
11
+ export declare function executeRollback(opts: {
12
+ apply: boolean;
13
+ }): RollbackResult;
@@ -0,0 +1,26 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { backupConfigPath, globalConfigPath } from './config-migration.js';
3
+ export function planRollback() {
4
+ const backup = backupConfigPath();
5
+ if (!existsSync(backup)) {
6
+ return { available: false, detectedVersion: null, backupPath: backup };
7
+ }
8
+ const raw = JSON.parse(readFileSync(backup, 'utf8'));
9
+ return {
10
+ available: true,
11
+ detectedVersion: raw.version ?? null,
12
+ backupPath: backup,
13
+ };
14
+ }
15
+ export function executeRollback(opts) {
16
+ const plan = planRollback();
17
+ if (!plan.available) {
18
+ throw new Error('NO_BACKUP: ~/.peaks/config.json.1.x.bak not found');
19
+ }
20
+ if (!opts.apply) {
21
+ return { ...plan, applied: false };
22
+ }
23
+ const restored = JSON.parse(readFileSync(plan.backupPath, 'utf8'));
24
+ writeFileSync(globalConfigPath(), JSON.stringify(restored, null, 2) + '\n', 'utf8');
25
+ return { ...plan, applied: true, restoredConfigPath: globalConfigPath() };
26
+ }
@@ -1,7 +1,22 @@
1
- import type { ConfigGetOptions, ConfigLayer, ConfigSetOptions, MiniMaxProviderConfig, PeaksConfig, TokenRef, WorkspaceConfig } from './config-types.js';
1
+ import type { ConfigGetOptions, ConfigLayer, ConfigSetOptions, ConfigV2, MiniMaxProviderConfig, OcrAuthHeader, OcrConfig, OcrLlmConfig, PeaksConfig, TokenRef, WorkspaceConfig } from './config-types.js';
2
2
  export { resolveProjectRootForConfig, resolveCanonicalProjectRoot } from './config-safety.js';
3
+ /**
4
+ * Load the slim 2.0 `~/.peaks/config.json` file. Returns the parsed
5
+ * object when the file is at schema 2.0.0; returns null when the
6
+ * file is absent (fresh install, no global config yet).
7
+ *
8
+ * Throws `CONFIG_LEGACY_VERSION` when the file exists at a 1.x
9
+ * schema version — the caller is expected to run
10
+ * `peaks config migrate --apply` to bring it forward before
11
+ * continuing. This gate is intentional: a slim 2.0 reader must
12
+ * not silently pass through a 1.x shape, because every field it
13
+ * ignores is a field the caller is going to look for elsewhere
14
+ * (preferences.json, .bak, _state/).
15
+ */
16
+ export declare function loadGlobalConfig(): ConfigV2 | null;
3
17
  export declare function isConfigLayer(value: string): value is ConfigLayer;
4
18
  export declare function isSensitiveConfigPath(path: string): boolean;
19
+ export declare function isLegacyConfigKey(path: string): boolean;
5
20
  export declare function containsSensitiveConfigValue(value: unknown): boolean;
6
21
  export type RedactedConfigValue = string | number | boolean | null | RedactedConfigValue[] | {
7
22
  [key: string]: RedactedConfigValue;
@@ -18,6 +33,23 @@ export type MiniMaxProviderStatus = {
18
33
  export declare function getMiniMaxProviderConfig(): MiniMaxProviderConfig;
19
34
  export declare function getMiniMaxProviderStatus(): MiniMaxProviderStatus;
20
35
  export declare function setMiniMaxProviderConfig(input: MiniMaxProviderConfig): MiniMaxProviderStatus;
36
+ /**
37
+ * Read the ocr LLM endpoint config from the user-layer
38
+ * `~/.peaks/config.json`. The user populates this themselves by
39
+ * pasting the `peaks code-review config-template` output (or by
40
+ * running `peaks config set --key ocr.llm.url --value '...'`).
41
+ * peaks-cli never auto-writes these values.
42
+ */
43
+ export declare function getOcrConfig(): OcrConfig;
44
+ /**
45
+ * Return the resolved `OcrLlmConfig` block (`peaksConfig.ocr.llm`)
46
+ * or `null` when the user has not populated the user config. The
47
+ * 5-state OCR detector uses this as the source of truth; when the
48
+ * returned block is missing required fields it produces a
49
+ * `config-missing` state with a templated `nextActions` payload
50
+ * the user can paste into their config.
51
+ */
52
+ export declare function getOcrLlmConfig(): OcrLlmConfig | null;
21
53
  export declare function bootstrapProjectLanguageConfig(projectRoot: string, language: string): void;
22
54
  export declare function readConfig(projectRoot?: string | null): PeaksConfig;
23
55
  export declare function writeConfig(partial: Partial<PeaksConfig>, layer?: ConfigLayer): void;
@@ -32,4 +64,6 @@ export declare function getWorkspaceConfigForPath(path?: string): WorkspaceConfi
32
64
  export declare function ensureWorkspaceConfigForPath(path?: string): WorkspaceConfig | null;
33
65
  export declare function getWorkspaceConfigForCurrentPath(): WorkspaceConfig | null;
34
66
  export declare function ensureWorkspaceConfigForCurrentPath(): WorkspaceConfig | null;
35
- export type { TokenRef, WorkspaceConfig, PeaksConfig, ConfigLayer };
67
+ export type { OcrAuthHeader, OcrConfig, OcrLlmConfig, TokenRef, WorkspaceConfig, PeaksConfig, ConfigLayer };
68
+ export { getUserConfigPath } from './config-safety.js';
69
+ export { globalConfigPath } from './config-migration.js';
@@ -3,8 +3,35 @@ import { dirname, isAbsolute, resolve } from 'node:path';
3
3
  import { DEFAULT_CONFIG } from './config-types.js';
4
4
  import { stablePath } from '../../shared/path-utils.js';
5
5
  import { findProjectRoot, getProjectBootstrapConfigPath, getProjectConfigPath, getUserConfigPath, isInsidePath, readConfigFileSafely, resolveProjectRootForConfig, validateArtifactWorkspaceMarkerPath, validateArtifactWorkspaceRoot, validateProjectBootstrapConfigPathForWrite, validateUserConfigPathForWrite, writeConfigFileSafely, writeProjectConfigFile, writeUserConfigFile } from './config-safety.js';
6
+ import { globalConfigPath, CONFIG_SCHEMA_VERSION_V2 } from './config-migration.js';
7
+ import { isConfigV2 } from './config-types.js';
6
8
  // Re-export resolveProjectRootForConfig and resolveCanonicalProjectRoot for external consumers
7
9
  export { resolveProjectRootForConfig, resolveCanonicalProjectRoot } from './config-safety.js';
10
+ /**
11
+ * Load the slim 2.0 `~/.peaks/config.json` file. Returns the parsed
12
+ * object when the file is at schema 2.0.0; returns null when the
13
+ * file is absent (fresh install, no global config yet).
14
+ *
15
+ * Throws `CONFIG_LEGACY_VERSION` when the file exists at a 1.x
16
+ * schema version — the caller is expected to run
17
+ * `peaks config migrate --apply` to bring it forward before
18
+ * continuing. This gate is intentional: a slim 2.0 reader must
19
+ * not silently pass through a 1.x shape, because every field it
20
+ * ignores is a field the caller is going to look for elsewhere
21
+ * (preferences.json, .bak, _state/).
22
+ */
23
+ export function loadGlobalConfig() {
24
+ const path = globalConfigPath();
25
+ if (!existsSync(path))
26
+ return null;
27
+ const content = readConfigFileSafely(path, 'Global config path must stay inside the user root');
28
+ const raw = JSON.parse(content);
29
+ if (isConfigV2(raw)) {
30
+ return raw;
31
+ }
32
+ const detected = typeof raw.version === 'string' ? raw.version : 'unknown';
33
+ throw new Error(`CONFIG_LEGACY_VERSION: ~/.peaks/config.json is at version "${detected}", expected ${CONFIG_SCHEMA_VERSION_V2}. Run \`peaks config migrate --apply\`.`);
34
+ }
8
35
  function readJsonFile(path, validateBeforeRead, errorMessage = 'Config path must stay inside the config root') {
9
36
  if (!path || !existsSync(path))
10
37
  return null;
@@ -89,6 +116,26 @@ export function isSensitiveConfigPath(path) {
89
116
  const normalized = path.toLowerCase().replace(/[^a-z0-9]/g, '');
90
117
  return normalized.includes('apikey') || normalized.includes('accesskey') || normalized.includes('privatekey') || normalized.includes('token') || normalized.includes('secret') || normalized.includes('password') || normalized.includes('bearer') || normalized.includes('credential') || normalized.includes('auth');
91
118
  }
119
+ /**
120
+ * 2.0.1 slim-config contract: `~/.peaks/config.json` only stores
121
+ * `version` + `ocr.llm.*` placeholders. The 1.x → 2.0 migration
122
+ * moved per-project fields (`language`, `model`, `economyMode`,
123
+ * `swarmMode`) to `<project>/.peaks/preferences.json` (per spec
124
+ * §10.4). `setConfig` rejects writes to those keys and points the
125
+ * user to the preferences path; tokens / providers / proxy still
126
+ * live in `~/.peaks/config.json` (the loader is tolerant of them
127
+ * but does not synthesise defaults for them anymore).
128
+ */
129
+ const LEGACY_CONFIG_KEYS = new Set([
130
+ 'language',
131
+ 'model',
132
+ 'economyMode',
133
+ 'swarmMode'
134
+ ]);
135
+ export function isLegacyConfigKey(path) {
136
+ const topLevel = path.split(/[.[].*/, 1)[0] ?? '';
137
+ return LEGACY_CONFIG_KEYS.has(topLevel);
138
+ }
92
139
  function isProviderConfigPath(path) {
93
140
  return path === 'providers' || path.startsWith('providers.');
94
141
  }
@@ -367,6 +414,58 @@ export function setMiniMaxProviderConfig(input) {
367
414
  writeConfig({ providers }, 'user');
368
415
  return createMiniMaxProviderStatus(providers.minimax ?? {});
369
416
  }
417
+ const OCR_AUTH_HEADERS = new Set(['authorization', 'x-api-key', 'bearer']);
418
+ function toOcrLlmConfig(value) {
419
+ if (!isRecord(value))
420
+ return {};
421
+ const url = typeof value.url === 'string' && value.url.trim().length > 0 ? value.url.trim() : undefined;
422
+ const authToken = typeof value.authToken === 'string' && value.authToken.length > 0 ? value.authToken : undefined;
423
+ const model = typeof value.model === 'string' && value.model.trim().length > 0 ? value.model.trim() : undefined;
424
+ const useAnthropic = typeof value.useAnthropic === 'boolean' ? value.useAnthropic : undefined;
425
+ const rawAuthHeader = typeof value.authHeader === 'string' ? value.authHeader : undefined;
426
+ const authHeader = rawAuthHeader !== undefined && OCR_AUTH_HEADERS.has(rawAuthHeader)
427
+ ? rawAuthHeader
428
+ : undefined;
429
+ return {
430
+ ...(url !== undefined ? { url } : {}),
431
+ ...(authToken !== undefined ? { authToken } : {}),
432
+ ...(model !== undefined ? { model } : {}),
433
+ ...(useAnthropic !== undefined ? { useAnthropic } : {}),
434
+ ...(authHeader !== undefined ? { authHeader } : {})
435
+ };
436
+ }
437
+ function toOcrConfig(value) {
438
+ if (!isRecord(value))
439
+ return {};
440
+ return {
441
+ ...(isRecord(value.llm) ? { llm: toOcrLlmConfig(value.llm) } : {})
442
+ };
443
+ }
444
+ /**
445
+ * Read the ocr LLM endpoint config from the user-layer
446
+ * `~/.peaks/config.json`. The user populates this themselves by
447
+ * pasting the `peaks code-review config-template` output (or by
448
+ * running `peaks config set --key ocr.llm.url --value '...'`).
449
+ * peaks-cli never auto-writes these values.
450
+ */
451
+ export function getOcrConfig() {
452
+ const userConfig = readUserJsonFile() ?? {};
453
+ return toOcrConfig(userConfig.ocr);
454
+ }
455
+ /**
456
+ * Return the resolved `OcrLlmConfig` block (`peaksConfig.ocr.llm`)
457
+ * or `null` when the user has not populated the user config. The
458
+ * 5-state OCR detector uses this as the source of truth; when the
459
+ * returned block is missing required fields it produces a
460
+ * `config-missing` state with a templated `nextActions` payload
461
+ * the user can paste into their config.
462
+ */
463
+ export function getOcrLlmConfig() {
464
+ const ocr = getOcrConfig();
465
+ if (!ocr.llm)
466
+ return null;
467
+ return ocr.llm;
468
+ }
370
469
  function inferHumanLanguage(value) {
371
470
  const normalized = value.trim();
372
471
  if (!normalized) {
@@ -472,6 +571,10 @@ export function setConfig(options) {
472
571
  if (!isConfigLayer(layer)) {
473
572
  throw new Error('Invalid config layer');
474
573
  }
574
+ if (isLegacyConfigKey(options.key)) {
575
+ throw new Error(`Legacy config key "${options.key}" is no longer stored in ~/.peaks/config.json. ` +
576
+ 'Set it under <project>/.peaks/preferences.json (e.g. `peaks preferences set --key <key> --value <value>`).');
577
+ }
475
578
  if (layer === 'project' && (isProviderConfigPath(options.key) || isProxyConfigPath(options.key) || isSensitiveConfigPath(options.key) || containsSensitiveConfigValue(options.value))) {
476
579
  throw new Error('Sensitive config keys must be stored in the user config layer');
477
580
  }
@@ -641,3 +744,5 @@ export function getWorkspaceConfigForCurrentPath() {
641
744
  export function ensureWorkspaceConfigForCurrentPath() {
642
745
  return ensureWorkspaceConfigForPath(process.cwd());
643
746
  }
747
+ export { getUserConfigPath } from './config-safety.js';
748
+ export { globalConfigPath } from './config-migration.js';
@@ -1,3 +1,4 @@
1
+ import { CONFIG_SCHEMA_VERSION_V2 } from './config-migration.js';
1
2
  export type TokenRef = {
2
3
  env: string;
3
4
  } | {
@@ -49,6 +50,39 @@ export type WorkspaceConfig = {
49
50
  artifactStorage?: ArtifactStorageConfig;
50
51
  installedCapabilityIds: string[];
51
52
  };
53
+ /**
54
+ * Open Code Review (ocr) LLM endpoint config. Stored under
55
+ * `peaksConfig.ocr.llm` so the user has a single, discoverable
56
+ * place to declare their LLM endpoint for the ocr second-opinion
57
+ * review. peaks-cli never auto-writes these values; the user pastes
58
+ * the template (printed by `peaks code-review config-template`) into
59
+ * their `~/.peaks/config.json` themselves.
60
+ *
61
+ * The field names map onto the OCR package's own env-var surface
62
+ * (the highest-priority config path for the ocr subprocess):
63
+ *
64
+ * peaksConfig.ocr.llm.url → OCR_LLM_URL
65
+ * peaksConfig.ocr.llm.authToken → OCR_LLM_TOKEN
66
+ * peaksConfig.ocr.llm.model → OCR_LLM_MODEL
67
+ * peaksConfig.ocr.llm.useAnthropic → OCR_USE_ANTHROPIC
68
+ * peaksConfig.ocr.llm.authHeader → OCR_LLM_AUTH_HEADER
69
+ *
70
+ * All fields are optional at the type level so the user can fill
71
+ * them in one at a time; the 5-state detector treats the
72
+ * `url + authToken + model` triple as the minimum for a `ready`
73
+ * state and reports the missing keys in `nextActions`.
74
+ */
75
+ export type OcrAuthHeader = 'authorization' | 'x-api-key' | 'bearer';
76
+ export type OcrLlmConfig = {
77
+ url?: string;
78
+ authToken?: string;
79
+ model?: string;
80
+ useAnthropic?: boolean;
81
+ authHeader?: OcrAuthHeader;
82
+ };
83
+ export type OcrConfig = {
84
+ llm?: OcrLlmConfig;
85
+ };
52
86
  export type PeaksConfig = {
53
87
  version: string;
54
88
  language: string;
@@ -78,6 +112,15 @@ export type PeaksConfig = {
78
112
  enabled: boolean;
79
113
  heartbeatIntervalMs: number;
80
114
  };
115
+ /**
116
+ * Open Code Review (ocr) second-opinion config. Source of truth
117
+ * for the LLM endpoint that the ocr subprocess consumes via env
118
+ * vars (`OCR_LLM_URL` / `OCR_LLM_TOKEN` / ...). peaks-cli does
119
+ * NOT auto-write this — the user populates it by pasting the
120
+ * `peaks code-review config-template` output into their
121
+ * `~/.peaks/config.json`. See `OcrLlmConfig` for the field map.
122
+ */
123
+ ocr?: OcrConfig;
81
124
  };
82
125
  export type ConfigLayer = 'user' | 'project';
83
126
  export type ConfigGetOptions = {
@@ -89,4 +132,34 @@ export type ConfigSetOptions = {
89
132
  value: unknown;
90
133
  layer?: ConfigLayer;
91
134
  };
135
+ /**
136
+ * 2.0.1 slim runtime default. The on-disk `~/.peaks/config.json`
137
+ * only carries `version` + `ocr.llm.*` placeholders. Legacy fields
138
+ * (language / model / economyMode / swarmMode / tokens / providers /
139
+ * proxy) live in `<project>/.peaks/preferences.json` (per spec
140
+ * §10.4) and are NOT synthesised here — `readConfig()` merges the
141
+ * user file over this default, and any legacy field that the user
142
+ * file still carries (1.x file) is exposed via `getConfig` for
143
+ * backward compatibility.
144
+ *
145
+ * Cast to `PeaksConfig` because the type still declares the legacy
146
+ * fields as required (they are part of the `readConfig()` contract
147
+ * for tolerant loading of pre-2.0.1 files); the runtime default
148
+ * itself does not supply them.
149
+ */
92
150
  export declare const DEFAULT_CONFIG: PeaksConfig;
151
+ /**
152
+ * Slim 2.0 schema for `~/.peaks/config.json`. After migration,
153
+ * the only meaningful field is `version`; everything else
154
+ * (language, model, economyMode, swarmMode, tokens, providers,
155
+ * proxy, workspaces, currentWorkspace) is stored elsewhere
156
+ * (`.peaks/preferences.json`, `.peaks/_state/`, or `.bak`).
157
+ *
158
+ * The type is intentionally minimal: extra keys are ignored at
159
+ * runtime, not rejected, so a hand-written or partially-migrated
160
+ * file does not fail the loader.
161
+ */
162
+ export interface ConfigV2 {
163
+ readonly version: typeof CONFIG_SCHEMA_VERSION_V2;
164
+ }
165
+ export declare function isConfigV2(raw: unknown): raw is ConfigV2;
@@ -1,19 +1,34 @@
1
1
  import { CLI_VERSION } from '../../shared/version.js';
2
+ import { CONFIG_SCHEMA_VERSION_V2 } from './config-migration.js';
3
+ /**
4
+ * 2.0.1 slim runtime default. The on-disk `~/.peaks/config.json`
5
+ * only carries `version` + `ocr.llm.*` placeholders. Legacy fields
6
+ * (language / model / economyMode / swarmMode / tokens / providers /
7
+ * proxy) live in `<project>/.peaks/preferences.json` (per spec
8
+ * §10.4) and are NOT synthesised here — `readConfig()` merges the
9
+ * user file over this default, and any legacy field that the user
10
+ * file still carries (1.x file) is exposed via `getConfig` for
11
+ * backward compatibility.
12
+ *
13
+ * Cast to `PeaksConfig` because the type still declares the legacy
14
+ * fields as required (they are part of the `readConfig()` contract
15
+ * for tolerant loading of pre-2.0.1 files); the runtime default
16
+ * itself does not supply them.
17
+ */
2
18
  export const DEFAULT_CONFIG = {
3
19
  version: CLI_VERSION,
4
- language: 'en',
5
- model: 'sonnet',
6
- economyMode: true,
7
- swarmMode: true,
8
- tokens: {},
9
- providers: {
10
- minimax: {
11
- model: 'minimax-2.7'
20
+ ocr: {
21
+ llm: {
22
+ url: '',
23
+ authToken: '',
24
+ model: '',
25
+ useAnthropic: false,
26
+ authHeader: 'authorization'
12
27
  }
13
- },
14
- proxy: {},
15
- progress: {
16
- enabled: true,
17
- heartbeatIntervalMs: 60000
18
28
  }
19
29
  };
30
+ export function isConfigV2(raw) {
31
+ return (typeof raw === 'object' &&
32
+ raw !== null &&
33
+ raw.version === CONFIG_SCHEMA_VERSION_V2);
34
+ }
@@ -1,7 +1,6 @@
1
- import { DEFAULT_CONFIG } from './config-types.js';
2
1
  export const STRONGEST_MODEL_ID = 'claude-opus-4-7';
3
2
  export function getConfiguredExecutionModelId(providers) {
4
- const providerConfigs = Object.values(providers ?? DEFAULT_CONFIG.providers);
3
+ const providerConfigs = Object.values(providers ?? {});
5
4
  const configuredModel = providerConfigs
6
5
  .map((provider) => provider?.model?.trim())
7
6
  .find((model) => typeof model === 'string' && model.length > 0);
@@ -11,5 +10,8 @@ export function getConfiguredExecutionModelId(providers) {
11
10
  return configuredModel;
12
11
  }
13
12
  export function getEconomyAwareExecutionModelId(config) {
14
- return config.economyMode ? getConfiguredExecutionModelId(config.providers) : STRONGEST_MODEL_ID;
13
+ // Slice 2.0.1-bug1 round 3: economy is the project default. Treat undefined as enabled
14
+ // (matches the pre-slice implicit default from DEFAULT_CONFIG.economyMode = true). Only an
15
+ // explicit `economyMode === false` switches execution to STRONGEST_MODEL_ID.
16
+ return config.economyMode !== false ? getConfiguredExecutionModelId(config.providers) : STRONGEST_MODEL_ID;
15
17
  }
@@ -10,6 +10,7 @@ import { loadSkillRegistry } from '../skills/skill-registry.js';
10
10
  import { getSkillPresence } from '../skills/skill-presence-service.js';
11
11
  import { planStatusLineInstall } from '../skills/statusline-settings-service.js';
12
12
  import { findProjectRoot } from '../config/config-safety.js';
13
+ import { isValidSessionId } from '../workspace/sid-naming-guard.js';
13
14
  import { CLI_VERSION } from '../../shared/version.js';
14
15
  const CODEGRAPH_EXPECTED_VERSION = '0.7.10';
15
16
  const SKILL_PRESENCE_FRESHNESS_THRESHOLD_MS = 24 * 60 * 60 * 1000;
@@ -809,6 +810,101 @@ export async function runDoctor(options = {}) {
809
810
  message: `Failed to load doctor-report.schema.json for self-validation: ${getErrorMessage(error)}`
810
811
  });
811
812
  }
813
+ // Slice L3.2: project doctor MVP — 2 new diagnostic checks.
814
+ // 1. L3:l3-orphan-sessions — flags bare sids in .peaks/_runtime/ that
815
+ // fail isValidSessionId. Reuses the Slice 0.5 sid-naming-guard.
816
+ // 2. L3:l3-memory-health — verifies .peaks/memory/index.json is
817
+ // well-formed JSON with the expected schema_version field and
818
+ // references real files on disk.
819
+ // Slice L3.2: project doctor MVP — 2 new diagnostic checks.
820
+ // 1. L3:l3-orphan-sessions — flags bare sids in .peaks/_runtime/ that
821
+ // fail isValidSessionId. Reuses the Slice 0.5 sid-naming-guard.
822
+ // 2. L3:l3-memory-health — verifies .peaks/memory/index.json is
823
+ // well-formed JSON with the expected schema_version field and
824
+ // references real files on disk.
825
+ const l3ProjectRoot = findProjectRoot(process.cwd()) ?? process.cwd();
826
+ try {
827
+ const runtimeDir = join(l3ProjectRoot, '.peaks/_runtime');
828
+ if (existsSync(runtimeDir)) {
829
+ const entries = readdirSync(runtimeDir, { withFileTypes: true })
830
+ .filter((e) => e.isDirectory())
831
+ .map((e) => e.name);
832
+ const validSids = entries.filter((sid) => isValidSessionId(sid));
833
+ const invalidSids = entries.filter((sid) => !isValidSessionId(sid));
834
+ checks.push({
835
+ id: 'L3:l3-orphan-sessions',
836
+ ok: invalidSids.length === 0,
837
+ message: invalidSids.length === 0
838
+ ? `All ${validSids.length} session(s) under .peaks/_runtime/ are valid (isValidSessionId)`
839
+ : `${invalidSids.length} orphan session(s) under .peaks/_runtime/ fail isValidSessionId: ${invalidSids.slice(0, 5).join(', ')}${invalidSids.length > 5 ? '...' : ''}. Run \`peaks workspace clean --project <repo>\` to archive.`
840
+ });
841
+ }
842
+ else {
843
+ checks.push({
844
+ id: 'L3:l3-orphan-sessions',
845
+ ok: true,
846
+ message: 'No .peaks/_runtime/ directory; nothing to check.'
847
+ });
848
+ }
849
+ }
850
+ catch (error) {
851
+ checks.push({
852
+ id: 'L3:l3-orphan-sessions',
853
+ ok: true,
854
+ message: `L3:l3-orphan-sessions probe failed (${getErrorMessage(error)}); skipping check`
855
+ });
856
+ }
857
+ try {
858
+ const memoryIndexPath = join(l3ProjectRoot, '.peaks/memory/index.json');
859
+ if (existsSync(memoryIndexPath)) {
860
+ const raw = readFileSync(memoryIndexPath, 'utf8');
861
+ try {
862
+ const parsed = JSON.parse(raw);
863
+ if (parsed.schema_version === undefined) {
864
+ checks.push({
865
+ id: 'L3:l3-memory-health',
866
+ ok: false,
867
+ message: '.peaks/memory/index.json missing schema_version field'
868
+ });
869
+ }
870
+ else if (!Array.isArray(parsed.entries)) {
871
+ checks.push({
872
+ id: 'L3:l3-memory-health',
873
+ ok: false,
874
+ message: '.peaks/memory/index.json entries is not an array'
875
+ });
876
+ }
877
+ else {
878
+ checks.push({
879
+ id: 'L3:l3-memory-health',
880
+ ok: true,
881
+ message: `.peaks/memory/index.json is well-formed JSON; schema_version=${parsed.schema_version}; ${parsed.entries.length} memory entries`
882
+ });
883
+ }
884
+ }
885
+ catch (parseError) {
886
+ checks.push({
887
+ id: 'L3:l3-memory-health',
888
+ ok: false,
889
+ message: `.peaks/memory/index.json is not valid JSON: ${getErrorMessage(parseError)}`
890
+ });
891
+ }
892
+ }
893
+ else {
894
+ checks.push({
895
+ id: 'L3:l3-memory-health',
896
+ ok: true,
897
+ message: 'No .peaks/memory/index.json yet (no memories extracted)'
898
+ });
899
+ }
900
+ }
901
+ catch (error) {
902
+ checks.push({
903
+ id: 'L3:l3-memory-health',
904
+ ok: true,
905
+ message: `L3:l3-memory-health probe failed (${getErrorMessage(error)}); skipping check`
906
+ });
907
+ }
812
908
  const failed = checks.filter((check) => !check.ok).length;
813
909
  return {
814
910
  checks,
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Hermes IDE adapter (Slice #0.7) — peaks-cli 的第七个内置 IDE 适配器。
3
+ *
4
+ * 不可消除的 per-IDE 字段(slice #0.7 填表):
5
+ * - settings.dirName = '.hermes' : Hermes 项目根下的配置目录
6
+ * - settings.settingsFileName = 'settings.json'
7
+ * - envVar = 'HERMES_PROJECT_DIR' : Hermes 注入的 env 变量(用于 ${...} 占位)
8
+ * - hookEvent = 'PreToolUse' : 现代 IDE 通用约定(UNVERIFIED — 待真实 Hermes 安装验证)
9
+ * - toolMatcher = 'Bash' : 同上(UNVERIFIED)
10
+ *
11
+ * Slice #0.7 状态:
12
+ * - Slim adapter shape 跟 trae-adapter.ts / claude-code-adapter.ts 同型,
13
+ * 验证 slice #1 抽出的形状在第 7 个 IDE 上仍然可以"填表"接入。
14
+ * - 4 UNVERIFIED fields (hookEvent, toolMatcher, envVar, dirName) 在
15
+ * Hermes 真实安装可用前均为占位值;待真实 Hermes fixture 验证后
16
+ * 跟 trae-adapter 一样会被 VERIFIED 标记。
17
+ * - 见 PRD §0.7 + memory trae-adapter-values-verified-against-1x.md
18
+ * 了解 UNVERIFIED → VERIFIED 的迁移路径。
19
+ */
20
+ import type { IdeAdapter } from '../ide-types.js';
21
+ export declare const HERMES_ADAPTER: IdeAdapter;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Hermes IDE adapter (Slice #0.7) — peaks-cli 的第七个内置 IDE 适配器。
3
+ *
4
+ * 不可消除的 per-IDE 字段(slice #0.7 填表):
5
+ * - settings.dirName = '.hermes' : Hermes 项目根下的配置目录
6
+ * - settings.settingsFileName = 'settings.json'
7
+ * - envVar = 'HERMES_PROJECT_DIR' : Hermes 注入的 env 变量(用于 ${...} 占位)
8
+ * - hookEvent = 'PreToolUse' : 现代 IDE 通用约定(UNVERIFIED — 待真实 Hermes 安装验证)
9
+ * - toolMatcher = 'Bash' : 同上(UNVERIFIED)
10
+ *
11
+ * Slice #0.7 状态:
12
+ * - Slim adapter shape 跟 trae-adapter.ts / claude-code-adapter.ts 同型,
13
+ * 验证 slice #1 抽出的形状在第 7 个 IDE 上仍然可以"填表"接入。
14
+ * - 4 UNVERIFIED fields (hookEvent, toolMatcher, envVar, dirName) 在
15
+ * Hermes 真实安装可用前均为占位值;待真实 Hermes fixture 验证后
16
+ * 跟 trae-adapter 一样会被 VERIFIED 标记。
17
+ * - 见 PRD §0.7 + memory trae-adapter-values-verified-against-1x.md
18
+ * 了解 UNVERIFIED → VERIFIED 的迁移路径。
19
+ */
20
+ import { homedir } from 'node:os';
21
+ import { join, resolve } from 'node:path';
22
+ import { traeSubAgentDispatcher } from '../../dispatch/sub-agent-dispatcher.js';
23
+ export const HERMES_ADAPTER = {
24
+ id: 'hermes',
25
+ displayName: 'Hermes',
26
+ settings: {
27
+ dirName: '.hermes', // UNVERIFIED — placeholder; pending real Hermes 1.x fixture
28
+ settingsFileName: 'settings.json',
29
+ resolveSettingsFile: (scope, projectRoot) => {
30
+ const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
31
+ return join(root, '.hermes', 'settings.json');
32
+ },
33
+ supportsScope: (scope) => scope === 'project' || scope === 'global'
34
+ },
35
+ envVar: 'HERMES_PROJECT_DIR', // UNVERIFIED
36
+ hookEvent: 'PreToolUse', // UNVERIFIED
37
+ toolMatcher: 'Bash', // UNVERIFIED
38
+ // Slice #0.7: Hermes sub-agent dispatcher UNVERIFIED — pending real Hermes
39
+ // dogfood. Reusing the Trae dispatcher as a uniform placeholder (byte-stable
40
+ // shape per slice #008; same rationale as Trae adapter).
41
+ subAgentDispatcher: traeSubAgentDispatcher,
42
+ // Slice #010 G9: Hermes PreToolUse is the assumed hook path. UNVERIFIED.
43
+ promptSizeAware: true,
44
+ installHints: [
45
+ 'Restart Hermes (or reload the workspace) so the PreToolUse hooks take effect.'
46
+ ],
47
+ capabilities: {
48
+ gateEnforce: true,
49
+ statusline: true
50
+ }
51
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * OpenClaw IDE adapter (Slice #0.7) — peaks-cli 的第八个内置 IDE 适配器。
3
+ *
4
+ * 不可消除的 per-IDE 字段(slice #0.7 填表):
5
+ * - settings.dirName = '.openclaw' : OpenClaw 项目根下的配置目录
6
+ * - settings.settingsFileName = 'settings.json'
7
+ * - envVar = 'OPENCLAW_PROJECT_DIR' : OpenClaw 注入的 env 变量
8
+ * - hookEvent = 'PreToolUse' : 现代 IDE 通用约定(UNVERIFIED)
9
+ * - toolMatcher = 'Bash' : 同上(UNVERIFIED)
10
+ *
11
+ * Slice #0.7 状态: 同 hermes-adapter.ts 的 UNVERIFIED 占位说明。
12
+ */
13
+ import type { IdeAdapter } from '../ide-types.js';
14
+ export declare const OPENCLAW_ADAPTER: IdeAdapter;