@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,83 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { z } from "zod";
6
+ import { BUCKETS } from "./merge-json.js";
7
+ /**
8
+ * The neutral permission policy — a content package's *second artifact kind*
9
+ * (docs/wiki/packages-and-emit.md "provides", docs/wiki/emitters.md; decisions D18/D19). Where `rules.md` is
10
+ * tool-neutral prose, `permissions.yaml` is tool-neutral **structured intent**:
11
+ * it lists semantic *capabilities* in allow/ask/deny buckets. It is deliberately
12
+ * NOT a list of Claude Code rule strings — that would make the source tool-bound.
13
+ * Per-tool emitters translate each capability to concrete rules (or emit nothing
14
+ * where the tool has no permission model). This keeps "tool-neutral by
15
+ * construction" intact for the enforcement layer too.
16
+ *
17
+ * The capability vocabulary is closed (a zod enum): a policy may only reference
18
+ * capabilities every emitter knows how to translate, so a capability can never be
19
+ * silently dropped. Adding a capability is a deliberate edit here plus a mapping
20
+ * in each emitter — verified by conformance.
21
+ */
22
+ const PACKAGES_DIR = new URL("../packages/", import.meta.url);
23
+ // The closed capability vocabulary. Each is a durable, tool-agnostic intent;
24
+ // emitters own the concrete per-tool translation. Grows deliberately.
25
+ const CAPABILITIES = [
26
+ "read-repo", // read files in the repository
27
+ "edit-repo", // create / edit / write files in the repository
28
+ "run-dev-scripts", // project scripts: test, lint, typecheck, build
29
+ "read-only-git", // git status / diff / log
30
+ "git-commit", // commit locally
31
+ "git-push", // push to a remote
32
+ "install-deps", // add / install project dependencies
33
+ "destructive-delete", // rm -rf and recursive force-deletes
34
+ "force-push", // git push --force
35
+ "read-secrets", // .env, *.pem, credential files
36
+ "global-install", // global package installs (npm/pnpm/yarn -g)
37
+ ];
38
+ const capabilityList = z.array(z.enum(CAPABILITIES)).default([]);
39
+ // A policy is the same three buckets the merge owns; each holds capabilities.
40
+ export const policySchema = z.strictObject({
41
+ allow: capabilityList,
42
+ ask: capabilityList,
43
+ deny: capabilityList,
44
+ });
45
+ /** Parse + validate neutral policy text. Throws a readable, agent-actionable
46
+ * error naming each problem — mirrors parseManifest in src/manifest.ts. */
47
+ export function parsePolicy(yamlText) {
48
+ let raw;
49
+ try {
50
+ raw = parseYaml(yamlText);
51
+ }
52
+ catch (err) {
53
+ throw new Error(`permissions.yaml is not valid YAML: ${err.message}`);
54
+ }
55
+ const result = policySchema.safeParse(raw ?? {});
56
+ if (!result.success) {
57
+ const issues = result.error.issues
58
+ .map((i) => ` - ${i.path.map(String).join(".") || "(root)"}: ${i.message}`)
59
+ .join("\n");
60
+ throw new Error(`permissions.yaml is invalid:\n${issues}\n` +
61
+ `Capabilities must be one of: ${CAPABILITIES.join(", ")}. ` +
62
+ `Add a new capability in src/permissions.ts and map it in every emitter.`);
63
+ }
64
+ return result.data;
65
+ }
66
+ /** The neutral policy for a content package, or undefined if it provides none.
67
+ * Scans packages/<layer>/<pkg>/permissions.yaml — mirrors loadPackageSource. */
68
+ export function loadPolicy(pkg) {
69
+ const root = fileURLToPath(PACKAGES_DIR);
70
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
71
+ if (!entry.isDirectory())
72
+ continue;
73
+ const candidate = join(root, entry.name, pkg, "permissions.yaml");
74
+ if (existsSync(candidate))
75
+ return parsePolicy(readFileSync(candidate, "utf8"));
76
+ }
77
+ return undefined;
78
+ }
79
+ /** All capabilities referenced by a policy, across every bucket. */
80
+ export function policyCapabilities(policy) {
81
+ return BUCKETS.flatMap((b) => policy[b]);
82
+ }
83
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AAEH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE9D,6EAA6E;AAC7E,sEAAsE;AACtE,MAAM,YAAY,GAAG;IACnB,WAAW,EAAE,+BAA+B;IAC5C,WAAW,EAAE,gDAAgD;IAC7D,iBAAiB,EAAE,gDAAgD;IACnE,eAAe,EAAE,0BAA0B;IAC3C,YAAY,EAAE,iBAAiB;IAC/B,UAAU,EAAE,mBAAmB;IAC/B,cAAc,EAAE,qCAAqC;IACrD,oBAAoB,EAAE,qCAAqC;IAC3D,YAAY,EAAE,mBAAmB;IACjC,cAAc,EAAE,gCAAgC;IAChD,gBAAgB,EAAE,6CAA6C;CACvD,CAAC;AAIX,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAEjE,8EAA8E;AAC9E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IACzC,KAAK,EAAE,cAAc;IACrB,GAAG,EAAE,cAAc;IACnB,IAAI,EAAE,cAAc;CACrB,CAAC,CAAC;AAIH;4EAC4E;AAC5E,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uCAAwC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC3E,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CACb,iCAAiC,MAAM,IAAI;YACzC,gCAAgC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC3D,yEAAyE,CAC5E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;iFACiF;AACjF,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAClE,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,WAAW,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ export declare const pluginsBlockSchema: z.ZodObject<{
3
+ targets: z.ZodArray<z.ZodString>;
4
+ marketplace: z.ZodObject<{
5
+ name: z.ZodString;
6
+ source: z.ZodObject<{
7
+ source: z.ZodLiteral<"github">;
8
+ repo: z.ZodString;
9
+ ref: z.ZodOptional<z.ZodString>;
10
+ sha: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strict>;
12
+ }, z.core.$strict>;
13
+ enable: z.ZodArray<z.ZodString>;
14
+ }, z.core.$strict>;
15
+ export type PluginsBlock = z.infer<typeof pluginsBlockSchema>;
16
+ /** Parse + validate neutral plugins.yaml text. Throws a readable,
17
+ * agent-actionable error naming each problem — mirrors parsePolicy. */
18
+ export declare function parsePluginsBlock(yamlText: string, pkgName: string): PluginsBlock;
19
+ /** The neutral plugins declaration for a content package, or undefined if it
20
+ * provides none. Scans packages/<layer>/<pkg>/plugins.yaml — mirrors loadPolicy. */
21
+ export declare function loadPluginsBlock(pkg: string): PluginsBlock | undefined;
@@ -0,0 +1,88 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { z } from "zod";
6
+ /**
7
+ * The declarative plugin-marketplace artifact (a content package's *fourth
8
+ * artifact kind*, docs/wiki/packages-and-emit.md "provides"). A thin package under
9
+ * `packages/external/<pkg>/` declares a Claude Code plugin marketplace and which
10
+ * plugins to enable; the emitter writes that declaration into
11
+ * `.claude/settings.json` (`extraKnownMarketplaces` + `enabledPlugins`) via the
12
+ * JSON merge engine. Praxis never executes anything — no npx, no git clone, no
13
+ * network at sync time. Writing those keys is enough for Claude Code itself to
14
+ * prompt the user/team to install and enable on next folder-trust.
15
+ *
16
+ * Mirrors `src/permissions.ts`: a neutral, validated YAML source per package,
17
+ * loaded by scanning `packages/<layer>/<pkg>/plugins.yaml`.
18
+ */
19
+ const PACKAGES_DIR = new URL("../packages/", import.meta.url);
20
+ // Marketplace source kinds Claude Code supports. Only "github" is verified and
21
+ // in use today; the schema is closed so an unverified source kind fails loud
22
+ // rather than being silently accepted and mis-emitted.
23
+ const marketplaceSourceSchema = z.strictObject({
24
+ source: z.literal("github"),
25
+ repo: z.string().min(1),
26
+ // Honest pinning: `ref` (a tag/branch) and/or `sha` (a commit). `sha` paired
27
+ // with `ref` is the effective pin; neither is required (some repos genuinely
28
+ // have no tags — never fabricate one).
29
+ ref: z.string().min(1).optional(),
30
+ sha: z.string().min(1).optional(),
31
+ });
32
+ const marketplaceSchema = z.strictObject({
33
+ name: z.string().min(1),
34
+ source: marketplaceSourceSchema,
35
+ });
36
+ export const pluginsBlockSchema = z.strictObject({
37
+ // Targets this declaration applies to; validated against known Targets by the
38
+ // caller (manifest.ts owns the Target enum, plugins.ts stays decoupled from it).
39
+ targets: z.array(z.string().min(1)).min(1),
40
+ marketplace: marketplaceSchema,
41
+ // "<plugin>@<marketplace-name>" — validated against marketplace.name below.
42
+ enable: z.array(z.string().min(1)).min(1),
43
+ });
44
+ /** Parse + validate neutral plugins.yaml text. Throws a readable,
45
+ * agent-actionable error naming each problem — mirrors parsePolicy. */
46
+ export function parsePluginsBlock(yamlText, pkgName) {
47
+ let raw;
48
+ try {
49
+ raw = parseYaml(yamlText);
50
+ }
51
+ catch (err) {
52
+ throw new Error(`plugins.yaml in "${pkgName}" is not valid YAML: ${err.message}`);
53
+ }
54
+ const result = pluginsBlockSchema.safeParse(raw ?? {});
55
+ if (!result.success) {
56
+ const issues = result.error.issues
57
+ .map((i) => ` - ${i.path.map(String).join(".") || "(root)"}: ${i.message}`)
58
+ .join("\n");
59
+ throw new Error(`plugins.yaml in "${pkgName}" is invalid:\n${issues}`);
60
+ }
61
+ // Every `enable` entry's `@<marketplace>` must equal marketplace.name — fail
62
+ // loud rather than silently emit a plugin pinned to a marketplace that isn't
63
+ // declared (an enabledPlugins entry Claude Code could never resolve).
64
+ const { marketplace, enable } = result.data;
65
+ for (const entry of enable) {
66
+ const at = entry.lastIndexOf("@");
67
+ const suffix = at === -1 ? undefined : entry.slice(at + 1);
68
+ if (suffix !== marketplace.name) {
69
+ throw new Error(`plugins.yaml in "${pkgName}" is invalid:\n` +
70
+ ` - enable: "${entry}" must end in "@${marketplace.name}" to match the declared marketplace.`);
71
+ }
72
+ }
73
+ return result.data;
74
+ }
75
+ /** The neutral plugins declaration for a content package, or undefined if it
76
+ * provides none. Scans packages/<layer>/<pkg>/plugins.yaml — mirrors loadPolicy. */
77
+ export function loadPluginsBlock(pkg) {
78
+ const root = fileURLToPath(PACKAGES_DIR);
79
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
80
+ if (!entry.isDirectory())
81
+ continue;
82
+ const candidate = join(root, entry.name, pkg, "plugins.yaml");
83
+ if (existsSync(candidate))
84
+ return parsePluginsBlock(readFileSync(candidate, "utf8"), pkg);
85
+ }
86
+ return undefined;
87
+ }
88
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;GAYG;AAEH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE9D,+EAA+E;AAC/E,6EAA6E;AAC7E,uDAAuD;AACvD,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7C,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,6EAA6E;IAC7E,6EAA6E;IAC7E,uCAAuC;IACvC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACjC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,uBAAuB;CAChC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC/C,8EAA8E;IAC9E,iFAAiF;IACjF,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,WAAW,EAAE,iBAAiB;IAC9B,4EAA4E;IAC5E,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC1C,CAAC,CAAC;AAIH;wEACwE;AACxE,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,OAAe;IACjE,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,wBAAyB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC3E,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,kBAAkB,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,iBAAiB;gBAC1C,gBAAgB,KAAK,mBAAmB,WAAW,CAAC,IAAI,sCAAsC,CACjG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;qFACqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9D,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,iBAAiB,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function buildProgram(): Command;
@@ -0,0 +1,201 @@
1
+ import { cancel, confirm, intro, isCancel, multiselect, note, outro, select } from "@clack/prompts";
2
+ import { Command } from "commander";
3
+ import { checkAnchors } from "./anchors.js";
4
+ import { applyInit, defaultManifest, detectContext, manifestFromSelections, renderManifestYaml, } from "./init.js";
5
+ import { STACKS } from "./manifest.js";
6
+ import { availablePackages } from "./packages.js";
7
+ import { applyManifest, runSync } from "./sync.js";
8
+ /**
9
+ * The CLI surface (docs/wiki/interaction-model.md). `init` is the minimal first-run
10
+ * install (detect → propose → preview → confirm → write + apply); `sync`/`check`
11
+ * reconcile an existing repo to its `praxis.yaml`.
12
+ */
13
+ const STATUS_SYMBOL = {
14
+ created: "+",
15
+ updated: "~",
16
+ unchanged: " ",
17
+ };
18
+ function formatPlan(report, mode) {
19
+ return report.files
20
+ .map((f) => {
21
+ const drift = mode === "check" && f.status !== "unchanged" ? " (drift)" : "";
22
+ const conflict = f.conflicts.length ? ` ⚠ conflict: ${f.conflicts.join(", ")}` : "";
23
+ return ` ${STATUS_SYMBOL[f.status]} ${f.path}${drift}${conflict}`;
24
+ })
25
+ .join("\n");
26
+ }
27
+ function formatAnchorReport(report) {
28
+ if (report.diagnostics.length === 0) {
29
+ return `\nKnowledge anchors: ${report.anchorsChecked} checked, 0 broken.`;
30
+ }
31
+ const diagnostics = report.diagnostics
32
+ .map((d) => {
33
+ const target = d.target === undefined ? "" : ` (${d.type}: ${d.target})`;
34
+ return ` - ${d.file}${target}: ${d.message}`;
35
+ })
36
+ .join("\n");
37
+ return `\nBroken knowledge anchors:\n${diagnostics}`;
38
+ }
39
+ // --- sync / check ---------------------------------------------------------
40
+ function runReconcile(write, mode) {
41
+ try {
42
+ const report = runSync({ cwd: process.cwd(), write });
43
+ console.log(formatPlan(report, mode));
44
+ const anchorReport = mode === "check" ? checkAnchors(process.cwd()) : undefined;
45
+ if (anchorReport) {
46
+ const output = formatAnchorReport(anchorReport);
47
+ if (anchorReport.ok)
48
+ console.log(output);
49
+ else
50
+ console.error(output);
51
+ }
52
+ if (report.hasConflicts) {
53
+ console.error("\nConflicts: you edited Praxis-managed content. It was left untouched.\n" +
54
+ "Resolve by promoting the change upstream or moving it outside the managed block.");
55
+ }
56
+ if (mode === "check"
57
+ ? report.changed || report.hasConflicts || !anchorReport?.ok
58
+ : report.hasConflicts) {
59
+ process.exitCode = 1;
60
+ }
61
+ }
62
+ catch (err) {
63
+ console.error(`praxis: ${err.message}`);
64
+ process.exitCode = 1;
65
+ }
66
+ }
67
+ // --- init -----------------------------------------------------------------
68
+ async function runInit(opts) {
69
+ const cwd = process.cwd();
70
+ intro("praxis — install the methodology layer");
71
+ try {
72
+ const ctx = detectContext(cwd);
73
+ if (ctx.manifestExists) {
74
+ note("This repo already has a praxis.yaml.\nRun `praxis sync` to apply changes.", "Already initialized");
75
+ outro("Nothing to do.");
76
+ return;
77
+ }
78
+ const detected = [
79
+ ctx.hasGit ? "git" : null,
80
+ ctx.hasClaudeMd ? "CLAUDE.md" : null,
81
+ ctx.hasAgentsMd ? "AGENTS.md" : null,
82
+ ctx.detectedStacks.length ? `stack: ${ctx.detectedStacks.join(", ")}` : null,
83
+ ].filter(Boolean);
84
+ note(detected.length ? detected.join(" · ") : "fresh repo", "Detected");
85
+ let manifest;
86
+ if (opts.yes) {
87
+ // Non-interactive: Quick start, no prompts
88
+ manifest = defaultManifest(ctx);
89
+ }
90
+ else {
91
+ // Ask Quick start vs Customize
92
+ const mode = await select({
93
+ message: "How do you want to install?",
94
+ options: [
95
+ { value: "quick", label: "Quick start", hint: "all Layer 1 + detected stack Layer 2" },
96
+ { value: "customize", label: "Customize", hint: "pick stacks, packages, and targets" },
97
+ ],
98
+ });
99
+ if (isCancel(mode)) {
100
+ cancel("No changes made.");
101
+ return;
102
+ }
103
+ if (mode === "quick") {
104
+ manifest = defaultManifest(ctx);
105
+ }
106
+ else {
107
+ // Customize: multiselect stacks
108
+ const chosenStacks = await multiselect({
109
+ message: "Which stacks does this repo use?",
110
+ options: STACKS.map((s) => ({ value: s, label: s })),
111
+ initialValues: ctx.detectedStacks,
112
+ required: false,
113
+ });
114
+ if (isCancel(chosenStacks)) {
115
+ cancel("No changes made.");
116
+ return;
117
+ }
118
+ const stackSet = new Set(chosenStacks);
119
+ // Build package options: layer1 + layer2 for chosen stacks + external
120
+ const all = availablePackages();
121
+ const recommended = new Set(defaultManifest({ ...ctx, detectedStacks: chosenStacks }).packages);
122
+ const pkgOptions = [];
123
+ for (const [name, pkg] of all) {
124
+ if (pkg.layer === "layer1" || pkg.layer === "external") {
125
+ pkgOptions.push({ value: name, label: name, hint: pkg.layer });
126
+ }
127
+ else if (pkg.layer === "layer2" && pkg.stack && stackSet.has(pkg.stack)) {
128
+ pkgOptions.push({ value: name, label: name, hint: `layer2 / ${pkg.stack}` });
129
+ }
130
+ }
131
+ pkgOptions.sort((a, b) => a.value.localeCompare(b.value));
132
+ const chosenPkgs = await multiselect({
133
+ message: "Which packages?",
134
+ options: pkgOptions,
135
+ initialValues: pkgOptions.map((o) => o.value).filter((n) => recommended.has(n)),
136
+ required: true,
137
+ });
138
+ if (isCancel(chosenPkgs)) {
139
+ cancel("No changes made.");
140
+ return;
141
+ }
142
+ // Multiselect targets
143
+ const targetOptions = [
144
+ { value: "claude-code", label: "claude-code" },
145
+ { value: "agents-md", label: "agents-md" },
146
+ ];
147
+ const chosenTargets = await multiselect({
148
+ message: "Which targets to emit?",
149
+ options: targetOptions,
150
+ initialValues: ["claude-code", "agents-md"],
151
+ required: true,
152
+ });
153
+ if (isCancel(chosenTargets)) {
154
+ cancel("No changes made.");
155
+ return;
156
+ }
157
+ manifest = manifestFromSelections(chosenStacks, chosenPkgs, chosenTargets);
158
+ }
159
+ }
160
+ // Preview + confirm (interactive) / apply immediately (--yes)
161
+ const manifestYaml = renderManifestYaml(manifest);
162
+ const report = applyManifest(manifest, cwd, false);
163
+ note(manifestYaml.trimEnd(), "praxis.yaml (proposed)");
164
+ note(formatPlan(report, "init"), "Will write");
165
+ if (!opts.yes) {
166
+ const ok = await confirm({ message: "Apply these changes?" });
167
+ if (isCancel(ok) || !ok) {
168
+ cancel("No changes made.");
169
+ return;
170
+ }
171
+ }
172
+ applyInit(cwd, manifest);
173
+ outro("Done. Run `praxis check` anytime to verify the repo is in sync.");
174
+ }
175
+ catch (err) {
176
+ cancel(`praxis: ${err.message}`);
177
+ process.exitCode = 1;
178
+ }
179
+ }
180
+ export function buildProgram() {
181
+ const program = new Command();
182
+ program
183
+ .name("praxis")
184
+ .description("Install and sync an AI methodology layer into a codebase.")
185
+ .version("0.0.0");
186
+ program
187
+ .command("init", { isDefault: true })
188
+ .description("Install the methodology layer (detect, preview, confirm, write).")
189
+ .option("-y, --yes", "skip prompts; accept the defaults (non-interactive)")
190
+ .action((opts) => runInit(opts));
191
+ program
192
+ .command("sync")
193
+ .description("Reconcile the repo to praxis.yaml; write managed methodology files.")
194
+ .action(() => runReconcile(true, "sync"));
195
+ program
196
+ .command("check")
197
+ .description("Report drift without writing; non-zero exit on drift.")
198
+ .action(() => runReconcile(false, "check"));
199
+ return program;
200
+ }
201
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type Manifest } from "./manifest.js";
2
+ /**
3
+ * Orchestration for `sync` and `check` (docs/wiki/interaction-model.md). Ties loader →
4
+ * emitter → engine and performs the file I/O: read `praxis.yaml`, plan the emit,
5
+ * apply each op against the destination file, and (in write mode) persist safe
6
+ * changes. Never clobbers a user-edited managed block (D10) — conflicts are
7
+ * surfaced, not overwritten.
8
+ */
9
+ export type FileStatus = "created" | "updated" | "unchanged";
10
+ export interface FileReport {
11
+ path: string;
12
+ status: FileStatus;
13
+ /** Block ids the user edited; left untouched and surfaced for resolution. */
14
+ conflicts: string[];
15
+ /** Whether this file was written this run (false in check/dry-run mode). */
16
+ written: boolean;
17
+ }
18
+ export interface SyncReport {
19
+ files: FileReport[];
20
+ /** Any file would be (or was) created/updated — i.e. the repo was out of sync. */
21
+ changed: boolean;
22
+ /** Any managed block was edited by the user. */
23
+ hasConflicts: boolean;
24
+ }
25
+ export interface SyncOptions {
26
+ cwd: string;
27
+ /** false = check (dry-run): compute the report, write nothing. */
28
+ write: boolean;
29
+ manifestPath?: string;
30
+ }
31
+ export declare function runSync(opts: SyncOptions): SyncReport;
32
+ /**
33
+ * Apply an in-memory manifest against the repo. Same engine as `runSync`, but the
34
+ * manifest comes from the caller rather than disk — `init` uses this to preview
35
+ * the emit before `praxis.yaml` exists.
36
+ */
37
+ export declare function applyManifest(manifest: Manifest, cwd: string, write: boolean): SyncReport;
package/dist/sync.js ADDED
@@ -0,0 +1,45 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { applyOp, planEmit } from "./emit.js";
4
+ import { loadManifest } from "./manifest.js";
5
+ export function runSync(opts) {
6
+ const manifestPath = opts.manifestPath ?? join(opts.cwd, "praxis.yaml");
7
+ return applyManifest(loadManifest(manifestPath), opts.cwd, opts.write);
8
+ }
9
+ /**
10
+ * Apply an in-memory manifest against the repo. Same engine as `runSync`, but the
11
+ * manifest comes from the caller rather than disk — `init` uses this to preview
12
+ * the emit before `praxis.yaml` exists.
13
+ */
14
+ export function applyManifest(manifest, cwd, write) {
15
+ const ops = planEmit(manifest);
16
+ const files = [];
17
+ for (const op of ops) {
18
+ const abs = join(cwd, op.path);
19
+ const existing = readFileIfExists(abs);
20
+ const result = applyOp(op, existing ?? "");
21
+ const status = existing === undefined ? "created" : result.changed ? "updated" : "unchanged";
22
+ // Write safe changes only: never in check mode, never a pure no-op. result.text
23
+ // already preserves any conflicted block, so writing it honors D10.
24
+ const written = write && (status === "created" || (status === "updated" && result.changed));
25
+ if (written) {
26
+ mkdirSync(dirname(abs), { recursive: true });
27
+ writeFileSync(abs, result.text, "utf8");
28
+ }
29
+ files.push({ path: op.path, status, conflicts: result.conflicts, written });
30
+ }
31
+ return {
32
+ files,
33
+ changed: files.some((f) => f.status !== "unchanged"),
34
+ hasConflicts: files.some((f) => f.conflicts.length > 0),
35
+ };
36
+ }
37
+ function readFileIfExists(path) {
38
+ try {
39
+ return readFileSync(path, "utf8");
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AAoC5D,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACxE,OAAO,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB,EAAE,GAAW,EAAE,KAAc;IAC3E,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE/B,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;QAE3C,MAAM,MAAM,GACV,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;QAEhF,gFAAgF;QAChF,oEAAoE;QACpE,MAAM,OAAO,GACX,KAAK,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9E,IAAI,OAAO,EAAE,CAAC;YACZ,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO;QACL,KAAK;QACL,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;QACpD,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@aacombarro89/praxis",
3
+ "version": "0.1.0",
4
+ "description": "A CLI that installs an AI methodology layer into a codebase.",
5
+ "type": "module",
6
+ "bin": {
7
+ "praxis": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "packages"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.build.json",
21
+ "typecheck": "tsc --noEmit",
22
+ "test": "vitest run test/",
23
+ "conformance": "vitest run conformance/",
24
+ "selfcheck": "node dist/cli.js check",
25
+ "test:watch": "vitest",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "license": "UNLICENSED",
29
+ "dependencies": {
30
+ "@clack/prompts": "^1.5.1",
31
+ "commander": "^14.0.3",
32
+ "yaml": "^2.9.0",
33
+ "zod": "^4.4.3"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.14.0",
37
+ "typescript": "^6.0.3",
38
+ "vitest": "^4.1.9"
39
+ }
40
+ }
@@ -0,0 +1,7 @@
1
+ # Package manifest (docs/wiki/packages-and-emit.md, D20). This package provides no
2
+ # methodology content of its own — it declares a Claude Code plugin marketplace
3
+ # (plugins.yaml) that Praxis emits into .claude/settings.json. See
4
+ # docs/wiki/emitters.md/D19 for the quarantine-per-tool shape this mirrors.
5
+ name: drawio
6
+ layer: external
7
+ provides: [plugins]
@@ -0,0 +1,13 @@
1
+ # Declarative Claude Code plugin-marketplace reference (src/plugins.ts).
2
+ # Praxis never executes anything here — this is committable config that Claude
3
+ # Code itself reads on folder-trust to offer installing/enabling the plugin.
4
+ # Verified against github.com/Agents365-ai/365-skills, which genuinely ships no
5
+ # tags — left unpinned rather than fabricating a ref/sha.
6
+ targets: [claude-code]
7
+ marketplace:
8
+ name: 365-skills
9
+ source:
10
+ source: github
11
+ repo: Agents365-ai/365-skills
12
+ enable:
13
+ - drawio@365-skills
@@ -0,0 +1,7 @@
1
+ # Package manifest (docs/wiki/packages-and-emit.md, D20). This package provides no
2
+ # methodology content of its own — it declares a Claude Code plugin marketplace
3
+ # (plugins.yaml) that Praxis emits into .claude/settings.json. See
4
+ # docs/wiki/emitters.md/D19 for the quarantine-per-tool shape this mirrors.
5
+ name: ponytail
6
+ layer: external
7
+ provides: [plugins]
@@ -0,0 +1,13 @@
1
+ # Declarative Claude Code plugin-marketplace reference (src/plugins.ts).
2
+ # Praxis never executes anything here — this is committable config that Claude
3
+ # Code itself reads on folder-trust to offer installing/enabling the plugin.
4
+ # Verified against github.com/DietrichGebert/ponytail (tag v4.7.0 exists).
5
+ targets: [claude-code]
6
+ marketplace:
7
+ name: ponytail
8
+ source:
9
+ source: github
10
+ repo: DietrichGebert/ponytail
11
+ ref: v4.7.0
12
+ enable:
13
+ - ponytail@ponytail
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: Audit the authored instruction files (CLAUDE.md, .claude/rules) against the project's current state and bring them back in line.
3
+ ---
4
+
5
+ Audit scope: $ARGUMENTS
6
+
7
+ This audits the **authored instruction layer** — `CLAUDE.md` and
8
+ `.claude/rules` — not the tool's native auto-memory and not any 3rd-party
9
+ agent-memory system. Those are out of scope.
10
+
11
+ 1. Read `CLAUDE.md` and every file under `.claude/rules`. Identify any
12
+ canonical doc they point to (architecture doc, decision log, design doc).
13
+ 2. Compare what the instruction files claim against that canonical doc and
14
+ against recent git history (`git log`, changed files) and the current
15
+ code/config. List concretely what is stale, missing, or now contradicted —
16
+ cite the file and line for each. Then check operational completeness
17
+ against this rubric — does the instruction layer document:
18
+ - build/test/run commands (and how CI verifies the repo, if there is one)
19
+ - project layout (key directories/modules)
20
+ - key conventions (e.g. branch/PR flow, what a change must ship with)
21
+ - always-do rules (the few non-negotiable habits)
22
+
23
+ Flag any rubric section that's entirely absent, citing where it should go.
24
+ 3. **Ask the user before editing anything.** Present the list from step 2 and
25
+ confirm which items to fix.
26
+ 4. For approved items, update only project-owned sections of the instruction
27
+ files — never invent a fact that isn't backed by the canonical doc, the
28
+ code, or the user's answer. Where an instruction file currently restates a
29
+ fact that lives in a canonical doc, prefer replacing the restatement with a
30
+ link to that doc (reference, don't duplicate) so it can't drift again.
31
+ 5. If an instruction file carries `praxisAnchors` frontmatter, revalidate and
32
+ update the anchors for any changed source reference. Phase-2 anchors are
33
+ deterministic only:
34
+ - `type: path`, `target: repo/relative/path`
35
+ - `type: command`, `target: npm run <script>` or `npm <script>`
36
+ - `type: section`, `target: docs/file.md#heading-slug`
37
+
38
+ Do not add hash/staleness tripwires here yet; those are future advisory
39
+ signals. Run `praxis check` if available and fix broken anchors before
40
+ reporting completion.
41
+ 6. Report what changed and what was left as-is, with reasons.