reffy-cli 1.0.0 → 1.1.2

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
@@ -4,7 +4,7 @@
4
4
  [![MIT License](https://img.shields.io/github/license/RoskiDeluge/reffy-ts.svg)](LICENSE)
5
5
  [![CI](https://github.com/RoskiDeluge/reffy-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/RoskiDeluge/reffy-ts/actions/workflows/ci.yml)
6
6
 
7
- Reffy is a CLI-first planning/runtime system for agent-friendly development workflows. It keeps ideation artifacts in straightforward version-controlled markdown files and uses `reffyspec/` as the canonical planning/spec layout.
7
+ Reffy is a CLI-first planning system for agent-friendly development workflows. It keeps ideation artifacts in straightforward version-controlled markdown files and manages formal planning files under `reffyspec/`.
8
8
 
9
9
  ## Install
10
10
 
@@ -17,22 +17,22 @@ npm install -g reffy-cli@latest
17
17
  Inside your project:
18
18
 
19
19
  ```bash
20
+ cd your-project
20
21
  reffy init
21
- reffy bootstrap
22
22
  ```
23
23
 
24
24
  Command summary:
25
25
 
26
- - `reffy init`: idempotently creates/updates root `AGENTS.md` managed block and `.reffy/AGENTS.md`.
27
- - `reffy bootstrap`: idempotently runs `init`, ensures `.reffy/` structure exists, then reindexes artifacts.
28
- - `reffy migrate`: migrates a legacy `.references/` workspace into the canonical `.reffy/` layout.
26
+ - `reffy init`: runs the canonical first-run setup flow, creates the `.reffy/` and `reffyspec/` structure, writes managed instructions, reindexes artifacts, and on first-run text output prints a copy/paste instruction for your agent harness.
27
+ - `reffy bootstrap`: compatibility alias for `reffy init`.
28
+ - `reffy migrate`: migrates a legacy `.references/` workspace into `.reffy/`.
29
29
  - `reffy doctor`: diagnoses required Reffy setup and workspace health.
30
30
  - `reffy reindex`: reconciles `.reffy/manifest.json` with `.reffy/artifacts` by adding missing files and removing stale entries.
31
31
  - `reffy validate`: validates `.reffy/manifest.json` against manifest v1 contract.
32
32
  - `reffy summarize`: generates a read-only handoff summary from indexed artifacts.
33
- - `reffy plan create`: generates proposal/tasks/spec scaffolds into the canonical `reffyspec/` layout from indexed Reffy artifacts.
34
- - `reffy plan validate|list|show|archive`: runs the planning lifecycle natively inside Reffy against `reffyspec/`.
35
- - `reffy spec list|show`: inspects current spec state natively from `reffyspec/`.
33
+ - `reffy plan create`: generates proposal, task, design, and spec scaffolds from indexed Reffy artifacts.
34
+ - `reffy plan validate|list|show|archive`: manages the planning lifecycle under `reffyspec/`.
35
+ - `reffy spec list|show`: inspects current spec state under `reffyspec/`.
36
36
  - `reffy diagram render`: renders Mermaid diagrams as SVG or ASCII, including spec-aware generation from compatible `spec.md` files.
37
37
 
38
38
  Output modes:
@@ -59,22 +59,11 @@ reffy diagram render --input reffyspec/specs/auth/spec.md --format ascii
59
59
  reffy diagram render --input reffyspec/specs/auth/spec.md --format svg --output .reffy/artifacts/auth-spec.svg
60
60
  ```
61
61
 
62
- ## Runtime Position
63
-
64
- Reffy is the planning/runtime authority in this repository. `reffyspec/` is the native planning/spec surface that matches that runtime ownership.
65
-
66
- That means:
67
-
68
- 1. Reffy owns ideation artifacts, planning scaffolds, inspection, validation, and archive lifecycle behavior.
69
- 2. The vendored fork at `.vendor/ReffySpec` is reference-only.
70
- 3. `reffyspec/` is canonical for active changes, archived changes, and current specs.
71
- 4. No external planning CLI is part of the normal workflow.
72
-
73
62
  ## Using Reffy With ReffySpec
74
63
 
75
64
  A practical pattern is:
76
65
 
77
- 1. Use Reffy for ideation and context capture in `.reffy/`.
66
+ 1. Use Reffy for ideation and context capture in `.reffy/artifacts/`.
78
67
  2. Use Reffy to scaffold and manage planning files in `reffyspec/`.
79
68
  3. Keep a clear traceable path from exploratory artifacts to formal specs.
80
69
  4. Use Reffy commands for day-to-day workflow.
@@ -84,36 +73,9 @@ Reference implementation in this repo:
84
73
  - `AGENTS.md`: contains both managed instruction blocks and encodes sequencing.
85
74
  - `AGENTS.md`: Reffy block routes ideation/exploration requests to `@/.reffy/AGENTS.md`.
86
75
  - `AGENTS.md`: ReffySpec block routes planning/proposal/spec requests to `@/reffyspec/AGENTS.md`.
87
- - `.reffy/AGENTS.md`: defines Reffy as the primary runtime and documents compatibility expectations.
76
+ - `.reffy/AGENTS.md`: defines the artifact and ideation workflow.
88
77
  - `reffyspec/AGENTS.md`: defines the ReffySpec planning/spec workflow conventions used in this repo.
89
- - `src/cli.ts`: `reffy init`/`reffy bootstrap` enforce this integration by idempotently writing the managed guidance into `AGENTS.md`, `.reffy/AGENTS.md`, and `reffyspec/AGENTS.md`.
90
-
91
- Practical connection pattern for any repo:
92
-
93
- 1. Run `reffy init` to install/refresh the Reffy instruction layer.
94
- 2. Keep ReffySpec instructions in the same root `AGENTS.md`.
95
- 3. During planning, cite only relevant Reffy artifacts from `.reffy/artifacts/` in your proposal/spec docs.
96
- 4. Prefer Reffy commands for day-to-day workflow.
97
-
98
- ## Current Boundary
99
-
100
- The current project boundary is:
101
-
102
- - Native Reffy ownership:
103
- - `reffy plan create`
104
- - `reffy plan validate`
105
- - `reffy plan list`
106
- - `reffy plan show`
107
- - `reffy plan archive`
108
- - `reffy spec list`
109
- - `reffy spec show`
110
- - Native ReffySpec surface:
111
- - the on-disk `reffyspec/changes/` and `reffyspec/specs/` layout
112
- - `reffyspec/AGENTS.md` as the planning/spec conventions file
113
- - Unresolved runtime gaps:
114
- - none identified for the normal local planning lifecycle in v1
115
-
116
- That means the normal workflow is fully Reffy and ReffySpec native.
78
+ - `reffyspec/project.md`: captures durable project context for agents, including purpose, stack, architecture, conventions, and constraints.
117
79
 
118
80
  ## Develop
119
81
 
@@ -127,3 +89,12 @@ npm test
127
89
  ```
128
90
 
129
91
  `npm install` runs this package's `prepare` step, which builds `dist/` automatically.
92
+
93
+ ## Release Security
94
+
95
+ Reffy publishes from GitHub Actions using npm trusted publishing with provenance enabled.
96
+
97
+ To verify an installed package:
98
+
99
+ - Check the package provenance details on npm.
100
+ - Run `npm audit signatures` after install to verify registry signatures and available provenance attestations.
package/dist/cli.js CHANGED
@@ -24,6 +24,10 @@ const REFFY_ASCII = [
24
24
  "|_| \\___||_| |_| \\__, |",
25
25
  " |___/ ",
26
26
  ].join("\n");
27
+ const BOOTSTRAP_AGENT_INSTRUCTION = [
28
+ 'Please read `AGENTS.md` and help me fill out the project context template in `reffyspec/project.md`',
29
+ "with details about my project, tech stack, architecture, and conventions.",
30
+ ].join(" ");
27
31
  function buildReffyBlock(refsDirName) {
28
32
  return `<!-- REFFY:START -->
29
33
  # Reffy Instructions
@@ -355,6 +359,56 @@ function printBanner(mode) {
355
359
  console.log("");
356
360
  }
357
361
  }
362
+ function shouldPrintBootstrapOnboarding(workspaceCreated, workspaceMigrated, planningCreated, planningMigrated) {
363
+ return workspaceCreated || workspaceMigrated || planningCreated || planningMigrated;
364
+ }
365
+ function printBootstrapOnboarding() {
366
+ console.log("");
367
+ console.log("Next step for your agent harness:");
368
+ console.log("Copy and paste this into your conversation with your chosen agent:");
369
+ console.log("");
370
+ console.log(BOOTSTRAP_AGENT_INSTRUCTION);
371
+ }
372
+ async function runSetupCommand(commandName, output, repoRoot) {
373
+ const workspace = await prepareCanonicalWorkspace(repoRoot);
374
+ const planning = await prepareCanonicalPlanningLayout(repoRoot);
375
+ const agents = await initAgents(repoRoot);
376
+ const store = new ReferencesStore(repoRoot);
377
+ const reindex = await store.reindexArtifacts();
378
+ const payload = {
379
+ status: "ok",
380
+ command: commandName,
381
+ workspace_mode: workspace.state.mode,
382
+ migrated_workspace: workspace.migrated,
383
+ created_workspace: workspace.created,
384
+ planning_mode: planning.state.mode,
385
+ migrated_planning_layout: planning.migrated,
386
+ created_planning_layout: planning.created,
387
+ ...agents,
388
+ refs_dir: store.refsDir,
389
+ manifest_path: store.manifestPath,
390
+ reindex,
391
+ };
392
+ if (output === "json") {
393
+ printResult(output, payload);
394
+ return 0;
395
+ }
396
+ if (workspace.message) {
397
+ console.log(workspace.message);
398
+ }
399
+ if (planning.message) {
400
+ console.log(planning.message);
401
+ }
402
+ console.log(`${commandName === "init" ? "Initialized" : "Bootstrapped"} ${store.refsDir}`);
403
+ console.log(`Updated ${agents.root_agents_path}`);
404
+ console.log(`Updated ${agents.reffy_agents_path}`);
405
+ console.log(`Updated ${agents.reffyspec_agents_path}`);
406
+ console.log(`Reindex: added=${String(reindex.added)} removed=${String(reindex.removed)} total=${String(reindex.total)}`);
407
+ if (shouldPrintBootstrapOnboarding(workspace.created, workspace.migrated, planning.created, planning.migrated)) {
408
+ printBootstrapOnboarding();
409
+ }
410
+ return 0;
411
+ }
358
412
  function usage() {
359
413
  return [
360
414
  "Usage: reffy <command> [--repo PATH] [--output text|json]",
@@ -363,8 +417,8 @@ function usage() {
363
417
  " --version Print the installed reffy package version.",
364
418
  "",
365
419
  "Commands:",
366
- " init Ensure root AGENTS.md block and .reffy/AGENTS.md are up to date.",
367
- " bootstrap Run init, ensure .reffy structure exists, then reindex artifacts.",
420
+ " init Run the canonical first-run setup flow and refresh managed instructions.",
421
+ " bootstrap Compatibility alias for init.",
368
422
  " migrate Migrate a legacy .references workspace to the canonical .reffy layout.",
369
423
  " doctor Diagnose required Reffy setup and optional tool availability.",
370
424
  " reindex Scan .reffy/artifacts and add missing files to manifest.",
@@ -873,74 +927,11 @@ async function main() {
873
927
  if (command === "init") {
874
928
  printBanner(output);
875
929
  const repoRoot = parseRepoArg(rest);
876
- const workspace = await prepareCanonicalWorkspace(repoRoot);
877
- const planning = await prepareCanonicalPlanningLayout(repoRoot);
878
- const agents = await initAgents(repoRoot);
879
- const payload = {
880
- status: "ok",
881
- command: "init",
882
- workspace_mode: workspace.state.mode,
883
- migrated_workspace: workspace.migrated,
884
- created_workspace: workspace.created,
885
- planning_mode: planning.state.mode,
886
- migrated_planning_layout: planning.migrated,
887
- created_planning_layout: planning.created,
888
- ...agents,
889
- };
890
- if (output === "json") {
891
- printResult(output, payload);
892
- }
893
- else {
894
- if (workspace.message) {
895
- console.log(workspace.message);
896
- }
897
- if (planning.message) {
898
- console.log(planning.message);
899
- }
900
- console.log(`Updated ${agents.root_agents_path}`);
901
- console.log(`Updated ${agents.reffy_agents_path}`);
902
- console.log(`Updated ${agents.reffyspec_agents_path}`);
903
- }
904
- return 0;
930
+ return await runSetupCommand("init", output, repoRoot);
905
931
  }
906
932
  if (command === "bootstrap") {
907
933
  const repoRoot = parseRepoArg(rest);
908
- const workspace = await prepareCanonicalWorkspace(repoRoot);
909
- const planning = await prepareCanonicalPlanningLayout(repoRoot);
910
- const agents = await initAgents(repoRoot);
911
- const store = new ReferencesStore(repoRoot);
912
- const reindex = await store.reindexArtifacts();
913
- const payload = {
914
- status: "ok",
915
- command: "bootstrap",
916
- workspace_mode: workspace.state.mode,
917
- migrated_workspace: workspace.migrated,
918
- created_workspace: workspace.created,
919
- planning_mode: planning.state.mode,
920
- migrated_planning_layout: planning.migrated,
921
- created_planning_layout: planning.created,
922
- ...agents,
923
- refs_dir: store.refsDir,
924
- manifest_path: store.manifestPath,
925
- reindex,
926
- };
927
- if (output === "json") {
928
- printResult(output, payload);
929
- }
930
- else {
931
- if (workspace.message) {
932
- console.log(workspace.message);
933
- }
934
- if (planning.message) {
935
- console.log(planning.message);
936
- }
937
- console.log(`Bootstrapped ${store.refsDir}`);
938
- console.log(`Updated ${agents.root_agents_path}`);
939
- console.log(`Updated ${agents.reffy_agents_path}`);
940
- console.log(`Updated ${agents.reffyspec_agents_path}`);
941
- console.log(`Reindex: added=${String(reindex.added)} removed=${String(reindex.removed)} total=${String(reindex.total)}`);
942
- }
943
- return 0;
934
+ return await runSetupCommand("bootstrap", output, repoRoot);
944
935
  }
945
936
  if (command === "migrate") {
946
937
  const repoRoot = parseRepoArg(rest);
@@ -33,20 +33,27 @@ async function listFilesRecursive(dir) {
33
33
  }
34
34
  return files.sort();
35
35
  }
36
- function parseAddedRequirements(content, relPath) {
36
+ function parseArchiveableRequirements(content, relPath) {
37
37
  const lines = content.split(/\r?\n/);
38
38
  const sectionTypes = new Set();
39
- const blocks = [];
40
- let inAddedSection = false;
39
+ const added = [];
40
+ const modified = [];
41
+ let currentSectionType = null;
41
42
  let currentTitle = null;
42
43
  let currentLines = [];
43
44
  const flush = () => {
44
45
  if (!currentTitle)
45
46
  return;
46
- blocks.push({
47
+ const block = {
47
48
  title: currentTitle,
48
49
  content: currentLines.join("\n").trimEnd(),
49
- });
50
+ };
51
+ if (currentSectionType === "ADDED") {
52
+ added.push(block);
53
+ }
54
+ else if (currentSectionType === "MODIFIED") {
55
+ modified.push(block);
56
+ }
50
57
  currentTitle = null;
51
58
  currentLines = [];
52
59
  };
@@ -56,7 +63,7 @@ function parseAddedRequirements(content, relPath) {
56
63
  flush();
57
64
  const sectionType = sectionMatch[1] ?? "";
58
65
  sectionTypes.add(sectionType);
59
- inAddedSection = sectionType === "ADDED";
66
+ currentSectionType = sectionType === "ADDED" || sectionType === "MODIFIED" ? sectionType : null;
60
67
  continue;
61
68
  }
62
69
  const requirementMatch = line.match(REQUIREMENT_PATTERN);
@@ -71,17 +78,17 @@ function parseAddedRequirements(content, relPath) {
71
78
  }
72
79
  }
73
80
  flush();
74
- const unsupported = Array.from(sectionTypes).filter((sectionType) => sectionType !== "ADDED");
81
+ const unsupported = Array.from(sectionTypes).filter((sectionType) => sectionType !== "ADDED" && sectionType !== "MODIFIED");
75
82
  if (unsupported.length > 0) {
76
83
  throw new Error(`${relPath}: unsupported delta sections for archive: ${unsupported.join(", ")}`);
77
84
  }
78
- if (!inAddedSection && sectionTypes.size === 0) {
85
+ if (sectionTypes.size === 0) {
79
86
  throw new Error(`${relPath}: no supported archiveable requirements found`);
80
87
  }
81
- if (blocks.length === 0) {
82
- throw new Error(`${relPath}: no ADDED requirements found to archive`);
88
+ if (added.length === 0 && modified.length === 0) {
89
+ throw new Error(`${relPath}: no archiveable requirements found`);
83
90
  }
84
- return blocks;
91
+ return { added, modified };
85
92
  }
86
93
  function buildNewCurrentSpec(capability, changeId, blocks) {
87
94
  return [
@@ -95,18 +102,74 @@ function buildNewCurrentSpec(capability, changeId, blocks) {
95
102
  "",
96
103
  ].join("\n");
97
104
  }
98
- function appendRequirementsToCurrentSpec(existing, blocks, relPath) {
105
+ function parseCurrentSpecRequirementBlocks(existing, relPath) {
99
106
  if (!existing.includes(REQUIREMENTS_HEADING)) {
100
107
  throw new Error(`${relPath}: current spec is missing a "## Requirements" section`);
101
108
  }
102
- for (const block of blocks) {
103
- const escaped = block.title.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
104
- const duplicatePattern = new RegExp(`^###\\s+Requirement:\\s+${escaped}\\s*$`, "m");
105
- if (duplicatePattern.test(existing)) {
109
+ const lines = existing.split(/\r?\n/);
110
+ const blocks = [];
111
+ let inRequirements = false;
112
+ let currentTitle = null;
113
+ let currentLines = [];
114
+ const flush = () => {
115
+ if (!currentTitle)
116
+ return;
117
+ blocks.push({
118
+ title: currentTitle,
119
+ content: currentLines.join("\n").trimEnd(),
120
+ });
121
+ currentTitle = null;
122
+ currentLines = [];
123
+ };
124
+ for (const line of lines) {
125
+ if (line.trim() === REQUIREMENTS_HEADING) {
126
+ inRequirements = true;
127
+ continue;
128
+ }
129
+ if (!inRequirements) {
130
+ continue;
131
+ }
132
+ if (/^##\s+/.test(line) && line.trim() !== REQUIREMENTS_HEADING) {
133
+ flush();
134
+ break;
135
+ }
136
+ const requirementMatch = line.match(REQUIREMENT_PATTERN);
137
+ if (requirementMatch) {
138
+ flush();
139
+ currentTitle = requirementMatch[1]?.trim() ?? "unknown";
140
+ currentLines = [line];
141
+ continue;
142
+ }
143
+ if (currentTitle) {
144
+ currentLines.push(line);
145
+ }
146
+ }
147
+ flush();
148
+ return blocks;
149
+ }
150
+ function mergeRequirementsIntoCurrentSpec(existing, delta, relPath) {
151
+ const currentBlocks = parseCurrentSpecRequirementBlocks(existing, relPath);
152
+ const currentTitles = new Set(currentBlocks.map((block) => block.title));
153
+ for (const block of delta.added) {
154
+ if (currentTitles.has(block.title)) {
106
155
  throw new Error(`${relPath}: current spec already contains requirement "${block.title}"`);
107
156
  }
157
+ currentBlocks.push(block);
158
+ currentTitles.add(block.title);
159
+ }
160
+ for (const block of delta.modified) {
161
+ const index = currentBlocks.findIndex((currentBlock) => currentBlock.title === block.title);
162
+ if (index < 0) {
163
+ throw new Error(`${relPath}: current spec is missing requirement "${block.title}" required for MODIFIED archive`);
164
+ }
165
+ currentBlocks[index] = block;
166
+ }
167
+ const requirementsHeadingIndex = existing.indexOf(REQUIREMENTS_HEADING);
168
+ if (requirementsHeadingIndex < 0) {
169
+ throw new Error(`${relPath}: current spec is missing a "## Requirements" section`);
108
170
  }
109
- return `${existing.trimEnd()}\n\n${blocks.map((block) => block.content).join("\n\n")}\n`;
171
+ const prefix = existing.slice(0, requirementsHeadingIndex + REQUIREMENTS_HEADING.length);
172
+ return `${prefix}\n${currentBlocks.map((block) => `\n${block.content}`).join("")}\n`;
110
173
  }
111
174
  async function buildSpecUpdates(repoRoot, changeId, changeDir) {
112
175
  const specsRoot = path.join(changeDir, "specs");
@@ -121,11 +184,17 @@ async function buildSpecUpdates(repoRoot, changeId, changeDir) {
121
184
  continue;
122
185
  const deltaContent = await fs.readFile(deltaPath, "utf8");
123
186
  const relDeltaPath = path.relative(repoRoot, deltaPath).split(path.sep).join("/");
124
- const blocks = parseAddedRequirements(deltaContent, relDeltaPath);
187
+ const delta = parseArchiveableRequirements(deltaContent, relDeltaPath);
125
188
  const currentSpecPath = path.join(repoRoot, DEFAULT_PLANNING_DIRNAME, "specs", capability, "spec.md");
126
- const nextContent = (await pathExists(currentSpecPath))
127
- ? appendRequirementsToCurrentSpec(await fs.readFile(currentSpecPath, "utf8"), blocks, path.relative(repoRoot, currentSpecPath).split(path.sep).join("/"))
128
- : buildNewCurrentSpec(capability, changeId, blocks);
189
+ const currentSpecExists = await pathExists(currentSpecPath);
190
+ const nextContent = currentSpecExists
191
+ ? mergeRequirementsIntoCurrentSpec(await fs.readFile(currentSpecPath, "utf8"), delta, path.relative(repoRoot, currentSpecPath).split(path.sep).join("/"))
192
+ : (() => {
193
+ if (delta.modified.length > 0) {
194
+ throw new Error(`${relDeltaPath}: cannot archive MODIFIED requirements without an existing current spec`);
195
+ }
196
+ return buildNewCurrentSpec(capability, changeId, delta.added);
197
+ })();
129
198
  updates.set(currentSpecPath, nextContent);
130
199
  }
131
200
  return updates;
@@ -1,6 +1,38 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { DEFAULT_PLANNING_DIRNAME, LEGACY_PLANNING_DIRNAME, detectPlanningState, resolveCanonicalPlanningDir, } from "./planning-paths.js";
4
+ const DEFAULT_PROJECT_CONTEXT_TEMPLATE = `# Project Context
5
+
6
+ ## Purpose
7
+ [Describe your project's purpose and goals]
8
+
9
+ ## Tech Stack
10
+ - [List your primary technologies]
11
+ - [e.g., TypeScript, React, Node.js]
12
+
13
+ ## Project Conventions
14
+
15
+ ### Code Style
16
+ [Describe your code style preferences, formatting rules, and naming conventions]
17
+
18
+ ### Architecture Patterns
19
+ [Document your architectural decisions and patterns]
20
+
21
+ ### Testing Strategy
22
+ [Explain your testing approach and requirements]
23
+
24
+ ### Git Workflow
25
+ [Describe your branching strategy and commit conventions]
26
+
27
+ ## Domain Context
28
+ [Add domain-specific knowledge that AI assistants need to understand]
29
+
30
+ ## Important Constraints
31
+ [List any technical, business, or regulatory constraints]
32
+
33
+ ## External Dependencies
34
+ [Document key external services, APIs, or systems]
35
+ `;
4
36
  async function pathExists(targetPath) {
5
37
  try {
6
38
  await fs.access(targetPath);
@@ -12,12 +44,16 @@ async function pathExists(targetPath) {
12
44
  }
13
45
  async function ensureCanonicalPlanningStructure(repoRoot) {
14
46
  const planningDir = resolveCanonicalPlanningDir(repoRoot);
47
+ const projectContextPath = path.join(planningDir, "project.md");
15
48
  let created = false;
16
49
  if (!(await pathExists(planningDir))) {
17
50
  created = true;
18
51
  }
19
52
  await fs.mkdir(path.join(planningDir, "changes", "archive"), { recursive: true });
20
53
  await fs.mkdir(path.join(planningDir, "specs"), { recursive: true });
54
+ if (!(await pathExists(projectContextPath))) {
55
+ await fs.writeFile(projectContextPath, DEFAULT_PROJECT_CONTEXT_TEMPLATE, "utf8");
56
+ }
21
57
  return created;
22
58
  }
23
59
  export async function prepareCanonicalPlanningLayout(repoRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reffy-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "CLI-first, framework-agnostic references workflow for any repo (TypeScript)",
5
5
  "keywords": [
6
6
  "reffy",
@@ -12,6 +12,14 @@
12
12
  "sdd"
13
13
  ],
14
14
  "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/RoskiDeluge/reffy.git"
18
+ },
19
+ "homepage": "https://github.com/RoskiDeluge/reffy",
20
+ "bugs": {
21
+ "url": "https://github.com/RoskiDeluge/reffy/issues"
22
+ },
15
23
  "type": "module",
16
24
  "main": "dist/index.js",
17
25
  "files": [
@@ -33,6 +41,10 @@
33
41
  "engines": {
34
42
  "node": ">=20"
35
43
  },
44
+ "publishConfig": {
45
+ "provenance": true,
46
+ "access": "public"
47
+ },
36
48
  "dependencies": {
37
49
  "beautiful-mermaid": "^0.1.3",
38
50
  "mime-types": "^2.1.35",