project-tiny-context-harness 0.2.54 → 0.2.56

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 (54) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +266 -243
  3. package/assets/README.md +302 -279
  4. package/assets/README.zh-CN.md +8 -6
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +56 -55
  7. package/assets/context_templates/architecture.md +31 -31
  8. package/assets/context_templates/area.md +24 -24
  9. package/assets/context_templates/context.toml +27 -27
  10. package/assets/context_templates/deployment.md +35 -35
  11. package/assets/context_templates/global.md +53 -53
  12. package/assets/context_templates/product-surface-contract.md +60 -0
  13. package/assets/context_templates/verification.md +28 -28
  14. package/assets/github/.gitkeep +1 -1
  15. package/assets/github/harness.yml +19 -19
  16. package/assets/make/.gitkeep +1 -1
  17. package/assets/make/ty-context.mk +48 -0
  18. package/assets/skills/context_development_engineer/SKILL.md +69 -66
  19. package/assets/skills/context_full_project_export/SKILL.md +25 -25
  20. package/assets/skills/context_harness_upgrade/SKILL.md +9 -9
  21. package/assets/skills/context_product_plan/SKILL.md +73 -70
  22. package/assets/skills/context_surface_contract/SKILL.md +168 -0
  23. package/assets/skills/context_uiux_design/SKILL.md +113 -110
  24. package/assets/skills/plan_acceptance_checklist_compiler/SKILL.md +427 -0
  25. package/assets/tools/validate_context.py +276 -276
  26. package/dist/cli.js +1 -1
  27. package/dist/commands/check-modularity.js +1 -1
  28. package/dist/commands/export-context.js +6 -6
  29. package/dist/commands/index.js +3 -3
  30. package/dist/commands/init.js +1 -1
  31. package/dist/commands/package-source.js +2 -2
  32. package/dist/commands/upgrade.js +1 -1
  33. package/dist/lib/config.js +2 -2
  34. package/dist/lib/constants.d.ts +1 -1
  35. package/dist/lib/constants.js +1 -1
  36. package/dist/lib/context-export.js +5 -5
  37. package/dist/lib/harness-root.d.ts +5 -0
  38. package/dist/lib/harness-root.js +32 -4
  39. package/dist/lib/legacy-managed-scan.d.ts +2 -0
  40. package/dist/lib/legacy-managed-scan.js +79 -0
  41. package/dist/lib/legacy-sdlc-migration.d.ts +2 -0
  42. package/dist/lib/legacy-sdlc-migration.js +189 -0
  43. package/dist/lib/managed-file.d.ts +18 -12
  44. package/dist/lib/managed-file.js +25 -14
  45. package/dist/lib/migrations.js +4 -2
  46. package/dist/lib/package-json-config.js +3 -3
  47. package/dist/lib/paths.d.ts +2 -2
  48. package/dist/lib/paths.js +2 -2
  49. package/dist/lib/sync-engine.js +33 -31
  50. package/dist/lib/validators.js +2 -2
  51. package/migrations/README.md +3 -3
  52. package/package.json +68 -68
  53. package/source-mappings.yaml +21 -21
  54. package/assets/make/sdlc-harness.mk +0 -48
@@ -17,8 +17,8 @@ export function defaultConfig(root) {
17
17
  { path: "AGENTS.md", strategy: "merge-block" },
18
18
  { path: "Makefile", strategy: "merge-block" },
19
19
  { path: harnessPath(root, "skills"), strategy: "managed" },
20
- { path: harnessPath(root, "pjsdlc_managed", "context_templates"), strategy: "managed" },
21
- { path: harnessPath(root, "pjsdlc_managed", "make", "sdlc-harness.mk"), strategy: "managed" },
20
+ { path: harnessPath(root, "ty-context-managed", "context_templates"), strategy: "managed" },
21
+ { path: harnessPath(root, "ty-context-managed", "make", "ty-context.mk"), strategy: "managed" },
22
22
  { path: "tools", strategy: "managed" },
23
23
  { path: ".github/workflows/harness.yml", strategy: "create-if-missing" }
24
24
  ],
@@ -1,3 +1,3 @@
1
1
  export declare const CANONICAL_CORE_PACKAGE = "project-tiny-context-harness";
2
2
  export declare const CURRENT_SCHEMA_VERSION = "4";
3
- export declare const CANONICAL_NPX_COMMAND = "npx --yes --package project-tiny-context-harness@latest sdlc-harness";
3
+ export declare const CANONICAL_NPX_COMMAND = "npx --yes --package project-tiny-context-harness@latest ty-context";
@@ -1,3 +1,3 @@
1
1
  export const CANONICAL_CORE_PACKAGE = "project-tiny-context-harness";
2
2
  export const CURRENT_SCHEMA_VERSION = "4";
3
- export const CANONICAL_NPX_COMMAND = `npx --yes --package ${CANONICAL_CORE_PACKAGE}@latest sdlc-harness`;
3
+ export const CANONICAL_NPX_COMMAND = `npx --yes --package ${CANONICAL_CORE_PACKAGE}@latest ty-context`;
@@ -8,7 +8,7 @@ import { harnessRoot } from "./harness-root.js";
8
8
  import { SAFE_EXAMPLE_FILE_NAMES, shouldExcludeRelativePath, shouldIncludeCodeFile, toPosix } from "./source-files.js";
