@gurulu/cli 0.4.2 → 0.4.3

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.
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu attribution report|compare|channels`.
3
+ *
4
+ * Mirror of MCP tools `gurulu.attribution.report` / `.compare` / `.channels`.
5
+ * Hits new `/api/cli/attribution/*` proxy endpoints (CLI-auth, not session-auth)
6
+ * so the same surface is reachable from agents and humans.
7
+ */
8
+ export interface AttributionArgs {
9
+ action?: string;
10
+ site?: string;
11
+ from?: string;
12
+ to?: string;
13
+ range?: string;
14
+ model?: string;
15
+ baselineModel?: string;
16
+ variantModel?: string;
17
+ conversionEvent?: string;
18
+ format?: string;
19
+ json?: boolean;
20
+ profile?: string;
21
+ }
22
+ export declare function attributionCommand(args: AttributionArgs): Promise<void>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu attribution report|compare|channels`.
4
+ *
5
+ * Mirror of MCP tools `gurulu.attribution.report` / `.compare` / `.channels`.
6
+ * Hits new `/api/cli/attribution/*` proxy endpoints (CLI-auth, not session-auth)
7
+ * so the same surface is reachable from agents and humans.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.attributionCommand = attributionCommand;
11
+ const api_client_1 = require("../api-client");
12
+ const ui_1 = require("../utils/ui");
13
+ async function attributionCommand(args) {
14
+ const action = args.action || '';
15
+ switch (action) {
16
+ case 'report':
17
+ return reportCmd(args);
18
+ case 'compare':
19
+ return compareCmd(args);
20
+ case 'channels':
21
+ return channelsCmd(args);
22
+ default:
23
+ (0, ui_1.error)(`Unknown attribution action: ${action}`);
24
+ (0, ui_1.info)('Usage: gurulu attribution [report|compare|channels] --site=<id> [...]');
25
+ process.exit(1);
26
+ }
27
+ }
28
+ function buildQuery(params) {
29
+ const usp = new URLSearchParams();
30
+ for (const [k, v] of Object.entries(params)) {
31
+ if (v === undefined || v === '' || v === null)
32
+ continue;
33
+ usp.set(k, String(v));
34
+ }
35
+ const s = usp.toString();
36
+ return s ? `?${s}` : '';
37
+ }
38
+ function emit(args, body, table) {
39
+ const format = args.format || (args.json ? 'json' : 'json');
40
+ if (format === 'table') {
41
+ process.stdout.write(table());
42
+ return;
43
+ }
44
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
45
+ }
46
+ async function reportCmd(args) {
47
+ if (!args.site) {
48
+ (0, ui_1.error)('Usage: gurulu attribution report --site=<id> [--from=ISO --to=ISO | --range=7d|30d|90d] [--model=...]');
49
+ process.exit(1);
50
+ }
51
+ const path = `/api/cli/attribution/report${buildQuery({
52
+ site: args.site,
53
+ from: args.from,
54
+ to: args.to,
55
+ range: args.range,
56
+ model: args.model,
57
+ conversionEvent: args.conversionEvent,
58
+ })}`;
59
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
60
+ emit(args, body, () => {
61
+ const rows = body.results || [];
62
+ const out = [['CHANNEL', 'CREDIT', 'CONVERSIONS', 'REVENUE'].join('\t')];
63
+ for (const r of rows) {
64
+ out.push([r.channel ?? '-', r.credit ?? 0, r.conversions ?? 0, r.revenue ?? 0].join('\t'));
65
+ }
66
+ return out.join('\n') + '\n';
67
+ });
68
+ }
69
+ async function compareCmd(args) {
70
+ if (!args.site || !args.baselineModel || !args.variantModel) {
71
+ (0, ui_1.error)('Usage: gurulu attribution compare --site=<id> --baseline-model=<m> --variant-model=<m>');
72
+ process.exit(1);
73
+ }
74
+ const path = `/api/cli/attribution/compare${buildQuery({
75
+ site: args.site,
76
+ baselineModel: args.baselineModel,
77
+ variantModel: args.variantModel,
78
+ from: args.from,
79
+ to: args.to,
80
+ range: args.range,
81
+ conversionEvent: args.conversionEvent,
82
+ })}`;
83
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
84
+ emit(args, body, () => {
85
+ const rows = body.comparison || body.results || [];
86
+ const out = [['CHANNEL', 'BASELINE', 'VARIANT', 'DELTA'].join('\t')];
87
+ for (const r of rows) {
88
+ out.push([r.channel ?? '-', r.baseline ?? 0, r.variant ?? 0, r.delta ?? 0].join('\t'));
89
+ }
90
+ return out.join('\n') + '\n';
91
+ });
92
+ }
93
+ async function channelsCmd(args) {
94
+ if (!args.site) {
95
+ (0, ui_1.error)('Usage: gurulu attribution channels --site=<id>');
96
+ process.exit(1);
97
+ }
98
+ const path = `/api/cli/attribution/channels${buildQuery({
99
+ site: args.site,
100
+ range: args.range,
101
+ })}`;
102
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
103
+ emit(args, body, () => {
104
+ const rows = body.channels || [];
105
+ const out = [['CHANNEL', 'SESSIONS', 'CONVERSIONS'].join('\t')];
106
+ for (const r of rows) {
107
+ out.push([r.channel ?? '-', r.sessions ?? 0, r.conversions ?? 0].join('\t'));
108
+ }
109
+ return out.join('\n') + '\n';
110
+ });
111
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu conversion-paths list`.
3
+ *
4
+ * Mirror of MCP `gurulu.conversion_paths.list`. Hits new
5
+ * `/api/cli/conversion-paths` proxy endpoint (CLI-auth).
6
+ */
7
+ export interface ConversionPathsArgs {
8
+ action?: string;
9
+ site?: string;
10
+ from?: string;
11
+ to?: string;
12
+ range?: string;
13
+ conversionEvent?: string;
14
+ limit?: number;
15
+ format?: string;
16
+ json?: boolean;
17
+ profile?: string;
18
+ }
19
+ export declare function conversionPathsCommand(args: ConversionPathsArgs): Promise<void>;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu conversion-paths list`.
4
+ *
5
+ * Mirror of MCP `gurulu.conversion_paths.list`. Hits new
6
+ * `/api/cli/conversion-paths` proxy endpoint (CLI-auth).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.conversionPathsCommand = conversionPathsCommand;
10
+ const api_client_1 = require("../api-client");
11
+ const ui_1 = require("../utils/ui");
12
+ async function conversionPathsCommand(args) {
13
+ const action = args.action || 'list';
14
+ switch (action) {
15
+ case 'list':
16
+ return listCmd(args);
17
+ default:
18
+ (0, ui_1.error)(`Unknown conversion-paths action: ${action}`);
19
+ (0, ui_1.info)('Usage: gurulu conversion-paths list --site=<id> [--range=30d] [--limit=20]');
20
+ process.exit(1);
21
+ }
22
+ }
23
+ async function listCmd(args) {
24
+ if (!args.site) {
25
+ (0, ui_1.error)('Usage: gurulu conversion-paths list --site=<id> [--range=30d] [--limit=20]');
26
+ process.exit(1);
27
+ }
28
+ const usp = new URLSearchParams();
29
+ usp.set('site', args.site);
30
+ if (args.from)
31
+ usp.set('from', args.from);
32
+ if (args.to)
33
+ usp.set('to', args.to);
34
+ if (args.range)
35
+ usp.set('range', args.range);
36
+ if (args.conversionEvent)
37
+ usp.set('conversionEvent', args.conversionEvent);
38
+ if (args.limit)
39
+ usp.set('limit', String(args.limit));
40
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/conversion-paths?${usp.toString()}`, {
41
+ profile: args.profile,
42
+ });
43
+ if (args.format === 'table') {
44
+ const rows = body.paths || [];
45
+ process.stdout.write(['PATH', 'CONVERSIONS', 'REVENUE'].join('\t') + '\n');
46
+ for (const p of rows) {
47
+ const path = Array.isArray(p.path)
48
+ ? p.path.join(' → ')
49
+ : String(p.path ?? '-');
50
+ process.stdout.write([path.slice(0, 80), p.conversions ?? 0, p.revenue ?? 0].join('\t') + '\n');
51
+ }
52
+ return;
53
+ }
54
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
55
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Sprint E SE-B — `gurulu errors list|detail|resolve|mute`.
3
+ *
4
+ * Mirror of MCP `gurulu.errors.list` / `.detail` plus two new write tools
5
+ * (`.resolve`, `.mute`). Hits new `/api/cli/errors/*` proxy endpoints which
6
+ * use CLI-auth instead of the session-auth used by `/api/analytics/error-groups`.
7
+ *
8
+ * Note: `gurulu errors upload-sourcemap` and `upload-native-symbols` are
9
+ * documented aliases of `gurulu sourcemap upload [--platform=...]` — see
10
+ * `commands/sourcemap.ts`.
11
+ */
12
+ export interface ErrorsArgs {
13
+ action?: string;
14
+ site?: string;
15
+ fingerprint?: string;
16
+ level?: string;
17
+ resolved?: string;
18
+ environment?: string;
19
+ releaseId?: string;
20
+ limit?: number;
21
+ duration?: string;
22
+ format?: string;
23
+ json?: boolean;
24
+ yes?: boolean;
25
+ profile?: string;
26
+ }
27
+ export declare function errorsCommand(args: ErrorsArgs): Promise<void>;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ /**
3
+ * Sprint E SE-B — `gurulu errors list|detail|resolve|mute`.
4
+ *
5
+ * Mirror of MCP `gurulu.errors.list` / `.detail` plus two new write tools
6
+ * (`.resolve`, `.mute`). Hits new `/api/cli/errors/*` proxy endpoints which
7
+ * use CLI-auth instead of the session-auth used by `/api/analytics/error-groups`.
8
+ *
9
+ * Note: `gurulu errors upload-sourcemap` and `upload-native-symbols` are
10
+ * documented aliases of `gurulu sourcemap upload [--platform=...]` — see
11
+ * `commands/sourcemap.ts`.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.errorsCommand = errorsCommand;
15
+ const api_client_1 = require("../api-client");
16
+ const ui_1 = require("../utils/ui");
17
+ const confirm_1 = require("../utils/confirm");
18
+ async function errorsCommand(args) {
19
+ const action = args.action || '';
20
+ switch (action) {
21
+ case 'list':
22
+ return listCmd(args);
23
+ case 'detail':
24
+ return detailCmd(args);
25
+ case 'resolve':
26
+ return resolveCmd(args);
27
+ case 'mute':
28
+ return muteCmd(args);
29
+ case 'upload-sourcemap':
30
+ case 'upload-native-symbols':
31
+ (0, ui_1.info)('Use `gurulu sourcemap upload [--platform=ios|android|web|server] ...` — these names are aliases.');
32
+ return;
33
+ default:
34
+ (0, ui_1.error)(`Unknown errors action: ${action}`);
35
+ (0, ui_1.info)('Usage: gurulu errors [list|detail|resolve|mute] --site=<id> [--fingerprint=<hash>] ...');
36
+ process.exit(1);
37
+ }
38
+ }
39
+ async function listCmd(args) {
40
+ if (!args.site) {
41
+ (0, ui_1.error)('Usage: gurulu errors list --site=<id> [--level=error|warning] [--resolved=false] [--limit=20]');
42
+ process.exit(1);
43
+ }
44
+ const usp = new URLSearchParams();
45
+ usp.set('site', args.site);
46
+ if (args.level)
47
+ usp.set('level', args.level);
48
+ if (args.resolved !== undefined)
49
+ usp.set('resolved', args.resolved);
50
+ if (args.environment)
51
+ usp.set('environment', args.environment);
52
+ if (args.releaseId)
53
+ usp.set('releaseId', args.releaseId);
54
+ if (args.limit)
55
+ usp.set('limit', String(args.limit));
56
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/errors?${usp.toString()}`, {
57
+ profile: args.profile,
58
+ });
59
+ if (args.format === 'table') {
60
+ const rows = body.groups || [];
61
+ process.stdout.write(['FINGERPRINT', 'COUNT', 'LEVEL', 'STATUS', 'TITLE'].join('\t') + '\n');
62
+ for (const g of rows) {
63
+ process.stdout.write([
64
+ (g.fingerprint || '').slice(0, 16),
65
+ g.count ?? 0,
66
+ g.level ?? '-',
67
+ g.status ?? 'unresolved',
68
+ String(g.title || '-').slice(0, 80),
69
+ ].join('\t') + '\n');
70
+ }
71
+ return;
72
+ }
73
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
74
+ }
75
+ async function detailCmd(args) {
76
+ if (!args.site || !args.fingerprint) {
77
+ (0, ui_1.error)('Usage: gurulu errors detail --site=<id> --fingerprint=<hash>');
78
+ process.exit(1);
79
+ }
80
+ const path = `/api/cli/errors/${encodeURIComponent(args.fingerprint)}?site=${encodeURIComponent(args.site)}`;
81
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
82
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
83
+ }
84
+ async function resolveCmd(args) {
85
+ if (!args.site || !args.fingerprint) {
86
+ (0, ui_1.error)('Usage: gurulu errors resolve --site=<id> --fingerprint=<hash>');
87
+ process.exit(1);
88
+ }
89
+ const ok = await (0, confirm_1.promptConfirm)(`Mark error ${args.fingerprint.slice(0, 12)} as resolved?`, { yes: args.yes, defaultYes: true });
90
+ if (!ok) {
91
+ (0, ui_1.info)('Aborted.');
92
+ return;
93
+ }
94
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/errors/${encodeURIComponent(args.fingerprint)}/resolve`, {
95
+ profile: args.profile,
96
+ method: 'POST',
97
+ json: { siteId: args.site },
98
+ });
99
+ if (args.json) {
100
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
101
+ return;
102
+ }
103
+ (0, ui_1.success)(`Resolved ${args.fingerprint.slice(0, 12)}.`);
104
+ }
105
+ async function muteCmd(args) {
106
+ if (!args.site || !args.fingerprint) {
107
+ (0, ui_1.error)('Usage: gurulu errors mute --site=<id> --fingerprint=<hash> [--duration=24h]');
108
+ process.exit(1);
109
+ }
110
+ const duration = args.duration || '24h';
111
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/errors/${encodeURIComponent(args.fingerprint)}/mute`, {
112
+ profile: args.profile,
113
+ method: 'POST',
114
+ json: { siteId: args.site, duration },
115
+ });
116
+ if (args.json) {
117
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
118
+ return;
119
+ }
120
+ (0, ui_1.success)(`Muted ${args.fingerprint.slice(0, 12)} for ${duration}.`);
121
+ }
@@ -1,5 +1,8 @@
1
1
  /**
2
2
  * Phase 19.5 W2 B8 — `gurulu identity decay|transfers|cdc-sources`.
3
+ *
4
+ * Sprint E SE-D — `gurulu identity identify|alias|merge` write surface
5
+ * (mirrors MCP `gurulu.identity.identify` / `.alias` / `.merge`).
3
6
  */
