@ghl-ai/aw 0.1.73-beta.0 → 0.1.73-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/init.mjs CHANGED
@@ -25,7 +25,6 @@ import * as fmt from '../fmt.mjs';
25
25
  import { chalk, setSilent } from '../fmt.mjs';
26
26
  import { linkWorkspace } from '../link.mjs';
27
27
  import { generateCommands, copyInstructions, initAwDocs, syncHomeHarnessInstructions } from '../integrate.mjs';
28
- import { renderRules } from '../render-rules.mjs';
29
28
  import { setupMcp } from '../mcp.mjs';
30
29
  import { isContextModeRequested } from '../integrations/context-mode.mjs';
31
30
  import { applyStoredStartupPreferences, ensureAwRuntimeHook, isDefaultRoutingEnabled } from '../startup.mjs';
@@ -151,11 +150,6 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
151
150
  initAwDocs(HOME);
152
151
  if (cwd !== HOME) {
153
152
  syncInstructionsAndAwDocs(cwd, namespace);
154
- } else {
155
- // Running from $HOME (fresh-laptop flow): render global IDE rules directly.
156
- // The project branch above is otherwise the only renderRules call site, so
157
- // init from $HOME used to leave ~/.claude/rules and ~/.cursor/rules empty.
158
- renderRules(HOME, { homeDir: HOME });
159
153
  }
160
154
  }
161
155
 
package/constants.mjs CHANGED
@@ -105,7 +105,7 @@ export const RULES_SOURCE_DIR = '.aw_rules';
105
105
  /** Runtime location exposed to harnesses and generated instructions */
106
106
  export const RULES_RUNTIME_DIR = '.aw/.aw_rules';
107
107
  /** Telemetry endpoint — override with AW_TELEMETRY_URL env var */
108
- export const TELEMETRY_URL = process.env.AW_TELEMETRY_URL || 'https://services.leadconnectorhq.com/agentic-workspace/api/telemetry/events';
108
+ export const TELEMETRY_URL = process.env.AW_TELEMETRY_URL || 'https://staging.services.leadconnectorhq.com/agentic-workspace/api/telemetry/events';
109
109
 
110
110
  /** AW bot identity for Co-Authored-By trailers */
111
111
  export const AW_BOT_NAME = 'AW';
package/ecc.mjs CHANGED
@@ -12,7 +12,7 @@ import { applyStoredStartupPreferences } from "./startup.mjs";
12
12
 
13
13
  const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
14
14
  const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
