peaks-loop 4.0.15 → 4.0.17
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 +82 -0
- package/README-en.md +12 -9
- package/README.md +15 -4
- package/config/eslint/.peaks-rules.cjs +47 -27
- package/dist/cli/commands/_register.js +4 -2
- package/dist/cli/commands/audit-commands.d.ts +1 -1
- package/dist/cli/commands/code-gate-command.d.ts +25 -0
- package/dist/cli/commands/code-gate-command.js +86 -0
- package/dist/cli/commands/code-mode-gate-commands.js +3 -4
- package/dist/cli/commands/code-orchestrator-can-do.js +2 -1
- package/dist/cli/commands/code-review-commands.d.ts +2 -30
- package/dist/cli/commands/code-review-commands.js +43 -65
- package/dist/cli/commands/cron-commands.js +16 -4
- package/dist/cli/commands/dashboard-long-run.js +8 -4
- package/dist/cli/commands/dispatch-from-dag.d.ts +1 -1
- package/dist/cli/commands/hooks-commands.js +56 -3
- package/dist/cli/commands/lint-commands.d.ts +21 -0
- package/dist/cli/commands/lint-commands.js +146 -0
- package/dist/cli/commands/qa-commands.js +1 -2
- package/dist/cli/commands/security-audit-commands.d.ts +1 -1
- package/dist/cli/commands/security-audit-commands.js +1 -1
- package/dist/cli/commands/sediment-commands.d.ts +1 -1
- package/dist/cli/commands/slice-commands.js +20 -8
- package/dist/cli/commands/statusline-commands.d.ts +1 -0
- package/dist/cli/commands/statusline-commands.js +17 -3
- package/dist/cli/commands/tech-commands.d.ts +1 -1
- package/dist/services/artifacts/request-artifact-service.d.ts +2 -2
- package/dist/services/artifacts/request-artifact-service.js +10 -1
- package/dist/services/audit/enforcers/lint-reference-shape.js +18 -4
- package/dist/services/code/auto-compact-orchestrator.js +9 -1
- package/dist/services/code/batch-heartbeat-poller.js +26 -0
- package/dist/services/code/orchestrator-can-do.d.ts +24 -0
- package/dist/services/code/orchestrator-can-do.js +48 -2
- package/dist/services/code/post-compact-detector.d.ts +1 -1
- package/dist/services/code-review/ecc-bridge.d.ts +2 -7
- package/dist/services/compact-statusline/compact-statusline-service.js +19 -11
- package/dist/services/config/config-service.d.ts +2 -19
- package/dist/services/config/config-service.js +2 -56
- package/dist/services/config/config-types.d.ts +4 -24
- package/dist/services/config/config-types.js +1 -10
- package/dist/services/container/container-lease.js +21 -8
- package/dist/services/context/spillover-store.d.ts +1 -1
- package/dist/services/crystallization/crystallization-types.js +20 -8
- package/dist/services/crystallization/evidence-brief-builder.js +22 -11
- package/dist/services/dispatch/dispatch-record-writer.js +231 -141
- package/dist/services/evolution/evolution-types.js +38 -21
- package/dist/services/feedback/feedback-promotion-service.js +9 -1
- package/dist/services/hooks/pre-tool-code-gate.d.ts +46 -0
- package/dist/services/hooks/pre-tool-code-gate.js +91 -0
- package/dist/services/ide/current-model-detector.js +1 -2
- package/dist/services/lint/detect-eslint.d.ts +10 -0
- package/dist/services/lint/detect-eslint.js +62 -0
- package/dist/services/lint/detect-ocr-18.d.ts +10 -0
- package/dist/services/lint/detect-ocr-18.js +40 -0
- package/dist/services/lint/eslint-runner.d.ts +57 -0
- package/dist/services/lint/eslint-runner.js +370 -0
- package/dist/services/lint/npx-resolver.d.ts +6 -0
- package/dist/services/lint/npx-resolver.js +47 -0
- package/dist/services/lint/ocr-multilang-adapter.d.ts +34 -0
- package/dist/services/lint/ocr-multilang-adapter.js +143 -0
- package/dist/services/loop/loop-release-types.js +32 -13
- package/dist/services/loop/spec-service.d.ts +7 -1
- package/dist/services/loop/spec-service.js +218 -182
- package/dist/services/rd/rd-service.js +22 -9
- package/dist/services/scan/archetype-service.js +21 -9
- package/dist/services/session/binding-store.js +1 -2
- package/dist/services/share/bundle-reader.d.ts +6 -9
- package/dist/services/share/bundle-reader.js +297 -223
- package/dist/services/skills/hooks-settings-service.d.ts +33 -0
- package/dist/services/skills/hooks-settings-service.js +27 -1
- package/dist/services/slice/calibration-store.js +21 -8
- package/dist/services/slice/slice-check-service.js +35 -14
- package/dist/services/slice/slice-decompose-service.js +459 -223
- package/dist/services/standards/project-context.js +135 -73
- package/dist/services/verdict/envelopes.d.ts +1 -2
- package/dist/services/verdict/envelopes.js +1 -2
- package/dist/services/verdict/verdict-aggregator.d.ts +1 -2
- package/dist/services/vm/vm-lease.js +19 -8
- package/dist/services/worktree/worktree-lease.d.ts +0 -10
- package/dist/services/worktree/worktree-lease.js +19 -8
- package/dist/shared/fs-utils.d.ts +1 -1
- package/package.json +8 -12
- package/skills/bee/peaks-rd/SKILL.md +1 -1
- package/skills/bee/peaks-rd/references/jsts-eslint-gate.md +197 -0
- package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +132 -0
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +1 -1
- package/skills/peaks-code/SKILL.md +47 -1
- package/dist/services/code-review/ocr-service.d.ts +0 -129
- package/dist/services/code-review/ocr-service.js +0 -372
- package/skills/bee/peaks-rd/references/ocr-integration.md +0 -229
|
@@ -54,46 +54,83 @@ function majorOf(version) {
|
|
|
54
54
|
const major = cleaned.split('.')[0];
|
|
55
55
|
return major !== undefined && /^\d+$/.test(major) ? major : undefined;
|
|
56
56
|
}
|
|
57
|
+
const LIBRARY_DETECTORS = [
|
|
58
|
+
detectAntd,
|
|
59
|
+
detectMui,
|
|
60
|
+
detectElementPlus,
|
|
61
|
+
detectElementUi,
|
|
62
|
+
detectArco,
|
|
63
|
+
detectTdesign,
|
|
64
|
+
detectSemi,
|
|
65
|
+
detectNextui,
|
|
66
|
+
detectChakra,
|
|
67
|
+
detectVant,
|
|
68
|
+
detectShadcn
|
|
69
|
+
];
|
|
57
70
|
export function detectComponentLibrary(projectRoot, deps) {
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
? { name: 'antd-pro', ...(major !== undefined ? { majorVersion: major } : {}), hasProSuite: true }
|
|
63
|
-
: { name: 'antd', ...(major !== undefined ? { majorVersion: major } : {}) };
|
|
71
|
+
for (const detector of LIBRARY_DETECTORS) {
|
|
72
|
+
const result = detector(deps, projectRoot);
|
|
73
|
+
if (result !== null)
|
|
74
|
+
return result;
|
|
64
75
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
return { name: 'none' };
|
|
77
|
+
}
|
|
78
|
+
function detectAntd(deps, _projectRoot) {
|
|
79
|
+
if (!('antd' in deps))
|
|
80
|
+
return null;
|
|
81
|
+
const major = majorOf(deps['antd']);
|
|
82
|
+
const hasProSuite = '@ant-design/pro-components' in deps
|
|
83
|
+
|| Object.keys(deps).some((d) => d.startsWith('@ant-design/pro-'));
|
|
84
|
+
return hasProSuite
|
|
85
|
+
? { name: 'antd-pro', ...(major !== undefined ? { majorVersion: major } : {}), hasProSuite: true }
|
|
86
|
+
: { name: 'antd', ...(major !== undefined ? { majorVersion: major } : {}) };
|
|
87
|
+
}
|
|
88
|
+
function detectMui(deps, _projectRoot) {
|
|
89
|
+
if (!('@mui/material' in deps))
|
|
90
|
+
return null;
|
|
91
|
+
const major = majorOf(deps['@mui/material']);
|
|
92
|
+
return { name: 'mui', ...(major !== undefined ? { majorVersion: major } : {}) };
|
|
93
|
+
}
|
|
94
|
+
function detectElementPlus(deps, _projectRoot) {
|
|
95
|
+
return 'element-plus' in deps ? { name: 'element-plus' } : null;
|
|
96
|
+
}
|
|
97
|
+
function detectElementUi(deps, _projectRoot) {
|
|
98
|
+
return 'element-ui' in deps ? { name: 'element-ui' } : null;
|
|
99
|
+
}
|
|
100
|
+
function detectArco(deps, _projectRoot) {
|
|
101
|
+
return '@arco-design/web-react' in deps ? { name: 'arco' } : null;
|
|
102
|
+
}
|
|
103
|
+
function detectTdesign(deps, _projectRoot) {
|
|
104
|
+
return ('tdesign-react' in deps || 'tdesign-vue-next' in deps) ? { name: 'tdesign' } : null;
|
|
105
|
+
}
|
|
106
|
+
function detectSemi(deps, _projectRoot) {
|
|
107
|
+
return '@douyinfe/semi-ui' in deps ? { name: 'semi' } : null;
|
|
108
|
+
}
|
|
109
|
+
function detectNextui(deps, _projectRoot) {
|
|
110
|
+
return '@nextui-org/react' in deps ? { name: 'nextui' } : null;
|
|
111
|
+
}
|
|
112
|
+
function detectChakra(deps, _projectRoot) {
|
|
113
|
+
return '@chakra-ui/react' in deps ? { name: 'chakra' } : null;
|
|
114
|
+
}
|
|
115
|
+
function detectVant(deps, _projectRoot) {
|
|
116
|
+
return 'vant' in deps ? { name: 'vant' } : null;
|
|
117
|
+
}
|
|
118
|
+
/** shadcn / ui is a Tailwind-based component primitive set shipped as
|
|
119
|
+
* copyable source files under `components/ui/`. It has no top-level
|
|
120
|
+
* package of its own, so the signal we use is: at least one of its
|
|
121
|
+
* peer utilities (cva, clsx, tailwind-merge, lucide-react) is present
|
|
122
|
+
* AND Tailwind is present (either as a dep or via a components/ui dir). */
|
|
123
|
+
function detectShadcn(deps, projectRoot) {
|
|
124
|
+
const hasPeerUtility = 'class-variance-authority' in deps
|
|
88
125
|
|| 'clsx' in deps
|
|
89
126
|
|| 'tailwind-merge' in deps
|
|
90
|
-
|| 'lucide-react' in deps
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return { name: '
|
|
127
|
+
|| 'lucide-react' in deps;
|
|
128
|
+
if (!hasPeerUtility)
|
|
129
|
+
return null;
|
|
130
|
+
const hasTailwind = 'tailwindcss' in deps
|
|
131
|
+
|| existsSync(join(projectRoot, 'components', 'ui'))
|
|
132
|
+
|| existsSync(join(projectRoot, 'src', 'components', 'ui'));
|
|
133
|
+
return hasTailwind ? { name: 'shadcn' } : null;
|
|
97
134
|
}
|
|
98
135
|
function detectCssFrameworks(projectRoot, deps) {
|
|
99
136
|
const frameworks = [];
|
|
@@ -166,6 +203,11 @@ function detectDataFetching(deps) {
|
|
|
166
203
|
}
|
|
167
204
|
function detectLegacySignals(projectRoot, deps) {
|
|
168
205
|
const signals = [];
|
|
206
|
+
collectPackageLegacySignals(deps, signals);
|
|
207
|
+
collectSourceFileLegacySignals(projectRoot, signals);
|
|
208
|
+
return signals;
|
|
209
|
+
}
|
|
210
|
+
function collectPackageLegacySignals(deps, signals) {
|
|
169
211
|
if ('moment' in deps)
|
|
170
212
|
signals.push('`moment` in deps — prefer `dayjs` or `date-fns` for new code');
|
|
171
213
|
if (Object.keys(deps).some((d) => d.startsWith('enzyme')))
|
|
@@ -180,59 +222,53 @@ function detectLegacySignals(projectRoot, deps) {
|
|
|
180
222
|
signals.push('Backbone — legacy; do not add new Backbone code');
|
|
181
223
|
if (deps['vue']?.startsWith('2') === true)
|
|
182
224
|
signals.push('Vue 2 — preserve Options API for existing components');
|
|
183
|
-
|
|
225
|
+
}
|
|
226
|
+
function collectSourceFileLegacySignals(projectRoot, signals) {
|
|
184
227
|
const srcRoot = join(projectRoot, 'src');
|
|
185
|
-
if (existsSync(srcRoot))
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
228
|
+
if (!existsSync(srcRoot))
|
|
229
|
+
return;
|
|
230
|
+
const sample = sampleSourceFiles(srcRoot, 80);
|
|
231
|
+
const { classComponentHits, inlineStyleHits } = scanSampleForLegacyMarkers(sample);
|
|
232
|
+
if (classComponentHits >= 1)
|
|
233
|
+
signals.push(`React class components detected (${classComponentHits}+ files) — keep class style for existing modules, use function components + hooks for new code`);
|
|
234
|
+
if (inlineStyleHits >= 50)
|
|
235
|
+
signals.push(`Inline styles dominant (${inlineStyleHits}+ occurrences) — match existing styling for new code in same modules`);
|
|
236
|
+
}
|
|
237
|
+
function scanSampleForLegacyMarkers(sample) {
|
|
238
|
+
let classComponentHits = 0;
|
|
239
|
+
let inlineStyleHits = 0;
|
|
240
|
+
for (const filePath of sample) {
|
|
241
|
+
try {
|
|
242
|
+
const content = readFileSync(filePath, 'utf8');
|
|
243
|
+
if (/extends\s+(?:React\.)?Component\b/.test(content))
|
|
244
|
+
classComponentHits += 1;
|
|
245
|
+
const matches = content.match(/style=\{\{/g);
|
|
246
|
+
if (matches !== null)
|
|
247
|
+
inlineStyleHits += matches.length;
|
|
248
|
+
}
|
|
249
|
+
catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
|
|
250
|
+
// ignore unreadable files
|
|
201
251
|
}
|
|
202
|
-
if (classComponentHits >= 1)
|
|
203
|
-
signals.push(`React class components detected (${classComponentHits}+ files) — keep class style for existing modules, use function components + hooks for new code`);
|
|
204
|
-
if (inlineStyleHits >= 50)
|
|
205
|
-
signals.push(`Inline styles dominant (${inlineStyleHits}+ occurrences) — match existing styling for new code in same modules`);
|
|
206
252
|
}
|
|
207
|
-
return
|
|
253
|
+
return { classComponentHits, inlineStyleHits };
|
|
208
254
|
}
|
|
209
255
|
function sampleSourceFiles(root, limit) {
|
|
210
256
|
const out = [];
|
|
211
257
|
const queue = [root];
|
|
212
258
|
while (queue.length > 0 && out.length < limit) {
|
|
213
259
|
const dir = queue.shift();
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
entries = readdirSync(dir);
|
|
217
|
-
}
|
|
218
|
-
catch {
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
260
|
+
const entries = readDirOrEmpty(dir);
|
|
221
261
|
for (const entry of entries) {
|
|
222
|
-
if (
|
|
262
|
+
if (isIgnoredEntry(entry))
|
|
223
263
|
continue;
|
|
224
264
|
const full = join(dir, entry);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
s = statSync(full);
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
265
|
+
const s = statOrNull(full);
|
|
266
|
+
if (s === null || s === undefined)
|
|
230
267
|
continue;
|
|
231
|
-
}
|
|
232
268
|
if (s.isDirectory()) {
|
|
233
269
|
queue.push(full);
|
|
234
270
|
}
|
|
235
|
-
else if (
|
|
271
|
+
else if (isJsxFile(entry)) {
|
|
236
272
|
out.push(full);
|
|
237
273
|
if (out.length >= limit)
|
|
238
274
|
break;
|
|
@@ -241,6 +277,32 @@ function sampleSourceFiles(root, limit) {
|
|
|
241
277
|
}
|
|
242
278
|
return out;
|
|
243
279
|
}
|
|
280
|
+
function readDirOrEmpty(dir) {
|
|
281
|
+
try {
|
|
282
|
+
return readdirSync(dir);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function statOrNull(path) {
|
|
289
|
+
try {
|
|
290
|
+
return statSync(path);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function isIgnoredEntry(entry) {
|
|
297
|
+
if (entry.startsWith('.'))
|
|
298
|
+
return true;
|
|
299
|
+
if (entry === 'node_modules' || entry === 'dist' || entry === 'build')
|
|
300
|
+
return true;
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
function isJsxFile(entry) {
|
|
304
|
+
return /\.(tsx|jsx)$/.test(entry);
|
|
305
|
+
}
|
|
244
306
|
function notableDepsList(deps) {
|
|
245
307
|
const interesting = ['monaco-editor', '@monaco-editor/react', 'react-querybuilder', '@dnd-kit/core', 'react-dnd', 'echarts', 'recharts', '@ant-design/charts', 'antd-style', 'lodash', 'lodash-es', 'rxjs', 'socket.io-client'];
|
|
246
308
|
return interesting.filter((d) => d in deps);
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
* "missing → null" contract). Callers can detect "envelope missing"
|
|
24
24
|
* without throwing.
|
|
25
25
|
*/
|
|
26
|
-
import { type SecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
27
|
-
import { type PerfAuditEnvelope } from '../audit-independent/index.js';
|
|
26
|
+
import { type PerfAuditEnvelope, type SecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
28
27
|
import { type KarpathyEnvelope, type MutEnvelope, type QaEnvelope, type AggregatorInput } from './verdict-aggregator.js';
|
|
29
28
|
/**
|
|
30
29
|
* `kind` is the discriminator. The on-disk file format is unchanged;
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
* "missing → null" contract). Callers can detect "envelope missing"
|
|
24
24
|
* without throwing.
|
|
25
25
|
*/
|
|
26
|
-
import { isSecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
27
|
-
import { isPerfAuditEnvelope } from '../audit-independent/index.js';
|
|
26
|
+
import { isPerfAuditEnvelope, isSecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
28
27
|
// ─── 5 parsers (pure, never throw) ─────────────────────────────────────
|
|
29
28
|
/**
|
|
30
29
|
* v2.13.3 AC-1 — parse real v2.12.0 audit markdown.
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
* SecurityAuditEnvelope / PerfAuditEnvelope type re-exports.
|
|
28
28
|
* - hard precedence; no scoring, no weighting, no RFC voting.
|
|
29
29
|
*/
|
|
30
|
-
import type { SecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
31
|
-
import type { PerfAuditEnvelope } from '../audit-independent/index.js';
|
|
30
|
+
import type { PerfAuditEnvelope, SecurityAuditEnvelope } from '../audit-independent/index.js';
|
|
32
31
|
import type { ThresholdViolation } from 'peaks-loop-mut';
|
|
33
32
|
export interface KarpathyViolation {
|
|
34
33
|
readonly guideline: 'think-before-coding' | 'simplicity-first' | 'surgical-changes' | 'goal-driven-execution';
|
|
@@ -24,13 +24,24 @@ import { normalizePath } from '../../shared/path-utils.js';
|
|
|
24
24
|
export const VM_HYPERVISORS = Object.freeze([
|
|
25
25
|
'kvm', 'hyperkit', 'hyperv'
|
|
26
26
|
]);
|
|
27
|
+
// PRD-002b slice 2 — extract lease-TTL primitives (same shape as
|
|
28
|
+
// container-lease.ts / worktree-lease.ts). See config diff comment.
|
|
29
|
+
const MINUTES_PER_HOUR = 60;
|
|
30
|
+
const MS_PER_MINUTE = MINUTES_PER_HOUR * 1_000;
|
|
31
|
+
const LEASE_RD_TTL_MINUTES = 30;
|
|
32
|
+
const LEASE_QA_TTL_MINUTES = 15;
|
|
33
|
+
const LEASE_UI_TTL_MINUTES = MINUTES_PER_HOUR;
|
|
34
|
+
const LEASE_SC_TTL_MINUTES = 30;
|
|
35
|
+
const LEASE_PRD_TTL_MINUTES = 15;
|
|
36
|
+
const LEASE_GENERAL_TTL_MINUTES = 30;
|
|
37
|
+
const LEASE_ID_RANDOM_BYTES = 8;
|
|
27
38
|
export const DEFAULT_TTL_BY_ROLE = Object.freeze({
|
|
28
|
-
rd:
|
|
29
|
-
qa:
|
|
30
|
-
ui:
|
|
31
|
-
sc:
|
|
32
|
-
prd:
|
|
33
|
-
general:
|
|
39
|
+
rd: LEASE_RD_TTL_MINUTES * MS_PER_MINUTE,
|
|
40
|
+
qa: LEASE_QA_TTL_MINUTES * MS_PER_MINUTE,
|
|
41
|
+
ui: LEASE_UI_TTL_MINUTES * MS_PER_MINUTE,
|
|
42
|
+
sc: LEASE_SC_TTL_MINUTES * MS_PER_MINUTE,
|
|
43
|
+
prd: LEASE_PRD_TTL_MINUTES * MS_PER_MINUTE,
|
|
44
|
+
general: LEASE_GENERAL_TTL_MINUTES * MS_PER_MINUTE
|
|
34
45
|
});
|
|
35
46
|
export const DEFAULT_TTL_MS = DEFAULT_TTL_BY_ROLE.rd;
|
|
36
47
|
export const DEFAULT_VM_IMAGE = 'peaks-base:22-slim';
|
|
@@ -41,12 +52,12 @@ export function vmLeaseFilePath(sessionRuntimeDir, leaseId) {
|
|
|
41
52
|
return joinPath(vmLeaseStoreDir(sessionRuntimeDir), `${leaseId}.json`);
|
|
42
53
|
}
|
|
43
54
|
export function generateVmLeaseId() {
|
|
44
|
-
return randomBytes(
|
|
55
|
+
return randomBytes(LEASE_ID_RANDOM_BYTES).toString('hex');
|
|
45
56
|
}
|
|
46
57
|
export function ttlForVmRole(role) {
|
|
47
58
|
const normalized = role.toLowerCase();
|
|
48
59
|
const candidate = DEFAULT_TTL_BY_ROLE[normalized];
|
|
49
|
-
const fallback = DEFAULT_TTL_BY_ROLE['rd'] ??
|
|
60
|
+
const fallback = DEFAULT_TTL_BY_ROLE['rd'] ?? LEASE_RD_TTL_MINUTES * MS_PER_MINUTE;
|
|
50
61
|
return candidate ?? fallback;
|
|
51
62
|
}
|
|
52
63
|
export function finalizeVmLease(draft) {
|
|
@@ -24,16 +24,6 @@
|
|
|
24
24
|
* implicitly authorizes worktree operations on its path (the hook
|
|
25
25
|
* integration is Part 2 of this slice).
|
|
26
26
|
*/
|
|
27
|
-
/**
|
|
28
|
-
* Per-role default TTL. Sub-agent dispatch duration varies by role:
|
|
29
|
-
* - rd (long planning + impl) → 30 min
|
|
30
|
-
* - qa (test design + browser validation) → 15 min
|
|
31
|
-
* - ui (high-fidelity prototype iteration) → 1 h
|
|
32
|
-
* - txt (handoff capsule, no worktree expected) → n/a (no worktree)
|
|
33
|
-
* - general-purpose / unknown → 30 min (rd-like)
|
|
34
|
-
*
|
|
35
|
-
* Users MAY override with `--ttl <duration>` (e.g. `--ttl 4h`).
|
|
36
|
-
*/
|
|
37
27
|
export declare const DEFAULT_TTL_BY_ROLE: Readonly<Record<string, number>>;
|
|
38
28
|
export declare const DEFAULT_TTL_MS: number | undefined;
|
|
39
29
|
export type WorktreeLeaseStatus = 'active' | 'released' | 'expired' | 'gc';
|
|
@@ -37,13 +37,24 @@ import { normalizePath } from '../../shared/path-utils.js';
|
|
|
37
37
|
*
|
|
38
38
|
* Users MAY override with `--ttl <duration>` (e.g. `--ttl 4h`).
|
|
39
39
|
*/
|
|
40
|
+
// PRD-002b slice 2 — extract lease-TTL primitives (same shape as
|
|
41
|
+
// container-lease.ts / vm-lease.ts). See config diff comment.
|
|
42
|
+
const MINUTES_PER_HOUR = 60;
|
|
43
|
+
const MS_PER_MINUTE = MINUTES_PER_HOUR * 1_000;
|
|
44
|
+
const LEASE_RD_TTL_MINUTES = 30;
|
|
45
|
+
const LEASE_QA_TTL_MINUTES = 15;
|
|
46
|
+
const LEASE_UI_TTL_MINUTES = MINUTES_PER_HOUR;
|
|
47
|
+
const LEASE_SC_TTL_MINUTES = 30;
|
|
48
|
+
const LEASE_PRD_TTL_MINUTES = 15;
|
|
49
|
+
const LEASE_GENERAL_TTL_MINUTES = 30;
|
|
50
|
+
const LEASE_ID_RANDOM_BYTES = 8;
|
|
40
51
|
export const DEFAULT_TTL_BY_ROLE = Object.freeze({
|
|
41
|
-
rd:
|
|
42
|
-
qa:
|
|
43
|
-
ui:
|
|
44
|
-
sc:
|
|
45
|
-
prd:
|
|
46
|
-
general:
|
|
52
|
+
rd: LEASE_RD_TTL_MINUTES * MS_PER_MINUTE,
|
|
53
|
+
qa: LEASE_QA_TTL_MINUTES * MS_PER_MINUTE,
|
|
54
|
+
ui: LEASE_UI_TTL_MINUTES * MS_PER_MINUTE,
|
|
55
|
+
sc: LEASE_SC_TTL_MINUTES * MS_PER_MINUTE,
|
|
56
|
+
prd: LEASE_PRD_TTL_MINUTES * MS_PER_MINUTE,
|
|
57
|
+
general: LEASE_GENERAL_TTL_MINUTES * MS_PER_MINUTE
|
|
47
58
|
});
|
|
48
59
|
export const DEFAULT_TTL_MS = DEFAULT_TTL_BY_ROLE.rd;
|
|
49
60
|
/**
|
|
@@ -76,7 +87,7 @@ export function worktreePath(sessionRuntimeDir, leaseId) {
|
|
|
76
87
|
* are irrelevant because each session has its own lease directory.
|
|
77
88
|
*/
|
|
78
89
|
export function generateLeaseId() {
|
|
79
|
-
return randomBytes(
|
|
90
|
+
return randomBytes(LEASE_ID_RANDOM_BYTES).toString('hex');
|
|
80
91
|
}
|
|
81
92
|
/**
|
|
82
93
|
* Compute the TTL ms for a role, falling back to DEFAULT_TTL_MS for
|
|
@@ -86,7 +97,7 @@ export function generateLeaseId() {
|
|
|
86
97
|
export function ttlForRole(role) {
|
|
87
98
|
const normalized = role.toLowerCase();
|
|
88
99
|
const candidate = DEFAULT_TTL_BY_ROLE[normalized];
|
|
89
|
-
const fallback = DEFAULT_TTL_BY_ROLE['rd'] ??
|
|
100
|
+
const fallback = DEFAULT_TTL_BY_ROLE['rd'] ?? LEASE_RD_TTL_MINUTES * MS_PER_MINUTE;
|
|
90
101
|
return candidate ?? fallback;
|
|
91
102
|
}
|
|
92
103
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Platform } from './platform.js';
|
|
2
2
|
export declare function getDirectoryLinkType(targetPlatform?: Platform): 'junction' | 'dir';
|
|
3
3
|
export declare function createDirectoryLinkSync(target: string, linkPath: string): void;
|
|
4
4
|
export declare function readDirectoryLinkTarget(linkPath: string): string | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-loop",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.17",
|
|
4
4
|
"description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"keywords": [
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"node": ">=20.0.0"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
+
"@alibaba-group/open-code-review": "^1.8.9",
|
|
96
97
|
"@colbymchenry/codegraph": "0.7.10",
|
|
97
98
|
"better-sqlite3": "^12.11.1",
|
|
98
99
|
"commander": "^12.1.0",
|
|
@@ -100,17 +101,9 @@
|
|
|
100
101
|
"headroom-ai": "0.22.4",
|
|
101
102
|
"yaml": "^2.9.0",
|
|
102
103
|
"zod": "^3.25.76",
|
|
103
|
-
"peaks-loop-mut": "0.1.
|
|
104
|
-
"peaks-loop-shared-channel": "0.0.
|
|
105
|
-
"peaks-loop-shared": "0.0.
|
|
106
|
-
},
|
|
107
|
-
"peerDependencies": {
|
|
108
|
-
"@alibaba-group/open-code-review": "1.3.1"
|
|
109
|
-
},
|
|
110
|
-
"peerDependenciesMeta": {
|
|
111
|
-
"@alibaba-group/open-code-review": {
|
|
112
|
-
"optional": true
|
|
113
|
-
}
|
|
104
|
+
"peaks-loop-mut": "0.1.20",
|
|
105
|
+
"peaks-loop-shared-channel": "0.0.24",
|
|
106
|
+
"peaks-loop-shared": "0.0.48"
|
|
114
107
|
},
|
|
115
108
|
"devDependencies": {
|
|
116
109
|
"@changesets/cli": "2.31.1",
|
|
@@ -120,9 +113,12 @@
|
|
|
120
113
|
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
121
114
|
"@types/better-sqlite3": "^7.6.13",
|
|
122
115
|
"@types/node": "^22.10.2",
|
|
116
|
+
"@typescript-eslint/eslint-plugin": "8.66.0",
|
|
117
|
+
"@typescript-eslint/parser": "8.66.0",
|
|
123
118
|
"@vitest/coverage-istanbul": "^4.1.10",
|
|
124
119
|
"@vitest/coverage-v8": "^4.1.10",
|
|
125
120
|
"c8": "^10.1.3",
|
|
121
|
+
"eslint": "8.57.1",
|
|
126
122
|
"tsx": "^4.19.2",
|
|
127
123
|
"typescript": "^5.7.2",
|
|
128
124
|
"vite": "^6.0.0",
|
|
@@ -84,7 +84,7 @@ See `references/rd-runbook.md` for the full 9-step runbook (steps #0–#8) with
|
|
|
84
84
|
|
|
85
85
|
You cannot declare a phase complete from memory. Each gate below is a `ls` or `grep` command you **MUST run** and whose output you **MUST see** before proceeding. CLI enforcement: the gates are ALSO enforced by `peaks request transition`, which fails with `code: PREREQUISITES_MISSING` if any are absent.
|
|
86
86
|
|
|
87
|
-
Gate index: A (project-scan), A2 (tech-doc path verification), A3 (CLAUDE.md + .claude/rules), B (tech-doc + request artifact), B2 (unit tests on changed surface), B3 (code-review.md), B4 (security-review.md), B5 (lint — no unfilled placeholders), B6 (request-type-sanity), B7 (repair-status — 3-cycle cap), B8 (diff-vs-scope), B9 (perf-baseline).
|
|
87
|
+
Gate index: A (project-scan), A2 (tech-doc path verification), A3 (CLAUDE.md + .claude/rules), B (tech-doc + request artifact), B2 (unit tests on changed surface), B3 (code-review.md), B4 (security-review.md), B5 (lint — no unfilled placeholders + JS/TS ESLint 卡控 via `peaks lint check`. First-time project setup: run `peaks lint baseline` to generate the project-level `.peaks/lint/baseline.json`. Per-slice flow uses `peaks lint check` (= diffOnly: true + baseline waiver + redLineMode: 'baseline-aware'). LLM red-line reference lives at `.peaks/memory/lint-redline-summary.md` (auto-written by `peaks lint check --red-line`); read it BEFORE writing JS/TS so the next slice sees prior sibling-violation context. Project-aware baseline: each project regenerates its own baseline; cross-project reuse is low-value — see `references/jsts-eslint-gate.md` §7), B6 (request-type-sanity), B7 (repair-status — 3-cycle cap), B8 (diff-vs-scope), B9 (perf-baseline).
|
|
88
88
|
|
|
89
89
|
→ see `references/rd-transition-gates.md` for the per-gate contract + `ls` / `grep` shell snippets.
|
|
90
90
|
|