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.
- package/.claude-plugin/marketplace.json +51 -0
- package/CHANGELOG.md +279 -0
- package/README-en.md +226 -0
- package/README.md +152 -122
- 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/capability-commands.js +2 -1
- 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 +60 -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/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/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 +117 -2
- package/dist/src/cli/program.js +30 -0
- package/dist/src/lib/render/message-renderer.d.ts +20 -0
- package/dist/src/lib/render/message-renderer.js +80 -0
- 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 +111 -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 +36 -2
- package/dist/src/services/config/config-service.js +105 -0
- package/dist/src/services/config/config-types.d.ts +73 -0
- package/dist/src/services/config/config-types.js +28 -13
- package/dist/src/services/config/model-routing.js +5 -3
- package/dist/src/services/doctor/doctor-service.js +96 -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/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/rd/rd-service.js +29 -1
- 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 +86 -0
- package/dist/src/services/skills/sync-service.js +271 -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/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/workflow/workflow-router-service.js +15 -4
- package/dist/src/services/workspace/claude-settings-template.d.ts +53 -0
- package/dist/src/services/workspace/claude-settings-template.js +133 -0
- 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-service.d.ts +24 -0
- package/dist/src/services/workspace/workspace-service.js +124 -2
- 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 +8 -2
- 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 +16 -4
- package/skills/peaks-solo/references/anchoring-and-session-info.md +9 -0
- 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
|
@@ -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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 ??
|
|
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
|
-
|
|
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;
|