@opencanon/canon 0.1.1 → 0.2.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/README.md CHANGED
@@ -1,36 +1,171 @@
1
1
  # @opencanon/canon
2
2
 
3
- Canon worldbuilding CLI scaffold, validate, and manage shared fiction universes.
3
+ Validation toolkit for shared fiction worldbuilding. CLI + library.
4
+
5
+ - **CLI binary**: `canon` — scaffold repos, run compliance checks, generate lock files
6
+ - **Library**: pure validation core + adapters (filesystem, GitHub)
7
+ - **Schema**: metadata v1.2, check report v2
8
+
9
+ ---
4
10
 
5
11
  ## Install
6
12
 
7
13
  ```bash
8
- npm install -g @opencanon/canon
14
+ npm install -g @opencanon/canon # CLI (global)
15
+ npm install @opencanon/canon # Library (local)
9
16
  ```
10
17
 
11
- ## CLI Usage
18
+ ---
12
19
 
13
- ```bash
14
- canon init # Scaffold a new canon repo
15
- canon new story <id> # Create a new story from template
16
- canon new character <id> # Create a new character
17
- canon new location <id> # Create a new location
18
- canon check [dir] # Run compliance checks (exit 1 on failure)
19
- canon lock [dir] # Regenerate canon.lock.json
20
+ ## CLI Commands
21
+
22
+ | Command | Description | Exit code |
23
+ |---|---|---|
24
+ | `canon init [dir]` | Scaffold repo: `canon/`, `stories/`, `.canonrc.json`, `CONVENTIONS.md` | 0 |
25
+ | `canon new story <id>` | Create `stories/<id>/metadata.json` from template | 0 or 1 |
26
+ | `canon new character <id>` | Create `canon/characters/<id>/definition.json` | 0 or 1 |
27
+ | `canon new location <id>` | Create `canon/worldbuilding/locations/<id>.json` | 0 or 1 |
28
+ | `canon check [dir]` | Run all compliance checks. **0** = all pass, **1** = any fail or no stories | 0 or 1 |
29
+ | `canon lock [dir]` | Run compliance pre-check, then regenerate `canon.lock.json`. Genesis lock skips pre-check. | 0 or 1 |
30
+
31
+ ID constraint: `/^[a-z0-9][a-z0-9_-]*$/` — lowercase alphanumeric, hyphens, underscores.
32
+
33
+ ---
34
+
35
+ ## Compliance Checks
36
+
37
+ Seven checks run per story. Check IDs are stable (additive-only, never removed).
38
+
39
+ | Check ID | Rule |
40
+ |---|---|
41
+ | `metadata_schema_valid` | Required fields present, correct types, `schema_version === "1.2"` |
42
+ | `characters_valid` | Every `characters[]` entry exists in `canon/characters/` |
43
+ | `locations_valid` | Every `locations[]` entry exists in `canon/worldbuilding/locations/` |
44
+ | `timeline_consistent` | `timeline` is strict ISO date `YYYY-MM-DD`, round-trip validated |
45
+ | `continuity_valid` | `temporal_context.prev_episode`, `next_episode`, `thematic_echoes` all resolve |
46
+ | `canon_version_match` | `canon_ref` matches `canon.lock.json` `canon_commit` |
47
+ | `contributor_valid` | `contributor` field is present and non-empty |
48
+
49
+ ---
50
+
51
+ ## Library Exports
52
+
53
+ Three entry points. Core is pure (no I/O). Adapters convert external data into `RepoModel`.
54
+
55
+ ### `@opencanon/canon` — core
56
+
57
+ ```ts
58
+ import {
59
+ validateRepo, // (model: RepoModel) => RepoCheckReport
60
+ validateStory, // single story validation
61
+ checkCharacters, checkLocations, checkTimeline,
62
+ checkContinuity, checkCanonVersion, checkContributor,
63
+ checkMetadataSchema,
64
+ type RepoModel, type RepoCheckReport, type StoryCheckReport,
65
+ type CheckResult, type CheckId,
66
+ type CanonLock, type StoryMetadata, type CanonConfig,
67
+ type GitHubRepoInput, type GitHubTreeEntry,
68
+ } from "@opencanon/canon"
20
69
  ```
21
70
 
22
- ## Library Usage
71
+ `RepoCheckReport.schemaVersion` is always `"check.v2"`. Includes `summary: { score, totalChecks, passingChecks }`.
72
+
73
+ ### `@opencanon/canon/adapters/fs` — filesystem
23
74
 
24
75
  ```ts
