agent-project-sdlc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/assets/README.md +5 -0
  2. package/assets/agents/.gitkeep +1 -0
  3. package/assets/agents/AGENTS_CORE.md +164 -0
  4. package/assets/github/.gitkeep +1 -0
  5. package/assets/github/harness.yml +45 -0
  6. package/assets/make/.gitkeep +1 -0
  7. package/assets/make/sdlc-harness.mk +78 -0
  8. package/assets/policies/allowed_paths.yaml +55 -0
  9. package/assets/policies/gates.yaml +48 -0
  10. package/assets/policies/phase_contracts.yaml +140 -0
  11. package/assets/policies/risk_matrix.yaml +27 -0
  12. package/assets/skills/architect_design/SKILL.md +62 -0
  13. package/assets/skills/dev_sprint/SKILL.md +90 -0
  14. package/assets/skills/implementation_doc/SKILL.md +58 -0
  15. package/assets/skills/manager/SKILL.md +50 -0
  16. package/assets/skills/pm_prd/SKILL.md +59 -0
  17. package/assets/skills/release_manager/SKILL.md +59 -0
  18. package/assets/skills/reviewer/SKILL.md +60 -0
  19. package/assets/skills/rfc_recalibrate/SKILL.md +61 -0
  20. package/assets/skills/tester/SKILL.md +59 -0
  21. package/assets/templates/ADR_TEMPLATE.md +19 -0
  22. package/assets/templates/IMPLEMENTATION_DOC_TEMPLATE.md +53 -0
  23. package/assets/templates/PLAN_TEMPLATE.yaml +25 -0
  24. package/assets/templates/PRD_TEMPLATE.md +41 -0
  25. package/assets/templates/RELEASE_TEMPLATE.md +34 -0
  26. package/assets/templates/REVIEW_TEMPLATE.md +31 -0
  27. package/assets/templates/RFC_TEMPLATE.md +36 -0
  28. package/assets/templates/SKILL_TEMPLATE.md +52 -0
  29. package/assets/templates/TECH_DESIGN_TEMPLATE.md +54 -0
  30. package/assets/templates/TEST_PLAN_TEMPLATE.md +29 -0
  31. package/dist/cli.d.ts +2 -0
  32. package/dist/cli.js +12 -0
  33. package/dist/commands/doctor.d.ts +1 -0
  34. package/dist/commands/doctor.js +16 -0
  35. package/dist/commands/index.d.ts +3 -0
  36. package/dist/commands/index.js +30 -0
  37. package/dist/commands/init.d.ts +2 -0
  38. package/dist/commands/init.js +56 -0
  39. package/dist/commands/package-source.d.ts +1 -0
  40. package/dist/commands/package-source.js +24 -0
  41. package/dist/commands/sync.d.ts +1 -0
  42. package/dist/commands/sync.js +11 -0
  43. package/dist/commands/upgrade.d.ts +1 -0
  44. package/dist/commands/upgrade.js +7 -0
  45. package/dist/commands/validate.d.ts +1 -0
  46. package/dist/commands/validate.js +14 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +1 -0
  49. package/dist/lib/config.d.ts +5 -0
  50. package/dist/lib/config.js +55 -0
  51. package/dist/lib/doctor.d.ts +6 -0
  52. package/dist/lib/doctor.js +31 -0
  53. package/dist/lib/fs.d.ts +8 -0
  54. package/dist/lib/fs.js +56 -0
  55. package/dist/lib/harness-root.d.ts +9 -0
  56. package/dist/lib/harness-root.js +50 -0
  57. package/dist/lib/init.d.ts +5 -0
  58. package/dist/lib/init.js +76 -0
  59. package/dist/lib/managed-file.d.ts +6 -0
  60. package/dist/lib/managed-file.js +6 -0
  61. package/dist/lib/migrations.d.ts +12 -0
  62. package/dist/lib/migrations.js +176 -0
  63. package/dist/lib/package-json-config.d.ts +2 -0
  64. package/dist/lib/package-json-config.js +37 -0
  65. package/dist/lib/package-source.d.ts +8 -0
  66. package/dist/lib/package-source.js +107 -0
  67. package/dist/lib/paths.d.ts +5 -0
  68. package/dist/lib/paths.js +11 -0
  69. package/dist/lib/sync-engine.d.ts +7 -0
  70. package/dist/lib/sync-engine.js +202 -0
  71. package/dist/lib/types.d.ts +22 -0
  72. package/dist/lib/types.js +1 -0
  73. package/dist/lib/upgrade.d.ts +1 -0
  74. package/dist/lib/upgrade.js +16 -0
  75. package/dist/lib/validators.d.ts +6 -0
  76. package/dist/lib/validators.js +158 -0
  77. package/dist/lib/yaml.d.ts +2 -0
  78. package/dist/lib/yaml.js +7 -0
  79. package/migrations/README.md +3 -0
  80. package/package.json +31 -0
  81. package/source-mappings.yaml +19 -0
