codeadd 0.1.0 → 0.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 +15 -15
- package/package.json +2 -2
- package/src/config.js +5 -5
- package/src/doctor.js +14 -14
- package/src/github.js +3 -3
- package/src/installer.js +11 -11
- package/src/uninstaller.js +18 -18
- package/src/updater.js +8 -8
- package/src/validator.js +5 -5
package/README.md
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# add
|
|
2
2
|
|
|
3
|
-
CLI installer for [Product Flow Factory (
|
|
3
|
+
CLI installer for [Product Flow Factory (ADD)](https://github.com/brabos-ai/product-flow-factory).
|
|
4
4
|
|
|
5
5
|
## Install and run
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# interactive install
|
|
9
|
-
npx
|
|
9
|
+
npx add install
|
|
10
10
|
|
|
11
11
|
# install from main branch
|
|
12
|
-
npx
|
|
12
|
+
npx add install --version main
|
|
13
13
|
|
|
14
14
|
# install from a specific tag
|
|
15
|
-
npx
|
|
15
|
+
npx add install --version v2.0.1
|
|
16
16
|
|
|
17
17
|
# update installed files to latest release
|
|
18
|
-
npx
|
|
18
|
+
npx add update
|
|
19
19
|
|
|
20
20
|
# environment checks
|
|
21
|
-
npx
|
|
21
|
+
npx add doctor
|
|
22
22
|
|
|
23
23
|
# integrity checks
|
|
24
|
-
npx
|
|
24
|
+
npx add validate
|
|
25
25
|
|
|
26
26
|
# repair integrity issues by restoring from release
|
|
27
|
-
npx
|
|
27
|
+
npx add validate --repair
|
|
28
28
|
|
|
29
29
|
# remove installed files
|
|
30
|
-
npx
|
|
31
|
-
npx
|
|
30
|
+
npx add uninstall
|
|
31
|
+
npx add uninstall --force
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Commands
|
|
@@ -37,15 +37,15 @@ npx pff uninstall --force
|
|
|
37
37
|
- `install --version main`: install from GitHub `main` branch
|
|
38
38
|
- `install --version <tag>`: install from a specific GitHub tag
|
|
39
39
|
- `update`: update installed files to latest GitHub release
|
|
40
|
-
- `doctor`: verify Node, Git, and
|
|
41
|
-
- `validate`: verify file hashes from `.
|
|
40
|
+
- `doctor`: verify Node, Git, and ADD installation health
|
|
41
|
+
- `validate`: verify file hashes from `.add/manifest.json`
|
|
42
42
|
- `validate --repair`: restore missing or modified files
|
|
43
|
-
- `config show`: print current
|
|
43
|
+
- `config show`: print current ADD installation config
|
|
44
44
|
- `config show --verbose`: config + release update check
|
|
45
45
|
|
|
46
46
|
## What gets installed
|
|
47
47
|
|
|
48
|
-
- Core (`.
|
|
48
|
+
- Core (`.add/`): always installed
|
|
49
49
|
- Provider integration (optional, selected interactively):
|
|
50
50
|
- Claude Code -> `.claude/`
|
|
51
51
|
- Codex (OpenAI) -> `.agent/`
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeadd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI for Code Addiction framework - AI-powered development workflows",
|
|
5
5
|
"type": "module",
|
|
6
|
-
|
|
6
|
+
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/brabos-ai/product-flow-factory.git"
|
|
9
9
|
},
|
package/src/config.js
CHANGED
|
@@ -4,12 +4,12 @@ import { intro, outro, spinner, log } from '@clack/prompts';
|
|
|
4
4
|
import { getLatestTag } from './github.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Read and parse .
|
|
7
|
+
* Read and parse .add/manifest.json.
|
|
8
8
|
* @param {string} cwd
|
|
9
9
|
* @returns {{ version: string, releaseTag: string, installedAt: string, providers: string[], files: string[], hashes?: object } | null}
|
|
10
10
|
*/
|
|
11
11
|
function readManifest(cwd) {
|
|
12
|
-
const manifestPath = path.join(cwd, '.
|
|
12
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
13
13
|
if (!fs.existsSync(manifestPath)) return null;
|
|
14
14
|
try {
|
|
15
15
|
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -45,12 +45,12 @@ function formatDate(isoDate) {
|
|
|
45
45
|
* @param {boolean} verbose if true, checks for updates
|
|
46
46
|
*/
|
|
47
47
|
export async function config(cwd, verbose = false) {
|
|
48
|
-
intro('
|
|
48
|
+
intro('ADD CLI - Config');
|
|
49
49
|
|
|
50
50
|
const manifest = readManifest(cwd);
|
|
51
51
|
|
|
52
52
|
if (!manifest) {
|
|
53
|
-
outro('ERROR:
|
|
53
|
+
outro('ERROR: ADD not installed. Run `npx add install` first.');
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -82,7 +82,7 @@ export async function config(cwd, verbose = false) {
|
|
|
82
82
|
log.success('OK You are running the latest version!');
|
|
83
83
|
} else {
|
|
84
84
|
log.warn(`WARN Update available: ${latestTag} (current: ${manifest.releaseTag || manifest.version})`);
|
|
85
|
-
log.info(' Run `npx
|
|
85
|
+
log.info(' Run `npx add update` to upgrade.');
|
|
86
86
|
}
|
|
87
87
|
} catch (err) {
|
|
88
88
|
s.stop('Update check failed.');
|
package/src/doctor.js
CHANGED
|
@@ -42,17 +42,17 @@ function checkGit() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* Check if .
|
|
45
|
+
* Check if .add/ directory exists and is non-empty.
|
|
46
46
|
* @param {string} cwd
|
|
47
47
|
* @returns {{ok: boolean, exists: boolean, hasFiles: boolean}}
|
|
48
48
|
*/
|
|
49
|
-
function
|
|
50
|
-
const
|
|
51
|
-
const exists = fs.existsSync(
|
|
49
|
+
function checkAddDir(cwd) {
|
|
50
|
+
const addDir = path.join(cwd, '.add');
|
|
51
|
+
const exists = fs.existsSync(addDir);
|
|
52
52
|
let hasFiles = false;
|
|
53
53
|
if (exists) {
|
|
54
54
|
try {
|
|
55
|
-
const entries = fs.readdirSync(
|
|
55
|
+
const entries = fs.readdirSync(addDir);
|
|
56
56
|
hasFiles = entries.length > 0;
|
|
57
57
|
} catch {
|
|
58
58
|
// ignore
|
|
@@ -71,7 +71,7 @@ function checkPffDir(cwd) {
|
|
|
71
71
|
* @returns {{ok: boolean, exists: boolean, valid: boolean}}
|
|
72
72
|
*/
|
|
73
73
|
function checkManifest(cwd) {
|
|
74
|
-
const manifestPath = path.join(cwd, '.
|
|
74
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
75
75
|
const exists = fs.existsSync(manifestPath);
|
|
76
76
|
let valid = false;
|
|
77
77
|
if (exists) {
|
|
@@ -94,14 +94,14 @@ function checkManifest(cwd) {
|
|
|
94
94
|
* @param {string} cwd
|
|
95
95
|
*/
|
|
96
96
|
export async function doctor(cwd) {
|
|
97
|
-
intro('
|
|
97
|
+
intro('ADD CLI - Doctor');
|
|
98
98
|
|
|
99
99
|
const s = spinner();
|
|
100
100
|
s.start('Checking environment...');
|
|
101
101
|
|
|
102
102
|
const nodeCheck = checkNode();
|
|
103
103
|
const gitCheck = await checkGit();
|
|
104
|
-
const
|
|
104
|
+
const addCheck = checkAddDir(cwd);
|
|
105
105
|
const manifestCheck = checkManifest(cwd);
|
|
106
106
|
|
|
107
107
|
s.stop('Checks complete.');
|
|
@@ -116,9 +116,9 @@ export async function doctor(cwd) {
|
|
|
116
116
|
const gitIcon = gitCheck.ok ? 'OK' : 'ERROR';
|
|
117
117
|
log.info(`${gitIcon} Git: ${gitCheck.ok ? gitCheck.version : 'not found'}`);
|
|
118
118
|
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
log.info(`${
|
|
119
|
+
const addIcon = addCheck.ok ? 'OK' : addCheck.exists ? 'WARN' : 'ERROR';
|
|
120
|
+
const addStatus = addCheck.ok ? 'present' : addCheck.exists ? 'empty' : 'missing';
|
|
121
|
+
log.info(`${addIcon} .add/ directory: ${addStatus}`);
|
|
122
122
|
|
|
123
123
|
let manifestIcon;
|
|
124
124
|
let manifestStatus;
|
|
@@ -136,16 +136,16 @@ export async function doctor(cwd) {
|
|
|
136
136
|
|
|
137
137
|
log.info('');
|
|
138
138
|
|
|
139
|
-
const allOk = nodeCheck.ok && gitCheck.ok &&
|
|
139
|
+
const allOk = nodeCheck.ok && gitCheck.ok && addCheck.ok && manifestCheck.ok;
|
|
140
140
|
|
|
141
141
|
if (allOk) {
|
|
142
|
-
outro('OK All checks passed!
|
|
142
|
+
outro('OK All checks passed! ADD is properly installed.');
|
|
143
143
|
process.exit(0);
|
|
144
144
|
} else {
|
|
145
145
|
const issues = [];
|
|
146
146
|
if (!nodeCheck.ok) issues.push('Node.js >= 18 required');
|
|
147
147
|
if (!gitCheck.ok) issues.push('Git not found');
|
|
148
|
-
if (!
|
|
148
|
+
if (!addCheck.ok) issues.push('.add/ directory missing or empty');
|
|
149
149
|
if (!manifestCheck.ok) issues.push('manifest.json missing or invalid');
|
|
150
150
|
|
|
151
151
|
outro(`ERROR Issues found:\n${issues.map((i) => ` - ${i}`).join('\n')}`);
|
package/src/github.js
CHANGED
|
@@ -11,7 +11,7 @@ export async function getLatestTag(repo = REPO) {
|
|
|
11
11
|
const res = await fetch(url, {
|
|
12
12
|
headers: {
|
|
13
13
|
Accept: 'application/vnd.github+json',
|
|
14
|
-
'User-Agent': '
|
|
14
|
+
'User-Agent': 'add-cli',
|
|
15
15
|
},
|
|
16
16
|
}).catch(() => {
|
|
17
17
|
throw new Error('Could not reach GitHub. Check your connection.');
|
|
@@ -37,7 +37,7 @@ export async function getLatestTag(repo = REPO) {
|
|
|
37
37
|
export async function downloadTagZip(tag, repo = REPO) {
|
|
38
38
|
const url = `https://github.com/${repo}/archive/refs/tags/${tag}.zip`;
|
|
39
39
|
const res = await fetch(url, {
|
|
40
|
-
headers: { 'User-Agent': '
|
|
40
|
+
headers: { 'User-Agent': 'add-cli' },
|
|
41
41
|
}).catch(() => {
|
|
42
42
|
throw new Error('Could not reach GitHub. Check your connection.');
|
|
43
43
|
});
|
|
@@ -64,7 +64,7 @@ export async function downloadTagZip(tag, repo = REPO) {
|
|
|
64
64
|
export async function downloadBranchZip(branch, repo = REPO) {
|
|
65
65
|
const url = `https://github.com/${repo}/archive/refs/heads/${branch}.zip`;
|
|
66
66
|
const res = await fetch(url, {
|
|
67
|
-
headers: { 'User-Agent': '
|
|
67
|
+
headers: { 'User-Agent': 'add-cli' },
|
|
68
68
|
}).catch(() => {
|
|
69
69
|
throw new Error('Could not reach GitHub. Check your connection.');
|
|
70
70
|
});
|
package/src/installer.js
CHANGED
|
@@ -39,7 +39,7 @@ function calculateHash(filePath) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Write .
|
|
42
|
+
* Write .add/manifest.json
|
|
43
43
|
* @param {string} cwd
|
|
44
44
|
* @param {string} version
|
|
45
45
|
* @param {string[]} providers
|
|
@@ -48,7 +48,7 @@ function calculateHash(filePath) {
|
|
|
48
48
|
* @param {object} [metadata]
|
|
49
49
|
*/
|
|
50
50
|
export function writeManifest(cwd, version, providers, files, releaseTag, metadata = {}) {
|
|
51
|
-
const manifestPath = path.join(cwd, '.
|
|
51
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
52
52
|
|
|
53
53
|
const hashes = {};
|
|
54
54
|
for (const file of files) {
|
|
@@ -160,7 +160,7 @@ function dirExists(dir) {
|
|
|
160
160
|
*
|
|
161
161
|
* @param {AdmZip} zip
|
|
162
162
|
* @param {string} zipRoot top-level folder name inside zip (e.g. "product-flow-factory-2.0.1")
|
|
163
|
-
* @param {string} srcPrefix path inside zip after zipRoot (e.g. "framwork/.
|
|
163
|
+
* @param {string} srcPrefix path inside zip after zipRoot (e.g. "framwork/.add")
|
|
164
164
|
* @param {string} destDir absolute destination directory
|
|
165
165
|
* @param {string} cwd project root
|
|
166
166
|
* @returns {string[]}
|
|
@@ -195,7 +195,7 @@ function copyFromZip(zip, zipRoot, srcPrefix, destDir, cwd) {
|
|
|
195
195
|
* @param {{version?: string}} [options]
|
|
196
196
|
*/
|
|
197
197
|
export async function install(cwd, options = {}) {
|
|
198
|
-
intro('
|
|
198
|
+
intro('ADD CLI - Install');
|
|
199
199
|
|
|
200
200
|
const s = spinner();
|
|
201
201
|
s.start('Resolving install source from GitHub...');
|
|
@@ -208,9 +208,9 @@ export async function install(cwd, options = {}) {
|
|
|
208
208
|
s.stop(`Selected tag: ${installSource.downloadValue}`);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
const
|
|
212
|
-
if (dirExists(
|
|
213
|
-
await promptConfirm('.
|
|
211
|
+
const addDir = path.join(cwd, '.add');
|
|
212
|
+
if (dirExists(addDir)) {
|
|
213
|
+
await promptConfirm('.add/ already exists. Overwrite with latest version?');
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
const selectedKeys = await promptProviders();
|
|
@@ -237,7 +237,7 @@ export async function install(cwd, options = {}) {
|
|
|
237
237
|
|
|
238
238
|
const allFiles = [];
|
|
239
239
|
|
|
240
|
-
const coreFiles = copyFromZip(zip, zipRoot, 'framwork/.
|
|
240
|
+
const coreFiles = copyFromZip(zip, zipRoot, 'framwork/.add', addDir, cwd);
|
|
241
241
|
allFiles.push(...coreFiles);
|
|
242
242
|
|
|
243
243
|
for (const p of providers) {
|
|
@@ -248,7 +248,7 @@ export async function install(cwd, options = {}) {
|
|
|
248
248
|
|
|
249
249
|
s.stop(`Installed ${allFiles.length} files.`);
|
|
250
250
|
|
|
251
|
-
fixLineEndings(path.join(
|
|
251
|
+
fixLineEndings(path.join(addDir, 'scripts'));
|
|
252
252
|
|
|
253
253
|
writeManifest(
|
|
254
254
|
cwd,
|
|
@@ -263,9 +263,9 @@ export async function install(cwd, options = {}) {
|
|
|
263
263
|
log.success(`Providers installed: ${providerList}`);
|
|
264
264
|
|
|
265
265
|
outro(
|
|
266
|
-
`
|
|
266
|
+
`ADD installed successfully!\n\n` +
|
|
267
267
|
`Next steps:\n` +
|
|
268
|
-
` 1. Open your AI editor and run: /
|
|
268
|
+
` 1. Open your AI editor and run: /add-init\n` +
|
|
269
269
|
` 2. Follow the onboarding to configure your project\n\n` +
|
|
270
270
|
`Docs: https://github.com/brabos-ai/product-flow-factory`
|
|
271
271
|
);
|
package/src/uninstaller.js
CHANGED
|
@@ -3,17 +3,17 @@ import path from 'node:path';
|
|
|
3
3
|
import { intro, outro, spinner, log } from '@clack/prompts';
|
|
4
4
|
import { promptConfirm } from './prompt.js';
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const ADD_DIRS = ['.add', '.claude', '.agent', '.agents', '.kilocode', '.opencode'];
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Read and parse .
|
|
9
|
+
* Read and parse .add/manifest.json.
|
|
10
10
|
* @param {string} cwd
|
|
11
11
|
* @returns {{ version: string, providers: string[], files: string[], corrupted?: boolean } | null}
|
|
12
12
|
* Returns null if manifest does not exist.
|
|
13
13
|
* Returns object with corrupted=true if file exists but JSON is invalid.
|
|
14
14
|
*/
|
|
15
15
|
export function readManifest(cwd) {
|
|
16
|
-
const manifestPath = path.join(cwd, '.
|
|
16
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
17
17
|
if (!fs.existsSync(manifestPath)) return null;
|
|
18
18
|
try {
|
|
19
19
|
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -72,32 +72,32 @@ function removeEmptyDirs(dir) {
|
|
|
72
72
|
* @param {boolean} force skip confirmation
|
|
73
73
|
*/
|
|
74
74
|
export async function uninstall(cwd, force = false) {
|
|
75
|
-
intro('
|
|
75
|
+
intro('ADD CLI - Uninstall');
|
|
76
76
|
|
|
77
77
|
const manifest = readManifest(cwd);
|
|
78
78
|
|
|
79
79
|
if (!manifest) {
|
|
80
|
-
throw new Error('No
|
|
80
|
+
throw new Error('No ADD installation found. Run `npx add install` first.');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
if (manifest.corrupted) {
|
|
84
84
|
log.warn('Manifest is corrupted. Falling back to directory-based removal.');
|
|
85
85
|
|
|
86
86
|
const allPresent = [];
|
|
87
|
-
for (const dir of
|
|
87
|
+
for (const dir of ADD_DIRS) {
|
|
88
88
|
allPresent.push(...walkDir(path.join(cwd, dir), cwd));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
if (allPresent.length === 0) {
|
|
92
|
-
outro('No
|
|
92
|
+
outro('No ADD files found. Nothing to remove.');
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
log.info(`Found ${allPresent.length} file(s) in
|
|
96
|
+
log.info(`Found ${allPresent.length} file(s) in ADD directories.`);
|
|
97
97
|
|
|
98
98
|
if (!force) {
|
|
99
99
|
await promptConfirm(
|
|
100
|
-
`Remove all ${allPresent.length} files found in
|
|
100
|
+
`Remove all ${allPresent.length} files found in ADD directories? This cannot be undone.`
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -117,31 +117,31 @@ export async function uninstall(cwd, force = false) {
|
|
|
117
117
|
}
|
|
118
118
|
s.stop(`Removed ${removed} files.`);
|
|
119
119
|
|
|
120
|
-
for (const dir of
|
|
120
|
+
for (const dir of ADD_DIRS) {
|
|
121
121
|
removeEmptyDirs(path.join(cwd, dir));
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
outro('
|
|
124
|
+
outro('ADD removed successfully.');
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
const manifestFiles = new Set(manifest.files ?? []);
|
|
129
129
|
|
|
130
130
|
const allPresent = [];
|
|
131
|
-
for (const dir of
|
|
131
|
+
for (const dir of ADD_DIRS) {
|
|
132
132
|
allPresent.push(...walkDir(path.join(cwd, dir), cwd));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const userFiles = allPresent.filter((f) => !manifestFiles.has(f));
|
|
136
136
|
|
|
137
|
-
log.info(`Files installed by
|
|
137
|
+
log.info(`Files installed by ADD: ${manifestFiles.size}`);
|
|
138
138
|
if (userFiles.length > 0) {
|
|
139
|
-
log.warn(`Found ${userFiles.length} file(s) not installed by
|
|
139
|
+
log.warn(`Found ${userFiles.length} file(s) not installed by ADD (will be kept):`);
|
|
140
140
|
for (const f of userFiles) log.warn(` ${f}`);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
if (!force) {
|
|
144
|
-
await promptConfirm(`Remove ${manifestFiles.size}
|
|
144
|
+
await promptConfirm(`Remove ${manifestFiles.size} ADD files? (user files will be kept)`);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
const s = spinner();
|
|
@@ -160,7 +160,7 @@ export async function uninstall(cwd, force = false) {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
const manifestPath = path.join(cwd, '.
|
|
163
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
164
164
|
try {
|
|
165
165
|
if (fs.existsSync(manifestPath)) {
|
|
166
166
|
fs.unlinkSync(manifestPath);
|
|
@@ -172,9 +172,9 @@ export async function uninstall(cwd, force = false) {
|
|
|
172
172
|
|
|
173
173
|
s.stop(`Removed ${removed} files.`);
|
|
174
174
|
|
|
175
|
-
for (const dir of
|
|
175
|
+
for (const dir of ADD_DIRS) {
|
|
176
176
|
removeEmptyDirs(path.join(cwd, dir));
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
outro('
|
|
179
|
+
outro('ADD removed successfully.');
|
|
180
180
|
}
|
package/src/updater.js
CHANGED
|
@@ -59,18 +59,18 @@ function copyFromZip(zip, zipRoot, srcPrefix, destDir, cwd) {
|
|
|
59
59
|
* @param {string} cwd
|
|
60
60
|
*/
|
|
61
61
|
export async function update(cwd) {
|
|
62
|
-
intro('
|
|
62
|
+
intro('ADD CLI - Update');
|
|
63
63
|
|
|
64
|
-
const manifestPath = path.join(cwd, '.
|
|
64
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
65
65
|
if (!fs.existsSync(manifestPath)) {
|
|
66
|
-
throw new Error('No
|
|
66
|
+
throw new Error('No ADD installation found. Run `npx add install` first.');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
let manifest;
|
|
70
70
|
try {
|
|
71
71
|
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
72
72
|
} catch {
|
|
73
|
-
throw new Error('Manifest is corrupted. Run `npx
|
|
73
|
+
throw new Error('Manifest is corrupted. Run `npx add install` to reinstall.');
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const currentVersion = manifest.version ?? 'unknown';
|
|
@@ -97,9 +97,9 @@ export async function update(cwd) {
|
|
|
97
97
|
if (!zipRoot) throw new Error('Unexpected zip structure.');
|
|
98
98
|
|
|
99
99
|
const allFiles = [];
|
|
100
|
-
const
|
|
100
|
+
const addDir = path.join(cwd, '.add');
|
|
101
101
|
|
|
102
|
-
const coreFiles = copyFromZip(zip, zipRoot, 'framwork/.
|
|
102
|
+
const coreFiles = copyFromZip(zip, zipRoot, 'framwork/.add', addDir, cwd);
|
|
103
103
|
allFiles.push(...coreFiles);
|
|
104
104
|
|
|
105
105
|
const providers = resolveSelected(providerKeys);
|
|
@@ -111,10 +111,10 @@ export async function update(cwd) {
|
|
|
111
111
|
|
|
112
112
|
s.stop(`Updated ${allFiles.length} files.`);
|
|
113
113
|
|
|
114
|
-
fixLineEndings(path.join(
|
|
114
|
+
fixLineEndings(path.join(addDir, 'scripts'));
|
|
115
115
|
|
|
116
116
|
writeManifest(cwd, tag, providerKeys, allFiles);
|
|
117
117
|
|
|
118
118
|
log.success(`Updated from v${currentVersion} to v${newVersion}`);
|
|
119
|
-
outro('
|
|
119
|
+
outro('ADD updated successfully!');
|
|
120
120
|
}
|
package/src/validator.js
CHANGED
|
@@ -6,12 +6,12 @@ import { intro, outro, spinner, log } from '@clack/prompts';
|
|
|
6
6
|
import { downloadZip } from './github.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Read and parse .
|
|
9
|
+
* Read and parse .add/manifest.json.
|
|
10
10
|
* @param {string} cwd
|
|
11
11
|
* @returns {{ version: string, releaseTag: string, installedAt: string, providers: string[], files: string[], hashes?: object } | null}
|
|
12
12
|
*/
|
|
13
13
|
function readManifest(cwd) {
|
|
14
|
-
const manifestPath = path.join(cwd, '.
|
|
14
|
+
const manifestPath = path.join(cwd, '.add', 'manifest.json');
|
|
15
15
|
if (!fs.existsSync(manifestPath)) return null;
|
|
16
16
|
try {
|
|
17
17
|
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -110,19 +110,19 @@ async function repairFiles(cwd, filesToRepair, releaseTag) {
|
|
|
110
110
|
* @param {boolean} repair if true, restore missing/modified files
|
|
111
111
|
*/
|
|
112
112
|
export async function validate(cwd, repair = false) {
|
|
113
|
-
intro('
|
|
113
|
+
intro('ADD CLI - Validate');
|
|
114
114
|
|
|
115
115
|
const manifest = readManifest(cwd);
|
|
116
116
|
|
|
117
117
|
if (!manifest) {
|
|
118
|
-
outro('ERROR:
|
|
118
|
+
outro('ERROR: ADD not installed. Run `npx add install` first.');
|
|
119
119
|
process.exit(1);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
if (!manifest.hashes || Object.keys(manifest.hashes).length === 0) {
|
|
123
123
|
log.warn('');
|
|
124
124
|
log.warn('WARN Hash not available for this install.');
|
|
125
|
-
log.warn(' Run `npx
|
|
125
|
+
log.warn(' Run `npx add update` to enable validation.');
|
|
126
126
|
log.warn('');
|
|
127
127
|
outro('Validation skipped.');
|
|
128
128
|
process.exit(0);
|