25
- import { validateRepo } from "@opencanon/canon"
26
76
  import { loadRepoFromFs } from "@opencanon/canon/adapters/fs"
77
+ const model: RepoModel = loadRepoFromFs("/path/to/repo")
78
+ ```
79
+
80
+ Reads `canon/`, `stories/`, `canon.lock.json` from disk. Returns `RepoModel`.
81
+
82
+ ### `@opencanon/canon/adapters/github` — GitHub (pure conversion)
83
+
84
+ ```ts
27
85
  import { buildRepoModel } from "@opencanon/canon/adapters/github"
86
+ const model: RepoModel = buildRepoModel({ tree, files })
28
87
  ```
29
88
 
89
+ Takes pre-fetched GitHub Trees API entries + file contents. No HTTP, no Octokit. Your app fetches, this function converts.
90
+
91
+ **`GitHubRepoInput`**:
92
+ - `tree`: `{ path: string, type: "blob" | "tree", sha: string }[]`
93
+ - `files`: `Map<string, string>` — path → content for `canon.lock.json`, `stories/*/metadata.json`
94
+
95
+ ---
96
+
97
+ ## Repo Structure
98
+
99
+ ```
100
+ <repo-root>/
101
+ ├── canon/
102
+ │ ├── characters/<id>/definition.json
103
+ │ └── worldbuilding/locations/<id>.json
104
+ ├── stories/<slug>/
105
+ │ ├── metadata.json # required
106
+ │ └── chapter-*.md # content
107
+ ├── canon.lock.json # generated by `canon lock`
108
+ ├── .canonrc.json # generated by `canon init`
109
+ └── CONVENTIONS.md # generated by `canon init`
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Metadata Schema v1.2
115
+
116
+ ```jsonc
117
+ {
118
+ "schema_version": "1.2", // fixed
119
+ "canon_ref": "<commit-sha>", // must match canon.lock.json
120
+ "id": "episode-slug", // unique story ID
121
+ "episode": 1, // number
122
+ "title": { "ko": "...", "en": "..." },
123
+ "timeline": "2025-01-15", // strict YYYY-MM-DD
124
+ "synopsis": { "ko": "...", "en": "..." },
125
+ "characters": ["alice", "bob"], // must exist in canon/characters/
126
+ "locations": ["market-square"], // must exist in canon/worldbuilding/locations/
127
+ "contributor": "github-username", // immutable after creation
128
+ "canon_status": "canonical", // "canonical" | "non-canonical"
129
+ // optional:
130
+ "themes": ["identity"],
131
+ "canon_events": ["first-contact"],
132
+ "word_count": { "ko": 3200, "en": 2800 },
133
+ "temporal_context": {
134
+ "prev_episode": "episode-slug-prev",
135
+ "next_episode": null,
136
+ "thematic_echoes": ["related-slug"]
137
+ }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Lock File
144
+
145
+ `canon.lock.json` is generated by `canon lock`. Do not edit manually.
146
+
147
+ ```json
148
+ {
149
+ "schema_version": "canon.lock.v2",
150
+ "canon_commit": "<git-HEAD-sha>",
151
+ "worldbuilding_hash": "<sha256-hex>",
152
+ "hash_algo": "sha256",
153
+ "generated_at": "2025-01-15T00:00:00.000Z",
154
+ "contributors": ["github-username"]
155
+ }
156
+ ```
157
+
158
+ Hash algorithm: for each file under `canon/` in lexicographic path order, feed `relativePath + \0 + fileBytes + \0` into SHA-256.
159
+
160
+ `canon lock` runs compliance pre-check before generating. If any story fails → lock refused (exit 1). First lock (no existing `canon.lock.json`) skips pre-check.
161
+
30
162
  ---
31
163
 
32
- - [English docs](docs/en/README.md)
33
- - [한국어 문서](docs/ko/README.md)
164
+ ## Migration from v1.1
165
+
166
+ See [MIGRATION-v1.2.md](./MIGRATION-v1.2.md).
167
+
168
+ ---
34
169
 
35
170
  ## License
36
171
 
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ const program = new Command();
8
8
  program
9
9
  .name("canon")
10
10
  .description("Canon worldbuilding CLI — scaffold, validate, and manage shared fiction universes")
11
- .version("0.1.0");
11
+ .version("0.2.0");
12
12
  program.addCommand(checkCommand);
13
13
  program.addCommand(lockCommand);
14
14
  program.addCommand(initCommand);
@@ -3,6 +3,8 @@ import { resolve, join, relative, sep } from "node:path";
3
3
  import { readFileSync, readdirSync, writeFileSync, existsSync } from "node:fs";
4
4
  import { createHash } from "node:crypto";
5
5
  import { execSync } from "node:child_process";
6
+ import { loadRepoFromFs } from "../adapters/fs.js";
7
+ import { validateRepo } from "../core/validate.js";
6
8
  function collectFiles(dir) {
7
9
  const results = [];
8
10
  if (!existsSync(dir))
@@ -29,6 +31,22 @@ export const lockCommand = new Command("lock")
29
31
  console.error("Error: canon/ directory not found");
30
32
  process.exit(1);
31
33
  }
34
+ // Pre-check: all stories must pass compliance before lock is allowed.
35
+ // Skip if no canon.lock.json exists yet (genesis lock — nothing to check against).
36
+ const model = loadRepoFromFs(repoRoot);
37
+ const isGenesis = model.canonLock === null;
38
+ if (!isGenesis) {
39
+ const report = validateRepo(model);
40
+ if (report.totalStories > 0 && report.passingStories < report.totalStories) {
41
+ const failing = report.stories.filter(s => !s.allPass);
42
+ console.error("Error: compliance check failed — lock refused");
43
+ for (const s of failing) {
44
+ const fails = s.checks.filter(c => !c.pass).map(c => c.id).join(", ");
45
+ console.error(` ${s.storyId}: ${fails}`);
46
+ }
47
+ process.exit(1);
48
+ }
49
+ }
32
50
  // git rev-parse HEAD — hard error on failure
33
51
  let canonCommit;
34
52
  try {
@@ -58,12 +76,32 @@ export const lockCommand = new Command("lock")
58
76
  hash.update("\0");
59
77
  }
60
78
  const worldbuildingHash = hash.digest("hex");
79
+ // Extract unique contributors from story metadata (append-only)
80
+ const contributors = new Set();
81
+ // Preserve existing contributors from previous lock
82
+ const existingLockPath = join(repoRoot, "canon.lock.json");
83
+ if (existsSync(existingLockPath)) {
84
+ try {
85
+ const prev = JSON.parse(readFileSync(existingLockPath, "utf-8"));
86
+ if (Array.isArray(prev.contributors)) {
87
+ for (const c of prev.contributors)
88
+ contributors.add(c);
89
+ }
90
+ }
91
+ catch { /* ignore malformed lock */ }
92
+ }
93
+ // Add contributors from current stories
94
+ for (const [, { meta }] of model.stories) {
95
+ if (meta.contributor)
96
+ contributors.add(meta.contributor);
97
+ }
61
98
  const lock = {
62
- schema_version: "canon.lock.v1",
99
+ schema_version: "canon.lock.v2",
63
100
  canon_commit: canonCommit,
64
101
  worldbuilding_hash: worldbuildingHash,
65
102
  hash_algo: "sha256",
66
103
  generated_at: new Date().toISOString(),
104
+ contributors: [...contributors].sort(),
67
105
  };
