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 +27 -18
- package/dist/doctor.js +10 -6
- package/dist/refs-paths.d.ts +5 -0
- package/dist/refs-paths.js +32 -0
- package/dist/storage.js +2 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
38
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}${
|
|
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${
|
|
133
|
+
return `${before.trimEnd()}\n\n${reffyBlock}\n\n${OPENSPEC_START}${after}`;
|
|
126
134
|
}
|
|
127
|
-
return content.trim().length > 0 ? `${
|
|
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,
|
|
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 =
|
|
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,
|
|
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 (
|
|
168
|
+
if (looksLikeRefsDir(current)) {
|
|
160
169
|
return path.dirname(current);
|
|
161
170
|
}
|
|
162
|
-
if (isDirectory(path.join(current, ".
|
|
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
|
|
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 ?
|
|
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
|
|
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 ?
|
|
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:
|
|
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
|
-
?
|
|
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 =
|
|
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();
|