ma-agents 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -120,60 +120,113 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
120
120
  const skills = listSkills();
121
121
  const agents = listAgents();
122
122
 
123
+ let selectedSkillIds = preselectedSkill ? [preselectedSkill] : [];
124
+ let selectedAgentIds = preselectedAgents || [];
125
+ let installScope = 'project';
126
+ let installPath = customPath || '';
127
+
128
+ const existingStatus = getStatus([], '', 'project');
129
+ let isUpdate = false;
130
+
131
+ // Step 0: Check for existing installation ONLY in interactive mode (no preselected skill)
132
+ if (!preselectedSkill && !customPath && existingStatus.length > 0) {
133
+ console.log(chalk.cyan('\n Existing installation detected in project.'));
134
+
135
+ const { action } = await prompts({
136
+ type: 'select',
137
+ name: 'action',
138
+ message: 'What would you like to do?',
139
+ choices: [
140
+ { title: 'Update (add/remove skills)', value: 'update' },
141
+ { title: 'Clean reinstall (remove all first)', value: 'reinstall' },
142
+ { title: 'Uninstall all skills', value: 'uninstall' },
143
+ { title: 'Cancel', value: 'cancel' }
144
+ ]
145
+ });
146
+
147
+ if (!action || action === 'cancel') process.exit(0);
148
+
149
+ if (action === 'uninstall') {
150
+ const { confirmed } = await prompts({
151
+ type: 'confirm',
152
+ name: 'confirmed',
153
+ message: 'This will remove all ma-agents skills from this project. Are you sure?',
154
+ initial: false
155
+ });
156
+ if (!confirmed) process.exit(0);
157
+
158
+ for (const entry of existingStatus) {
159
+ for (const skillId of Object.keys(entry.skills)) {
160
+ await uninstallSkill(skillId, [entry.agent.id], '', 'project');
161
+ }
162
+ }
163
+ console.log(chalk.bold.green('\n All skills successfully removed!\n'));
164
+ return;
165
+ }
166
+
167
+ if (action === 'reinstall') {
168
+ for (const entry of existingStatus) {
169
+ for (const skillId of Object.keys(entry.skills)) {
170
+ await uninstallSkill(skillId, [entry.agent.id], '', 'project');
171
+ }
172
+ }
173
+ console.log(chalk.gray(' Clean slate prepared.'));
174
+ }
175
+
176
+ if (action === 'update') {
177
+ isUpdate = true;
178
+ // Pre-populate selections from existing status
179
+ const existingSkillIds = new Set();
180
+ const existingAgentIds = new Set();
181
+ existingStatus.forEach(entry => {
182
+ existingAgentIds.add(entry.agent.id);
183
+ Object.keys(entry.skills).forEach(id => existingSkillIds.add(id));
184
+ });
185
+ selectedSkillIds = Array.from(existingSkillIds);
186
+ selectedAgentIds = Array.from(existingAgentIds);
187
+ }
188
+ }
189
+
123
190
  // Step 1: Select skills