68
106
  const lockPath = join(repoRoot, "canon.lock.json");
69
107
  writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n");
@@ -1 +1 @@
1
- {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/commands/lock.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Command } from "commander";
2
2
  import { resolve, join } from "node:path";
3
- import { mkdirSync, writeFileSync, existsSync } from "node:fs";
3
+ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
4
4
  import { metadataTemplate } from "../templates/metadata.js";
5
5
  import { characterTemplate } from "../templates/character.js";
6
6
  import { locationTemplate } from "../templates/location.js";
@@ -31,8 +31,18 @@ export const newCommand = new Command("new")
31
31
  console.error(`Error: stories/${slug} already exists`);
32
32
  process.exit(1);
33
33
  }
34
+ // Read contributor from .canonrc.json author field
35
+ let contributor = "";
36
+ const rcPath = join(root, ".canonrc.json");
37
+ if (existsSync(rcPath)) {
38
+ try {
39
+ const rc = JSON.parse(readFileSync(rcPath, "utf-8"));
40
+ contributor = rc.author || "";
41
+ }
42
+ catch { /* ignore malformed rc */ }
43
+ }
34
44
  mkdirSync(storyDir, { recursive: true });
35
- writeFileSync(join(storyDir, "metadata.json"), metadataTemplate(slug));
45
+ writeFileSync(join(storyDir, "metadata.json"), metadataTemplate(slug, contributor));
36
46
  console.log(`Created stories/${slug}/metadata.json`);
