@ghl-ai/aw 0.1.35-beta.14 → 0.1.35-beta.16
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/ecc.mjs +116 -21
- package/package.json +1 -1
package/ecc.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import {
|
|
3
|
-
existsSync, mkdirSync, readFileSync, readdirSync,
|
|
3
|
+
cpSync, existsSync, mkdirSync, readFileSync, readdirSync,
|
|
4
4
|
renameSync, rmSync, writeFileSync,
|
|
5
5
|
} from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
@@ -9,13 +9,18 @@ import * as fmt from "./fmt.mjs";
|
|
|
9
9
|
|
|
10
10
|
const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
|
|
11
11
|
const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
|
|
12
|
-
const AW_ECC_TAG = "v1.
|
|
12
|
+
const AW_ECC_TAG = "v1.1.0";
|
|
13
13
|
const TMP_DIR = "/tmp/aw-ecc";
|
|
14
14
|
|
|
15
|
+
const PLUGIN_NAME = "aw";
|
|
16
|
+
const PLUGIN_VERSION = "1.1.0";
|
|
17
|
+
const PLUGIN_KEY = `${PLUGIN_NAME}@${PLUGIN_NAME}`;
|
|
18
|
+
|
|
19
|
+
const FILE_COPY_TARGETS = ["cursor", "codex"];
|
|
20
|
+
|
|
15
21
|
const TARGET_STATE = {
|
|
16
22
|
cursor: { state: ".cursor/ecc-install-state.json" },
|
|
17
|
-
|
|
18
|
-
codex: { state: ".codex/ecc-install-state.json" },
|
|
23
|
+
codex: { state: ".codex/ecc-install-state.json" },
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
function run(cmd, opts = {}) {
|
|
@@ -30,6 +35,66 @@ function cloneRepo(tag, dest) {
|
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
function getPluginPaths() {
|
|
39
|
+
const HOME = homedir();
|
|
40
|
+
const pluginsDir = join(HOME, ".claude", "plugins");
|
|
41
|
+
const cachePath = join(pluginsDir, "cache", PLUGIN_NAME, PLUGIN_NAME, PLUGIN_VERSION);
|
|
42
|
+
const registryPath = join(pluginsDir, "installed_plugins.json");
|
|
43
|
+
return { pluginsDir, cachePath, registryPath };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function registerClaudePlugin(repoDir) {
|
|
47
|
+
const { cachePath, registryPath } = getPluginPaths();
|
|
48
|
+
|
|
49
|
+
if (existsSync(cachePath)) rmSync(cachePath, { recursive: true, force: true });
|
|
50
|
+
mkdirSync(cachePath, { recursive: true });
|
|
51
|
+
cpSync(repoDir, cachePath, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// Remove .git from the cached copy
|
|
54
|
+
const dotGit = join(cachePath, ".git");
|
|
55
|
+
if (existsSync(dotGit)) rmSync(dotGit, { recursive: true, force: true });
|
|
56
|
+
|
|
57
|
+
let registry = { version: 2, plugins: {} };
|
|
58
|
+
if (existsSync(registryPath)) {
|
|
59
|
+
try { registry = JSON.parse(readFileSync(registryPath, "utf8")); } catch { /* fresh */ }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let commitSha = "";
|
|
63
|
+
try { commitSha = run("git rev-parse HEAD", { cwd: repoDir }).toString().trim(); } catch { /* ok */ }
|
|
64
|
+
|
|
65
|
+
registry.plugins[PLUGIN_KEY] = [
|
|
66
|
+
{
|
|
67
|
+
scope: "user",
|
|
68
|
+
installPath: cachePath,
|
|
69
|
+
version: PLUGIN_VERSION,
|
|
70
|
+
installedAt: new Date().toISOString(),
|
|
71
|
+
lastUpdated: new Date().toISOString(),
|
|
72
|
+
...(commitSha && { gitCommitSha: commitSha }),
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
writeFileSync(registryPath, JSON.stringify(registry, null, 2));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function unregisterClaudePlugin() {
|
|
80
|
+
const { cachePath, registryPath } = getPluginPaths();
|
|
81
|
+
|
|
82
|
+
if (existsSync(cachePath)) {
|
|
83
|
+
rmSync(cachePath, { recursive: true, force: true });
|
|
84
|
+
pruneEmptyParents(cachePath, join(homedir(), ".claude"));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (existsSync(registryPath)) {
|
|
88
|
+
try {
|
|
89
|
+
const registry = JSON.parse(readFileSync(registryPath, "utf8"));
|
|
90
|
+
if (registry.plugins?.[PLUGIN_KEY]) {
|
|
91
|
+
delete registry.plugins[PLUGIN_KEY];
|
|
92
|
+
writeFileSync(registryPath, JSON.stringify(registry, null, 2));
|
|
93
|
+
}
|
|
94
|
+
} catch { /* best effort */ }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
33
98
|
function namespaceCommands(target) {
|
|
34
99
|
const HOME = homedir();
|
|
35
100
|
const cfg = TARGET_STATE[target];
|
|
@@ -44,9 +109,6 @@ function namespaceCommands(target) {
|
|
|
44
109
|
|
|
45
110
|
for (const op of state.operations) {
|
|
46
111
|
const dest = op.destinationPath;
|
|
47
|
-
// Match top-level command files only (e.g. /commands/plan.md)
|
|
48
|
-
// Skip files already in subdirs (e.g. /commands/aw/plan.md)
|
|
49
|
-
// Skip .opencode paths (different command structure)
|
|
50
112
|
if (dest.includes(".opencode/")) continue;
|
|
51
113
|
const match = dest.match(/^(.+\/commands)\/([^/]+\.md)$/);
|
|
52
114
|
if (!match) continue;
|
|
@@ -78,17 +140,26 @@ export async function installAwEcc(
|
|
|
78
140
|
|
|
79
141
|
try {
|
|
80
142
|
cloneRepo(AW_ECC_TAG, TMP_DIR);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
143
|
+
|
|
144
|
+
// Claude Code: register as plugin (proper agent dispatch)
|
|
145
|
+
if (targets.includes("claude")) {
|
|
146
|
+
registerClaudePlugin(TMP_DIR);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Cursor + Codex: file-copy approach + namespace commands into /aw:
|
|
150
|
+
const fileCopyTargets = targets.filter((t) => FILE_COPY_TARGETS.includes(t));
|
|
151
|
+
if (fileCopyTargets.length > 0) {
|
|
152
|
+
run("npm install --no-audit --no-fund --ignore-scripts --loglevel=error", {
|
|
153
|
+
cwd: TMP_DIR,
|
|
154
|
+
});
|
|
155
|
+
for (const target of fileCopyTargets) {
|
|
156
|
+
try {
|
|
157
|
+
run(
|
|
158
|
+
`node ${join(TMP_DIR, "scripts/install-apply.js")} --target ${target} --profile full`,
|
|
159
|
+
{ cwd },
|
|
160
|
+
);
|
|
161
|
+
namespaceCommands(target);
|
|
162
|
+
} catch { /* target not supported — skip */ }
|
|
92
163
|
}
|
|
93
164
|
}
|
|
94
165
|
|
|
@@ -104,9 +175,14 @@ export function uninstallAwEcc({ silent = false } = {}) {
|
|
|
104
175
|
const HOME = homedir();
|
|
105
176
|
let removed = 0;
|
|
106
177
|
|
|
178
|
+
// Claude Code: unregister plugin + remove cache
|
|
179
|
+
unregisterClaudePlugin();
|
|
180
|
+
removed++;
|
|
181
|
+
|
|
182
|
+
// Cursor + Codex: remove file-copied content via install-state
|
|
107
183
|
for (const cfg of Object.values(TARGET_STATE)) {
|
|
108
184
|
const statePath = join(HOME, cfg.state);
|
|
109
|
-
const ideDir = join(HOME, cfg.state.split("/")[0]);
|
|
185
|
+
const ideDir = join(HOME, cfg.state.split("/")[0]);
|
|
110
186
|
if (!existsSync(statePath)) continue;
|
|
111
187
|
|
|
112
188
|
try {
|
|
@@ -123,7 +199,24 @@ export function uninstallAwEcc({ silent = false } = {}) {
|
|
|
123
199
|
} catch { /* corrupted state — skip */ }
|
|
124
200
|
}
|
|
125
201
|
|
|
126
|
-
|
|
202
|
+
// Clean leftover claude install-state from older versions
|
|
203
|
+
const claudeState = join(HOME, ".claude", "ecc", "install-state.json");
|
|
204
|
+
if (existsSync(claudeState)) {
|
|
205
|
+
try {
|
|
206
|
+
const data = JSON.parse(readFileSync(claudeState, "utf8"));
|
|
207
|
+
for (const op of data.operations || []) {
|
|
208
|
+
if (op.destinationPath && existsSync(op.destinationPath)) {
|
|
209
|
+
rmSync(op.destinationPath, { recursive: true, force: true });
|
|
210
|
+
removed++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
rmSync(claudeState, { force: true });
|
|
214
|
+
pruneEmptyParents(claudeState, join(HOME, ".claude"));
|
|
215
|
+
} catch { /* best effort */ }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!silent && removed > 0)
|
|
219
|
+
fmt.logStep(`Removed ${removed} aw-ecc file${removed > 1 ? "s" : ""}`);
|
|
127
220
|
return removed;
|
|
128
221
|
}
|
|
129
222
|
|
|
@@ -137,6 +230,8 @@ function pruneEmptyParents(filePath, stopAt) {
|
|
|
137
230
|
} else {
|
|
138
231
|
break;
|
|
139
232
|
}
|
|
140
|
-
} catch {
|
|
233
|
+
} catch {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
141
236
|
}
|
|
142
237
|
}
|