peaks-cli 1.4.1 → 2.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/.claude-plugin/marketplace.json +51 -0
- package/CHANGELOG.md +238 -0
- package/README-en.md +226 -0
- package/README.md +142 -165
- package/dist/src/cli/commands/agent-commands.d.ts +20 -0
- package/dist/src/cli/commands/agent-commands.js +48 -0
- package/dist/src/cli/commands/audit-commands.d.ts +18 -0
- package/dist/src/cli/commands/audit-commands.js +138 -0
- package/dist/src/cli/commands/classify-classify-commands.d.ts +19 -0
- package/dist/src/cli/commands/classify-classify-commands.js +151 -0
- package/dist/src/cli/commands/code-review-commands.d.ts +34 -0
- package/dist/src/cli/commands/code-review-commands.js +83 -0
- package/dist/src/cli/commands/config-commands.js +90 -0
- package/dist/src/cli/commands/context-commands.d.ts +21 -0
- package/dist/src/cli/commands/context-commands.js +167 -0
- package/dist/src/cli/commands/core-artifact-commands.js +81 -2
- package/dist/src/cli/commands/hook-handle.js +50 -0
- package/dist/src/cli/commands/loop-commands.d.ts +21 -0
- package/dist/src/cli/commands/loop-commands.js +128 -0
- package/dist/src/cli/commands/memory-commands.d.ts +13 -0
- package/dist/src/cli/commands/memory-commands.js +60 -0
- package/dist/src/cli/commands/openspec-commands.js +37 -0
- package/dist/src/cli/commands/preferences-commands.d.ts +2 -0
- package/dist/src/cli/commands/preferences-commands.js +147 -0
- package/dist/src/cli/commands/retrospective-commands.d.ts +9 -0
- package/dist/src/cli/commands/retrospective-commands.js +58 -0
- package/dist/src/cli/commands/skill-conformance-commands.d.ts +9 -0
- package/dist/src/cli/commands/skill-conformance-commands.js +39 -0
- package/dist/src/cli/commands/understand-commands.js +34 -0
- package/dist/src/cli/commands/upgrade-commands.d.ts +23 -0
- package/dist/src/cli/commands/upgrade-commands.js +57 -0
- package/dist/src/cli/commands/workflow-commands.js +70 -0
- package/dist/src/cli/commands/workspace-commands.js +86 -0
- package/dist/src/cli/program.js +46 -22
- package/dist/src/services/agent/ecc-agent-service.d.ts +47 -0
- package/dist/src/services/agent/ecc-agent-service.js +143 -0
- package/dist/src/services/artifacts/request-artifact-service.js +14 -0
- package/dist/src/services/audit/backing-detector.d.ts +24 -0
- package/dist/src/services/audit/backing-detector.js +59 -0
- package/dist/src/services/audit/classifier.d.ts +38 -0
- package/dist/src/services/audit/classifier.js +127 -0
- package/dist/src/services/audit/enforcers/active-skill-resolver.d.ts +29 -0
- package/dist/src/services/audit/enforcers/active-skill-resolver.js +71 -0
- package/dist/src/services/audit/enforcers/design-draft-confirm.d.ts +25 -0
- package/dist/src/services/audit/enforcers/design-draft-confirm.js +54 -0
- package/dist/src/services/audit/enforcers/lint-audit-regression.d.ts +21 -0
- package/dist/src/services/audit/enforcers/lint-audit-regression.js +86 -0
- package/dist/src/services/audit/enforcers/lint-catalog-governance.d.ts +27 -0
- package/dist/src/services/audit/enforcers/lint-catalog-governance.js +38 -0
- package/dist/src/services/audit/enforcers/lint-cli-back.d.ts +16 -0
- package/dist/src/services/audit/enforcers/lint-cli-back.js +35 -0
- package/dist/src/services/audit/enforcers/lint-output-style.d.ts +11 -0
- package/dist/src/services/audit/enforcers/lint-output-style.js +94 -0
- package/dist/src/services/audit/enforcers/lint-reference-integrity.d.ts +6 -0
- package/dist/src/services/audit/enforcers/lint-reference-integrity.js +83 -0
- package/dist/src/services/audit/enforcers/lint-reference-shape.d.ts +30 -0
- package/dist/src/services/audit/enforcers/lint-reference-shape.js +272 -0
- package/dist/src/services/audit/enforcers/lint-style.d.ts +49 -0
- package/dist/src/services/audit/enforcers/lint-style.js +173 -0
- package/dist/src/services/audit/enforcers/lint-workflow-shape.d.ts +5 -0
- package/dist/src/services/audit/enforcers/lint-workflow-shape.js +141 -0
- package/dist/src/services/audit/enforcers/login-gate.d.ts +23 -0
- package/dist/src/services/audit/enforcers/login-gate.js +40 -0
- package/dist/src/services/audit/enforcers/mock-placement.d.ts +25 -0
- package/dist/src/services/audit/enforcers/mock-placement.js +48 -0
- package/dist/src/services/audit/enforcers/no-root-pollution.d.ts +21 -0
- package/dist/src/services/audit/enforcers/no-root-pollution.js +56 -0
- package/dist/src/services/audit/enforcers/pre-rd-scan.d.ts +22 -0
- package/dist/src/services/audit/enforcers/pre-rd-scan.js +23 -0
- package/dist/src/services/audit/enforcers/prototype-fidelity.d.ts +25 -0
- package/dist/src/services/audit/enforcers/prototype-fidelity.js +75 -0
- package/dist/src/services/audit/enforcers/resume-detection.d.ts +21 -0
- package/dist/src/services/audit/enforcers/resume-detection.js +52 -0
- package/dist/src/services/audit/enforcers/solo-code-ban.d.ts +23 -0
- package/dist/src/services/audit/enforcers/solo-code-ban.js +27 -0
- package/dist/src/services/audit/enforcers/sub-agent-sid.d.ts +25 -0
- package/dist/src/services/audit/enforcers/sub-agent-sid.js +63 -0
- package/dist/src/services/audit/enforcers/tech-doc-presence.d.ts +28 -0
- package/dist/src/services/audit/enforcers/tech-doc-presence.js +35 -0
- package/dist/src/services/audit/red-line-catalog-p2-a.d.ts +21 -0
- package/dist/src/services/audit/red-line-catalog-p2-a.js +233 -0
- package/dist/src/services/audit/red-line-catalog-p2-b.d.ts +19 -0
- package/dist/src/services/audit/red-line-catalog-p2-b.js +225 -0
- package/dist/src/services/audit/red-line-catalog.d.ts +51 -0
- package/dist/src/services/audit/red-line-catalog.js +210 -0
- package/dist/src/services/audit/red-lines-service.d.ts +23 -0
- package/dist/src/services/audit/red-lines-service.js +486 -0
- package/dist/src/services/audit/scanners/openspec-scanner.d.ts +15 -0
- package/dist/src/services/audit/scanners/openspec-scanner.js +55 -0
- package/dist/src/services/audit/scanners/rules-tree-scanner.d.ts +16 -0
- package/dist/src/services/audit/scanners/rules-tree-scanner.js +56 -0
- package/dist/src/services/audit/scanners/skills-tree-scanner.d.ts +17 -0
- package/dist/src/services/audit/scanners/skills-tree-scanner.js +46 -0
- package/dist/src/services/audit/static-service.d.ts +57 -0
- package/dist/src/services/audit/static-service.js +125 -0
- package/dist/src/services/audit/types.d.ts +69 -0
- package/dist/src/services/audit/types.js +13 -0
- package/dist/src/services/classify/classify-service.d.ts +42 -0
- package/dist/src/services/classify/classify-service.js +122 -0
- package/dist/src/services/classify/classify-types.d.ts +79 -0
- package/dist/src/services/classify/classify-types.js +90 -0
- package/dist/src/services/code-review/ocr-service.d.ts +129 -0
- package/dist/src/services/code-review/ocr-service.js +362 -0
- package/dist/src/services/config/config-migration.d.ts +32 -0
- package/dist/src/services/config/config-migration.js +92 -0
- package/dist/src/services/config/config-restore.d.ts +10 -0
- package/dist/src/services/config/config-restore.js +47 -0
- package/dist/src/services/config/config-rollback.d.ts +13 -0
- package/dist/src/services/config/config-rollback.js +26 -0
- package/dist/src/services/config/config-service.d.ts +35 -2
- package/dist/src/services/config/config-service.js +81 -0
- package/dist/src/services/config/config-types.d.ts +58 -0
- package/dist/src/services/config/config-types.js +6 -0
- package/dist/src/services/doctor/doctor-service.js +96 -0
- package/dist/src/services/fuzzy-matching/fuzzy-match-service.d.ts +15 -0
- package/dist/src/services/fuzzy-matching/fuzzy-match-service.js +56 -0
- package/dist/src/services/fuzzy-matching/types.d.ts +20 -0
- package/dist/src/services/fuzzy-matching/types.js +1 -0
- package/dist/src/services/ide/adapters/hermes-adapter.d.ts +21 -0
- package/dist/src/services/ide/adapters/hermes-adapter.js +51 -0
- package/dist/src/services/ide/adapters/openclaw-adapter.d.ts +14 -0
- package/dist/src/services/ide/adapters/openclaw-adapter.js +42 -0
- package/dist/src/services/ide/ide-registry.js +7 -0
- package/dist/src/services/ide/ide-types.d.ts +1 -1
- package/dist/src/services/memory/memory-search-service.d.ts +61 -0
- package/dist/src/services/memory/memory-search-service.js +80 -0
- package/dist/src/services/openspec/openspec-propose-from-doctor-service.d.ts +31 -0
- package/dist/src/services/openspec/openspec-propose-from-doctor-service.js +95 -0
- package/dist/src/services/preferences/preferences-service.d.ts +6 -0
- package/dist/src/services/preferences/preferences-service.js +43 -0
- package/dist/src/services/preferences/preferences-types.d.ts +90 -0
- package/dist/src/services/preferences/preferences-types.js +38 -0
- package/dist/src/services/recommendations/capability-seed-items.js +0 -1
- package/dist/src/services/recommendations/capability-seed-mappings.js +0 -1
- package/dist/src/services/recommendations/capability-seed-sources.js +0 -1
- package/dist/src/services/retrospective/retrospective-search-service.d.ts +37 -0
- package/dist/src/services/retrospective/retrospective-search-service.js +75 -0
- package/dist/src/services/skills/skill-conformance-service.d.ts +40 -0
- package/dist/src/services/skills/skill-conformance-service.js +136 -0
- package/dist/src/services/skills/skill-runbook-service.js +44 -10
- package/dist/src/services/skills/sync-service.d.ts +43 -0
- package/dist/src/services/skills/sync-service.js +99 -0
- package/dist/src/services/slice/slice-check-service.js +166 -13
- package/dist/src/services/slice/slice-check-types.d.ts +1 -1
- package/dist/src/services/standards/migrate-claude-rules-service.d.ts +19 -0
- package/dist/src/services/standards/migrate-claude-rules-service.js +193 -0
- package/dist/src/services/standards/project-context.d.ts +1 -1
- package/dist/src/services/standards/project-context.js +0 -4
- package/dist/src/services/standards/project-standards-service.js +1 -3
- package/dist/src/services/understand/understand-scan-service.js +15 -2
- package/dist/src/services/understand/understand-types.d.ts +26 -0
- package/dist/src/services/upgrade/1x-detector-service.d.ts +7 -0
- package/dist/src/services/upgrade/1x-detector-service.js +94 -0
- package/dist/src/services/upgrade/gitignore-migrate-service.d.ts +56 -0
- package/dist/src/services/upgrade/gitignore-migrate-service.js +170 -0
- package/dist/src/services/upgrade/upgrade-service.d.ts +47 -0
- package/dist/src/services/upgrade/upgrade-service.js +381 -0
- package/dist/src/services/workspace/migrate-1-4-1-service.js +1 -1
- package/dist/src/services/workspace/sid-naming-guard.d.ts +14 -0
- package/dist/src/services/workspace/sid-naming-guard.js +31 -0
- package/dist/src/services/workspace/workspace-archive-service.d.ts +19 -0
- package/dist/src/services/workspace/workspace-archive-service.js +32 -0
- package/dist/src/services/workspace/workspace-clean-service.d.ts +41 -0
- package/dist/src/services/workspace/workspace-clean-service.js +86 -0
- package/dist/src/services/workspace/workspace-state-service.d.ts +7 -0
- package/dist/src/services/workspace/workspace-state-service.js +43 -0
- package/dist/src/shared/change-id.js +4 -1
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +10 -8
- package/schemas/doctor-report.schema.json +1 -1
- package/scripts/install-skills.mjs +296 -12
- package/skills/peaks-doctor/SKILL.md +59 -0
- package/skills/peaks-doctor/references/doctor-check-catalog.md +31 -0
- package/skills/peaks-doctor/references/from-doctor-flow.md +64 -0
- package/skills/peaks-doctor/test_prompts.json +17 -0
- package/skills/peaks-ide/SKILL.md +2 -0
- package/skills/peaks-qa/SKILL.md +9 -7
- package/skills/peaks-qa/references/artifact-per-request.md +19 -5
- package/skills/peaks-qa/references/qa-perf-test-plan.md +6 -6
- package/skills/peaks-qa/references/qa-runbook.md +1 -1
- package/skills/peaks-rd/SKILL.md +25 -10
- package/skills/peaks-rd/references/ocr-integration.md +214 -0
- package/skills/peaks-rd/references/rd-fanout-contracts.md +70 -0
- package/skills/peaks-rd/references/rd-runbook.md +1 -1
- package/skills/peaks-solo/SKILL.md +11 -5
- package/skills/peaks-solo/references/completion-handoff.md +3 -1
- package/skills/peaks-solo/references/step-0-55-1x-detection.md +82 -0
- package/skills/peaks-solo/references/workflow-gates-and-types.md +9 -0
- package/dist/src/cli/commands/shadcn-commands.d.ts +0 -3
- package/dist/src/cli/commands/shadcn-commands.js +0 -35
- package/dist/src/cli/commands/skill-context-stats-command.d.ts +0 -40
- package/dist/src/cli/commands/skill-context-stats-command.js +0 -96
- package/dist/src/cli/commands/skill-scope-commands.d.ts +0 -51
- package/dist/src/cli/commands/skill-scope-commands.js +0 -310
- package/dist/src/services/shadcn/shadcn-service.d.ts +0 -27
- package/dist/src/services/shadcn/shadcn-service.js +0 -128
- package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +0 -39
- package/dist/src/services/skill-scope/adapters/_stub-helper.js +0 -98
- package/dist/src/services/skill-scope/adapters/claude-code.d.ts +0 -59
- package/dist/src/services/skill-scope/adapters/claude-code.js +0 -304
- package/dist/src/services/skill-scope/adapters/codex.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/codex.js +0 -12
- package/dist/src/services/skill-scope/adapters/cursor.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/cursor.js +0 -13
- package/dist/src/services/skill-scope/adapters/qoder.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/qoder.js +0 -13
- package/dist/src/services/skill-scope/adapters/tongyi.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/tongyi.js +0 -13
- package/dist/src/services/skill-scope/adapters/trae.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/trae.js +0 -12
- package/dist/src/services/skill-scope/detect.d.ts +0 -81
- package/dist/src/services/skill-scope/detect.js +0 -513
- package/dist/src/services/skill-scope/registry.d.ts +0 -41
- package/dist/src/services/skill-scope/registry.js +0 -83
- package/dist/src/services/skill-scope/source-of-truth.d.ts +0 -44
- package/dist/src/services/skill-scope/source-of-truth.js +0 -118
- package/dist/src/services/skill-scope/types.d.ts +0 -195
- package/dist/src/services/skill-scope/types.js +0 -97
|
@@ -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,5 +1,19 @@
|
|
|
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;
|
|
5
19
|
export declare function containsSensitiveConfigValue(value: unknown): boolean;
|
|
@@ -18,6 +32,23 @@ export type MiniMaxProviderStatus = {
|
|
|
18
32
|
export declare function getMiniMaxProviderConfig(): MiniMaxProviderConfig;
|
|
19
33
|
export declare function getMiniMaxProviderStatus(): MiniMaxProviderStatus;
|
|
20
34
|
export declare function setMiniMaxProviderConfig(input: MiniMaxProviderConfig): MiniMaxProviderStatus;
|
|
35
|
+
/**
|
|
36
|
+
* Read the ocr LLM endpoint config from the user-layer
|
|
37
|
+
* `~/.peaks/config.json`. The user populates this themselves by
|
|
38
|
+
* pasting the `peaks code-review config-template` output (or by
|
|
39
|
+
* running `peaks config set --key ocr.llm.url --value '...'`).
|
|
40
|
+
* peaks-cli never auto-writes these values.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getOcrConfig(): OcrConfig;
|
|
43
|
+
/**
|
|
44
|
+
* Return the resolved `OcrLlmConfig` block (`peaksConfig.ocr.llm`)
|
|
45
|
+
* or `null` when the user has not populated the user config. The
|
|
46
|
+
* 5-state OCR detector uses this as the source of truth; when the
|
|
47
|
+
* returned block is missing required fields it produces a
|
|
48
|
+
* `config-missing` state with a templated `nextActions` payload
|
|
49
|
+
* the user can paste into their config.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getOcrLlmConfig(): OcrLlmConfig | null;
|
|
21
52
|
export declare function bootstrapProjectLanguageConfig(projectRoot: string, language: string): void;
|
|
22
53
|
export declare function readConfig(projectRoot?: string | null): PeaksConfig;
|
|
23
54
|
export declare function writeConfig(partial: Partial<PeaksConfig>, layer?: ConfigLayer): void;
|
|
@@ -32,4 +63,6 @@ export declare function getWorkspaceConfigForPath(path?: string): WorkspaceConfi
|
|
|
32
63
|
export declare function ensureWorkspaceConfigForPath(path?: string): WorkspaceConfig | null;
|
|
33
64
|
export declare function getWorkspaceConfigForCurrentPath(): WorkspaceConfig | null;
|
|
34
65
|
export declare function ensureWorkspaceConfigForCurrentPath(): WorkspaceConfig | null;
|
|
35
|
-
export type { TokenRef, WorkspaceConfig, PeaksConfig, ConfigLayer };
|
|
66
|
+
export type { OcrAuthHeader, OcrConfig, OcrLlmConfig, TokenRef, WorkspaceConfig, PeaksConfig, ConfigLayer };
|
|
67
|
+
export { getUserConfigPath } from './config-safety.js';
|
|
68
|
+
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;
|
|
@@ -367,6 +394,58 @@ export function setMiniMaxProviderConfig(input) {
|
|
|
367
394
|
writeConfig({ providers }, 'user');
|
|
368
395
|
return createMiniMaxProviderStatus(providers.minimax ?? {});
|
|
369
396
|
}
|
|
397
|
+
const OCR_AUTH_HEADERS = new Set(['authorization', 'x-api-key', 'bearer']);
|
|
398
|
+
function toOcrLlmConfig(value) {
|
|
399
|
+
if (!isRecord(value))
|
|
400
|
+
return {};
|
|
401
|
+
const url = typeof value.url === 'string' && value.url.trim().length > 0 ? value.url.trim() : undefined;
|
|
402
|
+
const authToken = typeof value.authToken === 'string' && value.authToken.length > 0 ? value.authToken : undefined;
|
|
403
|
+
const model = typeof value.model === 'string' && value.model.trim().length > 0 ? value.model.trim() : undefined;
|
|
404
|
+
const useAnthropic = typeof value.useAnthropic === 'boolean' ? value.useAnthropic : undefined;
|
|
405
|
+
const rawAuthHeader = typeof value.authHeader === 'string' ? value.authHeader : undefined;
|
|
406
|
+
const authHeader = rawAuthHeader !== undefined && OCR_AUTH_HEADERS.has(rawAuthHeader)
|
|
407
|
+
? rawAuthHeader
|
|
408
|
+
: undefined;
|
|
409
|
+
return {
|
|
410
|
+
...(url !== undefined ? { url } : {}),
|
|
411
|
+
...(authToken !== undefined ? { authToken } : {}),
|
|
412
|
+
...(model !== undefined ? { model } : {}),
|
|
413
|
+
...(useAnthropic !== undefined ? { useAnthropic } : {}),
|
|
414
|
+
...(authHeader !== undefined ? { authHeader } : {})
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function toOcrConfig(value) {
|
|
418
|
+
if (!isRecord(value))
|
|
419
|
+
return {};
|
|
420
|
+
return {
|
|
421
|
+
...(isRecord(value.llm) ? { llm: toOcrLlmConfig(value.llm) } : {})
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Read the ocr LLM endpoint config from the user-layer
|
|
426
|
+
* `~/.peaks/config.json`. The user populates this themselves by
|
|
427
|
+
* pasting the `peaks code-review config-template` output (or by
|
|
428
|
+
* running `peaks config set --key ocr.llm.url --value '...'`).
|
|
429
|
+
* peaks-cli never auto-writes these values.
|
|
430
|
+
*/
|
|
431
|
+
export function getOcrConfig() {
|
|
432
|
+
const userConfig = readUserJsonFile() ?? {};
|
|
433
|
+
return toOcrConfig(userConfig.ocr);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Return the resolved `OcrLlmConfig` block (`peaksConfig.ocr.llm`)
|
|
437
|
+
* or `null` when the user has not populated the user config. The
|
|
438
|
+
* 5-state OCR detector uses this as the source of truth; when the
|
|
439
|
+
* returned block is missing required fields it produces a
|
|
440
|
+
* `config-missing` state with a templated `nextActions` payload
|
|
441
|
+
* the user can paste into their config.
|
|
442
|
+
*/
|
|
443
|
+
export function getOcrLlmConfig() {
|
|
444
|
+
const ocr = getOcrConfig();
|
|
445
|
+
if (!ocr.llm)
|
|
446
|
+
return null;
|
|
447
|
+
return ocr.llm;
|
|
448
|
+
}
|
|
370
449
|
function inferHumanLanguage(value) {
|
|
371
450
|
const normalized = value.trim();
|
|
372
451
|
if (!normalized) {
|
|
@@ -641,3 +720,5 @@ export function getWorkspaceConfigForCurrentPath() {
|
|
|
641
720
|
export function ensureWorkspaceConfigForCurrentPath() {
|
|
642
721
|
return ensureWorkspaceConfigForPath(process.cwd());
|
|
643
722
|
}
|
|
723
|
+
export { getUserConfigPath } from './config-safety.js';
|
|
724
|
+
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 = {
|
|
@@ -90,3 +133,18 @@ export type ConfigSetOptions = {
|
|
|
90
133
|
layer?: ConfigLayer;
|
|
91
134
|
};
|
|
92
135
|
export declare const DEFAULT_CONFIG: PeaksConfig;
|
|
136
|
+
/**
|
|
137
|
+
* Slim 2.0 schema for `~/.peaks/config.json`. After migration,
|
|
138
|
+
* the only meaningful field is `version`; everything else
|
|
139
|
+
* (language, model, economyMode, swarmMode, tokens, providers,
|
|
140
|
+
* proxy, workspaces, currentWorkspace) is stored elsewhere
|
|
141
|
+
* (`.peaks/preferences.json`, `.peaks/_state/`, or `.bak`).
|
|
142
|
+
*
|
|
143
|
+
* The type is intentionally minimal: extra keys are ignored at
|
|
144
|
+
* runtime, not rejected, so a hand-written or partially-migrated
|
|
145
|
+
* file does not fail the loader.
|
|
146
|
+
*/
|
|
147
|
+
export interface ConfigV2 {
|
|
148
|
+
readonly version: typeof CONFIG_SCHEMA_VERSION_V2;
|
|
149
|
+
}
|
|
150
|
+
export declare function isConfigV2(raw: unknown): raw is ConfigV2;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CLI_VERSION } from '../../shared/version.js';
|
|
2
|
+
import { CONFIG_SCHEMA_VERSION_V2 } from './config-migration.js';
|
|
2
3
|
export const DEFAULT_CONFIG = {
|
|
3
4
|
version: CLI_VERSION,
|
|
4
5
|
language: 'en',
|
|
@@ -17,3 +18,8 @@ export const DEFAULT_CONFIG = {
|
|
|
17
18
|
heartbeatIntervalMs: 60000
|
|
18
19
|
}
|
|
19
20
|
};
|
|
21
|
+
export function isConfigV2(raw) {
|
|
22
|
+
return (typeof raw === 'object' &&
|
|
23
|
+
raw !== null &&
|
|
24
|
+
raw.version === CONFIG_SCHEMA_VERSION_V2);
|
|
25
|
+
}
|
|
@@ -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,15 @@
|
|
|
1
|
+
import type { FuzzyMatchOptions, FuzzyMatchResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* String-overload: when `items` is an array of strings, the searchable text
|
|
4
|
+
* is the string itself. No keyFn is required.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fuzzyMatch<T extends string>(query: string, items: T[], options?: FuzzyMatchOptions): FuzzyMatchResult<T>[];
|
|
7
|
+
/**
|
|
8
|
+
* Object-overload: caller provides a `keyFn` that extracts the searchable
|
|
9
|
+
* text from each item. The keyFn is invoked once per item per call; the
|
|
10
|
+
* caller is responsible for ensuring the result is stable (e.g., don't
|
|
11
|
+
* concatenate mutable fields).
|
|
12
|
+
*/
|
|
13
|
+
export declare function fuzzyMatchWithKey<T>(query: string, items: T[], options: FuzzyMatchOptions & {
|
|
14
|
+
keyFn: (item: T) => string;
|
|
15
|
+
}): FuzzyMatchResult<T>[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Fzf } from 'fzf';
|
|
2
|
+
/**
|
|
3
|
+
* Default limit for fuzzy-match. Aligned with the spec's "--limit default 6".
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_LIMIT = 6;
|
|
6
|
+
/**
|
|
7
|
+
* String-overload: when `items` is an array of strings, the searchable text
|
|
8
|
+
* is the string itself. No keyFn is required.
|
|
9
|
+
*/
|
|
10
|
+
export function fuzzyMatch(query, items, options = {}) {
|
|
11
|
+
return fuzzyMatchWithKey(query, items, { ...options, keyFn: (item) => item });
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Object-overload: caller provides a `keyFn` that extracts the searchable
|
|
15
|
+
* text from each item. The keyFn is invoked once per item per call; the
|
|
16
|
+
* caller is responsible for ensuring the result is stable (e.g., don't
|
|
17
|
+
* concatenate mutable fields).
|
|
18
|
+
*/
|
|
19
|
+
export function fuzzyMatchWithKey(query, items, options) {
|
|
20
|
+
const { keyFn } = options;
|
|
21
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
22
|
+
if (items.length === 0)
|
|
23
|
+
return [];
|
|
24
|
+
// Empty query: surface all items (capped at limit) with neutral score and
|
|
25
|
+
// empty positions. Useful for "list" or "preview" use cases where the
|
|
26
|
+
// caller wants a deterministic top-N without a query.
|
|
27
|
+
if (query === '') {
|
|
28
|
+
return items.slice(0, limit).map((item) => ({ item, score: 0, positions: [] }));
|
|
29
|
+
}
|
|
30
|
+
const fzf = new Fzf(items, {
|
|
31
|
+
selector: keyFn,
|
|
32
|
+
limit,
|
|
33
|
+
// Per spec: default is case-insensitive (NOT fzf's smart-case).
|
|
34
|
+
// The user explicitly opts into case-sensitive via caseSensitive:true.
|
|
35
|
+
casing: options.caseSensitive === true ? 'case-sensitive' : 'case-insensitive',
|
|
36
|
+
// normalize:true (default) strips diacritics; fzf returns more matches
|
|
37
|
+
// for non-ASCII text this way, which is what we want for
|
|
38
|
+
// bilingual (zh-CN + en) memory entries.
|
|
39
|
+
});
|
|
40
|
+
const raw = fzf.find(query);
|
|
41
|
+
if (raw.length === 0)
|
|
42
|
+
return [];
|
|
43
|
+
// fzf-for-js score is "higher = better". Normalize so the top of the
|
|
44
|
+
// current batch is exactly 1.0 and others are in [0, 1].
|
|
45
|
+
// When the top score is 0 (degenerate — exact-character-only query that
|
|
46
|
+
// still matched somehow), fall back to 1.0 to avoid divide-by-zero.
|
|
47
|
+
const topScore = raw[0]?.score ?? 1;
|
|
48
|
+
const denom = topScore > 0 ? topScore : 1;
|
|
49
|
+
return raw.slice(0, limit).map((entry) => {
|
|
50
|
+
const score = topScore > 0 ? Number((entry.score / denom).toFixed(4)) : 1;
|
|
51
|
+
// positions is a Set<number> in fzf-for-js; convert to a sorted array
|
|
52
|
+
// so the JSON envelope is stable and human-readable.
|
|
53
|
+
const positions = [...entry.positions].sort((a, b) => a - b);
|
|
54
|
+
return { item: entry.item, score, positions };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the generic fuzzy-match kernel.
|
|
3
|
+
*/
|
|
4
|
+
export interface FuzzyMatchOptions {
|
|
5
|
+
/** Maximum number of matches to return. Default 6. */
|
|
6
|
+
limit?: number;
|
|
7
|
+
/** When true, matching is case-sensitive. Default false (smart-case). */
|
|
8
|
+
caseSensitive?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A single fuzzy-match hit. `item` is the original entry; `score` is
|
|
12
|
+
* normalized to [0, 1] with the top of the current batch at 1.0;
|
|
13
|
+
* `positions` is the set of char indices in the searchable text that
|
|
14
|
+
* contributed to the match.
|
|
15
|
+
*/
|
|
16
|
+
export interface FuzzyMatchResult<T> {
|
|
17
|
+
item: T;
|
|
18
|
+
score: number;
|
|
19
|
+
positions: number[];
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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;
|
|
@@ -0,0 +1,42 @@
|
|
|
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 { homedir } from 'node:os';
|
|
14
|
+
import { join, resolve } from 'node:path';
|
|
15
|
+
import { traeSubAgentDispatcher } from '../../dispatch/sub-agent-dispatcher.js';
|
|
16
|
+
export const OPENCLAW_ADAPTER = {
|
|
17
|
+
id: 'openclaw',
|
|
18
|
+
displayName: 'OpenClaw',
|
|
19
|
+
settings: {
|
|
20
|
+
dirName: '.openclaw', // UNVERIFIED
|
|
21
|
+
settingsFileName: 'settings.json',
|
|
22
|
+
resolveSettingsFile: (scope, projectRoot) => {
|
|
23
|
+
const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
|
|
24
|
+
return join(root, '.openclaw', 'settings.json');
|
|
25
|
+
},
|
|
26
|
+
supportsScope: (scope) => scope === 'project' || scope === 'global'
|
|
27
|
+
},
|
|
28
|
+
envVar: 'OPENCLAW_PROJECT_DIR', // UNVERIFIED
|
|
29
|
+
hookEvent: 'PreToolUse', // UNVERIFIED
|
|
30
|
+
toolMatcher: 'Bash', // UNVERIFIED
|
|
31
|
+
// Slice #0.7: OpenClaw sub-agent dispatcher UNVERIFIED. Reusing Trae
|
|
32
|
+
// dispatcher as uniform placeholder.
|
|
33
|
+
subAgentDispatcher: traeSubAgentDispatcher,
|
|
34
|
+
promptSizeAware: true,
|
|
35
|
+
installHints: [
|
|
36
|
+
'Restart OpenClaw (or reload the workspace) so the PreToolUse hooks take effect.'
|
|
37
|
+
],
|
|
38
|
+
capabilities: {
|
|
39
|
+
gateEnforce: true,
|
|
40
|
+
statusline: true
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CLAUDE_CODE_ADAPTER } from './adapters/claude-code-adapter.js';
|
|
2
2
|
import { TRAE_ADAPTER } from './adapters/trae-adapter.js';
|
|
3
|
+
import { HERMES_ADAPTER } from './adapters/hermes-adapter.js';
|
|
4
|
+
import { OPENCLAW_ADAPTER } from './adapters/openclaw-adapter.js';
|
|
3
5
|
/**
|
|
4
6
|
* Built-in IDE adapter registry。Map<IdeId, IdeAdapter> 是单一来源。
|
|
5
7
|
*
|
|
@@ -9,10 +11,13 @@ import { TRAE_ADAPTER } from './adapters/trae-adapter.js';
|
|
|
9
11
|
* 后续 slice 注入 codex / cursor / qoder / tongyi-lingma 时,只需在此
|
|
10
12
|
* Map 加条目 —— 所有 adapter 使用方(hook-translator、hooks install、statusline
|
|
11
13
|
* install、mcp apply)通过 `getAdapter(ide)` 拿取,无需修改。
|
|
14
|
+
* Slice #0.7 注入 hermes + openclaw。
|
|
12
15
|
*/
|
|
13
16
|
const ADAPTERS = new Map([
|
|
14
17
|
['claude-code', CLAUDE_CODE_ADAPTER],
|
|
15
18
|
['trae', TRAE_ADAPTER],
|
|
19
|
+
['hermes', HERMES_ADAPTER],
|
|
20
|
+
['openclaw', OPENCLAW_ADAPTER],
|
|
16
21
|
]);
|
|
17
22
|
/** Get the adapter for a given IDE id. Throws on unsupported IDE. */
|
|
18
23
|
export function getAdapter(ide) {
|
|
@@ -42,4 +47,6 @@ export function _resetAdaptersForTesting() {
|
|
|
42
47
|
ADAPTERS.clear();
|
|
43
48
|
ADAPTERS.set('claude-code', CLAUDE_CODE_ADAPTER);
|
|
44
49
|
ADAPTERS.set('trae', TRAE_ADAPTER);
|
|
50
|
+
ADAPTERS.set('hermes', HERMES_ADAPTER);
|
|
51
|
+
ADAPTERS.set('openclaw', OPENCLAW_ADAPTER);
|
|
45
52
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 其他全部归一化到 peaks 内部模型(见 hook-protocol.ts)。
|
|
13
13
|
*/
|
|
14
14
|
import type { SubAgentDispatcher } from '../dispatch/sub-agent-dispatcher.js';
|
|
15
|
-
export type IdeId = 'claude-code' | 'trae' | 'codex' | 'cursor' | 'qoder' | 'tongyi-lingma';
|
|
15
|
+
export type IdeId = 'claude-code' | 'trae' | 'codex' | 'cursor' | 'qoder' | 'tongyi-lingma' | 'hermes' | 'openclaw';
|
|
16
16
|
export interface IdeCapabilities {
|
|
17
17
|
/** peaks gate enforce 是否适用该 IDE(必备) */
|
|
18
18
|
readonly gateEnforce: true;
|