Commit fad470ff8685
Changed files (47)
dots
config
claude
skills
.archive
Art
workflows
Journal
Notes
CORE
Jira
tools
workflows
Org
TODOs
dots/config/claude/skills/.archive/Android/workflows/Build.md
@@ -1,1162 +0,0 @@
-# Build Workflow
-
-Build Android applications and Go libraries for Android, manage dependencies with Gradle, and publish to Google Play Store.
-
-## Quick Start
-
-```bash
-# Navigate to project
-cd /path/to/android-project
-
-# Build debug APK
-./gradlew assembleDebug
-
-# Build release APK
-./gradlew assembleRelease
-
-# Build release AAB (for Play Store)
-./gradlew bundleRelease
-
-# Install debug on device
-./gradlew installDebug
-```
-
-## Build Types
-
-### APK (Android Package)
-- Direct install on devices
-- Can be shared and sideloaded
-- Larger file size (contains all ABIs)
-- Use for: Development, testing, direct distribution
-
-### AAB (Android App Bundle)
-- Play Store distribution format
-- Smaller download size (dynamic delivery)
-- Cannot be directly installed
-- Use for: Play Store releases
-
-## Gradle Fundamentals
-
-### Project Structure
-
-```
-myproject/
-├── gradle/
-│ ├── libs.versions.toml # Version catalog (recommended)
-│ └── wrapper/
-│ ├── gradle-wrapper.jar
-│ └── gradle-wrapper.properties
-├── app/
-│ └── build.gradle.kts # App module build file
-├── build.gradle.kts # Root build file
-├── settings.gradle.kts # Project settings
-├── gradle.properties # Global Gradle properties
-└── local.properties # Local config (not in git)
-```
-
-### Version Catalogs (Modern Approach)
-
-**gradle/libs.versions.toml:**
-```toml
-[versions]
-agp = "8.3.0"
-kotlin = "1.9.22"
-compileSdk = "34"
-minSdk = "24"
-targetSdk = "34"
-
-androidx-core = "1.12.0"
-material = "1.11.0"
-
-[libraries]
-androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
-
-[plugins]
-android-application = { id = "com.android.application", version.ref = "agp" }
-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
-```
-
-**Root build.gradle.kts:**
-```kotlin
-plugins {
- alias(libs.plugins.android.application) apply false
- alias(libs.plugins.kotlin.android) apply false
-}
-```
-
-**App build.gradle.kts:**
-```kotlin
-plugins {
- alias(libs.plugins.android.application)
- alias(libs.plugins.kotlin.android)
-}
-
-android {
- namespace = "com.example.myapp"
- compileSdk = 34
-
- defaultConfig {
- applicationId = "com.example.myapp"
- minSdk = 24
- targetSdk = 34
- versionCode = 1
- versionName = "1.0"
- }
-
- buildTypes {
- release {
- isMinifyEnabled = true
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
- }
-
- kotlinOptions {
- jvmTarget = "17"
- }
-}
-
-dependencies {
- implementation(libs.androidx.core.ktx)
- implementation(libs.material)
-
- // Gomobile AAR
- implementation(files("libs/mylib.aar"))
-}
-```
-
-### Managing Dependencies
-
-**Add dependency to version catalog:**
-```toml
-[versions]
-retrofit = "2.9.0"
-
-[libraries]
-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
-```
-
-**Use in build.gradle.kts:**
-```kotlin
-dependencies {
- implementation(libs.retrofit)
-}
-```
-
-**Dependency scopes:**
-```kotlin
-dependencies {
- implementation("...") // Recommended: Not exposed to consumers
- api("...") // Exposed to consumers
- compileOnly("...") // Compile only (not in APK)
- runtimeOnly("...") // Runtime only
-
- testImplementation("...") // Unit tests
- androidTestImplementation("...")// Instrumentation tests
-
- debugImplementation("...") // Debug build only
-}
-```
-
-**Local AAR/JAR files:**
-```kotlin
-dependencies {
- // Single AAR
- implementation(files("libs/mylib.aar"))
-
- // All AARs in libs directory
- implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
-}
-```
-
-### gradle.properties
-
-```properties
-# Performance
-org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
-org.gradle.daemon=true
-org.gradle.parallel=true
-org.gradle.caching=true
-
-# AndroidX
-android.useAndroidX=true
-android.enableJetifier=true
-
-# Build
-android.nonTransitiveRClass=true
-```
-
-### Build Variants
-
-**Build types:**
-```kotlin
-android {
- buildTypes {
- debug {
- applicationIdSuffix = ".debug"
- isDebuggable = true
- }
-
- release {
- isMinifyEnabled = true
- isShrinkResources = true
- proguardFiles(...)
- }
-
- create("staging") {
- initWith(getByName("debug"))
- applicationIdSuffix = ".staging"
- }
- }
-}
-```
-
-**Product flavors:**
-```kotlin
-android {
- flavorDimensions += "version"
-
- productFlavors {
- create("free") {
- dimension = "version"
- applicationIdSuffix = ".free"
- }
-
- create("paid") {
- dimension = "version"
- applicationIdSuffix = ".paid"
- }
- }
-}
-```
-
-Build specific variant:
-```bash
-./gradlew assembleFreeDebug
-./gradlew assemblePaidRelease
-```
-
-### Common Gradle Tasks
-
-```bash
-# List all tasks
-./gradlew tasks
-
-# Clean build outputs
-./gradlew clean
-
-# Build all variants
-./gradlew build
-
-# Check dependencies
-./gradlew dependencies
-
-# Show dependency tree
-./gradlew app:dependencies --configuration debugRuntimeClasspath
-
-# Refresh dependencies
-./gradlew --refresh-dependencies
-```
-
-## Building Go Libraries (gomobile bind)
-
-Use gomobile bind to create Android AAR packages from Go code. This allows you to use Go libraries in Android apps written in Java/Kotlin.
-
-### When to Use
-
-- You have existing Go code (networking, crypto, business logic)
-- You want to integrate Go into an existing Android app
-- You need Go's concurrency or standard library in Android
-- You want to share code between backend and mobile
-
-### Prerequisites
-
-```bash
-# Install gomobile
-go install golang.org/x/mobile/cmd/gomobile@latest
-
-# Initialize (downloads Android toolchain)
-gomobile init
-
-# Verify
-gomobile version
-```
-
-### Go Code Requirements
-
-**What works in gomobile:**
-- ✅ Exported functions with basic types
-- ✅ Exported structs with exported fields
-- ✅ Methods on exported structs
-- ✅ Interfaces (for callbacks)
-- ✅ Error return values (become exceptions)
-- ✅ Slices of basic types (`[]byte`, `[]int`, `[]string`)
-
-**What doesn't work:**
-- ❌ Maps (use structs instead)
-- ❌ Channels (use callbacks via interfaces)
-- ❌ Generics
-- ❌ Unexported types in function signatures
-- ❌ Variadic functions
-
-**Example Go library:**
-```go
-// File: golib/network.go
-package network
-
-import (
- "fmt"
- "net/http"
-)
-
-// FetchURL fetches content from a URL
-func FetchURL(url string) (string, error) {
- resp, err := http.Get(url)
- if err != nil {
- return "", err
- }
- defer resp.Body.Close()
-
- // Implementation...
- return "content", nil
-}
-
-// Config represents network configuration
-type Config struct {
- Timeout int
- BaseURL string
-}
-
-// NewConfig creates a new Config
-func NewConfig(timeout int, baseURL string) *Config {
- return &Config{
- Timeout: timeout,
- BaseURL: baseURL,
- }
-}
-
-// Callback interface for async operations
-type Callback interface {
- OnSuccess(data string)
- OnError(err error)
-}
-
-// FetchAsync fetches URL asynchronously
-func FetchAsync(url string, callback Callback) {
- go func() {
- result, err := FetchURL(url)
- if err != nil {
- callback.OnError(err)
- return
- }
- callback.OnSuccess(result)
- }()
-}
-```
-
-### Build AAR
-
-**Basic build (all architectures):**
-```bash
-gomobile bind -target=android -o mylib.aar ./golib
-```
-
-**Build specific architectures:**
-```bash
-# ARM64 only (most modern devices)
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-
-# ARM64 and ARM32
-gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
-
-# All common architectures
-gomobile bind -target=android/arm64,android/arm,android/amd64,android/386 -o mylib.aar ./golib
-```
-
-**Architecture options:**
-- `android/arm64`: 64-bit ARM (arm64-v8a) - modern devices
-- `android/arm`: 32-bit ARM (armeabi-v7a) - older devices
-- `android/amd64`: 64-bit x86 (x86_64) - emulators
-- `android/386`: 32-bit x86 (x86) - old emulators
-
-### Integrate AAR into Android Project
-
-**Copy AAR:**
-```bash
-cp mylib.aar /path/to/android-project/app/libs/
-```
-
-**Add dependency in app/build.gradle.kts:**
-```kotlin
-dependencies {
- implementation(files("libs/mylib.aar"))
-}
-```
-
-**Sync Gradle:**
-```bash
-./gradlew sync
-```
-
-### Use in Android Code
-
-**Kotlin example:**
-```kotlin
-import golib.Network
-
-class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // Call Go function
- try {
- val result = Network.fetchURL("https://example.com")
- println("Result: $result")
- } catch (e: Exception) {
- println("Error: ${e.message}")
- }
-
- // Use Go struct
- val config = Network.newConfig(30, "https://api.example.com")
- println("Timeout: ${config.timeout}")
-
- // Async with callback
- Network.fetchAsync("https://example.com", object : Network.Callback {
- override fun onSuccess(data: String) {
- println("Success: $data")
- }
-
- override fun onError(err: Exception) {
- println("Error: ${err.message}")
- }
- })
- }
-}
-```
-
-### Type Conversion Reference
-
-| Go Type | Java/Kotlin Type | Notes |
-|---------|------------------|-------|
-| `bool` | `boolean` / `Boolean` | |
-| `int`, `int32` | `int` / `Int` | 32-bit signed |
-| `int64` | `long` / `Long` | 64-bit signed |
-| `float64` | `double` / `Double` | |
-| `string` | `String` | UTF-8 encoded |
-| `[]byte` | `byte[]` / `ByteArray` | |
-| `error` | `Exception` | Go errors become Java exceptions |
-| `struct` | `class` | Exported fields become getters/setters |
-| `interface` | `interface` | Must have exported methods only |
-
-### Optimization Tips
-
-**Minimize boundary crossings:**
-
-Bad (many crossings):
-```go
-func ProcessItem(item string) string { ... }
-// Called 1000 times from Android = 1000 Go calls
-```
-
-Good (single crossing):
-```go
-func ProcessItems(items []string) []string { ... }
-// Called once from Android = 1 Go call
-```
-
-**Use appropriate architectures:**
-
-For development (faster builds):
-```bash
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-```
-
-For release (wider compatibility):
-```bash
-gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
-```
-
-### Build Automation Script
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-echo "Building AAR..."
-
-TARGETS=${ANDROID_TARGETS:-"android/arm64,android/arm"}
-OUTPUT=${AAR_OUTPUT:-"mylib.aar"}
-PACKAGE=${GO_PACKAGE:-"./golib"}
-
-gomobile bind \
- -target="$TARGETS" \
- -o "$OUTPUT" \
- "$PACKAGE"
-
-echo "AAR built: $OUTPUT"
-
-# Copy to Android project if path is set
-if [ -n "${ANDROID_PROJECT:-}" ]; then
- cp "$OUTPUT" "$ANDROID_PROJECT/app/libs/"
- echo "Copied to Android project"
-fi
-```
-
-Usage:
-```bash
-# Build for ARM64 only
-ANDROID_TARGETS=android/arm64 ./build-aar.sh
-
-# Build and copy to Android project
-ANDROID_PROJECT=/path/to/android-app ./build-aar.sh
-```
-
-## Building Standalone Go Apps (gomobile build)
-
-Build standalone Android applications entirely in Go using `gomobile build`. Creates a complete APK with Go code as the main application logic.
-
-### When to Use vs gomobile bind
-
-| Feature | gomobile build | gomobile bind |
-|---------|----------------|---------------|
-| UI | OpenGL only | Full Android UI |
-| Android APIs | Limited | Full access |
-| Development speed | Fast | Medium |
-| Production apps | Demos/utilities | Production ready |
-| Learning curve | Low | Medium |
-
-**Use gomobile build when:**
-- You want to write the entire app in Go
-- You're building a simple utility or demo
-- You want to prototype quickly
-- You're comfortable with limited UI
-
-**Use gomobile bind when:**
-- You need complex native Android UI
-- You need full Android framework APIs
-- You're building a production app with rich UI
-
-### Basic App Example
-
-```go
-package main
-
-import (
- "log"
- "golang.org/x/mobile/app"
- "golang.org/x/mobile/event/lifecycle"
- "golang.org/x/mobile/event/paint"
- "golang.org/x/mobile/gl"
-)
-
-func main() {
- app.Main(func(a app.App) {
- var glctx gl.Context
-
- for e := range a.Events() {
- switch e := a.Filter(e).(type) {
- case lifecycle.Event:
- switch e.Crosses(lifecycle.StageVisible) {
- case lifecycle.CrossOn:
- glctx, _ = e.DrawContext.(gl.Context)
- log.Println("App started")
- case lifecycle.CrossOff:
- log.Println("App stopped")
- glctx = nil
- }
-
- case paint.Event:
- if glctx == nil {
- continue
- }
- // Clear screen
- glctx.ClearColor(0.2, 0.2, 0.3, 1.0)
- glctx.Clear(gl.COLOR_BUFFER_BIT)
- a.Publish()
- }
- }
- })
-}
-```
-
-### Build APK
-
-**Basic build:**
-```bash
-gomobile build -target=android .
-```
-
-**Build with custom app ID:**
-```bash
-gomobile build -target=android -appid=com.example.myapp .
-```
-
-**Build for specific architectures:**
-```bash
-# ARM64 only
-gomobile build -target=android/arm64 -appid=com.example.myapp .
-
-# ARM64 and ARM32
-gomobile build -target=android/arm64,android/arm -appid=com.example.myapp .
-```
-
-**Build with custom icon:**
-```bash
-# Place icon at assets/icon.png
-gomobile build -target=android -icon=assets/icon.png .
-```
-
-### Install and Run
-
-```bash
-# Build and install
-gomobile install -target=android -appid=com.example.myapp .
-
-# Or build then install separately
-gomobile build -target=android -appid=com.example.myapp .
-adb install -r myapp.apk
-```
-
-**View logs:**
-```bash
-adb logcat | grep GoLog
-```
-
-### Limitations
-
-**UI Limitations:**
-- ❌ No native Android UI widgets
-- ❌ No Material Design components
-- ❌ Limited text rendering
-- ✅ OpenGL ES 2.0 for custom graphics
-- ✅ Touch event handling
-
-**Platform Limitations:**
-- ❌ No direct access to Android framework APIs
-- ❌ Can't use Java/Kotlin libraries
-- ✅ Network access (HTTP, TCP, UDP)
-- ✅ File I/O
-- ✅ Concurrency with goroutines
-
-## Building Android Apps
-
-### Build Debug
-
-```bash
-# Build debug APK
-./gradlew assembleDebug
-
-# Output location
-ls app/build/outputs/apk/debug/app-debug.apk
-
-# Install on device
-adb install app/build/outputs/apk/debug/app-debug.apk
-
-# Or use Gradle install task
-./gradlew installDebug
-```
-
-### Build Release
-
-```bash
-# Build signed release APK
-./gradlew assembleRelease
-
-# Output
-ls app/build/outputs/apk/release/app-release.apk
-
-# Build AAB for Play Store
-./gradlew bundleRelease
-
-# Output
-ls app/build/outputs/bundle/release/app-release.aab
-```
-
-### Build with Gomobile AAR
-
-If using gomobile bind:
-
-```bash
-# 1. Build AAR from Go code
-cd golib
-gomobile bind -target=android -o mylib.aar .
-
-# 2. Copy to Android project
-cp mylib.aar /path/to/android-project/app/libs/
-
-# 3. Build Android app
-cd /path/to/android-project
-./gradlew assembleDebug
-```
-
-Automate in script:
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-echo "Building Go library..."
-cd golib
-gomobile bind -target=android -o mylib.aar .
-
-echo "Copying AAR to Android project..."
-cp mylib.aar ../app/libs/
-
-echo "Building Android app..."
-cd ..
-./gradlew assembleDebug
-
-echo "Done! APK: app/build/outputs/apk/debug/app-debug.apk"
-```
-
-### Build Optimization
-
-**Enable build cache (gradle.properties):**
-```properties
-org.gradle.caching=true
-org.gradle.parallel=true
-org.gradle.daemon=true
-org.gradle.jvmargs=-Xmx4096m
-```
-
-**Optimize build time:**
-```kotlin
-android {
- // Use only needed ABIs during development
- splits {
- abi {
- isEnable = false
- }
- }
-
- // Disable PNG crunching in debug
- buildTypes {
- debug {
- isCrunchPngs = false
- }
- }
-}
-```
-
-**Incremental builds:**
-```bash
-# Good: Incremental build
-./gradlew assembleDebug
-
-# Bad: Full rebuild (slower)
-./gradlew clean assembleDebug
-```
-
-## Signing and Publishing
-
-### Create Signing Key
-
-Generate keystore:
-```bash
-keytool -genkey -v \
- -keystore release.keystore \
- -alias myapp-key \
- -keyalg RSA \
- -keysize 2048 \
- -validity 10000
-```
-
-**Important:**
-- **NEVER commit keystore to git**
-- **Backup keystore securely**
-- **Remember passwords**
-
-View keystore details:
-```bash
-keytool -list -v -keystore release.keystore
-```
-
-### Configure Signing in Gradle
-
-**Using environment variables (recommended):**
-
-app/build.gradle.kts:
-```kotlin
-android {
- signingConfigs {
- create("release") {
- storeFile = file("../release.keystore")
- storePassword = System.getenv("KEYSTORE_PASSWORD")
- keyAlias = "myapp-key"
- keyPassword = System.getenv("KEY_PASSWORD")
- }
- }
-
- buildTypes {
- release {
- signingConfig = signingConfigs.getByName("release")
- isMinifyEnabled = true
- isShrinkResources = true
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
-}
-```
-
-Build with environment variables:
-```bash
-export KEYSTORE_PASSWORD="your-keystore-password"
-export KEY_PASSWORD="your-key-password"
-./gradlew bundleRelease
-```
-
-### ProGuard / R8
-
-**Enable code shrinking:**
-```kotlin
-android {
- buildTypes {
- release {
- isMinifyEnabled = true
- isShrinkResources = true
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
-}
-```
-
-**proguard-rules.pro:**
-```proguard
-# Keep gomobile generated classes
--keep class go.** { *; }
--keep class mylib.** { *; }
-
-# Keep model classes
--keep class com.example.myapp.models.** { *; }
-
-# Kotlin
--keep class kotlin.** { *; }
--keep class kotlin.Metadata { *; }
-```
-
-### Build Release AAB
-
-```bash
-./gradlew bundleRelease
-
-# Output location
-ls app/build/outputs/bundle/release/app-release.aab
-```
-
-### Verify Signature
-
-```bash
-# Check AAB signature
-jarsigner -verify -verbose app-release.aab
-
-# Check APK signature
-apksigner verify --verbose app-release.apk
-```
-
-### Test Release Build
-
-**Generate APKs from AAB (using bundletool):**
-```bash
-# Download bundletool from:
-# https://github.com/google/bundletool/releases
-
-# Generate APKs
-java -jar bundletool-all.jar build-apks \
- --bundle=app-release.aab \
- --output=app.apks \
- --mode=universal
-
-# Extract universal APK
-unzip app.apks universal.apk
-
-# Install
-adb install universal.apk
-```
-
-**Test checklist:**
-- [ ] App installs successfully
-- [ ] App launches without crashes
-- [ ] All features work correctly
-- [ ] ProGuard hasn't broken anything
-- [ ] Gomobile integration works
-- [ ] Network requests succeed
-
-### Versioning
-
-**Version code (monotonically increasing):**
-```kotlin
-android {
- defaultConfig {
- versionCode = 1 // Increment for each release
- versionName = "1.0"
- }
-}
-```
-
-**Automated versioning from git:**
-```kotlin
-fun getVersionCode(): Int {
- val process = Runtime.getRuntime().exec("git rev-list --count HEAD")
- return process.inputStream.bufferedReader().readText().trim().toInt()
-}
-
-fun getVersionName(): String {
- val process = Runtime.getRuntime().exec("git describe --tags --always")
- return process.inputStream.bufferedReader().readText().trim()
-}
-
-android {
- defaultConfig {
- versionCode = getVersionCode()
- versionName = getVersionName()
- }
-}
-```
-
-### Upload to Play Console
-
-**Prerequisites:**
-1. Google Play Developer account ($25 one-time)
-2. App created in Play Console
-3. Store listing completed
-
-**Upload AAB:**
-1. Go to [Play Console](https://play.google.com/console)
-2. Select your app
-3. Production → Releases
-4. Create new release
-5. Upload AAB file
-6. Fill release notes
-7. Review and rollout
-
-**Release tracks:**
-| Track | Purpose | Audience |
-|-------|---------|----------|
-| Internal testing | Quick testing | Up to 100 testers |
-| Closed testing | Alpha/beta | Invited testers |
-| Open testing | Public beta | Anyone can join |
-| Production | Public release | All users |
-
-**Staged rollout (recommended):**
-1. Start with 5-10%
-2. Monitor crashes/issues
-3. Increase to 25%, 50%, 100%
-
-### Store Listing Requirements
-
-**Required assets:**
-- Screenshots: Minimum 2, recommended 4-8
-- Icon: 512x512 PNG
-- Feature graphic: 1024x500 PNG
-
-**Required information:**
-- App name
-- Short description (80 characters)
-- Full description (4000 characters)
-- Category
-- Content rating
-- Privacy policy URL (if app collects data)
-- Contact email
-
-### CI/CD Publishing Example
-
-**.github/workflows/release.yml:**
-```yaml
-name: Release
-
-on:
- push:
- tags:
- - 'v*'
-
-jobs:
- release:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up JDK 17
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: '17'
-
- - name: Setup Go
- uses: actions/setup-go@v5
- with:
- go-version: '1.22'
-
- - name: Build gomobile AAR
- run: |
- go install golang.org/x/mobile/cmd/gomobile@latest
- gomobile init
- cd golib
- gomobile bind -target=android -o mylib.aar .
- cp mylib.aar ../app/libs/
-
- - name: Build Release AAB
- env:
- KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
- KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- run: |
- echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.keystore
- ./gradlew bundleRelease
-
- - name: Upload to Play Store
- uses: r0adkll/upload-google-play@v1
- with:
- serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
- packageName: com.example.myapp
- releaseFiles: app/build/outputs/bundle/release/app-release.aab
- track: production
-```
-
-## Troubleshooting
-
-### Build Issues
-
-**SDK not found:**
-```bash
-# Create local.properties
-echo "sdk.dir=$ANDROID_HOME" > local.properties
-```
-
-**OutOfMemoryError during build:**
-```properties
-# Increase Gradle memory in gradle.properties
-org.gradle.jvmargs=-Xmx8192m -XX:MaxMetaspaceSize=2048m
-```
-
-**Duplicate class errors:**
-```kotlin
-android {
- packagingOptions {
- resources {
- excludes += "META-INF/*.kotlin_module"
- }
- }
-}
-```
-
-### Gomobile Issues
-
-**"gomobile: command not found":**
-```bash
-# Ensure Go bin is in PATH
-export PATH=$PATH:$(go env GOPATH)/bin
-
-# Reinstall gomobile
-go install golang.org/x/mobile/cmd/gomobile@latest
-```
-
-**"NDK not found":**
-```bash
-# Set ANDROID_HOME
-export ANDROID_HOME=$HOME/Android/Sdk
-
-# Install NDK
-sdkmanager "ndk;26.1.10909125"
-
-# Reinitialize gomobile
-gomobile init
-```
-
-**"Cannot use map in function signature":**
-```go
-// Bad - maps don't bind
-func BadFunc(data map[string]string) error { ... }
-
-// Good - use struct instead
-type KeyValue struct {
- Key string
- Value string
-}
-func GoodFunc(data []KeyValue) error { ... }
-```
-
-**AAR is very large:**
-```bash
-# Build only needed architectures
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-```
-
-### Dependency Issues
-
-**Dependency conflicts:**
-```bash
-# View conflict resolution
-./gradlew app:dependencies
-
-# Force specific version
-dependencies {
- implementation("com.example:library:1.0") {
- force = true
- }
-}
-
-# Exclude transitive dependency
-dependencies {
- implementation("com.example:library:1.0") {
- exclude(group = "com.unwanted", module = "module")
- }
-}
-```
-
-**Sync issues:**
-```bash
-# Refresh dependencies
-./gradlew --refresh-dependencies
-
-# Clear cache
-./gradlew clean cleanBuildCache
-```
-
-### Signing Issues
-
-**"App not signed" error:**
-```bash
-# Check signing configuration
-./gradlew :app:signingReport
-
-# Verify keystore exists and credentials are correct
-```
-
-**ProGuard breaks app:**
-```proguard
-# Add keep rules in proguard-rules.pro
--keep class com.your.package.** { *; }
-```
-
-**Upload rejected by Play Console:**
-- Version code not incremented
-- Signature mismatch
-- Missing required permissions
-- Policy violations
-
-## Next Steps
-
-- **Debug and test** - Use the **Debug** workflow
-- **Monitor app performance** - Play Console metrics
-- **Respond to user feedback** - Reviews and ratings
-- **Plan updates** - New features and bug fixes
-
-## Resources
-
-- [Gradle Build Documentation](https://developer.android.com/studio/build)
-- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
-- [Publish Your App](https://developer.android.com/studio/publish)
-- [Play Console](https://play.google.com/console)
-- [App Signing](https://developer.android.com/studio/publish/app-signing)
dots/config/claude/skills/.archive/Android/workflows/Debug.md
@@ -1,1288 +0,0 @@
-# Debug Workflow
-
-Debug, test, and profile Android applications using adb, emulator, logcat, and testing frameworks.
-
-## ADB (Android Debug Bridge)
-
-### Basic Commands
-
-```bash
-# List connected devices
-adb devices
-
-# Connect to specific device (if multiple)
-adb -s <device-id> <command>
-
-# Target emulator specifically
-adb -e <command>
-
-# Target physical device specifically
-adb -d <command>
-
-# Check device info
-adb shell getprop ro.product.model
-adb shell getprop ro.build.version.release
-```
-
-### App Management
-
-```bash
-# Install APK
-adb install app-debug.apk
-
-# Reinstall (keep data)
-adb install -r app-debug.apk
-
-# Uninstall app
-adb uninstall com.example.myapp
-
-# Clear app data
-adb shell pm clear com.example.myapp
-
-# List installed packages
-adb shell pm list packages
-adb shell pm list packages | grep myapp
-
-# Get app info
-adb shell dumpsys package com.example.myapp
-```
-
-### Start/Stop App
-
-```bash
-# Start app activity
-adb shell am start -n com.example.myapp/.MainActivity
-
-# Start with intent data
-adb shell am start -n com.example.myapp/.MainActivity -d "https://example.com"
-
-# Stop (force close) app
-adb shell am force-stop com.example.myapp
-
-# Kill app process
-adb shell am kill com.example.myapp
-```
-
-### File Operations
-
-```bash
-# Push file to device
-adb push local-file.txt /sdcard/
-
-# Pull file from device
-adb pull /sdcard/file.txt ./
-
-# List files
-adb shell ls /sdcard/
-
-# View file content
-adb shell cat /sdcard/file.txt
-
-# Remove file
-adb shell rm /sdcard/file.txt
-```
-
-### Screenshots and Screen Recording
-
-```bash
-# Take screenshot
-adb shell screencap /sdcard/screen.png
-adb pull /sdcard/screen.png
-adb shell rm /sdcard/screen.png
-
-# Or in one command
-adb exec-out screencap -p > screen.png
-
-# Record screen (max 3 minutes)
-adb shell screenrecord /sdcard/demo.mp4
-# Press Ctrl+C to stop
-adb pull /sdcard/demo.mp4
-adb shell rm /sdcard/demo.mp4
-```
-
-### System Information
-
-```bash
-# CPU info
-adb shell cat /proc/cpuinfo
-
-# Memory info
-adb shell cat /proc/meminfo
-
-# Battery status
-adb shell dumpsys battery
-
-# Display info
-adb shell dumpsys display
-
-# Running processes
-adb shell ps
-
-# App processes
-adb shell ps | grep com.example.myapp
-```
-
-### Network
-
-```bash
-# Check network connectivity
-adb shell ping -c 4 google.com
-
-# View network interfaces
-adb shell ip addr
-
-# Port forwarding (device port to local port)
-adb forward tcp:8080 tcp:8080
-
-# Reverse port forwarding (local port to device)
-adb reverse tcp:9000 tcp:9000
-```
-
-## Logcat
-
-### View Logs
-
-```bash
-# All logs (verbose)
-adb logcat
-
-# Clear logs first, then follow
-adb logcat -c && adb logcat
-
-# Filter by app package
-adb logcat | grep "com.example.myapp"
-
-# Filter by tag
-adb logcat -s MyTag
-
-# Filter by priority (V=Verbose, D=Debug, I=Info, W=Warn, E=Error, F=Fatal)
-adb logcat *:E # Only errors
-
-# Multiple filters
-adb logcat MyTag:D *:E # MyTag at Debug level, everything else at Error
-```
-
-### Log Priorities
-
-```
-V - Verbose (lowest priority)
-D - Debug
-I - Info
-W - Warning
-E - Error
-F - Fatal
-S - Silent (highest priority, nothing printed)
-```
-
-### Format Options
-
-```bash
-# Brief format (default)
-adb logcat -v brief
-
-# Time format (with timestamps)
-adb logcat -v time
-
-# Threadtime (time + PID + TID)
-adb logcat -v threadtime
-
-# Long format (all metadata)
-adb logcat -v long
-```
-
-### Save Logs to File
-
-```bash
-# Save all logs
-adb logcat > logs.txt
-
-# Save with timestamp in filename
-adb logcat > "logs-$(date +%Y%m%d-%H%M%S).txt"
-
-# Save for 10 seconds then stop
-timeout 10s adb logcat > logs.txt
-```
-
-### Filter Crash Logs
-
-```bash
-# Show only fatal errors
-adb logcat *:F
-
-# Show crashes and errors
-adb logcat *:E
-
-# Find specific crash
-adb logcat | grep -A 50 "FATAL EXCEPTION"
-
-# Show stack traces
-adb logcat | grep -E "(at |Caused by)"
-```
-
-### Gomobile / Go-specific Logs
-
-```bash
-# Go runtime logs
-adb logcat | grep GoLog
-
-# Go panic messages
-adb logcat | grep "panic:"
-
-# Your Go log.Printf output
-adb logcat | grep "GoLog"
-```
-
-## Emulator
-
-### System Images
-
-**Image Types:**
-
-| Type | Google Apps | Play Store | Use Case |
-|------|-------------|------------|----------|
-| `default` | ❌ | ❌ | AOSP, minimal |
-| `google_apis` | ✅ | ❌ | Google Maps, etc. |
-| `google_apis_playstore` | ✅ | ✅ | Full Play Services |
-
-**Install System Images:**
-
-```bash
-# List available system images
-sdkmanager --list | grep system-images
-
-# Android 14 (API 34) - x86_64 (for Intel/AMD)
-sdkmanager "system-images;android-34;google_apis;x86_64"
-
-# Android 14 (API 34) - ARM64 (for Apple Silicon)
-sdkmanager "system-images;android-34;google_apis;arm64-v8a"
-
-# Android 13 (API 33)
-sdkmanager "system-images;android-33;google_apis;x86_64"
-```
-
-**Choose architecture:**
-- Intel/AMD processors: `x86_64`
-- Apple Silicon (M1/M2/M3): `arm64-v8a`
-
-### Create AVD
-
-```bash
-# Quick start
-avdmanager create avd \
- -n Pixel7 \
- -k "system-images;android-34;google_apis;x86_64" \
- -d "pixel_7"
-
-# List available device definitions
-avdmanager list device
-
-# Common devices: pixel_7, pixel_7_pro, pixel_6, pixel_5, pixel_tablet
-```
-
-**Examples:**
-
-```bash
-# Pixel 6 with Android 13
-avdmanager create avd \
- -n Pixel6 \
- -k "system-images;android-33;google_apis;x86_64" \
- -d "pixel_6"
-
-# Tablet (Pixel Tablet)
-avdmanager create avd \
- -n PixelTablet \
- -k "system-images;android-34;google_apis;x86_64" \
- -d "pixel_tablet"
-
-# Custom configuration
-avdmanager create avd \
- -n CustomPhone \
- -k "system-images;android-34;google_apis;x86_64" \
- -d "pixel_7" \
- -c 512M \ # SD card size
- --force # Overwrite if exists
-```
-
-### Manage AVDs
-
-```bash
-# List all AVDs
-avdmanager list avd
-
-# List with paths
-avdmanager list avd -c
-
-# Delete AVD
-avdmanager delete avd -n Pixel7
-```
-
-### Start Emulator
-
-**Basic:**
-
-```bash
-# Start AVD
-emulator -avd Pixel7
-
-# Start in background
-emulator -avd Pixel7 &
-
-# Start with GPU acceleration
-emulator -avd Pixel7 -gpu host
-```
-
-**Common Options:**
-
-```bash
-# Wipe data (factory reset)
-emulator -avd Pixel7 -wipe-data
-
-# Read-only system
-emulator -avd Pixel7 -read-only
-
-# Increase RAM
-emulator -avd Pixel7 -memory 4096
-
-# Cold boot (ignore snapshot)
-emulator -avd Pixel7 -no-snapshot-load
-
-# No audio
-emulator -avd Pixel7 -no-audio
-
-# Maximum performance
-emulator -avd Pixel7 \
- -gpu host \
- -memory 4096 \
- -cores 4 \
- -no-boot-anim \
- -no-snapshot
-```
-
-**Network Options:**
-
-```bash
-# Use specific DNS
-emulator -avd Pixel7 -dns-server 8.8.8.8
-
-# Network latency simulation
-emulator -avd Pixel7 -netdelay gprs # or edge, umts, none
-
-# Network speed simulation
-emulator -avd Pixel7 -netspeed full # or gsm, hscsd, gprs, edge, umts, hsdpa
-
-# Proxy
-emulator -avd Pixel7 -http-proxy http://localhost:8888
-```
-
-### Emulator Control
-
-**Via Telnet:**
-
-```bash
-# Connect to emulator console
-telnet localhost 5554 # Port shown when emulator starts
-
-# In telnet session:
-# auth <token> # Token is in ~/.emulator_console_auth_token
-# help
-# sms send 1234567890 "Test message"
-# geo fix -122.084 37.422 # Set GPS location
-# quit
-```
-
-**Via ADB:**
-
-```bash
-# Rotate screen
-adb shell settings put system accelerometer_rotation 0
-adb shell settings put system user_rotation 1 # 0=0°, 1=90°, 2=180°, 3=270°
-
-# Simulate low battery
-adb shell dumpsys battery set level 10
-
-# Reset battery
-adb shell dumpsys battery reset
-```
-
-### Multiple Emulators
-
-```bash
-# Start first emulator
-emulator -avd Pixel7 &
-
-# Start second emulator (different AVD)
-emulator -avd Pixel6 &
-
-# Install on specific emulator
-adb -s emulator-5554 install app-debug.apk
-adb -s emulator-5556 install app-debug.apk
-```
-
-**Emulator Ports:**
-- First emulator: console 5554, adb 5555
-- Second emulator: console 5556, adb 5557
-
-## Testing
-
-### Test Types
-
-| Type | Runs On | Speed | Use For |
-|------|---------|-------|---------|
-| Unit Tests | JVM | Fast | Business logic, utilities |
-| Instrumentation Tests | Android device/emulator | Slow | Android APIs, database |
-| UI Tests | Android device/emulator | Slowest | User interface, interactions |
-
-### Unit Tests (Local Tests)
-
-**Location:** `app/src/test/java/com/example/myapp/`
-
-**Dependencies:**
-
-```kotlin
-dependencies {
- testImplementation("junit:junit:4.13.2")
- testImplementation("org.mockito:mockito-core:5.7.0")
- testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
-}
-```
-
-**Example:**
-
-```kotlin
-package com.example.myapp
-
-import org.junit.Test
-import org.junit.Assert.*
-
-class CalculatorTest {
-
- @Test
- fun addition_isCorrect() {
- val calculator = Calculator()
- val result = calculator.add(2, 3)
- assertEquals(5, result)
- }
-
- @Test
- fun division_byZero_throwsException() {
- val calculator = Calculator()
- assertThrows(ArithmeticException::class.java) {
- calculator.divide(10, 0)
- }
- }
-}
-```
-
-**Run Unit Tests:**
-
-```bash
-# All unit tests
-./gradlew test
-
-# Specific variant
-./gradlew testDebug
-
-# Specific test class
-./gradlew test --tests CalculatorTest
-
-# With coverage report
-./gradlew testDebugUnitTest jacocoTestReport
-```
-
-**View Results:**
-
-```bash
-# HTML report
-open app/build/reports/tests/testDebugUnitTest/index.html
-
-# Coverage report
-open app/build/reports/jacoco/jacocoTestReport/html/index.html
-```
-
-### Instrumentation Tests
-
-**Location:** `app/src/androidTest/java/com/example/myapp/`
-
-**Dependencies:**
-
-```kotlin
-android {
- defaultConfig {
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
-}
-
-dependencies {
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test:runner:1.5.2")
- androidTestImplementation("androidx.test:rules:1.5.0")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-}
-```
-
-**Example:**
-
-```kotlin
-package com.example.myapp
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.Assert.*
-
-@RunWith(AndroidJUnit4::class)
-class DatabaseTest {
-
- @Test
- fun useAppContext() {
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.example.myapp", appContext.packageName)
- }
-}
-```
-
-**Run Instrumentation Tests:**
-
-```bash
-# Connect device/emulator first
-adb devices
-
-# All instrumentation tests
-./gradlew connectedAndroidTest
-
-# Specific variant
-./gradlew connectedDebugAndroidTest
-
-# Specific test
-./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.myapp.DatabaseTest
-```
-
-### UI Tests (Espresso)
-
-**Dependencies:**
-
-```kotlin
-dependencies {
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
- androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
- androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
-}
-```
-
-**Example:**
-
-```kotlin
-package com.example.myapp
-
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.*
-import androidx.test.espresso.assertion.ViewAssertions.matches
-import androidx.test.espresso.matcher.ViewMatchers.*
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MainActivityTest {
-
- @get:Rule
- val activityRule = ActivityScenarioRule(MainActivity::class.java)
-
- @Test
- fun button_click_showsText() {
- // Type text in EditText
- onView(withId(R.id.editText))
- .perform(typeText("Hello"), closeSoftKeyboard())
-
- // Click button
- onView(withId(R.id.button))
- .perform(click())
-
- // Verify text is displayed
- onView(withId(R.id.textView))
- .check(matches(withText("Hello")))
- }
-}
-```
-
-**Common Espresso Operations:**
-
-```kotlin
-// Find views
-onView(withId(R.id.viewId))
-onView(withText("text"))
-onView(withContentDescription("description"))
-
-// Actions
-perform(click())
-perform(typeText("text"))
-perform(replaceText("new text"))
-perform(clearText())
-perform(closeSoftKeyboard())
-perform(swipeLeft())
-perform(swipeRight())
-perform(scrollTo())
-
-// Assertions
-check(matches(isDisplayed()))
-check(matches(withText("expected")))
-check(matches(isEnabled()))
-check(matches(isChecked()))
-check(doesNotExist())
-```
-
-### Testing Gomobile Integration
-
-**Unit Test Go Code (Before binding):**
-
-```bash
-cd golib
-go test ./...
-go test -v ./...
-go test -cover ./...
-```
-
-**Test AAR Integration:**
-
-```kotlin
-package com.example.myapp
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import mylib.Mylib // Import gomobile generated package
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.Assert.*
-
-@RunWith(AndroidJUnit4::class)
-class GomobileTest {
-
- @Test
- fun goFunction_returnsExpectedResult() {
- val result = Mylib.fetchURL("https://example.com")
- assertNotNull(result)
- assertTrue(result.isNotEmpty())
- }
-
- @Test
- fun goFunction_handlesError() {
- try {
- Mylib.fetchURL("invalid-url")
- fail("Should have thrown exception")
- } catch (e: Exception) {
- assertTrue(e.message?.contains("error") == true)
- }
- }
-}
-```
-
-### Code Coverage (JaCoCo)
-
-**Configure:**
-
-```kotlin
-plugins {
- id("jacoco")
-}
-
-android {
- buildTypes {
- debug {
- enableAndroidTestCoverage = true
- enableUnitTestCoverage = true
- }
- }
-}
-
-tasks.register<JacocoReport>("jacocoTestReport") {
- dependsOn("testDebugUnitTest", "createDebugCoverageReport")
-
- reports {
- xml.required.set(true)
- html.required.set(true)
- }
-
- val fileFilter = listOf(
- "**/R.class",
- "**/R$*.class",
- "**/BuildConfig.*",
- "**/Manifest*.*"
- )
-
- val debugTree = fileTree("${project.buildDir}/intermediates/javac/debug") {
- exclude(fileFilter)
- }
-
- val mainSrc = "${project.projectDir}/src/main/java"
-
- sourceDirectories.setFrom(files(mainSrc))
- classDirectories.setFrom(files(debugTree))
- executionData.setFrom(fileTree(project.buildDir) {
- include("jacoco/testDebugUnitTest.exec", "outputs/code_coverage/debugAndroidTest/connected/**/*.ec")
- })
-}
-```
-
-**Run Coverage:**
-
-```bash
-./gradlew jacocoTestReport
-open app/build/reports/jacoco/jacocoTestReport/html/index.html
-```
-
-### Test Best Practices
-
-**1. Follow AAA Pattern:**
-
-```kotlin
-@Test
-fun testName() {
- // Arrange - Set up test data
- val calculator = Calculator()
-
- // Act - Execute the function
- val result = calculator.add(2, 3)
-
- // Assert - Verify the result
- assertEquals(5, result)
-}
-```
-
-**2. Use Descriptive Names:**
-
-```kotlin
-// Good
-@Test
-fun addition_withPositiveNumbers_returnsSum() { }
-
-@Test
-fun division_withZeroDivisor_throwsException() { }
-
-// Bad
-@Test
-fun test1() { }
-```
-
-**3. Test One Thing:**
-
-```kotlin
-// Good - separate tests
-@Test
-fun add_returnsCorrectSum() { }
-
-@Test
-fun add_handlesNegativeNumbers() { }
-```
-
-**4. Mock External Dependencies:**
-
-```kotlin
-import org.mockito.Mock
-import org.mockito.Mockito.*
-
-class UserRepositoryTest {
-
- @Mock
- lateinit var api: ApiService
-
- @Test
- fun getUser_callsApi() {
- val userId = "123"
- `when`(api.fetchUser(userId)).thenReturn(User("John"))
-
- val repository = UserRepository(api)
- val user = repository.getUser(userId)
-
- verify(api).fetchUser(userId)
- assertEquals("John", user.name)
- }
-}
-```
-
-## Debugging from Code
-
-### Android (Kotlin/Java)
-
-```kotlin
-import android.util.Log
-
-class MainActivity : AppCompatActivity() {
- companion object {
- private const val TAG = "MainActivity"
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // Log levels
- Log.v(TAG, "Verbose message")
- Log.d(TAG, "Debug message")
- Log.i(TAG, "Info message")
- Log.w(TAG, "Warning message")
- Log.e(TAG, "Error message")
-
- // With exception
- try {
- // Code that might throw
- } catch (e: Exception) {
- Log.e(TAG, "Error occurred", e)
- }
- }
-}
-```
-
-**View in logcat:**
-
-```bash
-adb logcat -s MainActivity
-```
-
-### Go Code (Gomobile)
-
-```go
-package mylib
-
-import "log"
-
-func FetchData(url string) (string, error) {
- log.Printf("Fetching URL: %s", url) // Appears in logcat with GoLog tag
-
- result, err := doFetch(url)
-
- if err != nil {
- log.Printf("Error fetching: %v", err)
- return "", err
- }
-
- log.Printf("Fetch successful, got %d bytes", len(result))
- return result, nil
-}
-```
-
-**View in logcat:**
-
-```bash
-adb logcat | grep GoLog
-```
-
-### Interactive Debugging
-
-**Enable Debug Mode:**
-
-```kotlin
-// app/build.gradle.kts
-android {
- buildTypes {
- debug {
- isDebuggable = true
- }
- }
-}
-```
-
-**Using Android Studio Debugger:**
-
-1. Build debug APK: `./gradlew assembleDebug`
-2. Install on device: `adb install app-debug.apk`
-3. In Android Studio: Run → Attach Debugger to Android Process
-4. Select your app
-5. Set breakpoints in code
-6. Trigger the code path
-
-## Performance Profiling
-
-### CPU Profiling
-
-```bash
-# Start profiling
-adb shell am profile start com.example.myapp /sdcard/profile.trace
-
-# Stop profiling
-adb shell am profile stop com.example.myapp
-
-# Pull trace file
-adb pull /sdcard/profile.trace
-
-# Analyze with Android Studio or Perfetto
-```
-
-### Memory Profiling
-
-```bash
-# Get memory info for app
-adb shell dumpsys meminfo com.example.myapp
-
-# Heap dump
-adb shell am dumpheap com.example.myapp /sdcard/heap.hprof
-adb pull /sdcard/heap.hprof
-
-# Analyze with Android Studio Memory Profiler
-```
-
-### Monitor App Performance
-
-```bash
-# CPU usage
-adb shell top | grep com.example.myapp
-
-# Memory usage over time
-adb shell dumpsys meminfo com.example.myapp | grep TOTAL
-```
-
-## Debugging Crashes
-
-### 1. View Crash Logs
-
-```bash
-adb logcat -c # Clear old logs
-# Reproduce crash
-adb logcat *:E # View errors
-```
-
-### 2. Find Stack Trace
-
-Look for:
-```
-FATAL EXCEPTION: main
-Process: com.example.myapp, PID: 12345
-java.lang.NullPointerException: Attempt to invoke virtual method '...' on a null object reference
- at com.example.myapp.MainActivity.onCreate(MainActivity.kt:25)
- at android.app.Activity.performCreate(Activity.java:...)
-```
-
-### 3. Analyze Stack Trace
-
-- **Line number**: `MainActivity.kt:25` - exact location
-- **Exception type**: `NullPointerException` - what went wrong
-- **Message**: Details about the error
-- **Call stack**: How we got there
-
-### 4. Common Crash Types
-
-**NullPointerException:**
-
-```kotlin
-// Bad
-val user = getUser() // Returns null
-val name = user.name // Crash!
-
-// Good
-val user = getUser()
-val name = user?.name ?: "Unknown"
-```
-
-**ClassCastException:**
-
-```kotlin
-// Bad
-val text = view as TextView // Crash if not TextView!
-
-// Good
-val text = view as? TextView
-```
-
-**ActivityNotFoundException:**
-
-```kotlin
-// Check if intent can be handled
-if (intent.resolveActivity(packageManager) != null) {
- startActivity(intent)
-}
-```
-
-## Network Debugging
-
-### HTTP Traffic Inspection
-
-```bash
-# Use adb reverse to connect app to local proxy
-adb reverse tcp:8888 tcp:8888
-
-# App should connect to localhost:8888
-# Proxy (mitmproxy, Charles, Burp Suite) runs on port 8888
-```
-
-### View Network Stats
-
-```bash
-adb shell dumpsys netstats
-```
-
-## Troubleshooting
-
-### ADB Issues
-
-**"adb: device offline":**
-
-```bash
-adb kill-server
-adb start-server
-# Reconnect device
-```
-
-**"adb: device unauthorized":**
-
-- Check device screen for authorization prompt
-- Accept "Always allow from this computer"
-- If no prompt:
- ```bash
- adb kill-server
- rm ~/.android/adbkey*
- adb start-server
- ```
-
-**"Logcat shows nothing":**
-
-```bash
-# Clear buffer
-adb logcat -c
-
-# Check buffer size
-adb logcat -g
-
-# Increase buffer size
-adb logcat -G 16M
-```
-
-### Emulator Issues
-
-**Emulator won't start:**
-
-```bash
-# Check emulator location
-which emulator
-
-# Run with verbose logging
-emulator -avd Pixel7 -verbose
-
-# Check for errors
-emulator -avd Pixel7 -debug init
-```
-
-**Slow emulator - Enable hardware acceleration:**
-
-**Linux (KVM):**
-
-```bash
-# Check KVM support
-egrep -c '(vmx|svm)' /proc/cpuinfo # Should be > 0
-
-# Check KVM device
-ls -l /dev/kvm
-
-# Add user to kvm group
-sudo usermod -a -G kvm $USER
-# Log out and back in
-
-# Verify
-emulator -accel-check
-```
-
-**macOS:** Automatically uses Hypervisor Framework
-
-**Windows (HAXM):**
-
-```bash
-sdkmanager "extras;intel;Hardware_Accelerated_Execution_Manager"
-```
-
-**Graphics issues:**
-
-```bash
-# Try different GPU modes
-emulator -avd Pixel7 -gpu auto
-emulator -avd Pixel7 -gpu host
-emulator -avd Pixel7 -gpu swiftshader_indirect
-emulator -avd Pixel7 -gpu angle_indirect
-```
-
-### Test Issues
-
-**Tests fail on CI but pass locally:**
-
-- Check Android API level compatibility
-- Verify emulator configuration
-- Check for timing issues (add timeouts)
-- Ensure deterministic test data
-
-**Instrumentation tests hang:**
-
-```bash
-# Clear app data between tests
-adb shell pm clear com.example.myapp
-
-# Restart adb
-adb kill-server
-adb start-server
-```
-
-**Espresso can't find view:**
-
-```kotlin
-// Use idling resources for async operations
-import androidx.test.espresso.IdlingRegistry
-import androidx.test.espresso.IdlingResource
-```
-
-## Helper Scripts
-
-### Monitor App Script
-
-Create `monitor-app.sh`:
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-APP_PACKAGE="com.example.myapp"
-LOG_TAG="MyApp"
-
-# Clear logs
-adb logcat -c
-
-# Monitor logs with color
-adb logcat | grep --color=always -E "$APP_PACKAGE|$LOG_TAG|FATAL|ERROR"
-```
-
-### Install and Monitor Script
-
-Create `debug-install.sh`:
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-APK="app/build/outputs/apk/debug/app-debug.apk"
-PACKAGE="com.example.myapp"
-ACTIVITY=".MainActivity"
-
-echo "Installing $APK..."
-adb install -r "$APK"
-
-echo "Starting app..."
-adb shell am start -n "$PACKAGE/$ACTIVITY"
-
-echo "Monitoring logs (Ctrl+C to stop)..."
-adb logcat -c
-adb logcat | grep "$PACKAGE"
-```
-
-### Start Emulator and Wait
-
-Create `start-emulator.sh`:
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-AVD_NAME="Pixel7"
-
-echo "Starting emulator: $AVD_NAME"
-emulator -avd "$AVD_NAME" -no-snapshot-save &
-
-# Wait for device to boot
-echo "Waiting for device to boot..."
-adb wait-for-device
-
-# Wait for boot animation to finish
-while [ "$(adb shell getprop init.svc.bootanim | tr -d '\r')" = "running" ]; do
- sleep 1
-done
-
-echo "Emulator ready!"
-
-# Optional: Install app
-if [ -f "app-debug.apk" ]; then
- echo "Installing app..."
- adb install -r app-debug.apk
-fi
-
-echo "Done!"
-```
-
-## CI/CD Testing
-
-### GitHub Actions Example
-
-`.github/workflows/test.yml`:
-
-```yaml
-name: Android CI
-
-on:
- push:
- branches: [ main ]
- pull_request:
- branches: [ main ]
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up JDK 17
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: '17'
-
- - name: Setup Go
- uses: actions/setup-go@v5
- with:
- go-version: '1.22'
-
- - name: Test Go code
- run: |
- cd golib
- go test -v ./...
-
- - name: Build gomobile AAR
- run: |
- go install golang.org/x/mobile/cmd/gomobile@latest
- gomobile init
- cd golib
- gomobile bind -target=android -o mylib.aar .
- cp mylib.aar ../app/libs/
-
- - name: Run unit tests
- run: ./gradlew test
-
- - name: Run instrumented tests
- uses: reactivecircus/android-emulator-runner@v2
- with:
- api-level: 34
- target: google_apis
- arch: x86_64
- script: ./gradlew connectedCheck
-
- - name: Upload test results
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: test-results
- path: app/build/reports/tests/
-
- - name: Upload coverage
- uses: codecov/codecov-action@v3
- with:
- files: app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
-```
-
-## Next Steps
-
-- **Build and publish** - Use the **Build** workflow for release builds
-- **Increase coverage** - Aim for >80% test coverage
-- **Profile performance** - Identify and fix bottlenecks
-- **Monitor crashes** - Set up crash reporting (Firebase Crashlytics)
-
-## Resources
-
-- [ADB Documentation](https://developer.android.com/studio/command-line/adb)
-- [Logcat Command-line Tool](https://developer.android.com/studio/command-line/logcat)
-- [Debug Your App](https://developer.android.com/studio/debug)
-- [Android Testing](https://developer.android.com/training/testing)
-- [Espresso Documentation](https://developer.android.com/training/testing/espresso)
-- [Emulator Documentation](https://developer.android.com/studio/run/emulator)
dots/config/claude/skills/.archive/Android/workflows/Setup.md
@@ -1,496 +0,0 @@
-# Setup Workflow
-
-Configure Android development environment including SDK, NDK, command-line tools, and gomobile for both traditional and NixOS setups.
-
-## Installation Methods
-
-Choose based on your system:
-- **Traditional Linux/Mac/Windows**: Command-line tools or Android Studio
-- **NixOS/Nix**: Declarative configuration with androidenv
-- **Android Studio**: GUI-based (easiest for beginners)
-
-## Traditional Setup (Linux/Mac/Windows)
-
-### Option 1: Command-line Tools (Recommended)
-
-Lightweight, scriptable setup without Android Studio.
-
-#### 1. Download Command-line Tools
-
-```bash
-# Create SDK directory
-mkdir -p ~/Android/Sdk
-cd ~/Android/Sdk
-
-# Download latest cmdline-tools (Linux)
-wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
-
-# Or for Mac
-# wget https://dl.google.com/android/repository/commandlinetools-mac-11076708_latest.zip
-
-# Extract
-unzip commandlinetools-linux-*_latest.zip
-rm commandlinetools-linux-*_latest.zip
-
-# Move to correct location
-mkdir -p cmdline-tools/latest
-mv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || true
-```
-
-#### 2. Set Environment Variables
-
-Add to `~/.bashrc` or `~/.zshrc`:
-
-```bash
-# Android SDK
-export ANDROID_HOME=$HOME/Android/Sdk
-export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
-export PATH=$PATH:$ANDROID_HOME/platform-tools
-export PATH=$PATH:$ANDROID_HOME/emulator
-```
-
-Apply changes:
-```bash
-source ~/.bashrc # or ~/.zshrc
-```
-
-#### 3. Accept Licenses
-
-```bash
-sdkmanager --licenses
-```
-
-Type `y` and press Enter for each license.
-
-#### 4. Install SDK Components
-
-```bash
-# Platform tools (adb, fastboot)
-sdkmanager "platform-tools"
-
-# Latest Android platform (API 34 as of 2026)
-sdkmanager "platforms;android-34"
-
-# Build tools
-sdkmanager "build-tools;34.0.0"
-
-# NDK (required for gomobile)
-sdkmanager "ndk;26.1.10909125"
-
-# Emulator (optional)
-sdkmanager "emulator"
-
-# System image for emulator (optional)
-sdkmanager "system-images;android-34;google_apis;x86_64"
-```
-
-#### 5. Verify Installation
-
-```bash
-# Check SDK manager
-sdkmanager --list
-
-# Check adb
-adb version
-
-# Check environment
-echo $ANDROID_HOME
-ls $ANDROID_HOME
-```
-
-### Option 2: Android Studio
-
-**Pros**: GUI, automatic SDK management, emulator, IDE
-**Cons**: Large download (~1GB), more resources
-
-**Steps:**
-1. Download [Android Studio](https://developer.android.com/studio)
-2. Install and launch
-3. Follow setup wizard to install SDK and tools
-4. SDK installed at: `~/Android/Sdk` (Linux/Mac) or `C:\Users\<user>\AppData\Local\Android\Sdk` (Windows)
-
-## NixOS / Nix Setup
-
-### Why Nix for Android?
-
-**Benefits:**
-- Declarative configuration
-- Reproducible builds
-- Version pinning
-- No manual SDK management
-
-**Challenges:**
-- More complex initial setup
-- Not all Android SDK versions available
-
-### System Configuration (NixOS)
-
-`systems/common/programs/android.nix`:
-```nix
-{ config, pkgs, ... }:
-
-{
- programs.adb.enable = true;
- users.users.youruser.extraGroups = [ "adbusers" ];
-
- environment.systemPackages = with pkgs; [
- android-tools # adb, fastboot
- ];
-}
-```
-
-Apply configuration:
-```bash
-sudo nixos-rebuild switch
-```
-
-### Home Manager Configuration
-
-`home/common/dev/android.nix`:
-```nix
-{ config, pkgs, ... }:
-
-let
- androidSdk = pkgs.androidenv.composeAndroidPackages {
- platformVersions = [ "34" "33" ];
- buildToolsVersions = [ "34.0.0" ];
- includeNDK = true;
- ndkVersion = "26.1.10909125";
- includeEmulator = true;
- includeSystemImages = true;
- systemImageTypes = [ "google_apis" ];
- abiVersions = [ "x86_64" "arm64-v8a" ];
- };
-
-in
-{
- home.packages = with pkgs; [
- androidSdk.androidsdk
- jdk17
- go
- ];
-
- home.sessionVariables = {
- ANDROID_HOME = "${androidSdk.androidsdk}/libexec/android-sdk";
- ANDROID_SDK_ROOT = "${androidSdk.androidsdk}/libexec/android-sdk";
- };
-
- home.sessionPath = [
- "${androidSdk.androidsdk}/libexec/android-sdk/platform-tools"
- "${androidSdk.androidsdk}/libexec/android-sdk/tools"
- "${androidSdk.androidsdk}/libexec/android-sdk/emulator"
- ];
-}
-```
-
-Apply configuration:
-```bash
-home-manager switch --flake .#youruser@hostname
-```
-
-### Nix Development Shell (Project-specific)
-
-Create `flake.nix` in your project:
-
-```nix
-{
- description = "Android development environment";
-
- inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- flake-utils.url = "github:numtide/flake-utils";
- };
-
- outputs = { self, nixpkgs, flake-utils }:
- flake-utils.lib.eachDefaultSystem (system:
- let
- pkgs = nixpkgs.legacyPackages.${system};
-
- androidSdk = pkgs.androidenv.composeAndroidPackages {
- platformVersions = [ "34" ];
- buildToolsVersions = [ "34.0.0" ];
- includeNDK = true;
- ndkVersion = "26.1.10909125";
- };
-
- in
- {
- devShells.default = pkgs.mkShell {
- buildInputs = with pkgs; [
- androidSdk.androidsdk
- jdk17
- gradle
- go
- ];
-
- shellHook = ''
- export ANDROID_HOME="${androidSdk.androidsdk}/libexec/android-sdk"
- export ANDROID_SDK_ROOT="$ANDROID_HOME"
- export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
-
- echo "Android development environment ready!"
- echo "ANDROID_HOME: $ANDROID_HOME"
- '';
- };
- }
- );
-}
-```
-
-Enter development shell:
-```bash
-nix develop
-
-# Or use direnv for automatic activation
-echo "use flake" > .envrc
-direnv allow
-```
-
-### Nix androidenv Options
-
-```nix
-pkgs.androidenv.composeAndroidPackages {
- # Platform versions (Android API levels)
- platformVersions = [ "34" "33" "31" ];
-
- # Build tools versions
- buildToolsVersions = [ "34.0.0" ];
-
- # Include NDK
- includeNDK = true;
- ndkVersion = "26.1.10909125";
-
- # Include Android Emulator
- includeEmulator = true;
-
- # Include system images for emulator
- includeSystemImages = true;
- systemImageTypes = [ "google_apis" "google_apis_playstore" ];
- abiVersions = [ "x86_64" "arm64-v8a" ];
-
- # Include CMake (for C++ projects)
- cmakeVersions = [ "3.22.1" ];
-}
-```
-
-## Post-Installation (All Methods)
-
-### Install Gomobile
-
-```bash
-go install golang.org/x/mobile/cmd/gomobile@latest
-gomobile init
-```
-
-This downloads additional toolchains for cross-compilation.
-
-### Configure Gradle
-
-Create `~/.gradle/gradle.properties`:
-
-```properties
-# Use more memory for Gradle
-org.gradle.jvmargs=-Xmx4096m
-
-# Enable Gradle daemon
-org.gradle.daemon=true
-
-# Enable parallel execution
-org.gradle.parallel=true
-
-# Enable build cache
-org.gradle.caching=true
-
-# Use AndroidX
-android.useAndroidX=true
-android.enableJetifier=true
-```
-
-### Setup Device for Development
-
-#### Enable Developer Options
-
-1. Go to **Settings** → **About phone**
-2. Tap **Build number** 7 times
-3. Go back to **Settings** → **System** → **Developer options**
-4. Enable **USB debugging**
-
-#### Connect Device
-
-```bash
-# Connect via USB
-adb devices
-
-# Should show:
-# List of devices attached
-# 1234567890ABCDEF device
-
-# If shows "unauthorized", check phone for authorization prompt
-```
-
-#### Connect via WiFi (optional)
-
-```bash
-# First connect via USB
-adb tcpip 5555
-
-# Find device IP (Settings → About → Status → IP address)
-# Then connect wirelessly
-adb connect 192.168.1.100:5555
-
-# Disconnect USB cable
-# Verify wireless connection
-adb devices
-```
-
-## Directory Structure
-
-After setup, your SDK should look like:
-
-```
-~/Android/Sdk/
-├── build-tools/
-│ └── 34.0.0/
-│ ├── aapt
-│ ├── aapt2
-│ ├── apksigner
-│ └── ...
-├── cmdline-tools/
-│ └── latest/
-│ └── bin/
-│ ├── sdkmanager
-│ ├── avdmanager
-│ └── ...
-├── emulator/
-│ ├── emulator
-│ └── ...
-├── ndk/
-│ └── 26.1.10909125/
-├── platform-tools/
-│ ├── adb
-│ ├── fastboot
-│ └── ...
-├── platforms/
-│ └── android-34/
-└── system-images/
- └── android-34/
-```
-
-## Troubleshooting
-
-### sdkmanager: command not found
-
-```bash
-# Check ANDROID_HOME
-echo $ANDROID_HOME
-
-# Check PATH
-echo $PATH | grep Android
-
-# Reload shell configuration
-source ~/.bashrc
-```
-
-### adb: command not found
-
-```bash
-# Install platform-tools
-sdkmanager "platform-tools"
-
-# Add to PATH
-export PATH=$PATH:$ANDROID_HOME/platform-tools
-```
-
-### gomobile init fails
-
-```bash
-# Make sure NDK is installed
-sdkmanager "ndk;26.1.10909125"
-
-# Check ANDROID_HOME
-echo $ANDROID_HOME
-
-# Retry
-gomobile init -v
-```
-
-### Device not detected
-
-```bash
-# Check USB debugging is enabled
-adb devices
-
-# Restart adb
-adb kill-server
-adb start-server
-
-# Check USB connection/cable
-# Try different USB port
-```
-
-### NixOS: adb permission denied
-
-```bash
-# Add user to adbusers group (in configuration.nix)
-programs.adb.enable = true;
-users.users.youruser.extraGroups = [ "adbusers" ];
-
-# Rebuild and log out/in
-sudo nixos-rebuild switch
-```
-
-### Nix: Gradle can't find Android SDK
-
-```bash
-# Create local.properties
-echo "sdk.dir=$ANDROID_HOME" > local.properties
-```
-
-## Updating Components
-
-### Traditional Setup
-
-```bash
-# List installed and available packages
-sdkmanager --list
-
-# Update all installed packages
-sdkmanager --update
-
-# Install specific version
-sdkmanager "platforms;android-35"
-sdkmanager "build-tools;35.0.0"
-```
-
-### Nix Setup
-
-```bash
-# Update flake inputs
-nix flake update
-
-# Rebuild configuration
-nixos-rebuild switch # or home-manager switch
-```
-
-### Update Gomobile
-
-```bash
-go install golang.org/x/mobile/cmd/gomobile@latest
-gomobile init
-```
-
-## Next Steps
-
-After successful setup:
-
-- **Build Android app** - Use the **Build** workflow
-- **Debug app** - Use the **Debug** workflow
-- **Create emulator** - See emulator section in **Debug** workflow
-
-## Resources
-
-- [Android Command-line Tools](https://developer.android.com/studio/command-line)
-- [sdkmanager Reference](https://developer.android.com/studio/command-line/sdkmanager)
-- [ADB Documentation](https://developer.android.com/studio/command-line/adb)
-- [Gomobile Installation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
-- [Nixpkgs Android Documentation](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/mobile/androidenv/README.md)
dots/config/claude/skills/.archive/Android/SKILL.md
@@ -1,575 +0,0 @@
----
-name: Android
-description: Android app development with gomobile, Gradle, and modern tooling. USE WHEN building Android apps, working with gomobile bindings, managing Android SDK/NDK, running emulators, debugging with adb, or publishing to Play Store.
----
-
-# Android Development
-
-Expert guidance on Android application development with first-class support for Go integration via gomobile, modern Gradle build system, SDK/NDK management, and deployment workflows.
-
-## Purpose
-
-Guide Android development following official best practices, with specialized support for:
-- **Gomobile integration**: Building Go libraries (AAR) and apps for Android
-- **Modern Gradle**: Kotlin DSL, version catalogs, and build optimization
-- **SDK management**: Android SDK, NDK, and command-line tools
-- **Development workflows**: Build, debug, test, and deploy Android apps
-- **Nix integration**: Using Android tools in NixOS environments
-
-### Context Detection
-
-**This skill activates when:**
-- Current directory contains `build.gradle`, `build.gradle.kts`, or `AndroidManifest.xml`
-- Git repository has Android project structure (`app/`, `gradle/`)
-- User mentions gomobile, Android SDK, adb, or Android development
-- Working with `.aar`, `.apk`, or `.aab` files
-- Commands like `gradle`, `adb`, `gomobile`, or `sdkmanager` are mentioned
-
-## Workflow Routing
-
-**When executing a workflow, output this notification directly:**
-
-```
-Running the **WorkflowName** workflow from the **Android** skill...
-```
-
-| Workflow | Trigger | File |
-|----------|---------|------|
-| **Setup** | "setup android dev", "install android sdk", "android environment", "nixos android", "android on nix" | `workflows/Setup.md` |
-| **Build** | "build apk", "build aab", "gradle build", "assemble", "build go library", "create AAR", "gomobile bind", "gomobile build", "publish to play store", "sign apk", "release build", "keystore", "gradle dependencies" | `workflows/Build.md` |
-| **Debug** | "debug android", "adb logcat", "android logs", "troubleshoot app", "android emulator", "avd", "virtual device", "start emulator", "android tests", "instrumentation", "ui tests", "espresso" | `workflows/Debug.md` |
-
-## Core Principles
-
-1. **Gomobile for Go integration**: Use `gomobile bind` for libraries, `gomobile build` for standalone apps
-2. **Modern Gradle**: Prefer Kotlin DSL (`.gradle.kts`) over Groovy
-3. **Version catalogs**: Centralize dependency versions in `libs.versions.toml`
-4. **Multi-architecture**: Support arm64-v8a, armeabi-v7a, x86_64, x86
-5. **Type safety**: Handle Go ↔ Java/Kotlin type conversions carefully
-6. **SDK tools**: Use `sdkmanager` and `avdmanager` for command-line management
-7. **Reproducible builds**: Lock dependency versions and use consistent tooling
-
-## Android Project Structure
-
-### Standard Android Project
-
-```
-myapp/
-├── app/
-│ ├── src/
-│ │ ├── main/
-│ │ │ ├── java/com/example/myapp/
-│ │ │ │ └── MainActivity.kt
-│ │ │ ├── res/
-│ │ │ │ ├── layout/
-│ │ │ │ ├── values/
-│ │ │ │ └── drawable/
-│ │ │ └── AndroidManifest.xml
-│ │ ├── test/ # Unit tests
-│ │ └── androidTest/ # Instrumentation tests
-│ ├── build.gradle.kts
-│ └── proguard-rules.pro
-├── gradle/
-│ └── libs.versions.toml # Version catalog
-├── build.gradle.kts # Root build file
-├── settings.gradle.kts # Project settings
-├── gradle.properties
-└── local.properties # SDK location (not in git)
-```
-
-### Android Project with Gomobile
-
-```
-myapp/
-├── app/
-│ ├── libs/
-│ │ └── mylib.aar # Generated by gomobile bind
-│ ├── src/main/
-│ │ ├── java/
-│ │ └── AndroidManifest.xml
-│ └── build.gradle.kts
-├── golib/ # Go library source
-│ ├── mylib.go
-│ └── go.mod
-├── build-aar.sh # Script to build AAR with gomobile
-└── gradle/
-```
-
-## Gomobile Overview
-
-### What is Gomobile?
-
-Gomobile enables Go code to run on Android (and iOS). Two main modes:
-
-1. **gomobile bind**: Create native library (AAR) from Go package
- - Go code becomes Android library
- - Called from Java/Kotlin
- - Best for integrating Go into existing Android apps
-
-2. **gomobile build**: Create standalone Android app
- - Entire app written in Go
- - Uses gomobile app package
- - Limited Android UI capabilities
-
-### Type Mapping (Go ↔ Java/Kotlin)
-
-| Go Type | Java Type | Kotlin Type | Notes |
-|---------|-----------|-------------|-------|
-| `bool` | `boolean` | `Boolean` | |
-| `int`, `int32` | `int` | `Int` | 32-bit |
-| `int64` | `long` | `Long` | 64-bit |
-| `float32` | `float` | `Float` | |
-| `float64` | `double` | `Double` | |
-| `string` | `String` | `String` | |
-| `[]byte` | `byte[]` | `ByteArray` | |
-| `error` | `Exception` | `Exception` | Becomes checked exception |
-| `interface` | `interface` | `interface` | Must have exported methods |
-| `struct` | `class` | `class` | Exported fields become getters/setters |
-
-### Gomobile Limitations
-
-**What works:**
-- Exported functions and methods
-- Basic types (int, string, float, bool)
-- Slices of basic types
-- Interfaces with exported methods
-- Structs with exported fields
-- Callbacks via interfaces
-
-**What doesn't work:**
-- Generic types (Go 1.18+ generics)
-- Maps (use structs or custom types)
-- Channels (use callbacks instead)
-- Unexported types/functions
-- Complex nested types
-- Variadic functions
-
-## Android SDK Management
-
-### SDK Structure
-
-```
-$ANDROID_HOME/
-├── build-tools/
-│ └── 34.0.0/ # Build tools version
-├── cmdline-tools/
-│ └── latest/
-│ └── bin/
-│ ├── sdkmanager
-│ └── avdmanager
-├── emulator/ # Android Emulator
-├── ndk/
-│ └── 26.1.10909125/ # NDK version
-├── platform-tools/ # adb, fastboot
-└── platforms/
- └── android-34/ # API level 34
-```
-
-### Essential Tools
-
-- **sdkmanager**: Install and update SDK packages
-- **avdmanager**: Create and manage Android Virtual Devices (AVDs)
-- **adb**: Android Debug Bridge (device communication)
-- **emulator**: Run Android Virtual Devices
-- **gradle**: Build system
-
-## Gradle Configuration
-
-### Version Catalog (`gradle/libs.versions.toml`)
-
-```toml
-[versions]
-agp = "8.3.0" # Android Gradle Plugin
-kotlin = "1.9.22"
-compileSdk = "34"
-minSdk = "24"
-targetSdk = "34"
-
-[libraries]
-androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.12.0" }
-androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version = "1.6.1" }
-material = { group = "com.google.android.material", name = "material", version = "1.11.0" }
-
-[plugins]
-android-application = { id = "com.android.application", version.ref = "agp" }
-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
-```
-
-### Root `build.gradle.kts`
-
-```kotlin
-plugins {
- alias(libs.plugins.android.application) apply false
- alias(libs.plugins.kotlin.android) apply false
-}
-```
-
-### App `build.gradle.kts`
-
-```kotlin
-plugins {
- alias(libs.plugins.android.application)
- alias(libs.plugins.kotlin.android)
-}
-
-android {
- namespace = "com.example.myapp"
- compileSdk = 34
-
- defaultConfig {
- applicationId = "com.example.myapp"
- minSdk = 24
- targetSdk = 34
- versionCode = 1
- versionName = "1.0"
- }
-
- buildTypes {
- release {
- isMinifyEnabled = true
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
- }
-
- kotlinOptions {
- jvmTarget = "17"
- }
-}
-
-dependencies {
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.appcompat)
- implementation(libs.material)
-
- // Add gomobile AAR
- implementation(files("libs/mylib.aar"))
-}
-```
-
-## Testing
-
-### Test Types
-
-1. **Local Unit Tests** (`src/test/`)
- - Run on JVM
- - Fast, no Android device needed
- - Use JUnit, Mockito
-
-2. **Instrumentation Tests** (`src/androidTest/`)
- - Run on Android device/emulator
- - Test Android framework APIs
- - Use AndroidJUnit4, Espresso
-
-3. **UI Tests**
- - Test user interface
- - Use Espresso, UI Automator
- - Run on device/emulator
-
-### Example Unit Test
-
-```kotlin
-// src/test/java/com/example/myapp/CalculatorTest.kt
-import org.junit.Test
-import org.junit.Assert.*
-
-class CalculatorTest {
- @Test
- fun addition_isCorrect() {
- val calculator = Calculator()
- assertEquals(4, calculator.add(2, 2))
- }
-}
-```
-
-### Example Instrumentation Test
-
-```kotlin
-// src/androidTest/java/com/example/myapp/MainActivityTest.kt
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MainActivityTest {
- @get:Rule
- val activityRule = ActivityScenarioRule(MainActivity::class.java)
-
- @Test
- fun testButtonClick() {
- // Test implementation
- }
-}
-```
-
-## Signing and Publishing
-
-### Debug Signing
-
-Android Studio automatically creates debug keystore:
-- Location: `~/.android/debug.keystore`
-- Password: `android`
-- Alias: `androiddebugkey`
-
-### Release Signing
-
-Create release keystore:
-```bash
-keytool -genkey -v \
- -keystore release.keystore \
- -alias myapp \
- -keyalg RSA \
- -keysize 2048 \
- -validity 10000
-```
-
-Configure in `app/build.gradle.kts`:
-```kotlin
-android {
- signingConfigs {
- create("release") {
- storeFile = file("../release.keystore")
- storePassword = System.getenv("KEYSTORE_PASSWORD")
- keyAlias = "myapp"
- keyPassword = System.getenv("KEY_PASSWORD")
- }
- }
-
- buildTypes {
- release {
- signingConfig = signingConfigs.getByName("release")
- }
- }
-}
-```
-
-### Build Types
-
-- **APK**: Android Package (direct install)
- ```bash
- ./gradlew assembleRelease
- # Output: app/build/outputs/apk/release/app-release.apk
- ```
-
-- **AAB**: Android App Bundle (Play Store)
- ```bash
- ./gradlew bundleRelease
- # Output: app/build/outputs/bundle/release/app-release.aab
- ```
-
-## Common Commands
-
-### Gradle
-
-```bash
-# Build debug APK
-./gradlew assembleDebug
-
-# Build release AAB
-./gradlew bundleRelease
-
-# Install on connected device
-./gradlew installDebug
-
-# Clean build
-./gradlew clean
-
-# List tasks
-./gradlew tasks
-
-# Check dependencies
-./gradlew dependencies
-```
-
-### ADB (Android Debug Bridge)
-
-```bash
-# List devices
-adb devices
-
-# Install APK
-adb install app-debug.apk
-
-# Uninstall app
-adb uninstall com.example.myapp
-
-# View logs
-adb logcat
-
-# View logs for specific app
-adb logcat | grep com.example.myapp
-
-# Clear app data
-adb shell pm clear com.example.myapp
-
-# Take screenshot
-adb shell screencap /sdcard/screen.png
-adb pull /sdcard/screen.png
-
-# Get device properties
-adb shell getprop
-
-# Start activity
-adb shell am start -n com.example.myapp/.MainActivity
-```
-
-### Gomobile
-
-```bash
-# Install gomobile
-go install golang.org/x/mobile/cmd/gomobile@latest
-gomobile init
-
-# Build AAR for Android
-gomobile bind -target=android -o mylib.aar ./golib
-
-# Build AAR for specific architectures
-gomobile bind -target=android/arm64,android/amd64 -o mylib.aar ./golib
-
-# Build standalone Android app
-gomobile build -target=android ./cmd/myapp
-
-# Build with custom app ID
-gomobile build -target=android -appid=com.example.myapp ./cmd/myapp
-```
-
-## Nix Integration
-
-This repository uses NixOS. Android SDK can be configured via:
-
-### Using nixpkgs Android SDK
-
-```nix
-{ pkgs, ... }:
-{
- home.packages = with pkgs; [
- android-studio
- android-tools # adb, fastboot
- ];
-
- # Or use androidenv for more control
- android-sdk = pkgs.androidenv.composeAndroidPackages {
- platformVersions = [ "34" ];
- buildToolsVersions = [ "34.0.0" ];
- includeNDK = true;
- ndkVersion = "26.1.10909125";
- includeEmulator = true;
- };
-}
-```
-
-See **Setup** workflow for detailed Nix integration.
-
-## Best Practices
-
-### General
-
-- Use Kotlin over Java for new code
-- Follow Material Design guidelines
-- Support multiple screen sizes and orientations
-- Handle configuration changes properly
-- Use Android Architecture Components (ViewModel, LiveData, Room)
-- Implement proper error handling
-- Add crash reporting (Firebase Crashlytics, Sentry)
-
-### Gomobile Specific
-
-- Keep Go API simple and flat
-- Avoid complex nested types
-- Use interfaces for callbacks
-- Document type conversions
-- Test Go code separately from Android integration
-- Consider performance implications of crossing Go/Java boundary
-- Use goroutines carefully (they don't map to Android threads)
-
-### Security
-
-- Never commit keystores to version control
-- Use environment variables for secrets
-- Enable ProGuard/R8 for release builds
-- Validate all user inputs
-- Use HTTPS for network communication
-- Store sensitive data in Android Keystore
-- Follow OWASP Mobile Security guidelines
-
-### Performance
-
-- Use build variants for different configurations
-- Enable code shrinking and obfuscation
-- Optimize images and resources
-- Use vector drawables when possible
-- Profile with Android Profiler
-- Monitor memory usage
-- Implement proper caching strategies
-
-## Resources
-
-### Official Documentation
-
-- [Android Developers](https://developer.android.com/)
-- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
-- [Gradle Documentation](https://docs.gradle.org/)
-- [Android Studio](https://developer.android.com/studio)
-
-### Gomobile Resources
-
-- [Gomobile Wiki](https://github.com/golang/go/wiki/Mobile)
-- [Gomobile Examples](https://github.com/golang/mobile/tree/master/example)
-- [Go on Android](https://godoc.org/golang.org/x/mobile/app)
-
-### Tools
-
-- [Android SDK Command-Line Tools](https://developer.android.com/studio/command-line)
-- [ADB Documentation](https://developer.android.com/studio/command-line/adb)
-- [Gradle Build Tool](https://gradle.org/)
-
-### Best Practices
-
-- [Android Best Practices](https://developer.android.com/topic/best-practices)
-- [Kotlin Style Guide](https://developer.android.com/kotlin/style-guide)
-- [Material Design](https://material.io/design)
-
-## Examples
-
-**Example 1: Build Go library as Android AAR**
-```
-User: "I have a Go package with networking code. How do I use it in my Android app?"
-→ Invokes Build workflow
-→ Checks gomobile installation
-→ Generates AAR with `gomobile bind`
-→ Shows how to add AAR to build.gradle.kts
-→ Provides example of calling Go code from Kotlin
-```
-
-**Example 2: Debug Android app crash**
-```
-User: "My app crashes when I click the submit button. How do I debug this?"
-→ Invokes Debug workflow
-→ Shows how to use `adb logcat` to view crash logs
-→ Helps filter logs for the specific app
-→ Explains stack trace interpretation
-→ Suggests debugging strategies (breakpoints, logging)
-```
-
-**Example 3: Setup Android development on NixOS**
-```
-User: "How do I set up Android development on my NixOS machine?"
-→ Invokes Setup workflow
-→ Shows nixpkgs Android SDK configuration
-→ Explains how to compose Android packages with androidenv
-→ Configures environment variables (ANDROID_HOME, PATH)
-→ Verifies setup with adb and gradle commands
-```
-
----
-
-**Philosophy**: Android development should embrace modern tooling and practices. Gomobile provides a powerful bridge between Go's excellent concurrency and networking capabilities and Android's vast ecosystem. Use the right tool for each layer: Go for backend logic and performance-critical code, Kotlin for UI and Android framework integration.
dots/config/claude/skills/.archive/Art/workflows/Mermaid.md
@@ -1,164 +0,0 @@
-# Mermaid Workflow
-
-Create Mermaid diagrams for technical documentation and visualization.
-
-## When to Use Mermaid
-
-Mermaid is ideal for:
-- **Flowcharts**: Process flows, algorithms, decision trees
-- **Sequence diagrams**: API interactions, protocol flows
-- **State diagrams**: System states and transitions
-- **Class diagrams**: Object relationships
-- **Gantt charts**: Project timelines
-- **Git graphs**: Branch and merge visualizations
-
-## Mermaid Diagram Types
-
-### Flowchart (Most Common)
-
-```mermaid
-flowchart LR
- A[Start] --> B[Process]
- B --> C{Decision}
- C -->|Yes| D[Action 1]
- C -->|No| E[Action 2]
-```
-
-**Directions:**
-- `LR`: Left to Right
-- `TD` or `TB`: Top Down/Top to Bottom
-- `RL`: Right to Left
-- `BT`: Bottom to Top
-
-**Node Shapes:**
-- `[Text]`: Rectangle
-- `(Text)`: Rounded rectangle
-- `{Text}`: Diamond (decision)
-- `([Text])`: Stadium shape
-- `[[Text]]`: Subroutine
-- `[(Text)]`: Cylindrical (database)
-
-### Sequence Diagram
-
-```mermaid
-sequenceDiagram
- participant Client
- participant API
- participant DB
-
- Client->>API: Request
- API->>DB: Query
- DB-->>API: Result
- API-->>Client: Response
-```
-
-**Arrow types:**
-- `->`: Solid line
-- `-->`: Dotted line
-- `->>`: Solid arrow
-- `-->>`: Dotted arrow
-
-### State Diagram
-
-```mermaid
-stateDiagram-v2
- [*] --> Idle
- Idle --> Running: start()
- Running --> Paused: pause()
- Paused --> Running: resume()
- Running --> [*]: stop()
-```
-
-### Class Diagram
-
-```mermaid
-classDiagram
- class Animal {
- +String name
- +int age
- +makeSound()
- }
- class Dog {
- +String breed
- +bark()
- }
- Animal <|-- Dog
-```
-
-### Gantt Chart
-
-```mermaid
-gantt
- title Project Timeline
- dateFormat YYYY-MM-DD
- section Phase 1
- Task 1: 2024-01-01, 30d
- Task 2: 2024-01-15, 20d
- section Phase 2
- Task 3: 2024-02-01, 25d
-```
-
-## Best Practices
-
-1. **Keep it simple**: Don't overcomplicate
-2. **Use clear labels**: Make text descriptive
-3. **Logical flow**: Follow natural reading direction
-4. **Group related items**: Use subgraphs when appropriate
-5. **Test rendering**: Ensure it renders correctly
-
-## Common Patterns
-
-### Decision Tree
-
-```mermaid
-flowchart TD
- Start[User Request] --> Auth{Authenticated?}
- Auth -->|No| Login[Redirect to Login]
- Auth -->|Yes| Check{Has Permission?}
- Check -->|No| Error[Show Error]
- Check -->|Yes| Process[Process Request]
-```
-
-### API Flow
-
-```mermaid
-sequenceDiagram
- participant C as Client
- participant A as API
- participant D as Database
-
- C->>A: POST /api/data
- A->>A: Validate Input
- A->>D: INSERT data
- D-->>A: Success
- A-->>C: 201 Created
-```
-
-### State Machine
-
-```mermaid
-stateDiagram-v2
- [*] --> Draft
- Draft --> Review: Submit
- Review --> Approved: Approve
- Review --> Draft: Reject
- Approved --> Published: Publish
- Published --> [*]
-```
-
-## Output Format
-
-Present Mermaid diagrams with:
-1. Brief description of what it shows
-2. The Mermaid code block
-3. Explanation of key elements
-
-```
-Here's a flowchart showing the deployment process:
-
-[Mermaid diagram code]
-
-The diagram illustrates:
-- [Key element 1 explanation]
-- [Key element 2 explanation]
-```
dots/config/claude/skills/.archive/Art/workflows/Visualize.md
@@ -1,115 +0,0 @@
-# Visualize Workflow
-
-Create visual content to explain concepts, processes, or relationships.
-
-## Steps
-
-### 1. Understand the Content
-
-Ask:
-- What needs to be visualized?
-- What's the key insight or relationship?
-- Who's the audience?
-- Where will this be used?
-
-### 2. Choose the Right Format
-
-**Mermaid Diagrams** - When you need:
-- Flowcharts (process flows, decision trees)
-- Sequence diagrams (interactions over time)
-- State diagrams (system states and transitions)
-- Class/ER diagrams (relationships)
-- Gantt charts (timelines with tasks)
-
-**Tables** - When you need:
-- Comparisons (side-by-side evaluation)
-- Feature matrices
-- Decision matrices
-
-**ASCII Art** - When you need:
-- Terminal-friendly diagrams
-- Code comment diagrams
-- Simple box-and-arrow layouts
-
-**Structured Text** - When you need:
-- Hierarchies (org charts, file trees)
-- Simple timelines
-- Process steps
-
-### 3. Create the Visualization
-
-Follow these principles:
-- **Clear labels**: Every element should be clearly labeled
-- **Logical flow**: Left-to-right or top-to-bottom
-- **Minimal complexity**: Only include what's necessary
-- **Consistent style**: Use consistent shapes and connections
-
-### 4. Mermaid Syntax Reference
-
-**Flowchart:**
-```mermaid
-flowchart TD
- A[Start] --> B{Decision}
- B -->|Yes| C[Action 1]
- B -->|No| D[Action 2]
- C --> E[End]
- D --> E
-```
-
-**Sequence Diagram:**
-```mermaid
-sequenceDiagram
- participant A as Alice
- participant B as Bob
- A->>B: Request
- B-->>A: Response
-```
-
-**State Diagram:**
-```mermaid
-stateDiagram-v2
- [*] --> Idle
- Idle --> Processing: Start
- Processing --> Complete: Finish
- Complete --> [*]
-```
-
-### 5. Present and Refine
-
-- Show the visualization
-- Explain what it represents
-- Ask if refinements are needed
-- Iterate based on feedback
-
-## Output Format
-
-Present the visualization with context:
-
-```
-Here's a [type] diagram showing [what]:
-
-[The actual diagram/visualization]
-
-Key elements:
-- [Element 1]: [What it represents]
-- [Element 2]: [What it represents]
-
-This visualization shows [the main insight].
-```
-
-## Examples by Use Case
-
-### Process Flow
-Use flowchart when showing steps and decisions
-
-### System Architecture
-Use flowchart with boxes for components and arrows for data flow
-
-### Comparison
-Use table format for side-by-side evaluation
-
-### Timeline
-Use Gantt chart or ordered list depending on complexity
-
-### Relationships
-Use ER diagram or class diagram for entity relationships
dots/config/claude/skills/.archive/Art/SKILL.md
@@ -1,101 +0,0 @@
----
-name: Art
-description: Visual content generation and diagram creation. USE WHEN user needs diagrams, flowcharts, technical visualizations, or any visual content to explain concepts.
----
-
-# Art
-
-Visual content generation skill for creating diagrams, flowcharts, and technical visualizations to support documentation and communication.
-
-## Purpose
-
-This skill helps create visual content when text alone isn't sufficient.
-
-### Context Detection
-
-**This skill activates when:**
-- User asks for a diagram, flowchart, or visualization
-- User mentions creating visual representations of concepts
-- User wants to illustrate architecture, processes, or relationships
-- User asks for Mermaid diagrams, ASCII art, or graphical explanations
-- User explicitly says "show me a diagram" or "draw a flowchart"
-
-## Use Cases
-
-It's particularly useful for:
-- Technical diagrams and architecture visualizations
-- Flowcharts and process diagrams
-- Concept maps and taxonomies
-- Timeline visualizations
-- Comparison charts
-
-## Visual Approach
-
-When creating diagrams, consider:
-- **Clarity**: Make the diagram easy to understand
-- **Purpose**: What insight should the visual provide?
-- **Format**: Which format best serves the content (Mermaid, ASCII, etc.)?
-- **Context**: Will this be in documentation, a presentation, or for quick reference?
-
-## Supported Formats
-
-### Mermaid Diagrams
-Best for:
-- Flowcharts
-- Sequence diagrams
-- State diagrams
-- Class diagrams
-- Gantt charts
-
-### ASCII Art
-Best for:
-- Simple diagrams in plain text
-- Terminal-friendly visualizations
-- Code comments
-
-### Structured Text
-Best for:
-- Comparisons (tables)
-- Hierarchies (indented lists)
-- Timelines (ordered lists)
-
-## Workflow Routing
-
-| Workflow | Trigger | File |
-|----------|---------|------|
-| **Visualize** | "create a diagram", "visualize this" | `workflows/Visualize.md` |
-| **Mermaid** | "mermaid diagram", "flowchart" | `workflows/Mermaid.md` |
-
-## Examples
-
-**Example 1: Architecture Diagram**
-```
-User: "Create a diagram showing the NixOS deployment flow"
-→ Invokes Visualize workflow
-→ Determines Mermaid flowchart is appropriate
-→ Creates flowchart showing: local changes → build → deploy → activate
-```
-
-**Example 2: Comparison Chart**
-```
-User: "Compare stable vs unstable NixOS approaches"
-→ Invokes Visualize workflow
-→ Creates comparison table
-→ Shows trade-offs clearly
-```
-
-**Example 3: Process Flow**
-```
-User: "Show the git commit workflow as a diagram"
-→ Invokes Mermaid workflow
-→ Creates flowchart with decision points
-→ Shows happy path and error handling
-```
-
-## Integration
-
-This skill complements:
-- **Documentation**: Add diagrams to markdown docs
-- **Architecture planning**: Visualize system design
-- **Troubleshooting**: Map out problem flows
-- **Communication**: Explain complex concepts visually
dots/config/claude/skills/.archive/Journal/tools/get-location
@@ -1,224 +0,0 @@
-#!/usr/bin/env bash
-# get-location - Get current GPS coordinates
-# Copyright (C) 2025 Vincent Demeester
-# Part of Claude Code Journal skill
-
-set -euo pipefail
-
-# Configuration
-CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/journal-location"
-CACHE_TIMEOUT=3600 # 1 hour in seconds
-
-# Colors for output
-RED='\033[0;31m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "${DEBUG:-0}" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-usage() {
- cat <<EOF
-get-location - Get current GPS coordinates
-
-USAGE:
- get-location [options]
-
-OPTIONS:
- --json Output as JSON
- --city Output city name only
- --coords Output coordinates only (lat,lon)
- --all Output city and coordinates (default)
- --no-cache Don't use cached location
- --help, -h Show this help
-
-OUTPUT FORMATS:
- Default: Saint-Denis (48.9356,2.3539)
- --json: {"city":"Saint-Denis","lat":"48.9356","lon":"2.3539"}
- --city: Saint-Denis
- --coords: 48.9356,2.3539
-
-LOCATION SOURCES:
- 1. IP-based geolocation (ipinfo.io) - automatic, approximate
- 2. Cache (${CACHE_FILE}) - reuses location for ${CACHE_TIMEOUT}s
-
-EXAMPLES:
- # Get location with city name
- get-location
-
- # Get just coordinates for journelly-manager
- get-location --coords
-
- # Get JSON output
- get-location --json
-
- # Force refresh (ignore cache)
- get-location --no-cache
-
-NOTES:
- - IP-based location is approximate (city-level accuracy)
- - Location is cached for 1 hour to reduce API calls
- - No API key required
-
-VERSION:
- 1.0.0
-
-AUTHOR:
- Vincent Demeester <vincent@demeester.fr>
-EOF
-}
-
-# Check if cache is valid
-is_cache_valid() {
- [[ -f "$CACHE_FILE" ]] || return 1
-
- local cache_age
- cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
-
- [[ $cache_age -lt $CACHE_TIMEOUT ]]
-}
-
-# Get location from cache
-get_from_cache() {
- if [[ -f "$CACHE_FILE" ]]; then
- cat "$CACHE_FILE"
- return 0
- fi
- return 1
-}
-
-# Get location from IP geolocation
-get_from_ip() {
- debug "Fetching location from ipinfo.io"
-
- local response
- response=$(curl -s --max-time 5 https://ipinfo.io/json 2>/dev/null) || {
- error "Failed to fetch location from ipinfo.io"
- }
-
- # Validate response
- if ! echo "$response" | jq -e '.loc' >/dev/null 2>&1; then
- error "Invalid response from ipinfo.io"
- fi
-
- echo "$response"
-}
-
-# Parse and format output
-format_output() {
- local data="$1"
- local format="${2:-all}"
-
- local city
- local loc
- local lat
- local lon
-
- city=$(echo "$data" | jq -r '.city // "Unknown"')
- loc=$(echo "$data" | jq -r '.loc // ""')
-
- if [[ -z "$loc" ]]; then
- error "No location data available"
- fi
-
- lat=$(echo "$loc" | cut -d, -f1)
- lon=$(echo "$loc" | cut -d, -f2)
-
- case "$format" in
- json)
- jq -n \
- --arg city "$city" \
- --arg lat "$lat" \
- --arg lon "$lon" \
- '{city: $city, lat: $lat, lon: $lon}'
- ;;
- city)
- echo "$city"
- ;;
- coords)
- echo "$lat,$lon"
- ;;
- lat)
- echo "$lat"
- ;;
- lon)
- echo "$lon"
- ;;
- all)
- echo "$city ($lat,$lon)"
- ;;
- *)
- error "Unknown format: $format"
- ;;
- esac
-}
-
-# Main function
-main() {
- local use_cache=true
- local format="all"
-
- # Parse arguments
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --json)
- format="json"
- ;;
- --city)
- format="city"
- ;;
- --coords)
- format="coords"
- ;;
- --lat)
- format="lat"
- ;;
- --lon)
- format="lon"
- ;;
- --all)
- format="all"
- ;;
- --no-cache)
- use_cache=false
- ;;
- --help|-h)
- usage
- exit 0
- ;;
- *)
- error "Unknown option: $1. Use --help for usage."
- ;;
- esac
- shift
- done
-
- local data=""
-
- # Try cache first
- if [[ "$use_cache" == "true" ]] && is_cache_valid; then
- debug "Using cached location"
- data=$(get_from_cache)
- else
- # Fetch from IP geolocation
- data=$(get_from_ip)
-
- # Cache the result
- mkdir -p "$(dirname "$CACHE_FILE")"
- echo "$data" > "$CACHE_FILE"
- debug "Location cached to $CACHE_FILE"
- fi
-
- # Format and output
- format_output "$data" "$format"
-}
-
-main "$@"
dots/config/claude/skills/.archive/Journal/tools/get-location-el
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# get-location-el - Get location using Emacs Lisp
-# Copyright (C) 2025 Vincent Demeester
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-ELISP_FILE="$SCRIPT_DIR/journelly-location-weather.el"
-
-FORMAT="${1:-json}"
-
-exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-location \"$FORMAT\")" \
- 2>/dev/null
dots/config/claude/skills/.archive/Journal/tools/get-weather
@@ -1,355 +0,0 @@
-#!/usr/bin/env bash
-# get-weather - Get current weather conditions
-# Copyright (C) 2025 Vincent Demeester
-# Part of Claude Code Journal skill
-
-set -euo pipefail
-
-# Configuration
-CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/journal-weather"
-CACHE_TIMEOUT=1800 # 30 minutes in seconds
-
-# Colors for output
-RED='\033[0;31m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "${DEBUG:-0}" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-usage() {
- cat <<EOF
-get-weather - Get current weather conditions
-
-USAGE:
- get-weather [location] [options]
-
-ARGUMENTS:
- location Optional location (city name, coordinates, airport code)
- If not provided, uses current location from IP
-
-OPTIONS:
- --json Output as JSON with all fields
- --temperature Output temperature only (e.g., "15,2°C")
- --condition Output condition only (e.g., "Partly cloudy")
- --symbol Output weather symbol only (e.g., "cloud.sun")
- --all Output formatted: temp, condition, symbol (default)
- --no-cache Don't use cached weather data
- --help, -h Show this help
-
-OUTPUT FORMATS:
- Default: 15,2°C Partly cloudy (cloud.sun)
- --json: {"temperature":"15,2°C","condition":"Partly cloudy","symbol":"cloud.sun"}
- --temperature: 15,2°C
- --condition: Partly cloudy
- --symbol: cloud.sun
-
-WEATHER SYMBOLS (iOS SF Symbols compatible):
- sun.max - Clear/Sunny
- cloud.sun - Partly cloudy
- cloud - Cloudy/Overcast
- cloud.rain - Rain
- cloud.drizzle - Light rain/Drizzle
- cloud.heavyrain - Heavy rain
- cloud.snow - Snow
- cloud.sleet - Sleet
- cloud.fog - Fog/Mist
- smoke - Haze/Smoke
- wind - Windy
- cloud.bolt - Thunderstorm
- moon.stars - Clear night
- cloud.moon - Partly cloudy night
- cloud.moon.rain - Rainy night
-
-EXAMPLES:
- # Get weather for current location
- get-weather
-
- # Get weather for specific city
- get-weather Paris
-
- # Get just temperature for journelly-manager
- get-weather --temperature
-
- # Get all fields as JSON
- get-weather --json
-
- # Get weather for coordinates
- get-weather "48.8566,2.3522"
-
- # Force refresh (ignore cache)
- get-weather --no-cache
-
-NOTES:
- - Uses wttr.in weather service (no API key required)
- - Weather is cached for 30 minutes to reduce API calls
- - Automatic location detection via IP if no location specified
-
-VERSION:
- 1.0.0
-
-AUTHOR:
- Vincent Demeester <vincent@demeester.fr>
-EOF
-}
-
-# Map wttr.in weather codes to iOS SF Symbol names
-map_weather_symbol() {
- local desc="$1"
- local is_night="${2:-false}"
-
- desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]')
-
- # Night conditions
- if [[ "$is_night" == "true" ]]; then
- case "$desc" in
- *partly*cloudy*|*partly*clear*)
- echo "cloud.moon"
- ;;
- *clear*|*sunny*)
- echo "moon.stars"
- ;;
- *rain*|*drizzle*|*shower*)
- echo "cloud.moon.rain"
- ;;
- *)
- echo "cloud.moon"
- ;;
- esac
- return
- fi
-
- # Day conditions
- case "$desc" in
- *partly*cloudy*|*partly*clear*)
- echo "cloud.sun"
- ;;
- *clear*|*sunny*)
- echo "sun.max"
- ;;
- *cloudy*|*overcast*)
- echo "cloud"
- ;;
- *heavy*rain*)
- echo "cloud.heavyrain"
- ;;
- *drizzle*|*light*rain*)
- echo "cloud.drizzle"
- ;;
- *rain*|*shower*)
- echo "cloud.rain"
- ;;
- *snow*)
- echo "cloud.snow"
- ;;
- *sleet*)
- echo "cloud.sleet"
- ;;
- *fog*|*mist*)
- echo "cloud.fog"
- ;;
- *haze*|*smoke*)
- echo "smoke"
- ;;
- *wind*)
- echo "wind"
- ;;
- *thunder*|*storm*)
- echo "cloud.bolt"
- ;;
- *)
- echo "cloud"
- ;;
- esac
-}
-
-# Check if it's currently night time (simple heuristic based on hour)
-is_night() {
- local hour
- hour=$(date +%H)
- # Consider 20:00-06:00 as night
- [[ $hour -ge 20 || $hour -lt 6 ]]
-}
-
-# Check if cache is valid
-is_cache_valid() {
- local location="${1:-auto}"
- local cache_key="${CACHE_FILE}_${location// /_}"
-
- [[ -f "$cache_key" ]] || return 1
-
- local cache_age
- cache_age=$(($(date +%s) - $(stat -c %Y "$cache_key" 2>/dev/null || echo 0)))
-
- [[ $cache_age -lt $CACHE_TIMEOUT ]]
-}
-
-# Get weather from cache
-get_from_cache() {
- local location="${1:-auto}"
- local cache_key="${CACHE_FILE}_${location// /_}"
-
- if [[ -f "$cache_key" ]]; then
- cat "$cache_key"
- return 0
- fi
- return 1
-}
-
-# Get weather from wttr.in
-get_from_api() {
- local location="${1:-}"
-
- debug "Fetching weather from wttr.in for: ${location:-current location}"
-
- local url="https://wttr.in/${location}?format=j1"
- local response
-
- response=$(curl -s --max-time 10 "$url" 2>/dev/null) || {
- error "Failed to fetch weather from wttr.in"
- }
-
- # Validate response
- if ! echo "$response" | jq -e '.current_condition' >/dev/null 2>&1; then
- error "Invalid response from wttr.in"
- fi
-
- echo "$response"
-}
-
-# Parse and format output
-format_output() {
- local data="$1"
- local format="${2:-all}"
-
- local temp_c
- local condition
- local is_night_time
-
- # Extract data
- temp_c=$(echo "$data" | jq -r '.current_condition[0].temp_C // ""')
- condition=$(echo "$data" | jq -r '.current_condition[0].weatherDesc[0].value // ""')
-
- if [[ -z "$temp_c" || -z "$condition" ]]; then
- error "No weather data available"
- fi
-
- # Format temperature (use comma as decimal separator to match Journelly format)
- local temperature="${temp_c}°C"
-
- # Determine if it's night
- if is_night; then
- is_night_time="true"
- else
- is_night_time="false"
- fi
-
- # Map to symbol
- local symbol
- symbol=$(map_weather_symbol "$condition" "$is_night_time")
-
- case "$format" in
- json)
- jq -n \
- --arg temp "$temperature" \
- --arg cond "$condition" \
- --arg sym "$symbol" \
- '{temperature: $temp, condition: $cond, symbol: $sym}'
- ;;
- temperature)
- echo "$temperature"
- ;;
- condition)
- echo "$condition"
- ;;
- symbol)
- echo "$symbol"
- ;;
- all)
- echo "$temperature $condition ($symbol)"
- ;;
- journelly)
- # Format for journelly-manager command line
- echo "--temperature=\"$temperature\" --condition=\"$condition\" --symbol=\"$symbol\""
- ;;
- *)
- error "Unknown format: $format"
- ;;
- esac
-}
-
-# Main function
-main() {
- local use_cache=true
- local format="all"
- local location=""
-
- # Parse arguments
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --json)
- format="json"
- ;;
- --temperature)
- format="temperature"
- ;;
- --condition)
- format="condition"
- ;;
- --symbol)
- format="symbol"
- ;;
- --all)
- format="all"
- ;;
- --journelly)
- format="journelly"
- ;;
- --no-cache)
- use_cache=false
- ;;
- --help|-h)
- usage
- exit 0
- ;;
- -*)
- error "Unknown option: $1. Use --help for usage."
- ;;
- *)
- location="$1"
- ;;
- esac
- shift
- done
-
- local data=""
- local cache_key="${location:-auto}"
-
- # Try cache first
- if [[ "$use_cache" == "true" ]] && is_cache_valid "$cache_key"; then
- debug "Using cached weather for $cache_key"
- data=$(get_from_cache "$cache_key")
- else
- # Fetch from API
- data=$(get_from_api "$location")
-
- # Cache the result
- mkdir -p "$(dirname "$CACHE_FILE")"
- local cache_file="${CACHE_FILE}_${cache_key// /_}"
- echo "$data" > "$cache_file"
- debug "Weather cached to $cache_file"
- fi
-
- # Format and output
- format_output "$data" "$format"
-}
-
-main "$@"
dots/config/claude/skills/.archive/Journal/tools/get-weather-el
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# get-weather-el - Get weather using Emacs Lisp
-# Copyright (C) 2025 Vincent Demeester
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-ELISP_FILE="$SCRIPT_DIR/journelly-location-weather.el"
-
-LOCATION=""
-FORMAT="json"
-
-# Parse arguments
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --json) FORMAT="json" ;;
- --temperature) FORMAT="temperature" ;;
- --condition) FORMAT="condition" ;;
- --symbol) FORMAT="symbol" ;;
- --all) FORMAT="all" ;;
- *) LOCATION="$1" ;;
- esac
- shift
-done
-
-if [[ -n "$LOCATION" ]]; then
- exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-weather \"$LOCATION\" \"$FORMAT\")" \
- 2>/dev/null
-else
- exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-weather nil \"$FORMAT\")" \
- 2>/dev/null
-fi
dots/config/claude/skills/.archive/Journal/tools/journelly-batch-functions.el
@@ -1,474 +0,0 @@
-;;; journelly-batch-functions.el --- Batch functions for Journelly journal entries -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2025 Vincent Demeester
-
-;; Author: Vincent Demeester <vincent@demeester.fr>
-;; Keywords: org-mode, journelly, batch
-;; Version: 1.0.0
-
-;;; Commentary:
-
-;; Emacs batch mode functions for manipulating Journelly.org journal files.
-;; Journelly is an iOS app that stores journal entries in org-mode format.
-;;
-;; Format:
-;; - Single file with entries in reverse chronological order (newest first)
-;; - Each entry is a top-level heading: * [YYYY-MM-DD Day HH:MM] @ Location
-;; - Optional PROPERTIES drawer with GPS/weather metadata
-;; - Free-form org-mode content
-;;
-;; Functions:
-;; - journelly-batch-create-entry: Create new journal entry
-;; - journelly-batch-create-entry-auto: Create entry with automatic location/weather
-;; - journelly-batch-append-to-today: Append to today's entry
-;; - journelly-batch-list-entries: List recent entries
-;; - journelly-batch-search: Search entry content
-;; - journelly-batch-get-entry: Get specific entry by date/time
-;;
-;; Usage:
-;; emacs --batch \
-;; --load journelly-batch-functions.el \
-;; --eval "(journelly-batch-create-entry \
-;; \"~/desktop/org/Journelly.org\" \
-;; \"Home\" \
-;; \"Entry content\")"
-
-;;; Code:
-
-(require 'org)
-(require 'org-element)
-(require 'json)
-
-;; Load location/weather functions if available
-(let ((location-weather-file
- (expand-file-name "journelly-location-weather.el"
- (file-name-directory (or load-file-name buffer-file-name)))))
- (when (file-exists-p location-weather-file)
- (load location-weather-file)))
-
-;; Declare functions from journelly-location-weather.el (loaded conditionally above)
-(declare-function journelly-get-location "journelly-location-weather")
-(declare-function journelly-get-weather "journelly-location-weather")
-
-;;; Utility functions
-
-(defun journelly--format-timestamp ()
- "Generate org-mode timestamp for current time: [YYYY-MM-DD Day HH:MM]."
- (format-time-string "[%Y-%m-%d %a %H:%M]"))
-
-(defun journelly--format-date-only ()
- "Generate date only: YYYY-MM-DD."
- (format-time-string "%Y-%m-%d"))
-
-(defun journelly--parse-timestamp (heading)
- "Extract timestamp from HEADING.
-Expected format: * [YYYY-MM-DD Day HH:MM] @ Location
-Returns the timestamp string or nil."
- (when (string-match "\\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\) \\([A-Z][a-z][a-z]\\) \\([0-9]\\{2\\}:[0-9]\\{2\\}\\)\\]" heading)
- (match-string 0 heading)))
-
-(defun journelly--parse-location (heading)
- "Extract location from HEADING.
-Expected format: * [YYYY-MM-DD Day HH:MM] @ Location
-Returns the location string or nil."
- (when (string-match "@ \\(.+\\)$" heading)
- (match-string 1 heading)))
-
-(defun journelly--make-heading (location)
- "Create journal entry heading with current timestamp and LOCATION."
- (format "* %s @ %s" (journelly--format-timestamp) location))
-
-(defun journelly--make-properties (latitude longitude temperature condition symbol)
- "Create PROPERTIES drawer with GPS and weather data.
-LATITUDE, LONGITUDE, TEMPERATURE, CONDITION, SYMBOL are optional strings.
-Returns nil if no properties provided."
- (let ((props '()))
- (when latitude
- (push (format ":LATITUDE: %s" latitude) props))
- (when longitude
- (push (format ":LONGITUDE: %s" longitude) props))
- (when temperature
- (push (format ":WEATHER_TEMPERATURE: %s" temperature) props))
- (when condition
- (push (format ":WEATHER_CONDITION: %s" condition) props))
- (when symbol
- (push (format ":WEATHER_SYMBOL: %s" symbol) props))
- (when props
- (concat ":PROPERTIES:\n"
- (mapconcat 'identity (nreverse props) "\n")
- "\n:END:\n"))))
-
-(defun journelly--find-header-end (buffer)
- "Find the end of the Journelly header in BUFFER.
-Returns the position after the :end: line, or nil if not found."
- (with-current-buffer buffer
- (goto-char (point-min))
- (when (re-search-forward "^:end:$" nil t)
- (forward-line 1)
- (point))))
-
-(defun journelly--json-response (success data &optional message)
- "Create JSON response object.
-SUCCESS is boolean, DATA is any JSON-serializable value.
-MESSAGE is optional error/success message."
- (let ((response `((success . ,success)
- (data . ,data))))
- (when message
- (push `(message . ,message) response))
- (json-encode response)))
-
-(defun journelly--output-json (success data &optional message)
- "Output JSON response to stdout.
-SUCCESS is boolean, DATA is the response data, MESSAGE is optional."
- (princ (journelly--json-response success data message))
- (terpri))
-
-;;; Main functions
-
-(defun journelly-batch-create-entry (file location content &optional latitude longitude temperature condition symbol content-file)
- "Create new journal entry in FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- LOCATION: Location string (e.g., \"Home\", \"Kyushu\")
- CONTENT: Entry content (can be empty string)
- LATITUDE: Optional GPS latitude
- LONGITUDE: Optional GPS longitude
- TEMPERATURE: Optional temperature (e.g., \"15,2°C\")
- CONDITION: Optional weather condition (e.g., \"Cloudy\")
- SYMBOL: Optional weather symbol (e.g., \"cloud\")
- CONTENT-FILE: Optional path to file containing content
-
-If CONTENT-FILE is provided, reads content from file instead of CONTENT arg.
-
-Returns JSON with success status and entry details."
- (condition-case err
- (let ((actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content)))
- (with-temp-buffer
- (insert-file-contents file)
-
- ;; Find where to insert (after header)
- (let ((insert-pos (journelly--find-header-end (current-buffer))))
- (unless insert-pos
- (error "Could not find Journelly header end marker (:end:)"))
-
- (goto-char insert-pos)
-
- ;; Build entry
- (let ((heading (journelly--make-heading location))
- (properties (journelly--make-properties
- latitude longitude temperature condition symbol))
- (timestamp (journelly--format-timestamp)))
-
- ;; Insert entry
- (insert heading "\n")
- (when properties
- (insert properties))
- (when (and actual-content (not (string-empty-p actual-content)))
- (insert actual-content)
- (unless (string-suffix-p "\n" actual-content)
- (insert "\n")))
- (insert "\n") ;; Blank line after entry
-
- ;; Write back to file
- (write-region (point-min) (point-max) file)
-
- ;; Return success
- (journelly--output-json
- t
- `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if properties t :json-false))
- (file . ,file))
- "Journal entry created successfully")))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-append-to-today (file content &optional content-file)
- "Append CONTENT to today's journal entry in FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- CONTENT: Content to append
- CONTENT-FILE: Optional path to file containing content
-
-If no entry exists for today, returns error.
-Returns JSON with success status."
- (condition-case err
- (let ((actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content))
- (today-date (journelly--format-date-only)))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Find today's entry
- (let ((found nil)
- (search-pattern (format "^\\* \\[%s " today-date)))
- (while (and (not found)
- (re-search-forward search-pattern nil t))
- (setq found t))
-
- (unless found
- (error "No journal entry found for today (%s)" today-date))
-
- ;; Move to end of this entry (before next heading or end of file)
- (forward-line 1)
- (if (re-search-forward "^\\* \\[" nil t)
- (progn
- (beginning-of-line)
- (backward-char 1)) ;; Before the newline
- (goto-char (point-max)))
-
- ;; Insert content
- (insert "\n" actual-content)
- (unless (string-suffix-p "\n" actual-content)
- (insert "\n"))
-
- ;; Write back
- (write-region (point-min) (point-max) file)
-
- ;; Return success
- (journelly--output-json
- t
- `((date . ,today-date)
- (file . ,file))
- "Content appended to today's entry"))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-list-entries (file &optional limit)
- "List recent journal entries from FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- LIMIT: Optional number of entries to return (default 10)
-
-Returns JSON with list of entries."
- (condition-case err
- (let ((max-entries (or (and limit (string-to-number limit)) 10))
- (entries '()))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Parse entries
- (while (and (< (length entries) max-entries)
- (re-search-forward "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$" nil t))
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (has-properties nil)
- (content-preview ""))
-
- ;; Check for properties
- (save-excursion
- (forward-line 1)
- (when (looking-at "^:PROPERTIES:")
- (setq has-properties t)))
-
- ;; Get content preview (first 100 chars)
- (save-excursion
- (forward-line 1)
- (when has-properties
- (re-search-forward "^:END:$" nil t)
- (forward-line 1))
- (let ((content-start (point)))
- (if (re-search-forward "^\\* \\[" nil t)
- (beginning-of-line)
- (goto-char (point-max)))
- (setq content-preview
- (string-trim
- (buffer-substring-no-properties content-start (point))))
- (when (> (length content-preview) 100)
- (setq content-preview
- (concat (substring content-preview 0 100) "...")))))
-
- (push `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if has-properties t :json-false))
- (preview . ,content-preview))
- entries)))
-
- ;; Return results (already in reverse chronological from file)
- (journelly--output-json t (nreverse entries))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-search (file query)
- "Search journal entries in FILE for QUERY.
-
-Arguments:
- FILE: Path to Journelly.org file
- QUERY: Search string (case-insensitive)
-
-Returns JSON with matching entries."
- (condition-case err
- (let ((matches '())
- (query-lower (downcase query)))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Search entries
- (while (re-search-forward "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$" nil t)
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (entry-start (point))
- (entry-end nil)
- (entry-content ""))
-
- ;; Find entry end
- (save-excursion
- (if (re-search-forward "^\\* \\[" nil t)
- (setq entry-end (match-beginning 0))
- (setq entry-end (point-max))))
-
- ;; Get entry content
- (setq entry-content
- (buffer-substring-no-properties entry-start entry-end))
-
- ;; Check if query matches
- (when (string-match-p query-lower (downcase entry-content))
- (push `((timestamp . ,timestamp)
- (location . ,location)
- (content . ,(string-trim entry-content)))
- matches))))
-
- ;; Return results
- (journelly--output-json
- t
- (nreverse matches)
- (format "Found %d matching entries" (length matches)))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-get-entry (file date &optional time)
- "Get specific journal entry from FILE by DATE and optional TIME.
-
-Arguments:
- FILE: Path to Journelly.org file
- DATE: Date string (YYYY-MM-DD)
- TIME: Optional time string (HH:MM)
-
-Returns JSON with entry details or error if not found."
- (condition-case err
- (let ((search-pattern (if time
- (format "^\\* \\[%s .* %s\\]" date time)
- (format "^\\* \\[%s " date)))
- (found nil))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Search for entry
- (when (re-search-forward search-pattern nil t)
- (beginning-of-line)
- (when (looking-at "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$")
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (entry-end nil)
- (has-properties nil)
- (properties nil)
- (content ""))
-
- (forward-line 1)
-
- ;; Check for properties
- (when (looking-at "^:PROPERTIES:")
- (setq has-properties t)
- (let ((props-start (point)))
- (re-search-forward "^:END:$" nil t)
- (setq properties
- (buffer-substring-no-properties props-start (point)))
- (forward-line 1)))
-
- ;; Get content
- (let ((content-start (point)))
- (if (re-search-forward "^\\* \\[" nil t)
- (setq entry-end (match-beginning 0))
- (setq entry-end (point-max)))
- (setq content
- (string-trim
- (buffer-substring-no-properties content-start entry-end))))
-
- (setq found `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if has-properties t :json-false))
- (properties . ,(or properties ""))
- (content . ,content))))))
-
- (if found
- (journelly--output-json t found)
- (journelly--output-json
- nil
- nil
- (format "No entry found for %s%s"
- date
- (if time (format " at %s" time) ""))))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-create-entry-auto (file content &optional content-file use-location use-weather)
- "Create journal entry with automatic location and/or weather detection.
-
-Arguments:
- FILE: Path to Journelly.org file
- CONTENT: Entry content
- CONTENT-FILE: Optional path to file containing content
- USE-LOCATION: If non-nil, automatically detect location
- USE-WEATHER: If non-nil, automatically detect weather
-
-Requires journelly-location-weather.el to be loaded.
-
-Returns JSON with success status and entry details."
- (condition-case err
- (progn
- (unless (fboundp 'journelly-get-location)
- (error "Location/weather functions not available. Load journelly-location-weather.el"))
-
- (let ((location-data (when use-location (journelly-get-location)))
- (weather-data (when use-weather (journelly-get-weather)))
- (actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content)))
-
- ;; Extract data
- (let ((city (when location-data (cdr (assoc 'city location-data))))
- (lat (when location-data (cdr (assoc 'lat location-data))))
- (lon (when location-data (cdr (assoc 'lon location-data))))
- (temp (when weather-data (cdr (assoc 'temperature weather-data))))
- (cond (when weather-data (cdr (assoc 'condition weather-data))))
- (symbol (when weather-data (cdr (assoc 'symbol weather-data)))))
-
- ;; Create entry
- (journelly-batch-create-entry
- file
- (or city "Unknown")
- actual-content
- lat lon temp cond symbol nil))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-;;; Provide
-
-(provide 'journelly-batch-functions)
-
-;;; journelly-batch-functions.el ends here
dots/config/claude/skills/.archive/Journal/tools/journelly-location-weather.el
@@ -1,257 +0,0 @@
-;;; journelly-location-weather.el --- Location and weather helpers for Journelly -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2025 Vincent Demeester
-
-;; Author: Vincent Demeester <vincent@demeester.fr>
-;; Keywords: org-mode, journelly, location, weather
-;; Version: 1.0.0
-
-;;; Commentary:
-
-;; Emacs Lisp functions to get location and weather data for Journelly journal entries.
-;;
-;; Location:
-;; - Uses IP-based geolocation (ipinfo.io)
-;; - Returns city name and GPS coordinates
-;; - Caches results for 1 hour
-;;
-;; Weather:
-;; - Uses wttr.in weather service
-;; - Returns temperature, condition, and iOS SF Symbol
-;; - Caches results for 30 minutes
-;; - Intelligent day/night symbol mapping
-;;
-;; Functions:
-;; - journelly-get-location: Get current location via IP geolocation
-;; - journelly-get-weather: Get current weather
-;; - journelly-batch-get-location: Batch mode wrapper for location
-;; - journelly-batch-get-weather: Batch mode wrapper for weather
-;;
-;; Usage (batch mode):
-;; emacs --batch \
-;; --load journelly-location-weather.el \
-;; --eval "(journelly-batch-get-location)"
-;;
-;; emacs --batch \
-;; --load journelly-location-weather.el \
-;; --eval "(journelly-batch-get-weather)"
-
-;;; Code:
-
-(require 'url)
-(require 'json)
-
-;;; Configuration
-
-(defvar journelly-cache-dir
- (expand-file-name "journal" (or (getenv "XDG_CACHE_HOME")
- (expand-file-name ".cache" "~")))
- "Directory for caching location and weather data.")
-
-(defvar journelly-location-cache-timeout 3600
- "Location cache timeout in seconds (default: 1 hour).")
-
-(defvar journelly-weather-cache-timeout 1800
- "Weather cache timeout in seconds (default: 30 minutes).")
-
-;;; Utility functions
-
-(defun journelly--ensure-cache-dir ()
- "Ensure cache directory exists."
- (unless (file-exists-p journelly-cache-dir)
- (make-directory journelly-cache-dir t)))
-
-(defun journelly--cache-file (key)
- "Get cache file path for KEY."
- (expand-file-name (format "%s.json" key) journelly-cache-dir))
-
-(defun journelly--cache-valid-p (cache-file timeout)
- "Check if CACHE-FILE is valid within TIMEOUT seconds."
- (when (file-exists-p cache-file)
- (let* ((file-time (nth 5 (file-attributes cache-file)))
- (current-time (current-time))
- (age (float-time (time-subtract current-time file-time))))
- (< age timeout))))
-
-(defun journelly--read-cache (cache-file)
- "Read JSON data from CACHE-FILE."
- (when (file-exists-p cache-file)
- (with-temp-buffer
- (insert-file-contents cache-file)
- (goto-char (point-min))
- (json-read))))
-
-(defun journelly--write-cache (cache-file data)
- "Write DATA as JSON to CACHE-FILE."
- (journelly--ensure-cache-dir)
- (with-temp-file cache-file
- (insert (json-encode data))))
-
-(defun journelly--fetch-url (url)
- "Fetch URL and return parsed JSON response."
- (let ((url-request-method "GET")
- (url-request-extra-headers '(("User-Agent" . "Emacs/journelly"))))
- (with-current-buffer (url-retrieve-synchronously url t nil 10)
- (goto-char (point-min))
- ;; Skip HTTP headers
- (re-search-forward "^$")
- (forward-line)
- (let ((json-data (json-read)))
- (kill-buffer)
- json-data))))
-
-(defun journelly--is-night-p ()
- "Return t if current time is night (20:00-06:00)."
- (let ((hour (string-to-number (format-time-string "%H"))))
- (or (>= hour 20) (< hour 6))))
-
-;;; Location functions
-
-(defun journelly--map-weather-symbol (description &optional is-night)
- "Map weather DESCRIPTION to iOS SF Symbol name.
-If IS-NIGHT is non-nil, return night-appropriate symbols."
- (let ((desc (downcase description)))
- (if is-night
- ;; Night conditions
- (cond
- ((string-match-p "\\(clear\\|sunny\\)" desc) "moon.stars")
- ((string-match-p "partly.*cloud" desc) "cloud.moon")
- ((string-match-p "\\(rain\\|drizzle\\|shower\\)" desc) "cloud.moon.rain")
- (t "cloud.moon"))
- ;; Day conditions
- (cond
- ((string-match-p "\\(clear\\|sunny\\)" desc) "sun.max")
- ((string-match-p "partly.*cloud" desc) "cloud.sun")
- ((string-match-p "\\(cloudy\\|overcast\\)" desc) "cloud")
- ((string-match-p "heavy.*rain" desc) "cloud.heavyrain")
- ((string-match-p "\\(rain\\|shower\\)" desc) "cloud.rain")
- ((string-match-p "\\(drizzle\\|light.*rain\\)" desc) "cloud.drizzle")
- ((string-match-p "snow" desc) "cloud.snow")
- ((string-match-p "sleet" desc) "cloud.sleet")
- ((string-match-p "\\(fog\\|mist\\)" desc) "cloud.fog")
- ((string-match-p "\\(haze\\|smoke\\)" desc) "smoke")
- ((string-match-p "wind" desc) "wind")
- ((string-match-p "\\(thunder\\|storm\\)" desc) "cloud.bolt")
- (t "cloud")))))
-
-(defun journelly-get-location (&optional no-cache)
- "Get current location via IP geolocation.
-Returns alist with city, latitude, and longitude.
-If NO-CACHE is non-nil, fetch fresh data ignoring cache."
- (let ((cache-file (journelly--cache-file "location")))
- (if (and (not no-cache)
- (journelly--cache-valid-p cache-file journelly-location-cache-timeout))
- ;; Return cached data
- (journelly--read-cache cache-file)
- ;; Fetch fresh data
- (let* ((response (journelly--fetch-url "https://ipinfo.io/json"))
- (city (cdr (assoc 'city response)))
- (loc (cdr (assoc 'loc response)))
- (coords (when loc (split-string loc ",")))
- (lat (when coords (car coords)))
- (lon (when coords (cadr coords)))
- (data `((city . ,(or city "Unknown"))
- (lat . ,(or lat "0"))
- (lon . ,(or lon "0")))))
- ;; Cache the result
- (journelly--write-cache cache-file data)
- data))))
-
-(defun journelly-get-weather (&optional location no-cache)
- "Get current weather for LOCATION (city name or coordinates).
-If LOCATION is nil, uses current location via IP.
-Returns alist with temperature, condition, and symbol.
-If NO-CACHE is non-nil, fetch fresh data ignoring cache."
- (let* ((loc (or location ""))
- (cache-key (if (string-empty-p loc) "weather-auto" (format "weather-%s" loc)))
- (cache-file (journelly--cache-file cache-key)))
- (if (and (not no-cache)
- (journelly--cache-valid-p cache-file journelly-weather-cache-timeout))
- ;; Return cached data
- (journelly--read-cache cache-file)
- ;; Fetch fresh data
- (let* ((url (if (string-empty-p loc)
- "https://wttr.in/?format=j1"
- (format "https://wttr.in/%s?format=j1" (url-hexify-string loc))))
- (response (journelly--fetch-url url))
- (current (aref (cdr (assoc 'current_condition response)) 0))
- (temp-c (cdr (assoc 'temp_C current)))
- (weather-desc-array (cdr (assoc 'weatherDesc current)))
- (weather-desc (cdr (assoc 'value (aref weather-desc-array 0))))
- (temperature (format "%s°C" temp-c))
- (is-night (journelly--is-night-p))
- (symbol (journelly--map-weather-symbol weather-desc is-night))
- (data `((temperature . ,temperature)
- (condition . ,weather-desc)
- (symbol . ,symbol))))
- ;; Cache the result
- (journelly--write-cache cache-file data)
- data))))
-
-;;; Batch mode functions
-
-(defun journelly-batch-get-location (&optional format no-cache)
- "Batch mode: Get location and print to stdout.
-FORMAT can be: json (default), city, coords, lat, lon, or all.
-If NO-CACHE is non-nil, ignore cache."
- (let* ((format-type (or format "json"))
- (data (journelly-get-location no-cache))
- (city (cdr (assoc 'city data)))
- (lat (cdr (assoc 'lat data)))
- (lon (cdr (assoc 'lon data))))
- (cond
- ((string= format-type "json")
- (princ (json-encode data))
- (terpri))
- ((string= format-type "city")
- (princ city)
- (terpri))
- ((string= format-type "coords")
- (princ (format "%s,%s" lat lon))
- (terpri))
- ((string= format-type "lat")
- (princ lat)
- (terpri))
- ((string= format-type "lon")
- (princ lon)
- (terpri))
- ((string= format-type "all")
- (princ (format "%s (%s,%s)" city lat lon))
- (terpri))
- (t
- (error "Unknown format: %s" format-type)))))
-
-(defun journelly-batch-get-weather (&optional location format no-cache)
- "Batch mode: Get weather and print to stdout.
-LOCATION is optional city name or coordinates.
-FORMAT can be: json (default), temperature, condition, symbol, or all.
-If NO-CACHE is non-nil, ignore cache."
- (let* ((format-type (or format "json"))
- (data (journelly-get-weather location no-cache))
- (temperature (cdr (assoc 'temperature data)))
- (condition (cdr (assoc 'condition data)))
- (symbol (cdr (assoc 'symbol data))))
- (cond
- ((string= format-type "json")
- (princ (json-encode data))
- (terpri))
- ((string= format-type "temperature")
- (princ temperature)
- (terpri))
- ((string= format-type "condition")
- (princ condition)
- (terpri))
- ((string= format-type "symbol")
- (princ symbol)
- (terpri))
- ((string= format-type "all")
- (princ (format "%s %s (%s)" temperature condition symbol))
- (terpri))
- (t
- (error "Unknown format: %s" format-type)))))
-
-;;; Provide
-
-(provide 'journelly-location-weather)
-
-;;; journelly-location-weather.el ends here
dots/config/claude/skills/.archive/Journal/tools/journelly-manager
@@ -1,400 +0,0 @@
-#!/usr/bin/env bash
-# journelly-manager - CLI tool for Journelly journal file manipulation via Emacs batch mode
-# Copyright (C) 2025 Vincent Demeester
-# Part of Claude Code Journal skill
-
-set -euo pipefail
-
-# Configuration
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-BATCH_FUNCTIONS="${BATCH_FUNCTIONS:-$SCRIPT_DIR/journelly-batch-functions.el}"
-EMACS="${EMACS:-emacs}"
-
-# Debug mode
-DEBUG="${DEBUG:-0}"
-
-# Colors for output (if not outputting JSON)
-if [[ -t 1 ]] && [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- NC='\033[0m' # No Color
-else
- RED=''
- GREEN=''
- YELLOW=''
- BLUE=''
- NC=''
-fi
-
-# Error handling
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "$DEBUG" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-info() {
- if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- echo -e "${BLUE}$*${NC}" >&2
- fi
-}
-
-success() {
- if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- echo -e "${GREEN}$*${NC}" >&2
- fi
-}
-
-# Check dependencies
-check_deps() {
- if ! command -v "$EMACS" &> /dev/null; then
- error "Emacs not found. Set EMACS environment variable or install emacs."
- fi
-
- if [[ ! -f "$BATCH_FUNCTIONS" ]]; then
- error "journelly-batch-functions.el not found at: $BATCH_FUNCTIONS"
- fi
-}
-
-# Run Emacs batch command
-run_batch() {
- local function_call="$1"
- debug "Running: $EMACS --batch --load \"$BATCH_FUNCTIONS\" --eval \"$function_call\""
-
- "$EMACS" --batch \
- --load "$BATCH_FUNCTIONS" \
- --eval "$function_call" 2>&1
-}
-
-# Usage information
-usage() {
- cat <<EOF
-journelly-manager - CLI tool for managing Journelly journal files
-
-USAGE:
- journelly-manager <command> [arguments]
-
-COMMANDS:
- create FILE LOCATION CONTENT [options]
- Create new journal entry
-
- Options:
- --latitude=LAT GPS latitude
- --longitude=LON GPS longitude
- --temperature=TEMP Temperature (e.g., "15,2°C")
- --condition=COND Weather condition (e.g., "Cloudy")
- --symbol=SYM Weather symbol (e.g., "cloud")
- --content-file=PATH Read content from file instead
-
- Examples:
- journelly-manager create ~/desktop/org/Journelly.org "Home" "Today was great"
-
- journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
- "Work session notes" \\
- --latitude=48.8672 --longitude=2.1851 \\
- --temperature="15,2°C" --condition="Partly Cloudy" --symbol="cloud.sun"
-
- append FILE CONTENT [--content-file=PATH]
- Append to today's journal entry
-
- Examples:
- journelly-manager append ~/desktop/org/Journelly.org "Additional thoughts"
- journelly-manager append ~/desktop/org/Journelly.org --content-file=/tmp/notes.txt
-
- list FILE [--limit=N]
- List recent journal entries (default: 10)
-
- Examples:
- journelly-manager list ~/desktop/org/Journelly.org
- journelly-manager list ~/desktop/org/Journelly.org --limit=20
-
- search FILE QUERY
- Search journal entries for text
-
- Examples:
- journelly-manager search ~/desktop/org/Journelly.org "claude"
- journelly-manager search ~/desktop/org/Journelly.org "homelab"
-
- get FILE DATE [TIME]
- Get specific entry by date and optional time
-
- Examples:
- journelly-manager get ~/desktop/org/Journelly.org "2025-12-08"
- journelly-manager get ~/desktop/org/Journelly.org "2025-12-08" "15:30"
-
-GLOBAL OPTIONS:
- --help, -h Show this help message
- --version Show version information
- --debug Enable debug output
-
-ENVIRONMENT VARIABLES:
- EMACS Path to Emacs binary (default: emacs)
- BATCH_FUNCTIONS Path to journelly-batch-functions.el
- DEBUG Enable debug mode (1 or 0)
- JSON_OUTPUT Output JSON only (1 or 0)
-
-EXAMPLES:
- # Create simple entry
- journelly-manager create ~/desktop/org/Journelly.org "Home" \\
- "Productive day working on Claude skills."
-
- # Create entry with weather data
- journelly-manager create ~/desktop/org/Journelly.org "Rue Jean Bourguignon" \\
- "Evening reflection" \\
- --latitude=48.86721 --longitude=2.18509 \\
- --temperature="8,5°C" --condition="Clear" --symbol="moon.stars"
-
- # Create entry with content from file
- echo "My journal thoughts..." > /tmp/entry.txt
- journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
- --content-file=/tmp/entry.txt
-
- # Append to today's entry
- journelly-manager append ~/desktop/org/Journelly.org \\
- "Update: Made more progress on the project!"
-
- # List recent entries
- journelly-manager list ~/desktop/org/Journelly.org --limit=5
-
- # Search for entries about work
- journelly-manager search ~/desktop/org/Journelly.org "work"
-
- # Get specific entry
- journelly-manager get ~/desktop/org/Journelly.org "2025-12-08" "15:30"
-
-VERSION:
- 1.0.0
-
-AUTHOR:
- Vincent Demeester <vincent@demeester.fr>
-
-SEE ALSO:
- Journelly iOS App: https://journelly.com
- Org-mode: https://orgmode.org
-EOF
-}
-
-# Parse command line arguments
-parse_args() {
- local cmd="${1:-}"
- shift || true
-
- case "$cmd" in
- create)
- cmd_create "$@"
- ;;
- append)
- cmd_append "$@"
- ;;
- list)
- cmd_list "$@"
- ;;
- search)
- cmd_search "$@"
- ;;
- get)
- cmd_get "$@"
- ;;
- --help|-h|help)
- usage
- exit 0
- ;;
- --version)
- echo "journelly-manager 1.0.0"
- exit 0
- ;;
- "")
- error "No command specified. Use --help for usage."
- ;;
- *)
- error "Unknown command: $cmd. Use --help for usage."
- ;;
- esac
-}
-
-# Command: create
-cmd_create() {
- local file="${1:-}"
- local location="${2:-}"
- local content="${3:-}"
- shift 3 || error "create requires FILE LOCATION CONTENT arguments"
-
- [[ -z "$file" ]] && error "FILE argument required"
- [[ -z "$location" ]] && error "LOCATION argument required"
- [[ ! -f "$file" ]] && error "File not found: $file"
-
- # Parse optional arguments
- local latitude=""
- local longitude=""
- local temperature=""
- local condition=""
- local symbol=""
- local content_file=""
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --latitude=*)
- latitude="${1#*=}"
- ;;
- --longitude=*)
- longitude="${1#*=}"
- ;;
- --temperature=*)
- temperature="${1#*=}"
- ;;
- --condition=*)
- condition="${1#*=}"
- ;;
- --symbol=*)
- symbol="${1#*=}"
- ;;
- --content-file=*)
- content_file="${1#*=}"
- [[ ! -f "$content_file" ]] && error "Content file not found: $content_file"
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- shift
- done
-
- # Build Emacs Lisp call
- local elisp_call="(journelly-batch-create-entry \"$file\" \"$location\" \"$content\""
-
- [[ -n "$latitude" ]] && elisp_call="$elisp_call \"$latitude\"" || elisp_call="$elisp_call nil"
- [[ -n "$longitude" ]] && elisp_call="$elisp_call \"$longitude\"" || elisp_call="$elisp_call nil"
- [[ -n "$temperature" ]] && elisp_call="$elisp_call \"$temperature\"" || elisp_call="$elisp_call nil"
- [[ -n "$condition" ]] && elisp_call="$elisp_call \"$condition\"" || elisp_call="$elisp_call nil"
- [[ -n "$symbol" ]] && elisp_call="$elisp_call \"$symbol\"" || elisp_call="$elisp_call nil"
- [[ -n "$content_file" ]] && elisp_call="$elisp_call \"$content_file\"" || elisp_call="$elisp_call nil"
-
- elisp_call="$elisp_call)"
-
- info "Creating journal entry..."
- local result
- result=$(run_batch "$elisp_call")
- echo "$result"
-
- if echo "$result" | grep -q '"success":true'; then
- success "Journal entry created successfully"
- fi
-}
-
-# Command: append
-cmd_append() {
- local file="${1:-}"
- local content="${2:-}"
- shift 2 || error "append requires FILE CONTENT arguments"
-
- [[ -z "$file" ]] && error "FILE argument required"
- [[ ! -f "$file" ]] && error "File not found: $file"
-
- local content_file=""
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --content-file=*)
- content_file="${1#*=}"
- [[ ! -f "$content_file" ]] && error "Content file not found: $content_file"
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- shift
- done
-
- local elisp_call="(journelly-batch-append-to-today \"$file\" \"$content\""
- [[ -n "$content_file" ]] && elisp_call="$elisp_call \"$content_file\"" || elisp_call="$elisp_call nil"
- elisp_call="$elisp_call)"
-
- info "Appending to today's entry..."
- local result
- result=$(run_batch "$elisp_call")
- echo "$result"
-
- if echo "$result" | grep -q '"success":true'; then
- success "Content appended successfully"
- fi
-}
-
-# Command: list
-cmd_list() {
- local file="${1:-}"
- shift || error "list requires FILE argument"
-
- [[ -z "$file" ]] && error "FILE argument required"
- [[ ! -f "$file" ]] && error "File not found: $file"
-
- local limit=""
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --limit=*)
- limit="${1#*=}"
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- shift
- done
-
- local elisp_call="(journelly-batch-list-entries \"$file\""
- [[ -n "$limit" ]] && elisp_call="$elisp_call \"$limit\"" || elisp_call="$elisp_call nil"
- elisp_call="$elisp_call)"
-
- info "Listing recent entries..."
- run_batch "$elisp_call"
-}
-
-# Command: search
-cmd_search() {
- local file="${1:-}"
- local query="${2:-}"
- shift 2 || error "search requires FILE QUERY arguments"
-
- [[ -z "$file" ]] && error "FILE argument required"
- [[ -z "$query" ]] && error "QUERY argument required"
- [[ ! -f "$file" ]] && error "File not found: $file"
-
- local elisp_call="(journelly-batch-search \"$file\" \"$query\")"
-
- info "Searching for: $query"
- run_batch "$elisp_call"
-}
-
-# Command: get
-cmd_get() {
- local file="${1:-}"
- local date="${2:-}"
- local time="${3:-}"
- shift 2 || error "get requires FILE DATE arguments"
-
- [[ -z "$file" ]] && error "FILE argument required"
- [[ -z "$date" ]] && error "DATE argument required"
- [[ ! -f "$file" ]] && error "File not found: $file"
-
- local elisp_call="(journelly-batch-get-entry \"$file\" \"$date\""
- [[ -n "$time" ]] && elisp_call="$elisp_call \"$time\"" || elisp_call="$elisp_call nil"
- elisp_call="$elisp_call)"
-
- info "Getting entry for: $date${time:+ at $time}"
- run_batch "$elisp_call"
-}
-
-# Main
-main() {
- check_deps
- parse_args "$@"
-}
-
-main "$@"
dots/config/claude/skills/.archive/Journal/README.md
@@ -1,162 +0,0 @@
-# Journal Skill
-
-Claude Code skill for managing Journelly-format journal entries.
-
-## Overview
-
-This skill enables Claude to create and manage journal entries in the Journelly org-mode format. Journelly is an iOS app that stores journal entries as org-mode headings in a single file with optional GPS/weather metadata.
-
-## Components
-
-### SKILL.md
-The skill definition and documentation. Contains:
-- Journelly format specification
-- Usage examples
-- Best practices
-- Integration patterns
-
-### tools/journelly-batch-functions.el
-Emacs Lisp batch functions for programmatic journal manipulation:
-- `journelly-batch-create-entry` - Create new entry
-- `journelly-batch-append-to-today` - Append to today's entry
-- `journelly-batch-list-entries` - List recent entries
-- `journelly-batch-search` - Search content
-- `journelly-batch-get-entry` - Get specific entry by date
-
-### tools/journelly-manager
-Bash CLI wrapper around the Emacs batch functions. Provides a user-friendly interface for all journal operations.
-
-## Usage
-
-### Via Claude
-
-Simply ask Claude to create journal entries:
-
-```
-"Create a journal entry about today's work on the Journal skill"
-"Add to today's journal that I finished the implementation"
-"Search my journal for entries about homelab"
-"Show me my last 5 journal entries"
-```
-
-### Direct CLI Usage
-
-```bash
-# Create entry
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" "Today was productive!"
-
-# Create with weather
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Kyushu" "Work notes" \
- --latitude=48.8672 --longitude=2.1851 \
- --temperature="15,2°C" --condition="Cloudy" --symbol="cloud"
-
-# Append to today
-~/.config/claude/skills/Journal/tools/journelly-manager append \
- ~/desktop/org/Journelly.org "Additional thoughts for the day"
-
-# List recent entries
-~/.config/claude/skills/Journal/tools/journelly-manager list \
- ~/desktop/org/Journelly.org --limit=5
-
-# Search
-~/.config/claude/skills/Journal/tools/journelly-manager search \
- ~/desktop/org/Journelly.org "claude"
-
-# Get specific entry
-~/.config/claude/skills/Journal/tools/journelly-manager get \
- ~/desktop/org/Journelly.org "2025-12-08"
-```
-
-## Journelly Format
-
-Journal entries follow this structure:
-
-```org
-* [YYYY-MM-DD Day HH:MM] @ Location
-:PROPERTIES:
-:LATITUDE: 48.86721377062119
-:LONGITUDE: 2.1850910842231994
-:WEATHER_TEMPERATURE: 5,8°C
-:WEATHER_CONDITION: Cloudy
-:WEATHER_SYMBOL: cloud
-:END:
-Entry content goes here...
-
-- Section Title (use indented lists, NOT sub-headings)
- - Item 1
- - Item 2
-```
-
-- **Reverse chronological**: Newest entries at top
-- **Optional properties**: GPS/weather metadata from iOS app
-- **Content format**: Org-mode support (lists, links, code blocks)
-- **NO sub-headings**: Use indented lists instead of `**` level 2 headings
-- **Single file**: All entries in `~/desktop/org/Journelly.org`
-
-## Integration
-
-### With Journelly iOS App
-- Primary mobile interface for journal
-- Automatically adds GPS and weather data
-- Syncs via iCloud
-- Photos captured on iPhone
-
-### With Claude Code
-- Create entries via natural language
-- Search and review past entries
-- Append to existing entries
-- Generate journal entries from work sessions
-
-### With Syncthing
-- Sync across devices
-- Available on desktop and mobile
-- Changes sync bidirectionally
-
-## Testing
-
-Test the journelly-manager tool:
-
-```bash
-# Show help
-~/.config/claude/skills/Journal/tools/journelly-manager --help
-
-# List entries (read-only)
-~/.config/claude/skills/Journal/tools/journelly-manager list \
- ~/desktop/org/Journelly.org --limit=3
-
-# Search entries (read-only)
-~/.config/claude/skills/Journal/tools/journelly-manager search \
- ~/desktop/org/Journelly.org "test"
-```
-
-## Development
-
-The skill uses Emacs batch mode for reliable org-mode parsing and manipulation:
-
-1. **journelly-batch-functions.el**: Core logic in Emacs Lisp
-2. **journelly-manager**: Bash wrapper for CLI interface
-3. **SKILL.md**: Documentation for Claude integration
-
-All operations return JSON for programmatic integration.
-
-## Related Skills
-
-- **Notes**: Denote-format note-taking (multi-file, topic-based)
-- **TODOs**: Task management with org-mode
-- **Org**: Core org-mode manipulation library
-
-The Journal skill complements these by providing time-based, personal reflections while Notes provides topic-based knowledge management.
-
-## Version
-
-1.0.0
-
-## Author
-
-Vincent Demeester <vincent@demeester.fr>
-
-## License
-
-Part of personal Claude Code configuration.
dots/config/claude/skills/.archive/Journal/SKILL.md
@@ -1,594 +0,0 @@
----
-name: Journal
-description: Journelly-format journal entries. USE WHEN user wants to create journal entries, write reflections, or work with Journelly.org file.
----
-
-# Journal Writing Workflow
-
-## Purpose
-Assist with creating and managing journal entries in Journelly format using org-mode.
-
-### Context Detection
-
-**This skill activates when:**
-- User asks to create a journal entry, write a journal, or add to journal
-- User mentions Journelly, journaling, or daily reflections
-- User references `Journelly.org` file
-- User asks about logging thoughts, daily notes, or personal reflections
-
-## Journal Location
-**Journal file**: `~/desktop/org/Journelly.org`
-
-## Journelly Format
-
-Journelly is an iOS app that stores journal entries in org-mode format as a single file with entries in reverse chronological order (newest first).
-
-### Entry Structure
-
-**Basic entry format**:
-```org
-* [YYYY-MM-DD Day HH:MM] @ Location
-:PROPERTIES:
-:LATITUDE: 48.86721377062119
-:LONGITUDE: 2.1850910842231994
-:WEATHER_TEMPERATURE: 5,8°C
-:WEATHER_CONDITION: Cloudy
-:WEATHER_SYMBOL: cloud
-:END:
-Entry content goes here...
-
-Can have multiple paragraphs.
-
-- Lists work
-- [ ] Checkboxes work
-- [X] Completed items
-
-Images can be included:
-[[file:Journelly.org.assets/images/IMAGE.jpeg]]
-```
-
-**Entry without properties**:
-```org
-* [YYYY-MM-DD Day HH:MM] @ Location
-
-Simple entry without weather/GPS metadata.
-```
-
-### Key Components
-
-**Heading**:
-- Format: `* [YYYY-MM-DD Day HH:MM] @ Location`
-- Timestamp: Full date and time in org-mode format
-- Location: Can be a place name, address, or general location
-- Examples:
- - `* [2025-12-08 Mon 15:30] @ Home`
- - `* [2025-12-08 Mon 09:15] @ Kyushu`
- - `* [2025-12-08 Mon 22:00] @ Rue Jean Bourguignon`
-
-**Properties drawer** (optional):
-- `:LATITUDE:` - GPS latitude
-- `:LONGITUDE:` - GPS longitude
-- `:WEATHER_TEMPERATURE:` - Temperature with unit (e.g., `5,8°C`)
-- `:WEATHER_CONDITION:` - Weather description (e.g., `Cloudy`, `Clear`, `Partly Cloudy`)
-- `:WEATHER_SYMBOL:` - Icon symbol (e.g., `cloud`, `sun.max`, `cloud.moon`)
-
-**Content**:
-- Free-form text in org-mode format
-- Support for org-mode features: lists, checkboxes, links, code blocks
-- Images stored in `Journelly.org.assets/images/` directory
-- Can include hashtags (e.g., `#lang_en`)
-- Can reference other org files with org-mode links
-
-**IMPORTANT - Content Formatting Restrictions**:
-- **NO sub-headings**: Journelly does NOT support `**` level 2 headings or any sub-headings within entries
-- Use **indented lists** instead of sub-headings for structure:
- ```org
- - Section Title
- - Item 1
- - Item 2
- ```
-- For bold emphasis in list items, use text formatting: `- *Bold Section Title*`
-- Each journal entry must be a single `*` level 1 heading - no nested headings allowed
-
-### Entry Order
-
-Entries are in **reverse chronological order** - newest entries at the top of the file, after the header.
-
-## File Header
-
-The Journelly.org file starts with:
-```org
-#+TITLE: My Journal (via https://journelly.com)
-#+STARTUP: showall
-:journelly:
-:doc_version: 1.0
-:end:
-```
-
-This header should never be modified.
-
-## Creating Entries
-
-### Automatic Location and Weather Detection
-
-Helper scripts are available to automatically get current location and weather:
-
-**Get location (IP-based geolocation)**:
-```bash
-# Get city and coordinates
-~/.config/claude/skills/Journal/tools/get-location
-# Output: Saint-Denis (48.9356,2.3539)
-
-# Get just coordinates
-~/.config/claude/skills/Journal/tools/get-location --coords
-# Output: 48.9356,2.3539
-
-# Get as JSON
-~/.config/claude/skills/Journal/tools/get-location --json
-# Output: {"city":"Saint-Denis","lat":"48.9356","lon":"2.3539"}
-```
-
-**Get weather (from wttr.in)**:
-```bash
-# Get current weather
-~/.config/claude/skills/Journal/tools/get-weather
-# Output: 15°C Partly cloudy (cloud.sun)
-
-# Get just temperature
-~/.config/claude/skills/Journal/tools/get-weather --temperature
-# Output: 15°C
-
-# Get as JSON
-~/.config/claude/skills/Journal/tools/get-weather --json
-# Output: {"temperature":"15°C","condition":"Partly cloudy","symbol":"cloud.sun"}
-
-# Get weather for specific location
-~/.config/claude/skills/Journal/tools/get-weather Paris
-```
-
-**Notes**:
-- Location uses IP-based geolocation (city-level accuracy, no GPS hardware required)
-- Weather uses wttr.in service (no API key required)
-- Both are cached (location: 1 hour, weather: 30 minutes) to reduce API calls
-- Symbols are mapped to iOS SF Symbols for consistency with Journelly app
-
-### Using journelly-manager (Recommended)
-
-The `journelly-manager` tool provides batch mode operations for creating journal entries:
-
-```bash
-# Create simple entry with just location
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Kyushu" "Entry content here"
-
-# Create entry with weather/GPS metadata
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Rue Jean Bourguignon" "Entry content" \
- --latitude=48.86721 \
- --longitude=2.18509 \
- --temperature="15,2°C" \
- --condition="Partly Cloudy" \
- --symbol="cloud.sun"
-
-# Create entry with content from file
-echo "My thoughts today..." > /tmp/journal.txt
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" --content-file=/tmp/journal.txt
-
-# Append to today's entry (if one exists from today)
-~/.config/claude/skills/Journal/tools/journelly-manager append \
- ~/desktop/org/Journelly.org "Additional thoughts for today"
-
-# Create entry with automatic location and weather
-LOC_DATA=$(~/.config/claude/skills/Journal/tools/get-location --json)
-WEATHER_DATA=$(~/.config/claude/skills/Journal/tools/get-weather --json)
-
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org \
- "$(echo "$LOC_DATA" | jq -r .city)" \
- "Today's reflection with automatic metadata" \
- --latitude="$(echo "$LOC_DATA" | jq -r .lat)" \
- --longitude="$(echo "$LOC_DATA" | jq -r .lon)" \
- --temperature="$(echo "$WEATHER_DATA" | jq -r .temperature)" \
- --condition="$(echo "$WEATHER_DATA" | jq -r .condition)" \
- --symbol="$(echo "$WEATHER_DATA" | jq -r .symbol)"
-```
-
-**Output**: Returns JSON with success status and entry details.
-
-### Manual Creation
-
-If creating entries manually:
-
-1. Open `~/desktop/org/Journelly.org`
-2. After the file header (after `:end:`), insert new entry at the top
-3. Use timestamp format: `[YYYY-MM-DD Day HH:MM]`
-4. Add location after `@`
-5. Optionally add PROPERTIES drawer
-6. Write content below
-
-**Get current timestamp**:
-```bash
-# Org-mode format
-date +"[%Y-%m-%d %a %H:%M]"
-```
-
-## Entry Examples
-
-### Structured entry with indented lists (RECOMMENDED for complex entries):
-```org
-* [2025-12-10 Wed 17:05] @ Paris
-:PROPERTIES:
-:LATITUDE: 48.8534
-:LONGITUDE: 2.3488
-:END:
-Productive day with significant progress across multiple areas.
-
-- Work (Tekton/CI-CD)
- - [X] Fixed workflow call in pipeline (priority 1)
- - [X] Created issue for cherry-pick workflow
- - [X] Standardized retest workflow across repositories
-
-- Infrastructure & Homelab
- - [X] Set up Navidrome music streaming server
- - [X] Implemented color-scheme switcher on kyushu
- - [ ] Setup imap-filter (in progress)
-
-- Personal Productivity
- - Updated imapfilter to archive emails by year
- - Researched Everyday Systems habit formation
- - Created note documenting implementation ideas
-
-10 completed tasks, feeling productive!
-```
-
-**Note**: Use indented lists (with 2 spaces) instead of sub-headings (`**`) - Journelly doesn't support nested headings!
-
-### Simple reflection:
-```org
-* [2025-12-08 Mon 15:30] @ Home
-
-Had a productive day working on Claude skills. The Journal skill
-is coming together nicely. Need to test the batch functions next.
-```
-
-### Work notes with checklist:
-```org
-* [2025-12-08 Mon 09:00] @ Kyushu
-
-Today's focus:
-- [X] Review pull requests
-- [X] Fix bug in pipeline
-- [ ] Write documentation
-- [ ] Team meeting at 14:00
-
-Making good progress on the telemetry work.
-```
-
-### Evening reflection with location data:
-```org
-* [2025-12-08 Mon 22:30] @ Rue Jean Bourguignon
-:PROPERTIES:
-:LATITUDE: 48.86721377062119
-:LONGITUDE: 2.1850910842231994
-:WEATHER_TEMPERATURE: 8,5°C
-:WEATHER_CONDITION: Clear
-:WEATHER_SYMBOL: moon.stars
-:END:
-Quiet evening. Spent time with family and worked on some personal
-projects. Feeling good about the progress this week.
-
-Tomorrow's priorities:
-- House hunting follow-up
-- Finish keyboard configuration
-- Review Rhea setup
-```
-
-### Vacation entry with photos:
-```org
-* [2025-08-16 Sat 04:40] @ Rue Jean Bourguignon
-:PROPERTIES:
-:LATITUDE: 48.868139960083184
-:LONGITUDE: 2.184074493463655
-:WEATHER_TEMPERATURE: 16,8°C
-:WEATHER_CONDITION: Clear
-:WEATHER_SYMBOL: moon.stars
-:END:
-Good day at Plaisir with Malek and Ilyan. Everyone was tired at
-the end but for good reasons.
-
-[[file:Journelly.org.assets/images/CDB4EC5D-153A-4C35-A7E8-17BA94F7FEBD.jpeg]]
-
-[[file:Journelly.org.assets/images/9CB95E09-CD4E-4868-BFE0-F0B129A8356B.jpeg]]
-
-We need to find a way to have less mosquitoes bites for Ayla because
-it is a lot and it is painful to see her with all thoses..
-```
-
-## Best Practices
-
-### When to Journal
-
-- **Morning**: Day planning, intentions, priorities
-- **During work**: Quick thoughts, decisions, blockers
-- **Evening**: Reflections, gratitude, learnings
-- **Anytime**: Significant moments, insights, emotions
-
-### Writing Guidelines
-
-- **Be authentic**: Write for yourself, not an audience
-- **Be specific**: Include details, context, emotions
-- **Be brief**: Don't overthink it, capture the moment
-- **Be consistent**: Regular entries build a valuable record
-- **Use org-mode features**: Lists, checkboxes, links enhance entries
-
-### Location Guidelines
-
-- Use recognizable place names for your context
-- Can be specific (address) or general (room name, city)
-- Examples:
- - Work: `Kyushu` (machine name)
- - Home: `Home`, `Rue Jean Bourguignon`
- - Travel: `Amsterdam`, `R27`, `Boulevard de Turin`
-
-### Privacy Considerations
-
-- Journal contains personal thoughts and location data
-- Stored locally and synced via Syncthing/iCloud
-- No properties needed if you prefer not to log GPS/weather
-- You control what details to include
-
-## Common Use Cases
-
-### Daily Reflection
-```bash
-# Simple end-of-day reflection
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" \
- "Productive day. Made progress on the Journal skill. \
- Looking forward to testing it tomorrow."
-```
-
-### Work Log
-```bash
-# Log work accomplishments
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Kyushu" \
- "Completed PR review. Fixed authentication bug. \
- Team meeting went well - discussed Q1 roadmap."
-```
-
-### Quick Thought
-```bash
-# Capture an idea quickly
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" \
- "Idea: Create a dashboard for monitoring homelab services. \
- Could use Grafana + Prometheus."
-```
-
-### Adding to Today's Entry
-```bash
-# Append to existing entry from today
-~/.config/claude/skills/Journal/tools/journelly-manager append \
- ~/desktop/org/Journelly.org \
- "Update: The dashboard idea is working! Initial prototype running."
-```
-
-## Integration with Other Systems
-
-### Journelly iOS App
-
-- Primary mobile interface for journal entries
-- Automatically adds GPS and weather data
-- Syncs via iCloud to make file available on Mac
-- Can include photos captured on iPhone
-- Creates proper org-mode format automatically
-
-### Syncthing
-
-- Journal file synced across devices
-- Available on desktop for reading/searching
-- Can edit from Emacs on desktop
-- Changes sync back to iOS app
-
-### Emacs
-
-- Read and search journal with org-mode commands
-- Use `org-sparse-tree` to filter entries
-- Export to other formats (HTML, PDF)
-- Integration with org-agenda if desired
-
-## Searching and Reviewing
-
-### Find Recent Entries
-```bash
-# Show last 10 entries (most recent)
-grep -n "^\* \[" ~/desktop/org/Journelly.org | head -10
-```
-
-### Search by Date
-```bash
-# Find entries from December 2025
-grep "^\* \[2025-12-" ~/desktop/org/Journelly.org
-
-# Find entries from specific day
-grep "^\* \[2025-12-08" ~/desktop/org/Journelly.org
-```
-
-### Search by Location
-```bash
-# Find all entries at Kyushu
-grep "@ Kyushu" ~/desktop/org/Journelly.org
-```
-
-### Search Content
-```bash
-# Find entries mentioning specific topic
-grep -i "claude" ~/desktop/org/Journelly.org
-
-# Context around matches
-grep -C 3 -i "homelab" ~/desktop/org/Journelly.org
-```
-
-### Search with Ripgrep
-```bash
-# Case-insensitive search with context
-rg -i "keyboard" ~/desktop/org/Journelly.org
-
-# Show only entries about work
-rg "@ Kyushu" ~/desktop/org/Journelly.org -A 10
-```
-
-## Journelly vs Notes
-
-**Use Journelly for**:
-- Daily reflections and thoughts
-- Personal experiences and emotions
-- Time-based chronicle of life
-- Quick captures without much structure
-- Location-tagged memories
-
-**Use Notes (denote) for**:
-- Technical documentation
-- Learning notes and research
-- Reference material
-- Project planning
-- Knowledge that needs retrieval by topic
-
-**They complement each other**: Journal captures the journey, Notes capture the knowledge.
-
-## Org-Mode Features in Entries
-
-### Lists
-```org
-* [2025-12-08 Mon 10:00] @ Home
-
-Today's agenda:
-- Morning: Focus work
-- Afternoon: Meetings
-- Evening: Family time
-```
-
-### Checkboxes
-```org
-* [2025-12-08 Mon 08:00] @ Kyushu
-
-Weekly goals:
-- [X] Complete feature implementation
-- [X] Review 5 PRs
-- [ ] Write documentation
-- [ ] Update roadmap
-```
-
-### Links
-```org
-* [2025-12-08 Mon 16:00] @ Home
-
-Referenced my [[file:~/desktop/org/notes/20251205T140000--nixos-config__nixos.org][NixOS config notes]]
-for today's work.
-
-Useful article: [[https://example.com][Link Title]]
-```
-
-### Code Blocks
-```org
-* [2025-12-08 Mon 14:00] @ Kyushu
-
-Found a useful command today:
-
-#+begin_src bash
-nix build .#package-name
-#+end_src
-
-This made the build process much faster.
-```
-
-### Tables
-```org
-* [2025-12-08 Mon 12:00] @ Home
-
-Tracking house options:
-
-| Address | Price | Score |
-|------------------+-------+-------|
-| Rue Victor Hugo | 720k | 7/10|
-| Allée Perruchet | 750k | 9/10|
-```
-
-## Tips
-
-1. **Write regularly**: Even short entries build a valuable record
-2. **Don't overthink**: Capture thoughts as they come
-3. **Use location meaningfully**: Helps trigger memories later
-4. **Include context**: Future you will appreciate the details
-5. **Reference other files**: Link to notes, todos, or external resources
-6. **Use hashtags sparingly**: `#lang_en`, `#work`, `#personal` can help
-7. **Add photos when meaningful**: Visual memories are powerful
-8. **Review periodically**: Monthly or yearly reviews reveal patterns
-
-## Example Workflow
-
-### Mobile (Journelly iOS)
-1. Open Journelly app
-2. Tap to create new entry
-3. Write thoughts (voice dictation works)
-4. App automatically adds timestamp, location, weather
-5. Can attach photos from camera or library
-6. Entry syncs to iCloud → available on Mac
-
-### Desktop (Claude/Emacs)
-1. Ask Claude to create journal entry
-2. Claude uses `journelly-manager` to add entry
-3. Entry inserted at top of file (newest first)
-4. File syncs back via iCloud/Syncthing
-5. Entry appears in iOS app
-
-### Hybrid
-- Quick captures on mobile (always with you)
-- Longer reflections on desktop (better for typing)
-- Search and review on desktop (powerful tools)
-- Photos from mobile, text from either
-
-## Privacy & Sync
-
-- Journal is a plain text file you control
-- Location data is optional (from iOS app)
-- Synced via iCloud (encrypted in transit)
-- Can also use Syncthing for local-only sync
-- No third-party service has access to content
-- Backup with your regular backup strategy
-
-Remember: Your journal is for you. Write what helps you think, remember, and grow.
-
-## Examples
-
-**Example 1: Creating a daily journal entry**
-```
-User: "Create my journal entry for today"
-→ Uses Journelly format with date heading
-→ Prompts for reflections and highlights
-→ Adds structured sections (work, personal, learnings)
-→ Saves to ~/desktop/org/notes/Journelly.org
-→ Result: Well-structured journal entry for the day
-```
-
-**Example 2: Reviewing past entries**
-```
-User: "What did I write about last week?"
-→ Searches Journelly.org for date range
-→ Extracts entries from the past 7 days
-→ Summarizes key themes and activities
-→ Highlights important decisions or learnings
-→ Result: Quick recap of the week's journal
-```
-
-**Example 3: Adding a reflection**
-```
-User: "Add a reflection about the Tekton work we just did"
-→ Appends to today's journal entry
-→ Captures what was accomplished
-→ Notes lessons learned and insights
-→ Links to relevant history or notes
-→ Result: Reflection recorded for future reference
-```
dots/config/claude/skills/.archive/Notes/SKILL.md
@@ -1,612 +0,0 @@
----
-name: notes
-description: Note-taking workflow using denote format and org-mode. USE WHEN user wants to create notes, organize knowledge, document learnings, or work with denote/org-mode files.
----
-
-# Note Writing Workflow
-
-## Purpose
-Assist with creating and organizing notes in denote format using org-mode.
-
-### Context Detection
-
-**This skill activates when:**
-- User asks to create a note, write a note, or document something
-- User mentions denote, org-mode, or note-taking
-- Working directory is `/home/vincent/desktop/org/notes`
-- User references `.org` files with denote naming pattern
-- User asks about organizing knowledge or documentation
-
-## Note Location
-**Notes directory**: `/home/vincent/desktop/org/notes`
-
-## Denote Format
-
-Denote is an Emacs package for simple note management with a consistent naming scheme.
-
-### File Naming Pattern
-
-**Standard format**:
-```
-YYYYMMDDTHHMMSS--title__tag1_tag2_tag3.org
-```
-
-**With signature (for automated/system-generated notes)**:
-```
-YYYYMMDDTHHMMSS==signature--title__tag1_tag2_tag3.org
-```
-
-**Components**:
-- **Timestamp**: `YYYYMMDDTHHMMSS` - Unique identifier and creation time
-- **Signature** (optional): `==signature` - Identifies note origin/type (e.g., `==pkai` for automated history notes)
-- **Separator**: `--` - Separates timestamp/signature from title
-- **Title**: Descriptive, lowercase with hyphens
-- **Tag separator**: `__` - Separates title from tags
-- **Tags**: Underscore-separated, descriptive keywords
-
-**Examples**:
-```
-# Regular note
-20251203T151822--personal-ai-infrastructure-implementation-plan__ai_claude_infrastructure_nixos_plan.org
-
-# Automated history note (with pkai signature)
-20251203T155616==pkai--claude-skills-implementation-session__history_session_ai_claude.org
-```
-
-**Signature Usage**:
-- `==pkai` - Personal Knowledge Automated Infrastructure (history-generated notes)
-- Use signatures to identify system-generated or automated notes
-- Makes it easy to filter: `ls *==pkai*.org` shows all automated notes
-
-### File Structure
-
-Every denote note follows this org-mode structure:
-
-```org
-#+title: Note Title Here
-#+date: [YYYY-MM-DD Day HH:MM]
-#+filetags: :tag1:tag2:tag3:
-#+identifier: YYYYMMDDTHHMMSS
-#+category: category_name
-
-* First Section
-Content goes here...
-
-** Subsection
-More content...
-
-* Second Section
-Additional content...
-```
-
-**Key Elements**:
-- `#+title:` - Human-readable title (can have spaces, capitalization)
-- `#+date:` - Creation date in org-mode date format
-- `#+filetags:` - Tags surrounded by colons (`:tag1:tag2:`)
-- `#+identifier:` - Same as filename timestamp
-- `#+category:` - Optional category for organization
-
-## Common Tags
-
-### Technical Topics
-- `:ai:` - AI, LLMs, Claude Code
-- `:nixos:` - NixOS configuration and packages
-- `:golang:` - Go programming language
-- `:homelab:` - Infrastructure, servers, self-hosting
-- `:keyboards:` - Keyboard firmware (QMK, ZMK, Kanata)
-- `:emacs:` - Emacs editor and configuration
-- `:linux:` - Linux operating system
-- `:programming:` - General programming topics
-
-### Work and Projects
-- `:work:` - Work-related notes
-- `:redhat:` - Red Hat specific
-- `:openshift:` - OpenShift/Kubernetes
-- `:tekton:` - Tekton CI/CD
-- `:project:` - Project-specific notes
-
-### Personal
-- `:life:` - Personal life and experiences
-- `:books:` - Book notes and highlights
-- `:ideas:` - Ideas and brainstorming
-- `:plan:` - Planning and roadmaps
-- `:learning:` - Learning notes
-
-### Content Type
-- `:howto:` - How-to guides
-- `:reference:` - Reference material
-- `:tutorial:` - Tutorials and walkthroughs
-
-### History Integration
-- `:history:` - All history-related notes (required for integration)
-- `:history:session:` - Work session summaries
-- `:history:learning:` - Problem-solving insights and learnings
-- `:history:research:` - Deep technical investigations
-- `:history:decision:` - Architecture and design decisions
-- `:history:execution:` - Command execution logs
-
-**Note**: History-tagged notes are interconnected with `~/.config/claude/history/` entries. See the history-system skill for details.
-
-## Creating Notes
-
-### Using org-manager (Recommended)
-
-The `org-manager` tool provides batch mode denote integration for creating notes programmatically:
-
-```bash
-# Create a simple note
-org-manager denote-create "Note Title" "tag1,tag2,tag3" \
- --category=homelab --directory=~/desktop/org/notes
-
-# Create with signature (for automated notes)
-org-manager denote-create "Session Summary" "history,session" \
- --signature=pkai --category=history
-
-# Create with initial content from file
-echo "* Custom Content" > /tmp/content.org
-org-manager denote-create "My Note" "nixos,config" \
- --content=/tmp/content.org
-```
-
-**Output**: Returns JSON with created file path:
-```json
-{
- "success": true,
- "filepath": "/path/to/20251205T140049--note-title__tag1_tag2_tag3.org",
- "timestamp": "20251205T140049",
- "message": "Created note: ..."
-}
-```
-
-### Manual Creation
-
-If creating notes manually without org-manager:
-
-#### Generate Timestamp
-```bash
-# Get current timestamp for identifier
-date +"%Y%m%dT%H%M%S"
-```
-
-#### Generate Full Date
-```bash
-# Get org-mode formatted date
-date +"[%Y-%m-%d %a %H:%M]"
-```
-
-### Example: Creating a New Note
-
-**Filename**: `20251203T154500--setting-up-wireguard-vpn__homelab_networking_vpn.org`
-
-**Content**:
-```org
-#+title: Setting up Wireguard VPN
-#+date: [2025-12-03 Wed 15:45]
-#+filetags: :homelab:networking:vpn:
-#+identifier: 20251203T154500
-#+category: homelab
-
-* Overview
-Setting up Wireguard VPN for secure remote access to homelab.
-
-* Configuration
-** Server Setup
-Configuration in ~/src/home/modules/wireguard-server
-
-** Client Setup
-Configuration in ~/src/home/modules/wireguard-client
-
-* Testing
-Verify connection with ping...
-```
-
-## Best Practices
-
-### Title Guidelines
-- Use descriptive, specific titles
-- Keep titles concise (3-7 words ideal)
-- Use lowercase in filename, normal case in `#+title:`
-- Examples:
- - Good: "implementing-oauth2-authentication"
- - Bad: "notes" or "stuff-about-things"
-
-### Tagging Strategy
-- Use 2-5 tags per note
-- Start general, then specific: `:programming:golang:testing:`
-- Create tags consistently (don't use both `:ai:` and `:artificial-intelligence:`)
-- Tags are for finding, not categorizing everything
-
-### Content Organization
-- Use org-mode headings (`*`, `**`, `***`)
-- Include a brief overview/summary at the top
-- Use TODO items for actionable content
-- Link related notes with org-mode links
-
-### Org-Mode Features
-
-#### Headings
-```org
-* Level 1
-** Level 2
-*** Level 3
-```
-
-#### Links
-```org
-# Link to other notes
-[[file:~/desktop/org/notes/20251203T151822--personal-ai-infrastructure__ai.org][PAI Implementation Plan]]
-
-# External links
-[[https://example.com][Example Website]]
-
-# Link with description
-[[https://github.com/user/repo][GitHub Repository]]
-```
-
-#### Code Blocks
-```org
-#+begin_src bash
-# Shell commands
-make switch
-#+end_src
-
-#+begin_src nix
-# Nix configuration
-services.nginx.enable = true;
-#+end_src
-
-#+begin_src go
-// Go code
-func main() {
- fmt.Println("Hello")
-}
-#+end_src
-```
-
-#### Lists
-```org
-# Unordered
-- Item 1
-- Item 2
- - Subitem
-
-# Ordered
-1. First
-2. Second
-3. Third
-
-# TODO lists
-- [ ] Uncompleted task
-- [X] Completed task
-```
-
-#### TODO Items
-```org
-* TODO Implement feature X
-SCHEDULED: <2025-12-04 Thu>
-
-* DONE Fixed bug in handler
-CLOSED: [2025-12-03 Wed 14:23]
-
-* IN-PROGRESS Working on refactor
-```
-
-#### Tables
-```org
-| Name | Value | Status |
-|---------+-------+--------|
-| Item 1 | 100 | Done |
-| Item 2 | 200 | Pending|
-```
-
-## Finding and Organizing Notes
-
-### Using the Org Skill
-
-This skill integrates with the **Org skill** for programmatic org-mode operations on note files.
-
-**Tool location:** `~/.config/claude/skills/Org/tools/org-manager`
-
-**Create notes (denote integration):**
-```bash
-# Create new note with proper denote formatting
-org-manager denote-create "My Note Title" "tag1,tag2" \
- --category=category --directory=~/desktop/org/notes
-
-# Create automated note with signature
-org-manager denote-create "Session Log" "history,session" \
- --signature=pkai --category=history
-
-# Read note metadata
-org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
-
-# Update note frontmatter
-org-manager denote-update ~/desktop/org/notes/20251205T*.org \
- --title="New Title" --tags="new,tags"
-
-# Append content to existing note
-echo "* New Section" > /tmp/content.org
-org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
-```
-
-**Search and query notes:**
-```bash
-# Search for term across all notes
-org-manager search ~/desktop/org/notes/*.org "wireguard"
-
-# Count TODOs in notes
-org-manager count ~/desktop/org/notes/20251203T151822--project__ai.org
-
-# List sections in a note
-org-manager sections ~/desktop/org/notes/20251203T151822--project__ai.org
-```
-
-### By Tags
-Notes with `:nixos:` tag can be found by searching filenames with `_nixos`
-
-```bash
-# Find notes with specific tag
-ls ~/desktop/org/notes/*__*homelab*.org
-ls ~/desktop/org/notes/*__*nixos*.org
-```
-
-### By Date
-Files are naturally sorted by timestamp, most recent first when reverse sorted
-
-```bash
-# Most recent notes
-ls -t ~/desktop/org/notes/*.org | head -10
-
-# Notes from specific month
-ls ~/desktop/org/notes/202512*.org
-```
-
-### By Title
-Search for keywords in the title portion between `--` and `__`
-
-```bash
-# Find notes with "wireguard" in title
-ls ~/desktop/org/notes/*--*wireguard*.org
-```
-
-### Using grep (fallback)
-```bash
-# Find notes about a topic
-grep -l "wireguard" ~/desktop/org/notes/*.org
-
-# Search content
-grep -r "NixOS configuration" ~/desktop/org/notes/
-
-# Find notes with specific tag in frontmatter
-rg "#+filetags:.*:nixos:" ~/desktop/org/notes/
-```
-
-## Special Note Types
-
-### Readwise Notes
-Notes imported from Readwise follow a special format:
-- Filename: `TIMESTAMP==readwise=TYPE--title.org`
-- Include `#+property: READWISE_URL:`
-- Organized by highlights
-
-Don't create these manually; they're imported automatically.
-
-### Meeting Notes
-```org
-#+title: Team Meeting 2025-12-03
-#+date: [2025-12-03 Wed 10:00]
-#+filetags: :work:meetings:
-#+identifier: 20251203T100000
-#+category: work
-
-* Attendees
-- Alice
-- Bob
-- Charlie
-
-* Agenda
-** Topic 1
-** Topic 2
-
-* Action Items
-- [ ] Alice: Follow up on X
-- [ ] Bob: Complete Y by Friday
-```
-
-### Learning Notes
-```org
-#+title: Learning Rust Ownership
-#+date: [2025-12-03 Wed 16:00]
-#+filetags: :learning:rust:programming:
-#+identifier: 20251203T160000
-#+category: learning
-
-* Key Concepts
-** Ownership Rules
-1. Each value has an owner
-2. Only one owner at a time
-3. Value dropped when owner goes out of scope
-
-* Examples
-#+begin_src rust
-fn main() {
- let s = String::from("hello");
-}
-#+end_src
-
-* Resources
-- [[https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html][Rust Book: Ownership]]
-```
-
-### Project Notes
-```org
-#+title: Claude AI Infrastructure Project
-#+date: [2025-12-03 Wed 15:18]
-#+filetags: :project:ai:infrastructure:
-#+identifier: 20251203T151800
-#+category: project
-
-* Project Goals
-- [ ] Goal 1
-- [ ] Goal 2
-
-* Timeline
-** Phase 1: Setup
-SCHEDULED: <2025-12-05 Fri>
-
-** Phase 2: Implementation
-SCHEDULED: <2025-12-10 Wed>
-
-* Resources
-- [[file:other-note.org][Related Note]]
-```
-
-## Integration with Tools
-
-### Emacs Denote
-If using Emacs denote package:
-- `denote` - Create new note
-- `denote-link` - Link to existing note
-- `denote-org-extras-link-to-heading` - Link to specific heading
-- `denote-rename-file` - Rename note (updates title/tags)
-
-### Ripgrep Searches
-```bash
-# Find notes mentioning "wireguard"
-rg -i "wireguard" ~/desktop/org/notes/
-
-# Find notes with specific tag pattern
-rg -i "#+filetags:.*:nixos:" ~/desktop/org/notes/
-```
-
-## History Integration
-
-### When to Create History-Linked Notes
-
-Create notes with `:history:` tags when:
-- Documenting significant work sessions
-- Capturing important learnings from debugging
-- Recording research findings worth referencing
-- Noting architecture decisions
-
-### History Note Format
-
-**Filename with pkai signature**:
-```
-YYYYMMDDTHHMMSS==pkai--description__history_category_tags.org
-```
-
-**Content**:
-```org
-#+title: <Title>
-#+date: [YYYY-MM-DD Day HH:MM]
-#+filetags: :history:category:topic1:topic2:
-#+identifier: YYYYMMDDTHHMMSS
-#+category: history
-
-* Context
-What this documents
-
-* Details
-Main content
-
-* Related History Entries
-- [[file:~/.config/claude/history/sessions/2025-12/...][Session Entry]]
-- [[file:~/.config/claude/history/learnings/2025-12/...][Learning Entry]]
-
-* Related Notes
-- [[file:~/desktop/org/notes/...][Other Note]]
-```
-
-**Note**: The `==pkai` signature marks this as a history-system generated note.
-
-### Finding History Notes
-
-```bash
-# All history notes (by tag)
-ls ~/desktop/org/notes/*__history*.org
-
-# All automated history notes (by signature)
-ls ~/desktop/org/notes/*==pkai*.org
-
-# Specific category
-ls ~/desktop/org/notes/*__history_session*.org
-ls ~/desktop/org/notes/*==pkai*__history_learning*.org
-
-# Search content
-rg "topic" ~/desktop/org/notes/*__history*.org
-rg "topic" ~/desktop/org/notes/*==pkai*.org
-```
-
-### Cross-Referencing
-
-**From Notes to History**:
-```org
-* Related History
-- [[file:~/.config/claude/history/sessions/2025-12/2025-12-03-150000_SESSION_implementation.md][Implementation Session]]
-```
-
-**From History to Notes**:
-```markdown
-## Related Notes
-- [[file:~/desktop/org/notes/20251203T151822--topic__history_session.org][Topic Note]]
-```
-
-See the `history-system` skill for complete integration details.
-
-## Tips
-
-1. **Timestamps are unique**: Use current time for new notes
-2. **Tags are lowercase**: Keep them simple and consistent
-3. **Link liberally**: Connect related notes and history entries
-4. **Use TODO items**: Make notes actionable
-5. **Include examples**: Code, commands, configurations
-6. **Reference sources**: Link to documentation, articles
-7. **Review regularly**: Update and refine over time
-8. **History tag**: Add `:history:` for integration with history system
-
-## Example Workflow
-
-1. Identify topic to document
-2. Generate timestamp: `date +"%Y%m%dT%H%M%S"`
-3. Choose 2-4 relevant tags
-4. Create filename: `TIMESTAMP--topic-description__tag1_tag2.org`
-5. Add org-mode frontmatter
-6. Write content with headings, links, code blocks
-7. Add TODO items for follow-up
-8. Link related notes
-
-Remember: Notes are living documents. It's okay to update, refine, and reorganize as understanding deepens.
-
-## Examples
-
-**Example 1: Creating a new note**
-```
-User: "Create a note about NixOS flakes best practices"
-→ Generates timestamp identifier
-→ Creates denote-formatted filename with tags
-→ Adds org-mode frontmatter (title, date, filetags)
-→ Structures content with headings
-→ Saves to ~/desktop/org/notes/
-→ Result: Well-organized note ready for future reference
-```
-
-**Example 2: Finding related notes**
-```
-User: "Show me notes about Tekton"
-→ Searches denote notes by tag: :tekton:
-→ Also searches by title and content
-→ Returns list of matching notes with timestamps
-→ Shows snippets of relevant content
-→ Result: Quick access to all Tekton-related knowledge
-```
-
-**Example 3: Linking notes together**
-```
-User: "Link this note to my CI/CD planning note"
-→ Finds target note by search
-→ Adds denote link using org-mode format
-→ Creates bidirectional reference
-→ Updates related notes section
-→ Result: Knowledge graph connections established
-```
dots/config/claude/skills/.archive/README.md
@@ -1,72 +0,0 @@
-# Archived Skills
-
-This directory contains skills that are not actively used but may be restored in the future.
-
-## Archived Skills
-
-### Android (Archived: 2026-01-06)
-- **Size**: 575 lines, 3 workflows
-- **Purpose**: Android app development with gomobile, Gradle, and modern tooling
-- **Reason**: Not actively developing Android apps with gomobile
-- **Restore**: `mv ~/.config/claude/skills/.archive/Android ~/.config/claude/skills/`
-
-### Art (Archived: 2026-01-06)
-- **Size**: 101 lines, 2 workflows
-- **Purpose**: Visual content generation and diagram creation
-- **Reason**: Infrequently used for diagram generation
-- **Restore**: `mv ~/.config/claude/skills/.archive/Art ~/.config/claude/skills/`
-
-### Notes (Archived: 2026-01-06)
-- **Size**: 612 lines, 8 workflows
-- **Purpose**: Denote-format note-taking with org-mode
-- **Reason**: Consolidated into unified Org skill
-- **Restore**: `mv ~/.config/claude/skills/.archive/Notes ~/.config/claude/skills/`
-- **Note**: Org skill now handles both notes (denote) and journals (journelly)
-
-### Journal (Archived: 2026-01-06)
-- **Size**: 594 lines, 9 tools + 2 workflows
-- **Purpose**: Journelly-format journal entries
-- **Reason**: Consolidated into unified Org skill
-- **Restore**: `mv ~/.config/claude/skills/.archive/Journal ~/.config/claude/skills/`
-- **Note**: Org skill now includes all journal tools and workflows
-
-## How to Restore a Skill
-
-To restore an archived skill, simply move it back:
-
-```bash
-# Restore Android skill
-mv ~/.config/claude/skills/.archive/Android ~/.config/claude/skills/
-
-# Restore Art skill
-mv ~/.config/claude/skills/.archive/Art ~/.config/claude/skills/
-```
-
-The skill will be immediately available in the next Claude Code session.
-
-## Archive History
-
-- **2026-01-06 (Phase 2)**: Note-taking consolidation
- - Archived Notes skill (consolidated into Org)
- - Archived Journal skill (consolidated into Org)
- - Created unified Org skill with reference docs and combined tools
- - Skills reduced: 26 → 23 active skills (-11%)
- - Total lines reduced: 1,548 → 412 lines (-73% documentation)
- - See: `~/desktop/org/notes/20260106T165340--claude-skills-simplification-review-2026__claude_plan_review_simplification_skills.org`
-
-- **2026-01-06 (Phase 1)**: Initial archive creation
- - Archived Android (not actively used)
- - Archived Art (infrequently used)
- - Skills reduced: 28 → 26 active skills (-7%)
- - See: `~/desktop/org/notes/20260106T165340--claude-skills-simplification-review-2026__claude_plan_review_simplification_skills.org`
-
----
-
-## Final Summary (Post-Phase 3)
-
-**Active skills**: 23
-**Archived skills**: 4 (Android, Art, Notes, Journal)
-**Total skills (including archived)**: 27
-**Workflow files reduced**: 67 → 41 (-39%)
-**Advanced.md files created**: 8 (Git, Nix, TODOs, golang, Python, Rust, EmacsLisp, Tekton)
-**Status line**: Configured to show only active skills (23)
dots/config/claude/skills/CORE/history-system.md
@@ -77,6 +77,7 @@ Note: Tool-specific data (like tool-outputs JSONL) stays under tool config:
# Session: <Description>
**Date:** YYYY-MM-DD
+**Tool:** claude
## Summary
Brief description of what was accomplished.
@@ -97,6 +98,7 @@ Result of the session.
# Learning: <Description>
**Date:** YYYY-MM-DD
+**Tool:** claude
**Context:** What problem was being solved
## The Problem
@@ -118,6 +120,7 @@ How to avoid this in the future.
# Research: <Topic>
**Date:** YYYY-MM-DD
+**Tool:** claude
**Question:** What was being investigated
## Findings
@@ -136,6 +139,7 @@ What was learned and decided.
# Decision: <Description>
**Date:** YYYY-MM-DD
+**Tool:** claude
**Context:** Why this decision needed to be made
## Options Considered
@@ -154,6 +158,7 @@ What this means for the system.
# Execution: <Description>
**Date:** YYYY-MM-DD
+**Tool:** claude
**Context:** What was being built or executed
## What Was Done
dots/config/claude/skills/Jira/tools/jira-to-org
@@ -1,201 +0,0 @@
-#!/usr/bin/env bash
-# Convert Jira issue to org-mode format
-
-set -euo pipefail
-
-usage() {
- cat <<EOF
-Usage: jira-to-org ISSUE-KEY [options]
-
-Convert Jira issue to org-mode format for denote notes.
-
-Options:
- --template TYPE Template type: investigation, planning, discussion
- --output FILE Output file (default: stdout)
- -h, --help Show this help
-
-Examples:
- jira-to-org SRVKP-1234
- jira-to-org SRVKP-1234 --template investigation
- jira-to-org SRVKP-1234 --output ~/notes/issue.org
-EOF
- exit 1
-}
-
-ISSUE_KEY="${1:-}"
-TEMPLATE="basic"
-OUTPUT=""
-
-shift || true
-
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --template)
- TEMPLATE="$2"
- shift 2
- ;;
- --output)
- OUTPUT="$2"
- shift 2
- ;;
- -h|--help)
- usage
- ;;
- *)
- echo "Unknown option: $1"
- usage
- ;;
- esac
-done
-
-if [[ -z "$ISSUE_KEY" ]]; then
- echo "Error: ISSUE-KEY required"
- usage
-fi
-
-# Fetch issue details
-ISSUE_DATA=$(jira issue view "$ISSUE_KEY" --plain 2>/dev/null || {
- echo "Error: Failed to fetch issue $ISSUE_KEY"
- exit 1
-})
-
-# Extract fields (basic parsing - adjust based on actual output format)
-SUMMARY=$(echo "$ISSUE_DATA" | grep -A1 "^Summary:" | tail -1 | sed 's/^[[:space:]]*//')
-STATUS=$(echo "$ISSUE_DATA" | grep "Status:" | sed 's/.*Status:[[:space:]]*//' | awk '{print $1}')
-PRIORITY=$(echo "$ISSUE_DATA" | grep "Priority:" | sed 's/.*Priority:[[:space:]]*//' | awk '{print $1}')
-ASSIGNEE=$(echo "$ISSUE_DATA" | grep "Assignee:" | sed 's/.*Assignee:[[:space:]]*//')
-TYPE=$(echo "$ISSUE_DATA" | grep "Type:" | sed 's/.*Type:[[:space:]]*//' | awk '{print $1}')
-
-# Generate org-mode content based on template
-generate_org() {
- cat <<EOF
-* $ISSUE_KEY: $SUMMARY
-
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/$ISSUE_KEY
-:JIRA_KEY: $ISSUE_KEY
-:JIRA_STATUS: $STATUS
-:JIRA_PRIORITY: $PRIORITY
-:JIRA_TYPE: $TYPE
-:CREATED: $(date '+[%Y-%m-%d %a %H:%M]')
-:END:
-
-[[https://issues.redhat.com/browse/$ISSUE_KEY][View in Jira]]
-
-** Issue Details
-- Status: $STATUS
-- Priority: $PRIORITY
-- Assignee: $ASSIGNEE
-- Type: $TYPE
-
-** Description
-EOF
-
- # Add template-specific sections
- case "$TEMPLATE" in
- investigation)
- cat <<'EOF'
-
-** Investigation
-*** Hypothesis
-
-
-*** Findings
-
-
-*** Root Cause
-
-
-** Solution
-*** Proposed Fix
-
-
-*** Testing Plan
-- [ ] Test case 1
-- [ ] Test case 2
-
-*** Implementation Notes
-
-
-** Related
-- Related issues:
-- PRs:
-- Documentation:
-EOF
- ;;
- planning)
- cat <<'EOF'
-
-** Goals
-
-
-** Requirements
-*** Functional Requirements
-
-
-*** Non-Functional Requirements
-
-
-** Design
-*** Architecture
-
-
-*** Components
-
-
-** Implementation Plan
-*** Phase 1
-- [ ] Task 1
-- [ ] Task 2
-
-** Testing Strategy
-
-
-** Timeline
-| Phase | Start | End | Status |
-|-------+-------+-----+--------|
-| | | | |
-EOF
- ;;
- discussion)
- cat <<'EOF'
-
-** Context
-
-
-** Discussion Points
-
-
-** Decisions
-
-
-** Action Items
-- [ ] Action 1
-- [ ] Action 2
-
-** Next Steps
-
-EOF
- ;;
- basic)
- cat <<'EOF'
-
-** Notes
-
-
-** Tasks
-- [ ] Task 1
-
-** Related
-EOF
- ;;
- esac
-}
-
-# Output to file or stdout
-if [[ -n "$OUTPUT" ]]; then
- generate_org > "$OUTPUT"
- echo "Created org-mode note: $OUTPUT"
-else
- generate_org
-fi
dots/config/claude/skills/Jira/workflows/Create.md
@@ -282,7 +282,6 @@ After creating an issue:
- **Assign it**: To yourself or team member
- **Add to sprint**: Include in current sprint
- **Link to epic**: Associate with larger initiative
-- **Create note**: Document in org-mode
- **Add TODO**: Track in personal task list
- **Share**: Send link to team
@@ -326,7 +325,6 @@ jira issue create \
## Integration
-- **Notes Skill**: Reference issue in denote notes
- **TODOs Skill**: Create corresponding TODO
- **Git**: Reference issue key in commits
- **Email**: Notify team about new issue
dots/config/claude/skills/Jira/workflows/LinkToNote.md
@@ -1,356 +0,0 @@
-# Link Jira Issue to Org-Mode Note
-
-Create or link Jira issues with org-mode denote notes for documentation and context.
-
-## When to Use
-
-- User wants to document a Jira issue in detail
-- Need to track investigation or research
-- Creating comprehensive issue analysis
-- Linking work context with issues
-- Project planning with Jira integration
-
-## Steps
-
-1. **Identify the Jira issue** to document
-2. **Fetch issue details** for context
-3. **Create denote note** with appropriate tags
-4. **Add Jira issue link** to note
-5. **Optionally add note link** to Jira comment
-6. **Confirm creation** and offer next actions
-
-## Integration Pattern
-
-### 1. Fetch Issue Information
-```bash
-jira issue view ISSUE-KEY --plain
-```
-
-### 2. Create Denote Note
-Use Notes skill to create note with:
-- **Title**: "ISSUE-KEY: Issue Summary"
-- **Tags**: `jira`, project tag (e.g., `tekton`), issue type (e.g., `bug`)
-- **Keywords**: Relevant keywords from issue
-
-### 3. Add Jira Link to Note
-```org
-* ISSUE-KEY: Issue Summary
-
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/ISSUE-KEY
-:CREATED: [timestamp]
-:END:
-
-[[https://issues.redhat.com/browse/ISSUE-KEY][View in Jira]]
-
-** Issue Details
-- Status: [status]
-- Priority: [priority]
-- Assignee: [assignee]
-- Type: [type]
-
-** Description
-[Issue description]
-
-** Investigation Notes
-[Your notes here]
-
-** Related Work
-- Link to related notes
-- Link to code changes
-- Link to documentation
-```
-
-### 4. Link Note to Jira (Optional)
-```bash
-jira issue comment add ISSUE-KEY "Documentation: file://path/to/note.org"
-```
-
-## Examples
-
-### Example 1: Document Bug Investigation
-**User**: "Create a note for investigating SRVKP-7327"
-
-**Actions**:
-1. Fetch issue:
-```bash
-jira issue view SRVKP-7327 --plain
-```
-
-2. Create note with Notes skill:
-- Title: "SRVKP-7327: Affinity assistant pod not created"
-- Tags: `jira`, `tekton`, `bug`, `affinity-assistant`
-
-3. Populate note with issue details and add sections for:
-- Root cause analysis
-- Investigation steps
-- Testing notes
-- Solution approach
-
-4. Add comment to Jira:
-```bash
-jira issue comment add SRVKP-7327 "Investigation notes: [link to note]"
-```
-
-### Example 2: Feature Planning
-**User**: "Create a planning note for the new epic SRVKP-8000"
-
-**Actions**:
-1. Fetch epic details
-2. Create note with:
- - Epic summary
- - Goals and requirements
- - Sub-tasks breakdown
- - Technical approach
-3. Link sub-tasks as they're created
-
-### Example 3: Link Existing Note
-**User**: "Link my note about affinity assistants to SRVKP-7327"
-
-**Actions**:
-1. Find the note using Notes skill
-2. Add Jira property to note:
-```org
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/SRVKP-7327
-:END:
-```
-3. Add comment to Jira with note reference
-
-## Note Templates
-
-### Bug Investigation Note
-```org
-* SRVKP-XXXX: Bug Summary
-
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/SRVKP-XXXX
-:CREATED: [timestamp]
-:END:
-
-[[https://issues.redhat.com/browse/SRVKP-XXXX][View in Jira]]
-
-** Issue Details
-- Status: To Do
-- Priority: Major
-- Assignee: username
-- Reporter: reporter-name
-- Type: Bug
-
-** Description
-[Issue description from Jira]
-
-** Reproduction Steps
-1. Step 1
-2. Step 2
-3. Step 3
-
-** Investigation
-*** Hypothesis
-[Initial thoughts on root cause]
-
-*** Findings
-- Finding 1
-- Finding 2
-
-*** Root Cause
-[Identified root cause]
-
-** Solution
-*** Proposed Fix
-[Description of fix]
-
-*** Testing Plan
-- [ ] Test case 1
-- [ ] Test case 2
-- [ ] Regression testing
-
-*** Implementation Notes
-[Technical details]
-
-** Related
-- Related issues: [[SRVKP-YYYY]]
-- PRs: [[link]]
-- Documentation: [[link]]
-```
-
-### Feature Planning Note
-```org
-* SRVKP-XXXX: Feature Name
-
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/SRVKP-XXXX
-:TYPE: Epic
-:CREATED: [timestamp]
-:END:
-
-** Overview
-[Feature description]
-
-** Goals
-- Goal 1
-- Goal 2
-- Goal 3
-
-** Requirements
-*** Functional Requirements
-- Requirement 1
-- Requirement 2
-
-*** Non-Functional Requirements
-- Performance
-- Security
-- Scalability
-
-** Design
-*** Architecture
-[Architecture overview]
-
-*** Components
-- Component 1: [description]
-- Component 2: [description]
-
-*** Data Model
-[Data structures]
-
-** Implementation Plan
-*** Phase 1: [Name]
-- [ ] Task 1 - SRVKP-XXX1
-- [ ] Task 2 - SRVKP-XXX2
-
-*** Phase 2: [Name]
-- [ ] Task 3 - SRVKP-XXX3
-- [ ] Task 4 - SRVKP-XXX4
-
-** Testing Strategy
-- Unit tests
-- Integration tests
-- E2E tests
-
-** Documentation
-- User documentation
-- API documentation
-- Migration guide
-
-** Timeline
-| Phase | Start | End | Status |
-|-------+-------+-----+--------|
-| Phase 1 | [date] | [date] | In Progress |
-| Phase 2 | [date] | [date] | Planned |
-
-** Related Issues
-- SRVKP-XXX1: [[link]]
-- SRVKP-XXX2: [[link]]
-```
-
-### Meeting/Discussion Note
-```org
-* SRVKP-XXXX Discussion Notes
-
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/SRVKP-XXXX
-:MEETING_DATE: [date]
-:ATTENDEES: person1, person2, person3
-:END:
-
-** Context
-[Issue being discussed]
-
-** Discussion Points
-*** Point 1
-[Discussion and decisions]
-
-*** Point 2
-[Discussion and decisions]
-
-** Decisions
-- Decision 1
-- Decision 2
-
-** Action Items
-- [ ] Action 1 - @person1
-- [ ] Action 2 - @person2
-
-** Next Steps
-[What happens next]
-```
-
-## Workflow Integration
-
-### From Jira to Note
-1. User mentions Jira issue
-2. Fetch issue details
-3. Create denote note with structure
-4. Populate with issue information
-5. Add sections for work tracking
-
-### From Note to Jira
-1. User creates note about topic
-2. Realize it needs Jira tracking
-3. Create Jira issue with Notes skill context
-4. Link note to newly created issue
-5. Add Jira property to note
-
-### Bi-directional Linking
-1. Issue has note reference in comment
-2. Note has Jira property
-3. Easy navigation between both
-4. Synchronized updates
-
-## Org-Mode Properties for Jira
-
-```org
-:PROPERTIES:
-:JIRA: https://issues.redhat.com/browse/ISSUE-KEY
-:JIRA_KEY: SRVKP-1234
-:JIRA_STATUS: In Progress
-:JIRA_PRIORITY: Major
-:JIRA_ASSIGNEE: username
-:LAST_SYNCED: [timestamp]
-:END:
-```
-
-## Benefits
-
-1. **Rich Documentation**: More detailed than Jira allows
-2. **Local Search**: Find issues via denote search
-3. **Context Preservation**: Keep investigation notes
-4. **Linking**: Connect issues with code, docs, other notes
-5. **Version Control**: Track note changes over time
-6. **Offline Access**: Work without internet
-7. **Integration**: Part of your note-taking workflow
-
-## Best Practices
-
-1. **Create early**: Start note when beginning work
-2. **Update regularly**: Keep notes current
-3. **Link liberally**: Connect to related notes
-4. **Use org features**: Tags, TODOs, timestamps
-5. **Sync key info**: Update Jira with major findings
-6. **Preserve context**: Don't delete investigation paths
-7. **Structure consistently**: Use templates
-
-## Follow-up Actions
-
-After linking:
-- **Add TODO**: If action needed
-- **Update Jira**: Comment with note link
-- **Tag appropriately**: For denote searching
-- **Link related notes**: Cross-reference
-- **Schedule review**: Revisit for updates
-
-## Tips
-
-- **Use denote backlinks**: Find all issues-related notes
-- **Tag by project**: `tekton`, `pipelines`, etc.
-- **Tag by type**: `bug`, `feature`, `investigation`
-- **Include timestamps**: Track when work done
-- **Export to Jira**: Copy sections to Jira when done
-- **Archive when done**: Don't delete, archive
-
-## Integration with Other Skills
-
-- **Notes Skill**: Create and manage denote notes
-- **TODOs Skill**: Create action items from issues
-- **Git Skill**: Link commits to issues and notes
-- **Org Skill**: Manage org-mode structure
dots/config/claude/skills/Jira/workflows/Transition.md
@@ -287,7 +287,6 @@ Before transitioning, consider:
After transition:
- **Update TODO**: Sync with org-mode TODOs
- **Notify team**: If affects others
-- **Update notes**: Document in denote notes
- **Check dependencies**: Update linked issues
- **Plan next**: Identify next task
@@ -304,6 +303,5 @@ After transition:
## Integration
- **TODOs Skill**: Update org-mode TODO states
-- **Notes Skill**: Document transition reasoning
- **Git Skill**: Reference in commits
- **Email Skill**: Notify stakeholders
dots/config/claude/skills/Jira/workflows/View.md
@@ -58,7 +58,6 @@ After viewing an issue, ask the user if they want to:
- **Update status**: Move to different workflow state
- **Add comment**: Contribute to discussion
- **Assign**: Change assignee
-- **Create note**: Link to org-mode note for documentation
- **Create TODO**: Add to personal task list
- **View related issues**: Check linked issues or epic
@@ -107,6 +106,5 @@ Then extract and present:
## Integration with Other Skills
-- **Notes**: Create a denote note with `[[https://issues.redhat.com/browse/ISSUE-KEY][ISSUE-KEY]]` link
- **TODOs**: Add a TODO if action is needed
- **Email**: Draft email to assignee if needed
dots/config/claude/skills/Jira/INTEGRATION.md
@@ -1,545 +0,0 @@
-# Jira Skill Integration Guide
-
-Deep integration patterns between Jira and your existing workflow tools.
-
-## Integration Tools
-
-### 1. jira-to-todo
-Convert Jira issues to org-mode TODOs
-
-**Features**:
-- Single or multiple issue conversion
-- JQL query support
-- Automatic priority mapping (Jira → Org)
-- Automatic state mapping (Jira status → TODO state)
-- Add to inbox.org or output to file
-- Preserves Jira metadata in properties
-
-**Usage**:
-```bash
-# Convert single issue
-jira-to-todo SRVKP-1234
-
-# Convert my sprint issues
-jira-to-todo --jql "sprint in openSprints() AND assignee = currentUser()"
-
-# Add to inbox for refiling
-jira-to-todo --jql "..." --add
-
-# Save to file
-jira-to-todo SRVKP-1234 --output ~/notes/issue-todo.org
-```
-
-**Priority Mapping**:
-- Blocker/Critical → [#1]
-- Major/High → [#2]
-- Medium → [#3]
-- Minor → [#4]
-- Trivial → [#5]
-
-**State Mapping**:
-- To Do → TODO
-- In Progress/Code Review/QE Review → STRT
-- Done → DONE
-- (Others) → TODO
-
-### 2. jira-sprint-summary
-Generate sprint summaries for standups, retrospectives, and planning
-
-**Features**:
-- Current/previous/next sprint selection
-- Multiple report types (standup, retro, planning)
-- Multiple output formats (text, org, md)
-- Automatic metrics calculation
-- Issue breakdowns by status and type
-
-**Usage**:
-```bash
-# Daily standup notes
-jira-sprint-summary --standup
-
-# Retrospective in org format
-jira-sprint-summary --prev --retro --format org
-
-# Save planning notes
-jira-sprint-summary --next --planning --output ~/notes/sprint-plan.org
-
-# Current sprint summary
-jira-sprint-summary --current
-```
-
-### 3. jira-search
-Predefined search patterns (already covered in README)
-
-### 4. jira-to-org
-Convert issues to denote note format (already covered)
-
-## Integration Patterns
-
-### Pattern 1: Sprint → TODOs Workflow
-
-**Scenario**: New sprint starts, create TODOs for your sprint commitments
-
-```bash
-# 1. Get current sprint issues
-jira-sprint-summary --current
-
-# 2. Convert sprint issues to TODOs
-jira-to-todo --jql "sprint in openSprints() AND assignee = currentUser()" \
- --add --section Work
-
-# 3. Review in Emacs and refile to appropriate sections
-emacs ~/desktop/org/inbox.org
-```
-
-**Integration Points**:
-- Jira sprint → org-mode TODOs
-- Automatic priority/state mapping
-- Preserves Jira links in properties
-- Daily sync capability
-
-### Pattern 2: Issue → Note → TODO Workflow
-
-**Scenario**: Complex issue requiring investigation and tracking
-
-```bash
-# 1. View issue
-jira issue view SRVKP-7327
-
-# 2. Create investigation note
-jira-to-org SRVKP-7327 --template investigation \
- --output ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--srvkp-7327-affinity-assistant__jira_tekton_bug.org
-
-# 3. Create TODO
-jira-to-todo SRVKP-7327 --add --section Work
-
-# 4. Start work
-jira issue assign SRVKP-7327 $(jira me)
-jira issue move SRVKP-7327 "In Progress"
-
-# 5. Work in note, update Jira with findings
-# (In Emacs, edit note)
-
-# 6. Update Jira
-jira issue comment add SRVKP-7327 "$(cat <<EOF
-Root cause identified: [details from note]
-
-Investigation notes: ~/desktop/org/notes/[note-file]
-EOF
-)"
-
-# 7. Complete
-jira issue move SRVKP-7327 "Code Review"
-# Update TODO state in org-mode
-```
-
-**Integration Points**:
-- Jira → denote note with structure
-- Jira → org TODO
-- Note ↔ Jira bidirectional references
-- Manual sync of states
-
-### Pattern 3: Daily Standup Workflow
-
-**Scenario**: Prepare for daily standup
-
-```bash
-# 1. Generate standup notes
-jira-sprint-summary --standup --format org \
- --output ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--daily-standup__standup_work.org
-
-# 2. Review in Emacs
-emacs ~/desktop/org/notes/*standup*.org
-
-# 3. During standup, reference the note
-
-# 4. After standup, update Jira states based on discussion
-# (Use Transition workflow)
-```
-
-**Integration Points**:
-- Jira → standup note
-- Automatic yesterday/today/blockers extraction
-- Sprint progress visibility
-- Action item tracking
-
-### Pattern 4: Sprint Planning Workflow
-
-**Scenario**: Plan next sprint
-
-```bash
-# 1. Review previous sprint
-jira-sprint-summary --prev --retro --format org \
- --output ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--sprint-retro__sprint_tekton.org
-
-# 2. Generate planning notes
-jira-sprint-summary --next --planning --format org \
- --output ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--sprint-planning__sprint_tekton.org
-
-# 3. Add high priority items to sprint
-# (View planning notes, then add to sprint)
-SPRINT_ID=$(jira sprint list --next --plain | head -1 | awk '{print $1}')
-jira sprint add "$SPRINT_ID" SRVKP-1234 SRVKP-5678
-
-# 4. Create TODOs for sprint commitments
-jira-to-todo --jql "sprint = $SPRINT_ID AND assignee = currentUser()" --add
-
-# 5. In Emacs, organize TODOs and add to calendar
-```
-
-**Integration Points**:
-- Previous sprint retrospective
-- Planning notes generation
-- High priority backlog visibility
-- Sprint → TODOs conversion
-- Calendar integration (manual)
-
-### Pattern 5: Bug Triage Workflow
-
-**Scenario**: Weekly bug triage session
-
-```bash
-# 1. Find unassigned bugs
-jira-search team-bugs --limit 20 > /tmp/bugs.txt
-
-# 2. Review and assign
-cat /tmp/bugs.txt
-# For each bug you take:
-jira issue assign SRVKP-XXXX $(jira me)
-jira-to-todo SRVKP-XXXX --add --section Work
-
-# 3. Create triage notes
-cat > ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--bug-triage__triage_tekton.org <<EOF
-* Bug Triage - $(date +%Y-%m-%d)
-
-** Assigned to Me
-$(jira issue list --jql "type = Bug AND assignee = currentUser() AND created >= -7d" --plain | sed 's/^/- /')
-
-** Deferred
-- [List of bugs deferred with reason]
-
-** Action Items
-- [ ] Follow up on critical bugs
-- [ ] Update priorities
-EOF
-```
-
-**Integration Points**:
-- Jira search → file output
-- Assignment + TODO creation
-- Triage session notes
-- Action item tracking
-
-### Pattern 6: Weekly Review Workflow
-
-**Scenario**: End of week review
-
-```bash
-# 1. What did I accomplish this week?
-jira-search my-week --plain > ~/weekly-work.txt
-
-# 2. Sprint progress
-jira-sprint-summary --current --format org \
- --output ~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--weekly-review__review_work.org
-
-# 3. Create weekly review note
-cat >> ~/desktop/org/notes/*weekly-review*.org <<EOF
-
-** Completed This Week
-$(jira issue list --jql "assignee = currentUser() AND status changed to Done DURING (startOfWeek(), now())" --plain | sed 's/^/- DONE /')
-
-** In Progress
-$(jira-search my-progress --plain | sed 's/^/- STRT /')
-
-** Next Week Focus
-- [ ]
-- [ ]
-
-** Issues Encountered
--
-
-** Notes
--
-EOF
-
-# 4. Review in Emacs and plan next week
-```
-
-**Integration Points**:
-- Weekly accomplishments from Jira
-- Sprint summary
-- Weekly review note
-- Planning for next week
-
-## Automation Ideas
-
-### 1. Auto-sync TODO States
-
-**Cron job** to sync Jira → org-mode:
-```bash
-#!/bin/bash
-# ~/bin/jira-todo-sync
-
-# Get all Jira issues mentioned in org files
-grep -rho "SRVKP-[0-9]*" ~/desktop/org/*.org | sort -u | while read issue; do
- # Get Jira status
- status=$(jira issue view "$issue" --plain 2>/dev/null | grep -i "status" | awk '{print $2}')
-
- # Map to org state
- case "$status" in
- Done) org_state="DONE" ;;
- "In Progress"|"Code Review") org_state="STRT" ;;
- "To Do") org_state="TODO" ;;
- *) org_state="" ;;
- esac
-
- # Update org file (requires custom org-mode function)
- # emacsclient --eval "(update-todo-by-jira \"$issue\" \"$org_state\")"
-done
-```
-
-**Cron schedule**:
-```
-# Sync every morning at 8 AM
-0 8 * * 1-5 ~/bin/jira-todo-sync
-```
-
-### 2. Daily Standup Note Generator
-
-**Cron job** to generate daily standup notes:
-```bash
-#!/bin/bash
-# ~/bin/generate-standup
-
-NOTE_FILE=~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--daily-standup__standup_work.org
-
-jira-sprint-summary --standup --format org --output "$NOTE_FILE"
-
-# Optionally send notification
-notify-send "Standup Notes Ready" "Generated: $NOTE_FILE"
-```
-
-**Cron schedule**:
-```
-# Generate standup notes at 8:30 AM on weekdays
-30 8 * * 1-5 ~/bin/generate-standup
-```
-
-### 3. Sprint Start Automation
-
-**Script** to run at sprint start:
-```bash
-#!/bin/bash
-# ~/bin/sprint-start
-
-# Get sprint info
-SPRINT_ID=$(jira sprint list --current --plain | head -1 | awk '{print $1}')
-SPRINT_NAME=$(jira sprint list --current | head -1)
-
-# Create sprint planning note
-NOTE_FILE=~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--sprint-start-${SPRINT_ID}__sprint_tekton.org
-
-jira-sprint-summary --current --planning --format org --output "$NOTE_FILE"
-
-# Create TODOs for sprint
-jira-to-todo --jql "sprint in openSprints() AND assignee = currentUser()" --add
-
-echo "Sprint started: $SPRINT_NAME"
-echo "Planning note: $NOTE_FILE"
-echo "TODOs added to inbox.org"
-```
-
-### 4. Weekly Retrospective
-
-**Script** to run weekly:
-```bash
-#!/bin/bash
-# ~/bin/weekly-retro
-
-# Last week's work
-RETRO_FILE=~/desktop/org/notes/$(date +%Y%m%dT%H%M%S)--weekly-retro__retro_work.org
-
-cat > "$RETRO_FILE" <<EOF
-* Weekly Retrospective - $(date +%Y-%m-%d)
-
-** Completed This Week
-$(jira issue list --jql "assignee = currentUser() AND status changed to Done DURING (startOfWeek(), now())" --plain | sed 's/^/- /')
-
-** Sprint Progress
-$(jira-sprint-summary --current)
-
-** What Went Well
-
-
-** What Could Be Improved
-
-
-** Action Items for Next Week
-- [ ]
-
-** Notes
-
-EOF
-
-echo "Retrospective note created: $RETRO_FILE"
-```
-
-**Cron schedule**:
-```
-# Every Friday at 4 PM
-0 16 * * 5 ~/bin/weekly-retro
-```
-
-## Integration with Existing Skills
-
-### TODOs Skill
-
-**Bidirectional Integration**:
-```elisp
-;; In Emacs, function to fetch Jira status for TODO
-(defun my/sync-jira-todo ()
- "Sync Jira status to current TODO"
- (interactive)
- (save-excursion
- (org-back-to-heading t)
- (when-let ((jira-key (org-entry-get nil "JIRA_KEY")))
- (let* ((status (shell-command-to-string
- (format "jira issue view %s --plain | grep -i status | awk '{print $2}'" jira-key)))
- (org-state (pcase (string-trim status)
- ("Done" "DONE")
- ((or "In Progress" "Code Review" "QE Review") "STRT")
- ("To Do" "TODO")
- (_ nil))))
- (when org-state
- (org-todo org-state)
- (message "Synced %s: %s -> %s" jira-key status org-state))))))
-```
-
-### Notes Skill
-
-**Create Note with Jira Context**:
-When creating a denote note, automatically add Jira properties if issue mentioned:
-```elisp
-(defun my/denote-with-jira ()
- "Create denote note with Jira issue context"
- (interactive)
- (let ((issue-key (read-string "Jira issue (optional): ")))
- (when (and issue-key (not (string-empty-p issue-key)))
- ;; Fetch issue details and populate note
- (shell-command (format "jira-to-org %s" issue-key)))))
-```
-
-### Git Skill
-
-**Auto-reference in Commits**:
-```bash
-# Git prepare-commit-msg hook
-#!/bin/bash
-# .git/hooks/prepare-commit-msg
-
-# Extract branch name
-BRANCH=$(git symbolic-ref --short HEAD)
-
-# If branch contains Jira key, add to commit message
-if [[ $BRANCH =~ (SRVKP-[0-9]+) ]]; then
- JIRA_KEY="${BASH_REMATCH[1]}"
- # Add Jira reference if not already present
- if ! grep -q "$JIRA_KEY" "$1"; then
- echo "" >> "$1"
- echo "Refs: $JIRA_KEY" >> "$1"
- fi
-fi
-```
-
-### Email Skill
-
-**Send Sprint Summary via Email**:
-```bash
-#!/bin/bash
-# ~/bin/email-sprint-summary
-
-SUMMARY=$(jira-sprint-summary --current)
-
-# Use email skill or sendmail
-cat <<EOF | mail -s "Sprint Summary - $(date +%Y-%m-%d)" team@example.com
-$SUMMARY
-
---
-Generated by jira-sprint-summary
-EOF
-```
-
-## Keyboard Shortcuts (Emacs)
-
-Add to your Emacs config:
-```elisp
-;; Jira integration shortcuts
-(defun my/jira-view-at-point ()
- "View Jira issue at point"
- (interactive)
- (when-let ((issue-key (thing-at-point 'symbol)))
- (when (string-match "SRVKP-[0-9]+" issue-key)
- (shell-command (format "jira issue view %s" issue-key)))))
-
-(defun my/jira-create-todo ()
- "Create TODO from Jira issue at point"
- (interactive)
- (when-let ((issue-key (thing-at-point 'symbol)))
- (when (string-match "SRVKP-[0-9]+" issue-key)
- (shell-command (format "jira-to-todo %s --add" issue-key))
- (message "Created TODO for %s" issue-key))))
-
-(defun my/jira-sync-current-todo ()
- "Sync current TODO with Jira"
- (interactive)
- (my/sync-jira-todo))
-
-;; Keybindings
-(global-set-key (kbd "C-c j v") 'my/jira-view-at-point)
-(global-set-key (kbd "C-c j t") 'my/jira-create-todo)
-(global-set-key (kbd "C-c j s") 'my/jira-sync-current-todo)
-```
-
-## Best Practices
-
-1. **Regular Sync**: Run jira-todo-sync daily or before standup
-2. **Note References**: Always link Jira issues in notes
-3. **Consistent Workflow**: Follow established patterns
-4. **Automation**: Use cron for regular tasks
-5. **Documentation**: Document custom integrations
-6. **Backup**: Keep notes in version control
-7. **Review**: Weekly review of integration effectiveness
-
-## Troubleshooting
-
-### TODOs Out of Sync
-Run manual sync:
-```bash
-jira-to-todo --jql "assignee = currentUser() AND status != Done" --add
-```
-
-### Missing Sprint Data
-Verify sprint exists and you have access:
-```bash
-jira sprint list --current
-jira sprint list --prev
-```
-
-### Integration Scripts Failing
-Check:
-- jira command is in PATH
-- API token is valid
-- JQL queries are correct
-- File paths exist
-- Permissions are correct
-
-## Future Enhancements
-
-1. **Real-time Sync**: Watch for Jira webhooks
-2. **Conflict Resolution**: Handle divergent states
-3. **Bulk Operations**: Batch sync multiple issues
-4. **Calendar Integration**: Add sprint events to calendar
-5. **Dashboard**: Terminal dashboard with live data
-6. **Mobile Integration**: Phone notifications for updates
-7. **AI Summaries**: LLM-generated sprint summaries
-8. **Metrics Tracking**: Historical velocity and trends
dots/config/claude/skills/Jira/README.md
@@ -4,7 +4,7 @@ Comprehensive Jira issue management skill for Red Hat's issues.redhat.com, integ
## Overview
-This skill provides full command-line access to Jira issues through the jira-cli tool, with automatic API token injection from passage and deep integration with your existing workflow (org-mode notes, TODOs, Git).
+This skill provides full command-line access to Jira issues through the jira-cli tool, with automatic API token injection from passage and integration with your existing workflow (TODOs, Git).
## Installation
@@ -28,11 +28,9 @@ Jira/
│ ├── Update.md # Update issue fields
│ ├── Comment.md # Add comments
│ ├── Search.md # Advanced JQL searches
-│ ├── LinkToNote.md # Org-mode integration
│ └── Transition.md # Workflow state changes
└── tools/ # Helper scripts
- ├── jira-search # Common search patterns
- └── jira-to-org # Convert issue to org-mode
+ └── jira-search # Common search patterns
```
## Quick Start
@@ -89,12 +87,7 @@ jira issue list --jql "project = SRVKP AND status = 'To Do' ORDER BY priority DE
**Purpose**: Advanced JQL queries
**Example**: "Find stale high priority issues"
-### 7. LinkToNote Workflow
-**Trigger**: "create note for", "link to note"
-**Purpose**: Integrate with org-mode denote notes
-**Example**: "Create investigation note for SRVKP-7327"
-
-### 8. Transition Workflow
+### 7. Transition Workflow
**Trigger**: "start work", "mark done", "move to"
**Purpose**: Change workflow states
**Example**: "Move SRVKP-1234 to In Progress"
@@ -132,26 +125,6 @@ Available patterns:
- `high-priority` - Critical/High/Blocker items
- `needs-review` - In code/QE review
-### jira-to-org
-Convert Jira issues to org-mode format:
-
-```bash
-# Basic conversion
-jira-to-org SRVKP-1234
-
-# Investigation template
-jira-to-org SRVKP-1234 --template investigation
-
-# Save to file
-jira-to-org SRVKP-1234 --output ~/notes/issue.org
-```
-
-Templates:
-- `basic` - Simple note structure
-- `investigation` - Bug investigation template
-- `planning` - Feature planning template
-- `discussion` - Meeting/discussion notes
-
## Integration with Other Skills
### TODOs Skill
@@ -161,13 +134,6 @@ Create org-mode TODOs from Jira issues:
3. Create TODO in todos.org
4. Link back to Jira issue
-### Notes Skill
-Document Jira work in denote notes:
-1. Use jira-to-org to create note structure
-2. Add JIRA property for linking
-3. Track investigation and findings
-4. Reference in Jira comments
-
### Git Skill
Reference issues in commits:
```bash
@@ -213,20 +179,14 @@ jira sprint add SPRINT-123 SRVKP-1234
# 1. View issue
jira issue view SRVKP-7327
-# 2. Create investigation note
-jira-to-org SRVKP-7327 --template investigation --output ~/notes/investigation.org
-
-# 3. Start work
+# 2. Start work
jira issue assign SRVKP-7327 $(jira me)
jira issue move SRVKP-7327 "In Progress"
-# 4. Add findings
-# (Edit note, add findings)
-
-# 5. Update Jira
+# 3. Update Jira with findings
jira issue comment add SRVKP-7327 "Root cause identified: [details]"
-# 6. Complete
+# 4. Complete
jira issue move SRVKP-7327 "Code Review"
```
@@ -272,7 +232,6 @@ jira issue comment add ISSUE-KEY "Completed: [details]"
### Link Related Work
- Reference issue keys in commits
- Link PRs in Jira comments
-- Connect denote notes to issues
### Use Consistent Labels
- `documentation` - Needs docs
@@ -361,9 +320,6 @@ jira-search team-bugs --limit 5
jira issue assign SRVKP-1234 $(jira me)
jira issue move SRVKP-1234 "In Progress"
-# Create investigation note
-jira-to-org SRVKP-1234 --template investigation --output ~/notes/srvkp-1234.org
-
# Work on it...
# (Make code changes, test)
dots/config/claude/skills/Jira/SKILL.md
@@ -1,11 +1,11 @@
---
name: Jira
-description: Jira issue management for Red Hat issues.redhat.com. USE WHEN user mentions jira, ticket, issue, epic, sprint OR references Jira issue keys (SRVKP-1234, SRVCOM-456) OR wants to manage issue workflows, assignments, tracking OR needs to integrate Jira with org-mode notes and TODOs.
+description: Jira issue management for Red Hat issues.redhat.com. USE WHEN user mentions jira, ticket, issue, epic, sprint OR references Jira issue keys (SRVKP-1234, SRVCOM-456) OR wants to manage issue workflows, assignments, tracking.
---
# Jira
-Interactive command-line management of Jira issues, epics, and sprints for Red Hat's issues.redhat.com. Minimizes reliance on the web interface while providing comprehensive issue tracking, workflow automation, and integration with org-mode notes.
+Interactive command-line management of Jira issues, epics, and sprints for Red Hat's issues.redhat.com. Minimizes reliance on the web interface while providing comprehensive issue tracking and workflow automation.
## Workflow Routing
@@ -19,7 +19,6 @@ When the user's request matches specific Jira operations, route to the appropria
| **Update** | "update issue", "change status", "assign to", "move to" | `workflows/Update.md` |
| **Comment** | "add comment", "comment on", "reply to issue" | `workflows/Comment.md` |
| **Search** | "search for", "find issues", "JQL query", complex filtering | `workflows/Search.md` |
-| **LinkToNote** | "link to note", "create note for issue", "document issue" | `workflows/LinkToNote.md` |
| **Sprint** | "sprint", "current sprint", "add to sprint" | `workflows/Sprint.md` |
| **Transition** | "transition", "workflow", "move issue to", state changes | `workflows/Transition.md` |
@@ -46,10 +45,8 @@ When the user's request matches specific Jira operations, route to the appropria
- Filter issues by epic or sprint
### Integration with Org-Mode
-- Link Jira issues to org-mode notes
- Create TODOs from Jira issues
- Reference issues in project planning
-- Track issue progress in daily notes
## Common Jira Projects
@@ -81,7 +78,6 @@ project:
### 4. Integrate with Workflows
- Create org-mode TODOs for critical issues
-- Link issues to notes for context
- Use jira commands in scripts and automation
### 5. Common Filters
@@ -196,14 +192,7 @@ jira issue view SRVKP-1234 --plain
# Then use TODOs skill to add to org-mode
```
-### 3. Link to Org Notes
-```bash
-# View issue and create denote note with issue link
-jira issue view SRVKP-1234
-# Create note using Notes skill with Jira URL
-```
-
-### 4. Sprint Planning
+### 3. Sprint Planning
```bash
# List current sprint issues
jira sprint list --current
@@ -242,16 +231,6 @@ User: "Create a bug for the failing CI tests"
→ Creates issue and returns issue key (e.g., SRVKP-9999)
```
-**Example 4: Link Jira issue to org-mode note**
-```
-User: "Create a note for SRVKP-9243"
-→ Invokes LinkToNote workflow
-→ Fetches issue details from Jira
-→ Creates denote note with issue info
-→ Adds Jira URL to note
-→ Links note back in Jira comment (optional)
-```
-
## Tips
1. **Use aliases for common commands**: Add shell aliases for frequent operations
@@ -284,7 +263,6 @@ User: "Create a note for SRVKP-9243"
## Related Skills
- **TODOs**: Create TODOs from Jira issues
-- **Notes**: Document issues in denote notes
- **Git**: Reference issues in commits
- **Email**: Email issue summaries to team
dots/config/claude/skills/Org/reference/DenoteFormat.md
@@ -1,374 +0,0 @@
-# Denote Note-Taking Format
-
-Complete reference for creating and organizing notes in denote format using org-mode.
-
-## Note Location
-**Notes directory**: `/home/vincent/desktop/org/notes`
-
-## Denote Format
-
-Denote is an Emacs package for simple note management with a consistent naming scheme.
-
-### File Naming Pattern
-
-**Standard format**:
-```
-YYYYMMDDTHHMMSS--title__tag1_tag2_tag3.org
-```
-
-**With signature (for automated/system-generated notes)**:
-```
-YYYYMMDDTHHMMSS==signature--title__tag1_tag2_tag3.org
-```
-
-**Components**:
-- **Timestamp**: `YYYYMMDDTHHMMSS` - Unique identifier and creation time
-- **Signature** (optional): `==signature` - Identifies note origin/type (e.g., `==pkai` for automated history notes)
-- **Separator**: `--` - Separates timestamp/signature from title
-- **Title**: Descriptive, lowercase with hyphens
-- **Tag separator**: `__` - Separates title from tags
-- **Tags**: Underscore-separated, descriptive keywords
-
-**Examples**:
-```
-# Regular note
-20251203T151822--personal-ai-infrastructure-implementation-plan__ai_claude_infrastructure_nixos_plan.org
-
-# Automated history note (with pkai signature)
-20251203T155616==pkai--claude-skills-implementation-session__history_session_ai_claude.org
-```
-
-**Signature Usage**:
-- `==pkai` - Personal Knowledge Automated Infrastructure (history-generated notes)
-- Use signatures to identify system-generated or automated notes
-- Makes it easy to filter: `ls *==pkai*.org` shows all automated notes
-
-### File Structure
-
-Every denote note follows this org-mode structure:
-
-```org
-#+title: Note Title Here
-#+date: [YYYY-MM-DD Day HH:MM]
-#+filetags: :tag1:tag2:tag3:
-#+identifier: YYYYMMDDTHHMMSS
-#+category: category_name
-
-* First Section
-Content goes here...
-
-** Subsection
-More content...
-
-* Second Section
-Additional content...
-```
-
-**Key Elements**:
-- `#+title:` - Human-readable title (can have spaces, capitalization)
-- `#+date:` - Creation date in org-mode date format
-- `#+filetags:` - Tags surrounded by colons (`:tag1:tag2:`)
-- `#+identifier:` - Same as filename timestamp
-- `#+category:` - Optional category for organization
-
-## Common Tags
-
-### Technical Topics
-- `:ai:` - AI, LLMs, Claude Code
-- `:nixos:` - NixOS configuration and packages
-- `:golang:` - Go programming language
-- `:homelab:` - Infrastructure, servers, self-hosting
-- `:keyboards:` - Keyboard firmware (QMK, ZMK, Kanata)
-- `:emacs:` - Emacs editor and configuration
-- `:linux:` - Linux operating system
-- `:programming:` - General programming topics
-
-### Work and Projects
-- `:work:` - Work-related notes
-- `:redhat:` - Red Hat specific
-- `:openshift:` - OpenShift/Kubernetes
-- `:tekton:` - Tekton CI/CD
-- `:project:` - Project-specific notes
-
-### Personal
-- `:life:` - Personal life and experiences
-- `:books:` - Book notes and highlights
-- `:ideas:` - Ideas and brainstorming
-- `:plan:` - Planning and roadmaps
-- `:learning:` - Learning notes
-
-### Content Type
-- `:howto:` - How-to guides
-- `:reference:` - Reference material
-- `:tutorial:` - Tutorials and walkthroughs
-
-### History Integration
-- `:history:` - All history-related notes (required for integration)
-- `:history:session:` - Work session summaries
-- `:history:learning:` - Problem-solving insights and learnings
-- `:history:research:` - Deep technical investigations
-- `:history:decision:` - Architecture and design decisions
-- `:history:execution:` - Command execution logs
-
-**Note**: History-tagged notes are interconnected with `~/.config/claude/history/` entries.
-
-## Creating Notes
-
-### Using org-manager (Recommended)
-
-The `org-manager` tool provides batch mode denote integration:
-
-```bash
-# Create a simple note
-org-manager denote-create "Note Title" "tag1,tag2,tag3" \
- --category=homelab --directory=~/desktop/org/notes
-
-# Create with signature (for automated notes)
-org-manager denote-create "Session Summary" "history,session" \
- --signature=pkai --category=history
-
-# Create with initial content from file
-echo "* Custom Content" > /tmp/content.org
-org-manager denote-create "My Note" "nixos,config" \
- --content=/tmp/content.org
-```
-
-**Output**: Returns JSON with created file path:
-```json
-{
- "success": true,
- "filepath": "/path/to/20251205T140049--note-title__tag1_tag2_tag3.org",
- "timestamp": "20251205T140049",
- "message": "Created note: ..."
-}
-```
-
-### Manual Creation
-
-If creating notes manually without org-manager:
-
-#### Generate Timestamp
-```bash
-# Get current timestamp for identifier
-date +"%Y%m%dT%H%M%S"
-```
-
-#### Generate Full Date
-```bash
-# Get org-mode formatted date
-date +"[%Y-%m-%d %a %H:%M]"
-```
-
-## Best Practices
-
-### Title Guidelines
-- Use descriptive, specific titles
-- Keep titles concise (3-7 words ideal)
-- Use lowercase in filename, normal case in `#+title:`
-- Examples:
- - Good: "implementing-oauth2-authentication"
- - Bad: "notes" or "stuff-about-things"
-
-### Tagging Strategy
-- Use 2-5 tags per note
-- Start general, then specific: `:programming:golang:testing:`
-- Create tags consistently (don't use both `:ai:` and `:artificial-intelligence:`)
-- Tags are for finding, not categorizing everything
-
-### Content Organization
-- Use org-mode headings (`*`, `**`, `***`)
-- Include a brief overview/summary at the top
-- Use TODO items for actionable content
-- Link related notes with org-mode links
-
-## Org-Mode Features
-
-### Headings
-```org
-* Level 1
-** Level 2
-*** Level 3
-```
-
-### Links
-```org
-# Link to other notes
-[[file:~/desktop/org/notes/20251203T151822--personal-ai-infrastructure__ai.org][PAI Implementation Plan]]
-
-# External links
-[[https://example.com][Example Website]]
-```
-
-### Code Blocks
-```org
-#+begin_src bash
-make switch
-#+end_src
-
-#+begin_src nix
-services.nginx.enable = true;
-#+end_src
-```
-
-### Lists
-```org
-# Unordered
-- Item 1
-- Item 2
- - Subitem
-
-# Ordered
-1. First
-2. Second
-
-# TODO lists
-- [ ] Uncompleted task
-- [X] Completed task
-```
-
-### Tables
-```org
-| Name | Value | Status |
-|---------+-------+--------|
-| Item 1 | 100 | Done |
-| Item 2 | 200 | Pending|
-```
-
-## Finding Notes
-
-### By Tags
-```bash
-# Find notes with specific tag
-ls ~/desktop/org/notes/*__*homelab*.org
-ls ~/desktop/org/notes/*__*nixos*.org
-```
-
-### By Date
-```bash
-# Most recent notes
-ls -t ~/desktop/org/notes/*.org | head -10
-
-# Notes from specific month
-ls ~/desktop/org/notes/202512*.org
-```
-
-### By Title
-```bash
-# Find notes with "wireguard" in title
-ls ~/desktop/org/notes/*--*wireguard*.org
-```
-
-### Using grep
-```bash
-# Find notes about a topic
-grep -l "wireguard" ~/desktop/org/notes/*.org
-
-# Find notes with specific tag in frontmatter
-rg "#+filetags:.*:nixos:" ~/desktop/org/notes/
-```
-
-## Special Note Types
-
-### Readwise Notes
-- Filename: `TIMESTAMP==readwise=TYPE--title.org`
-- Include `#+property: READWISE_URL:`
-- Don't create manually; they're imported automatically
-
-### Meeting Notes
-```org
-#+title: Team Meeting 2025-12-03
-#+date: [2025-12-03 Wed 10:00]
-#+filetags: :work:meetings:
-#+identifier: 20251203T100000
-#+category: work
-
-* Attendees
-- Alice, Bob
-
-* Agenda
-** Topic 1
-
-* Action Items
-- [ ] Alice: Follow up on X
-```
-
-### Learning Notes
-```org
-#+title: Learning Rust Ownership
-#+date: [2025-12-03 Wed 16:00]
-#+filetags: :learning:rust:programming:
-#+identifier: 20251203T160000
-#+category: learning
-
-* Key Concepts
-** Ownership Rules
-1. Each value has an owner
-2. Only one owner at a time
-
-* Resources
-- [[https://doc.rust-lang.org/book/][Rust Book]]
-```
-
-## History Integration
-
-### When to Create History-Linked Notes
-
-Create notes with `:history:` tags when:
-- Documenting significant work sessions
-- Capturing important learnings from debugging
-- Recording research findings worth referencing
-- Noting architecture decisions
-
-### History Note Format
-
-**Filename with pkai signature**:
-```
-YYYYMMDDTHHMMSS==pkai--description__history_category_tags.org
-```
-
-**Content**:
-```org
-#+title: <Title>
-#+date: [YYYY-MM-DD Day HH:MM]
-#+filetags: :history:category:topic1:topic2:
-#+identifier: YYYYMMDDTHHMMSS
-#+category: history
-
-* Context
-What this documents
-
-* Details
-Main content
-
-* Related History Entries
-- [[file:~/.config/claude/history/sessions/2025-12/...][Session Entry]]
-
-* Related Notes
-- [[file:~/desktop/org/notes/...][Other Note]]
-```
-
-**Note**: The `==pkai` signature marks this as a history-system generated note.
-
-### Finding History Notes
-
-```bash
-# All history notes (by tag)
-ls ~/desktop/org/notes/*__history*.org
-
-# All automated history notes (by signature)
-ls ~/desktop/org/notes/*==pkai*.org
-
-# Specific category
-ls ~/desktop/org/notes/*__history_session*.org
-```
-
-## Tips
-
-1. **Timestamps are unique**: Use current time for new notes
-2. **Tags are lowercase**: Keep them simple and consistent
-3. **Link liberally**: Connect related notes and history entries
-4. **Use TODO items**: Make notes actionable
-5. **Include examples**: Code, commands, configurations
-6. **Reference sources**: Link to documentation, articles
-7. **Review regularly**: Update and refine over time
-8. **History tag**: Add `:history:` for integration with history system
dots/config/claude/skills/Org/reference/JournellyFormat.md
@@ -1,551 +0,0 @@
-# Journelly Journal Format
-
-Complete reference for creating journal entries in Journelly format using org-mode.
-
-
-**Journal file**: `~/desktop/org/Journelly.org`
-
-## Journelly Format
-
-Journelly is an iOS app that stores journal entries in org-mode format as a single file with entries in reverse chronological order (newest first).
-
-### Entry Structure
-
-**Basic entry format**:
-```org
-* [YYYY-MM-DD Day HH:MM] @ Location
-:PROPERTIES:
-:LATITUDE: 48.86721377062119
-:LONGITUDE: 2.1850910842231994
-:WEATHER_TEMPERATURE: 5,8°C
-:WEATHER_CONDITION: Cloudy
-:WEATHER_SYMBOL: cloud
-:END:
-Entry content goes here...
-
-Can have multiple paragraphs.
-
-- Lists work
-- [ ] Checkboxes work
-- [X] Completed items
-
-Images can be included:
-[[file:Journelly.org.assets/images/IMAGE.jpeg]]
-```
-
-**Entry without properties**:
-```org
-* [YYYY-MM-DD Day HH:MM] @ Location
-
-Simple entry without weather/GPS metadata.
-```
-
-### Key Components
-
-**Heading**:
-- Format: `* [YYYY-MM-DD Day HH:MM] @ Location`
-- Timestamp: Full date and time in org-mode format
-- Location: Can be a place name, address, or general location
-- Examples:
- - `* [2025-12-08 Mon 15:30] @ Home`
- - `* [2025-12-08 Mon 09:15] @ Kyushu`
- - `* [2025-12-08 Mon 22:00] @ Rue Jean Bourguignon`
-
-**Properties drawer** (optional):
-- `:LATITUDE:` - GPS latitude
-- `:LONGITUDE:` - GPS longitude
-- `:WEATHER_TEMPERATURE:` - Temperature with unit (e.g., `5,8°C`)
-- `:WEATHER_CONDITION:` - Weather description (e.g., `Cloudy`, `Clear`, `Partly Cloudy`)
-- `:WEATHER_SYMBOL:` - Icon symbol (e.g., `cloud`, `sun.max`, `cloud.moon`)
-
-**Content**:
-- Free-form text in org-mode format
-- Support for org-mode features: lists, checkboxes, links, code blocks
-- Images stored in `Journelly.org.assets/images/` directory
-- Can include hashtags (e.g., `#lang_en`)
-- Can reference other org files with org-mode links
-
-**IMPORTANT - Content Formatting Restrictions**:
-- **NO sub-headings**: Journelly does NOT support `**` level 2 headings or any sub-headings within entries
-- Use **indented lists** instead of sub-headings for structure:
- ```org
- - Section Title
- - Item 1
- - Item 2
- ```
-- For bold emphasis in list items, use text formatting: `- *Bold Section Title*`
-- Each journal entry must be a single `*` level 1 heading - no nested headings allowed
-
-### Entry Order
-
-Entries are in **reverse chronological order** - newest entries at the top of the file, after the header.
-
-## File Header
-
-The Journelly.org file starts with:
-```org
-#+TITLE: My Journal (via https://journelly.com)
-#+STARTUP: showall
-:journelly:
-:doc_version: 1.0
-:end:
-```
-
-This header should never be modified.
-
-## Creating Entries
-
-### Automatic Location and Weather Detection
-
-Helper scripts are available to automatically get current location and weather:
-
-**Get location (IP-based geolocation)**:
-```bash
-# Get city and coordinates
-~/.config/claude/skills/Journal/tools/get-location
-# Output: Saint-Denis (48.9356,2.3539)
-
-# Get just coordinates
-~/.config/claude/skills/Journal/tools/get-location --coords
-# Output: 48.9356,2.3539
-
-# Get as JSON
-~/.config/claude/skills/Journal/tools/get-location --json
-# Output: {"city":"Saint-Denis","lat":"48.9356","lon":"2.3539"}
-```
-
-**Get weather (from wttr.in)**:
-```bash
-# Get current weather
-~/.config/claude/skills/Journal/tools/get-weather
-# Output: 15°C Partly cloudy (cloud.sun)
-
-# Get just temperature
-~/.config/claude/skills/Journal/tools/get-weather --temperature
-# Output: 15°C
-
-# Get as JSON
-~/.config/claude/skills/Journal/tools/get-weather --json
-# Output: {"temperature":"15°C","condition":"Partly cloudy","symbol":"cloud.sun"}
-
-# Get weather for specific location
-~/.config/claude/skills/Journal/tools/get-weather Paris
-```
-
-**Notes**:
-- Location uses IP-based geolocation (city-level accuracy, no GPS hardware required)
-- Weather uses wttr.in service (no API key required)
-- Both are cached (location: 1 hour, weather: 30 minutes) to reduce API calls
-- Symbols are mapped to iOS SF Symbols for consistency with Journelly app
-
-### Using journelly-manager (Recommended)
-
-The `journelly-manager` tool provides batch mode operations for creating journal entries:
-
-```bash
-# Create simple entry with just location
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Kyushu" "Entry content here"
-
-# Create entry with weather/GPS metadata
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Rue Jean Bourguignon" "Entry content" \
- --latitude=48.86721 \
- --longitude=2.18509 \
- --temperature="15,2°C" \
- --condition="Partly Cloudy" \
- --symbol="cloud.sun"
-
-# Create entry with content from file
-echo "My thoughts today..." > /tmp/journal.txt
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" --content-file=/tmp/journal.txt
-
-# Append to today's entry (if one exists from today)
-~/.config/claude/skills/Journal/tools/journelly-manager append \
- ~/desktop/org/Journelly.org "Additional thoughts for today"
-
-# Create entry with automatic location and weather
-LOC_DATA=$(~/.config/claude/skills/Journal/tools/get-location --json)
-WEATHER_DATA=$(~/.config/claude/skills/Journal/tools/get-weather --json)
-
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org \
- "$(echo "$LOC_DATA" | jq -r .city)" \
- "Today's reflection with automatic metadata" \
- --latitude="$(echo "$LOC_DATA" | jq -r .lat)" \
- --longitude="$(echo "$LOC_DATA" | jq -r .lon)" \
- --temperature="$(echo "$WEATHER_DATA" | jq -r .temperature)" \
- --condition="$(echo "$WEATHER_DATA" | jq -r .condition)" \
- --symbol="$(echo "$WEATHER_DATA" | jq -r .symbol)"
-```
-
-**Output**: Returns JSON with success status and entry details.
-
-### Manual Creation
-
-If creating entries manually:
-
-1. Open `~/desktop/org/Journelly.org`
-2. After the file header (after `:end:`), insert new entry at the top
-3. Use timestamp format: `[YYYY-MM-DD Day HH:MM]`
-4. Add location after `@`
-5. Optionally add PROPERTIES drawer
-6. Write content below
-
-**Get current timestamp**:
-```bash
-# Org-mode format
-date +"[%Y-%m-%d %a %H:%M]"
-```
-
-## Entry Examples
-
-### Structured entry with indented lists (RECOMMENDED for complex entries):
-```org
-* [2025-12-10 Wed 17:05] @ Paris
-:PROPERTIES:
-:LATITUDE: 48.8534
-:LONGITUDE: 2.3488
-:END:
-Productive day with significant progress across multiple areas.
-
-- Work (Tekton/CI-CD)
- - [X] Fixed workflow call in pipeline (priority 1)
- - [X] Created issue for cherry-pick workflow
- - [X] Standardized retest workflow across repositories
-
-- Infrastructure & Homelab
- - [X] Set up Navidrome music streaming server
- - [X] Implemented color-scheme switcher on kyushu
- - [ ] Setup imap-filter (in progress)
-
-- Personal Productivity
- - Updated imapfilter to archive emails by year
- - Researched Everyday Systems habit formation
- - Created note documenting implementation ideas
-
-10 completed tasks, feeling productive!
-```
-
-**Note**: Use indented lists (with 2 spaces) instead of sub-headings (`**`) - Journelly doesn't support nested headings!
-
-### Simple reflection:
-```org
-* [2025-12-08 Mon 15:30] @ Home
-
-Had a productive day working on Claude skills. The Journal skill
-is coming together nicely. Need to test the batch functions next.
-```
-
-### Work notes with checklist:
-```org
-* [2025-12-08 Mon 09:00] @ Kyushu
-
-Today's focus:
-- [X] Review pull requests
-- [X] Fix bug in pipeline
-- [ ] Write documentation
-- [ ] Team meeting at 14:00
-
-Making good progress on the telemetry work.
-```
-
-### Evening reflection with location data:
-```org
-* [2025-12-08 Mon 22:30] @ Rue Jean Bourguignon
-:PROPERTIES:
-:LATITUDE: 48.86721377062119
-:LONGITUDE: 2.1850910842231994
-:WEATHER_TEMPERATURE: 8,5°C
-:WEATHER_CONDITION: Clear
-:WEATHER_SYMBOL: moon.stars
-:END:
-Quiet evening. Spent time with family and worked on some personal
-projects. Feeling good about the progress this week.
-
-Tomorrow's priorities:
-- House hunting follow-up
-- Finish keyboard configuration
-- Review Rhea setup
-```
-
-### Vacation entry with photos:
-```org
-* [2025-08-16 Sat 04:40] @ Rue Jean Bourguignon
-:PROPERTIES:
-:LATITUDE: 48.868139960083184
-:LONGITUDE: 2.184074493463655
-:WEATHER_TEMPERATURE: 16,8°C
-:WEATHER_CONDITION: Clear
-:WEATHER_SYMBOL: moon.stars
-:END:
-Good day at Plaisir with Malek and Ilyan. Everyone was tired at
-the end but for good reasons.
-
-[[file:Journelly.org.assets/images/CDB4EC5D-153A-4C35-A7E8-17BA94F7FEBD.jpeg]]
-
-[[file:Journelly.org.assets/images/9CB95E09-CD4E-4868-BFE0-F0B129A8356B.jpeg]]
-
-We need to find a way to have less mosquitoes bites for Ayla because
-it is a lot and it is painful to see her with all thoses..
-```
-
-## Best Practices
-
-### When to Journal
-
-- **Morning**: Day planning, intentions, priorities
-- **During work**: Quick thoughts, decisions, blockers
-- **Evening**: Reflections, gratitude, learnings
-- **Anytime**: Significant moments, insights, emotions
-
-### Writing Guidelines
-
-- **Be authentic**: Write for yourself, not an audience
-- **Be specific**: Include details, context, emotions
-- **Be brief**: Don't overthink it, capture the moment
-- **Be consistent**: Regular entries build a valuable record
-- **Use org-mode features**: Lists, checkboxes, links enhance entries
-
-### Location Guidelines
-
-- Use recognizable place names for your context
-- Can be specific (address) or general (room name, city)
-- Examples:
- - Work: `Kyushu` (machine name)
- - Home: `Home`, `Rue Jean Bourguignon`
- - Travel: `Amsterdam`, `R27`, `Boulevard de Turin`
-
-### Privacy Considerations
-
-- Journal contains personal thoughts and location data
-- Stored locally and synced via Syncthing/iCloud
-- No properties needed if you prefer not to log GPS/weather
-- You control what details to include
-
-## Common Use Cases
-
-### Daily Reflection
-```bash
-# Simple end-of-day reflection
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" \
- "Productive day. Made progress on the Journal skill. \
- Looking forward to testing it tomorrow."
-```
-
-### Work Log
-```bash
-# Log work accomplishments
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Kyushu" \
- "Completed PR review. Fixed authentication bug. \
- Team meeting went well - discussed Q1 roadmap."
-```
-
-### Quick Thought
-```bash
-# Capture an idea quickly
-~/.config/claude/skills/Journal/tools/journelly-manager create \
- ~/desktop/org/Journelly.org "Home" \
- "Idea: Create a dashboard for monitoring homelab services. \
- Could use Grafana + Prometheus."
-```
-
-### Adding to Today's Entry
-```bash
-# Append to existing entry from today
-~/.config/claude/skills/Journal/tools/journelly-manager append \
- ~/desktop/org/Journelly.org \
- "Update: The dashboard idea is working! Initial prototype running."
-```
-
-## Integration with Other Systems
-
-### Journelly iOS App
-
-- Primary mobile interface for journal entries
-- Automatically adds GPS and weather data
-- Syncs via iCloud to make file available on Mac
-- Can include photos captured on iPhone
-- Creates proper org-mode format automatically
-
-### Syncthing
-
-- Journal file synced across devices
-- Available on desktop for reading/searching
-- Can edit from Emacs on desktop
-- Changes sync back to iOS app
-
-### Emacs
-
-- Read and search journal with org-mode commands
-- Use `org-sparse-tree` to filter entries
-- Export to other formats (HTML, PDF)
-- Integration with org-agenda if desired
-
-## Searching and Reviewing
-
-### Find Recent Entries
-```bash
-# Show last 10 entries (most recent)
-grep -n "^\* \[" ~/desktop/org/Journelly.org | head -10
-```
-
-### Search by Date
-```bash
-# Find entries from December 2025
-grep "^\* \[2025-12-" ~/desktop/org/Journelly.org
-
-# Find entries from specific day
-grep "^\* \[2025-12-08" ~/desktop/org/Journelly.org
-```
-
-### Search by Location
-```bash
-# Find all entries at Kyushu
-grep "@ Kyushu" ~/desktop/org/Journelly.org
-```
-
-### Search Content
-```bash
-# Find entries mentioning specific topic
-grep -i "claude" ~/desktop/org/Journelly.org
-
-# Context around matches
-grep -C 3 -i "homelab" ~/desktop/org/Journelly.org
-```
-
-### Search with Ripgrep
-```bash
-# Case-insensitive search with context
-rg -i "keyboard" ~/desktop/org/Journelly.org
-
-# Show only entries about work
-rg "@ Kyushu" ~/desktop/org/Journelly.org -A 10
-```
-
-## Journelly vs Notes
-
-**Use Journelly for**:
-- Daily reflections and thoughts
-- Personal experiences and emotions
-- Time-based chronicle of life
-- Quick captures without much structure
-- Location-tagged memories
-
-**Use Notes (denote) for**:
-- Technical documentation
-- Learning notes and research
-- Reference material
-- Project planning
-- Knowledge that needs retrieval by topic
-
-**They complement each other**: Journal captures the journey, Notes capture the knowledge.
-
-## Org-Mode Features in Entries
-
-### Lists
-```org
-* [2025-12-08 Mon 10:00] @ Home
-
-Today's agenda:
-- Morning: Focus work
-- Afternoon: Meetings
-- Evening: Family time
-```
-
-### Checkboxes
-```org
-* [2025-12-08 Mon 08:00] @ Kyushu
-
-Weekly goals:
-- [X] Complete feature implementation
-- [X] Review 5 PRs
-- [ ] Write documentation
-- [ ] Update roadmap
-```
-
-### Links
-```org
-* [2025-12-08 Mon 16:00] @ Home
-
-Referenced my [[file:~/desktop/org/notes/20251205T140000--nixos-config__nixos.org][NixOS config notes]]
-for today's work.
-
-Useful article: [[https://example.com][Link Title]]
-```
-
-### Code Blocks
-```org
-* [2025-12-08 Mon 14:00] @ Kyushu
-
-Found a useful command today:
-
-#+begin_src bash
-nix build .#package-name
-#+end_src
-
-This made the build process much faster.
-```
-
-### Tables
-```org
-* [2025-12-08 Mon 12:00] @ Home
-
-Tracking house options:
-
-| Address | Price | Score |
-|------------------+-------+-------|
-| Rue Victor Hugo | 720k | 7/10|
-| Allée Perruchet | 750k | 9/10|
-```
-
-## Tips
-
-1. **Write regularly**: Even short entries build a valuable record
-2. **Don't overthink**: Capture thoughts as they come
-3. **Use location meaningfully**: Helps trigger memories later
-4. **Include context**: Future you will appreciate the details
-5. **Reference other files**: Link to notes, todos, or external resources
-6. **Use hashtags sparingly**: `#lang_en`, `#work`, `#personal` can help
-7. **Add photos when meaningful**: Visual memories are powerful
-8. **Review periodically**: Monthly or yearly reviews reveal patterns
-
-## Example Workflow
-
-### Mobile (Journelly iOS)
-1. Open Journelly app
-2. Tap to create new entry
-3. Write thoughts (voice dictation works)
-4. App automatically adds timestamp, location, weather
-5. Can attach photos from camera or library
-6. Entry syncs to iCloud → available on Mac
-
-### Desktop (Claude/Emacs)
-1. Ask Claude to create journal entry
-2. Claude uses `journelly-manager` to add entry
-3. Entry inserted at top of file (newest first)
-4. File syncs back via iCloud/Syncthing
-5. Entry appears in iOS app
-
-### Hybrid
-- Quick captures on mobile (always with you)
-- Longer reflections on desktop (better for typing)
-- Search and review on desktop (powerful tools)
-- Photos from mobile, text from either
-
-## Privacy & Sync
-
-- Journal is a plain text file you control
-- Location data is optional (from iOS app)
-- Synced via iCloud (encrypted in transit)
-- Can also use Syncthing for local-only sync
-- No third-party service has access to content
-- Backup with your regular backup strategy
-
-Remember: Your journal is for you. Write what helps you think, remember, and grow.
-
-## Examples
-
dots/config/claude/skills/Org/tools/denote-batch-functions.el
@@ -1,245 +0,0 @@
-;;; denote-batch-functions.el --- Batch operations for denote notes -*- lexical-binding: t; no-byte-compile: t; -*-
-
-;; Copyright (C) 2025 Vincent Demeester
-
-;; This file provides batch mode functions for creating and manipulating
-;; denote-formatted notes from the command line using the denote package.
-;;
-;; NOTE: This file requires the denote package to be installed in your Emacs
-;; configuration. It cannot be byte-compiled in isolation.
-
-;;; Commentary:
-
-;; These functions enable Claude Code and other tools to create denote notes
-;; programmatically using Emacs batch mode. They wrap the denote package's
-;; functions for non-interactive use.
-;;
-;; Usage:
-;; emacs --batch -l denote-batch-functions.el \
-;; --eval "(denote-batch-create-note \"Title\" '(tag1 tag2))"
-
-;;; Code:
-
-(require 'denote)
-(require 'json)
-
-;; Ensure denote-directory is set
-(unless (boundp 'denote-directory)
- (setq denote-directory "~/desktop/org/notes/"))
-
-;; Helper to output JSON
-(defun denote-batch--output-json (data)
- "Output DATA as JSON to stdout."
- (princ (json-encode data))
- (princ "\n"))
-
-;; Main function: Create denote note using denote package
-(defun denote-batch-create-note (title keywords &optional signature category directory)
- "Create a denote note with TITLE and KEYWORDS using denote package.
-KEYWORDS can be a list of strings or symbols (will be converted to strings).
-Optional SIGNATURE for automated notes (e.g., \"pkai\").
-Optional CATEGORY is stored in frontmatter.
-Optional DIRECTORY (defaults to denote-directory).
-
-Returns JSON with created file path."
- (condition-case err
- (let* ((denote-directory (or directory denote-directory))
- ;; Convert keywords to strings if they're symbols
- (keywords-list (mapcar (lambda (k)
- (if (symbolp k)
- (symbol-name k)
- k))
- keywords))
- ;; Use denote to create the note
- (filepath (denote title keywords-list 'org denote-directory nil nil signature nil)))
-
- ;; Add category to frontmatter if provided
- (when (and filepath category)
- (with-current-buffer (find-file-noselect filepath)
- (goto-char (point-min))
- ;; Find end of frontmatter
- (when (re-search-forward "^#\\+identifier:" nil t)
- (end-of-line)
- (insert (format "\n#+category: %s" category)))
- (save-buffer)
- (kill-buffer)))
-
- ;; Return JSON result
- (denote-batch--output-json
- (list :success t
- :filepath filepath
- :message (format "Created note: %s" (file-name-nondirectory filepath)))))
- (error
- (denote-batch--output-json
- (list :success :json-false
- :error (error-message-string err))))))
-
-;; Create note with content from file
-(defun denote-batch-create-note-from-file (title keywords content-file &optional signature category directory)
- "Create denote note with TITLE and KEYWORDS, reading content from CONTENT-FILE.
-KEYWORDS can be a list of strings or symbols (will be converted to strings).
-Uses denote package for creation, then appends content from file.
-Optional SIGNATURE, CATEGORY, DIRECTORY same as denote-batch-create-note."
- (condition-case err
- (let* ((denote-directory (or directory denote-directory))
- ;; Convert keywords to strings if they're symbols
- (keywords-list (mapcar (lambda (k)
- (if (symbolp k)
- (symbol-name k)
- k))
- keywords))
- ;; Create the note using denote
- (filepath (denote title keywords-list 'org denote-directory nil nil signature nil)))
-
- ;; Add category if provided
- (when category
- (with-current-buffer (find-file-noselect filepath)
- (goto-char (point-min))
- (when (re-search-forward "^#\\+identifier:" nil t)
- (end-of-line)
- (insert (format "\n#+category: %s" category)))
- (save-buffer)
- (kill-buffer)))
-
- ;; Append content from file
- (when (file-exists-p content-file)
- (with-current-buffer (find-file-noselect filepath)
- (goto-char (point-max))
- (insert-file-contents content-file)
- (save-buffer)
- (kill-buffer)))
-
- ;; Return JSON result
- (denote-batch--output-json
- (list :success t
- :filepath filepath
- :message (format "Created note: %s" (file-name-nondirectory filepath)))))
- (error
- (denote-batch--output-json
- (list :success :json-false
- :error (error-message-string err))))))
-
-;; Add content to existing denote note
-(defun denote-batch-append-content (filepath content)
- "Append CONTENT to existing denote note at FILEPATH."
- (condition-case err
- (progn
- (unless (file-exists-p filepath)
- (error "File does not exist: %s" filepath))
- (with-current-buffer (find-file-noselect filepath)
- (goto-char (point-max))
- ;; Ensure we're on a new line
- (unless (bolp)
- (insert "\n"))
- (insert "\n" content "\n")
- (save-buffer)
- (kill-buffer))
- (denote-batch--output-json
- (list :success t
- :filepath filepath
- :message "Content appended")))
- (error
- (denote-batch--output-json
- (list :success :json-false
- :error (error-message-string err))))))
-
-;; Update denote note using denote-rename functions
-(defun denote-batch-update-frontmatter (filepath &optional new-title new-keywords new-category)
- "Update frontmatter of denote note at FILEPATH.
-Optional NEW-TITLE to change title.
-Optional NEW-KEYWORDS (list of symbols) to change keywords.
-Optional NEW-CATEGORY to update category.
-
-Uses denote-rename-file-using-front-matter when possible."
- (condition-case err
- (progn
- (unless (file-exists-p filepath)
- (error "File does not exist: %s" filepath))
-
- (with-current-buffer (find-file-noselect filepath)
- ;; Update title in frontmatter
- (when new-title
- (goto-char (point-min))
- (when (re-search-forward "^#\\+title:[ \t]*\\(.*\\)$" nil t)
- (replace-match new-title nil nil nil 1)))
-
- ;; Update keywords in frontmatter
- (when new-keywords
- (goto-char (point-min))
- (when (re-search-forward "^#\\+filetags:[ \t]*\\(.*\\)$" nil t)
- (let ((tags-string (concat ":" (mapconcat #'symbol-name new-keywords ":") ":")))
- (replace-match tags-string nil nil nil 1))))
-
- ;; Update category
- (when new-category
- (goto-char (point-min))
- (if (re-search-forward "^#\\+category:[ \t]*\\(.*\\)$" nil t)
- (replace-match new-category nil nil nil 1)
- ;; Add category if it doesn't exist
- (when (re-search-forward "^#\\+identifier:" nil t)
- (end-of-line)
- (insert (format "\n#+category: %s" new-category)))))
-
- (save-buffer)
-
- ;; Use denote-rename-file-using-front-matter to update filename
- (when (or new-title new-keywords)
- (denote-rename-file-using-front-matter filepath))
-
- (kill-buffer))
-
- (denote-batch--output-json
- (list :success t
- :filepath filepath
- :message "Frontmatter updated")))
- (error
- (denote-batch--output-json
- (list :success :json-false
- :error (error-message-string err))))))
-
-;; Read denote note metadata using denote functions
-(defun denote-batch-read-metadata (filepath)
- "Read metadata from denote note at FILEPATH using denote functions.
-Returns JSON with title, keywords, identifier, signature, date, and category."
- (condition-case err
- (progn
- (unless (file-exists-p filepath)
- (error "File does not exist: %s" filepath))
-
- ;; Use denote's built-in metadata retrieval
- (let* ((file-type (denote-filetype-heuristics filepath))
- (title (denote-retrieve-title-value filepath file-type))
- (keywords (denote-extract-keywords-from-path filepath))
- (identifier (denote-retrieve-filename-identifier filepath))
- (signature (denote-retrieve-filename-signature filepath))
- (date-string nil)
- (category nil))
-
- ;; Get date and category from frontmatter
- (with-temp-buffer
- (insert-file-contents filepath)
- (goto-char (point-min))
- (when (re-search-forward "^#\\+date:[ \t]*\\(.*\\)$" nil t)
- (setq date-string (match-string 1)))
- (goto-char (point-min))
- (when (re-search-forward "^#\\+category:[ \t]*\\(.*\\)$" nil t)
- (setq category (match-string 1))))
-
- ;; Return JSON
- (denote-batch--output-json
- (list :success t
- :title title
- :keywords keywords
- :identifier identifier
- :signature (or signature "")
- :date date-string
- :category (or category "")
- :filepath filepath))))
- (error
- (denote-batch--output-json
- (list :success :json-false
- :error (error-message-string err))))))
-
-(provide 'denote-batch-functions)
-
-;;; denote-batch-functions.el ends here
dots/config/claude/skills/Org/tools/get-location
@@ -1,224 +0,0 @@
-#!/usr/bin/env bash
-# get-location - Get current GPS coordinates
-# Copyright (C) 2025 Vincent Demeester
-# Part of Claude Code Journal skill
-
-set -euo pipefail
-
-# Configuration
-CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/journal-location"
-CACHE_TIMEOUT=3600 # 1 hour in seconds
-
-# Colors for output
-RED='\033[0;31m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "${DEBUG:-0}" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-usage() {
- cat <<EOF
-get-location - Get current GPS coordinates
-
-USAGE:
- get-location [options]
-
-OPTIONS:
- --json Output as JSON
- --city Output city name only
- --coords Output coordinates only (lat,lon)
- --all Output city and coordinates (default)
- --no-cache Don't use cached location
- --help, -h Show this help
-
-OUTPUT FORMATS:
- Default: Saint-Denis (48.9356,2.3539)
- --json: {"city":"Saint-Denis","lat":"48.9356","lon":"2.3539"}
- --city: Saint-Denis
- --coords: 48.9356,2.3539
-
-LOCATION SOURCES:
- 1. IP-based geolocation (ipinfo.io) - automatic, approximate
- 2. Cache (${CACHE_FILE}) - reuses location for ${CACHE_TIMEOUT}s
-
-EXAMPLES:
- # Get location with city name
- get-location
-
- # Get just coordinates for journelly-manager
- get-location --coords
-
- # Get JSON output
- get-location --json
-
- # Force refresh (ignore cache)
- get-location --no-cache
-
-NOTES:
- - IP-based location is approximate (city-level accuracy)
- - Location is cached for 1 hour to reduce API calls
- - No API key required
-
-VERSION:
- 1.0.0
-
-AUTHOR:
- Vincent Demeester <vincent@demeester.fr>
-EOF
-}
-
-# Check if cache is valid
-is_cache_valid() {
- [[ -f "$CACHE_FILE" ]] || return 1
-
- local cache_age
- cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
-
- [[ $cache_age -lt $CACHE_TIMEOUT ]]
-}
-
-# Get location from cache
-get_from_cache() {
- if [[ -f "$CACHE_FILE" ]]; then
- cat "$CACHE_FILE"
- return 0
- fi
- return 1
-}
-
-# Get location from IP geolocation
-get_from_ip() {
- debug "Fetching location from ipinfo.io"
-
- local response
- response=$(curl -s --max-time 5 https://ipinfo.io/json 2>/dev/null) || {
- error "Failed to fetch location from ipinfo.io"
- }
-
- # Validate response
- if ! echo "$response" | jq -e '.loc' >/dev/null 2>&1; then
- error "Invalid response from ipinfo.io"
- fi
-
- echo "$response"
-}
-
-# Parse and format output
-format_output() {
- local data="$1"
- local format="${2:-all}"
-
- local city
- local loc
- local lat
- local lon
-
- city=$(echo "$data" | jq -r '.city // "Unknown"')
- loc=$(echo "$data" | jq -r '.loc // ""')
-
- if [[ -z "$loc" ]]; then
- error "No location data available"
- fi
-
- lat=$(echo "$loc" | cut -d, -f1)
- lon=$(echo "$loc" | cut -d, -f2)
-
- case "$format" in
- json)
- jq -n \
- --arg city "$city" \
- --arg lat "$lat" \
- --arg lon "$lon" \
- '{city: $city, lat: $lat, lon: $lon}'
- ;;
- city)
- echo "$city"
- ;;
- coords)
- echo "$lat,$lon"
- ;;
- lat)
- echo "$lat"
- ;;
- lon)
- echo "$lon"
- ;;
- all)
- echo "$city ($lat,$lon)"
- ;;
- *)
- error "Unknown format: $format"
- ;;
- esac
-}
-
-# Main function
-main() {
- local use_cache=true
- local format="all"
-
- # Parse arguments
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --json)
- format="json"
- ;;
- --city)
- format="city"
- ;;
- --coords)
- format="coords"
- ;;
- --lat)
- format="lat"
- ;;
- --lon)
- format="lon"
- ;;
- --all)
- format="all"
- ;;
- --no-cache)
- use_cache=false
- ;;
- --help|-h)
- usage
- exit 0
- ;;
- *)
- error "Unknown option: $1. Use --help for usage."
- ;;
- esac
- shift
- done
-
- local data=""
-
- # Try cache first
- if [[ "$use_cache" == "true" ]] && is_cache_valid; then
- debug "Using cached location"
- data=$(get_from_cache)
- else
- # Fetch from IP geolocation
- data=$(get_from_ip)
-
- # Cache the result
- mkdir -p "$(dirname "$CACHE_FILE")"
- echo "$data" > "$CACHE_FILE"
- debug "Location cached to $CACHE_FILE"
- fi
-
- # Format and output
- format_output "$data" "$format"
-}
-
-main "$@"
dots/config/claude/skills/Org/tools/get-location-el
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# get-location-el - Get location using Emacs Lisp
-# Copyright (C) 2025 Vincent Demeester
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-ELISP_FILE="$SCRIPT_DIR/journelly-location-weather.el"
-
-FORMAT="${1:-json}"
-
-exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-location \"$FORMAT\")" \
- 2>/dev/null
dots/config/claude/skills/Org/tools/get-weather
@@ -1,355 +0,0 @@
-#!/usr/bin/env bash
-# get-weather - Get current weather conditions
-# Copyright (C) 2025 Vincent Demeester
-# Part of Claude Code Journal skill
-
-set -euo pipefail
-
-# Configuration
-CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/journal-weather"
-CACHE_TIMEOUT=1800 # 30 minutes in seconds
-
-# Colors for output
-RED='\033[0;31m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "${DEBUG:-0}" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-usage() {
- cat <<EOF
-get-weather - Get current weather conditions
-
-USAGE:
- get-weather [location] [options]
-
-ARGUMENTS:
- location Optional location (city name, coordinates, airport code)
- If not provided, uses current location from IP
-
-OPTIONS:
- --json Output as JSON with all fields
- --temperature Output temperature only (e.g., "15,2°C")
- --condition Output condition only (e.g., "Partly cloudy")
- --symbol Output weather symbol only (e.g., "cloud.sun")
- --all Output formatted: temp, condition, symbol (default)
- --no-cache Don't use cached weather data
- --help, -h Show this help
-
-OUTPUT FORMATS:
- Default: 15,2°C Partly cloudy (cloud.sun)
- --json: {"temperature":"15,2°C","condition":"Partly cloudy","symbol":"cloud.sun"}
- --temperature: 15,2°C
- --condition: Partly cloudy
- --symbol: cloud.sun
-
-WEATHER SYMBOLS (iOS SF Symbols compatible):
- sun.max - Clear/Sunny
- cloud.sun - Partly cloudy
- cloud - Cloudy/Overcast
- cloud.rain - Rain
- cloud.drizzle - Light rain/Drizzle
- cloud.heavyrain - Heavy rain
- cloud.snow - Snow
- cloud.sleet - Sleet
- cloud.fog - Fog/Mist
- smoke - Haze/Smoke
- wind - Windy
- cloud.bolt - Thunderstorm
- moon.stars - Clear night
- cloud.moon - Partly cloudy night
- cloud.moon.rain - Rainy night
-
-EXAMPLES:
- # Get weather for current location
- get-weather
-
- # Get weather for specific city
- get-weather Paris
-
- # Get just temperature for journelly-manager
- get-weather --temperature
-
- # Get all fields as JSON
- get-weather --json
-
- # Get weather for coordinates
- get-weather "48.8566,2.3522"
-
- # Force refresh (ignore cache)
- get-weather --no-cache
-
-NOTES:
- - Uses wttr.in weather service (no API key required)
- - Weather is cached for 30 minutes to reduce API calls
- - Automatic location detection via IP if no location specified
-
-VERSION:
- 1.0.0
-
-AUTHOR:
- Vincent Demeester <vincent@demeester.fr>
-EOF
-}
-
-# Map wttr.in weather codes to iOS SF Symbol names
-map_weather_symbol() {
- local desc="$1"
- local is_night="${2:-false}"
-
- desc=$(echo "$desc" | tr '[:upper:]' '[:lower:]')
-
- # Night conditions
- if [[ "$is_night" == "true" ]]; then
- case "$desc" in
- *partly*cloudy*|*partly*clear*)
- echo "cloud.moon"
- ;;
- *clear*|*sunny*)
- echo "moon.stars"
- ;;
- *rain*|*drizzle*|*shower*)
- echo "cloud.moon.rain"
- ;;
- *)
- echo "cloud.moon"
- ;;
- esac
- return
- fi
-
- # Day conditions
- case "$desc" in
- *partly*cloudy*|*partly*clear*)
- echo "cloud.sun"
- ;;
- *clear*|*sunny*)
- echo "sun.max"
- ;;
- *cloudy*|*overcast*)
- echo "cloud"
- ;;
- *heavy*rain*)
- echo "cloud.heavyrain"
- ;;
- *drizzle*|*light*rain*)
- echo "cloud.drizzle"
- ;;
- *rain*|*shower*)
- echo "cloud.rain"
- ;;
- *snow*)
- echo "cloud.snow"
- ;;
- *sleet*)
- echo "cloud.sleet"
- ;;
- *fog*|*mist*)
- echo "cloud.fog"
- ;;
- *haze*|*smoke*)
- echo "smoke"
- ;;
- *wind*)
- echo "wind"
- ;;
- *thunder*|*storm*)
- echo "cloud.bolt"
- ;;
- *)
- echo "cloud"
- ;;
- esac
-}
-
-# Check if it's currently night time (simple heuristic based on hour)
-is_night() {
- local hour
- hour=$(date +%H)
- # Consider 20:00-06:00 as night
- [[ $hour -ge 20 || $hour -lt 6 ]]
-}
-
-# Check if cache is valid
-is_cache_valid() {
- local location="${1:-auto}"
- local cache_key="${CACHE_FILE}_${location// /_}"
-
- [[ -f "$cache_key" ]] || return 1
-
- local cache_age
- cache_age=$(($(date +%s) - $(stat -c %Y "$cache_key" 2>/dev/null || echo 0)))
-
- [[ $cache_age -lt $CACHE_TIMEOUT ]]
-}
-
-# Get weather from cache
-get_from_cache() {
- local location="${1:-auto}"
- local cache_key="${CACHE_FILE}_${location// /_}"
-
- if [[ -f "$cache_key" ]]; then
- cat "$cache_key"
- return 0
- fi
- return 1
-}
-
-# Get weather from wttr.in
-get_from_api() {
- local location="${1:-}"
-
- debug "Fetching weather from wttr.in for: ${location:-current location}"
-
- local url="https://wttr.in/${location}?format=j1"
- local response
-
- response=$(curl -s --max-time 10 "$url" 2>/dev/null) || {
- error "Failed to fetch weather from wttr.in"
- }
-
- # Validate response
- if ! echo "$response" | jq -e '.current_condition' >/dev/null 2>&1; then
- error "Invalid response from wttr.in"
- fi
-
- echo "$response"
-}
-
-# Parse and format output
-format_output() {
- local data="$1"
- local format="${2:-all}"
-
- local temp_c
- local condition
- local is_night_time
-
- # Extract data
- temp_c=$(echo "$data" | jq -r '.current_condition[0].temp_C // ""')
- condition=$(echo "$data" | jq -r '.current_condition[0].weatherDesc[0].value // ""')
-
- if [[ -z "$temp_c" || -z "$condition" ]]; then
- error "No weather data available"
- fi
-
- # Format temperature (use comma as decimal separator to match Journelly format)
- local temperature="${temp_c}°C"
-
- # Determine if it's night
- if is_night; then
- is_night_time="true"
- else
- is_night_time="false"
- fi
-
- # Map to symbol
- local symbol
- symbol=$(map_weather_symbol "$condition" "$is_night_time")
-
- case "$format" in
- json)
- jq -n \
- --arg temp "$temperature" \
- --arg cond "$condition" \
- --arg sym "$symbol" \
- '{temperature: $temp, condition: $cond, symbol: $sym}'
- ;;
- temperature)
- echo "$temperature"
- ;;
- condition)
- echo "$condition"
- ;;
- symbol)
- echo "$symbol"
- ;;
- all)
- echo "$temperature $condition ($symbol)"
- ;;
- journelly)
- # Format for journelly-manager command line
- echo "--temperature=\"$temperature\" --condition=\"$condition\" --symbol=\"$symbol\""
- ;;
- *)
- error "Unknown format: $format"
- ;;
- esac
-}
-
-# Main function
-main() {
- local use_cache=true
- local format="all"
- local location=""
-
- # Parse arguments
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --json)
- format="json"
- ;;
- --temperature)
- format="temperature"
- ;;
- --condition)
- format="condition"
- ;;
- --symbol)
- format="symbol"
- ;;
- --all)
- format="all"
- ;;
- --journelly)
- format="journelly"
- ;;
- --no-cache)
- use_cache=false
- ;;
- --help|-h)
- usage
- exit 0
- ;;
- -*)
- error "Unknown option: $1. Use --help for usage."
- ;;
- *)
- location="$1"
- ;;
- esac
- shift
- done
-
- local data=""
- local cache_key="${location:-auto}"
-
- # Try cache first
- if [[ "$use_cache" == "true" ]] && is_cache_valid "$cache_key"; then
- debug "Using cached weather for $cache_key"
- data=$(get_from_cache "$cache_key")
- else
- # Fetch from API
- data=$(get_from_api "$location")
-
- # Cache the result
- mkdir -p "$(dirname "$CACHE_FILE")"
- local cache_file="${CACHE_FILE}_${cache_key// /_}"
- echo "$data" > "$cache_file"
- debug "Weather cached to $cache_file"
- fi
-
- # Format and output
- format_output "$data" "$format"
-}
-
-main "$@"
dots/config/claude/skills/Org/tools/get-weather-el
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# get-weather-el - Get weather using Emacs Lisp
-# Copyright (C) 2025 Vincent Demeester
-
-set -euo pipefail
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-ELISP_FILE="$SCRIPT_DIR/journelly-location-weather.el"
-
-LOCATION=""
-FORMAT="json"
-
-# Parse arguments
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --json) FORMAT="json" ;;
- --temperature) FORMAT="temperature" ;;
- --condition) FORMAT="condition" ;;
- --symbol) FORMAT="symbol" ;;
- --all) FORMAT="all" ;;
- *) LOCATION="$1" ;;
- esac
- shift
-done
-
-if [[ -n "$LOCATION" ]]; then
- exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-weather \"$LOCATION\" \"$FORMAT\")" \
- 2>/dev/null
-else
- exec emacs --batch \
- --load "$ELISP_FILE" \
- --eval "(journelly-batch-get-weather nil \"$FORMAT\")" \
- 2>/dev/null
-fi
dots/config/claude/skills/Org/tools/journelly-batch-functions.el
@@ -1,474 +0,0 @@
-;;; journelly-batch-functions.el --- Batch functions for Journelly journal entries -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2025 Vincent Demeester
-
-;; Author: Vincent Demeester <vincent@demeester.fr>
-;; Keywords: org-mode, journelly, batch
-;; Version: 1.0.0
-
-;;; Commentary:
-
-;; Emacs batch mode functions for manipulating Journelly.org journal files.
-;; Journelly is an iOS app that stores journal entries in org-mode format.
-;;
-;; Format:
-;; - Single file with entries in reverse chronological order (newest first)
-;; - Each entry is a top-level heading: * [YYYY-MM-DD Day HH:MM] @ Location
-;; - Optional PROPERTIES drawer with GPS/weather metadata
-;; - Free-form org-mode content
-;;
-;; Functions:
-;; - journelly-batch-create-entry: Create new journal entry
-;; - journelly-batch-create-entry-auto: Create entry with automatic location/weather
-;; - journelly-batch-append-to-today: Append to today's entry
-;; - journelly-batch-list-entries: List recent entries
-;; - journelly-batch-search: Search entry content
-;; - journelly-batch-get-entry: Get specific entry by date/time
-;;
-;; Usage:
-;; emacs --batch \
-;; --load journelly-batch-functions.el \
-;; --eval "(journelly-batch-create-entry \
-;; \"~/desktop/org/Journelly.org\" \
-;; \"Home\" \
-;; \"Entry content\")"
-
-;;; Code:
-
-(require 'org)
-(require 'org-element)
-(require 'json)
-
-;; Load location/weather functions if available
-(let ((location-weather-file
- (expand-file-name "journelly-location-weather.el"
- (file-name-directory (or load-file-name buffer-file-name)))))
- (when (file-exists-p location-weather-file)
- (load location-weather-file)))
-
-;; Declare functions from journelly-location-weather.el (loaded conditionally above)
-(declare-function journelly-get-location "journelly-location-weather")
-(declare-function journelly-get-weather "journelly-location-weather")
-
-;;; Utility functions
-
-(defun journelly--format-timestamp ()
- "Generate org-mode timestamp for current time: [YYYY-MM-DD Day HH:MM]."
- (format-time-string "[%Y-%m-%d %a %H:%M]"))
-
-(defun journelly--format-date-only ()
- "Generate date only: YYYY-MM-DD."
- (format-time-string "%Y-%m-%d"))
-
-(defun journelly--parse-timestamp (heading)
- "Extract timestamp from HEADING.
-Expected format: * [YYYY-MM-DD Day HH:MM] @ Location
-Returns the timestamp string or nil."
- (when (string-match "\\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\) \\([A-Z][a-z][a-z]\\) \\([0-9]\\{2\\}:[0-9]\\{2\\}\\)\\]" heading)
- (match-string 0 heading)))
-
-(defun journelly--parse-location (heading)
- "Extract location from HEADING.
-Expected format: * [YYYY-MM-DD Day HH:MM] @ Location
-Returns the location string or nil."
- (when (string-match "@ \\(.+\\)$" heading)
- (match-string 1 heading)))
-
-(defun journelly--make-heading (location)
- "Create journal entry heading with current timestamp and LOCATION."
- (format "* %s @ %s" (journelly--format-timestamp) location))
-
-(defun journelly--make-properties (latitude longitude temperature condition symbol)
- "Create PROPERTIES drawer with GPS and weather data.
-LATITUDE, LONGITUDE, TEMPERATURE, CONDITION, SYMBOL are optional strings.
-Returns nil if no properties provided."
- (let ((props '()))
- (when latitude
- (push (format ":LATITUDE: %s" latitude) props))
- (when longitude
- (push (format ":LONGITUDE: %s" longitude) props))
- (when temperature
- (push (format ":WEATHER_TEMPERATURE: %s" temperature) props))
- (when condition
- (push (format ":WEATHER_CONDITION: %s" condition) props))
- (when symbol
- (push (format ":WEATHER_SYMBOL: %s" symbol) props))
- (when props
- (concat ":PROPERTIES:\n"
- (mapconcat 'identity (nreverse props) "\n")
- "\n:END:\n"))))
-
-(defun journelly--find-header-end (buffer)
- "Find the end of the Journelly header in BUFFER.
-Returns the position after the :end: line, or nil if not found."
- (with-current-buffer buffer
- (goto-char (point-min))
- (when (re-search-forward "^:end:$" nil t)
- (forward-line 1)
- (point))))
-
-(defun journelly--json-response (success data &optional message)
- "Create JSON response object.
-SUCCESS is boolean, DATA is any JSON-serializable value.
-MESSAGE is optional error/success message."
- (let ((response `((success . ,success)
- (data . ,data))))
- (when message
- (push `(message . ,message) response))
- (json-encode response)))
-
-(defun journelly--output-json (success data &optional message)
- "Output JSON response to stdout.
-SUCCESS is boolean, DATA is the response data, MESSAGE is optional."
- (princ (journelly--json-response success data message))
- (terpri))
-
-;;; Main functions
-
-(defun journelly-batch-create-entry (file location content &optional latitude longitude temperature condition symbol content-file)
- "Create new journal entry in FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- LOCATION: Location string (e.g., \"Home\", \"Kyushu\")
- CONTENT: Entry content (can be empty string)
- LATITUDE: Optional GPS latitude
- LONGITUDE: Optional GPS longitude
- TEMPERATURE: Optional temperature (e.g., \"15,2°C\")
- CONDITION: Optional weather condition (e.g., \"Cloudy\")
- SYMBOL: Optional weather symbol (e.g., \"cloud\")
- CONTENT-FILE: Optional path to file containing content
-
-If CONTENT-FILE is provided, reads content from file instead of CONTENT arg.
-
-Returns JSON with success status and entry details."
- (condition-case err
- (let ((actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content)))
- (with-temp-buffer
- (insert-file-contents file)
-
- ;; Find where to insert (after header)
- (let ((insert-pos (journelly--find-header-end (current-buffer))))
- (unless insert-pos
- (error "Could not find Journelly header end marker (:end:)"))
-
- (goto-char insert-pos)
-
- ;; Build entry
- (let ((heading (journelly--make-heading location))
- (properties (journelly--make-properties
- latitude longitude temperature condition symbol))
- (timestamp (journelly--format-timestamp)))
-
- ;; Insert entry
- (insert heading "\n")
- (when properties
- (insert properties))
- (when (and actual-content (not (string-empty-p actual-content)))
- (insert actual-content)
- (unless (string-suffix-p "\n" actual-content)
- (insert "\n")))
- (insert "\n") ;; Blank line after entry
-
- ;; Write back to file
- (write-region (point-min) (point-max) file)
-
- ;; Return success
- (journelly--output-json
- t
- `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if properties t :json-false))
- (file . ,file))
- "Journal entry created successfully")))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-append-to-today (file content &optional content-file)
- "Append CONTENT to today's journal entry in FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- CONTENT: Content to append
- CONTENT-FILE: Optional path to file containing content
-
-If no entry exists for today, returns error.
-Returns JSON with success status."
- (condition-case err
- (let ((actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content))
- (today-date (journelly--format-date-only)))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Find today's entry
- (let ((found nil)
- (search-pattern (format "^\\* \\[%s " today-date)))
- (while (and (not found)
- (re-search-forward search-pattern nil t))
- (setq found t))
-
- (unless found
- (error "No journal entry found for today (%s)" today-date))
-
- ;; Move to end of this entry (before next heading or end of file)
- (forward-line 1)
- (if (re-search-forward "^\\* \\[" nil t)
- (progn
- (beginning-of-line)
- (backward-char 1)) ;; Before the newline
- (goto-char (point-max)))
-
- ;; Insert content
- (insert "\n" actual-content)
- (unless (string-suffix-p "\n" actual-content)
- (insert "\n"))
-
- ;; Write back
- (write-region (point-min) (point-max) file)
-
- ;; Return success
- (journelly--output-json
- t
- `((date . ,today-date)
- (file . ,file))
- "Content appended to today's entry"))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-list-entries (file &optional limit)
- "List recent journal entries from FILE.
-
-Arguments:
- FILE: Path to Journelly.org file
- LIMIT: Optional number of entries to return (default 10)
-
-Returns JSON with list of entries."
- (condition-case err
- (let ((max-entries (or (and limit (string-to-number limit)) 10))
- (entries '()))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Parse entries
- (while (and (< (length entries) max-entries)
- (re-search-forward "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$" nil t))
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (has-properties nil)
- (content-preview ""))
-
- ;; Check for properties
- (save-excursion
- (forward-line 1)
- (when (looking-at "^:PROPERTIES:")
- (setq has-properties t)))
-
- ;; Get content preview (first 100 chars)
- (save-excursion
- (forward-line 1)
- (when has-properties
- (re-search-forward "^:END:$" nil t)
- (forward-line 1))
- (let ((content-start (point)))
- (if (re-search-forward "^\\* \\[" nil t)
- (beginning-of-line)
- (goto-char (point-max)))
- (setq content-preview
- (string-trim
- (buffer-substring-no-properties content-start (point))))
- (when (> (length content-preview) 100)
- (setq content-preview
- (concat (substring content-preview 0 100) "...")))))
-
- (push `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if has-properties t :json-false))
- (preview . ,content-preview))
- entries)))
-
- ;; Return results (already in reverse chronological from file)
- (journelly--output-json t (nreverse entries))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-search (file query)
- "Search journal entries in FILE for QUERY.
-
-Arguments:
- FILE: Path to Journelly.org file
- QUERY: Search string (case-insensitive)
-
-Returns JSON with matching entries."
- (condition-case err
- (let ((matches '())
- (query-lower (downcase query)))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Search entries
- (while (re-search-forward "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$" nil t)
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (entry-start (point))
- (entry-end nil)
- (entry-content ""))
-
- ;; Find entry end
- (save-excursion
- (if (re-search-forward "^\\* \\[" nil t)
- (setq entry-end (match-beginning 0))
- (setq entry-end (point-max))))
-
- ;; Get entry content
- (setq entry-content
- (buffer-substring-no-properties entry-start entry-end))
-
- ;; Check if query matches
- (when (string-match-p query-lower (downcase entry-content))
- (push `((timestamp . ,timestamp)
- (location . ,location)
- (content . ,(string-trim entry-content)))
- matches))))
-
- ;; Return results
- (journelly--output-json
- t
- (nreverse matches)
- (format "Found %d matching entries" (length matches)))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-get-entry (file date &optional time)
- "Get specific journal entry from FILE by DATE and optional TIME.
-
-Arguments:
- FILE: Path to Journelly.org file
- DATE: Date string (YYYY-MM-DD)
- TIME: Optional time string (HH:MM)
-
-Returns JSON with entry details or error if not found."
- (condition-case err
- (let ((search-pattern (if time
- (format "^\\* \\[%s .* %s\\]" date time)
- (format "^\\* \\[%s " date)))
- (found nil))
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
-
- ;; Skip header
- (journelly--find-header-end (current-buffer))
-
- ;; Search for entry
- (when (re-search-forward search-pattern nil t)
- (beginning-of-line)
- (when (looking-at "^\\* \\(\\[.*?\\]\\) @ \\(.+\\)$")
- (let ((timestamp (match-string 1))
- (location (match-string 2))
- (entry-end nil)
- (has-properties nil)
- (properties nil)
- (content ""))
-
- (forward-line 1)
-
- ;; Check for properties
- (when (looking-at "^:PROPERTIES:")
- (setq has-properties t)
- (let ((props-start (point)))
- (re-search-forward "^:END:$" nil t)
- (setq properties
- (buffer-substring-no-properties props-start (point)))
- (forward-line 1)))
-
- ;; Get content
- (let ((content-start (point)))
- (if (re-search-forward "^\\* \\[" nil t)
- (setq entry-end (match-beginning 0))
- (setq entry-end (point-max)))
- (setq content
- (string-trim
- (buffer-substring-no-properties content-start entry-end))))
-
- (setq found `((timestamp . ,timestamp)
- (location . ,location)
- (has-properties . ,(if has-properties t :json-false))
- (properties . ,(or properties ""))
- (content . ,content))))))
-
- (if found
- (journelly--output-json t found)
- (journelly--output-json
- nil
- nil
- (format "No entry found for %s%s"
- date
- (if time (format " at %s" time) ""))))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-(defun journelly-batch-create-entry-auto (file content &optional content-file use-location use-weather)
- "Create journal entry with automatic location and/or weather detection.
-
-Arguments:
- FILE: Path to Journelly.org file
- CONTENT: Entry content
- CONTENT-FILE: Optional path to file containing content
- USE-LOCATION: If non-nil, automatically detect location
- USE-WEATHER: If non-nil, automatically detect weather
-
-Requires journelly-location-weather.el to be loaded.
-
-Returns JSON with success status and entry details."
- (condition-case err
- (progn
- (unless (fboundp 'journelly-get-location)
- (error "Location/weather functions not available. Load journelly-location-weather.el"))
-
- (let ((location-data (when use-location (journelly-get-location)))
- (weather-data (when use-weather (journelly-get-weather)))
- (actual-content (if content-file
- (with-temp-buffer
- (insert-file-contents content-file)
- (buffer-string))
- content)))
-
- ;; Extract data
- (let ((city (when location-data (cdr (assoc 'city location-data))))
- (lat (when location-data (cdr (assoc 'lat location-data))))
- (lon (when location-data (cdr (assoc 'lon location-data))))
- (temp (when weather-data (cdr (assoc 'temperature weather-data))))
- (cond (when weather-data (cdr (assoc 'condition weather-data))))
- (symbol (when weather-data (cdr (assoc 'symbol weather-data)))))
-
- ;; Create entry
- (journelly-batch-create-entry
- file
- (or city "Unknown")
- actual-content
- lat lon temp cond symbol nil))))
- (error
- (journelly--output-json nil nil (error-message-string err)))))
-
-;;; Provide
-
-(provide 'journelly-batch-functions)
-
-;;; journelly-batch-functions.el ends here
dots/config/claude/skills/Org/tools/journelly-location-weather.el
@@ -1,257 +0,0 @@
-;;; journelly-location-weather.el --- Location and weather helpers for Journelly -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2025 Vincent Demeester
-
-;; Author: Vincent Demeester <vincent@demeester.fr>
-;; Keywords: org-mode, journelly, location, weather
-;; Version: 1.0.0
-
-;;; Commentary:
-
-;; Emacs Lisp functions to get location and weather data for Journelly journal entries.
-;;
-;; Location:
-;; - Uses IP-based geolocation (ipinfo.io)
-;; - Returns city name and GPS coordinates
-;; - Caches results for 1 hour
-;;
-;; Weather:
-;; - Uses wttr.in weather service
-;; - Returns temperature, condition, and iOS SF Symbol
-;; - Caches results for 30 minutes
-;; - Intelligent day/night symbol mapping
-;;
-;; Functions:
-;; - journelly-get-location: Get current location via IP geolocation
-;; - journelly-get-weather: Get current weather
-;; - journelly-batch-get-location: Batch mode wrapper for location
-;; - journelly-batch-get-weather: Batch mode wrapper for weather
-;;
-;; Usage (batch mode):
-;; emacs --batch \
-;; --load journelly-location-weather.el \
-;; --eval "(journelly-batch-get-location)"
-;;
-;; emacs --batch \
-;; --load journelly-location-weather.el \
-;; --eval "(journelly-batch-get-weather)"
-
-;;; Code:
-
-(require 'url)
-(require 'json)
-
-;;; Configuration
-
-(defvar journelly-cache-dir
- (expand-file-name "journal" (or (getenv "XDG_CACHE_HOME")
- (expand-file-name ".cache" "~")))
- "Directory for caching location and weather data.")
-
-(defvar journelly-location-cache-timeout 3600
- "Location cache timeout in seconds (default: 1 hour).")
-
-(defvar journelly-weather-cache-timeout 1800
- "Weather cache timeout in seconds (default: 30 minutes).")
-
-;;; Utility functions
-
-(defun journelly--ensure-cache-dir ()
- "Ensure cache directory exists."
- (unless (file-exists-p journelly-cache-dir)
- (make-directory journelly-cache-dir t)))
-
-(defun journelly--cache-file (key)
- "Get cache file path for KEY."
- (expand-file-name (format "%s.json" key) journelly-cache-dir))
-
-(defun journelly--cache-valid-p (cache-file timeout)
- "Check if CACHE-FILE is valid within TIMEOUT seconds."
- (when (file-exists-p cache-file)
- (let* ((file-time (nth 5 (file-attributes cache-file)))
- (current-time (current-time))
- (age (float-time (time-subtract current-time file-time))))
- (< age timeout))))
-
-(defun journelly--read-cache (cache-file)
- "Read JSON data from CACHE-FILE."
- (when (file-exists-p cache-file)
- (with-temp-buffer
- (insert-file-contents cache-file)
- (goto-char (point-min))
- (json-read))))
-
-(defun journelly--write-cache (cache-file data)
- "Write DATA as JSON to CACHE-FILE."
- (journelly--ensure-cache-dir)
- (with-temp-file cache-file
- (insert (json-encode data))))
-
-(defun journelly--fetch-url (url)
- "Fetch URL and return parsed JSON response."
- (let ((url-request-method "GET")
- (url-request-extra-headers '(("User-Agent" . "Emacs/journelly"))))
- (with-current-buffer (url-retrieve-synchronously url t nil 10)
- (goto-char (point-min))
- ;; Skip HTTP headers
- (re-search-forward "^$")
- (forward-line)
- (let ((json-data (json-read)))
- (kill-buffer)
- json-data))))
-
-(defun journelly--is-night-p ()
- "Return t if current time is night (20:00-06:00)."
- (let ((hour (string-to-number (format-time-string "%H"))))
- (or (>= hour 20) (< hour 6))))
-
-;;; Location functions
-
-(defun journelly--map-weather-symbol (description &optional is-night)
- "Map weather DESCRIPTION to iOS SF Symbol name.
-If IS-NIGHT is non-nil, return night-appropriate symbols."
- (let ((desc (downcase description)))
- (if is-night
- ;; Night conditions
- (cond
- ((string-match-p "\\(clear\\|sunny\\)" desc) "moon.stars")
- ((string-match-p "partly.*cloud" desc) "cloud.moon")
- ((string-match-p "\\(rain\\|drizzle\\|shower\\)" desc) "cloud.moon.rain")
- (t "cloud.moon"))
- ;; Day conditions
- (cond
- ((string-match-p "\\(clear\\|sunny\\)" desc) "sun.max")
- ((string-match-p "partly.*cloud" desc) "cloud.sun")
- ((string-match-p "\\(cloudy\\|overcast\\)" desc) "cloud")
- ((string-match-p "heavy.*rain" desc) "cloud.heavyrain")
- ((string-match-p "\\(rain\\|shower\\)" desc) "cloud.rain")
- ((string-match-p "\\(drizzle\\|light.*rain\\)" desc) "cloud.drizzle")
- ((string-match-p "snow" desc) "cloud.snow")
- ((string-match-p "sleet" desc) "cloud.sleet")
- ((string-match-p "\\(fog\\|mist\\)" desc) "cloud.fog")
- ((string-match-p "\\(haze\\|smoke\\)" desc) "smoke")
- ((string-match-p "wind" desc) "wind")
- ((string-match-p "\\(thunder\\|storm\\)" desc) "cloud.bolt")
- (t "cloud")))))
-
-(defun journelly-get-location (&optional no-cache)
- "Get current location via IP geolocation.
-Returns alist with city, latitude, and longitude.
-If NO-CACHE is non-nil, fetch fresh data ignoring cache."
- (let ((cache-file (journelly--cache-file "location")))
- (if (and (not no-cache)
- (journelly--cache-valid-p cache-file journelly-location-cache-timeout))
- ;; Return cached data
- (journelly--read-cache cache-file)
- ;; Fetch fresh data
- (let* ((response (journelly--fetch-url "https://ipinfo.io/json"))
- (city (cdr (assoc 'city response)))
- (loc (cdr (assoc 'loc response)))
- (coords (when loc (split-string loc ",")))
- (lat (when coords (car coords)))
- (lon (when coords (cadr coords)))
- (data `((city . ,(or city "Unknown"))
- (lat . ,(or lat "0"))
- (lon . ,(or lon "0")))))
- ;; Cache the result
- (journelly--write-cache cache-file data)
- data))))
-
-(defun journelly-get-weather (&optional location no-cache)
- "Get current weather for LOCATION (city name or coordinates).
-If LOCATION is nil, uses current location via IP.
-Returns alist with temperature, condition, and symbol.
-If NO-CACHE is non-nil, fetch fresh data ignoring cache."
- (let* ((loc (or location ""))
- (cache-key (if (string-empty-p loc) "weather-auto" (format "weather-%s" loc)))
- (cache-file (journelly--cache-file cache-key)))
- (if (and (not no-cache)
- (journelly--cache-valid-p cache-file journelly-weather-cache-timeout))
- ;; Return cached data
- (journelly--read-cache cache-file)
- ;; Fetch fresh data
- (let* ((url (if (string-empty-p loc)
- "https://wttr.in/?format=j1"
- (format "https://wttr.in/%s?format=j1" (url-hexify-string loc))))
- (response (journelly--fetch-url url))
- (current (aref (cdr (assoc 'current_condition response)) 0))
- (temp-c (cdr (assoc 'temp_C current)))
- (weather-desc-array (cdr (assoc 'weatherDesc current)))
- (weather-desc (cdr (assoc 'value (aref weather-desc-array 0))))
- (temperature (format "%s°C" temp-c))
- (is-night (journelly--is-night-p))
- (symbol (journelly--map-weather-symbol weather-desc is-night))
- (data `((temperature . ,temperature)
- (condition . ,weather-desc)
- (symbol . ,symbol))))
- ;; Cache the result
- (journelly--write-cache cache-file data)
- data))))
-
-;;; Batch mode functions
-
-(defun journelly-batch-get-location (&optional format no-cache)
- "Batch mode: Get location and print to stdout.
-FORMAT can be: json (default), city, coords, lat, lon, or all.
-If NO-CACHE is non-nil, ignore cache."
- (let* ((format-type (or format "json"))
- (data (journelly-get-location no-cache))
- (city (cdr (assoc 'city data)))
- (lat (cdr (assoc 'lat data)))
- (lon (cdr (assoc 'lon data))))
- (cond
- ((string= format-type "json")
- (princ (json-encode data))
- (terpri))
- ((string= format-type "city")
- (princ city)
- (terpri))
- ((string= format-type "coords")
- (princ (format "%s,%s" lat lon))
- (terpri))
- ((string= format-type "lat")
- (princ lat)
- (terpri))
- ((string= format-type "lon")
- (princ lon)
- (terpri))
- ((string= format-type "all")
- (princ (format "%s (%s,%s)" city lat lon))
- (terpri))
- (t
- (error "Unknown format: %s" format-type)))))
-
-(defun journelly-batch-get-weather (&optional location format no-cache)
- "Batch mode: Get weather and print to stdout.
-LOCATION is optional city name or coordinates.
-FORMAT can be: json (default), temperature, condition, symbol, or all.
-If NO-CACHE is non-nil, ignore cache."
- (let* ((format-type (or format "json"))
- (data (journelly-get-weather location no-cache))
- (temperature (cdr (assoc 'temperature data)))
- (condition (cdr (assoc 'condition data)))
- (symbol (cdr (assoc 'symbol data))))
- (cond
- ((string= format-type "json")
- (princ (json-encode data))
- (terpri))
- ((string= format-type "temperature")
- (princ temperature)
- (terpri))
- ((string= format-type "condition")
- (princ condition)
- (terpri))
- ((string= format-type "symbol")
- (princ symbol)
- (terpri))
- ((string= format-type "all")
- (princ (format "%s %s (%s)" temperature condition symbol))
- (terpri))
- (t
- (error "Unknown format: %s" format-type)))))
-
-;;; Provide
-
-(provide 'journelly-location-weather)
-
-;;; journelly-location-weather.el ends here
dots/config/claude/skills/Org/tools/journelly-manager
@@ -1,326 +0,0 @@
-#!/usr/bin/env bash
-# journelly-manager - CLI tool for Journelly journal file manipulation via Emacs batch mode
-# Copyright (C) 2026 Vincent Demeester
-# Loads elisp from site-lisp for consistency with interactive Emacs
-
-set -euo pipefail
-
-# Configuration
-EMACS="${EMACS:-emacs}"
-EMACS_DIR="${EMACS_DIR:-$HOME/.config/emacs}"
-SITE_LISP="$EMACS_DIR/site-lisp"
-
-# Debug mode
-DEBUG="${DEBUG:-0}"
-
-# Colors for output (if not outputting JSON)
-if [[ -t 1 ]] && [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- NC='\033[0m' # No Color
-else
- RED=''
- GREEN=''
- YELLOW=''
- BLUE=''
- NC=''
-fi
-
-# Error handling
-error() {
- echo -e "${RED}Error: $*${NC}" >&2
- exit 1
-}
-
-debug() {
- if [[ "$DEBUG" == "1" ]]; then
- echo -e "${YELLOW}Debug: $*${NC}" >&2
- fi
-}
-
-info() {
- if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- echo -e "${BLUE}$*${NC}" >&2
- fi
-}
-
-success() {
- if [[ "${JSON_OUTPUT:-1}" != "1" ]]; then
- echo -e "${GREEN}$*${NC}" >&2
- fi
-}
-
-# Check dependencies
-check_deps() {
- if ! command -v "$EMACS" &> /dev/null; then
- error "Emacs not found. Set EMACS environment variable or install emacs."
- fi
-
- if [[ ! -d "$SITE_LISP" ]]; then
- error "Emacs site-lisp directory not found at: $SITE_LISP"
- fi
-
- if [[ ! -f "$SITE_LISP/journelly-batch-functions.el" ]]; then
- error "journelly-batch-functions.el not found in site-lisp"
- fi
-}
-
-# Run Emacs batch command
-run_batch() {
- local function_call="$1"
- debug "Running: $EMACS --batch --directory \"$SITE_LISP\" --load journelly-batch-functions.el --eval \"$function_call\""
-
- "$EMACS" --batch \
- --directory "$SITE_LISP" \
- --load journelly-batch-functions.el \
- --eval "$function_call" 2>&1
-}
-
-# Usage information
-usage() {
- cat <<EOF
-journelly-manager - CLI tool for managing Journelly journal files
-
-USAGE:
- journelly-manager <command> [arguments]
-
-COMMANDS:
- create FILE LOCATION CONTENT [options]
- Create new journal entry
-
- Options:
- --latitude=LAT GPS latitude
- --longitude=LON GPS longitude
- --temperature=TEMP Temperature (e.g., "15,2°C")
- --condition=COND Weather condition (e.g., "Cloudy")
- --symbol=SYM Weather symbol (e.g., "cloud")
-
- Examples:
- journelly-manager create ~/desktop/org/Journelly.org "Home" "Today was great"
-
- journelly-manager create ~/desktop/org/Journelly.org "Kyushu" \\
- "Work session notes" \\
- --latitude=48.8534 --longitude=2.3488 \\
- --temperature="15°C" --condition="Cloudy" --symbol="cloud"
-
- append FILE DATE CONTENT
- Append to existing journal entry by date (YYYY-MM-DD)
-
- Examples:
- journelly-manager append ~/desktop/org/Journelly.org \\
- "2026-01-16" "Additional thoughts"
-
- list FILE [--limit=N]
- List recent journal entries
-
- Options:
- --limit=N Number of entries to show (default: 10)
-
- Examples:
- journelly-manager list ~/desktop/org/Journelly.org
- journelly-manager list ~/desktop/org/Journelly.org --limit=20
-
- search FILE QUERY
- Search journal entries for keyword
-
- Examples:
- journelly-manager search ~/desktop/org/Journelly.org "wireguard"
-
- get FILE DATE
- Get specific entry by date (YYYY-MM-DD)
-
- Examples:
- journelly-manager get ~/desktop/org/Journelly.org "2026-01-16"
-
-ENVIRONMENT:
- EMACS Emacs executable (default: emacs)
- EMACS_DIR Emacs config directory (default: ~/.config/emacs)
- DEBUG Set to 1 for debug output
- JSON_OUTPUT Set to 1 for JSON output (no colors)
-
-EXAMPLES:
- # Create entry with auto location/weather (use get-location/get-weather)
- LOC=\$(get-location --json)
- WEATHER=\$(get-weather --json)
- journelly-manager create ~/desktop/org/Journelly.org \\
- "\$(echo \$LOC | jq -r .city)" "Entry content" \\
- --latitude="\$(echo \$LOC | jq -r .lat)" \\
- --longitude="\$(echo \$LOC | jq -r .lon)" \\
- --temperature="\$(echo \$WEATHER | jq -r .temperature)" \\
- --condition="\$(echo \$WEATHER | jq -r .condition)" \\
- --symbol="\$(echo \$WEATHER | jq -r .symbol)"
-
- # Append to today's entry
- journelly-manager append ~/desktop/org/Journelly.org \\
- "\$(date +%Y-%m-%d)" "More thoughts"
-
- # Search entries
- journelly-manager search ~/desktop/org/Journelly.org "claude"
-
-EOF
- exit 0
-}
-
-# Parse create command
-cmd_create() {
- local file="$1"
- local location="$2"
- local content="$3"
- shift 3
-
- local latitude="" longitude="" temperature="" condition="" symbol=""
-
- # Parse options
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --latitude=*)
- latitude="${1#*=}"
- shift
- ;;
- --longitude=*)
- longitude="${1#*=}"
- shift
- ;;
- --temperature=*)
- temperature="${1#*=}"
- shift
- ;;
- --condition=*)
- condition="${1#*=}"
- shift
- ;;
- --symbol=*)
- symbol="${1#*=}"
- shift
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- done
-
- # Build Emacs Lisp call
- local elisp_call="(journelly-batch-create-entry \"$file\" \"$location\" \"$content\""
-
- if [[ -n "$latitude" ]]; then elisp_call="$elisp_call :latitude \"$latitude\""; fi
- if [[ -n "$longitude" ]]; then elisp_call="$elisp_call :longitude \"$longitude\""; fi
- if [[ -n "$temperature" ]]; then elisp_call="$elisp_call :temperature \"$temperature\""; fi
- if [[ -n "$condition" ]]; then elisp_call="$elisp_call :condition \"$condition\""; fi
- if [[ -n "$symbol" ]]; then elisp_call="$elisp_call :symbol \"$symbol\""; fi
-
- elisp_call="$elisp_call)"
-
- run_batch "$elisp_call"
- success "Journal entry created"
-}
-
-# Parse append command
-cmd_append() {
- local file="$1"
- local date="$2"
- local content="$3"
-
- local elisp_call="(journelly-batch-append-to-date \"$file\" \"$date\" \"$content\")"
- run_batch "$elisp_call"
- success "Content appended to entry"
-}
-
-# Parse list command
-cmd_list() {
- local file="$1"
- shift
-
- local limit="10"
-
- # Parse options
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --limit=*)
- limit="${1#*=}"
- shift
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- done
-
- local elisp_call="(journelly-batch-list-entries \"$file\" $limit)"
- run_batch "$elisp_call"
-}
-
-# Parse search command
-cmd_search() {
- local file="$1"
- local query="$2"
-
- local elisp_call="(journelly-batch-search \"$file\" \"$query\")"
- run_batch "$elisp_call"
-}
-
-# Parse get command
-cmd_get() {
- local file="$1"
- local date="$2"
-
- local elisp_call="(journelly-batch-get-entry \"$file\" \"$date\")"
- run_batch "$elisp_call"
-}
-
-# Main command dispatcher
-main() {
- if [[ $# -eq 0 ]]; then
- usage
- fi
-
- local command="$1"
- shift
-
- case "$command" in
- -h|--help|help)
- usage
- ;;
- create)
- check_deps
- if [[ $# -lt 3 ]]; then
- error "create requires: FILE LOCATION CONTENT"
- fi
- cmd_create "$@"
- ;;
- append)
- check_deps
- if [[ $# -lt 3 ]]; then
- error "append requires: FILE DATE CONTENT"
- fi
- cmd_append "$@"
- ;;
- list)
- check_deps
- if [[ $# -lt 1 ]]; then
- error "list requires: FILE"
- fi
- cmd_list "$@"
- ;;
- search)
- check_deps
- if [[ $# -lt 2 ]]; then
- error "search requires: FILE QUERY"
- fi
- cmd_search "$@"
- ;;
- get)
- check_deps
- if [[ $# -lt 2 ]]; then
- error "get requires: FILE DATE"
- fi
- cmd_get "$@"
- ;;
- *)
- error "Unknown command: $command (try --help)"
- ;;
- esac
-}
-
-main "$@"
dots/config/claude/skills/Org/tools/org-manager
@@ -70,25 +70,6 @@ run_elisp() {
fi
}
-# Run Emacs in batch mode with denote functions
-run_denote_elisp() {
- local elisp_code="$1"
-
- debug "Running denote elisp: $elisp_code"
-
- if [[ "$DEBUG" == "1" ]]; then
- "$EMACS" --batch --no-init-file \
- --directory "$SITE_LISP" \
- --load denote-batch-functions.el \
- --eval "$elisp_code" 2>&1
- else
- "$EMACS" --batch --no-init-file \
- --directory "$SITE_LISP" \
- --load denote-batch-functions.el \
- --eval "$elisp_code" 2>/dev/null
- fi
-}
-
# Usage information
usage() {
cat <<EOF
@@ -265,20 +246,6 @@ DEPENDENCIES & RELATIONSHIPS:
Get all relationships for a task
Returns all RELATED_* properties
-DENOTE COMMANDS:
- denote-create <title> <tags> [--signature=SIG] [--category=CAT] [--directory=DIR] [--content=FILE]
- Create a denote-formatted note with proper naming and frontmatter
- Tags: comma-separated list (e.g., nixos,homelab,plan)
-
- denote-append <filepath> <content-file>
- Append content to existing denote note
-
- denote-metadata <filepath>
- Read metadata from denote note
-
- denote-update <filepath> [--title=TITLE] [--tags=TAGS] [--category=CAT]
- Update denote note frontmatter
-
OPTIONS:
--state=STATE Filter by TODO state
--priority=N Filter by priority (1-5) or list: 1,2
@@ -321,17 +288,6 @@ EXAMPLES:
# Get full content of a TODO
org-manager get ~/desktop/org/todos.org "Review PR"
- # Create denote note
- org-manager denote-create "NixOS Refactoring Plan" "nixos,refactoring,plan" \\
- --category=homelab --directory=~/desktop/org/notes
-
- # Create automated note with signature
- org-manager denote-create "Session Summary" "history,session" \\
- --signature=pkai --category=history
-
- # Read note metadata
- org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
-
OUTPUT:
All commands return JSON for easy parsing:
{"success": true, "data": [...]}
@@ -1197,151 +1153,6 @@ cmd_get_related() {
run_elisp "$elisp"
}
-# Denote commands
-
-cmd_denote_create() {
- local title="$1"
- local tags="$2"; shift 2
- local signature="" category="" directory="" content_file=""
-
- [[ -n "$title" ]] || error "Title required"
- [[ -n "$tags" ]] || error "Tags required (comma-separated)"
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --signature=*)
- signature=$(parse_option "$1" "--signature=")
- shift
- ;;
- --category=*)
- category=$(parse_option "$1" "--category=")
- shift
- ;;
- --directory=*)
- directory=$(parse_option "$1" "--directory=")
- shift
- ;;
- --content=*)
- content_file=$(parse_option "$1" "--content=")
- shift
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- done
-
- # Convert comma-separated tags to elisp list
- local tags_list="'(${tags//,/ })"
-
- # Build elisp call
- local elisp="(progn"
-
- if [[ -n "$content_file" ]]; then
- [[ -f "$content_file" ]] || error "Content file not found: $content_file"
- elisp="$elisp
- (denote-batch-create-note-from-file
- \"$title\"
- $tags_list
- \"$content_file\""
- else
- elisp="$elisp
- (denote-batch-create-note
- \"$title\"
- $tags_list"
- fi
-
- # Add optional parameters
- [[ -n "$signature" ]] && elisp="$elisp
- \"$signature\"" || elisp="$elisp
- nil"
- [[ -n "$category" ]] && elisp="$elisp
- \"$category\"" || elisp="$elisp
- nil"
- [[ -n "$directory" ]] && elisp="$elisp
- \"$directory\"" || elisp="$elisp
- nil"
-
- elisp="$elisp)
- (kill-emacs 0))"
-
- run_denote_elisp "$elisp"
-}
-
-cmd_denote_append() {
- local filepath="$1"
- local content_file="$2"
-
- [[ -f "$filepath" ]] || error "File not found: $filepath"
- [[ -f "$content_file" ]] || error "Content file not found: $content_file"
-
- # Read content
- local content
- content=$(<"$content_file")
-
- # Escape quotes for elisp
- content="${content//\"/\\\"}"
-
- local elisp="(progn
- (denote-batch-append-content \"$filepath\" \"$content\")
- (kill-emacs 0))"
-
- run_denote_elisp "$elisp"
-}
-
-cmd_denote_metadata() {
- local filepath="$1"
-
- [[ -f "$filepath" ]] || error "File not found: $filepath"
-
- local elisp="(progn
- (denote-batch-read-metadata \"$filepath\")
- (kill-emacs 0))"
-
- run_denote_elisp "$elisp"
-}
-
-cmd_denote_update() {
- local filepath="$1"; shift
- local new_title="" new_tags="" new_category=""
-
- [[ -f "$filepath" ]] || error "File not found: $filepath"
-
- while [[ $# -gt 0 ]]; do
- case "$1" in
- --title=*)
- new_title=$(parse_option "$1" "--title=")
- shift
- ;;
- --tags=*)
- new_tags=$(parse_option "$1" "--tags=")
- shift
- ;;
- --category=*)
- new_category=$(parse_option "$1" "--category=")
- shift
- ;;
- *)
- error "Unknown option: $1"
- ;;
- esac
- done
-
- # Convert tags if provided
- local tags_list="nil"
- [[ -n "$new_tags" ]] && tags_list="'(${new_tags//,/ })"
-
- # shellcheck disable=SC2155
- local elisp="(progn
- (denote-batch-update-frontmatter \"$filepath\"
- $([ -n "$new_title" ] && echo "\"$new_title\"" || echo "nil")
- $tags_list
- $([ -n "$new_category" ] && echo "\"$new_category\"" || echo "nil"))
- (kill-emacs 0))"
-
- run_denote_elisp "$elisp"
-}
-
# Main
main() {
check_deps
@@ -1482,18 +1293,6 @@ main() {
get-related)
cmd_get_related "$@"
;;
- denote-create)
- cmd_denote_create "$@"
- ;;
- denote-append)
- cmd_denote_append "$@"
- ;;
- denote-metadata)
- cmd_denote_metadata "$@"
- ;;
- denote-update)
- cmd_denote_update "$@"
- ;;
*)
error "Unknown command: $command. Use --help for usage."
;;
dots/config/claude/skills/Org/README.md
@@ -1,135 +0,0 @@
-# Org Skill - Org-Mode File Manipulation
-
-Programmatic org-mode file manipulation using Emacs batch mode and the org-element API.
-
-## Overview
-
-This skill provides reliable access to org-mode files for TODO management, note parsing, and structured content manipulation. Used by other Claude Code skills (TODOs, Notes) and can be used standalone.
-
-## Tool: org-manager
-
-CLI tool for org-mode and denote operations via Emacs batch mode.
-
-### TODO Operations
-
-```bash
-# List TODOs
-./tools/org-manager list ~/desktop/org/todos.org --state=NEXT
-
-# Count by state
-./tools/org-manager count ~/desktop/org/todos.org
-
-# Get scheduled items
-./tools/org-manager scheduled ~/desktop/org/todos.org
-
-# Add TODO
-./tools/org-manager add ~/desktop/org/todos.org "Task name" \
- --section=Work --priority=2 --scheduled=2025-12-10
-
-# Update state
-./tools/org-manager update-state ~/desktop/org/todos.org "Task name" DONE
-```
-
-### Denote Operations
-
-```bash
-# Create denote-formatted note
-./tools/org-manager denote-create "Note Title" "tag1,tag2,tag3" \
- --category=homelab --directory=~/desktop/org/notes
-
-# Create with signature (automated notes)
-./tools/org-manager denote-create "Session Log" "history,session" \
- --signature=pkai --category=history
-
-# Read note metadata
-./tools/org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
-
-# Update note frontmatter
-./tools/org-manager denote-update ~/desktop/org/notes/20251205T*.org \
- --title="New Title" --tags="new,tags"
-
-# Append content to note
-echo "* New Section" > /tmp/content.org
-./tools/org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
-```
-
-### Output
-
-All commands return JSON:
-
-```json
-{
- "success": true,
- "data": [
- {
- "heading": "Task name",
- "todo": "NEXT",
- "priority": 2,
- "tags": ["tag1"],
- "level": 2,
- "scheduled": "2025-12-05"
- }
- ]
-}
-```
-
-## Implementation
-
-### Files
-
-- `tools/batch-functions.el` - Core elisp TODO operations (343 lines)
-- `tools/denote-batch-functions.el` - Denote note creation and management (300 lines)
-- `tools/org-manager` - Bash CLI wrapper (680 lines)
-
-### Functions
-
-**TODO Operations (batch-functions.el):**
-- `org-batch-list-todos` - Parse and filter TODOs
-- `org-batch-scheduled-today` - Get scheduled items
-- `org-batch-by-section` - Filter by section
-- `org-batch-count-by-state` - Count statistics
-- `org-batch-search` - Full-text search
-- `org-batch-get-sections` - List sections
-- `org-batch-add-todo` - Add new TODO
-- `org-batch-update-state` - Change states
-- `org-batch-schedule-task` - Set SCHEDULED
-- `org-batch-set-deadline` - Set DEADLINE
-- `org-batch-set-priority` - Set priority
-- `org-batch-archive-done` - Archive items
-
-**Denote Operations (denote-batch-functions.el):**
-- `denote-batch-create-note` - Create denote note with proper formatting
-- `denote-batch-create-note-from-file` - Create note with content from file
-- `denote-batch-append-content` - Append content to existing note
-- `denote-batch-update-frontmatter` - Update note metadata
-- `denote-batch-read-metadata` - Read note metadata as JSON
-
-**Features:**
-- Automatic timestamp generation (YYYYMMDDTHHMMSS)
-- Signature support for automated notes (`==pkai`)
-- Proper denote filename format: `TIMESTAMP==SIG--title__tags.org`
-- Org-mode frontmatter generation (#+title, #+date, #+filetags, etc.)
-- JSON output for all operations
-
-## Performance
-
-Tested on 354-item todos.org:
-- Parse: <100ms
-- Filter: <50ms
-- Updates: <100ms per item
-
-## Configuration
-
-Configured for your TODO setup:
-
-```elisp
-(setq org-todo-keywords
- '((sequence "STRT" "NEXT" "TODO" "WAIT" "|" "DONE" "CANX")))
-
-(setq org-priority-highest 1
- org-priority-lowest 5)
-```
-
-## References
-
-- [[file:~/desktop/org/notes/20251205T092927--emacs-batch-mode-for-org-automation__emacs_orgmode_automation_elisp_reference.org][Research Note: Emacs Batch Mode for Org Automation]]
dots/config/claude/skills/Org/SKILL.md
@@ -1,454 +1,185 @@
---
name: Org
-description: Org-mode operations for notes, journals, and TODOs. USE WHEN user wants to create notes, write journal entries, work with org-mode files, or manipulate org content.
+description: Org-mode file manipulation tools. USE WHEN user needs programmatic org-mode operations OR the TODOs skill requires org-manager tool.
---
# Org-Mode Operations
## Purpose
-Unified skill for all org-mode operations including note-taking (denote format), journaling (Journelly format), and org-mode file manipulation.
+Provides programmatic org-mode file manipulation via Emacs batch mode. This skill is primarily used by the TODOs skill for reliable TODO operations.
### Context Detection
**This skill activates when:**
-- User asks to create a note, write a note, or document something
-- User asks to create a journal entry or write reflections
-- User mentions denote, Journelly, or note-taking
-- User needs to manipulate org-mode files (.org)
-- Working directory is `/home/vincent/desktop/org/notes` or `/home/vincent/desktop/org/`
-- User asks about organizing knowledge or documentation
-
-## Quick Reference
-
-### File Locations
-- **Notes**: `~/desktop/org/notes/` - Denote-formatted notes
-- **Journal**: `~/desktop/org/Journelly.org` - Single journal file
-- **TODOs**: `~/desktop/org/todos.org` - Task management (see TODOs skill)
-
-### Tools Available
-- **`org-manager`** - Org-mode file manipulation, denote operations
-- **`journelly-manager`** - Journal entry creation and management
-- **Helper scripts** - `get-location`, `get-weather` for journal metadata
-
-## Workflow Routing
-
-| Workflow | Trigger | File |
-|----------|---------|------|
-| **CreateNote** | "create note", "document this", "take notes about" | `workflows/CreateNote.md` |
-| **CreateJournal** | "journal entry", "write reflection", "add to journal" | `workflows/CreateJournal.md` |
-| **SearchOrg** | "find notes", "search journal", "what did I write about" | `workflows/SearchOrg.md` |
+- User needs programmatic org-mode file operations
+- TODOs skill requires org-manager for TODO manipulation
+- Working with org-mode files (.org) programmatically
## Core Tools
### org-manager
-**Location**: `tools/org-manager`
+**Location**: `~/.config/claude/skills/Org/tools/org-manager`
-Batch-mode Emacs wrapper for org-mode operations.
+Batch-mode Emacs wrapper for org-mode operations. Returns JSON output for easy parsing.
+
+**Output format**: `{"success": true, "data": [...]}`
+
+### TODO Operations
-**Denote Operations** (note-taking):
```bash
-# Create new note
-org-manager denote-create "Note Title" "tag1,tag2,tag3" \
- --category=homelab --directory=~/desktop/org/notes
+# List TODOs with filters
+org-manager list ~/desktop/org/todos.org --state=NEXT
+org-manager list ~/desktop/org/todos.org --priority=1,2
+org-manager list ~/desktop/org/todos.org --tags=work,urgent
-# Create with signature (automated notes)
-org-manager denote-create "Session Summary" "history,session" \
- --signature=pkai --category=history
+# Get scheduled items
+org-manager scheduled ~/desktop/org/todos.org
+org-manager scheduled ~/desktop/org/todos.org --date=2025-12-10
-# Update note metadata
-org-manager denote-update ~/desktop/org/notes/20251205T*.org \
- --title="New Title" --tags="new,tags"
-```
-
-**Org-mode Operations**:
-```bash
-# Search for content (basic mode - metadata only)
-org-manager search ~/desktop/org/notes/*.org "keyword"
-
-# Search with full content and properties
-org-manager search ~/desktop/org/notes/*.org "keyword" --with-content
-
-# Count TODOs in file
+# Count by state
org-manager count ~/desktop/org/todos.org
-# List sections
-org-manager sections ~/desktop/org/notes/20251205T*.org
+# Search (basic mode - metadata only)
+org-manager search ~/desktop/org/todos.org "keyword"
+
+# Search with full content
+org-manager search ~/desktop/org/todos.org "keyword" --with-content
+
+# Get sections
+org-manager sections ~/desktop/org/todos.org
+
+# Get children of heading
+org-manager children ~/desktop/org/todos.org "Parent heading"
+
+# Get full TODO content
+org-manager get ~/desktop/org/todos.org "Task heading"
+
+# Get overdue tasks
+org-manager overdue ~/desktop/org/todos.org
+
+# Get upcoming tasks
+org-manager upcoming ~/desktop/org/todos.org --days=7
```
-**Search modes:**
-- **Basic mode** (default): Fast scan returning heading, state, priority, tags, matched-in location
-- **With content** (`--with-content`): Includes full TODO body and properties for complete context
-- Use `--with-content` when you need to see the full details of matching items
+### Write Operations
-**Output**: Returns JSON with operation results.
-
-### journelly-manager
-
-**Location**: `tools/journelly-manager`
-
-Journal entry creation and management for Journelly.org.
-
-**Basic Usage**:
```bash
-# Create simple entry
-journelly-manager create ~/desktop/org/Journelly.org \
- "Location" "Entry content here"
+# Add new TODO
+org-manager add ~/desktop/org/todos.org "Task description" \
+ --section=Work --priority=2 --scheduled=2025-12-10
-# Create with weather/GPS metadata
-journelly-manager create ~/desktop/org/Journelly.org \
- "Paris" "Today's reflection" \
- --latitude=48.8534 --longitude=2.3488 \
- --temperature="15°C" --condition="Cloudy" --symbol="cloud"
+# Append content to TODO
+org-manager append-content ~/desktop/org/todos.org "Task heading" /tmp/content.org
-# Append to today's entry
-journelly-manager append ~/desktop/org/Journelly.org \
- "Additional thoughts for today"
+# Update state
+org-manager update-state ~/desktop/org/todos.org "Task heading" DONE
+
+# Schedule task
+org-manager schedule ~/desktop/org/todos.org "Task heading" 2025-12-10
+
+# Set deadline
+org-manager deadline ~/desktop/org/todos.org "Task heading" 2025-12-15
+
+# Set priority
+org-manager priority ~/desktop/org/todos.org "Task heading" 2
+
+# Archive completed items
+org-manager archive ~/desktop/org/todos.org
```
-**With automatic location/weather**:
+### Tag Management
+
```bash
-LOC=$(get-location --json)
-WEATHER=$(get-weather --json)
+# Add tags
+org-manager add-tags ~/desktop/org/todos.org "Task heading" "work,urgent"
-journelly-manager create ~/desktop/org/Journelly.org \
- "$(echo $LOC | jq -r .city)" "Entry content" \
- --latitude="$(echo $LOC | jq -r .lat)" \
- --longitude="$(echo $LOC | jq -r .lon)" \
- --temperature="$(echo $WEATHER | jq -r .temperature)" \
- --condition="$(echo $WEATHER | jq -r .condition)" \
- --symbol="$(echo $WEATHER | jq -r .symbol)"
+# Remove tags
+org-manager remove-tags ~/desktop/org/todos.org "Task heading" "urgent"
+
+# Replace all tags
+org-manager replace-tags ~/desktop/org/todos.org "Task heading" "new,tags"
+
+# List all tags in file
+org-manager list-tags ~/desktop/org/todos.org
```
-### Helper Scripts
+### Property Operations
-**get-location** - IP-based geolocation:
```bash
-get-location # Saint-Denis (48.9356,2.3539)
-get-location --coords # 48.9356,2.3539
-get-location --json # {"city":"Saint-Denis","lat":"48.9356","lon":"2.3539"}
+# Get property value
+org-manager get-property ~/desktop/org/todos.org "Task heading" "CATEGORY"
+
+# Set property
+org-manager set-property ~/desktop/org/todos.org "Task heading" "CATEGORY" "work"
+
+# List all properties
+org-manager list-properties ~/desktop/org/todos.org "Task heading"
```
-**get-weather** - Weather from wttr.in:
+### Bulk Operations
+
```bash
-get-weather # 15°C Partly cloudy (cloud.sun)
-get-weather --temperature # 15°C
-get-weather --json # {"temperature":"15°C","condition":"Partly cloudy","symbol":"cloud.sun"}
-get-weather Paris # Weather for specific location
+# Update state for all matching tasks
+org-manager bulk-update-state ~/desktop/org/todos.org TODO DONE
+
+# Add tags to all matching tasks
+org-manager bulk-add-tags ~/desktop/org/todos.org NEXT "urgent,review"
+
+# Set priority for all matching tasks
+org-manager bulk-set-priority ~/desktop/org/todos.org TODO 1
```
-**Notes**:
-- Location: IP-based (city-level), cached 1 hour
-- Weather: From wttr.in (no API key), cached 30 minutes
-- Symbols: Mapped to iOS SF Symbols for Journelly app compatibility
+### Time Tracking
-## Format Documentation
-
-### Denote Format (Note-Taking)
-
-**See**: `reference/DenoteFormat.md` for complete documentation
-
-**Quick overview**:
-- Filename: `YYYYMMDDTHHMMSS--title__tag1_tag2_tag3.org`
-- With signature: `YYYYMMDDTHHMMSS==pkai--title__tags.org`
-- Frontmatter: title, date, filetags, identifier, category
-- Location: `~/desktop/org/notes/`
-
-**Common use cases**:
-- Technical documentation
-- Learning notes and research
-- Project planning
-- Reference material
-- Knowledge organized by topic
-
-**Example**:
-```org
-#+title: Setting up Wireguard VPN
-#+date: [2025-12-03 Wed 15:45]
-#+filetags: :homelab:networking:vpn:
-#+identifier: 20251203T154500
-#+category: homelab
-
-* Overview
-Setting up Wireguard VPN for secure remote access to homelab.
-
-* Configuration
-** Server Setup
-...
-```
-
-### Journelly Format (Journaling)
-
-**See**: `reference/JournellyFormat.md` for complete documentation
-
-**Quick overview**:
-- Single file: `~/desktop/org/Journelly.org`
-- Entry format: `* [YYYY-MM-DD Day HH:MM] @ Location`
-- Optional properties: GPS coordinates, weather data
-- Reverse chronological order (newest first)
-- **NO sub-headings** - use indented lists instead
-
-**Common use cases**:
-- Daily reflections and thoughts
-- Personal experiences
-- Time-based chronicle
-- Location-tagged memories
-- Work logs and quick captures
-
-**Example**:
-```org
-* [2025-12-10 Wed 17:05] @ Paris
-:PROPERTIES:
-:LATITUDE: 48.8534
-:LONGITUDE: 2.3488
-:WEATHER_TEMPERATURE: 15°C
-:WEATHER_CONDITION: Partly cloudy
-:END:
-Productive day working on Claude skills.
-
-- Work
- - [X] Completed PR review
- - [X] Fixed authentication bug
-
-- Personal
- - Started reading new book
- - Evening walk with family
-```
-
-## When to Use What
-
-| Use Case | Format | Tool | Location |
-|----------|--------|------|----------|
-| Document technical knowledge | Denote | org-manager denote-create | ~/desktop/org/notes/ |
-| Daily reflection | Journelly | journelly-manager create | ~/desktop/org/Journelly.org |
-| Track tasks (separate skill) | Org-mode | TODOs skill | ~/desktop/org/todos.org |
-| Meeting notes | Denote | org-manager denote-create | ~/desktop/org/notes/ |
-| Quick thought | Journelly | journelly-manager append | ~/desktop/org/Journelly.org |
-| Learning notes | Denote | org-manager denote-create | ~/desktop/org/notes/ |
-
-**Rule of thumb**:
-- **Notes (Denote)**: Knowledge you need to retrieve by topic
-- **Journal (Journelly)**: Experience you want to remember chronologically
-- **TODOs**: Actions you need to track and complete
-
-## Claude Guidelines for Journal Entries
-
-**CRITICAL: Claude should NEVER automatically create journal entries.**
-
-### Regular Journal Entries (@ Location (hostname))
-- **NEVER create these** - These are the user's personal voice and reflections only
-- The user writes these manually via:
- - Journelly iOS app
- - Emacs org-capture (`C-c o c j`)
- - Manual editing
-
-### Claude Session Entries (@ Claude session)
-- **Only create when explicitly asked** by the user
-- Use the `journelly-claude-session` function via Emacs batch mode
-- Format: Brief, factual, technical summaries of work accomplished
-- **Auto-tagging**: Tags are automatically detected and appended based on:
- - File types changed (`.nix` → `#nixos`, `.el` → `#emacs`, `.go` → `#golang`)
- - Git repository (`home` → `#homelab`, `tekton*` → `#tekton`)
- - Keywords (`fix/bug` → `#debugging`, `feature/implement` → `#development`)
- - Tools mentioned (`docker`, `kubernetes`, etc.)
-- Examples:
- - "Fixed bug in authentication handler" → auto-tagged with `#debugging`
- - "Implemented feature X with tests" → auto-tagged with `#development #testing`
- - "Refactored module Y for better performance" → auto-tagged with `#refactoring`
-- **Never** write in first person or user's voice
-- Keep entries concise (1-2 lines per timestamp)
-
-### When User Asks to "Save This Session"
-1. Save detailed summary to history system (`~/.config/claude/history/sessions/`)
-2. **Only** add Claude session entry if user explicitly requests it
-3. Let user write their own personal journal reflections
-
-**Remember**: Journal entries are personal. Claude provides technical logging only when requested.
-
-## Common Operations
-
-### Create a Note
```bash
-# With org-manager
-org-manager denote-create "NixOS Flakes Best Practices" \
- "nixos,flakes,reference" \
- --category=reference \
- --directory=~/desktop/org/notes
+# Clock in
+org-manager clock-in ~/desktop/org/todos.org "Task heading"
-# Returns: filepath to new note
+# Clock out
+org-manager clock-out ~/desktop/org/todos.org
+
+# Get active clock
+org-manager get-active-clock ~/desktop/org/todos.org
+
+# Get clocked time
+org-manager get-clocked-time ~/desktop/org/todos.org "Task heading"
```
-### Create a Journal Entry
+### Statistics
+
```bash
-# Simple entry
-journelly-manager create ~/desktop/org/Journelly.org \
- "Home" "Reflection on today's work with Claude skills"
+# Get comprehensive statistics
+org-manager get-statistics ~/desktop/org/todos.org
-# With automatic location/weather
-LOC=$(get-location --json)
-WEATHER=$(get-weather --json)
-journelly-manager create ~/desktop/org/Journelly.org \
- "$(echo $LOC | jq -r .city)" \
- "Today's entry content" \
- --latitude="$(echo $LOC | jq -r .lat)" \
- --longitude="$(echo $LOC | jq -r .lon)" \
- --temperature="$(echo $WEATHER | jq -r .temperature)" \
- --condition="$(echo $WEATHER | jq -r .condition)" \
- --symbol="$(echo $WEATHER | jq -r .symbol)"
+# Get priority distribution
+org-manager get-priority-distribution ~/desktop/org/todos.org
+
+# Get tag statistics
+org-manager get-tag-statistics ~/desktop/org/todos.org
```
-### Search Notes
+### Export
+
```bash
-# Find notes with tag
-ls ~/desktop/org/notes/*__*homelab*.org
+# Export to CSV
+org-manager export-csv ~/desktop/org/todos.org /tmp/todos.csv
-# Search content
-org-manager search ~/desktop/org/notes/*.org "wireguard"
-
-# Using ripgrep
-rg "kubernetes" ~/desktop/org/notes/
+# Export to JSON
+org-manager export-json ~/desktop/org/todos.org /tmp/todos.json
```
-### Search Journal
-```bash
-# Find entries from date
-grep "^\* \[2025-12-" ~/desktop/org/Journelly.org
+## File Locations
-# Search by location
-grep "@ Kyushu" ~/desktop/org/Journelly.org
+- **TODOs**: `~/desktop/org/todos.org` - Task management
+- **Inbox**: `~/desktop/org/inbox.org` - Quick capture
-# Search content
-rg "homelab" ~/desktop/org/Journelly.org
-```
+## Requirements
-## Org-Mode Features
-
-All org-mode features work in both notes and journal entries:
-
-**Lists**:
-```org
-- Unordered item
- - Sub-item
-1. Ordered item
-- [ ] TODO checkbox
-- [X] Completed checkbox
-```
-
-**Links**:
-```org
-[[file:~/desktop/org/notes/other-note.org][Link Text]]
-[[https://example.com][External Link]]
-```
-
-**Code Blocks**:
-```org
-#+begin_src bash
-nix build .#package
-#+end_src
-```
-
-**Tables**:
-```org
-| Col 1 | Col 2 |
-|-------+-------|
-| Val 1 | Val 2 |
-```
-
-**Emphasis**:
-```org
-*bold* /italic/ _underline_ =code= ~verbatim~
-```
-
-## Integration
-
-### History System
-- Notes with `:history:` tag link to `~/.config/claude/history/`
-- Use `==pkai` signature for automated history notes
-- Cross-reference between notes and history entries
-
-### Journelly iOS App
-- Primary mobile interface for journaling
-- Auto-adds GPS and weather
-- Syncs via iCloud
-- Can include photos
-
-### Syncthing
-- Journal file synced across devices
-- Available on desktop for reading/searching
-
-### Emacs
-- Full org-mode editing capabilities
-- org-sparse-tree for filtering
-- Export to HTML, PDF, etc.
-
-## Best Practices
-
-### Note-Taking
-- Use descriptive, specific titles
-- Tag consistently (2-5 tags)
-- Link related notes
-- Include code examples
-- Add references to sources
-
-### Journaling
-- Write regularly, even brief entries
-- Use location meaningfully
-- Be authentic and specific
-- Use org-mode features (lists, checkboxes)
-- Review periodically
-
-### Organization
-- Notes: Organized by tags and topics
-- Journal: Chronological by nature
-- Cross-link when relevant
-- Use categories consistently
-
-## Examples
-
-**Example 1: Creating a technical note**
-```
-User: "Create a note about NixOS flakes best practices"
-→ Uses org-manager denote-create
-→ Generates timestamp identifier
-→ Creates denote-formatted filename
-→ Adds org-mode frontmatter
-→ Saves to ~/desktop/org/notes/
-→ Result: 20251206T153000--nixos-flakes-best-practices__nixos_flakes_reference.org
-```
-
-**Example 2: Daily journal reflection**
-```
-User: "Create my journal entry for today"
-→ Uses journelly-manager create
-→ Gets current location and weather
-→ Formats with timestamp and location
-→ Adds properties drawer with metadata
-→ Inserts at top of Journelly.org
-→ Result: Entry with full context captured
-```
-
-**Example 3: Searching across both**
-```
-User: "Find everything about Tekton from last month"
-→ Searches notes: ~/desktop/org/notes/*__*tekton*.org
-→ Searches journal: grep "tekton" ~/desktop/org/Journelly.org
-→ Shows both structured notes and journal reflections
-→ Result: Complete picture of Tekton work
-```
-
-## Reference Documentation
-
-For complete format specifications and advanced features:
-- **`reference/DenoteFormat.md`** - Complete denote note-taking reference
-- **`reference/JournellyFormat.md`** - Complete journelly journal reference
+- Emacs with org-mode
+- `org-batch-functions.el` in `~/.config/emacs/site-lisp/`
## Related Skills
-- **TODOs** - Task management in org-mode (separate skill)
-- **CORE** - History system integration
-- **Git** - Version control for org files
-
----
-
-**This completes the unified Org skill. All org-mode operations, note-taking, and journaling are now in one place.**
+- **TODOs** - Uses org-manager for TODO operations
dots/config/claude/skills/Org/SKILL.md.backup-20260106
@@ -1,342 +0,0 @@
----
-name: Org
-description: Org-mode file manipulation using Emacs batch mode. USE WHEN you need to programmatically read, parse, or modify org-mode files (.org) for TODOs, notes, or other structured content.
----
-
-# Org-Mode File Manipulation
-
-## Purpose
-Provide reliable, programmatic access to org-mode files using Emacs batch mode and the org-element API. This skill is used by other skills (TODOs, Notes) for org-mode operations.
-
-### Context Detection
-
-**This skill activates when:**
-- Other skills need to manipulate org-mode files
-- Parsing TODO items, denote notes, or org content
-- Updating TODO states, scheduling, or properties
-- Querying org-mode structure or metadata
-- Working with files ending in `.org`
-
-## Tool: org-manager
-
-### Location
-`tools/org-manager` - Bash CLI wrapper around Emacs batch mode
-
-### Usage
-
-#### TODO Operations
-```bash
-# List TODOs
-./tools/org-manager list ~/desktop/org/todos.org --state=NEXT
-
-# Add TODO
-./tools/org-manager add ~/desktop/org/todos.org "Task name" \
- --section=Work --priority=2 --scheduled=2025-12-10
-
-# Update state
-./tools/org-manager update-state ~/desktop/org/todos.org "Task name" DONE
-
-# Count by state
-./tools/org-manager count ~/desktop/org/todos.org
-
-# Get scheduled items
-./tools/org-manager scheduled ~/desktop/org/todos.org
-
-# Search
-./tools/org-manager search ~/desktop/org/todos.org "term"
-
-# Get full TODO content (metadata + body)
-./tools/org-manager get ~/desktop/org/todos.org "Task name"
-
-# Get overdue tasks (deadline before today)
-./tools/org-manager overdue ~/desktop/org/todos.org
-
-# Get upcoming tasks (scheduled/deadline in next N days)
-./tools/org-manager upcoming ~/desktop/org/todos.org --days=7
-```
-
-#### Tag Management
-```bash
-# List all unique tags in file
-./tools/org-manager list-tags ~/desktop/org/todos.org
-
-# Add tags to existing TODO
-./tools/org-manager add-tags ~/desktop/org/todos.org "Task name" "urgent,review"
-
-# Remove specific tags
-./tools/org-manager remove-tags ~/desktop/org/todos.org "Task name" "urgent"
-
-# Replace all tags with new set
-./tools/org-manager replace-tags ~/desktop/org/todos.org "Task name" "done,archived"
-```
-
-#### Property Operations
-```bash
-# List all properties of a heading
-./tools/org-manager list-properties ~/desktop/org/todos.org "Task name"
-
-# Get specific property value
-./tools/org-manager get-property ~/desktop/org/todos.org "Task name" "PR_URL"
-
-# Set property value
-./tools/org-manager set-property ~/desktop/org/todos.org "Task name" "STATUS" "In Progress"
-```
-
-#### Bulk Operations
-```bash
-# Update all tasks matching a state to a new state
-./tools/org-manager bulk-update-state ~/desktop/org/todos.org "TODO" "DONE"
-
-# Update with tag filter (only tasks with specific tags)
-./tools/org-manager bulk-update-state ~/desktop/org/todos.org "TODO" "DONE" "work,urgent"
-
-# Add tags to all tasks with a specific state
-./tools/org-manager bulk-add-tags ~/desktop/org/todos.org "NEXT" "urgent,review"
-
-# Set priority for all tasks with a specific state
-./tools/org-manager bulk-set-priority ~/desktop/org/todos.org "TODO" 1
-```
-
-#### Time Tracking
-```bash
-# Start time tracking on a task
-./tools/org-manager clock-in ~/desktop/org/todos.org "Implement feature X"
-
-# Stop time tracking (clocks out of currently active task)
-./tools/org-manager clock-out ~/desktop/org/todos.org
-
-# Check what task is currently being tracked
-./tools/org-manager get-active-clock ~/desktop/org/todos.org
-
-# Get total time spent on a task (returns minutes)
-./tools/org-manager get-clocked-time ~/desktop/org/todos.org "Implement feature X"
-```
-
-#### Statistics & Analytics
-```bash
-# Get comprehensive statistics (counts by state, priority, tags, overdue, etc.)
-./tools/org-manager get-statistics ~/desktop/org/todos.org
-
-# Get priority distribution across all tasks
-./tools/org-manager get-priority-distribution ~/desktop/org/todos.org
-
-# Get tag usage statistics (sorted by frequency)
-./tools/org-manager get-tag-statistics ~/desktop/org/todos.org
-```
-
-#### Export & Reporting
-```bash
-# Export to CSV for spreadsheet analysis
-./tools/org-manager export-csv ~/desktop/org/todos.org /tmp/todos.csv
-
-# Export to JSON for programmatic processing
-./tools/org-manager export-json ~/desktop/org/todos.org /tmp/todos.json
-```
-
-#### Recurring Tasks
-```bash
-# Set repeater for a task (+1w = weekly, .+2d = 2 days after completion)
-./tools/org-manager set-repeater ~/desktop/org/todos.org "Weekly Review" "+1w"
-
-# Get all recurring tasks
-./tools/org-manager get-recurring-tasks ~/desktop/org/todos.org
-```
-
-#### Dependencies & Relationships
-```bash
-# Set a blocker for a task
-./tools/org-manager set-blocker ~/desktop/org/todos.org "Deploy to production" "Complete testing"
-
-# Get blocker for a specific task
-./tools/org-manager get-blocker ~/desktop/org/todos.org "Deploy to production"
-
-# List all blocked tasks
-./tools/org-manager get-blocked-tasks ~/desktop/org/todos.org
-
-# Create task relationship (child/parent/related/depends-on)
-./tools/org-manager set-related ~/desktop/org/todos.org "Implement feature" "Design review" "depends-on"
-
-# Get all relationships for a task
-./tools/org-manager get-related ~/desktop/org/todos.org "Implement feature"
-```
-
-#### Denote Operations
-```bash
-# Create denote-formatted note
-./tools/org-manager denote-create "My Note Title" "tag1,tag2,tag3" \
- --category=homelab --directory=~/desktop/org/notes
-
-# Create with signature (for automated notes)
-./tools/org-manager denote-create "Session Log" "history,session" \
- --signature=pkai --category=history
-
-# Read note metadata
-./tools/org-manager denote-metadata ~/desktop/org/notes/20251205T*.org
-
-# Update note frontmatter
-./tools/org-manager denote-update ~/desktop/org/notes/20251205T*.org \
- --title="New Title" --tags="new,tags" --category="updated"
-
-# Append content to note
-echo "* New Section" > /tmp/content.org
-./tools/org-manager denote-append ~/desktop/org/notes/20251205T*.org /tmp/content.org
-```
-
-### Output Format
-
-All commands return JSON:
-
-```json
-{
- "success": true,
- "data": [
- {
- "heading": "Task name",
- "todo": "NEXT",
- "priority": 2,
- "tags": ["tag1"],
- "level": 2,
- "scheduled": "2025-12-05",
- "deadline": null
- }
- ]
-}
-```
-
-## Implementation
-
-### Core Functions (batch-functions.el)
-
-**TODO Operations:**
-- `org-batch-list-todos` - Parse and filter TODOs
-- `org-batch-scheduled-today` - Get scheduled items
-- `org-batch-by-section` - Filter by section
-- `org-batch-count-by-state` - Count statistics
-- `org-batch-search` - Full-text search
-- `org-batch-get-children` - Get direct children of a heading
-- `org-batch-get-sections` - List all top-level sections
-- `org-batch-get-todo-content` - Get full TODO content (metadata + body + properties)
-- `org-batch-get-overdue` - Get tasks with deadline before today
-- `org-batch-get-upcoming` - Get tasks scheduled/due in next N days
-- `org-batch-add-todo` - Add new TODO
-- `org-batch-update-state` - Change states
-- `org-batch-schedule-task` - Set SCHEDULED
-- `org-batch-set-deadline` - Set DEADLINE
-- `org-batch-set-priority` - Set priority
-- `org-batch-archive-done` - Archive items
-
-**Tag Operations:**
-- `org-batch-add-tags` - Add tags while preserving existing
-- `org-batch-remove-tags` - Remove specific tags
-- `org-batch-replace-tags` - Replace all tags with new set
-- `org-batch-list-all-tags` - Get all unique tags in file
-
-**Property Operations:**
-- `org-batch-get-property` - Get specific property value
-- `org-batch-set-property` - Set property value
-- `org-batch-list-properties` - List all properties of a heading
-
-**Bulk Operations:**
-- `org-batch-bulk-update-state` - Update all tasks matching a state
-- `org-batch-bulk-add-tags` - Add tags to all tasks with specific state
-- `org-batch-bulk-set-priority` - Set priority for all tasks with specific state
-
-**Time Tracking:**
-- `org-batch-clock-in` - Start time tracking on a task
-- `org-batch-clock-out` - Stop time tracking
-- `org-batch-get-active-clock` - Get currently clocked task
-- `org-batch-get-clocked-time` - Get total time spent on a task
-
-**Statistics & Analytics:**
-- `org-batch-get-statistics` - Comprehensive statistics (counts, priorities, tags, overdue)
-- `org-batch-get-priority-distribution` - Priority distribution across tasks
-- `org-batch-get-tag-statistics` - Tag usage statistics
-
-**Export & Reporting:**
-- `org-batch-export-csv` - Export TODOs to CSV format
-- `org-batch-export-json` - Export TODOs to JSON format
-
-**Recurring Tasks:**
-- `org-batch-set-repeater` - Set repeater specification for a task
-- `org-batch-get-recurring-tasks` - List all tasks with repeaters
-
-**Dependencies & Relationships:**
-- `org-batch-set-blocker` - Set task blocker
-- `org-batch-get-blocker` - Get blocker for a task
-- `org-batch-get-blocked-tasks` - List all blocked tasks
-- `org-batch-set-related` - Create task relationships (child/parent/related/depends-on)
-- `org-batch-get-related` - Get all relationships for a task
-
-### Denote Functions (denote-batch-functions.el)
-
-**Note Creation and Management:**
-- `denote-batch-create-note` - Create denote note with proper naming and frontmatter
-- `denote-batch-create-note-from-file` - Create note with content from file
-- `denote-batch-append-content` - Append content to existing note
-- `denote-batch-update-frontmatter` - Update note metadata (title, tags, category)
-- `denote-batch-read-metadata` - Read note metadata as JSON
-
-**Features:**
-- Automatic timestamp generation (YYYYMMDDTHHMMSS)
-- Signature support for automated notes (e.g., `==pkai`)
-- Proper denote filename format: `TIMESTAMP==SIG--title__tags.org`
-- Org-mode frontmatter generation (#+title, #+date, #+filetags, etc.)
-- JSON output for programmatic integration
-
-### Configuration
-
-TODO keywords and priorities are configured for your setup:
-
-```elisp
-(setq org-todo-keywords
- '((sequence "STRT(s)" "NEXT(n)" "TODO(t)" "WAIT(w)" "|" "DONE(d!)" "CANX(c@/!)")))
-
-(setq org-priority-highest 1
- org-priority-lowest 5
- org-priority-default 4)
-```
-
-## Performance
-
-Tested on 354-item todos.org:
-- Parse: <100ms
-- Filter: <50ms
-- Updates: <100ms per item
-
-## References
-
-- [[file:~/desktop/org/notes/20251205T092927--emacs-batch-mode-for-org-automation__emacs_orgmode_automation_elisp_reference.org][Research Note]]
-- See `README.md` for full documentation
-
-## Examples
-
-**Example 1: Reading and parsing org file**
-```
-User: "What TODOs are in my project.org file?"
-→ Uses Emacs batch mode to parse org file
-→ Extracts TODO items with org-element-map
-→ Returns formatted list with priorities and tags
-→ Shows deadlines and scheduled dates
-→ Result: Complete overview of project TODOs
-```
-
-**Example 2: Updating org file programmatically**
-```
-User: "Mark all DONE items as archived"
-→ Reads org file with Emacs batch mode
-→ Finds all DONE entries
-→ Moves them to archive section
-→ Preserves timestamps and properties
-→ Saves updated file
-→ Result: Clean org file with archived history
-```
-
-**Example 3: Extracting information from org**
-```
-User: "Get all meeting notes from last month"
-→ Parses org files for date range
-→ Filters entries with :meeting: tag
-→ Extracts content and metadata
-→ Formats as summary report
-→ Result: Month's meeting notes compiled
-```
dots/config/claude/skills/TODOs/workflows/Add.md
@@ -340,8 +340,6 @@ Topics to cover:
:PROPERTIES:
:CREATED: [2025-12-04 Thu 15:30]
:END:
-
-From: [[file:~/desktop/org/notes/20251203T160000--learning-rust-ownership__learning_rust.org][Learning Rust Ownership]]
```
## Integration
dots/config/claude/skills/TODOs/workflows/Project.md
@@ -532,13 +532,6 @@ Good project has:
## Integration
-### Link to Notes
-```org
-** TODO Project Name [0/3]
-
-Design doc: [[file:~/desktop/org/notes/20251204--project-design__work.org][Project Design Note]]
-```
-
### Link to Code
```org
** TODO Refactor Authentication [1/3]
dots/config/claude/skills/TODOs/workflows/Recurring.md
@@ -491,8 +491,6 @@ Link to meeting notes or habit journal:
```org
** STRT Weekly 1:1
SCHEDULED: <2025-12-05 Fri 10:00 +1w>
-
-Notes: [[file:~/desktop/org/notes/20251204--1on1-notes__work.org][1:1 Meeting Notes]]
```
### With Projects
dots/config/claude/skills/TODOs/Advanced.md
@@ -776,13 +776,6 @@ $(git log --oneline | head -1)
" >> ~/desktop/org/inbox.org
```
-### With Notes
-Link refiled TODO to note:
-```org
-** TODO Implement new feature
-From: [[file:~/desktop/org/notes/20251204--feature-design__work.org][Feature Design]]
-```
-
## Avoiding Common Mistakes
❌ **Don't leave in inbox forever**: Process regularly
dots/config/claude/skills/TODOs/README.md
@@ -213,13 +213,6 @@ last_commit=$(git log -1 --oneline)
echo "* TODO Test changes from $last_commit" >> ~/desktop/org/inbox.org
```
-### With Notes
-Link between TODOs and notes:
-```org
-** TODO Implement new feature
-From: [[file:~/desktop/org/notes/20251204--feature-design__work.org][Feature Design Note]]
-```
-
### With Code
Reference specific code locations:
```org
dots/config/claude/skills/TODOs/SKILL.md
@@ -267,13 +267,6 @@ Meeting notes: https://docs.example.com/...
## Linking TODOs
-### Link to Notes
-```org
-** TODO Write blog post about NixOS setup
-
-From: [[file:~/desktop/org/notes/20251203T151822--nixos-configuration__nixos.org][NixOS Configuration Note]]
-```
-
### Link to Other TODOs
```org
** TODO Deploy new configuration
@@ -517,15 +510,6 @@ From commit: $(git log -1 --oneline)
" >> ~/desktop/org/inbox.org
```
-### Create TODO from Note
-When working in denote notes, reference TODOs:
-
-```org
-* Implementation Plan
-
-See TODO: [[file:~/desktop/org/todos.org::*Implement feature X][Implement feature X]]
-```
-
## Summary
**Your TODO system is organized around**: