@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.
Files changed (72) hide show
  1. package/dist/anchors.d.ts +36 -0
  2. package/dist/anchors.js +197 -0
  3. package/dist/anchors.js.map +1 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +4 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/emit.d.ts +51 -0
  8. package/dist/emit.js +253 -0
  9. package/dist/emit.js.map +1 -0
  10. package/dist/external.d.ts +61 -0
  11. package/dist/external.js +155 -0
  12. package/dist/external.js.map +1 -0
  13. package/dist/init.d.ts +45 -0
  14. package/dist/init.js +123 -0
  15. package/dist/init.js.map +1 -0
  16. package/dist/manifest.d.ts +36 -0
  17. package/dist/manifest.js +63 -0
  18. package/dist/manifest.js.map +1 -0
  19. package/dist/merge-json.d.ts +83 -0
  20. package/dist/merge-json.js +207 -0
  21. package/dist/merge-json.js.map +1 -0
  22. package/dist/merge.d.ts +53 -0
  23. package/dist/merge.js +89 -0
  24. package/dist/merge.js.map +1 -0
  25. package/dist/packages.d.ts +47 -0
  26. package/dist/packages.js +121 -0
  27. package/dist/packages.js.map +1 -0
  28. package/dist/permissions.d.ts +54 -0
  29. package/dist/permissions.js +83 -0
  30. package/dist/permissions.js.map +1 -0
  31. package/dist/plugins.d.ts +21 -0
  32. package/dist/plugins.js +88 -0
  33. package/dist/plugins.js.map +1 -0
  34. package/dist/program.d.ts +2 -0
  35. package/dist/program.js +201 -0
  36. package/dist/program.js.map +1 -0
  37. package/dist/sync.d.ts +37 -0
  38. package/dist/sync.js +45 -0
  39. package/dist/sync.js.map +1 -0
  40. package/package.json +40 -0
  41. package/packages/external/drawio/package.yaml +7 -0
  42. package/packages/external/drawio/plugins.yaml +13 -0
  43. package/packages/external/ponytail/package.yaml +7 -0
  44. package/packages/external/ponytail/plugins.yaml +13 -0
  45. package/packages/layer1/instruction-upkeep/commands/audit.md +41 -0
  46. package/packages/layer1/instruction-upkeep/package.yaml +8 -0
  47. package/packages/layer1/instruction-upkeep/rules.md +21 -0
  48. package/packages/layer1/karpathy-claude/package.yaml +5 -0
  49. package/packages/layer1/karpathy-claude/rules.md +35 -0
  50. package/packages/layer1/safe-permissions/package.yaml +5 -0
  51. package/packages/layer1/safe-permissions/permissions.yaml +26 -0
  52. package/packages/layer1/session-handoff/commands/handoff.md +185 -0
  53. package/packages/layer1/session-handoff/package.yaml +10 -0
  54. package/packages/layer1/session-handoff/rules.md +20 -0
  55. package/packages/layer1/upkeep/commands/upkeep.md +31 -0
  56. package/packages/layer1/upkeep/package.yaml +10 -0
  57. package/packages/layer1/upkeep/rules.md +12 -0
  58. package/packages/layer1/wiki-memory/commands/wiki.md +103 -0
  59. package/packages/layer1/wiki-memory/package.yaml +10 -0
  60. package/packages/layer1/wiki-memory/rules.md +49 -0
  61. package/packages/layer2/node-recipes/package.yaml +10 -0
  62. package/packages/layer2/node-recipes/rules.md +33 -0
  63. package/packages/layer2/node-testing/package.yaml +10 -0
  64. package/packages/layer2/node-testing/rules.md +33 -0
  65. package/packages/layer2/python-backend-recipes/package.yaml +10 -0
  66. package/packages/layer2/python-backend-recipes/rules.md +37 -0
  67. package/packages/layer2/python-testing/package.yaml +10 -0
  68. package/packages/layer2/python-testing/rules.md +33 -0
  69. package/packages/layer2/react-components/package.yaml +10 -0
  70. package/packages/layer2/react-components/rules.md +40 -0
  71. package/packages/layer2/react-testing/package.yaml +10 -0
  72. 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 {};
@@ -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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { buildProgram } from "./program.js";
3
+ buildProgram().parseAsync(process.argv);
4
+ //# sourceMappingURL=cli.js.map
@@ -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
@@ -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 {};