@freetesh/canon 0.15.0 → 0.16.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +17 -2
- package/cli/dist/api.d.ts +2 -2
- package/cli/dist/api.js +2 -2
- package/cli/dist/checks/{external.js → commands.js} +2 -0
- package/cli/dist/checks/index.js +4 -3
- package/cli/dist/checks/stale-copies.d.ts +2 -0
- package/cli/dist/checks/stale-copies.js +18 -0
- package/cli/dist/checks/{copies.d.ts → standards-copies.d.ts} +0 -1
- package/cli/dist/checks/{copies.js → standards-copies.js} +0 -16
- package/cli/dist/commands/add.d.ts +1 -1
- package/cli/dist/commands/add.js +1 -1
- package/cli/dist/commands/check.d.ts +1 -1
- package/cli/dist/commands/check.js +2 -2
- package/cli/dist/commands/copies.d.ts +1 -1
- package/cli/dist/commands/hooks.d.ts +1 -1
- package/cli/dist/commands/hooks.js +2 -2
- package/cli/dist/commands/init.d.ts +1 -1
- package/cli/dist/commands/init.js +2 -2
- package/cli/dist/commands/learn.d.ts +1 -1
- package/cli/dist/commands/learn.js +2 -2
- package/cli/dist/commands/pipeline.d.ts +1 -1
- package/cli/dist/{search → commands}/search.js +1 -1
- package/cli/dist/commands/standards.d.ts +1 -1
- package/cli/dist/commands/status.d.ts +1 -1
- package/cli/dist/commands/sync.d.ts +1 -1
- package/cli/dist/commands/sync.js +1 -1
- package/cli/dist/commands/ui.d.ts +1 -1
- package/cli/dist/commands/ui.js +2 -2
- package/cli/dist/config/load.js +16 -4
- package/cli/dist/config/schema.d.ts +21 -2
- package/cli/dist/config/schema.js +12 -2
- package/cli/dist/format.d.ts +1 -1
- package/cli/dist/index.js +2 -2
- package/cli/dist/init.js +2 -2
- package/cli/dist/packs.d.ts +3 -1
- package/cli/dist/status.d.ts +1 -1
- package/cli/dist/status.js +1 -1
- package/package.json +2 -3
- package/packs/expo/pack.yaml +1 -1
- package/packs/firebase/pack.yaml +1 -1
- package/packs/react/pack.yaml +1 -1
- package/packs/revenuecat/pack.yaml +1 -1
- package/packs/uniwind/pack.yaml +1 -1
- /package/cli/dist/{run.d.ts → check.d.ts} +0 -0
- /package/cli/dist/{run.js → check.js} +0 -0
- /package/cli/dist/checks/{external.d.ts → commands.d.ts} +0 -0
- /package/cli/dist/checks/{metrics.d.ts → metrics-definitions.d.ts} +0 -0
- /package/cli/dist/checks/{metrics.js → metrics-definitions.js} +0 -0
- /package/cli/dist/{search → commands}/mcp.d.ts +0 -0
- /package/cli/dist/{search → commands}/mcp.js +0 -0
- /package/cli/dist/{search → commands}/search.d.ts +0 -0
- /package/cli/dist/{commands → lib}/env.d.ts +0 -0
- /package/cli/dist/{commands → lib}/env.js +0 -0
- /package/cli/dist/{search → lib}/qmd.d.ts +0 -0
- /package/cli/dist/{search → lib}/qmd.js +0 -0
package/README.md
CHANGED
|
@@ -100,6 +100,7 @@ estate: true
|
|
|
100
100
|
standards: [.canon/standards] # estate rules; same name replaces a canon default
|
|
101
101
|
commands: # your own checkers, run by `canon check`
|
|
102
102
|
- { id: designs, run: ./scripts/check-designs.sh, if: .context/designs, module: design }
|
|
103
|
+
- { id: designs-first, run: test -d .context/designs, kinds: [mobile, web] } # only repos that ship screens
|
|
103
104
|
copies: # files products copy from one source repo, in named sets
|
|
104
105
|
source: design-system
|
|
105
106
|
sets:
|
|
@@ -110,8 +111,8 @@ copies: # files products copy from one source repo,
|
|
|
110
111
|
|
|
111
112
|
```yaml
|
|
112
113
|
# <repo>/.claude/canon.yaml — written by `canon init`
|
|
113
|
-
canon: 0.
|
|
114
|
-
kind:
|
|
114
|
+
canon: 0.16.0
|
|
115
|
+
kind: mobile # mobile | web | mobile-and-web | package | content
|
|
115
116
|
modules: [docs, knowledge, code, listing, refs]
|
|
116
117
|
packs: [expo]
|
|
117
118
|
name: Acme # how the product is written; `named` copies swap it in
|
|
@@ -138,6 +139,20 @@ evals/ plugin evals and the fixture product
|
|
|
138
139
|
cli/ the canon CLI (TypeScript, vitest); `@freetesh/canon/api` for tools built on it
|
|
139
140
|
```
|
|
140
141
|
|
|
142
|
+
Inside `cli/src/`:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
index.ts the program: registers every command
|
|
146
|
+
api.ts what other tools import (`@freetesh/canon/api`)
|
|
147
|
+
commands/ one file per command — options and output only
|
|
148
|
+
checks/ one file per check, named by its id; index.ts lists them
|
|
149
|
+
config/ .claude/canon.yaml: schema, layered loading, estate discovery
|
|
150
|
+
lib/ shared helpers: files, frontmatter, env, qmd, system calls
|
|
151
|
+
<name>.ts the logic the commands call: check (+ format for its output), init, packs (add), sync, …
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Tests mirror it in `cli/test/`.
|
|
155
|
+
|
|
141
156
|
## Develop
|
|
142
157
|
|
|
143
158
|
`pnpm check` runs `canon check` on this repo, the typecheck and every test.
|
package/cli/dist/api.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { findEstate } from "./config/load.ts";
|
|
2
|
-
export { findProducts } from "./
|
|
3
|
-
export { qmdQuery, qmdRefresh } from "./
|
|
2
|
+
export { findProducts } from "./check.ts";
|
|
3
|
+
export { qmdQuery, qmdRefresh } from "./lib/qmd.ts";
|
package/cli/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// What other tools build on: the canon knowledge site reads repos the way `canon check --all` finds them.
|
|
2
2
|
export { findEstate } from "./config/load.js";
|
|
3
|
-
export { findProducts } from "./
|
|
4
|
-
export { qmdQuery, qmdRefresh } from "./
|
|
3
|
+
export { findProducts } from "./check.js";
|
|
4
|
+
export { qmdQuery, qmdRefresh } from "./lib/qmd.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { kindCovers } from "../config/schema.js";
|
|
4
5
|
const MAX_OUTPUT_LINES = 15;
|
|
5
6
|
function tail(text) {
|
|
6
7
|
return text.trim().split("\n").slice(-MAX_OUTPUT_LINES).join("\n");
|
|
@@ -18,6 +19,7 @@ export const commandChecks = {
|
|
|
18
19
|
return config.commands
|
|
19
20
|
.filter((command) => !command.if || existsSync(join(repo, command.if)))
|
|
20
21
|
.filter((command) => !command.module || config.modules.includes(command.module))
|
|
22
|
+
.filter((command) => !command.kinds || command.kinds.some((kind) => kindCovers(config.kind, kind)))
|
|
21
23
|
.flatMap((command) => {
|
|
22
24
|
const result = spawnSync("sh", ["-c", command.run], { cwd: repo, encoding: "utf8" });
|
|
23
25
|
if (result.error) {
|
package/cli/dist/checks/index.js
CHANGED
|
@@ -2,11 +2,12 @@ import { architecture } from "./architecture.js";
|
|
|
2
2
|
import { canonicalValues } from "./canonical-values.js";
|
|
3
3
|
import { contextLayout } from "./context-layout.js";
|
|
4
4
|
import { contextSize } from "./context-size.js";
|
|
5
|
-
import { staleCopies
|
|
6
|
-
import {
|
|
5
|
+
import { staleCopies } from "./stale-copies.js";
|
|
6
|
+
import { standardsCopies } from "./standards-copies.js";
|
|
7
|
+
import { commandChecks } from "./commands.js";
|
|
7
8
|
import { features } from "./features.js";
|
|
8
9
|
import { listing } from "./listing.js";
|
|
9
|
-
import { metricsDefinitions } from "./metrics.js";
|
|
10
|
+
import { metricsDefinitions } from "./metrics-definitions.js";
|
|
10
11
|
import { pipeline } from "./pipeline.js";
|
|
11
12
|
import { refs } from "./refs.js";
|
|
12
13
|
import { sharedFiles } from "./shared-files.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
// Instruction files for agents other than Claude Code. They are copies of
|
|
4
|
+
// AGENTS.md that nobody reads, so they only drift.
|
|
5
|
+
const OTHER_AGENT_FILES = [".github/copilot-instructions.md", ".cursorrules", ".windsurfrules"];
|
|
6
|
+
export const staleCopies = {
|
|
7
|
+
id: "stale-copies",
|
|
8
|
+
module: null,
|
|
9
|
+
description: "no instruction files for agents that are not used (Copilot, Cursor, Windsurf)",
|
|
10
|
+
run({ repo }) {
|
|
11
|
+
return OTHER_AGENT_FILES.filter((file) => existsSync(join(repo, file))).map((file) => ({
|
|
12
|
+
check: "stale-copies",
|
|
13
|
+
level: "warn",
|
|
14
|
+
file,
|
|
15
|
+
message: "instructions for an agent not in use — delete, Claude reads AGENTS.md",
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
// Instruction files for agents other than Claude Code. They are copies of
|
|
4
|
-
// AGENTS.md that nobody reads, so they only drift.
|
|
5
|
-
const OTHER_AGENT_FILES = [".github/copilot-instructions.md", ".cursorrules", ".windsurfrules"];
|
|
6
3
|
// Fraction of a shared standard's lines that must appear in a product file
|
|
7
4
|
// before it counts as a copy rather than a reference.
|
|
8
5
|
const COPY_THRESHOLD = 0.6;
|
|
9
6
|
const STANDARD_COPIES = [{ file: ".context/wiki/CLAUDE.md", standard: "standards/wiki-schema.md" }];
|
|
10
|
-
export const staleCopies = {
|
|
11
|
-
id: "stale-copies",
|
|
12
|
-
module: null,
|
|
13
|
-
description: "no instruction files for agents that are not used (Copilot, Cursor, Windsurf)",
|
|
14
|
-
run({ repo }) {
|
|
15
|
-
return OTHER_AGENT_FILES.filter((file) => existsSync(join(repo, file))).map((file) => ({
|
|
16
|
-
check: "stale-copies",
|
|
17
|
-
level: "warn",
|
|
18
|
-
file,
|
|
19
|
-
message: "instructions for an agent not in use — delete, Claude reads AGENTS.md",
|
|
20
|
-
}));
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
7
|
function meaningfulLines(text) {
|
|
24
8
|
return new Set(text
|
|
25
9
|
.split("\n")
|
package/cli/dist/commands/add.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { CACHE_DIR, fetchText, runCommand } from "../lib/system.js";
|
|
3
3
|
import { addPack, fetchPackDocs, findPack, listPacks } from "../packs.js";
|
|
4
|
-
import { requireEstate } from "
|
|
4
|
+
import { requireEstate } from "../lib/env.js";
|
|
5
5
|
export function registerAdd(program, env) {
|
|
6
6
|
program
|
|
7
7
|
.command("add")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { exitCode, formatReports } from "../format.js";
|
|
3
|
-
import { findProducts, runChecks } from "../
|
|
4
|
-
import { requireEstate } from "
|
|
3
|
+
import { findProducts, runChecks } from "../check.js";
|
|
4
|
+
import { requireEstate } from "../lib/env.js";
|
|
5
5
|
export function registerCheck(program, env) {
|
|
6
6
|
program
|
|
7
7
|
.command("check")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import { type Env } from "
|
|
2
|
+
import { type Env } from "../lib/env.ts";
|
|
3
3
|
export declare const HOOK = "#!/bin/sh\n# installed by `canon hooks install` \u2014 run it again rather than editing this file.\n# A machine without canon pushes unchecked, as it would without any hook.\ncommand -v canon >/dev/null 2>&1 || exit 0\n# The product is the folder that owns .git, not the folder being pushed from: a worktree shares these hooks.\nroot=$(cd \"$(git rev-parse --git-common-dir)/..\" && pwd) || exit 0\nexec canon check \"$root\"\n";
|
|
4
4
|
/** Writes the pre-push hook into one repo. Returns a one-line outcome. */
|
|
5
5
|
export declare function installHook(repo: string): string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { chmodSync, existsSync, lstatSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { findProducts } from "../
|
|
5
|
-
import { requireEstate } from "
|
|
4
|
+
import { findProducts } from "../check.js";
|
|
5
|
+
import { requireEstate } from "../lib/env.js";
|
|
6
6
|
const MARK = "installed by `canon hooks install`";
|
|
7
7
|
// Hooks written by the tools this command replaced (the estate script, then zeno); they are ours to overwrite.
|
|
8
8
|
const OLD_MARKS = [MARK, "installed by ~/git/bin/install-hooks", "installed by `zeno hooks install`"];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { basename, resolve } from "node:path";
|
|
2
2
|
import * as prompts from "@clack/prompts";
|
|
3
3
|
import { Option } from "commander";
|
|
4
|
-
import { KINDS, MODULES } from "../config/schema.js";
|
|
4
|
+
import { KINDS, kindCovers, MODULES } from "../config/schema.js";
|
|
5
5
|
import { RULES_FILES } from "../checks/architecture.js";
|
|
6
6
|
import { applyInit, parseChoices, planInit } from "../init.js";
|
|
7
7
|
import { listPacks } from "../packs.js";
|
|
@@ -29,7 +29,7 @@ async function askAnswers(inferred, packs) {
|
|
|
29
29
|
});
|
|
30
30
|
if (cancelled(modules))
|
|
31
31
|
return null;
|
|
32
|
-
const forKind = packs.filter((pack) => pack.kinds.
|
|
32
|
+
const forKind = packs.filter((pack) => pack.kinds.some((wanted) => kindCovers(kind, wanted)));
|
|
33
33
|
if (forKind.length === 0)
|
|
34
34
|
return { kind, modules, packs: [] };
|
|
35
35
|
const chosen = await prompts.multiselect({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { proposals, readDecisions, readVerdicts, recordDecision, summarise } from "../learn.js";
|
|
2
|
-
import { findProducts } from "../
|
|
3
|
-
import { requireEstate } from "
|
|
2
|
+
import { findProducts } from "../check.js";
|
|
3
|
+
import { requireEstate } from "../lib/env.js";
|
|
4
4
|
const DEFAULT_THRESHOLD = 3;
|
|
5
5
|
export function registerLearn(program, env) {
|
|
6
6
|
program
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { exitCode, formatReports } from "../format.js";
|
|
3
3
|
import { CACHE_DIR, fetchText, runCommand } from "../lib/system.js";
|
|
4
|
-
import { runChecks } from "../
|
|
4
|
+
import { runChecks } from "../check.js";
|
|
5
5
|
import { syncRepo } from "../sync.js";
|
|
6
6
|
export function registerSync(program, env) {
|
|
7
7
|
program
|
package/cli/dist/commands/ui.js
CHANGED
|
@@ -4,9 +4,9 @@ import { join } from "node:path";
|
|
|
4
4
|
import { architectureGraph } from "../checks/architecture.js";
|
|
5
5
|
import { formatReports } from "../format.js";
|
|
6
6
|
import { CACHE_DIR } from "../lib/system.js";
|
|
7
|
-
import { findProducts, runChecks } from "../
|
|
7
|
+
import { findProducts, runChecks } from "../check.js";
|
|
8
8
|
import { buildSite, quartzConfig } from "../ui.js";
|
|
9
|
-
import { requireEstate } from "
|
|
9
|
+
import { requireEstate } from "../lib/env.js";
|
|
10
10
|
// Quartz v5 has no tagged release yet, so pin the exact commit: an unpinned
|
|
11
11
|
// clone could change the site under us on the next fresh install.
|
|
12
12
|
const QUARTZ_REPO = "https://github.com/jackyzha0/quartz";
|
package/cli/dist/config/load.js
CHANGED
|
@@ -36,14 +36,26 @@ function appRoots(repo) {
|
|
|
36
36
|
const children = readdirSync(apps, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
37
37
|
return [repo, ...children.map((entry) => join(apps, entry.name))];
|
|
38
38
|
}
|
|
39
|
+
/** A root package.json that ships a CLI or exports a library. */
|
|
40
|
+
function isPackage(repo) {
|
|
41
|
+
const file = join(repo, "package.json");
|
|
42
|
+
if (!existsSync(file))
|
|
43
|
+
return false;
|
|
44
|
+
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
45
|
+
return Boolean(pkg.bin || pkg.exports || pkg.main) && !pkg.private;
|
|
46
|
+
}
|
|
39
47
|
export function inferKind(repo) {
|
|
40
48
|
const roots = appRoots(repo);
|
|
41
49
|
const anyHas = (markers) => roots.some((root) => markers.some((file) => existsSync(join(root, file))));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
50
|
+
const mobile = anyHas(EXPO_MARKERS);
|
|
51
|
+
const web = anyHas(WEB_MARKERS);
|
|
52
|
+
if (mobile && web)
|
|
53
|
+
return "mobile-and-web";
|
|
54
|
+
if (mobile)
|
|
55
|
+
return "mobile";
|
|
56
|
+
if (web)
|
|
45
57
|
return "web";
|
|
46
|
-
return "content";
|
|
58
|
+
return isPackage(repo) ? "package" : "content";
|
|
47
59
|
}
|
|
48
60
|
export function inferModules(repo, kind) {
|
|
49
61
|
const modules = MODULES_BY_KIND[kind];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
2
|
+
/** What a repo ships: an app to the stores, a site to the web, both, a package to a registry, or no code at all. */
|
|
3
|
+
export declare const KINDS: readonly ["mobile", "web", "mobile-and-web", "package", "content"];
|
|
3
4
|
export declare const MODULES: readonly ["docs", "design", "knowledge", "metrics", "code", "architecture", "listing", "refs"];
|
|
4
5
|
/**
|
|
5
6
|
* The wiki page types every repo has, and the folder each lives in. A repo adds more under `wiki.types`.
|
|
@@ -26,6 +27,13 @@ declare const command: z.ZodObject<{
|
|
|
26
27
|
metrics: "metrics";
|
|
27
28
|
refs: "refs";
|
|
28
29
|
}>>;
|
|
30
|
+
kinds: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
31
|
+
content: "content";
|
|
32
|
+
mobile: "mobile";
|
|
33
|
+
"mobile-and-web": "mobile-and-web";
|
|
34
|
+
package: "package";
|
|
35
|
+
web: "web";
|
|
36
|
+
}>>>;
|
|
29
37
|
}, z.core.$strict>;
|
|
30
38
|
declare const wikiType: z.ZodObject<{
|
|
31
39
|
type: z.ZodString;
|
|
@@ -60,7 +68,9 @@ export declare const layerSchema: z.ZodObject<{
|
|
|
60
68
|
canon: z.ZodOptional<z.ZodString>;
|
|
61
69
|
kind: z.ZodOptional<z.ZodEnum<{
|
|
62
70
|
content: "content";
|
|
63
|
-
|
|
71
|
+
mobile: "mobile";
|
|
72
|
+
"mobile-and-web": "mobile-and-web";
|
|
73
|
+
package: "package";
|
|
64
74
|
web: "web";
|
|
65
75
|
}>>;
|
|
66
76
|
modules: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -92,6 +102,13 @@ export declare const layerSchema: z.ZodObject<{
|
|
|
92
102
|
metrics: "metrics";
|
|
93
103
|
refs: "refs";
|
|
94
104
|
}>>;
|
|
105
|
+
kinds: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
106
|
+
content: "content";
|
|
107
|
+
mobile: "mobile";
|
|
108
|
+
"mobile-and-web": "mobile-and-web";
|
|
109
|
+
package: "package";
|
|
110
|
+
web: "web";
|
|
111
|
+
}>>>;
|
|
95
112
|
}, z.core.$strict>>>;
|
|
96
113
|
doctor: z.ZodOptional<z.ZodObject<{
|
|
97
114
|
tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -178,4 +195,6 @@ export interface Config {
|
|
|
178
195
|
};
|
|
179
196
|
}
|
|
180
197
|
export declare const MODULES_BY_KIND: Record<Kind, Module[]>;
|
|
198
|
+
/** A repo that ships both counts as mobile and as web: it gets both kinds' packs. */
|
|
199
|
+
export declare function kindCovers(kind: Kind, wanted: Kind): boolean;
|
|
181
200
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
2
|
+
/** What a repo ships: an app to the stores, a site to the web, both, a package to a registry, or no code at all. */
|
|
3
|
+
export const KINDS = ["mobile", "web", "mobile-and-web", "package", "content"];
|
|
3
4
|
export const MODULES = ["docs", "design", "knowledge", "metrics", "code", "architecture", "listing", "refs"];
|
|
4
5
|
/**
|
|
5
6
|
* The wiki page types every repo has, and the folder each lives in. A repo adds more under `wiki.types`.
|
|
@@ -28,6 +29,8 @@ const command = z
|
|
|
28
29
|
if: z.string().optional(),
|
|
29
30
|
/** Only run in a repo that uses this module. */
|
|
30
31
|
module: z.enum(MODULES).optional(),
|
|
32
|
+
/** Only run in a repo of one of these kinds. */
|
|
33
|
+
kinds: z.array(z.enum(KINDS)).optional(),
|
|
31
34
|
})
|
|
32
35
|
.strict();
|
|
33
36
|
const kebab = z.string().regex(/^[a-z0-9-]+$/, "use lowercase-kebab");
|
|
@@ -100,7 +103,14 @@ export const layerSchema = z
|
|
|
100
103
|
.partial()
|
|
101
104
|
.strict();
|
|
102
105
|
export const MODULES_BY_KIND = {
|
|
103
|
-
|
|
106
|
+
mobile: ["docs", "knowledge", "code", "listing", "refs"],
|
|
104
107
|
web: ["docs", "knowledge", "code", "refs"],
|
|
108
|
+
"mobile-and-web": ["docs", "knowledge", "code", "listing", "refs"],
|
|
109
|
+
// A package's docs describe other repos' .context/ paths, so refs would flag them.
|
|
110
|
+
package: ["docs", "knowledge", "code"],
|
|
105
111
|
content: ["docs", "knowledge", "refs"],
|
|
106
112
|
};
|
|
113
|
+
/** A repo that ships both counts as mobile and as web: it gets both kinds' packs. */
|
|
114
|
+
export function kindCovers(kind, wanted) {
|
|
115
|
+
return kind === wanted || (kind === "mobile-and-web" && (wanted === "mobile" || wanted === "web"));
|
|
116
|
+
}
|
package/cli/dist/format.d.ts
CHANGED
package/cli/dist/index.js
CHANGED
|
@@ -16,8 +16,8 @@ import { registerSync } from "./commands/sync.js";
|
|
|
16
16
|
import { registerUi } from "./commands/ui.js";
|
|
17
17
|
import { registerWeigh } from "./commands/weigh.js";
|
|
18
18
|
import { findEstate } from "./config/load.js";
|
|
19
|
-
import { registerMcp } from "./
|
|
20
|
-
import { registerSearch } from "./
|
|
19
|
+
import { registerMcp } from "./commands/mcp.js";
|
|
20
|
+
import { registerSearch } from "./commands/search.js";
|
|
21
21
|
import { canonVersion } from "./status.js";
|
|
22
22
|
const env = {
|
|
23
23
|
canonRoot: resolve(import.meta.dirname, "..", ".."),
|
package/cli/dist/init.js
CHANGED
|
@@ -4,7 +4,7 @@ import { stringify } from "yaml";
|
|
|
4
4
|
import { RULES_FILES } from "./checks/architecture.js";
|
|
5
5
|
import { requiredContext } from "./checks/context-layout.js";
|
|
6
6
|
import { inferKind, inferModules } from "./config/load.js";
|
|
7
|
-
import { MODULES_BY_KIND } from "./config/schema.js";
|
|
7
|
+
import { kindCovers, MODULES_BY_KIND } from "./config/schema.js";
|
|
8
8
|
export const ARCHITECTURE_BASE = ".dependency-cruiser.base.cjs";
|
|
9
9
|
const ARCHITECTURE_TEMPLATES = join("templates", "dependency-cruiser");
|
|
10
10
|
const WORKSPACE_PARENTS = ["apps", "packages"];
|
|
@@ -86,7 +86,7 @@ export function planInit(repo, answers) {
|
|
|
86
86
|
}
|
|
87
87
|
const kind = answers.kind ?? inferKind(repo);
|
|
88
88
|
const modules = answers.modules ?? (answers.kind ? MODULES_BY_KIND[kind] : inferModules(repo, kind));
|
|
89
|
-
const sdk = kind
|
|
89
|
+
const sdk = kindCovers(kind, "mobile") ? detectExpoSdk(repo) : undefined;
|
|
90
90
|
const packs = answers.packs?.length ? { packs: answers.packs } : {};
|
|
91
91
|
const config = { canon: answers.canonVersion, kind, modules, ...(sdk ? { expo: { sdk } } : {}), ...packs };
|
|
92
92
|
const create = requiredContext(modules).filter((name) => !existsSync(join(repo, ".context", name))).map((name) => FIRST_FILE[name]);
|
package/cli/dist/packs.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ declare const packSchema: z.ZodObject<{
|
|
|
4
4
|
description: z.ZodString;
|
|
5
5
|
kinds: z.ZodArray<z.ZodEnum<{
|
|
6
6
|
content: "content";
|
|
7
|
-
|
|
7
|
+
mobile: "mobile";
|
|
8
|
+
"mobile-and-web": "mobile-and-web";
|
|
9
|
+
package: "package";
|
|
8
10
|
web: "web";
|
|
9
11
|
}>>;
|
|
10
12
|
skill: z.ZodOptional<z.ZodString>;
|
package/cli/dist/status.d.ts
CHANGED
package/cli/dist/status.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { loadConfig } from "./config/load.js";
|
|
4
|
-
import { runChecks } from "./
|
|
4
|
+
import { runChecks } from "./check.js";
|
|
5
5
|
export function canonVersion(canonRoot) {
|
|
6
6
|
const manifest = join(canonRoot, ".claude-plugin", "plugin.json");
|
|
7
7
|
if (!existsSync(manifest))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freetesh/canon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "canon CLI: keep your domain knowledge and AI rules in your repo, and enforce them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ritesh Firodiya",
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"test": "vitest run --root cli",
|
|
36
36
|
"coverage": "vitest run --root cli --coverage",
|
|
37
37
|
"typecheck": "tsc -p cli",
|
|
38
|
-
"prepack": "pnpm build"
|
|
39
|
-
"release": "bash scripts/publish.sh"
|
|
38
|
+
"prepack": "pnpm build"
|
|
40
39
|
},
|
|
41
40
|
"dependencies": {
|
|
42
41
|
"@clack/prompts": "^1.8.1",
|
package/packs/expo/pack.yaml
CHANGED
package/packs/firebase/pack.yaml
CHANGED
package/packs/react/pack.yaml
CHANGED
package/packs/uniwind/pack.yaml
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|