project-tiny-context-harness 0.2.39

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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +399 -0
  3. package/assets/README.md +455 -0
  4. package/assets/README.zh-CN.md +131 -0
  5. package/assets/agents/.gitkeep +1 -0
  6. package/assets/agents/AGENTS_CORE.md +58 -0
  7. package/assets/context_templates/architecture.md +31 -0
  8. package/assets/context_templates/area.md +31 -0
  9. package/assets/context_templates/context.toml +27 -0
  10. package/assets/context_templates/deployment.md +35 -0
  11. package/assets/context_templates/global.md +53 -0
  12. package/assets/context_templates/verification.md +31 -0
  13. package/assets/github/.gitkeep +1 -0
  14. package/assets/github/harness.yml +37 -0
  15. package/assets/make/.gitkeep +1 -0
  16. package/assets/make/sdlc-harness.mk +39 -0
  17. package/assets/skills/context_development_engineer/SKILL.md +86 -0
  18. package/assets/skills/context_full_project_export/SKILL.md +55 -0
  19. package/assets/skills/context_product_plan/SKILL.md +85 -0
  20. package/assets/skills/context_uiux_design/SKILL.md +110 -0
  21. package/assets/tools/validate_context.py +276 -0
  22. package/dist/cli.d.ts +2 -0
  23. package/dist/cli.js +12 -0
  24. package/dist/commands/doctor.d.ts +1 -0
  25. package/dist/commands/doctor.js +16 -0
  26. package/dist/commands/export-context.d.ts +1 -0
  27. package/dist/commands/export-context.js +149 -0
  28. package/dist/commands/index.d.ts +3 -0
  29. package/dist/commands/index.js +33 -0
  30. package/dist/commands/init.d.ts +3 -0
  31. package/dist/commands/init.js +108 -0
  32. package/dist/commands/package-source.d.ts +1 -0
  33. package/dist/commands/package-source.js +24 -0
  34. package/dist/commands/sync.d.ts +1 -0
  35. package/dist/commands/sync.js +14 -0
  36. package/dist/commands/upgrade.d.ts +1 -0
  37. package/dist/commands/upgrade.js +7 -0
  38. package/dist/commands/validate.d.ts +1 -0
  39. package/dist/commands/validate.js +14 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +1 -0
  42. package/dist/lib/config.d.ts +5 -0
  43. package/dist/lib/config.js +52 -0
  44. package/dist/lib/constants.d.ts +3 -0
  45. package/dist/lib/constants.js +3 -0
  46. package/dist/lib/context-export.d.ts +21 -0
  47. package/dist/lib/context-export.js +845 -0
  48. package/dist/lib/context-manifest.d.ts +3 -0
  49. package/dist/lib/context-manifest.js +103 -0
  50. package/dist/lib/context-templates.d.ts +5 -0
  51. package/dist/lib/context-templates.js +204 -0
  52. package/dist/lib/design-md.d.ts +2 -0
  53. package/dist/lib/design-md.js +132 -0
  54. package/dist/lib/doctor.d.ts +6 -0
  55. package/dist/lib/doctor.js +41 -0
  56. package/dist/lib/fs.d.ts +8 -0
  57. package/dist/lib/fs.js +56 -0
  58. package/dist/lib/harness-root.d.ts +9 -0
  59. package/dist/lib/harness-root.js +50 -0
  60. package/dist/lib/init.d.ts +5 -0
  61. package/dist/lib/init.js +65 -0
  62. package/dist/lib/managed-file.d.ts +19 -0
  63. package/dist/lib/managed-file.js +21 -0
  64. package/dist/lib/migrations.d.ts +11 -0
  65. package/dist/lib/migrations.js +180 -0
  66. package/dist/lib/package-json-config.d.ts +2 -0
  67. package/dist/lib/package-json-config.js +37 -0
  68. package/dist/lib/package-source.d.ts +8 -0
  69. package/dist/lib/package-source.js +124 -0
  70. package/dist/lib/paths.d.ts +5 -0
  71. package/dist/lib/paths.js +11 -0
  72. package/dist/lib/schema-guard.d.ts +3 -0
  73. package/dist/lib/schema-guard.js +28 -0
  74. package/dist/lib/sync-engine.d.ts +7 -0
  75. package/dist/lib/sync-engine.js +350 -0
  76. package/dist/lib/types.d.ts +21 -0
  77. package/dist/lib/types.js +1 -0
  78. package/dist/lib/upgrade.d.ts +1 -0
  79. package/dist/lib/upgrade.js +21 -0
  80. package/dist/lib/validators.d.ts +5 -0
  81. package/dist/lib/validators.js +459 -0
  82. package/dist/lib/yaml.d.ts +2 -0
  83. package/dist/lib/yaml.js +7 -0
  84. package/migrations/README.md +3 -0
  85. package/package.json +68 -0
  86. package/source-mappings.yaml +25 -0