9
9
  const execFileAsync = promisify(execFile);
10
10
  const EXPORT_HEADER = "Export artifact. Do not reference from project_context/context.toml.";
11
- const DEFAULT_EXPORT_DIR = "tmp/sdlc/context-exports";
11
+ const DEFAULT_EXPORT_DIR = "tmp/ty-context/context-exports";
12
12
  const CODE_EXPORT_FILE_NAME = "code-level-implementation.md";
13
13
  const MAX_TREE_ENTRIES = 300;
14
14
  const MAX_TREE_DEPTH = 4;
@@ -187,17 +187,17 @@ function resolveOutputPath(projectRoot, requestedOutput, now, mode) {
187
187
  const absoluteOutput = path.resolve(projectRoot, rawOutput);
188
188
  const relative = repoRelative(projectRoot, absoluteOutput);
189
189
  if (relative.startsWith("..") || path.isAbsolute(relative)) {
190
- throw new Error("export-context --output must stay inside the workspace; use tmp/sdlc/context-exports/<name>.md");
190
+ throw new Error("export-context --output must stay inside the workspace; use tmp/ty-context/context-exports/<name>.md");
191
191
  }
192
192
  const normalized = toPosix(relative);
193
193
  if (normalized === "project_context" || normalized.startsWith("project_context/")) {
194
- throw new Error("export-context output is a temporary artifact; use tmp/sdlc/context-exports/** instead of project_context/**");
194
+ throw new Error("export-context output is a temporary artifact; use tmp/ty-context/context-exports/** instead of project_context/**");
195
195
  }
196
196
  if (!normalized.startsWith(`${DEFAULT_EXPORT_DIR}/`)) {
197
- throw new Error("export-context only writes temporary artifacts under tmp/sdlc/context-exports/**");
197
+ throw new Error("export-context only writes temporary artifacts under tmp/ty-context/context-exports/**");
198
198
  }
199
199
  if (!normalized.endsWith(".md")) {
200
- throw new Error("export-context --output must be a Markdown file under tmp/sdlc/context-exports/**");
200
+ throw new Error("export-context --output must be a Markdown file under tmp/ty-context/context-exports/**");
201
201
  }
202
202
  return absoluteOutput;
203
203
  }
@@ -2,7 +2,12 @@ export interface HarnessRootConfig {
2
2
  harnessFolderName: string;
3
3
  source: string;
4
4
  }
5
+ export interface HarnessRootConfigCandidate extends HarnessRootConfig {
6
+ legacy: boolean;
7
+ }
8
+ export declare const LEGACY_HARNESS_JSON_CONFIG_PATH = "sdlc-harness.config.json";
5
9
  export declare function readHarnessRootConfig(projectRoot: string): Promise<HarnessRootConfig>;
10
+ export declare function readHarnessRootConfigCandidates(projectRoot: string): Promise<HarnessRootConfigCandidate[]>;
6
11
  export declare function harnessRoot(projectRoot: string): Promise<string>;
7
12
  export declare function harnessConfigPath(projectRoot: string): Promise<string>;
8
13
  export declare function harnessPath(root: string, ...segments: string[]): string;
@@ -1,19 +1,47 @@
1
1
  import path from "node:path";
2
2
  import { DEFAULT_HARNESS_ROOT, HARNESS_JSON_CONFIG_PATH } from "./paths.js";
3
3
  import { pathExists, readText } from "./fs.js";