37
47
  break;
38
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
1
  export type { CanonLock, StoryMetadata, CharacterDefinition, LocationDefinition, RepoModel, GitHubTreeEntry, GitHubRepoInput, CheckId, CheckResult, StoryCheckReport, RepoCheckReport, CanonConfig, } from "./types.js";
2
- export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
2
+ export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkContributor, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
@@ -1,2 +1,2 @@
1
- export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
1
+ export { checkCharacters, checkLocations, checkTimeline, checkContinuity, checkCanonVersion, checkContributor, checkMetadataSchema, validateStory, validateRepo, } from "./validate.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAA"}
@@ -3,14 +3,15 @@
3
3
  * No protocol internals — these are platform-facing types only.
4
4
  */
5
5
  export interface CanonLock {
6
- schema_version: "canon.lock.v1";
6
+ schema_version: "canon.lock.v2";
7
7
  canon_commit: string;
8
8
  worldbuilding_hash: string;
9
9
  hash_algo: "sha256";
10
10
  generated_at: string;
11
+ contributors: string[];
11
12
  }
12
13
  export interface StoryMetadata {
13
- schema_version: "1.1";
14
+ schema_version: "1.2";
14
15
  canon_ref: string;
15
16
  id: string;
16
17
  episode: number;
@@ -25,6 +26,7 @@ export interface StoryMetadata {
25
26
  };
26
27
  characters: string[];
27
28
  locations: string[];
29
+ contributor: string;
28
30
  themes?: string[];
29
31
  canon_events?: string[];
30
32
  canon_status: "canonical" | "non-canonical";
@@ -79,7 +81,7 @@ export interface GitHubRepoInput {
79
81
  tree: GitHubTreeEntry[];
80
82
  files: Map<string, string>;
81
83
  }
82
- export type CheckId = "metadata_schema_valid" | "characters_valid" | "locations_valid" | "timeline_consistent" | "continuity_valid" | "canon_version_match";
84
+ export type CheckId = "metadata_schema_valid" | "characters_valid" | "locations_valid" | "timeline_consistent" | "continuity_valid" | "canon_version_match" | "contributor_valid";
83
85
  export interface CheckResult {
84
86
  id: CheckId;
85
87
  pass: boolean;
@@ -91,7 +93,12 @@ export interface StoryCheckReport {
91
93
  allPass: boolean;
92
94
  }
93
95
  export interface RepoCheckReport {
94
- schemaVersion: "check.v1";
96
+ schemaVersion: "check.v2";
97
+ summary: {
98
+ score: number;
99
+ totalChecks: number;
100
+ passingChecks: number;
101
+ };
95
102
  stories: StoryCheckReport[];
96
103
  totalStories: number;
97
104
  passingStories: number;
@@ -29,6 +29,10 @@ export declare function checkCanonVersion(meta: StoryMetadata, canonLock: CanonL
29
29
  * Validate metadata.json schema conformance.
30
30
  */
31
31
  export declare function checkMetadataSchema(meta: Record<string, unknown>): CheckResult;
32
+ /**
33
+ * Validate that contributor field is present and non-empty.
34
+ */
35
+ export declare function checkContributor(meta: StoryMetadata): CheckResult;
32
36
  /**
33
37
  * Run all checks for a single story.
34
38
  */
@@ -66,13 +66,13 @@ export function checkCanonVersion(meta, canonLock) {
66
66
  * Validate metadata.json schema conformance.
67
67
  */
68
68
  export function checkMetadataSchema(meta) {
69
- const required = ["schema_version", "canon_ref", "id", "episode", "title", "timeline", "synopsis", "characters", "locations", "canon_status"];
69
+ const required = ["schema_version", "canon_ref", "id", "episode", "title", "timeline", "synopsis", "characters", "locations", "contributor", "canon_status"];
70
70
  const missing = required.filter(f => !(f in meta));
71
71
  if (missing.length > 0) {
72
72
  return check("metadata_schema_valid", false, `Missing fields: ${missing.join(", ")}`);
73
73
  }
74
- if (meta.schema_version !== "1.1") {
75
- return check("metadata_schema_valid", false, `Expected schema_version "1.1", got "${meta.schema_version}"`);
74
+ if (meta.schema_version !== "1.2") {
75
+ return check("metadata_schema_valid", false, `Expected schema_version "1.2", got "${meta.schema_version}"`);
76
76
  }
77
77
  if (typeof meta.episode !== "number") {
78
78
  return check("metadata_schema_valid", false, `episode must be a number`);
@@ -89,6 +89,13 @@ export function checkMetadataSchema(meta) {
89
89
  }
90
90
  return check("metadata_schema_valid", true);
91
91
  }
92
+ /**
93
+ * Validate that contributor field is present and non-empty.
94
+ */
95
+ export function checkContributor(meta) {
96
+ const valid = typeof meta.contributor === "string" && meta.contributor.trim().length > 0;
97
+ return check("contributor_valid", valid, valid ? undefined : "contributor must be a non-empty string");
98
+ }
92
99
  /**
93
100
  * Run all checks for a single story.
94
101
  */
@@ -100,6 +107,7 @@ export function validateStory(input) {
100
107
  checkTimeline(input.meta),
101
108
  checkContinuity(input.meta, input.knownEpisodes),
102
109
  checkCanonVersion(input.meta, input.canonLock),
110
+ checkContributor(input.meta),
103
111
  ];
104
112
  return {
105
113
  storyId: input.meta.id,
@@ -123,11 +131,20 @@ export function validateRepo(model) {
123
131
  canonLock: model.canonLock,
124
132
  }));
125
133
  }
134
+ const totalStories = stories.length;
135
+ const passingStories = stories.filter(s => s.allPass).length;
136
+ const totalChecks = stories.reduce((sum, s) => sum + s.checks.length, 0);
137
+ const passingChecks = stories.reduce((sum, s) => sum + s.checks.filter(c => c.pass).length, 0);
126
138
  return {
127
- schemaVersion: "check.v1",
139
+ schemaVersion: "check.v2",
140
+ summary: {
141
+ score: totalStories > 0 ? passingStories / totalStories : 0,
142
+ totalChecks,
143
+ passingChecks,
144
+ },
128
145
  stories,
129
- totalStories: stories.length,
130
- passingStories: stories.filter(s => s.allPass).length,
146
+ totalStories,
147
+ passingStories,
131
148
  };
132
149
  }
133
150
  //# sourceMappingURL=validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/core/validate.ts"],"names":[],"mappings":"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"}
@@ -19,13 +19,13 @@ canon.lock.json # Generated — do not edit manually
19
19
  CONVENTIONS.md # This file
20
20
  \`\`\`
21
21
 
22
- ## Metadata Schema (v1.1)
22
+ ## Metadata Schema (v1.2)
23
23
 
24
24
  Every story directory must contain a \`metadata.json\` with these required fields:
25
25
 
26
26
  | Field | Type | Description |
27
27
  |------------------|----------|--------------------------------------|
28
- | schema_version | "1.1" | Fixed schema version |
28
+ | schema_version | "1.2" | Fixed schema version |
29
29
  | canon_ref | string | Commit SHA from canon.lock.json |
30
30
  | id | string | Unique story identifier (slug) |
31
31
  | episode | number | Episode number |
@@ -34,6 +34,7 @@ Every story directory must contain a \`metadata.json\` with these required field
34
34
  | synopsis | {ko, en} | Bilingual synopsis |
35
35
  | characters | string[] | Character IDs used in this story |
36
36
  | locations | string[] | Location IDs used in this story |
37
+ | contributor | string | GitHub username (immutable) |
37
38
  | canon_status | string | "canonical" or "non-canonical" |
38
39
 
39
40
  ## Compliance Checks
@@ -45,6 +46,7 @@ Run \`canon check\` to validate:
45
46
  - **timeline_consistent** — timeline is a valid ISO date
46
47
  - **continuity_valid** — temporal_context references point to existing episodes
47
48
  - **canon_version_match** — canon_ref matches canon.lock.json commit
49
+ - **contributor_valid** — contributor field is present and non-empty
48
50
  `;
49
51
  }
50
52
  //# sourceMappingURL=conventions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/templates/conventions.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CR,CAAA;AACD,CAAC"}
1
+ {"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/templates/conventions.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDR,CAAA;AACD,CAAC"}
@@ -1 +1 @@
1
- export declare function metadataTemplate(id: string): string;
1
+ export declare function metadataTemplate(id: string, contributor?: string): string;
@@ -1,6 +1,6 @@
1
- export function metadataTemplate(id) {
1
+ export function metadataTemplate(id, contributor = "") {
2
2
  return JSON.stringify({
3
- schema_version: "1.1",
3
+ schema_version: "1.2",
4
4
  canon_ref: "",
5
5
  id,
6
6
  episode: 0,
@@ -9,6 +9,7 @@ export function metadataTemplate(id) {
9
9
  synopsis: { ko: "", en: "" },
10
10
  characters: [],
11
11
  locations: [],
12
+ contributor,
12
13
  canon_status: "non-canonical",
13
14
  }, null, 2) + "\n";
14
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/templates/metadata.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,EAAE;QACb,EAAE;QACF,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACzB,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC5B,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE;QACb,YAAY,EAAE,eAAe;KAC9B,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/templates/metadata.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,WAAW,GAAG,EAAE;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,EAAE;QACb,EAAE;QACF,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACzB,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC5B,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE;QACb,WAAW;QACX,YAAY,EAAE,eAAe;KAC9B,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AACpB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencanon/canon",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Canon worldbuilding CLI — scaffold, validate, and manage shared fiction universes",
5
5
  "type": "module",
6
6
  "bin": {