From 128f140a036942b374f12e2df5ca946dadcc8993 Mon Sep 17 00:00:00 2001 From: Riccardo Carlesso Date: Mon, 20 Apr 2026 14:08:19 +0200 Subject: [PATCH 1/3] feat: extract and weaponize ASCII sparklines --- skills/cloud-monitoring/CHANGELOG.md | 6 + skills/cloud-monitoring/SKILL.md | 4 +- .../scripts/export_timeseries_to_csv.py | 14 ++- skills/gcp-mcp-setup/TODO.md.dontcommit | 1 + skills/monitoring-graphs/CHANGELOG.md | 4 + skills/monitoring-graphs/SKILL.md | 2 +- skills/monitoring-graphs/TODO.md | 1 + .../scripts/csv_to_sparkline.py | 111 ++++++++++++++++++ skills/postmortem-generator/TODO copy.md | 1 + skills/postmortem-generator/TODO copy2.md | 1 + skills/pre-publish-checker/SKILL.md | 52 ++++++++ .../references/checklist_template.md | 18 +++ .../pre-publish-checker/scripts/check_file.py | 75 ++++++++++++ .../scripts/summarize_checklist.py | 57 +++++++++ 14 files changed, 338 insertions(+), 9 deletions(-) create mode 120000 skills/gcp-mcp-setup/TODO.md.dontcommit create mode 120000 skills/monitoring-graphs/TODO.md create mode 100755 skills/monitoring-graphs/scripts/csv_to_sparkline.py create mode 120000 skills/postmortem-generator/TODO copy.md create mode 120000 skills/postmortem-generator/TODO copy2.md create mode 100644 skills/pre-publish-checker/SKILL.md create mode 100644 skills/pre-publish-checker/references/checklist_template.md create mode 100755 skills/pre-publish-checker/scripts/check_file.py create mode 100755 skills/pre-publish-checker/scripts/summarize_checklist.py diff --git a/skills/cloud-monitoring/CHANGELOG.md b/skills/cloud-monitoring/CHANGELOG.md index 525a691..4f5a2b2 100644 --- a/skills/cloud-monitoring/CHANGELOG.md +++ b/skills/cloud-monitoring/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this skill will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.17] - 2026-04-20 +### Changed +- Replaced 'gist' terminology with 'Sparkline' and 'ASCII Art'. +- Added pipe `|` wrappers around the generated sparklines to ensure empty spaces are visibly framed. +- Delegated standalone CSV usage to the new `csv_to_sparkline.py` script hosted in `monitoring-graphs`. + ## [0.2.16] - 2026-04-16 ### Added - Rephrased gist the 'graphical' part. diff --git a/skills/cloud-monitoring/SKILL.md b/skills/cloud-monitoring/SKILL.md index f7bfef0..bed562a 100644 --- a/skills/cloud-monitoring/SKILL.md +++ b/skills/cloud-monitoring/SKILL.md @@ -1,7 +1,7 @@ --- name: cloud-monitoring description: 🐉 Skill for interacting with Google Cloud Monitoring (CM) via APIs to avoid large context bloat. Produces nice short synoptic "gists" of graphs -version: 0.2.16 +version: 0.2.17 tools: - mcp_google-monitoring_get_alert - mcp_google-monitoring_get_alert_policy @@ -36,5 +36,5 @@ This skill provides utilities for analyzing and extracting data from Google Clou ## Available Tools * `scripts/export_timeseries_to_csv.py`: Fetches time-series data for specified metric(s) and time range, outputting a CSV file with metadata headers. Supports extracting two or more variables for direct correlation and comparison. This is an amazing synoptic you can surface to the user - * It's very effective particularly to share the "**gist**" / **Shape** part showing use in TEXT the graphical shape of the curve, eg "█▇▆▇ ▂▃ ▂ ▂ ▂ ▂" or "█▆▃▂▄▅▅_▄▄▄▂▄▄█▆". This is very useful to humans for visual feedback! + * It's very effective particularly to share the "**Sparkline**" / **ASCII Art** part showing in TEXT the graphical shape of the curve (e.g., `|█▇▆▇ ▂▃ ▂ ▂|`). This is very useful for humans for rapid visual feedback! Use `csv_to_sparkline.py` from `monitoring-graphs` to recreate it for arbitrary CSVs. * `scripts/setup-frontend-slo.sh`: A bash script to automatically set up a 99.9% availability SLO using Log-based Metrics for a 'frontend' service. (See `scripts/README.md` for full manual). diff --git a/skills/cloud-monitoring/scripts/export_timeseries_to_csv.py b/skills/cloud-monitoring/scripts/export_timeseries_to_csv.py index ad14761..c9cad91 100755 --- a/skills/cloud-monitoring/scripts/export_timeseries_to_csv.py +++ b/skills/cloud-monitoring/scripts/export_timeseries_to_csv.py @@ -91,8 +91,9 @@ def generate_sparkline(vals, num_bins=16): return "▄" * len(binned) normalized = np.round((binned - vmin) / (vmax - vmin) * 7).astype(int) - chars = ['_', '▂', '▃', '▄', '▅', '▆', '▇', '█'] - return "".join([chars[i] for i in normalized]) + chars = [' ', '▂', '▃', '▄', '▅', '▆', '▇', '█'] + shape_str = "".join([chars[i] for i in normalized]) + return f"|{shape_str}|" def main(): epilog_text = """ @@ -255,7 +256,7 @@ def main(): stats_summary[metric_name] = { "count": len(vals), - "shape": generate_sparkline(vals, num_bins=args.num_bins), + "sparkline": generate_sparkline(vals, num_bins=args.num_bins), "min": vals[min_idx], "min_ts": metric_points[min_idx]["timestamp"], "min_pos": get_position_label(min_idx, len(vals)), @@ -291,7 +292,8 @@ def main(): max_time = s['max_ts'][11:19] print(f"\nMetric: {m}") - print(f" Shape: {s['shape']}") + print(f" Sparkline:{s['sparkline']}") + print(f" To gen: uv run ../monitoring-graphs/scripts/csv_to_sparkline.py --csv {args.output} --values-column-name value") print(f" Count: {s['count']}") print(f" Average: {s['avg']:.4f}") print(f" Variance: {s['var']:.4f}") @@ -299,7 +301,7 @@ def main(): print(f" Maximum: [{max_time}] {s['max']:.4f} ({s['max_pos']})") # Write to CSV header - csvfile.write(f"# stats_{m}_shape: {s['shape']}\n") + csvfile.write(f"# stats_{m}_sparkline: {s['sparkline']}\n") csvfile.write(f"# stats_{m}_avg: {s['avg']}\n") csvfile.write(f"# stats_{m}_min: [{min_time}] {s['min']} ({s['min_pos']})\n") csvfile.write(f"# stats_{m}_max: [{max_time}] {s['max']} ({s['max_pos']})\n") @@ -310,7 +312,7 @@ def main(): print("SYNOPTIC COMPARISON") print("="*40) for m, s in stats_summary.items(): - print(f"{s['shape']} {m}") + print(f"{s['sparkline']} {m}") print("="*40) csvfile.write(f"# metadata_point_count: {len(all_data_points)}\n") diff --git a/skills/gcp-mcp-setup/TODO.md.dontcommit b/skills/gcp-mcp-setup/TODO.md.dontcommit new file mode 120000 index 0000000..02ff7a4 --- /dev/null +++ b/skills/gcp-mcp-setup/TODO.md.dontcommit @@ -0,0 +1 @@ +/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/gcp-mcp-setup/TODO.md.dontcommit \ No newline at end of file diff --git a/skills/monitoring-graphs/CHANGELOG.md b/skills/monitoring-graphs/CHANGELOG.md index 123c7c1..16ff4dc 100644 --- a/skills/monitoring-graphs/CHANGELOG.md +++ b/skills/monitoring-graphs/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the `monitoring-graphs` skill will be documented in this file. +## [0.0.6] - 2026-04-20 +### Added +- Added `scripts/csv_to_sparkline.py` script extracted from `cloud-monitoring`. This generic script allows you to auto-generate an ASCII sparkline graphic wrapped in `|` for any given CSV numeric column. You can specify `--values-column-name` explicitly. + ## [0.0.5] - 2026-04-07 ### 📊 The "Data Integrity & Aesthetics" Update diff --git a/skills/monitoring-graphs/SKILL.md b/skills/monitoring-graphs/SKILL.md index f0a8a89..cf7b8ed 100644 --- a/skills/monitoring-graphs/SKILL.md +++ b/skills/monitoring-graphs/SKILL.md @@ -4,7 +4,7 @@ description: 🐉 skill for generating high-quality, annotated incident graphs f #verbose_description: Expert SRE skill for generating high-quality, annotated incident graphs for post-mortems using Python and Monitoring MCP on Google Cloud. Use this when the user needs to visualize an outage, show error rates, or correlate metrics with incident milestones (start, detection, mitigation, end). metadata: author: Riccardo Carlesso - version: 0.0.5 + version: 0.0.6 --- # 📈 Monitoring & Incident Graphing Skill 📊 diff --git a/skills/monitoring-graphs/TODO.md b/skills/monitoring-graphs/TODO.md new file mode 120000 index 0000000..3029d7d --- /dev/null +++ b/skills/monitoring-graphs/TODO.md @@ -0,0 +1 @@ +/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/monitoring-graphs/TODO.md \ No newline at end of file diff --git a/skills/monitoring-graphs/scripts/csv_to_sparkline.py b/skills/monitoring-graphs/scripts/csv_to_sparkline.py new file mode 100755 index 0000000..806caaa --- /dev/null +++ b/skills/monitoring-graphs/scripts/csv_to_sparkline.py @@ -0,0 +1,111 @@ +#!/usr/bin/env -S uv run +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "pandas", +# "numpy", +# "python-dateutil" +# ] +# /// + +import argparse +import sys +import numpy as np +import pandas as pd + +def generate_sparkline(vals, num_bins=16, wrapper="|"): + """ + Generates an ASCII sparkline from a sequence of values. + Uses ' ' (space) for the lowest/empty bucket to ensure empty spots are visible when framed. + """ + if len(vals) == 0: + return f"{wrapper}{wrapper}" + if len(vals) < num_bins: + num_bins = len(vals) + + splits = np.array_split(vals, num_bins) + binned = np.array([np.mean(s) for s in splits if len(s) > 0]) + + vmin, vmax = np.min(binned), np.max(binned) + if vmin == vmax: + shape_str = "▄" * len(binned) + return f"{wrapper}{shape_str}{wrapper}" + + normalized = np.round((binned - vmin) / (vmax - vmin) * 7).astype(int) + # Replaced lowest char '_' with ' ' (space) as requested for better framing visibility + chars = [' ', '▂', '▃', '▄', '▅', '▆', '▇', '█'] + shape_str = "".join([chars[i] for i in normalized]) + return f"{wrapper}{shape_str}{wrapper}" + +def main(): + parser = argparse.ArgumentParser(description="Generates an ASCII sparkline from a CSV file.") + parser.add_argument("--csv", required=True, help="Path to the input CSV data") + parser.add_argument("--values-column-name", dest="values_column", required=True, help="Column name containing the numeric values.") + parser.add_argument("--time-column-name", dest="time_column", required=False, help="(Optional) Time column name. Will try to auto-infer if not provided.") + parser.add_argument("--bins", type=int, default=16, help="Number of bins (characters) in the sparkline.") + parser.add_argument("--wrapper", type=str, default="|", help="Wrapper character around the sparkline (default: |).") + args = parser.parse_args() + + try: + df = pd.read_csv(args.csv) + except Exception as e: + print(f"Error reading CSV: {e}", file=sys.stderr) + sys.exit(1) + + if args.values_column not in df.columns: + print(f"Error: Column '{args.values_column}' not found in CSV. Available columns: {', '.join(df.columns)}", file=sys.stderr) + sys.exit(1) + + # Autoinfer time column if not provided + time_col = args.time_column + if not time_col: + # Check heuristics for time column + time_keywords = ['time', 'timestamp', 'date', 'datetime'] + for col in df.columns: + if any(k in col.lower() for k in time_keywords): + time_col = col + break + + # If still not found, try to see if the first column can be parsed as datetime, or just use the first column + if not time_col and len(df.columns) > 0: + potential_col = df.columns[0] + if potential_col != args.values_column: + try: + pd.to_datetime(df[potential_col][:5]) + time_col = potential_col + except: + # just pick the first column if no time column is found, as long as it's not the value! + time_col = potential_col + + if time_col and time_col in df.columns: + try: + # Sort by the inferred or explicit time column + df[time_col] = pd.to_datetime(df[time_col]) + df = df.sort_values(by=time_col) + except Exception: + pass # ignore sorting if it's not actually a time parsing compatible column + + values = df[args.values_column].dropna().values + if len(values) == 0: + print(f"Error: No valid numeric data found in column '{args.values_column}'.", file=sys.stderr) + sys.exit(1) + + sparkline = generate_sparkline(values, num_bins=args.bins, wrapper=args.wrapper) + print(sparkline) + +if __name__ == "__main__": + main() diff --git a/skills/postmortem-generator/TODO copy.md b/skills/postmortem-generator/TODO copy.md new file mode 120000 index 0000000..80315b1 --- /dev/null +++ b/skills/postmortem-generator/TODO copy.md @@ -0,0 +1 @@ +/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/postmortem-generator/TODO copy.md \ No newline at end of file diff --git a/skills/postmortem-generator/TODO copy2.md b/skills/postmortem-generator/TODO copy2.md new file mode 120000 index 0000000..0d19758 --- /dev/null +++ b/skills/postmortem-generator/TODO copy2.md @@ -0,0 +1 @@ +/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/postmortem-generator/TODO copy2.md \ No newline at end of file diff --git a/skills/pre-publish-checker/SKILL.md b/skills/pre-publish-checker/SKILL.md new file mode 100644 index 0000000..f6c2e2a --- /dev/null +++ b/skills/pre-publish-checker/SKILL.md @@ -0,0 +1,52 @@ +--- +name: pre-publish-checker +description: Thoroughly checks files for profanity, internal links, sensitive paths, and professionalism before publication. Use when preparing a codebase or extension for public release. +--- + +# Pre-Publish Checker + +This skill guides you through a thorough audit of your project to ensure it meets publication standards. It combines automated script-based checks with a manual review workflow. + +## Policies + +1. **Do Not Simply Remove TODOs**: Do not delete `TODO` markers just to make the check pass. Instead: + * **Action them**: Resolve the task described. + * **Move them**: Transfer the `TODO` to a tracking system (e.g., GitHub Issues) and replace the marker with a link to the issue. +2. **User Bypass**: Any flagged issue can be bypassed if there is a valid reason. In `PUBLICATION_CHECKLIST.md`, mark the status as `[Pass]` and include the keyword `USER_BYPASS: `. + +## Workflow + +### 1. Initialization + +- List all relevant files: Markdown (.md), code files (.py, .js, .go, etc.), `justfile`, and `README`s. +- Create a `PUBLICATION_CHECKLIST.md` in the project root by copying the template from `references/checklist_template.md`. +- Populate the file list in the checklist. + +### 2. Automated Audit +For each file in the checklist, run the automated check script: +```bash +uv run -s skills/pre-publish-checker/scripts/check_file.py +``` +*Note: If `uv` is not available, use `python3`.* + +### 3. Iterative Review +- Process files one by one (or in small batches). +- Update the `PUBLICATION_CHECKLIST.md` with the status and any issues found. +- Fix issues as they are identified. +- For "long work", you can resume the process by reading the current state of `PUBLICATION_CHECKLIST.md`. +- **Get a Synopsis**: At any point, you can generate a quick summary of remaining work by running: + ```bash + uv run -s skills/pre-publish-checker/scripts/summarize_checklist.py + ``` + *(Defaults to reading `PUBLICATION_CHECKLIST.md`)* + +### 4. Final Verification +- Perform manual checks for "Google-bar professionalism" and overall quality. +- Ensure all boxes in `PUBLICATION_CHECKLIST.md` are checked. + +## Check Categories + +1. **Profanity**: Scans for common offensive language. +2. **Internal Links**: Finds internal shortlinks (e.g., `go/` links) which are not accessible outside. +3. **Internal Paths**: Identifies local machine or company-specific paths that reveal internal infrastructure. +4. **Professionalism**: Checks for development markers (e.g., "to-do", "fix-me") that should be resolved before release. diff --git a/skills/pre-publish-checker/references/checklist_template.md b/skills/pre-publish-checker/references/checklist_template.md new file mode 100644 index 0000000..d4b4caa --- /dev/null +++ b/skills/pre-publish-checker/references/checklist_template.md @@ -0,0 +1,18 @@ +# Publication Checklist + +## Automated Checks +- [ ] No profanity +- [ ] No internal `go/` links +- [ ] No internal paths (`/google/`, `/usr/local/google/`) +- [ ] Professionalism (No TODOs, FIXMEs) + +## Manual Verification +- [ ] Meets Google-bar professionalism +- [ ] Clear and concise documentation +- [ ] No sensitive credentials or secrets + +## File List and Status +| File Path | Status | Issues Found / Bypass Reason | +|-----------|--------|------------------------------| +| [Example] | [Pass] | USER_BYPASS: Internal link required for docs | +| [Example] | [Pending] | | diff --git a/skills/pre-publish-checker/scripts/check_file.py b/skills/pre-publish-checker/scripts/check_file.py new file mode 100755 index 0000000..7177316 --- /dev/null +++ b/skills/pre-publish-checker/scripts/check_file.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +import sys +import re +import os + +# Basic list of common profanities (to be expanded if needed) +PROFANITY_LIST = [ + r'\bshit\b', r'\bfuck\b', r'\bdamn\b', r'\bass\b', r'\bbitch\b' +] + +# Patterns for internal links and paths +INTERNAL_PATTERNS = [ + (r'\bgo/[a-zA-Z0-9\-/]+\b', "Internal go/ link found"), + (r'/google/', "Internal /google/ path found"), + (r'/usr/local/google/', "Internal /usr/local/google/ path found"), +] + +def check_file(file_path): + issues = [] + if not os.path.exists(file_path): + return [f"File not found: {file_path}"] + + try: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + lines = content.splitlines() + + for i, line in enumerate(lines): + if 'pre-publish-checker: ignore' in line: + continue + line_no = i + 1 + + # 1. Profanity check + for pattern in PROFANITY_LIST: + matches = re.finditer(pattern, line, re.IGNORECASE) + for match in matches: + issues.append(f"Line {line_no}: Potential profanity found: '{match.group()}'") + + # 2. Internal links and paths + for pattern, message in INTERNAL_PATTERNS: + matches = re.finditer(pattern, line) + for match in matches: + issues.append(f"Line {line_no}: {message}: '{match.group()}'") + + # 3. Professionalism (TODO, FIXME, etc.) + prof_patterns = [ + (r'\bTODO\b', "TODO found"), + (r'\bFIXME\b', "FIXME found"), + (r'\bHACK\b', "HACK found"), + (r'\bXXX\b', "XXX found"), + ] + for pattern, message in prof_patterns: + matches = re.finditer(pattern, line) + for match in matches: + issues.append(f"Line {line_no}: {message}") + + except Exception as e: + return [f"Error reading file: {str(e)}"] + + return issues + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: check_file.py ") + sys.exit(1) + + file_to_check = sys.argv[1] + results = check_file(file_to_check) + + if results: + print(f"Issues found in {file_to_check}:") + for issue in results: + print(f" - {issue}") + else: + print(f"No issues found in {file_to_check}.") diff --git a/skills/pre-publish-checker/scripts/summarize_checklist.py b/skills/pre-publish-checker/scripts/summarize_checklist.py new file mode 100755 index 0000000..9ef0922 --- /dev/null +++ b/skills/pre-publish-checker/scripts/summarize_checklist.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +import sys +import os +import re + +def summarize(file_path): + if not os.path.exists(file_path): + print(f"Error: Checklist file '{file_path}' not found.") + sys.exit(1) + + pending_files = [] + failed_files = [] + + # Match markdown table rows like: | ./file.md | [Fail] | Reason | + row_pattern = re.compile(r'^\|\s*(.*?)\s*\|\s*\[(.*?)\]\s*\|\s*(.*?)\s*\|') + + with open(file_path, 'r', encoding='utf-8') as f: + for line in f: + match = row_pattern.match(line.strip()) + if match: + filepath, status, reason = match.groups() + + # Skip the header separator line + if '---' in filepath: + continue + + status_lower = status.strip().lower() + if status_lower == 'pending': + pending_files.append(filepath.strip()) + elif status_lower == 'fail': + failed_files.append((filepath.strip(), reason.strip())) + + print(f"=== Publication Synopsis for '{file_path}' ===\n") + + if not failed_files and not pending_files: + print("✅ All clear! No pending or failed items found. You are ready to publish.") + return + + if failed_files: + print(f"❌ FAILED CHECKS ({len(failed_files)}):") + print("These items have flagged issues that need to be resolved or bypassed.") + for fp, reason in failed_files: + print(f" - {fp}") + print(f" Reason: {reason}") + print("") + + if pending_files: + print(f"⏳ PENDING REVIEWS ({len(pending_files)}):") + print("These items have not yet been evaluated.") + for fp in pending_files: + print(f" - {fp}") + print("") + +if __name__ == "__main__": + # Default to PUBLICATION_CHECKLIST.md if no argument is provided + target_file = sys.argv[1] if len(sys.argv) > 1 else "PUBLICATION_CHECKLIST.md" + summarize(target_file) From 651002f6f2cbf14a37c226ed2ea1726c99dda4af Mon Sep 17 00:00:00 2001 From: Riccardo Carlesso Date: Mon, 20 Apr 2026 14:08:40 +0200 Subject: [PATCH 2/3] fix: remove accidentally added WIP files from PR --- skills/gcp-mcp-setup/TODO.md.dontcommit | 1 - skills/monitoring-graphs/TODO.md | 1 - skills/postmortem-generator/TODO copy.md | 1 - skills/postmortem-generator/TODO copy2.md | 1 - skills/pre-publish-checker/SKILL.md | 52 ------------- .../references/checklist_template.md | 18 ----- .../pre-publish-checker/scripts/check_file.py | 75 ------------------- .../scripts/summarize_checklist.py | 57 -------------- 8 files changed, 206 deletions(-) delete mode 120000 skills/gcp-mcp-setup/TODO.md.dontcommit delete mode 120000 skills/monitoring-graphs/TODO.md delete mode 120000 skills/postmortem-generator/TODO copy.md delete mode 120000 skills/postmortem-generator/TODO copy2.md delete mode 100644 skills/pre-publish-checker/SKILL.md delete mode 100644 skills/pre-publish-checker/references/checklist_template.md delete mode 100755 skills/pre-publish-checker/scripts/check_file.py delete mode 100755 skills/pre-publish-checker/scripts/summarize_checklist.py diff --git a/skills/gcp-mcp-setup/TODO.md.dontcommit b/skills/gcp-mcp-setup/TODO.md.dontcommit deleted file mode 120000 index 02ff7a4..0000000 --- a/skills/gcp-mcp-setup/TODO.md.dontcommit +++ /dev/null @@ -1 +0,0 @@ -/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/gcp-mcp-setup/TODO.md.dontcommit \ No newline at end of file diff --git a/skills/monitoring-graphs/TODO.md b/skills/monitoring-graphs/TODO.md deleted file mode 120000 index 3029d7d..0000000 --- a/skills/monitoring-graphs/TODO.md +++ /dev/null @@ -1 +0,0 @@ -/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/monitoring-graphs/TODO.md \ No newline at end of file diff --git a/skills/postmortem-generator/TODO copy.md b/skills/postmortem-generator/TODO copy.md deleted file mode 120000 index 80315b1..0000000 --- a/skills/postmortem-generator/TODO copy.md +++ /dev/null @@ -1 +0,0 @@ -/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/postmortem-generator/TODO copy.md \ No newline at end of file diff --git a/skills/postmortem-generator/TODO copy2.md b/skills/postmortem-generator/TODO copy2.md deleted file mode 120000 index 0d19758..0000000 --- a/skills/postmortem-generator/TODO copy2.md +++ /dev/null @@ -1 +0,0 @@ -/usr/local/google/home/ricc/git/gic/private/projects/git-privatize/github.com__gemini-cli-extensions__sre/skills/postmortem-generator/TODO copy2.md \ No newline at end of file diff --git a/skills/pre-publish-checker/SKILL.md b/skills/pre-publish-checker/SKILL.md deleted file mode 100644 index f6c2e2a..0000000 --- a/skills/pre-publish-checker/SKILL.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: pre-publish-checker -description: Thoroughly checks files for profanity, internal links, sensitive paths, and professionalism before publication. Use when preparing a codebase or extension for public release. ---- - -# Pre-Publish Checker - -This skill guides you through a thorough audit of your project to ensure it meets publication standards. It combines automated script-based checks with a manual review workflow. - -## Policies - -1. **Do Not Simply Remove TODOs**: Do not delete `TODO` markers just to make the check pass. Instead: - * **Action them**: Resolve the task described. - * **Move them**: Transfer the `TODO` to a tracking system (e.g., GitHub Issues) and replace the marker with a link to the issue. -2. **User Bypass**: Any flagged issue can be bypassed if there is a valid reason. In `PUBLICATION_CHECKLIST.md`, mark the status as `[Pass]` and include the keyword `USER_BYPASS: `. - -## Workflow - -### 1. Initialization - -- List all relevant files: Markdown (.md), code files (.py, .js, .go, etc.), `justfile`, and `README`s. -- Create a `PUBLICATION_CHECKLIST.md` in the project root by copying the template from `references/checklist_template.md`. -- Populate the file list in the checklist. - -### 2. Automated Audit -For each file in the checklist, run the automated check script: -```bash -uv run -s skills/pre-publish-checker/scripts/check_file.py -``` -*Note: If `uv` is not available, use `python3`.* - -### 3. Iterative Review -- Process files one by one (or in small batches). -- Update the `PUBLICATION_CHECKLIST.md` with the status and any issues found. -- Fix issues as they are identified. -- For "long work", you can resume the process by reading the current state of `PUBLICATION_CHECKLIST.md`. -- **Get a Synopsis**: At any point, you can generate a quick summary of remaining work by running: - ```bash - uv run -s skills/pre-publish-checker/scripts/summarize_checklist.py - ``` - *(Defaults to reading `PUBLICATION_CHECKLIST.md`)* - -### 4. Final Verification -- Perform manual checks for "Google-bar professionalism" and overall quality. -- Ensure all boxes in `PUBLICATION_CHECKLIST.md` are checked. - -## Check Categories - -1. **Profanity**: Scans for common offensive language. -2. **Internal Links**: Finds internal shortlinks (e.g., `go/` links) which are not accessible outside. -3. **Internal Paths**: Identifies local machine or company-specific paths that reveal internal infrastructure. -4. **Professionalism**: Checks for development markers (e.g., "to-do", "fix-me") that should be resolved before release. diff --git a/skills/pre-publish-checker/references/checklist_template.md b/skills/pre-publish-checker/references/checklist_template.md deleted file mode 100644 index d4b4caa..0000000 --- a/skills/pre-publish-checker/references/checklist_template.md +++ /dev/null @@ -1,18 +0,0 @@ -# Publication Checklist - -## Automated Checks -- [ ] No profanity -- [ ] No internal `go/` links -- [ ] No internal paths (`/google/`, `/usr/local/google/`) -- [ ] Professionalism (No TODOs, FIXMEs) - -## Manual Verification -- [ ] Meets Google-bar professionalism -- [ ] Clear and concise documentation -- [ ] No sensitive credentials or secrets - -## File List and Status -| File Path | Status | Issues Found / Bypass Reason | -|-----------|--------|------------------------------| -| [Example] | [Pass] | USER_BYPASS: Internal link required for docs | -| [Example] | [Pending] | | diff --git a/skills/pre-publish-checker/scripts/check_file.py b/skills/pre-publish-checker/scripts/check_file.py deleted file mode 100755 index 7177316..0000000 --- a/skills/pre-publish-checker/scripts/check_file.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -import sys -import re -import os - -# Basic list of common profanities (to be expanded if needed) -PROFANITY_LIST = [ - r'\bshit\b', r'\bfuck\b', r'\bdamn\b', r'\bass\b', r'\bbitch\b' -] - -# Patterns for internal links and paths -INTERNAL_PATTERNS = [ - (r'\bgo/[a-zA-Z0-9\-/]+\b', "Internal go/ link found"), - (r'/google/', "Internal /google/ path found"), - (r'/usr/local/google/', "Internal /usr/local/google/ path found"), -] - -def check_file(file_path): - issues = [] - if not os.path.exists(file_path): - return [f"File not found: {file_path}"] - - try: - with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: - content = f.read() - lines = content.splitlines() - - for i, line in enumerate(lines): - if 'pre-publish-checker: ignore' in line: - continue - line_no = i + 1 - - # 1. Profanity check - for pattern in PROFANITY_LIST: - matches = re.finditer(pattern, line, re.IGNORECASE) - for match in matches: - issues.append(f"Line {line_no}: Potential profanity found: '{match.group()}'") - - # 2. Internal links and paths - for pattern, message in INTERNAL_PATTERNS: - matches = re.finditer(pattern, line) - for match in matches: - issues.append(f"Line {line_no}: {message}: '{match.group()}'") - - # 3. Professionalism (TODO, FIXME, etc.) - prof_patterns = [ - (r'\bTODO\b', "TODO found"), - (r'\bFIXME\b', "FIXME found"), - (r'\bHACK\b', "HACK found"), - (r'\bXXX\b', "XXX found"), - ] - for pattern, message in prof_patterns: - matches = re.finditer(pattern, line) - for match in matches: - issues.append(f"Line {line_no}: {message}") - - except Exception as e: - return [f"Error reading file: {str(e)}"] - - return issues - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: check_file.py ") - sys.exit(1) - - file_to_check = sys.argv[1] - results = check_file(file_to_check) - - if results: - print(f"Issues found in {file_to_check}:") - for issue in results: - print(f" - {issue}") - else: - print(f"No issues found in {file_to_check}.") diff --git a/skills/pre-publish-checker/scripts/summarize_checklist.py b/skills/pre-publish-checker/scripts/summarize_checklist.py deleted file mode 100755 index 9ef0922..0000000 --- a/skills/pre-publish-checker/scripts/summarize_checklist.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -import sys -import os -import re - -def summarize(file_path): - if not os.path.exists(file_path): - print(f"Error: Checklist file '{file_path}' not found.") - sys.exit(1) - - pending_files = [] - failed_files = [] - - # Match markdown table rows like: | ./file.md | [Fail] | Reason | - row_pattern = re.compile(r'^\|\s*(.*?)\s*\|\s*\[(.*?)\]\s*\|\s*(.*?)\s*\|') - - with open(file_path, 'r', encoding='utf-8') as f: - for line in f: - match = row_pattern.match(line.strip()) - if match: - filepath, status, reason = match.groups() - - # Skip the header separator line - if '---' in filepath: - continue - - status_lower = status.strip().lower() - if status_lower == 'pending': - pending_files.append(filepath.strip()) - elif status_lower == 'fail': - failed_files.append((filepath.strip(), reason.strip())) - - print(f"=== Publication Synopsis for '{file_path}' ===\n") - - if not failed_files and not pending_files: - print("✅ All clear! No pending or failed items found. You are ready to publish.") - return - - if failed_files: - print(f"❌ FAILED CHECKS ({len(failed_files)}):") - print("These items have flagged issues that need to be resolved or bypassed.") - for fp, reason in failed_files: - print(f" - {fp}") - print(f" Reason: {reason}") - print("") - - if pending_files: - print(f"⏳ PENDING REVIEWS ({len(pending_files)}):") - print("These items have not yet been evaluated.") - for fp in pending_files: - print(f" - {fp}") - print("") - -if __name__ == "__main__": - # Default to PUBLICATION_CHECKLIST.md if no argument is provided - target_file = sys.argv[1] if len(sys.argv) > 1 else "PUBLICATION_CHECKLIST.md" - summarize(target_file) From 2dc1c7380219dc136e05e493d9f920c1548bd5e0 Mon Sep 17 00:00:00 2001 From: Riccardo Carlesso Date: Mon, 20 Apr 2026 14:19:24 +0200 Subject: [PATCH 3/3] Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- skills/monitoring-graphs/scripts/csv_to_sparkline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/skills/monitoring-graphs/scripts/csv_to_sparkline.py b/skills/monitoring-graphs/scripts/csv_to_sparkline.py index 806caaa..45985dd 100755 --- a/skills/monitoring-graphs/scripts/csv_to_sparkline.py +++ b/skills/monitoring-graphs/scripts/csv_to_sparkline.py @@ -60,9 +60,13 @@ def main(): parser.add_argument("--wrapper", type=str, default="|", help="Wrapper character around the sparkline (default: |).") args = parser.parse_args() + try: + df = pd.read_csv(args.csv) try: df = pd.read_csv(args.csv) except Exception as e: + print(f"Error reading CSV '{args.csv}': {e}", file=sys.stderr) + sys.exit(1) print(f"Error reading CSV: {e}", file=sys.stderr) sys.exit(1)