@ghl-ai/aw 0.1.25-beta.9 → 0.1.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 +62 -22
- package/commands/pull.mjs +17 -4
- package/constants.mjs +1 -1
- package/integrate.mjs +38 -68
- package/package.json +1 -1
- package/plan.mjs +7 -5
package/cli.mjs
CHANGED
|
@@ -71,8 +71,9 @@ function printHelp() {
|
|
|
71
71
|
const sec = (title) => `\n ${chalk.bold.underline(title)}`;
|
|
72
72
|
const help = [
|
|
73
73
|
sec('Setup'),
|
|
74
|
-
cmd('aw init --namespace <team>', 'Initialize workspace (required)'),
|
|
74
|
+
cmd('aw init --namespace <team/sub-team>', 'Initialize workspace (required)'),
|
|
75
75
|
` ${chalk.dim('Teams: revex, mobile, commerce, leadgen, crm, marketplace, ai')}`,
|
|
76
|
+
` ${chalk.dim('Example: aw init --namespace revex/courses')}`,
|
|
76
77
|
|
|
77
78
|
sec('Download'),
|
|
78
79
|
cmd('aw pull', 'Re-pull all synced paths (like git pull)'),
|
package/commands/init.mjs
CHANGED
|
@@ -146,26 +146,47 @@ export async function initCommand(args) {
|
|
|
146
146
|
fmt.cancel([
|
|
147
147
|
`Missing required ${chalk.bold('--namespace')} flag`,
|
|
148
148
|
'',
|
|
149
|
-
` ${chalk.dim('Usage:')} aw init --namespace <team>`,
|
|
149
|
+
` ${chalk.dim('Usage:')} aw init --namespace <team/sub-team>`,
|
|
150
150
|
` ${chalk.dim('Teams:')} ${list}`,
|
|
151
151
|
'',
|
|
152
|
-
` ${chalk.dim('Example:')} ${chalk.bold('aw init --namespace commerce')}`,
|
|
152
|
+
` ${chalk.dim('Example:')} ${chalk.bold('aw init --namespace commerce/payments')}`,
|
|
153
153
|
].join('\n'));
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
// Parse team/sub-team
|
|
157
|
+
const nsParts = namespace ? namespace.split('/') : [];
|
|
158
|
+
const team = nsParts[0] || null;
|
|
159
|
+
const subTeam = nsParts[1] || null;
|
|
160
|
+
const teamNS = subTeam ? `${team}-${subTeam}` : team; // for $TEAM_NS replacement
|
|
161
|
+
const folderName = subTeam ? `${team}/${subTeam}` : team; // for .aw_registry/ path
|
|
162
|
+
|
|
163
|
+
if (team && !ALLOWED_NAMESPACES.includes(team)) {
|
|
157
164
|
const list = ALLOWED_NAMESPACES.map(n => chalk.cyan(n)).join(', ');
|
|
158
165
|
fmt.cancel([
|
|
159
|
-
`Unknown
|
|
166
|
+
`Unknown team ${chalk.red(team)}`,
|
|
160
167
|
'',
|
|
161
168
|
` ${chalk.dim('Allowed:')} ${list}`,
|
|
162
169
|
'',
|
|
163
|
-
` ${chalk.dim('Example:')} ${chalk.bold('aw init --namespace commerce')}`,
|
|
170
|
+
` ${chalk.dim('Example:')} ${chalk.bold('aw init --namespace commerce/payments')}`,
|
|
171
|
+
].join('\n'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (team && !subTeam) {
|
|
175
|
+
fmt.cancel([
|
|
176
|
+
`Missing sub-team in ${chalk.red(namespace)}`,
|
|
177
|
+
'',
|
|
178
|
+
` ${chalk.dim('Format:')} --namespace <team>/<sub-team>`,
|
|
179
|
+
'',
|
|
180
|
+
` ${chalk.dim('Example:')} ${chalk.bold(`aw init --namespace ${team}/courses`)}`,
|
|
164
181
|
].join('\n'));
|
|
165
182
|
}
|
|
166
183
|
|
|
167
|
-
|
|
168
|
-
|
|
184
|
+
const SLUG_RE = /^[a-z][a-z0-9-]{1,38}[a-z0-9]$/;
|
|
185
|
+
if (team && !SLUG_RE.test(team)) {
|
|
186
|
+
fmt.cancel(`Invalid team '${team}' — must match: ${SLUG_RE}`);
|
|
187
|
+
}
|
|
188
|
+
if (subTeam && !SLUG_RE.test(subTeam)) {
|
|
189
|
+
fmt.cancel(`Invalid sub-team '${subTeam}' — must match: ${SLUG_RE}`);
|
|
169
190
|
}
|
|
170
191
|
|
|
171
192
|
const hasConfig = config.exists(GLOBAL_AW_DIR);
|
|
@@ -176,21 +197,40 @@ export async function initCommand(args) {
|
|
|
176
197
|
// ── Fast path: already initialized → just pull + link ─────────────────
|
|
177
198
|
|
|
178
199
|
if (isExisting) {
|
|
179
|
-
|
|
200
|
+
const cfg = config.load(GLOBAL_AW_DIR);
|
|
201
|
+
|
|
202
|
+
// Add new sub-team if not already tracked
|
|
203
|
+
const isNewSubTeam = folderName && cfg && !cfg.include.includes(folderName);
|
|
204
|
+
if (isNewSubTeam) {
|
|
205
|
+
if (!silent) fmt.logStep(`Adding sub-team ${chalk.cyan(folderName)}...`);
|
|
206
|
+
await pullAsync({
|
|
207
|
+
...args,
|
|
208
|
+
_positional: ['[template]'],
|
|
209
|
+
_renameNamespace: folderName,
|
|
210
|
+
_teamNS: teamNS,
|
|
211
|
+
_workspaceDir: GLOBAL_AW_DIR,
|
|
212
|
+
_skipIntegrate: true,
|
|
213
|
+
});
|
|
214
|
+
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
215
|
+
} else {
|
|
216
|
+
if (!silent) fmt.logStep('Already initialized — syncing...');
|
|
217
|
+
}
|
|
180
218
|
|
|
181
219
|
// Pull latest (parallel)
|
|
182
|
-
// cfg.include has the renamed namespace (e.g. 'revex'), but the repo
|
|
220
|
+
// cfg.include has the renamed namespace (e.g. 'revex/courses'), but the repo
|
|
183
221
|
// only has 'registry/[template]/' — remap non-platform entries back.
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
186
|
-
const pullJobs =
|
|
222
|
+
const freshCfg = config.load(GLOBAL_AW_DIR);
|
|
223
|
+
if (freshCfg && freshCfg.include.length > 0) {
|
|
224
|
+
const pullJobs = freshCfg.include.map(p => {
|
|
187
225
|
const isTeamNs = p !== 'platform';
|
|
226
|
+
const derivedTeamNS = isTeamNs ? p.replace(/\//g, '-') : undefined;
|
|
188
227
|
return pullAsync({
|
|
189
228
|
...args,
|
|
190
229
|
_positional: [isTeamNs ? '[template]' : p],
|
|
191
230
|
_workspaceDir: GLOBAL_AW_DIR,
|
|
192
231
|
_skipIntegrate: true,
|
|
193
232
|
_renameNamespace: isTeamNs ? p : undefined,
|
|
233
|
+
_teamNS: derivedTeamNS,
|
|
194
234
|
});
|
|
195
235
|
});
|
|
196
236
|
await Promise.all(pullJobs);
|
|
@@ -199,9 +239,9 @@ export async function initCommand(args) {
|
|
|
199
239
|
// Re-link IDE dirs (idempotent)
|
|
200
240
|
linkWorkspace(HOME);
|
|
201
241
|
generateCommands(HOME);
|
|
202
|
-
copyInstructions(HOME, null, namespace) || [];
|
|
242
|
+
copyInstructions(HOME, null, freshCfg?.namespace || team) || [];
|
|
203
243
|
initAwDocs(HOME);
|
|
204
|
-
setupMcp(HOME, namespace) || [];
|
|
244
|
+
setupMcp(HOME, freshCfg?.namespace || team) || [];
|
|
205
245
|
|
|
206
246
|
// Link current project if needed
|
|
207
247
|
if (cwd !== HOME && !existsSync(join(cwd, '.aw_registry'))) {
|
|
@@ -213,7 +253,7 @@ export async function initCommand(args) {
|
|
|
213
253
|
|
|
214
254
|
if (!silent) {
|
|
215
255
|
fmt.outro([
|
|
216
|
-
'Sync complete',
|
|
256
|
+
isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Sync complete',
|
|
217
257
|
'',
|
|
218
258
|
` ${chalk.green('✓')} Registry updated`,
|
|
219
259
|
` ${chalk.green('✓')} IDE integration refreshed`,
|
|
@@ -235,26 +275,26 @@ export async function initCommand(args) {
|
|
|
235
275
|
// Step 1: Create global source of truth
|
|
236
276
|
mkdirSync(GLOBAL_AW_DIR, { recursive: true });
|
|
237
277
|
|
|
238
|
-
const cfg = config.create(GLOBAL_AW_DIR, { namespace, user });
|
|
278
|
+
const cfg = config.create(GLOBAL_AW_DIR, { namespace: team, user });
|
|
239
279
|
|
|
240
280
|
fmt.note([
|
|
241
281
|
`${chalk.dim('source:')} ~/.aw_registry/`,
|
|
242
|
-
|
|
282
|
+
folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
|
|
243
283
|
user ? `${chalk.dim('user:')} ${cfg.user}` : null,
|
|
244
284
|
`${chalk.dim('version:')} v${VERSION}`,
|
|
245
285
|
].filter(Boolean).join('\n'), 'Config created');
|
|
246
286
|
|
|
247
287
|
// Step 2: Pull registry content (parallel)
|
|
248
288
|
const s = fmt.spinner();
|
|
249
|
-
const pullTargets =
|
|
289
|
+
const pullTargets = folderName ? `platform + ${folderName}` : 'platform';
|
|
250
290
|
s.start(`Pulling ${pullTargets}...`);
|
|
251
291
|
|
|
252
292
|
const pullJobs = [
|
|
253
293
|
pullAsync({ ...args, _positional: ['platform'], _workspaceDir: GLOBAL_AW_DIR, _skipIntegrate: true }),
|
|
254
294
|
];
|
|
255
|
-
if (
|
|
295
|
+
if (folderName) {
|
|
256
296
|
pullJobs.push(
|
|
257
|
-
pullAsync({ ...args, _positional: ['[template]'], _renameNamespace:
|
|
297
|
+
pullAsync({ ...args, _positional: ['[template]'], _renameNamespace: folderName, _teamNS: teamNS, _workspaceDir: GLOBAL_AW_DIR, _skipIntegrate: true }),
|
|
258
298
|
);
|
|
259
299
|
}
|
|
260
300
|
|
|
@@ -275,9 +315,9 @@ export async function initCommand(args) {
|
|
|
275
315
|
fmt.logStep('Linking IDE symlinks...');
|
|
276
316
|
linkWorkspace(HOME);
|
|
277
317
|
generateCommands(HOME);
|
|
278
|
-
const instructionFiles = copyInstructions(HOME, null,
|
|
318
|
+
const instructionFiles = copyInstructions(HOME, null, team) || [];
|
|
279
319
|
initAwDocs(HOME);
|
|
280
|
-
const mcpFiles = setupMcp(HOME,
|
|
320
|
+
const mcpFiles = setupMcp(HOME, team) || [];
|
|
281
321
|
const gitTemplateInstalled = installGitTemplate();
|
|
282
322
|
installIdeTasks();
|
|
283
323
|
|
package/commands/pull.mjs
CHANGED
|
@@ -17,6 +17,13 @@ import { resolveInput } from '../paths.mjs';
|
|
|
17
17
|
import { linkWorkspace } from '../link.mjs';
|
|
18
18
|
import { generateCommands, copyInstructions } from '../integrate.mjs';
|
|
19
19
|
|
|
20
|
+
// Filter out top-level platform CLI meta-commands (drop, pull, push, etc.)
|
|
21
|
+
// but keep domain-specific commands (platform/design/commands/, platform/infra/commands/).
|
|
22
|
+
function filterActions(actions, pattern) {
|
|
23
|
+
if (pattern !== 'platform') return actions;
|
|
24
|
+
return actions.filter(a => !(a.type === 'commands' && a.namespacePath === 'platform'));
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
export async function pullCommand(args) {
|
|
21
28
|
const input = args._positional?.[0] || '';
|
|
22
29
|
const cwd = process.cwd();
|
|
@@ -27,6 +34,7 @@ export async function pullCommand(args) {
|
|
|
27
34
|
const verbose = args['-v'] === true || args['--verbose'] === true;
|
|
28
35
|
const silent = args['--silent'] === true || args._silent === true;
|
|
29
36
|
const renameNamespace = args._renameNamespace || null;
|
|
37
|
+
const teamNSOverride = args._teamNS || null;
|
|
30
38
|
|
|
31
39
|
// Silent mode: wrap fmt to suppress all output and exit cleanly on errors
|
|
32
40
|
const log = {
|
|
@@ -51,12 +59,14 @@ export async function pullCommand(args) {
|
|
|
51
59
|
log.logInfo(`Pulling ${chalk.cyan(cfg.include.length)} synced path${cfg.include.length > 1 ? 's' : ''}...`);
|
|
52
60
|
const pullJobs = cfg.include.map(p => {
|
|
53
61
|
const isTeamNs = p !== 'platform';
|
|
62
|
+
const derivedTeamNS = isTeamNs ? p.replace(/\//g, '-') : undefined;
|
|
54
63
|
return pullAsync({
|
|
55
64
|
...args,
|
|
56
65
|
_positional: [isTeamNs ? '[template]' : p],
|
|
57
66
|
_workspaceDir: workspaceDir,
|
|
58
67
|
_skipIntegrate: true,
|
|
59
68
|
_renameNamespace: isTeamNs ? p : undefined,
|
|
69
|
+
_teamNS: derivedTeamNS,
|
|
60
70
|
});
|
|
61
71
|
});
|
|
62
72
|
await Promise.all(pullJobs);
|
|
@@ -153,9 +163,10 @@ export async function pullCommand(args) {
|
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
// Compute plan
|
|
156
|
-
const { actions } = computePlan(registryDirs, workspaceDir, [pattern], {
|
|
166
|
+
const { actions: rawActions } = computePlan(registryDirs, workspaceDir, [pattern], {
|
|
157
167
|
skipOrphans: true,
|
|
158
168
|
});
|
|
169
|
+
const actions = filterActions(rawActions, pattern);
|
|
159
170
|
|
|
160
171
|
if (dryRun) {
|
|
161
172
|
printDryRun(actions, verbose);
|
|
@@ -165,7 +176,7 @@ export async function pullCommand(args) {
|
|
|
165
176
|
// Apply
|
|
166
177
|
const s2 = log.spinner();
|
|
167
178
|
s2.start('Applying changes...');
|
|
168
|
-
const conflictCount = applyActions(actions, { teamNS: renameNamespace || undefined });
|
|
179
|
+
const conflictCount = applyActions(actions, { teamNS: teamNSOverride || renameNamespace || undefined });
|
|
169
180
|
updateManifest(workspaceDir, actions, cfg.namespace);
|
|
170
181
|
s2.stop('Changes applied');
|
|
171
182
|
|
|
@@ -208,6 +219,7 @@ export async function pullAsync(args) {
|
|
|
208
219
|
const input = args._positional?.[0] || '';
|
|
209
220
|
const workspaceDir = args._workspaceDir;
|
|
210
221
|
const renameNamespace = args._renameNamespace || null;
|
|
222
|
+
const teamNSOverride = args._teamNS || null;
|
|
211
223
|
|
|
212
224
|
const resolved = resolveInput(input, workspaceDir);
|
|
213
225
|
let pattern = resolved.registryPath;
|
|
@@ -259,8 +271,9 @@ export async function pullAsync(args) {
|
|
|
259
271
|
config.addPattern(workspaceDir, pattern);
|
|
260
272
|
}
|
|
261
273
|
|
|
262
|
-
const { actions } = computePlan(registryDirs, workspaceDir, [pattern], { skipOrphans: true });
|
|
263
|
-
const
|
|
274
|
+
const { actions: rawActions } = computePlan(registryDirs, workspaceDir, [pattern], { skipOrphans: true });
|
|
275
|
+
const actions = filterActions(rawActions, pattern);
|
|
276
|
+
const conflictCount = applyActions(actions, { teamNS: teamNSOverride || renameNamespace || undefined });
|
|
264
277
|
updateManifest(workspaceDir, actions, cfg.namespace);
|
|
265
278
|
|
|
266
279
|
const ROOT_REGISTRY_FILES = ['AW-PROTOCOL.md'];
|
package/constants.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// constants.mjs — Single source of truth for registry settings.
|
|
2
2
|
|
|
3
3
|
/** Base branch for PRs and sync checkout */
|
|
4
|
-
export const REGISTRY_BASE_BRANCH = '
|
|
4
|
+
export const REGISTRY_BASE_BRANCH = 'master';
|
|
5
5
|
|
|
6
6
|
/** Default registry repository */
|
|
7
7
|
export const REGISTRY_REPO = 'GoHighLevel/ghl-agentic-workspace';
|
package/integrate.mjs
CHANGED
|
@@ -3,24 +3,11 @@
|
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import * as fmt from './fmt.mjs';
|
|
6
|
-
|
|
7
|
-
// AW CLI commands to generate
|
|
8
|
-
const AW_COMMANDS = [
|
|
9
|
-
{ name: 'pull', description: 'Pull agents & skills from registry', hint: '<path>' },
|
|
10
|
-
{ name: 'push', description: 'Push local changes to registry', hint: '<path>' },
|
|
11
|
-
{ name: 'status', description: 'Show workspace sync status', hint: '' },
|
|
12
|
-
{ name: 'drop', description: 'Stop syncing a path', hint: '<path>' },
|
|
13
|
-
{ name: 'search', description: 'Search local and remote registry', hint: '<query>' },
|
|
14
|
-
{ name: 'nuke', description: 'Remove entire .aw_registry/', hint: '' },
|
|
15
|
-
];
|
|
6
|
+
import * as config from './config.mjs';
|
|
16
7
|
|
|
17
8
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* link.mjs symlinks everything from commands/ → .claude/commands/aw/ for discovery.
|
|
21
|
-
*
|
|
22
|
-
* A .generated-manifest.json tracks which files were generated so they can be
|
|
23
|
-
* cleaned on rebuild without needing filename prefixes.
|
|
9
|
+
* Count hand-written commands already present in the registry.
|
|
10
|
+
* No CLI stub generation — all commands come from the registry itself.
|
|
24
11
|
*/
|
|
25
12
|
export function generateCommands(cwd) {
|
|
26
13
|
const awDir = join(cwd, '.aw_registry');
|
|
@@ -29,56 +16,15 @@ export function generateCommands(cwd) {
|
|
|
29
16
|
const oldGenDir = join(awDir, '.generated-commands');
|
|
30
17
|
if (existsSync(oldGenDir)) rmSync(oldGenDir, { recursive: true, force: true });
|
|
31
18
|
|
|
19
|
+
// Count hand-written commands across all namespaces for reporting
|
|
32
20
|
let count = 0;
|
|
33
|
-
const namespaces =
|
|
34
|
-
|
|
21
|
+
const namespaces = getTeamNamespaces(awDir);
|
|
35
22
|
for (const ns of namespaces) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for (const cmd of AW_COMMANDS) {
|
|
41
|
-
const fileName = `${cmd.name}.md`;
|
|
42
|
-
// Skip if a hand-written command with same name exists
|
|
43
|
-
if (existsSync(join(commandsDir, fileName))) continue;
|
|
44
|
-
|
|
45
|
-
const content = [
|
|
46
|
-
'---',
|
|
47
|
-
`name: aw:${cmd.name}`,
|
|
48
|
-
`description: ${cmd.description}`,
|
|
49
|
-
cmd.hint ? `argument-hint: "${cmd.hint}"` : null,
|
|
50
|
-
'---',
|
|
51
|
-
'',
|
|
52
|
-
'Run the following command:',
|
|
53
|
-
'',
|
|
54
|
-
'```',
|
|
55
|
-
`node bin/aw ${cmd.name} $ARGUMENTS`,
|
|
56
|
-
'```',
|
|
57
|
-
'',
|
|
58
|
-
].filter(v => v !== null).join('\n');
|
|
59
|
-
|
|
60
|
-
writeFileSync(join(commandsDir, fileName), content);
|
|
61
|
-
count++;
|
|
23
|
+
const nsDir = join(awDir, ns);
|
|
24
|
+
if (existsSync(nsDir)) {
|
|
25
|
+
const cmdFiles = findFiles(nsDir, 'commands');
|
|
26
|
+
count += cmdFiles.length;
|
|
62
27
|
}
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Codex skills — .agents/skills/<name>/SKILL.md
|
|
67
|
-
const agentsSkillsDir = join(cwd, '.agents/skills');
|
|
68
|
-
mkdirSync(agentsSkillsDir, { recursive: true });
|
|
69
|
-
for (const cmd of AW_COMMANDS) {
|
|
70
|
-
const skillDir = join(agentsSkillsDir, cmd.name);
|
|
71
|
-
mkdirSync(skillDir, { recursive: true });
|
|
72
|
-
const content = [
|
|
73
|
-
'---',
|
|
74
|
-
`name: ${cmd.name}`,
|
|
75
|
-
`description: ${cmd.description}`,
|
|
76
|
-
'---',
|
|
77
|
-
'',
|
|
78
|
-
`Run: \`node bin/aw ${cmd.name}\` followed by the user's arguments.`,
|
|
79
|
-
'',
|
|
80
|
-
].join('\n');
|
|
81
|
-
writeFileSync(join(skillDir, 'SKILL.md'), content);
|
|
82
28
|
}
|
|
83
29
|
|
|
84
30
|
if (count > 0) {
|
|
@@ -88,6 +34,27 @@ export function generateCommands(cwd) {
|
|
|
88
34
|
return count;
|
|
89
35
|
}
|
|
90
36
|
|
|
37
|
+
function findFiles(dir, typeName) {
|
|
38
|
+
const results = [];
|
|
39
|
+
function walk(d) {
|
|
40
|
+
for (const entry of readdirSync(d, { withFileTypes: true })) {
|
|
41
|
+
if (entry.name.startsWith('.')) continue;
|
|
42
|
+
const full = join(d, entry.name);
|
|
43
|
+
if (entry.isDirectory()) {
|
|
44
|
+
if (entry.name === typeName) {
|
|
45
|
+
for (const f of readdirSync(full)) {
|
|
46
|
+
if (f.endsWith('.md')) results.push(join(full, f));
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
walk(full);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
walk(dir);
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
|
|
91
58
|
/**
|
|
92
59
|
* Copy CLAUDE.md and AGENTS.md to project root.
|
|
93
60
|
*/
|
|
@@ -460,10 +427,13 @@ No active tasks. Tasks are created during workflow execution.
|
|
|
460
427
|
fmt.logSuccess('Created .aw_docs/ (local orchestration state)');
|
|
461
428
|
}
|
|
462
429
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
430
|
+
/**
|
|
431
|
+
* Return team namespace paths from config (excludes 'platform').
|
|
432
|
+
* E.g. ['revex/courses'] — generated CLI commands only go into team namespaces.
|
|
433
|
+
*/
|
|
434
|
+
function getTeamNamespaces(awDir) {
|
|
435
|
+
const cfg = config.load(awDir);
|
|
436
|
+
if (!cfg || !cfg.include) return [];
|
|
437
|
+
return cfg.include.filter(p => p !== 'platform');
|
|
468
438
|
}
|
|
469
439
|
|
package/package.json
CHANGED
package/plan.mjs
CHANGED
|
@@ -24,11 +24,12 @@ export function computePlan(registryDirs, workspaceDir, includePatterns = [], {
|
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
27
|
+
// Key must include namespacePath to avoid collisions when the same
|
|
28
|
+
// slug exists under different domains (e.g. backend/agents/developer
|
|
29
|
+
// vs frontend/agents/developer).
|
|
29
30
|
const key = entry.skillRelPath
|
|
30
|
-
? `${entry.type}/${entry.slug}/${entry.skillRelPath}`
|
|
31
|
-
: `${entry.type}/${entry.slug}`;
|
|
31
|
+
? `${entry.namespacePath}/${entry.type}/${entry.slug}/${entry.skillRelPath}`
|
|
32
|
+
: `${entry.namespacePath}/${entry.type}/${entry.slug}`;
|
|
32
33
|
plan.set(key, { ...entry, source: entry.namespacePath || name });
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -108,9 +109,10 @@ export function computePlan(registryDirs, workspaceDir, includePatterns = [], {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
if (typeIdx === -1) continue;
|
|
112
|
+
const namespace = parts.slice(0, typeIdx).join('/');
|
|
111
113
|
const type = parts[typeIdx];
|
|
112
114
|
const slug = parts[typeIdx + 1]?.replace(/\.md$/, '');
|
|
113
|
-
const key = `${type}/${slug}`;
|
|
115
|
+
const key = `${namespace}/${type}/${slug}`;
|
|
114
116
|
if (!plan.has(key)) {
|
|
115
117
|
actions.push({
|
|
116
118
|
slug,
|