4
+ export const LEGACY_HARNESS_JSON_CONFIG_PATH = "sdlc-harness.config.json";
4
5
  export async function readHarnessRootConfig(projectRoot) {
6
+ const candidates = await readHarnessRootConfigCandidates(projectRoot);
7
+ if (candidates.length > 0) {
8
+ const candidate = candidates[0];
9
+ return { harnessFolderName: candidate.harnessFolderName, source: candidate.source };
10
+ }
11
+ return { harnessFolderName: DEFAULT_HARNESS_ROOT, source: "default" };
12
+ }
13
+ export async function readHarnessRootConfigCandidates(projectRoot) {
14
+ const candidates = [];
5
15
  const packageJson = await readJsonConfig(path.join(projectRoot, "package.json"));
6
- const packageConfig = packageJson && typeof packageJson === "object" ? packageJson.sdlcHarness : undefined;
16
+ const packageConfig = packageJson && typeof packageJson === "object" ? packageJson.tyContext : undefined;
7
17
  const packageValue = folderNameFromObject(packageConfig);
8
18
  if (packageValue) {
9
- return { harnessFolderName: normalizeHarnessFolderName(packageValue), source: "package.json#sdlcHarness" };
19
+ candidates.push({ harnessFolderName: normalizeHarnessFolderName(packageValue), source: "package.json#tyContext", legacy: false });
10
20
  }
11
21
  const explicitConfig = await readJsonConfig(path.join(projectRoot, HARNESS_JSON_CONFIG_PATH));
12
22
  const explicitValue = folderNameFromObject(explicitConfig);
13
23
  if (explicitValue) {
14
- return { harnessFolderName: normalizeHarnessFolderName(explicitValue), source: HARNESS_JSON_CONFIG_PATH };
24
+ candidates.push({ harnessFolderName: normalizeHarnessFolderName(explicitValue), source: HARNESS_JSON_CONFIG_PATH, legacy: false });
15
25
  }
16
- return { harnessFolderName: DEFAULT_HARNESS_ROOT, source: "default" };
26
+ const legacyPackageConfig = packageJson && typeof packageJson === "object" ? packageJson.sdlcHarness : undefined;
27
+ const legacyPackageValue = folderNameFromObject(legacyPackageConfig);
28
+ if (legacyPackageValue) {
29
+ candidates.push({
30
+ harnessFolderName: normalizeHarnessFolderName(legacyPackageValue),
31
+ source: "package.json#sdlcHarness",
32
+ legacy: true
33
+ });
34
+ }
35
+ const legacyExplicitConfig = await readJsonConfig(path.join(projectRoot, LEGACY_HARNESS_JSON_CONFIG_PATH));
36
+ const legacyExplicitValue = folderNameFromObject(legacyExplicitConfig);
37
+ if (legacyExplicitValue) {
38
+ candidates.push({
39
+ harnessFolderName: normalizeHarnessFolderName(legacyExplicitValue),
40
+ source: LEGACY_HARNESS_JSON_CONFIG_PATH,
41
+ legacy: true
42
+ });
43
+ }
44
+ return candidates;
17
45
  }
