peaks-loop 4.0.0-beta.12 → 4.0.0-beta.15
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/CHANGELOG.md +282 -147
- package/README-en.md +1 -1
- package/README.md +32 -1
- package/bin/peaks.js +0 -0
- package/dist/cli/cli-helpers.d.ts +1 -1
- package/dist/cli/cli-helpers.js +1 -1
- package/dist/cli/commands/adapter-commands-s2a.js +1 -1
- package/dist/cli/commands/asset-commands.js +33 -16
- package/dist/cli/commands/audit-commands.js +1 -1
- package/dist/cli/commands/bee-commands.js +1 -1
- package/dist/cli/commands/capability-commands.js +1 -1
- package/dist/cli/commands/classify-classify-commands.js +1 -1
- package/dist/cli/commands/code-commands.js +1 -1
- package/dist/cli/commands/code-review-commands.js +1 -1
- package/dist/cli/commands/codegraph-commands.js +1 -1
- package/dist/cli/commands/compact-command.js +1 -1
- package/dist/cli/commands/complexity-commands.js +1 -1
- package/dist/cli/commands/config-commands.js +1 -1
- package/dist/cli/commands/context-commands.js +1 -1
- package/dist/cli/commands/contract-commands.js +1 -1
- package/dist/cli/commands/core/artifacts-command.js +1 -1
- package/dist/cli/commands/core/binding-commands.js +1 -1
- package/dist/cli/commands/core/doctor-command.js +63 -3
- package/dist/cli/commands/core/memory-command.js +1 -1
- package/dist/cli/commands/core/profile-command.js +1 -1
- package/dist/cli/commands/core/proxy-command.js +1 -1
- package/dist/cli/commands/core/session-command.js +1 -1
- package/dist/cli/commands/core/skill-command.js +2 -2
- package/dist/cli/commands/core/standards-command.js +1 -1
- package/dist/cli/commands/dispatch-commands.js +2 -2
- package/dist/cli/commands/dispatch-from-dag.js +1 -1
- package/dist/cli/commands/doc-commands.js +1 -1
- package/dist/cli/commands/ecc-commands.js +2 -2
- package/dist/cli/commands/evolution-commands.js +1 -1
- package/dist/cli/commands/feedback-commands.js +1 -1
- package/dist/cli/commands/final-review-commands.js +1 -1
- package/dist/cli/commands/fixture-commands.js +1 -1
- package/dist/cli/commands/fork-commands.js +1 -1
- package/dist/cli/commands/gate-commands.js +1 -1
- package/dist/cli/commands/heartbeat-commands.js +1 -1
- package/dist/cli/commands/hook-handle.js +1 -1
- package/dist/cli/commands/hooks-commands.js +1 -1
- package/dist/cli/commands/ide-commands.js +1 -1
- package/dist/cli/commands/impact-commands.js +1 -1
- package/dist/cli/commands/job-commands.js +1 -1
- package/dist/cli/commands/legacy-commands.js +1 -1
- package/dist/cli/commands/log-commands.js +1 -1
- package/dist/cli/commands/loop-commands.js +1 -1
- package/dist/cli/commands/loop-eval-commands.js +1 -1
- package/dist/cli/commands/memory-commands.js +1 -1
- package/dist/cli/commands/migrate-1-4-1-command.js +1 -1
- package/dist/cli/commands/migrate-v2-10-to-v2-11-command.js +1 -1
- package/dist/cli/commands/mut-commands.d.ts +1 -1
- package/dist/cli/commands/mut-commands.js +1 -5
- package/dist/cli/commands/observability-commands.js +1 -1
- package/dist/cli/commands/openspec-commands.js +2 -2
- package/dist/cli/commands/perf-audit-commands.js +2 -2
- package/dist/cli/commands/perf-commands.js +1 -1
- package/dist/cli/commands/polyrepo-commands.js +1 -1
- package/dist/cli/commands/prd-blocks-commands.js +1 -1
- package/dist/cli/commands/prd-commands.js +1 -1
- package/dist/cli/commands/project-commands.js +1 -1
- package/dist/cli/commands/qa-business-review-commands.js +1 -1
- package/dist/cli/commands/qa-commands.d.ts +1 -1
- package/dist/cli/commands/qa-commands.js +2 -2
- package/dist/cli/commands/release-commands.js +1 -1
- package/dist/cli/commands/request-commands.js +1 -1
- package/dist/cli/commands/retrospective-commands.js +1 -1
- package/dist/cli/commands/reviewer-commands.js +1 -1
- package/dist/cli/commands/role-commands.js +1 -1
- package/dist/cli/commands/runtime-commands.js +1 -2
- package/dist/cli/commands/sc-commands.js +1 -1
- package/dist/cli/commands/scan-commands.js +1 -1
- package/dist/cli/commands/security-audit-commands.js +2 -2
- package/dist/cli/commands/sediment-commands.js +10 -1
- package/dist/cli/commands/share-commands.d.ts +19 -0
- package/dist/cli/commands/share-commands.js +121 -2
- package/dist/cli/commands/skill-conformance-commands.js +1 -1
- package/dist/cli/commands/skill-loop-engineering-readiness-commands.js +1 -1
- package/dist/cli/commands/skill-search-commands.js +1 -1
- package/dist/cli/commands/slice-commands.js +1 -1
- package/dist/cli/commands/slice-integrate-commands.js +1 -1
- package/dist/cli/commands/slice-review-commands.js +1 -1
- package/dist/cli/commands/smoke-commands.js +1 -1
- package/dist/cli/commands/sop-commands.js +1 -1
- package/dist/cli/commands/statusline-commands.js +1 -1
- package/dist/cli/commands/sub-agent-commands.js +2 -1
- package/dist/cli/commands/understand-commands.js +1 -1
- package/dist/cli/commands/upgrade-commands.js +1 -1
- package/dist/cli/commands/user-touchpoint-commands.js +1 -1
- package/dist/cli/commands/verdict-aggregate-command.js +1 -1
- package/dist/cli/commands/workflow-commands.js +1 -1
- package/dist/cli/commands/workflow-plan-commands.js +1 -1
- package/dist/cli/commands/workspace/init-command.js +1 -1
- package/dist/cli/commands/workspace/migrate-command.js +1 -1
- package/dist/cli/commands/workspace/reconcile-command.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/program.js +2 -2
- package/dist/services/artifacts/artifact-prerequisites.js +1 -1
- package/dist/services/artifacts/request-artifact-service.js +1 -1
- package/dist/services/artifacts/workspace-service.js +1 -1
- package/dist/services/code/auto-compact-orchestrator.js +5 -2
- package/dist/services/code-review/ocr-service.d.ts +1 -1
- package/dist/services/code-review/ocr-service.js +6 -3
- package/dist/services/config/config-types.js +1 -1
- package/dist/services/dashboard/project-dashboard-service.js +2 -2
- package/dist/services/dispatch/batch-counter.js +1 -1
- package/dist/services/dispatch/dispatch-record-writer.js +1 -1
- package/dist/services/job/job-progress-store.d.ts +2 -2
- package/dist/services/job/job-types.d.ts +27 -27
- package/dist/services/log/retention.js +1 -1
- package/dist/services/openspec/openspec-archive-service.js +1 -1
- package/dist/services/openspec/openspec-bridge-service.js +1 -1
- package/dist/services/openspec/openspec-init-service.js +1 -1
- package/dist/services/openspec/openspec-propose-from-doctor-service.js +1 -1
- package/dist/services/openspec/openspec-render-service.js +1 -1
- package/dist/services/openspec/openspec-scan-service.js +1 -1
- package/dist/services/openspec/openspec-validate-service.js +1 -1
- package/dist/services/scan/acceptance-coverage-service.js +1 -1
- package/dist/services/scan/archetype-service.js +1 -1
- package/dist/services/scan/existing-system-service.js +1 -1
- package/dist/services/scan/libraries-service.js +1 -1
- package/dist/services/skill/skill-search-service.d.ts +5 -5
- package/dist/services/skill/skill-search-service.js +2 -2
- package/dist/services/skillhub/migrations/001-initial.sql +71 -0
- package/dist/services/skillhub/migrations/002-loop-release.sql +32 -0
- package/dist/services/skillhub/migrations/003-loop-bee-relation.sql +35 -0
- package/dist/services/skillhub/migrations/004-loop-bee-extension.sql +65 -0
- package/dist/services/skillhub/migrations/005-evolution-evaluation.sql +81 -0
- package/dist/services/skillhub/migrations/006-crystallization-event.sql +92 -0
- package/dist/services/skills/skill-registry.js +2 -2
- package/dist/services/skills/skill-runbook-service.js +1 -1
- package/dist/services/slice/slice-check-service.js +1 -1
- package/dist/services/understand/understand-hybrid-service.js +1 -1
- package/dist/services/understand/understand-scan-service.js +2 -2
- package/dist/services/upgrade/upgrade-service.js +4 -3
- package/dist/services/verdict/envelopes.d.ts +2 -2
- package/dist/services/verdict/envelopes.js +2 -2
- package/dist/services/verdict/verdict-aggregator.d.ts +3 -3
- package/dist/services/workflow/autonomous-resume-writer.js +1 -1
- package/dist/services/workflow/plan-reader.d.ts +1 -1
- package/dist/services/workflow/plan-reader.js +1 -1
- package/dist/services/workflow/plan-refresher.d.ts +1 -1
- package/dist/services/workflow/plan-refresher.js +1 -1
- package/dist/services/workflow/plan-trigger-detector.d.ts +1 -1
- package/dist/services/workflow/plan-trigger-detector.js +1 -1
- package/dist/services/workflow/workflow-router-service.js +11 -9
- package/dist/services/workspace/migrate-service.js +1 -1
- package/dist/services/workspace/workspace-service.js +1 -1
- package/package.json +14 -8
- package/scripts/copy-templates.mjs +16 -0
- package/scripts/sync-version.mjs +8 -1
- package/skills/bee/peaks-rd/SKILL.md +14 -1
- package/skills/peaks-code/SKILL.md +44 -0
- package/skills/peaks-code/references/sub-agent-dispatch.md +30 -0
- package/dist/services/agent/ecc-cache-service.d.ts +0 -97
- package/dist/services/agent/ecc-cache-service.js +0 -491
- package/dist/services/audit-independent/perf-audit-service.d.ts +0 -151
- package/dist/services/audit-independent/perf-audit-service.js +0 -316
- package/dist/services/audit-independent/security-audit-service.d.ts +0 -155
- package/dist/services/audit-independent/security-audit-service.js +0 -316
- package/dist/services/context/dispatch-context-guard.d.ts +0 -27
- package/dist/services/context/dispatch-context-guard.js +0 -192
- package/dist/services/context/shared-channel.d.ts +0 -93
- package/dist/services/context/shared-channel.js +0 -308
- package/dist/services/crystallization/crystallization-service.d.ts +0 -239
- package/dist/services/crystallization/crystallization-service.js +0 -397
- package/dist/services/crystallization/crystallization-store.d.ts +0 -75
- package/dist/services/crystallization/crystallization-store.js +0 -193
- package/dist/services/crystallization/crystallization-types.d.ts +0 -313
- package/dist/services/crystallization/crystallization-types.js +0 -299
- package/dist/services/crystallization/evidence-brief-builder.d.ts +0 -237
- package/dist/services/crystallization/evidence-brief-builder.js +0 -240
- package/dist/services/doctor/doctor-service.d.ts +0 -149
- package/dist/services/doctor/doctor-service.js +0 -929
- package/dist/services/filesystem/file-lock.d.ts +0 -11
- package/dist/services/filesystem/file-lock.js +0 -152
- package/dist/services/final-review/final-review-service.d.ts +0 -12
- package/dist/services/final-review/final-review-service.js +0 -52
- package/dist/services/final-review/final-review-types.d.ts +0 -42
- package/dist/services/final-review/final-review-types.js +0 -18
- package/dist/services/job/job-resource-snapshot.d.ts +0 -7
- package/dist/services/job/job-resource-snapshot.js +0 -75
- package/dist/services/mut/assert-scanner.d.ts +0 -6
- package/dist/services/mut/assert-scanner.js +0 -116
- package/dist/services/mut/index.d.ts +0 -8
- package/dist/services/mut/index.js +0 -7
- package/dist/services/mut/mut-runner.d.ts +0 -39
- package/dist/services/mut/mut-runner.js +0 -29
- package/dist/services/mut/production-stryker.d.ts +0 -2
- package/dist/services/mut/production-stryker.js +0 -75
- package/dist/services/mut/report-builder.d.ts +0 -11
- package/dist/services/mut/report-builder.js +0 -110
- package/dist/services/mut/report-loader.d.ts +0 -31
- package/dist/services/mut/report-loader.js +0 -78
- package/dist/services/mut/thresholds.d.ts +0 -21
- package/dist/services/mut/thresholds.js +0 -22
- package/dist/services/mut/types.d.ts +0 -336
- package/dist/services/mut/types.js +0 -59
- package/dist/shared/fs.d.ts +0 -4
- package/dist/shared/fs.js +0 -26
- package/dist/shared/paths.d.ts +0 -6
- package/dist/shared/paths.js +0 -50
- package/dist/shared/result.d.ts +0 -22
- package/dist/shared/result.js +0 -46
- package/dist/shared/version.d.ts +0 -1
- package/dist/shared/version.js +0 -1
- package/examples/video-demo/README.md +0 -142
- package/examples/video-demo/out/en-closing-960.png +0 -0
- package/examples/video-demo/out/zh-closing-960.png +0 -0
- package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
- package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ECC cache service — Slice 3 of 4.0.0-beta.11.
|
|
3
|
-
*
|
|
4
|
-
* Drop-in replacement for the deleted `ecc-agent-service.ts`. The
|
|
5
|
-
* pre-Slice-3 implementation shelled out to `npx ecc agent run ...`,
|
|
6
|
-
* but the upstream `affaan-m/everything-claude-code` v2.0.0 release
|
|
7
|
-
* has NO `ecc` binary — the repo is `agents/*.md` flat files plus
|
|
8
|
-
* SKILL.md descriptors. The peaks-loop path is therefore download
|
|
9
|
-
* + cache only; the LLM consumes cached `agents/*.md` directly
|
|
10
|
-
* through `peaks ecc show <name>`.
|
|
11
|
-
*
|
|
12
|
-
* Six exports per RD §2:
|
|
13
|
-
* - setCacheDirPermissions
|
|
14
|
-
* - downloadToCache
|
|
15
|
-
* - readCacheManifest
|
|
16
|
-
* - listCachedAgents
|
|
17
|
-
* - readAgentSkill
|
|
18
|
-
* - cleanupStaleCache
|
|
19
|
-
*
|
|
20
|
-
* The cache layout is `~/.peaks/cache/ecc-installed.json` (manifest)
|
|
21
|
-
* + `~/.peaks/cache/ecc-<sha>/agents/<name>.md`. The manifest is
|
|
22
|
-
* the active-cache pointer; cleanupStaleCache iterates the on-disk
|
|
23
|
-
* sha directories and decides based on the manifest's `fetchedAt`
|
|
24
|
-
* (active) vs `mtimeMs` (orphan).
|
|
25
|
-
*
|
|
26
|
-
* Hard contracts (Karpathy #2 Simplicity First):
|
|
27
|
-
* - No subprocess plumbing. Network is `fetch()` only.
|
|
28
|
-
* - No factory; one module, six functions, six dependencies.
|
|
29
|
-
* - Strict selective extract: agents/*.md only, no symlinks, no
|
|
30
|
-
* `..`, no absolute paths, no devices.
|
|
31
|
-
* - D-009 fallback: parseFrontmatter throws on malformed input —
|
|
32
|
-
* wrap it in try/catch and synthesize `{name, description}`
|
|
33
|
-
* from the file name + first non-empty body line.
|
|
34
|
-
* - D-010 fallback: PRD's `ecc.tar.gz` asset URL does NOT exist
|
|
35
|
-
* upstream; try PRD first, fall back to GitHub's `tarball_url`
|
|
36
|
-
* on the release JSON.
|
|
37
|
-
*/
|
|
38
|
-
import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
39
|
-
import { join } from 'node:path';
|
|
40
|
-
import { homedir } from 'node:os';
|
|
41
|
-
import { parseFrontmatter } from '../../shared/frontmatter.js';
|
|
42
|
-
export const ECC_REPO_OWNER = 'affaan-m';
|
|
43
|
-
export const ECC_REPO_NAME = 'everything-claude-code';
|
|
44
|
-
const ECC_CACHE_VERSION = '1';
|
|
45
|
-
const ECC_TARBALL_BASENAME = 'ecc.tar.gz';
|
|
46
|
-
/**
|
|
47
|
-
* Cache dir resolution — single source of truth. Tests inject
|
|
48
|
-
* via `dirOverride` on the functions that take one.
|
|
49
|
-
*/
|
|
50
|
-
export function resolveEccCacheDir() {
|
|
51
|
-
return join(homedir(), '.peaks', 'cache');
|
|
52
|
-
}
|
|
53
|
-
export function resolveManifestPath(dirOverride) {
|
|
54
|
-
return join(dirOverride ?? resolveEccCacheDir(), 'ecc-installed.json');
|
|
55
|
-
}
|
|
56
|
-
export function resolveShaDir(sha, dirOverride) {
|
|
57
|
-
return join(dirOverride ?? resolveEccCacheDir(), `ecc-${sha}`);
|
|
58
|
-
}
|
|
59
|
-
export function resolveAgentsDir(sha, dirOverride) {
|
|
60
|
-
return join(resolveShaDir(sha, dirOverride), 'agents');
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Best-effort chmod 0o700 on POSIX; no-op on Windows (NTFS uses
|
|
64
|
-
* ACLs, not POSIX mode bits). Swallows errors with WARN so a
|
|
65
|
-
* permissions failure never blocks the CLI.
|
|
66
|
-
*/
|
|
67
|
-
export function setCacheDirPermissions(cacheDir) {
|
|
68
|
-
if (process.platform === 'win32')
|
|
69
|
-
return;
|
|
70
|
-
try {
|
|
71
|
-
chmodSync(cacheDir, 0o700);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
/* best-effort */
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function isSafeAgentName(name) {
|
|
78
|
-
return /^[a-z][a-z0-9-]*$/.test(name);
|
|
79
|
-
}
|
|
80
|
-
function isSafeArchiveEntry(entryName) {
|
|
81
|
-
if (entryName.length === 0)
|
|
82
|
-
return false;
|
|
83
|
-
if (entryName.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(entryName))
|
|
84
|
-
return false;
|
|
85
|
-
if (entryName.includes('..'))
|
|
86
|
-
return false;
|
|
87
|
-
// Strip the leading "<repo-root>/" segment (github tarballs prefix
|
|
88
|
-
// every entry with "<repo>-<sha>/..."); everything after must
|
|
89
|
-
// start with "agents/" and end with ".md" at a single level.
|
|
90
|
-
const segments = entryName.split('/');
|
|
91
|
-
// Drop the first "<repo>-<sha>" segment if present.
|
|
92
|
-
if (segments.length >= 3 && segments[0]?.startsWith(`${ECC_REPO_NAME}-`)) {
|
|
93
|
-
segments.shift();
|
|
94
|
-
}
|
|
95
|
-
if (segments.length !== 2)
|
|
96
|
-
return false;
|
|
97
|
-
return segments[0] === 'agents' && segments[1]?.endsWith('.md') === true;
|
|
98
|
-
}
|
|
99
|
-
function safeAgentNameFromEntry(entryName) {
|
|
100
|
-
const segments = entryName.split('/');
|
|
101
|
-
if (segments.length >= 3 && segments[0]?.startsWith(`${ECC_REPO_NAME}-`)) {
|
|
102
|
-
segments.shift();
|
|
103
|
-
}
|
|
104
|
-
const file = segments[segments.length - 1] ?? '';
|
|
105
|
-
const base = file.replace(/\.md$/i, '');
|
|
106
|
-
return isSafeAgentName(base) ? base : null;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Best-effort fetch of the upstream release JSON. Returns the
|
|
110
|
-
* parsed body, or null on any network/parse error. Used as the
|
|
111
|
-
* D-010 fallback path.
|
|
112
|
-
*/
|
|
113
|
-
async function fetchReleaseJson(apiBase) {
|
|
114
|
-
try {
|
|
115
|
-
const res = await fetch(apiBase, {
|
|
116
|
-
headers: { accept: 'application/vnd.github+json', 'user-agent': 'peaks-loop' },
|
|
117
|
-
});
|
|
118
|
-
if (!res.ok)
|
|
119
|
-
return null;
|
|
120
|
-
return (await res.json());
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Resolve a 40-char commit SHA for `tag`. Prefers the GitHub
|
|
128
|
-
* releases API (which dereferences annotated tags to commit SHA
|
|
129
|
-
* via `target_commitish`). Falls back to the tag name if the
|
|
130
|
-
* upstream returns a non-SHA-shaped identifier (older tags).
|
|
131
|
-
*/
|
|
132
|
-
async function resolveCommitSha(ref) {
|
|
133
|
-
const apiBase = `https://api.github.com/repos/${ECC_REPO_OWNER}/${ECC_REPO_NAME}/releases/tags/${encodeURIComponent(ref)}`;
|
|
134
|
-
const release = await fetchReleaseJson(apiBase);
|
|
135
|
-
const tagName = release?.tag_name ?? ref;
|
|
136
|
-
if (/^[0-9a-f]{40}$/.test(tagName))
|
|
137
|
-
return tagName;
|
|
138
|
-
return tagName;
|
|
139
|
-
}
|
|
140
|
-
function asSha(value) {
|
|
141
|
-
return /^[0-9a-f]{40}$/.test(value) ? value : value;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Tiny tar.gz extractor. Public-domain-compatible tar parser:
|
|
145
|
-
* - reads 512-byte header blocks
|
|
146
|
-
* - validates name + size
|
|
147
|
-
* - rejects anything that is not a regular file at a safe path
|
|
148
|
-
* - filters to the `agents/*.md` allowlist via isSafeArchiveEntry
|
|
149
|
-
*
|
|
150
|
-
* We do NOT decompress gzip via a third-party lib; Node's
|
|
151
|
-
* `node:zlib` ships with `gunzip` so the implementation is
|
|
152
|
-
* self-contained.
|
|
153
|
-
*/
|
|
154
|
-
async function extractAgentsFromTarGz(buffer, outDir) {
|
|
155
|
-
if (!existsSync(outDir))
|
|
156
|
-
mkdirSync(outDir, { recursive: true });
|
|
157
|
-
const extracted = [];
|
|
158
|
-
// Lazy import to keep the cold path cheap.
|
|
159
|
-
const { gunzipSync } = await import('node:zlib');
|
|
160
|
-
let tar;
|
|
161
|
-
try {
|
|
162
|
-
tar = gunzipSync(buffer);
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
const BLOCK = 512;
|
|
168
|
-
let offset = 0;
|
|
169
|
-
while (offset + BLOCK <= tar.length) {
|
|
170
|
-
const header = tar.subarray(offset, offset + BLOCK);
|
|
171
|
-
// Two consecutive zero blocks mark end-of-archive.
|
|
172
|
-
if (header.every((b) => b === 0))
|
|
173
|
-
break;
|
|
174
|
-
const name = readCString(header, 0, 100);
|
|
175
|
-
if (name === null || name.length === 0)
|
|
176
|
-
break;
|
|
177
|
-
const sizeOctal = readCString(header, 124, 12);
|
|
178
|
-
if (sizeOctal === null)
|
|
179
|
-
break;
|
|
180
|
-
const size = parseOctal(sizeOctal);
|
|
181
|
-
if (size === null)
|
|
182
|
-
break;
|
|
183
|
-
const typeFlag = String.fromCharCode(header[156] ?? 0);
|
|
184
|
-
// '0' or '\0' = regular file. Reject symlinks (2), dirs (5), etc.
|
|
185
|
-
if (typeFlag !== '0' && typeFlag !== '\0') {
|
|
186
|
-
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
if (!isSafeArchiveEntry(name)) {
|
|
190
|
-
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
const safeName = safeAgentNameFromEntry(name);
|
|
194
|
-
if (safeName === null) {
|
|
195
|
-
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
const data = tar.subarray(offset + BLOCK, offset + BLOCK + size);
|
|
199
|
-
writeFileSync(join(outDir, `${safeName}.md`), data);
|
|
200
|
-
extracted.push(safeName);
|
|
201
|
-
offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
|
|
202
|
-
}
|
|
203
|
-
return extracted;
|
|
204
|
-
}
|
|
205
|
-
function readCString(buf, start, len) {
|
|
206
|
-
let end = start;
|
|
207
|
-
while (end < start + len && buf[end] !== 0)
|
|
208
|
-
end += 1;
|
|
209
|
-
if (end === start)
|
|
210
|
-
return '';
|
|
211
|
-
try {
|
|
212
|
-
return Buffer.from(buf.subarray(start, end)).toString('utf8');
|
|
213
|
-
}
|
|
214
|
-
catch {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
function parseOctal(value) {
|
|
219
|
-
const trimmed = value.trim().replace(/\0+$/g, '');
|
|
220
|
-
if (trimmed.length === 0)
|
|
221
|
-
return 0;
|
|
222
|
-
const n = Number.parseInt(trimmed, 8);
|
|
223
|
-
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
224
|
-
}
|
|
225
|
-
async function fetchBuffer(url) {
|
|
226
|
-
try {
|
|
227
|
-
const res = await fetch(url, {
|
|
228
|
-
headers: { accept: 'application/octet-stream', 'user-agent': 'peaks-loop' },
|
|
229
|
-
});
|
|
230
|
-
if (!res.ok)
|
|
231
|
-
return null;
|
|
232
|
-
const ab = await res.arrayBuffer();
|
|
233
|
-
return new Uint8Array(ab);
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async function downloadTarball(ref, sha, outDir) {
|
|
240
|
-
// D-010: PRD URL first, GitHub fallback second.
|
|
241
|
-
const prdUrl = `https://github.com/${ECC_REPO_OWNER}/${ECC_REPO_NAME}/releases/download/${encodeURIComponent(ref)}/${ECC_TARBALL_BASENAME}`;
|
|
242
|
-
let buffer = await fetchBuffer(prdUrl);
|
|
243
|
-
if (buffer === null) {
|
|
244
|
-
const release = await fetchReleaseJson(`https://api.github.com/repos/${ECC_REPO_OWNER}/${ECC_REPO_NAME}/releases/tags/${encodeURIComponent(ref)}`);
|
|
245
|
-
const tarballUrl = release?.tarball_url;
|
|
246
|
-
if (typeof tarballUrl === 'string' && tarballUrl.length > 0) {
|
|
247
|
-
buffer = await fetchBuffer(tarballUrl);
|
|
248
|
-
}
|
|
249
|
-
if (buffer === null && Array.isArray(release?.assets)) {
|
|
250
|
-
const asset = release.assets.find((a) => a.name === ECC_TARBALL_BASENAME) ??
|
|
251
|
-
release.assets.find((a) => a.name === `${ECC_REPO_NAME}-universal-${ref}.tgz`) ??
|
|
252
|
-
release.assets.find((a) => a.name.endsWith('.tgz') || a.name.endsWith('.tar.gz'));
|
|
253
|
-
if (asset)
|
|
254
|
-
buffer = await fetchBuffer(asset.browser_download_url);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (buffer === null) {
|
|
258
|
-
throw new Error('fetch-failed');
|
|
259
|
-
}
|
|
260
|
-
return extractAgentsFromTarGz(buffer, outDir);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Download ECC into `~/.peaks/cache/ecc-<sha>/agents/`. Returns
|
|
264
|
-
* the resolved SHA + the number of agents extracted.
|
|
265
|
-
*
|
|
266
|
-
* Throws `Error('fetch-failed')` on network failure so the CLI
|
|
267
|
-
* layer can render the manual-install instructions.
|
|
268
|
-
*/
|
|
269
|
-
export async function downloadToCache({ ref } = {}) {
|
|
270
|
-
const requestedRef = ref ?? 'latest';
|
|
271
|
-
let resolvedSha;
|
|
272
|
-
if (requestedRef === 'latest') {
|
|
273
|
-
const release = await fetchReleaseJson(`https://api.github.com/repos/${ECC_REPO_OWNER}/${ECC_REPO_NAME}/releases/latest`);
|
|
274
|
-
const tag = release?.tag_name;
|
|
275
|
-
if (typeof tag !== 'string' || tag.length === 0) {
|
|
276
|
-
throw new Error('fetch-failed');
|
|
277
|
-
}
|
|
278
|
-
resolvedSha = asSha(await resolveCommitSha(tag) ?? tag);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
resolvedSha = asSha(await resolveCommitSha(requestedRef) ?? requestedRef);
|
|
282
|
-
}
|
|
283
|
-
const cacheDir = resolveEccCacheDir();
|
|
284
|
-
if (!existsSync(cacheDir))
|
|
285
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
286
|
-
setCacheDirPermissions(cacheDir);
|
|
287
|
-
const agentsDir = resolveAgentsDir(resolvedSha, cacheDir);
|
|
288
|
-
if (existsSync(agentsDir) && readdirSync(agentsDir).length > 0) {
|
|
289
|
-
// Already populated; emit manifest + return.
|
|
290
|
-
const agents = readdirSync(agentsDir)
|
|
291
|
-
.filter((f) => f.endsWith('.md'))
|
|
292
|
-
.map((f) => f.replace(/\.md$/i, ''));
|
|
293
|
-
writeManifest({ version: ECC_CACHE_VERSION, sha: resolvedSha, fetchedAt: new Date().toISOString(), agents });
|
|
294
|
-
return { sha: resolvedSha, agents: agents.length };
|
|
295
|
-
}
|
|
296
|
-
const extracted = await downloadTarball(requestedRef === 'latest' ? resolvedSha : requestedRef, resolvedSha, agentsDir);
|
|
297
|
-
const manifest = {
|
|
298
|
-
version: ECC_CACHE_VERSION,
|
|
299
|
-
sha: resolvedSha,
|
|
300
|
-
fetchedAt: new Date().toISOString(),
|
|
301
|
-
agents: extracted,
|
|
302
|
-
};
|
|
303
|
-
writeManifest(manifest);
|
|
304
|
-
return { sha: resolvedSha, agents: extracted.length };
|
|
305
|
-
}
|
|
306
|
-
function writeManifest(manifest) {
|
|
307
|
-
const path = resolveManifestPath();
|
|
308
|
-
writeFileSync(path, JSON.stringify(manifest, null, 2));
|
|
309
|
-
}
|
|
310
|
-
export function readCacheManifest() {
|
|
311
|
-
const path = resolveManifestPath();
|
|
312
|
-
if (!existsSync(path))
|
|
313
|
-
return null;
|
|
314
|
-
try {
|
|
315
|
-
const raw = readFileSync(path, 'utf8');
|
|
316
|
-
const parsed = JSON.parse(raw);
|
|
317
|
-
if (typeof parsed.version === 'string' &&
|
|
318
|
-
typeof parsed.sha === 'string' &&
|
|
319
|
-
typeof parsed.fetchedAt === 'string' &&
|
|
320
|
-
Array.isArray(parsed.agents)) {
|
|
321
|
-
return parsed;
|
|
322
|
-
}
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
let warnedAboutFallback = false;
|
|
330
|
-
function fallbackMetadata(fileName) {
|
|
331
|
-
const base = fileName.replace(/\.md$/i, '');
|
|
332
|
-
let description = '';
|
|
333
|
-
try {
|
|
334
|
-
const body = readFileSync(join(resolveManifestDirForAgents(), fileName), 'utf8');
|
|
335
|
-
const lines = body.split(/\r?\n/);
|
|
336
|
-
let inFrontmatter = false;
|
|
337
|
-
let seenClosing = false;
|
|
338
|
-
for (const raw of lines) {
|
|
339
|
-
const line = raw.trim();
|
|
340
|
-
if (!seenClosing) {
|
|
341
|
-
if (!inFrontmatter && line === '---') {
|
|
342
|
-
inFrontmatter = true;
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
if (inFrontmatter && line === '---') {
|
|
346
|
-
seenClosing = true;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
if (line.length === 0)
|
|
352
|
-
continue;
|
|
353
|
-
description = line;
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
catch {
|
|
358
|
-
/* best-effort */
|
|
359
|
-
}
|
|
360
|
-
if (description.length > 80)
|
|
361
|
-
description = `${description.slice(0, 77)}...`;
|
|
362
|
-
if (!warnedAboutFallback) {
|
|
363
|
-
try {
|
|
364
|
-
process.stderr.write(`warning: cached agent "${base}" has malformed frontmatter; falling back to filename + first body line\n`);
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
/* best-effort */
|
|
368
|
-
}
|
|
369
|
-
warnedAboutFallback = true;
|
|
370
|
-
}
|
|
371
|
-
return { name: base, description };
|
|
372
|
-
}
|
|
373
|
-
function resolveManifestDirForAgents() {
|
|
374
|
-
const manifest = readCacheManifest();
|
|
375
|
-
if (manifest === null)
|
|
376
|
-
return '';
|
|
377
|
-
return resolveAgentsDir(manifest.sha);
|
|
378
|
-
}
|
|
379
|
-
export function listCachedAgents() {
|
|
380
|
-
const manifest = readCacheManifest();
|
|
381
|
-
if (manifest === null)
|
|
382
|
-
return [];
|
|
383
|
-
const agentsDir = resolveAgentsDir(manifest.sha);
|
|
384
|
-
if (!existsSync(agentsDir))
|
|
385
|
-
return [];
|
|
386
|
-
const out = [];
|
|
387
|
-
for (const file of readdirSync(agentsDir).sort()) {
|
|
388
|
-
if (!file.endsWith('.md'))
|
|
389
|
-
continue;
|
|
390
|
-
const fullPath = join(agentsDir, file);
|
|
391
|
-
let meta;
|
|
392
|
-
try {
|
|
393
|
-
const raw = readFileSync(fullPath, 'utf8');
|
|
394
|
-
const fm = parseFrontmatter(raw);
|
|
395
|
-
meta = { name: fm.name, description: fm.description };
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
// D-009 fallback — name from filename, description from first body line.
|
|
399
|
-
meta = fallbackMetadata(file);
|
|
400
|
-
}
|
|
401
|
-
out.push(meta);
|
|
402
|
-
}
|
|
403
|
-
return out;
|
|
404
|
-
}
|
|
405
|
-
export function readAgentSkill(name) {
|
|
406
|
-
if (!isSafeAgentName(name))
|
|
407
|
-
return null;
|
|
408
|
-
const manifest = readCacheManifest();
|
|
409
|
-
if (manifest === null)
|
|
410
|
-
return null;
|
|
411
|
-
const file = join(resolveAgentsDir(manifest.sha), `${name}.md`);
|
|
412
|
-
if (!existsSync(file))
|
|
413
|
-
return null;
|
|
414
|
-
try {
|
|
415
|
-
return readFileSync(file, 'utf8');
|
|
416
|
-
}
|
|
417
|
-
catch {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* 7-day TTL sweep over `ecc-<sha>/` directories.
|
|
423
|
-
*
|
|
424
|
-
* - Active cache (per manifest `sha`): use `fetchedAt` as the
|
|
425
|
-
* age source; remove if older than `retentionDays`.
|
|
426
|
-
* - Orphan caches (any other `ecc-<40hex>` directory): use
|
|
427
|
-
* directory `mtimeMs` as the age source.
|
|
428
|
-
* - If the active cache is removed, invalidate the manifest.
|
|
429
|
-
*
|
|
430
|
-
* Returns the absolute paths of the removed directories.
|
|
431
|
-
*/
|
|
432
|
-
export function cleanupStaleCache({ retentionDays, nowMs, dirOverride, }) {
|
|
433
|
-
const cacheDir = dirOverride ?? resolveEccCacheDir();
|
|
434
|
-
if (!existsSync(cacheDir))
|
|
435
|
-
return { removed: [] };
|
|
436
|
-
const dayMs = 24 * 60 * 60 * 1000;
|
|
437
|
-
const cutoff = nowMs - retentionDays * dayMs;
|
|
438
|
-
const manifest = readCacheManifest();
|
|
439
|
-
const activeSha = manifest?.sha ?? null;
|
|
440
|
-
const activeFetchedAt = manifest?.fetchedAt ? Date.parse(manifest.fetchedAt) : NaN;
|
|
441
|
-
const removed = [];
|
|
442
|
-
let names;
|
|
443
|
-
try {
|
|
444
|
-
names = readdirSync(cacheDir);
|
|
445
|
-
}
|
|
446
|
-
catch {
|
|
447
|
-
return { removed: [] };
|
|
448
|
-
}
|
|
449
|
-
for (const name of names) {
|
|
450
|
-
const match = /^ecc-([0-9a-f]{40})$/.exec(name);
|
|
451
|
-
if (match === null)
|
|
452
|
-
continue;
|
|
453
|
-
const sha = match[1] ?? '';
|
|
454
|
-
const fullPath = join(cacheDir, name);
|
|
455
|
-
let stat;
|
|
456
|
-
try {
|
|
457
|
-
stat = statSync(fullPath);
|
|
458
|
-
}
|
|
459
|
-
catch {
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
if (!stat.isDirectory())
|
|
463
|
-
continue;
|
|
464
|
-
let ageMs;
|
|
465
|
-
if (sha === activeSha && Number.isFinite(activeFetchedAt)) {
|
|
466
|
-
ageMs = nowMs - activeFetchedAt;
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
ageMs = nowMs - stat.mtimeMs;
|
|
470
|
-
}
|
|
471
|
-
if (ageMs > retentionDays * dayMs) {
|
|
472
|
-
try {
|
|
473
|
-
rmSync(fullPath, { recursive: true, force: true });
|
|
474
|
-
removed.push(fullPath);
|
|
475
|
-
}
|
|
476
|
-
catch {
|
|
477
|
-
/* best-effort */
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
// Invalidate manifest if active cache was removed.
|
|
482
|
-
if (activeSha !== null && removed.some((p) => p.includes(`ecc-${activeSha}`))) {
|
|
483
|
-
try {
|
|
484
|
-
rmSync(resolveManifestPath(cacheDir), { force: true });
|
|
485
|
-
}
|
|
486
|
-
catch {
|
|
487
|
-
/* best-effort */
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return { removed };
|
|
491
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* peaks-perf-audit service — independent performance audit skill driver.
|
|
3
|
-
*
|
|
4
|
-
* Slice v2.12.0 (Group A, Tier 3). Symmetric to security-audit-service.ts:
|
|
5
|
-
* - 5-state detection of the perf-audit runtime
|
|
6
|
-
* - Loading + sha256 verification of the prd/handoff.md
|
|
7
|
-
* - Loading the project-level perf-template.md
|
|
8
|
-
* - Producing the audit envelope (verdict + violations) to write
|
|
9
|
-
* to `.peaks/_runtime/<sid>/audit/perf-<rid>.md`
|
|
10
|
-
*
|
|
11
|
-
* The actual LLM-in-loop audit step is NOT in this service — the
|
|
12
|
-
* parent LLM (peaks-perf-audit skill prompt) reads the template,
|
|
13
|
-
* runs the audit, and returns a structured envelope. This service
|
|
14
|
-
* is the pure I/O + validation core; the LLM is the judgement core.
|
|
15
|
-
*
|
|
16
|
-
* Cross-references:
|
|
17
|
-
* - PRD: `.peaks/_runtime/<sid>/prd/handoff.md` (sha256-locked, schemaVersion: 2)
|
|
18
|
-
* - Template: `.peaks/project-scan/perf-template.md`
|
|
19
|
-
* - Output: `.peaks/_runtime/<sid>/audit/perf-<rid>.md`
|
|
20
|
-
* - Schema: `.peaks/project-scan/audit-output-schema.md` (schemaVersion: 1)
|
|
21
|
-
* - Companion: `src/services/audit-independent/security-audit-service.ts`
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* 5-state detection result. Mirrors `SecurityAuditDetectState` and
|
|
25
|
-
* the ecc-bridge `detectEcc` shape (slice 7 Group D).
|
|
26
|
-
*
|
|
27
|
-
* - `ready` — handoff + template + project all present
|
|
28
|
-
* - `handoff-missing` — `.peaks/_runtime/<sid>/prd/handoff.md` absent
|
|
29
|
-
* - `template-missing` — `.peaks/project-scan/perf-template.md` absent
|
|
30
|
-
* - `dispatch-failed` — parent LLM threw before returning the audit envelope
|
|
31
|
-
* - `envelope-malformed` — parent LLM returned a value that fails `isPerfAuditEnvelope`
|
|
32
|
-
*/
|
|
33
|
-
export type PerfAuditDetectState = 'ready' | 'handoff-missing' | 'template-missing' | 'dispatch-failed' | 'envelope-malformed';
|
|
34
|
-
export interface PerfAuditDetectResult {
|
|
35
|
-
readonly state: PerfAuditDetectState;
|
|
36
|
-
readonly handoffPresent: boolean;
|
|
37
|
-
readonly templatePresent: boolean;
|
|
38
|
-
readonly warnings: readonly string[];
|
|
39
|
-
readonly nextActions: readonly string[];
|
|
40
|
-
}
|
|
41
|
-
export type PerfAuditVerdict = 'pass' | 'warn' | 'block';
|
|
42
|
-
export interface PerfAuditViolation {
|
|
43
|
-
readonly dimension: string;
|
|
44
|
-
readonly severity: 'CRITICAL' | 'HIGH' | 'MED' | 'LOW';
|
|
45
|
-
readonly file: string;
|
|
46
|
-
readonly line: number;
|
|
47
|
-
readonly hint: string;
|
|
48
|
-
}
|
|
49
|
-
export interface PerfAuditEnvelope {
|
|
50
|
-
readonly verdict: PerfAuditVerdict;
|
|
51
|
-
readonly violations: ReadonlyArray<PerfAuditViolation>;
|
|
52
|
-
readonly summary: string;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Validate a raw value as a PerfAuditEnvelope. Mirrors the
|
|
56
|
-
* `isSecurityAuditEnvelope` strict-shape pattern.
|
|
57
|
-
*/
|
|
58
|
-
export declare function isPerfAuditEnvelope(value: unknown): value is PerfAuditEnvelope;
|
|
59
|
-
/**
|
|
60
|
-
* Handoff frontmatter — minimal projection of peaks-prd v2.11.0 handoff
|
|
61
|
-
* frontmatter. We only need sha256 for verification; the rest of the
|
|
62
|
-
* handoff body is consumed by the parent LLM, not by this service.
|
|
63
|
-
*
|
|
64
|
-
* Symmetric to `HandoffFrontmatter` in `security-audit-service.ts`.
|
|
65
|
-
* We duplicate the type (rather than share) so the two services
|
|
66
|
-
* can be independently type-evolved.
|
|
67
|
-
*/
|
|
68
|
-
export interface HandoffFrontmatter {
|
|
69
|
-
readonly sha256: string;
|
|
70
|
-
readonly schemaVersion: number;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Read the peaks-prd handoff frontmatter + body, verify sha256 matches
|
|
74
|
-
* the body. Returns null on missing file or sha256 mismatch; otherwise
|
|
75
|
-
* returns the parsed frontmatter + body string.
|
|
76
|
-
*
|
|
77
|
-
* Defensive: we read frontmatter via a 1-line regex match, not via
|
|
78
|
-
* a YAML parser. The handoff service (`src/services/prd/handoff-service.ts`)
|
|
79
|
-
* is the canonical writer; we only consume its output here.
|
|
80
|
-
*/
|
|
81
|
-
export declare function readAndVerifyHandoff(handoffPath: string, projectRoot: string): {
|
|
82
|
-
frontmatter: HandoffFrontmatter;
|
|
83
|
-
body: string;
|
|
84
|
-
} | null;
|
|
85
|
-
/**
|
|
86
|
-
* Read the project-level perf-template.md. Returns the body
|
|
87
|
-
* string (with frontmatter stripped) or null if the file is absent.
|
|
88
|
-
* Does NOT validate the template content; the parent LLM validates
|
|
89
|
-
* by reading.
|
|
90
|
-
*/
|
|
91
|
-
export declare function readPerfTemplate(projectRoot: string): string | null;
|
|
92
|
-
/**
|
|
93
|
-
* 5-state detector. The parent skill inspects the state and either
|
|
94
|
-
* proceeds (when `ready`) or records a degradation note and falls
|
|
95
|
-
* back (when any other state).
|
|
96
|
-
*
|
|
97
|
-
* Soft-fail policy mirrors `detectEcc` (slice 7 Group D) and
|
|
98
|
-
* `detectSecurityAudit`: the CLI never throws; it returns a typed
|
|
99
|
-
* detect result.
|
|
100
|
-
*/
|
|
101
|
-
export declare function detectPerfAudit(input: {
|
|
102
|
-
readonly projectRoot: string;
|
|
103
|
-
readonly sessionId: string;
|
|
104
|
-
readonly dispatchError?: unknown;
|
|
105
|
-
readonly envelope?: unknown;
|
|
106
|
-
}): PerfAuditDetectResult;
|
|
107
|
-
/**
|
|
108
|
-
* Render a PerfAuditEnvelope to the canonical audit artifact body
|
|
109
|
-
* (markdown). Mirrors `renderSecurityAuditArtifact` in
|
|
110
|
-
* `security-audit-service.ts` and `adaptEccEnvelopeToRdCodeReview`
|
|
111
|
-
* in ecc-bridge.ts: produce a markdown body with all required
|
|
112
|
-
* sections, ready to write to
|
|
113
|
-
* `.peaks/_runtime/<sid>/audit/perf-<rid>.md`.
|
|
114
|
-
*/
|
|
115
|
-
export declare function renderPerfAuditArtifact(env: PerfAuditEnvelope, opts: {
|
|
116
|
-
rid: string;
|
|
117
|
-
handoffHash: string;
|
|
118
|
-
generatedAt: string;
|
|
119
|
-
}): {
|
|
120
|
-
body: string;
|
|
121
|
-
violationsCount: number;
|
|
122
|
-
verdict: PerfAuditVerdict;
|
|
123
|
-
};
|
|
124
|
-
/**
|
|
125
|
-
* Write the rendered audit artifact to
|
|
126
|
-
* `.peaks/_runtime/<sid>/audit/perf-<rid>.md`.
|
|
127
|
-
*
|
|
128
|
-
* Atomic write (tmp + rename) to avoid partial writes on crash.
|
|
129
|
-
* Returns the absolute path on success.
|
|
130
|
-
*/
|
|
131
|
-
export declare function writePerfAuditArtifact(projectRoot: string, sessionId: string, rid: string, body: string): string;
|
|
132
|
-
/**
|
|
133
|
-
* Convenience: run `detectPerfAudit` + `renderPerfAuditArtifact`
|
|
134
|
-
* + `writePerfAuditArtifact` in one call.
|
|
135
|
-
*
|
|
136
|
-
* Returns `{ detect, artifactPath, violationsCount, verdict }` or
|
|
137
|
-
* `{ detect, artifactPath: null, ... }` when not ready.
|
|
138
|
-
*/
|
|
139
|
-
export declare function runPerfAudit(input: {
|
|
140
|
-
readonly projectRoot: string;
|
|
141
|
-
readonly sessionId: string;
|
|
142
|
-
readonly rid: string;
|
|
143
|
-
readonly generatedAt: string;
|
|
144
|
-
readonly dispatchError?: unknown;
|
|
145
|
-
readonly envelope?: unknown;
|
|
146
|
-
}): {
|
|
147
|
-
detect: PerfAuditDetectResult;
|
|
148
|
-
artifactPath: string | null;
|
|
149
|
-
violationsCount: number;
|
|
150
|
-
verdict: PerfAuditVerdict | null;
|
|
151
|
-
};
|