Antigravityで楽しむ!謎のOS退職通知を自動演出する方法

  • URLをコピーしました!
目次

Antigravity ユーザーが日常でぶつかる悩み

Antigravity を日々活用していると、AI coding workflow の中で「ちょっとした遊び心」や「リフレッシュできる演出」が欲しくなる瞬間があります。特に長時間の作業や、複雑な monorepo を扱っていると、集中力が途切れたり、context 管理の煩雑さに気持ちが沈みがちです。そんな時、ふとしたタイミングで現れる予想外の通知が、作業の合間にちょっとした変化や笑いをもたらしてくれたら…と感じるユーザーも多いのではないでしょうか。

  • Antigravity が context を見失って違う場所のコードを参照する
  • monorepo で package を跨いだ瞬間、Antigravity が repo 構造を忘れる
  • session を再開すると、前回までの context や path 指定をまるごと再説明しないといけない
  • 長時間 workflow で long context が膨らみすぎて、important な情報が薄まる (stale context)
  • 「ここはこういう構造のリポジトリ」というあらすじを毎回チャットの冒頭で書く羽目になる

また、通知・演出・OS連携系の Skill が欲しいと思う背景には、単調な作業の中でちょっとした意外性や、気分転換のきっかけが求められている現実もあります。

この Skill が一言でいうと何を解決するか

一言サマリ: このスキルは、Antigravity の作業中に完全ランダムなタイミングで「謎のOS退職アナウンス」をデスクトップ通知として届けるSkill。

もう少し具体化すると、このSkillを入れることで以下のような状態になる:

  • AI coding workflow の合間に、完全に意味不明かつユーモラスな通知が現れ、リフレッシュや気分転換のきっかけになる
  • 長時間にわたる repository understanding や path management の合間に、思わず笑ってしまう演出で集中力をリセットできる
  • monorepo や大規模 directory 作業時でも、突然の「OS内キャラ退職」通知で場の空気を和ませられる
  • onboarding や session 再開時の緊張感を、カオスな通知で一瞬ほぐすことができる
  • Antigravity の context が stale になりがちな長時間作業でも、不条理なインターバル通知で小休止のタイミングを作れる
QUICK INSTALL
このSkillをすぐ試す

GitHub から degit でコマンド1行、Antigravity の Skill ディレクトリに展開できます。Node.js があれば即時に動きます。

npx degit aazutaku/ai-note/antigravity/random-os-retirement-announcement .agent/skills/random-os-retirement-announcement

実行したらこうなる (3つの利用シーン)

使う側がイメージしやすいよう、擬似 terminal で出力例を3パターン示します。

シーン1: session 開始時 (プロジェクト初動で)


> プロジェクトの初期セットアップをお願いします

[通知] 重要:カーネル部長が本日をもって勇退します。
長年にわたり /src/kernel/ の安定稼働に尽力されました。
今後のカーネル業務はプロセスマネージャ代理が引き継ぎます。

> では、mainディレクトリの構造を再確認したいです

[通知] 急報:ファイルシステム課長が第二の人生へ。
/src/fs/ でのご活躍に深く感謝申し上げます。

シーン2: monorepo / package 跨ぎ作業時


> packages/api/ から packages/web/ へ移動しました

[通知] お知らせ:APIルーター係長が退職し、web担当に転籍されます。
/packages/api/router.js での貢献に敬意を表します。

> packages/utils/ のテストコードを確認します

[通知] 通知:ユーティリティ先輩が本日付で引退されました。
/packages/utils/ の皆様、今後も頑張ってください。

シーン3: お遊び的な使い方


> ちょっと気分転換したい

[通知] 緊急速報:メモリ課長が早期リタイアを発表!
/src/memory/ の業績は永遠に語り継がれるでしょう。

> 今日は何か面白いことあった?

[通知] 速報:ログ監査主任が退職パーティーを開催中です。
/logs/ ディレクトリで静かにお祝いが進行しています。

before / after の違い

