@ghl-ai/aw 0.1.34-beta.1 → 0.1.34-beta.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/commands/pull.mjs CHANGED
@@ -178,7 +178,7 @@ export async function pullCommand(args) {
178
178
  const s2 = log.spinner();
179
179
  s2.start('Applying changes...');
180
180
  const conflictCount = applyActions(actions, { teamNS: teamNSOverride || renameNamespace || undefined });
181
- updateManifest(workspaceDir, actions, cfg.namespace);
181
+ updateManifest(workspaceDir, actions, cfg.namespace, { fromTemplate: !!renameNamespace });
182
182
  s2.stop('Changes applied');
183
183
 
184
184
  // Copy root-level registry files (e.g. AW-PROTOCOL.md) that are fetched via
@@ -282,7 +282,7 @@ export async function pullAsync(args) {
282
282
  const { actions: rawActions } = computePlan(registryDirs, workspaceDir, [pattern], { skipOrphans: true });
283
283
  const actions = filterActions(rawActions, pattern);
284
284
  const conflictCount = applyActions(actions, { teamNS: teamNSOverride || renameNamespace || undefined });
285
- updateManifest(workspaceDir, actions, cfg.namespace);
285
+ updateManifest(workspaceDir, actions, cfg.namespace, { fromTemplate: !!renameNamespace });
286
286
 
287
287
  const ROOT_REGISTRY_FILES = ['AW-PROTOCOL.md'];
288
288
  for (const fname of ROOT_REGISTRY_FILES) {
package/commands/push.mjs CHANGED
@@ -8,7 +8,7 @@ import * as fmt from '../fmt.mjs';
8
8
  import { chalk } from '../fmt.mjs';
9
9
  import { REGISTRY_REPO, REGISTRY_BASE_BRANCH, REGISTRY_DIR } from '../constants.mjs';
10
10
  import { resolveInput } from '../paths.mjs';
11
- import { load as loadManifest } from '../manifest.mjs';
11
+ import { load as loadManifest, save as saveManifest } from '../manifest.mjs';
12
12
  import { hashFile, walkRegistryTree } from '../registry.mjs';
13
13
 
14
14
  const PUSHABLE_TYPES = ['agents', 'skills', 'commands', 'evals'];
@@ -53,7 +53,9 @@ function collectModifiedFiles(workspaceDir) {
53
53
  const filePath = join(workspaceDir, key);
54
54
  if (!existsSync(filePath)) continue;
55
55
  const currentHash = hashFile(filePath);
56
- if (currentHash !== entry.sha256) {
56
+ const isModified = currentHash !== entry.sha256;
57
+ const isNew = !entry.registry_sha256; // Template-derived, never pushed to remote
58
+ if (isModified || isNew) {
57
59
  const meta = parseManifestKey(key);
58
60
  if (meta) {
59
61
  files.push({
@@ -170,7 +172,7 @@ function generatePrBody(files, newNamespaces) {
170
172
 
171
173
  // ── Git pipeline (works for single file or batch) ────────────────────
172
174
 
173
- function pushFiles(files, { repo, dryRun }) {
175
+ function pushFiles(files, { repo, dryRun, workspaceDir }) {
174
176
  // Summary
175
177
  const counts = {};
176
178
  for (const f of files) {
@@ -289,6 +291,19 @@ function pushFiles(files, { repo, dryRun }) {
289
291
  fmt.logSuccess(`${items.length} ${type} pushed`);
290
292
  }
291
293
  }
294
+ // Update manifest — mark pushed files as synced (set registry_sha256 = sha256)
295
+ if (workspaceDir) {
296
+ const manifest = loadManifest(workspaceDir);
297
+ for (const file of files) {
298
+ // Convert registryTarget back to manifest key (strip REGISTRY_DIR/ prefix)
299
+ const manifestKey = file.registryTarget.replace(`${REGISTRY_DIR}/`, '');
300
+ if (manifest.files[manifestKey]) {
301
+ manifest.files[manifestKey].registry_sha256 = manifest.files[manifestKey].sha256;
302
+ }
303
+ }
304
+ saveManifest(workspaceDir, manifest);
305
+ }
306
+
292
307
  fmt.logSuccess(`PR: ${chalk.cyan(prUrl)}`);
293
308
  fmt.outro('Push complete');
294
309
  } catch (e) {
@@ -319,13 +334,13 @@ export function pushCommand(args) {
319
334
 
320
335
  fmt.intro('aw push');
321
336
 
322
- // No args = push all modified files
337
+ // No args = push all modified + new (unpushed) files
323
338
  if (!input) {
324
339
  const files = collectModifiedFiles(workspaceDir);
325
340
  if (files.length === 0) {
326
- fmt.cancel('Nothing to push — no modified files.\n\n Use `aw status` to see synced files.');
341
+ fmt.cancel('Nothing to push — no modified or new files.\n\n Use `aw status` to see synced files.');
327
342
  }
328
- pushFiles(files, { repo, dryRun });
343
+ pushFiles(files, { repo, dryRun, workspaceDir });
329
344
  return;
330
345
  }
331
346
 
@@ -354,7 +369,7 @@ export function pushCommand(args) {
354
369
  if (files.length === 0) {
355
370
  fmt.cancel(`Nothing to push in ${chalk.cyan(input)} — no agents, skills, commands, or evals found.`);
356
371
  }
357
- pushFiles(files, { repo, dryRun });
372
+ pushFiles(files, { repo, dryRun, workspaceDir });
358
373
  return;
359
374
  }
360
375
 
@@ -394,7 +409,7 @@ export function pushCommand(args) {
394
409
  namespace: namespacePath,
395
410
  slug,
396
411
  isDir,
397
- }], { repo, dryRun });
412
+ }], { repo, dryRun, workspaceDir });
398
413
  }
399
414
 
400
415
  // ── Helpers ──────────────────────────────────────────────────────────
@@ -39,6 +39,7 @@ export function statusCommand(args) {
39
39
 
40
40
  const files = Object.entries(manifest.files || {});
41
41
  const modified = [];
42
+ const unpushed = [];
42
43
  const missing = [];
43
44
  const conflicts = [];
44
45
 
@@ -51,6 +52,12 @@ export function statusCommand(args) {
51
52
 
52
53
  const currentHash = hashFile(filePath);
53
54
 
55
+ // Template-derived files that were never pushed to the remote registry
56
+ if (!entry.registry_sha256) {
57
+ unpushed.push(key);
58
+ continue;
59
+ }
60
+
54
61
  if (currentHash !== entry.sha256) {
55
62
  const content = readFileSync(filePath, 'utf8');
56
63
  if (content.includes('<<<<<<<')) {
@@ -64,6 +71,7 @@ export function statusCommand(args) {
64
71
  // Summary line
65
72
  const summaryParts = [
66
73
  `${files.length} synced`,
74
+ unpushed.length > 0 ? chalk.green(`${unpushed.length} new (unpushed)`) : null,
67
75
  modified.length > 0 ? chalk.yellow(`${modified.length} modified`) : null,
68
76
  conflicts.length > 0 ? chalk.red(`${conflicts.length} conflicts`) : null,
69
77
  missing.length > 0 ? chalk.dim(`${missing.length} missing`) : null,
@@ -78,6 +86,17 @@ export function statusCommand(args) {
78
86
  );
79
87
  }
80
88
 
89
+ if (unpushed.length > 0) {
90
+ // Group by namespace for cleaner display
91
+ const nsCounts = {};
92
+ for (const key of unpushed) {
93
+ const ns = key.split('/').slice(0, 2).join('/');
94
+ nsCounts[ns] = (nsCounts[ns] || 0) + 1;
95
+ }
96
+ const nsLines = Object.entries(nsCounts).map(([ns, count]) => chalk.green(` ${ns}: ${count} files`)).join('\n');
97
+ fmt.note(nsLines, chalk.green('New (unpushed)'));
98
+ }
99
+
81
100
  if (modified.length > 0) {
82
101
  fmt.note(
83
102
  modified.map(m => chalk.yellow(m)).join('\n'),
@@ -92,7 +111,7 @@ export function statusCommand(args) {
92
111
  );
93
112
  }
94
113
 
95
- if (modified.length === 0 && conflicts.length === 0 && missing.length === 0) {
114
+ if (modified.length === 0 && conflicts.length === 0 && missing.length === 0 && unpushed.length === 0) {
96
115
  fmt.logSuccess('Workspace is clean');
97
116
  }
98
117
 
@@ -100,6 +119,9 @@ export function statusCommand(args) {
100
119
  if (conflicts.length > 0) {
101
120
  fmt.logWarn(`Fix conflicts: ${chalk.dim('grep -r "<<<<<<< " .aw_registry/')}`);
102
121
  }
122
+ if (unpushed.length > 0) {
123
+ fmt.logInfo(`Push new files: ${chalk.dim('aw push')} or ${chalk.dim('aw push <namespace>')}`);
124
+ }
103
125
  if (modified.length > 0) {
104
126
  fmt.logInfo(`Push changes: ${chalk.dim('aw push <path>')}`);
105
127
  }
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 = 'main';
4
+ export const REGISTRY_BASE_BRANCH = 'feat/aw-push-batch';
5
5
 
6
6
  /** Default registry repository */
7
7
  export const REGISTRY_REPO = 'GoHighLevel/platform-docs';
package/manifest.mjs CHANGED
@@ -24,7 +24,7 @@ export function save(workspaceDir, manifest) {
24
24
  writeFileSync(manifestPath(workspaceDir), JSON.stringify(manifest, null, 2) + '\n');
25
25
  }
26
26
 
27
- export function update(workspaceDir, actions, namespaceName) {
27
+ export function update(workspaceDir, actions, namespaceName, opts = {}) {
28
28
  const manifest = load(workspaceDir);
29
29
  manifest.synced_at = new Date().toISOString();
30
30
  manifest.namespace = namespaceName || manifest.namespace;
@@ -38,7 +38,9 @@ export function update(workspaceDir, actions, namespaceName) {
38
38
  manifest.files[manifestKey] = {
39
39
  sha256: hashFile(act.targetPath),
40
40
  source: act.source,
41
- registry_sha256: act.registryHash,
41
+ // When pulled from template (renamed namespace), these files don't exist
42
+ // in the remote registry yet — mark as null so push detects them as new.
43
+ registry_sha256: opts.fromTemplate ? null : act.registryHash,
42
44
  };
43
45
  }
44
46
  } else if (act.action === 'CONFLICT') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.34-beta.1",
3
+ "version": "0.1.34-beta.2",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": {
package/plan.mjs CHANGED
@@ -68,7 +68,10 @@ export function computePlan(registryDirs, workspaceDir, includePatterns = [], {
68
68
  action = 'UPDATE';
69
69
  } else {
70
70
  const localChanged = localHash !== manifestEntry.sha256;
71
- const registryChanged = registryHash !== manifestEntry.registry_sha256;
71
+ // registry_sha256 is null for template-derived files (never pushed).
72
+ // Treat null as "no known registry version" — can't conflict with unknown.
73
+ const registryChanged = manifestEntry.registry_sha256 != null
74
+ && registryHash !== manifestEntry.registry_sha256;
72
75
  if (localChanged && registryChanged) {
73
76
  action = 'CONFLICT';
74
77
  } else if (localChanged) {