proofpress 0.0.1 → 0.1.0-alpha.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.
package/lib/setup.js ADDED
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+
6
+ const START = "<!-- proofpress:setup:start -->";
7
+ const END = "<!-- proofpress:setup:end -->";
8
+ const BADGE_ALT = "Proofpress: verifiable revision history";
9
+ const BADGE = `[![${BADGE_ALT}](https://img.shields.io/badge/Proofpress-verifiable_revision_history-5FB3C4)](https://github.com/chenmingtang830/proofpress)`;
10
+ const AGENTS = new Set(["codex", "claude", "cursor"]);
11
+
12
+ function usage() {
13
+ return [
14
+ "usage: proofpress setup [options]",
15
+ "",
16
+ "options:",
17
+ " --agent <codex|claude|cursor|all> adapter to install (default: auto)",
18
+ " --badge <markdown-file> add a visible Proofpress badge",
19
+ " --skip-git-config do not configure ledger ref syncing",
20
+ " -h, --help show this help"
21
+ ].join("\n");
22
+ }
23
+
24
+ function parseArgs(argv) {
25
+ const result = { agents: [], badge: null, skipGitConfig: false, help: false };
26
+ for (let i = 0; i < argv.length; i += 1) {
27
+ const arg = argv[i];
28
+ if (arg === "-h" || arg === "--help") {
29
+ result.help = true;
30
+ } else if (arg === "--skip-git-config") {
31
+ result.skipGitConfig = true;
32
+ } else if (arg === "--agent") {
33
+ const value = argv[++i];
34
+ if (!value) throw new Error("--agent requires a value");
35
+ result.agents.push(value);
36
+ } else if (arg.startsWith("--agent=")) {
37
+ result.agents.push(arg.slice("--agent=".length));
38
+ } else if (arg === "--badge") {
39
+ result.badge = argv[++i];
40
+ if (!result.badge) throw new Error("--badge requires a Markdown file");
41
+ } else if (arg.startsWith("--badge=")) {
42
+ result.badge = arg.slice("--badge=".length);
43
+ } else {
44
+ throw new Error(`unknown option: ${arg}`);
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+
50
+ function detectAgents(cwd) {
51
+ const detected = [];
52
+ if (fs.existsSync(path.join(cwd, ".claude"))) detected.push("claude");
53
+ if (fs.existsSync(path.join(cwd, ".cursor"))) detected.push("cursor");
54
+ if (fs.existsSync(path.join(cwd, "AGENTS.md"))) detected.push("codex");
55
+ return detected.length ? detected : ["codex"];
56
+ }
57
+
58
+ function normalizeAgents(values, cwd) {
59
+ const expanded = values.length ? values : detectAgents(cwd);
60
+ const normalized = new Set();
61
+ for (const value of expanded) {
62
+ if (value === "all") {
63
+ for (const agent of AGENTS) normalized.add(agent);
64
+ } else if (AGENTS.has(value)) {
65
+ normalized.add(value);
66
+ } else {
67
+ throw new Error(`unsupported agent "${value}"`);
68
+ }
69
+ }
70
+ return [...normalized];
71
+ }
72
+
73
+ function stripCarrierTransport(text) {
74
+ return text
75
+ .split(/\r?\n/)
76
+ .filter((line) => !/^\s*\[\/\/\]: # \((?:ob|proofpress:meta|proofpress:capsule):/.test(line))
77
+ .join("\n")
78
+ .replace(/^<!-- Append to AGENTS\.md.*?-->\s*/s, "")
79
+ .trim();
80
+ }
81
+
82
+ function npmCommands(text) {
83
+ return text
84
+ .replaceAll("python3 proofpress.py", "npx --no-install proofpress")
85
+ .replace(
86
+ "in a repository containing proofpress.py",
87
+ "in a repository configured for Proofpress"
88
+ );
89
+ }
90
+
91
+ function writeIfChanged(target, content) {
92
+ const prior = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : null;
93
+ if (prior === content) return false;
94
+ fs.mkdirSync(path.dirname(target), { recursive: true });
95
+ fs.writeFileSync(target, content);
96
+ return true;
97
+ }
98
+
99
+ function installCodex(cwd, packageRoot) {
100
+ const target = path.join(cwd, "AGENTS.md");
101
+ const source = fs.readFileSync(
102
+ path.join(packageRoot, "skills", "codex", "AGENTS-snippet.md"),
103
+ "utf8"
104
+ );
105
+ const block = `${START}\n${npmCommands(stripCarrierTransport(source))}\n${END}`;
106
+ const prior = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
107
+ const pattern = new RegExp(`${START}[\\s\\S]*?${END}`, "m");
108
+ const next = pattern.test(prior)
109
+ ? prior.replace(pattern, block)
110
+ : `${prior.trimEnd()}${prior.trim() ? "\n\n" : ""}${block}\n`;
111
+ const changed = writeIfChanged(target, next);
112
+ console.log(`${changed ? "installed" : "already current"}: Codex contract -> AGENTS.md`);
113
+ }
114
+
115
+ function installSkill(cwd, packageRoot, agent) {
116
+ const sourceDir = path.join(packageRoot, "skills", agent === "claude" ? "claude-code" : "cursor", "proofpress");
117
+ const targetDir = agent === "claude"
118
+ ? path.join(cwd, ".claude", "skills", "proofpress")
119
+ : path.join(cwd, ".agents", "skills", "proofpress");
120
+ const files = fs.readdirSync(sourceDir, { withFileTypes: true });
121
+ let changed = false;
122
+ for (const entry of files) {
123
+ if (entry.isDirectory()) {
124
+ const nestedSource = path.join(sourceDir, entry.name);
125
+ const nestedTarget = path.join(targetDir, entry.name);
126
+ fs.mkdirSync(nestedTarget, { recursive: true });
127
+ for (const nested of fs.readdirSync(nestedSource)) {
128
+ const content = fs.readFileSync(path.join(nestedSource, nested), "utf8");
129
+ changed = writeIfChanged(path.join(nestedTarget, nested), content) || changed;
130
+ }
131
+ } else {
132
+ const content = npmCommands(
133
+ entry.name.endsWith(".md")
134
+ ? stripCarrierTransport(fs.readFileSync(path.join(sourceDir, entry.name), "utf8"))
135
+ : fs.readFileSync(path.join(sourceDir, entry.name), "utf8")
136
+ );
137
+ changed = writeIfChanged(path.join(targetDir, entry.name), `${content.trimEnd()}\n`) || changed;
138
+ }
139
+ }
140
+ console.log(`${changed ? "installed" : "already current"}: ${agent} skill -> ${path.relative(cwd, targetDir)}`);
141
+ }
142
+
143
+ function addBadge(cwd, relativeFile) {
144
+ const target = path.resolve(cwd, relativeFile);
145
+ const relative = path.relative(cwd, target);
146
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
147
+ throw new Error("--badge target must be inside the repository");
148
+ }
149
+ if (!/\.md$/i.test(target)) throw new Error("--badge target must be a Markdown file");
150
+ if (!fs.existsSync(target)) throw new Error(`badge target does not exist: ${relativeFile}`);
151
+ const prior = fs.readFileSync(target, "utf8");
152
+ if (prior.includes(BADGE_ALT)) {
153
+ console.log(`already current: badge -> ${relativeFile}`);
154
+ return;
155
+ }
156
+ const lines = prior.split(/\r?\n/);
157
+ let index = 0;
158
+ while (index < lines.length && /^\s*\[\/\/\]: # \(ob:/.test(lines[index])) index += 1;
159
+ if (index < lines.length && /^#\s+/.test(lines[index])) index += 1;
160
+ lines.splice(index, 0, "", BADGE);
161
+ writeIfChanged(target, lines.join("\n"));
162
+ console.log(`installed: badge -> ${relativeFile}`);
163
+ }
164
+
165
+ function writeManifest(cwd, agents) {
166
+ const target = path.join(cwd, ".proofpress", "manifest.json");
167
+ const manifest = {
168
+ schema: 1,
169
+ runtime: "npm",
170
+ package: "proofpress",
171
+ adapters: agents.sort()
172
+ };
173
+ const changed = writeIfChanged(target, `${JSON.stringify(manifest, null, 2)}\n`);
174
+ console.log(`${changed ? "installed" : "already current"}: .proofpress/manifest.json`);
175
+ }
176
+
177
+ function runSetup(argv, options) {
178
+ const parsed = parseArgs(argv);
179
+ if (parsed.help) {
180
+ console.log(usage());
181
+ return { skipGitConfig: true };
182
+ }
183
+ const agents = normalizeAgents(parsed.agents, options.cwd);
184
+ for (const agent of agents) {
185
+ if (agent === "codex") installCodex(options.cwd, options.packageRoot);
186
+ else installSkill(options.cwd, options.packageRoot, agent);
187
+ }
188
+ writeManifest(options.cwd, agents);
189
+ if (parsed.badge) addBadge(options.cwd, parsed.badge);
190
+ console.log("Proofpress setup complete. Portable history remains opt-in per artifact.");
191
+ return { skipGitConfig: parsed.skipGitConfig };
192
+ }
193
+
194
+ module.exports = { runSetup };
package/package.json CHANGED
@@ -1,8 +1,40 @@
1
1
  {
2
2
  "name": "proofpress",
3
- "version": "0.0.1",
4
- "description": "Proofpress the version ledger for agent-produced knowledge work. Draft Review → Approve, for agents. Placeholder release; the real thing is coming.",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Verifiable revision provenance for Markdown and static HTML knowledge artifacts",
5
5
  "license": "Apache-2.0",
6
- "repository": { "type": "git", "url": "git+https://github.com/chenmingtang830/proofpress.git" },
7
- "keywords": ["agents", "version-control", "review", "semantic-diff", "knowledge-work", "ledger"]
6
+ "bin": {
7
+ "proofpress": "bin/proofpress.js"
8
+ },
9
+ "files": [
10
+ "assets/",
11
+ "bin/",
12
+ "lib/",
13
+ "skills/",
14
+ "proofpress.py",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "test": "python3 -m unittest discover -s tests -v && node --test tests/npm.test.js",
19
+ "pack:check": "npm pack --dry-run",
20
+ "prepublishOnly": "npm test && npm run pack:check"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "keywords": [
26
+ "provenance",
27
+ "markdown",
28
+ "documentation",
29
+ "ai-agents",
30
+ "audit-log"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/chenmingtang830/proofpress.git"
35
+ },
36
+ "homepage": "https://github.com/chenmingtang830/proofpress#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/chenmingtang830/proofpress/issues"
39
+ }
8
40
  }