124
- let selectedSkillIds;
125
- if (preselectedSkill) {
126
- selectedSkillIds = [preselectedSkill];
127
- } else {
191
+ if (selectedSkillIds.length === 0 || isUpdate) {
128
192
  const { skills: chosen } = await prompts({
129
193
  type: 'multiselect',
130
194
  name: 'skills',
131
- message: 'Which skills do you want to install?',
195
+ message: 'Select the skills you want to have installed:',
132
196
  choices: skills.map(s => ({
133
197
  title: chalk.white(s.name) + chalk.gray(` v${s.version} - ${s.description}`),
134
198
  value: s.id,
135
- selected: false
199
+ selected: selectedSkillIds.includes(s.id)
136
200
  })),
137
201
  instructions: chalk.gray(' Use space to select, enter to confirm'),
138
202
  min: 1
139
203
  });
140
204
 
141
- if (!chosen || chosen.length === 0) {
142
- console.log(chalk.yellow('No skills selected. Exiting.'));
143
- process.exit(0);
144
- }
205
+ if (!chosen) process.exit(0);
145
206
  selectedSkillIds = chosen;
146
207
  }
147
208
 
148
209
  // Step 2: Select agents
149
- let selectedAgentIds;
150
- if (preselectedAgents && preselectedAgents.length > 0) {
151
- selectedAgentIds = preselectedAgents;
152
- } else {
210
+ if (selectedAgentIds.length === 0 || isUpdate) {
153
211
  const { agents: chosen } = await prompts({
154
212
  type: 'multiselect',
155
213
  name: 'agents',
156
- message: 'Which coding agents do you want to install to?',
214
+ message: 'Select which coding agents to include:',
157
215
  choices: agents.map(a => ({
158
216
  title: chalk.white(a.name) + chalk.gray(` v${a.version} - ${a.description}`),
159
217
  value: a.id,
160
- selected: false
218
+ selected: selectedAgentIds.includes(a.id)
161
219
  })),
162
220
  instructions: chalk.gray(' Use space to select, enter to confirm'),
163
221
  min: 1
164
222
  });
165
223
 
166
- if (!chosen || chosen.length === 0) {
167
- console.log(chalk.yellow('No agents selected. Exiting.'));
168
- process.exit(0);
169
- }
224
+ if (!chosen) process.exit(0);
170
225
  selectedAgentIds = chosen;
171
226
  }
172
227
 
173
- // Step 3: Installation scope
174
- let installPath = customPath || '';
175
- let installScope = 'project';
176
- if (!installPath) {
228
+ // Step 3: Scope (Skip if update, we already know it's project)
229
+ if (!isUpdate && !installPath) {
177
230
  const { pathChoice } = await prompts({
178
231
  type: 'select',
179
232
  name: 'pathChoice',
@@ -209,16 +262,33 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
209
262
  const { confirmed } = await prompts({
210
263
  type: 'confirm',
211
264
  name: 'confirmed',
212
- message: 'Proceed with installation?',
265
+ message: isUpdate ? 'Apply changes?' : 'Proceed with installation?',
213
266
  initial: true
214
267
  });
215
268
 
216
269
  if (!confirmed) {
217
- console.log(chalk.yellow('Installation cancelled.'));
270
+ console.log(chalk.yellow('Operation cancelled.'));
218
271
  process.exit(0);
219
272
  }
220
273
 
221
- // Step 5: Install each skill (with upgrade detection)
274
+ // Step 5: Execution
275
+ if (isUpdate) {
276
+ // Identify removals
277
+ const currentlyInstalled = new Set();
278
+ existingStatus.forEach(entry => Object.keys(entry.skills).forEach(id => currentlyInstalled.add(id)));
279
+
280
+ const finalSkills = new Set(selectedSkillIds);
281
+ const toRemove = [...currentlyInstalled].filter(id => !finalSkills.has(id));
282
+
283
+ if (toRemove.length > 0) {
284
+ console.log(chalk.gray(`\n Cleaning up ${toRemove.length} removed skills...`));
285
+ for (const skillId of toRemove) {
286
+ await uninstallSkill(skillId, selectedAgentIds, installPath, 'project');
287
+ }
288
+ }
289
+ }
290
+
291
+ // Install requested skills
222
292
  for (const skillId of selectedSkillIds) {
223
293
  try {
224
294
  await installSkill(skillId, selectedAgentIds, installPath, installScope, { force: !!forceFlag });
@@ -227,7 +297,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath, fo
227
297
  }
228
298
  }
229
299
 
230
- console.log(chalk.bold.green('\n Installation complete!\n'));
300
+ console.log(chalk.bold.green('\n Done!\n'));
231
301
  }
232
302
 
233
303
  // --- Command handlers ---
package/out.txt ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor)",
5
5
  "main": "index.js",
6
6
  "bin": {
package/skills/README.md CHANGED
@@ -216,6 +216,30 @@ Standardizes structured logging (JSON) across Backend, Frontend, Realtime, and A
216
216
 
217
217
  ---
218
218
 
219
+ ### 7. OpenTelemetry Best Practices
220
+ **Directory:** `opentelemetry-best-practices/`
221
+
222
+ Standardizes distributed tracing, metrics, and semantic conventions for high-quality system observability. Enforces trace context propagation and OTel semantic naming.
223
+
224
+ **Key Features:**
225
+ - ✅ **Standardized Tracing**: Mandates spans for DB calls and external requests.
226
+ - ✅ **Semantic Conventions**: Enforces OTel naming standards.
227
+ - ✅ **Context Propagation**: Ensures trace IDs flow across async boundaries.
228
+
229
+ ---
230
+
231
+ ### 8. C++ Safe Memory Handling
232
+ **Directory:** `cpp-memory-handling/`
233
+
234
+ Enforces Modern C++ practices (RAII, Smart Pointers) to prevent memory leaks and dangling pointers.
235
+
236
+ **Key Features:**
237
+ - ✅ **Smart Pointers**: Mandates `std::unique_ptr` and `std::shared_ptr`.
238
+ - ✅ **RAII**: Ensures every resource is bound to object lifetimes.
239
+ - ✅ **Safe Containers**: Forbids raw buffers in favor of `std::vector` and `std::string`.
240
+
241
+ ---
242
+
219
243
  ## Requirements
220
244
 
221
245
  ### All Skills
@@ -0,0 +1,38 @@
1
+ # C++ Safe Memory Handling
2
+
3
+ Guidelines for writing robust, memory-safe C++ code by leveraging Modern C++ features and RAII principles.
4
+
5
+ ## Mandatory Policies
6
+
7
+ ### 1. Smart Pointers by Default
8
+ You MUST use smart pointers for all heap allocations.
9
+ - Use `std::unique_ptr` for exclusive ownership. This is the preferred default.
10
+ - Use `std::shared_ptr` only when multiple objects share ownership of a resource.
11
+ - Use `std::weak_ptr` to break circular dependencies or for non-owning references that must be checked for validity.
12
+
13
+ ### 2. Forbid raw new/delete
14
+ You MUST NOT use raw `new` and `delete` operators.
15
+ - Use `std::make_unique` (C++14+) or `std::make_shared` to allocate objects on the heap.
16
+ - Exceptions are only allowed within low-level custom container implementations, which must be encapsulated.
17
+
18
+ ### 3. Prefer Value Semantics
19
+ Always prefer stack-allocated objects and value semantics over pointer-based indirection unless heap allocation is strictly necessary (e.g., polymorphism, very large objects, or dynamic lifetimes).
20
+
21
+ ### 4. RAII for All Resources
22
+ Every resource (file handles, sockets, mutexes, memory) MUST be managed by an object whose constructor acquires the resource and destructor releases it. Never rely on manual cleanup blocks.
23
+
24
+ ### 5. Standard Containers
25
+ NEVER use raw buffers or manually managed arrays.
26
+ - Use `std::vector` for dynamic arrays.
27
+ - Use `std::string` for text.
28
+ - Use `std::array` (C++11) for fixed-size stack arrays.
29
+ - Access elements using `.at()` if bounds checking is required for security-critical logic.
30
+
31
+ ## Critical Rules
32
+ - **No Dangling Pointers**: Never return a pointer or reference to a local stack-allocated variable.
33
+ - **Rule of Zero/Five**: Follow the "Rule of Zero" (prefer components that handle their own resource management). If manual management is needed, correctly implement the "Rule of Five".
34
+ - **Nullable Checks**: Always check pointers (even smart ones) for `nullptr` before dereferencing if there is any chance they could be empty.
35
+
36
+ ## Resources
37
+ - [Modern C++ Examples](file:///skills/cpp-memory-handling/examples/modern-cpp.md)
38
+ - [Smart Pointer Reference](file:///skills/cpp-memory-handling/examples/smart-pointers.md)
@@ -0,0 +1,49 @@
1
+ # Modern C++ Safe Memory Examples
2
+
3
+ Demonstrating RAII and safe resource management in modern C++.
4
+
5
+ ## RAII Resource Management
6
+
7
+ ```cpp
8
+ #include <iostream>
9
+ #include <fstream>
10
+ #include <vector>
11
+ #include <memory>
12
+
13
+ class FileHandler {
14
+ public:
15
+ FileHandler(const std::string& filename) : file(filename) {
16
+ if (!file.is_open()) throw std::runtime_error("Could not open file");
17
+ }
18
+ // Destructor automatically closes the file
19
+ ~FileHandler() { if (file.is_open()) file.close(); }
20
+
21
+ void write(const std::string& data) { file << data; }
22
+
23
+ private:
24
+ std::ofstream file;
25
+ };
26
+
27
+ void log_data(const std::string& message) {
28
+ // RAII handles lifetime. No manual close needed.
29
+ FileHandler handler("app.log");
30
+ handler.write(message);
31
+ }
32
+ ```
33
+
34
+ ## Vector vs Raw Arrays
35
+
36
+ ```cpp
37
+ // BAD: Manual memory management
38
+ void bad_array() {
39
+ int* arr = new int[100];
40
+ // ... logic ...
41
+ delete[] arr; // Manual cleanup is error-prone
42
+ }
43
+
44
+ // GOOD: Automatic memory management
45
+ void safe_vector() {
46
+ std::vector<int> arr(100);
47
+ // Vector manages its own heap memory. RAII cleans up at scope exit.
48
+ }
49
+ ```
@@ -0,0 +1,46 @@
1
+ # C++ Smart Pointer Reference
2
+
3
+ Guidelines for selecting and using smart pointers correctly.
4
+
5
+ ## unique_ptr (Preferred Default)
6
+ - Represents **exclusive ownership**.
7
+ - Zero runtime overhead compared to raw pointers.
8
+
9
+ ```cpp
10
+ #include <memory>
11
+
12
+ class Widget { /* ... */ };
13
+
14
+ void exclusive_use() {
15
+ // Use make_unique for safety and efficiency
16
+ auto widget = std::make_unique<Widget>();
17
+
18
+ // Ownership can be moved but not copied
19
+ std::unique_ptr<Widget> other = std::move(widget);
20
+ }
21
+ ```
22
+
23
+ ## shared_ptr (For Shared Ownership)
24
+ - Use only when **multiple** owners truly exist.
25
+ - Reference counting has small overhead.
26
+
27
+ ```cpp
28
+ void shared_use() {
29
+ auto shared_widget = std::make_shared<Widget>();
30
+
31
+ auto owner1 = shared_widget; // Count = 2
32
+ auto owner2 = shared_widget; // Count = 3
33
+ }
34
+ ```
35
+
36
+ ## weak_ptr (Breaking Cycles)
37
+ - Non-owning reference. Must be locked before use.
38
+
39
+ ```cpp
40
+ void weak_use(std::weak_ptr<Widget> weak_widget) {
41
+ if (auto shared = weak_widget.lock()) {
42
+ // Resource is still alive
43
+ shared->do_something();
44
+ }
45
+ }
46
+ ```
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "C++ Safe Memory Handling",
3
+ "description": "Enforces Modern C++ practices (RAII, Smart Pointers) to prevent memory leaks, dangling pointers, and buffer overflows.",
4
+ "version": "1.0.0",
5
+ "author": "Antigravity",
6
+ "tags": [
7
+ "cpp",
8
+ "memory-management",
9
+ "raii",
10
+ "smart-pointers",
11
+ "cplusplus",
12
+ "security"
13
+ ],
14
+ "applies_when": [
15
+ "writing c++ code",
16
+ "modifying memory-intensive C++ logic",
17
+ "debugging memory leaks in c++",
18
+ "implementing new c++ classes"
19
+ ]
20
+ }
@@ -0,0 +1,30 @@
1
+ # OpenTelemetry Best Practices
2
+
3
+ Guidelines for implementing standardized distributed tracing and metrics using OpenTelemetry to ensure high-quality observability.
4
+
5
+ ## Mandatory Policies
6
+
7
+ ### 1. Semantic Conventions
8
+ You MUST use OpenTelemetry Semantic Conventions for all attribute names. Never invent custom names for standard concepts (e.g., use `http.method` instead of `method` or `http_method`).
9
+
10
+ ### 2. Context Propagation
11
+ You MUST ensure trace context is propagated across asynchronous boundaries. When starting a background task or making a network call, ensure the active span is correctly parented or the context is injected into headers.
12
+
13
+ ### 3. Span Granularity
14
+ - **Database**: Every query MUST have its own span with attributes for the statement (sanitized) and database name.
15
+ - **External API**: Every outgoing request MUST have a span.
16
+ - **Complex Logic**: Large internal computation blocks SHOULD have spans if they represent a distinct logical step.
17
+
18
+ ### 4. Meaningful Metrics
19
+ - **Counters**: Use for discrete events (e.g., `api.requests.total`).
20
+ - **Histograms**: Use for durations and sizes (e.g., `api.latency`).
21
+ - **Attributes**: Common attributes (e.g., `status_code`, `service.name`) SHOULD be applied to both spans and metrics.
22
+
23
+ ## Critical Rules
24
+ - **No Sensitive Data**: Never include PII, passwords, or tokens in span attributes or logs associated with spans.
25
+ - **Fail Gracefully**: Instrumentation should never crash the application. Use `tracer.startActiveSpan` carefully with `try...finally` to ensure spans are always ended.
26
+ - **Status Codes**: Always set the span status to `Error` when an exception is caught that isn't handled.
27
+
28
+ ## Resources
29
+ - [Semantic Conventions Reference](file:///skills/opentelemetry-best-practices/references/otel-standards.md)
30
+ - [Example Implementations](file:///skills/opentelemetry-best-practices/examples/javascript.md)
@@ -0,0 +1,32 @@
1
+ # OpenTelemetry Go Examples
2
+
3
+ Idiomatic usage of the OpenTelemetry Go SDK.
4
+
5
+ ## Context-Aware Tracing
6
+
7
+ ```go
8
+ import (
9
+ "context"
10
+ "go.opentelemetry.io/otel"
11
+ "go.opentelemetry.io/otel/attribute"
12
+ "go.opentelemetry.io/otel/codes"
13
+ )
14
+
15
+ var tracer = otel.Tracer("my-service")
16
+
17
+ func (s *Server) GetUser(ctx context.Context, id string) (*User, error) {
18
+ ctx, span := tracer.Start(ctx, "GetUser")
19
+ defer span.End()
20
+
21
+ span.SetAttributes(attribute.String("user.id", id))
22
+
23
+ user, err := s.db.FetchUser(ctx, id)
24
+ if err != nil {
25
+ span.RecordError(err)
26
+ span.SetStatus(codes.Error, err.Error())
27
+ return nil, err
28
+ }
29
+
30
+ return user, nil
31
+ }
32
+ ```
@@ -0,0 +1,58 @@
1
+ # OpenTelemetry JavaScript (Node.js) Examples
2
+
3
+ Idiomatic usage of the OpenTelemetry SDK in Node.js applications.
4
+
5
+ ## Tracing a Function
6
+
7
+ ```javascript
8
+ const { trace } = require('@opentelemetry/api');
9
+
10
+ const tracer = trace.getTracer('my-service');
11
+
12
+ async function processOrder(orderId) {
13
+ // Use startActiveSpan to automatically handle context propagation
14
+ return tracer.startActiveSpan('process.order', async (span) => {
15
+ try {
16
+ // Set semantic attributes
17
+ span.setAttribute('order.id', orderId);
18
+
19
+ const result = await someDatabaseCall(orderId);
20
+
21
+ span.setStatus({ code: SpanStatusCode.OK });
22
+ return result;
23
+ } catch (error) {
24
+ span.recordException(error);
25
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
26
+ throw error;
27
+ } finally {
28
+ span.end();
29
+ }
30
+ });
31
+ }
32
+ ```
33
+
34
+ ## Creating Metrics
35
+
36
+ ```javascript
37
+ const { metrics } = require('@opentelemetry/api');
38
+
39
+ const meter = metrics.getMeter('my-service');
40
+
41
+ const requestCounter = meter.createCounter('api.requests.total', {
42
+ description: 'Total number of API requests',
43
+ });
44
+
45
+ const latencyHistogram = meter.createHistogram('api.latency', {
46
+ description: 'API request latency',
47
+ unit: 'ms',
48
+ });
49
+
50
+ function handleRequest(req) {
51
+ const startTime = Date.now();
52
+
53
+ // Logic...
54
+
55
+ requestCounter.add(1, { 'http.method': req.method, 'status_code': 200 });
56
+ latencyHistogram.record(Date.now() - startTime, { 'http.method': req.method });
57
+ }
58
+ ```
@@ -0,0 +1,37 @@
1
+ # OpenTelemetry Python Examples
2
+
3
+ Idiomatic usage of the OpenTelemetry Python SDK.
4
+
5
+ ## Tracing with Decorators
6
+
7
+ ```python
8
+ from opentelemetry import trace
9
+
10
+ tracer = trace.get_tracer(__name__)
11
+
12
+ @tracer.start_as_current_span("process_data")
13
+ def process_data(data):
14
+ span = trace.get_current_span()
15
+ span.set_attribute("data.size", len(data))
16
+
17
+ # Process...
18
+ return True
19
+ ```
20
+
21
+ ## Manual Span Management
22
+
23
+ ```python
24
+ def fetch_external_resource(url):
25
+ with tracer.start_as_current_span("http_request") as span:
26
+ span.set_attribute("http.url", url)
27
+ span.set_attribute("http.method", "GET")
28
+
29
+ try:
30
+ response = requests.get(url)
31
+ span.set_attribute("http.status_code", response.status_code)
32
+ return response.json()
33
+ except Exception as e:
34
+ span.record_exception(e)
35
+ span.set_status(trace.Status(trace.StatusCode.ERROR))
36
+ raise
37
+ ```
@@ -0,0 +1,37 @@
1
+ # OpenTelemetry Standards & Semantic Conventions
2
+
3
+ This document outlines the specific attributes and naming conventions to follow when instrumenting services.
4
+
5
+ ## Common Attributes
6
+
7
+ | Attribute | Description | Example |
8
+ |-----------|-------------|---------|
9
+ | `service.name` | Logical name of the service | `user-auth-service` |
10
+ | `service.version` | Version of the service | `1.2.3` |
11
+ | `deployment.environment` | Target environment | `production` |
12
+
13
+ ## HTTP Semantic Conventions
14
+
15
+ | Attribute | Description | Example |
16
+ |-----------|-------------|---------|
17
+ | `http.method` | HTTP request method | `GET` |
18
+ | `http.status_code` | response status | `200` |
19
+ | `http.url` | Full request URL | `https://api.example.com/v1/users` |
20
+ | `http.user_agent` | User agent header | `Mozilla/5.0...` |
21
+
22
+ ## Database Semantic Conventions
23
+
24
+ | Attribute | Description | Example |
25
+ |-----------|-------------|---------|
26
+ | `db.system` | Database vendor | `postgresql` |
27
+ | `db.statement` | Sanitized SQL/query | `SELECT * FROM users WHERE id = ?` |
28
+ | `db.name` | Database name | `prod_db` |
29
+ | `db.operation` | Operation name | `SELECT` |
30
+
31
+ ## Span Status Values
32
+ - **Unset**: Default status.
33
+ - **Ok**: Explicitly marked as successful.
34
+ - **Error**: Encountered a failure. Describe the error in the `exception` event or attributes.
35
+
36
+ ## Useful links
37
+ - [Official OTel Semantic Conventions](https://opentelemetry.io/docs/specs/otel/common/semantic-conventions/)
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "OpenTelemetry Best Practices",
3
+ "description": "Standardizes distributed tracing, metrics, and semantic conventions for high-quality system observability.",
4
+ "version": "1.0.0",
5
+ "author": "Antigravity",
6
+ "tags": [
7
+ "opentelemetry",
8
+ "observability",
9
+ "tracing",
10
+ "metrics",
11
+ "distributed-systems"
12
+ ],
13
+ "applies_when": [
14
+ "implementing api endpoints",
15
+ "handling distributed requests",
16
+ "optimizing performance",
17
+ "adding monitoring"
18
+ ]
19
+ }