frontend-harness 0.3.1 → 0.3.2
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 +42 -0
- package/README.md +10 -0
- package/dist/runtime/common/parsing.d.ts +11 -0
- package/dist/runtime/common/parsing.js +30 -0
- package/dist/runtime/common/parsing.js.map +1 -0
- package/dist/runtime/project-discovery.d.ts +17 -0
- package/dist/runtime/project-discovery.js +174 -0
- package/dist/runtime/project-discovery.js.map +1 -0
- package/dist/runtime/skills.d.ts +19 -0
- package/dist/runtime/skills.js +230 -0
- package/dist/runtime/skills.js.map +1 -0
- package/dist/runtime/task-signals.d.ts +24 -0
- package/dist/runtime/task-signals.js +47 -0
- package/dist/runtime/task-signals.js.map +1 -0
- package/dist/runtime/verification-commands.d.ts +11 -0
- package/dist/runtime/verification-commands.js +93 -0
- package/dist/runtime/verification-commands.js.map +1 -0
- package/dist/storage/json.d.ts +5 -0
- package/dist/storage/json.js +29 -0
- package/dist/storage/json.js.map +1 -0
- package/dist/storage/paths.d.ts +3 -0
- package/dist/storage/paths.js +9 -0
- package/dist/storage/paths.js.map +1 -0
- package/docs/RELEASE.md +97 -0
- package/package.json +3 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `frontend-harness` are documented here.
|
|
4
|
+
|
|
5
|
+
This project follows semver while it is pre-1.0: patch releases may include harness policy improvements that tighten validation, and minor releases may change generated protocol expectations.
|
|
6
|
+
|
|
7
|
+
## 0.3.2 - 2026-05-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Fixed the npm package contents so CLI runtime dependencies such as `dist/runtime/skills.js` are included in published tarballs.
|
|
12
|
+
- Added package-output regression coverage for required runtime files used by the CLI.
|
|
13
|
+
|
|
14
|
+
## 0.3.1 - 2026-05-20
|
|
15
|
+
|
|
16
|
+
### Known Issue
|
|
17
|
+
|
|
18
|
+
- The published npm tarball is missing `dist/runtime/skills.js`, which can make `npx frontend-harness ...` fail with `ERR_MODULE_NOT_FOUND`. Use `0.3.2` or newer.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added structured UI verification evidence gating through `.frontend-harness/verification/evidence.json`.
|
|
23
|
+
- Added shared task-signal detection for workflow routing, source-root selection, and component role inference.
|
|
24
|
+
- Added execution-boundary tracking for accepted agent `planningEvidence.targetCandidates`.
|
|
25
|
+
- Added warnings for low-confidence target candidates that are ignored during planning.
|
|
26
|
+
- Added PRD knowledge frontmatter drift detection.
|
|
27
|
+
- Added UI restoration checklist parity coverage.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Tightened Pixso/MCP/HTML UI workflow detection to avoid classifying generic MCP or HTML maintenance work as UI implementation.
|
|
32
|
+
- Improved page/table/form component placement so page orchestration and child components can land in role-appropriate roots.
|
|
33
|
+
- Improved multi-page execution-unit dependency wiring.
|
|
34
|
+
- Changed UI verification evidence from advisory plan metadata into a verify-time gate.
|
|
35
|
+
- Clarified generated protocol guidance for agent proposal envelopes and state tracking.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Fixed Unicode-heavy UI task names colliding through empty PascalCase names.
|
|
40
|
+
- Fixed UI implementation fallback classification for unrecognized tasks.
|
|
41
|
+
- Fixed duplicated UI restoration guidance drift across planner/protocol/scaffold text.
|
|
42
|
+
- Fixed malformed knowledge-card rendering where `verification` could be nested under `tags`.
|
package/README.md
CHANGED
|
@@ -344,3 +344,13 @@ npm_config_cache=/tmp/frontend-harness-npm-cache npm pack --dry-run
|
|
|
344
344
|
```
|
|
345
345
|
|
|
346
346
|
The temporary npm cache avoids local global-cache permission issues on machines where `~/.npm` contains root-owned files.
|
|
347
|
+
|
|
348
|
+
Release checklist:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
npm run verify
|
|
352
|
+
npm_config_cache=/tmp/frontend-harness-npm-cache npm pack --dry-run
|
|
353
|
+
npm_config_cache=/tmp/frontend-harness-npm-cache npm pack --pack-destination /tmp
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Update `CHANGELOG.md` before publishing, then install the generated tarball in a temporary consumer project and run `npx frontend-harness --help` plus scaffold dry-run. See `docs/RELEASE.md` for the full npm release process.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface BoundedStringOptions {
|
|
2
|
+
prefix: string;
|
|
3
|
+
maxLength?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface StringArrayOptions extends BoundedStringOptions {
|
|
6
|
+
minItems?: number;
|
|
7
|
+
maxItems?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
10
|
+
export declare function parseBoundedString(value: unknown, label: string, options: BoundedStringOptions): string;
|
|
11
|
+
export declare function parseStringArray(value: unknown, label: string, options: StringArrayOptions): string[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null;
|
|
3
|
+
}
|
|
4
|
+
export function parseBoundedString(value, label, options) {
|
|
5
|
+
const maxLength = options.maxLength ?? 500;
|
|
6
|
+
if (typeof value !== "string") {
|
|
7
|
+
throw new Error(`${options.prefix} ${label} must be a string`);
|
|
8
|
+
}
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (!trimmed) {
|
|
11
|
+
throw new Error(`${options.prefix} ${label} cannot be empty`);
|
|
12
|
+
}
|
|
13
|
+
if (trimmed.length > maxLength) {
|
|
14
|
+
throw new Error(`${options.prefix} ${label} cannot exceed ${maxLength} characters`);
|
|
15
|
+
}
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
export function parseStringArray(value, label, options) {
|
|
19
|
+
if (!Array.isArray(value)) {
|
|
20
|
+
throw new Error(`${options.prefix} ${label} must be an array of strings`);
|
|
21
|
+
}
|
|
22
|
+
if (options.minItems !== undefined && value.length < options.minItems) {
|
|
23
|
+
throw new Error(`${options.prefix} ${label} must contain at least ${options.minItems} item`);
|
|
24
|
+
}
|
|
25
|
+
if (options.maxItems !== undefined && value.length > options.maxItems) {
|
|
26
|
+
throw new Error(`${options.prefix} ${label} cannot contain more than ${options.maxItems} items`);
|
|
27
|
+
}
|
|
28
|
+
return value.map((item, index) => parseBoundedString(item, `${label}[${index}]`, options));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=parsing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parsing.js","sourceRoot":"","sources":["../../../src/runtime/common/parsing.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAAE,KAAa,EAAE,OAA6B;IAC7F,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,mBAAmB,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,kBAAkB,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,kBAAkB,SAAS,aAAa,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,KAAa,EAAE,OAA2B;IACzF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,8BAA8B,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,0BAA0B,OAAO,CAAC,QAAQ,OAAO,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,6BAA6B,OAAO,CAAC,QAAQ,QAAQ,CAAC,CAAC;IACnG,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,KAAK,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DetectedStack {
|
|
2
|
+
packageManager: string;
|
|
3
|
+
framework: string;
|
|
4
|
+
language: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProjectSurface {
|
|
7
|
+
sourceRoots: string[];
|
|
8
|
+
entryFiles: string[];
|
|
9
|
+
fileExtensions: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function detectProjectStack(projectRoot: string): DetectedStack;
|
|
12
|
+
export declare function discoverProjectSurface(projectRoot: string): ProjectSurface;
|
|
13
|
+
export declare function resolveComponentPlacement(projectRoot: string, scope: string): {
|
|
14
|
+
sourceRoot: string;
|
|
15
|
+
extension: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function chooseComponentExtension(fileExtensions: string[], stack: DetectedStack): string;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { hasRoleSignal } from "./task-signals.js";
|
|
4
|
+
const SOURCE_ROOT_CANDIDATES = [
|
|
5
|
+
"src/pages",
|
|
6
|
+
"src/app",
|
|
7
|
+
"src/components",
|
|
8
|
+
"src/views",
|
|
9
|
+
"src/features",
|
|
10
|
+
"src",
|
|
11
|
+
"app",
|
|
12
|
+
"pages",
|
|
13
|
+
"components",
|
|
14
|
+
"views",
|
|
15
|
+
"features"
|
|
16
|
+
];
|
|
17
|
+
const ENTRY_FILE_CANDIDATES = [
|
|
18
|
+
"src/main.tsx",
|
|
19
|
+
"src/main.ts",
|
|
20
|
+
"src/main.jsx",
|
|
21
|
+
"src/main.js",
|
|
22
|
+
"src/index.tsx",
|
|
23
|
+
"src/index.ts",
|
|
24
|
+
"src/index.jsx",
|
|
25
|
+
"src/index.js",
|
|
26
|
+
"src/App.vue",
|
|
27
|
+
"src/App.tsx",
|
|
28
|
+
"src/App.ts",
|
|
29
|
+
"src/App.jsx",
|
|
30
|
+
"src/App.js",
|
|
31
|
+
"app/main.tsx",
|
|
32
|
+
"app/main.ts",
|
|
33
|
+
"app/main.jsx",
|
|
34
|
+
"app/main.js",
|
|
35
|
+
"app/page.tsx",
|
|
36
|
+
"app/page.ts",
|
|
37
|
+
"app/page.jsx",
|
|
38
|
+
"app/page.js"
|
|
39
|
+
];
|
|
40
|
+
const IGNORED_DIRECTORIES = new Set([
|
|
41
|
+
".frontend-harness",
|
|
42
|
+
".git",
|
|
43
|
+
"build",
|
|
44
|
+
"coverage",
|
|
45
|
+
"dist",
|
|
46
|
+
"node_modules",
|
|
47
|
+
".cache",
|
|
48
|
+
".turbo"
|
|
49
|
+
]);
|
|
50
|
+
const EXTENSION_PRIORITY = [".vue", ".tsx", ".jsx", ".ts", ".js", ".mjs", ".cjs"];
|
|
51
|
+
export function detectProjectStack(projectRoot) {
|
|
52
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
53
|
+
const packageJson = readPackageJson(packageJsonPath);
|
|
54
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
55
|
+
return {
|
|
56
|
+
packageManager: detectPackageManager(projectRoot, packageJson),
|
|
57
|
+
framework: deps["vite"]
|
|
58
|
+
? "vite"
|
|
59
|
+
: deps["next"]
|
|
60
|
+
? "next"
|
|
61
|
+
: deps["vue"]
|
|
62
|
+
? "vue"
|
|
63
|
+
: deps["react"]
|
|
64
|
+
? "react"
|
|
65
|
+
: "unknown",
|
|
66
|
+
language: deps["typescript"] || fs.existsSync(path.join(projectRoot, "tsconfig.json"))
|
|
67
|
+
? "typescript"
|
|
68
|
+
: "unknown"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function readPackageJson(packageJsonPath) {
|
|
72
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function discoverProjectSurface(projectRoot) {
|
|
83
|
+
const sourceRoots = SOURCE_ROOT_CANDIDATES.filter((candidate) => directoryHasFiles(path.join(projectRoot, candidate)));
|
|
84
|
+
const entryFiles = ENTRY_FILE_CANDIDATES.filter((candidate) => fs.existsSync(path.join(projectRoot, candidate)));
|
|
85
|
+
const fileExtensions = EXTENSION_PRIORITY.filter((extension) => sourceRoots.some((root) => hasExtensionInDirectory(path.join(projectRoot, root), extension)));
|
|
86
|
+
return {
|
|
87
|
+
sourceRoots,
|
|
88
|
+
entryFiles,
|
|
89
|
+
fileExtensions
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function resolveComponentPlacement(projectRoot, scope) {
|
|
93
|
+
const stack = detectProjectStack(projectRoot);
|
|
94
|
+
const surface = discoverProjectSurface(projectRoot);
|
|
95
|
+
return {
|
|
96
|
+
sourceRoot: chooseSourceRoot(surface.sourceRoots, scope),
|
|
97
|
+
extension: chooseComponentExtension(surface.fileExtensions, stack)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function chooseComponentExtension(fileExtensions, stack) {
|
|
101
|
+
for (const extension of EXTENSION_PRIORITY) {
|
|
102
|
+
if (fileExtensions.includes(extension)) {
|
|
103
|
+
return extension;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (stack.framework === "vue") {
|
|
107
|
+
return ".vue";
|
|
108
|
+
}
|
|
109
|
+
if (stack.framework === "react") {
|
|
110
|
+
return ".tsx";
|
|
111
|
+
}
|
|
112
|
+
if (stack.language === "typescript") {
|
|
113
|
+
return ".tsx";
|
|
114
|
+
}
|
|
115
|
+
return ".js";
|
|
116
|
+
}
|
|
117
|
+
function chooseSourceRoot(sourceRoots, scope) {
|
|
118
|
+
if (sourceRoots.length === 0) {
|
|
119
|
+
return "src";
|
|
120
|
+
}
|
|
121
|
+
const preferComponentRoots = hasRoleSignal(scope, "component") || hasRoleSignal(scope, "form") || hasRoleSignal(scope, "table");
|
|
122
|
+
const preferPageRoots = hasRoleSignal(scope, "page");
|
|
123
|
+
const preferredRoots = preferComponentRoots
|
|
124
|
+
? sourceRoots.filter((root) => root.includes("components") || root.includes("widgets"))
|
|
125
|
+
: preferPageRoots
|
|
126
|
+
? sourceRoots.filter((root) => root.includes("pages") || root.includes("app") || root.includes("views") || root.includes("features"))
|
|
127
|
+
: [];
|
|
128
|
+
return preferredRoots[0] ?? sourceRoots[0] ?? "src";
|
|
129
|
+
}
|
|
130
|
+
function detectPackageManager(projectRoot, packageJson) {
|
|
131
|
+
const declared = packageJson.packageManager?.split("@", 1)[0];
|
|
132
|
+
if (declared && ["npm", "pnpm", "yarn", "bun"].includes(declared)) {
|
|
133
|
+
return declared;
|
|
134
|
+
}
|
|
135
|
+
if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) {
|
|
136
|
+
return "pnpm";
|
|
137
|
+
}
|
|
138
|
+
if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) {
|
|
139
|
+
return "yarn";
|
|
140
|
+
}
|
|
141
|
+
if (fs.existsSync(path.join(projectRoot, "bun.lock")) || fs.existsSync(path.join(projectRoot, "bun.lockb"))) {
|
|
142
|
+
return "bun";
|
|
143
|
+
}
|
|
144
|
+
if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) {
|
|
145
|
+
return "npm";
|
|
146
|
+
}
|
|
147
|
+
return "unknown";
|
|
148
|
+
}
|
|
149
|
+
function directoryHasFiles(directory) {
|
|
150
|
+
return listFiles(directory, 4).length > 0;
|
|
151
|
+
}
|
|
152
|
+
function hasExtensionInDirectory(directory, extension) {
|
|
153
|
+
return listFiles(directory, 4).some((file) => file.endsWith(extension));
|
|
154
|
+
}
|
|
155
|
+
function listFiles(directory, maxDepth, depth = 0) {
|
|
156
|
+
if (!fs.existsSync(directory) || depth > maxDepth) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
160
|
+
const files = [];
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
const fullPath = path.join(directory, entry.name);
|
|
163
|
+
if (entry.isDirectory()) {
|
|
164
|
+
if (IGNORED_DIRECTORIES.has(entry.name)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
files.push(...listFiles(fullPath, maxDepth, depth + 1));
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
files.push(fullPath);
|
|
171
|
+
}
|
|
172
|
+
return files;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=project-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery.js","sourceRoot":"","sources":["../../src/runtime/project-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ProjectSkill {
|
|
2
|
+
name: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string | null;
|
|
5
|
+
path: string;
|
|
6
|
+
triggers?: string[];
|
|
7
|
+
intents?: string[];
|
|
8
|
+
examples?: string[];
|
|
9
|
+
}
|
|
10
|
+
export type SkillCheckStatus = "passed" | "failed" | "not_configured";
|
|
11
|
+
export interface SkillCheckResult {
|
|
12
|
+
status: SkillCheckStatus;
|
|
13
|
+
skillsRoot: string;
|
|
14
|
+
skillCount: number;
|
|
15
|
+
errors: string[];
|
|
16
|
+
warnings: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function discoverSkills(projectRoot: string): ProjectSkill[];
|
|
19
|
+
export declare function checkSkills(projectRoot: string): SkillCheckResult;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { harnessPath } from "../storage/paths.js";
|
|
4
|
+
export function discoverSkills(projectRoot) {
|
|
5
|
+
const skillsRoot = harnessPath(projectRoot, "skills");
|
|
6
|
+
if (!isReadableDirectory(skillsRoot)) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
return listMarkdownFiles(skillsRoot)
|
|
10
|
+
.flatMap((fullPath) => {
|
|
11
|
+
const skill = describeSkill(projectRoot, fullPath);
|
|
12
|
+
return skill ? [skill] : [];
|
|
13
|
+
})
|
|
14
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
15
|
+
}
|
|
16
|
+
export function checkSkills(projectRoot) {
|
|
17
|
+
const skillsRoot = harnessPath(projectRoot, "skills");
|
|
18
|
+
const relativeSkillsRoot = path.relative(projectRoot, skillsRoot).split(path.sep).join(path.posix.sep);
|
|
19
|
+
if (!fs.existsSync(skillsRoot)) {
|
|
20
|
+
return {
|
|
21
|
+
status: "not_configured",
|
|
22
|
+
skillsRoot: relativeSkillsRoot,
|
|
23
|
+
skillCount: 0,
|
|
24
|
+
errors: [],
|
|
25
|
+
warnings: ["Create .frontend-harness/skills before checking project-local skills."]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const errors = [];
|
|
29
|
+
const warnings = [];
|
|
30
|
+
if (!isReadableDirectory(skillsRoot)) {
|
|
31
|
+
errors.push(".frontend-harness/skills must be a readable directory.");
|
|
32
|
+
}
|
|
33
|
+
const skillFiles = isReadableDirectory(skillsRoot) ? listMarkdownFiles(skillsRoot, errors) : [];
|
|
34
|
+
const names = new Set();
|
|
35
|
+
const duplicateNames = new Set();
|
|
36
|
+
for (const fullPath of skillFiles) {
|
|
37
|
+
const relativePath = path.relative(projectRoot, fullPath).split(path.sep).join(path.posix.sep);
|
|
38
|
+
const skillPath = path.relative(skillsRoot, fullPath);
|
|
39
|
+
const name = slugFromSkillPath(skillPath);
|
|
40
|
+
if (names.has(name)) {
|
|
41
|
+
duplicateNames.add(name);
|
|
42
|
+
}
|
|
43
|
+
names.add(name);
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = fs.readFileSync(fullPath, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
errors.push(`${relativePath} cannot be read: ${message}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const title = extractTitle(content);
|
|
54
|
+
if (!title) {
|
|
55
|
+
errors.push(`${relativePath} must include a non-empty "# Title" heading.`);
|
|
56
|
+
}
|
|
57
|
+
if (!hasBodyGuidance(content)) {
|
|
58
|
+
errors.push(`${relativePath} must include non-empty body guidance after the title.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const duplicateName of duplicateNames) {
|
|
62
|
+
errors.push(`Duplicate project skill name: ${duplicateName}.`);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
status: errors.length > 0 ? "failed" : "passed",
|
|
66
|
+
skillsRoot: relativeSkillsRoot,
|
|
67
|
+
skillCount: skillFiles.length,
|
|
68
|
+
errors,
|
|
69
|
+
warnings
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function describeSkill(projectRoot, fullPath) {
|
|
73
|
+
const relativePath = path.relative(projectRoot, fullPath).split(path.sep).join(path.posix.sep);
|
|
74
|
+
const skillsRoot = harnessPath(projectRoot, "skills");
|
|
75
|
+
const skillPath = path.relative(skillsRoot, fullPath);
|
|
76
|
+
let content;
|
|
77
|
+
try {
|
|
78
|
+
content = fs.readFileSync(fullPath, "utf8");
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const name = slugFromSkillPath(skillPath);
|
|
84
|
+
const title = extractTitle(content) ?? name;
|
|
85
|
+
const metadata = parseFrontmatter(content);
|
|
86
|
+
return {
|
|
87
|
+
name: metadata.name ?? name,
|
|
88
|
+
title: metadata.title ?? title,
|
|
89
|
+
description: metadata.description ?? extractDescription(content),
|
|
90
|
+
path: relativePath,
|
|
91
|
+
...(metadata.triggers.length ? { triggers: metadata.triggers } : {}),
|
|
92
|
+
...(metadata.intents.length ? { intents: metadata.intents } : {}),
|
|
93
|
+
...(metadata.examples.length ? { examples: metadata.examples } : {})
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function parseFrontmatter(content) {
|
|
97
|
+
const empty = {
|
|
98
|
+
triggers: [],
|
|
99
|
+
intents: [],
|
|
100
|
+
examples: []
|
|
101
|
+
};
|
|
102
|
+
if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
|
|
103
|
+
return empty;
|
|
104
|
+
}
|
|
105
|
+
const endIndex = content.search(/\r?\n---(?:\r?\n|$)/);
|
|
106
|
+
if (endIndex < 0) {
|
|
107
|
+
return empty;
|
|
108
|
+
}
|
|
109
|
+
const lines = content.slice(content.startsWith("---\r\n") ? 5 : 4, endIndex).split(/\r?\n/);
|
|
110
|
+
const result = {
|
|
111
|
+
triggers: [],
|
|
112
|
+
intents: [],
|
|
113
|
+
examples: []
|
|
114
|
+
};
|
|
115
|
+
let currentList = null;
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
const trimmed = line.trim();
|
|
118
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const listItem = trimmed.match(/^-\s+(.+)$/);
|
|
122
|
+
if (listItem && currentList) {
|
|
123
|
+
const value = listItem[1];
|
|
124
|
+
if (value) {
|
|
125
|
+
result[currentList].push(unquoteScalar(value));
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const pair = trimmed.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/);
|
|
130
|
+
if (!pair) {
|
|
131
|
+
currentList = null;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const key = pair[1] ?? "";
|
|
135
|
+
const value = (pair[2] ?? "").trim();
|
|
136
|
+
if (key === "name" || key === "title" || key === "description") {
|
|
137
|
+
result[key] = unquoteScalar(value);
|
|
138
|
+
currentList = null;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (key === "triggers" || key === "intents" || key === "examples") {
|
|
142
|
+
result[key] = parseListValue(value);
|
|
143
|
+
currentList = value ? null : key;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
currentList = null;
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function parseListValue(value) {
|
|
151
|
+
if (!value) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
const inline = value.match(/^\[(.*)\]$/);
|
|
155
|
+
if (!inline) {
|
|
156
|
+
return [unquoteScalar(value)];
|
|
157
|
+
}
|
|
158
|
+
return (inline[1] ?? "")
|
|
159
|
+
.split(",")
|
|
160
|
+
.map((item) => unquoteScalar(item.trim()))
|
|
161
|
+
.filter(Boolean);
|
|
162
|
+
}
|
|
163
|
+
function unquoteScalar(value) {
|
|
164
|
+
return value.replace(/^["']|["']$/g, "").trim();
|
|
165
|
+
}
|
|
166
|
+
function extractTitle(content) {
|
|
167
|
+
const line = content.split("\n").find((item) => item.startsWith("# "));
|
|
168
|
+
return line ? line.replace(/^#\s+/, "").trim() || null : null;
|
|
169
|
+
}
|
|
170
|
+
function extractDescription(content) {
|
|
171
|
+
const lines = content.split("\n");
|
|
172
|
+
const titleIndex = lines.findIndex((line) => line.startsWith("# "));
|
|
173
|
+
const body = lines
|
|
174
|
+
.slice(titleIndex >= 0 ? titleIndex + 1 : 0)
|
|
175
|
+
.map((line) => line.trim())
|
|
176
|
+
.find((line) => line && !line.startsWith("#") && !line.startsWith("- "));
|
|
177
|
+
return body ?? null;
|
|
178
|
+
}
|
|
179
|
+
function hasBodyGuidance(content) {
|
|
180
|
+
const lines = content.split("\n");
|
|
181
|
+
const titleIndex = lines.findIndex((line) => line.startsWith("# "));
|
|
182
|
+
return lines
|
|
183
|
+
.slice(titleIndex >= 0 ? titleIndex + 1 : 0)
|
|
184
|
+
.some((line) => {
|
|
185
|
+
const trimmed = line.trim();
|
|
186
|
+
return trimmed && !trimmed.startsWith("#");
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function isReadableDirectory(directory) {
|
|
190
|
+
try {
|
|
191
|
+
return fs.statSync(directory).isDirectory();
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function slugFromSkillPath(skillPath) {
|
|
198
|
+
const parsed = path.parse(skillPath);
|
|
199
|
+
const parts = skillPath.split(path.sep);
|
|
200
|
+
const nameParts = parsed.name.toLowerCase() === "skill"
|
|
201
|
+
? parts.slice(0, -1)
|
|
202
|
+
: [...parts.slice(0, -1), parsed.name];
|
|
203
|
+
return nameParts.join("-")
|
|
204
|
+
.toLowerCase()
|
|
205
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
206
|
+
.replace(/^-+|-+$/g, "") || "skill";
|
|
207
|
+
}
|
|
208
|
+
function listMarkdownFiles(directory, errors) {
|
|
209
|
+
try {
|
|
210
|
+
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
211
|
+
const fullPath = path.join(directory, entry.name);
|
|
212
|
+
if (entry.isDirectory()) {
|
|
213
|
+
if (entry.name.startsWith(".")) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
return listMarkdownFiles(fullPath, errors);
|
|
217
|
+
}
|
|
218
|
+
return entry.name.endsWith(".md") ? [fullPath] : [];
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (errors) {
|
|
223
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
224
|
+
errors.push(`Cannot read .frontend-harness/skills: ${message}`);
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/runtime/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ComponentRoleSignal = "page" | "table" | "form" | "component";
|
|
2
|
+
export interface TaskSignalInput {
|
|
3
|
+
prd?: string;
|
|
4
|
+
ui?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TaskSignals {
|
|
7
|
+
hasAttachedUiSource: boolean;
|
|
8
|
+
hasUiImplementationSignal: boolean;
|
|
9
|
+
hasUiSource: boolean;
|
|
10
|
+
hasPrdSource: boolean;
|
|
11
|
+
hasPrdKnowledgeSignal: boolean;
|
|
12
|
+
hasReviewSignal: boolean;
|
|
13
|
+
hasBugSignal: boolean;
|
|
14
|
+
hasTestSignal: boolean;
|
|
15
|
+
hasApiSignal: boolean;
|
|
16
|
+
hasRequirementChange: boolean;
|
|
17
|
+
hasDocumentationSignal: boolean;
|
|
18
|
+
hasExplicitDocumentationTarget: boolean;
|
|
19
|
+
hasMaintenanceSignal: boolean;
|
|
20
|
+
hasRefactorSignal: boolean;
|
|
21
|
+
hasComponentPlacementSignal: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function detectTaskSignals(scope: string, input?: TaskSignalInput): TaskSignals;
|
|
24
|
+
export declare function hasRoleSignal(value: string, role: ComponentRoleSignal): boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function detectTaskSignals(scope, input = {}) {
|
|
2
|
+
const normalized = scope.toLowerCase();
|
|
3
|
+
const hasAttachedUiSource = Boolean(input.ui);
|
|
4
|
+
const hasUiImplementationSignal = hasUiImplementationSourceSignal(normalized);
|
|
5
|
+
const hasUiSource = hasAttachedUiSource || /\b(ui)\b/.test(normalized) || hasUiImplementationSignal;
|
|
6
|
+
const hasPrdSource = Boolean(input.prd) || /\b(prd|product|requirement|requirements|knowledge|document)\b|\u4ea7\u54c1|\u9700\u6c42|\u77e5\u8bc6\u5e93|\u6587\u6863/.test(normalized);
|
|
7
|
+
const hasPrdKnowledgeSignal = hasPrdSource &&
|
|
8
|
+
(Boolean(input.prd) || /\b(prd|requirement|requirements|knowledge)\b|\u9700\u6c42|\u77e5\u8bc6\u5e93/.test(normalized)) &&
|
|
9
|
+
/\b(digest|distill|capture|index|knowledge|document|organize|summari[sz]e)\b|\u6c89\u6dc0|\u6574\u7406|\u7d22\u5f15|\u77e5\u8bc6\u5e93|\u63d0\u70bc|\u68b3\u7406|\u603b\u7ed3/.test(normalized);
|
|
10
|
+
return {
|
|
11
|
+
hasAttachedUiSource,
|
|
12
|
+
hasUiImplementationSignal,
|
|
13
|
+
hasUiSource,
|
|
14
|
+
hasPrdSource,
|
|
15
|
+
hasPrdKnowledgeSignal,
|
|
16
|
+
hasReviewSignal: /\b(review|code review|audit|handoff)\b|\u8bc4\u5ba1|\u4ee3\u7801\u5ba1\u67e5|\u5ba1\u67e5|\u5ba1\u6838|\u590d\u6838|\u4ea4\u4ed8/.test(normalized),
|
|
17
|
+
hasBugSignal: /\b(bug|fix|regression|error)\b|\u4fee\u590d|\u9519\u8bef|\u62a5\u9519|\u7f3a\u9677/.test(normalized),
|
|
18
|
+
hasTestSignal: /\b(e2e|integration test|test|playwright|vitest)\b|\u6d4b\u8bd5|\u7aef\u5230\u7aef|\u96c6\u6210\u6d4b\u8bd5/.test(normalized),
|
|
19
|
+
hasApiSignal: /\b(api|swagger|endpoint|interface|request|dto)\b|\u63a5\u53e3|\u8bf7\u6c42|\u5bf9\u63a5|\u8054\u8c03/.test(normalized),
|
|
20
|
+
hasRequirementChange: /\b(requirement changes?|changed [a-z0-9 -]*requirements?|update [a-z0-9 -]*requirements?|sync [a-z0-9 -]*requirements?)\b|\u9700\u6c42\u53d8\u66f4|\u53d8\u66f4\u9700\u6c42|\u540c\u6b65\u9700\u6c42/.test(normalized),
|
|
21
|
+
hasDocumentationSignal: /\b(readme|docs?|documentation|guide|manual|agents\.md|claude\.md)\b|\u6587\u6863|\u8bf4\u660e|\u6307\u5357/.test(normalized),
|
|
22
|
+
hasExplicitDocumentationTarget: /\b(readme|docs?|guide|manual|agents\.md|claude\.md)\b|\u6587\u6863|\u8bf4\u660e|\u6307\u5357/.test(normalized),
|
|
23
|
+
hasMaintenanceSignal: /\b(cli|runtime|config|package|script|scaffold|harness|protocol|workflow|planning|planner|resolver|policy|maintenance|refactor)\b|\u811a\u624b\u67b6|\u534f\u8bae|\u89c4\u5212|\u5206\u6d3e|\u7ef4\u62a4|\u91cd\u6784|\u51c6\u786e\u6027/.test(normalized),
|
|
24
|
+
hasRefactorSignal: /\brefactor\b|\u91cd\u6784/.test(normalized),
|
|
25
|
+
hasComponentPlacementSignal: hasRoleSignal(normalized, "component")
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function hasRoleSignal(value, role) {
|
|
29
|
+
const normalized = value.toLowerCase();
|
|
30
|
+
switch (role) {
|
|
31
|
+
case "page":
|
|
32
|
+
return /\b(page|screen|dashboard|view|route|layout|detail)\b|\u9875\u9762|\u754c\u9762|\u89c6\u56fe|\u770b\u677f/.test(normalized);
|
|
33
|
+
case "table":
|
|
34
|
+
return /\b(table|grid|list)\b|\u8868\u683c|\u5217\u8868|\u6e05\u5355/.test(normalized);
|
|
35
|
+
case "form":
|
|
36
|
+
return /\b(form|create|edit|filter|search|input|select|modal|dialog|button|widget|card)\b|\u8868\u5355|\u65b0\u589e|\u7f16\u8f91|\u7b5b\u9009|\u641c\u7d22|\u67e5\u8be2|\u8fc7\u6ee4|\u8f93\u5165|\u9009\u62e9|\u5f39\u7a97|\u6309\u94ae|\u5361\u7247/.test(normalized);
|
|
37
|
+
case "component":
|
|
38
|
+
return /\b(component|widget|card|modal|dialog|button|input|select)\b|\u7ec4\u4ef6|\u5361\u7247|\u5f39\u7a97|\u6309\u94ae|\u8f93\u5165|\u9009\u62e9/.test(normalized);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function hasUiImplementationSourceSignal(normalized) {
|
|
42
|
+
return (/\b(stitch|figma|pixso|dst|design|screenshot|selected|restore)\b|\u622a\u56fe|\u9009\u4e2d|\u8fd8\u539f|\u8bbe\u8ba1/.test(normalized) ||
|
|
43
|
+
/\b(?:pixso|figma|stitch)\s+mcp\b|\bmcp\s+(?:selected|selection|design|page|screen|ui)\b/.test(normalized) ||
|
|
44
|
+
/\bhtml\s+(?:mockup|prototype|design|screen|page|ui)\b|\b(?:restore|adapt|convert)\s+html\b/.test(normalized) ||
|
|
45
|
+
hasRoleSignal(normalized, "page"));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=task-signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-signals.js","sourceRoot":"","sources":["../../src/runtime/task-signals.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,QAAyB,EAAE;IAC1E,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9C,MAAM,yBAAyB,GAAG,+BAA+B,CAAC,UAAU,CAAC,CAAC;IAC9E,MAAM,WAAW,GAAG,mBAAmB,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,yBAAyB,CAAC;IACpG,MAAM,YAAY,GAChB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,yHAAyH,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnK,MAAM,qBAAqB,GACzB,YAAY;QACZ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,8EAA8E,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvH,8KAA8K,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAElM,OAAO;QACL,mBAAmB;QACnB,yBAAyB;QACzB,WAAW;QACX,YAAY;QACZ,qBAAqB;QACrB,eAAe,EAAE,kIAAkI,CAAC,IAAI,CAAC,UAAU,CAAC;QACpK,YAAY,EAAE,oFAAoF,CAAC,IAAI,CAAC,UAAU,CAAC;QACnH,aAAa,EAAE,4GAA4G,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5I,YAAY,EAAE,sGAAsG,CAAC,IAAI,CAAC,UAAU,CAAC;QACrI,oBAAoB,EAClB,sMAAsM,CAAC,IAAI,CAAC,UAAU,CAAC;QACzN,sBAAsB,EAAE,4GAA4G,CAAC,IAAI,CAAC,UAAU,CAAC;QACrJ,8BAA8B,EAAE,8FAA8F,CAAC,IAAI,CAAC,UAAU,CAAC;QAC/I,oBAAoB,EAClB,yOAAyO,CAAC,IAAI,CAAC,UAAU,CAAC;QAC5P,iBAAiB,EAAE,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC;QAC/D,2BAA2B,EAAE,aAAa,CAAC,UAAU,EAAE,WAAW,CAAC;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,IAAyB;IACpE,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,0GAA0G,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrI,KAAK,OAAO;YACV,OAAO,8DAA8D,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACzF,KAAK,MAAM;YACT,OAAO,+OAA+O,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1Q,KAAK,WAAW;YACd,OAAO,4IAA4I,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzK,CAAC;AACH,CAAC;AAED,SAAS,+BAA+B,CAAC,UAAkB;IACzD,OAAO,CACL,qHAAqH,CAAC,IAAI,CAAC,UAAU,CAAC;QACtI,yFAAyF,CAAC,IAAI,CAAC,UAAU,CAAC;QAC1G,4FAA4F,CAAC,IAAI,CAAC,UAAU,CAAC;QAC7G,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAClC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const defaultVerificationCommandNames: readonly ["typecheck", "test", "build"];
|
|
2
|
+
export type DefaultVerificationCommandName = typeof defaultVerificationCommandNames[number];
|
|
3
|
+
export type VerificationCommandName = DefaultVerificationCommandName | string;
|
|
4
|
+
export type VerificationCommandSource = "flag" | "config" | "package" | "none";
|
|
5
|
+
export type VerificationCommandOverrides = Partial<Record<DefaultVerificationCommandName, string>>;
|
|
6
|
+
export interface VerificationCommandDiscovery {
|
|
7
|
+
name: VerificationCommandName;
|
|
8
|
+
source: VerificationCommandSource;
|
|
9
|
+
command: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function discoverVerificationCommands(projectRoot: string, overrides?: VerificationCommandOverrides): VerificationCommandDiscovery[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { harnessPath } from "../storage/paths.js";
|
|
4
|
+
import { readJson } from "../storage/json.js";
|
|
5
|
+
import { isRecord } from "./common/parsing.js";
|
|
6
|
+
export const defaultVerificationCommandNames = ["typecheck", "test", "build"];
|
|
7
|
+
export function discoverVerificationCommands(projectRoot, overrides = {}) {
|
|
8
|
+
const config = readJson(harnessPath(projectRoot, "config.json")) ?? {};
|
|
9
|
+
const scripts = readPackageScripts(projectRoot);
|
|
10
|
+
const commands = defaultVerificationCommandNames.map((name) => {
|
|
11
|
+
const overrideCommand = overrides[name];
|
|
12
|
+
if (overrideCommand) {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
source: "flag",
|
|
16
|
+
command: overrideCommand
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const configCommand = config.verification?.[name];
|
|
20
|
+
if (configCommand) {
|
|
21
|
+
return {
|
|
22
|
+
name,
|
|
23
|
+
source: "config",
|
|
24
|
+
command: configCommand
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (scripts[name]) {
|
|
28
|
+
return {
|
|
29
|
+
name,
|
|
30
|
+
source: "package",
|
|
31
|
+
command: `npm run ${name}`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
name,
|
|
36
|
+
source: "none",
|
|
37
|
+
command: null
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
const customCommands = readCustomConfigCommands(config);
|
|
41
|
+
const defaultNames = new Set(defaultVerificationCommandNames);
|
|
42
|
+
const configuredNames = new Set(commands.map((entry) => entry.name));
|
|
43
|
+
for (const customCommand of customCommands) {
|
|
44
|
+
if (defaultNames.has(customCommand.name) || configuredNames.has(customCommand.name)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
commands.push(customCommand);
|
|
48
|
+
configuredNames.add(customCommand.name);
|
|
49
|
+
}
|
|
50
|
+
return commands;
|
|
51
|
+
}
|
|
52
|
+
function readCustomConfigCommands(config) {
|
|
53
|
+
if (!Array.isArray(config.verification?.commands)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const commands = [];
|
|
57
|
+
const names = new Set();
|
|
58
|
+
for (const entry of config.verification.commands) {
|
|
59
|
+
if (!isRecord(entry)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const name = typeof entry["name"] === "string" ? entry["name"].trim() : "";
|
|
63
|
+
const command = typeof entry["command"] === "string" ? entry["command"].trim() : "";
|
|
64
|
+
if (!isValidCommandName(name) || !command || names.has(name)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
commands.push({
|
|
68
|
+
name,
|
|
69
|
+
source: "config",
|
|
70
|
+
command
|
|
71
|
+
});
|
|
72
|
+
names.add(name);
|
|
73
|
+
}
|
|
74
|
+
return commands;
|
|
75
|
+
}
|
|
76
|
+
function readPackageScripts(projectRoot) {
|
|
77
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
78
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
let packageJson;
|
|
82
|
+
try {
|
|
83
|
+
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
return packageJson.scripts ?? {};
|
|
89
|
+
}
|
|
90
|
+
function isValidCommandName(value) {
|
|
91
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,79}$/.test(value);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=verification-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-commands.js","sourceRoot":"","sources":["../../src/runtime/verification-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function ensureDir(dir: string): void;
|
|
2
|
+
export declare function writeJson(filePath: string, value: unknown): void;
|
|
3
|
+
export declare function writeJsonExclusive(filePath: string, value: unknown): void;
|
|
4
|
+
export declare function readJson<T>(filePath: string): T | null;
|
|
5
|
+
export declare function writeText(filePath: string, value: string): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function ensureDir(dir) {
|
|
4
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
5
|
+
}
|
|
6
|
+
export function writeJson(filePath, value) {
|
|
7
|
+
ensureDir(path.dirname(filePath));
|
|
8
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
9
|
+
}
|
|
10
|
+
export function writeJsonExclusive(filePath, value) {
|
|
11
|
+
ensureDir(path.dirname(filePath));
|
|
12
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
13
|
+
}
|
|
14
|
+
export function readJson(filePath) {
|
|
15
|
+
if (!fs.existsSync(filePath)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function writeText(filePath, value) {
|
|
26
|
+
ensureDir(path.dirname(filePath));
|
|
27
|
+
fs.writeFileSync(filePath, value, "utf8");
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/storage/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAc;IACxD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAc;IACjE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,QAAgB;IAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAM,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAa;IACvD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export const HARNESS_DIR = ".frontend-harness";
|
|
3
|
+
export function harnessPath(projectRoot, ...parts) {
|
|
4
|
+
return path.join(projectRoot, HARNESS_DIR, ...parts);
|
|
5
|
+
}
|
|
6
|
+
export function relativeHarnessPath(...parts) {
|
|
7
|
+
return path.posix.join(HARNESS_DIR, ...parts);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAE/C,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,GAAG,KAAe;IACjE,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAG,KAAe;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC;AAChD,CAAC"}
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
Use this checklist for every npm release of `frontend-harness`.
|
|
4
|
+
|
|
5
|
+
## Preconditions
|
|
6
|
+
|
|
7
|
+
- Work from a clean git tree except for the intended release changes.
|
|
8
|
+
- Confirm `package.json` and `package-lock.json` have the same version.
|
|
9
|
+
- Update `CHANGELOG.md` for the version being released.
|
|
10
|
+
- Keep `package.json#files` aligned with the intended npm package contents.
|
|
11
|
+
|
|
12
|
+
## Verification
|
|
13
|
+
|
|
14
|
+
Run the full project gate:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm run verify
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Inspect the npm package contents before publishing:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm_config_cache=/tmp/frontend-harness-npm-cache npm pack --dry-run
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The temporary npm cache avoids local global-cache permission issues on machines where `~/.npm` contains root-owned files.
|
|
27
|
+
|
|
28
|
+
Confirm the dry-run output includes only intended public artifacts, normally:
|
|
29
|
+
|
|
30
|
+
- `dist/**`
|
|
31
|
+
- `README.md`
|
|
32
|
+
- `CHANGELOG.md`
|
|
33
|
+
- `docs/DIRECTION.md`
|
|
34
|
+
- `docs/RELEASE.md`
|
|
35
|
+
- `package.json`
|
|
36
|
+
|
|
37
|
+
Create and smoke-test the exact tarball that will be published:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm_config_cache=/tmp/frontend-harness-npm-cache npm pack --pack-destination /tmp
|
|
41
|
+
SMOKE_DIR="$(mktemp -d /tmp/frontend-harness-smoke-XXXXXX)"
|
|
42
|
+
cd "$SMOKE_DIR"
|
|
43
|
+
npm init -y
|
|
44
|
+
npm install /tmp/frontend-harness-<version>.tgz --save-dev
|
|
45
|
+
npx frontend-harness --help
|
|
46
|
+
npx frontend-harness scaffold --preset vue --target my-vue-app --dry-run --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The smoke test must run from a temporary consumer project, not from this source checkout. It catches missing published files such as `dist/runtime/*.js` imports before npm publish.
|
|
50
|
+
|
|
51
|
+
## Versioning
|
|
52
|
+
|
|
53
|
+
Use semver:
|
|
54
|
+
|
|
55
|
+
- Patch: bug fixes, stricter gates, protocol guidance clarifications, and compatible planner improvements.
|
|
56
|
+
- Minor: new commands, new public artifacts, or changed generated protocol expectations.
|
|
57
|
+
- Major: reserved for post-1.0 breaking changes.
|
|
58
|
+
|
|
59
|
+
For a version bump:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm version patch --no-git-tag-version
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use `minor` instead of `patch` when the release changes consumer-visible workflow contracts.
|
|
66
|
+
|
|
67
|
+
## Publish
|
|
68
|
+
|
|
69
|
+
After verification, tarball smoke testing, and review:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm publish --access public
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then create a git commit and tag if they were not already created:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git add package.json package-lock.json CHANGELOG.md docs/RELEASE.md
|
|
79
|
+
git commit -m "Release v<version>"
|
|
80
|
+
git tag v<version>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Push the commit and tag:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git push
|
|
87
|
+
git push origin v<version>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Post-Publish Smoke Test
|
|
91
|
+
|
|
92
|
+
In a temporary directory outside this repository, validate the published package by version:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx frontend-harness@<version> --help
|
|
96
|
+
npx frontend-harness@<version> scaffold --preset vue --target my-vue-app --dry-run --json
|
|
97
|
+
```
|