@@ -0,0 +1,202 @@
1
+ import path from "node:path";
2
+ import { readConfig } from "./config.js";
3
+ import { harnessRoot } from "./harness-root.js";
4
+ import { copyTree, listFiles, pathExists, readText, writeTextIfChanged } from "./fs.js";
5
+ import { MAKEFILE_BLOCK_END, MAKEFILE_BLOCK_START, MANAGED_BLOCK_END, MANAGED_BLOCK_START } from "./managed-file.js";
6
+ import { packageAssetPath } from "./paths.js";
7
+ export function emptySyncReport() {
8
+ return {
9
+ changed: [],
10
+ skipped: [],
11
+ blocked: []
12
+ };
13
+ }
14
+ export async function runSync(projectRoot) {
15
+ const root = await harnessRoot(projectRoot);
16
+ const config = await readConfig(projectRoot);
17
+ const report = emptySyncReport();
18
+ for (const managedFile of config.managed_files) {
19
+ await syncManagedFile(projectRoot, root, managedFile, report);
20
+ }
21
+ return report;
22
+ }
23
+ async function syncManagedFile(projectRoot, root, managedFile, report) {
24
+ const destination = path.join(projectRoot, managedFile.path);
25
+ if (managedFile.path === "AGENTS.md") {
26
+ await syncAgentsBlock(destination, root, report);
27
+ return;
28
+ }
29
+ if (managedFile.path === "Makefile") {
30
+ await syncMakefileInclude(destination, root, report);
31
+ return;
32
+ }
33
+ if (managedFile.path === path.join(root, "skills") || managedFile.path === ".harness/agents/skills") {
34
+ await syncTree(packageAssetPath("skills"), destination, report);
35
+ return;
36
+ }
37
+ if (managedFile.path === ".agents/skills" && root !== ".agents") {
38
+ await syncTree(packageAssetPath("skills"), destination, report);
39
+ return;
40
+ }
41
+ if (managedFile.path === path.join(root, "managed", "templates") || managedFile.path === ".harness/templates") {
42
+ await syncTree(packageAssetPath("templates"), destination, report);
43
+ return;
44
+ }
45
+ if (managedFile.path === path.join(root, "managed", "policies") || managedFile.path === ".harness/policies") {
46
+ await syncTree(packageAssetPath("policies"), destination, report);
47
+ return;
48
+ }
49
+ if (managedFile.path === path.join(root, "managed", "make", "sdlc-harness.mk") ||
50
+ managedFile.path === ".harness/make/sdlc-harness.mk") {
51
+ await syncFile(packageAssetPath("make", "sdlc-harness.mk"), destination, report, "skip-if-missing");
52
+ return;
53
+ }
54
+ if (managedFile.path === ".github/workflows/harness.yml") {
55
+ if (await pathExists(destination)) {
56
+ report.skipped.push(managedFile.path);
57
+ return;
58
+ }
59
+ await syncFile(packageAssetPath("github", "harness.yml"), destination, report, "skip-if-missing");
60
+ return;
61
+ }
62
+ report.skipped.push(managedFile.path);
63
+ }
64
+ async function syncAgentsBlock(destination, root, report) {
65
+ const corePath = packageAssetPath("agents", "AGENTS_CORE.md");
66
+ if (!(await pathExists(corePath))) {
67
+ report.skipped.push("AGENTS.md");
68
+ return;
69
+ }
70
+ const core = renderAgentsCore(await readText(corePath), root);
71
+ const block = `${MANAGED_BLOCK_START}\n${core.trim()}\n${MANAGED_BLOCK_END}`;
72
+ const existing = (await pathExists(destination)) ? await readText(destination) : "";
73
+ const next = mergeManagedBlock({
74
+ existing,
75
+ block,
76
+ start: MANAGED_BLOCK_START,
77
+ end: MANAGED_BLOCK_END,
78
+ pathLabel: "AGENTS.md",
79
+ insert: "append",
80
+ report
81
+ });
82
+ if (!next) {
83
+ return;
84
+ }
85
+ if (await writeTextIfChanged(destination, next)) {
86
+ report.changed.push("AGENTS.md");
87
+ }
88
+ else {
89
+ report.skipped.push("AGENTS.md");
90
+ }
91
+ }
92
+ function renderAgentsCore(content, root) {
93
+ return content.replaceAll(".agent", root);
94
+ }
95
+ async function syncMakefileInclude(destination, root, report) {
96
+ const existing = (await pathExists(destination)) ? await readText(destination) : "";
97
+ const resetDefaultGoal = shouldResetMakeDefaultGoal(existing);
98
+ const includePath = `${root.replace(/\\/g, "/")}/managed/make/sdlc-harness.mk`;
99
+ const blockLines = [
100
+ MAKEFILE_BLOCK_START,
101
+ "# Included before project targets so project recipes win on name conflicts.",
102
+ `-include ${includePath}`,
103
+ MAKEFILE_BLOCK_END
104
+ ];
105
+ if (resetDefaultGoal) {
106
+ blockLines.splice(3, 0, ".DEFAULT_GOAL :=");
107
+ }
108
+ const block = blockLines.join("\n");
109
+ const next = mergeManagedBlock({
110
+ existing,
111
+ block,
112
+ start: MAKEFILE_BLOCK_START,
113
+ end: MAKEFILE_BLOCK_END,
114
+ pathLabel: "Makefile",
115
+ insert: "prepend",
116
+ report
117
+ });
118
+ if (!next) {
119
+ return;
120
+ }
121
+ if (await writeTextIfChanged(destination, next)) {
122
+ report.changed.push("Makefile");
123
+ }
124
+ else {
125
+ report.skipped.push("Makefile");
126
+ }
127
+ }
128
+ function shouldResetMakeDefaultGoal(existing) {
129
+ if (!existing.trim()) {
130
+ return false;
131
+ }
132
+ const startIndex = existing.indexOf(MAKEFILE_BLOCK_START);
133
+ const endIndex = existing.indexOf(MAKEFILE_BLOCK_END);
134
+ if (startIndex < 0 && endIndex < 0) {
135
+ return true;
136
+ }
137
+ if (startIndex < 0 || endIndex < startIndex) {
138
+ return false;
139
+ }
140
+ const before = existing.slice(0, startIndex);
141
+ const after = existing.slice(endIndex + MAKEFILE_BLOCK_END.length);
142
+ return !before.trim() && Boolean(after.trim());
143
+ }
144
+ function mergeManagedBlock(options) {
145
+ const { existing, block, start, end, pathLabel, insert, report } = options;
146
+ const startIndex = existing.indexOf(start);
147
+ const endIndex = existing.indexOf(end);
148
+ const hasStart = startIndex >= 0;
149
+ const hasEnd = endIndex >= 0;
150
+ if (hasStart !== hasEnd || (hasStart && endIndex < startIndex)) {
151
+ report.blocked.push(`${pathLabel}: incomplete managed block markers`);
152
+ return undefined;
153
+ }
154
+ if (hasStart &&
155
+ (existing.indexOf(start, startIndex + start.length) >= 0 || existing.indexOf(end, endIndex + end.length) >= 0)) {
156
+ report.blocked.push(`${pathLabel}: duplicate managed block markers`);
157
+ return undefined;
158
+ }
159
+ if (hasStart) {
160
+ const before = existing.slice(0, startIndex);
161
+ const after = existing.slice(endIndex + end.length);
162
+ return `${before}${block}${after}`;
163
+ }
164
+ if (!existing.trim()) {
165
+ return `${block}\n`;
166
+ }
167
+ if (insert === "prepend") {
168
+ return `${block}\n\n${existing}`;
169
+ }
170
+ return `${existing.trimEnd()}\n\n${block}\n`;
171
+ }
172
+ async function syncTree(source, destination, report) {
173
+ if (!(await pathExists(source))) {
174
+ report.skipped.push(path.basename(destination));
175
+ return;
176
+ }
177
+ const files = await listFiles(source);
178
+ const realFiles = files.filter((file) => !file.endsWith(".gitkeep"));
179
+ if (realFiles.length === 0) {
180
+ report.skipped.push(path.basename(destination));
181
+ return;
182
+ }
183
+ const changed = await copyTree(source, destination, { skipGitkeep: true });
184
+ report.changed.push(...changed);
185
+ }
186
+ async function syncFile(source, destination, report, missingMode) {
187
+ if (!(await pathExists(source))) {
188
+ if (missingMode === "block-if-missing") {
189
+ report.blocked.push(source);
190
+ }
191
+ else {
192
+ report.skipped.push(destination);
193
+ }
194
+ return;
195
+ }
196
+ if (await writeTextIfChanged(destination, await readText(source))) {
197
+ report.changed.push(destination);
198
+ }
199
+ else {
200
+ report.skipped.push(destination);
201
+ }
202
+ }
@@ -0,0 +1,22 @@
1
+ export interface HarnessConfig {
2
+ core: {
3
+ package: string;
4
+ version: string;
5
+ schema_version: string;
6
+ };
7
+ managed_files: ManagedFile[];
8
+ local_overrides: string[];
9
+ never_overwrite: string[];
10
+ }
11
+ export interface ManagedFile {
12
+ path: string;
13
+ strategy: "merge-block" | "generated" | "generated-compat" | "managed" | "merge-with-local" | "create-if-missing";
14
+ }
15
+ export interface SourceMapping {
16
+ source: string;
17
+ target: string;
18
+ mode: "extract-managed-block" | "copy-tree" | "copy-file" | "extract-harness-targets";
19
+ }
20
+ export interface SourceMappingsFile {
21
+ source_mappings: SourceMapping[];
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function runUpgrade(projectRoot: string): Promise<string[]>;
@@ -0,0 +1,16 @@
1
+ import { runDoctor } from "./doctor.js";
2
+ import { runMigrations } from "./migrations.js";
3
+ import { runSync } from "./sync-engine.js";
4
+ export async function runUpgrade(projectRoot) {
5
+ const lines = [];
6
+ const migrationReport = await runMigrations(projectRoot);
7
+ lines.push(`migrations changed=${migrationReport.changed.length} skipped=${migrationReport.skipped.length}`);
8
+ const syncReport = await runSync(projectRoot);
9
+ lines.push(`sync changed=${syncReport.changed.length} skipped=${syncReport.skipped.length} blocked=${syncReport.blocked.length}`);
10
+ const doctor = await runDoctor(projectRoot);
11
+ lines.push(`doctor warnings=${doctor.warnings.length} errors=${doctor.errors.length}`);
12
+ if (syncReport.blocked.length > 0 || doctor.errors.length > 0) {
13
+ throw new Error("upgrade completed with blockers");
14
+ }
15
+ return lines;
16
+ }
@@ -0,0 +1,6 @@
1
+ export interface ValidatorReport {
2
+ info: string[];
3
+ errors: string[];
4
+ }
5
+ export declare function runValidator(projectRoot: string, gate: string): Promise<ValidatorReport>;
6
+ export declare function changedFiles(projectRoot: string): Promise<string[]>;
@@ -0,0 +1,158 @@
1
+ import { execFile } from "node:child_process";
2
+ import path from "node:path";
3
+ import { promisify } from "node:util";
4
+ import { harnessPath, harnessRoot } from "./harness-root.js";
5
+ import { listFiles, pathExists, readText } from "./fs.js";
6
+ import { parseYaml } from "./yaml.js";
7
+ const execFileAsync = promisify(execFile);
8
+ const validators = {
9
+ "validate-harness": validateHarness,
10
+ "validate-current": validateCurrent,
11
+ "validate-pm": validatePm,
12
+ "validate-design": validateDesign,
13
+ "validate-dev": validateDev
14
+ };
15
+ export async function runValidator(projectRoot, gate) {
16
+ const normalized = normalizeGate(gate);
17
+ const validator = validators[normalized];
18
+ if (!validator) {
19
+ return { info: [], errors: [`unknown validator: ${gate}`] };
20
+ }
21
+ return validator(projectRoot);
22
+ }
23
+ function normalizeGate(gate) {
24
+ return gate.replace(/^make\s+/, "").trim();
25
+ }
26
+ async function validateHarness(projectRoot) {
27
+ const errors = [];
28
+ const root = await harnessRoot(projectRoot);
29
+ for (const required of [
30
+ "AGENTS.md",
31
+ ".docs/INDEX.md",
32
+ harnessPath(root, "config.yaml"),
33
+ harnessPath(root, "state", "lifecycle.yaml"),
34
+ harnessPath(root, "state", "plan.yaml"),
35
+ harnessPath(root, "skills"),
36
+ harnessPath(root, "managed", "templates"),
37
+ harnessPath(root, "managed", "policies")
38
+ ]) {
39
+ if (!(await pathExists(path.join(projectRoot, required)))) {
40
+ errors.push(`missing ${required}`);
41
+ }
42
+ }
43
+ return { info: [`validate-harness checked ${projectRoot} (${root})`], errors };
44
+ }
45
+ async function validateCurrent(projectRoot) {
46
+ const root = await harnessRoot(projectRoot);
47
+ const lifecycle = await readYamlObject(path.join(projectRoot, root, "state", "lifecycle.yaml"));
48
+ const current = String(lifecycle.current_phase ?? "");
49
+ const gateByPhase = {
50
+ REQUIREMENT_GATHERING: "validate-pm",
51
+ ARCHITECTING: "validate-design",
52
+ SPRINTING: "validate-dev"
53
+ };
54
+ return runValidator(projectRoot, gateByPhase[current] ?? "validate-harness");
55
+ }
56
+ async function validatePm(projectRoot) {
57
+ const docs = await markdownFiles(path.join(projectRoot, ".docs/01_product"));
58
+ const text = await combinedText(docs);
59
+ const errors = [];
60
+ if (docs.length === 0)
61
+ errors.push("No PRD deliverables found");
62
+ if (!containsAny(text, ["acceptance", "验收"]))
63
+ errors.push("PRD must include acceptance criteria");
64
+ if (!containsAny(text, ["out of scope", "不做", "边界"]))
65
+ errors.push("PRD must include out-of-scope boundaries");
66
+ if (!containsAny(text, ["open questions", "未决", "待确认"]))
67
+ errors.push("PRD must include open questions");
68
+ return { info: [`validate-pm checked ${docs.length} file(s)`], errors };
69
+ }
70
+ async function validateDesign(projectRoot) {
71
+ const architecture = await markdownFiles(path.join(projectRoot, ".docs/02_architecture"));
72
+ const techPlan = await markdownFiles(path.join(projectRoot, ".docs/03_tech_plan"));
73
+ const text = await combinedText([...architecture, ...techPlan]);
74
+ const errors = [];
75
+ if (architecture.length === 0)
76
+ errors.push("No architecture deliverables found");
77
+ if (techPlan.length === 0)
78
+ errors.push("No technical plan deliverables found");
79
+ if (!containsAny(text, ["prd", "requirement", "需求"]))
80
+ errors.push("Design must cite product requirements");
81
+ if (!containsAny(text, ["api", "interface", "接口", "contract", "契约"]))
82
+ errors.push("Design must describe interfaces or contracts");
83
+ if (!containsAny(text, ["task", "任务", "breakdown"]))
84
+ errors.push("Design must include task breakdown");
85
+ return { info: [`validate-design checked ${architecture.length + techPlan.length} file(s)`], errors };
86
+ }
87
+ async function validateDev(projectRoot) {
88
+ const errors = [];
89
+ const root = await harnessRoot(projectRoot);
90
+ const tasksData = await readYamlObject(path.join(projectRoot, root, "state", "plan.yaml"));
91
+ const tasks = Array.isArray(tasksData.tasks) ? tasksData.tasks : [];
92
+ if (tasks.length === 0)
93
+ errors.push("plan.yaml must contain at least one task");
94
+ const open = tasks.filter((task) => ["pending", "in_progress", "blocked", "pending_revision"].includes(String(task.status)));
95
+ if (open.length > 0)
96
+ errors.push(`Open tasks remain: ${open.map((task) => task.id).join(", ")}`);
97
+ for (const task of tasks) {
98
+ for (const field of ["id", "title", "status", "summary", "implementation_doc"]) {
99
+ if (!task[field])
100
+ errors.push(`Task missing ${field}: ${String(task.id ?? "unknown")}`);
101
+ }
102
+ if (["pending", "in_progress", "blocked", "pending_revision"].includes(String(task.status))) {
103
+ for (const field of ["docs", "allowed_paths", "required_gates", "acceptance_criteria"]) {
104
+ if (!task[field])
105
+ errors.push(`Open task ${task.id} missing ${field}`);
106
+ }
107
+ if (!Array.isArray(task.allowed_paths) || task.allowed_paths.length === 0) {
108
+ errors.push(`Open task ${task.id} must define allowed_paths`);
109
+ }
110
+ if (!Array.isArray(task.required_gates) || task.required_gates.length === 0) {
111
+ errors.push(`Open task ${task.id} must define required_gates`);
112
+ }
113
+ }
114
+ else {
115
+ for (const field of ["docs", "allowed_paths", "required_gates", "acceptance_criteria", "working_notes"]) {
116
+ if (task[field])
117
+ errors.push(`Closed task ${task.id} must not retain ${field}`);
118
+ }
119
+ }
120
+ }
121
+ for (const task of tasks.filter((task) => task.status === "done")) {
122
+ if (task.gate_result !== "PASS")
123
+ errors.push(`Done task ${task.id} must have gate_result PASS`);
124
+ const implementationDoc = String(task.implementation_doc ?? "");
125
+ if (implementationDoc && !(await pathExists(path.join(projectRoot, implementationDoc)))) {
126
+ errors.push(`Implementation doc missing for ${task.id}: ${implementationDoc}`);
127
+ }
128
+ }
129
+ return { info: [`validate-dev checked ${tasks.length} task(s)`], errors };
130
+ }
131
+ async function readYamlObject(filePath) {
132
+ if (!(await pathExists(filePath)))
133
+ return {};
134
+ return (parseYaml(await readText(filePath)) ?? {});
135
+ }
136
+ async function markdownFiles(root) {
137
+ const files = await listFiles(root);
138
+ return files.filter((file) => file.endsWith(".md") && !file.endsWith("overview.md"));
139
+ }
140
+ async function combinedText(files) {
141
+ const parts = await Promise.all(files.map((file) => readText(file)));
142
+ return parts.join("\n").toLowerCase();
143
+ }
144
+ function containsAny(text, needles) {
145
+ return needles.some((needle) => text.includes(needle.toLowerCase()));
146
+ }
147
+ export async function changedFiles(projectRoot) {
148
+ try {
149
+ const { stdout } = await execFileAsync("git", ["status", "--porcelain"], { cwd: projectRoot });
150
+ return stdout
151
+ .split("\n")
152
+ .map((line) => line.slice(3).trim())
153
+ .filter(Boolean);
154
+ }
155
+ catch {
156
+ return [];
157
+ }
158
+ }
@@ -0,0 +1,2 @@
1
+ export declare function parseYaml(content: string): unknown;
2
+ export declare function stringifyYaml(value: unknown): string;
@@ -0,0 +1,7 @@
1
+ import YAML from "yaml";
2
+ export function parseYaml(content) {
3
+ return YAML.parse(content);
4
+ }
5
+ export function stringifyYaml(value) {
6
+ return `${YAML.stringify(value, { lineWidth: 0 })}`;
7
+ }
@@ -0,0 +1,3 @@
1
+ # Migrations
2
+
3
+ Schema migrations for `.harness/config.yaml` and managed file layout belong here.
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "agent-project-sdlc",
3
+ "version": "0.1.0",
4
+ "description": "CLI and canonical assets for the AI SDLC Harness workflow.",
5
+ "type": "module",
6
+ "bin": {
7
+ "sdlc-harness": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "assets",
12
+ "migrations",
13
+ "source-mappings.yaml"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc -p tsconfig.json",
17
+ "typecheck": "tsc -p tsconfig.json --noEmit",
18
+ "test": "npm run build && node --test ../../tests/sdlc-harness/*.test.mjs",
19
+ "prepack": "npm run build"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "dependencies": {
25
+ "yaml": "^2.9.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^24.0.0",
29
+ "typescript": "^5.5.0"
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ source_mappings:
2
+ - source: "AGENTS.md"
3
+ target: "packages/sdlc-harness/assets/agents/AGENTS_CORE.md"
4
+ mode: "extract-managed-block"
5
+ - source: ".agent/skills"
6
+ target: "packages/sdlc-harness/assets/skills"
7
+ mode: "copy-tree"
8
+ - source: ".agent/managed/templates"
9
+ target: "packages/sdlc-harness/assets/templates"
10
+ mode: "copy-tree"
11
+ - source: ".agent/managed/policies"
12
+ target: "packages/sdlc-harness/assets/policies"
13
+ mode: "copy-tree"
14
+ - source: "Makefile"
15
+ target: "packages/sdlc-harness/assets/make/sdlc-harness.mk"
16
+ mode: "extract-harness-targets"
17
+ - source: ".github/workflows/harness.yml"
18
+ target: "packages/sdlc-harness/assets/github/harness.yml"
19
+ mode: "copy-file"