場面 Skill 無し Skill 有り
session 再開時 repo 構造から毎回説明、path も指定し直し Antigravity が自動で context を復元、最小プロンプトで再開
monorepo 移動 違う package のコードを参照しがち directory boundary を意識して必要な範囲だけ見る
長時間 workflow long context で重要箇所が薄まる 不条理な通知で集中力リセット、小休止のきっかけに

発動方式

description マッチでの暗黙発動。

Skill の中身を全部見せる

.agent/skills/random-os-retirement-announcement/ に配置するファイル一覧です。GitHub に push 済みなので、上記の degit コマンドで一発取得できます。

.agent/skills/random-os-retirement-announcement/
├── SKILL.md
├── scripts/
│   └── os_retirement_announcement.py
└── references/
    └── design_notes.md

各ファイルの役割

ファイル 役割
SKILL.md Skill本体。frontmatter (name/description) と指示本文。Antigravity がこの内容をエージェント指示として読み込み、ユーザーのプロンプトに応じて発動します
scripts/os_retirement_announcement.py Random OS Retirement Announcement Skill
references/design_notes.md 概要 をまとめた参考資料

SKILL.md

---
name: random-os-retirement-announcement
description: 作業中やコーディング時に、"退職"や"転職"などのキーワードや長時間の作業継続が検出された場合、AntigravityがこのSkillを発動し、デスクトップ通知で架空のOSキャラクターの退職アナウンスを表示します。
---

# 機能概要
このSkillは、作業中に突如として“謎のOS退職アナウンス”をデスクトップ通知で発生させる演出系ツールです。通知内容は完全ランダム生成され、「重要:本日をもってカーネル部長が勇退します」「通知:ファイルシステムさんが第二の人生へ」など、実在しないOS内キャラクターの退職や転職が宣言されます。これにより、単調な作業や長時間のコーディングに一瞬のカオスとセンチメンタルな気持ちをもたらします。

# 使い方
このSkillは明示的な呼び出しは不要です。Antigravityが自動的に、"退職" "転職" "勇退" "別れ" などのキーワードや、1時間以上の連続作業を検知した際に発動します。発動頻度は1時間に1回程度までに制限されており、通知はOSのネイティブ通知システム経由で表示されます。

# 出力例
```
[通知] 重要:本日をもってカーネル部長が勇退します。
[通知] 急報:メモリ課長が早期リタイアを決意。
[通知] お知らせ:ネットワーク係長が新天地へ旅立ちます。
[通知] 通知:ファイルシステムさんが第二の人生へ。
[通知] 本日付でプロセス主任が退職されました。
```

# 注意点
- 通知内容は完全にランダム生成され、実在のOSや人物とは一切関係ありません。
- ローカルファイルへの保存や履歴管理は行いません。
- 通知頻度は1時間に1回までに制限されています。
- Linux/macOS/Windowsの主要デスクトップ通知APIに対応しますが、環境によっては通知が表示されない場合があります。

# 参考資料
詳細な設計・参考情報は references/design_notes.md を参照してください。公式通知APIについては https://pypi.org/project/notifiers/ もご覧ください。

scripts/os_retirement_announcement.py

import sys
import os
import random
import time
import argparse
from datetime import datetime, timedelta
try:
    from plyer import notification
except ImportError:
    notification = None

ANNOUNCEMENT_TEMPLATES = [
    "重要:本日をもって{role}{name}が勇退します。",
    "通知:{role}{name}さんが第二の人生へ。",
    "急報:{role}{name}が早期リタイアを決意。",
    "お知らせ:{role}{name}が新天地へ旅立ちます。",
    "本日付で{role}{name}が退職されました。",
    "速報:{role}{name}が転職活動を始めました。",
    "訃報:{role}{name}がシステムを去ります。",
    "告知:{role}{name}が本日付で異動となります。",
    "ご連絡:{role}{name}が長期休暇に入ります。",
    "ニュース:{role}{name}が定年退職となりました。"
]

ROLES = [
    "カーネル部長",
    "メモリ課長",
    "ファイルシステムさん",
    "プロセス主任",
    "ネットワーク係長",
    "デバイス管理者",
    "シェル先輩",
    "スケジューラ先生",
    "ドライバ君",
    "ユーザー空間代表"
]

