Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(telemetry): notify users of telemetry collection on first run by Hweinstock · Pull Request #10 · Hweinstock/agentcore-cli · GitHub
Skip to content

feat(telemetry): notify users of telemetry collection on first run - #10

Closed
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice
Closed

feat(telemetry): notify users of telemetry collection on first run#10
Hweinstock wants to merge 4 commits into
refactorfrom
feat/telemetry-first-run-notice

Conversation

@Hweinstock

@HweinstockHweinstock commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Notify users of telemetry collection (and how to opt out) on the first run of the CLI — when no installationId has yet been persisted to ~/.agentcore/config.json. The notice is printed to stderr and never shown again once an installationId exists.

Spec

Problem: The user is never notified of telemetry collection, or of ways to opt out.

Definition of Done:

  • On fresh install (no installationId in ~/.agentcore/config.json), print a notice that the CLI collects usage analytics, mirroring the message style of src/cli/notices.ts on main.
  • The message must NOT display on installs where an installationId already exists.
  • A unit test exercises both paths.

Base branch: refactor.

Implementation

First-run detection is done explicitly at the root; the config accessor no longer mutates config as a side effect.

  • installationId defaults to unset.DEFAULT_GLOBAL_CONFIG.installationId is undefined and GlobalConfig.installationId is optional. DefaultGlobalConfigAccessor.get() no longer generates or persists an id.
  • Root check (src/index.ts): const isFirstRun = config.installationId === undefined. On first run, generate crypto.randomUUID() and persist it via globalConfigAccessor.set(...) (best-effort) before routing — telemetry validates installation_id as a required UUID, so a value must exist by the time metrics emit. The notice is then printed in the finally block.
  • src/telemetry/notice.ts: a single printFirstRunNotice(isFirstRun, out).

Fallout from making the default id unset

  • Removed two config.test.tsx tests that asserted the previous accessor auto-population — that behavior is intentionally relocated to the root (still exercised end-to-end below).
  • Three telemetry tests now supply an explicit installationId (they model an already-installed CLI, whose resource attributes require a UUID).

Verification

Automated

  • bun test2557 pass, 0 fail.
  • bun run typecheck — clean.
  • bun run lint:check — clean.

Manual (end-to-end, isolated HOME)

$ TH=$(mktemp -d)
# Run 1 — fresh install: notice on stderr, config.json created with an installationId
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout>
# stderr:
The AgentCore CLI collects aggregated, anonymous usage analytics to help improve the tool.
To opt out: agentcore config telemetry.enabled false
To audit: agentcore config telemetry.audit true
$ cat "$TH/.agentcore/config.json"
{ "installationId": "<uuid>", ... }
# Run 2 — installationId now exists: no notice
$ HOME="$TH" bun run src/index.ts config telemetry.enabled
<command output on stdout, no notice on stderr>

Notice count on stderr — Run 1: 1, Run 2: 0.

How to reproduce

git fetch fork feat/telemetry-first-run-notice && git checkout feat/telemetry-first-run-notice
bun install
bun test src/telemetry/notice.test.ts
TH=$(mktemp -d); HOME="$TH" bun run src/index.ts config telemetry.enabled # prints notice on stderr
HOME="$TH" bun run src/index.ts config telemetry.enabled # silent
rm -rf "$TH"

@HweinstockHweinstock left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets simplify this approach. Lets just remove the logic that globalConfigAccessor populates the installationId as a side effect, then ecplicitly at the root level check,

does installationId exist in global config?

  • if no, set it to a random uuid, and set isFirstRun (a local variable) to true.
  • if yes, do nothing.

This will require updating the default installationId to undefined.

Comment threadsrc/index.ts Outdated
exit_reason: "success",
});

// Capture first-run state up front: routing and telemetry emission both read

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these useless comments. Also the isFirstRun should still return true, even if the installationId was written as a side effect.

Comment threadsrc/testing/globalConfig.tsx Outdated

/**
* In-memory GlobalConfigAccessor for tests.
* In-memory GlobalConfigAccessor for tests. Represents an already-installed

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to change this comment.

Comment threadsrc/telemetry/notice.ts Outdated
}

/** Writes the telemetry notice to the given stream, but only on a first run. */
export function printFirstRunNotice(isFirstRun: boolean, out: { write(text: string): void }): void {

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just inline the function above here, and only have one function.

@@ -0,0 +1,34 @@
import { test, describe, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need one test, a test.each that checks verifies isFirstRun prints, and isFirstRun false doesn't.

Comment threadsrc/globalConfig/types.tsx Outdated
set(newConfig: GlobalConfig): Promise<GlobalConfig>;
/**
* Returns true on the first run of the CLI, i.e. when no installationId has
* yet been persisted to the config file. Reflects the state before {@link get}

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just replace this with Returns true on the first run of the CLI on a new machine.

Comment threadsrc/globalConfig/accessor.tsx Outdated
public async isFirstRun(): Promise<boolean> {
if (this.firstRun !== undefined) return this.firstRun;

// A first run is one where no installationId has been persisted yet. A

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid all these comments.

Comment threadsrc/globalConfig/accessor.test.tsx Outdated
@@ -0,0 +1,69 @@
import { test, describe, beforeEach, afterEach, expect } from "bun:test";

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these tests.

Comment threadsrc/globalConfig/accessor.tsx Outdated

const configFileData = await this.readConfigFile();

// capture first-run state before we populate an installationId below, so a

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove these comments

Comment threadsrc/globalConfig/config.tsx Outdated

/**
* Default values for the global config. Includes a unique installationId for each process.
* Default values for the global config. installationId is unset by default and

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the installationId part of this comment.

Comment threadsrc/index.ts Outdated
try {
await globalConfigAccessor.set({ ...globalConfig, installationId: crypto.randomUUID() });
} catch {
// best effort

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the error here, using the pattern below.

@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from 1e8a58f to cfb9da2CompareSeptember 1, 2026 00:25
@Hweinstock
Hweinstockforce-pushed the feat/telemetry-first-run-notice branch from cfb9da2 to 8254946CompareSeptember 1, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Hweinstock