@@ -0,0 +1,108 @@
1
+ import { stdin as input, stdout as output } from "node:process";
2
+ import * as readline from "node:readline/promises";
3
+ import { runInit } from "../lib/init.js";
4
+ import { normalizeHarnessFolderName, readHarnessRootConfig } from "../lib/harness-root.js";
5
+ import { writePackageHarnessRoot } from "../lib/package-json-config.js";
6
+ import { DEFAULT_HARNESS_ROOT } from "../lib/paths.js";
7
+ const AGENT_HARNESS_OPTIONS = [
8
+ { key: "codex", label: "Codex", harnessFolderName: ".codex" },
9
+ { key: "claude", label: "Claude Code", harnessFolderName: ".claude" },
10
+ { key: "cursor", label: "Cursor", harnessFolderName: ".cursor" },
11
+ { key: "cline", label: "Cline", harnessFolderName: ".cline" },
12
+ { key: "roo", label: "Roo Code", harnessFolderName: ".roo" },
13
+ { key: "gemini", label: "Gemini CLI", harnessFolderName: ".gemini" },
14
+ { key: "other", label: "Other" }
15
+ ];
16
+ export async function init(args) {
17
+ const adopt = args.includes("--adopt");
18
+ const force = args.includes("--force");
19
+ const projectRoot = process.cwd();
20
+ const configuredRoot = await resolveInitHarnessRoot(projectRoot, args);
21
+ if (configuredRoot) {
22
+ await writePackageHarnessRoot(projectRoot, configuredRoot);
23
+ console.log(`configured package.json sdlcHarness.harnessFolderName=${JSON.stringify(configuredRoot)}`);
24
+ }
25
+ const report = await runInit(projectRoot, { adopt, force });
26
+ for (const line of report) {
27
+ console.log(line);
28
+ }
29
+ }
30
+ export async function resolveInitHarnessRoot(projectRoot, args) {
31
+ const argRoot = valueForArg(args, "--harness-folder") ?? valueForArg(args, "--harnessFolderName");
32
+ if (argRoot) {
33
+ return normalizeHarnessFolderName(argRoot);
34
+ }
35
+ const current = await readHarnessRootConfig(projectRoot);
36
+ if (current.source !== "default") {
37
+ return undefined;
38
+ }
39
+ return promptAgentHarnessRoot();
40
+ }
41
+ async function promptAgentHarnessRoot() {
42
+ if (!input.isTTY || !output.isTTY) {
43
+ return resolveAgentHarnessFolderName("");
44
+ }
45
+ const rl = readline.createInterface({ input, output });
46
+ try {
47
+ const agent = await questionUntilValid(rl, `${formatAgentChoices()}\nTarget agent (default 1 Codex): `);
48
+ const option = agentOptionForAnswer(agent);
49
+ if (option?.harnessFolderName) {
50
+ return normalizeHarnessFolderName(option.harnessFolderName);
51
+ }
52
+ const folder = await rl.question(`Harness folder name (default ${DEFAULT_HARNESS_ROOT}; press Enter to use default): `);
53
+ return normalizeHarnessFolderName(folder.trim() || DEFAULT_HARNESS_ROOT);
54
+ }
55
+ finally {
56
+ rl.close();
57
+ }
58
+ }
59
+ async function questionUntilValid(rl, query) {
60
+ while (true) {
61
+ const answer = await rl.question(query);
62
+ if (agentOptionForAnswer(answer)) {
63
+ return answer;
64
+ }
65
+ console.log("Unknown agent choice. Enter a number, agent name, or Other.");
66
+ }
67
+ }
68
+ function formatAgentChoices() {
69
+ const lines = ["Choose target agent:"];
70
+ AGENT_HARNESS_OPTIONS.forEach((option, index) => {
71
+ const folder = option.harnessFolderName ? ` -> ${option.harnessFolderName}` : "";
72
+ lines.push(`${index + 1}) ${option.label}${folder}`);
73
+ });
74
+ return lines.join("\n");
75
+ }
76
+ export function resolveAgentHarnessFolderName(agentAnswer, customFolderAnswer = "") {
77
+ const option = agentOptionForAnswer(agentAnswer);
78
+ if (!option) {
79
+ throw new Error("Unknown agent choice");
80
+ }
81
+ return normalizeHarnessFolderName(option.harnessFolderName ?? (customFolderAnswer.trim() || DEFAULT_HARNESS_ROOT));
82
+ }
83
+ function agentOptionForAnswer(answer) {
84
+ const normalized = answer.trim().toLowerCase();
85
+ if (!normalized) {
86
+ return AGENT_HARNESS_OPTIONS[0];
87
+ }
88
+ const index = Number.parseInt(normalized, 10);
89
+ if (Number.isInteger(index) && String(index) === normalized) {
90
+ return AGENT_HARNESS_OPTIONS[index - 1];
91
+ }
92
+ return AGENT_HARNESS_OPTIONS.find((option) => {
93
+ const label = option.label.toLowerCase();
94
+ return option.key === normalized || label === normalized || label.replace(/\s+/g, "-") === normalized;
95
+ });
96
+ }
97
+ function valueForArg(args, name) {
98
+ const prefix = `${name}=`;
99
+ const inline = args.find((arg) => arg.startsWith(prefix));
100
+ if (inline) {
101
+ return inline.slice(prefix.length);
102
+ }
103
+ const index = args.indexOf(name);
104
+ if (index >= 0) {
105
+ return args[index + 1];
106
+ }
107
+ return undefined;
108
+ }
@@ -0,0 +1 @@
1
+ export declare function packageSource(args: string[]): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { checkSource, syncSource } from "../lib/package-source.js";
2
+ export async function packageSource(args) {
3
+ const subcommand = args[0] ?? "help";
4
+ if (subcommand === "sync-source") {
5
+ const report = await syncSource(process.cwd());
6
+ console.log(`package sync-source changed=${report.changed.length}`);
7
+ return;
8
+ }
9
+ if (subcommand === "check-source") {
10
+ const report = await checkSource(process.cwd());
11
+ if (report.drift.length > 0) {
12
+ for (const item of report.drift) {
13
+ console.error(`drift: ${item}`);
14
+ }
15
+ process.exitCode = 1;
16
+ return;
17
+ }
18
+ console.log("package source OK");
19
+ return;
20
+ }
21
+ console.log(`sdlc-harness package commands:
22
+ sync-source Update package canonical assets from this source workspace
23
+ check-source Verify package canonical assets match this source workspace`);
24
+ }
@@ -0,0 +1 @@
1
+ export declare function sync(): Promise<void>;
@@ -0,0 +1,14 @@
1
+ import { runSync } from "../lib/sync-engine.js";
2
+ export async function sync() {
3
+ const report = await runSync(process.cwd());
4
+ console.log(`sync changed=${report.changed.length} skipped=${report.skipped.length} blocked=${report.blocked.length}`);
5
+ for (const skipped of report.skipped.filter((line) => line.includes("customized"))) {
6
+ console.log(`skipped: ${skipped}`);
7
+ }
8
+ for (const blocked of report.blocked) {
9
+ console.error(`blocked: ${blocked}`);
10
+ }
11
+ if (report.blocked.length > 0) {
12
+ process.exitCode = 1;
13
+ }
14
+ }
@@ -0,0 +1 @@
1
+ export declare function upgrade(): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { runUpgrade } from "../lib/upgrade.js";
2
+ export async function upgrade() {
3
+ const report = await runUpgrade(process.cwd());
4
+ for (const line of report) {
5
+ console.log(line);
6
+ }
7
+ }
@@ -0,0 +1 @@
1
+ export declare function validate(args: string[]): Promise<void>;
@@ -0,0 +1,14 @@
1
+ import { runValidator } from "../lib/validators.js";
2
+ export async function validate(args) {
3
+ const gate = args[0] ?? "validate-harness";
4
+ const report = await runValidator(process.cwd(), gate);
5
+ for (const line of report.info) {
6
+ console.log(line);
7
+ }
8
+ for (const error of report.errors) {
9
+ console.error(`error: ${error}`);
10
+ }
11
+ if (report.errors.length > 0) {
12
+ process.exitCode = 1;
13
+ }
14
+ }
@@ -0,0 +1,2 @@
1
+ export { commands } from "./commands/index.js";
2
+ export type { HarnessConfig, ManagedFile, SourceMapping } from "./lib/types.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { commands } from "./commands/index.js";
@@ -0,0 +1,5 @@
1
+ import type { HarnessConfig } from "./types.js";
2
+ export declare function defaultConfig(root: string): HarnessConfig;
3
+ export declare function readConfig(projectRoot: string): Promise<HarnessConfig>;
4
+ export declare function writeConfigIfMissing(projectRoot: string): Promise<boolean>;
5
+ export declare function normalizeConfig(value: Partial<HarnessConfig>, root?: string): HarnessConfig;
@@ -0,0 +1,52 @@
1
+ import path from "node:path";
2
+ import { CANONICAL_CORE_PACKAGE, CURRENT_SCHEMA_VERSION } from "./constants.js";
3
+ import { harnessConfigPath, harnessPath, harnessRoot } from "./harness-root.js";
4
+ import { pathExists, readText, writeTextIfChanged } from "./fs.js";
5
+ import { parseYaml, stringifyYaml } from "./yaml.js";
6
+ export function defaultConfig(root) {
7
+ return {
8
+ core: {
9
+ package: CANONICAL_CORE_PACKAGE,
10
+ schema_version: CURRENT_SCHEMA_VERSION
11
+ },
12
+ managed_files: [
13
+ { path: "AGENTS.md", strategy: "merge-block" },
14
+ { path: "Makefile", strategy: "merge-block" },
15
+ { path: harnessPath(root, "skills"), strategy: "managed" },
16
+ { path: harnessPath(root, "pjsdlc_managed", "context_templates"), strategy: "managed" },
17
+ { path: harnessPath(root, "pjsdlc_managed", "make", "sdlc-harness.mk"), strategy: "managed" },
18
+ { path: "tools", strategy: "managed" },
19
+ { path: ".github/workflows/harness.yml", strategy: "create-if-missing" }
20
+ ],
21
+ never_overwrite: ["project_context/**", "DESIGN.md", "src/**", "tests/**"]
22
+ };
23
+ }
24
+ export async function readConfig(projectRoot) {
25
+ const root = await harnessRoot(projectRoot);
26
+ const configPath = path.join(projectRoot, await harnessConfigPath(projectRoot));
27
+ if (!(await pathExists(configPath))) {
28
+ return defaultConfig(root);
29
+ }
30
+ const parsed = parseYaml(await readText(configPath));
31
+ return normalizeConfig(parsed, root);
32
+ }
33
+ export async function writeConfigIfMissing(projectRoot) {
34
+ const root = await harnessRoot(projectRoot);
35
+ const configPath = path.join(projectRoot, await harnessConfigPath(projectRoot));
36
+ if (await pathExists(configPath)) {
37
+ return false;
38
+ }
39
+ await writeTextIfChanged(configPath, stringifyYaml(defaultConfig(root)));
40
+ return true;
41
+ }
42
+ export function normalizeConfig(value, root = ".agent") {
43
+ const fallback = defaultConfig(root);
44
+ return {
45
+ core: {
46
+ package: value.core?.package ?? fallback.core.package,
47
+ schema_version: value.core?.schema_version ?? fallback.core.schema_version
48
+ },
49
+ managed_files: value.managed_files ?? fallback.managed_files,
50
+ never_overwrite: value.never_overwrite ?? fallback.never_overwrite
51
+ };
52
+ }
@@ -0,0 +1,3 @@
1
+ export declare const CANONICAL_CORE_PACKAGE = "project-tiny-context-harness";
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";
@@ -0,0 +1,3 @@
1
+ export const CANONICAL_CORE_PACKAGE = "project-tiny-context-harness";
2
+ export const CURRENT_SCHEMA_VERSION = "4";
3
+ export const CANONICAL_NPX_COMMAND = `npx --yes --package ${CANONICAL_CORE_PACKAGE}@latest sdlc-harness`;
@@ -0,0 +1,21 @@
1
+ export type ExportContextMode = "full" | "code";
2
+ export interface ExportContextOptions {
3
+ full?: boolean;
4
+ code?: boolean;
5
+ output?: string;
6
+ check?: boolean;
7
+ now?: Date;
8
+ }
9
+ export interface ExportContextReport {
10
+ mode: ExportContextMode;
11
+ outputPath: string;
12
+ outputRelativePath: string;
13
+ sourceFiles: string[];
14
+ sourceContextCount: number;
15
+ sourceCodeCount?: number;
16
+ totalLines?: number;
17
+ totalCharacters?: number;
18
+ warnings: string[];
19
+ wrote: boolean;
20
+ }
21
+ export declare function runExportContext(projectRoot: string, options?: ExportContextOptions): Promise<ExportContextReport>;