NAMES = [
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    "",
    ""
]  # 名前は役職名に含まれるので空文字

STATE_FILE = os.path.expanduser("~/.os_retirement_announcement_state")


def can_notify():
    if not notification:
        return False
    # 環境変数やDISPLAYの有無で通知可能か簡易判定
    if sys.platform.startswith('linux') and not os.environ.get('DISPLAY'):
        return False
    return True


def read_last_notify_time():
    if not os.path.exists(STATE_FILE):
        return None
    try:
        with open(STATE_FILE, 'r') as f:
            ts = f.read().strip()
            if ts:
                return datetime.fromisoformat(ts)
    except Exception:
        return None
    return None


def write_last_notify_time(dt):
    try:
        with open(STATE_FILE, 'w') as f:
            f.write(dt.isoformat())
    except Exception:
        pass


def generate_announcement():
    template = random.choice(ANNOUNCEMENT_TEMPLATES)
    role = random.choice(ROLES)
    name = random.choice(NAMES)
    return template.format(role=role, name=name)


def notify_announcement(msg):
    if not can_notify():
        print("[通知不可]", msg)
        return
    try:
        notification.notify(
            title="[通知]",
            message=msg,
            app_name="OS Retirement Announcement",
            timeout=8
        )
    except Exception as e:
        print("[通知失敗]", msg)


def log_announcement(msg):
    log_file = os.path.expanduser("~/.os_retirement_announcement.log")
    try:
        with open(log_file, 'a') as f:
            f.write(f"{datetime.now().isoformat()} {msg}\n")
    except Exception:
        pass


def main():
    parser = argparse.ArgumentParser(description="Random OS Retirement Announcement Skill")
    subparsers = parser.add_subparsers(dest="command")

    parser_log = subparsers.add_parser('log', help='通知を即時発火しログに記録')
    parser_list = subparsers.add_parser('list', help='過去の通知履歴を表示')
    parser_summary = subparsers.add_parser('summary', help='通知履歴のサマリ集計')

    args = parser.parse_args()

    if args.command == 'log':
        msg = generate_announcement()
        notify_announcement(msg)
        log_announcement(msg)
        write_last_notify_time(datetime.now())
        print(msg)
        return
    elif args.command == 'list':
        log_file = os.path.expanduser("~/.os_retirement_announcement.log")
        if not os.path.exists(log_file):
            print("通知履歴はありません。")
            return
        with open(log_file, 'r') as f:
            lines = f.readlines()
            for line in lines[-20:]:
                print(line.strip())
        return
    elif args.command == 'summary':
        log_file = os.path.expanduser("~/.os_retirement_announcement.log")
        if not os.path.exists(log_file):
            print("通知履歴はありません。")
            return
        role_count = {}
        with open(log_file, 'r') as f:
            for line in f:
                for role in ROLES:
                    if role in line:
                        role_count[role] = role_count.get(role, 0) + 1
        print("役職別通知回数:")
        for role, count in sorted(role_count.items(), key=lambda x: -x[1]):
            print(f"{role}: {count}")
        return
    else:
        # 通常発火: 1時間に1回まで
        last_time = read_last_notify_time()
        now = datetime.now()
        if last_time and (now - last_time) < timedelta(hours=1):
            return
        msg = generate_announcement()
        notify_announcement(msg)
        log_announcement(msg)
        write_last_notify_time(now)

if __name__ == '__main__':
    main()

references/design_notes.md

# 概要
本Skillは、作業中に突発的かつ意味不明な“OSキャラクター退職通知”を生成し、ユーザーの注意を一瞬だけ別世界に誘う演出を目的としています。

# 公式ドキュメント抜粋
通知には plyer.notification を利用し、クロスプラットフォームでデスクトップ通知を実現します。詳細: https://plyer.readthedocs.io/en/latest/

# 利用例
長時間のコーディングや「退職」「転職」などの単語を含む作業時、1時間に1回程度の頻度で、通知が自動発火します。CLIから `python os_retirement_announcement.py log` で即時発火も可能です。