4
7
  export interface IdentityArgs {
5
8
  action?: string;
@@ -7,6 +10,16 @@ export interface IdentityArgs {
7
10
  direction?: string;
8
11
  status?: string;
9
12
  limit?: number;
13
+ site?: string;
14
+ userId?: string;
15
+ email?: string;
16
+ phone?: string;
17
+ traits?: string;
18
+ previousUserId?: string;
19
+ newUserId?: string;
20
+ canonicalId?: string;
21
+ duplicateId?: string;
22
+ yes?: boolean;
10
23
  json?: boolean;
11
24
  profile?: string;
12
25
  }
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Phase 19.5 W2 B8 — `gurulu identity decay|transfers|cdc-sources`.
4
+ *
5
+ * Sprint E SE-D — `gurulu identity identify|alias|merge` write surface
6
+ * (mirrors MCP `gurulu.identity.identify` / `.alias` / `.merge`).
4
7
  */
5
8
  Object.defineProperty(exports, "__esModule", { value: true });
6
9
  exports.identityCommand = identityCommand;
@@ -15,9 +18,15 @@ async function identityCommand(args) {
15
18
  return transfersCmd(args);
16
19
  case 'cdc-sources':
17
20
  return cdcSourcesCmd(args);
21
+ case 'identify':
22
+ return identifyCmd(args);
23
+ case 'alias':
24
+ return aliasCmd(args);
25
+ case 'merge':
26
+ return mergeCmd(args);
18
27
  default:
19
28
  (0, ui_1.error)(`Unknown identity action: ${action}`);
20
- (0, ui_1.info)('Usage: gurulu identity [decay stats|transfers list|cdc-sources list]');
29
+ (0, ui_1.info)('Usage: gurulu identity [decay stats|transfers list|cdc-sources list|identify|alias|merge]');
21
30
  process.exit(1);
22
31
  }
23
32
  }
