reffy-cli 0.6.7 → 0.7.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/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { existsSync, promises as fs, statSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { renderDiagram } from "./diagram.js";
6
6
  import { runDoctor } from "./doctor.js";
7
+ import { DEFAULT_REFS_DIRNAME, looksLikeRefsDir, resolveRefsDirName } from "./refs-paths.js";
7
8
  import { ReferencesStore } from "./storage.js";
8
9
  import { summarizeArtifacts } from "./summarize.js";
9
10
  const require = createRequire(import.meta.url);
@@ -16,33 +17,35 @@ const REFFY_ASCII = [
16
17
  "|_| \\___||_| |_| \\__, |",
17
18
  " |___/ ",
18
19
  ].join("\n");
19
- const REFFY_BLOCK = `<!-- REFFY:START -->
20
+ function buildReffyBlock(refsDirName) {
21
+ return `<!-- REFFY:START -->
20
22
  # Reffy Instructions
21
23
 
22
24
  These instructions are for AI assistants working in this project.
23
25
 
24
- Always open \`@/.reffy/AGENTS.md\` when the request:
26
+ Always open \`@/${refsDirName}/AGENTS.md\` when the request:
25
27
  - Mentions early-stage ideation, exploration, brainstorming, or raw notes
26
28
  - Needs context before drafting specs or proposals
27
29
  - Refers to "reffy", "references", "explore", or "context layer"
28
30
 
29
- Use \`@/.reffy/AGENTS.md\` to learn:
31
+ Use \`@/${refsDirName}/AGENTS.md\` to learn:
30
32
  - Reffy workflow and artifact conventions
31
33
  - How Reffy and OpenSpec should be sequenced
32
- - How to store and consume ideation context in \`.reffy/\`
34
+ - How to store and consume ideation context in \`${refsDirName}/\`
33
35
 
34
36
  Keep this managed block so \`reffy init\` can refresh the instructions.
35
37
 
36
38
  <!-- REFFY:END -->`;
37
- const REFFY_AGENTS_RELATIVE = path.join(".reffy", "AGENTS.md");
38
- const REFFY_AGENTS_CONTENT = `# Reffy Instructions
39
+ }
40
+ function buildReffyAgentsContent(refsDirName) {
41
+ return `# Reffy Instructions
39
42
 
40
43
  These instructions are for AI assistants working in this project.
41
44
 
42
45
  ## TL;DR Checklist
43
46
 
44
47
  - Decide whether Reffy ideation is needed for this request.
45
- - If needed, read existing context in \`.reffy/artifacts/\`.
48
+ - If needed, read existing context in \`${refsDirName}/artifacts/\`.
46
49
  - Add/update exploratory artifacts and keep them concise.
47
50
  - Run \`reffy reindex\` and \`reffy validate\` after artifact changes.
48
51
  - After ideation approval, run \`reffy summarize --output json\` and pick only directly relevant artifacts for proposal citations.
@@ -63,9 +66,9 @@ You can skip Reffy when the request is:
63
66
 
64
67
  ## Reffy Workflow
65
68
 
66
- 1. Read existing artifacts in \`.reffy/artifacts/\`.
69
+ 1. Read existing artifacts in \`${refsDirName}/artifacts/\`.
67
70
  2. Add or update artifacts to capture exploratory context.
68
- 3. Run \`reffy reindex\` to index newly added files into \`.reffy/manifest.json\`.
71
+ 3. Run \`reffy reindex\` to index newly added files into \`${refsDirName}/manifest.json\`.
69
72
  4. Run \`reffy validate\` to verify manifest contract compliance.
70
73
 
71
74
  ## Relationship To OpenSpec
@@ -105,30 +108,36 @@ No Reffy references used.
105
108
 
106
109
  ## Artifact Conventions
107
110
 
108
- - Treat \`.reffy/\` as a repository-local guidance and ideation context layer.
111
+ - Treat \`${refsDirName}/\` as a repository-local guidance and ideation context layer.
109
112
  - Keep artifact names clear and stable.
110
113
  - Prefer markdown notes for exploratory content.
111
114
  - Keep manifests machine-readable and schema-compliant (version 1).
112
115
  `;
116
+ }
113
117
  const REFFY_START = "<!-- REFFY:START -->";
114
118
  const REFFY_END = "<!-- REFFY:END -->";
115
119
  const OPENSPEC_START = "<!-- OPENSPEC:START -->";