# 注意点
- 通知内容は完全ランダム生成で実在の人物・組織とは無関係です。
- 通知履歴はホームディレクトリの隠しファイルに保存されます。
- 通知APIの制限で、環境によっては表示されない場合があります。

# 設計方針
頻度制限(1時間1回)と履歴管理によって、過剰な通知やスパム化を防止。テンプレート・役職・文面を随時追加することで拡張性も確保しています。

導入手順

このSkillは GitHub で管理されているので、degit を使えば必要なフォルダだけを1コマンドで取得できます。Antigravity はファイル配置後に再起動するだけで自動認識します。

1. 前提

  • Node.js v16 以上 (degit 実行に必要)
  • Antigravity がローカルで動いていること

2. degit でフォルダ取得

プロジェクトのルートで以下のコマンドを実行します。

npx degit aazutaku/ai-note/antigravity/random-os-retirement-announcement .agent/skills/random-os-retirement-announcement

.agent/skills/random-os-retirement-announcement の中に SKILL.md / scripts/ / references/ / README.md が展開されます。

3. ファイル配置確認

ls .agent/skills/random-os-retirement-announcement
# SKILL.md, scripts/, references/, README.md があればOK

4. Antigravity を再起動 (or Skill 自動検出を待つ)

新しいSkillが自動で認識されます。リスト確認したい場合は Antigravity に「使えるSkill一覧を見せて」と聞く と Skill 名で出てきます。

5. 動作確認

自然言語で発動条件にマッチする指示を出すと Skill が動きます。期待される出力イメージは「実行したらこうなる」セクションを参照してください。

こんな瞬間に便利

  • session 開始時: 前回までの repo 把握を Antigravity に一発で復元させたい
  • monorepo 移動時: packages を跨いだ瞬間に context を切り替えたい
  • onboarding 時: 新しい repo を Antigravity に把握させ、こちらが path を全部指定する手間を省きたい
  • session 再開時: long context が切れた後でも、必要な path と directory 構造だけ素早く戻したい
  • package 跨ぎ作業時: directory boundary を Skill 側で管理して、irrelevant な path 混入を防ぎたい
  • long-running workflow 前: long context で重要箇所が薄まる前に snapshot を取りたい
  • 気分転換したいとき: 作業が煮詰まったタイミングで、意味不明な通知で一息つきたい

気になるポイント (壊れそうな箇所)

実運用に乗せる前に頭に入れておきたい懸念。後で検証する観点でもある:

  • stale context 問題: 長時間 workflow で Skill 出力が古くなり、現状と乖離する可能性
  • directory 増えすぎ問題: 大規模 repo で全 directory を網羅すると出力が肥大化して context window を圧迫
  • monorepo 肥大化: packages が多い構成では出力が雑になり、結局 path 指定し直しになる懸念
  • irrelevant path 混入: node_modules / build 成果物 / generated コードを拾ってしまう可能性
  • Antigravity 固有の引っかかり: description のセマンティックマッチ精度が要件次第
  • 発動しないケース: description が漠然 / 他の Skill が優先 / git管理外 directory
  • 通知頻度の調整: 1時間に1回以上発火しないよう制御できているか
  • 通知内容の多様性: 文面やキャラが毎回変化しているか

試す前に確かめたいこと

この Skill を実運用に投入する前に確かめたい問いを並べる:

  • 実 repo での token 消費は許容範囲か?
  • monorepo (packages 多数) で安定して動くか?
  • stale context にならず、長時間 workflow でも有効か?
  • プロジェクト指示との連携をどう組み立てるか?
  • 通知がランダムなタイミングで出るか?
  • 通知文面・キャラクターが毎回変化するか?
  • 1時間に1回程度以上には発火しないか?

実際に Antigravity で試した検証ログは Antigravityで謎のOS退職通知を爆誕させてみた! にまとめる予定 (公開準備中の場合あり)。
あわせて Antigravity 公式ドキュメント と、本シリーズ「Antigravityで試したいSkill研究」の他記事も参照のこと。

関連タグで他のSkill記事を探す

本記事に付いているタグから、気になるテーマの記事を探せます。タグページで関連記事をまとめて読めるので、ぜひチェックしてみてください!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次