15
- export const AW_ECC_TAG = "v1.4.66";
15
+ export const AW_ECC_TAG = "v1.4.67";
16
16
  const REQUIRED_ECC_FILES = [
17
17
  "package.json",
18
18
  "scripts/install-apply.js",
@@ -11,14 +11,23 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- const { buildEvent, sendAsync, isDisabled } = require('../lib/aw-usage-telemetry');
14
+ const {
15
+ buildEvent,
16
+ sendAsync,
17
+ isDisabled,
18
+ findRecentSdlcSessionForProject,
19
+ } = require('../lib/aw-usage-telemetry');
15
20
 
16
21
  function buildCommitCreatedEvent({ commitHash = 'unknown', branch = 'unknown', cwd = process.cwd() } = {}) {
17
- // Git hooks have no harness session context, but cwd lets buildEvent derive
18
- // project_hash so the dashboard can correlate commits back to /aw:* sessions.
19
- const event = buildEvent({ cwd }, 'commit_created', {
22
+ const linkedSession = findRecentSdlcSessionForProject(cwd);
23
+ const event = buildEvent({
24
+ cwd,
25
+ ...(linkedSession?.session_id ? { session_id: linkedSession.session_id } : {}),
26
+ }, 'commit_created', {
20
27
  commit_hash: commitHash,
28
+ commit_sha: commitHash,
21
29
  branch,
30
+ ...(linkedSession?.session_id ? { linked_session_source: 'project_recent_sdlc_session' } : {}),
22
31
  });
23
32
 
24
33
  // Override harness to 'git' since this fires from a git hook, not a harness.
@@ -109,7 +109,9 @@ function processPromptSubmitInput(input, deps = {}) {
109
109
 
110
110
  if (slash) {
111
111
  persistSkill(getSessionId(input), input?.turn_id || null, slash);
112
- persistSlashCmd(getSessionId(input), slash);
112
+ persistSlashCmd(getSessionId(input), slash, {
113
+ cwd: input?.cwd || (input?.workspace_roots && input.workspace_roots[0]) || null,
114
+ });
113
115
  events.push({
114
116
  eventType: 'skill_invoked',
115
117
  payload: {
@@ -12,7 +12,7 @@
12
12
  const https = require('https');
13
13
  const http = require('http');
14
14
 
15
- const DEFAULT_URL = 'https://services.leadconnectorhq.com/agentic-workspace/api/telemetry/usage-events';
15
+ const DEFAULT_URL = 'https://staging.services.leadconnectorhq.com/agentic-workspace/api/telemetry/usage-events';
16
16
  const TIMEOUT_MS = 10_000;
17
17
 
18
18
  function post(url, body) {
@@ -21,6 +21,7 @@ const SENDER_SCRIPT = path.join(__dirname, '..', 'hooks', 'aw-usage-telemetry-se
21
21
  const AW_HOME = path.join(os.homedir(), '.aw');
22
22
  const CONFIG_PATH = path.join(AW_HOME, 'telemetry', 'config.json');
23
23
  const SESSION_DIR = path.join(AW_HOME, 'telemetry', 'sessions');
24
+ const SESSION_PROJECT_DIR = path.join(SESSION_DIR, 'by-project');
24
25
  const DEDUPE_DIR = path.join(os.tmpdir(), 'aw-usage-telemetry-dedupe');
25
26
 
26
27
  // ── Git config cache (once per process) ──────────────────────────────
@@ -157,6 +158,16 @@ function computeProjectHash(cwd) {
157
158
  return crypto.createHash('sha256').update(cwd).digest('hex').slice(0, 16);
158
159
  }
159
160
 
161
+ function normalizeProjectHash(value) {
162
+ return typeof value === 'string' && /^[a-f0-9]{16}$/i.test(value) ? value.toLowerCase() : null;
163
+ }
164
+
165
+ function resolveProjectHash(context) {
166
+ if (!context || typeof context !== 'object') return null;
167
+ return normalizeProjectHash(context.project_hash || context.projectHash)
168
+ || computeProjectHash(context.cwd);
169
+ }
170
+
160
171
  // ── Session file cleanup ─────────────────────────────────────────────
161
172
  // Prune session files older than SESSION_MAX_AGE_MS to prevent unbounded growth.
162
173
  // Called once per session start — best-effort, never blocks.
@@ -241,21 +252,40 @@ function readSessionSkill(sessionId, turnId) {
241
252
  // to the originating /aw:* invocation. Separate from `last_skill` because
242
253
  // the source is the prompt, not the tool, and the lifetime is the whole
243
254
  // session (not just one turn).
244
- function persistSessionSlashCommand(sessionId, slashCommand) {
255
+ function writeProjectSessionIndex(projectHash, sessionId, slashCommand) {
256
+ if (!projectHash || !sessionId || !slashCommand?.is_sdlc_stage) return;
257
+ try {
258
+ fs.mkdirSync(SESSION_PROJECT_DIR, { recursive: true });
259
+ fs.writeFileSync(path.join(SESSION_PROJECT_DIR, `${projectHash}.json`), JSON.stringify({
260
+ project_hash: projectHash,
261
+ session_id: sessionId,
262
+ command_namespace: slashCommand.command_namespace || null,
263
+ command_name: slashCommand.command_name,
264
+ is_sdlc_stage: Boolean(slashCommand.is_sdlc_stage),
265
+ updated_at: new Date().toISOString(),
266
+ }));
267
+ } catch { /* ignore */ }
268
+ }
269
+
270
+ function persistSessionSlashCommand(sessionId, slashCommand, context = {}) {
245
271
  if (!sessionId || !slashCommand?.command_name) return;
246
272
  try {
247
273
  fs.mkdirSync(SESSION_DIR, { recursive: true });
248
274
  const state = readSessionState(sessionId);
275
+ const projectHash = resolveProjectHash(context) || state.project_hash || null;
249
276
  fs.writeFileSync(path.join(SESSION_DIR, sessionId + '.json'), JSON.stringify({
250
277
  ...state,
278
+ ...(projectHash ? { project_hash: projectHash } : {}),
251
279
  last_slash_command: {
252
280
  command_namespace: slashCommand.command_namespace || null,
253
281
  command_name: slashCommand.command_name,
254
282
  command_args: slashCommand.command_args || '',
255
283
  is_sdlc_stage: Boolean(slashCommand.is_sdlc_stage),
284
+ ...(projectHash ? { project_hash: projectHash } : {}),
256
285
  updated_at: new Date().toISOString(),
257
286
  },
258
287
  }));
288
+ writeProjectSessionIndex(projectHash, sessionId, slashCommand);
259
289
  } catch { /* ignore */ }
260
290
  }
261
291
 
@@ -265,6 +295,59 @@ function readSessionLastSlashCommand(sessionId) {
265
295
  return cmd;
266
296
  }
267
297
 
298
+ function parseUpdatedAt(value) {
299
+ const millis = Date.parse(value || '');
300
+ return Number.isFinite(millis) ? millis : 0;
301
+ }
302
+
303
+ function isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs) {
304
+ if (!candidate?.session_id) return false;
305
+ if (!candidate?.is_sdlc_stage) return false;
306
+ if (candidate.project_hash !== projectHash) return false;
307
+ const updatedAt = parseUpdatedAt(candidate.updated_at);
308
+ if (!updatedAt) return false;
309
+ return Date.now() - updatedAt <= maxAgeMs;
310
+ }
311
+
312
+ function readProjectSessionIndex(projectHash, maxAgeMs) {
313
+ try {
314
+ const candidate = JSON.parse(fs.readFileSync(path.join(SESSION_PROJECT_DIR, `${projectHash}.json`), 'utf8'));
315
+ return isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs) ? candidate : null;
316
+ } catch {
317
+ return null;
318
+ }
319
+ }
320
+
321
+ function findRecentSdlcSessionForProject(cwd, maxAgeMs = SESSION_MAX_AGE_MS) {
322
+ const projectHash = computeProjectHash(cwd);
323
+ if (!projectHash) return null;
324
+
325
+ const indexed = readProjectSessionIndex(projectHash, maxAgeMs);
326
+ if (indexed) return indexed;
327
+
328
+ let best = null;
329
+ try {
330
+ const entries = fs.readdirSync(SESSION_DIR);
331
+ for (const entry of entries) {
332
+ if (!entry.endsWith('.json')) continue;
333
+ const sessionId = entry.slice(0, -'.json'.length);
334
+ const state = readSessionState(sessionId);
335
+ const cmd = state.last_slash_command;
336
+ const candidate = {
337
+ session_id: sessionId,
338
+ project_hash: cmd?.project_hash || state.project_hash || null,
339
+ is_sdlc_stage: Boolean(cmd?.is_sdlc_stage),
340
+ updated_at: cmd?.updated_at || state.updated_at || null,
341
+ };
342
+ if (!isRecentSdlcSessionCandidate(candidate, projectHash, maxAgeMs)) continue;
343
+ if (!best || parseUpdatedAt(candidate.updated_at) > parseUpdatedAt(best.updated_at)) {
344
+ best = candidate;
345
+ }
346
+ }
347
+ } catch { /* ignore */ }
348
+ return best;
349
+ }
350
+
268
351
  // ── Short-TTL dedupe guards ──────────────────────────────────────────
269
352
 
270
353
  function normalizeDedupePart(value) {
@@ -503,6 +586,7 @@ module.exports = {
503
586
  readSessionSkill,
504
587
  persistSessionSlashCommand,
505
588
  readSessionLastSlashCommand,
589
+ findRecentSdlcSessionForProject,
506
590
  readLastAssistantFromTranscript,
507
591
  resolvePromptText,
508
592
  getAwCliVersionDetails,
package/hooks.mjs CHANGED
@@ -102,7 +102,7 @@ if git log -1 --format='%b' HEAD 2>/dev/null | grep -qF "Co-Authored-By: AW"; th
102
102
  TELEMETRY_HOOK="$HOME/.aw-ecc/scripts/hooks/aw-usage-commit-created.js"
103
103
  fi
104
104
  if command -v node >/dev/null 2>&1 && [ -f "$TELEMETRY_HOOK" ]; then
105
- COMMIT_HASH="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
105
+ COMMIT_HASH="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
106
106
  BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)"
107
107
  node "$TELEMETRY_HOOK" "$COMMIT_HASH" "$BRANCH" "$(pwd)" >/dev/null 2>&1
108
108
  fi
@@ -286,7 +286,7 @@ if git log -1 --format='%b' HEAD 2>/dev/null | grep -qF "Co-Authored-By: AW"; th
286
286
  TELEMETRY_HOOK="$HOME/.aw-ecc/scripts/hooks/aw-usage-commit-created.js"
287
287
  fi
288
288
  if command -v node >/dev/null 2>&1 && [ -f "$TELEMETRY_HOOK" ]; then
289
- COMMIT_HASH="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
289
+ COMMIT_HASH="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
290
290
  BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)"
291
291
  node "$TELEMETRY_HOOK" "$COMMIT_HASH" "$BRANCH" "$(pwd)" >/dev/null 2>&1
292
292
  fi
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.73-beta.0",
4
- "description": "Agentic Workspace CLI \u2014 pull, push & manage agents, skills and commands from the registry",
3
+ "version": "0.1.73-beta.2",
4
+ "description": "Agentic Workspace CLI pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "aw": "bin.js"
@@ -74,4 +74,4 @@
74
74
  "devDependencies": {
75
75
  "vitest": "^4.1.2"
76
76
  }
77
- }
77
+ }