116
120
  function upsertReffyBlock(content) {
121
+ return upsertReffyBlockForDir(content, DEFAULT_REFS_DIRNAME);
122
+ }
123
+ function upsertReffyBlockForDir(content, refsDirName) {
124
+ const reffyBlock = buildReffyBlock(refsDirName);
117
125
  if (content.includes(REFFY_START) && content.includes(REFFY_END)) {
118
126
  const prefix = content.split(REFFY_START)[0] ?? "";
119
127
  const suffix = content.split(REFFY_END, 2)[1] ?? "";
120
128
  const trimmedSuffix = suffix.trimStart();
121
- return trimmedSuffix.length > 0 ? `${prefix}${REFFY_BLOCK}\n\n${trimmedSuffix}` : `${prefix}${REFFY_BLOCK}\n`;
129
+ return trimmedSuffix.length > 0 ? `${prefix}${reffyBlock}\n\n${trimmedSuffix}` : `${prefix}${reffyBlock}\n`;
122
130
  }
123
131
  if (content.includes(OPENSPEC_START)) {
124
132
  const [before, after] = content.split(OPENSPEC_START, 2);
125
- return `${before.trimEnd()}\n\n${REFFY_BLOCK}\n\n${OPENSPEC_START}${after}`;
133
+ return `${before.trimEnd()}\n\n${reffyBlock}\n\n${OPENSPEC_START}${after}`;
126
134
  }
127
- return content.trim().length > 0 ? `${REFFY_BLOCK}\n\n${content.trimStart()}` : `${REFFY_BLOCK}\n`;
135
+ return content.trim().length > 0 ? `${reffyBlock}\n\n${content.trimStart()}` : `${reffyBlock}\n`;
128
136
  }
