repofence 0.1.7 → 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 +30 -12
- package/dist/cli.js +13 -6
- package/dist/commands/init.js +66 -25
- package/dist/commands/reverse.js +17 -3
- package/dist/commands/status.js +23 -10
- package/dist/core/pack-manager.js +155 -13
- package/dist/core/pack-public-key.js +2 -2
- package/dist/core/targets.js +40 -0
- package/dist/templates/CLAUDE.md +27 -0
- package/dist/templates/CLAUDE_RULES_SDD.md +20 -0
- package/dist/templates/IDE_CLAUDE.md +33 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -14,13 +14,15 @@ npm install -g repofence
|
|
|
14
14
|
# 1. Purchase a license and get your token
|
|
15
15
|
repofence auth --web
|
|
16
16
|
|
|
17
|
-
# 2. Initialize spec files + install Cursor
|
|
17
|
+
# 2. Initialize spec files + install assistant assets (Cursor by default)
|
|
18
18
|
repofence init
|
|
19
19
|
|
|
20
20
|
# 3. Auto-populate specs from your codebase
|
|
21
21
|
repofence reverse
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
Brownfield tip: run `repofence reverse` first after `init` so your spec layer reflects the current codebase before any AI-driven changes.
|
|
25
|
+
|
|
24
26
|
## Commands
|
|
25
27
|
|
|
26
28
|
### `repofence auth`
|
|
@@ -39,12 +41,14 @@ Get your license at [repofence.com](https://repofence.com).
|
|
|
39
41
|
|
|
40
42
|
---
|
|
41
43
|
|
|
42
|
-
### `repofence init`
|
|
44
|
+
### `repofence init [ide]`
|
|
43
45
|
|
|
44
|
-
Initialize spec files in the current project and install
|
|
46
|
+
Initialize spec files in the current project and install assistant assets.
|
|
45
47
|
|
|
46
48
|
```bash
|
|
47
|
-
repofence init
|
|
49
|
+
repofence init # defaults to Cursor
|
|
50
|
+
repofence init cursor
|
|
51
|
+
repofence init claude
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
**Requires**: a valid token saved via `repofence auth`.
|
|
@@ -57,9 +61,11 @@ Creates in your project:
|
|
|
57
61
|
| `spec/CORE_FLOWS.md` | Critical user and system flows |
|
|
58
62
|
| `spec/BOUNDARIES.md` | Architectural boundaries and constraints |
|
|
59
63
|
| `spec/AI_RULES.md` | Rules for AI agents working on the codebase |
|
|
60
|
-
| `spec/IDE_CURSOR.md` |
|
|
64
|
+
| `spec/IDE_CURSOR.md` or `spec/IDE_CLAUDE.md` | Assistant-specific integration guide |
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
Install targets:
|
|
67
|
+
- `cursor`: installs `/repofence.*` command files in `~/.cursor/commands/`
|
|
68
|
+
- `claude`: installs repofence skills in `~/.claude/skills/` and creates project files `CLAUDE.md` + `.claude/rules/sdd-core.md`
|
|
63
69
|
|
|
64
70
|
**Options**:
|
|
65
71
|
|
|
@@ -89,6 +95,8 @@ Detects:
|
|
|
89
95
|
|
|
90
96
|
Updates `spec/SYSTEM_OVERVIEW.md`, `spec/CORE_FLOWS.md`, `spec/BOUNDARIES.md`, and `spec/AI_RULES.md` with what it finds, preserving sections you've already customized.
|
|
91
97
|
|
|
98
|
+
For brownfield projects this is the most important command to run early.
|
|
99
|
+
|
|
92
100
|
**Options**:
|
|
93
101
|
|
|
94
102
|
| Flag | Description |
|
|
@@ -99,12 +107,14 @@ Updates `spec/SYSTEM_OVERVIEW.md`, `spec/CORE_FLOWS.md`, `spec/BOUNDARIES.md`, a
|
|
|
99
107
|
|
|
100
108
|
---
|
|
101
109
|
|
|
102
|
-
### `repofence status`
|
|
110
|
+
### `repofence status [ide]`
|
|
103
111
|
|
|
104
|
-
Show the status of your local token and installed pack.
|
|
112
|
+
Show the status of your local token and installed pack manifests.
|
|
105
113
|
|
|
106
114
|
```bash
|
|
107
|
-
repofence status
|
|
115
|
+
repofence status # shows cursor + claude
|
|
116
|
+
repofence status cursor
|
|
117
|
+
repofence status claude
|
|
108
118
|
```
|
|
109
119
|
|
|
110
120
|
---
|
|
@@ -113,11 +123,12 @@ repofence status
|
|
|
113
123
|
|
|
114
124
|
```
|
|
115
125
|
repofence auth --web # one-time: purchase license + get token
|
|
116
|
-
repofence init
|
|
117
|
-
repofence reverse #
|
|
126
|
+
repofence init claude # or: repofence init cursor
|
|
127
|
+
repofence reverse # critical for brownfield: auto-fill specs from code
|
|
118
128
|
```
|
|
119
129
|
|
|
120
|
-
After running `init`, open
|
|
130
|
+
After running `init cursor`, open Cursor and type `/repofence.help`.
|
|
131
|
+
After running `init claude`, open Claude Code and type `/repofence-reverse`.
|
|
121
132
|
|
|
122
133
|
## Global Options
|
|
123
134
|
|
|
@@ -144,6 +155,12 @@ Deploy (e.g. **Railway**): see [`backend/README.md`](backend/README.md) and root
|
|
|
144
155
|
|
|
145
156
|
Step-by-step: **[docs/RELEASE.md](docs/RELEASE.md)** (`npm version`, `npm publish`). The published package only includes **`dist/`**; the HTTP API in `backend/` is deployed separately.
|
|
146
157
|
|
|
158
|
+
## Claude rollout docs
|
|
159
|
+
|
|
160
|
+
- [docs/CLAUDE-ASSET-CONTRACT.md](docs/CLAUDE-ASSET-CONTRACT.md)
|
|
161
|
+
- [docs/CLAUDE-SKILL-MAPPING.md](docs/CLAUDE-SKILL-MAPPING.md)
|
|
162
|
+
- [docs/CLAUDE-GA-CHECKLIST.md](docs/CLAUDE-GA-CHECKLIST.md)
|
|
163
|
+
|
|
147
164
|
## Environment variables
|
|
148
165
|
|
|
149
166
|
| Variable | Description |
|
|
@@ -151,6 +168,7 @@ Step-by-step: **[docs/RELEASE.md](docs/RELEASE.md)** (`npm version`, `npm publis
|
|
|
151
168
|
| `REPOFENCE_API_BASE_URL` | API host for the CLI (`auth`, `init`). Default normalizes to `…/api`. |
|
|
152
169
|
| `REPOFENCE_MOCK` | Set to `1` for mock API responses (no network). |
|
|
153
170
|
| `REPOFENCE_PACK_ID` | Pack id for `repofence init` (default: `core`). |
|
|
171
|
+
| `REPOFENCE_CLAUDE_PACK_ID` | Optional pack id override for `repofence init claude` (falls back to `REPOFENCE_PACK_ID`). |
|
|
154
172
|
| `REPOFENCE_PUBLIC_KEY` | Optional. PEM or base64 public key to verify signed packs. If unset, the CLI uses its built-in public key. |
|
|
155
173
|
|
|
156
174
|
## Requirements
|
package/dist/cli.js
CHANGED
|
@@ -56,6 +56,7 @@ const init_1 = require("./commands/init");
|
|
|
56
56
|
const reverse_1 = require("./commands/reverse");
|
|
57
57
|
const auth_1 = require("./commands/auth");
|
|
58
58
|
const status_1 = require("./commands/status");
|
|
59
|
+
const targets_1 = require("./core/targets");
|
|
59
60
|
const program = new commander_1.Command();
|
|
60
61
|
program
|
|
61
62
|
.name('repofence')
|
|
@@ -69,18 +70,19 @@ program
|
|
|
69
70
|
program
|
|
70
71
|
.command('init')
|
|
71
72
|
.description('Initialize spec files in the current directory')
|
|
72
|
-
.argument('[ide]', 'IDE
|
|
73
|
+
.argument('[ide]', 'IDE target: cursor | claude', 'cursor')
|
|
73
74
|
.option('-f, --force', 'overwrite existing files')
|
|
74
75
|
.option('--name <name>', 'override project name detection')
|
|
75
76
|
.action(async (ide = 'cursor', options) => {
|
|
76
|
-
if (
|
|
77
|
-
console.error(chalk_1.default.red(`❌ Error: Unsupported IDE "${ide}".
|
|
77
|
+
if (!(0, targets_1.isIdeTarget)(ide)) {
|
|
78
|
+
console.error(chalk_1.default.red(`❌ Error: Unsupported IDE "${ide}". Supported: "cursor", "claude".`));
|
|
78
79
|
process.exit(1);
|
|
79
80
|
}
|
|
80
81
|
const cwd = process.cwd();
|
|
81
82
|
const globalOpts = program.opts();
|
|
82
83
|
try {
|
|
83
84
|
await (0, init_1.initCommand)(cwd, {
|
|
85
|
+
ide,
|
|
84
86
|
force: options.force || false,
|
|
85
87
|
dryRun: globalOpts.dryRun || false,
|
|
86
88
|
verbose: globalOpts.verbose || false,
|
|
@@ -134,11 +136,16 @@ program
|
|
|
134
136
|
// Status command
|
|
135
137
|
program
|
|
136
138
|
.command('status')
|
|
137
|
-
.description('Show local token status and installed pack manifest')
|
|
138
|
-
.
|
|
139
|
+
.description('Show local token status and installed pack manifest(s)')
|
|
140
|
+
.argument('[ide]', 'Status target: cursor | claude | all', 'all')
|
|
141
|
+
.action(async (ide = 'all') => {
|
|
142
|
+
if (ide !== 'all' && !(0, targets_1.isIdeTarget)(ide)) {
|
|
143
|
+
console.error(chalk_1.default.red(`❌ Error: Unsupported status target "${ide}". Supported: "cursor", "claude", "all".`));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
139
146
|
const cwd = process.cwd();
|
|
140
147
|
try {
|
|
141
|
-
await (0, status_1.statusCommand)(cwd);
|
|
148
|
+
await (0, status_1.statusCommand)(cwd, { ide });
|
|
142
149
|
}
|
|
143
150
|
catch (error) {
|
|
144
151
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
package/dist/commands/init.js
CHANGED
|
@@ -39,21 +39,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.initCommand = initCommand;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
-
const os = __importStar(require("os"));
|
|
43
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
44
43
|
const templates_1 = require("../core/templates");
|
|
45
44
|
const api_client_1 = require("../core/api-client");
|
|
46
45
|
const auth_1 = require("../core/auth");
|
|
47
46
|
const pack_manager_1 = require("../core/pack-manager");
|
|
48
|
-
const
|
|
47
|
+
const targets_1 = require("../core/targets");
|
|
48
|
+
const BASE_SPEC_FILES = [
|
|
49
49
|
'SYSTEM_OVERVIEW.md',
|
|
50
50
|
'CORE_FLOWS.md',
|
|
51
51
|
'BOUNDARIES.md',
|
|
52
52
|
'AI_RULES.md',
|
|
53
|
-
|
|
53
|
+
];
|
|
54
|
+
const CLAUDE_PROJECT_FILES = [
|
|
55
|
+
{ relPath: 'CLAUDE.md', template: 'CLAUDE.md' },
|
|
56
|
+
{ relPath: path.join('.claude', 'rules', 'sdd-core.md'), template: 'CLAUDE_RULES_SDD.md' },
|
|
54
57
|
];
|
|
55
58
|
async function initCommand(cwd, options) {
|
|
56
|
-
const { force = false, dryRun = false, verbose = false, name } = options;
|
|
59
|
+
const { ide, force = false, dryRun = false, verbose = false, name } = options;
|
|
60
|
+
const profile = (0, targets_1.getTargetProfile)(ide);
|
|
61
|
+
const specFiles = [...BASE_SPEC_FILES, profile.specGuideTemplate];
|
|
57
62
|
// Show banner
|
|
58
63
|
console.log('\n' + chalk_1.default.bold('='.repeat(60)));
|
|
59
64
|
console.log(chalk_1.default.bold('Repofence CLI (packs)'));
|
|
@@ -68,7 +73,7 @@ async function initCommand(cwd, options) {
|
|
|
68
73
|
console.log(`📦 Project name: ${projectName}`);
|
|
69
74
|
}
|
|
70
75
|
console.log(`Project: ${cwd}`);
|
|
71
|
-
console.log(`Installing for:
|
|
76
|
+
console.log(`Installing for: ${profile.displayName}\n`);
|
|
72
77
|
// Create spec directory
|
|
73
78
|
const specDir = path.join(cwd, 'spec');
|
|
74
79
|
if (!dryRun) {
|
|
@@ -90,7 +95,7 @@ async function initCommand(cwd, options) {
|
|
|
90
95
|
// Process each template file
|
|
91
96
|
const createdFiles = [];
|
|
92
97
|
const skippedFiles = [];
|
|
93
|
-
for (const fileName of
|
|
98
|
+
for (const fileName of specFiles) {
|
|
94
99
|
const filePath = path.join(specDir, fileName);
|
|
95
100
|
const fileExists = fs.existsSync(filePath);
|
|
96
101
|
if (fileExists && !force) {
|
|
@@ -124,9 +129,33 @@ async function initCommand(cwd, options) {
|
|
|
124
129
|
process.exit(1);
|
|
125
130
|
}
|
|
126
131
|
}
|
|
132
|
+
// Claude-only project-level files
|
|
133
|
+
if (ide === 'claude') {
|
|
134
|
+
for (const file of CLAUDE_PROJECT_FILES) {
|
|
135
|
+
const filePath = path.join(cwd, file.relPath);
|
|
136
|
+
const fileExists = fs.existsSync(filePath);
|
|
137
|
+
if (fileExists && !force) {
|
|
138
|
+
skippedFiles.push(file.relPath);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const template = (0, templates_1.loadTemplate)(file.template);
|
|
142
|
+
const rendered = (0, templates_1.renderTemplate)(template, context);
|
|
143
|
+
if (dryRun) {
|
|
144
|
+
console.log(`\n[DRY RUN] Would create: ${filePath}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
148
|
+
fs.writeFileSync(filePath, rendered, 'utf-8');
|
|
149
|
+
createdFiles.push(file.relPath);
|
|
150
|
+
if (verbose) {
|
|
151
|
+
console.log(chalk_1.default.green(`✓ Created: ${file.relPath}`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
127
156
|
// Download and install repofence pack
|
|
128
157
|
let packInstalled = false;
|
|
129
|
-
const packId =
|
|
158
|
+
const packId = (0, targets_1.getPackIdForTarget)(ide);
|
|
130
159
|
if (!dryRun) {
|
|
131
160
|
const token = await (0, auth_1.readToken)();
|
|
132
161
|
if (!token) {
|
|
@@ -163,33 +192,31 @@ async function initCommand(cwd, options) {
|
|
|
163
192
|
if (!isValid) {
|
|
164
193
|
throw new Error('La firma del pack no es válida.');
|
|
165
194
|
}
|
|
166
|
-
await (0, pack_manager_1.installPack)(pack, { force, verbose });
|
|
195
|
+
await (0, pack_manager_1.installPack)(pack, { force, verbose, target: ide }, profile.installBaseDir);
|
|
167
196
|
packInstalled = true;
|
|
168
197
|
if (verbose) {
|
|
169
198
|
console.log(chalk_1.default.green(`✓ Pack "${pack.packId}" instalado`));
|
|
170
199
|
}
|
|
171
200
|
}
|
|
172
201
|
else {
|
|
173
|
-
|
|
174
|
-
console.log(`[DRY RUN] Would download pack "${packId}" and install into: ${path.join(homeDir, '.cursor/commands/')}`);
|
|
202
|
+
console.log(`[DRY RUN] Would download pack "${packId}" and install into: ${profile.installBaseDir}`);
|
|
175
203
|
}
|
|
176
204
|
// Print summary
|
|
177
205
|
if (!dryRun) {
|
|
178
|
-
printSummary(createdFiles, skippedFiles, force, packInstalled);
|
|
206
|
+
printSummary(createdFiles, skippedFiles, force, packInstalled, ide);
|
|
179
207
|
}
|
|
180
208
|
else {
|
|
181
209
|
console.log(`\n[DRY RUN] Would create ${createdFiles.length} file(s), skip ${skippedFiles.length} file(s) and install pack "${packId}"`);
|
|
182
210
|
}
|
|
183
211
|
}
|
|
184
|
-
function printSummary(createdFiles, skippedFiles, force, packInstalled) {
|
|
185
|
-
const
|
|
186
|
-
const cursorDir = path.join(homeDir, '.cursor');
|
|
212
|
+
function printSummary(createdFiles, skippedFiles, force, packInstalled, ide) {
|
|
213
|
+
const profile = (0, targets_1.getTargetProfile)(ide);
|
|
187
214
|
console.log('\n' + chalk_1.default.bold('='.repeat(60)));
|
|
188
215
|
console.log(chalk_1.default.green.bold('✨ Installation successful!'));
|
|
189
216
|
console.log(chalk_1.default.bold('='.repeat(60)));
|
|
190
217
|
if (packInstalled) {
|
|
191
218
|
console.log('\n📊 Repofence pack:');
|
|
192
|
-
console.log(` • Installed at: ${
|
|
219
|
+
console.log(` • Installed at: ${profile.installBaseDir}`);
|
|
193
220
|
}
|
|
194
221
|
console.log('\n' + chalk_1.default.dim('─'.repeat(60)));
|
|
195
222
|
console.log(chalk_1.default.bold('What was installed:'));
|
|
@@ -203,21 +230,35 @@ function printSummary(createdFiles, skippedFiles, force, packInstalled) {
|
|
|
203
230
|
});
|
|
204
231
|
}
|
|
205
232
|
// Pack installation
|
|
206
|
-
if (packInstalled) {
|
|
233
|
+
if (packInstalled && ide === 'cursor') {
|
|
207
234
|
console.log('\n Cursor (GLOBAL at ~/.cursor/):');
|
|
208
|
-
console.log(
|
|
235
|
+
console.log(' • ~/.cursor/commands/ repofence commands');
|
|
236
|
+
}
|
|
237
|
+
if (packInstalled && ide === 'claude') {
|
|
238
|
+
console.log('\n Claude Code (GLOBAL at ~/.claude/):');
|
|
239
|
+
console.log(' • ~/.claude/skills/ repofence skills');
|
|
209
240
|
}
|
|
210
241
|
console.log('\n' + chalk_1.default.dim('─'.repeat(60)));
|
|
211
242
|
console.log(chalk_1.default.cyan.bold('Next Steps - Read Carefully!'));
|
|
212
243
|
console.log(chalk_1.default.dim('─'.repeat(60)));
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
244
|
+
if (ide === 'cursor') {
|
|
245
|
+
console.log(chalk_1.default.green('\n✓ Installation complete. The /repofence.* commands are available in Cursor.\n'));
|
|
246
|
+
console.log(chalk_1.default.yellow.bold('IMPORTANT:') + ' These commands run ' + chalk_1.default.green.bold('INSIDE CURSOR'));
|
|
247
|
+
console.log(' ' + chalk_1.default.dim('(not in this terminal)') + '\n');
|
|
248
|
+
console.log(' 1. Open any project in Cursor');
|
|
249
|
+
console.log(' 2. Start a chat with the AI assistant');
|
|
250
|
+
console.log(' 3. In the chat window, type: ' + chalk_1.default.cyan('/repofence.help'));
|
|
251
|
+
console.log(' 4. Use pack commands such as: ' + chalk_1.default.cyan('/repofence.read-specs'));
|
|
252
|
+
console.log(chalk_1.default.yellow('\nRemember:') + ' /repofence.* commands are used in the Cursor chat, not in bash.');
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
console.log(chalk_1.default.green('\n✓ Installation complete. The repofence skills are available in Claude Code.\n'));
|
|
256
|
+
console.log(chalk_1.default.yellow.bold('IMPORTANT:') + ' These skills run ' + chalk_1.default.green.bold('INSIDE CLAUDE CODE'));
|
|
257
|
+
console.log(' ' + chalk_1.default.dim('(not in this terminal)') + '\n');
|
|
258
|
+
console.log(' 1. Open Claude Code in any project');
|
|
259
|
+
console.log(' 2. Type: ' + chalk_1.default.cyan('/repofence-reverse'));
|
|
260
|
+
console.log(' 3. Continue with: ' + chalk_1.default.cyan('/repofence-plan') + ' and ' + chalk_1.default.cyan('/repofence-build'));
|
|
261
|
+
}
|
|
221
262
|
console.log(chalk_1.default.dim('─'.repeat(60)) + '\n');
|
|
222
263
|
}
|
|
223
264
|
//# sourceMappingURL=init.js.map
|
package/dist/commands/reverse.js
CHANGED
|
@@ -142,13 +142,27 @@ async function reverseCommand(cwd, options) {
|
|
|
142
142
|
}
|
|
143
143
|
// Print summary
|
|
144
144
|
if (!dryRun) {
|
|
145
|
-
printSummary(updatedFiles, skippedFiles, scanResult, force);
|
|
145
|
+
printSummary(updatedFiles, skippedFiles, scanResult, force, specDir);
|
|
146
146
|
}
|
|
147
147
|
else {
|
|
148
148
|
console.log(`\n[DRY RUN] Would update ${updatedFiles.length} file(s), skip ${skippedFiles.length} file(s)`);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
function
|
|
151
|
+
function getIdeGuideLine(specDir) {
|
|
152
|
+
const cursorGuide = fs.existsSync(path.join(specDir, 'IDE_CURSOR.md'));
|
|
153
|
+
const claudeGuide = fs.existsSync(path.join(specDir, 'IDE_CLAUDE.md'));
|
|
154
|
+
if (claudeGuide && !cursorGuide) {
|
|
155
|
+
return ' 4. Use these specs with Claude Code (see /spec/IDE_CLAUDE.md)';
|
|
156
|
+
}
|
|
157
|
+
if (cursorGuide && !claudeGuide) {
|
|
158
|
+
return ' 4. Use these specs with Cursor AI (see /spec/IDE_CURSOR.md)';
|
|
159
|
+
}
|
|
160
|
+
if (claudeGuide && cursorGuide) {
|
|
161
|
+
return ' 4. Use the guide that matches your target: /spec/IDE_CLAUDE.md or /spec/IDE_CURSOR.md';
|
|
162
|
+
}
|
|
163
|
+
return ' 4. Add an IDE guide in /spec/ (IDE_CLAUDE.md or IDE_CURSOR.md)';
|
|
164
|
+
}
|
|
165
|
+
function printSummary(updatedFiles, skippedFiles, scanResult, force, specDir) {
|
|
152
166
|
console.log('\n' + chalk_1.default.bold('='.repeat(60)));
|
|
153
167
|
console.log(chalk_1.default.green.bold('✨ Reverse engineering complete!'));
|
|
154
168
|
console.log(chalk_1.default.bold('='.repeat(60)));
|
|
@@ -177,7 +191,7 @@ function printSummary(updatedFiles, skippedFiles, scanResult, force) {
|
|
|
177
191
|
console.log(' 1. Review the updated spec files in /spec/');
|
|
178
192
|
console.log(' 2. Fill in any UNKNOWN sections with actual details');
|
|
179
193
|
console.log(' 3. Customize flows and boundaries as needed');
|
|
180
|
-
console.log(
|
|
194
|
+
console.log(getIdeGuideLine(specDir));
|
|
181
195
|
console.log('');
|
|
182
196
|
}
|
|
183
197
|
//# sourceMappingURL=reverse.js.map
|
package/dist/commands/status.js
CHANGED
|
@@ -7,25 +7,38 @@ exports.statusCommand = void 0;
|
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const auth_1 = require("../core/auth");
|
|
9
9
|
const pack_manager_1 = require("../core/pack-manager");
|
|
10
|
-
const
|
|
10
|
+
const targets_1 = require("../core/targets");
|
|
11
|
+
const printManifest = async (target) => {
|
|
12
|
+
const profile = (0, targets_1.getTargetProfile)(target);
|
|
13
|
+
const manifest = await (0, pack_manager_1.readManifest)(profile.installBaseDir);
|
|
14
|
+
console.log(chalk_1.default.bold(`\n[${profile.displayName}]`));
|
|
15
|
+
if (!manifest) {
|
|
16
|
+
console.log(chalk_1.default.yellow(`⚠️ No se encontró manifest de pack en ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
console.log(chalk_1.default.cyan(`Pack: ${manifest.pack_id} v${manifest.pack_version}`));
|
|
20
|
+
console.log(chalk_1.default.cyan(`Instalado: ${manifest.installed_at}`));
|
|
21
|
+
console.log(chalk_1.default.cyan(`Expira: ${manifest.expires_at ?? 'sin expirar'}`));
|
|
22
|
+
console.log(chalk_1.default.cyan(`Artefactos:`));
|
|
23
|
+
manifest.commands.forEach((cmd) => console.log(` - ${cmd}`));
|
|
24
|
+
console.log(chalk_1.default.gray(`Manifest: ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
|
|
25
|
+
};
|
|
26
|
+
const statusCommand = async (cwd, options = {}) => {
|
|
27
|
+
void cwd;
|
|
28
|
+
const target = options.ide || 'all';
|
|
11
29
|
const token = await (0, auth_1.readToken)();
|
|
12
|
-
const manifest = await (0, pack_manager_1.readManifest)();
|
|
13
30
|
if (token) {
|
|
14
31
|
console.log(chalk_1.default.green(`✅ Token presente (${auth_1.tokenPath})`));
|
|
15
32
|
}
|
|
16
33
|
else {
|
|
17
34
|
console.log(chalk_1.default.yellow('⚠️ Token no configurado. Ejecuta `repofence auth --token <valor>`'));
|
|
18
35
|
}
|
|
19
|
-
if (
|
|
20
|
-
|
|
36
|
+
if (target === 'all') {
|
|
37
|
+
await printManifest('cursor');
|
|
38
|
+
await printManifest('claude');
|
|
21
39
|
return;
|
|
22
40
|
}
|
|
23
|
-
|
|
24
|
-
console.log(chalk_1.default.cyan(`Instalado: ${manifest.installed_at}`));
|
|
25
|
-
console.log(chalk_1.default.cyan(`Expira: ${manifest.expires_at ?? 'sin expirar'}`));
|
|
26
|
-
console.log(chalk_1.default.cyan(`Comandos:`));
|
|
27
|
-
manifest.commands.forEach((cmd) => console.log(` - ${cmd}`));
|
|
28
|
-
console.log(chalk_1.default.gray(`Manifest: ${(0, pack_manager_1.manifestFilePath)()}`));
|
|
41
|
+
await printManifest(target);
|
|
29
42
|
};
|
|
30
43
|
exports.statusCommand = statusCommand;
|
|
31
44
|
//# sourceMappingURL=status.js.map
|
|
@@ -54,7 +54,86 @@ const downloadBuffer = async (url) => {
|
|
|
54
54
|
const arrayBuffer = await res.arrayBuffer();
|
|
55
55
|
return Buffer.from(arrayBuffer);
|
|
56
56
|
};
|
|
57
|
+
const listFilesRecursive = async (root, relative = '.') => {
|
|
58
|
+
const dir = path_1.default.join(root, relative);
|
|
59
|
+
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
60
|
+
const files = [];
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const rel = path_1.default.join(relative, entry.name);
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
files.push(...(await listFilesRecursive(root, rel)));
|
|
65
|
+
}
|
|
66
|
+
else if (entry.isFile()) {
|
|
67
|
+
files.push(rel);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return files;
|
|
71
|
+
};
|
|
72
|
+
const toSkillName = (commandPath) => {
|
|
73
|
+
const base = path_1.default.basename(commandPath, '.md');
|
|
74
|
+
if (base.startsWith('repofence.')) {
|
|
75
|
+
return `repofence-${base.slice('repofence.'.length)}`;
|
|
76
|
+
}
|
|
77
|
+
return base.replace(/[._]/g, '-');
|
|
78
|
+
};
|
|
79
|
+
const extractDescription = (markdown) => {
|
|
80
|
+
const match = markdown.match(/\*\*Description\*\*:\s*(.+)/);
|
|
81
|
+
if (match?.[1])
|
|
82
|
+
return match[1].trim();
|
|
83
|
+
const firstSentence = markdown.split('\n').find((line) => line.trim() && !line.startsWith('#'));
|
|
84
|
+
return (firstSentence || 'Repofence workflow skill').replace(/^[-*]\s*/, '').trim();
|
|
85
|
+
};
|
|
86
|
+
const stripCursorCommandHeader = (markdown) => {
|
|
87
|
+
const lines = markdown.split('\n');
|
|
88
|
+
const filtered = [];
|
|
89
|
+
let skipUntilAfterVersion = false;
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
if (line.startsWith('# Command: /')) {
|
|
92
|
+
skipUntilAfterVersion = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (skipUntilAfterVersion && line.startsWith('**Version**:')) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (skipUntilAfterVersion && line.startsWith('**Description**:')) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (skipUntilAfterVersion && line.trim() === '---') {
|
|
102
|
+
skipUntilAfterVersion = false;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
filtered.push(line);
|
|
106
|
+
}
|
|
107
|
+
return filtered.join('\n').trimStart();
|
|
108
|
+
};
|
|
109
|
+
const adaptBodyForClaude = (markdown) => {
|
|
110
|
+
return markdown
|
|
111
|
+
.replace(/\/repofence\./g, '/repofence-')
|
|
112
|
+
.replace(/\/spec\/IDE_CURSOR\.md/g, '/spec/IDE_CLAUDE.md')
|
|
113
|
+
.replace(/Cursor chat/gi, 'Claude Code chat')
|
|
114
|
+
.replace(/Cursor AI/gi, 'Claude Code');
|
|
115
|
+
};
|
|
116
|
+
const toClaudeSkillMarkdown = (commandPath, markdown) => {
|
|
117
|
+
const skillName = toSkillName(commandPath);
|
|
118
|
+
const description = extractDescription(markdown);
|
|
119
|
+
const hasSideEffects = skillName.includes('build') ||
|
|
120
|
+
skillName.includes('update') ||
|
|
121
|
+
skillName.includes('finish') ||
|
|
122
|
+
skillName.includes('start') ||
|
|
123
|
+
skillName.includes('reverse');
|
|
124
|
+
const frontmatter = [
|
|
125
|
+
'---',
|
|
126
|
+
`name: ${skillName}`,
|
|
127
|
+
`description: ${description}`,
|
|
128
|
+
...(hasSideEffects ? ['disable-model-invocation: true'] : []),
|
|
129
|
+
'---',
|
|
130
|
+
'',
|
|
131
|
+
].join('\n');
|
|
132
|
+
const body = adaptBodyForClaude(stripCursorCommandHeader(markdown));
|
|
133
|
+
return { skillName, content: `${frontmatter}${body}\n` };
|
|
134
|
+
};
|
|
57
135
|
const installFromArchive = async (pack, options, baseDir) => {
|
|
136
|
+
const target = options.target || 'cursor';
|
|
58
137
|
const dir = commandsDir(baseDir);
|
|
59
138
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
60
139
|
const archiveUrl = pack.archiveUrl;
|
|
@@ -77,16 +156,9 @@ const installFromArchive = async (pack, options, baseDir) => {
|
|
|
77
156
|
// Extraer lista de archivos y luego extraer contenido
|
|
78
157
|
const tmpDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'repofence-archive-'));
|
|
79
158
|
const archivePath = path_1.default.join(tmpDir, 'pack.tar.gz');
|
|
159
|
+
const extractedPath = path_1.default.join(tmpDir, 'extracted');
|
|
160
|
+
await promises_1.default.mkdir(extractedPath, { recursive: true });
|
|
80
161
|
await promises_1.default.writeFile(archivePath, archiveBuf);
|
|
81
|
-
const extractedFiles = [];
|
|
82
|
-
await tar_1.default.t({
|
|
83
|
-
file: archivePath,
|
|
84
|
-
onentry: (entry) => {
|
|
85
|
-
if (entry.type === 'File') {
|
|
86
|
-
extractedFiles.push(entry.path);
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
162
|
// Validación de firma (manifestHash ya viene en el pack; archiveHash validada arriba)
|
|
91
163
|
await (0, exports.validateSignature)({
|
|
92
164
|
...pack,
|
|
@@ -94,29 +166,99 @@ const installFromArchive = async (pack, options, baseDir) => {
|
|
|
94
166
|
});
|
|
95
167
|
await tar_1.default.x({
|
|
96
168
|
file: archivePath,
|
|
97
|
-
cwd:
|
|
169
|
+
cwd: extractedPath,
|
|
98
170
|
});
|
|
171
|
+
const manifest = target === 'claude'
|
|
172
|
+
? await installClaudeFromExtracted(extractedPath, pack, baseDir, options)
|
|
173
|
+
: await installCursorFromExtracted(extractedPath, pack, baseDir);
|
|
99
174
|
await promises_1.default.rm(tmpDir, { recursive: true, force: true });
|
|
175
|
+
if (options.verbose) {
|
|
176
|
+
console.log(`[repofence] instalado desde archive ${archiveUrl}`);
|
|
177
|
+
}
|
|
178
|
+
return manifest;
|
|
179
|
+
};
|
|
180
|
+
const installCursorFromExtracted = async (extractedPath, pack, baseDir) => {
|
|
181
|
+
const dir = commandsDir(baseDir);
|
|
182
|
+
await promises_1.default.mkdir(dir, { recursive: true });
|
|
183
|
+
const files = await listFilesRecursive(extractedPath);
|
|
184
|
+
for (const rel of files) {
|
|
185
|
+
const src = path_1.default.join(extractedPath, rel);
|
|
186
|
+
const dst = path_1.default.join(dir, rel);
|
|
187
|
+
await promises_1.default.mkdir(path_1.default.dirname(dst), { recursive: true });
|
|
188
|
+
await promises_1.default.copyFile(src, dst);
|
|
189
|
+
}
|
|
100
190
|
const manifest = {
|
|
101
191
|
pack_id: pack.packId,
|
|
102
192
|
pack_version: pack.packVersion,
|
|
103
193
|
installed_at: new Date().toISOString(),
|
|
104
194
|
expires_at: pack.expiresAt ?? null,
|
|
105
|
-
commands:
|
|
195
|
+
commands: files.map((f) => f.replace(/^\.\//, '')),
|
|
106
196
|
};
|
|
107
197
|
await (0, exports.writeManifest)(baseDir, manifest);
|
|
108
|
-
|
|
109
|
-
|
|
198
|
+
return manifest;
|
|
199
|
+
};
|
|
200
|
+
const installClaudeFromExtracted = async (extractedPath, pack, baseDir, options) => {
|
|
201
|
+
const dir = commandsDir(baseDir);
|
|
202
|
+
await promises_1.default.mkdir(dir, { recursive: true });
|
|
203
|
+
const files = await listFilesRecursive(extractedPath);
|
|
204
|
+
const markdownFiles = files.filter((f) => f.endsWith('.md'));
|
|
205
|
+
const artifacts = [];
|
|
206
|
+
for (const rel of markdownFiles) {
|
|
207
|
+
const sourcePath = path_1.default.join(extractedPath, rel);
|
|
208
|
+
const sourceMarkdown = await promises_1.default.readFile(sourcePath, 'utf8');
|
|
209
|
+
const { skillName, content } = toClaudeSkillMarkdown(rel, sourceMarkdown);
|
|
210
|
+
const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
|
|
211
|
+
if (!options.force && (await fileExists(targetPath))) {
|
|
212
|
+
throw new Error(`El skill ya existe: ${targetPath} (usa --force para sobrescribir)`);
|
|
213
|
+
}
|
|
214
|
+
await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
|
|
215
|
+
await promises_1.default.writeFile(targetPath, content, 'utf8');
|
|
216
|
+
artifacts.push(path_1.default.relative(dir, targetPath));
|
|
110
217
|
}
|
|
218
|
+
const manifest = {
|
|
219
|
+
pack_id: pack.packId,
|
|
220
|
+
pack_version: pack.packVersion,
|
|
221
|
+
installed_at: new Date().toISOString(),
|
|
222
|
+
expires_at: pack.expiresAt ?? null,
|
|
223
|
+
commands: artifacts,
|
|
224
|
+
};
|
|
225
|
+
await (0, exports.writeManifest)(baseDir, manifest);
|
|
111
226
|
return manifest;
|
|
112
227
|
};
|
|
113
228
|
const installPack = async (pack, options = {}, baseDir) => {
|
|
229
|
+
const target = options.target || 'cursor';
|
|
114
230
|
// Si viene archiveUrl, descargar tar.gz y extraer.
|
|
115
231
|
if (pack.archiveUrl) {
|
|
116
232
|
return installFromArchive(pack, options, baseDir);
|
|
117
233
|
}
|
|
118
234
|
const dir = commandsDir(baseDir);
|
|
119
235
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
236
|
+
if (target === 'claude') {
|
|
237
|
+
const artifacts = [];
|
|
238
|
+
for (const file of pack.files) {
|
|
239
|
+
const source = Buffer.from(file.contentBase64, 'base64').toString('utf8');
|
|
240
|
+
const { skillName, content } = toClaudeSkillMarkdown(file.path, source);
|
|
241
|
+
const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
|
|
242
|
+
if (!options.force && (await fileExists(targetPath))) {
|
|
243
|
+
throw new Error(`El skill ya existe: ${targetPath} (usa --force para sobrescribir)`);
|
|
244
|
+
}
|
|
245
|
+
await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
|
|
246
|
+
await promises_1.default.writeFile(targetPath, content, 'utf8');
|
|
247
|
+
artifacts.push(path_1.default.relative(dir, targetPath));
|
|
248
|
+
if (options.verbose) {
|
|
249
|
+
console.log(`[repofence] skill escrito ${targetPath}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const manifest = {
|
|
253
|
+
pack_id: pack.packId,
|
|
254
|
+
pack_version: pack.packVersion,
|
|
255
|
+
installed_at: new Date().toISOString(),
|
|
256
|
+
expires_at: pack.expiresAt ?? null,
|
|
257
|
+
commands: artifacts,
|
|
258
|
+
};
|
|
259
|
+
await (0, exports.writeManifest)(baseDir, manifest);
|
|
260
|
+
return manifest;
|
|
261
|
+
}
|
|
120
262
|
for (const file of pack.files) {
|
|
121
263
|
const target = path_1.default.join(dir, file.path);
|
|
122
264
|
if (!options.force && (await fileExists(target))) {
|
|
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EMBEDDED_PACK_PUBLIC_KEY_PEM = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Default Ed25519 public key for verifying signed packs from the Repofence API.
|
|
6
|
-
*
|
|
6
|
+
* Must match `public_key.pem` in the repo (copy PEM lines verbatim from that file).
|
|
7
7
|
* Override with env REPOFENCE_PUBLIC_KEY for testing or key rotation.
|
|
8
8
|
*/
|
|
9
9
|
exports.EMBEDDED_PACK_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
10
|
-
|
|
10
|
+
MCowBQYDK2VwAyEAJTfxc7baW85w7r3uxbYNuaZRMoe1e2lcva2l7MO0iak=
|
|
11
11
|
-----END PUBLIC KEY-----
|
|
12
12
|
`;
|
|
13
13
|
//# sourceMappingURL=pack-public-key.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getPackIdForTarget = exports.isIdeTarget = exports.getTargetProfile = void 0;
|
|
7
|
+
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const home = os_1.default.homedir();
|
|
10
|
+
const TARGETS = {
|
|
11
|
+
cursor: {
|
|
12
|
+
id: 'cursor',
|
|
13
|
+
displayName: 'Cursor',
|
|
14
|
+
installBaseDir: path_1.default.join(home, '.cursor', 'commands'),
|
|
15
|
+
helpCommand: '/repofence.help',
|
|
16
|
+
packIdEnv: 'REPOFENCE_PACK_ID',
|
|
17
|
+
specGuideTemplate: 'IDE_CURSOR.md',
|
|
18
|
+
},
|
|
19
|
+
claude: {
|
|
20
|
+
id: 'claude',
|
|
21
|
+
displayName: 'Claude Code',
|
|
22
|
+
installBaseDir: path_1.default.join(home, '.claude', 'skills'),
|
|
23
|
+
helpCommand: '/repofence-reverse',
|
|
24
|
+
packIdEnv: 'REPOFENCE_CLAUDE_PACK_ID',
|
|
25
|
+
specGuideTemplate: 'IDE_CLAUDE.md',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const getTargetProfile = (target) => TARGETS[target];
|
|
29
|
+
exports.getTargetProfile = getTargetProfile;
|
|
30
|
+
const isIdeTarget = (value) => value === 'cursor' || value === 'claude';
|
|
31
|
+
exports.isIdeTarget = isIdeTarget;
|
|
32
|
+
const getPackIdForTarget = (target) => {
|
|
33
|
+
const profile = (0, exports.getTargetProfile)(target);
|
|
34
|
+
if (profile.id === 'claude') {
|
|
35
|
+
return process.env[profile.packIdEnv] || process.env.REPOFENCE_PACK_ID || 'core';
|
|
36
|
+
}
|
|
37
|
+
return process.env[profile.packIdEnv] || 'core';
|
|
38
|
+
};
|
|
39
|
+
exports.getPackIdForTarget = getPackIdForTarget;
|
|
40
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Repofence SDD Instructions
|
|
2
|
+
|
|
3
|
+
Use the spec layer as the primary source of truth in this repository.
|
|
4
|
+
|
|
5
|
+
## Mandatory workflow
|
|
6
|
+
|
|
7
|
+
1. Read `/spec/AI_RULES.md` first.
|
|
8
|
+
2. Read `/spec/SYSTEM_OVERVIEW.md`, `/spec/CORE_FLOWS.md`, and `/spec/BOUNDARIES.md`.
|
|
9
|
+
3. For brownfield projects, run `/repofence-reverse` before planning implementation work.
|
|
10
|
+
4. If specs are missing or stale, say so explicitly and propose how to refresh.
|
|
11
|
+
|
|
12
|
+
## Commanding model
|
|
13
|
+
|
|
14
|
+
- Use Repofence skills for repeatable workflows:
|
|
15
|
+
- `/repofence-reverse`
|
|
16
|
+
- `/repofence-ask`
|
|
17
|
+
- `/repofence-plan`
|
|
18
|
+
- `/repofence-build`
|
|
19
|
+
- `/repofence-review`
|
|
20
|
+
- `/repofence-finish`
|
|
21
|
+
|
|
22
|
+
## Boundaries
|
|
23
|
+
|
|
24
|
+
- Do not invent architecture details not present in specs.
|
|
25
|
+
- Do not silently violate constraints in `BOUNDARIES.md`.
|
|
26
|
+
- If implementation work conflicts with specs, stop and ask for direction.
|
|
27
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# SDD Rules (Repofence)
|
|
2
|
+
|
|
3
|
+
These rules apply when editing this project with Claude Code.
|
|
4
|
+
|
|
5
|
+
## Spec-first
|
|
6
|
+
|
|
7
|
+
- Always read `spec/AI_RULES.md` before implementation.
|
|
8
|
+
- Treat `spec/SYSTEM_OVERVIEW.md`, `spec/CORE_FLOWS.md`, and `spec/BOUNDARIES.md` as authoritative context.
|
|
9
|
+
|
|
10
|
+
## Brownfield priority
|
|
11
|
+
|
|
12
|
+
- Use `/repofence-reverse` early to keep specs aligned with code.
|
|
13
|
+
- If spec coverage is incomplete, label unknowns explicitly instead of guessing.
|
|
14
|
+
|
|
15
|
+
## Quality checks
|
|
16
|
+
|
|
17
|
+
- Validate changes against boundaries before finalizing.
|
|
18
|
+
- Keep specs updated when workflows, boundaries, or invariants change.
|
|
19
|
+
- Prefer small, traceable diffs from task to implementation.
|
|
20
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Claude Code Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide provides ready-to-copy prompts for using Claude Code with `{{PROJECT_NAME}}`.
|
|
4
|
+
|
|
5
|
+
## First Prompt
|
|
6
|
+
|
|
7
|
+
Use this at the start of every session:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Read /spec/AI_RULES.md first, then /spec/SYSTEM_OVERVIEW.md, /spec/CORE_FLOWS.md, and /spec/BOUNDARIES.md. Summarize constraints before changing code.
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Brownfield Fast Path (reverse-first)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Run /repofence-reverse first to refresh the spec layer for this codebase. Then use /repofence-ask to answer questions only from specs.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Recommended Skill Flow
|
|
20
|
+
|
|
21
|
+
1. `/repofence-reverse` for brownfield discovery
|
|
22
|
+
2. `/repofence-ask` for spec-only Q&A
|
|
23
|
+
3. `/repofence-plan` before code changes
|
|
24
|
+
4. `/repofence-build` for task execution
|
|
25
|
+
5. `/repofence-review` and `/repofence-finish` to close work
|
|
26
|
+
|
|
27
|
+
## Guardrails
|
|
28
|
+
|
|
29
|
+
- Use specs as source of truth.
|
|
30
|
+
- If a spec is stale or missing, call it out explicitly.
|
|
31
|
+
- Never guess implementation details when specs do not contain them.
|
|
32
|
+
- Update specs when behavior or boundaries change.
|
|
33
|
+
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repofence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Repofence CLI (packs + backend auth)",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"repofence": "
|
|
7
|
+
"repofence": "dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/**/*.js",
|
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
"commander": "^11.1.0",
|
|
38
38
|
"dotenv": "^16.4.5",
|
|
39
39
|
"open": "^11.0.0",
|
|
40
|
-
"repofence": "^0.1.2",
|
|
41
40
|
"tar": "^6.2.1"
|
|
42
41
|
},
|
|
43
42
|
"devDependencies": {
|