paqad-ai 0.1.4 → 0.1.5
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/dist/cli/index.js +1208 -420
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +58 -2
- package/dist/index.js +1521 -494
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/base/skills/diff-doc-sync/SKILL.md +2 -2
- package/runtime/hooks/stale-doc-detector.sh +3 -8
package/dist/cli/index.js
CHANGED
|
@@ -360,13 +360,18 @@ function buildDetectedStackProfile(input) {
|
|
|
360
360
|
if (input.fallbackStack === "laravel") {
|
|
361
361
|
frameworks.add("laravel");
|
|
362
362
|
}
|
|
363
|
-
const versionBands =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
363
|
+
const versionBands = Array.from(
|
|
364
|
+
input.packages.filter((pkg) => VERSIONED_PACKAGES.has(pkg.name)).sort(compareVersionBandPackages).reduce(
|
|
365
|
+
(bands, pkg) => bands.has(pkg.name) ? bands : bands.set(pkg.name, {
|
|
366
|
+
name: `${pkg.name}:${majorBand(pkg.locked_version || pkg.version_constraint)}`,
|
|
367
|
+
package_name: pkg.name,
|
|
368
|
+
range: majorBand(pkg.locked_version || pkg.version_constraint),
|
|
369
|
+
locked_version: pkg.locked_version,
|
|
370
|
+
source: pkg.locked_version !== "" && pkg.locked_version !== pkg.version_constraint ? "lockfile" : "manifest"
|
|
371
|
+
}),
|
|
372
|
+
/* @__PURE__ */ new Map()
|
|
373
|
+
).values()
|
|
374
|
+
).sort((left, right) => left.package_name.localeCompare(right.package_name));
|
|
370
375
|
return {
|
|
371
376
|
frameworks: Array.from(frameworks).sort(compareFrameworks),
|
|
372
377
|
traits: Array.from(traits).sort(),
|
|
@@ -498,6 +503,15 @@ function deriveReviewTargets(changes) {
|
|
|
498
503
|
function compareFrameworks(left, right) {
|
|
499
504
|
return frameworkRank(left) - frameworkRank(right) || left.localeCompare(right);
|
|
500
505
|
}
|
|
506
|
+
function compareVersionBandPackages(left, right) {
|
|
507
|
+
return rootDepth(left.root) - rootDepth(right.root) || left.name.localeCompare(right.name);
|
|
508
|
+
}
|
|
509
|
+
function rootDepth(root) {
|
|
510
|
+
if (!root || root === ".") {
|
|
511
|
+
return 0;
|
|
512
|
+
}
|
|
513
|
+
return root.split("/").filter(Boolean).length;
|
|
514
|
+
}
|
|
501
515
|
function frameworkRank(name) {
|
|
502
516
|
switch (name) {
|
|
503
517
|
case "laravel":
|
|
@@ -749,7 +763,58 @@ var detection_report_schema_default = {
|
|
|
749
763
|
}
|
|
750
764
|
}
|
|
751
765
|
},
|
|
752
|
-
timestamp: { type: "string" }
|
|
766
|
+
timestamp: { type: "string" },
|
|
767
|
+
repository: {
|
|
768
|
+
type: "object",
|
|
769
|
+
additionalProperties: false,
|
|
770
|
+
required: [
|
|
771
|
+
"selected_root",
|
|
772
|
+
"scan_max_depth",
|
|
773
|
+
"ignored_paths",
|
|
774
|
+
"projects",
|
|
775
|
+
"applications",
|
|
776
|
+
"primary_project_root"
|
|
777
|
+
],
|
|
778
|
+
properties: {
|
|
779
|
+
selected_root: { type: "string" },
|
|
780
|
+
scan_max_depth: { type: "integer" },
|
|
781
|
+
ignored_paths: { type: "array", items: { type: "string" } },
|
|
782
|
+
primary_project_root: { type: ["string", "null"] },
|
|
783
|
+
projects: {
|
|
784
|
+
type: "array",
|
|
785
|
+
items: {
|
|
786
|
+
type: "object",
|
|
787
|
+
additionalProperties: false,
|
|
788
|
+
required: ["root", "role", "parent_root", "markers", "ecosystems"],
|
|
789
|
+
properties: {
|
|
790
|
+
root: { type: "string" },
|
|
791
|
+
role: { type: "string", enum: ["standalone", "component"] },
|
|
792
|
+
parent_root: { type: ["string", "null"] },
|
|
793
|
+
markers: { type: "array", items: { type: "string" } },
|
|
794
|
+
ecosystems: {
|
|
795
|
+
type: "array",
|
|
796
|
+
items: {
|
|
797
|
+
type: "string",
|
|
798
|
+
enum: ["node", "php", "python", "ruby", "jvm", "go", "rust", "dart"]
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
applications: {
|
|
805
|
+
type: "array",
|
|
806
|
+
items: {
|
|
807
|
+
type: "object",
|
|
808
|
+
additionalProperties: false,
|
|
809
|
+
required: ["root", "component_roots"],
|
|
810
|
+
properties: {
|
|
811
|
+
root: { type: "string" },
|
|
812
|
+
component_roots: { type: "array", items: { type: "string" } }
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
753
818
|
}
|
|
754
819
|
};
|
|
755
820
|
|
|
@@ -885,6 +950,93 @@ var error_catalog_schema_default = {
|
|
|
885
950
|
}
|
|
886
951
|
};
|
|
887
952
|
|
|
953
|
+
// src/validators/schemas/feature-development-policy.schema.json
|
|
954
|
+
var feature_development_policy_schema_default = {
|
|
955
|
+
$id: "feature-development-policy",
|
|
956
|
+
type: "object",
|
|
957
|
+
additionalProperties: false,
|
|
958
|
+
required: ["schema_version", "stages"],
|
|
959
|
+
properties: {
|
|
960
|
+
schema_version: { type: "string", const: "1" },
|
|
961
|
+
merge_mode: { type: "string", enum: ["append"] },
|
|
962
|
+
stages: {
|
|
963
|
+
type: "object",
|
|
964
|
+
additionalProperties: false,
|
|
965
|
+
properties: {
|
|
966
|
+
planning: { $ref: "#/$defs/stagePolicy" },
|
|
967
|
+
specification: { $ref: "#/$defs/stagePolicy" },
|
|
968
|
+
development: { $ref: "#/$defs/stagePolicy" },
|
|
969
|
+
review: { $ref: "#/$defs/stagePolicy" },
|
|
970
|
+
checks: { $ref: "#/$defs/stagePolicy" },
|
|
971
|
+
documentation_sync: { $ref: "#/$defs/stagePolicy" }
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
$defs: {
|
|
976
|
+
stagePolicy: {
|
|
977
|
+
type: "object",
|
|
978
|
+
additionalProperties: false,
|
|
979
|
+
properties: {
|
|
980
|
+
read: {
|
|
981
|
+
type: "array",
|
|
982
|
+
items: { type: "string" }
|
|
983
|
+
},
|
|
984
|
+
instructions: {
|
|
985
|
+
type: "array",
|
|
986
|
+
items: { type: "string" }
|
|
987
|
+
},
|
|
988
|
+
required_inputs: {
|
|
989
|
+
type: "array",
|
|
990
|
+
items: { type: "string" }
|
|
991
|
+
},
|
|
992
|
+
strictness: {
|
|
993
|
+
type: "object",
|
|
994
|
+
additionalProperties: { type: "boolean" }
|
|
995
|
+
},
|
|
996
|
+
escalation: {
|
|
997
|
+
type: "object",
|
|
998
|
+
additionalProperties: {
|
|
999
|
+
type: "string",
|
|
1000
|
+
enum: ["warn", "ask", "stop"]
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
artifacts: {
|
|
1004
|
+
type: "array",
|
|
1005
|
+
items: { type: "string" }
|
|
1006
|
+
},
|
|
1007
|
+
checks: {
|
|
1008
|
+
type: "object",
|
|
1009
|
+
additionalProperties: false,
|
|
1010
|
+
properties: {
|
|
1011
|
+
use_project_profile_commands: { type: "boolean" },
|
|
1012
|
+
commands: {
|
|
1013
|
+
type: "array",
|
|
1014
|
+
items: {
|
|
1015
|
+
type: "string",
|
|
1016
|
+
enum: [
|
|
1017
|
+
"install",
|
|
1018
|
+
"dev",
|
|
1019
|
+
"test",
|
|
1020
|
+
"test_single",
|
|
1021
|
+
"lint",
|
|
1022
|
+
"format",
|
|
1023
|
+
"migrate",
|
|
1024
|
+
"build"
|
|
1025
|
+
]
|
|
1026
|
+
}
|
|
1027
|
+
},
|
|
1028
|
+
shell_commands: {
|
|
1029
|
+
type: "array",
|
|
1030
|
+
items: { type: "string" }
|
|
1031
|
+
},
|
|
1032
|
+
block_on_failure: { type: "boolean" }
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
|
|
888
1040
|
// src/validators/schemas/gate-result.schema.json
|
|
889
1041
|
var gate_result_schema_default = {
|
|
890
1042
|
$id: "gate-result",
|
|
@@ -1004,6 +1156,7 @@ var onboarding_manifest_schema_default = {
|
|
|
1004
1156
|
"project_root",
|
|
1005
1157
|
"profile",
|
|
1006
1158
|
"detected",
|
|
1159
|
+
"repository",
|
|
1007
1160
|
"generated_at",
|
|
1008
1161
|
"generated_artifacts"
|
|
1009
1162
|
],
|
|
@@ -1013,6 +1166,9 @@ var onboarding_manifest_schema_default = {
|
|
|
1013
1166
|
project_root: { type: "string" },
|
|
1014
1167
|
profile: { $ref: "project-profile" },
|
|
1015
1168
|
detected: { anyOf: [{ $ref: "detection-report" }, { type: "null" }] },
|
|
1169
|
+
repository: {
|
|
1170
|
+
anyOf: [{ $ref: "detection-report#/properties/repository" }, { type: "null" }]
|
|
1171
|
+
},
|
|
1016
1172
|
generated_at: { type: "string" },
|
|
1017
1173
|
generated_artifacts: {
|
|
1018
1174
|
type: "array",
|
|
@@ -1814,6 +1970,7 @@ var SCHEMAS = [
|
|
|
1814
1970
|
api_endpoint_doc_schema_default,
|
|
1815
1971
|
integration_doc_schema_default,
|
|
1816
1972
|
error_catalog_schema_default,
|
|
1973
|
+
feature_development_policy_schema_default,
|
|
1817
1974
|
skill_frontmatter_schema_default,
|
|
1818
1975
|
gate_result_schema_default,
|
|
1819
1976
|
stack_pack_schema_default
|
|
@@ -2607,24 +2764,232 @@ function removeActiveCapability(profile, capability) {
|
|
|
2607
2764
|
|
|
2608
2765
|
// src/detection/detector.ts
|
|
2609
2766
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
2610
|
-
import { join as
|
|
2767
|
+
import { join as join16 } from "path";
|
|
2611
2768
|
import fg from "fast-glob";
|
|
2612
2769
|
|
|
2770
|
+
// src/introspection/stack-introspector.ts
|
|
2771
|
+
import { join as join14 } from "path";
|
|
2772
|
+
|
|
2773
|
+
// src/repository/discovery.ts
|
|
2774
|
+
import { readdir } from "fs/promises";
|
|
2775
|
+
import { basename as basename3, join as join10 } from "path";
|
|
2776
|
+
var DEFAULT_SCAN_MAX_DEPTH = 5;
|
|
2777
|
+
var PROJECT_MARKERS = /* @__PURE__ */ new Map([
|
|
2778
|
+
["package.json", "node"],
|
|
2779
|
+
["composer.json", "php"],
|
|
2780
|
+
["pubspec.yaml", "dart"],
|
|
2781
|
+
["pyproject.toml", "python"],
|
|
2782
|
+
["requirements.txt", "python"],
|
|
2783
|
+
["Pipfile", "python"],
|
|
2784
|
+
["setup.py", "python"],
|
|
2785
|
+
["Gemfile", "ruby"],
|
|
2786
|
+
["go.mod", "go"],
|
|
2787
|
+
["Cargo.toml", "rust"],
|
|
2788
|
+
["pom.xml", "jvm"],
|
|
2789
|
+
["build.gradle", "jvm"],
|
|
2790
|
+
["build.gradle.kts", "jvm"],
|
|
2791
|
+
["artisan", "php"],
|
|
2792
|
+
["manage.py", "python"]
|
|
2793
|
+
]);
|
|
2794
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
2795
|
+
".git",
|
|
2796
|
+
".next",
|
|
2797
|
+
".nuxt",
|
|
2798
|
+
".dart_tool",
|
|
2799
|
+
".gradle",
|
|
2800
|
+
".turbo",
|
|
2801
|
+
"build",
|
|
2802
|
+
"coverage",
|
|
2803
|
+
"dist",
|
|
2804
|
+
"node_modules",
|
|
2805
|
+
"out",
|
|
2806
|
+
"target",
|
|
2807
|
+
"vendor"
|
|
2808
|
+
]);
|
|
2809
|
+
var NON_CANONICAL_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
2810
|
+
"__fixtures__",
|
|
2811
|
+
"demo",
|
|
2812
|
+
"demos",
|
|
2813
|
+
"example",
|
|
2814
|
+
"examples",
|
|
2815
|
+
"fixtures",
|
|
2816
|
+
"test-fixtures",
|
|
2817
|
+
"tmp"
|
|
2818
|
+
]);
|
|
2819
|
+
var COMPONENT_DIRECTORY_NAMES = /* @__PURE__ */ new Set(["client", "frontend", "ui", "web"]);
|
|
2820
|
+
async function discoverRepositoryContext(projectRoot, options) {
|
|
2821
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_SCAN_MAX_DEPTH;
|
|
2822
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
2823
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2824
|
+
await walk(projectRoot, ".", 0, maxDepth, candidates, ignoredPaths);
|
|
2825
|
+
const sortedCandidates = Array.from(candidates.values()).sort(compareCandidates);
|
|
2826
|
+
const projects = classifyProjects(sortedCandidates);
|
|
2827
|
+
const applications = buildApplications(projects);
|
|
2828
|
+
const primaryProjectRoot = resolvePrimaryProjectRoot(projects, applications);
|
|
2829
|
+
return {
|
|
2830
|
+
selected_root: projectRoot,
|
|
2831
|
+
scan_max_depth: maxDepth,
|
|
2832
|
+
ignored_paths: Array.from(ignoredPaths).sort(),
|
|
2833
|
+
projects,
|
|
2834
|
+
applications,
|
|
2835
|
+
primary_project_root: primaryProjectRoot
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
function prefixRepositoryPath(root, relativePath) {
|
|
2839
|
+
if (root === "." || root === "") {
|
|
2840
|
+
return relativePath;
|
|
2841
|
+
}
|
|
2842
|
+
return join10(root, relativePath);
|
|
2843
|
+
}
|
|
2844
|
+
async function walk(projectRoot, relativeDir, depth, maxDepth, candidates, ignoredPaths) {
|
|
2845
|
+
const absoluteDir = relativeDir === "." ? projectRoot : join10(projectRoot, relativeDir);
|
|
2846
|
+
let entries;
|
|
2847
|
+
try {
|
|
2848
|
+
entries = await readdir(absoluteDir, { withFileTypes: true });
|
|
2849
|
+
} catch {
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
const entryNames = new Set(entries.map((entry) => entry.name));
|
|
2853
|
+
const markers = Array.from(PROJECT_MARKERS.keys()).filter((marker) => entryNames.has(marker));
|
|
2854
|
+
if (markers.length > 0) {
|
|
2855
|
+
const ecosystems = Array.from(
|
|
2856
|
+
new Set(markers.map((marker) => PROJECT_MARKERS.get(marker)).filter(isDefined))
|
|
2857
|
+
).sort();
|
|
2858
|
+
candidates.set(relativeDir, {
|
|
2859
|
+
root: relativeDir,
|
|
2860
|
+
role: "standalone",
|
|
2861
|
+
parent_root: null,
|
|
2862
|
+
markers: markers.sort(),
|
|
2863
|
+
ecosystems
|
|
2864
|
+
});
|
|
2865
|
+
}
|
|
2866
|
+
if (depth >= maxDepth) {
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
for (const entry of entries) {
|
|
2870
|
+
if (!entry.isDirectory()) {
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
const childRelativePath = relativeDir === "." ? entry.name : join10(relativeDir, entry.name);
|
|
2874
|
+
if (IGNORED_DIRECTORIES.has(entry.name) || NON_CANONICAL_DIRECTORIES.has(entry.name)) {
|
|
2875
|
+
ignoredPaths.add(childRelativePath);
|
|
2876
|
+
continue;
|
|
2877
|
+
}
|
|
2878
|
+
await walk(projectRoot, childRelativePath, depth + 1, maxDepth, candidates, ignoredPaths);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
function classifyProjects(candidates) {
|
|
2882
|
+
const byRoot = new Map(candidates.map((candidate) => [candidate.root, candidate]));
|
|
2883
|
+
const result = [];
|
|
2884
|
+
for (const candidate of candidates) {
|
|
2885
|
+
const ancestors = findAncestorCandidates(candidate.root, byRoot);
|
|
2886
|
+
const parent = ancestors[0] ?? null;
|
|
2887
|
+
const role = parent && isLinkedComponent(parent, candidate) ? "component" : "standalone";
|
|
2888
|
+
result.push({
|
|
2889
|
+
...candidate,
|
|
2890
|
+
role,
|
|
2891
|
+
parent_root: role === "component" ? parent.root : null
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
return result.sort(compareCandidates);
|
|
2895
|
+
}
|
|
2896
|
+
function findAncestorCandidates(root, candidates) {
|
|
2897
|
+
if (root === ".") {
|
|
2898
|
+
return [];
|
|
2899
|
+
}
|
|
2900
|
+
const segments = root.split("/").filter(Boolean);
|
|
2901
|
+
const ancestors = [];
|
|
2902
|
+
for (let index = segments.length - 1; index >= 1; index -= 1) {
|
|
2903
|
+
const candidate = candidates.get(segments.slice(0, index).join("/"));
|
|
2904
|
+
if (candidate) {
|
|
2905
|
+
ancestors.push(candidate);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
const selectedRoot = candidates.get(".");
|
|
2909
|
+
if (selectedRoot) {
|
|
2910
|
+
ancestors.push(selectedRoot);
|
|
2911
|
+
}
|
|
2912
|
+
return ancestors.sort(compareCandidates);
|
|
2913
|
+
}
|
|
2914
|
+
function isLinkedComponent(parent, candidate) {
|
|
2915
|
+
const candidateName = basename3(candidate.root);
|
|
2916
|
+
const candidateNodeOnly = candidate.ecosystems.length > 0 && candidate.ecosystems.every((ecosystem) => ecosystem === "node");
|
|
2917
|
+
const parentHasNonNode = parent.ecosystems.some((ecosystem) => ecosystem !== "node");
|
|
2918
|
+
return candidateNodeOnly && parentHasNonNode && COMPONENT_DIRECTORY_NAMES.has(candidateName);
|
|
2919
|
+
}
|
|
2920
|
+
function buildApplications(projects) {
|
|
2921
|
+
const standaloneRoots = projects.filter((project) => project.role === "standalone").map((project) => project.root);
|
|
2922
|
+
return standaloneRoots.map((root) => ({
|
|
2923
|
+
root,
|
|
2924
|
+
component_roots: projects.filter((project) => project.parent_root === root).map((project) => project.root).sort()
|
|
2925
|
+
}));
|
|
2926
|
+
}
|
|
2927
|
+
function resolvePrimaryProjectRoot(projects, applications) {
|
|
2928
|
+
if (applications.length === 0) {
|
|
2929
|
+
return null;
|
|
2930
|
+
}
|
|
2931
|
+
const byRoot = new Map(projects.map((project) => [project.root, project]));
|
|
2932
|
+
return [...applications].sort((left, right) => {
|
|
2933
|
+
const leftProject = byRoot.get(left.root);
|
|
2934
|
+
const rightProject = byRoot.get(right.root);
|
|
2935
|
+
return scoreCandidate(rightProject) - scoreCandidate(leftProject) || compareByRoot(left.root, right.root);
|
|
2936
|
+
})[0]?.root ?? null;
|
|
2937
|
+
}
|
|
2938
|
+
function compareCandidates(left, right) {
|
|
2939
|
+
return compareByRoot(left.root, right.root);
|
|
2940
|
+
}
|
|
2941
|
+
function compareByRoot(left, right) {
|
|
2942
|
+
return depthOf(left) - depthOf(right) || left.localeCompare(right);
|
|
2943
|
+
}
|
|
2944
|
+
function depthOf(root) {
|
|
2945
|
+
return root === "." ? 0 : root.split("/").filter(Boolean).length;
|
|
2946
|
+
}
|
|
2947
|
+
function scoreCandidate(candidate) {
|
|
2948
|
+
if (!candidate) {
|
|
2949
|
+
return -1;
|
|
2950
|
+
}
|
|
2951
|
+
return candidate.markers.reduce((score, marker) => score + markerScore(marker), 0) - depthOf(candidate.root);
|
|
2952
|
+
}
|
|
2953
|
+
function markerScore(marker) {
|
|
2954
|
+
switch (marker) {
|
|
2955
|
+
case "composer.json":
|
|
2956
|
+
case "package.json":
|
|
2957
|
+
case "pubspec.yaml":
|
|
2958
|
+
return 5;
|
|
2959
|
+
case "artisan":
|
|
2960
|
+
case "manage.py":
|
|
2961
|
+
return 4;
|
|
2962
|
+
case "pyproject.toml":
|
|
2963
|
+
case "Gemfile":
|
|
2964
|
+
case "go.mod":
|
|
2965
|
+
case "Cargo.toml":
|
|
2966
|
+
case "pom.xml":
|
|
2967
|
+
case "build.gradle":
|
|
2968
|
+
case "build.gradle.kts":
|
|
2969
|
+
return 3;
|
|
2970
|
+
default:
|
|
2971
|
+
return 1;
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
function isDefined(value) {
|
|
2975
|
+
return value !== void 0;
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2613
2978
|
// src/introspection/cache.ts
|
|
2614
2979
|
import { createHash as createHash5 } from "crypto";
|
|
2615
2980
|
import { mkdir as mkdir6, readFile as readFile6, stat as stat2, writeFile as writeFile6 } from "fs/promises";
|
|
2616
|
-
import { dirname as dirname9, join as
|
|
2981
|
+
import { dirname as dirname9, join as join11 } from "path";
|
|
2617
2982
|
var StackSnapshotCache = class {
|
|
2618
2983
|
async read(projectRoot) {
|
|
2619
2984
|
try {
|
|
2620
|
-
const raw = await readFile6(
|
|
2985
|
+
const raw = await readFile6(join11(projectRoot, PATHS.STACK_SNAPSHOT), "utf8");
|
|
2621
2986
|
return JSON.parse(raw);
|
|
2622
2987
|
} catch {
|
|
2623
2988
|
return null;
|
|
2624
2989
|
}
|
|
2625
2990
|
}
|
|
2626
2991
|
async write(projectRoot, snapshot) {
|
|
2627
|
-
const target =
|
|
2992
|
+
const target = join11(projectRoot, PATHS.STACK_SNAPSHOT);
|
|
2628
2993
|
await mkdir6(dirname9(target), { recursive: true });
|
|
2629
2994
|
await writeFile6(target, `${JSON.stringify(snapshot, null, 2)}
|
|
2630
2995
|
`);
|
|
@@ -2633,11 +2998,11 @@ var StackSnapshotCache = class {
|
|
|
2633
2998
|
const hashes = {};
|
|
2634
2999
|
for (const relativePath of relativePaths) {
|
|
2635
3000
|
try {
|
|
2636
|
-
const content = await readFile6(
|
|
3001
|
+
const content = await readFile6(join11(projectRoot, relativePath), "utf8");
|
|
2637
3002
|
hashes[relativePath] = `sha256:${createHash5("sha256").update(content).digest("hex")}`;
|
|
2638
3003
|
} catch {
|
|
2639
3004
|
try {
|
|
2640
|
-
const pathStat = await stat2(
|
|
3005
|
+
const pathStat = await stat2(join11(projectRoot, relativePath));
|
|
2641
3006
|
hashes[relativePath] = pathStat.isDirectory() ? "exists:directory" : "exists:file";
|
|
2642
3007
|
} catch {
|
|
2643
3008
|
continue;
|
|
@@ -2650,10 +3015,10 @@ var StackSnapshotCache = class {
|
|
|
2650
3015
|
|
|
2651
3016
|
// src/introspection/ecosystems/shared.ts
|
|
2652
3017
|
import { readFile as readFile7 } from "fs/promises";
|
|
2653
|
-
import { join as
|
|
3018
|
+
import { join as join12 } from "path";
|
|
2654
3019
|
async function readProjectFile(projectRoot, relativePath) {
|
|
2655
3020
|
try {
|
|
2656
|
-
return await readFile7(
|
|
3021
|
+
return await readFile7(join12(projectRoot, relativePath), "utf8");
|
|
2657
3022
|
} catch {
|
|
2658
3023
|
return null;
|
|
2659
3024
|
}
|
|
@@ -3133,7 +3498,7 @@ function createDefaultEcosystemParserRegistry() {
|
|
|
3133
3498
|
|
|
3134
3499
|
// src/introspection/environment-traits.ts
|
|
3135
3500
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
3136
|
-
import { join as
|
|
3501
|
+
import { join as join13 } from "path";
|
|
3137
3502
|
var COMPOSE_FILES = ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"];
|
|
3138
3503
|
function detectEnvironmentTraits(projectRoot, options) {
|
|
3139
3504
|
const packageNames = new Set(options?.packageNames ?? []);
|
|
@@ -3141,7 +3506,7 @@ function detectEnvironmentTraits(projectRoot, options) {
|
|
|
3141
3506
|
const sources = [];
|
|
3142
3507
|
const signals = [];
|
|
3143
3508
|
for (const file of COMPOSE_FILES) {
|
|
3144
|
-
if (existsSync3(
|
|
3509
|
+
if (existsSync3(join13(projectRoot, file))) {
|
|
3145
3510
|
traits.add("compose");
|
|
3146
3511
|
sources.push({
|
|
3147
3512
|
file,
|
|
@@ -3211,14 +3576,14 @@ function listDockerIndicators(projectRoot) {
|
|
|
3211
3576
|
const indicators = [];
|
|
3212
3577
|
const directFiles = ["Dockerfile", "Dockerfile.dev", "Dockerfile.prod"];
|
|
3213
3578
|
for (const file of directFiles) {
|
|
3214
|
-
if (existsSync3(
|
|
3579
|
+
if (existsSync3(join13(projectRoot, file))) {
|
|
3215
3580
|
indicators.push(file);
|
|
3216
3581
|
}
|
|
3217
3582
|
}
|
|
3218
|
-
if (existsSync3(
|
|
3583
|
+
if (existsSync3(join13(projectRoot, "docker"))) {
|
|
3219
3584
|
indicators.push("docker/");
|
|
3220
3585
|
}
|
|
3221
|
-
if (existsSync3(
|
|
3586
|
+
if (existsSync3(join13(projectRoot, ".docker"))) {
|
|
3222
3587
|
indicators.push(".docker/");
|
|
3223
3588
|
}
|
|
3224
3589
|
return indicators;
|
|
@@ -3230,7 +3595,7 @@ function detectSailReferenceFile(projectRoot) {
|
|
|
3230
3595
|
const candidates = ["composer.json", "package.json"];
|
|
3231
3596
|
for (const relativePath of candidates) {
|
|
3232
3597
|
try {
|
|
3233
|
-
const content = readFileSync3(
|
|
3598
|
+
const content = readFileSync3(join13(projectRoot, relativePath), "utf8");
|
|
3234
3599
|
if (content.includes("vendor/bin/sail")) {
|
|
3235
3600
|
return relativePath;
|
|
3236
3601
|
}
|
|
@@ -3246,50 +3611,101 @@ var StackIntrospector = class {
|
|
|
3246
3611
|
cache = new StackSnapshotCache();
|
|
3247
3612
|
parserRegistry = createDefaultEcosystemParserRegistry();
|
|
3248
3613
|
async snapshot(projectRoot) {
|
|
3249
|
-
const
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
"Dockerfile",
|
|
3256
|
-
"Dockerfile.dev",
|
|
3257
|
-
"Dockerfile.prod",
|
|
3258
|
-
"docker",
|
|
3259
|
-
".docker"
|
|
3260
|
-
]);
|
|
3614
|
+
const repository = await discoverRepositoryContext(projectRoot);
|
|
3615
|
+
const candidateRoots = repository.projects.map((project) => project.root);
|
|
3616
|
+
const currentHashes = await this.cache.hashFiles(
|
|
3617
|
+
projectRoot,
|
|
3618
|
+
buildRepositoryHashInputs(candidateRoots, this.parserRegistry.getKnownFiles())
|
|
3619
|
+
);
|
|
3261
3620
|
const cached = await this.cache.read(projectRoot);
|
|
3262
3621
|
if (cached !== null && JSON.stringify(cached.source_hashes) === JSON.stringify(currentHashes)) {
|
|
3263
3622
|
return cached;
|
|
3264
3623
|
}
|
|
3265
|
-
const
|
|
3266
|
-
const
|
|
3267
|
-
const
|
|
3268
|
-
const
|
|
3269
|
-
|
|
3270
|
-
|
|
3624
|
+
const toolchains = [];
|
|
3625
|
+
const packages = [];
|
|
3626
|
+
const environmentSources = [];
|
|
3627
|
+
const environmentTraits = /* @__PURE__ */ new Set();
|
|
3628
|
+
for (const project of repository.projects) {
|
|
3629
|
+
const absoluteRoot = project.root === "." ? projectRoot : join14(projectRoot, project.root);
|
|
3630
|
+
const results = await this.parserRegistry.parseProject(absoluteRoot);
|
|
3631
|
+
for (const result of results) {
|
|
3632
|
+
toolchains.push({
|
|
3633
|
+
...result.toolchain,
|
|
3634
|
+
lockfile: prefixRepositoryPath(project.root, result.toolchain.lockfile)
|
|
3635
|
+
});
|
|
3636
|
+
packages.push(
|
|
3637
|
+
...result.packages.map((pkg) => ({
|
|
3638
|
+
...pkg,
|
|
3639
|
+
root: project.root
|
|
3640
|
+
}))
|
|
3641
|
+
);
|
|
3642
|
+
}
|
|
3643
|
+
const packageNames = packages.filter((pkg) => pkg.root === project.root).map((pkg) => pkg.name);
|
|
3644
|
+
const environment = detectEnvironmentTraits(absoluteRoot, { packageNames });
|
|
3645
|
+
for (const trait of environment.traits) {
|
|
3646
|
+
environmentTraits.add(trait);
|
|
3647
|
+
}
|
|
3648
|
+
environmentSources.push(
|
|
3649
|
+
...environment.sources.map((source) => ({
|
|
3650
|
+
...source,
|
|
3651
|
+
file: prefixRepositoryPath(project.root, source.file)
|
|
3652
|
+
}))
|
|
3653
|
+
);
|
|
3654
|
+
}
|
|
3271
3655
|
const hashedSources = Object.keys(currentHashes).sort().map((file) => ({
|
|
3272
3656
|
file,
|
|
3273
3657
|
kind: classifySourceKind(file),
|
|
3274
3658
|
detail: classifySourceKind(file) === "config" ? "Used for environment trait detection" : "Used for stack detection and version resolution"
|
|
3275
3659
|
}));
|
|
3276
|
-
const sources = dedupeSources([...hashedSources, ...
|
|
3660
|
+
const sources = dedupeSources([...hashedSources, ...environmentSources]);
|
|
3277
3661
|
const snapshot = {
|
|
3278
3662
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3279
3663
|
source_hashes: currentHashes,
|
|
3280
|
-
toolchains,
|
|
3664
|
+
toolchains: dedupeToolchains(toolchains),
|
|
3281
3665
|
packages,
|
|
3282
3666
|
profile: buildDetectedStackProfile({
|
|
3283
|
-
toolchains,
|
|
3667
|
+
toolchains: dedupeToolchains(toolchains),
|
|
3284
3668
|
packages,
|
|
3285
3669
|
sources,
|
|
3286
|
-
detectedTraits:
|
|
3287
|
-
})
|
|
3670
|
+
detectedTraits: Array.from(environmentTraits).sort()
|
|
3671
|
+
}),
|
|
3672
|
+
repository
|
|
3288
3673
|
};
|
|
3289
3674
|
await this.cache.write(projectRoot, snapshot);
|
|
3290
3675
|
return snapshot;
|
|
3291
3676
|
}
|
|
3292
3677
|
};
|
|
3678
|
+
function buildRepositoryHashInputs(candidateRoots, knownFiles) {
|
|
3679
|
+
const inputs = /* @__PURE__ */ new Set();
|
|
3680
|
+
const extraFiles = [
|
|
3681
|
+
"docker-compose.yml",
|
|
3682
|
+
"docker-compose.yaml",
|
|
3683
|
+
"compose.yml",
|
|
3684
|
+
"compose.yaml",
|
|
3685
|
+
"Dockerfile",
|
|
3686
|
+
"Dockerfile.dev",
|
|
3687
|
+
"Dockerfile.prod",
|
|
3688
|
+
"docker",
|
|
3689
|
+
".docker"
|
|
3690
|
+
];
|
|
3691
|
+
for (const root of candidateRoots.length > 0 ? candidateRoots : ["."]) {
|
|
3692
|
+
for (const file of [...knownFiles, ...extraFiles]) {
|
|
3693
|
+
inputs.add(prefixRepositoryPath(root, file));
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
return Array.from(inputs).sort();
|
|
3697
|
+
}
|
|
3698
|
+
function dedupeToolchains(toolchains) {
|
|
3699
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3700
|
+
return toolchains.filter((toolchain) => {
|
|
3701
|
+
const key = `${toolchain.ecosystem}:${toolchain.package_manager}:${toolchain.lockfile}`;
|
|
3702
|
+
if (seen.has(key)) {
|
|
3703
|
+
return false;
|
|
3704
|
+
}
|
|
3705
|
+
seen.add(key);
|
|
3706
|
+
return true;
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3293
3709
|
function classifySourceKind(file) {
|
|
3294
3710
|
if (file.endsWith(".lock") || file.includes("lock")) {
|
|
3295
3711
|
return "lockfile";
|
|
@@ -3323,17 +3739,18 @@ function buildDetectionReport(input) {
|
|
|
3323
3739
|
detection_phase: input.detectionPhase,
|
|
3324
3740
|
confidence: input.confidence,
|
|
3325
3741
|
signals: input.signals,
|
|
3326
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3742
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3743
|
+
repository: input.repository
|
|
3327
3744
|
};
|
|
3328
3745
|
}
|
|
3329
3746
|
|
|
3330
3747
|
// src/detection/signals/short-video.ts
|
|
3331
3748
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
3332
|
-
import { join as
|
|
3749
|
+
import { join as join15 } from "path";
|
|
3333
3750
|
import { parse } from "yaml";
|
|
3334
3751
|
function detectShortVideoSignals(projectRoot) {
|
|
3335
|
-
const profilePath =
|
|
3336
|
-
const markerPath =
|
|
3752
|
+
const profilePath = join15(projectRoot, ".paqad", "project-profile.yaml");
|
|
3753
|
+
const markerPath = join15(projectRoot, ".short-video-project");
|
|
3337
3754
|
const signals = [];
|
|
3338
3755
|
if (existsSync4(profilePath)) {
|
|
3339
3756
|
const profile = parse(readFileSync4(profilePath, "utf8"));
|
|
@@ -3363,10 +3780,7 @@ var Detector = class {
|
|
|
3363
3780
|
packLoader = new StackPackLoader();
|
|
3364
3781
|
async detect(projectRoot) {
|
|
3365
3782
|
const snapshot = await this.introspector.snapshot(projectRoot);
|
|
3366
|
-
const
|
|
3367
|
-
const environment = detectEnvironmentTraits(projectRoot, {
|
|
3368
|
-
packageNames: snapshot.packages.map((pkg) => pkg.name)
|
|
3369
|
-
});
|
|
3783
|
+
const repository = snapshot.repository ?? emptyRepositoryContext(projectRoot);
|
|
3370
3784
|
const registry = this.packLoader.load({
|
|
3371
3785
|
runtimeRoot: getRuntimeRoot(),
|
|
3372
3786
|
projectRoot
|
|
@@ -3374,17 +3788,24 @@ var Detector = class {
|
|
|
3374
3788
|
const allPacks = Array.from(registry.packs.values());
|
|
3375
3789
|
const frameworkPacks = allPacks.filter((p) => (p.manifest.tier ?? "framework") === "framework");
|
|
3376
3790
|
const archetypePacks = allPacks.filter((p) => p.manifest.tier === "archetype");
|
|
3377
|
-
const
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3791
|
+
const applications = repository.applications.length > 0 ? repository.applications : [{ root: ".", component_roots: [] }];
|
|
3792
|
+
const applicationResults = applications.map(
|
|
3793
|
+
(application) => detectApplication(
|
|
3794
|
+
projectRoot,
|
|
3795
|
+
application,
|
|
3796
|
+
snapshot.packages,
|
|
3797
|
+
frameworkPacks,
|
|
3798
|
+
archetypePacks
|
|
3799
|
+
)
|
|
3800
|
+
).sort(compareApplicationResults);
|
|
3801
|
+
const primaryResult = applicationResults[0];
|
|
3802
|
+
const matchedPacks = primaryResult?.matches ?? [];
|
|
3803
|
+
const detectionPhase = primaryResult?.detectionPhase ?? "none";
|
|
3804
|
+
const repositoryEnvironment = mergeEnvironmentTraits(
|
|
3805
|
+
projectRoot,
|
|
3806
|
+
repository.projects.map((project) => project.root),
|
|
3807
|
+
snapshot.packages
|
|
3808
|
+
);
|
|
3388
3809
|
if (matchedPacks.length === 0) {
|
|
3389
3810
|
const shortVideoSignals = detectShortVideoSignals(projectRoot);
|
|
3390
3811
|
if (shortVideoSignals.length > 0) {
|
|
@@ -3395,58 +3816,97 @@ var Detector = class {
|
|
|
3395
3816
|
detectedTraits: [],
|
|
3396
3817
|
recommendedCapabilities: ["content"],
|
|
3397
3818
|
detectionPhase: "none",
|
|
3398
|
-
signals: [...shortVideoSignals, ...
|
|
3399
|
-
capabilities:
|
|
3400
|
-
confidence: "low"
|
|
3819
|
+
signals: [...shortVideoSignals, ...repositoryEnvironment.signals],
|
|
3820
|
+
capabilities: repositoryEnvironment.traits,
|
|
3821
|
+
confidence: "low",
|
|
3822
|
+
repository
|
|
3401
3823
|
});
|
|
3402
3824
|
}
|
|
3403
3825
|
return buildDetectionReport({
|
|
3404
3826
|
domain: null,
|
|
3405
3827
|
stack: null,
|
|
3406
3828
|
matchedPacks: [],
|
|
3407
|
-
detectedTraits:
|
|
3829
|
+
detectedTraits: repositoryEnvironment.traits,
|
|
3408
3830
|
recommendedCapabilities: ["content"],
|
|
3409
3831
|
detectionPhase: "none",
|
|
3410
|
-
capabilities:
|
|
3411
|
-
signals:
|
|
3412
|
-
confidence: "low"
|
|
3832
|
+
capabilities: repositoryEnvironment.traits,
|
|
3833
|
+
signals: repositoryEnvironment.signals,
|
|
3834
|
+
confidence: "low",
|
|
3835
|
+
repository
|
|
3413
3836
|
});
|
|
3414
3837
|
}
|
|
3415
3838
|
const topScore = matchedPacks[0].score;
|
|
3416
3839
|
const topMatches = matchedPacks.filter((match) => match.score === topScore);
|
|
3417
3840
|
const ambiguous = topMatches.length > 1;
|
|
3418
3841
|
const detectedTraits = Array.from(
|
|
3419
|
-
/* @__PURE__ */ new Set([
|
|
3842
|
+
/* @__PURE__ */ new Set([
|
|
3843
|
+
...applicationResults.flatMap((result) => result.matches.flatMap((match) => match.traits)),
|
|
3844
|
+
...repositoryEnvironment.traits
|
|
3845
|
+
])
|
|
3846
|
+
).sort();
|
|
3847
|
+
const repositoryMatchedPacks = Array.from(
|
|
3848
|
+
new Set(applicationResults.flatMap((result) => result.matches.map((match) => match.name)))
|
|
3420
3849
|
).sort();
|
|
3850
|
+
const confidence = ambiguous ? "low" : applicationResults.length > 1 ? "medium" : scoreToConfidence(topScore);
|
|
3421
3851
|
return buildDetectionReport({
|
|
3422
3852
|
domain: ambiguous ? null : "coding",
|
|
3423
3853
|
stack: ambiguous ? null : matchedPacks[0].name,
|
|
3424
|
-
matchedPacks:
|
|
3854
|
+
matchedPacks: repositoryMatchedPacks,
|
|
3425
3855
|
detectedTraits,
|
|
3426
3856
|
recommendedCapabilities: ["content", "coding", "security"],
|
|
3427
3857
|
detectionPhase,
|
|
3428
3858
|
capabilities: detectedTraits.filter(isCapability2),
|
|
3429
|
-
signals: [
|
|
3430
|
-
|
|
3859
|
+
signals: [
|
|
3860
|
+
...applicationResults.flatMap((result) => result.matches.flatMap((match) => match.signals)),
|
|
3861
|
+
...repositoryEnvironment.signals
|
|
3862
|
+
],
|
|
3863
|
+
confidence,
|
|
3864
|
+
repository
|
|
3431
3865
|
});
|
|
3432
3866
|
}
|
|
3433
3867
|
};
|
|
3434
|
-
function
|
|
3868
|
+
function detectApplication(projectRoot, application, packages, frameworkPacks, archetypePacks) {
|
|
3869
|
+
const roots = [application.root, ...application.component_roots];
|
|
3870
|
+
const packageNames = new Set(
|
|
3871
|
+
packages.filter((pkg) => roots.includes(pkg.root ?? ".")).map((pkg) => pkg.name)
|
|
3872
|
+
);
|
|
3873
|
+
const environment = mergeEnvironmentTraits(projectRoot, roots, packages);
|
|
3874
|
+
const frameworkMatches = frameworkPacks.map((pack) => evaluatePack(projectRoot, roots, pack, packageNames)).filter((match) => match !== null).sort(compareMatchedPacks);
|
|
3875
|
+
if (frameworkMatches.length > 0) {
|
|
3876
|
+
return {
|
|
3877
|
+
application,
|
|
3878
|
+
matches: frameworkMatches,
|
|
3879
|
+
detectionPhase: "framework",
|
|
3880
|
+
environment
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
const archetypeMatches = archetypePacks.map((pack) => evaluatePack(projectRoot, roots, pack, packageNames)).filter((match) => match !== null).sort(compareMatchedPacks);
|
|
3884
|
+
return {
|
|
3885
|
+
application,
|
|
3886
|
+
matches: archetypeMatches,
|
|
3887
|
+
detectionPhase: archetypeMatches.length > 0 ? "archetype" : "none",
|
|
3888
|
+
environment
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
function evaluatePack(projectRoot, roots, pack, packageNames) {
|
|
3435
3892
|
const signals = [
|
|
3436
3893
|
...evaluateRules(
|
|
3437
3894
|
projectRoot,
|
|
3895
|
+
roots,
|
|
3438
3896
|
pack.manifest.name,
|
|
3439
3897
|
pack.manifest.detection.manifests,
|
|
3440
3898
|
packageNames
|
|
3441
3899
|
),
|
|
3442
3900
|
...evaluateRules(
|
|
3443
3901
|
projectRoot,
|
|
3902
|
+
roots,
|
|
3444
3903
|
pack.manifest.name,
|
|
3445
3904
|
pack.manifest.detection.lockfiles,
|
|
3446
3905
|
packageNames
|
|
3447
3906
|
),
|
|
3448
3907
|
...evaluateRules(
|
|
3449
3908
|
projectRoot,
|
|
3909
|
+
roots,
|
|
3450
3910
|
pack.manifest.name,
|
|
3451
3911
|
pack.manifest.detection.heuristics,
|
|
3452
3912
|
packageNames
|
|
@@ -3455,7 +3915,7 @@ function evaluatePack(projectRoot, pack, packageNames) {
|
|
|
3455
3915
|
if (signals.length === 0) {
|
|
3456
3916
|
return null;
|
|
3457
3917
|
}
|
|
3458
|
-
const traitMatches = detectTraits(projectRoot, pack.manifest.traits ?? [], packageNames);
|
|
3918
|
+
const traitMatches = detectTraits(projectRoot, roots, pack.manifest.traits ?? [], packageNames);
|
|
3459
3919
|
const allSignals = [...signals, ...traitMatches.signals];
|
|
3460
3920
|
return {
|
|
3461
3921
|
name: pack.manifest.name,
|
|
@@ -3464,27 +3924,38 @@ function evaluatePack(projectRoot, pack, packageNames) {
|
|
|
3464
3924
|
traits: traitMatches.names
|
|
3465
3925
|
};
|
|
3466
3926
|
}
|
|
3467
|
-
function evaluateRules(projectRoot, implies, rules, packageNames) {
|
|
3468
|
-
return (rules ?? []).map((rule) => evaluateRule(projectRoot, implies, rule, packageNames)).filter((signal) => signal !== null);
|
|
3927
|
+
function evaluateRules(projectRoot, roots, implies, rules, packageNames) {
|
|
3928
|
+
return (rules ?? []).map((rule) => evaluateRule(projectRoot, roots, implies, rule, packageNames)).filter((signal) => signal !== null);
|
|
3469
3929
|
}
|
|
3470
|
-
function evaluateRule(projectRoot, implies, rule, packageNames) {
|
|
3471
|
-
const fileMatched = rule.file ? existsSync5(join14(projectRoot, rule.file)) : true;
|
|
3472
|
-
const directoryMatched = rule.directory ? existsSync5(join14(projectRoot, rule.directory)) : true;
|
|
3930
|
+
function evaluateRule(projectRoot, roots, implies, rule, packageNames) {
|
|
3473
3931
|
const packagesMatched = rule.packages === void 0 || rule.packages.every((pkg) => packageNames.has(pkg));
|
|
3474
|
-
const
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
if (!fileMatched || !directoryMatched || !packagesMatched || !patternsMatched || !contentMatched || !fieldsMatched || !fieldAbsentMatched) {
|
|
3932
|
+
const matchedRoot = roots.find(
|
|
3933
|
+
(root) => matchesRuleAtRoot(projectRoot, root, rule, packageNames)
|
|
3934
|
+
);
|
|
3935
|
+
if (!packagesMatched || matchedRoot === void 0) {
|
|
3479
3936
|
return null;
|
|
3480
3937
|
}
|
|
3481
3938
|
return {
|
|
3482
3939
|
signal: buildRuleSignal(rule, implies),
|
|
3483
|
-
file:
|
|
3940
|
+
file: prefixRepositoryPath(
|
|
3941
|
+
matchedRoot,
|
|
3942
|
+
rule.file ?? rule.directory ?? rule.patterns?.[0] ?? "package metadata"
|
|
3943
|
+
),
|
|
3484
3944
|
implies,
|
|
3485
3945
|
confidence: rule.file && rule.packages ? "high" : rule.file || rule.directory ? "medium" : "low"
|
|
3486
3946
|
};
|
|
3487
3947
|
}
|
|
3948
|
+
function matchesRuleAtRoot(projectRoot, root, rule, packageNames) {
|
|
3949
|
+
const absoluteRoot = root === "." ? projectRoot : join16(projectRoot, root);
|
|
3950
|
+
const fileMatched = rule.file ? existsSync5(join16(absoluteRoot, rule.file)) : true;
|
|
3951
|
+
const directoryMatched = rule.directory ? existsSync5(join16(absoluteRoot, rule.directory)) : true;
|
|
3952
|
+
const patternsMatched = rule.patterns === void 0 || rule.patterns.every((pattern) => fg.sync(pattern, { cwd: absoluteRoot }).length > 0);
|
|
3953
|
+
const contentMatched = rule.content_match === void 0 || rule.file !== void 0 && existsSync5(join16(absoluteRoot, rule.file)) && readFileSync5(join16(absoluteRoot, rule.file), "utf8").includes(rule.content_match);
|
|
3954
|
+
const fieldsMatched = evaluateFieldRules(absoluteRoot, rule);
|
|
3955
|
+
const fieldAbsentMatched = evaluateFieldAbsentRules(absoluteRoot, rule);
|
|
3956
|
+
const packagesMatched = rule.packages === void 0 || rule.packages.every((pkg) => packageNames.has(pkg));
|
|
3957
|
+
return fileMatched && directoryMatched && patternsMatched && contentMatched && fieldsMatched && fieldAbsentMatched && packagesMatched;
|
|
3958
|
+
}
|
|
3488
3959
|
function evaluateFieldRules(projectRoot, rule) {
|
|
3489
3960
|
if (!rule.fields?.length) return true;
|
|
3490
3961
|
if (!rule.file) return false;
|
|
@@ -3528,7 +3999,7 @@ function getNestedField(obj, dotPath) {
|
|
|
3528
3999
|
return current;
|
|
3529
4000
|
}
|
|
3530
4001
|
function parseManifestFile(projectRoot, file) {
|
|
3531
|
-
const filePath =
|
|
4002
|
+
const filePath = join16(projectRoot, file);
|
|
3532
4003
|
if (!existsSync5(filePath)) return null;
|
|
3533
4004
|
try {
|
|
3534
4005
|
const content = readFileSync5(filePath, "utf8");
|
|
@@ -3537,7 +4008,7 @@ function parseManifestFile(projectRoot, file) {
|
|
|
3537
4008
|
return null;
|
|
3538
4009
|
}
|
|
3539
4010
|
}
|
|
3540
|
-
function detectTraits(projectRoot, traits, packageNames) {
|
|
4011
|
+
function detectTraits(projectRoot, roots, traits, packageNames) {
|
|
3541
4012
|
const names = [];
|
|
3542
4013
|
const signals = [];
|
|
3543
4014
|
for (const trait of traits) {
|
|
@@ -3551,21 +4022,31 @@ function detectTraits(projectRoot, traits, packageNames) {
|
|
|
3551
4022
|
});
|
|
3552
4023
|
continue;
|
|
3553
4024
|
}
|
|
3554
|
-
|
|
4025
|
+
const fileRoot = trait.detect_file ? roots.find(
|
|
4026
|
+
(root) => existsSync5(
|
|
4027
|
+
join16(root === "." ? projectRoot : join16(projectRoot, root), trait.detect_file)
|
|
4028
|
+
)
|
|
4029
|
+
) : void 0;
|
|
4030
|
+
if (trait.detect_file && fileRoot) {
|
|
3555
4031
|
names.push(trait.name);
|
|
3556
4032
|
signals.push({
|
|
3557
4033
|
signal: `${trait.name} detected from ${trait.detect_file}`,
|
|
3558
|
-
file: trait.detect_file,
|
|
4034
|
+
file: prefixRepositoryPath(fileRoot, trait.detect_file),
|
|
3559
4035
|
implies: trait.name,
|
|
3560
4036
|
confidence: "medium"
|
|
3561
4037
|
});
|
|
3562
4038
|
continue;
|
|
3563
4039
|
}
|
|
3564
|
-
|
|
4040
|
+
const directoryRoot = trait.detect_directory ? roots.find(
|
|
4041
|
+
(root) => existsSync5(
|
|
4042
|
+
join16(root === "." ? projectRoot : join16(projectRoot, root), trait.detect_directory)
|
|
4043
|
+
)
|
|
4044
|
+
) : void 0;
|
|
4045
|
+
if (trait.detect_directory && directoryRoot) {
|
|
3565
4046
|
names.push(trait.name);
|
|
3566
4047
|
signals.push({
|
|
3567
4048
|
signal: `${trait.name} detected from ${trait.detect_directory}`,
|
|
3568
|
-
file: trait.detect_directory,
|
|
4049
|
+
file: prefixRepositoryPath(directoryRoot, trait.detect_directory),
|
|
3569
4050
|
implies: trait.name,
|
|
3570
4051
|
confidence: "medium"
|
|
3571
4052
|
});
|
|
@@ -3594,12 +4075,68 @@ function buildRuleSignal(rule, implies) {
|
|
|
3594
4075
|
function compareMatchedPacks(left, right) {
|
|
3595
4076
|
return right.score - left.score || left.name.localeCompare(right.name);
|
|
3596
4077
|
}
|
|
4078
|
+
function compareApplicationResults(left, right) {
|
|
4079
|
+
const leftScore = left.matches[0]?.score ?? -1;
|
|
4080
|
+
const rightScore = right.matches[0]?.score ?? -1;
|
|
4081
|
+
return rightScore - leftScore || left.application.root.localeCompare(right.application.root);
|
|
4082
|
+
}
|
|
3597
4083
|
function scoreToConfidence(score) {
|
|
3598
4084
|
if (score >= 2) {
|
|
3599
4085
|
return "high";
|
|
3600
4086
|
}
|
|
3601
4087
|
return "low";
|
|
3602
4088
|
}
|
|
4089
|
+
function mergeEnvironmentTraits(projectRoot, roots, packages) {
|
|
4090
|
+
const traits = /* @__PURE__ */ new Set();
|
|
4091
|
+
const signals = [];
|
|
4092
|
+
const sources = [];
|
|
4093
|
+
for (const root of roots.length > 0 ? roots : ["."]) {
|
|
4094
|
+
const packageNames = packages.filter((pkg) => (pkg.root ?? ".") === root).map((pkg) => pkg.name);
|
|
4095
|
+
const absoluteRoot = root === "." ? projectRoot : join16(projectRoot, root);
|
|
4096
|
+
const environment = detectEnvironmentTraits(absoluteRoot, { packageNames });
|
|
4097
|
+
for (const trait of environment.traits) {
|
|
4098
|
+
traits.add(trait);
|
|
4099
|
+
}
|
|
4100
|
+
sources.push(
|
|
4101
|
+
...environment.sources.map((source) => ({
|
|
4102
|
+
...source,
|
|
4103
|
+
file: prefixRepositoryPath(root, source.file)
|
|
4104
|
+
}))
|
|
4105
|
+
);
|
|
4106
|
+
signals.push(
|
|
4107
|
+
...environment.signals.map((signal) => ({
|
|
4108
|
+
...signal,
|
|
4109
|
+
file: prefixRepositoryPath(root, signal.file)
|
|
4110
|
+
}))
|
|
4111
|
+
);
|
|
4112
|
+
}
|
|
4113
|
+
return {
|
|
4114
|
+
traits: Array.from(traits).sort(),
|
|
4115
|
+
sources,
|
|
4116
|
+
signals: dedupeSignals(signals)
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
4119
|
+
function dedupeSignals(signals) {
|
|
4120
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4121
|
+
return signals.filter((signal) => {
|
|
4122
|
+
const key = `${signal.signal}:${signal.file}:${signal.implies}:${signal.confidence}`;
|
|
4123
|
+
if (seen.has(key)) {
|
|
4124
|
+
return false;
|
|
4125
|
+
}
|
|
4126
|
+
seen.add(key);
|
|
4127
|
+
return true;
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
function emptyRepositoryContext(projectRoot) {
|
|
4131
|
+
return {
|
|
4132
|
+
selected_root: projectRoot,
|
|
4133
|
+
scan_max_depth: 0,
|
|
4134
|
+
ignored_paths: [],
|
|
4135
|
+
projects: [],
|
|
4136
|
+
applications: [],
|
|
4137
|
+
primary_project_root: null
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
3603
4140
|
function isCapability2(value) {
|
|
3604
4141
|
return [
|
|
3605
4142
|
"inertia",
|
|
@@ -3623,20 +4160,20 @@ function isCapability2(value) {
|
|
|
3623
4160
|
|
|
3624
4161
|
// src/detection/signals/flutter.ts
|
|
3625
4162
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3626
|
-
import { join as
|
|
4163
|
+
import { join as join17 } from "path";
|
|
3627
4164
|
import { parse as parse2 } from "yaml";
|
|
3628
4165
|
|
|
3629
4166
|
// src/detection/signals/laravel.ts
|
|
3630
4167
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
3631
|
-
import { join as
|
|
4168
|
+
import { join as join18 } from "path";
|
|
3632
4169
|
|
|
3633
4170
|
// src/detection/signals/react.ts
|
|
3634
4171
|
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
3635
|
-
import { join as
|
|
4172
|
+
import { join as join19 } from "path";
|
|
3636
4173
|
|
|
3637
4174
|
// src/detection/signals/vue.ts
|
|
3638
4175
|
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
3639
|
-
import { join as
|
|
4176
|
+
import { join as join20 } from "path";
|
|
3640
4177
|
|
|
3641
4178
|
// src/design-tokens/defaults.ts
|
|
3642
4179
|
var DEFAULT_DESIGN_TOKENS = {
|
|
@@ -3750,13 +4287,13 @@ var DEFAULT_DESIGN_TOKENS = {
|
|
|
3750
4287
|
|
|
3751
4288
|
// src/design-tokens/service.ts
|
|
3752
4289
|
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
3753
|
-
import { dirname as dirname10, join as
|
|
4290
|
+
import { dirname as dirname10, join as join21 } from "path";
|
|
3754
4291
|
var DesignTokenService = class {
|
|
3755
4292
|
constructor(validator = new SchemaValidator()) {
|
|
3756
4293
|
this.validator = validator;
|
|
3757
4294
|
}
|
|
3758
4295
|
async seed(projectRoot) {
|
|
3759
|
-
const target =
|
|
4296
|
+
const target = join21(projectRoot, PATHS.DESIGN_TOKENS_FILE);
|
|
3760
4297
|
await mkdir7(dirname10(target), { recursive: true });
|
|
3761
4298
|
await writeFile7(target, `${JSON.stringify(DEFAULT_DESIGN_TOKENS, null, 2)}
|
|
3762
4299
|
`, {
|
|
@@ -3768,7 +4305,7 @@ var DesignTokenService = class {
|
|
|
3768
4305
|
});
|
|
3769
4306
|
}
|
|
3770
4307
|
async load(projectRoot) {
|
|
3771
|
-
const target =
|
|
4308
|
+
const target = join21(projectRoot, PATHS.DESIGN_TOKENS_FILE);
|
|
3772
4309
|
const raw = await readFile8(target, "utf8");
|
|
3773
4310
|
const parsed = JSON.parse(raw);
|
|
3774
4311
|
const validation = this.validator.validate("design-tokens", parsed);
|
|
@@ -3785,27 +4322,27 @@ var DesignTokenService = class {
|
|
|
3785
4322
|
const sections = groupTokens(flattened);
|
|
3786
4323
|
return [
|
|
3787
4324
|
{
|
|
3788
|
-
path:
|
|
4325
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "tokens.md"),
|
|
3789
4326
|
content: buildTokensMarkdown(flattened)
|
|
3790
4327
|
},
|
|
3791
4328
|
{
|
|
3792
|
-
path:
|
|
4329
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "components.md"),
|
|
3793
4330
|
content: buildSectionMarkdown("Component Defaults", sections.components)
|
|
3794
4331
|
},
|
|
3795
4332
|
{
|
|
3796
|
-
path:
|
|
4333
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "motion.md"),
|
|
3797
4334
|
content: buildSectionMarkdown("Motion", sections.motion)
|
|
3798
4335
|
},
|
|
3799
4336
|
{
|
|
3800
|
-
path:
|
|
4337
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "accessibility.md"),
|
|
3801
4338
|
content: buildSectionMarkdown("Accessibility", sections.accessibility)
|
|
3802
4339
|
},
|
|
3803
4340
|
{
|
|
3804
|
-
path:
|
|
4341
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "responsive.md"),
|
|
3805
4342
|
content: buildResponsiveMarkdown(sections.spacing)
|
|
3806
4343
|
},
|
|
3807
4344
|
{
|
|
3808
|
-
path:
|
|
4345
|
+
path: join21(PATHS.DESIGN_SYSTEM_DIR, "patterns.md"),
|
|
3809
4346
|
content: buildPatternsMarkdown(flattened)
|
|
3810
4347
|
}
|
|
3811
4348
|
];
|
|
@@ -3814,7 +4351,7 @@ var DesignTokenService = class {
|
|
|
3814
4351
|
const docs = await this.generateDocs(projectRoot);
|
|
3815
4352
|
await Promise.all(
|
|
3816
4353
|
docs.map(async (artifact) => {
|
|
3817
|
-
const target =
|
|
4354
|
+
const target = join21(projectRoot, artifact.path);
|
|
3818
4355
|
await mkdir7(dirname10(target), { recursive: true });
|
|
3819
4356
|
await writeFile7(target, artifact.content);
|
|
3820
4357
|
})
|
|
@@ -3865,7 +4402,7 @@ ${flutterEntries}
|
|
|
3865
4402
|
const artifacts = await this.exportTheme(projectRoot, stack);
|
|
3866
4403
|
await Promise.all(
|
|
3867
4404
|
artifacts.map(async (artifact) => {
|
|
3868
|
-
const target =
|
|
4405
|
+
const target = join21(projectRoot, artifact.path);
|
|
3869
4406
|
await mkdir7(dirname10(target), { recursive: true });
|
|
3870
4407
|
await writeFile7(target, artifact.content);
|
|
3871
4408
|
})
|
|
@@ -3954,7 +4491,7 @@ function toIdentifier(value) {
|
|
|
3954
4491
|
|
|
3955
4492
|
// src/health/checker.ts
|
|
3956
4493
|
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync } from "fs";
|
|
3957
|
-
import { join as
|
|
4494
|
+
import { join as join22 } from "path";
|
|
3958
4495
|
var STALENESS_WINDOW_MS = 1e3 * 60 * 60 * 24 * 7;
|
|
3959
4496
|
var HealthChecker = class {
|
|
3960
4497
|
validator = new SchemaValidator();
|
|
@@ -3989,7 +4526,7 @@ var HealthChecker = class {
|
|
|
3989
4526
|
return readProjectProfile(projectRoot);
|
|
3990
4527
|
}
|
|
3991
4528
|
detectModules(projectRoot) {
|
|
3992
|
-
const modulesRoot =
|
|
4529
|
+
const modulesRoot = join22(projectRoot, "docs/modules");
|
|
3993
4530
|
if (!existsSync10(modulesRoot)) {
|
|
3994
4531
|
return [];
|
|
3995
4532
|
}
|
|
@@ -3997,7 +4534,7 @@ var HealthChecker = class {
|
|
|
3997
4534
|
}
|
|
3998
4535
|
checkFrameworkArtifacts(projectRoot) {
|
|
3999
4536
|
const required = [PATHS.PROJECT_PROFILE, PATHS.FRAMEWORK_VERSION, PATHS.FRAMEWORK_PATH];
|
|
4000
|
-
const missing = required.filter((relative11) => !existsSync10(
|
|
4537
|
+
const missing = required.filter((relative11) => !existsSync10(join22(projectRoot, relative11)));
|
|
4001
4538
|
return missing.length === 0 ? pass("Framework artifacts exist", "Framework artifacts are present") : fail(
|
|
4002
4539
|
"Framework artifacts exist",
|
|
4003
4540
|
`Missing framework artifacts: ${missing.join(", ")}`,
|
|
@@ -4020,15 +4557,15 @@ var HealthChecker = class {
|
|
|
4020
4557
|
);
|
|
4021
4558
|
}
|
|
4022
4559
|
checkInstructionCopies(projectRoot, profile) {
|
|
4023
|
-
const rulesRoot =
|
|
4560
|
+
const rulesRoot = join22(projectRoot, PATHS.RULES_DIR);
|
|
4024
4561
|
const stack = profile === null ? null : getPrimaryStack(profile);
|
|
4025
|
-
const toolsRoot = stack === null || profile === null || getProfileDomain(profile) !== "coding" ? null :
|
|
4562
|
+
const toolsRoot = stack === null || profile === null || getProfileDomain(profile) !== "coding" ? null : join22(projectRoot, PATHS.TOOLS_DIR, stack);
|
|
4026
4563
|
const missing = [];
|
|
4027
4564
|
if (!existsSync10(rulesRoot)) {
|
|
4028
4565
|
missing.push(PATHS.RULES_DIR);
|
|
4029
4566
|
}
|
|
4030
4567
|
if (toolsRoot !== null && !existsSync10(toolsRoot)) {
|
|
4031
|
-
missing.push(
|
|
4568
|
+
missing.push(join22(PATHS.TOOLS_DIR, stack ?? "unknown"));
|
|
4032
4569
|
}
|
|
4033
4570
|
return missing.length === 0 ? pass("Instruction copies exist", "Copied rules and tools are present") : fail(
|
|
4034
4571
|
"Instruction copies exist",
|
|
@@ -4038,9 +4575,9 @@ var HealthChecker = class {
|
|
|
4038
4575
|
}
|
|
4039
4576
|
checkIndexesCurrent(projectRoot) {
|
|
4040
4577
|
const missing = REGISTRIES.filter(
|
|
4041
|
-
(registry) => !existsSync10(
|
|
4578
|
+
(registry) => !existsSync10(join22(projectRoot, PATHS.REGISTRIES_DIR, registry))
|
|
4042
4579
|
);
|
|
4043
|
-
const statusPath =
|
|
4580
|
+
const statusPath = join22(projectRoot, ".paqad/indexes/registry-status.json");
|
|
4044
4581
|
if (missing.length > 0 || !existsSync10(statusPath)) {
|
|
4045
4582
|
return warn(
|
|
4046
4583
|
"Indexes are current",
|
|
@@ -4053,7 +4590,7 @@ var HealthChecker = class {
|
|
|
4053
4590
|
}
|
|
4054
4591
|
checkAdapterConfig(projectRoot) {
|
|
4055
4592
|
const configs = [PATHS.CLAUDE_MD, PATHS.AGENTS_MD, PATHS.GEMINI_MD];
|
|
4056
|
-
const existing = configs.filter((config) => existsSync10(
|
|
4593
|
+
const existing = configs.filter((config) => existsSync10(join22(projectRoot, config)));
|
|
4057
4594
|
return existing.length > 0 ? pass("Adapter config is present", "Adapter config files are present") : fail(
|
|
4058
4595
|
"Adapter config is present",
|
|
4059
4596
|
"No adapter config files were found",
|
|
@@ -4083,7 +4620,7 @@ var HealthChecker = class {
|
|
|
4083
4620
|
);
|
|
4084
4621
|
}
|
|
4085
4622
|
checkStableFrameworkPaths(projectRoot) {
|
|
4086
|
-
const frameworkPath =
|
|
4623
|
+
const frameworkPath = join22(projectRoot, PATHS.FRAMEWORK_PATH);
|
|
4087
4624
|
if (!existsSync10(frameworkPath)) {
|
|
4088
4625
|
return fail(
|
|
4089
4626
|
"Stable framework paths only",
|
|
@@ -4100,7 +4637,7 @@ var HealthChecker = class {
|
|
|
4100
4637
|
) : pass("Stable framework paths only", "Framework path is stable");
|
|
4101
4638
|
}
|
|
4102
4639
|
checkBrokenScaffold(projectRoot) {
|
|
4103
|
-
const broken =
|
|
4640
|
+
const broken = walk2(projectRoot).filter(
|
|
4104
4641
|
(path) => path.endsWith(".partial") || path.endsWith(".tmp")
|
|
4105
4642
|
);
|
|
4106
4643
|
return broken.length === 0 ? pass("No broken scaffold state", "No partial scaffold artifacts detected") : fail(
|
|
@@ -4114,7 +4651,7 @@ var HealthChecker = class {
|
|
|
4114
4651
|
return pass("UI docs present", "UI docs are checked after documentation generation");
|
|
4115
4652
|
}
|
|
4116
4653
|
const missing = modules.filter(
|
|
4117
|
-
(moduleName) => !existsSync10(
|
|
4654
|
+
(moduleName) => !existsSync10(join22(projectRoot, "docs/modules", moduleName, "ui/screens.md")) || !existsSync10(join22(projectRoot, "docs/modules", moduleName, "ui/components.md")) || !existsSync10(join22(projectRoot, "docs/modules", moduleName, "ui/states.md"))
|
|
4118
4655
|
);
|
|
4119
4656
|
return missing.length === 0 ? pass("UI docs present", "UI docs are present for all modules with UI docs") : fail(
|
|
4120
4657
|
"UI docs present",
|
|
@@ -4127,7 +4664,7 @@ var HealthChecker = class {
|
|
|
4127
4664
|
return pass("API docs present", "API docs are checked after documentation generation");
|
|
4128
4665
|
}
|
|
4129
4666
|
const missing = modules.filter(
|
|
4130
|
-
(moduleName) => !existsSync10(
|
|
4667
|
+
(moduleName) => !existsSync10(join22(projectRoot, "docs/modules", moduleName, "api/endpoints.md")) || !existsSync10(join22(projectRoot, "docs/modules", moduleName, "api/schemas.md")) || !existsSync10(join22(projectRoot, "docs/modules", moduleName, "api/error-codes.md"))
|
|
4131
4668
|
);
|
|
4132
4669
|
return missing.length === 0 ? pass("API docs present", "API docs are present for all modules with APIs") : fail(
|
|
4133
4670
|
"API docs present",
|
|
@@ -4143,7 +4680,7 @@ var HealthChecker = class {
|
|
|
4143
4680
|
);
|
|
4144
4681
|
}
|
|
4145
4682
|
const missing = modules.filter(
|
|
4146
|
-
(moduleName) => !existsSync10(
|
|
4683
|
+
(moduleName) => !existsSync10(join22(projectRoot, "docs/modules", moduleName, "integration/events.md")) || !existsSync10(join22(projectRoot, "docs/modules", moduleName, "integration/contracts.md"))
|
|
4147
4684
|
);
|
|
4148
4685
|
return missing.length === 0 ? pass("Integration docs present", "Integration docs are present for all modules") : fail(
|
|
4149
4686
|
"Integration docs present",
|
|
@@ -4159,7 +4696,7 @@ var HealthChecker = class {
|
|
|
4159
4696
|
);
|
|
4160
4697
|
}
|
|
4161
4698
|
const missing = modules.filter(
|
|
4162
|
-
(moduleName) => !existsSync10(
|
|
4699
|
+
(moduleName) => !existsSync10(join22(projectRoot, "docs/modules", moduleName, "error-catalog.md"))
|
|
4163
4700
|
);
|
|
4164
4701
|
return missing.length === 0 ? pass("Error catalog present", "Error catalogs are present for all modules") : fail(
|
|
4165
4702
|
"Error catalog present",
|
|
@@ -4176,7 +4713,7 @@ var HealthChecker = class {
|
|
|
4176
4713
|
);
|
|
4177
4714
|
}
|
|
4178
4715
|
const candidates = [".claude/settings.mcp.json", ".codex/mcp.json", ".gemini/mcp.json"];
|
|
4179
|
-
const existing = candidates.filter((candidate) => existsSync10(
|
|
4716
|
+
const existing = candidates.filter((candidate) => existsSync10(join22(projectRoot, candidate)));
|
|
4180
4717
|
const expectedServers = getServersForStack(
|
|
4181
4718
|
getPrimaryStack(profile),
|
|
4182
4719
|
getLegacyCapabilities(profile)
|
|
@@ -4191,7 +4728,7 @@ var HealthChecker = class {
|
|
|
4191
4728
|
const configured = /* @__PURE__ */ new Set();
|
|
4192
4729
|
for (const path of existing) {
|
|
4193
4730
|
try {
|
|
4194
|
-
const parsed = JSON.parse(readFileSync10(
|
|
4731
|
+
const parsed = JSON.parse(readFileSync10(join22(projectRoot, path), "utf8"));
|
|
4195
4732
|
Object.keys(parsed.mcpServers ?? {}).forEach((server) => configured.add(server));
|
|
4196
4733
|
} catch {
|
|
4197
4734
|
continue;
|
|
@@ -4205,7 +4742,7 @@ var HealthChecker = class {
|
|
|
4205
4742
|
);
|
|
4206
4743
|
}
|
|
4207
4744
|
checkSkillCache(projectRoot) {
|
|
4208
|
-
const cacheDir =
|
|
4745
|
+
const cacheDir = join22(projectRoot, PATHS.SKILL_CACHE_DIR);
|
|
4209
4746
|
if (!existsSync10(cacheDir)) {
|
|
4210
4747
|
return warn(
|
|
4211
4748
|
"Skill cache healthy",
|
|
@@ -4215,7 +4752,7 @@ var HealthChecker = class {
|
|
|
4215
4752
|
}
|
|
4216
4753
|
const corrupt = readdirSync2(cacheDir).filter((entry) => entry.endsWith(".json")).filter((entry) => {
|
|
4217
4754
|
try {
|
|
4218
|
-
JSON.parse(readFileSync10(
|
|
4755
|
+
JSON.parse(readFileSync10(join22(cacheDir, entry), "utf8"));
|
|
4219
4756
|
return false;
|
|
4220
4757
|
} catch {
|
|
4221
4758
|
return true;
|
|
@@ -4228,7 +4765,7 @@ var HealthChecker = class {
|
|
|
4228
4765
|
);
|
|
4229
4766
|
}
|
|
4230
4767
|
checkContextHitRate(projectRoot, profile) {
|
|
4231
|
-
const path =
|
|
4768
|
+
const path = join22(projectRoot, PATHS.CONTEXT_HIT_LOG);
|
|
4232
4769
|
if (!existsSync10(path) || profile === null) {
|
|
4233
4770
|
return pass("Context hit rate acceptable", "No recent context logs yet");
|
|
4234
4771
|
}
|
|
@@ -4250,7 +4787,7 @@ var HealthChecker = class {
|
|
|
4250
4787
|
}
|
|
4251
4788
|
}
|
|
4252
4789
|
buildEfficiencySummary(projectRoot, profile) {
|
|
4253
|
-
const path =
|
|
4790
|
+
const path = join22(projectRoot, PATHS.CONTEXT_HIT_LOG);
|
|
4254
4791
|
let contextHitRate = 1;
|
|
4255
4792
|
if (existsSync10(path)) {
|
|
4256
4793
|
try {
|
|
@@ -4262,12 +4799,12 @@ var HealthChecker = class {
|
|
|
4262
4799
|
}
|
|
4263
4800
|
return {
|
|
4264
4801
|
context_hit_rate: contextHitRate,
|
|
4265
|
-
skill_cache_hit_rate: existsSync10(
|
|
4802
|
+
skill_cache_hit_rate: existsSync10(join22(projectRoot, PATHS.SKILL_CACHE_DIR)) ? 1 : 0,
|
|
4266
4803
|
mcp_usage_rate: profile?.efficiency?.mcp_first ? 1 : 0
|
|
4267
4804
|
};
|
|
4268
4805
|
}
|
|
4269
4806
|
documentationHasRun(projectRoot) {
|
|
4270
|
-
return existsSync10(
|
|
4807
|
+
return existsSync10(join22(projectRoot, PATHS.DOC_PROGRESS));
|
|
4271
4808
|
}
|
|
4272
4809
|
};
|
|
4273
4810
|
function pass(name, detail) {
|
|
@@ -4291,12 +4828,12 @@ function deriveOverallStatus(checks) {
|
|
|
4291
4828
|
function isStale(timestampMs) {
|
|
4292
4829
|
return Date.now() - timestampMs > STALENESS_WINDOW_MS;
|
|
4293
4830
|
}
|
|
4294
|
-
function
|
|
4831
|
+
function walk2(root) {
|
|
4295
4832
|
const results = [];
|
|
4296
4833
|
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
4297
|
-
const path =
|
|
4834
|
+
const path = join22(root, entry.name);
|
|
4298
4835
|
if (entry.isDirectory()) {
|
|
4299
|
-
results.push(...
|
|
4836
|
+
results.push(...walk2(path));
|
|
4300
4837
|
continue;
|
|
4301
4838
|
}
|
|
4302
4839
|
results.push(path);
|
|
@@ -4311,35 +4848,35 @@ import { dirname as dirname12 } from "path";
|
|
|
4311
4848
|
// src/onboarding/manifest-writer.ts
|
|
4312
4849
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4313
4850
|
import { homedir } from "os";
|
|
4314
|
-
import { dirname as dirname11, join as
|
|
4851
|
+
import { dirname as dirname11, join as join23 } from "path";
|
|
4315
4852
|
function writeProjectProfile2(projectRoot, profile) {
|
|
4316
4853
|
return writeProjectProfile(projectRoot, profile);
|
|
4317
4854
|
}
|
|
4318
4855
|
function writeDetectionReport(projectRoot, report) {
|
|
4319
|
-
const path =
|
|
4856
|
+
const path = join23(projectRoot, PATHS.DETECTION_REPORT);
|
|
4320
4857
|
mkdirSync2(dirname11(path), { recursive: true });
|
|
4321
4858
|
writeFileSync2(path, JSON.stringify(report, null, 2));
|
|
4322
4859
|
return path;
|
|
4323
4860
|
}
|
|
4324
4861
|
function writeFrameworkMetadata(projectRoot, version) {
|
|
4325
|
-
mkdirSync2(dirname11(
|
|
4862
|
+
mkdirSync2(dirname11(join23(projectRoot, PATHS.FRAMEWORK_VERSION)), {
|
|
4326
4863
|
recursive: true
|
|
4327
4864
|
});
|
|
4328
4865
|
const content = `version=${version}
|
|
4329
4866
|
updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
4330
4867
|
`;
|
|
4331
|
-
writeFileSync2(
|
|
4332
|
-
writeFileSync2(
|
|
4868
|
+
writeFileSync2(join23(projectRoot, PATHS.FRAMEWORK_VERSION), content);
|
|
4869
|
+
writeFileSync2(join23(projectRoot, PATHS.FRAMEWORK_PATH), `${resolveFrameworkInstallPath()}
|
|
4333
4870
|
`);
|
|
4334
4871
|
}
|
|
4335
4872
|
function writeOnboardingManifest(projectRoot, manifest) {
|
|
4336
|
-
const path =
|
|
4873
|
+
const path = join23(projectRoot, PATHS.ONBOARDING_MANIFEST);
|
|
4337
4874
|
mkdirSync2(dirname11(path), { recursive: true });
|
|
4338
4875
|
writeFileSync2(path, JSON.stringify(manifest, null, 2));
|
|
4339
4876
|
return path;
|
|
4340
4877
|
}
|
|
4341
4878
|
function resolveFrameworkInstallPath() {
|
|
4342
|
-
return process.env.PAQAD_FRAMEWORK_HOME ??
|
|
4879
|
+
return process.env.PAQAD_FRAMEWORK_HOME ?? join23(homedir(), ".paqad-ai/current");
|
|
4343
4880
|
}
|
|
4344
4881
|
|
|
4345
4882
|
// src/install/bootstrap.ts
|
|
@@ -4384,23 +4921,23 @@ function isExpectedRuntimeSymlink(runtimeRoot, frameworkHome) {
|
|
|
4384
4921
|
}
|
|
4385
4922
|
|
|
4386
4923
|
// src/document/workflow.ts
|
|
4387
|
-
import { mkdir as mkdir10, readFile as readFile12, readdir as
|
|
4388
|
-
import { dirname as dirname14, join as
|
|
4924
|
+
import { mkdir as mkdir10, readFile as readFile12, readdir as readdir3, writeFile as writeFile10 } from "fs/promises";
|
|
4925
|
+
import { dirname as dirname14, join as join28, relative } from "path";
|
|
4389
4926
|
|
|
4390
4927
|
// src/onboarding/registry-generator.ts
|
|
4391
|
-
import { readdir } from "fs/promises";
|
|
4392
|
-
import { join as
|
|
4928
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
4929
|
+
import { join as join24 } from "path";
|
|
4393
4930
|
async function discoverModules(projectRoot) {
|
|
4394
4931
|
const candidates = [
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4932
|
+
join24(projectRoot, "docs/modules"),
|
|
4933
|
+
join24(projectRoot, "app"),
|
|
4934
|
+
join24(projectRoot, "lib"),
|
|
4935
|
+
join24(projectRoot, "src")
|
|
4399
4936
|
];
|
|
4400
4937
|
const modules = /* @__PURE__ */ new Set(["core"]);
|
|
4401
4938
|
for (const root of candidates) {
|
|
4402
4939
|
try {
|
|
4403
|
-
const entries = await
|
|
4940
|
+
const entries = await readdir2(root, { withFileTypes: true });
|
|
4404
4941
|
for (const entry of entries) {
|
|
4405
4942
|
if (!entry.isDirectory()) {
|
|
4406
4943
|
continue;
|
|
@@ -4419,36 +4956,36 @@ async function discoverModules(projectRoot) {
|
|
|
4419
4956
|
|
|
4420
4957
|
// src/stack-docs/generator.ts
|
|
4421
4958
|
import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
4422
|
-
import { join as
|
|
4959
|
+
import { join as join25 } from "path";
|
|
4423
4960
|
async function writeStackArtifacts(projectRoot, snapshot, previousSnapshot, options = {}) {
|
|
4424
4961
|
const existingDrift = await readExistingDrift(projectRoot);
|
|
4425
4962
|
const computedDrift = compareStackProfiles(previousSnapshot?.profile ?? null, snapshot.profile);
|
|
4426
4963
|
const drift = computedDrift.status === "no-drift" && existingDrift !== null ? existingDrift : computedDrift;
|
|
4427
4964
|
await writeFile8(
|
|
4428
|
-
|
|
4965
|
+
join25(projectRoot, PATHS.STACK_SNAPSHOT),
|
|
4429
4966
|
`${JSON.stringify(snapshot, null, 2)}
|
|
4430
4967
|
`
|
|
4431
4968
|
);
|
|
4432
|
-
await writeFile8(
|
|
4969
|
+
await writeFile8(join25(projectRoot, PATHS.STACK_DRIFT), `${JSON.stringify(drift, null, 2)}
|
|
4433
4970
|
`);
|
|
4434
4971
|
if (!options.writeHumanDocs) {
|
|
4435
4972
|
return drift;
|
|
4436
4973
|
}
|
|
4437
|
-
await mkdir8(
|
|
4438
|
-
const stackDir =
|
|
4439
|
-
await writeFile8(
|
|
4440
|
-
await writeFile8(
|
|
4441
|
-
await writeFile8(
|
|
4442
|
-
await writeFile8(
|
|
4443
|
-
await writeFile8(
|
|
4444
|
-
await writeFile8(
|
|
4445
|
-
await writeFile8(
|
|
4974
|
+
await mkdir8(join25(projectRoot, PATHS.FRAMEWORK_STACK_DIR), { recursive: true });
|
|
4975
|
+
const stackDir = join25(projectRoot, PATHS.FRAMEWORK_STACK_DIR);
|
|
4976
|
+
await writeFile8(join25(stackDir, "overview.md"), await buildOverview(projectRoot, snapshot, drift));
|
|
4977
|
+
await writeFile8(join25(stackDir, "frameworks.md"), await buildFrameworks(projectRoot, snapshot));
|
|
4978
|
+
await writeFile8(join25(stackDir, "dependencies.md"), buildDependencies(snapshot));
|
|
4979
|
+
await writeFile8(join25(stackDir, "tooling.md"), buildTooling(snapshot));
|
|
4980
|
+
await writeFile8(join25(stackDir, "version-rules.md"), buildVersionRules(snapshot, drift));
|
|
4981
|
+
await writeFile8(join25(stackDir, "sources.md"), buildSources(snapshot));
|
|
4982
|
+
await writeFile8(join25(stackDir, "drift-report.md"), buildDriftReport(drift));
|
|
4446
4983
|
return drift;
|
|
4447
4984
|
}
|
|
4448
4985
|
async function readExistingDrift(projectRoot) {
|
|
4449
4986
|
try {
|
|
4450
4987
|
return JSON.parse(
|
|
4451
|
-
await readFile9(
|
|
4988
|
+
await readFile9(join25(projectRoot, PATHS.STACK_DRIFT), "utf8")
|
|
4452
4989
|
);
|
|
4453
4990
|
} catch {
|
|
4454
4991
|
return null;
|
|
@@ -4611,7 +5148,7 @@ async function readTemplate(packRoot, templatePath) {
|
|
|
4611
5148
|
return null;
|
|
4612
5149
|
}
|
|
4613
5150
|
try {
|
|
4614
|
-
return await readFile9(
|
|
5151
|
+
return await readFile9(join25(packRoot, templatePath), "utf8");
|
|
4615
5152
|
} catch {
|
|
4616
5153
|
return null;
|
|
4617
5154
|
}
|
|
@@ -4619,7 +5156,7 @@ async function readTemplate(packRoot, templatePath) {
|
|
|
4619
5156
|
|
|
4620
5157
|
// src/document/progress-tracker.ts
|
|
4621
5158
|
import { mkdir as mkdir9, readFile as readFile10, rm, writeFile as writeFile9 } from "fs/promises";
|
|
4622
|
-
import { dirname as dirname13, join as
|
|
5159
|
+
import { dirname as dirname13, join as join26 } from "path";
|
|
4623
5160
|
var DocumentProgressTracker = class {
|
|
4624
5161
|
constructor(validator = new SchemaValidator()) {
|
|
4625
5162
|
this.validator = validator;
|
|
@@ -4627,7 +5164,7 @@ var DocumentProgressTracker = class {
|
|
|
4627
5164
|
async load(projectRoot) {
|
|
4628
5165
|
try {
|
|
4629
5166
|
const parsed = JSON.parse(
|
|
4630
|
-
await readFile10(
|
|
5167
|
+
await readFile10(join26(projectRoot, PATHS.DOC_PROGRESS), "utf8")
|
|
4631
5168
|
);
|
|
4632
5169
|
const validation = this.validator.validate("doc-progress", parsed);
|
|
4633
5170
|
if (!validation.valid) {
|
|
@@ -4639,7 +5176,7 @@ var DocumentProgressTracker = class {
|
|
|
4639
5176
|
}
|
|
4640
5177
|
}
|
|
4641
5178
|
async save(projectRoot, progress) {
|
|
4642
|
-
const target =
|
|
5179
|
+
const target = join26(projectRoot, PATHS.DOC_PROGRESS);
|
|
4643
5180
|
await mkdir9(dirname13(target), { recursive: true });
|
|
4644
5181
|
await writeFile9(target, `${JSON.stringify(progress, null, 2)}
|
|
4645
5182
|
`);
|
|
@@ -4647,7 +5184,7 @@ var DocumentProgressTracker = class {
|
|
|
4647
5184
|
async resetGeneratingEntries(projectRoot, progress) {
|
|
4648
5185
|
await Promise.all(
|
|
4649
5186
|
resetGeneratingEntries(progress).map(
|
|
4650
|
-
(entry) => rm(
|
|
5187
|
+
(entry) => rm(join26(projectRoot, entry.output_path), { force: true })
|
|
4651
5188
|
)
|
|
4652
5189
|
);
|
|
4653
5190
|
}
|
|
@@ -4697,12 +5234,12 @@ function collectEntries(progress) {
|
|
|
4697
5234
|
// src/document/staleness.ts
|
|
4698
5235
|
import { createHash as createHash6 } from "crypto";
|
|
4699
5236
|
import { readFile as readFile11 } from "fs/promises";
|
|
4700
|
-
import { join as
|
|
5237
|
+
import { join as join27 } from "path";
|
|
4701
5238
|
async function hashSourceFiles(projectRoot, sourceFiles) {
|
|
4702
5239
|
const hash = createHash6("sha1");
|
|
4703
5240
|
for (const relativePath of sourceFiles) {
|
|
4704
5241
|
try {
|
|
4705
|
-
hash.update(await readFile11(
|
|
5242
|
+
hash.update(await readFile11(join27(projectRoot, relativePath), "utf8"));
|
|
4706
5243
|
} catch {
|
|
4707
5244
|
hash.update(`missing:${relativePath}`);
|
|
4708
5245
|
}
|
|
@@ -4760,7 +5297,11 @@ var DocumentationWorkflow = class {
|
|
|
4760
5297
|
routing,
|
|
4761
5298
|
stackSnapshot
|
|
4762
5299
|
);
|
|
4763
|
-
const sourceFiles = await gatherSourceFiles(
|
|
5300
|
+
const sourceFiles = await gatherSourceFiles(
|
|
5301
|
+
options.projectRoot,
|
|
5302
|
+
routing.stack,
|
|
5303
|
+
stackSnapshot.repository
|
|
5304
|
+
);
|
|
4764
5305
|
const modules = await discoverModules(options.projectRoot);
|
|
4765
5306
|
const generated = [];
|
|
4766
5307
|
const skipped = [];
|
|
@@ -4778,13 +5319,13 @@ var DocumentationWorkflow = class {
|
|
|
4778
5319
|
{ writeHumanDocs: true }
|
|
4779
5320
|
);
|
|
4780
5321
|
const stackDocs = [
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
5322
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "overview.md"),
|
|
5323
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "frameworks.md"),
|
|
5324
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "dependencies.md"),
|
|
5325
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "tooling.md"),
|
|
5326
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "version-rules.md"),
|
|
5327
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "sources.md"),
|
|
5328
|
+
join28(PATHS.FRAMEWORK_STACK_DIR, "drift-report.md")
|
|
4788
5329
|
];
|
|
4789
5330
|
generated.push(...stackDocs);
|
|
4790
5331
|
steps.push({
|
|
@@ -4803,8 +5344,8 @@ var DocumentationWorkflow = class {
|
|
|
4803
5344
|
stackSnapshot,
|
|
4804
5345
|
profile
|
|
4805
5346
|
});
|
|
4806
|
-
await mkdir10(dirname14(
|
|
4807
|
-
await writeFile10(
|
|
5347
|
+
await mkdir10(dirname14(join28(options.projectRoot, contentOutputPath)), { recursive: true });
|
|
5348
|
+
await writeFile10(join28(options.projectRoot, contentOutputPath), contentBody);
|
|
4808
5349
|
generated.push(contentOutputPath);
|
|
4809
5350
|
steps.push({
|
|
4810
5351
|
id: "content-deliverable",
|
|
@@ -4827,7 +5368,7 @@ var DocumentationWorkflow = class {
|
|
|
4827
5368
|
await processEntry({
|
|
4828
5369
|
projectRoot: options.projectRoot,
|
|
4829
5370
|
entry: progress.global.designSystem.designTokens,
|
|
4830
|
-
content: await readFile12(
|
|
5371
|
+
content: await readFile12(join28(options.projectRoot, PATHS.DESIGN_TOKENS_FILE), "utf8"),
|
|
4831
5372
|
generated: designSystemGenerated,
|
|
4832
5373
|
skipped: designSystemSkipped
|
|
4833
5374
|
});
|
|
@@ -4846,8 +5387,8 @@ var DocumentationWorkflow = class {
|
|
|
4846
5387
|
}
|
|
4847
5388
|
await Promise.all(
|
|
4848
5389
|
themeArtifacts.map(async (artifact) => {
|
|
4849
|
-
await mkdir10(dirname14(
|
|
4850
|
-
await writeFile10(
|
|
5390
|
+
await mkdir10(dirname14(join28(options.projectRoot, artifact.path)), { recursive: true });
|
|
5391
|
+
await writeFile10(join28(options.projectRoot, artifact.path), artifact.content);
|
|
4851
5392
|
designSystemGenerated.push(artifact.path);
|
|
4852
5393
|
})
|
|
4853
5394
|
);
|
|
@@ -4861,15 +5402,15 @@ var DocumentationWorkflow = class {
|
|
|
4861
5402
|
});
|
|
4862
5403
|
progress.global.architecture ??= {};
|
|
4863
5404
|
progress.global.architecture.overview ??= this.tracker.createEntry(
|
|
4864
|
-
|
|
5405
|
+
join28(PATHS.ARCHITECTURE_DIR, "overview.md"),
|
|
4865
5406
|
sourceFiles
|
|
4866
5407
|
);
|
|
4867
5408
|
progress.global.architecture.decisions ??= this.tracker.createEntry(
|
|
4868
|
-
|
|
5409
|
+
join28(PATHS.ARCHITECTURE_DIR, "decisions.md"),
|
|
4869
5410
|
sourceFiles
|
|
4870
5411
|
);
|
|
4871
5412
|
progress.global.architecture.patterns ??= this.tracker.createEntry(
|
|
4872
|
-
|
|
5413
|
+
join28(PATHS.ARCHITECTURE_DIR, "patterns.md"),
|
|
4873
5414
|
sourceFiles
|
|
4874
5415
|
);
|
|
4875
5416
|
const architectureGenerated = [];
|
|
@@ -4907,7 +5448,7 @@ var DocumentationWorkflow = class {
|
|
|
4907
5448
|
const registrySkipped = [];
|
|
4908
5449
|
progress.global.registries ??= {};
|
|
4909
5450
|
for (const registry of REGISTRIES) {
|
|
4910
|
-
const path =
|
|
5451
|
+
const path = join28(PATHS.REGISTRIES_DIR, registry);
|
|
4911
5452
|
progress.global.registries[registry] ??= this.tracker.createEntry(path, sourceFiles);
|
|
4912
5453
|
await processEntry({
|
|
4913
5454
|
projectRoot: options.projectRoot,
|
|
@@ -4927,12 +5468,12 @@ var DocumentationWorkflow = class {
|
|
|
4927
5468
|
});
|
|
4928
5469
|
progress.global.benchmarks ??= {};
|
|
4929
5470
|
progress.global.benchmarks.index ??= this.tracker.createEntry(
|
|
4930
|
-
|
|
5471
|
+
join28(PATHS.BENCHMARKS_DIR, "index.md"),
|
|
4931
5472
|
sourceFiles
|
|
4932
5473
|
);
|
|
4933
5474
|
progress.global.techDebt ??= {};
|
|
4934
5475
|
progress.global.techDebt.index ??= this.tracker.createEntry(
|
|
4935
|
-
|
|
5476
|
+
join28(PATHS.TECH_DEBT_DIR, "index.md"),
|
|
4936
5477
|
sourceFiles
|
|
4937
5478
|
);
|
|
4938
5479
|
const maintenanceGenerated = [];
|
|
@@ -4972,7 +5513,7 @@ var DocumentationWorkflow = class {
|
|
|
4972
5513
|
);
|
|
4973
5514
|
for (const feature of features) {
|
|
4974
5515
|
for (const definition of FEATURE_DOCS) {
|
|
4975
|
-
const path =
|
|
5516
|
+
const path = join28(
|
|
4976
5517
|
PATHS.MODULES_DIR,
|
|
4977
5518
|
moduleName,
|
|
4978
5519
|
PATHS.MODULE_FEATURES_DIR,
|
|
@@ -5005,7 +5546,7 @@ var DocumentationWorkflow = class {
|
|
|
5005
5546
|
}
|
|
5006
5547
|
}
|
|
5007
5548
|
for (const definition of MODULE_DOCS) {
|
|
5008
|
-
const path =
|
|
5549
|
+
const path = join28(PATHS.MODULES_DIR, moduleName, definition.outputPath);
|
|
5009
5550
|
progress.modules[moduleName][definition.key] ??= this.tracker.createEntry(
|
|
5010
5551
|
path,
|
|
5011
5552
|
moduleFiles
|
|
@@ -5037,7 +5578,7 @@ var DocumentationWorkflow = class {
|
|
|
5037
5578
|
}
|
|
5038
5579
|
progress.global.handover ??= {};
|
|
5039
5580
|
progress.global.handover.summary ??= this.tracker.createEntry(
|
|
5040
|
-
|
|
5581
|
+
join28(".paqad/handover", "product-summary.md"),
|
|
5041
5582
|
sourceFiles
|
|
5042
5583
|
);
|
|
5043
5584
|
const handoverGenerated = [];
|
|
@@ -5062,7 +5603,7 @@ var DocumentationWorkflow = class {
|
|
|
5062
5603
|
generated,
|
|
5063
5604
|
skipped,
|
|
5064
5605
|
progress_path: PATHS.DOC_PROGRESS,
|
|
5065
|
-
handover_path:
|
|
5606
|
+
handover_path: join28(".paqad/handover", "product-summary.md"),
|
|
5066
5607
|
stack_snapshot: stackSnapshot,
|
|
5067
5608
|
effective_routing: routing,
|
|
5068
5609
|
profile_updated: profileUpdated,
|
|
@@ -5093,7 +5634,8 @@ async function syncOnboardingState(projectRoot, profile, detection, routing, sta
|
|
|
5093
5634
|
if (manifest !== null) {
|
|
5094
5635
|
writeOnboardingManifest(projectRoot, {
|
|
5095
5636
|
...manifest,
|
|
5096
|
-
profile: updatedProfile
|
|
5637
|
+
profile: updatedProfile,
|
|
5638
|
+
repository: detection.repository ?? stackSnapshot.repository
|
|
5097
5639
|
});
|
|
5098
5640
|
}
|
|
5099
5641
|
return true;
|
|
@@ -5133,8 +5675,8 @@ async function processEntry(input) {
|
|
|
5133
5675
|
input.entry.state = "generating";
|
|
5134
5676
|
input.entry.started_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
5135
5677
|
input.entry.error = null;
|
|
5136
|
-
await mkdir10(dirname14(
|
|
5137
|
-
await writeFile10(
|
|
5678
|
+
await mkdir10(dirname14(join28(input.projectRoot, input.entry.output_path)), { recursive: true });
|
|
5679
|
+
await writeFile10(join28(input.projectRoot, input.entry.output_path), input.content);
|
|
5138
5680
|
input.generated.push(input.entry.output_path);
|
|
5139
5681
|
input.entry.state = "done";
|
|
5140
5682
|
input.entry.completed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5153,8 +5695,8 @@ async function processEntry(input) {
|
|
|
5153
5695
|
}
|
|
5154
5696
|
return { generated: [input.entry.output_path], skipped: [] };
|
|
5155
5697
|
}
|
|
5156
|
-
async function gatherSourceFiles(projectRoot, stack) {
|
|
5157
|
-
const roots = stack === "flutter" ? ["lib", "test", "web", "assets", "pubspec.yaml"] : stack === "short-video" ? ["src", "content", "scripts", "package.json"] : [
|
|
5698
|
+
async function gatherSourceFiles(projectRoot, stack, repository) {
|
|
5699
|
+
const roots = repository?.projects.length ? repository.projects.map((project) => project.root) : stack === "flutter" ? ["lib", "test", "web", "assets", "pubspec.yaml"] : stack === "short-video" ? ["src", "content", "scripts", "package.json"] : [
|
|
5158
5700
|
"app",
|
|
5159
5701
|
"routes",
|
|
5160
5702
|
"config",
|
|
@@ -5183,7 +5725,7 @@ async function gatherSourceFiles(projectRoot, stack) {
|
|
|
5183
5725
|
const results = /* @__PURE__ */ new Set();
|
|
5184
5726
|
for (const root of roots) {
|
|
5185
5727
|
try {
|
|
5186
|
-
for (const file of await
|
|
5728
|
+
for (const file of await walk3(join28(projectRoot, root))) {
|
|
5187
5729
|
results.add(relative(projectRoot, file));
|
|
5188
5730
|
}
|
|
5189
5731
|
} catch {
|
|
@@ -5200,7 +5742,7 @@ async function gatherSourceFiles(projectRoot, stack) {
|
|
|
5200
5742
|
}
|
|
5201
5743
|
async function buildContentDeliverable(input) {
|
|
5202
5744
|
const styleGuide = await readOptionalFile(
|
|
5203
|
-
|
|
5745
|
+
join28(input.projectRoot, "docs/instructions/rules/writing-style.md")
|
|
5204
5746
|
);
|
|
5205
5747
|
const codingContext = input.profile.active_capabilities.includes("coding") && input.stackSnapshot.profile.frameworks.length > 0 ? [
|
|
5206
5748
|
"## Technical Context",
|
|
@@ -5248,19 +5790,36 @@ async function readOptionalFile(path) {
|
|
|
5248
5790
|
}
|
|
5249
5791
|
}
|
|
5250
5792
|
function defaultContentOutputPath(requestText) {
|
|
5251
|
-
return
|
|
5793
|
+
return join28("content", `${slugify(requestText)}.md`);
|
|
5252
5794
|
}
|
|
5253
5795
|
function slugify(requestText) {
|
|
5254
5796
|
const slug = requestText.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
5255
5797
|
return slug.length > 0 ? slug : "content-draft";
|
|
5256
5798
|
}
|
|
5257
|
-
async function
|
|
5258
|
-
const entries = await
|
|
5799
|
+
async function walk3(root) {
|
|
5800
|
+
const entries = await readdir3(root, { withFileTypes: true });
|
|
5259
5801
|
const files = [];
|
|
5260
5802
|
for (const entry of entries) {
|
|
5261
|
-
const target =
|
|
5803
|
+
const target = join28(root, entry.name);
|
|
5262
5804
|
if (entry.isDirectory()) {
|
|
5263
|
-
|
|
5805
|
+
if ([
|
|
5806
|
+
".dart_tool",
|
|
5807
|
+
".git",
|
|
5808
|
+
".gradle",
|
|
5809
|
+
".next",
|
|
5810
|
+
".nuxt",
|
|
5811
|
+
".turbo",
|
|
5812
|
+
"build",
|
|
5813
|
+
"coverage",
|
|
5814
|
+
"dist",
|
|
5815
|
+
"node_modules",
|
|
5816
|
+
"out",
|
|
5817
|
+
"target",
|
|
5818
|
+
"vendor"
|
|
5819
|
+
].includes(entry.name)) {
|
|
5820
|
+
continue;
|
|
5821
|
+
}
|
|
5822
|
+
files.push(...await walk3(target));
|
|
5264
5823
|
continue;
|
|
5265
5824
|
}
|
|
5266
5825
|
files.push(target);
|
|
@@ -5301,8 +5860,8 @@ async function discoverFeaturesForModule(projectRoot, moduleName, moduleFiles) {
|
|
|
5301
5860
|
}
|
|
5302
5861
|
async function readExistingFeatures(projectRoot, moduleName) {
|
|
5303
5862
|
try {
|
|
5304
|
-
const root =
|
|
5305
|
-
const entries = await
|
|
5863
|
+
const root = join28(projectRoot, PATHS.MODULES_DIR, moduleName, PATHS.MODULE_FEATURES_DIR);
|
|
5864
|
+
const entries = await readdir3(root, { withFileTypes: true });
|
|
5306
5865
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
|
|
5307
5866
|
} catch {
|
|
5308
5867
|
return [];
|
|
@@ -5321,7 +5880,7 @@ function loadProjectProfile(projectRoot) {
|
|
|
5321
5880
|
async function readOnboardingManifest(projectRoot) {
|
|
5322
5881
|
try {
|
|
5323
5882
|
return JSON.parse(
|
|
5324
|
-
await readFile12(
|
|
5883
|
+
await readFile12(join28(projectRoot, PATHS.ONBOARDING_MANIFEST), "utf8")
|
|
5325
5884
|
);
|
|
5326
5885
|
} catch {
|
|
5327
5886
|
return null;
|
|
@@ -5712,12 +6271,12 @@ function basenameWithoutExtension(path) {
|
|
|
5712
6271
|
|
|
5713
6272
|
// src/onboarding/file-writer.ts
|
|
5714
6273
|
import { chmodSync, existsSync as existsSync12, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
5715
|
-
import { dirname as dirname15, join as
|
|
6274
|
+
import { dirname as dirname15, join as join29 } from "path";
|
|
5716
6275
|
function writeGeneratedFiles(projectRoot, files) {
|
|
5717
6276
|
const written = [];
|
|
5718
6277
|
const skipped = [];
|
|
5719
6278
|
for (const file of files) {
|
|
5720
|
-
const target =
|
|
6279
|
+
const target = join29(projectRoot, file.path);
|
|
5721
6280
|
mkdirSync4(dirname15(target), { recursive: true });
|
|
5722
6281
|
if (!file.autoUpdate && existsSync12(target)) {
|
|
5723
6282
|
skipped.push(file.path);
|
|
@@ -5732,13 +6291,190 @@ function writeGeneratedFiles(projectRoot, files) {
|
|
|
5732
6291
|
return { written, skipped };
|
|
5733
6292
|
}
|
|
5734
6293
|
|
|
6294
|
+
// src/pipeline/feature-development-policy.ts
|
|
6295
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
6296
|
+
import { join as join30 } from "path";
|
|
6297
|
+
import YAML5 from "yaml";
|
|
6298
|
+
function resolveFeatureDevelopmentCheckCommands(checks, profile) {
|
|
6299
|
+
if (checks === null) {
|
|
6300
|
+
return { commands: [], warnings: [] };
|
|
6301
|
+
}
|
|
6302
|
+
const commands = [];
|
|
6303
|
+
const warnings = [];
|
|
6304
|
+
if (checks.use_project_profile_commands) {
|
|
6305
|
+
if (profile === null) {
|
|
6306
|
+
return { commands, warnings };
|
|
6307
|
+
}
|
|
6308
|
+
for (const logicalCommand of checks.commands) {
|
|
6309
|
+
const resolved = profile?.commands?.[logicalCommand];
|
|
6310
|
+
if (!resolved) {
|
|
6311
|
+
warnings.push(
|
|
6312
|
+
`Feature development policy requested project command "${logicalCommand}" but it was not found in ${PATHS.PROJECT_PROFILE}.`
|
|
6313
|
+
);
|
|
6314
|
+
continue;
|
|
6315
|
+
}
|
|
6316
|
+
commands.push({
|
|
6317
|
+
logical_command: logicalCommand,
|
|
6318
|
+
command: resolved,
|
|
6319
|
+
source: "project-profile"
|
|
6320
|
+
});
|
|
6321
|
+
}
|
|
6322
|
+
}
|
|
6323
|
+
for (const shellCommand of checks.shell_commands) {
|
|
6324
|
+
commands.push({
|
|
6325
|
+
logical_command: null,
|
|
6326
|
+
command: shellCommand,
|
|
6327
|
+
source: "policy"
|
|
6328
|
+
});
|
|
6329
|
+
}
|
|
6330
|
+
return { commands, warnings };
|
|
6331
|
+
}
|
|
6332
|
+
function renderDefaultFeatureDevelopmentPolicyYaml() {
|
|
6333
|
+
return `# Feature Development Stage Policy
|
|
6334
|
+
# This file customizes how the built-in feature-development workflow behaves in this project.
|
|
6335
|
+
# The framework still owns routing, phase order, and mandatory safety stages.
|
|
6336
|
+
schema_version: "1"
|
|
6337
|
+
merge_mode: append
|
|
6338
|
+
|
|
6339
|
+
stages:
|
|
6340
|
+
planning:
|
|
6341
|
+
# Extra context to load before planning starts.
|
|
6342
|
+
read:
|
|
6343
|
+
- docs/modules/**
|
|
6344
|
+
- docs/instructions/**
|
|
6345
|
+
instructions:
|
|
6346
|
+
- Review the canonical module and instruction docs before planning the change.
|
|
6347
|
+
- Keep planning scoped to the requested feature and the current repository state.
|
|
6348
|
+
required_inputs:
|
|
6349
|
+
- active request
|
|
6350
|
+
- canonical docs
|
|
6351
|
+
strictness:
|
|
6352
|
+
require_docs_context: true
|
|
6353
|
+
escalation:
|
|
6354
|
+
missing_docs_context: warn
|
|
6355
|
+
artifacts:
|
|
6356
|
+
- implementation sequence
|
|
6357
|
+
|
|
6358
|
+
specification:
|
|
6359
|
+
instructions:
|
|
6360
|
+
- Write or refine the feature specification before implementation when the lane includes specification.
|
|
6361
|
+
required_inputs:
|
|
6362
|
+
- approved spec boundary
|
|
6363
|
+
strictness:
|
|
6364
|
+
require_spec: true
|
|
6365
|
+
escalation:
|
|
6366
|
+
missing_spec: stop
|
|
6367
|
+
artifacts:
|
|
6368
|
+
- specification
|
|
6369
|
+
|
|
6370
|
+
development:
|
|
6371
|
+
instructions:
|
|
6372
|
+
- Implement only the requested feature behavior and avoid unrelated refactors.
|
|
6373
|
+
required_inputs:
|
|
6374
|
+
- approved spec
|
|
6375
|
+
- implementation sequence
|
|
6376
|
+
strictness:
|
|
6377
|
+
avoid_unrelated_refactors: true
|
|
6378
|
+
escalation:
|
|
6379
|
+
scope_expansion: ask
|
|
6380
|
+
artifacts:
|
|
6381
|
+
- code changes
|
|
6382
|
+
|
|
6383
|
+
review:
|
|
6384
|
+
instructions:
|
|
6385
|
+
- Review the change against correctness, regressions, and rollback risk before treating it as complete.
|
|
6386
|
+
required_inputs:
|
|
6387
|
+
- code diff
|
|
6388
|
+
- verification summary
|
|
6389
|
+
strictness:
|
|
6390
|
+
require_review: true
|
|
6391
|
+
escalation:
|
|
6392
|
+
review_findings: stop
|
|
6393
|
+
artifacts:
|
|
6394
|
+
- review summary
|
|
6395
|
+
|
|
6396
|
+
checks:
|
|
6397
|
+
instructions:
|
|
6398
|
+
- Run the project command checks after implementation and before finalizing the feature.
|
|
6399
|
+
required_inputs:
|
|
6400
|
+
- working tree diff
|
|
6401
|
+
strictness:
|
|
6402
|
+
block_on_failure: true
|
|
6403
|
+
escalation:
|
|
6404
|
+
missing_command_mapping: stop
|
|
6405
|
+
artifacts:
|
|
6406
|
+
- verification summary
|
|
6407
|
+
checks:
|
|
6408
|
+
use_project_profile_commands: true
|
|
6409
|
+
commands:
|
|
6410
|
+
- format
|
|
6411
|
+
- test
|
|
6412
|
+
- build
|
|
6413
|
+
shell_commands: []
|
|
6414
|
+
block_on_failure: true
|
|
6415
|
+
# Add project-specific commands here when needed.
|
|
6416
|
+
# shell_commands:
|
|
6417
|
+
# - pnpm typecheck
|
|
6418
|
+
|
|
6419
|
+
documentation_sync:
|
|
6420
|
+
instructions:
|
|
6421
|
+
- Sync canonical docs affected by the feature diff after verification passes.
|
|
6422
|
+
required_inputs:
|
|
6423
|
+
- changed files
|
|
6424
|
+
strictness:
|
|
6425
|
+
require_canonical_sync: true
|
|
6426
|
+
escalation:
|
|
6427
|
+
stale_docs: stop
|
|
6428
|
+
artifacts:
|
|
6429
|
+
- stale doc targets
|
|
6430
|
+
`;
|
|
6431
|
+
}
|
|
6432
|
+
function summarizeFeatureDevelopmentStage(policy, stageName) {
|
|
6433
|
+
if (policy === null) {
|
|
6434
|
+
return null;
|
|
6435
|
+
}
|
|
6436
|
+
const stage = policy.stages[stageName];
|
|
6437
|
+
const fragments = [];
|
|
6438
|
+
if (stage.read.length > 0) {
|
|
6439
|
+
fragments.push(`reads ${stage.read.length} path(s)`);
|
|
6440
|
+
}
|
|
6441
|
+
if (stage.instructions.length > 0) {
|
|
6442
|
+
fragments.push(`${stage.instructions.length} instruction(s)`);
|
|
6443
|
+
}
|
|
6444
|
+
if (stage.required_inputs.length > 0) {
|
|
6445
|
+
fragments.push(`${stage.required_inputs.length} required input(s)`);
|
|
6446
|
+
}
|
|
6447
|
+
if (stage.artifacts.length > 0) {
|
|
6448
|
+
fragments.push(`${stage.artifacts.length} expected artifact(s)`);
|
|
6449
|
+
}
|
|
6450
|
+
if (stage.checks !== null) {
|
|
6451
|
+
const checkCount = stage.checks.commands.length + stage.checks.shell_commands.length;
|
|
6452
|
+
fragments.push(`${checkCount} configured check(s)`);
|
|
6453
|
+
}
|
|
6454
|
+
return fragments.length > 0 ? fragments.join(", ") : null;
|
|
6455
|
+
}
|
|
6456
|
+
|
|
6457
|
+
// src/onboarding/feature-policy-generator.ts
|
|
6458
|
+
function generateFeatureDevelopmentPolicy(domain) {
|
|
6459
|
+
if (domain !== "coding") {
|
|
6460
|
+
return [];
|
|
6461
|
+
}
|
|
6462
|
+
return [
|
|
6463
|
+
{
|
|
6464
|
+
path: `${PATHS.WORKFLOWS_DIR}/feature-development.yaml`,
|
|
6465
|
+
content: renderDefaultFeatureDevelopmentPolicyYaml(),
|
|
6466
|
+
autoUpdate: false
|
|
6467
|
+
}
|
|
6468
|
+
];
|
|
6469
|
+
}
|
|
6470
|
+
|
|
5735
6471
|
// src/onboarding/orchestrator.ts
|
|
5736
|
-
import { readFileSync as
|
|
5737
|
-
import { join as
|
|
6472
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
6473
|
+
import { join as join36 } from "path";
|
|
5738
6474
|
|
|
5739
6475
|
// src/resolver/resolver.ts
|
|
5740
6476
|
import fg2 from "fast-glob";
|
|
5741
|
-
import { basename as
|
|
6477
|
+
import { basename as basename4, extname, relative as relative3 } from "pathe";
|
|
5742
6478
|
|
|
5743
6479
|
// src/resolver/artifact-types.ts
|
|
5744
6480
|
var ARTIFACT_TYPES = [
|
|
@@ -5776,14 +6512,14 @@ var ARTIFACT_OUTPUT_KEYS = {
|
|
|
5776
6512
|
};
|
|
5777
6513
|
|
|
5778
6514
|
// src/resolver/inheritance.ts
|
|
5779
|
-
import { join as
|
|
6515
|
+
import { join as join32, relative as relative2 } from "pathe";
|
|
5780
6516
|
|
|
5781
6517
|
// src/resolver/capability-resolver.ts
|
|
5782
|
-
import { join as
|
|
6518
|
+
import { join as join31 } from "pathe";
|
|
5783
6519
|
function resolveCapabilityDirectories(runtimeRoot, stack, capabilities, artifactType) {
|
|
5784
6520
|
const directoryName = resolveCapabilityArtifactDirectory(artifactType);
|
|
5785
6521
|
return capabilities.map(
|
|
5786
|
-
(capability) =>
|
|
6522
|
+
(capability) => join31(
|
|
5787
6523
|
runtimeRoot,
|
|
5788
6524
|
"capabilities",
|
|
5789
6525
|
"coding",
|
|
@@ -5800,9 +6536,9 @@ function resolveCapabilityArtifactDirectory(artifactType) {
|
|
|
5800
6536
|
case "mcp-configs":
|
|
5801
6537
|
return "mcp";
|
|
5802
6538
|
case "patterns":
|
|
5803
|
-
return
|
|
6539
|
+
return join31("benchmarks", "patterns");
|
|
5804
6540
|
case "anti-patterns":
|
|
5805
|
-
return
|
|
6541
|
+
return join31("benchmarks", "anti-patterns");
|
|
5806
6542
|
default:
|
|
5807
6543
|
return artifactType;
|
|
5808
6544
|
}
|
|
@@ -5813,18 +6549,18 @@ function getInheritanceDirectories(runtimeRoot, routing, artifactType) {
|
|
|
5813
6549
|
if (artifactType === "hooks") {
|
|
5814
6550
|
return [
|
|
5815
6551
|
{
|
|
5816
|
-
path:
|
|
6552
|
+
path: join32(runtimeRoot, "hooks"),
|
|
5817
6553
|
level: 0,
|
|
5818
|
-
source: relative2(runtimeRoot,
|
|
6554
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "hooks"))
|
|
5819
6555
|
}
|
|
5820
6556
|
];
|
|
5821
6557
|
}
|
|
5822
6558
|
if (artifactType === "templates") {
|
|
5823
6559
|
return [
|
|
5824
6560
|
{
|
|
5825
|
-
path:
|
|
6561
|
+
path: join32(runtimeRoot, "templates"),
|
|
5826
6562
|
level: 0,
|
|
5827
|
-
source: relative2(runtimeRoot,
|
|
6563
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "templates"))
|
|
5828
6564
|
}
|
|
5829
6565
|
];
|
|
5830
6566
|
}
|
|
@@ -5834,37 +6570,37 @@ function getInheritanceDirectories(runtimeRoot, routing, artifactType) {
|
|
|
5834
6570
|
const traits = deriveTraits(routing);
|
|
5835
6571
|
const directories = [
|
|
5836
6572
|
{
|
|
5837
|
-
path:
|
|
6573
|
+
path: join32(runtimeRoot, "base", directoryName),
|
|
5838
6574
|
level: 0,
|
|
5839
|
-
source: relative2(runtimeRoot,
|
|
6575
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "base", directoryName))
|
|
5840
6576
|
},
|
|
5841
6577
|
{
|
|
5842
|
-
path:
|
|
6578
|
+
path: join32(runtimeRoot, "capabilities", "content", directoryName),
|
|
5843
6579
|
level: 1,
|
|
5844
|
-
source: relative2(runtimeRoot,
|
|
6580
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "capabilities", "content", directoryName))
|
|
5845
6581
|
}
|
|
5846
6582
|
];
|
|
5847
6583
|
if (activeCapabilities.includes("coding")) {
|
|
5848
6584
|
directories.push(
|
|
5849
6585
|
{
|
|
5850
|
-
path:
|
|
6586
|
+
path: join32(runtimeRoot, "capabilities", "coding", directoryName),
|
|
5851
6587
|
level: 2,
|
|
5852
|
-
source: relative2(runtimeRoot,
|
|
6588
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "capabilities", "coding", directoryName))
|
|
5853
6589
|
},
|
|
5854
6590
|
{
|
|
5855
|
-
path:
|
|
6591
|
+
path: join32(runtimeRoot, "capabilities", "coding", "stacks", "_shared", directoryName),
|
|
5856
6592
|
level: 3,
|
|
5857
6593
|
source: relative2(
|
|
5858
6594
|
runtimeRoot,
|
|
5859
|
-
|
|
6595
|
+
join32(runtimeRoot, "capabilities", "coding", "stacks", "_shared", directoryName)
|
|
5860
6596
|
)
|
|
5861
6597
|
},
|
|
5862
6598
|
...matchedPacks.map((pack, index) => ({
|
|
5863
|
-
path:
|
|
6599
|
+
path: join32(runtimeRoot, "capabilities", "coding", "stacks", pack, directoryName),
|
|
5864
6600
|
level: 4 + Math.min(index, 1),
|
|
5865
6601
|
source: relative2(
|
|
5866
6602
|
runtimeRoot,
|
|
5867
|
-
|
|
6603
|
+
join32(runtimeRoot, "capabilities", "coding", "stacks", pack, directoryName)
|
|
5868
6604
|
)
|
|
5869
6605
|
})),
|
|
5870
6606
|
...matchedPacks.flatMap(
|
|
@@ -5878,9 +6614,9 @@ function getInheritanceDirectories(runtimeRoot, routing, artifactType) {
|
|
|
5878
6614
|
}
|
|
5879
6615
|
if (activeCapabilities.includes("security")) {
|
|
5880
6616
|
directories.push({
|
|
5881
|
-
path:
|
|
6617
|
+
path: join32(runtimeRoot, "capabilities", "security", directoryName),
|
|
5882
6618
|
level: 6,
|
|
5883
|
-
source: relative2(runtimeRoot,
|
|
6619
|
+
source: relative2(runtimeRoot, join32(runtimeRoot, "capabilities", "security", directoryName))
|
|
5884
6620
|
});
|
|
5885
6621
|
}
|
|
5886
6622
|
return directories;
|
|
@@ -5917,9 +6653,9 @@ function resolveArtifactDirectoryName(artifactType) {
|
|
|
5917
6653
|
case "mcp-configs":
|
|
5918
6654
|
return "mcp";
|
|
5919
6655
|
case "patterns":
|
|
5920
|
-
return
|
|
6656
|
+
return join32("benchmarks", "patterns");
|
|
5921
6657
|
case "anti-patterns":
|
|
5922
|
-
return
|
|
6658
|
+
return join32("benchmarks", "anti-patterns");
|
|
5923
6659
|
default:
|
|
5924
6660
|
return artifactType;
|
|
5925
6661
|
}
|
|
@@ -6021,14 +6757,14 @@ function compareRuleSeedOrder(left, right) {
|
|
|
6021
6757
|
return left.source.localeCompare(right.source);
|
|
6022
6758
|
}
|
|
6023
6759
|
function getRulePriority(filePath) {
|
|
6024
|
-
const name =
|
|
6760
|
+
const name = basename4(filePath, extname(filePath));
|
|
6025
6761
|
const index = RULE_SEED_PRIORITY.indexOf(name);
|
|
6026
6762
|
return index === -1 ? RULE_SEED_PRIORITY.length : index;
|
|
6027
6763
|
}
|
|
6028
6764
|
|
|
6029
6765
|
// src/onboarding/gitignore-writer.ts
|
|
6030
|
-
import { existsSync as
|
|
6031
|
-
import { join as
|
|
6766
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
6767
|
+
import { join as join33 } from "path";
|
|
6032
6768
|
var PAQAD_MARKER = "# paqad-ai";
|
|
6033
6769
|
var PAQAD_GITIGNORE_ENTRIES = [
|
|
6034
6770
|
PAQAD_MARKER,
|
|
@@ -6041,8 +6777,8 @@ var PAQAD_GITIGNORE_ENTRIES = [
|
|
|
6041
6777
|
".paqad/theme/"
|
|
6042
6778
|
].join("\n");
|
|
6043
6779
|
function writeGitignore(projectRoot) {
|
|
6044
|
-
const gitignorePath =
|
|
6045
|
-
const existing =
|
|
6780
|
+
const gitignorePath = join33(projectRoot, ".gitignore");
|
|
6781
|
+
const existing = existsSync14(gitignorePath) ? readFileSync12(gitignorePath, "utf8") : "";
|
|
6046
6782
|
if (existing.includes(PAQAD_MARKER)) {
|
|
6047
6783
|
return;
|
|
6048
6784
|
}
|
|
@@ -6514,9 +7250,9 @@ function inferSelectionDomain(detection, overrides, snapshot) {
|
|
|
6514
7250
|
}
|
|
6515
7251
|
|
|
6516
7252
|
// src/onboarding/reference-generator.ts
|
|
6517
|
-
import { existsSync as
|
|
7253
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6518
7254
|
import { readFile as readFile13 } from "fs/promises";
|
|
6519
|
-
import { join as
|
|
7255
|
+
import { join as join34, relative as relative4 } from "path";
|
|
6520
7256
|
import fg3 from "fast-glob";
|
|
6521
7257
|
async function generateReferenceGuides(runtimeRoot, context) {
|
|
6522
7258
|
if (context.domain !== "coding") {
|
|
@@ -6527,8 +7263,8 @@ async function generateReferenceGuides(runtimeRoot, context) {
|
|
|
6527
7263
|
routing: { domain: context.domain },
|
|
6528
7264
|
stack_profile: context.stack_profile
|
|
6529
7265
|
});
|
|
6530
|
-
const referencesRoot =
|
|
6531
|
-
if (!
|
|
7266
|
+
const referencesRoot = join34(runtimeRoot, "capabilities", "coding", "stacks", stack, "references");
|
|
7267
|
+
if (!existsSync15(referencesRoot)) {
|
|
6532
7268
|
return [buildFallbackReferenceGuide(stack)];
|
|
6533
7269
|
}
|
|
6534
7270
|
const entries = await fg3(["tools/*.md", "tools-catalog.md"], {
|
|
@@ -6550,14 +7286,14 @@ async function generateReferenceGuides(runtimeRoot, context) {
|
|
|
6550
7286
|
function toProjectReferencePath(stack, relativePath) {
|
|
6551
7287
|
const normalized = relativePath.replaceAll("\\", "/");
|
|
6552
7288
|
if (normalized === "tools-catalog.md") {
|
|
6553
|
-
return
|
|
7289
|
+
return join34(PATHS.TOOLS_DIR, stack, "README.md");
|
|
6554
7290
|
}
|
|
6555
|
-
return
|
|
7291
|
+
return join34(PATHS.TOOLS_DIR, stack, normalized.replace(/^tools\//, ""));
|
|
6556
7292
|
}
|
|
6557
7293
|
function buildFallbackReferenceGuide(stack) {
|
|
6558
7294
|
const title = stack.split("-").map((segment) => segment.slice(0, 1).toUpperCase() + segment.slice(1)).join(" ");
|
|
6559
7295
|
return {
|
|
6560
|
-
path:
|
|
7296
|
+
path: join34(PATHS.TOOLS_DIR, stack, "README.md"),
|
|
6561
7297
|
autoUpdate: false,
|
|
6562
7298
|
content: [
|
|
6563
7299
|
`# ${title} Tool References`,
|
|
@@ -6575,7 +7311,7 @@ function buildFallbackReferenceGuide(stack) {
|
|
|
6575
7311
|
|
|
6576
7312
|
// src/onboarding/rule-generator.ts
|
|
6577
7313
|
import { readFile as readFile14 } from "fs/promises";
|
|
6578
|
-
import { join as
|
|
7314
|
+
import { join as join35 } from "path";
|
|
6579
7315
|
async function generateProjectRules(rules) {
|
|
6580
7316
|
return Promise.all(
|
|
6581
7317
|
rules.map(async (rule) => ({
|
|
@@ -6588,22 +7324,22 @@ async function generateProjectRules(rules) {
|
|
|
6588
7324
|
function toProjectRulePath(source) {
|
|
6589
7325
|
const normalized = source.replaceAll("\\", "/");
|
|
6590
7326
|
if (normalized.startsWith("base/rules/")) {
|
|
6591
|
-
return
|
|
7327
|
+
return join35(PATHS.RULES_DIR, "_shared", normalized.replace(/^base\/rules\//, ""));
|
|
6592
7328
|
}
|
|
6593
7329
|
if (normalized.startsWith("capabilities/")) {
|
|
6594
7330
|
const capabilityNormalized = normalized.replace(/^capabilities\//, "");
|
|
6595
7331
|
const [prefix2, suffix2] = capabilityNormalized.split("/rules/");
|
|
6596
7332
|
if (prefix2 !== void 0 && suffix2 !== void 0) {
|
|
6597
7333
|
const target2 = suffix2.endsWith("/guide.md") ? suffix2.replace("/guide.md", ".md") : suffix2;
|
|
6598
|
-
return
|
|
7334
|
+
return join35(PATHS.RULES_DIR, prefix2, target2);
|
|
6599
7335
|
}
|
|
6600
7336
|
}
|
|
6601
7337
|
const [prefix, suffix] = normalized.split("/rules/");
|
|
6602
7338
|
if (prefix === void 0 || suffix === void 0) {
|
|
6603
|
-
return
|
|
7339
|
+
return join35(PATHS.RULES_DIR, normalized);
|
|
6604
7340
|
}
|
|
6605
7341
|
const target = suffix.endsWith("/guide.md") ? suffix.replace("/guide.md", ".md") : suffix;
|
|
6606
|
-
return
|
|
7342
|
+
return join35(PATHS.RULES_DIR, prefix, target);
|
|
6607
7343
|
}
|
|
6608
7344
|
|
|
6609
7345
|
// src/onboarding/orchestrator.ts
|
|
@@ -6641,15 +7377,16 @@ var OnboardingOrchestrator = class {
|
|
|
6641
7377
|
}
|
|
6642
7378
|
}
|
|
6643
7379
|
generatedFiles.push(...await generateProjectRules(resolved.rules));
|
|
7380
|
+
generatedFiles.push(...generateFeatureDevelopmentPolicy(selections.domain));
|
|
6644
7381
|
generatedFiles.push(
|
|
6645
7382
|
...await generateReferenceGuides(runtimeRoot, {
|
|
6646
7383
|
domain: selections.domain,
|
|
6647
7384
|
stack_profile: selections.stack_profile
|
|
6648
7385
|
})
|
|
6649
7386
|
);
|
|
6650
|
-
const silentUpdateSrc =
|
|
7387
|
+
const silentUpdateSrc = join36(runtimeRoot, "hooks", "silent-update.sh");
|
|
6651
7388
|
try {
|
|
6652
|
-
const hookContent =
|
|
7389
|
+
const hookContent = readFileSync13(silentUpdateSrc, "utf8");
|
|
6653
7390
|
generatedFiles.push({
|
|
6654
7391
|
path: PATHS.HOOKS_SILENT_UPDATE,
|
|
6655
7392
|
content: hookContent,
|
|
@@ -6676,6 +7413,7 @@ var OnboardingOrchestrator = class {
|
|
|
6676
7413
|
project_root: options.projectRoot,
|
|
6677
7414
|
profile,
|
|
6678
7415
|
detected: detection,
|
|
7416
|
+
repository: detection.repository,
|
|
6679
7417
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6680
7418
|
generated_artifacts: generatedFiles.map((file) => ({
|
|
6681
7419
|
path: file.path,
|
|
@@ -6942,22 +7680,22 @@ function buildRustCommands(usingCompose) {
|
|
|
6942
7680
|
}
|
|
6943
7681
|
|
|
6944
7682
|
// src/onboarding/scaffold-generator.ts
|
|
6945
|
-
import { join as
|
|
7683
|
+
import { join as join37 } from "path";
|
|
6946
7684
|
|
|
6947
7685
|
// src/templates/registry.ts
|
|
6948
7686
|
import fg4 from "fast-glob";
|
|
6949
|
-
import { basename as
|
|
7687
|
+
import { basename as basename5, relative as relative5 } from "pathe";
|
|
6950
7688
|
|
|
6951
7689
|
// src/onboarding/scaffold-generator.ts
|
|
6952
7690
|
var FEATURE_TEMPLATE_TARGETS = [
|
|
6953
|
-
["business.md.hbs",
|
|
6954
|
-
["technical.md.hbs",
|
|
7691
|
+
["business.md.hbs", join37(PATHS.MODULE_FEATURES_DIR, "core", "business.md")],
|
|
7692
|
+
["technical.md.hbs", join37(PATHS.MODULE_FEATURES_DIR, "core", "technical.md")]
|
|
6955
7693
|
];
|
|
6956
7694
|
|
|
6957
7695
|
// src/packs/manager.ts
|
|
6958
7696
|
import {
|
|
6959
7697
|
cpSync,
|
|
6960
|
-
existsSync as
|
|
7698
|
+
existsSync as existsSync16,
|
|
6961
7699
|
mkdirSync as mkdirSync5,
|
|
6962
7700
|
mkdtempSync,
|
|
6963
7701
|
readdirSync as readdirSync3,
|
|
@@ -6965,14 +7703,14 @@ import {
|
|
|
6965
7703
|
writeFileSync as writeFileSync5
|
|
6966
7704
|
} from "fs";
|
|
6967
7705
|
import { homedir as homedir2 } from "os";
|
|
6968
|
-
import { join as
|
|
7706
|
+
import { join as join38, resolve as resolve2 } from "path";
|
|
6969
7707
|
import { execa } from "execa";
|
|
6970
7708
|
var SOURCE_ORDER2 = ["built-in", "global", "project"];
|
|
6971
7709
|
function resolvePackManagerRoots(projectRoot = process.cwd(), overrides = {}) {
|
|
6972
7710
|
return {
|
|
6973
7711
|
runtimeRoot: overrides.runtimeRoot ?? getRuntimeRoot(),
|
|
6974
|
-
globalPacksRoot: overrides.globalPacksRoot ?? process.env.PAQAD_GLOBAL_PACKS_ROOT ??
|
|
6975
|
-
projectPacksRoot: overrides.projectPacksRoot ??
|
|
7712
|
+
globalPacksRoot: overrides.globalPacksRoot ?? process.env.PAQAD_GLOBAL_PACKS_ROOT ?? join38(homedir2(), ".paqad", "packs"),
|
|
7713
|
+
projectPacksRoot: overrides.projectPacksRoot ?? join38(projectRoot, ".paqad", "packs"),
|
|
6976
7714
|
registryUrl: overrides.registryUrl ?? process.env.PAQAD_PACK_REGISTRY_URL
|
|
6977
7715
|
};
|
|
6978
7716
|
}
|
|
@@ -7021,7 +7759,7 @@ async function installPack(source, options = {}) {
|
|
|
7021
7759
|
if (!pack.validation.valid) {
|
|
7022
7760
|
throw new Error(formatValidationIssues(pack.validation.issues));
|
|
7023
7761
|
}
|
|
7024
|
-
const destination =
|
|
7762
|
+
const destination = join38(installRoot, pack.manifest.name);
|
|
7025
7763
|
rmSync2(destination, { recursive: true, force: true });
|
|
7026
7764
|
cpSync(candidateRoot, destination, { recursive: true });
|
|
7027
7765
|
const installed = loader.validatePack(destination, scope === "project" ? "project" : "global");
|
|
@@ -7033,13 +7771,13 @@ async function installPack(source, options = {}) {
|
|
|
7033
7771
|
function removePack(name, projectRoot = process.cwd(), scope = "global", overrides = {}) {
|
|
7034
7772
|
const roots = resolvePackManagerRoots(projectRoot, overrides);
|
|
7035
7773
|
const targetRoot = scope === "project" ? roots.projectPacksRoot : roots.globalPacksRoot;
|
|
7036
|
-
const target =
|
|
7037
|
-
if (
|
|
7774
|
+
const target = join38(targetRoot, name);
|
|
7775
|
+
if (existsSync16(target)) {
|
|
7038
7776
|
rmSync2(target, { recursive: true, force: true });
|
|
7039
7777
|
return;
|
|
7040
7778
|
}
|
|
7041
|
-
const builtInRoot =
|
|
7042
|
-
if (
|
|
7779
|
+
const builtInRoot = join38(roots.runtimeRoot, "capabilities", "coding", "stacks", name);
|
|
7780
|
+
if (existsSync16(builtInRoot)) {
|
|
7043
7781
|
throw new Error(
|
|
7044
7782
|
`Cannot remove built-in pack "${name}"; remove a global or project override instead`
|
|
7045
7783
|
);
|
|
@@ -7057,14 +7795,14 @@ function createPack(name, options = {}) {
|
|
|
7057
7795
|
const destinationRoot = options.destinationRoot ?? process.cwd();
|
|
7058
7796
|
const ecosystem = options.ecosystem ?? "node";
|
|
7059
7797
|
const tier = options.tier ?? "framework";
|
|
7060
|
-
const packRoot =
|
|
7061
|
-
if (
|
|
7798
|
+
const packRoot = join38(destinationRoot, name);
|
|
7799
|
+
if (existsSync16(packRoot)) {
|
|
7062
7800
|
throw new Error(`Pack scaffold already exists at ${packRoot}`);
|
|
7063
7801
|
}
|
|
7064
|
-
mkdirSync5(
|
|
7065
|
-
writeFileSync5(
|
|
7802
|
+
mkdirSync5(join38(packRoot, "rules"), { recursive: true });
|
|
7803
|
+
writeFileSync5(join38(packRoot, "pack.yaml"), renderPackTemplate(name, ecosystem, tier));
|
|
7066
7804
|
writeFileSync5(
|
|
7067
|
-
|
|
7805
|
+
join38(packRoot, "rules", "conventions.md"),
|
|
7068
7806
|
`# ${name}
|
|
7069
7807
|
|
|
7070
7808
|
Document project-specific conventions for the ${name} stack here.
|
|
@@ -7074,14 +7812,14 @@ Document project-specific conventions for the ${name} stack here.
|
|
|
7074
7812
|
}
|
|
7075
7813
|
function listPackNamesForSource(source, roots) {
|
|
7076
7814
|
const sourceRoot = resolveSourceRoot(source, roots);
|
|
7077
|
-
if (!
|
|
7815
|
+
if (!existsSync16(sourceRoot)) {
|
|
7078
7816
|
return [];
|
|
7079
7817
|
}
|
|
7080
7818
|
return readdirSync3(sourceRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
7081
7819
|
}
|
|
7082
7820
|
function resolveSourceRoot(source, roots) {
|
|
7083
7821
|
if (source === "built-in") {
|
|
7084
|
-
return
|
|
7822
|
+
return join38(roots.runtimeRoot, "capabilities", "coding", "stacks");
|
|
7085
7823
|
}
|
|
7086
7824
|
return source === "global" ? roots.globalPacksRoot : roots.projectPacksRoot;
|
|
7087
7825
|
}
|
|
@@ -7101,13 +7839,13 @@ async function materializePackSource(source, roots) {
|
|
|
7101
7839
|
return clonePackSource(buildRegistryPackUrl(roots.registryUrl, source));
|
|
7102
7840
|
}
|
|
7103
7841
|
function looksLikeLocalPath(source) {
|
|
7104
|
-
return source.startsWith(".") || source.startsWith("/") ||
|
|
7842
|
+
return source.startsWith(".") || source.startsWith("/") || existsSync16(resolve2(source));
|
|
7105
7843
|
}
|
|
7106
7844
|
function looksLikeGitUrl(source) {
|
|
7107
7845
|
return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("ssh://") || source.startsWith("git@") || source.startsWith("file://") || source.endsWith(".git");
|
|
7108
7846
|
}
|
|
7109
7847
|
async function clonePackSource(source) {
|
|
7110
|
-
const tempRoot = mkdtempSync(
|
|
7848
|
+
const tempRoot = mkdtempSync(join38(homedir2(), ".paqad-pack-clone-"));
|
|
7111
7849
|
try {
|
|
7112
7850
|
await execa("git", ["clone", "--depth", "1", source, tempRoot]);
|
|
7113
7851
|
} catch (error) {
|
|
@@ -7117,11 +7855,11 @@ async function clonePackSource(source) {
|
|
|
7117
7855
|
return findPackRoot(tempRoot);
|
|
7118
7856
|
}
|
|
7119
7857
|
function findPackRoot(root) {
|
|
7120
|
-
const rootManifest =
|
|
7121
|
-
if (
|
|
7858
|
+
const rootManifest = join38(root, "pack.yaml");
|
|
7859
|
+
if (existsSync16(rootManifest)) {
|
|
7122
7860
|
return root;
|
|
7123
7861
|
}
|
|
7124
|
-
const candidates = readdirSync3(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
7862
|
+
const candidates = readdirSync3(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join38(root, entry.name)).filter((candidate) => existsSync16(join38(candidate, "pack.yaml")));
|
|
7125
7863
|
if (candidates.length === 1) {
|
|
7126
7864
|
return candidates[0];
|
|
7127
7865
|
}
|
|
@@ -7239,15 +7977,15 @@ async function queryOsv(packages) {
|
|
|
7239
7977
|
}
|
|
7240
7978
|
|
|
7241
7979
|
// src/pentest/progress-tracker.ts
|
|
7242
|
-
import { existsSync as
|
|
7243
|
-
import { mkdir as mkdir12, readdir as
|
|
7244
|
-
import { dirname as dirname17, join as
|
|
7980
|
+
import { existsSync as existsSync18 } from "fs";
|
|
7981
|
+
import { mkdir as mkdir12, readdir as readdir5, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
|
|
7982
|
+
import { dirname as dirname17, join as join40 } from "path";
|
|
7245
7983
|
|
|
7246
7984
|
// src/pentest/shared.ts
|
|
7247
7985
|
import { createHash as createHash7 } from "crypto";
|
|
7248
|
-
import { existsSync as
|
|
7249
|
-
import { mkdir as mkdir11, readFile as readFile15, readdir as
|
|
7250
|
-
import { basename as
|
|
7986
|
+
import { existsSync as existsSync17 } from "fs";
|
|
7987
|
+
import { mkdir as mkdir11, readFile as readFile15, readdir as readdir4, writeFile as writeFile11 } from "fs/promises";
|
|
7988
|
+
import { basename as basename6, dirname as dirname16, join as join39, relative as relative6 } from "path";
|
|
7251
7989
|
import { execa as execa2 } from "execa";
|
|
7252
7990
|
import fg5 from "fast-glob";
|
|
7253
7991
|
function toLocalTimestamp(date) {
|
|
@@ -7265,7 +8003,7 @@ async function writeJson(target, data) {
|
|
|
7265
8003
|
`);
|
|
7266
8004
|
}
|
|
7267
8005
|
async function readJsonIfExists(target) {
|
|
7268
|
-
if (!
|
|
8006
|
+
if (!existsSync17(target)) {
|
|
7269
8007
|
return null;
|
|
7270
8008
|
}
|
|
7271
8009
|
return JSON.parse(await readFile15(target, "utf8"));
|
|
@@ -7311,8 +8049,8 @@ async function discoverTargetUrl(projectRoot, stack, explicit) {
|
|
|
7311
8049
|
}
|
|
7312
8050
|
const envFiles = [".env", ".env.local", ".env.example"];
|
|
7313
8051
|
for (const envFile of envFiles) {
|
|
7314
|
-
const path =
|
|
7315
|
-
if (!
|
|
8052
|
+
const path = join39(projectRoot, envFile);
|
|
8053
|
+
if (!existsSync17(path)) {
|
|
7316
8054
|
continue;
|
|
7317
8055
|
}
|
|
7318
8056
|
const content = await readFile15(path, "utf8");
|
|
@@ -7333,11 +8071,11 @@ async function discoverTargetUrl(projectRoot, stack, explicit) {
|
|
|
7333
8071
|
return null;
|
|
7334
8072
|
}
|
|
7335
8073
|
async function runProjectScript(projectRoot, scriptName, env, logDir) {
|
|
7336
|
-
const scriptPath =
|
|
7337
|
-
const stdoutPath =
|
|
7338
|
-
const stderrPath =
|
|
8074
|
+
const scriptPath = join39(projectRoot, PATHS.SCRIPTS_DIR, scriptName);
|
|
8075
|
+
const stdoutPath = join39(logDir, `${scriptName}.stdout.log`);
|
|
8076
|
+
const stderrPath = join39(logDir, `${scriptName}.stderr.log`);
|
|
7339
8077
|
await mkdir11(logDir, { recursive: true });
|
|
7340
|
-
if (!
|
|
8078
|
+
if (!existsSync17(scriptPath)) {
|
|
7341
8079
|
await writeFile11(stdoutPath, "");
|
|
7342
8080
|
await writeFile11(stderrPath, `Missing script: ${relative6(projectRoot, scriptPath)}
|
|
7343
8081
|
`);
|
|
@@ -7365,11 +8103,11 @@ async function runProjectScript(projectRoot, scriptName, env, logDir) {
|
|
|
7365
8103
|
};
|
|
7366
8104
|
}
|
|
7367
8105
|
async function loadModuleDocs(projectRoot, focusModules = []) {
|
|
7368
|
-
const moduleRoot =
|
|
7369
|
-
if (!
|
|
8106
|
+
const moduleRoot = join39(projectRoot, PATHS.MODULES_DIR);
|
|
8107
|
+
if (!existsSync17(moduleRoot)) {
|
|
7370
8108
|
return [];
|
|
7371
8109
|
}
|
|
7372
|
-
const dirs = (await
|
|
8110
|
+
const dirs = (await readdir4(moduleRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((moduleName) => focusModules.length === 0 || focusModules.includes(moduleName)).sort();
|
|
7373
8111
|
return Promise.all(
|
|
7374
8112
|
dirs.map(async (moduleName) => {
|
|
7375
8113
|
const paths = (await fg5(
|
|
@@ -7434,7 +8172,7 @@ function inferSourceArtifacts(baseDir, scriptResults) {
|
|
|
7434
8172
|
return [
|
|
7435
8173
|
...new Set(
|
|
7436
8174
|
artifacts.map(
|
|
7437
|
-
(artifact) => artifact.startsWith(".") ? artifact : relative6(dirname16(baseDir),
|
|
8175
|
+
(artifact) => artifact.startsWith(".") ? artifact : relative6(dirname16(baseDir), join39(dirname16(baseDir), artifact))
|
|
7438
8176
|
)
|
|
7439
8177
|
)
|
|
7440
8178
|
];
|
|
@@ -7508,8 +8246,8 @@ var PentestProgressTracker = class {
|
|
|
7508
8246
|
};
|
|
7509
8247
|
}
|
|
7510
8248
|
async load(projectRoot, runId) {
|
|
7511
|
-
const target =
|
|
7512
|
-
if (!
|
|
8249
|
+
const target = join40(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "progress.json");
|
|
8250
|
+
if (!existsSync18(target)) {
|
|
7513
8251
|
return null;
|
|
7514
8252
|
}
|
|
7515
8253
|
const parsed = JSON.parse(await readFile16(target, "utf8"));
|
|
@@ -7520,7 +8258,7 @@ var PentestProgressTracker = class {
|
|
|
7520
8258
|
return parsed;
|
|
7521
8259
|
}
|
|
7522
8260
|
async save(projectRoot, progress) {
|
|
7523
|
-
const target =
|
|
8261
|
+
const target = join40(projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "progress.json");
|
|
7524
8262
|
progress.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7525
8263
|
await mkdir12(dirname17(target), { recursive: true });
|
|
7526
8264
|
await writeFile12(target, `${JSON.stringify(progress, null, 2)}
|
|
@@ -7579,11 +8317,11 @@ var PentestProgressTracker = class {
|
|
|
7579
8317
|
return step.status === "completed" && step.input_hash === inputHash;
|
|
7580
8318
|
}
|
|
7581
8319
|
async findIncompleteRun(projectRoot, workflow, sourceReportPath) {
|
|
7582
|
-
const runsDir =
|
|
7583
|
-
if (!
|
|
8320
|
+
const runsDir = join40(projectRoot, PATHS.PENTEST_RUNS_DIR);
|
|
8321
|
+
if (!existsSync18(runsDir)) {
|
|
7584
8322
|
return null;
|
|
7585
8323
|
}
|
|
7586
|
-
const entries = (await
|
|
8324
|
+
const entries = (await readdir5(runsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().reverse();
|
|
7587
8325
|
for (const runId of entries) {
|
|
7588
8326
|
const progress = await this.load(projectRoot, runId);
|
|
7589
8327
|
if (progress === null) {
|
|
@@ -7622,21 +8360,21 @@ var PentestProgressTracker = class {
|
|
|
7622
8360
|
}
|
|
7623
8361
|
};
|
|
7624
8362
|
function runArtifactsDir(projectRoot, runId) {
|
|
7625
|
-
return
|
|
8363
|
+
return join40(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "artifacts");
|
|
7626
8364
|
}
|
|
7627
8365
|
function runLogsDir(projectRoot, runId) {
|
|
7628
|
-
return
|
|
8366
|
+
return join40(projectRoot, PATHS.PENTEST_RUNS_DIR, runId, "logs");
|
|
7629
8367
|
}
|
|
7630
8368
|
|
|
7631
8369
|
// src/pipeline/lane-runner.ts
|
|
7632
|
-
import { existsSync as
|
|
8370
|
+
import { existsSync as existsSync21 } from "fs";
|
|
7633
8371
|
import { mkdir as mkdir17, readFile as readFile22, writeFile as writeFile17 } from "fs/promises";
|
|
7634
|
-
import { dirname as dirname21, join as
|
|
7635
|
-
import { basename as
|
|
8372
|
+
import { dirname as dirname21, join as join49 } from "path";
|
|
8373
|
+
import { basename as basename7, dirname as pathDirname } from "pathe";
|
|
7636
8374
|
import fg7 from "fast-glob";
|
|
7637
8375
|
|
|
7638
8376
|
// src/skills/frontmatter-parser.ts
|
|
7639
|
-
import
|
|
8377
|
+
import YAML6 from "yaml";
|
|
7640
8378
|
|
|
7641
8379
|
// src/skills/conditional-processor.ts
|
|
7642
8380
|
var ConditionalSectionProcessor = class {
|
|
@@ -7657,13 +8395,13 @@ var conditionalProcessor = new ConditionalSectionProcessor();
|
|
|
7657
8395
|
|
|
7658
8396
|
// src/workflows/engine.ts
|
|
7659
8397
|
import { readFile as readFile18, writeFile as writeFile13, mkdir as mkdir13 } from "fs/promises";
|
|
7660
|
-
import { join as
|
|
8398
|
+
import { join as join42, dirname as dirname18 } from "path";
|
|
7661
8399
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
7662
8400
|
|
|
7663
8401
|
// src/workflows/template-loader.ts
|
|
7664
|
-
import { readFile as readFile17, readdir as
|
|
7665
|
-
import { join as
|
|
7666
|
-
import
|
|
8402
|
+
import { readFile as readFile17, readdir as readdir6 } from "fs/promises";
|
|
8403
|
+
import { join as join41 } from "path";
|
|
8404
|
+
import YAML7 from "yaml";
|
|
7667
8405
|
|
|
7668
8406
|
// src/pipeline/phases/shared.ts
|
|
7669
8407
|
function createPassResult(phase, summary, context, artifacts = [`handoff:${context.phases.length + 1}`]) {
|
|
@@ -7725,7 +8463,15 @@ var DocumentationUpdatePhase = class {
|
|
|
7725
8463
|
);
|
|
7726
8464
|
}
|
|
7727
8465
|
}
|
|
7728
|
-
|
|
8466
|
+
const stageSummary = summarizeFeatureDevelopmentStage(
|
|
8467
|
+
context.feature_policy,
|
|
8468
|
+
"documentation_sync"
|
|
8469
|
+
);
|
|
8470
|
+
return createPassResult(
|
|
8471
|
+
this.phase,
|
|
8472
|
+
stageSummary === null ? "Canonical docs updated" : `Canonical docs updated (${stageSummary})`,
|
|
8473
|
+
context
|
|
8474
|
+
);
|
|
7729
8475
|
}
|
|
7730
8476
|
};
|
|
7731
8477
|
|
|
@@ -7761,7 +8507,12 @@ var ImplementationReviewPhase = class {
|
|
|
7761
8507
|
async execute(context) {
|
|
7762
8508
|
const tier = selectReviewTier(context.classification, context.lane);
|
|
7763
8509
|
const mode = selectReviewMode(false, 0);
|
|
7764
|
-
|
|
8510
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "review");
|
|
8511
|
+
return createPassResult(
|
|
8512
|
+
this.phase,
|
|
8513
|
+
stageSummary === null ? `Implementation review passed (${tier}, ${mode})` : `Implementation review passed (${tier}, ${mode}; ${stageSummary})`,
|
|
8514
|
+
context
|
|
8515
|
+
);
|
|
7765
8516
|
}
|
|
7766
8517
|
};
|
|
7767
8518
|
|
|
@@ -7769,7 +8520,12 @@ var ImplementationReviewPhase = class {
|
|
|
7769
8520
|
var ImplementationPhase = class {
|
|
7770
8521
|
phase = "implementation";
|
|
7771
8522
|
async execute(context) {
|
|
7772
|
-
|
|
8523
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "development");
|
|
8524
|
+
return createPassResult(
|
|
8525
|
+
this.phase,
|
|
8526
|
+
stageSummary === null ? "Implementation completed" : `Implementation completed (${stageSummary})`,
|
|
8527
|
+
context
|
|
8528
|
+
);
|
|
7773
8529
|
}
|
|
7774
8530
|
};
|
|
7775
8531
|
|
|
@@ -7777,18 +8533,23 @@ var ImplementationPhase = class {
|
|
|
7777
8533
|
var LoadDocsPhase = class {
|
|
7778
8534
|
phase = "docs-first-load";
|
|
7779
8535
|
async execute(context) {
|
|
7780
|
-
|
|
8536
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "planning");
|
|
8537
|
+
return createPassResult(
|
|
8538
|
+
this.phase,
|
|
8539
|
+
stageSummary === null ? "Docs-first context prepared" : `Docs-first context prepared (${stageSummary})`,
|
|
8540
|
+
context
|
|
8541
|
+
);
|
|
7781
8542
|
}
|
|
7782
8543
|
};
|
|
7783
8544
|
|
|
7784
8545
|
// src/workflows/pentest.ts
|
|
7785
8546
|
import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
|
|
7786
|
-
import { join as
|
|
8547
|
+
import { join as join45, relative as relative7 } from "path";
|
|
7787
8548
|
|
|
7788
8549
|
// src/pentest/findings.ts
|
|
7789
|
-
import { existsSync as
|
|
8550
|
+
import { existsSync as existsSync19 } from "fs";
|
|
7790
8551
|
import { readFile as readFile19 } from "fs/promises";
|
|
7791
|
-
import { join as
|
|
8552
|
+
import { join as join43 } from "path";
|
|
7792
8553
|
function createFinding(input) {
|
|
7793
8554
|
return {
|
|
7794
8555
|
id: "",
|
|
@@ -8215,8 +8976,8 @@ function buildModuleFindings(docs, tests) {
|
|
|
8215
8976
|
return findings;
|
|
8216
8977
|
}
|
|
8217
8978
|
async function buildSecretFindings(artifactDir) {
|
|
8218
|
-
const path =
|
|
8219
|
-
if (!
|
|
8979
|
+
const path = join43(artifactDir, "secrets", "matches.txt");
|
|
8980
|
+
if (!existsSync19(path)) {
|
|
8220
8981
|
return [];
|
|
8221
8982
|
}
|
|
8222
8983
|
const matches = parseSecretMatches(await readFile19(path, "utf8"));
|
|
@@ -8331,8 +9092,8 @@ function normalizeImpact(content) {
|
|
|
8331
9092
|
}
|
|
8332
9093
|
async function readNativeAuditFindings(artifactDir) {
|
|
8333
9094
|
const findings = [];
|
|
8334
|
-
const dependencyDir =
|
|
8335
|
-
const npmAudit = await readJsonMaybe(
|
|
9095
|
+
const dependencyDir = join43(artifactDir, "dependencies");
|
|
9096
|
+
const npmAudit = await readJsonMaybe(join43(dependencyDir, "npm-audit.json"));
|
|
8336
9097
|
for (const [name, vulnerability] of Object.entries(npmAudit?.vulnerabilities ?? {})) {
|
|
8337
9098
|
const via = (vulnerability.via ?? []).find((entry) => typeof entry === "object");
|
|
8338
9099
|
findings.push({
|
|
@@ -8344,7 +9105,7 @@ async function readNativeAuditFindings(artifactDir) {
|
|
|
8344
9105
|
details: via?.url ?? ""
|
|
8345
9106
|
});
|
|
8346
9107
|
}
|
|
8347
|
-
const pnpmAudit = await readJsonMaybe(
|
|
9108
|
+
const pnpmAudit = await readJsonMaybe(join43(dependencyDir, "pnpm-audit.json"));
|
|
8348
9109
|
for (const advisory of Object.values(pnpmAudit?.advisories ?? {})) {
|
|
8349
9110
|
findings.push({
|
|
8350
9111
|
package_name: advisory.module_name ?? "unknown",
|
|
@@ -8355,7 +9116,7 @@ async function readNativeAuditFindings(artifactDir) {
|
|
|
8355
9116
|
details: advisory.overview ?? ""
|
|
8356
9117
|
});
|
|
8357
9118
|
}
|
|
8358
|
-
const composerAudit = await readJsonMaybe(
|
|
9119
|
+
const composerAudit = await readJsonMaybe(join43(dependencyDir, "composer-audit.json"));
|
|
8359
9120
|
if (Array.isArray(composerAudit?.advisories)) {
|
|
8360
9121
|
for (const advisory of composerAudit.advisories) {
|
|
8361
9122
|
findings.push({
|
|
@@ -8392,7 +9153,7 @@ async function readNativeAuditFindings(artifactDir) {
|
|
|
8392
9153
|
});
|
|
8393
9154
|
}
|
|
8394
9155
|
async function readJsonMaybe(path) {
|
|
8395
|
-
if (!
|
|
9156
|
+
if (!existsSync19(path)) {
|
|
8396
9157
|
return null;
|
|
8397
9158
|
}
|
|
8398
9159
|
try {
|
|
@@ -8491,7 +9252,7 @@ var FileCheckMapper = class {
|
|
|
8491
9252
|
|
|
8492
9253
|
// src/pentest/incremental-scanner.ts
|
|
8493
9254
|
import { readFile as readFile20 } from "fs/promises";
|
|
8494
|
-
import { join as
|
|
9255
|
+
import { join as join44 } from "path";
|
|
8495
9256
|
import { createHash as createHash8 } from "crypto";
|
|
8496
9257
|
import { execa as execa3 } from "execa";
|
|
8497
9258
|
var IncrementalScanner = class {
|
|
@@ -8525,7 +9286,7 @@ var IncrementalScanner = class {
|
|
|
8525
9286
|
}
|
|
8526
9287
|
}
|
|
8527
9288
|
async gitDiff(projectRoot, lastRunId) {
|
|
8528
|
-
const progressPath =
|
|
9289
|
+
const progressPath = join44(projectRoot, ".paqad", "pentest", "runs", lastRunId, "progress.json");
|
|
8529
9290
|
let baseCommit;
|
|
8530
9291
|
try {
|
|
8531
9292
|
const raw = await readFile20(progressPath, "utf8");
|
|
@@ -8542,7 +9303,7 @@ var IncrementalScanner = class {
|
|
|
8542
9303
|
return result.stdout.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
8543
9304
|
}
|
|
8544
9305
|
async hashDiff(projectRoot, lastRunId) {
|
|
8545
|
-
const manifestPath =
|
|
9306
|
+
const manifestPath = join44(projectRoot, ".paqad", "pentest", "runs", lastRunId, "progress.json");
|
|
8546
9307
|
let fileManifest = {};
|
|
8547
9308
|
try {
|
|
8548
9309
|
const raw = await readFile20(manifestPath, "utf8");
|
|
@@ -8554,7 +9315,7 @@ var IncrementalScanner = class {
|
|
|
8554
9315
|
const changed = [];
|
|
8555
9316
|
for (const [filePath, storedHash] of Object.entries(fileManifest)) {
|
|
8556
9317
|
try {
|
|
8557
|
-
const content = await readFile20(
|
|
9318
|
+
const content = await readFile20(join44(projectRoot, filePath), "utf8");
|
|
8558
9319
|
const currentHash = createHash8("sha256").update(content).digest("hex");
|
|
8559
9320
|
if (currentHash !== storedHash) {
|
|
8560
9321
|
changed.push(filePath);
|
|
@@ -8567,11 +9328,11 @@ var IncrementalScanner = class {
|
|
|
8567
9328
|
}
|
|
8568
9329
|
async warnIfFullScanStale(projectRoot, thresholdDays) {
|
|
8569
9330
|
try {
|
|
8570
|
-
const runsDir =
|
|
8571
|
-
const { readdir:
|
|
8572
|
-
const runs = await
|
|
9331
|
+
const runsDir = join44(projectRoot, ".paqad", "pentest", "runs");
|
|
9332
|
+
const { readdir: readdir9 } = await import("fs/promises");
|
|
9333
|
+
const runs = await readdir9(runsDir).catch(() => []);
|
|
8573
9334
|
for (const run of runs.sort().reverse()) {
|
|
8574
|
-
const progressPath =
|
|
9335
|
+
const progressPath = join44(runsDir, run, "progress.json");
|
|
8575
9336
|
try {
|
|
8576
9337
|
const raw = await readFile20(progressPath, "utf8");
|
|
8577
9338
|
const progress = JSON.parse(raw);
|
|
@@ -8769,8 +9530,8 @@ var PentestWorkflow = class {
|
|
|
8769
9530
|
await this.tracker.save(options.projectRoot, progress);
|
|
8770
9531
|
const docs = await loadModuleDocs(options.projectRoot, focusModules);
|
|
8771
9532
|
const tests = await loadTests(options.projectRoot, focusModules);
|
|
8772
|
-
const docsPath =
|
|
8773
|
-
const testsPath =
|
|
9533
|
+
const docsPath = join45(artifactsDir, "docs-summary.json");
|
|
9534
|
+
const testsPath = join45(artifactsDir, "tests-summary.json");
|
|
8774
9535
|
await writeJson(docsPath, docs);
|
|
8775
9536
|
await writeJson(
|
|
8776
9537
|
testsPath,
|
|
@@ -8871,7 +9632,7 @@ var PentestWorkflow = class {
|
|
|
8871
9632
|
progressRunId: progress.run_id,
|
|
8872
9633
|
reportTimestamp: new Date(progress.started_at)
|
|
8873
9634
|
});
|
|
8874
|
-
const findingIndexPath =
|
|
9635
|
+
const findingIndexPath = join45(
|
|
8875
9636
|
options.projectRoot,
|
|
8876
9637
|
PATHS.PENTEST_RUNS_DIR,
|
|
8877
9638
|
progress.run_id,
|
|
@@ -8888,7 +9649,7 @@ var PentestWorkflow = class {
|
|
|
8888
9649
|
}
|
|
8889
9650
|
if (report === null) {
|
|
8890
9651
|
const existingSidecar = progress.sidecar_path ? await readJsonIfExists(
|
|
8891
|
-
|
|
9652
|
+
join45(options.projectRoot, progress.sidecar_path)
|
|
8892
9653
|
) : null;
|
|
8893
9654
|
report = existingSidecar;
|
|
8894
9655
|
}
|
|
@@ -8903,18 +9664,18 @@ var PentestWorkflow = class {
|
|
|
8903
9664
|
this.tracker.markStepRunning(progress, "write-report", writeHash);
|
|
8904
9665
|
await this.tracker.save(options.projectRoot, progress);
|
|
8905
9666
|
await writeJson(
|
|
8906
|
-
|
|
9667
|
+
join45(options.projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "report-preview.json"),
|
|
8907
9668
|
{ report_id: report.report_id, findings: report.findings.length }
|
|
8908
9669
|
);
|
|
8909
|
-
await writeJson(
|
|
9670
|
+
await writeJson(join45(options.projectRoot, report.sidecar_path), report);
|
|
8910
9671
|
await writeJson(
|
|
8911
|
-
|
|
9672
|
+
join45(options.projectRoot, PATHS.PENTEST_RUNS_DIR, progress.run_id, "blocked-checks.json"),
|
|
8912
9673
|
report.blocked_checks
|
|
8913
9674
|
);
|
|
8914
|
-
await mkdir14(
|
|
9675
|
+
await mkdir14(join45(options.projectRoot, PATHS.PENTEST_DIR), { recursive: true });
|
|
8915
9676
|
const markdown = buildPentestMarkdown(report);
|
|
8916
|
-
await writeJson(
|
|
8917
|
-
await writeFile14(
|
|
9677
|
+
await writeJson(join45(options.projectRoot, report.sidecar_path), report);
|
|
9678
|
+
await writeFile14(join45(options.projectRoot, report.report_path), markdown);
|
|
8918
9679
|
this.tracker.markStepCompleted(
|
|
8919
9680
|
progress,
|
|
8920
9681
|
"write-report",
|
|
@@ -8941,7 +9702,7 @@ async function buildCurrentPentestReport(input) {
|
|
|
8941
9702
|
const docs = await loadModuleDocs(input.projectRoot, input.focusModules);
|
|
8942
9703
|
const tests = await loadTests(input.projectRoot, input.focusModules);
|
|
8943
9704
|
const osvFindings = await queryOsv(input.snapshot.packages);
|
|
8944
|
-
const osvPath =
|
|
9705
|
+
const osvPath = join45(input.artifactsDir, "dependencies", "osv-results.json");
|
|
8945
9706
|
await writeJson(osvPath, osvFindings);
|
|
8946
9707
|
const dependencyFindings = await buildDependencyFindings(
|
|
8947
9708
|
input.snapshot,
|
|
@@ -8950,7 +9711,7 @@ async function buildCurrentPentestReport(input) {
|
|
|
8950
9711
|
);
|
|
8951
9712
|
const moduleFindings = buildModuleFindings(docs, tests);
|
|
8952
9713
|
const secretFindings = await buildSecretFindings(input.artifactsDir);
|
|
8953
|
-
const runtimePayload = await readJsonIfExists(
|
|
9714
|
+
const runtimePayload = await readJsonIfExists(join45(input.artifactsDir, "runtime", "runtime-checks.json"));
|
|
8954
9715
|
const runtimeStatus = input.targetUrl ? runtimePayload?.reachable ? {
|
|
8955
9716
|
target_url: input.targetUrl,
|
|
8956
9717
|
status: "reachable",
|
|
@@ -8976,8 +9737,8 @@ async function buildCurrentPentestReport(input) {
|
|
|
8976
9737
|
...runtimeFindings
|
|
8977
9738
|
]);
|
|
8978
9739
|
const timestamp = toLocalTimestamp(input.reportTimestamp);
|
|
8979
|
-
const reportPath =
|
|
8980
|
-
const sidecarPath =
|
|
9740
|
+
const reportPath = join45(PATHS.PENTEST_DIR, `${timestamp}.md`);
|
|
9741
|
+
const sidecarPath = join45(PATHS.PENTEST_DIR, `${timestamp}.json`);
|
|
8981
9742
|
const stack = getPrimaryStack({
|
|
8982
9743
|
routing: { domain: "coding" },
|
|
8983
9744
|
stack_profile: input.snapshot.profile
|
|
@@ -9030,9 +9791,9 @@ async function buildCurrentPentestReport(input) {
|
|
|
9030
9791
|
raw_evidence_paths: [
|
|
9031
9792
|
relative7(input.projectRoot, osvPath),
|
|
9032
9793
|
...[
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9794
|
+
join45(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "finding-index.json"),
|
|
9795
|
+
join45(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "artifacts", "docs-summary.json"),
|
|
9796
|
+
join45(PATHS.PENTEST_RUNS_DIR, input.progressRunId, "artifacts", "tests-summary.json")
|
|
9036
9797
|
]
|
|
9037
9798
|
]
|
|
9038
9799
|
};
|
|
@@ -9072,7 +9833,7 @@ var PentestPhase = class {
|
|
|
9072
9833
|
|
|
9073
9834
|
// src/workflows/pentest-retest.ts
|
|
9074
9835
|
import { mkdir as mkdir15, writeFile as writeFile15 } from "fs/promises";
|
|
9075
|
-
import { dirname as dirname19, join as
|
|
9836
|
+
import { dirname as dirname19, join as join46 } from "path";
|
|
9076
9837
|
var RETEST_STEPS = [
|
|
9077
9838
|
{ id: "load-source-report", title: "Load source pentest report" },
|
|
9078
9839
|
{ id: "rerun-evidence", title: "Collect fresh evidence for source findings" },
|
|
@@ -9093,7 +9854,7 @@ var PentestRetestWorkflow = class {
|
|
|
9093
9854
|
}
|
|
9094
9855
|
const normalizedSourcePath = normalizeSidecarPath(sourceReportPath);
|
|
9095
9856
|
const sourceSidecar = await readJsonIfExists(
|
|
9096
|
-
|
|
9857
|
+
join46(options.projectRoot, normalizedSourcePath)
|
|
9097
9858
|
);
|
|
9098
9859
|
if (sourceSidecar === null) {
|
|
9099
9860
|
throw new Error(`Source pentest sidecar not found at ${normalizedSourcePath}`);
|
|
@@ -9118,12 +9879,12 @@ var PentestRetestWorkflow = class {
|
|
|
9118
9879
|
if (!this.tracker.shouldSkipStep(progress, "load-source-report", sourceHash)) {
|
|
9119
9880
|
this.tracker.markStepRunning(progress, "load-source-report", sourceHash);
|
|
9120
9881
|
await this.tracker.save(options.projectRoot, progress);
|
|
9121
|
-
const sourceCopy =
|
|
9882
|
+
const sourceCopy = join46(artifactsDir, "source-report.json");
|
|
9122
9883
|
await writeJson(sourceCopy, sourceSidecar);
|
|
9123
9884
|
this.tracker.markStepCompleted(
|
|
9124
9885
|
progress,
|
|
9125
9886
|
"load-source-report",
|
|
9126
|
-
[
|
|
9887
|
+
[join46(PATHS.PENTEST_RUNS_DIR, progress.run_id, "artifacts", "source-report.json")],
|
|
9127
9888
|
[...RETEST_SKILLS.source].map(skillPath)
|
|
9128
9889
|
);
|
|
9129
9890
|
await this.tracker.save(options.projectRoot, progress);
|
|
@@ -9140,7 +9901,7 @@ var PentestRetestWorkflow = class {
|
|
|
9140
9901
|
PENTEST_RUN_ID: progress.run_id,
|
|
9141
9902
|
PENTEST_ARTIFACT_DIR: artifactsDir,
|
|
9142
9903
|
PENTEST_TARGET_URL: targetUrl ?? "",
|
|
9143
|
-
PENTEST_SOURCE_REPORT:
|
|
9904
|
+
PENTEST_SOURCE_REPORT: join46(options.projectRoot, normalizedSourcePath),
|
|
9144
9905
|
PENTEST_DB_CONNECTION_NAME: options.dbConnectionName ?? ""
|
|
9145
9906
|
};
|
|
9146
9907
|
const scriptResults = await Promise.all([
|
|
@@ -9189,8 +9950,8 @@ var PentestRetestWorkflow = class {
|
|
|
9189
9950
|
...currentReport,
|
|
9190
9951
|
report_id: toReportId("RETEST", new Date(progress.started_at)),
|
|
9191
9952
|
workflow: "pentest-retest",
|
|
9192
|
-
report_path:
|
|
9193
|
-
sidecar_path:
|
|
9953
|
+
report_path: join46(PATHS.PENTEST_RETEST_DIR, `${timestamp}-${sourceSlug}.md`),
|
|
9954
|
+
sidecar_path: join46(PATHS.PENTEST_RETEST_DIR, `${timestamp}-${sourceSlug}.json`),
|
|
9194
9955
|
source_report_path: normalizedSourcePath,
|
|
9195
9956
|
source_report_id: sourceSidecar.report_id,
|
|
9196
9957
|
findings: retestFindings,
|
|
@@ -9203,7 +9964,7 @@ var PentestRetestWorkflow = class {
|
|
|
9203
9964
|
...[...RETEST_SKILLS.source, ...RETEST_SKILLS.evaluate].map(skillPath)
|
|
9204
9965
|
]
|
|
9205
9966
|
};
|
|
9206
|
-
const findingIndexPath =
|
|
9967
|
+
const findingIndexPath = join46(
|
|
9207
9968
|
options.projectRoot,
|
|
9208
9969
|
PATHS.PENTEST_RUNS_DIR,
|
|
9209
9970
|
progress.run_id,
|
|
@@ -9213,14 +9974,14 @@ var PentestRetestWorkflow = class {
|
|
|
9213
9974
|
this.tracker.markStepCompleted(
|
|
9214
9975
|
progress,
|
|
9215
9976
|
"evaluate-source-findings",
|
|
9216
|
-
[
|
|
9977
|
+
[join46(PATHS.PENTEST_RUNS_DIR, progress.run_id, "finding-index.json")],
|
|
9217
9978
|
[...RETEST_SKILLS.evaluate].map(skillPath)
|
|
9218
9979
|
);
|
|
9219
9980
|
await this.tracker.save(options.projectRoot, progress);
|
|
9220
9981
|
}
|
|
9221
9982
|
if (retestReport === null) {
|
|
9222
9983
|
const existing = progress.sidecar_path ? await readJsonIfExists(
|
|
9223
|
-
|
|
9984
|
+
join46(options.projectRoot, progress.sidecar_path)
|
|
9224
9985
|
) : null;
|
|
9225
9986
|
retestReport = existing;
|
|
9226
9987
|
}
|
|
@@ -9234,12 +9995,12 @@ var PentestRetestWorkflow = class {
|
|
|
9234
9995
|
if (!this.tracker.shouldSkipStep(progress, "write-report", writeHash)) {
|
|
9235
9996
|
this.tracker.markStepRunning(progress, "write-report", writeHash);
|
|
9236
9997
|
await this.tracker.save(options.projectRoot, progress);
|
|
9237
|
-
await writeJson(
|
|
9238
|
-
await mkdir15(dirname19(
|
|
9998
|
+
await writeJson(join46(options.projectRoot, retestReport.sidecar_path), retestReport);
|
|
9999
|
+
await mkdir15(dirname19(join46(options.projectRoot, retestReport.report_path)), {
|
|
9239
10000
|
recursive: true
|
|
9240
10001
|
});
|
|
9241
10002
|
await writeFile15(
|
|
9242
|
-
|
|
10003
|
+
join46(options.projectRoot, retestReport.report_path),
|
|
9243
10004
|
buildPentestMarkdown(retestReport)
|
|
9244
10005
|
);
|
|
9245
10006
|
this.tracker.markStepCompleted(
|
|
@@ -9317,7 +10078,7 @@ var ProjectQuestionPhase = class {
|
|
|
9317
10078
|
|
|
9318
10079
|
// src/workflows/root-cause-analysis.ts
|
|
9319
10080
|
import { mkdir as mkdir16, writeFile as writeFile16 } from "fs/promises";
|
|
9320
|
-
import { dirname as dirname20, join as
|
|
10081
|
+
import { dirname as dirname20, join as join47 } from "path";
|
|
9321
10082
|
var DEFAULT_TITLE_SLUG = "root-cause-analysis";
|
|
9322
10083
|
var RCA_SECTIONS = [
|
|
9323
10084
|
"Summary",
|
|
@@ -9336,8 +10097,8 @@ var RootCauseAnalysisWorkflow = class {
|
|
|
9336
10097
|
async run(options) {
|
|
9337
10098
|
const title = deriveTitle(options.classification.request_text);
|
|
9338
10099
|
const filename = `${toLocalTimestamp2(/* @__PURE__ */ new Date())}-${slugify2(title) || DEFAULT_TITLE_SLUG}.md`;
|
|
9339
|
-
const relativePath =
|
|
9340
|
-
const outputPath =
|
|
10100
|
+
const relativePath = join47(PATHS.RCA_DIR, filename);
|
|
10101
|
+
const outputPath = join47(options.projectRoot, relativePath);
|
|
9341
10102
|
await mkdir16(dirname20(outputPath), { recursive: true });
|
|
9342
10103
|
await writeFile16(outputPath, buildRcaDocument(title, options.classification));
|
|
9343
10104
|
return {
|
|
@@ -9445,7 +10206,12 @@ var SpecReviewPhase = class {
|
|
|
9445
10206
|
var SpecWritingPhase = class {
|
|
9446
10207
|
phase = "specification";
|
|
9447
10208
|
async execute(context) {
|
|
9448
|
-
|
|
10209
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "specification");
|
|
10210
|
+
return createPassResult(
|
|
10211
|
+
this.phase,
|
|
10212
|
+
stageSummary === null ? "Specification written" : `Specification written (${stageSummary})`,
|
|
10213
|
+
context
|
|
10214
|
+
);
|
|
9449
10215
|
}
|
|
9450
10216
|
};
|
|
9451
10217
|
|
|
@@ -9453,7 +10219,12 @@ var SpecWritingPhase = class {
|
|
|
9453
10219
|
var StoryPlanningPhase = class {
|
|
9454
10220
|
phase = "sequence-planning";
|
|
9455
10221
|
async execute(context) {
|
|
9456
|
-
|
|
10222
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "planning");
|
|
10223
|
+
return createPassResult(
|
|
10224
|
+
this.phase,
|
|
10225
|
+
stageSummary === null ? "Story sequence planned" : `Story sequence planned (${stageSummary})`,
|
|
10226
|
+
context
|
|
10227
|
+
);
|
|
9457
10228
|
}
|
|
9458
10229
|
};
|
|
9459
10230
|
|
|
@@ -9461,14 +10232,30 @@ var StoryPlanningPhase = class {
|
|
|
9461
10232
|
var VerificationPhase = class {
|
|
9462
10233
|
phase = "verification-gates";
|
|
9463
10234
|
async execute(context) {
|
|
9464
|
-
|
|
10235
|
+
const profile = readProjectProfile(context.project_root);
|
|
10236
|
+
const stage = context.feature_policy?.stages.checks ?? null;
|
|
10237
|
+
const resolved = resolveFeatureDevelopmentCheckCommands(stage?.checks ?? null, profile);
|
|
10238
|
+
const stageSummary = summarizeFeatureDevelopmentStage(context.feature_policy, "checks");
|
|
10239
|
+
if (resolved.warnings.length > 0 && stage?.checks?.block_on_failure) {
|
|
10240
|
+
return createFailResult(
|
|
10241
|
+
this.phase,
|
|
10242
|
+
`Verification blocked (${resolved.warnings.join(" ")})`,
|
|
10243
|
+
context
|
|
10244
|
+
);
|
|
10245
|
+
}
|
|
10246
|
+
const commandSummary = resolved.commands.length > 0 ? `commands: ${resolved.commands.map((command) => command.command).join("; ")}` : "no configured commands";
|
|
10247
|
+
return createPassResult(
|
|
10248
|
+
this.phase,
|
|
10249
|
+
stageSummary === null ? `Verification gates passed (${commandSummary})` : `Verification gates passed (${stageSummary}; ${commandSummary})`,
|
|
10250
|
+
context
|
|
10251
|
+
);
|
|
9465
10252
|
}
|
|
9466
10253
|
};
|
|
9467
10254
|
|
|
9468
10255
|
// src/pipeline/workflow-router.ts
|
|
9469
10256
|
import { readFile as readFile21 } from "fs/promises";
|
|
9470
|
-
import { existsSync as
|
|
9471
|
-
import { join as
|
|
10257
|
+
import { existsSync as existsSync20 } from "fs";
|
|
10258
|
+
import { join as join48, relative as relative8 } from "path";
|
|
9472
10259
|
import fg6 from "fast-glob";
|
|
9473
10260
|
|
|
9474
10261
|
// src/pipeline/lane-runner.ts
|
|
@@ -9492,38 +10279,38 @@ var DEFAULT_PHASES = {
|
|
|
9492
10279
|
|
|
9493
10280
|
// src/pipeline/stream-truncator.ts
|
|
9494
10281
|
import { appendFile, mkdir as mkdir18 } from "fs/promises";
|
|
9495
|
-
import { join as
|
|
10282
|
+
import { join as join50, dirname as dirname22 } from "path";
|
|
9496
10283
|
|
|
9497
10284
|
// src/resolver/deduplicator.ts
|
|
9498
10285
|
import { createHash as createHash9 } from "crypto";
|
|
9499
10286
|
import { readFile as readFile23, writeFile as writeFile18, mkdir as mkdir19 } from "fs/promises";
|
|
9500
|
-
import { join as
|
|
10287
|
+
import { join as join51, dirname as dirname23 } from "path";
|
|
9501
10288
|
|
|
9502
10289
|
// src/scripts/generator.ts
|
|
9503
10290
|
import { chmodSync as chmodSync2 } from "fs";
|
|
9504
10291
|
import { mkdir as mkdir20, writeFile as writeFile19 } from "fs/promises";
|
|
9505
|
-
import { dirname as dirname24, join as
|
|
10292
|
+
import { dirname as dirname24, join as join52 } from "path";
|
|
9506
10293
|
|
|
9507
10294
|
// src/skills/cache-manager.ts
|
|
9508
10295
|
import { createHash as createHash10 } from "crypto";
|
|
9509
|
-
import { mkdir as mkdir21, readFile as readFile24, readdir as
|
|
9510
|
-
import { join as
|
|
10296
|
+
import { mkdir as mkdir21, readFile as readFile24, readdir as readdir7, rm as rm2, stat as stat3, writeFile as writeFile20 } from "fs/promises";
|
|
10297
|
+
import { join as join53 } from "path";
|
|
9511
10298
|
import fg8 from "fast-glob";
|
|
9512
10299
|
|
|
9513
10300
|
// src/skills/index-generator.ts
|
|
9514
10301
|
import { mkdir as mkdir22, readFile as readFile25, writeFile as writeFile21 } from "fs/promises";
|
|
9515
|
-
import { dirname as dirname25, join as
|
|
10302
|
+
import { dirname as dirname25, join as join54, relative as relative9 } from "path";
|
|
9516
10303
|
import fg9 from "fast-glob";
|
|
9517
10304
|
|
|
9518
10305
|
// src/skills/loader.ts
|
|
9519
10306
|
import { readFile as readFile26 } from "fs/promises";
|
|
9520
|
-
import { basename as
|
|
10307
|
+
import { basename as basename8 } from "pathe";
|
|
9521
10308
|
|
|
9522
10309
|
// src/update/audit.ts
|
|
9523
10310
|
import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
|
|
9524
|
-
import { dirname as dirname26, join as
|
|
10311
|
+
import { dirname as dirname26, join as join55 } from "path";
|
|
9525
10312
|
function auditPath(projectRoot) {
|
|
9526
|
-
return
|
|
10313
|
+
return join55(projectRoot, PATHS.AUDIT_LOG);
|
|
9527
10314
|
}
|
|
9528
10315
|
function ts() {
|
|
9529
10316
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -9545,16 +10332,16 @@ function appendAuditLogFailure(projectRoot, previous, target, error) {
|
|
|
9545
10332
|
}
|
|
9546
10333
|
|
|
9547
10334
|
// src/update/updater.ts
|
|
9548
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
9549
|
-
import { mkdtemp, readdir as
|
|
10335
|
+
import { chmodSync as chmodSync3, existsSync as existsSync22, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
|
|
10336
|
+
import { mkdtemp, readdir as readdir8, readFile as readFile27, rm as rm3 } from "fs/promises";
|
|
9550
10337
|
import { tmpdir } from "os";
|
|
9551
|
-
import { dirname as dirname27, join as
|
|
10338
|
+
import { dirname as dirname27, join as join56, relative as relative10 } from "path";
|
|
9552
10339
|
var FrameworkUpdater = class {
|
|
9553
10340
|
constructor(options = {}) {
|
|
9554
10341
|
this.options = options;
|
|
9555
10342
|
}
|
|
9556
10343
|
async run(projectRoot) {
|
|
9557
|
-
const previousVersion = readText(
|
|
10344
|
+
const previousVersion = readText(join56(projectRoot, PATHS.FRAMEWORK_VERSION));
|
|
9558
10345
|
const manifest = readManifest(projectRoot);
|
|
9559
10346
|
const artifactPolicy = new Map(
|
|
9560
10347
|
manifest?.generated_artifacts.map((artifact) => [artifact.path, artifact.auto_update]) ?? []
|
|
@@ -9564,13 +10351,13 @@ var FrameworkUpdater = class {
|
|
|
9564
10351
|
const skipped = [];
|
|
9565
10352
|
const newScripts = [];
|
|
9566
10353
|
for (const candidate of candidates) {
|
|
9567
|
-
const target =
|
|
9568
|
-
const existed =
|
|
10354
|
+
const target = join56(projectRoot, candidate.path);
|
|
10355
|
+
const existed = existsSync22(target);
|
|
9569
10356
|
const autoUpdate = artifactPolicy.get(candidate.path) ?? candidate.autoUpdate;
|
|
9570
10357
|
if (existed && autoUpdate === false) {
|
|
9571
10358
|
skipped.push({
|
|
9572
10359
|
path: candidate.path,
|
|
9573
|
-
before:
|
|
10360
|
+
before: readFileSync14(target, "utf8"),
|
|
9574
10361
|
after: candidate.content
|
|
9575
10362
|
});
|
|
9576
10363
|
continue;
|
|
@@ -9585,9 +10372,9 @@ var FrameworkUpdater = class {
|
|
|
9585
10372
|
newScripts.push(candidate.path);
|
|
9586
10373
|
}
|
|
9587
10374
|
}
|
|
9588
|
-
mkdirSync7(dirname27(
|
|
10375
|
+
mkdirSync7(dirname27(join56(projectRoot, PATHS.FRAMEWORK_VERSION)), { recursive: true });
|
|
9589
10376
|
writeFileSync6(
|
|
9590
|
-
|
|
10377
|
+
join56(projectRoot, PATHS.FRAMEWORK_VERSION),
|
|
9591
10378
|
`version=${VERSION}
|
|
9592
10379
|
updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
9593
10380
|
`
|
|
@@ -9610,7 +10397,7 @@ updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
|
9610
10397
|
if (profile === null) {
|
|
9611
10398
|
throw new Error("Cannot update framework-managed artifacts without a project profile");
|
|
9612
10399
|
}
|
|
9613
|
-
const tempRoot = await mkdtemp(
|
|
10400
|
+
const tempRoot = await mkdtemp(join56(tmpdir(), "paqad-ai-update-"));
|
|
9614
10401
|
try {
|
|
9615
10402
|
const result = await new OnboardingOrchestrator().run({
|
|
9616
10403
|
projectRoot: tempRoot,
|
|
@@ -9630,41 +10417,41 @@ updated_at=${(/* @__PURE__ */ new Date()).toISOString()}
|
|
|
9630
10417
|
}
|
|
9631
10418
|
};
|
|
9632
10419
|
function readManifest(projectRoot) {
|
|
9633
|
-
const path =
|
|
9634
|
-
if (!
|
|
10420
|
+
const path = join56(projectRoot, PATHS.ONBOARDING_MANIFEST);
|
|
10421
|
+
if (!existsSync22(path)) {
|
|
9635
10422
|
return null;
|
|
9636
10423
|
}
|
|
9637
|
-
return JSON.parse(
|
|
10424
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
9638
10425
|
}
|
|
9639
10426
|
function readProfile(projectRoot) {
|
|
9640
10427
|
return readProjectProfile(projectRoot);
|
|
9641
10428
|
}
|
|
9642
10429
|
function readText(path) {
|
|
9643
|
-
if (!
|
|
10430
|
+
if (!existsSync22(path)) {
|
|
9644
10431
|
return null;
|
|
9645
10432
|
}
|
|
9646
|
-
const raw =
|
|
10433
|
+
const raw = readFileSync14(path, "utf8");
|
|
9647
10434
|
const match = raw.match(/^version=(.+)$/m);
|
|
9648
10435
|
return match ? match[1].trim() : raw.trim();
|
|
9649
10436
|
}
|
|
9650
10437
|
async function collectFiles(root, generated) {
|
|
9651
|
-
const paths = generated.length > 0 ? generated : await
|
|
10438
|
+
const paths = generated.length > 0 ? generated : await walk4(root);
|
|
9652
10439
|
return Promise.all(
|
|
9653
10440
|
paths.map(async (file) => ({
|
|
9654
10441
|
path: file,
|
|
9655
|
-
content: await readFile27(
|
|
10442
|
+
content: await readFile27(join56(root, file), "utf8"),
|
|
9656
10443
|
autoUpdate: true,
|
|
9657
10444
|
executable: file.startsWith("scripts/")
|
|
9658
10445
|
}))
|
|
9659
10446
|
);
|
|
9660
10447
|
}
|
|
9661
|
-
async function
|
|
9662
|
-
const entries = await
|
|
10448
|
+
async function walk4(root, current = root) {
|
|
10449
|
+
const entries = await readdir8(current, { withFileTypes: true });
|
|
9663
10450
|
const files = [];
|
|
9664
10451
|
for (const entry of entries) {
|
|
9665
|
-
const absolute =
|
|
10452
|
+
const absolute = join56(current, entry.name);
|
|
9666
10453
|
if (entry.isDirectory()) {
|
|
9667
|
-
files.push(...await
|
|
10454
|
+
files.push(...await walk4(root, absolute));
|
|
9668
10455
|
continue;
|
|
9669
10456
|
}
|
|
9670
10457
|
files.push(relative10(root, absolute));
|
|
@@ -9673,31 +10460,31 @@ async function walk3(root, current = root) {
|
|
|
9673
10460
|
}
|
|
9674
10461
|
|
|
9675
10462
|
// src/verification/gates/documentation-freshness.ts
|
|
9676
|
-
import { existsSync as
|
|
9677
|
-
import { join as
|
|
10463
|
+
import { existsSync as existsSync23 } from "fs";
|
|
10464
|
+
import { join as join57 } from "path";
|
|
9678
10465
|
var STALENESS_WINDOW_MS2 = 1e3 * 60 * 60 * 24 * 7;
|
|
9679
10466
|
|
|
9680
10467
|
// src/patterns/pattern-store.ts
|
|
9681
10468
|
import { readFile as readFile28, writeFile as writeFile22, mkdir as mkdir23, unlink } from "fs/promises";
|
|
9682
|
-
import { join as
|
|
10469
|
+
import { join as join58, dirname as dirname28 } from "path";
|
|
9683
10470
|
import { homedir as homedir3 } from "os";
|
|
9684
|
-
var GLOBAL_PATTERNS_DIR =
|
|
10471
|
+
var GLOBAL_PATTERNS_DIR = join58(homedir3(), ".paqad", "patterns");
|
|
9685
10472
|
var PatternStore = class {
|
|
9686
10473
|
get indexPath() {
|
|
9687
|
-
return
|
|
10474
|
+
return join58(GLOBAL_PATTERNS_DIR, "index.json");
|
|
9688
10475
|
}
|
|
9689
10476
|
get entriesDir() {
|
|
9690
|
-
return
|
|
10477
|
+
return join58(GLOBAL_PATTERNS_DIR, "entries");
|
|
9691
10478
|
}
|
|
9692
10479
|
async save(pattern) {
|
|
9693
10480
|
await mkdir23(this.entriesDir, { recursive: true });
|
|
9694
|
-
const entryPath =
|
|
10481
|
+
const entryPath = join58(this.entriesDir, `${pattern.id}.json`);
|
|
9695
10482
|
await writeFile22(entryPath, JSON.stringify(pattern, null, 2), "utf8");
|
|
9696
10483
|
await this.updateIndex(pattern);
|
|
9697
10484
|
}
|
|
9698
10485
|
async load(id) {
|
|
9699
10486
|
try {
|
|
9700
|
-
const raw = await readFile28(
|
|
10487
|
+
const raw = await readFile28(join58(this.entriesDir, `${id}.json`), "utf8");
|
|
9701
10488
|
return JSON.parse(raw);
|
|
9702
10489
|
} catch {
|
|
9703
10490
|
return null;
|
|
@@ -9732,7 +10519,7 @@ var PatternStore = class {
|
|
|
9732
10519
|
}
|
|
9733
10520
|
async delete(id) {
|
|
9734
10521
|
try {
|
|
9735
|
-
await unlink(
|
|
10522
|
+
await unlink(join58(this.entriesDir, `${id}.json`));
|
|
9736
10523
|
} catch {
|
|
9737
10524
|
}
|
|
9738
10525
|
const index = await this.loadIndex();
|
|
@@ -9831,7 +10618,7 @@ ${p.solution}
|
|
|
9831
10618
|
};
|
|
9832
10619
|
|
|
9833
10620
|
// src/index.ts
|
|
9834
|
-
var VERSION = "0.1.
|
|
10621
|
+
var VERSION = "0.1.5";
|
|
9835
10622
|
|
|
9836
10623
|
// src/cli/commands/capabilities.ts
|
|
9837
10624
|
import { Command } from "commander";
|
|
@@ -10010,12 +10797,13 @@ function createPacksCommand() {
|
|
|
10010
10797
|
|
|
10011
10798
|
// src/cli/commands/refresh.ts
|
|
10012
10799
|
import { Command as Command7 } from "commander";
|
|
10013
|
-
import { readFileSync as
|
|
10014
|
-
import { join as
|
|
10800
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
10801
|
+
import { join as join59 } from "path";
|
|
10015
10802
|
function createRefreshCommand() {
|
|
10016
10803
|
return new Command7("refresh").description("Refresh derived framework artifacts").option("--project-root <path>", "Project root", process.cwd()).option("--design-system", "Refresh design-system markdown from design tokens").option("--stack", "Refresh the cached stack snapshot").action(async (options) => {
|
|
10017
|
-
const
|
|
10018
|
-
const
|
|
10804
|
+
const hasExplicitTarget = options.designSystem === true || options.stack === true;
|
|
10805
|
+
const shouldRefreshDesignSystem = hasExplicitTarget ? options.designSystem === true : true;
|
|
10806
|
+
const shouldRefreshStack = hasExplicitTarget ? options.stack === true : true;
|
|
10019
10807
|
if (shouldRefreshDesignSystem) {
|
|
10020
10808
|
await new DesignTokenService().writeDocs(options.projectRoot);
|
|
10021
10809
|
}
|
|
@@ -10047,14 +10835,14 @@ function createRefreshCommand() {
|
|
|
10047
10835
|
});
|
|
10048
10836
|
}
|
|
10049
10837
|
function writeRefreshDrift(projectRoot, refreshDrift) {
|
|
10050
|
-
const path =
|
|
10838
|
+
const path = join59(projectRoot, PATHS.STACK_DRIFT);
|
|
10051
10839
|
const current = readExistingJson(path);
|
|
10052
10840
|
writeFileSync7(path, `${JSON.stringify({ ...current ?? {}, ...refreshDrift }, null, 2)}
|
|
10053
10841
|
`);
|
|
10054
10842
|
}
|
|
10055
10843
|
function readExistingJson(path) {
|
|
10056
10844
|
try {
|
|
10057
|
-
return JSON.parse(
|
|
10845
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
10058
10846
|
} catch {
|
|
10059
10847
|
return null;
|
|
10060
10848
|
}
|