18
46
  export async function harnessRoot(projectRoot) {
19
47
  return (await readHarnessRootConfig(projectRoot)).harnessFolderName;
@@ -0,0 +1,2 @@
1
+ import type { Migration, UpgradePlanItem } from "./migrations.js";
2
+ export declare function detectLegacyManagedDirectory(projectRoot: string, root: string, migration: Migration): Promise<UpgradePlanItem[]>;
@@ -0,0 +1,79 @@
1
+ import path from "node:path";
2
+ import { listFiles, pathExists, readText } from "./fs.js";
3
+ import { packageAssetPath } from "./paths.js";
4
+ const LEGACY_MANAGED_ROOT = "pjsdlc_managed";
5
+ const CURRENT_MANAGED_ROOT = "ty-context-managed";
6
+ const LEGACY_MAKEFILE = "sdlc-harness.mk";
7
+ const CURRENT_MAKEFILE = "ty-context.mk";
8
+ export async function detectLegacyManagedDirectory(projectRoot, root, migration) {
9
+ const legacyRoot = path.join(projectRoot, root, LEGACY_MANAGED_ROOT);
10
+ const files = await listFiles(legacyRoot);
11
+ const items = [];
12
+ for (const file of files) {
13
+ if (path.basename(file) === ".gitkeep") {
14
+ continue;
15
+ }
16
+ const relativeToLegacyRoot = path.relative(legacyRoot, file).split(path.sep).join("/");
17
+ const relativeToProject = path.relative(projectRoot, file).split(path.sep).join("/");
18
+ const classification = await classifyLegacyManagedFile(relativeToLegacyRoot);
19
+ if (classification.kind === "override") {
20
+ items.push(item(migration, "manual_required", relativeToProject, "Legacy skill overrides are no longer supported; move rules into a standalone project-local Skill."));
21
+ continue;
22
+ }
23
+ if (classification.kind === "unknown") {
24
+ items.push(item(migration, "manual_required", relativeToProject, "Legacy pjsdlc_managed content is not recognized as package-generated; review it manually."));
25
+ continue;
26
+ }
27
+ const target = path.join(projectRoot, root, CURRENT_MANAGED_ROOT, ...classification.targetRelative.split("/"));
28
+ if (!(await pathExists(target))) {
29
+ continue;
30
+ }
31
+ const assetContent = await readText(classification.assetPath);
32
+ if ((await readText(target)) !== assetContent) {
33
+ items.push(item(migration, "blocked", path.relative(projectRoot, target).split(path.sep).join("/"), `Cannot refresh legacy generated file because ${classification.targetRelative} already exists with non-package content.`));
34
+ }
35
+ }
36
+ return items;
37
+ }
38
+ async function classifyLegacyManagedFile(relative) {
39
+ if (relative.startsWith("override_skills/")) {
40
+ return { kind: "override" };
41
+ }
42
+ const mapped = legacyGeneratedMapping(relative);
43
+ if (!mapped) {
44
+ return { kind: "unknown" };
45
+ }
46
+ const assetPath = packageAssetPath(...mapped.assetSegments);
47
+ if (!(await pathExists(assetPath))) {
48
+ return { kind: "unknown" };
49
+ }
50
+ return { kind: "generated", targetRelative: mapped.targetRelative, assetPath };
51
+ }
52
+ function legacyGeneratedMapping(relative) {
53
+ if (relative === `make/${LEGACY_MAKEFILE}`) {
54
+ return { targetRelative: `make/${CURRENT_MAKEFILE}`, assetSegments: ["make", CURRENT_MAKEFILE] };
55
+ }
56
+ for (const [prefix, assetPrefix] of [
57
+ ["agents/", "agents"],
58
+ ["context_templates/", "context_templates"],
59
+ ["skills/", "skills"],
60
+ ["minimal_tools/", "tools"]
61
+ ]) {
62
+ if (relative.startsWith(prefix)) {
63
+ const rest = relative.slice(prefix.length);
64
+ return { targetRelative: `${prefix}${rest}`, assetSegments: [assetPrefix, ...rest.split("/")] };
65
+ }
66
+ }
67
+ return undefined;
68
+ }
69
+ function item(migration, status, pathLabel, message) {
70
+ return {
71
+ id: migration.id,
72
+ introducedIn: migration.introducedIn,
73
+ description: migration.description,
74
+ scope: migration.scope,
75
+ status,
76
+ path: pathLabel,
77
+ message
78
+ };
79
+ }
@@ -0,0 +1,2 @@
1
+ import type { Migration } from "./migrations.js";
2
+ export declare const legacySdlcHarnessMigration: Migration;
@@ -0,0 +1,189 @@
1
+ import path from "node:path";
2
+ import { LEGACY_HARNESS_JSON_CONFIG_PATH, readHarnessRootConfigCandidates } from "./harness-root.js";
3
+ import { pathExists, readText, writeTextIfChanged } from "./fs.js";
4
+ import { detectLegacyManagedDirectory } from "./legacy-managed-scan.js";
5
+ import { HARNESS_JSON_CONFIG_PATH } from "./paths.js";
6
+ const LEGACY_MANAGED_ROOT = "pjsdlc_managed";
7
+ const CURRENT_MANAGED_ROOT = "ty-context-managed";
8
+ const LEGACY_MAKEFILE = "sdlc-harness.mk";
9
+ const CURRENT_MAKEFILE = "ty-context.mk";
10
+ export const legacySdlcHarnessMigration = {
11
+ id: "legacy-sdlc-harness-rename",
12
+ introducedIn: "0.2.54",
13
+ description: "Rename legacy sdlc-harness configuration and pjsdlc_managed package-managed paths.",
14
+ scope: "package.json, sdlc-harness.config.json, <harnessRoot>/config.yaml, old managed markers and pjsdlc_managed/**",
15
+ risk: "safe",
16
+ manualMessage: "Resolve legacy sdlc-harness conflicts or move old override rules into standalone project-local Skills.",
17
+ detect: detectLegacySdlcHarness,
18
+ apply: migrateLegacySdlcHarness,
19
+ verify: async () => undefined
20
+ };
21
+ async function detectLegacySdlcHarness(projectRoot, root, migration) {
22
+ const items = [];
23
+ const candidates = await readHarnessRootConfigCandidates(projectRoot);
24
+ const currentCandidates = candidates.filter((candidate) => !candidate.legacy);
25
+ const legacyCandidates = candidates.filter((candidate) => candidate.legacy);
26
+ const currentRoot = currentCandidates[0]?.harnessFolderName;
27
+ const distinctLegacyRoots = Array.from(new Set(legacyCandidates.map((candidate) => candidate.harnessFolderName)));
28
+ if (currentRoot && distinctLegacyRoots.some((legacyRoot) => legacyRoot !== currentRoot)) {
29
+ items.push(item(migration, "blocked", rootConflictPath(currentCandidates, legacyCandidates), "Current ty-context and legacy sdlc-harness root configuration disagree; choose the harness root manually before upgrade."));
30
+ }
31
+ else if (!currentRoot && distinctLegacyRoots.length > 1) {
32
+ items.push(item(migration, "blocked", rootConflictPath(currentCandidates, legacyCandidates), "Legacy sdlc-harness root sources disagree; choose one harness root manually before upgrade."));
33
+ }
34
+ items.push(...(await detectLegacyManagedDirectory(projectRoot, root, legacySdlcHarnessMigration)));
35
+ if (items.some((entry) => entry.status === "blocked")) {
36
+ return items;
37
+ }
38
+ if (await needsLegacySafeMigration(projectRoot, root, legacyCandidates.length > 0)) {
39
+ items.unshift(item(migration, "safe_pending", "legacy sdlc-harness surfaces", "Legacy sdlc-harness naming can be copied to ty-context configuration and refreshed by sync."));
40
+ }
41
+ return items;
42
+ }
43
+ async function migrateLegacySdlcHarness(projectRoot, root, report) {
44
+ await migratePackageJsonConfig(projectRoot, report);
45
+ await migrateJsonConfig(projectRoot, report);
46
+ await migrateConfigYamlPaths(projectRoot, root, report);
47
+ }
48
+ async function needsLegacySafeMigration(projectRoot, root, hasLegacyRootConfig) {
49
+ if (hasLegacyRootConfig && (await legacyPackageConfigNeedsCopy(projectRoot))) {
50
+ return true;
51
+ }
52
+ if (await legacyJsonConfigNeedsCopy(projectRoot)) {
53
+ return true;
54
+ }
55
+ if (await configYamlHasLegacyManagedPaths(projectRoot, root)) {
56
+ return true;
57
+ }
58
+ if (await hasLegacyManagedMarkers(projectRoot)) {
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ async function legacyPackageConfigNeedsCopy(projectRoot) {
64
+ const packagePath = path.join(projectRoot, "package.json");
65
+ if (!(await pathExists(packagePath))) {
66
+ return false;
67
+ }
68
+ const packageJson = parseJsonRecord(await readText(packagePath), "package.json");
69
+ return isJsonRecord(packageJson.sdlcHarness) && !isJsonRecord(packageJson.tyContext);
70
+ }
71
+ async function legacyJsonConfigNeedsCopy(projectRoot) {
72
+ const legacyPath = path.join(projectRoot, LEGACY_HARNESS_JSON_CONFIG_PATH);
73
+ const currentPath = path.join(projectRoot, HARNESS_JSON_CONFIG_PATH);
74
+ return (await pathExists(legacyPath)) && !(await pathExists(currentPath));
75
+ }
76
+ async function configYamlHasLegacyManagedPaths(projectRoot, root) {
77
+ const configPath = path.join(projectRoot, root, "config.yaml");
78
+ if (!(await pathExists(configPath))) {
79
+ return false;
80
+ }
81
+ return hasLegacyManagedPath(await readText(configPath));
82
+ }
83
+ async function hasLegacyManagedMarkers(projectRoot) {
84
+ for (const relative of ["AGENTS.md", "Makefile", ".github/workflows/harness.yml"]) {
85
+ const target = path.join(projectRoot, ...relative.split("/"));
86
+ if ((await pathExists(target)) && hasLegacyMarker(await readText(target))) {
87
+ return true;
88
+ }
89
+ }
90
+ return false;
91
+ }
92
+ async function migratePackageJsonConfig(projectRoot, report) {
93
+ const packagePath = path.join(projectRoot, "package.json");
94
+ if (!(await pathExists(packagePath))) {
95
+ report.skipped.push("package.json#sdlcHarness");
96
+ return;
97
+ }
98
+ const packageJson = parseJsonRecord(await readText(packagePath), "package.json");
99
+ if (!isJsonRecord(packageJson.sdlcHarness) || isJsonRecord(packageJson.tyContext)) {
100
+ report.skipped.push("package.json#sdlcHarness");
101
+ return;
102
+ }
103
+ packageJson.tyContext = { ...packageJson.sdlcHarness };
104
+ if (await writeTextIfChanged(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`)) {
105
+ report.changed.push("package.json#tyContext");
106
+ }
107
+ else {
108
+ report.skipped.push("package.json#tyContext");
109
+ }
110
+ }
111
+ async function migrateJsonConfig(projectRoot, report) {
112
+ const legacyPath = path.join(projectRoot, LEGACY_HARNESS_JSON_CONFIG_PATH);
113
+ const currentPath = path.join(projectRoot, HARNESS_JSON_CONFIG_PATH);
114
+ if (!(await pathExists(legacyPath))) {
115
+ report.skipped.push(LEGACY_HARNESS_JSON_CONFIG_PATH);
116
+ return;
117
+ }
118
+ if (await pathExists(currentPath)) {
119
+ report.skipped.push(HARNESS_JSON_CONFIG_PATH);
120
+ return;
121
+ }
122
+ if (await writeTextIfChanged(currentPath, await readText(legacyPath))) {
123
+ report.changed.push(HARNESS_JSON_CONFIG_PATH);
124
+ }
125
+ else {
126
+ report.skipped.push(HARNESS_JSON_CONFIG_PATH);
127
+ }
128
+ }
129
+ async function migrateConfigYamlPaths(projectRoot, root, report) {
130
+ const relative = `${root}/config.yaml`;
131
+ const configPath = path.join(projectRoot, root, "config.yaml");
132
+ if (!(await pathExists(configPath))) {
133
+ report.skipped.push(relative);
134
+ return;
135
+ }
136
+ const original = await readText(configPath);
137
+ const next = rewriteLegacyManagedPaths(original);
138
+ if (next === original) {
139
+ report.skipped.push(relative);
140
+ return;
141
+ }
142
+ if (await writeTextIfChanged(configPath, next)) {
143
+ report.changed.push(`${relative}#legacy-managed-paths`);
144
+ }
145
+ else {
146
+ report.skipped.push(`${relative}#legacy-managed-paths`);
147
+ }
148
+ }
149
+ function rewriteLegacyManagedPaths(content) {
150
+ return content.replaceAll(LEGACY_MANAGED_ROOT, CURRENT_MANAGED_ROOT).replaceAll(LEGACY_MAKEFILE, CURRENT_MAKEFILE);
151
+ }
152
+ function hasLegacyManagedPath(content) {
153
+ return content.includes(LEGACY_MANAGED_ROOT) || content.includes(LEGACY_MAKEFILE);
154
+ }
155
+ function hasLegacyMarker(content) {
156
+ return [
157
+ "<!-- pjsdlc:sdlc-harness:begin -->",
158
+ "<!-- sdlc-harness:begin -->",
159
+ "# pjsdlc:sdlc-harness:make:begin",
160
+ "# sdlc-harness:make:begin",
161
+ "# pjsdlc:sdlc-harness:github-workflow:begin"
162
+ ].some((marker) => content.includes(marker));
163
+ }
164
+ function rootConflictPath(currentCandidates, legacyCandidates) {
165
+ return [...currentCandidates, ...legacyCandidates]
166
+ .map((candidate) => `${candidate.source}=${candidate.harnessFolderName}`)
167
+ .join(", ");
168
+ }
169
+ function item(migrationId, status, pathLabel, message) {
170
+ return {
171
+ id: legacySdlcHarnessMigration.id,
172
+ introducedIn: legacySdlcHarnessMigration.introducedIn,
173
+ description: legacySdlcHarnessMigration.description,
174
+ scope: legacySdlcHarnessMigration.scope,
175
+ status,
176
+ path: pathLabel,
177
+ message
178
+ };
179
+ }
180
+ function parseJsonRecord(content, pathLabel) {
181
+ const parsed = JSON.parse(content);
182
+ if (!isJsonRecord(parsed)) {
183
+ throw new Error(`${pathLabel} must contain a JSON object`);
184
+ }
185
+ return parsed;
186
+ }
187
+ function isJsonRecord(value) {
188
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
189
+ }
@@ -2,18 +2,24 @@ export interface ManagedBlockMarkers {
2
2
  start: string;
3
3
  end: string;
4
4
  }
5
- export declare const MANAGED_BLOCK_START = "<!-- pjsdlc:sdlc-harness:begin -->";
6
- export declare const MANAGED_BLOCK_END = "<!-- pjsdlc:sdlc-harness:end -->";
7
- export declare const LEGACY_MANAGED_BLOCK_START = "<!-- sdlc-harness:begin -->";
8
- export declare const LEGACY_MANAGED_BLOCK_END = "<!-- sdlc-harness:end -->";
9
- export declare const MAKEFILE_BLOCK_START = "# pjsdlc:sdlc-harness:make:begin";
10
- export declare const MAKEFILE_BLOCK_END = "# pjsdlc:sdlc-harness:make:end";
11
- export declare const LEGACY_MAKEFILE_BLOCK_START = "# sdlc-harness:make:begin";
12
- export declare const LEGACY_MAKEFILE_BLOCK_END = "# sdlc-harness:make:end";
13
- export declare const GITHUB_WORKFLOW_BLOCK_START = "# pjsdlc:sdlc-harness:github-workflow:begin";
14
- export declare const GITHUB_WORKFLOW_BLOCK_END = "# pjsdlc:sdlc-harness:github-workflow:end";
15
- export declare const MANAGED_METADATA_START = "<!-- pjsdlc:sdlc-harness-managed";
16
- export declare const LEGACY_MANAGED_METADATA_START = "<!-- sdlc-harness-managed";
5
+ export declare const MANAGED_BLOCK_START = "<!-- ty-context:managed:begin -->";
6
+ export declare const MANAGED_BLOCK_END = "<!-- ty-context:managed:end -->";
7
+ export declare const LEGACY_PJSDLC_MANAGED_BLOCK_START = "<!-- pjsdlc:sdlc-harness:begin -->";
8
+ export declare const LEGACY_PJSDLC_MANAGED_BLOCK_END = "<!-- pjsdlc:sdlc-harness:end -->";
9
+ export declare const LEGACY_SDLC_MANAGED_BLOCK_START = "<!-- sdlc-harness:begin -->";
10
+ export declare const LEGACY_SDLC_MANAGED_BLOCK_END = "<!-- sdlc-harness:end -->";
11
+ export declare const MAKEFILE_BLOCK_START = "# ty-context:make:begin";
12
+ export declare const MAKEFILE_BLOCK_END = "# ty-context:make:end";
13
+ export declare const LEGACY_PJSDLC_MAKEFILE_BLOCK_START = "# pjsdlc:sdlc-harness:make:begin";
14
+ export declare const LEGACY_PJSDLC_MAKEFILE_BLOCK_END = "# pjsdlc:sdlc-harness:make:end";
15
+ export declare const LEGACY_SDLC_MAKEFILE_BLOCK_START = "# sdlc-harness:make:begin";
16
+ export declare const LEGACY_SDLC_MAKEFILE_BLOCK_END = "# sdlc-harness:make:end";
17
+ export declare const GITHUB_WORKFLOW_BLOCK_START = "# ty-context:github-workflow:begin";
18
+ export declare const GITHUB_WORKFLOW_BLOCK_END = "# ty-context:github-workflow:end";
19
+ export declare const LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_START = "# pjsdlc:sdlc-harness:github-workflow:begin";
20
+ export declare const LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_END = "# pjsdlc:sdlc-harness:github-workflow:end";
21
+ export declare const MANAGED_METADATA_START = "<!-- ty-context-managed";
17
22
  export declare const MANAGED_METADATA_END = "-->";
18
23
  export declare const AGENTS_BLOCK_MARKERS: ManagedBlockMarkers[];
19
24
  export declare const MAKEFILE_BLOCK_MARKERS: ManagedBlockMarkers[];
25
+ export declare const GITHUB_WORKFLOW_BLOCK_MARKERS: ManagedBlockMarkers[];
@@ -1,21 +1,32 @@
1
- export const MANAGED_BLOCK_START = "<!-- pjsdlc:sdlc-harness:begin -->";
2
- export const MANAGED_BLOCK_END = "<!-- pjsdlc:sdlc-harness:end -->";
3
- export const LEGACY_MANAGED_BLOCK_START = "<!-- sdlc-harness:begin -->";
4
- export const LEGACY_MANAGED_BLOCK_END = "<!-- sdlc-harness:end -->";
5
- export const MAKEFILE_BLOCK_START = "# pjsdlc:sdlc-harness:make:begin";
6
- export const MAKEFILE_BLOCK_END = "# pjsdlc:sdlc-harness:make:end";
7
- export const LEGACY_MAKEFILE_BLOCK_START = "# sdlc-harness:make:begin";
8
- export const LEGACY_MAKEFILE_BLOCK_END = "# sdlc-harness:make:end";
9
- export const GITHUB_WORKFLOW_BLOCK_START = "# pjsdlc:sdlc-harness:github-workflow:begin";
10
- export const GITHUB_WORKFLOW_BLOCK_END = "# pjsdlc:sdlc-harness:github-workflow:end";
11
- export const MANAGED_METADATA_START = "<!-- pjsdlc:sdlc-harness-managed";
12
- export const LEGACY_MANAGED_METADATA_START = "<!-- sdlc-harness-managed";
1
+ export const MANAGED_BLOCK_START = "<!-- ty-context:managed:begin -->";
2
+ export const MANAGED_BLOCK_END = "<!-- ty-context:managed:end -->";
3
+ export const LEGACY_PJSDLC_MANAGED_BLOCK_START = "<!-- pjsdlc:sdlc-harness:begin -->";
4
+ export const LEGACY_PJSDLC_MANAGED_BLOCK_END = "<!-- pjsdlc:sdlc-harness:end -->";
5
+ export const LEGACY_SDLC_MANAGED_BLOCK_START = "<!-- sdlc-harness:begin -->";
6
+ export const LEGACY_SDLC_MANAGED_BLOCK_END = "<!-- sdlc-harness:end -->";
7
+ export const MAKEFILE_BLOCK_START = "# ty-context:make:begin";
8
+ export const MAKEFILE_BLOCK_END = "# ty-context:make:end";
9
+ export const LEGACY_PJSDLC_MAKEFILE_BLOCK_START = "# pjsdlc:sdlc-harness:make:begin";
10
+ export const LEGACY_PJSDLC_MAKEFILE_BLOCK_END = "# pjsdlc:sdlc-harness:make:end";
11
+ export const LEGACY_SDLC_MAKEFILE_BLOCK_START = "# sdlc-harness:make:begin";
12
+ export const LEGACY_SDLC_MAKEFILE_BLOCK_END = "# sdlc-harness:make:end";
13
+ export const GITHUB_WORKFLOW_BLOCK_START = "# ty-context:github-workflow:begin";
14
+ export const GITHUB_WORKFLOW_BLOCK_END = "# ty-context:github-workflow:end";
15
+ export const LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_START = "# pjsdlc:sdlc-harness:github-workflow:begin";
16
+ export const LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_END = "# pjsdlc:sdlc-harness:github-workflow:end";
17
+ export const MANAGED_METADATA_START = "<!-- ty-context-managed";
13
18
  export const MANAGED_METADATA_END = "-->";
14
19
  export const AGENTS_BLOCK_MARKERS = [
15
20
  { start: MANAGED_BLOCK_START, end: MANAGED_BLOCK_END },
16
- { start: LEGACY_MANAGED_BLOCK_START, end: LEGACY_MANAGED_BLOCK_END }
21
+ { start: LEGACY_PJSDLC_MANAGED_BLOCK_START, end: LEGACY_PJSDLC_MANAGED_BLOCK_END },
22
+ { start: LEGACY_SDLC_MANAGED_BLOCK_START, end: LEGACY_SDLC_MANAGED_BLOCK_END }
17
23
  ];
18
24
  export const MAKEFILE_BLOCK_MARKERS = [
19
25
  { start: MAKEFILE_BLOCK_START, end: MAKEFILE_BLOCK_END },
20
- { start: LEGACY_MAKEFILE_BLOCK_START, end: LEGACY_MAKEFILE_BLOCK_END }
26
+ { start: LEGACY_PJSDLC_MAKEFILE_BLOCK_START, end: LEGACY_PJSDLC_MAKEFILE_BLOCK_END },
27
+ { start: LEGACY_SDLC_MAKEFILE_BLOCK_START, end: LEGACY_SDLC_MAKEFILE_BLOCK_END }
28
+ ];
29
+ export const GITHUB_WORKFLOW_BLOCK_MARKERS = [
30
+ { start: GITHUB_WORKFLOW_BLOCK_START, end: GITHUB_WORKFLOW_BLOCK_END },
31
+ { start: LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_START, end: LEGACY_PJSDLC_GITHUB_WORKFLOW_BLOCK_END }
21
32
  ];
@@ -7,11 +7,13 @@ import { defaultConfig, readConfig } from "./config.js";
7
7
  import { createDesignMdIfMissing, DESIGN_MD_PATH } from "./design-md.js";
8
8
  import { ensureDir, listFiles, pathExists, readText, writeTextIfChanged } from "./fs.js";
9
9
  import { harnessConfigPath, harnessRoot } from "./harness-root.js";
10
+ import { legacySdlcHarnessMigration } from "./legacy-sdlc-migration.js";
10
11
  import { stringifyYaml } from "./yaml.js";
11
12
  async function verifyNoop() {
12
13
  return;
13
14
  }
14
15
  export const migrations = [
16
+ legacySdlcHarnessMigration,
15
17
  {
16
18
  id: "schema-v4-config-refresh",
17
19
  introducedIn: "0.2.0",
@@ -71,7 +73,7 @@ export const migrations = [
71
73
  id: "deprecated-skill-overrides",
72
74
  introducedIn: "0.2.0",
73
75
  description: "Report deprecated managed skill overrides that must move to standalone project-local Skills.",
74
- scope: "<harnessRoot>/pjsdlc_managed/override_skills/**",
76
+ scope: "<harnessRoot>/ty-context-managed/override_skills/**",
75
77
  risk: "manual",
76
78
  manualMessage: "Move override files into standalone project-local Skills before running sync.",
77
79
  detect: detectDeprecatedSkillOverrides,
@@ -355,7 +357,7 @@ async function migrateConfig(projectRoot, root, report) {
355
357
  }
356
358
  }
357
359
  async function detectDeprecatedSkillOverrides(projectRoot, root, migration) {
358
- const overrideRoot = path.join(projectRoot, root, "pjsdlc_managed", "override_skills");
360
+ const overrideRoot = path.join(projectRoot, root, "ty-context-managed", "override_skills");
359
361
  if (!(await pathExists(overrideRoot))) {
360
362
  return [];
361
363
  }
@@ -7,7 +7,7 @@ export async function packageHarnessRoot(projectRoot) {
7
7
  return undefined;
8
8
  }
9
9
  const packageJson = parsePackageJson(await readText(packagePath));
10
- const config = packageJson.sdlcHarness;
10
+ const config = packageJson.tyContext;
11
11
  if (!config || typeof config !== "object" || Array.isArray(config)) {
12
12
  return undefined;
13
13
  }
@@ -18,13 +18,13 @@ export async function writePackageHarnessRoot(projectRoot, folderName) {
18
18
  const normalized = normalizeHarnessFolderName(folderName);
19
19
  const packagePath = path.join(projectRoot, "package.json");
20
20
  const packageJson = (await pathExists(packagePath)) ? parsePackageJson(await readText(packagePath)) : {};
21
- const existingConfig = packageJson.sdlcHarness;
21
+ const existingConfig = packageJson.tyContext;
22
22
  const nextConfig = existingConfig && typeof existingConfig === "object" && !Array.isArray(existingConfig)
23
23
  ? { ...existingConfig, harnessFolderName: normalized }
24
24
  : { harnessFolderName: normalized };
25
25
  const next = {
26
26
  ...packageJson,
27
- sdlcHarness: nextConfig
27
+ tyContext: nextConfig
28
28
  };
29
29
  return writeTextIfChanged(packagePath, `${JSON.stringify(next, null, 2)}\n`);
30
30
  }
@@ -1,5 +1,5 @@
1
- export declare const SOURCE_MAPPINGS_PATH = "packages/sdlc-harness/source-mappings.yaml";
1
+ export declare const SOURCE_MAPPINGS_PATH = "packages/ty-context/source-mappings.yaml";
2
2
  export declare const DEFAULT_HARNESS_ROOT = ".agent";
3
- export declare const HARNESS_JSON_CONFIG_PATH = "sdlc-harness.config.json";
3
+ export declare const HARNESS_JSON_CONFIG_PATH = "ty-context.config.json";
4
4
  export declare function packageRoot(): string;
5
5
  export declare function packageAssetPath(...segments: string[]): string;
package/dist/lib/paths.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- export const SOURCE_MAPPINGS_PATH = "packages/sdlc-harness/source-mappings.yaml";
3
+ export const SOURCE_MAPPINGS_PATH = "packages/ty-context/source-mappings.yaml";
4
4
  export const DEFAULT_HARNESS_ROOT = ".agent";
5
- export const HARNESS_JSON_CONFIG_PATH = "sdlc-harness.config.json";
5
+ export const HARNESS_JSON_CONFIG_PATH = "ty-context.config.json";
6
6
  export function packageRoot() {
7
7
  return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
8
8
  }