@@ -76,6 +85,81 @@ async function transfersCmd(args) {
76
85
  ].join('\t') + '\n');
77
86
  }
78
87
  }
88
+ async function identifyCmd(args) {
89
+ if (!args.site || !args.userId) {
90
+ (0, ui_1.error)('Usage: gurulu identity identify --site=<id> --user-id=<id> [--email=...] [--phone=...] [--traits=<json>]');
91
+ process.exit(1);
92
+ }
93
+ let traits;
94
+ if (args.traits) {
95
+ try {
96
+ traits = JSON.parse(args.traits);
97
+ }
98
+ catch {
99
+ (0, ui_1.error)('--traits must be valid JSON');
100
+ process.exit(1);
101
+ }
102
+ }
103
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/identify', {
104
+ profile: args.profile,
105
+ method: 'POST',
106
+ json: {
107
+ siteId: args.site,
108
+ userId: args.userId,
109
+ email: args.email,
110
+ phone: args.phone,
111
+ traits,
112
+ },
113
+ });
114
+ if (args.json) {
115
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
116
+ return;
117
+ }
118
+ (0, ui_1.success)(`Identified ${args.userId}.`);
119
+ if (body.canonicalPersonId) {
120
+ process.stdout.write(` canonicalPersonId: ${body.canonicalPersonId}\n`);
121
+ }
122
+ }
123
+ async function aliasCmd(args) {
124
+ if (!args.site || !args.previousUserId || !args.newUserId) {
125
+ (0, ui_1.error)('Usage: gurulu identity alias --site=<id> --previous-user-id=<anon> --new-user-id=<u123>');
126
+ process.exit(1);
127
+ }
128
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/alias', {
129
+ profile: args.profile,
130
+ method: 'POST',
131
+ json: {
132
+ siteId: args.site,
133
+ previousUserId: args.previousUserId,
134
+ newUserId: args.newUserId,
135
+ },
136
+ });
137
+ if (args.json) {
138
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
139
+ return;
140
+ }
141
+ (0, ui_1.success)(`Aliased ${args.previousUserId} → ${args.newUserId}.`);
142
+ }
143
+ async function mergeCmd(args) {
144
+ if (!args.site || !args.canonicalId || !args.duplicateId) {
145
+ (0, ui_1.error)('Usage: gurulu identity merge --site=<id> --canonical-id=<winner> --duplicate-id=<loser>');
146
+ process.exit(1);
147
+ }
148
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/merge', {
149
+ profile: args.profile,
150
+ method: 'POST',
151
+ json: {
152
+ siteId: args.site,
153
+ canonicalId: args.canonicalId,
154
+ duplicateId: args.duplicateId,
155
+ },
156
+ });
157
+ if (args.json) {
158
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
159
+ return;
160
+ }
161
+ (0, ui_1.success)(`Merged ${args.duplicateId} → ${args.canonicalId}.`);
162
+ }
79
163
  async function cdcSourcesCmd(args) {
80
164
  if (args.sub && args.sub !== 'list') {
81
165
  (0, ui_1.error)(`Unknown identity cdc-sources sub: ${args.sub}`);
@@ -22,7 +22,7 @@ async function initCommand(args) {
22
22
  if (!args.framework && !args.noInteractive && framework === 'unknown') {
23
23
  console.log('');
24
24
  (0, ui_1.warn)('Could not auto-detect framework.');
25
- const frameworks = ['nextjs-app', 'nextjs-pages', 'react-vite', 'react-cra', 'vue3', 'nuxt3', 'svelte', 'sveltekit', 'astro', 'express', 'nestjs', 'react-native', 'ios-swift', 'android-kotlin', 'flutter', 'html'];
25
+ const frameworks = ['nextjs-app', 'nextjs-pages', 'react-vite', 'react-cra', 'vue3', 'nuxt3', 'svelte', 'sveltekit', 'astro', 'express', 'fastify', 'hono', 'nestjs', 'react-native', 'ios-swift', 'android-kotlin', 'flutter', 'html'];
26
26
  const idx = await (0, ui_1.promptSelect)(' Select your framework: ', frameworks.map(f => (0, detect_1.getFrameworkDisplayName)(f)));
27
27
  framework = frameworks[idx];
28
28
  (0, ui_1.info)(`Using: ${(0, ui_1.bold)((0, detect_1.getFrameworkDisplayName)(framework))}`);
@@ -46,7 +46,10 @@ export interface InstallArgs {
46
46
  * up with `// TODO: <prop>` placeholders. See Sprint A fix A2.
47
47
  */
48
48
  authToken?: string;
49
+ /** Sprint E1.5 — explicit workspace path inside an npm/pnpm/yarn workspace repo. */
50
+ workspace?: string;
49
51
  }
52
+ export declare function resolveWorkspaceRoot(repoRoot: string, args: InstallArgs, deps: InstallDeps): Promise<string>;
50
53
  export interface IntentPreSeedResult {
51
54
  ok: boolean;
52
55
  proposalId?: string;
@@ -151,6 +154,8 @@ export interface InstallSummary {
151
154
  verify: VerifyResult | null;
152
155
  rolledBack: boolean;
153
156
  errors: string[];
157
+ partiallyInstalled?: boolean;
158
+ installedComponents?: string[];
154
159
  intent?: {
155
160
  skipped: boolean;
156
161
  dryRun: boolean;