129
137
  async function initAgents(repoRoot) {
138
+ const refsDirName = resolveRefsDirName(repoRoot);
130
139
  const agentsPath = path.join(repoRoot, "AGENTS.md");
131
- const reffyAgentsPath = path.join(repoRoot, REFFY_AGENTS_RELATIVE);
140
+ const reffyAgentsPath = path.join(repoRoot, refsDirName, "AGENTS.md");
132
141
  let content = "";
133
142
  try {
134
143
  content = await fs.readFile(agentsPath, "utf8");
@@ -136,10 +145,10 @@ async function initAgents(repoRoot) {
136
145
  catch {
137
146
  content = "";
138
147
  }
139
- const updated = upsertReffyBlock(content);
148
+ const updated = upsertReffyBlockForDir(content, refsDirName);
140
149
  await fs.mkdir(path.dirname(reffyAgentsPath), { recursive: true });
141
150
  await fs.writeFile(agentsPath, updated, "utf8");
142
- await fs.writeFile(reffyAgentsPath, REFFY_AGENTS_CONTENT, "utf8");
151
+ await fs.writeFile(reffyAgentsPath, buildReffyAgentsContent(refsDirName), "utf8");
143
152
  return { root_agents_path: agentsPath, reffy_agents_path: reffyAgentsPath };
144
153
  }
145
154
  function pathExists(targetPath) {
@@ -156,10 +165,10 @@ function isDirectory(targetPath) {
156
165
  function discoverRepoRoot(startDir) {
157
166
  let current = path.resolve(startDir);
158
167
  while (true) {
159
- if (path.basename(current) === ".reffy" && isDirectory(path.join(current, "artifacts"))) {
168
+ if (looksLikeRefsDir(current)) {
160
169
  return path.dirname(current);
161
170
  }
162
- if (isDirectory(path.join(current, ".reffy"))) {
171
+ if (isDirectory(path.join(current, DEFAULT_REFS_DIRNAME)) || isDirectory(path.join(current, ".references"))) {
163
172
  return current;
164
173
  }
165
174
  if (pathExists(path.join(current, "AGENTS.md")) || pathExists(path.join(current, ".git"))) {
package/dist/doctor.js CHANGED
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import { promises as fs } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { validateManifest } from "./manifest.js";
5
+ import { resolveRefsDirName } from "./refs-paths.js";
5
6
  async function pathExists(targetPath) {
6
7
  try {
7
8
  await fs.access(targetPath);
@@ -27,7 +28,8 @@ function summarizeChecks(checks) {
27
28
  }
28
29
  export async function runDoctor(repoRoot, options) {
29
30
  const checks = [];
30
- const refsDir = path.join(repoRoot, ".reffy");
31
+ const refsDirName = resolveRefsDirName(repoRoot);
32
+ const refsDir = path.join(repoRoot, refsDirName);
31
33
  const artifactsDir = path.join(refsDir, "artifacts");
32
34
  const manifestPath = path.join(refsDir, "manifest.json");
33
35
  const rootAgentsPath = path.join(repoRoot, "AGENTS.md");
@@ -37,14 +39,16 @@ export async function runDoctor(repoRoot, options) {
37
39
  id: "refs_dir_exists",
38
40
  level: "required",
39
41
  ok: refsDirExists,
40
- message: refsDirExists ? ".reffy directory found" : ".reffy directory is missing",
42
+ message: refsDirExists ? `${refsDirName} directory found` : `${refsDirName} directory is missing`,
41
43
  });
42
44
  const artifactsDirExists = await pathExists(artifactsDir);
43
45
  checks.push({
44
46
  id: "artifacts_dir_exists",
45
47
  level: "required",
46
48
  ok: artifactsDirExists,
47
- message: artifactsDirExists ? ".reffy/artifacts directory found" : ".reffy/artifacts directory is missing",
49
+ message: artifactsDirExists
50
+ ? `${refsDirName}/artifacts directory found`
51
+ : `${refsDirName}/artifacts directory is missing`,
48
52
  });
49
53
  const rootAgentsExists = await pathExists(rootAgentsPath);
50
54
  checks.push({
@@ -58,7 +62,7 @@ export async function runDoctor(repoRoot, options) {
58
62
  id: "refs_agents_exists",
59
63
  level: "required",
60
64
  ok: refsAgentsExists,
61
- message: refsAgentsExists ? ".reffy/AGENTS.md found" : ".reffy/AGENTS.md is missing",
65
+ message: refsAgentsExists ? `${refsDirName}/AGENTS.md found` : `${refsDirName}/AGENTS.md is missing`,
62
66
  });
63
67
  const manifestExists = await pathExists(manifestPath);
64
68
  if (!manifestExists) {
@@ -66,7 +70,7 @@ export async function runDoctor(repoRoot, options) {
66
70
  id: "manifest_valid",
67
71
  level: "required",
68
72
  ok: false,
69
- message: ".reffy/manifest.json is missing",
73
+ message: `${refsDirName}/manifest.json is missing`,
70
74
  });
71
75
  }
72
76
  else {
@@ -76,7 +80,7 @@ export async function runDoctor(repoRoot, options) {
76
80
  level: "required",
77
81
  ok: manifestResult.ok,
78
82
  message: manifestResult.ok
79
- ? `.reffy/manifest.json is valid (artifacts=${String(manifestResult.artifact_count)})`
83
+ ? `${refsDirName}/manifest.json is valid (artifacts=${String(manifestResult.artifact_count)})`
80
84
  : `manifest invalid: ${manifestResult.errors.join("; ")}`,
81
85
  });
82
86
  }
@@ -0,0 +1,5 @@
1
+ export declare const DEFAULT_REFS_DIRNAME = ".reffy";
2
+ export declare const LEGACY_REFS_DIRNAME = ".references";
3
+ export declare function resolveRefsDirName(repoRoot: string): string;
4
+ export declare function resolveRefsDir(repoRoot: string): string;
5
+ export declare function looksLikeRefsDir(targetPath: string): boolean;
@@ -0,0 +1,32 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ export const DEFAULT_REFS_DIRNAME = ".reffy";
4
+ export const LEGACY_REFS_DIRNAME = ".references";
5
+ function isDirectory(targetPath) {
6
+ try {
7
+ return statSync(targetPath).isDirectory();
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export function resolveRefsDirName(repoRoot) {
14
+ const reffyDir = path.join(repoRoot, DEFAULT_REFS_DIRNAME);
15
+ if (isDirectory(reffyDir)) {
16
+ return DEFAULT_REFS_DIRNAME;
17
+ }
18
+ const legacyDir = path.join(repoRoot, LEGACY_REFS_DIRNAME);
19
+ if (isDirectory(legacyDir)) {
20
+ return LEGACY_REFS_DIRNAME;
21
+ }
22
+ return DEFAULT_REFS_DIRNAME;
23
+ }
24
+ export function resolveRefsDir(repoRoot) {
25
+ return path.join(repoRoot, resolveRefsDirName(repoRoot));
26
+ }
27
+ export function looksLikeRefsDir(targetPath) {
28
+ const base = path.basename(targetPath);
29
+ return ((base === DEFAULT_REFS_DIRNAME || base === LEGACY_REFS_DIRNAME) &&
30
+ existsSync(path.join(targetPath, "artifacts")) &&
31
+ isDirectory(path.join(targetPath, "artifacts")));
32
+ }
package/dist/storage.js CHANGED
@@ -3,6 +3,7 @@ import { mkdirSync } from "node:fs";
3
3
  import { promises as fs } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { inferArtifactType, MANIFEST_VERSION, validateManifest } from "./manifest.js";
6
+ import { resolveRefsDir } from "./refs-paths.js";
6
7
  function utcNow() {
7
8
  return new Date().toISOString();
8
9
  }
@@ -16,7 +17,7 @@ export class ReferencesStore {
16
17
  manifestPath;
17
18
  constructor(repoRoot) {
18
19
  this.repoRoot = repoRoot;
19
- this.refsDir = path.join(repoRoot, ".reffy");
20
+ this.refsDir = resolveRefsDir(repoRoot);
20
21
  this.artifactsDir = path.join(this.refsDir, "artifacts");
21
22
  this.manifestPath = path.join(this.refsDir, "manifest.json");
22
23
  this.ensureStructure();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reffy-cli",
3
- "version": "0.6.7",
3
+ "version": "0.7.0",
4
4
  "description": "CLI-first, framework-agnostic references workflow for any repo (TypeScript)",
5
5
  "keywords": [
6
6
  "reffy",