@ghl-ai/aw 0.1.35-beta.23 → 0.1.35-beta.25
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/cli.mjs +2 -1
- package/commands/init.mjs +63 -28
- package/config.mjs +2 -2
- package/ecc.mjs +1 -1
- package/fmt.mjs +2 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -72,7 +72,8 @@ function printHelp() {
|
|
|
72
72
|
const sec = (title) => `\n ${chalk.bold.underline(title)}`;
|
|
73
73
|
const help = [
|
|
74
74
|
sec('Setup'),
|
|
75
|
-
cmd('aw init
|
|
75
|
+
cmd('aw init', 'Initialize workspace (platform/ only)'),
|
|
76
|
+
cmd('aw init --namespace <team/sub-team>', 'Add a team namespace (optional)'),
|
|
76
77
|
` ${chalk.dim('Teams: platform, revex, mobile, commerce, leadgen, crm, marketplace, ai')}`,
|
|
77
78
|
` ${chalk.dim('Example: aw init --namespace revex/courses')}`,
|
|
78
79
|
|
package/commands/init.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Uses core.hooksPath (git-lfs pattern) for system-wide hook interception.
|
|
5
5
|
// Uses IDE tasks for auto-pull on workspace open.
|
|
6
6
|
|
|
7
|
-
import { mkdirSync, existsSync, writeFileSync, symlinkSync } from 'node:fs';
|
|
7
|
+
import { mkdirSync, existsSync, writeFileSync, symlinkSync, readdirSync } from 'node:fs';
|
|
8
8
|
import { execSync } from 'node:child_process';
|
|
9
9
|
import { join, dirname } from 'node:path';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
@@ -14,6 +14,8 @@ import * as config from '../config.mjs';
|
|
|
14
14
|
import * as fmt from '../fmt.mjs';
|
|
15
15
|
import { chalk } from '../fmt.mjs';
|
|
16
16
|
import { pullCommand, pullAsync } from './pull.mjs';
|
|
17
|
+
import { sparseCheckoutAsync, includeToSparsePaths, cleanup } from '../git.mjs';
|
|
18
|
+
import { REGISTRY_DIR, REGISTRY_REPO } from '../constants.mjs';
|
|
17
19
|
import { linkWorkspace } from '../link.mjs';
|
|
18
20
|
import { generateCommands, copyInstructions, initAwDocs } from '../integrate.mjs';
|
|
19
21
|
import { setupMcp } from '../mcp.mjs';
|
|
@@ -96,7 +98,7 @@ function printPullSummary(pattern, actions) {
|
|
|
96
98
|
const ALLOWED_NAMESPACES = ['platform', 'revex', 'mobile', 'commerce', 'leadgen', 'crm', 'marketplace', 'ai'];
|
|
97
99
|
|
|
98
100
|
export async function initCommand(args) {
|
|
99
|
-
|
|
101
|
+
let namespace = args['--namespace'] || null;
|
|
100
102
|
let user = args['--user'] || '';
|
|
101
103
|
const silent = args['--silent'] === true;
|
|
102
104
|
|
|
@@ -104,24 +106,12 @@ export async function initCommand(args) {
|
|
|
104
106
|
|
|
105
107
|
// ── Validate ──────────────────────────────────────────────────────────
|
|
106
108
|
|
|
107
|
-
if (!namespace && !silent) {
|
|
108
|
-
const list = ALLOWED_NAMESPACES.map(n => chalk.cyan(n)).join(', ');
|
|
109
|
-
fmt.cancel([
|
|
110
|
-
`Missing required ${chalk.bold('--namespace')} flag`,
|
|
111
|
-
'',
|
|
112
|
-
` ${chalk.dim('Usage:')} aw init --namespace <team/sub-team>`,
|
|
113
|
-
` ${chalk.dim('Teams:')} ${list}`,
|
|
114
|
-
'',
|
|
115
|
-
` ${chalk.dim('Example:')} ${chalk.bold('aw init --namespace commerce/payments')}`,
|
|
116
|
-
].join('\n'));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
109
|
// Parse team/sub-team
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
110
|
+
let nsParts = namespace ? namespace.split('/') : [];
|
|
111
|
+
let team = nsParts[0] || null;
|
|
112
|
+
let subTeam = nsParts[1] || null;
|
|
113
|
+
let teamNS = subTeam ? `${team}-${subTeam}` : team; // for $TEAM_NS replacement
|
|
114
|
+
let folderName = subTeam ? `${team}/${subTeam}` : team; // for .aw_registry/ path
|
|
125
115
|
|
|
126
116
|
if (team && !ALLOWED_NAMESPACES.includes(team)) {
|
|
127
117
|
const list = ALLOWED_NAMESPACES.map(n => chalk.cyan(n)).join(', ');
|
|
@@ -152,6 +142,47 @@ export async function initCommand(args) {
|
|
|
152
142
|
fmt.cancel(`Invalid sub-team '${subTeam}' — must match: ${SLUG_RE}`);
|
|
153
143
|
}
|
|
154
144
|
|
|
145
|
+
// ── Probe remote registry to check if namespace exists ────────────────
|
|
146
|
+
|
|
147
|
+
let namespaceExistsInRemote = false;
|
|
148
|
+
if (folderName && !silent) {
|
|
149
|
+
try {
|
|
150
|
+
const probePaths = includeToSparsePaths([folderName]);
|
|
151
|
+
const probeDir = await sparseCheckoutAsync(REGISTRY_REPO, probePaths);
|
|
152
|
+
try {
|
|
153
|
+
const fullNsPath = join(probeDir, REGISTRY_DIR, ...folderName.split('/'));
|
|
154
|
+
namespaceExistsInRemote = existsSync(fullNsPath) &&
|
|
155
|
+
readdirSync(fullNsPath, { withFileTypes: true })
|
|
156
|
+
.some(d => d.isDirectory() && !d.name.startsWith('.'));
|
|
157
|
+
} finally {
|
|
158
|
+
cleanup(probeDir);
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
// Network error — skip probe, proceed without prompt
|
|
162
|
+
namespaceExistsInRemote = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If namespace does NOT exist in remote, ask user to confirm
|
|
167
|
+
if (folderName && !silent && !namespaceExistsInRemote && process.stdin.isTTY) {
|
|
168
|
+
const choice = await fmt.select({
|
|
169
|
+
message: `The namespace '${folderName}' does not exist in the registry yet and will be created from [template].\nplatform/ includes shared agents, skills & commands that cover most use cases.\nHow would you like to proceed?`,
|
|
170
|
+
options: [
|
|
171
|
+
{ value: 'platform-only', label: 'Continue with platform/ only (recommended for most users)' },
|
|
172
|
+
{ value: 'create-namespace', label: `Create '${folderName}' namespace from template` },
|
|
173
|
+
],
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (fmt.isCancel(choice)) {
|
|
177
|
+
fmt.cancel('Operation cancelled.');
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (choice === 'platform-only') {
|
|
182
|
+
namespace = null; team = null; subTeam = null; teamNS = null; folderName = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
155
186
|
const hasConfig = config.exists(GLOBAL_AW_DIR);
|
|
156
187
|
const hasPlatform = existsSync(join(GLOBAL_AW_DIR, 'platform'));
|
|
157
188
|
const isExisting = hasConfig && hasPlatform;
|
|
@@ -182,22 +213,26 @@ export async function initCommand(args) {
|
|
|
182
213
|
// Pull latest (parallel)
|
|
183
214
|
// cfg.include has the renamed namespace (e.g. 'revex/courses'), but the repo
|
|
184
215
|
// only has '.aw_registry/[template]/' — remap non-platform entries back.
|
|
216
|
+
// Platform is never stored in cfg.include but must always be pulled.
|
|
185
217
|
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
218
|
+
const pullJobs = [
|
|
219
|
+
pullAsync({ ...args, _positional: ['platform'], _workspaceDir: GLOBAL_AW_DIR, _skipIntegrate: true }),
|
|
220
|
+
];
|
|
186
221
|
if (freshCfg && freshCfg.include.length > 0) {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
const derivedTeamNS =
|
|
190
|
-
|
|
222
|
+
for (const p of freshCfg.include) {
|
|
223
|
+
if (p === 'platform') continue; // already added above
|
|
224
|
+
const derivedTeamNS = p.replace(/\//g, '-');
|
|
225
|
+
pullJobs.push(pullAsync({
|
|
191
226
|
...args,
|
|
192
|
-
_positional: [
|
|
227
|
+
_positional: ['[template]'],
|
|
193
228
|
_workspaceDir: GLOBAL_AW_DIR,
|
|
194
229
|
_skipIntegrate: true,
|
|
195
|
-
_renameNamespace:
|
|
230
|
+
_renameNamespace: p,
|
|
196
231
|
_teamNS: derivedTeamNS,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
await Promise.all(pullJobs);
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
200
234
|
}
|
|
235
|
+
await Promise.all(pullJobs);
|
|
201
236
|
|
|
202
237
|
// Re-link IDE dirs + hooks (idempotent)
|
|
203
238
|
linkWorkspace(HOME);
|
package/config.mjs
CHANGED
|
@@ -48,7 +48,7 @@ export function create(workspaceDir, { namespace, user }) {
|
|
|
48
48
|
|
|
49
49
|
export function addPattern(workspaceDir, pattern) {
|
|
50
50
|
const config = load(workspaceDir);
|
|
51
|
-
if (!config) throw new Error('No .sync-config.json found. Run: aw
|
|
51
|
+
if (!config) throw new Error('No .sync-config.json found. Run: aw init');
|
|
52
52
|
// If a parent path already covers this, skip
|
|
53
53
|
if (config.include.some(p => pattern === p || pattern.startsWith(p + '/'))) {
|
|
54
54
|
return config;
|
|
@@ -62,7 +62,7 @@ export function addPattern(workspaceDir, pattern) {
|
|
|
62
62
|
|
|
63
63
|
export function removePattern(workspaceDir, pattern) {
|
|
64
64
|
const config = load(workspaceDir);
|
|
65
|
-
if (!config) throw new Error('No .sync-config.json found. Run: aw
|
|
65
|
+
if (!config) throw new Error('No .sync-config.json found. Run: aw init');
|
|
66
66
|
// Remove exact match + all children
|
|
67
67
|
config.include = config.include.filter(p => p !== pattern && !p.startsWith(pattern + '/'));
|
|
68
68
|
save(workspaceDir, config);
|
package/ecc.mjs
CHANGED
|
@@ -9,7 +9,7 @@ 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.2.
|
|
12
|
+
const AW_ECC_TAG = "v1.2.2";
|
|
13
13
|
|
|
14
14
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
15
15
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
package/fmt.mjs
CHANGED
|
@@ -36,6 +36,8 @@ export function banner(text, opts = {}) {
|
|
|
36
36
|
export const intro = (msg) => p.intro(chalk.bgCyan.black(` ${msg} `));
|
|
37
37
|
export const outro = (msg) => p.outro(chalk.green(msg));
|
|
38
38
|
export const spinner = () => p.spinner();
|
|
39
|
+
export const select = p.select;
|
|
40
|
+
export const isCancel = p.isCancel;
|
|
39
41
|
|
|
40
42
|
export function cancel(msg) {
|
|
41
43
|
p.cancel(msg);
|