@aacombarro89/praxis 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.
- package/dist/anchors.d.ts +36 -0
- package/dist/anchors.js +197 -0
- package/dist/anchors.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -0
- package/dist/emit.d.ts +51 -0
- package/dist/emit.js +253 -0
- package/dist/emit.js.map +1 -0
- package/dist/external.d.ts +61 -0
- package/dist/external.js +155 -0
- package/dist/external.js.map +1 -0
- package/dist/init.d.ts +45 -0
- package/dist/init.js +123 -0
- package/dist/init.js.map +1 -0
- package/dist/manifest.d.ts +36 -0
- package/dist/manifest.js +63 -0
- package/dist/manifest.js.map +1 -0
- package/dist/merge-json.d.ts +83 -0
- package/dist/merge-json.js +207 -0
- package/dist/merge-json.js.map +1 -0
- package/dist/merge.d.ts +53 -0
- package/dist/merge.js +89 -0
- package/dist/merge.js.map +1 -0
- package/dist/packages.d.ts +47 -0
- package/dist/packages.js +121 -0
- package/dist/packages.js.map +1 -0
- package/dist/permissions.d.ts +54 -0
- package/dist/permissions.js +83 -0
- package/dist/permissions.js.map +1 -0
- package/dist/plugins.d.ts +21 -0
- package/dist/plugins.js +88 -0
- package/dist/plugins.js.map +1 -0
- package/dist/program.d.ts +2 -0
- package/dist/program.js +201 -0
- package/dist/program.js.map +1 -0
- package/dist/sync.d.ts +37 -0
- package/dist/sync.js +45 -0
- package/dist/sync.js.map +1 -0
- package/package.json +40 -0
- package/packages/external/drawio/package.yaml +7 -0
- package/packages/external/drawio/plugins.yaml +13 -0
- package/packages/external/ponytail/package.yaml +7 -0
- package/packages/external/ponytail/plugins.yaml +13 -0
- package/packages/layer1/instruction-upkeep/commands/audit.md +41 -0
- package/packages/layer1/instruction-upkeep/package.yaml +8 -0
- package/packages/layer1/instruction-upkeep/rules.md +21 -0
- package/packages/layer1/karpathy-claude/package.yaml +5 -0
- package/packages/layer1/karpathy-claude/rules.md +35 -0
- package/packages/layer1/safe-permissions/package.yaml +5 -0
- package/packages/layer1/safe-permissions/permissions.yaml +26 -0
- package/packages/layer1/session-handoff/commands/handoff.md +185 -0
- package/packages/layer1/session-handoff/package.yaml +10 -0
- package/packages/layer1/session-handoff/rules.md +20 -0
- package/packages/layer1/upkeep/commands/upkeep.md +31 -0
- package/packages/layer1/upkeep/package.yaml +10 -0
- package/packages/layer1/upkeep/rules.md +12 -0
- package/packages/layer1/wiki-memory/commands/wiki.md +103 -0
- package/packages/layer1/wiki-memory/package.yaml +10 -0
- package/packages/layer1/wiki-memory/rules.md +49 -0
- package/packages/layer2/node-recipes/package.yaml +10 -0
- package/packages/layer2/node-recipes/rules.md +33 -0
- package/packages/layer2/node-testing/package.yaml +10 -0
- package/packages/layer2/node-testing/rules.md +33 -0
- package/packages/layer2/python-backend-recipes/package.yaml +10 -0
- package/packages/layer2/python-backend-recipes/rules.md +37 -0
- package/packages/layer2/python-testing/package.yaml +10 -0
- package/packages/layer2/python-testing/rules.md +33 -0
- package/packages/layer2/react-components/package.yaml +10 -0
- package/packages/layer2/react-components/rules.md +40 -0
- package/packages/layer2/react-testing/package.yaml +10 -0
- package/packages/layer2/react-testing/rules.md +35 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic knowledge-anchor checks for D26 Tier 2.
|
|
4
|
+
*
|
|
5
|
+
* Anchors live in markdown frontmatter under `praxisAnchors`. They are not a
|
|
6
|
+
* truth check for prose; they only prove that cited repo reality still resolves.
|
|
7
|
+
*/
|
|
8
|
+
declare const anchorSchema: z.ZodObject<{
|
|
9
|
+
type: z.ZodEnum<{
|
|
10
|
+
path: "path";
|
|
11
|
+
command: "command";
|
|
12
|
+
section: "section";
|
|
13
|
+
}>;
|
|
14
|
+
target: z.ZodString;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type Anchor = z.infer<typeof anchorSchema>;
|
|
17
|
+
export interface AnchorDiagnostic {
|
|
18
|
+
file: string;
|
|
19
|
+
type?: Anchor["type"];
|
|
20
|
+
target?: string;
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AnchorCheckReport {
|
|
24
|
+
filesScanned: number;
|
|
25
|
+
anchorsChecked: number;
|
|
26
|
+
diagnostics: AnchorDiagnostic[];
|
|
27
|
+
ok: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface AnchorFile {
|
|
30
|
+
path: string;
|
|
31
|
+
anchors: Anchor[];
|
|
32
|
+
diagnostics: AnchorDiagnostic[];
|
|
33
|
+
}
|
|
34
|
+
export declare function checkAnchors(cwd: string): AnchorCheckReport;
|
|
35
|
+
export declare function parseAnchorsFromMarkdown(file: string, text: string): AnchorFile;
|
|
36
|
+
export {};
|
package/dist/anchors.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, relative, sep } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
/**
|
|
6
|
+
* Deterministic knowledge-anchor checks for D26 Tier 2.
|
|
7
|
+
*
|
|
8
|
+
* Anchors live in markdown frontmatter under `praxisAnchors`. They are not a
|
|
9
|
+
* truth check for prose; they only prove that cited repo reality still resolves.
|
|
10
|
+
*/
|
|
11
|
+
const anchorSchema = z.strictObject({
|
|
12
|
+
type: z.enum(["path", "command", "section"]),
|
|
13
|
+
target: z.string().min(1),
|
|
14
|
+
});
|
|
15
|
+
const frontmatterSchema = z.object({
|
|
16
|
+
praxisAnchors: z.array(anchorSchema).optional(),
|
|
17
|
+
});
|
|
18
|
+
export function checkAnchors(cwd) {
|
|
19
|
+
const files = listKnowledgeMarkdownFiles(cwd);
|
|
20
|
+
const diagnostics = [];
|
|
21
|
+
let anchorsChecked = 0;
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const parsed = parseAnchorFile(cwd, file);
|
|
24
|
+
diagnostics.push(...parsed.diagnostics);
|
|
25
|
+
for (const anchor of parsed.anchors) {
|
|
26
|
+
anchorsChecked += 1;
|
|
27
|
+
const diagnostic = resolveAnchor(cwd, parsed.path, anchor);
|
|
28
|
+
if (diagnostic)
|
|
29
|
+
diagnostics.push(diagnostic);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
filesScanned: files.length,
|
|
34
|
+
anchorsChecked,
|
|
35
|
+
diagnostics,
|
|
36
|
+
ok: diagnostics.length === 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function parseAnchorsFromMarkdown(file, text) {
|
|
40
|
+
const frontmatter = readFrontmatter(text);
|
|
41
|
+
if (frontmatter === undefined)
|
|
42
|
+
return { path: file, anchors: [], diagnostics: [] };
|
|
43
|
+
let raw;
|
|
44
|
+
try {
|
|
45
|
+
raw = parseYaml(frontmatter);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return {
|
|
49
|
+
path: file,
|
|
50
|
+
anchors: [],
|
|
51
|
+
diagnostics: [{ file, message: `frontmatter is not valid YAML: ${err.message}` }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const result = frontmatterSchema.safeParse(raw ?? {});
|
|
55
|
+
if (!result.success) {
|
|
56
|
+
return {
|
|
57
|
+
path: file,
|
|
58
|
+
anchors: [],
|
|
59
|
+
diagnostics: result.error.issues.map((issue) => ({
|
|
60
|
+
file,
|
|
61
|
+
message: `invalid praxisAnchors metadata at ${issue.path.map(String).join(".")}: ${issue.message}`,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { path: file, anchors: result.data.praxisAnchors ?? [], diagnostics: [] };
|
|
66
|
+
}
|
|
67
|
+
function parseAnchorFile(cwd, file) {
|
|
68
|
+
return parseAnchorsFromMarkdown(file, readFileSync(join(cwd, file), "utf8"));
|
|
69
|
+
}
|
|
70
|
+
function resolveAnchor(cwd, file, anchor) {
|
|
71
|
+
if (anchor.type === "path")
|
|
72
|
+
return resolvePathAnchor(cwd, file, anchor);
|
|
73
|
+
if (anchor.type === "command")
|
|
74
|
+
return resolveCommandAnchor(cwd, file, anchor);
|
|
75
|
+
return resolveSectionAnchor(cwd, file, anchor);
|
|
76
|
+
}
|
|
77
|
+
function resolvePathAnchor(cwd, file, anchor) {
|
|
78
|
+
const target = safeRepoTarget(cwd, anchor.target);
|
|
79
|
+
if (target === undefined) {
|
|
80
|
+
return diagnostic(file, anchor, "path anchor must be a repo-relative path without '..'");
|
|
81
|
+
}
|
|
82
|
+
if (!existsSync(target.abs)) {
|
|
83
|
+
return diagnostic(file, anchor, `path does not exist: ${anchor.target}`);
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
function resolveCommandAnchor(cwd, file, anchor) {
|
|
88
|
+
const script = npmScriptName(anchor.target);
|
|
89
|
+
if (script === undefined) {
|
|
90
|
+
return diagnostic(file, anchor, `unsupported command anchor: ${anchor.target}`);
|
|
91
|
+
}
|
|
92
|
+
let pkg;
|
|
93
|
+
try {
|
|
94
|
+
pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return diagnostic(file, anchor, "package.json is missing or invalid");
|
|
98
|
+
}
|
|
99
|
+
const scripts = z.object({ scripts: z.record(z.string(), z.string()).optional() }).safeParse(pkg);
|
|
100
|
+
if (!scripts.success || scripts.data.scripts?.[script] === undefined) {
|
|
101
|
+
return diagnostic(file, anchor, `package.json has no "${script}" script`);
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
function resolveSectionAnchor(cwd, file, anchor) {
|
|
106
|
+
const [path, hash, extra] = anchor.target.split("#");
|
|
107
|
+
if (!path || !hash || extra !== undefined) {
|
|
108
|
+
return diagnostic(file, anchor, "section anchor must look like docs/file.md#heading-slug");
|
|
109
|
+
}
|
|
110
|
+
const target = safeRepoTarget(cwd, path);
|
|
111
|
+
if (target === undefined) {
|
|
112
|
+
return diagnostic(file, anchor, "section anchor path must be repo-relative and without '..'");
|
|
113
|
+
}
|
|
114
|
+
if (!existsSync(target.abs)) {
|
|
115
|
+
return diagnostic(file, anchor, `section file does not exist: ${path}`);
|
|
116
|
+
}
|
|
117
|
+
const headings = markdownHeadingSlugs(readFileSync(target.abs, "utf8"));
|
|
118
|
+
if (!headings.has(hash)) {
|
|
119
|
+
return diagnostic(file, anchor, `section does not exist: ${anchor.target}`);
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
function diagnostic(file, anchor, message) {
|
|
124
|
+
return { file, type: anchor.type, target: anchor.target, message };
|
|
125
|
+
}
|
|
126
|
+
function npmScriptName(command) {
|
|
127
|
+
const run = command.match(/^npm run ([A-Za-z0-9:_-]+)$/);
|
|
128
|
+
if (run?.[1])
|
|
129
|
+
return run[1];
|
|
130
|
+
const direct = command.match(/^npm ([A-Za-z0-9:_-]+)$/);
|
|
131
|
+
if (direct?.[1])
|
|
132
|
+
return direct[1];
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
function markdownHeadingSlugs(text) {
|
|
136
|
+
const slugs = new Set();
|
|
137
|
+
for (const line of text.split(/\r?\n/)) {
|
|
138
|
+
const match = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
139
|
+
if (match?.[2])
|
|
140
|
+
slugs.add(slugifyHeading(match[2]));
|
|
141
|
+
}
|
|
142
|
+
return slugs;
|
|
143
|
+
}
|
|
144
|
+
function slugifyHeading(heading) {
|
|
145
|
+
return heading
|
|
146
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
147
|
+
.trim()
|
|
148
|
+
.toLowerCase()
|
|
149
|
+
.replace(/[^\p{Letter}\p{Number}\s-]/gu, "")
|
|
150
|
+
.replace(/\s+/g, "-")
|
|
151
|
+
.replace(/-+/g, "-");
|
|
152
|
+
}
|
|
153
|
+
function safeRepoTarget(cwd, target) {
|
|
154
|
+
if (isAbsolute(target))
|
|
155
|
+
return undefined;
|
|
156
|
+
const parts = target.split(/[\\/]+/);
|
|
157
|
+
if (parts.includes(".."))
|
|
158
|
+
return undefined;
|
|
159
|
+
const abs = join(cwd, ...parts);
|
|
160
|
+
const rel = relative(cwd, abs);
|
|
161
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
|
|
162
|
+
return undefined;
|
|
163
|
+
return { abs, rel: rel.split(sep).join("/") };
|
|
164
|
+
}
|
|
165
|
+
function readFrontmatter(text) {
|
|
166
|
+
if (!text.startsWith("---\n"))
|
|
167
|
+
return undefined;
|
|
168
|
+
const end = text.indexOf("\n---", 4);
|
|
169
|
+
if (end === -1)
|
|
170
|
+
return undefined;
|
|
171
|
+
const after = text[end + 4];
|
|
172
|
+
if (after !== "\n" && after !== "\r" && after !== undefined)
|
|
173
|
+
return undefined;
|
|
174
|
+
return text.slice(4, end);
|
|
175
|
+
}
|
|
176
|
+
function listKnowledgeMarkdownFiles(cwd) {
|
|
177
|
+
return [
|
|
178
|
+
...["CLAUDE.md", "MEMORY.md", "USER.md"].filter((file) => existsSync(join(cwd, file))),
|
|
179
|
+
...listMarkdownUnder(cwd, "docs/wiki"),
|
|
180
|
+
...listMarkdownUnder(cwd, "docs/proposals"),
|
|
181
|
+
].sort();
|
|
182
|
+
}
|
|
183
|
+
function listMarkdownUnder(cwd, root) {
|
|
184
|
+
const absRoot = join(cwd, root);
|
|
185
|
+
if (!existsSync(absRoot))
|
|
186
|
+
return [];
|
|
187
|
+
const files = [];
|
|
188
|
+
for (const entry of readdirSync(absRoot, { withFileTypes: true })) {
|
|
189
|
+
const rel = `${root}/${entry.name}`;
|
|
190
|
+
if (entry.isDirectory())
|
|
191
|
+
files.push(...listMarkdownUnder(cwd, rel));
|
|
192
|
+
if (entry.isFile() && entry.name.endsWith(".md"))
|
|
193
|
+
files.push(rel);
|
|
194
|
+
}
|
|
195
|
+
return files;
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=anchors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchors.js","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,YAAY,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
package/dist/emit.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Manifest, Target } from "./manifest.js";
|
|
2
|
+
import { type PluginsDesired, type RuleSet } from "./merge-json.js";
|
|
3
|
+
/** The neutral methodology source for a content package, or undefined if the
|
|
4
|
+
* package has no rules.md (e.g. skills, Layer 2 recipes handled by other phases). */
|
|
5
|
+
export declare function loadPackageSource(pkg: string): string | undefined;
|
|
6
|
+
/** A package's slash commands: one file per command, named for the command it
|
|
7
|
+
* becomes (the filename, sans extension, is what the user types after `/`). */
|
|
8
|
+
export interface CommandSource {
|
|
9
|
+
name: string;
|
|
10
|
+
content: string;
|
|
11
|
+
}
|
|
12
|
+
/** A content package's commands, or [] if it has none. Scans
|
|
13
|
+
* packages/<layer>/<pkg>/commands/*.md — mirrors loadPackageSource. */
|
|
14
|
+
export declare function loadCommandSources(pkg: string): CommandSource[];
|
|
15
|
+
/** A planned write. "owned" files are placed/replaced wholesale; "block" files
|
|
16
|
+
* have their managed blocks reconciled into existing text; "settings" carries
|
|
17
|
+
* whichever of permissions/plugins this target emits — always exactly one op
|
|
18
|
+
* per target per settings path, so the two concerns compose into a single
|
|
19
|
+
* write instead of racing on the same file. */
|
|
20
|
+
export type EmitOp = {
|
|
21
|
+
kind: "owned";
|
|
22
|
+
target: Target;
|
|
23
|
+
path: string;
|
|
24
|
+
content: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "block";
|
|
27
|
+
target: Target;
|
|
28
|
+
path: string;
|
|
29
|
+
blocks: Record<string, string>;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "settings";
|
|
32
|
+
target: Target;
|
|
33
|
+
path: string;
|
|
34
|
+
rules?: RuleSet;
|
|
35
|
+
plugins?: PluginsDesired;
|
|
36
|
+
};
|
|
37
|
+
/** Build the emit plan from a manifest: the file operations needed to install
|
|
38
|
+
* the selected methodology for each target. */
|
|
39
|
+
export declare function planEmit(manifest: Manifest): EmitOp[];
|
|
40
|
+
export interface ApplyResult {
|
|
41
|
+
text: string;
|
|
42
|
+
changed: boolean;
|
|
43
|
+
/** Conflicts (user-edited managed content): block ids for block delivery,
|
|
44
|
+
* `permissions.<bucket>` / `plugins.marketplaces` / `plugins.enable` for the
|
|
45
|
+
* JSON merge; never for owned. */
|
|
46
|
+
conflicts: string[];
|
|
47
|
+
}
|
|
48
|
+
/** Apply one emit op against the destination file's existing text (pure; no I/O).
|
|
49
|
+
* Owned files are replaced wholesale; block files are reconciled; settings ops
|
|
50
|
+
* reconcile permissions and/or plugins in one pass over the same JSON file. */
|
|
51
|
+
export declare function applyOp(op: EmitOp, existing?: string): ApplyResult;
|
package/dist/emit.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { reconcile } from "./merge.js";
|
|
5
|
+
import { BUCKETS, reconcileSettings, } from "./merge-json.js";
|
|
6
|
+
import { resolvePackages } from "./packages.js";
|
|
7
|
+
import { loadPolicy } from "./permissions.js";
|
|
8
|
+
import { loadPluginsBlock } from "./plugins.js";
|
|
9
|
+
/**
|
|
10
|
+
* Layer 1 emit (CLAUDE.md non-negotiable "tool-neutral by construction";
|
|
11
|
+
* docs/wiki/merge-engine.md / docs/wiki/emitters.md, decisions D7/D13). The methodology source is **singular
|
|
12
|
+
* and tool-neutral**; emitters *translate* it per target — they never fork the
|
|
13
|
+
* content.
|
|
14
|
+
*
|
|
15
|
+
* Two delivery shapes, per target:
|
|
16
|
+
* - "owned": Praxis owns whole files (place/replace, no merge). Claude Code
|
|
17
|
+
* methodology ships as `.claude/rules/praxis-*.md`, auto-loaded every
|
|
18
|
+
* session; Praxis never edits the user's CLAUDE.md (D13).
|
|
19
|
+
* - "block": content is inlined into a flat instruction file via a managed
|
|
20
|
+
* block (merge engine). Used for AGENTS.md, which has no include standard.
|
|
21
|
+
*
|
|
22
|
+
* Package layout (D16): packages/<layer>/<pkg>/rules.md. `../packages/` resolves
|
|
23
|
+
* the same from src/ (dev/vitest) and dist/ (built/installed) — both one level
|
|
24
|
+
* under repo root.
|
|
25
|
+
*/
|
|
26
|
+
// Root of the per-layer package tree. Resolves identically from src/ and dist/.
|
|
27
|
+
const PACKAGES_DIR = new URL("../packages/", import.meta.url);
|
|
28
|
+
// Where each target's methodology lives, and how it is delivered.
|
|
29
|
+
const TARGET_DELIVERY = {
|
|
30
|
+
"claude-code": "owned",
|
|
31
|
+
"agents-md": "block",
|
|
32
|
+
};
|
|
33
|
+
// Block-delivered targets each own a managed block in one flat file.
|
|
34
|
+
const BLOCK_FILE = {
|
|
35
|
+
"agents-md": "AGENTS.md",
|
|
36
|
+
};
|
|
37
|
+
/** The neutral methodology source for a content package, or undefined if the
|
|
38
|
+
* package has no rules.md (e.g. skills, Layer 2 recipes handled by other phases). */
|
|
39
|
+
export function loadPackageSource(pkg) {
|
|
40
|
+
const root = fileURLToPath(PACKAGES_DIR);
|
|
41
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
42
|
+
if (!entry.isDirectory())
|
|
43
|
+
continue;
|
|
44
|
+
const candidate = join(root, entry.name, pkg, "rules.md");
|
|
45
|
+
if (existsSync(candidate))
|
|
46
|
+
return readFileSync(candidate, "utf8").trimEnd();
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
/** Translate neutral source for a specific target. The seam for per-tool
|
|
51
|
+
* framing; Layer 1 prose is identical across tools today. */
|
|
52
|
+
function renderForTarget(source, _target) {
|
|
53
|
+
return source;
|
|
54
|
+
}
|
|
55
|
+
/** A content package's commands, or [] if it has none. Scans
|
|
56
|
+
* packages/<layer>/<pkg>/commands/*.md — mirrors loadPackageSource. */
|
|
57
|
+
export function loadCommandSources(pkg) {
|
|
58
|
+
const root = fileURLToPath(PACKAGES_DIR);
|
|
59
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
60
|
+
if (!entry.isDirectory())
|
|
61
|
+
continue;
|
|
62
|
+
const dir = join(root, entry.name, pkg, "commands");
|
|
63
|
+
if (!existsSync(dir))
|
|
64
|
+
continue;
|
|
65
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
66
|
+
.filter((f) => f.isFile() && f.name.endsWith(".md"))
|
|
67
|
+
.map((f) => ({
|
|
68
|
+
name: f.name.slice(0, -3),
|
|
69
|
+
content: readFileSync(join(dir, f.name), "utf8").trimEnd(),
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
/** Targets with a slash-command model, and where commands land. A target absent
|
|
75
|
+
* here has no command model → Praxis emits nothing for it (a no-op, never a
|
|
76
|
+
* fabricated file). Claude Code first (D19's "no permission model → no-op",
|
|
77
|
+
* same shape for commands). */
|
|
78
|
+
const COMMAND_DIR = {
|
|
79
|
+
"claude-code": ".claude/commands",
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Claude Code permission emitter (docs/wiki/emitters.md — *quarantined per-tool*;
|
|
83
|
+
* D19). Maps each neutral capability to concrete Claude Code rule strings.
|
|
84
|
+
* Grammar verified against code.claude.com/docs/en/settings: `Tool(pattern)`,
|
|
85
|
+
* gitignore-style path globs, `:*` command-prefix wildcard, `deny` overrides
|
|
86
|
+
* `allow`. The bucket (allow/ask/deny) is chosen by the policy, not here — this
|
|
87
|
+
* table only knows the rule strings a capability denotes.
|
|
88
|
+
*
|
|
89
|
+
* `pipe-network-to-shell` (e.g. `curl … | sh`) is deliberately NOT a capability:
|
|
90
|
+
* prefix-matched rules cannot reliably catch a pipe, and a denylist that looks
|
|
91
|
+
* protective but isn't is worse than none (the "denylist delusion"). Excluded by
|
|
92
|
+
* the content bar (durable + verified) rather than emitted as a false guarantee.
|
|
93
|
+
*/
|
|
94
|
+
const CLAUDE_CODE_RULES = {
|
|
95
|
+
"read-repo": ["Read(./**)"],
|
|
96
|
+
"edit-repo": ["Edit(./**)", "Write(./**)"],
|
|
97
|
+
"run-dev-scripts": [
|
|
98
|
+
"Bash(npm run test:*)",
|
|
99
|
+
"Bash(npm run lint:*)",
|
|
100
|
+
"Bash(npm run typecheck:*)",
|
|
101
|
+
"Bash(npm run build:*)",
|
|
102
|
+
],
|
|
103
|
+
"read-only-git": ["Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)"],
|
|
104
|
+
"git-commit": ["Bash(git commit:*)"],
|
|
105
|
+
"git-push": ["Bash(git push:*)"],
|
|
106
|
+
"install-deps": ["Bash(npm install:*)", "Bash(npm i:*)", "Bash(npm ci:*)"],
|
|
107
|
+
"destructive-delete": ["Bash(rm -rf:*)"],
|
|
108
|
+
"force-push": ["Bash(git push --force:*)", "Bash(git push -f:*)"],
|
|
109
|
+
"read-secrets": ["Read(./.env)", "Read(./.env.*)", "Read(./**/*.pem)", "Read(./**/credentials*)"],
|
|
110
|
+
"global-install": ["Bash(npm install -g:*)", "Bash(npm i -g:*)", "Bash(pnpm add -g:*)"],
|
|
111
|
+
};
|
|
112
|
+
/** Render a neutral policy into Claude Code's allow/ask/deny rule strings. */
|
|
113
|
+
function toClaudeRuleSet(policy) {
|
|
114
|
+
const out = { allow: [], ask: [], deny: [] };
|
|
115
|
+
for (const bucket of BUCKETS) {
|
|
116
|
+
for (const cap of policy[bucket]) {
|
|
117
|
+
for (const rule of CLAUDE_CODE_RULES[cap]) {
|
|
118
|
+
if (!out[bucket].includes(rule))
|
|
119
|
+
out[bucket].push(rule);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
/** Targets with a permission model, and where the rendered policy lands. A
|
|
126
|
+
* target absent here has no permission model → Praxis emits nothing for it
|
|
127
|
+
* (a no-op, never a fabricated file). Claude Code first (D19). Shares a path
|
|
128
|
+
* with PLUGIN_EMITTERS below — both concerns compose into one settings op per
|
|
129
|
+
* target (see the single `kind: "settings"` EmitOp). */
|
|
130
|
+
const PERMISSION_EMITTERS = {
|
|
131
|
+
"claude-code": { path: ".claude/settings.json", render: toClaudeRuleSet },
|
|
132
|
+
};
|
|
133
|
+
/** Targets with a Claude Code plugin-marketplace model, and where the
|
|
134
|
+
* declaration lands. A target absent here has no plugin model (e.g.
|
|
135
|
+
* agents-md, a flat-file convention with no marketplace concept) → Praxis
|
|
136
|
+
* emits nothing for it. Claude Code only, today. */
|
|
137
|
+
const PLUGIN_EMITTERS = {
|
|
138
|
+
"claude-code": { path: ".claude/settings.json" },
|
|
139
|
+
};
|
|
140
|
+
/** Build the emit plan from a manifest: the file operations needed to install
|
|
141
|
+
* the selected methodology for each target. */
|
|
142
|
+
export function planEmit(manifest) {
|
|
143
|
+
// Validate the package set and resolve requires/conflicts up front (D20/D21):
|
|
144
|
+
// an unknown package or unmet dependency fails loudly here, not silently later.
|
|
145
|
+
const packages = resolvePackages(manifest.packages, manifest.stacks ?? []);
|
|
146
|
+
const rulesPackages = packages.filter((p) => p.provides.includes("rules"));
|
|
147
|
+
const permissionPackages = packages.filter((p) => p.provides.includes("permissions"));
|
|
148
|
+
const commandPackages = packages.filter((p) => p.provides.includes("commands"));
|
|
149
|
+
const pluginPackages = packages.filter((p) => p.provides.includes("plugins"));
|
|
150
|
+
const ops = [];
|
|
151
|
+
for (const target of manifest.targets) {
|
|
152
|
+
// Prose rules — delivery (owned vs block) is the target's; `provides` decides
|
|
153
|
+
// which packages contribute. `loadPackageSource` is guaranteed by `provides`.
|
|
154
|
+
const sources = rulesPackages.map((p) => ({ pkg: p.name, source: loadPackageSource(p.name) ?? "" }));
|
|
155
|
+
if (sources.length > 0) {
|
|
156
|
+
if (TARGET_DELIVERY[target] === "owned") {
|
|
157
|
+
for (const { pkg, source } of sources) {
|
|
158
|
+
ops.push({
|
|
159
|
+
kind: "owned",
|
|
160
|
+
target,
|
|
161
|
+
path: `.claude/rules/praxis-${pkg}.md`,
|
|
162
|
+
content: `${renderForTarget(source, target)}\n`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const blocks = {};
|
|
168
|
+
for (const { pkg, source } of sources)
|
|
169
|
+
blocks[pkg] = renderForTarget(source, target);
|
|
170
|
+
ops.push({ kind: "block", target, path: BLOCK_FILE[target], blocks });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Structured permission policy (second artifact kind, docs/wiki/packages-and-emit.md "provides")
|
|
174
|
+
// and the plugin-marketplace declaration (fourth artifact kind) both land in
|
|
175
|
+
// `.claude/settings.json`. Compute each independently, but emit at most ONE
|
|
176
|
+
// settings op per target so the two concerns compose into a single write
|
|
177
|
+
// instead of two ops racing on the same path (the same-file compose bug).
|
|
178
|
+
const permEmitter = PERMISSION_EMITTERS[target];
|
|
179
|
+
let rules;
|
|
180
|
+
if (permEmitter && permissionPackages.length > 0) {
|
|
181
|
+
rules = { allow: [], ask: [], deny: [] };
|
|
182
|
+
for (const p of permissionPackages) {
|
|
183
|
+
const policy = loadPolicy(p.name);
|
|
184
|
+
if (!policy)
|
|
185
|
+
continue;
|
|
186
|
+
const r = permEmitter.render(policy);
|
|
187
|
+
for (const bucket of BUCKETS) {
|
|
188
|
+
for (const rule of r[bucket])
|
|
189
|
+
if (!rules[bucket].includes(rule))
|
|
190
|
+
rules[bucket].push(rule);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const pluginEmitter = PLUGIN_EMITTERS[target];
|
|
195
|
+
let plugins;
|
|
196
|
+
if (pluginEmitter && pluginPackages.length > 0) {
|
|
197
|
+
const marketplaces = [];
|
|
198
|
+
const enable = [];
|
|
199
|
+
for (const p of pluginPackages) {
|
|
200
|
+
const block = loadPluginsBlock(p.name);
|
|
201
|
+
if (!block || !block.targets.includes(target))
|
|
202
|
+
continue;
|
|
203
|
+
if (!marketplaces.some((m) => m.name === block.marketplace.name)) {
|
|
204
|
+
marketplaces.push(block.marketplace);
|
|
205
|
+
}
|
|
206
|
+
for (const entry of block.enable)
|
|
207
|
+
if (!enable.includes(entry))
|
|
208
|
+
enable.push(entry);
|
|
209
|
+
}
|
|
210
|
+
if (marketplaces.length > 0 || enable.length > 0)
|
|
211
|
+
plugins = { marketplaces, enable };
|
|
212
|
+
}
|
|
213
|
+
const settingsPath = permEmitter?.path ?? pluginEmitter?.path;
|
|
214
|
+
if (settingsPath && (rules || plugins)) {
|
|
215
|
+
ops.push({ kind: "settings", target, path: settingsPath, rules, plugins });
|
|
216
|
+
}
|
|
217
|
+
// Slash commands (third artifact kind, docs/wiki/packages-and-emit.md "provides"). One owned file per
|
|
218
|
+
// command, prefixed so it's identifiable as Praxis-managed; targets with no
|
|
219
|
+
// command model are a no-op.
|
|
220
|
+
const commandDir = COMMAND_DIR[target];
|
|
221
|
+
if (commandDir) {
|
|
222
|
+
for (const pkg of commandPackages) {
|
|
223
|
+
for (const { name, content } of loadCommandSources(pkg.name)) {
|
|
224
|
+
ops.push({
|
|
225
|
+
kind: "owned",
|
|
226
|
+
target,
|
|
227
|
+
path: `${commandDir}/praxis-${name}.md`,
|
|
228
|
+
content: `${content}\n`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return ops;
|
|
235
|
+
}
|
|
236
|
+
/** Apply one emit op against the destination file's existing text (pure; no I/O).
|
|
237
|
+
* Owned files are replaced wholesale; block files are reconciled; settings ops
|
|
238
|
+
* reconcile permissions and/or plugins in one pass over the same JSON file. */
|
|
239
|
+
export function applyOp(op, existing = "") {
|
|
240
|
+
if (op.kind === "owned") {
|
|
241
|
+
return { text: op.content, changed: existing !== op.content, conflicts: [] };
|
|
242
|
+
}
|
|
243
|
+
if (op.kind === "settings") {
|
|
244
|
+
const desired = {};
|
|
245
|
+
if (op.rules)
|
|
246
|
+
desired.permissions = op.rules;
|
|
247
|
+
if (op.plugins)
|
|
248
|
+
desired.plugins = op.plugins;
|
|
249
|
+
return reconcileSettings(existing, desired);
|
|
250
|
+
}
|
|
251
|
+
return reconcile(existing, op.blocks);
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=emit.js.map
|
package/dist/emit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Manifest, Target } from "./manifest.js";
|
|
3
|
+
declare const lockEntrySchema: z.ZodObject<{
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
spec: z.ZodArray<z.ZodString>;
|
|
6
|
+
targets: z.ZodArray<z.ZodString>;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
declare const lockSchema: z.ZodObject<{
|
|
9
|
+
version: z.ZodLiteral<1>;
|
|
10
|
+
installed: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
11
|
+
name: z.ZodString;
|
|
12
|
+
spec: z.ZodArray<z.ZodString>;
|
|
13
|
+
targets: z.ZodArray<z.ZodString>;
|
|
14
|
+
}, z.core.$strict>>>;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type LockEntry = z.infer<typeof lockEntrySchema>;
|
|
17
|
+
export type Lock = z.infer<typeof lockSchema>;
|
|
18
|
+
export declare const EMPTY_LOCK: Lock;
|
|
19
|
+
export type ExternalActionKind = "install" | "reinstall" | "skip" | "orphan";
|
|
20
|
+
export interface ExternalAction {
|
|
21
|
+
kind: ExternalActionKind;
|
|
22
|
+
name: string;
|
|
23
|
+
/** Targets the action applies to (the intersection of manifest.targets and
|
|
24
|
+
* the package's declared external.targets — empty if fully quarantined out). */
|
|
25
|
+
targets: Target[];
|
|
26
|
+
/** argv to run for install/reinstall; the declared uninstall argv for
|
|
27
|
+
* orphan (undefined if the package declared none — warn-only removal). */
|
|
28
|
+
command?: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Decide what `sync` would do with every selected external package, against
|
|
32
|
+
* the current lock. Pure: the lock is injected so this is testable without
|
|
33
|
+
* disk, and it performs no execution itself.
|
|
34
|
+
*
|
|
35
|
+
* - `install`: no lock entry for this package yet.
|
|
36
|
+
* - `reinstall`: lock entry exists but its spec/targets no longer match the
|
|
37
|
+
* package's current declaration (the manifest changed under it).
|
|
38
|
+
* - `skip`: lock entry matches exactly — idempotent no-op.
|
|
39
|
+
* - `orphan`: a lock entry exists for a package no longer selected (or with
|
|
40
|
+
* no targets left after quarantine) — offer to remove, never silently.
|
|
41
|
+
*/
|
|
42
|
+
export declare function planExternal(manifest: Manifest, lock: Lock): ExternalAction[];
|
|
43
|
+
/** Runs one argv in `cwd`. The real implementation for `executeAction`'s
|
|
44
|
+
* injected runner — uses `execFileSync` (argv form, no shell string) so
|
|
45
|
+
* there is no pipe/shell-injection surface from a pinned command. */
|
|
46
|
+
export declare function runCommand(command: string[], cwd: string): void;
|
|
47
|
+
export type CommandRunner = (command: string[], cwd: string) => void;
|
|
48
|
+
/** Execute one planned action via the injected runner. Tests pass a stub
|
|
49
|
+
* runner so they never actually shell out. No-op for `skip`, and for
|
|
50
|
+
* `orphan` when the package declared no `uninstall` (Praxis doesn't own
|
|
51
|
+
* those files and never deletes them on the user's behalf). */
|
|
52
|
+
export declare function executeAction(action: ExternalAction, cwd: string, runner?: CommandRunner): void;
|
|
53
|
+
/** Read `praxis-external.lock` from `cwd`, or the empty lock if absent. */
|
|
54
|
+
export declare function readLock(cwd: string): Lock;
|
|
55
|
+
/** Apply a list of executed actions to a lock, returning the next lock.
|
|
56
|
+
* Pure — the caller decides whether/when to persist it. */
|
|
57
|
+
export declare function applyActionsToLock(lock: Lock, actions: ExternalAction[]): Lock;
|
|
58
|
+
/** Persist the lock to `praxis-external.lock` at `cwd`, sorted for a stable
|
|
59
|
+
* diff (deterministic, reviewable — no timestamps anywhere in the file). */
|
|
60
|
+
export declare function writeLock(cwd: string, lock: Lock): void;
|
|
61
|
+
export {};
|