@justinforfun/redo-skill 0.1.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/bin/install.js +264 -0
- package/package.json +31 -0
- package/skills/redo/SKILL.md +145 -0
- package/skills/redo/agents/openai.yaml +4 -0
package/bin/install.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import process from 'node:process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import inquirer from 'inquirer';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
16
|
+
const sourceSkillDir = path.join(packageRoot, 'skills', 'redo');
|
|
17
|
+
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '-');
|
|
18
|
+
|
|
19
|
+
const targets = [
|
|
20
|
+
{
|
|
21
|
+
id: 'codex',
|
|
22
|
+
name: 'Codex',
|
|
23
|
+
description: 'Install redo as a Codex skill.',
|
|
24
|
+
tasks: [
|
|
25
|
+
{
|
|
26
|
+
type: 'skill',
|
|
27
|
+
label: 'Codex skill',
|
|
28
|
+
destination: path.join(os.homedir(), '.agents', 'skills', 'redo')
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
trigger: '$redo kafka, redo kafka, or select redo from the skill picker'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'codex-cli',
|
|
35
|
+
name: 'Codex CLI',
|
|
36
|
+
description: 'Install redo as a Codex CLI skill.',
|
|
37
|
+
tasks: [
|
|
38
|
+
{
|
|
39
|
+
type: 'skill',
|
|
40
|
+
label: 'Codex CLI skill',
|
|
41
|
+
destination: path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'skills', 'redo')
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
trigger: '$redo kafka, redo kafka, or /skills then choose redo'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'claude-code',
|
|
48
|
+
name: 'Claude Code',
|
|
49
|
+
description: 'Install redo as a Claude Code skill and /redo command.',
|
|
50
|
+
tasks: [
|
|
51
|
+
{
|
|
52
|
+
type: 'skill',
|
|
53
|
+
label: 'Claude Code skill',
|
|
54
|
+
destination: path.join(os.homedir(), '.claude', 'skills', 'redo')
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'claude-command',
|
|
58
|
+
label: 'Claude Code /redo command',
|
|
59
|
+
destination: path.join(os.homedir(), '.claude', 'commands', 'redo.md')
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
trigger: '/redo kafka'
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
async function main() {
|
|
67
|
+
printHeader();
|
|
68
|
+
await ensureSourceSkillExists();
|
|
69
|
+
|
|
70
|
+
const selectedTargets = await promptTargets();
|
|
71
|
+
if (selectedTargets.length === 0) {
|
|
72
|
+
console.log(chalk.yellow('No tools selected. Nothing installed.'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const plan = selectedTargets.flatMap((target) =>
|
|
77
|
+
target.tasks.map((task) => ({
|
|
78
|
+
...task,
|
|
79
|
+
targetName: target.name,
|
|
80
|
+
trigger: target.trigger
|
|
81
|
+
}))
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const conflictActions = await promptConflictActions(plan);
|
|
85
|
+
const spinner = ora('Installing redo skill...').start();
|
|
86
|
+
const results = [];
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
for (const task of plan) {
|
|
90
|
+
const action = conflictActions.get(task.destination) || 'install';
|
|
91
|
+
const result = await installTask(task, action);
|
|
92
|
+
results.push(result);
|
|
93
|
+
}
|
|
94
|
+
spinner.succeed('redo skill installed.');
|
|
95
|
+
} catch (error) {
|
|
96
|
+
spinner.fail('Installation failed.');
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
printSummary(results, selectedTargets);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function printHeader() {
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log(chalk.bold('redo skill installer'));
|
|
106
|
+
console.log(chalk.dim('Reverse-learn technologies through engineering constraints, trade-offs, and technical debt.'));
|
|
107
|
+
console.log('');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function ensureSourceSkillExists() {
|
|
111
|
+
try {
|
|
112
|
+
await fs.access(path.join(sourceSkillDir, 'SKILL.md'));
|
|
113
|
+
} catch {
|
|
114
|
+
throw new Error(`Cannot find source skill at ${sourceSkillDir}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function promptTargets() {
|
|
119
|
+
const answers = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'checkbox',
|
|
122
|
+
name: 'targetIds',
|
|
123
|
+
message: 'Select AI tools to install redo for:',
|
|
124
|
+
choices: targets.map((target) => ({
|
|
125
|
+
name: `${target.name} ${chalk.dim('- ' + target.description)}`,
|
|
126
|
+
value: target.id,
|
|
127
|
+
checked: true
|
|
128
|
+
})),
|
|
129
|
+
validate: (values) => (values.length > 0 ? true : 'Select at least one tool.')
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
return targets.filter((target) => answers.targetIds.includes(target.id));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function promptConflictActions(plan) {
|
|
137
|
+
const actions = new Map();
|
|
138
|
+
|
|
139
|
+
for (const task of plan) {
|
|
140
|
+
if (!(await exists(task.destination))) {
|
|
141
|
+
actions.set(task.destination, 'install');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const answers = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'list',
|
|
148
|
+
name: 'action',
|
|
149
|
+
message: `${task.label} already exists at ${task.destination}`,
|
|
150
|
+
default: 'backup',
|
|
151
|
+
choices: [
|
|
152
|
+
{
|
|
153
|
+
name: 'Backup then overwrite',
|
|
154
|
+
value: 'backup'
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'Overwrite',
|
|
158
|
+
value: 'overwrite'
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'Skip',
|
|
162
|
+
value: 'skip'
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
actions.set(task.destination, answers.action);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return actions;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function installTask(task, action) {
|
|
175
|
+
if (action === 'skip') {
|
|
176
|
+
return {
|
|
177
|
+
status: 'skipped',
|
|
178
|
+
task
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (await exists(task.destination)) {
|
|
183
|
+
if (action === 'backup') {
|
|
184
|
+
const backupPath = await uniqueBackupPath(task.destination);
|
|
185
|
+
await fs.rename(task.destination, backupPath);
|
|
186
|
+
} else if (action === 'overwrite') {
|
|
187
|
+
await fs.rm(task.destination, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await fs.mkdir(path.dirname(task.destination), { recursive: true });
|
|
192
|
+
|
|
193
|
+
if (task.type === 'skill') {
|
|
194
|
+
await fs.cp(sourceSkillDir, task.destination, { recursive: true });
|
|
195
|
+
} else if (task.type === 'claude-command') {
|
|
196
|
+
await fs.writeFile(task.destination, claudeCommandTemplate(), 'utf8');
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error(`Unknown task type: ${task.type}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
status: 'installed',
|
|
203
|
+
task
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function exists(filePath) {
|
|
208
|
+
try {
|
|
209
|
+
await fs.access(filePath);
|
|
210
|
+
return true;
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function uniqueBackupPath(filePath) {
|
|
217
|
+
let candidate = `${filePath}.backup-${timestamp}`;
|
|
218
|
+
let index = 1;
|
|
219
|
+
|
|
220
|
+
while (await exists(candidate)) {
|
|
221
|
+
candidate = `${filePath}.backup-${timestamp}-${index}`;
|
|
222
|
+
index += 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return candidate;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function claudeCommandTemplate() {
|
|
229
|
+
return `---
|
|
230
|
+
description: Reverse-learn a technology by reconstructing its engineering constraints, trade-offs, debt, fixes, and unresolved pain points.
|
|
231
|
+
argument-hint: <technology/tool> [--lang zh|en]
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
Use the redo skill to analyze:
|
|
235
|
+
|
|
236
|
+
$ARGUMENTS
|
|
237
|
+
|
|
238
|
+
If the user did not provide a topic, ask for one. Otherwise, follow the redo skill output contract. Use the user's current language unless --lang zh or --lang en is provided.
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function printSummary(results, selectedTargets) {
|
|
243
|
+
console.log('');
|
|
244
|
+
console.log(chalk.bold('Installed targets'));
|
|
245
|
+
|
|
246
|
+
for (const result of results) {
|
|
247
|
+
const icon = result.status === 'installed' ? chalk.green('✓') : chalk.yellow('-');
|
|
248
|
+
console.log(`${icon} ${result.task.label}: ${result.task.destination}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log(chalk.bold('How to trigger'));
|
|
253
|
+
|
|
254
|
+
for (const target of selectedTargets) {
|
|
255
|
+
console.log(`${chalk.cyan(target.name)}: ${target.trigger}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log('');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
main().catch((error) => {
|
|
262
|
+
console.error(chalk.red(error.message));
|
|
263
|
+
process.exitCode = 1;
|
|
264
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@justinforfun/redo-skill",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install the redo skill for Codex, Codex CLI, and Claude Code.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"redo-skill": "bin/install.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"skills/redo/"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node ../../scripts/sync-skill-package.js redo packages/redo-skill",
|
|
18
|
+
"check": "node --check bin/install.js",
|
|
19
|
+
"prepack": "npm run build",
|
|
20
|
+
"start": "node bin/install.js"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"chalk": "^5.3.0",
|
|
24
|
+
"inquirer": "^9.2.23",
|
|
25
|
+
"ora": "^8.0.1"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redo
|
|
3
|
+
description: Reverse-learn any programming technology, framework, tool, or infrastructure system by reconstructing its evolution as a sequence of real engineering constraints, candidate designs, trade-offs, technical debt, later fixes, and unresolved pain points. Use this skill when the user asks with /redo, redo, $redo, reverse learn, retrace, re-derive, or asks to understand why a technology evolved the way it did.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> Every mature system is a fossil record of the constraints it survived.
|
|
7
|
+
|
|
8
|
+
# Redo
|
|
9
|
+
|
|
10
|
+
Redo is a reverse-learning skill for understanding a technology as if you were one of the engineers who evolved it from zero to today. Do not write a feature tour, timeline summary, or encyclopedia article. Reconstruct the path of necessity: what problem existed at each stage, what options were available, why the chosen design won, what trade-off it accepted, and what debt it left behind.
|
|
11
|
+
|
|
12
|
+
## Trigger Patterns
|
|
13
|
+
|
|
14
|
+
Use this skill when the user asks to reverse-learn, redo, re-derive, reconstruct, or deeply understand a programming technology, framework, language, database, infrastructure system, protocol, build tool, runtime, AI tool, or developer platform.
|
|
15
|
+
|
|
16
|
+
Explicit examples:
|
|
17
|
+
|
|
18
|
+
- `/redo kafka`
|
|
19
|
+
- `$redo kafka`
|
|
20
|
+
- `redo kafka`
|
|
21
|
+
- `redo kafka --lang en`
|
|
22
|
+
- `Reverse-learn Kafka`
|
|
23
|
+
- `Retrace React's evolution path`
|
|
24
|
+
- `Explain Docker by retracing its engineering decisions`
|
|
25
|
+
|
|
26
|
+
## Arguments
|
|
27
|
+
|
|
28
|
+
Parse the request as:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
redo <topic> [--lang zh|en]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `<topic>` is the technology, tool, or system to analyze.
|
|
35
|
+
- `--lang zh` forces Chinese output.
|
|
36
|
+
- `--lang en` forces English output.
|
|
37
|
+
- If `--lang` is absent, respond in the user's current conversation language.
|
|
38
|
+
|
|
39
|
+
## Evidence Requirements
|
|
40
|
+
|
|
41
|
+
For real technologies, do not rely only on memory when dates, versions, authorship, current status, or historical claims matter.
|
|
42
|
+
|
|
43
|
+
- If web access is available and the user has not forbidden it, verify with primary or high-authority sources first: official documentation, release notes, RFCs, design docs, papers, project repositories, or authoritative engineering blogs.
|
|
44
|
+
- If web access is unavailable, blocked, or the user forbids browsing, state clearly that the analysis is not freshly verified from online sources.
|
|
45
|
+
- Distinguish sourced facts from inference. It is acceptable to infer engineering motivations, but label them as inference when the source does not explicitly say so.
|
|
46
|
+
- Prefer fewer, stronger stages over many shallow ones. A good answer usually has 5-9 stages.
|
|
47
|
+
|
|
48
|
+
## Output Contract
|
|
49
|
+
|
|
50
|
+
Start with a compact orientation:
|
|
51
|
+
|
|
52
|
+
- What the system is.
|
|
53
|
+
- The central pressure that shaped its evolution.
|
|
54
|
+
- The main trade-off theme that appears repeatedly.
|
|
55
|
+
|
|
56
|
+
Then produce the sections below.
|
|
57
|
+
|
|
58
|
+
### 1. Evolution Stages
|
|
59
|
+
|
|
60
|
+
For each stage, use this structure:
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
## Stage N: <stage name> (<approximate years or versions>)
|
|
64
|
+
|
|
65
|
+
**Constraint:** <the real engineering situation at the time>
|
|
66
|
+
|
|
67
|
+
| Option | Cost | Why it did or did not win |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| A. <candidate> | <cost> | <reason> |
|
|
70
|
+
| B. <candidate> | <cost> | <reason> |
|
|
71
|
+
| C. <chosen candidate> | <cost> | Chosen because <reason> |
|
|
72
|
+
|
|
73
|
+
**Key trade-off:** <the most important exchange>
|
|
74
|
+
|
|
75
|
+
**Debt introduced:** <what this choice made harder later>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Stage quality rules:
|
|
79
|
+
|
|
80
|
+
- Every stage must be driven by a concrete constraint, not by a release note.
|
|
81
|
+
- Every table must contain at least two rejected options and one chosen path.
|
|
82
|
+
- Explain why a reasonable engineer would choose the winning path at that time, even if it later caused problems.
|
|
83
|
+
- Avoid hindsight moralizing. The point is to recreate the decision pressure, not to mock past designs.
|
|
84
|
+
|
|
85
|
+
### 2. Throughline
|
|
86
|
+
|
|
87
|
+
Summarize the recurring design philosophy in one or two paragraphs. Make it specific to the topic, for example:
|
|
88
|
+
|
|
89
|
+
- "Push complexity into the runtime to keep application code simple."
|
|
90
|
+
- "Preserve backward compatibility even when it complicates internals."
|
|
91
|
+
- "Use logs as the universal abstraction."
|
|
92
|
+
|
|
93
|
+
### 3. Debt Map
|
|
94
|
+
|
|
95
|
+
Create two tables.
|
|
96
|
+
|
|
97
|
+
Resolved debt:
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
| Debt | Introduced in | Resolved in | Resolution |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Unresolved debt:
|
|
105
|
+
|
|
106
|
+
```markdown
|
|
107
|
+
| Pain point | Why it remains hard | Current manifestation |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 4. Pain Point Ranking
|
|
112
|
+
|
|
113
|
+
Rank the top unresolved problems that users still feel today.
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
| Rank | Pain point | One-line explanation | Competitive attack angle |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use the competitive attack angle only when there is a meaningful comparison. Otherwise write "N/A".
|
|
121
|
+
|
|
122
|
+
### 5. Causal Chain
|
|
123
|
+
|
|
124
|
+
End with a causal chain that makes the evolution memorable:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
early constraint -> chosen design -> solved problem -> new debt -> later fix -> remaining pain
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Keep it concise and legible.
|
|
131
|
+
|
|
132
|
+
## Style
|
|
133
|
+
|
|
134
|
+
- Write like a senior engineer explaining architecture history to another engineer.
|
|
135
|
+
- Prefer concrete mechanisms, failure modes, and operational consequences.
|
|
136
|
+
- Use direct language. Avoid vague praise such as "powerful", "robust", or "revolutionary" unless immediately explained.
|
|
137
|
+
- Use Chinese if the user is writing Chinese, English if the user is writing English, unless `--lang` overrides.
|
|
138
|
+
- If the topic is too broad, choose the core system path and say what you intentionally left out.
|
|
139
|
+
- If the historical record is uncertain, say so and give the most likely interpretation.
|
|
140
|
+
|
|
141
|
+
## Tool-Specific Invocation Notes
|
|
142
|
+
|
|
143
|
+
- Claude Code may expose this as `/redo <topic>` when installed with a command wrapper.
|
|
144
|
+
- Codex and Codex CLI should be invoked through skill selection or explicit skill mention, such as `$redo kafka`, `redo kafka`, or natural language requests that match this skill.
|
|
145
|
+
- Do not promise that every AI tool supports a native `/redo` slash command.
|