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.
- package/LICENSE +21 -0
- package/README.md +399 -0
- package/assets/README.md +455 -0
- package/assets/README.zh-CN.md +131 -0
- package/assets/agents/.gitkeep +1 -0
- package/assets/agents/AGENTS_CORE.md +58 -0
- package/assets/context_templates/architecture.md +31 -0
- package/assets/context_templates/area.md +31 -0
- package/assets/context_templates/context.toml +27 -0
- package/assets/context_templates/deployment.md +35 -0
- package/assets/context_templates/global.md +53 -0
- package/assets/context_templates/verification.md +31 -0
- package/assets/github/.gitkeep +1 -0
- package/assets/github/harness.yml +37 -0
- package/assets/make/.gitkeep +1 -0
- package/assets/make/sdlc-harness.mk +39 -0
- package/assets/skills/context_development_engineer/SKILL.md +86 -0
- package/assets/skills/context_full_project_export/SKILL.md +55 -0
- package/assets/skills/context_product_plan/SKILL.md +85 -0
- package/assets/skills/context_uiux_design/SKILL.md +110 -0
- package/assets/tools/validate_context.py +276 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +12 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +16 -0
- package/dist/commands/export-context.d.ts +1 -0
- package/dist/commands/export-context.js +149 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +33 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +108 -0
- package/dist/commands/package-source.d.ts +1 -0
- package/dist/commands/package-source.js +24 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +14 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/commands/upgrade.js +7 -0
- package/dist/commands/validate.d.ts +1 -0
- package/dist/commands/validate.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lib/config.d.ts +5 -0
- package/dist/lib/config.js +52 -0
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.js +3 -0
- package/dist/lib/context-export.d.ts +21 -0
- package/dist/lib/context-export.js +845 -0
- package/dist/lib/context-manifest.d.ts +3 -0
- package/dist/lib/context-manifest.js +103 -0
- package/dist/lib/context-templates.d.ts +5 -0
- package/dist/lib/context-templates.js +204 -0
- package/dist/lib/design-md.d.ts +2 -0
- package/dist/lib/design-md.js +132 -0
- package/dist/lib/doctor.d.ts +6 -0
- package/dist/lib/doctor.js +41 -0
- package/dist/lib/fs.d.ts +8 -0
- package/dist/lib/fs.js +56 -0
- package/dist/lib/harness-root.d.ts +9 -0
- package/dist/lib/harness-root.js +50 -0
- package/dist/lib/init.d.ts +5 -0
- package/dist/lib/init.js +65 -0
- package/dist/lib/managed-file.d.ts +19 -0
- package/dist/lib/managed-file.js +21 -0
- package/dist/lib/migrations.d.ts +11 -0
- package/dist/lib/migrations.js +180 -0
- package/dist/lib/package-json-config.d.ts +2 -0
- package/dist/lib/package-json-config.js +37 -0
- package/dist/lib/package-source.d.ts +8 -0
- package/dist/lib/package-source.js +124 -0
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +11 -0
- package/dist/lib/schema-guard.d.ts +3 -0
- package/dist/lib/schema-guard.js +28 -0
- package/dist/lib/sync-engine.d.ts +7 -0
- package/dist/lib/sync-engine.js +350 -0
- package/dist/lib/types.d.ts +21 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/upgrade.d.ts +1 -0
- package/dist/lib/upgrade.js +21 -0
- package/dist/lib/validators.d.ts +5 -0
- package/dist/lib/validators.js +459 -0
- package/dist/lib/yaml.d.ts +2 -0
- package/dist/lib/yaml.js +7 -0
- package/migrations/README.md +3 -0
- package/package.json +68 -0
- 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 @@
|
|
|
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
|
+
}
|
package/dist/index.d.ts
ADDED
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,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>;
|