chore(instrumentation): add persona analytics extension#6927
chore(instrumentation): add persona analytics extension#6927octavian-snyk wants to merge 1 commit into
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This comment has been minimized.
This comment has been minimized.
|
|
||
| // getInteractiveMode builds an InteractiveMode bitmask using check to test | ||
| // each standard stream. | ||
| func getInteractiveMode(check func(*os.File) bool) InteractiveMode { |
There was a problem hiding this comment.
Question: The PR description mentions this code is unchanged but the file seems different.
There was a problem hiding this comment.
The persona.interactive key remains the same (true only if stdin is a TTY) here. The function simply generates a bitmask for all streams and records it in persona.interactive_mode.
I'm happy to adjust this based on your preference. I could:
- Keep it as is
- Rename it to be clearer
- Drop it completely
- Add an IsInteractive function (taking InteractiveMode and returning InteractiveMode & StdinTTY) rather than leaving it inline in persona.go.
Please let me know what you think is best 😅
87bd584 to
62c0c58
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Why are agent* and interactive* in the package persona? Without looking down below, I have the feeling that agent, interactive and persona have their own (complex?) internal logic.
Wouldn't it be cleaner to have them in separate packages and offer just the necessary resources, to isolate such domain specific logic? (i.e. avoiding tight coupling between them)
| const ( | ||
| StdinTTY InteractiveMode = 1 << iota // stdin is attached to a terminal | ||
| StdoutTTY // stdout is attached to a terminal | ||
| StderrTTY // stderr is attached to a terminal | ||
| ) |
There was a problem hiding this comment.
I am not sure if I follow what is happening here, how is it that those constants (even those not initialised) are attached to a terminal? - do you mind to explain? :-)
There was a problem hiding this comment.
🤣 i will change the comments
62c0c58 to
4335d94
Compare
|
|
||
| func report(a Analytics, mode InteractiveMode, detect func() (Agent, bool)) { | ||
| interactive := mode&StdinTTY != 0 | ||
| if a.IsCiEnvironment() { |
There was a problem hiding this comment.
There was a problem hiding this comment.
please add a comment to document it in code
This comment has been minimized.
This comment has been minimized.
| @@ -0,0 +1,37 @@ | |||
| package persona | |||
There was a problem hiding this comment.
nit: probably this file should be named analytics.go, or something different. I cannot see any persona type in the whole PR. Not blocker
| } | ||
|
|
||
| func report(a Analytics, mode InteractiveMode, detect func() (Agent, bool)) { | ||
| interactive := mode&StdinTTY != 0 |
There was a problem hiding this comment.
this line is scary - do we have any other way to check this apart from bit operations? For sake of simplicity
There was a problem hiding this comment.
please document the bitmask
There was a problem hiding this comment.
I added a Has method, and I documented the bitmask on InteractiveMode definition and on keyInteractiveMode definition. Please let me know if you'd like it be documented somewhere else additionally.
| } | ||
|
|
||
| // Report adds the persona extension values to the given analytics sink. | ||
| func Report(a Analytics) { |
There was a problem hiding this comment.
I would prefer to use a receiver here and in report() - you are even modifying the a (look in lines 32 to 35)
There was a problem hiding this comment.
That would be nice, but main.go will probably end up looking the same
To clarify the approach, I set up Analytics as a local interface to capture a subset of GAF interface methods for testing.
4335d94 to
c704163
Compare
PR Reviewer Guide 🔍
|
Pull Request Submission Checklist
are release-note ready, emphasizing
what was changed, not how.
What does this PR do?
Extends CLI instrumentation with a dedicated persona package that reports how the CLI is invoked and, when detectable, which AI coding agent is driving the run.
Changes:
New cliv2/internal/persona package — centralises persona analytics reporting via persona.Report().
Richer interactive telemetry — moves TTY detection out of cliv2/internal/utils and adds a per-stream bitmask:
persona.interactive (bool) — unchanged: true when stdin is a TTY
persona.interactive_mode (int) — new: bitmask of stdin (1), stdout (2), and stderr (4) TTY attachment
AI agent detection — new optional analytics field persona.agent when an agent is recognised from environment/filesystem signals.
Wiring — cliv2/pkg/core/main.go now calls persona.Report(cliAnalytics) instead of inline AddExtensionBoolValue("persona.interactive", ...).
Removed: cliv2/internal/utils/interactive.go and its tests (logic moved into persona).
Where should the reviewer start?
How should this be manually tested?
Run the built binary with
-dusing Cursor, Gemini, Claude Code, and other agents you have available, and verify thepersona.agentoutput.What's the product update that needs to be communicated to CLI users?
N/A
What are the relevant tickets?
CLI-1604
Risk assessment (Low | Medium | High)?
Low