peaks-cli 1.4.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +51 -0
- package/CHANGELOG.md +238 -0
- package/README-en.md +226 -0
- package/README.md +142 -165
- package/dist/src/cli/commands/agent-commands.d.ts +20 -0
- package/dist/src/cli/commands/agent-commands.js +48 -0
- package/dist/src/cli/commands/audit-commands.d.ts +18 -0
- package/dist/src/cli/commands/audit-commands.js +138 -0
- package/dist/src/cli/commands/classify-classify-commands.d.ts +19 -0
- package/dist/src/cli/commands/classify-classify-commands.js +151 -0
- package/dist/src/cli/commands/code-review-commands.d.ts +34 -0
- package/dist/src/cli/commands/code-review-commands.js +83 -0
- package/dist/src/cli/commands/config-commands.js +90 -0
- package/dist/src/cli/commands/context-commands.d.ts +21 -0
- package/dist/src/cli/commands/context-commands.js +167 -0
- package/dist/src/cli/commands/core-artifact-commands.js +81 -2
- package/dist/src/cli/commands/hook-handle.js +50 -0
- package/dist/src/cli/commands/loop-commands.d.ts +21 -0
- package/dist/src/cli/commands/loop-commands.js +128 -0
- package/dist/src/cli/commands/memory-commands.d.ts +13 -0
- package/dist/src/cli/commands/memory-commands.js +60 -0
- package/dist/src/cli/commands/openspec-commands.js +37 -0
- package/dist/src/cli/commands/preferences-commands.d.ts +2 -0
- package/dist/src/cli/commands/preferences-commands.js +147 -0
- package/dist/src/cli/commands/retrospective-commands.d.ts +9 -0
- package/dist/src/cli/commands/retrospective-commands.js +58 -0
- package/dist/src/cli/commands/skill-conformance-commands.d.ts +9 -0
- package/dist/src/cli/commands/skill-conformance-commands.js +39 -0
- package/dist/src/cli/commands/understand-commands.js +34 -0
- package/dist/src/cli/commands/upgrade-commands.d.ts +23 -0
- package/dist/src/cli/commands/upgrade-commands.js +57 -0
- package/dist/src/cli/commands/workflow-commands.js +70 -0
- package/dist/src/cli/commands/workspace-commands.js +86 -0
- package/dist/src/cli/program.js +46 -22
- package/dist/src/services/agent/ecc-agent-service.d.ts +47 -0
- package/dist/src/services/agent/ecc-agent-service.js +143 -0
- package/dist/src/services/artifacts/request-artifact-service.js +14 -0
- package/dist/src/services/audit/backing-detector.d.ts +24 -0
- package/dist/src/services/audit/backing-detector.js +59 -0
- package/dist/src/services/audit/classifier.d.ts +38 -0
- package/dist/src/services/audit/classifier.js +127 -0
- package/dist/src/services/audit/enforcers/active-skill-resolver.d.ts +29 -0
- package/dist/src/services/audit/enforcers/active-skill-resolver.js +71 -0
- package/dist/src/services/audit/enforcers/design-draft-confirm.d.ts +25 -0
- package/dist/src/services/audit/enforcers/design-draft-confirm.js +54 -0
- package/dist/src/services/audit/enforcers/lint-audit-regression.d.ts +21 -0
- package/dist/src/services/audit/enforcers/lint-audit-regression.js +86 -0
- package/dist/src/services/audit/enforcers/lint-catalog-governance.d.ts +27 -0
- package/dist/src/services/audit/enforcers/lint-catalog-governance.js +38 -0
- package/dist/src/services/audit/enforcers/lint-cli-back.d.ts +16 -0
- package/dist/src/services/audit/enforcers/lint-cli-back.js +35 -0
- package/dist/src/services/audit/enforcers/lint-output-style.d.ts +11 -0
- package/dist/src/services/audit/enforcers/lint-output-style.js +94 -0
- package/dist/src/services/audit/enforcers/lint-reference-integrity.d.ts +6 -0
- package/dist/src/services/audit/enforcers/lint-reference-integrity.js +83 -0
- package/dist/src/services/audit/enforcers/lint-reference-shape.d.ts +30 -0
- package/dist/src/services/audit/enforcers/lint-reference-shape.js +272 -0
- package/dist/src/services/audit/enforcers/lint-style.d.ts +49 -0
- package/dist/src/services/audit/enforcers/lint-style.js +173 -0
- package/dist/src/services/audit/enforcers/lint-workflow-shape.d.ts +5 -0
- package/dist/src/services/audit/enforcers/lint-workflow-shape.js +141 -0
- package/dist/src/services/audit/enforcers/login-gate.d.ts +23 -0
- package/dist/src/services/audit/enforcers/login-gate.js +40 -0
- package/dist/src/services/audit/enforcers/mock-placement.d.ts +25 -0
- package/dist/src/services/audit/enforcers/mock-placement.js +48 -0
- package/dist/src/services/audit/enforcers/no-root-pollution.d.ts +21 -0
- package/dist/src/services/audit/enforcers/no-root-pollution.js +56 -0
- package/dist/src/services/audit/enforcers/pre-rd-scan.d.ts +22 -0
- package/dist/src/services/audit/enforcers/pre-rd-scan.js +23 -0
- package/dist/src/services/audit/enforcers/prototype-fidelity.d.ts +25 -0
- package/dist/src/services/audit/enforcers/prototype-fidelity.js +75 -0
- package/dist/src/services/audit/enforcers/resume-detection.d.ts +21 -0
- package/dist/src/services/audit/enforcers/resume-detection.js +52 -0
- package/dist/src/services/audit/enforcers/solo-code-ban.d.ts +23 -0
- package/dist/src/services/audit/enforcers/solo-code-ban.js +27 -0
- package/dist/src/services/audit/enforcers/sub-agent-sid.d.ts +25 -0
- package/dist/src/services/audit/enforcers/sub-agent-sid.js +63 -0
- package/dist/src/services/audit/enforcers/tech-doc-presence.d.ts +28 -0
- package/dist/src/services/audit/enforcers/tech-doc-presence.js +35 -0
- package/dist/src/services/audit/red-line-catalog-p2-a.d.ts +21 -0
- package/dist/src/services/audit/red-line-catalog-p2-a.js +233 -0
- package/dist/src/services/audit/red-line-catalog-p2-b.d.ts +19 -0
- package/dist/src/services/audit/red-line-catalog-p2-b.js +225 -0
- package/dist/src/services/audit/red-line-catalog.d.ts +51 -0
- package/dist/src/services/audit/red-line-catalog.js +210 -0
- package/dist/src/services/audit/red-lines-service.d.ts +23 -0
- package/dist/src/services/audit/red-lines-service.js +486 -0
- package/dist/src/services/audit/scanners/openspec-scanner.d.ts +15 -0
- package/dist/src/services/audit/scanners/openspec-scanner.js +55 -0
- package/dist/src/services/audit/scanners/rules-tree-scanner.d.ts +16 -0
- package/dist/src/services/audit/scanners/rules-tree-scanner.js +56 -0
- package/dist/src/services/audit/scanners/skills-tree-scanner.d.ts +17 -0
- package/dist/src/services/audit/scanners/skills-tree-scanner.js +46 -0
- package/dist/src/services/audit/static-service.d.ts +57 -0
- package/dist/src/services/audit/static-service.js +125 -0
- package/dist/src/services/audit/types.d.ts +69 -0
- package/dist/src/services/audit/types.js +13 -0
- package/dist/src/services/classify/classify-service.d.ts +42 -0
- package/dist/src/services/classify/classify-service.js +122 -0
- package/dist/src/services/classify/classify-types.d.ts +79 -0
- package/dist/src/services/classify/classify-types.js +90 -0
- package/dist/src/services/code-review/ocr-service.d.ts +129 -0
- package/dist/src/services/code-review/ocr-service.js +362 -0
- package/dist/src/services/config/config-migration.d.ts +32 -0
- package/dist/src/services/config/config-migration.js +92 -0
- package/dist/src/services/config/config-restore.d.ts +10 -0
- package/dist/src/services/config/config-restore.js +47 -0
- package/dist/src/services/config/config-rollback.d.ts +13 -0
- package/dist/src/services/config/config-rollback.js +26 -0
- package/dist/src/services/config/config-service.d.ts +35 -2
- package/dist/src/services/config/config-service.js +81 -0
- package/dist/src/services/config/config-types.d.ts +58 -0
- package/dist/src/services/config/config-types.js +6 -0
- package/dist/src/services/doctor/doctor-service.js +96 -0
- package/dist/src/services/fuzzy-matching/fuzzy-match-service.d.ts +15 -0
- package/dist/src/services/fuzzy-matching/fuzzy-match-service.js +56 -0
- package/dist/src/services/fuzzy-matching/types.d.ts +20 -0
- package/dist/src/services/fuzzy-matching/types.js +1 -0
- package/dist/src/services/ide/adapters/hermes-adapter.d.ts +21 -0
- package/dist/src/services/ide/adapters/hermes-adapter.js +51 -0
- package/dist/src/services/ide/adapters/openclaw-adapter.d.ts +14 -0
- package/dist/src/services/ide/adapters/openclaw-adapter.js +42 -0
- package/dist/src/services/ide/ide-registry.js +7 -0
- package/dist/src/services/ide/ide-types.d.ts +1 -1
- package/dist/src/services/memory/memory-search-service.d.ts +61 -0
- package/dist/src/services/memory/memory-search-service.js +80 -0
- package/dist/src/services/openspec/openspec-propose-from-doctor-service.d.ts +31 -0
- package/dist/src/services/openspec/openspec-propose-from-doctor-service.js +95 -0
- package/dist/src/services/preferences/preferences-service.d.ts +6 -0
- package/dist/src/services/preferences/preferences-service.js +43 -0
- package/dist/src/services/preferences/preferences-types.d.ts +90 -0
- package/dist/src/services/preferences/preferences-types.js +38 -0
- package/dist/src/services/recommendations/capability-seed-items.js +0 -1
- package/dist/src/services/recommendations/capability-seed-mappings.js +0 -1
- package/dist/src/services/recommendations/capability-seed-sources.js +0 -1
- package/dist/src/services/retrospective/retrospective-search-service.d.ts +37 -0
- package/dist/src/services/retrospective/retrospective-search-service.js +75 -0
- package/dist/src/services/skills/skill-conformance-service.d.ts +40 -0
- package/dist/src/services/skills/skill-conformance-service.js +136 -0
- package/dist/src/services/skills/skill-runbook-service.js +44 -10
- package/dist/src/services/skills/sync-service.d.ts +43 -0
- package/dist/src/services/skills/sync-service.js +99 -0
- package/dist/src/services/slice/slice-check-service.js +166 -13
- package/dist/src/services/slice/slice-check-types.d.ts +1 -1
- package/dist/src/services/standards/migrate-claude-rules-service.d.ts +19 -0
- package/dist/src/services/standards/migrate-claude-rules-service.js +193 -0
- package/dist/src/services/standards/project-context.d.ts +1 -1
- package/dist/src/services/standards/project-context.js +0 -4
- package/dist/src/services/standards/project-standards-service.js +1 -3
- package/dist/src/services/understand/understand-scan-service.js +15 -2
- package/dist/src/services/understand/understand-types.d.ts +26 -0
- package/dist/src/services/upgrade/1x-detector-service.d.ts +7 -0
- package/dist/src/services/upgrade/1x-detector-service.js +94 -0
- package/dist/src/services/upgrade/gitignore-migrate-service.d.ts +56 -0
- package/dist/src/services/upgrade/gitignore-migrate-service.js +170 -0
- package/dist/src/services/upgrade/upgrade-service.d.ts +47 -0
- package/dist/src/services/upgrade/upgrade-service.js +381 -0
- package/dist/src/services/workspace/migrate-1-4-1-service.js +1 -1
- package/dist/src/services/workspace/sid-naming-guard.d.ts +14 -0
- package/dist/src/services/workspace/sid-naming-guard.js +31 -0
- package/dist/src/services/workspace/workspace-archive-service.d.ts +19 -0
- package/dist/src/services/workspace/workspace-archive-service.js +32 -0
- package/dist/src/services/workspace/workspace-clean-service.d.ts +41 -0
- package/dist/src/services/workspace/workspace-clean-service.js +86 -0
- package/dist/src/services/workspace/workspace-state-service.d.ts +7 -0
- package/dist/src/services/workspace/workspace-state-service.js +43 -0
- package/dist/src/shared/change-id.js +4 -1
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +10 -8
- package/schemas/doctor-report.schema.json +1 -1
- package/scripts/install-skills.mjs +296 -12
- package/skills/peaks-doctor/SKILL.md +59 -0
- package/skills/peaks-doctor/references/doctor-check-catalog.md +31 -0
- package/skills/peaks-doctor/references/from-doctor-flow.md +64 -0
- package/skills/peaks-doctor/test_prompts.json +17 -0
- package/skills/peaks-ide/SKILL.md +2 -0
- package/skills/peaks-qa/SKILL.md +9 -7
- package/skills/peaks-qa/references/artifact-per-request.md +19 -5
- package/skills/peaks-qa/references/qa-perf-test-plan.md +6 -6
- package/skills/peaks-qa/references/qa-runbook.md +1 -1
- package/skills/peaks-rd/SKILL.md +25 -10
- package/skills/peaks-rd/references/ocr-integration.md +214 -0
- package/skills/peaks-rd/references/rd-fanout-contracts.md +70 -0
- package/skills/peaks-rd/references/rd-runbook.md +1 -1
- package/skills/peaks-solo/SKILL.md +11 -5
- package/skills/peaks-solo/references/completion-handoff.md +3 -1
- package/skills/peaks-solo/references/step-0-55-1x-detection.md +82 -0
- package/skills/peaks-solo/references/workflow-gates-and-types.md +9 -0
- package/dist/src/cli/commands/shadcn-commands.d.ts +0 -3
- package/dist/src/cli/commands/shadcn-commands.js +0 -35
- package/dist/src/cli/commands/skill-context-stats-command.d.ts +0 -40
- package/dist/src/cli/commands/skill-context-stats-command.js +0 -96
- package/dist/src/cli/commands/skill-scope-commands.d.ts +0 -51
- package/dist/src/cli/commands/skill-scope-commands.js +0 -310
- package/dist/src/services/shadcn/shadcn-service.d.ts +0 -27
- package/dist/src/services/shadcn/shadcn-service.js +0 -128
- package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +0 -39
- package/dist/src/services/skill-scope/adapters/_stub-helper.js +0 -98
- package/dist/src/services/skill-scope/adapters/claude-code.d.ts +0 -59
- package/dist/src/services/skill-scope/adapters/claude-code.js +0 -304
- package/dist/src/services/skill-scope/adapters/codex.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/codex.js +0 -12
- package/dist/src/services/skill-scope/adapters/cursor.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/cursor.js +0 -13
- package/dist/src/services/skill-scope/adapters/qoder.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/qoder.js +0 -13
- package/dist/src/services/skill-scope/adapters/tongyi.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/tongyi.js +0 -13
- package/dist/src/services/skill-scope/adapters/trae.d.ts +0 -2
- package/dist/src/services/skill-scope/adapters/trae.js +0 -12
- package/dist/src/services/skill-scope/detect.d.ts +0 -81
- package/dist/src/services/skill-scope/detect.js +0 -513
- package/dist/src/services/skill-scope/registry.d.ts +0 -41
- package/dist/src/services/skill-scope/registry.js +0 -83
- package/dist/src/services/skill-scope/source-of-truth.d.ts +0 -44
- package/dist/src/services/skill-scope/source-of-truth.js +0 -118
- package/dist/src/services/skill-scope/types.d.ts +0 -195
- package/dist/src/services/skill-scope/types.js +0 -97
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2-b Theme L — Audit regression enforcers.
|
|
3
|
+
*
|
|
4
|
+
* Slice #7 L2.4. These four enforcers run during the audit
|
|
5
|
+
* and check the audit framework's own integrity. They are
|
|
6
|
+
* the "gating" layer that `peaks slice check`'s new
|
|
7
|
+
* audit-regression stage asserts.
|
|
8
|
+
*
|
|
9
|
+
* Four checks:
|
|
10
|
+
* 1. catalog-stability — catalog size has not grown >20% in 90 days
|
|
11
|
+
* 2. no-orphan-enforcer — every enforcerRef points to a real file
|
|
12
|
+
* 3. no-orphan-catalog — every catalog entry has a non-null enforcerRef
|
|
13
|
+
* 4. runtime-budget — audit completes in < 2s on a 100-reference project
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { RED_LINE_CATALOG } from '../red-line-catalog.js';
|
|
18
|
+
export const CATALOG_STABILITY_GROWTH_CAP = 0.20;
|
|
19
|
+
export const CATALOG_STABILITY_WINDOW_DAYS = 90;
|
|
20
|
+
export const RUNTIME_BUDGET_MS = 2000;
|
|
21
|
+
const fakeCatalogPath = 'src/services/audit/red-line-catalog.ts';
|
|
22
|
+
function syntheticHit(catalogId, rule, detail) {
|
|
23
|
+
return {
|
|
24
|
+
catalogId,
|
|
25
|
+
rule,
|
|
26
|
+
file: fakeCatalogPath,
|
|
27
|
+
line: 1,
|
|
28
|
+
matchedText: detail,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function lintCatalogStability(input) {
|
|
32
|
+
if (input.sizeNinetyDaysAgo === null || input.sizeNinetyDaysAgo === 0) {
|
|
33
|
+
// No historical data — can't check stability. Treat as a
|
|
34
|
+
// soft pass (the catalog-stability enforcer is
|
|
35
|
+
// non-blocking when the data is missing).
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const growth = (input.currentSize - input.sizeNinetyDaysAgo) / input.sizeNinetyDaysAgo;
|
|
39
|
+
if (growth <= CATALOG_STABILITY_GROWTH_CAP)
|
|
40
|
+
return [];
|
|
41
|
+
return [syntheticHit('rl-audit-catalog-stability-001', 'catalog size has not grown > 20% in the last 90 days', `(growth ${(growth * 100).toFixed(1)}% over 90 days; currentSize=${input.currentSize}, priorSize=${input.sizeNinetyDaysAgo})`)];
|
|
42
|
+
}
|
|
43
|
+
export function lintNoOrphanEnforcer(projectRoot) {
|
|
44
|
+
const hits = [];
|
|
45
|
+
for (const entry of RED_LINE_CATALOG) {
|
|
46
|
+
if (!entry.enforcerRef)
|
|
47
|
+
continue;
|
|
48
|
+
const absPath = join(projectRoot, entry.enforcerRef);
|
|
49
|
+
if (!existsSync(absPath)) {
|
|
50
|
+
hits.push(syntheticHit('rl-audit-no-orphan-enforcer-001', 'every enforcerRef points to a real file', `(enforcerRef "${entry.enforcerRef}" for ${entry.id} does not exist on disk)`));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return hits;
|
|
54
|
+
}
|
|
55
|
+
export function lintNoOrphanCatalog() {
|
|
56
|
+
const hits = [];
|
|
57
|
+
for (const entry of RED_LINE_CATALOG) {
|
|
58
|
+
if (entry.enforcerRef === null) {
|
|
59
|
+
hits.push(syntheticHit('rl-audit-no-orphan-catalog-001', 'every catalog entry has a non-null enforcerRef (or a documented reason)', `(catalog entry ${entry.id} has enforcerRef: null)`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return hits;
|
|
63
|
+
}
|
|
64
|
+
export function lintRuntimeBudget(projectRoot, observedMs) {
|
|
65
|
+
if (observedMs <= RUNTIME_BUDGET_MS)
|
|
66
|
+
return [];
|
|
67
|
+
return [syntheticHit('rl-audit-runtime-budget-001', `peaks audit red-lines completes in < ${RUNTIME_BUDGET_MS}ms on a 100-reference project`, `(observed ${observedMs}ms > budget ${RUNTIME_BUDGET_MS}ms)`)];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read the catalog-stability history file (if it exists). The
|
|
71
|
+
* file is a small JSON document maintained by the release
|
|
72
|
+
* pipeline; absent → null. We do not invent the historical
|
|
73
|
+
* data; absent data means "soft pass".
|
|
74
|
+
*/
|
|
75
|
+
export function readCatalogHistory(projectRoot) {
|
|
76
|
+
const path = join(projectRoot, '.peaks', 'audit-catalog-history.json');
|
|
77
|
+
if (!existsSync(path))
|
|
78
|
+
return null;
|
|
79
|
+
try {
|
|
80
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
81
|
+
return typeof raw.sizeNinetyDaysAgo === 'number' ? raw.sizeNinetyDaysAgo : null;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2-a Theme G — catalog governance.
|
|
3
|
+
*
|
|
4
|
+
* Two enforcers: catalog size must grow to ≥ 40 (the P2-a target),
|
|
5
|
+
* and the prose-only ratio must stay ≤ 5% (per spec §10.2 L2
|
|
6
|
+
* acceptance). Both fire on the catalog's static state — no file
|
|
7
|
+
* scan, just the catalog itself.
|
|
8
|
+
*/
|
|
9
|
+
import type { LintHit } from './lint-style.js';
|
|
10
|
+
export declare const CATALOG_SIZE_TARGET = 40;
|
|
11
|
+
export declare const PROSE_ONLY_RATIO_TARGET = 0.05;
|
|
12
|
+
export interface CatalogSize {
|
|
13
|
+
readonly size: number;
|
|
14
|
+
readonly target: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CatalogProseOnlyRatio {
|
|
17
|
+
readonly ratio: number;
|
|
18
|
+
readonly target: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function lintCatalogSize(actualSize: number): readonly LintHit[];
|
|
21
|
+
/**
|
|
22
|
+
* Prose-only ratio: count catalog entries whose `enforcerRef` is
|
|
23
|
+
* null (i.e. not backed by a CLI surface) divided by the total
|
|
24
|
+
* catalog size. Per spec §10.2, the L2 acceptance is ≤ 10% at
|
|
25
|
+
* P2-a; this slice tightens to ≤ 5%.
|
|
26
|
+
*/
|
|
27
|
+
export declare function lintCatalogProseOnlyRatio(catalogSize: number, proseOnlyCount: number): readonly LintHit[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const CATALOG_SIZE_TARGET = 40;
|
|
2
|
+
export const PROSE_ONLY_RATIO_TARGET = 0.05;
|
|
3
|
+
function syntheticHit(catalogId, rule, matched) {
|
|
4
|
+
// No specific file to point at — return a synthetic hit against
|
|
5
|
+
// the catalog source file so the audit report can render a row.
|
|
6
|
+
const fake = {
|
|
7
|
+
name: 'catalog',
|
|
8
|
+
path: 'src/services/audit/red-line-catalog.ts',
|
|
9
|
+
body: '',
|
|
10
|
+
lines: [],
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
catalogId,
|
|
14
|
+
rule,
|
|
15
|
+
file: fake.path,
|
|
16
|
+
line: 1,
|
|
17
|
+
matchedText: matched,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function lintCatalogSize(actualSize) {
|
|
21
|
+
if (actualSize >= CATALOG_SIZE_TARGET)
|
|
22
|
+
return [];
|
|
23
|
+
return [syntheticHit('rl-catalog-total-001', 'Catalog governance: catalog size must grow to ≥ 40 (L2.3 P2-a target)', `(catalog size ${actualSize} < target ${CATALOG_SIZE_TARGET})`)];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Prose-only ratio: count catalog entries whose `enforcerRef` is
|
|
27
|
+
* null (i.e. not backed by a CLI surface) divided by the total
|
|
28
|
+
* catalog size. Per spec §10.2, the L2 acceptance is ≤ 10% at
|
|
29
|
+
* P2-a; this slice tightens to ≤ 5%.
|
|
30
|
+
*/
|
|
31
|
+
export function lintCatalogProseOnlyRatio(catalogSize, proseOnlyCount) {
|
|
32
|
+
if (catalogSize === 0)
|
|
33
|
+
return [];
|
|
34
|
+
const ratio = proseOnlyCount / catalogSize;
|
|
35
|
+
if (ratio <= PROSE_ONLY_RATIO_TARGET)
|
|
36
|
+
return [];
|
|
37
|
+
return [syntheticHit('rl-catalog-prose-only-ratio-001', 'Catalog governance: prose-only ratio must stay ≤ 5% (per §10.2 L2 acceptance)', `(prose-only ratio ${(ratio * 100).toFixed(1)}% > target ${PROSE_ONLY_RATIO_TARGET * 100}%; ${proseOnlyCount}/${catalogSize})`)];
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2-a Theme D — CLI-back gaps enforcers.
|
|
3
|
+
*
|
|
4
|
+
* A red line that says "BLOCKING" / "MANDATORY" / "MUST NOT" / "MUST"
|
|
5
|
+
* / "REQUIRED" must point at a `peaks *` enforcer in the
|
|
6
|
+
* surrounding ±2 lines, or it stays as `prose-only` (per §5.2).
|
|
7
|
+
*
|
|
8
|
+
* The enforcer walks each skill body, locates every
|
|
9
|
+
* `MANDATORY` / `BLOCKING` / `MUST NOT` marker, and reports an
|
|
10
|
+
* orphan-hit when the surrounding text does NOT name a peaks CLI
|
|
11
|
+
* command.
|
|
12
|
+
*/
|
|
13
|
+
import type { LintHit, SkillFile } from './lint-style.js';
|
|
14
|
+
export declare function lintCliBackMandatorText(skill: SkillFile): readonly LintHit[];
|
|
15
|
+
export declare function lintCliBackNoOrphanBlocking(skill: SkillFile): readonly LintHit[];
|
|
16
|
+
export declare function lintCliBackNoOrphanMustNot(skill: SkillFile): readonly LintHit[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const MANDATORY_PATTERN = /\bMANDATORY\b/;
|
|
2
|
+
const BLOCKING_PATTERN = /\bBLOCKING\b/;
|
|
3
|
+
const MUST_NOT_PATTERN = /\bMUST NOT\b/;
|
|
4
|
+
const PEAKS_CLI_PATTERN = /\bpeaks\s+[a-z][a-z0-9-]*/;
|
|
5
|
+
function findOrphans(skill, pattern, catalogId, rule) {
|
|
6
|
+
const hits = [];
|
|
7
|
+
for (let i = 0; i < skill.lines.length; i += 1) {
|
|
8
|
+
const line = skill.lines[i] ?? '';
|
|
9
|
+
if (!pattern.test(line))
|
|
10
|
+
continue;
|
|
11
|
+
// Look at ±2 lines for a `peaks *` reference
|
|
12
|
+
const ctx = skill.lines
|
|
13
|
+
.slice(Math.max(0, i - 2), Math.min(skill.lines.length, i + 3))
|
|
14
|
+
.join('\n');
|
|
15
|
+
if (!PEAKS_CLI_PATTERN.test(ctx)) {
|
|
16
|
+
hits.push({
|
|
17
|
+
catalogId,
|
|
18
|
+
rule,
|
|
19
|
+
file: skill.path,
|
|
20
|
+
line: i + 1,
|
|
21
|
+
matchedText: line.trim()
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return hits;
|
|
26
|
+
}
|
|
27
|
+
export function lintCliBackMandatorText(skill) {
|
|
28
|
+
return findOrphans(skill, MANDATORY_PATTERN, 'rl-cli-back-mandatory-text-001', 'MANDATORY text has peaks * enforcer in the surrounding ±2 lines');
|
|
29
|
+
}
|
|
30
|
+
export function lintCliBackNoOrphanBlocking(skill) {
|
|
31
|
+
return findOrphans(skill, BLOCKING_PATTERN, 'rl-cli-back-no-orphan-blocking-001', 'no orphan BLOCKING marker without a peaks * enforcer');
|
|
32
|
+
}
|
|
33
|
+
export function lintCliBackNoOrphanMustNot(skill) {
|
|
34
|
+
return findOrphans(skill, MUST_NOT_PATTERN, 'rl-cli-back-no-orphan-must-not-001', 'no orphan MUST NOT marker without a peaks * enforcer');
|
|
35
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LintHit, SkillFile } from './lint-style.js';
|
|
2
|
+
export declare function lintNoFluff(skill: SkillFile): readonly LintHit[];
|
|
3
|
+
export declare function lintNoClosingPrompt(skill: SkillFile): readonly LintHit[];
|
|
4
|
+
/**
|
|
5
|
+
* Status header detection: scan the most-recent session log for the
|
|
6
|
+
* canonical header line. The session log is read from the project's
|
|
7
|
+
* `.peaks/_runtime/<sid>/session.log` (or the audit's per-test
|
|
8
|
+
* fixture). For static analysis (no session), the lint returns an
|
|
9
|
+
* empty array (the runtime check is elsewhere).
|
|
10
|
+
*/
|
|
11
|
+
export declare function lintStatusHeader(projectRoot: string, sessionId: string): readonly LintHit[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2-a Theme C — output style enforcers.
|
|
3
|
+
*
|
|
4
|
+
* The peaks-cli dogfood rule: every skill response carries a status
|
|
5
|
+
* header (`Peaks-Cli Skill: <name> | Peaks-Cli Gate: <gate> | Next:
|
|
6
|
+
* <action>`), the SKILL.md body has no greeting/closing-prompt
|
|
7
|
+
* flattery, and the status header is detectable in the recent
|
|
8
|
+
* session transcript (test-only fixture).
|
|
9
|
+
*
|
|
10
|
+
* The status-header detection is a real check on the fixture's
|
|
11
|
+
* most-recent session file; the no-fluff and no-closing-prompt
|
|
12
|
+
* checks are static pattern scans of the skill's SKILL.md.
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
const FLUFF_GREETINGS = [
|
|
17
|
+
/\b你好[!!。,,.]?\s*$/m,
|
|
18
|
+
/^\s*你好[!!。,,.]?\s*$/m,
|
|
19
|
+
/\bHello[,,]?\s*I am\b/i,
|
|
20
|
+
/\bI am (an? )?(helpful )?assistant\b/i,
|
|
21
|
+
/\b作为一个 AI\b/,
|
|
22
|
+
/\b我是 (一个 )?AI\b/,
|
|
23
|
+
/\b作为一个\b/,
|
|
24
|
+
/\b我是(助手|模型|AI)/i
|
|
25
|
+
];
|
|
26
|
+
const CLOSING_PROMPTS = [
|
|
27
|
+
/\bLet me know if you need\b/i,
|
|
28
|
+
/\b如有(任何)?需要\b/,
|
|
29
|
+
/\b如有需要\b/,
|
|
30
|
+
/\bfeel free to ask\b/i,
|
|
31
|
+
/\bdo not hesitate to\b/i,
|
|
32
|
+
/\b随时(欢迎)?(联系|提问)/i,
|
|
33
|
+
/\b随时 (欢迎 )?(联系|提问)/i
|
|
34
|
+
];
|
|
35
|
+
const STATUS_HEADER_PATTERN = /Peaks-Cli Skill:\s*peaks-[a-z0-9-]+\s*\|\s*Peaks-Cli Gate:\s*[A-Za-z0-9_.-]+\s*\|\s*Next:\s*\S/;
|
|
36
|
+
export function lintNoFluff(skill) {
|
|
37
|
+
const hits = [];
|
|
38
|
+
for (const pattern of FLUFF_GREETINGS) {
|
|
39
|
+
const lineIdx = skill.lines.findIndex((l) => pattern.test(l));
|
|
40
|
+
if (lineIdx !== -1) {
|
|
41
|
+
hits.push({
|
|
42
|
+
catalogId: 'rl-output-style-no-fluff-001',
|
|
43
|
+
rule: 'no greeting / persona fluff in SKILL.md',
|
|
44
|
+
file: skill.path,
|
|
45
|
+
line: lineIdx + 1,
|
|
46
|
+
matchedText: (skill.lines[lineIdx] ?? '').trim()
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return hits;
|
|
51
|
+
}
|
|
52
|
+
export function lintNoClosingPrompt(skill) {
|
|
53
|
+
const hits = [];
|
|
54
|
+
for (const pattern of CLOSING_PROMPTS) {
|
|
55
|
+
const lineIdx = skill.lines.findIndex((l) => pattern.test(l));
|
|
56
|
+
if (lineIdx !== -1) {
|
|
57
|
+
hits.push({
|
|
58
|
+
catalogId: 'rl-output-style-no-closing-prompt-001',
|
|
59
|
+
rule: 'no closing-prompt flattery',
|
|
60
|
+
file: skill.path,
|
|
61
|
+
line: lineIdx + 1,
|
|
62
|
+
matchedText: (skill.lines[lineIdx] ?? '').trim()
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return hits;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Status header detection: scan the most-recent session log for the
|
|
70
|
+
* canonical header line. The session log is read from the project's
|
|
71
|
+
* `.peaks/_runtime/<sid>/session.log` (or the audit's per-test
|
|
72
|
+
* fixture). For static analysis (no session), the lint returns an
|
|
73
|
+
* empty array (the runtime check is elsewhere).
|
|
74
|
+
*/
|
|
75
|
+
export function lintStatusHeader(projectRoot, sessionId) {
|
|
76
|
+
const logPath = join(projectRoot, '.peaks', '_runtime', sessionId, 'session.log');
|
|
77
|
+
let body;
|
|
78
|
+
try {
|
|
79
|
+
body = readFileSync(logPath, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
if (!STATUS_HEADER_PATTERN.test(body)) {
|
|
85
|
+
return [{
|
|
86
|
+
catalogId: 'rl-output-style-status-header-001',
|
|
87
|
+
rule: 'Peaks-Cli status header on every response',
|
|
88
|
+
file: logPath,
|
|
89
|
+
line: 1,
|
|
90
|
+
matchedText: '(no Peaks-Cli Skill / Gate / Next header found in session.log)'
|
|
91
|
+
}];
|
|
92
|
+
}
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LintHit, SkillFile } from './lint-style.js';
|
|
2
|
+
/** Theme E — reference integrity. */
|
|
3
|
+
export declare function lintRefPathResolves(skillsRoot: string, name: string, refs: readonly string[]): readonly LintHit[];
|
|
4
|
+
export declare function lintNoBrokenMkdir(skill: SkillFile): readonly LintHit[];
|
|
5
|
+
export declare function lintNoPwdSymlinkJumps(skill: SkillFile): readonly LintHit[];
|
|
6
|
+
export declare function lintNoRelativeArchivePaths(skill: SkillFile): readonly LintHit[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2-a Theme E — reference integrity enforcers.
|
|
3
|
+
*
|
|
4
|
+
* Static pattern scans of skill bodies + references/ for inline
|
|
5
|
+
* shell snippets that violate the project's "no
|
|
6
|
+
* mkdir-outside-project" / "no /tmp cp" / "no cd .." rules.
|
|
7
|
+
*
|
|
8
|
+
* These are pattern-based: the audit framework invokes the helper
|
|
9
|
+
* with a skill's body and the helper returns lint hits.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
const MKDIR_PATTERN = /\bmkdir\s+(?:-p\s+)?(\/[^\s'`"]+|[A-Z]:[^\s'`"]+)/g;
|
|
14
|
+
const CD_OUT_PATTERN = /\bcd\s+(\.\.[\\/]|[A-Z]:[\\/])/g;
|
|
15
|
+
const CP_MV_LN_TMP_PATTERN = /\b(cp|mv|ln)\b[^\n]*\/tmp\b/g;
|
|
16
|
+
/** Theme E — reference integrity. */
|
|
17
|
+
export function lintRefPathResolves(skillsRoot, name, refs) {
|
|
18
|
+
const hits = [];
|
|
19
|
+
for (const ref of refs) {
|
|
20
|
+
const path = join(skillsRoot, name, 'references', ref);
|
|
21
|
+
if (!existsSync(path)) {
|
|
22
|
+
hits.push({
|
|
23
|
+
catalogId: 'rl-ref-path-resolves-001',
|
|
24
|
+
rule: 'every references/<file>.md link resolves',
|
|
25
|
+
file: join(skillsRoot, name, 'SKILL.md'),
|
|
26
|
+
line: 1,
|
|
27
|
+
matchedText: ref
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return hits;
|
|
32
|
+
}
|
|
33
|
+
export function lintNoBrokenMkdir(skill) {
|
|
34
|
+
const hits = [];
|
|
35
|
+
for (let i = 0; i < skill.lines.length; i += 1) {
|
|
36
|
+
const line = skill.lines[i] ?? '';
|
|
37
|
+
if (MKDIR_PATTERN.test(line)) {
|
|
38
|
+
hits.push({
|
|
39
|
+
catalogId: 'rl-ref-no-broken-mkdir-001',
|
|
40
|
+
rule: 'no `mkdir -p` outside the project root',
|
|
41
|
+
file: skill.path,
|
|
42
|
+
line: i + 1,
|
|
43
|
+
matchedText: line.trim()
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
MKDIR_PATTERN.lastIndex = 0;
|
|
47
|
+
}
|
|
48
|
+
return hits;
|
|
49
|
+
}
|
|
50
|
+
export function lintNoPwdSymlinkJumps(skill) {
|
|
51
|
+
const hits = [];
|
|
52
|
+
for (let i = 0; i < skill.lines.length; i += 1) {
|
|
53
|
+
const line = skill.lines[i] ?? '';
|
|
54
|
+
if (CD_OUT_PATTERN.test(line)) {
|
|
55
|
+
hits.push({
|
|
56
|
+
catalogId: 'rl-ref-no-pwd-symlink-jumps-001',
|
|
57
|
+
rule: 'no `cd ..` chain jumping outside the project',
|
|
58
|
+
file: skill.path,
|
|
59
|
+
line: i + 1,
|
|
60
|
+
matchedText: line.trim()
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
CD_OUT_PATTERN.lastIndex = 0;
|
|
64
|
+
}
|
|
65
|
+
return hits;
|
|
66
|
+
}
|
|
67
|
+
export function lintNoRelativeArchivePaths(skill) {
|
|
68
|
+
const hits = [];
|
|
69
|
+
for (let i = 0; i < skill.lines.length; i += 1) {
|
|
70
|
+
const line = skill.lines[i] ?? '';
|
|
71
|
+
if (CP_MV_LN_TMP_PATTERN.test(line)) {
|
|
72
|
+
hits.push({
|
|
73
|
+
catalogId: 'rl-ref-no-relative-archive-paths-001',
|
|
74
|
+
rule: 'no `cp`/`mv`/`ln` to absolute /tmp paths',
|
|
75
|
+
file: skill.path,
|
|
76
|
+
line: i + 1,
|
|
77
|
+
matchedText: line.trim()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
CP_MV_LN_TMP_PATTERN.lastIndex = 0;
|
|
81
|
+
}
|
|
82
|
+
return hits;
|
|
83
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { LintHit, SkillFile } from './lint-style.js';
|
|
2
|
+
export interface ReferenceFile {
|
|
3
|
+
readonly skill: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly body: string;
|
|
7
|
+
readonly lines: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function lintH1TitleRequired(ref: ReferenceFile): readonly LintHit[];
|
|
10
|
+
export declare function lintApplicableTaskLevels(ref: ReferenceFile): readonly LintHit[];
|
|
11
|
+
export declare function lintSeeAlsoSection(ref: ReferenceFile): readonly LintHit[];
|
|
12
|
+
export declare function lintCrossRefResolves(ref: ReferenceFile, refsDir: string, siblings: readonly string[]): readonly LintHit[];
|
|
13
|
+
export declare function lintNoSelfReference(ref: ReferenceFile): readonly LintHit[];
|
|
14
|
+
export declare function lintNoOrphanLink(ref: ReferenceFile): readonly LintHit[];
|
|
15
|
+
export declare function lintLineCountLe800(ref: ReferenceFile): readonly LintHit[];
|
|
16
|
+
export declare function lintH2CountLe12(ref: ReferenceFile): readonly LintHit[];
|
|
17
|
+
export declare function lintOverviewNearTop(ref: ReferenceFile): readonly LintHit[];
|
|
18
|
+
export declare function lintLoadStrategyOnDemandFallback(ref: ReferenceFile): readonly LintHit[];
|
|
19
|
+
export declare function lintLoadStrategyAlwaysCacheable(ref: ReferenceFile): readonly LintHit[];
|
|
20
|
+
export declare function lintNoBashHeredoc(ref: ReferenceFile): readonly LintHit[];
|
|
21
|
+
export declare function lintNoSudo(ref: ReferenceFile): readonly LintHit[];
|
|
22
|
+
export declare function lintNoCurlPipeBash(ref: ReferenceFile): readonly LintHit[];
|
|
23
|
+
export declare function lintCodeBlockLanguage(ref: ReferenceFile): readonly LintHit[];
|
|
24
|
+
export declare function lintNoFakePrompt(ref: ReferenceFile): readonly LintHit[];
|
|
25
|
+
export declare function lintNoAbsolutePaths(ref: ReferenceFile): readonly LintHit[];
|
|
26
|
+
export declare function lintNoChmod777(ref: ReferenceFile): readonly LintHit[];
|
|
27
|
+
export declare function lintNoMagicNumbers(ref: ReferenceFile): readonly LintHit[];
|
|
28
|
+
export declare function lintSkillCitesEveryReference(ref: ReferenceFile, skill: SkillFile): readonly LintHit[];
|
|
29
|
+
export declare function lintLoadStrategyMatchesSize(ref: ReferenceFile): readonly LintHit[];
|
|
30
|
+
export declare function readReferenceFiles(skillsRoot: string, skillName: string, refNames: readonly string[]): readonly ReferenceFile[];
|