diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6f2448a8..cb9b55c9 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "homepage": "https://github.com/alirezarezvani/claude-skills", "repository": "https://github.com/alirezarezvani/claude-skills", "metadata": { - "description": "359 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 635 Python tools, 732 reference guides, 99 agents (cs-* + personas), 111 slash commands across 85 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.", + "description": "362 production-ready skills across 18 domains (engineering, engineering-core, marketing, product, c-level, compliance-os, project management, RA/QM, business growth, finance, productivity, marketing top-level, research, research-ops, business-operations, commercial, markdown-html, loop-library, plus standards). 644 Python tools, 741 reference guides, 102 agents (cs-* + personas), 116 slash commands across 88 marketplace plugins. v2.11.2 vendors engineering/skillopt-sleep — a verbatim copy of microsoft/SkillOpt's stdlib-only skillopt_sleep engine + Claude Code plugin surface, giving a local agent a nightly gated self-improvement cycle (read-only session harvest -> mine -> offline replay -> held-out-gated CLAUDE.md/SKILL.md edits -> staged for explicit /skillopt-sleep adopt). productivity/fable-goal (unreleased, post-v2.11.1) converts a rambling description of a desired outcome into one polished /goal prompt for a fresh autonomous session. v2.11.1 turns product-team and project-management into agent-harness domains: fork-orchestrators with deterministic goal routers, a Jira MCP snapshot bridge (Kanban flow metrics + Monte Carlo forecasting), a delegation-governance loop gate, a continuous-discovery cadence tracker, and an Opportunity Solution Tree linter, with /cs:pm and /cs:product command families. v2.10.3 completes the markdown-html domain with md-slides — slide-deck converter (arrow-key / Space / PgDn / Home/End / P keyboard navigation + presenter mode with split-view clock + speaker notes + next-slide preview + URL-hash deep linking like #3 for direct slide jumps + @media print page-per-slide for browser-native PDF export). Reuses md-document's markdown parser; vanilla JS only (no framework runtime); Prism.js opt-in via --syntax. Joins md-review (v2.10.2 code-review converter), md-document (v2.10.1 long-form converter), and the v2.10.0 foundation (orchestrator + design-system). Compatible with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenClaw, Hermes Agent, Mistral Vibe, and 5 more coding agents.", "version": "2.11.2" }, "plugins": [ @@ -1131,6 +1131,60 @@ ], "category": "productivity" }, + { + "name": "weekly-review", + "source": "./productivity/weekly-review", + "description": "GTD weekly-review loop. Scans the workspace for open loops (unchecked boxes, TODO/FIXME, stale files), walks the three-phase GET CLEAR / GET CURRENT / GET CREATIVE checklist with a refusal gate (review is never COMPLETE while a mandatory GET CURRENT step is missing), and audits commitments for stalled / no-next-action / someday candidates with a 0-100 health score. Fills the periodic-review gap from audit/productivity-2026-07.", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani" + }, + "keywords": [ + "weekly-review", + "gtd", + "open-loops", + "review-cadence", + "productivity", + "commitments" + ], + "category": "productivity" + }, + { + "name": "deep-work", + "source": "./productivity/deep-work", + "description": "Deep Work day planner. Classifies tasks deep vs shallow with a shallow-work budget verdict (plus the recent-graduate forcing question), builds a time-blocked schedule (deep blocks of 90+ minutes first, shallow batched, buffers, refuses more than 4 hours of deep demand), and logs focus sessions against a weekly deep-hours target with streaks. Fills the time/attention-management gap from audit/productivity-2026-07.", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani" + }, + "keywords": [ + "deep-work", + "time-blocking", + "focus", + "shallow-work", + "productivity", + "attention" + ], + "category": "productivity" + }, + { + "name": "meetings", + "source": "./productivity/meetings", + "description": "Meeting discipline. Cost-gates every meeting before it exists (MEET / ASYNC / NOT-READY verdicts with real dollar cost incl. optional refocus overhead), builds timeboxed agendas that refuse topics without a desired outcome (decision topics first), and extracts owned action items from raw notes with ORPHAN / NO-DUE flags. Fills the meeting-hygiene gap from audit/productivity-2026-07.", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani" + }, + "keywords": [ + "meetings", + "meeting-cost", + "agenda", + "action-items", + "productivity", + "async" + ], + "category": "productivity" + }, { "name": "landing", "source": "./marketing/landing", diff --git a/.codex/skills-index.json b/.codex/skills-index.json index 25c4827a..79463921 100644 --- a/.codex/skills-index.json +++ b/.codex/skills-index.json @@ -3,7 +3,7 @@ "name": "claude-code-skills", "description": "Production-ready skill packages for AI agents - Marketing, Engineering, Product, C-Level, PM, and RA/QM", "repository": "https://github.com/alirezarezvani/claude-skills", - "total_skills": 357, + "total_skills": 360, "skills": [ { "name": "business-growth-skills", @@ -1859,6 +1859,12 @@ "category": "productivity", "description": "Captures and organizes chaotic brain dumps into a structured, actionable system with zero information loss. Use this skill whenever the user says 'capture this', 'brain dump', 'let me dump some ideas', 'I've got a bunch of thoughts', 'here's everything on my mind', 'idea dump', 'let me get this out of my head', 'I need to organize my thoughts', 'here's what I'm thinking', or any variation where someone is unloading a messy stream of ideas, tasks, thoughts, and plans wanting them turned into something coherent. Also trigger when the user pastes or dictates a long, unstructured block of mixed ideas \u2014 even without the exact phrase \u2014 the intent is the same. Fast-to-action by design: no upfront intake. Output is four sections (Projects/Ideas, Tasks, Connections, How I Can Help) ending with a directive question. Asks at most one mid-organization clarifying question when a single item is genuinely ambiguous between task and project." }, + { + "name": "deep-work", + "source": "../../productivity/deep-work/skills/deep-work", + "category": "productivity", + "description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target." + }, { "name": "fable-goal", "source": "../../productivity/fable-goal/skills/fable-goal", @@ -1883,6 +1889,12 @@ "category": "productivity", "description": "Runs a full inbox triage using the knowledge base created by the 'inbox-setup' skill. Light-intake by design (most invocations skip questions and run with KB-default preferences); asks at most 2 grill-me override questions when invocation is outside normal cadence or includes category-skip intent. Searches recent emails, classifies them via the user's taxonomy, researches new senders, generates recommendations, drafts replies (NEVER sends), delivers a report in the user's preferred format, and updates the knowledge base with learnings. Designed to run on a recurring schedule (1-3x daily) or on demand. Use when the user wants their inbox processed, in any variation (e.g., 'triage my inbox', 'inbox triage', 'check my email', 'run email triage', 'process my inbox', 'what's new in my email', 'handle my email', 'email triage'). Requires the inbox-setup skill to have been run first." }, + { + "name": "meetings", + "source": "../../productivity/meetings/skills/meetings", + "category": "productivity", + "description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan." + }, { "name": "reflect", "source": "../../productivity/reflect/skills/reflect", @@ -1895,6 +1907,12 @@ "category": "productivity", "description": "Use when someone asks to roast an idea, pressure-test or stress-test an idea, validate a business idea, \"convene the panel\", get a brutal second opinion before building something, or says \"/roast\". Spins up a 5-angle panel (Critic, Champion, Analyst, Investigator, Customer) that attacks the idea from every angle, then a Judge returns one GO / RESHAPE / KILL verdict with the cheapest test to de-risk it." }, + { + "name": "weekly-review", + "source": "../../productivity/weekly-review/skills/weekly-review", + "category": "productivity", + "description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100." + }, { "name": "atlassian-admin", "source": "../../project-management/skills/atlassian-admin", @@ -2205,7 +2223,7 @@ "description": "Product management and design skills" }, "productivity": { - "count": 8, + "count": 11, "source": "../../productivity", "description": "Personal-productivity skills - capture, email, reflect, handoff, andreessen, roast, weekly-review, deep-work, meetings" }, diff --git a/.codex/skills/deep-work b/.codex/skills/deep-work new file mode 120000 index 00000000..e3d0dd28 --- /dev/null +++ b/.codex/skills/deep-work @@ -0,0 +1 @@ +../../productivity/deep-work/skills/deep-work \ No newline at end of file diff --git a/.codex/skills/meetings b/.codex/skills/meetings new file mode 120000 index 00000000..ab0b99d9 --- /dev/null +++ b/.codex/skills/meetings @@ -0,0 +1 @@ +../../productivity/meetings/skills/meetings \ No newline at end of file diff --git a/.codex/skills/weekly-review b/.codex/skills/weekly-review new file mode 120000 index 00000000..b352ce8f --- /dev/null +++ b/.codex/skills/weekly-review @@ -0,0 +1 @@ +../../productivity/weekly-review/skills/weekly-review \ No newline at end of file diff --git a/.gemini/skills-index.json b/.gemini/skills-index.json index 9a49cd84..106d5958 100644 --- a/.gemini/skills-index.json +++ b/.gemini/skills-index.json @@ -1,7 +1,7 @@ { "version": "1.0.0", "name": "gemini-cli-skills", - "total_skills": 420, + "total_skills": 434, "skills": [ { "name": "README", @@ -41,7 +41,7 @@ { "name": "cs-content-creator", "category": "agent", - "description": "AI-powered content creation specialist for brand voice consistency, SEO optimization, and multi-platform content strategy. Use when producing or reviewing marketing content that must stay on-brand and rank \u2014 e.g., turning one pillar blog post into a LinkedIn/X/newsletter bundle, or auditing draft copy against an established brand voice profile before publishing." + "description": "Long-form marketing content producer orchestrating the content-production skill (research \u2192 brief \u2192 draft \u2192 optimize \u2192 gate). Use when content must be written, scored, or made publish-ready \u2014 e.g., drafting a 2,000-word blog post against a target keyword and blocking publish until content_quality_gates.py passes, or auditing a draft for brand-voice drift with brand_voice_analyzer.py before it ships. Routes planning requests (topic clusters, calendars) to content-strategy. Supersedes the deprecated content-creator skill." }, { "name": "cs-cto-advisor", @@ -51,7 +51,7 @@ { "name": "cs-demand-gen-specialist", "category": "agent", - "description": "Demand generation and customer acquisition specialist for lead generation, conversion optimization, and multi-channel acquisition campaigns. Use when building or fixing the acquisition funnel \u2014 e.g., diagnosing why MQL-to-SQL conversion dropped after a pricing change, or designing a multi-channel campaign plan with budget split across paid, content, and email." + "description": "Demand generation and acquisition-funnel specialist orchestrating the marketing-demand-acquisition, paid-ads, and email-sequence skills. Use when building or fixing the acquisition engine \u2014 e.g., comparing channel CAC against B2B SaaS benchmarks before reallocating a $40k/month budget, scoring paid-ads account health with ad_health_scorer.py before scaling spend, or designing a nurture sequence that must score 70+ on sequence_analyzer.py before launch. Covers channel mix, CAC/ROAS math, MQL\u2192SQL workflows, attribution, and nurture design." }, { "name": "cs-engineering-lead", @@ -238,6 +238,11 @@ "category": "c-level", "description": "Inter-agent communication protocol for C-suite agent teams. Defines invocation syntax, loop prevention, isolation rules, and response formats. Use when C-suite agents need to query each other, coordinate cross-functional analysis, or run board meetings with multiple agent roles." }, + { + "name": "arquiteto-de-empresa", + "category": "c-level", + "description": "Company Architect: builds a business from scratch as an OKF (Open Knowledge Format) bundle \u2014 a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md, readable by humans and agents. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, few questions per block, and generates the concepts as conformant markdown. Trigger when the user wants to create, structure, or document an entire company in folders and .md files; when they mention build my company from scratch, company as code, company knowledge base for AI to read, company wiki for agents, OKF, or knowledge bundle. In English." + }, { "name": "board-deck-builder", "category": "c-level", @@ -518,6 +523,11 @@ "category": "c-level", "description": "Cross-functional what-if modeling for cascading multi-variable scenarios. Unlike single-assumption stress testing, this models compound adversity across all business functions simultaneously. Use when facing complex risk scenarios, strategic decisions with major downside, or when the user asks 'what if X AND Y both happen?'" }, + { + "name": "skills-arquiteto-de-empresa", + "category": "c-level", + "description": "Company Architect: builds a business from scratch as an OKF (Open Knowledge Format) bundle \u2014 a tree of version-controllable .md files with frontmatter type, links forming a graph, and reserved index.md/log.md, readable by humans and agents. Guides the founder through a 12-phase interview (foundation, strategy, market, financial, sales, marketing, product, operations, tech, people, legal, governance), one phase at a time, few questions per block, and generates the concepts as conformant markdown. Trigger when the user wants to create, structure, or document an entire company in folders and .md files; when they mention build my company from scratch, company as code, company knowledge base for AI to read, company wiki for agents, OKF, or knowledge bundle. In English." + }, { "name": "skills-chief-ai-officer-advisor", "category": "c-level", @@ -958,6 +968,11 @@ "category": "engineering", "description": "Microsoft 365 tenant administration for Global Administrators. Automate M365 tenant setup, Office 365 admin tasks, Azure AD user management, Exchange Online configuration, Teams administration, and security policies. Generate PowerShell scripts for bulk operations, Conditional Access policies, license management, and compliance reporting. Use for M365 tenant manager, Office 365 admin, Azure AD users, Global Administrator, tenant configuration, or Microsoft 365 automation." }, + { + "name": "named-persona-adversarial-review", + "category": "engineering", + "description": "Code review through the lens of real engineers' documented philosophies (Torvalds, Thompson, Carmack, Kent Beck, Jobs, Cagan). Complements abstract-role adversarial review with named, sourced perspectives. Use when automated review findings feel generic, when a PR has architectural or UX impact, or when the author wants pre-submit hardening beyond standard checks." + }, { "name": "promote", "category": "engineering", @@ -1046,7 +1061,7 @@ { "name": "senior-prompt-engineer", "category": "engineering", - "description": "This skill should be used when the user asks to \"optimize prompts\", \"design prompt templates\", \"evaluate LLM outputs\", \"build agentic systems\", \"implement RAG\", \"create few-shot examples\", \"analyze token usage\", or \"design AI workflows\". Use for prompt engineering patterns, LLM evaluation frameworks, agent architectures, and structured output design." + "description": "Use when the user asks to optimize prompts, design prompt templates, evaluate LLM outputs with an eval set, measure RAG retrieval quality, validate agent/tool configurations, analyze token usage, or design structured-output contracts. Covers eval-driven prompt iteration, RAG metrics (relevance, faithfulness, coverage), agent workflow validation, and token/cost budgeting \u2014 all model-agnostic, with three stdlib Python tools." }, { "name": "senior-qa", @@ -1061,23 +1076,13 @@ { "name": "senior-security", "category": "engineering", - "description": "Security engineering toolkit for threat modeling, vulnerability analysis, secure architecture, and penetration testing. Includes STRIDE analysis, OWASP guidance, cryptography patterns, and security scanning tools. Use when the user asks about security reviews, threat analysis, vulnerability assessments, secure coding practices, security audits, attack surface analysis, CVE remediation, or security best practices." + "description": "Use when the user asks for STRIDE threat modeling, DREAD risk scoring, data-flow-diagram threat analysis, or a quick secret scan \u2014 or when a security request needs routing to the right specialist skill (pen-testing, incident response, cloud posture, red team, AI security, threat hunting, secure code review). This skill owns threat modeling; everything else routes to a sibling." }, { "name": "skills-init", "category": "engineering", "description": ">-" }, - { - "name": "skills-review", - "category": "engineering", - "description": ">-" - }, - { - "name": "skills-status-2", - "category": "engineering", - "description": "Memory health dashboard showing line counts, topic files, capacity, stale entries, and recommendations. Use when the user runs /si:status or asks how full or healthy the agent memory is." - }, { "name": "snowflake-development", "category": "engineering", @@ -1113,6 +1118,11 @@ "category": "engineering-advanced", "description": "Use when the user asks to design a multi-agent system, pick an orchestration pattern (supervisor/swarm/pipeline), generate tool schemas for agents, or evaluate agent execution logs for cost, latency, and failure bottlenecks. Examples: 'design an agent architecture for research automation', 'generate Anthropic tool schemas from these tool descriptions', 'analyze these agent run logs for bottlenecks'. NOT for Claude Code workflow files (use workflow-builder) or single-agent prompt design (use agent-workflow-designer)." }, + { + "name": "agent-harness", + "category": "engineering-advanced", + "description": "Turn any domain folder of skills into a bounded agentic loop: compile a goal into a verifiable task plan, execute tasks with the domain's own tools, verify every task with machine-run checks, retry with caps, escalate to a human when budgets exhaust, and refuse to close until everything is verified or explicitly waived. Use when you want an agent or subagent to pick up a goal and drive it to a verified close across one of this repo's 18 domains ('run this goal through the engineering harness', 'set up an agentic loop for marketing work', 'make the finance domain self-verifying'). NOT for authoring Claude Code Workflow-tool .js scripts (workflow-builder), N-agent tournaments on one task (agenthub), single-file metric optimization (autoresearch-agent), or discovering published loop recipes (loop-library)." + }, { "name": "agent-workflow-designer", "category": "engineering-advanced", @@ -1323,6 +1333,11 @@ "category": "engineering-advanced", "description": "Zero-downtime migration planning, compatibility validation, and rollback strategy generation. Tools for system, database, and infrastructure migrations with minimal business impact. Use when planning a database migration, infrastructure cutover, system replacement, or any high-risk transition that needs explicit rollback paths." }, + { + "name": "minimalist", + "category": "engineering-advanced", + "description": "Use when the user asks to write code efficiently, avoid over-engineering, reduce dependencies, or prevent unnecessary abstractions. Enforces a strict efficiency ladder: YAGNI, reuse, stdlib, native platform, existing deps \u2014 before writing any new code." + }, { "name": "monorepo-navigator", "category": "engineering-advanced", @@ -1408,6 +1423,11 @@ "category": "engineering-advanced", "description": "Validate, test, and score the quality of skills within the claude-skills ecosystem. Comprehensive meta-skill: structure validation, Python script testing (syntax + imports + runtime + output format), multi-dimensional quality scoring with letter grades and tier classification (BASIC/STANDARD/POWERFUL). Use when authoring a new skill, auditing existing skills for tier promotion, setting up pre-commit hooks for skill quality, or integrating skill QA into CI." }, + { + "name": "skillopt-sleep", + "category": "engineering-advanced", + "description": "Use when the user wants their Claude agent to self-improve from past usage, asks about a nightly/offline 'sleep' or 'dream' cycle, memory/skill consolidation, or says things like 'make my agent better the more I use it', 'review my past sessions', 'learn my preferences', 'consolidate what you learned', 'run the sleep cycle', or wants to schedule offline self-optimization. Drives the skillopt_sleep engine: harvest past sessions -> mine recurring tasks -> replay offline -> consolidate validated CLAUDE.md and SKILL.md behind a held-out gate." + }, { "name": "skills-chaos-engineering", "category": "engineering-advanced", @@ -1473,6 +1493,11 @@ "category": "engineering-advanced", "description": "Show experiment dashboard with results, active loops, and progress. Use when the user runs /ar:status or asks how an autoresearch experiment is going." }, + { + "name": "strict-api", + "category": "engineering-advanced", + "description": "Use when the user says 'no hallucinations', 'verify APIs', 'reality check', or 'don't invent functions'. Prevents the agent from calling methods, imports, or variables that do not provably exist in the user's installed version." + }, { "name": "tc-tracker", "category": "engineering-advanced", @@ -1503,6 +1528,11 @@ "category": "engineering-advanced", "description": "Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, build, or author a new skill." }, + { + "name": "zero-hallucination-coder", + "category": "engineering-advanced", + "description": "Runs a disciplined Discuss -> Map -> Decompose -> Execute -> Verify loop that grounds code in verified structure \u2014 no invented APIs, no assumed imports, no placeholder code \u2014 with a lazy-senior-dev YAGNI ladder that deletes unnecessary code before it is written. Use when a coding task is high-stakes, complex, or spans existing code (auth, databases, migrations, multi-file features), or when the user explicitly asks to plan carefully before coding, avoid hallucinated code, or work rigorously. Not for trivial edits, typos, or throwaway one-off scripts \u2014 those do not need the full loop." + }, { "name": "business-investment-advisor", "category": "finance", @@ -1648,6 +1678,11 @@ "category": "marketing", "description": "When the user wants to plan a product launch, feature announcement, or release strategy. Also use when the user mentions 'launch,' 'Product Hunt,' 'feature release,' 'announcement,' 'go-to-market,' 'beta launch,' 'early access,' 'waitlist,' 'product update,' 'GTM plan,' 'launch checklist,' or 'launch momentum.' This skill covers phased launches, channel strategy, and ongoing launch momentum." }, + { + "name": "local-seo-manager", + "category": "marketing", + "description": "Manage local SEO for service-area businesses \u2014 appliance repair, HVAC, plumbing, cleaning, and any business that serves customers at their location. Use when the user wants to: audit Google Business Profile, generate neighborhood service area pages, check NAP consistency across directories, create LocalBusiness schema, or write review responses. Triggers: 'local SEO', 'Google Business Profile', 'GBP', 'service area page', 'NAP consistency', 'local citations', 'LocalBusiness schema', 'review responses', 'Google Maps ranking'. NOT for national SEO (use seo-audit). NOT for general schema (use schema-markup). NOT for AI answer-engine visibility (use aeo)." + }, { "name": "marketing-context", "category": "marketing", @@ -1836,7 +1871,7 @@ { "name": "product-skills", "category": "product", - "description": "Router/index for the 12 product skills bundled in this plugin (RICE prioritization, OKRs, UX research, design tokens, competitive teardown, analytics, experiments, discovery, roadmaps, spec-to-repo, landing pages, SaaS scaffolding). Use when a product request doesn't obviously match one skill and you need to pick the right one (e.g., 'help me prioritize features', 'plan a product experiment')." + "description": "Use when coordinating product work across the 12 bundled product sub-skills (RICE, OKRs, UX research, design tokens, competitive teardown, analytics, experiments, discovery, roadmaps, spec-to-repo, landing pages, SaaS scaffolding) or the 4 standalone product-team plugins (user stories, Apple HIG, code-to-PRD, research summarizer). Triggers on 'help me prioritize', 'plan a product experiment', 'we ship features nobody uses', 'run the discovery loop', 'is our OST sound'. Forks context to route to one sub-skill via a deterministic signal router and returns a digest; can also drive a continuous-discovery loop (Torres cadence tracker + OST linter as machine gates) or a full goal\u2192plan\u2192execute\u2192verify\u2192close run through the repo-wide agent-harness. Distinct from project-management (how to deliver vs what to build), marketing/landing (from-scratch pages), and engineering/agent-harness (the generic loop engine this orchestrator plugs into)." }, { "name": "product-strategist", @@ -1883,6 +1918,16 @@ "category": "productivity", "description": "Captures and organizes chaotic brain dumps into a structured, actionable system with zero information loss. Use this skill whenever the user says 'capture this', 'brain dump', 'let me dump some ideas', 'I've got a bunch of thoughts', 'here's everything on my mind', 'idea dump', 'let me get this out of my head', 'I need to organize my thoughts', 'here's what I'm thinking', or any variation where someone is unloading a messy stream of ideas, tasks, thoughts, and plans wanting them turned into something coherent. Also trigger when the user pastes or dictates a long, unstructured block of mixed ideas \u2014 even without the exact phrase \u2014 the intent is the same. Fast-to-action by design: no upfront intake. Output is four sections (Projects/Ideas, Tasks, Connections, How I Can Help) ending with a directive question. Asks at most one mid-organization clarifying question when a single item is genuinely ambiguous between task and project." }, + { + "name": "deep-work", + "category": "productivity", + "description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target." + }, + { + "name": "fable-goal", + "category": "productivity", + "description": "Convert a rambling description of a desired outcome into one polished, autonomous /goal prompt ready to paste into a fresh session. Use when the user says \"/fable-goal\", \"turn this into a goal prompt\", \"write me a fable prompt\", \"write the prompt that builds X\", or rambles about something they want made and asks for the prompt that makes it happen. The output is a single copy-paste prompt, never the build itself. Do NOT use when the user wants the thing built right now in this session \u2014 only when they want the PROMPT that will make it happen in a fresh session." + }, { "name": "handoff", "category": "productivity", @@ -1898,11 +1943,26 @@ "category": "productivity", "description": "Runs a full inbox triage using the knowledge base created by the 'inbox-setup' skill. Light-intake by design (most invocations skip questions and run with KB-default preferences); asks at most 2 grill-me override questions when invocation is outside normal cadence or includes category-skip intent. Searches recent emails, classifies them via the user's taxonomy, researches new senders, generates recommendations, drafts replies (NEVER sends), delivers a report in the user's preferred format, and updates the knowledge base with learnings. Designed to run on a recurring schedule (1-3x daily) or on demand. Use when the user wants their inbox processed, in any variation (e.g., 'triage my inbox', 'inbox triage', 'check my email', 'run email triage', 'process my inbox', 'what's new in my email', 'handle my email', 'email triage'). Requires the inbox-setup skill to have been run first." }, + { + "name": "meetings", + "category": "productivity", + "description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan." + }, { "name": "reflect", "category": "productivity", "description": "Mid-conversation reflection skill that pauses execution and zooms out from detail-mode to honestly reassess direction, assumptions, and bias. Use when the user says 'reflect', 'take a step back', 'step back', 'zoom out', 'are we missing something', 'bigger picture', 'sanity check this', 'are we on track', 'are we overthinking this', 'forest for the trees', or any variation signaling intent to break out of detail-mode and reassess. Also trigger when the conversation has gone deep on implementation details without strategic check-in, or when the user shows signs of being stuck \u2014 that's often a signal the framing needs a reset, not more detail work. Intentionally low-intake: runs the 5-dimension analysis immediately when prior context is rich enough; asks one forcing clarifier only when invocation context is too thin to reassess from." }, + { + "name": "roast", + "category": "productivity", + "description": "Use when someone asks to roast an idea, pressure-test or stress-test an idea, validate a business idea, \"convene the panel\", get a brutal second opinion before building something, or says \"/roast\". Spins up a 5-angle panel (Critic, Champion, Analyst, Investigator, Customer) that attacks the idea from every angle, then a Judge returns one GO / RESHAPE / KILL verdict with the cheapest test to de-risk it." + }, + { + "name": "weekly-review", + "category": "productivity", + "description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100." + }, { "name": "atlassian-admin", "category": "project-management", @@ -1931,7 +1991,7 @@ { "name": "pm-skills", "category": "project-management", - "description": "Router/index for the 8 project-management skills bundled in this plugin (senior PM quant toolkit, scrum master, Jira/JQL, Confluence, Atlassian admin, Atlassian templates, meeting analyzer, team communications). Use when a PM request doesn't obviously match one skill and you need to pick the right one (e.g., 'our sprints feel off', 'audit our Jira permissions'). Bundles an Atlassian Remote MCP config (.mcp.json) for live Jira/Confluence access." + "description": "Use when coordinating project-delivery work across the 8 project-management sub-skills \u2014 sprint/velocity analytics, portfolio health, Jira/JQL, Confluence, Atlassian admin, templates, meeting analysis, team comms. Triggers on 'our sprints feel off', 'project health report', 'audit our Jira permissions', 'when will it be done', 'run the delivery loop'. Forks context to route to one sub-skill via a deterministic signal router and returns a digest; can also drive a full goal\u2192plan\u2192execute\u2192verify\u2192close delivery loop through the repo-wide agent-harness with Jira MCP data bridged into the domain's analytics tools. Distinct from product-team (what to build vs how to deliver it), business-operations (internal ops), and engineering/agent-harness (the generic loop engine this orchestrator plugs into)." }, { "name": "scrum-master", @@ -1948,6 +2008,11 @@ "category": "project-management", "description": "Write internal company communications \u2014 3P updates (Progress/Plans/Problems), company-wide newsletters, FAQ roundups, incident reports, leadership updates, status reports, project updates, and general internal comms. Use this skill any time the user asks to draft, edit, or format something meant for internal audiences. Trigger on keywords like \"3P\", \"weekly update\", \"newsletter\", \"FAQ\", \"internal comms\", \"status report\", \"company update\", \"team update\", \"incident report\", or any request to summarize work for leadership, teammates, or the broader company. Even casual requests like \"write my update\" or \"summarize what my team did this week\" should trigger this skill." }, + { + "name": "agent-decision-receipts", + "category": "ra-qm", + "description": "Mint a tamper-evident, post-quantum-signed receipt for a consequential agent action (deploy, delete, pay, grant-access, model decision) so it can be verified later from the certificate alone. Use when an autonomous agent takes a side-effecting action that may need to be proven later, or when satisfying EU AI Act Article 12 record-keeping. Three decisions: whether an action needs a receipt, minting it, verifying it. Signing is delegated to the open-source OpenAgentOntology package. Not after-the-fact log analysis; not a hosted notary; not a legal opinion." + }, { "name": "capa-officer", "category": "ra-qm", @@ -2038,6 +2103,11 @@ "category": "ra-qm", "description": "Use when the user asks to prepare for SOC 2 audits, map Trust Service Criteria, build control matrices, collect audit evidence, perform gap analysis, or assess SOC 2 Type I vs Type II readiness." }, + { + "name": "deep-research", + "category": "research", + "description": "Run a disciplined, multi-source research investigation for a high-stakes question or decision \u2014 fan-out web search across many channels, parallel sub-agents, source triangulation (each claim backed by \u22653 independent sources), an adversarial review pass, and every source saved to its own file with verbatim quotes for reuse. Use when a low-quality answer is expensive: strategy work, comparing N products/methods/markets, validating a hypothesis with external data, or mapping how a field works. NOT for quick fact-checks (answer directly), structured 12-dimension competitor scoring (use competitive-teardown), or fast topic overviews where the decision risk is low (use the research router instead)." + }, { "name": "dossier", "category": "research", @@ -2051,7 +2121,7 @@ { "name": "litreview", "category": "research", - "description": "Academic literature orientation skill that searches papers via Consensus, builds a strategic search plan using PICO (default) or SPIDER / Decomposition / hybrid as fallbacks, and synthesizes findings into a formatted Word (.docx) research guide. Grill-me intake (research question specificity + framework hint + tentative depth) before the recon search; a second forcing checkpoint after Phase 2 confirms framework + sub-areas + depth before searches consume budget. Configurable depth (5/10/20 queries) controls coverage vs. speed. Output is a 'launching pad' \u2014 an orientation guide that lets a researcher dive in confidently, not a finished review. Use when the user starts literature-oriented research (e.g., 'litreview on [topic]', 'literature review on [topic]', 'I'm starting a literature review on X', 'I'm writing a paper on X', 'help me research X', 'I'm doing research on X', 'can you help me research X'). Do NOT use for single one-off paper searches wanting a quick list \u2014 that's a plain Consensus search." + "description": "Academic literature orientation skill that searches papers via free keyless APIs (PubMed E-utilities + OpenAlex) by default \u2014 with the Consensus MCP as an optional enhancement lane when connected \u2014 builds a strategic search plan using PICO (default) or SPIDER / Decomposition / hybrid as fallbacks, and synthesizes findings into a formatted Word (.docx) research guide. Grill-me intake (research question specificity + framework hint + tentative depth) before the recon search; a second forcing checkpoint after Phase 2 confirms framework + sub-areas + depth before searches consume budget. Configurable depth (5/10/20 queries) controls coverage vs. speed. Output is a 'launching pad' \u2014 an orientation guide that lets a researcher dive in confidently, not a finished review. Use when the user starts literature-oriented research (e.g., 'litreview on [topic]', 'literature review on [topic]', 'I'm starting a literature review on X', 'I'm writing a paper on X', 'help me research X', 'I'm doing research on X', 'can you help me research X'). Do NOT use for single one-off paper searches wanting a quick list \u2014 that's a plain PubMed/OpenAlex (or Consensus) query." }, { "name": "notebooklm", @@ -2118,7 +2188,7 @@ "description": "Business-operations resources" }, "c-level": { - "count": 66, + "count": 68, "description": "C-level resources" }, "command": { @@ -2134,11 +2204,11 @@ "description": "Compliance-os resources" }, "engineering": { - "count": 51, + "count": 52, "description": "Engineering resources" }, "engineering-advanced": { - "count": 79, + "count": 84, "description": "Engineering-advanced resources" }, "finance": { @@ -2150,7 +2220,7 @@ "description": "Markdown-html resources" }, "marketing": { - "count": 47, + "count": 48, "description": "Marketing resources" }, "marketing-top-level": { @@ -2162,7 +2232,7 @@ "description": "Product resources" }, "productivity": { - "count": 6, + "count": 11, "description": "Productivity resources" }, "project-management": { @@ -2170,11 +2240,11 @@ "description": "Project-management resources" }, "ra-qm": { - "count": 18, + "count": 19, "description": "Ra-qm resources" }, "research": { - "count": 8, + "count": 9, "description": "Research resources" }, "research-ops": { diff --git a/.gemini/skills/agent-decision-receipts/SKILL.md b/.gemini/skills/agent-decision-receipts/SKILL.md new file mode 120000 index 00000000..9c33184b --- /dev/null +++ b/.gemini/skills/agent-decision-receipts/SKILL.md @@ -0,0 +1 @@ +../../../ra-qm-team/skills/agent-decision-receipts/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/agent-harness/SKILL.md b/.gemini/skills/agent-harness/SKILL.md new file mode 120000 index 00000000..9c1f421b --- /dev/null +++ b/.gemini/skills/agent-harness/SKILL.md @@ -0,0 +1 @@ +../../../engineering/agent-harness/skills/agent-harness/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/arquiteto-de-empresa/SKILL.md b/.gemini/skills/arquiteto-de-empresa/SKILL.md new file mode 120000 index 00000000..9c0f16e3 --- /dev/null +++ b/.gemini/skills/arquiteto-de-empresa/SKILL.md @@ -0,0 +1 @@ +../../../c-level-advisor/skills/arquiteto-de-empresa/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/deep-research/SKILL.md b/.gemini/skills/deep-research/SKILL.md new file mode 120000 index 00000000..66613218 --- /dev/null +++ b/.gemini/skills/deep-research/SKILL.md @@ -0,0 +1 @@ +../../../research/deep-research/skills/deep-research/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/deep-work/SKILL.md b/.gemini/skills/deep-work/SKILL.md new file mode 120000 index 00000000..513a2036 --- /dev/null +++ b/.gemini/skills/deep-work/SKILL.md @@ -0,0 +1 @@ +../../../productivity/deep-work/skills/deep-work/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/fable-goal/SKILL.md b/.gemini/skills/fable-goal/SKILL.md new file mode 120000 index 00000000..cf6540c7 --- /dev/null +++ b/.gemini/skills/fable-goal/SKILL.md @@ -0,0 +1 @@ +../../../productivity/fable-goal/skills/fable-goal/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/local-seo-manager/SKILL.md b/.gemini/skills/local-seo-manager/SKILL.md new file mode 120000 index 00000000..1fcf7940 --- /dev/null +++ b/.gemini/skills/local-seo-manager/SKILL.md @@ -0,0 +1 @@ +../../../marketing-skill/skills/local-seo-manager/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/meetings/SKILL.md b/.gemini/skills/meetings/SKILL.md new file mode 120000 index 00000000..407a2f58 --- /dev/null +++ b/.gemini/skills/meetings/SKILL.md @@ -0,0 +1 @@ +../../../productivity/meetings/skills/meetings/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/minimalist/SKILL.md b/.gemini/skills/minimalist/SKILL.md new file mode 120000 index 00000000..474ad462 --- /dev/null +++ b/.gemini/skills/minimalist/SKILL.md @@ -0,0 +1 @@ +../../../engineering/minimalist/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/named-persona-adversarial-review/SKILL.md b/.gemini/skills/named-persona-adversarial-review/SKILL.md new file mode 120000 index 00000000..82969519 --- /dev/null +++ b/.gemini/skills/named-persona-adversarial-review/SKILL.md @@ -0,0 +1 @@ +../../../engineering-team/skills/named-persona-adversarial-review/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/roast/SKILL.md b/.gemini/skills/roast/SKILL.md new file mode 120000 index 00000000..df76c0c8 --- /dev/null +++ b/.gemini/skills/roast/SKILL.md @@ -0,0 +1 @@ +../../../productivity/roast/skills/roast/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/skillopt-sleep/SKILL.md b/.gemini/skills/skillopt-sleep/SKILL.md new file mode 120000 index 00000000..13b084d2 --- /dev/null +++ b/.gemini/skills/skillopt-sleep/SKILL.md @@ -0,0 +1 @@ +../../../engineering/skillopt-sleep/skills/skillopt-sleep/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/skills-arquiteto-de-empresa/SKILL.md b/.gemini/skills/skills-arquiteto-de-empresa/SKILL.md new file mode 120000 index 00000000..ef86ff3d --- /dev/null +++ b/.gemini/skills/skills-arquiteto-de-empresa/SKILL.md @@ -0,0 +1 @@ +../../../c-level-advisor/arquiteto-de-empresa/skills/arquiteto-de-empresa/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/strict-api/SKILL.md b/.gemini/skills/strict-api/SKILL.md new file mode 120000 index 00000000..6793d1e7 --- /dev/null +++ b/.gemini/skills/strict-api/SKILL.md @@ -0,0 +1 @@ +../../../engineering/strict-api/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/weekly-review/SKILL.md b/.gemini/skills/weekly-review/SKILL.md new file mode 120000 index 00000000..29193b4e --- /dev/null +++ b/.gemini/skills/weekly-review/SKILL.md @@ -0,0 +1 @@ +../../../productivity/weekly-review/skills/weekly-review/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/zero-hallucination-coder/SKILL.md b/.gemini/skills/zero-hallucination-coder/SKILL.md new file mode 120000 index 00000000..9c384c72 --- /dev/null +++ b/.gemini/skills/zero-hallucination-coder/SKILL.md @@ -0,0 +1 @@ +../../../engineering/zero-hallucination-coder/skills/zero-hallucination-coder/SKILL.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 166cd0de..42faef2b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co This is a **comprehensive skills library** for Claude AI and Claude Code - reusable, production-ready skill packages that bundle domain expertise, best practices, analysis tools, and strategic frameworks. The repository provides modular skills that teams can download and use directly in their workflows. -**Current Scope:** 359 production-ready skills across 18 domains with 635 Python automation tools, 732 reference guides, 99 agents (cs-* + 7 personas), and 111 slash commands, distributed as 85 marketplace plugins. Headline counters are derived from the tree by `scripts/derive_counters.py` (run with `--check` to verify the docs still match). **v2.11.2 (current)** vendors **engineering/skillopt-sleep/** — started as a verbatim, byte-for-byte copy of `microsoft/SkillOpt`'s `skillopt_sleep` engine (stdlib-only, zero third-party deps) and its Claude Code plugin surface (`skills/`, `hooks/`, `commands/`, `scripts/`), then received 23 targeted patches after ten rounds of adversarial review (see `engineering/skillopt-sleep/README.md`'s numbered "Deviations from upstream" list, the authoritative source — re-apply all 23 on re-vendor). Gives a local agent a nightly "sleep cycle": read-only harvest of past Claude Code session transcripts → mine recurring tasks → replay offline on the user's own API budget → consolidate into `CLAUDE.md`/`SKILL.md` edits behind a held-out validation gate → stage for review; nothing live changes until an explicit `/skillopt-sleep adopt` (which backs up first). Default `mock` backend spends no API budget. The heavier `skillopt` *training* package (benchmark-driven, needs `numpy`/`openai`/`azure-*` + hand-labeled train/val/test data per task) was deliberately **not** vendored — it optimizes one narrow, scoreable task at a time, which doesn't fit this repo's broad domain-expertise skills or its no-ML-in-scripts/no-test-framework conventions; `skillopt_sleep` mines its "benchmark" from real usage instead, which does fit. Attribution preserved in `plugin.json` + `LICENSE` + `README.md` (MIT, © Microsoft Corporation / Yifan Yang), following the same verbatim-vendor pattern as `loop-library/`. **Unreleased (post-v2.11.1)** added **productivity/fable-goal** — converts a rambling description of a desired outcome into one polished, copy-paste `/goal` prompt for a fresh autonomous session (ported from `duncan-buildroom/freeskills`). **v2.11.1 (complete)** upgrades **product-team/** and **project-management/** into agent-harness domains: both prose routers rebuilt as `context: fork` orchestrators with deterministic goal routers (exit-code route/ask/refuse), a Jira MCP snapshot bridge (Kanban-Guide-2025 flow metrics + seeded Monte Carlo forecasts, verified end-to-end into velocity_analyzer), a delegation-governance loop gate (human owner / reviewer / machine-checkable acceptance / close refusal), a Torres continuous-discovery cadence tracker + Opportunity Solution Tree linter, cs-pm-orchestrator + cs-product-orchestrator agents, and /cs:pm|grill-pm|pm-loop + /cs:product|grill-product|product-loop commands — plus the public audit record `audit/pm-product-agentic-2026-07/` (AR-rubric scores for all 26 skills, research-backed improvement fields, executable verification criteria). **v2.9.0 (complete)** added the **research-ops/** top-level domain — enterprise Research Operations (orchestrator + clinical-research + research-finance + market-research + product-research), the managed counterpart to the academic research/ domain, with `context: fork` orchestration and a Matt Pocock "Forcing-question library" in every SKILL.md plus `/cs:grill-research-ops`. **v2.8.0 (complete)** added 2 new top-level domains — **business-operations/** (7 internal-ops skills: orchestrator + process-mapper + vendor-management + capacity-planner + internal-comms + knowledge-ops + procurement-optimizer) and **commercial/** (8 per-deal-economics skills: orchestrator + pricing-strategist + deal-desk + partnerships-architect + channel-economics + commercial-policy + rfp-responder + commercial-forecaster) — with orchestrator skills using `context: fork` for chaining, Matt Pocock docs-anchored "Forcing-question library" in every SKILL.md, plus `/cs:grill-bizops` and `/cs:grill-commercial`. **v2.8.2** adds a productivity-shaped `handoff` skill (sibling to engineering/handoff) inspired by Matt Pocock — first-run setup with configurable save location, redaction linter, SessionStart + SessionEnd hooks, fidelity self-check, `--refresh` flag. **v2.8.1** upgraded the engineering role-skills (senior-fullstack / senior-frontend / senior-backend) with karpathy-coder + Matt Pocock decision engines + per-role forcing questions. v2.7.3 ports `alirezarezvani/aeo-box` — AEO (Answer Engine Optimization) skill into marketing-skill/ + security-guidance PreToolUse hook into engineering/. v2.7.0 added 13 Path-B skills across 3 top-level domains (productivity, marketing, research). v2.6.0 added 4 Matt Pocock-derived productivity skills. +**Current Scope:** 362 production-ready skills across 18 domains with 644 Python automation tools, 741 reference guides, 102 agents (cs-* + 7 personas), and 116 slash commands, distributed as 88 marketplace plugins. Headline counters are derived from the tree by `scripts/derive_counters.py` (run with `--check` to verify the docs still match). **v2.11.2 (current)** vendors **engineering/skillopt-sleep/** — started as a verbatim, byte-for-byte copy of `microsoft/SkillOpt`'s `skillopt_sleep` engine (stdlib-only, zero third-party deps) and its Claude Code plugin surface (`skills/`, `hooks/`, `commands/`, `scripts/`), then received 23 targeted patches after ten rounds of adversarial review (see `engineering/skillopt-sleep/README.md`'s numbered "Deviations from upstream" list, the authoritative source — re-apply all 23 on re-vendor). Gives a local agent a nightly "sleep cycle": read-only harvest of past Claude Code session transcripts → mine recurring tasks → replay offline on the user's own API budget → consolidate into `CLAUDE.md`/`SKILL.md` edits behind a held-out validation gate → stage for review; nothing live changes until an explicit `/skillopt-sleep adopt` (which backs up first). Default `mock` backend spends no API budget. The heavier `skillopt` *training* package (benchmark-driven, needs `numpy`/`openai`/`azure-*` + hand-labeled train/val/test data per task) was deliberately **not** vendored — it optimizes one narrow, scoreable task at a time, which doesn't fit this repo's broad domain-expertise skills or its no-ML-in-scripts/no-test-framework conventions; `skillopt_sleep` mines its "benchmark" from real usage instead, which does fit. Attribution preserved in `plugin.json` + `LICENSE` + `README.md` (MIT, © Microsoft Corporation / Yifan Yang), following the same verbatim-vendor pattern as `loop-library/`. **Unreleased (post-v2.11.2)** ships the **productivity coverage expansion** — public audit record `audit/productivity-2026-07/` (all 7 legacy skills scored, 24/24 scripts smoke-tested, coverage map vs the personal-productivity canon) + 3 gap-filling plugins, each with a cs-* agent, /cs:* commands, 3 stdlib scripts and 3 cited references: **weekly-review** (GTD loop; review-gate refuses COMPLETE while a mandatory GET CURRENT step is missing), **deep-work** (time-block planner refusing >4h deep demand, shallow-work budget auditor, focus-session logger), **meetings** (MEET/ASYNC/NOT-READY cost gate, outcome-required agenda builder, action-item extractor with ORPHAN/NO-DUE flags). **Unreleased (post-v2.11.1)** added **productivity/fable-goal** — converts a rambling description of a desired outcome into one polished, copy-paste `/goal` prompt for a fresh autonomous session (ported from `duncan-buildroom/freeskills`). **v2.11.1 (complete)** upgrades **product-team/** and **project-management/** into agent-harness domains: both prose routers rebuilt as `context: fork` orchestrators with deterministic goal routers (exit-code route/ask/refuse), a Jira MCP snapshot bridge (Kanban-Guide-2025 flow metrics + seeded Monte Carlo forecasts, verified end-to-end into velocity_analyzer), a delegation-governance loop gate (human owner / reviewer / machine-checkable acceptance / close refusal), a Torres continuous-discovery cadence tracker + Opportunity Solution Tree linter, cs-pm-orchestrator + cs-product-orchestrator agents, and /cs:pm|grill-pm|pm-loop + /cs:product|grill-product|product-loop commands — plus the public audit record `audit/pm-product-agentic-2026-07/` (AR-rubric scores for all 26 skills, research-backed improvement fields, executable verification criteria). **v2.9.0 (complete)** added the **research-ops/** top-level domain — enterprise Research Operations (orchestrator + clinical-research + research-finance + market-research + product-research), the managed counterpart to the academic research/ domain, with `context: fork` orchestration and a Matt Pocock "Forcing-question library" in every SKILL.md plus `/cs:grill-research-ops`. **v2.8.0 (complete)** added 2 new top-level domains — **business-operations/** (7 internal-ops skills: orchestrator + process-mapper + vendor-management + capacity-planner + internal-comms + knowledge-ops + procurement-optimizer) and **commercial/** (8 per-deal-economics skills: orchestrator + pricing-strategist + deal-desk + partnerships-architect + channel-economics + commercial-policy + rfp-responder + commercial-forecaster) — with orchestrator skills using `context: fork` for chaining, Matt Pocock docs-anchored "Forcing-question library" in every SKILL.md, plus `/cs:grill-bizops` and `/cs:grill-commercial`. **v2.8.2** adds a productivity-shaped `handoff` skill (sibling to engineering/handoff) inspired by Matt Pocock — first-run setup with configurable save location, redaction linter, SessionStart + SessionEnd hooks, fidelity self-check, `--refresh` flag. **v2.8.1** upgraded the engineering role-skills (senior-fullstack / senior-frontend / senior-backend) with karpathy-coder + Matt Pocock decision engines + per-role forcing questions. v2.7.3 ports `alirezarezvani/aeo-box` — AEO (Answer Engine Optimization) skill into marketing-skill/ + security-guidance PreToolUse hook into engineering/. v2.7.0 added 13 Path-B skills across 3 top-level domains (productivity, marketing, research). v2.6.0 added 4 Matt Pocock-derived productivity skills. **Key Distinction**: This is NOT a traditional application. It's a library of skill packages meant to be extracted and deployed by users into their own Claude workflows. @@ -577,6 +577,6 @@ When I correct you, or you catch yourself making a mistake: before continuing ad --- -**Last Updated:** July 3, 2026 -**Version:** v2.11.1 -**Status:** 358 skills deployed across 18 domains, 84 marketplace plugins, docs site live (counters derived via `scripts/derive_counters.py`) +**Last Updated:** July 17, 2026 +**Version:** v2.11.2 (+ unreleased productivity coverage expansion) +**Status:** 362 skills deployed across 18 domains, 88 marketplace plugins, docs site live (counters derived via `scripts/derive_counters.py`) diff --git a/README.md b/README.md index 51c6d464..7af908d9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Claude Code Skills & Plugins — Agent Skills for Every Coding Tool -**359 production-ready Claude Code skills, plugins, and agent skills for 13 AI coding tools.** +**362 production-ready Claude Code skills, plugins, and agent skills for 13 AI coding tools.** -The most comprehensive open-source library of Claude Code skills and agent plugins — also works with OpenAI Codex, Gemini CLI, Cursor, and 9 more coding agents. Reusable expertise packages covering engineering, DevOps, marketing (incl. AEO — Answer Engine Optimization for LLM citation), security (PreToolUse hooks), compliance, C-level advisory (incl. founder-mode CFO/CMO/CRO/CPO/COO/CHRO/CISO/GC/CDO/CAIO/CCO/VPE personas + 21 /cs:* slash commands), productivity (capture/email/reflect), an academic research stack (litreview/grants/dossier/patent/syllabus/pulse/notebooklm/deep-research + hybrid router), and enterprise Research Operations (clinical-research/research-finance/market-research/product-research, v2.9.0). +The most comprehensive open-source library of Claude Code skills and agent plugins — also works with OpenAI Codex, Gemini CLI, Cursor, and 9 more coding agents. Reusable expertise packages covering engineering, DevOps, marketing (incl. AEO — Answer Engine Optimization for LLM citation), security (PreToolUse hooks), compliance, C-level advisory (incl. founder-mode CFO/CMO/CRO/CPO/COO/CHRO/CISO/GC/CDO/CAIO/CCO/VPE personas + 21 /cs:* slash commands), productivity (capture/email/reflect/weekly-review/deep-work/meetings), an academic research stack (litreview/grants/dossier/patent/syllabus/pulse/notebooklm/deep-research + hybrid router), and enterprise Research Operations (clinical-research/research-finance/market-research/product-research, v2.9.0). **Works with:** Claude Code · OpenAI Codex · Gemini CLI · OpenClaw · Hermes Agent[^hermes] · Mistral Vibe[^vibe] · Cursor · Aider · Windsurf · Kilo Code · OpenCode · Augment · Antigravity @@ -10,10 +10,10 @@ The most comprehensive open-source library of Claude Code skills and agent plugi [^vibe]: Mistral Vibe is also **BYO-sync tier**: the repo ships a pre-generated `.vibe/skills/claude-skills/` tree, run `./scripts/vibe-install.sh` once locally to install into `~/.vibe/skills/`. Same agentskills.io SKILL.md standard — no format conversion. Docs: . [![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT) -[![Skills](https://img.shields.io/badge/Skills-359-brightgreen?style=for-the-badge)](#skills-overview) -[![Agents](https://img.shields.io/badge/Agents-99-blue?style=for-the-badge)](#agents) +[![Skills](https://img.shields.io/badge/Skills-362-brightgreen?style=for-the-badge)](#skills-overview) +[![Agents](https://img.shields.io/badge/Agents-102-blue?style=for-the-badge)](#agents) [![Personas](https://img.shields.io/badge/Personas-7-purple?style=for-the-badge)](#personas) -[![Commands](https://img.shields.io/badge/Commands-111-orange?style=for-the-badge)](#commands) +[![Commands](https://img.shields.io/badge/Commands-116-orange?style=for-the-badge)](#commands) [![Stars](https://img.shields.io/github/stars/alirezarezvani/claude-skills?style=for-the-badge)](https://github.com/alirezarezvani/claude-skills/stargazers) [![SkillCheck Validated](https://img.shields.io/badge/SkillCheck-Validated-4c1?style=for-the-badge)](https://getskillcheck.com) @@ -26,10 +26,10 @@ The most comprehensive open-source library of Claude Code skills and agent plugi Claude Code skills (also called agent skills or coding agent plugins) are modular instruction packages that give AI coding agents domain expertise they don't have out of the box. Each skill includes: - **SKILL.md** — structured instructions, workflows, and decision frameworks -- **Python tools** — 635 CLI scripts (all stdlib-only, zero pip installs) -- **Reference docs** — 732 templates, checklists, and domain-specific knowledge files +- **Python tools** — 644 CLI scripts (all stdlib-only, zero pip installs) +- **Reference docs** — 741 templates, checklists, and domain-specific knowledge files -**One repo, thirteen platforms.** Works natively as Claude Code plugins, Codex agent skills, Gemini CLI skills, Hermes Agent skills, Mistral Vibe skills, and converts to more tools via `scripts/convert.sh`. All 635 Python tools run anywhere Python runs. +**One repo, thirteen platforms.** Works natively as Claude Code plugins, Codex agent skills, Gemini CLI skills, Hermes Agent skills, Mistral Vibe skills, and converts to more tools via `scripts/convert.sh`. All 644 Python tools run anywhere Python runs. ### Skills vs Agents vs Personas @@ -150,7 +150,7 @@ Run `./scripts/convert.sh --tool all` to generate tool-specific outputs locally. ## Skills Overview -**359 skills across 18 domains:** +**362 skills across 18 domains:** | Domain | Skills | Highlights | Details | |--------|--------|------------|---------| @@ -158,7 +158,7 @@ Run `./scripts/convert.sh --tool all` to generate tool-specific outputs locally. | **⚡ Engineering — POWERFUL** | 84 | Agent designer, RAG architect, database designer, CI/CD builder, security auditor, MCP builder, AgentHub, Helm charts, Terraform, self-eval, llm-wiki, tc-tracker, autoresearch-agent, **reliability portfolio** (feature-flags-architect, kubernetes-operator, chaos-engineering, slo-architect), ship-gate, security-guidance PreToolUse hook, **Matt Pocock skills** (write-a-skill, caveman, grill-me, handoff, grill-with-docs), **zero-hallucination-coder** (Discuss→Map→Decompose→Execute→Verify), **agent-harness** (goal→plan→execute→verify→close loops over any domain), **skillopt-sleep** (nightly gated self-evolution from real Claude Code sessions, vendored from microsoft/SkillOpt) | [engineering/](engineering/) | | **🎯 Product** | 17 | Product manager, agile PO, strategist, UX researcher, UI design, landing pages, SaaS scaffolder, analytics, experiment designer, discovery, roadmap communicator, code-to-prd, apple-hig-expert | [product-team/](product-team/) | | **📣 Marketing** | 48 | 8 pods: Content, SEO + AEO (`aeo` — E-E-A-T audit, citation tracking across 5 LLMs) + local (`local-seo-manager` — GBP/NAP/Map-Pack), CRO, Channels, Growth, Intelligence, Sales + context foundation + orchestration router | [marketing-skill/](marketing-skill/) | -| **🚀 Productivity** | 8 | `capture` (brain-dump-to-action), `email` pair (inbox-setup + inbox-triage), `reflect` (journal), `handoff` (Matt Pocock-inspired), `andreessen` (market-first decision mode), `roast` (5-angle idea panel → GO/RESHAPE/KILL), `fable-goal` (ramble → autonomous /goal prompt) | [productivity/](productivity/) | +| **🚀 Productivity** | 11 | `capture` (brain-dump-to-action), `email` pair (inbox-setup + inbox-triage), `reflect` (journal), `handoff` (Matt Pocock-inspired), `andreessen` (market-first decision mode), `roast` (5-angle idea panel → GO/RESHAPE/KILL), `fable-goal` (ramble → autonomous /goal prompt), `weekly-review` (GTD loop with refusal gate), `deep-work` (time-blocking + shallow-work budget), `meetings` (cost gate + agenda + action items) | [productivity/](productivity/) | | **🎨 Marketing (top-level)** | 1 | `landing` — single-file HTML landing-page generator (4 design styles, GSAP patterns, brand palette validator) | [marketing/](marketing/) | | **🔬 Research (academic)** | 9 | `research` orchestrator (hybrid router + fallback) + 8 specialists: `pulse`, `litreview`, `grants` (NIH), `dossier`, `patent`, `syllabus`, `notebooklm`, `deep-research` (rigor-first meta-research) | [research/](research/) | | **🧪 Research Operations** ✨v2.9.0 | 5 | Enterprise/cross-functional research: orchestrator + `clinical-research` (study design), `research-finance` (R&D program finance), `market-research` (sizing/survey/segmentation), `product-research` (user research) — each with onboarding + customization + opt-in autoresearch bridge | [research-ops/](research-ops/) | @@ -354,7 +354,7 @@ Yes. Skills work natively with 13 tools: Claude Code, OpenAI Codex, Gemini CLI, No. We follow semantic versioning and maintain backward compatibility within patch releases. Existing script arguments, plugin source paths, and SKILL.md structures are never changed in patch versions. See the [CHANGELOG](CHANGELOG.md) for details on each release. **Are the Python tools dependency-free?** -Yes. All 635 Python tools use the standard library only — zero pip installs required. Every skill's CLI entry point is verified to run with `--help` (most skills ship one script per tool; a few, like the vendored `engineering/skillopt-sleep` engine, ship a multi-module package behind a single `python -m` entry point). +Yes. All 644 Python tools use the standard library only — zero pip installs required. Every skill's CLI entry point is verified to run with `--help` (most skills ship one script per tool; a few, like the vendored `engineering/skillopt-sleep` engine, ship a multi-module package behind a single `python -m` entry point). **How do I create my own Claude Code skill?** Each skill is a folder with a `SKILL.md` (frontmatter + instructions), optional `scripts/`, `references/`, and `assets/`. See the [Skills & Agents Factory](https://github.com/alirezarezvani/claude-code-skills-agents-factory) for a step-by-step guide. diff --git a/engineering/agent-harness/skills/agent-harness/assets/harnesses/productivity.json b/engineering/agent-harness/skills/agent-harness/assets/harnesses/productivity.json index 2b9426a1..958c5762 100644 --- a/engineering/agent-harness/skills/agent-harness/assets/harnesses/productivity.json +++ b/engineering/agent-harness/skills/agent-harness/assets/harnesses/productivity.json @@ -1,7 +1,7 @@ { "schema": "agent-harness/manifest.v1", "domain": "productivity", - "skill_count": 7, + "skill_count": 11, "loop_defaults": { "max_attempts_per_task": 3, "max_loop_iterations": 12, @@ -154,6 +154,76 @@ "workspace_detection.md" ] }, + { + "name": "deep-work", + "path": "productivity/deep-work/skills/deep-work", + "description": "Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says \"/deep-work\" or \"/time-block\". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target.", + "tools": [ + { + "script": "productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/deep-work/skills/deep-work/scripts/time_block_planner.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/time_block_planner.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/deep-work/skills/deep-work/scripts/time_block_planner.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + } + ], + "agentic_signals": { + "goal_intake": true, + "refusal_gate": true, + "verification": true, + "loop_discipline": true, + "close_out": false + }, + "references": [ + "deep_work_canon.md", + "shallow_work_budget.md", + "time_blocking_method.md" + ] + }, { "name": "inbox-setup", "path": "productivity/email/skills/inbox-setup", @@ -284,6 +354,40 @@ "triage_decision_framework.md" ] }, + { + "name": "fable-goal", + "path": "productivity/fable-goal/skills/fable-goal", + "description": "Convert a rambling description of a desired outcome into one polished, autonomous /goal prompt ready to paste into a fresh session. Use when the user says \"/fable-goal\", \"turn this into a goal prompt\", \"write me a fable prompt\", \"write the prompt that builds X\", or rambles about something they want made and asks for the prompt that makes it happen. The output is a single copy-paste prompt, never the build itself. Do NOT use when the user wants the thing built right now in this session \u2014 only when they want the PROMPT that will make it happen in a fresh session.", + "tools": [ + { + "script": "productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/fable-goal/skills/fable-goal/scripts/goal_prompt_self_check.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + } + ], + "agentic_signals": { + "goal_intake": false, + "refusal_gate": false, + "verification": true, + "loop_discipline": true, + "close_out": true + }, + "references": [ + "goal_prompt_patterns.md" + ] + }, { "name": "handoff", "path": "productivity/handoff/skills/handoff", @@ -424,6 +528,76 @@ "redaction_checklist.md" ] }, + { + "name": "meetings", + "path": "productivity/meetings/skills/meetings", + "description": "Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items \u2014 or says \"should this be a meeting\", \"/cs:meeting-prep\", or \"/cs:meeting-actions\". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan.", + "tools": [ + { + "script": "productivity/meetings/skills/meetings/scripts/action_item_extractor.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/action_item_extractor.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/action_item_extractor.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/meetings/skills/meetings/scripts/agenda_builder.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/agenda_builder.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/agenda_builder.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + } + ], + "agentic_signals": { + "goal_intake": true, + "refusal_gate": true, + "verification": true, + "loop_discipline": true, + "close_out": true + }, + "references": [ + "action_item_discipline.md", + "agenda_discipline.md", + "meeting_cost_canon.md" + ] + }, { "name": "reflect", "path": "productivity/reflect/skills/reflect", @@ -563,6 +737,76 @@ "cheapest_test_canon.md", "verdict_synthesis_method.md" ] + }, + { + "name": "weekly-review", + "path": "productivity/weekly-review/skills/weekly-review", + "description": "Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says \"/cs:weekly-review\". Walks David Allen's three-phase loop \u2014 GET CLEAR, GET CURRENT, GET CREATIVE \u2014 with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100.", + "tools": [ + { + "script": "productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + }, + { + "script": "productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py", + "wired": true, + "supports_sample": true, + "verification": [ + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py --help", + "expect_exit": 0, + "kind": "smoke" + }, + { + "cmd": "python3 productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py --sample", + "expect_exit": 0, + "kind": "sample" + } + ] + } + ], + "agentic_signals": { + "goal_intake": true, + "refusal_gate": true, + "verification": true, + "loop_discipline": true, + "close_out": true + }, + "references": [ + "gtd_weekly_review_canon.md", + "open_loop_psychology.md", + "review_cadence_design.md" + ] } ] } diff --git a/productivity/deep-work/.claude-plugin/plugin.json b/productivity/deep-work/.claude-plugin/plugin.json new file mode 100644 index 00000000..50ebe242 --- /dev/null +++ b/productivity/deep-work/.claude-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "deep-work", + "description": "Plan a deep work day the Cal Newport way: time-block planning + shallow-work budgeting + focus-session tracking. Classifies every task deep vs shallow (recent-graduate heuristic, explicit override wins), audits the shallow share against a 30-50% budget with a WITHIN-BUDGET / OVER-BUDGET verdict, then builds an energy-first time-blocked day — deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling the planner refuses to exceed, shallow work batched into at most two windows, 10-minute attention-residue buffers, a fixed lunch, and a hard stop that never silently extends. A JSON-backed focus-session logger tracks weekly deep hours vs target and consecutive-day streaks, and an end-of-day shutdown ritual closes every open loop. Deterministic stdlib scripts do the arithmetic; refusals (exit 2) name exactly what to cut or defer.", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani", + "url": "https://alirezarezvani.com" + }, + "homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/deep-work", + "repository": "https://github.com/alirezarezvani/claude-skills", + "license": "MIT", + "skills": [ + "./skills/deep-work" + ], + "source": { + "build_pattern": "Path-B method skill — Cal Newport time-block discipline preserved + deterministic scheduling/auditing/logging tooling added", + "distinct_from": "productivity/andreessen (the 3x5 card picks WHAT to do today; deep-work plans WHEN and HOW with attention protected — run the card first, then block the day here); project-management capacity planning (team-level capacity and sprint math; this is one person's attention across one day and one week)" + } +} diff --git a/productivity/deep-work/README.md b/productivity/deep-work/README.md new file mode 100644 index 00000000..6181678d --- /dev/null +++ b/productivity/deep-work/README.md @@ -0,0 +1,62 @@ +# deep-work — Time-Block the Day, Budget the Shallow + +> Plan a deep work day the Cal Newport way. Attention is the protected resource, not time. + +`/deep-work` turns a raw task list into a time-blocked day: every task classified deep vs shallow, +the shallow share audited against a budget, deep blocks of at least 90 minutes placed in the +earliest hours under a hard **4-hour ceiling**, shallow work batched into at most two windows, and +the day closed with a shutdown ritual. A local ledger tracks weekly deep hours and streaks. + +## The discipline + +| Rule | Enforced by | +|---|---| +| Shallow work gets a budget (default 50%) before anything is scheduled | `shallow_work_auditor.py` — OVER-BUDGET exits 2 | +| Deep blocks: ≥90 minutes, earliest hours, energy-first | `time_block_planner.py` | +| Deep demand past 4 hours is refused, naming what to defer | `time_block_planner.py` — exit 2 | +| Shallow work: at most two batches (late morning + end of day) | `time_block_planner.py` | +| 10-minute buffers drain attention residue between blocks | `time_block_planner.py` | +| The hard stop never silently extends — overflow is refused by name | `time_block_planner.py` — exit 2 | +| Deep hours are measured, not felt | `focus_session_logger.py` — status / streak | +| The day ends with a shutdown ritual, not a fade-out | `assets/shutdown_checklist.md` | + +## Quick start + +```bash +# 1. Audit the task list — deep vs shallow, share vs budget +python skills/deep-work/scripts/shallow_work_auditor.py --sample + +# 2. Build the time-blocked day (refuses >4h deep, refuses overflow) +python skills/deep-work/scripts/time_block_planner.py --sample + +# 3. Log real focus sessions; check the weekly target and streak +python skills/deep-work/scripts/focus_session_logger.py log --minutes 90 --label "Write spec" +python skills/deep-work/scripts/focus_session_logger.py status --target 15 +``` + +Or just say **"plan my deep work day: …"**, run `/cs:deep-work` (full workflow), or +`/cs:time-block` (quick plan from a task list). + +## What's in the box + +- **Skill:** [`skills/deep-work/SKILL.md`](skills/deep-work/SKILL.md) +- **Agent:** [`agents/cs-deep-work.md`](agents/cs-deep-work.md) +- **Commands:** [`commands/cs-deep-work.md`](commands/cs-deep-work.md) — `/cs:deep-work`; + [`commands/cs-time-block.md`](commands/cs-time-block.md) — `/cs:time-block` +- **3 stdlib scripts:** `shallow_work_auditor.py`, `time_block_planner.py`, `focus_session_logger.py` +- **3 references** (6-7 sources each: Newport, Leroy, Mark, Ericsson, Csikszentmihalyi, Graham, + Gollwitzer, Eyal, Parkinson, Zeigarnik, Masicampo & Baumeister, Perlow, RescueTime, Atlassian), + **2 assets** (worked example day + shutdown-ritual checklist) + +## Not the same as + +- **`productivity/andreessen`** — the 3x5 card picks WHAT matters today. Deep-work plans WHEN and + HOW, with attention protected. Run the card first, then block the day here. +- **`project-management` capacity planning** — team-level capacity and sprint math. This is one + person's attention across one day and one week. +- **`productivity/reflect`** — end-of-week reflection prose. The shutdown ritual here is a daily, + mechanical close. + +## License + +MIT. diff --git a/productivity/deep-work/agents/cs-deep-work.md b/productivity/deep-work/agents/cs-deep-work.md new file mode 100644 index 00000000..97533fab --- /dev/null +++ b/productivity/deep-work/agents/cs-deep-work.md @@ -0,0 +1,86 @@ +--- +name: cs-deep-work +description: Plans a deep work day the Cal Newport way — audits a task list deep vs shallow against a 30-50% shallow budget, builds an energy-first time-blocked schedule (deep blocks of at least 90 minutes in the earliest hours, a hard 4-hour deep ceiling, shallow work batched into at most two windows, 10-minute buffers, an immovable hard stop), logs focus sessions against a weekly target, and closes the day with a shutdown ritual. Respects the scripts' exit-2 refusals: deep demand past the cap and overflow past the hard stop are named and deferred, never squeezed in. Use to plan or re-plan a focused day, cut shallow work, or track deep-hours streaks. +skills: productivity/deep-work/skills/deep-work +domain: productivity +model: opus +tools: [Read, Bash] +--- + +# Deep Work Agent + +## Purpose + +The `cs-deep-work` agent orchestrates the `deep-work` skill to turn a raw task list into a day +where attention is the protected resource: + +1. **Intake** — collect today's tasks with rough minutes each, plus the day's hard start, hard + stop, and lunch time. Ask one batched round of questions at most; a task list plus "9 to 5" is + enough to proceed. +2. **Audit the shallow** — run `shallow_work_auditor.py` (keyword heuristics; an explicit + `:deep`/`:shallow` suffix always wins). Surface the shallow share vs the budget (default 50%) + and the recent-graduate forcing question for every shallow item. `OVER-BUDGET` (exit 2) means + the user cuts, batches, or delegates *before* any schedule is built. +3. **Block the day** — run `time_block_planner.py` with the surviving tasks: deep blocks ≥90 min + in the earliest hours, 4-hour deep cap, ≤2 shallow batches (late morning + end of day), + 10-minute buffers, fixed lunch. Present the markdown schedule and read it back in plain words. +4. **Handle refusals honestly** — an exit-2 refusal (deep cap exceeded / overflow past the hard + stop) is the product, not an error. Relay exactly what the planner says to cut or defer, help + the user choose, then re-run. Never hand-edit a schedule around a refusal. +5. **Close the loop** — after real focus blocks, log them with `focus_session_logger.py log`; + report `status` (weekly deep hours vs target, default 15) and `streak`. At day's end, walk the + shutdown ritual (`../skills/deep-work/assets/shutdown_checklist.md`) to its closing phrase. + +## Voice + +- Calm and unsentimental about arithmetic. Four hours of deep work is the ceiling, not a challenge. +- Protective of mornings. The best hours go to the hardest work; email does not get 09:00. +- Guilt-free about revision. A broken block means redraw the rest of the day — the plan's value + survives its own destruction. + +## Hard rules + +1. **Audit before schedule.** No time-block plan is built while the shallow share is over budget. +2. **The refusals stand.** Deep demand past 4 hours and overflow past `--end` are deferred by + name, never squeezed, shrunk below 90 minutes, or pushed into the evening. +3. **The hard stop does not move.** Fixed-schedule productivity: the end time is a constraint, + not a suggestion. +4. **Shallow work is batched, never sprinkled.** At most two windows per day. +5. **Measured, not felt.** Weekly deep hours come from the ledger (`status`), never from vibes. + +## Skill Integration + +**Skill Location:** `../skills/deep-work/` + +### Python Scripts (Stdlib) + +1. **Shallow-Work Auditor** — `skills/deep-work/scripts/shallow_work_auditor.py` — deep/shallow + classification + shallow share vs `--budget` → WITHIN-BUDGET / OVER-BUDGET (exit 2) + the + recent-graduate forcing question per shallow item. +2. **Time-Block Planner** — `skills/deep-work/scripts/time_block_planner.py` — energy-first + schedule with the 4-hour deep cap and overflow refusal (both exit 2, both name what to defer). +3. **Focus-Session Logger** — `skills/deep-work/scripts/focus_session_logger.py` — JSON ledger: + `log` / `status` (weekly hours vs target) / `streak`; atomic writes via `os.replace`. + +### Knowledge Bases + +- `skills/deep-work/references/deep_work_canon.md` — deep vs shallow, the deep work hypothesis, the 4-hour ceiling, attention residue (6 sources) +- `skills/deep-work/references/time_blocking_method.md` — plan every minute, block sizes, buffers, guilt-free revision, the hard stop (6+ sources) +- `skills/deep-work/references/shallow_work_budget.md` — the 30-50% band, saying no, batching, why the shutdown ritual works (6 sources) + +## Differentiates From Siblings + +- **vs `cs-andreessen`** (productivity): the 3x5 card picks WHAT matters today; deep-work plans + WHEN and HOW with attention protected. Run the card first, then block the day here. +- **vs `project-management` capacity planning**: team-level capacity and sprint math; this is one + person's attention across one day and one week. +- **vs `productivity/reflect`**: end-of-week reflection prose; the shutdown ritual here is a + daily, mechanical close. + +## Related Agents + +- [cs-andreessen](../../andreessen/agents/cs-andreessen.md) — productivity sibling; picks the day's 3-5 priorities before this agent blocks them + +--- + +**Version:** 1.0.0 diff --git a/productivity/deep-work/commands/cs-deep-work.md b/productivity/deep-work/commands/cs-deep-work.md new file mode 100644 index 00000000..1aa3044a --- /dev/null +++ b/productivity/deep-work/commands/cs-deep-work.md @@ -0,0 +1,94 @@ +--- +name: "cs-deep-work" +description: "/cs:deep-work — Plan a deep work day the Cal Newport way: audit the task list deep vs shallow against a budget, build an energy-first time-blocked schedule with a hard 4-hour deep ceiling and an immovable hard stop, then log focus sessions and close with the shutdown ritual." +argument-hint: "[today's task list, with rough minutes per task]" +--- + +# /cs:deep-work — Audit the Shallow, Block the Day, Bank the Hours + +**Command:** `/cs:deep-work [today's task list]` + +A calendar full of reactions is not a plan. `/cs:deep-work` runs the full attention-first +workflow: classify every task deep vs shallow, audit the shallow share against a budget, build a +time-blocked day where deep work owns the earliest hours, and close the loop with a focus-session +ledger and a shutdown ritual. + +## When to Run + +- "Plan my deep work day" / "time-block my day" / "protect my focus hours" +- The task list is drowning in email, meetings, and admin and you want the honest split +- You keep "working all day" and shipping nothing hard — depth is unmeasured +- Start of day (plan), mid-day after the plan broke (re-plan), end of day (log + shutdown) + +## When NOT to Run + +- You need to pick WHAT matters today → run `/cs:andreessen` (3x5 card) first, then come back +- Team-level capacity or sprint math → `project-management` skills, not personal attention +- You just want a quick schedule from a ready task list with no audit → `/cs:time-block` + +## What You Get + +1. **A shallow-work audit** — every task classified DEEP/SHALLOW with the basis shown, the shallow + share vs your budget (default 50%), a WITHIN-BUDGET / OVER-BUDGET verdict, and the + recent-graduate forcing question for every shallow item. +2. **A time-blocked day** — deep blocks ≥90 min in the earliest hours (capped at 4 hours), shallow + work in at most two batches, 10-minute buffers, fixed lunch, hard stop. Refusals name exactly + what to cut or defer. +3. **A focus ledger** — sessions logged, weekly deep hours vs target (default 15), streak count. +4. **A shutdown ritual** — open loops captured, tomorrow's first block chosen, "shutdown complete." + +## Trigger Phrases (auto-invoke without /cs:) + +- "plan my deep work day" / "deep work plan" +- "time-block my day" / "time block my calendar" +- "how much of my day is shallow work" +- "protect my focus time" / "I need focus hours" + +## Discipline + +- **Audit before schedule** — an OVER-BUDGET day gets cut, batched, or delegated first. +- **The refusals stand** — >4h deep demand and overflow past the hard stop are deferred by name, + never squeezed in or pushed into the evening. +- **The hard stop does not move** — fixed-schedule productivity. +- **Batch, never sprinkle** — shallow work lives in at most two windows. +- **Revise, don't abandon** — when a block breaks, re-run the planner from the current time. +- **Measured, not felt** — the weekly target is checked against the ledger, not memory. + +## Workflow + +```bash +# 1. Audit the task list — deep vs shallow, share vs budget (OVER-BUDGET exits 2) +python ../skills/deep-work/scripts/shallow_work_auditor.py \ + --task "Write investor update:60" --task "Email triage:45" \ + --task "Analyze churn cohort:90:deep" --budget 50 + +# 2. Build the time-blocked day (deep-cap and overflow refusals exit 2, naming deferrals) +python ../skills/deep-work/scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \ + --task "Write investor update:90:deep" --task "Analyze churn cohort:90:deep" \ + --task "Email triage:45:shallow" + +# 3. After each real focus block, log it; check the week and the streak +python ../skills/deep-work/scripts/focus_session_logger.py log --minutes 90 --label "Investor update" +python ../skills/deep-work/scripts/focus_session_logger.py status --target 15 +python ../skills/deep-work/scripts/focus_session_logger.py streak + +# 4. End of day: walk ../skills/deep-work/assets/shutdown_checklist.md to "shutdown complete" +``` + +## Stop Conditions + +- Plan emitted + user accepts the blocks → done; return at day's end for log + shutdown. +- Planner refuses (exit 2) → user picks what to defer from the named candidates, re-run once; if + it refuses again, the day is overcommitted — cut scope, don't fight the arithmetic. +- User says "stop" → drop it; the ledger keeps whatever was already logged. + +## Related + +- Agent: [`cs-deep-work`](../agents/cs-deep-work.md) +- Skill: [`deep-work`](../skills/deep-work/SKILL.md) +- Quick variant: [`/cs:time-block`](cs-time-block.md) — schedule only, no audit +- Siblings: `/cs:andreessen` (picks WHAT today; run before this), `/cs:reflect` (weekly reflection) + +--- + +**Version:** 1.0.0 diff --git a/productivity/deep-work/commands/cs-time-block.md b/productivity/deep-work/commands/cs-time-block.md new file mode 100644 index 00000000..9ad3605d --- /dev/null +++ b/productivity/deep-work/commands/cs-time-block.md @@ -0,0 +1,80 @@ +--- +name: "cs-time-block" +description: "/cs:time-block — Build today's time-block plan from a task list, fast: deep blocks of at least 90 minutes in the earliest hours under a hard 4-hour ceiling, shallow work batched into at most two windows, 10-minute buffers, fixed lunch, immovable hard stop. The quick variant of /cs:deep-work — schedule only, no audit, no ledger." +argument-hint: "[task list with minutes, plus start/end times]" +--- + +# /cs:time-block — Just Build Today's Blocks + +**Command:** `/cs:time-block [task list + start/end]` + +The quick variant of `/cs:deep-work`: skip the shallow audit and the ledger, take a ready task +list, and emit the time-blocked day. Same arithmetic, same refusals — deep work first and +earliest, capped at 4 hours; shallow work batched; the hard stop does not move. + +## When to Run + +- "Time-block my day" with a task list already in hand +- Mid-day re-plan after a block broke — feed the surviving tasks and the current time as `--start` +- You already know what's deep and what's shallow and just need the schedule + +## When NOT to Run + +- The task list hasn't been triaged — shallow work will eat the plan → run `/cs:deep-work` (it + audits first) +- You need to pick WHAT matters today → `/cs:andreessen` (3x5 card) +- Team capacity or sprint planning → `project-management` skills + +## What You Get + +A markdown schedule table from hard start to hard stop with **no unassigned minutes**: deep blocks +(≥90 min, earliest hours), at most two shallow batches (late morning + end of day), 10-minute +buffers, optional fixed 30-minute lunch, and named flex blocks that absorb what the plan didn't +foresee. Or a refusal (exit 2) that names exactly what to cut or defer — which is the plan working, +not failing. + +## Trigger Phrases (auto-invoke without /cs:) + +- "time-block my day" / "build my time blocks" +- "block out my calendar for today" +- "re-plan the rest of my day" + +## Discipline + +- **Every task needs minutes and a mode** — `"name:minutes:deep|shallow"`. If the user doesn't + know a task's mode, that's the tell to run `/cs:deep-work` instead. +- **Deep demand past 4 hours is deferred by name** — never shrunk below 90 minutes or squeezed. +- **Overflow past `--end` is deferred by name** — the day never silently extends. +- **Revision is normal** — a broken day is re-planned from the current time, same rules. + +## Workflow + +```bash +# Build the day (markdown table; add --json for machine-readable output) +python ../skills/deep-work/scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \ + --task "Write product spec:120:deep" \ + --task "Design onboarding flow:90:deep" \ + --task "Email sweep:30:shallow" \ + --task "Expense report:15:shallow" + +# Mid-day re-plan: surviving tasks, current time as --start, same hard stop +python ../skills/deep-work/scripts/time_block_planner.py --start 13:00 --end 17:00 \ + --task "Finish product spec:90:deep" --task "Email sweep:30:shallow" +``` + +## Stop Conditions + +- Schedule emitted and accepted → done. +- Refusal (exit 2) → user picks a deferral from the named candidates, re-run once; still refusing + means the day is overcommitted — cut scope. +- User says "stop" → drop it. + +## Related + +- Full workflow: [`/cs:deep-work`](cs-deep-work.md) — audit + plan + ledger + shutdown +- Agent: [`cs-deep-work`](../agents/cs-deep-work.md) +- Skill: [`deep-work`](../skills/deep-work/SKILL.md) + +--- + +**Version:** 1.0.0 diff --git a/productivity/deep-work/skills/deep-work/SKILL.md b/productivity/deep-work/skills/deep-work/SKILL.md new file mode 100644 index 00000000..8861987a --- /dev/null +++ b/productivity/deep-work/skills/deep-work/SKILL.md @@ -0,0 +1,100 @@ +--- +name: deep-work +description: Use when someone wants to plan a deep work day, time-block their calendar or task list, budget or cut shallow work, protect focus hours, track deep-work sessions and streaks, run an end-of-day shutdown ritual, or says "/deep-work" or "/time-block". Classifies tasks deep vs shallow, builds an energy-first time-blocked schedule that refuses deep demand past the 4-hour ceiling, batches shallow work into at most two windows, and logs focus sessions against a weekly target. +argument-hint: "[today's task list]" +license: MIT +metadata: + version: 1.0.0 + build_pattern: "Path-B method skill — Cal Newport time-block discipline preserved + deterministic scheduling scripts added" + distinct_from: "andreessen 3x5 card (picks WHAT to do today; deep-work plans WHEN and HOW with attention protected); project-management capacity planning (team capacity; this is one person's attention)" +--- + +# Deep Work — Time-Block the Day, Budget the Shallow + +> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls in scripts. Works in Claude Code CLI and Claude.ai web. The scripts fix the arithmetic; you keep the judgment. + +## What this does + +A calendar full of reactions is not a plan. This skill turns a raw task list into a day where +attention is the protected resource: deep tasks get the earliest hours in blocks of at least 90 +minutes, shallow work is batched into at most two windows, buffers absorb attention residue, and the +schedule flatly refuses more than 4 hours of deep demand — the trained daily ceiling. A local ledger +of focus sessions keeps the weekly deep-hours target measured, not felt. + +## Step 1 — Classify and budget the shallow + +Ask for today's task list with rough minutes per task (or take it from `$ARGUMENTS`). The auditor +classifies each task deep vs shallow (keyword heuristics; an explicit `:deep`/`:shallow` suffix always +wins), computes the shallow share against the budget, and prints the forcing question for every +shallow item — *how long would it take to train a smart recent graduate to do this?* + +```bash +python scripts/shallow_work_auditor.py \ + --task "Write investor update:60" --task "Email triage:45" \ + --task "Analyze churn cohort:90:deep" --budget 50 +``` + +`OVER-BUDGET` (exit 2) means cut, batch, or delegate before any schedule is built. + +## Step 2 — Block the day + +Feed the surviving tasks to the planner with the day's hard start and hard end (fixed-schedule +productivity: the end time does not move). Deep first and earliest, 10-minute buffers, shallow in +two batches (late morning + end of day), an optional fixed lunch: + +```bash +python scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \ + --task "Write product spec:120:deep" --task "Email sweep:30:shallow" +``` + +Two refusals, both exit 2: deep demand past the 4-hour cap (the planner names what to defer), and shallow overflow past `--end` (the planner names what to drop — the day never silently extends). + +## Step 3 — Log the session, keep the streak + +After each real focus block, log it. `status` shows this week's deep hours against the target (default 15); `streak` counts consecutive days with at least one session: + +```bash +python scripts/focus_session_logger.py log --minutes 90 --label "Write product spec" +python scripts/focus_session_logger.py status --target 15 +``` + +## Step 4 — Shutdown ritual + +End the day with the shutdown checklist (`assets/shutdown_checklist.md`): capture every open loop, glance at tomorrow, say the closing phrase. An incompletely closed day steals tomorrow's first block. + +## Scripts + +| Script | Role | +|---|---| +| `scripts/shallow_work_auditor.py` | Deep/shallow classification + shallow share vs budget → WITHIN-BUDGET / OVER-BUDGET (exit 2) + the recent-graduate forcing question per shallow item. | +| `scripts/time_block_planner.py` | Energy-first schedule: deep blocks ≥90 min earliest, 4-hour deep cap (refuses, exit 2), ≤2 shallow batches, 10-min buffers, fixed lunch, overflow refusal. | +| `scripts/focus_session_logger.py` | JSON ledger of focus sessions: `log` / `status` (weekly hours vs target) / `streak`; atomic writes. | + +## References + +- [`references/deep_work_canon.md`](references/deep_work_canon.md) — deep vs shallow, the deep work hypothesis, the 4-hour ceiling, attention residue (6 sources) +- [`references/time_blocking_method.md`](references/time_blocking_method.md) — plan every minute, block sizes, buffers, guilt-free revision, fixed-schedule productivity (6 sources) +- [`references/shallow_work_budget.md`](references/shallow_work_budget.md) — the 30-50% band, saying no, batching, the recent-graduate heuristic, why the shutdown ritual works (6 sources) + +## Assets + +- [`assets/example_time_block_plan.md`](assets/example_time_block_plan.md) — a full worked day (audit → plan → mid-day revision → shutdown) +- [`assets/shutdown_checklist.md`](assets/shutdown_checklist.md) — end-of-day shutdown ritual template + +## Rules + +- **Depth first, earliest.** Deep blocks take the best hours; shallow work gets what is left, never the reverse. +- **Respect the refusals.** More than 4 deep hours is fake depth; overflow past the hard stop is a broken budget, not extra output. +- **Batch, never sprinkle.** Shallow work lives in at most two windows; a sprinkled inbox costs a full refocus each time. +- **Revise, don't abandon.** A broken block means redraw the rest of the day, not "the plan failed." +- **Close the day.** No shutdown ritual, no evening — open loops steal tomorrow's first block. + +## Distinct From (don't reach for the wrong skill) + +- **`productivity/andreessen`** — the 3x5 card picks WHAT matters today. Deep-work plans WHEN and HOW, with attention protected. Run the card first, then block the day here. +- **`project-management` capacity planning** — team-level capacity and sprint math. This is one person's attention across one day and one week. + +--- + +**Version:** 1.0.0 +**Build pattern:** Path-B method skill — Newport discipline preserved + deterministic scheduling scripts added. diff --git a/productivity/deep-work/skills/deep-work/assets/example_time_block_plan.md b/productivity/deep-work/skills/deep-work/assets/example_time_block_plan.md new file mode 100644 index 00000000..a10881ac --- /dev/null +++ b/productivity/deep-work/skills/deep-work/assets/example_time_block_plan.md @@ -0,0 +1,93 @@ +# Worked Example — A Full Deep-Work Day (Audit → Plan → Mid-Day Revision → Shutdown) + +> A complete pass through the `deep-work` skill for one realistic day, showing the exact script +> invocations and their outputs, including the mid-day revision that every real day eventually needs. + +## The raw task list (as the user gave it) + +- Write the Q3 product spec — ~2h, the week's most important output +- Design the onboarding flow — ~1.5h +- Email sweep — 30 min +- Team status update — 20 min +- Expense report — 15 min + +Day: 08:30 hard start, 17:00 hard stop, lunch 12:30. + +## Step 1 — Shallow audit + +```bash +python scripts/shallow_work_auditor.py \ + --task "Write Q3 product spec:120" \ + --task "Design onboarding flow:90" \ + --task "Email sweep:30" \ + --task "Team status update:20" \ + --task "Expense report:15" \ + --budget 50 +``` + +Result: `Write Q3 product spec` and `Design onboarding flow` classify DEEP (write/design signals); +the other three classify SHALLOW (email/status/expense signals). Shallow share = 65 of 275 min = +**23.6% vs a 50% budget → WITHIN-BUDGET (exit 0)**. The forcing question printed for each shallow +item confirmed none of them deserves a promotion to deep. Proceed. + +## Step 2 — The plan + +```bash +python scripts/time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \ + --task "Write Q3 product spec:120:deep" \ + --task "Design onboarding flow:90:deep" \ + --task "Email sweep:30:shallow" \ + --task "Team status update:20:shallow" \ + --task "Expense report:15:shallow" +``` + +| Start | End | Block | Mode | +|-------|-----|-------|------| +| 08:30 | 10:30 | DEEP — Write Q3 product spec | DEEP | +| 10:30 | 10:40 | Buffer — stand up, reset | BUFFER | +| 10:40 | 12:10 | DEEP — Design onboarding flow | DEEP | +| 12:10 | 12:20 | Buffer — stand up, reset | BUFFER | +| 12:20 | 12:30 | Flex — reset, no inputs | FLEX | +| 12:30 | 13:00 | Lunch — away from the desk | BREAK | +| 13:00 | 13:50 | SHALLOW batch (late morning) — Email sweep · Team status update | SHALLOW | +| 13:50 | 16:45 | Flex — overflow absorber | FLEX | +| 16:45 | 17:00 | SHALLOW batch (end of day) — Expense report | SHALLOW | + +Deep 3h30 / 4h cap · Shallow 1h05 · Flex 3h05 · Buffers 20min + +Reading the plan: both deep blocks own the morning — the best hours — before the manager-schedule +world wakes up. Shallow work exists in exactly two windows. The big afternoon flex block is not +waste; it is where meetings, overruns, and surprises land without touching the deep blocks. + +## The revision (because the day broke, as days do) + +At 10:15 an incident call ate the rest of the spec block. The rule: **revise, don't abandon.** +At 11:00, re-plan the remaining day with what actually survives: + +```bash +python scripts/time_block_planner.py --start 11:00 --end 17:00 --lunch 12:30 \ + --task "Finish Q3 product spec:90:deep" \ + --task "Email sweep:30:shallow" \ + --task "Team status update:20:shallow" \ + --task "Expense report:15:shallow" +``` + +The onboarding-flow design moved to tomorrow's first block — named and deferred, not silently +squeezed into the evening. The redrawn day still has one protected 90-minute deep block, and the +hard stop did not move. + +## Step 3 — Log what actually happened + +```bash +python scripts/focus_session_logger.py log --minutes 75 --label "Q3 product spec (pre-incident)" +python scripts/focus_session_logger.py log --minutes 90 --label "Q3 product spec (finish)" +python scripts/focus_session_logger.py status --target 15 +``` + +2h45 banked today. The week's ledger, not memory, decides whether the 15-hour target is on track. + +## Step 4 — Shutdown + +Run `assets/shutdown_checklist.md`: the deferred design block is already on tomorrow's plan, the +incident follow-up is captured as a task, tomorrow's first block is confirmed. **"Shutdown +complete."** No work thoughts after the phrase. diff --git a/productivity/deep-work/skills/deep-work/assets/shutdown_checklist.md b/productivity/deep-work/skills/deep-work/assets/shutdown_checklist.md new file mode 100644 index 00000000..8d7a7dac --- /dev/null +++ b/productivity/deep-work/skills/deep-work/assets/shutdown_checklist.md @@ -0,0 +1,51 @@ +# End-of-Day Shutdown Ritual — Checklist Template + +> Run this at the hard stop, every working day. The ritual exists because unfinished tasks keep +> firing in memory until they either finish or get a concrete plan — and a plan is enough +> (Zeigarnik; Masicampo & Baumeister). Ten minutes here buys a recovered evening and protects +> tomorrow's first deep block. Copy this file into your notes system, or fill it in place. + +**Date:** ____________ **Hard stop:** ____________ + +## 1. Capture every open loop + +Sweep every place a commitment can hide. Nothing stays only in your head. + +- [ ] Inbox — no unread message that represents an action I haven't captured +- [ ] Chat (Slack/Teams/etc.) — same standard as email +- [ ] Notes and scratch files from today — actions extracted +- [ ] The day's time-block plan — every unfinished block accounted for + +Open loops captured (task + where it now lives): + +| Open loop | Captured to (tomorrow's list / calendar / project plan) | +|---|---| +| | | +| | | +| | | + +## 2. Give every incomplete a plan + +For each item above: either it has a **time block tomorrow**, a **calendar date**, or an explicit +**deferred / dropped** decision. "I'll get to it" is not a plan — a plan names when. + +- [ ] Every incomplete has a when, or a conscious "not doing this" + +## 3. Glance at tomorrow + +- [ ] Calendar checked — no surprise that should reshape tomorrow's blocks +- [ ] Tomorrow's **first deep block** is chosen and written down: + + **Tomorrow, first block:** ____________________________________________ + +## 4. Bank today's evidence + +- [ ] Focus sessions logged (`focus_session_logger.py log ...`) +- [ ] Weekly status glanced at (`focus_session_logger.py status`) — hours vs target, streak intact + +## 5. Close + +- [ ] Say the closing phrase, out loud or written: **"Shutdown complete."** + +After the phrase: no email, no chat, no "quick checks." If a work thought surfaces tonight, the +answer is already true — *it's captured, it has a plan, shutdown is complete.* diff --git a/productivity/deep-work/skills/deep-work/references/deep_work_canon.md b/productivity/deep-work/skills/deep-work/references/deep_work_canon.md new file mode 100644 index 00000000..139acce0 --- /dev/null +++ b/productivity/deep-work/skills/deep-work/references/deep_work_canon.md @@ -0,0 +1,93 @@ +# The Deep Work Canon — Deep vs Shallow, the Hypothesis, the Ceiling, the Residue + +> Reference for the `deep-work` skill. This is the conceptual spine behind all three scripts: +> why deep and shallow are different kinds of work, why depth is scarce and valuable, why the +> planner caps deep time at 4 hours, and why buffers between blocks are not optional. + +## 1. The deep/shallow distinction + +Cal Newport defines the two modes precisely (*Deep Work*, 2016): + +- **Deep work** — "professional activities performed in a state of distraction-free concentration + that push your cognitive capabilities to their limit. These efforts create new value, improve + your skill, and are hard to replicate." +- **Shallow work** — "noncognitively demanding, logistical-style tasks, often performed while + distracted. These efforts tend to not create much new value in the world and are easy to + replicate." + +The operational test Newport supplies — and the one `shallow_work_auditor.py` prints for every +shallow item — is the **recent-graduate heuristic**: *how long would it take to train a smart +recent college graduate with no specialized training to do this task?* Months or years of training +means the task is probably deep. Days or weeks means it is shallow, however urgent it feels. + +The auditor's keyword heuristic (email, slack, status, meeting, expense, scheduling, admin → +shallow; write, design, code, build, research, analyze, study → deep) is a first-pass proxy for +this test — which is why the explicit `:deep`/`:shallow` override always wins. The heuristic +starts the conversation; the recent-graduate question settles it. + +## 2. The deep work hypothesis + +Newport's central economic claim: **the ability to perform deep work is becoming increasingly rare +at exactly the same time it is becoming increasingly valuable.** As machines and markets absorb +routine cognitive work, the two abilities that compound are (a) quickly mastering hard things and +(b) producing at an elite level of quality and speed — and both are functions of depth. The people +who cultivate deep work "will thrive"; those who default to reactive shallowness compete in the +most crowded segment of the labor market. + +This is why the skill treats attention — not time — as the protected resource. Eight hours of +fragmented availability produce less durable value than three well-defended deep hours. + +## 3. The 4-hour trained limit + +The planner's hard cap (`DEEP_CAP_MIN = 240`) comes from the deliberate-practice literature that +Newport builds on. Ericsson, Krampe, and Tesch-Römer's landmark study of expert violinists (1993) +found that even elite performers sustain roughly **four hours of deliberate, maximally effortful +practice per day** — typically in sessions of about 60-90 minutes with rest between — before the +quality of the work collapses. Newport translates this directly: "for someone new to such practice, +an hour a day is a reasonable limit. For experts, four hours — but rarely more." + +Two consequences are encoded in `time_block_planner.py`: + +1. Scheduling more than 4 hours of deep blocks is refused (exit 2), not warned about. Depth past + the ceiling is fake depth — the block is on the calendar but the concentration is not in it. +2. The minimum deep block is 90 minutes. Csikszentmihalyi's flow research shows that full + absorption takes time to enter and rewards long uninterrupted stretches; blocks shorter than + about 90 minutes spend most of their length ramping up rather than producing. + +## 4. Attention residue — why buffers and batching exist + +Sophie Leroy (2009) named the mechanism that fragmented schedules quietly bleed: **attention +residue**. When a person switches from Task A to Task B, part of their attention remains stuck on +A — especially when A was unfinished or left under time pressure — and performance on B measurably +degrades. People who worked on a single task without switching significantly outperformed those +who hopped, even when total time was equal. + +Gloria Mark's field studies of information workers quantify the recovery cost: after a significant +interruption, it takes on the order of **23 minutes** to fully re-engage with the original task, +and interrupted workers compensate by working faster at the price of more stress and frustration. + +The planner encodes both findings: + +- **10-minute buffers** between consecutive work blocks give residue somewhere to drain — stand + up, reset, close the loop — instead of carrying it into the next block. +- **Shallow work is batched into at most two windows.** Every sprinkle of "just one email" between + deep tasks is a residue event plus a ~23-minute refocus tax. Two batches means at most two + context-switch penalties per day instead of a dozen. + +## 5. Maker's schedule — why deep blocks go earliest + +Paul Graham's essay "Maker's Schedule, Manager's Schedule" (2009) explains why a single meeting +can destroy an afternoon: makers operate in units of half a day at minimum, because "a single +meeting can blow a whole afternoon, by breaking it into two pieces each too small to do anything +hard in." The planner therefore front-loads deep blocks into the earliest hours — before the +manager-schedule world wakes up and starts fragmenting the calendar — and pushes shallow batches +to late morning and end of day, where fragmentation does the least damage. + +## Sources + +1. Cal Newport, *Deep Work: Rules for Focused Success in a Distracted World* (Grand Central, 2016) — the deep/shallow definitions, the deep work hypothesis, the recent-graduate heuristic, the 4-hour claim. +2. Sophie Leroy, "Why is it so hard to do my work? The challenge of attention residue when switching between work tasks," *Organizational Behavior and Human Decision Processes* 109(2), 2009. +3. Gloria Mark, Daniela Gudith, and Ulrich Klocke, "The Cost of Interrupted Work: More Speed and Stress," *Proceedings of CHI 2008* — the ~23-minute refocus finding popularized from Mark's research program. +4. K. Anders Ericsson, Ralf Th. Krampe, and Clemens Tesch-Römer, "The Role of Deliberate Practice in the Acquisition of Expert Performance," *Psychological Review* 100(3), 1993 — the ~4-hour daily limit on deliberate practice. +5. Mihaly Csikszentmihalyi, *Flow: The Psychology of Optimal Experience* (Harper & Row, 1990) — absorption, the cost of entry into flow, why long blocks outperform fragments. +6. Paul Graham, "Maker's Schedule, Manager's Schedule" (paulgraham.com, 2009) — half-day units of maker time; why meetings fragment afternoons. diff --git a/productivity/deep-work/skills/deep-work/references/shallow_work_budget.md b/productivity/deep-work/skills/deep-work/references/shallow_work_budget.md new file mode 100644 index 00000000..01f7314b --- /dev/null +++ b/productivity/deep-work/skills/deep-work/references/shallow_work_budget.md @@ -0,0 +1,92 @@ +# The Shallow-Work Budget — The 30-50% Band, Saying No, and the Shutdown Ritual + +> Reference for `shallow_work_auditor.py` and the Step-4 shutdown ritual. Why shallow work gets +> an explicit percentage budget, how to enforce it (batching, refusal), and why the day must be +> closed with a ritual rather than simply stopped. + +## 1. The 30-50% band + +Newport's prescription in *Deep Work* (Rule #4, "Drain the Shallows") is to confront shallow work +quantitatively: decide, ideally with whoever you answer to, **what fraction of your time should go +to shallow work — and hold the line**. His reported experience: for almost anyone in a +non-entry-level knowledge role, the honest answer lands in the **30-50% band**. Below 30% is +usually fantasy (organizations run on some coordination overhead); consistently above 50% means +the role is being consumed by work "easy to replicate" and the deep output that justifies the +seat is quietly starving. + +The auditor's `--budget` default of 50 is deliberately the *top* of the band — the most permissive +defensible line. `OVER-BUDGET` (exit 2) at 50% is not a style warning; it means the day as listed +cannot contain the deep work that matters, so something must be cut, batched, or delegated +*before* a schedule is built. Budgeting after scheduling is how shallow work wins. + +## 2. The recent-graduate heuristic + +Classifying honestly is the hard part — everything feels important to the person doing it. Newport's +forcing question, printed by the auditor for every shallow item: + +> **"How long would it take (in months) to train a smart recent college graduate with no +> specialized training in my field to complete this task?"** + +Months of training → the task leverages hard-won expertise → probably deep. Days or weeks → the +task is shallow *regardless of how urgent, social, or visible it is*. The question works because +it strips away urgency and identity and asks only about the skill embedded in the work. + +## 3. What time audits actually find + +Self-estimates of deep time are systematically inflated, which is why the auditor computes the +share from declared minutes instead of asking for a feeling: + +- **RescueTime's** analyses of anonymized knowledge-worker data found users averaging only about + **2 hours 48 minutes of productive device time per day**, checking email or IM roughly every + 6 minutes, and getting long uninterrupted focus stretches rarely — most days contained no block + of more than an hour without a communication check. +- **Atlassian's** workplace research aggregates put the average worker at **~62 meetings a month**, + with half of surveyed workers rating many of them unnecessary, and inbox/context-switch overhead + consuming a large share of nominal working hours. + +The pattern across both: unbudgeted shallow work does not stay small; it metastasizes to fill +whatever attention is left unclaimed. Hence: budget first, then schedule. + +## 4. Saying no and batching — enforcing the budget + +Three enforcement moves, in order of leverage: + +1. **Refuse.** The budget gives the refusal a number: "that puts me over my shallow budget this + week" is a policy, not a mood. Newport pairs this with fixed-schedule productivity — a hard + stop makes every yes visibly displace something. +2. **Batch.** Shallow work that survives triage is consolidated into at most two windows (the + planner enforces this), converting many context switches into two. Leslie Perlow's field + experiments — from "quiet time" studies with software engineers to the **predictable time off** + program run with Boston Consulting Group teams (*HBR*, "Making Time Off Predictable — and + Required," 2009) — showed that even elite always-on teams could carve out protected, + communication-free periods and see collaboration, satisfaction, and work quality *improve*. + Protection works when it is scheduled and collectively respected, not heroic. +3. **Delegate or automate.** Anything a recent graduate could learn in days is a candidate. + +## 5. The shutdown ritual — why closing loops works + +Newport's rule: end every workday with a **shutdown ritual** — review every open loop, capture it +into a trusted plan (today's incompletes get a home on tomorrow's list or the calendar), glance at +the next day, and mark completion with a fixed phrase ("shutdown complete"). No work thoughts +after the phrase. + +The mechanism is Zeigarnik's classic finding (1927) that **interrupted and unfinished tasks +intrude on memory far more than finished ones** — open loops keep firing. Masicampo and +Baumeister's follow-up (2011) supplies the crucial refinement: unfulfilled goals stop intruding +**not only when completed, but as soon as a specific plan for them is made**. The shutdown ritual +is that plan-making, done in batch at day's end: it purchases a genuinely recovered evening, and +recovery is what refills the capacity the 4-hour ceiling spends. Skipping the ritual leaks +rumination into the evening and taxes tomorrow's first — best — deep block. + +The `assets/shutdown_checklist.md` template operationalizes exactly this sequence, and +`focus_session_logger.py status` gives the ritual its review step: hours banked this week vs the +target, and the streak worth protecting. + +## Sources + +1. Cal Newport, *Deep Work*, Rule #4 "Drain the Shallows" (Grand Central, 2016) — the shallow-work budget, the 30-50% band, the recent-graduate question, and the shutdown ritual ("shutdown complete"). +2. Bluma Zeigarnik, "Über das Behalten von erledigten und unerledigten Handlungen" ("On Finished and Unfinished Tasks"), *Psychologische Forschung* 9, 1927 — unfinished tasks intrude on memory. +3. E. J. Masicampo and Roy F. Baumeister, "Consider It Done! Plan Making Can Eliminate the Cognitive Effects of Unfulfilled Goals," *Journal of Personality and Social Psychology* 101(4), 2011 — making a plan quiets the loop without finishing the task. +4. RescueTime research reports on knowledge-worker attention (rescuetime.com/blog) — ~2h48m of daily productive device time; communication checks about every 6 minutes; rarity of hour-plus focus stretches. +5. Leslie A. Perlow and Jessica L. Porter, "Making Time Off Predictable — and Required," *Harvard Business Review*, October 2009 (and Perlow, *Sleeping with Your Smartphone*, HBR Press, 2012) — predictable, protected off-time improves both wellbeing and output. +6. Atlassian, "You Waste a Lot of Time at Work" workplace research (atlassian.com/time-wasting-at-work-infographic) — meeting load (~62/month) and the cost of unnecessary coordination overhead. diff --git a/productivity/deep-work/skills/deep-work/references/time_blocking_method.md b/productivity/deep-work/skills/deep-work/references/time_blocking_method.md new file mode 100644 index 00000000..aa0c8a22 --- /dev/null +++ b/productivity/deep-work/skills/deep-work/references/time_blocking_method.md @@ -0,0 +1,88 @@ +# The Time-Blocking Method — Plan Every Minute, Revise Without Guilt + +> Reference for `time_block_planner.py`. The mechanics of turning a task list into a +> time-blocked day: why every minute gets a job, how big blocks should be, why buffers and +> revision are part of the method (not failures of it), and why the day needs a hard stop. + +## 1. Plan every minute + +Newport's core scheduling rule: **give every minute of the workday a job.** At the start of the +day (or the evening before), divide the hours into blocks and assign each block to an activity — +including lunch, buffers, and an explicit "flex" block. The point is not rigidity; it is that an +unassigned minute defaults to the path of least resistance, which is almost always shallow: +inboxes, feeds, "quick checks." + +Newport's own estimate from years of running this system: a time-blocked hour is worth roughly +**1.5-2x** an unstructured hour of the same nominal work, because decisions about *what to do +next* are made once, in batch, instead of continuously under depletion. + +The planner enforces this literally: its output timeline is gap-free from `--start` to `--end`. +Anything not claimed by a deep block, shallow batch, buffer, or lunch becomes a named **flex +block** — scheduled slack, not accidental drift. + +## 2. Block sizes + +- **Deep blocks: 90 minutes minimum.** Entry into real concentration is slow (see the canon + reference on flow and attention residue); a 30-minute "deep" block spends most of itself + ramping. The planner widens any deep task below 90 minutes up to the floor and says so. +- **Shallow batches: consolidated, not sprinkled.** Shallow tasks are individually small; their + cost is the switch, not the task. Batching them into one late-morning and one end-of-day window + converts a dozen context switches into two. +- **Buffers: 10 minutes between work blocks.** Attention residue needs somewhere to drain. + Buffers are also where the tiny physical resets live — stand up, water, close the previous + block's tabs. + +## 3. Implementation intentions — why written blocks beat willpower + +The psychology underneath time-blocking is Gollwitzer's research on **implementation intentions** +(1999): plans of the form "at time X in situation Y, I will do Z" dramatically outperform bare +goal intentions ("I'll write the spec this week"). Meta-analytic effect sizes are medium-to-large +(d ≈ .65 across ~94 studies in Gollwitzer & Sheeran's later review). A time-block is exactly an +implementation intention: the when and where are pre-decided, so the depleted 2 p.m. self doesn't +renegotiate the plan — it just follows it. + +Nir Eyal (*Indistractable*) builds his "timeboxing" chapter on the same foundation and adds the +inversion that matters for auditing: **"you can't call something a distraction unless you know +what it distracted you from."** Without a time-blocked plan there is no such thing as +off-plan behavior — which is why the planner runs *after* the shallow audit has decided what +deserves time at all. + +## 4. Rescheduling without guilt + +Every practitioner's day breaks by mid-morning. Newport's rule (*The Time-Block Planner*): the +plan's value survives its own destruction. When a block is blown up — an incident, an overrun, a +surprise call — you do not abandon the system; you **redraw the remaining blocks at the next free +moment**. His planner pages literally provide columns for multiple revisions of the same day. The +goal, in his words, is not to win a fight against a changing schedule; it is "to maintain, at all +times, a thoughtful say in what you're doing with your time going forward." + +Two disciplines follow: + +1. **Revision is normal.** Re-run the planner with the surviving tasks and the current time as + `--start`. Same rules, shorter day. +2. **Overflow is a decision, not an accident.** If the redrawn day no longer fits, something gets + deferred by name. The planner's exit-2 overflow refusal forces that choice to be explicit. + +## 5. Fixed-schedule productivity — the hard stop + +Newport's "fixed-schedule productivity" (first described on his Study Hacks blog, later formalized +in *Deep Work*, Rule #4): **fix the endpoint of the workday first — then work backward from it.** +The hard stop functions as a forcing constraint: it makes shallow-work budgets real, forces +ruthless triage of commitments, and converts "I'll just stay late" from a safety valve into a +visible system failure. + +Parkinson's observation (*The Economist*, 1955) — "work expands so as to fill the time available +for its completion" — is the mechanism the hard stop exploits in reverse: bounded time compresses +work back toward its true size. This is why `--end` in the planner is immovable and why shallow +overflow past it is a refusal (exit 2, naming what to defer) rather than a silent extension of the +evening. + +## Sources + +1. Cal Newport, *The Time-Block Planner: A Daily Method for Deep Work in a Distracted World* (Portfolio, 2020) — plan every minute, revision columns, the 1.5-2x estimate. +2. Cal Newport, *Deep Work*, Rule #4 "Drain the Shallows" (Grand Central, 2016) — schedule every minute; fixed-schedule productivity as a forcing function. +3. Cal Newport, "Fixed-Schedule Productivity: How I Accomplish a Large Amount of Work in a Small Number of Work Hours" (Study Hacks blog, calnewport.com, 2008) — the original formulation of the hard stop. +4. Peter M. Gollwitzer, "Implementation Intentions: Strong Effects of Simple Plans," *American Psychologist* 54(7), 1999; and Gollwitzer & Sheeran, "Implementation Intentions and Goal Achievement: A Meta-analysis," *Advances in Experimental Social Psychology* 38, 2006. +5. Nir Eyal, *Indistractable: How to Control Your Attention and Choose Your Life* (BenBella, 2019) — the timeboxing chapter; "you can't call something a distraction unless you know what it distracted you from." +6. C. Northcote Parkinson, "Parkinson's Law," *The Economist*, November 1955 — work expands to fill the time available. +7. Paul Graham, "Maker's Schedule, Manager's Schedule" (paulgraham.com, 2009) — why deep blocks must be defended as contiguous half-day units. diff --git a/productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py b/productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py new file mode 100644 index 00000000..83761c34 --- /dev/null +++ b/productivity/deep-work/skills/deep-work/scripts/focus_session_logger.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""focus_session_logger.py — JSON-backed focus-session log: deep hours this week, streaks, targets. + +You cannot improve a deep-work practice you do not measure. This script keeps a tiny local +ledger of focus sessions and answers two questions: + + status — how many deep hours this week (ISO week, Mon-Sun) vs --target (default 15)? + streak — how many consecutive days (ending today, with a one-day grace) have at least + one logged session? + +Subcommands: + log --minutes N --label X [--date YYYY-MM-DD] append one focus session + status [--target HOURS] [--date YYYY-MM-DD] this week's deep hours vs target + streak [--date YYYY-MM-DD] consecutive-day streak + +State lives in a single JSON file (--state, default ~/.deep-work/sessions.json), created on +demand. Writes are atomic (tmp file + os.replace) so a crash never corrupts the ledger. +The top-level --sample flag prints a canned status report WITHOUT touching disk. + +NO LLM CALLS. Stdlib only. Deterministic given the same state file and --date. + +Exit codes: + 0 success (log appended / report printed) + 1 usage error, bad date, or unreadable/corrupt state file + +Usage: + python focus_session_logger.py log --minutes 90 --label "Write product spec" + python focus_session_logger.py status --target 15 + python focus_session_logger.py streak + python focus_session_logger.py --sample +""" + +import argparse +import datetime as dt +import json +import os +import sys +import tempfile +from typing import Any, Dict, List, Tuple + +DEFAULT_STATE = os.path.join(os.path.expanduser("~"), ".deep-work", "sessions.json") +DEFAULT_TARGET_HOURS = 15.0 + + +def parse_date(value: str, flag: str) -> dt.date: + try: + return dt.date.fromisoformat(value) + except ValueError: + raise ValueError(f"{flag} must be YYYY-MM-DD, got: {value!r}") + + +def load_state(path: str) -> Dict[str, Any]: + if not os.path.exists(path): + return {"sessions": []} + try: + with open(path, "r", encoding="utf-8") as f: + state = json.load(f) + except (OSError, json.JSONDecodeError) as exc: + raise ValueError(f"cannot read state file {path}: {exc}") + if not isinstance(state, dict) or not isinstance(state.get("sessions"), list): + raise ValueError(f"state file {path} is not a valid session ledger") + return state + + +def save_state(path: str, state: Dict[str, Any]) -> None: + """Atomic write: temp file in the same directory, then os.replace.""" + directory = os.path.dirname(os.path.abspath(path)) + os.makedirs(directory, exist_ok=True) + fd, tmp = tempfile.mkstemp(prefix=".sessions-", suffix=".tmp", dir=directory) + try: + with os.fdopen(fd, "w", encoding="utf-8") as f: + json.dump(state, f, indent=2) + f.write("\n") + os.replace(tmp, path) + except BaseException: + if os.path.exists(tmp): + os.unlink(tmp) + raise + + +def week_bounds(day: dt.date) -> Tuple[dt.date, dt.date]: + monday = day - dt.timedelta(days=day.weekday()) + return monday, monday + dt.timedelta(days=6) + + +def cmd_log(state: Dict[str, Any], minutes: int, label: str, day: dt.date) -> Dict[str, Any]: + state["sessions"].append({"date": day.isoformat(), "minutes": minutes, "label": label}) + total_today = sum(s["minutes"] for s in state["sessions"] if s["date"] == day.isoformat()) + return {"logged": {"date": day.isoformat(), "minutes": minutes, "label": label}, + "sessions_total": len(state["sessions"]), + "minutes_today": total_today} + + +def cmd_status(state: Dict[str, Any], target_hours: float, day: dt.date) -> Dict[str, Any]: + monday, sunday = week_bounds(day) + week = [s for s in state["sessions"] + if monday.isoformat() <= s["date"] <= sunday.isoformat()] + minutes = sum(s["minutes"] for s in week) + hours = round(minutes / 60.0, 2) + by_day: Dict[str, int] = {} + for s in week: + by_day[s["date"]] = by_day.get(s["date"], 0) + s["minutes"] + remaining = round(max(0.0, target_hours - hours), 2) + on_track = hours >= target_hours + return { + "week": f"{monday.isoformat()} .. {sunday.isoformat()}", + "as_of": day.isoformat(), + "sessions_this_week": len(week), + "deep_hours_this_week": hours, + "target_hours": target_hours, + "remaining_hours": remaining, + "on_track": on_track, + "by_day_minutes": dict(sorted(by_day.items())), + "headline": (f"{hours}h of deep work this week vs a {target_hours:g}h target — " + + ("target met. Protect the streak." + if on_track else f"{remaining}h still to block.")), + } + + +def cmd_streak(state: Dict[str, Any], day: dt.date) -> Dict[str, Any]: + days = {s["date"] for s in state["sessions"]} + cursor = day + if cursor.isoformat() not in days: # one-day grace: today not logged yet + cursor = cursor - dt.timedelta(days=1) + streak = 0 + while cursor.isoformat() in days: + streak += 1 + cursor = cursor - dt.timedelta(days=1) + return { + "as_of": day.isoformat(), + "streak_days": streak, + "headline": (f"{streak} consecutive day(s) with at least one focus session." + if streak else "No active streak — one 90-minute block today starts it."), + } + + +def render_human(kind: str, r: Dict[str, Any]) -> str: + out: List[str] = [] + if kind == "log": + s = r["logged"] + out.append(f"Logged: {s['minutes']} min — \"{s['label']}\" on {s['date']}") + out.append(f" Today so far: {r['minutes_today']} min · Ledger total: {r['sessions_total']} session(s)") + elif kind == "status": + out.append(f"Deep-Work Status — week {r['week']} (as of {r['as_of']})") + out.append("=" * 64) + tail = "ON TRACK" if r["on_track"] else f"{r['remaining_hours']}h remaining" + out.append(f" Deep hours: {r['deep_hours_this_week']}h / {r['target_hours']:g}h target ({tail})") + for d, m in r["by_day_minutes"].items(): + out.append(f" {d}: {m} min") + out.append(f" {r['headline']}") + else: # streak + out.append(f"Deep-Work Streak (as of {r['as_of']})") + out.append("=" * 64) + out.append(f" {r['headline']}") + return "\n".join(out) + + +SAMPLE_STATUS = { + "kind": "status", + "sample": True, + "week": "2026-07-13 .. 2026-07-19", + "as_of": "2026-07-16", + "deep_hours_this_week": 8.5, + "target_hours": 15.0, + "on_track": False, + "remaining_hours": 6.5, + "by_day_minutes": {"2026-07-13": 120, "2026-07-14": 180, + "2026-07-15": 90, "2026-07-16": 120}, + "streak_days": 4, + "headline": "8.5h of deep work this week vs a 15h target — 6.5h still to block.", +} + +SAMPLE_REPORT = """Deep-Work Status — week 2026-07-13 .. 2026-07-19 (sample, no disk touched) +================================================================ + Deep hours: 8.5h / 15h target (6.5h remaining) + 2026-07-13: 120 min + 2026-07-14: 180 min + 2026-07-15: 90 min + 2026-07-16: 120 min + 8.5h of deep work this week vs a 15h target — 6.5h still to block. + Streak: 4 consecutive day(s) with at least one focus session.""" + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="JSON-backed focus-session log: deep hours this week, streaks, targets.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=("Exit codes:\n" + " 0 success (log appended / report printed)\n" + " 1 usage error, bad date, or unreadable/corrupt state file"), + ) + p.add_argument("--state", default=DEFAULT_STATE, + help=f"Path to the JSON ledger (default {DEFAULT_STATE})") + p.add_argument("--sample", action="store_true", + help="Print a canned status report without touching disk") + p.add_argument("--json", action="store_true", + help="Emit JSON instead of the report (also honored with --sample)") + common = argparse.ArgumentParser(add_help=False) + common.add_argument("--json", action="store_true", help="Emit JSON instead of the report") + sub = p.add_subparsers(dest="cmd") + + p_log = sub.add_parser("log", help="Append one focus session", parents=[common]) + p_log.add_argument("--minutes", type=int, required=True, help="Session length in minutes") + p_log.add_argument("--label", default="deep work", help="What the session was spent on") + p_log.add_argument("--date", help="Session date YYYY-MM-DD (default today)") + + p_status = sub.add_parser("status", help="This week's deep hours vs target", parents=[common]) + p_status.add_argument("--target", type=float, default=DEFAULT_TARGET_HOURS, + help=f"Weekly deep-hours target (default {DEFAULT_TARGET_HOURS:g})") + p_status.add_argument("--date", help="As-of date YYYY-MM-DD (default today)") + + p_streak = sub.add_parser("streak", help="Consecutive days with at least one session", parents=[common]) + p_streak.add_argument("--date", help="As-of date YYYY-MM-DD (default today)") + + args = p.parse_args(argv) + + if args.sample: + if args.json: + print(json.dumps(SAMPLE_STATUS, indent=2)) + else: + print(SAMPLE_REPORT) + return 0 + if not args.cmd: + p.print_help() + print("\nerror: a subcommand is required (log / status / streak) or --sample", + file=sys.stderr) + return 1 + + try: + day = parse_date(args.date, "--date") if getattr(args, "date", None) else dt.date.today() + state = load_state(args.state) + if args.cmd == "log": + if args.minutes <= 0: + raise ValueError("--minutes must be a positive integer") + result = cmd_log(state, args.minutes, args.label, day) + save_state(args.state, state) + elif args.cmd == "status": + result = cmd_status(state, args.target, day) + else: + result = cmd_streak(state, day) + except ValueError as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(args.cmd, result)) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py b/productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py new file mode 100644 index 00000000..3b6d1f8c --- /dev/null +++ b/productivity/deep-work/skills/deep-work/scripts/shallow_work_auditor.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""shallow_work_auditor.py — Classify tasks deep vs shallow and audit the shallow share against a budget. + +Encodes Cal Newport's shallow-work budget discipline: + + 1. Every task is classified DEEP or SHALLOW by keyword heuristics on its name: + shallow signals: email, inbox, slack, status, standup, meeting, expense, invoice, + scheduling, calendar, admin, paperwork, timesheet, errand, triage + deep signals: write, draft, design, code, build, research, analyze, study, + architect, prototype, prove, strategy, model + A tie (or no signal at all) counts as SHALLOW — depth must be claimed deliberately. + Override any task with an explicit :deep or :shallow suffix; the suffix always wins. + 2. The shallow share of total hours is compared against --budget percent (default 50 — + the top of Newport's 30-50 percent band for most knowledge roles). + 3. For every shallow item, the forcing question is printed: + "How long would it take to train a smart recent graduate to do this?" + Months of training -> it is probably deep; days or weeks -> it is shallow. Batch it, + delegate it, or say no to it. + +Verdicts: + WITHIN-BUDGET (exit 0) shallow share <= budget + OVER-BUDGET (exit 2) shallow share > budget — cut, batch, or delegate before planning the day + +NO LLM CALLS. Deterministic keyword scoring. Identical inputs, identical verdict. + +Exit codes: + 0 WITHIN-BUDGET + 1 usage / input error (bad task spec) + 2 OVER-BUDGET + +Usage: + python shallow_work_auditor.py --task "Write investor update:60" \\ + --task "Email triage:45" --task "Analyze churn cohort:90:deep" --budget 50 + python shallow_work_auditor.py --sample + python shallow_work_auditor.py --sample --json +""" + +import argparse +import json +import re +import sys +from typing import Any, Dict, List + +SHALLOW_KEYWORDS = [ + "email", "inbox", "slack", "status", "standup", "meeting", "expense", "invoice", + "scheduling", "schedule", "calendar", "admin", "paperwork", "timesheet", "errand", + "triage", +] +DEEP_KEYWORDS = [ + "write", "writing", "draft", "design", "code", "coding", "program", "build", + "research", "analyze", "analysis", "study", "architect", "prototype", "prove", + "strategy", "model", +] + +FORCING_QUESTION = ('How long would it take to train a smart recent graduate to do "{name}"? ' + "Days or weeks -> shallow: batch it, delegate it, or say no.") + + +def parse_task(spec: str) -> Dict[str, Any]: + """Accepts "name:minutes" (heuristic classifies) or "name:minutes:deep|shallow" (override).""" + parts = spec.rsplit(":", 2) + override = None + if len(parts) == 3 and parts[2].strip().lower() in ("deep", "shallow"): + name, minutes_s, override = parts[0], parts[1], parts[2].strip().lower() + else: + parts = spec.rsplit(":", 1) + if len(parts) != 2: + raise ValueError(f'task must be "name:minutes[:deep|shallow]", got: {spec!r}') + name, minutes_s = parts + name, minutes_s = name.strip(), minutes_s.strip() + if not name: + raise ValueError(f"task name is empty in: {spec!r}") + if not minutes_s.isdigit() or int(minutes_s) <= 0: + raise ValueError(f"task minutes must be a positive integer in: {spec!r}") + return {"name": name, "minutes": int(minutes_s), "override": override} + + +def classify(name: str) -> Dict[str, Any]: + lowered = name.lower() + deep_hits = [k for k in DEEP_KEYWORDS if re.search(rf"\b{re.escape(k)}\b", lowered)] + shallow_hits = [k for k in SHALLOW_KEYWORDS if re.search(rf"\b{re.escape(k)}\b", lowered)] + if len(deep_hits) > len(shallow_hits): + mode, why = "deep", f"deep signals: {', '.join(deep_hits)}" + elif shallow_hits: + mode, why = "shallow", f"shallow signals: {', '.join(shallow_hits)}" + else: + mode, why = "shallow", "no signal — unclassified counts as shallow; claim :deep explicitly if wrong" + return {"mode": mode, "why": why} + + +def audit(tasks: List[Dict[str, Any]], budget_pct: float) -> Dict[str, Any]: + items = [] + for t in tasks: + if t["override"]: + mode, why = t["override"], f"explicit :{t['override']} override" + else: + c = classify(t["name"]) + mode, why = c["mode"], c["why"] + item = {"name": t["name"], "minutes": t["minutes"], "mode": mode, "why": why} + if mode == "shallow": + item["forcing_question"] = FORCING_QUESTION.format(name=t["name"]) + items.append(item) + + total = sum(i["minutes"] for i in items) + shallow_min = sum(i["minutes"] for i in items if i["mode"] == "shallow") + deep_min = total - shallow_min + share = round(100.0 * shallow_min / total, 1) if total else 0.0 + verdict = "WITHIN-BUDGET" if share <= budget_pct else "OVER-BUDGET" + return { + "items": items, + "total_minutes": total, + "deep_minutes": deep_min, + "shallow_minutes": shallow_min, + "shallow_share_pct": share, + "budget_pct": budget_pct, + "verdict": verdict, + "headline": ( + f"Shallow work is {share}% of the day against a {budget_pct:.0f}% budget. " + + ("Within budget — protect the deep hours you just earned." + if verdict == "WITHIN-BUDGET" + else "Over budget — cut, batch, or delegate shallow items before planning the day.") + ), + } + + +def render_human(r: Dict[str, Any]) -> str: + out: List[str] = [] + out.append("Shallow-Work Audit (deep vs shallow, share vs budget)") + out.append("=" * 64) + out.append("") + out.append("| Task | Min | Mode | Basis |") + out.append("|------|-----|------|-------|") + for i in r["items"]: + out.append(f"| {i['name']} | {i['minutes']} | {i['mode'].upper()} | {i['why']} |") + out.append("") + out.append(f" Deep {r['deep_minutes']} min · Shallow {r['shallow_minutes']} min · " + f"Shallow share {r['shallow_share_pct']}% vs budget {r['budget_pct']:.0f}%") + out.append("") + out.append(f" VERDICT: {r['verdict']}") + out.append(f" {r['headline']}") + shallow_items = [i for i in r["items"] if i["mode"] == "shallow"] + if shallow_items: + out.append("") + out.append(" The forcing question, per shallow item:") + for i in shallow_items: + out.append(f" - {i['forcing_question']}") + return "\n".join(out) + + +SAMPLE_TASKS = [ + "Write investor update:60", + "Analyze churn cohort:90:deep", + "Email triage:45", + "Slack catch-up:30", + "Expense report:20", + "Team scheduling:15", +] + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Classify tasks deep vs shallow and audit the shallow share against a budget.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=("Exit codes:\n" + " 0 WITHIN-BUDGET — shallow share <= --budget\n" + " 1 usage / input error\n" + " 2 OVER-BUDGET — shallow share > --budget; cut, batch, or delegate first"), + ) + p.add_argument("--task", action="append", default=[], + help='Repeatable: "name:minutes" (heuristic) or "name:minutes:deep|shallow" (override)') + p.add_argument("--budget", type=float, default=50.0, + help="Shallow budget as percent of total time (default 50 — top of the 30-50 band)") + p.add_argument("--json", action="store_true", help="Emit JSON instead of the table") + p.add_argument("--sample", action="store_true", help="Run the embedded sample task list") + args = p.parse_args(argv) + + if args.sample: + args.task = SAMPLE_TASKS + if not args.task: + p.print_help() + print("\nerror: at least one --task is required (or --sample)", file=sys.stderr) + return 1 + if not 0 < args.budget <= 100: + print("error: --budget must be in (0, 100]", file=sys.stderr) + return 1 + try: + tasks = [parse_task(s) for s in args.task] + except ValueError as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + result = audit(tasks, args.budget) + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + return 0 if result["verdict"] == "WITHIN-BUDGET" else 2 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/deep-work/skills/deep-work/scripts/time_block_planner.py b/productivity/deep-work/skills/deep-work/scripts/time_block_planner.py new file mode 100644 index 00000000..043055c4 --- /dev/null +++ b/productivity/deep-work/skills/deep-work/scripts/time_block_planner.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python3 +"""time_block_planner.py — Build a time-blocked day: deep work first, shallow batched, buffers everywhere. + +Encodes Cal Newport's time-block planning discipline as deterministic scheduling: + + 1. Deep tasks are scheduled FIRST, in the earliest hours of the day (energy-first). + Each deep task gets a block of at least 90 minutes — shorter blocks never reach depth. + 2. Total deep time is capped at 4 hours (240 min) — the trained practitioner's daily + ceiling. Demand beyond the cap is not squeezed in; the planner REFUSES (exit 2) + and names exactly which deep tasks to cut or defer. + 3. Shallow tasks are batched into at most two batches: one late morning (right after + the deep blocks) and one at the end of the day. A single shallow task goes to the + end-of-day batch so the morning stays protected. + 4. A 10-minute buffer separates consecutive work blocks (attention residue is real). + 5. An optional --lunch HH:MM inserts a fixed 30-minute break; blocks never straddle it. + 6. Shallow work that spills past --end is an overflow: the planner REFUSES (exit 2) + and names the overflow instead of silently extending the day. + +NO LLM CALLS. Pure arithmetic. Identical inputs always produce the identical plan. + +Exit codes: + 0 plan fits — markdown schedule (or --json) emitted + 1 usage / input error (bad time format, bad task spec, end <= start) + 2 refusal — deep demand exceeds the 4-hour cap, or the day overflows --end + +Usage: + python time_block_planner.py --start 08:30 --end 17:00 --lunch 12:30 \\ + --task "Write product spec:120:deep" --task "Email sweep:30:shallow" + python time_block_planner.py --sample + python time_block_planner.py --sample --json +""" + +import argparse +import json +import sys +from typing import Any, Dict, List, Optional + +DEEP_CAP_MIN = 240 # Newport's trained-limit ceiling: ~4 hours of deep work per day +MIN_DEEP_BLOCK = 90 # a deep block shorter than this never reaches depth +BUFFER_MIN = 10 # context-switch buffer between consecutive work blocks +LUNCH_MIN = 30 # fixed lunch break length + + +def parse_hhmm(value: str, flag: str) -> int: + parts = value.split(":") + if len(parts) != 2 or not parts[0].isdigit() or not parts[1].isdigit(): + raise ValueError(f"{flag} must be HH:MM, got: {value!r}") + h, m = int(parts[0]), int(parts[1]) + if h > 23 or m > 59: + raise ValueError(f"{flag} out of range: {value!r}") + return h * 60 + m + + +def fmt(minutes: int) -> str: + return f"{minutes // 60:02d}:{minutes % 60:02d}" + + +def fmt_dur(minutes: int) -> str: + h, m = divmod(minutes, 60) + if h and m: + return f"{h}h{m:02d}" + if h: + return f"{h}h" + return f"{m}min" + + +def parse_task(spec: str) -> Dict[str, Any]: + parts = spec.rsplit(":", 2) + if len(parts) != 3: + raise ValueError(f'task must be "name:minutes:deep|shallow", got: {spec!r}') + name, minutes_s, mode = parts[0].strip(), parts[1].strip(), parts[2].strip().lower() + if not name: + raise ValueError(f"task name is empty in: {spec!r}") + if not minutes_s.isdigit() or int(minutes_s) <= 0: + raise ValueError(f"task minutes must be a positive integer in: {spec!r}") + if mode not in ("deep", "shallow"): + raise ValueError(f'task mode must be "deep" or "shallow" in: {spec!r}') + return {"name": name, "minutes": int(minutes_s), "mode": mode} + + +def _ev(start: int, end: int, label: str, kind: str) -> Dict[str, Any]: + return {"start": start, "end": end, "label": label, "kind": kind, + "minutes": end - start} + + +def plan(start: int, end: int, tasks: List[Dict[str, Any]], + lunch: Optional[int]) -> Dict[str, Any]: + """Return a plan dict. verdict is PLANNED, DEEP-CAP-EXCEEDED, or OVERFLOW.""" + deep = [t for t in tasks if t["mode"] == "deep"] + shallow = [t for t in tasks if t["mode"] == "shallow"] + notes: List[str] = [] + + # --- 1. Deep blocks: at least 90 min each, earliest hours, capped at 4h total --- + deep_blocks = [] + for t in deep: + dur = max(MIN_DEEP_BLOCK, t["minutes"]) + if dur > t["minutes"]: + notes.append(f'"{t["name"]}" widened from {t["minutes"]} to {dur} min ' + f"(deep blocks below {MIN_DEEP_BLOCK} min never reach depth).") + deep_blocks.append({"name": t["name"], "dur": dur}) + total_deep = sum(b["dur"] for b in deep_blocks) + if total_deep > DEEP_CAP_MIN: + acc, cut = 0, [] + for b in deep_blocks: + if acc + b["dur"] <= DEEP_CAP_MIN: + acc += b["dur"] + else: + cut.append(b["name"]) + return { + "verdict": "DEEP-CAP-EXCEEDED", + "reason": (f"Deep demand is {fmt_dur(total_deep)} but the trained daily ceiling " + f"is {fmt_dur(DEEP_CAP_MIN)}. Depth past the cap is fake depth — " + f"cut or defer to another day: " + ", ".join(f'"{n}"' for n in cut)), + "deep_demand_minutes": total_deep, + "deep_cap_minutes": DEEP_CAP_MIN, + "cut_or_defer": cut, + } + + # --- 2. Shallow batches: at most two (late morning + end of day) --- + if len(shallow) >= 2: + n_a = (len(shallow) + 1) // 2 + batch_a, batch_b = shallow[:n_a], shallow[n_a:] + elif len(shallow) == 1: + batch_a, batch_b = [], shallow[:] # single batch goes end-of-day + else: + batch_a, batch_b = [], [] + dur_a = sum(t["minutes"] for t in batch_a) + dur_b = sum(t["minutes"] for t in batch_b) + + # --- 3. Forward pass: deep blocks, then shallow batch A, lunch-aware --- + events: List[Dict[str, Any]] = [] + ctx = {"lunch": lunch, "lunch_placed": lunch is None} + cursor = start + + def advance(cursor: int, dur: int, label: str, kind: str) -> int: + if events and events[-1]["kind"] in ("deep", "shallow"): + events.append(_ev(cursor, cursor + BUFFER_MIN, "Buffer — stand up, reset", "buffer")) + cursor += BUFFER_MIN + if not ctx["lunch_placed"]: + if cursor >= ctx["lunch"]: + events.append(_ev(cursor, cursor + LUNCH_MIN, "Lunch — away from the desk", "break")) + cursor += LUNCH_MIN + ctx["lunch_placed"] = True + elif cursor + dur > ctx["lunch"]: + if ctx["lunch"] > cursor: + events.append(_ev(cursor, ctx["lunch"], "Flex — reset, no inputs", "flex")) + events.append(_ev(ctx["lunch"], ctx["lunch"] + LUNCH_MIN, + "Lunch — away from the desk", "break")) + cursor = ctx["lunch"] + LUNCH_MIN + ctx["lunch_placed"] = True + events.append(_ev(cursor, cursor + dur, label, kind)) + return cursor + dur + + for b in deep_blocks: + cursor = advance(cursor, b["dur"], f'DEEP — {b["name"]}', "deep") + if batch_a: + label_a = "SHALLOW batch (late morning) — " + " · ".join(t["name"] for t in batch_a) + cursor = advance(cursor, dur_a, label_a, "shallow") + + # --- 4. Backward pass: shallow batch B ends exactly at --end --- + # The 10-min buffer invariant applies before batch B too: when batch B would + # directly follow a work block, that buffer is part of the day's budget. + need_buffer = (BUFFER_MIN if batch_b and events and events[-1]["kind"] in ("deep", "shallow") + else 0) + if cursor + need_buffer + dur_b > end: + overflow = cursor + need_buffer + dur_b - end + return { + "verdict": "OVERFLOW", + "reason": (f"The day overflows {fmt(end)} by {overflow} min. " + + (("Defer shallow work instead of extending the day: " + + ", ".join(f'"{t["name"]}"' for t in (batch_b or batch_a))) + if (batch_b or batch_a) else + "Deep demand alone exceeds the day — trim a deep block or extend --end.")), + "overflow_minutes": overflow, + "defer_candidates": [t["name"] for t in (batch_b or batch_a)], + } + if need_buffer: + events.append(_ev(cursor, cursor + BUFFER_MIN, "Buffer — stand up, reset", "buffer")) + cursor += BUFFER_MIN + start_b = end - dur_b + if start_b > cursor: + if not ctx["lunch_placed"] and cursor <= ctx["lunch"] and ctx["lunch"] + LUNCH_MIN <= start_b: + if ctx["lunch"] > cursor: + events.append(_ev(cursor, ctx["lunch"], "Flex — overflow absorber", "flex")) + events.append(_ev(ctx["lunch"], ctx["lunch"] + LUNCH_MIN, + "Lunch — away from the desk", "break")) + cursor = ctx["lunch"] + LUNCH_MIN + ctx["lunch_placed"] = True + if start_b > cursor: + events.append(_ev(cursor, start_b, "Flex — overflow absorber", "flex")) + if not ctx["lunch_placed"]: + notes.append(f"Lunch could not be placed at {fmt(lunch)} without splitting a block — " + "move it or shorten a block.") + if batch_b: + label_b = "SHALLOW batch (end of day) — " + " · ".join(t["name"] for t in batch_b) + events.append(_ev(start_b, end, label_b, "shallow")) + + totals = {} + for e in events: + totals[e["kind"]] = totals.get(e["kind"], 0) + e["minutes"] + return { + "verdict": "PLANNED", + "start": fmt(start), + "end": fmt(end), + "events": [{"start": fmt(e["start"]), "end": fmt(e["end"]), + "label": e["label"], "kind": e["kind"], "minutes": e["minutes"]} + for e in events], + "totals_minutes": totals, + "deep_total_minutes": totals.get("deep", 0), + "deep_cap_minutes": DEEP_CAP_MIN, + "notes": notes, + } + + +def render_human(r: Dict[str, Any]) -> str: + out: List[str] = [] + if r["verdict"] != "PLANNED": + out.append(f"REFUSED — {r['verdict']}") + out.append("=" * 64) + out.append(f" {r['reason']}") + return "\n".join(out) + out.append(f"## Time-Block Plan — {r['start']} → {r['end']}") + out.append("") + out.append("| Start | End | Block | Mode |") + out.append("|-------|-----|-------|------|") + for e in r["events"]: + out.append(f"| {e['start']} | {e['end']} | {e['label']} | {e['kind'].upper()} |") + out.append("") + t = r["totals_minutes"] + out.append(f"Deep {fmt_dur(t.get('deep', 0))} / {fmt_dur(r['deep_cap_minutes'])} cap · " + f"Shallow {fmt_dur(t.get('shallow', 0))} · " + f"Flex {fmt_dur(t.get('flex', 0))} · " + f"Buffers {fmt_dur(t.get('buffer', 0))}") + for n in r["notes"]: + out.append(f"- Note: {n}") + out.append("") + out.append("Flex absorbs what the plan didn't foresee — when the day breaks, " + "revise the blocks; never abandon them.") + return "\n".join(out) + + +SAMPLE = { + "start": "08:30", "end": "17:00", "lunch": "12:30", + "tasks": [ + "Write product spec:120:deep", + "Design onboarding flow:90:deep", + "Email sweep:30:shallow", + "Team status update:20:shallow", + "Expense report:15:shallow", + ], +} + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Build a time-blocked day: deep work first, shallow batched, buffers everywhere.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=("Exit codes:\n" + " 0 plan fits — schedule emitted\n" + " 1 usage / input error\n" + " 2 refusal — deep demand exceeds the 4-hour cap, or the day overflows --end"), + ) + p.add_argument("--start", help="Day start, HH:MM") + p.add_argument("--end", help="Day end, HH:MM (hard stop — fixed-schedule productivity)") + p.add_argument("--task", action="append", default=[], + help='Repeatable: "name:minutes:deep|shallow"') + p.add_argument("--lunch", help="Optional lunch start, HH:MM (fixed 30-min break)") + p.add_argument("--json", action="store_true", help="Emit JSON instead of markdown") + p.add_argument("--sample", action="store_true", help="Run the embedded sample day") + args = p.parse_args(argv) + + if args.sample: + args.start, args.end = SAMPLE["start"], SAMPLE["end"] + args.lunch, args.task = SAMPLE["lunch"], SAMPLE["tasks"] + if not args.start or not args.end or not args.task: + p.print_help() + print("\nerror: --start, --end and at least one --task are required (or --sample)", + file=sys.stderr) + return 1 + try: + start = parse_hhmm(args.start, "--start") + end = parse_hhmm(args.end, "--end") + lunch = parse_hhmm(args.lunch, "--lunch") if args.lunch else None + if end <= start: + raise ValueError("--end must be after --start") + tasks = [parse_task(s) for s in args.task] + except ValueError as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + result = plan(start, end, tasks, lunch) + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + return 0 if result["verdict"] == "PLANNED" else 2 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/meetings/.claude-plugin/plugin.json b/productivity/meetings/.claude-plugin/plugin.json new file mode 100644 index 00000000..92ab21f3 --- /dev/null +++ b/productivity/meetings/.claude-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "meetings", + "description": "Personal meeting hygiene, end to end. Before the meeting: a cost gate that prices the meeting in real dollars (attendees x minutes x rate, optionally + the 23-minute refocus overhead per attendee) and refuses to let it exist without a decision, an agenda, and a named owner — verdicts are ASYNC / NOT-READY / MEET, never a shrug. Then a timeboxed agenda builder that rejects any topic without a desired outcome, orders decision topics first, enforces a 5-minute closing actions-recap buffer, and refuses overflowing agendas by name. After the meeting: a deterministic action-item extractor that turns raw notes into an owner + due-date checklist and flags ORPHAN (no owner) and NO-DUE items, because an action item without an owner and a date is theater. Built on the Rogelberg / HBR / Grove meeting-science canon with stdlib-only scripts.", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani", + "url": "https://alirezarezvani.com" + }, + "homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/meetings", + "repository": "https://github.com/alirezarezvani/claude-skills", + "license": "MIT", + "skills": [ + "./skills/meetings" + ], + "source": { + "build_pattern": "Path-B discipline skill — Rogelberg/HBR meeting-science canon + deterministic cost-gate/agenda/action-extraction scripts", + "distinct_from": "project-management (team ceremonies, sprint planning, Jira delivery flow — this is personal meeting hygiene for the individual calling or attending the meeting); business-operations/internal-comms (org-level communication channel design — this gates one meeting at a time and never auto-sends anything); productivity/capture (personal brain-dump triage — this extracts owned actions from a shared meeting's notes)" + } +} diff --git a/productivity/meetings/README.md b/productivity/meetings/README.md new file mode 100644 index 00000000..f0c1edc9 --- /dev/null +++ b/productivity/meetings/README.md @@ -0,0 +1,60 @@ +# meetings — Cost Gate → Timeboxed Agenda → Owned Actions + +> Most meetings should be an email. This plugin makes that a testable claim instead of a complaint. + +`/cs:meeting-prep` prices a meeting in real dollars and refuses to let it exist without a decision, +an agenda, and an owner. If it survives the gate, it gets a timeboxed, decision-first agenda where +every topic has a desired outcome. `/cs:meeting-actions` then turns the raw notes into an +owner + due-date checklist — and flags every orphan, because an action item without an owner and a +date is theater. + +## The three disciplines + +| Stage | The question it answers | Script | +|---|---|---| +| **The Gate** | "Should this meeting exist at all?" — cost + decision/agenda/owner checks | `meeting_cost_calculator.py` | +| **The Agenda** | "What will we decide, in what order, in how many minutes?" | `agenda_builder.py` | +| **The Extraction** | "Who owes what, by when?" — orphan and no-due flagging | `action_item_extractor.py` | + +The gate returns one of three verdicts: **ASYNC** (no decision needed — send a memo instead), +**NOT-READY** (decision exists but the agenda or owner is missing — names what's missing), or +**MEET** (with the total cost and a cost-per-minute line so timeboxes get budgeted like money). + +## Quick start + +```bash +# 1. Gate the meeting — should it exist? +python skills/meetings/scripts/meeting_cost_calculator.py --sample + +# 2. Build the timeboxed, decision-first agenda +python skills/meetings/scripts/agenda_builder.py --sample + +# 3. After the meeting: extract owned action items from raw notes +python skills/meetings/scripts/action_item_extractor.py --sample +``` + +Or just say **"should this be a meeting?"** / **"pull the action items out of these notes"**, or run +`/cs:meeting-prep` and `/cs:meeting-actions`. + +## What's in the box + +- **Skill:** [`skills/meetings/SKILL.md`](skills/meetings/SKILL.md) +- **Agent:** [`agents/cs-meeting-discipline.md`](agents/cs-meeting-discipline.md) +- **Commands:** [`commands/cs-meeting-prep.md`](commands/cs-meeting-prep.md) — `/cs:meeting-prep` · + [`commands/cs-meeting-actions.md`](commands/cs-meeting-actions.md) — `/cs:meeting-actions` +- **3 stdlib scripts:** `meeting_cost_calculator.py`, `agenda_builder.py`, `action_item_extractor.py` +- **3 references** (Rogelberg / HBR / Grove / Amazon canon, 5-7 sources each), **2 assets** + (worked timeboxed agenda + fillable should-this-be-a-meeting worksheet) + +## Not the same as + +- **`project-management/`** — team ceremonies, sprint cadence, Jira delivery flow. This is personal + meeting hygiene: one meeting, one gate, one agenda, one checklist. +- **`business-operations/internal-comms`** — org-level communication channel design. This never + designs a comms program and never auto-sends anything. +- **`productivity/capture`** — triages your own brain-dump. This extracts owned actions from a + shared meeting's notes. + +## License + +MIT. diff --git a/productivity/meetings/agents/cs-meeting-discipline.md b/productivity/meetings/agents/cs-meeting-discipline.md new file mode 100644 index 00000000..af2335cb --- /dev/null +++ b/productivity/meetings/agents/cs-meeting-discipline.md @@ -0,0 +1,91 @@ +--- +name: cs-meeting-discipline +description: Enforces personal meeting hygiene end to end. Before a meeting it runs the cost gate (attendees x minutes x rate, optionally + 23-minute refocus overhead per attendee) and refuses to schedule anything without a decision, an agenda, and a named owner — verdict is ASYNC / NOT-READY / MEET, never a shrug. If the meeting survives, it builds a timeboxed, decision-first agenda where every topic has a desired outcome. After the meeting it extracts action items from raw notes into an owner + due-date checklist and flags every ORPHAN and NO-DUE item. Use before calling a meeting or after one ends with messy notes. +skills: productivity/meetings/skills/meetings +domain: productivity +model: opus +tools: [Read, Bash] +--- + +# Meeting Discipline Agent + +## Purpose + +The `cs-meeting-discipline` agent orchestrates the `meetings` skill to keep one person's calendar +honest — before a meeting is called, and after it ends: + +1. **Gate** — price the meeting (`meeting_cost_calculator.py`): attendees × minutes × hourly rate, + optionally + the 23-minute refocus overhead per attendee. Then apply the three checks: is there a + decision to make? is there an agenda? is there a named owner? Verdicts: + - **ASYNC** (exit 2) — no decision needed; this is a status update. Recommend a memo/thread instead. + - **NOT-READY** (exit 3) — a decision exists but the agenda or owner is missing; name what's missing. + - **MEET** (exit 0) — all three present; print the total cost and the cost-per-minute line so + timeboxes get budgeted like money. +2. **Build the agenda** — only for a MEET verdict (`agenda_builder.py`): every topic needs a + desired outcome (refused by name otherwise), decision topics sort first, a 5-minute closing + "actions recap" buffer is enforced, and an overflowing agenda is refused with the exact overflow. +3. **Run** — the human runs the meeting. The agent's job here is only the pre-read reminder and the + printed agenda; it never joins, records, or sends anything. +4. **Extract** — after the meeting (`action_item_extractor.py`): parse the raw notes for checkboxes, + ACTION:/TODO: lines, "@name will …" and "Name will … by date" patterns; emit a markdown + checklist grouped by owner with summary counts; flag every **ORPHAN** (no owner) and **NO-DUE** + item so they get resolved before anyone leaves the thread. +5. **Deliver** — the gate verdict + cost, the timeboxed agenda (or the async recommendation), and + the owned-actions checklist with orphans called out for immediate assignment. + +## Voice + +- Blunt about cost. A 6-person hour costs real money; say the number before debating the invite list. +- "No decision, no meeting" is the default, not the exception. Recommending ASYNC is a win, not a failure. +- Zero tolerance for orphan actions. "Someone should…" is not an action item; a name and a date are. + +## Hard rules + +1. **Gate before agenda.** Never build an agenda for a meeting that hasn't passed the cost gate. + An ASYNC verdict ends the prep — draft the memo outline instead. +2. **No desired outcome, no agenda slot.** `agenda_builder.py` refuses topics with empty outcomes; + do not paraphrase around it — go back and get the outcome. +3. **Decisions first.** Decision topics (decide/choose/approve) sort before discuss/inform topics. + Do not reorder them back for politeness. +4. **Every action item has an owner and a date — or it is not an action item.** Surface every + ORPHAN and NO-DUE flag; never silently drop or auto-assign one. +5. **Never auto-send.** No calendar invites, no emails, no messages. Output is text the user sends. + +## Skill Integration + +**Skill Location:** `../skills/meetings/` + +### Python Scripts (Stdlib) + +1. **Meeting Cost Calculator** — `skills/meetings/scripts/meeting_cost_calculator.py` — dollars + + refocus overhead + decision/agenda/owner gate → ASYNC / NOT-READY / MEET. +2. **Agenda Builder** — `skills/meetings/scripts/agenda_builder.py` — timeboxed, decision-first + agenda; refuses empty outcomes and overflow; enforces the closing actions-recap buffer. +3. **Action Item Extractor** — `skills/meetings/scripts/action_item_extractor.py` — raw notes → + owner-grouped checklist with ORPHAN / NO-DUE flags and summary counts. + +### Knowledge Bases + +- `skills/meetings/references/meeting_cost_canon.md` — the real cost of meetings and the + should-this-exist gate (Perlow/HBR, Rogelberg, Shopify, Bezos, Grove; 7 sources) +- `skills/meetings/references/agenda_discipline.md` — agendas as questions, timeboxing, + decision-first ordering, the owner role, pre-reads (Rogelberg, Parkinson, Sutherland, Grove; 7 sources) +- `skills/meetings/references/action_item_discipline.md` — why meetings without owned actions are + theater (Allen/GTD, Doran/SMART, Gollwitzer, Locke & Latham, DACI; 6 sources) + +## Differentiates From Siblings + +- **vs `project-management/`**: PM skills run team ceremonies and Jira delivery flow. This agent + gates one meeting at a time for the person calling it — personal hygiene, not delivery process. +- **vs `business-operations/internal-comms`**: internal-comms designs org-level communication + programs. This never designs a program and never auto-sends anything. +- **vs `cs-capture-triage`** (productivity/capture): capture triages your own brain-dump into + actions. This extracts owned actions from a shared meeting's notes and flags the orphans. + +## Related Agents + +- [cs-roast-judge](../../roast/agents/cs-roast-judge.md) — productivity sibling, adversarial idea panel + +--- + +**Version:** 1.0.0 diff --git a/productivity/meetings/commands/cs-meeting-actions.md b/productivity/meetings/commands/cs-meeting-actions.md new file mode 100644 index 00000000..bdb06505 --- /dev/null +++ b/productivity/meetings/commands/cs-meeting-actions.md @@ -0,0 +1,81 @@ +--- +name: "cs-meeting-actions" +description: "/cs:meeting-actions — Turn raw meeting notes into an owned action-item checklist: extracts checkboxes, ACTION:/TODO: lines, '@name will …' and 'Name will … by date' patterns, groups by owner, and flags every ORPHAN (no owner) and NO-DUE item. An action without an owner and a date is not an action." +argument-hint: "[path to notes file, or paste the notes]" +--- + +# /cs:meeting-actions — Raw Notes → Owned Action Checklist + +**Command:** `/cs:meeting-actions [notes file or pasted notes]` + +A meeting that ends without owned, dated actions was theater. This command runs immediately after +the meeting — while attendees still remember what they agreed to — and turns the messy notes into a +checklist where every item has a name and a date, or is loudly flagged until it does. + +## When to Run + +- The meeting just ended and the notes are a wall of prose +- "Pull the action items out of these notes" +- "Who owes what from Thursday's meeting?" +- Before posting a meeting summary — so the summary leads with the actions + +## When NOT to Run + +- Before the meeting → use `/cs:meeting-prep` (cost gate + agenda) +- Triaging your own private brain-dump → `productivity/capture` owns that +- Turning actions into Jira issues and sprint work → `project-management/` owns delivery flow + +## What You Get + +1. **A markdown checklist grouped by owner** — each item with its due date where one was captured. +2. **ORPHAN flags** — every action with no owner, grouped under "(unassigned)" so they get claimed + before the thread goes cold. +3. **NO-DUE flags** — owned actions with no date, listed so a date gets attached now, not "later". +4. **Summary counts** — total actions · owned · orphaned · missing dates, in one line. + +## Trigger Phrases (auto-invoke without /cs:) + +- "extract the action items" / "pull out the actions" +- "who owes what" / "turn these notes into a checklist" +- "action items from this meeting" + +## Discipline + +- **Every action item has an owner and a date — or it is not an action item.** Flags are the output, + not noise; never silently drop or auto-assign an orphan. +- **Extraction is deterministic** — the script's patterns decide what counts; don't invent actions + the notes don't contain. +- **Orphans get resolved by a human** — present them for assignment; never guess an owner. +- **Never auto-send** — the checklist is text the user posts. No emails, no messages, no issues filed. + +## Workflow + +```bash +# From a notes file +python ../skills/meetings/scripts/action_item_extractor.py --input notes.md + +# From pasted notes on stdin +cat notes.md | python ../skills/meetings/scripts/action_item_extractor.py + +# Machine-readable, for piping into other checklists +python ../skills/meetings/scripts/action_item_extractor.py --input notes.md --json +``` + +Then walk the flags: assign every ORPHAN, date every NO-DUE, and post the checklist. + +## Stop Conditions + +- Checklist delivered, every ORPHAN either assigned by the user or explicitly left flagged → done. +- Zero actions extracted → say so plainly and ask whether the meeting actually decided anything + (that's a `/cs:meeting-prep` conversation for next time). Don't fabricate items. +- User says "just give me the list" → checklist + summary counts, no assignment walkthrough. + +## Related + +- Agent: [`cs-meeting-discipline`](../agents/cs-meeting-discipline.md) +- Skill: [`meetings`](../skills/meetings/SKILL.md) +- Sibling command: [`/cs:meeting-prep`](cs-meeting-prep.md) (pre-meeting gate + agenda) + +--- + +**Version:** 1.0.0 diff --git a/productivity/meetings/commands/cs-meeting-prep.md b/productivity/meetings/commands/cs-meeting-prep.md new file mode 100644 index 00000000..e29d1b16 --- /dev/null +++ b/productivity/meetings/commands/cs-meeting-prep.md @@ -0,0 +1,86 @@ +--- +name: "cs-meeting-prep" +description: "/cs:meeting-prep — Gate a meeting before it exists: price it in real dollars (attendees x minutes x rate + optional 23-minute refocus overhead), demand a decision + agenda + owner, then either recommend async or build a timeboxed, decision-first agenda where every topic has a desired outcome." +argument-hint: "[the meeting: who, how long, what decision]" +--- + +# /cs:meeting-prep — Cost Gate → Timeboxed Agenda (or Async) + +**Command:** `/cs:meeting-prep [the meeting]` + +Most meetings should be an email. This command makes that a testable claim: it prices the meeting, +runs the decision/agenda/owner gate, and only if the meeting survives does it build the timeboxed +agenda. An ASYNC verdict is a win — draft the memo instead. + +## When to Run + +- "Should this be a meeting?" / "Is this meeting worth it?" +- Before sending any invite with 3+ attendees +- "Build the agenda for Thursday's pricing meeting" +- You suspect a recurring meeting has outlived its decision + +## When NOT to Run + +- After the meeting, with notes in hand → use `/cs:meeting-actions` +- Sprint ceremonies, standups, and Jira delivery cadence → `project-management/` owns those +- Designing an org-wide comms program → `business-operations/internal-comms` + +## What You Get + +1. **The price** — direct cost (attendees × minutes × rate) plus, with `--include-refocus`, the + 23-minute-per-attendee refocus overhead, and a cost-per-minute line. +2. **One gate verdict** — `ASYNC` (no decision → send a memo; exit 2), `NOT-READY` (decision but + missing agenda/owner, named; exit 3), or `MEET` (exit 0). +3. **On MEET: a timeboxed agenda** — decision topics first, per-topic desired outcome + owner + + timebox, a pre-read line, and a mandatory 5-minute closing "actions recap" slot. +4. **On ASYNC: a memo outline** — the decision-free content restructured as a written update. + +## Trigger Phrases (auto-invoke without /cs:) + +- "should this be a meeting" / "does this need a meeting" +- "what does this meeting cost" +- "build a timeboxed agenda" / "prep this meeting" +- "can this be async" + +## Discipline + +- **Gate before agenda** — never build an agenda for a meeting that hasn't passed the gate. +- **No decision, no meeting** — status updates go async, every time. +- **No desired outcome, no agenda slot** — the builder refuses empty outcomes by name; get the outcome. +- **Decisions first** — decide/choose/approve topics sort before discuss/inform. Keep them there. +- **Timeboxes are budgets** — overflow + the 5-minute closing buffer gets refused with the exact overage. + +## Workflow + +```bash +# 1. Price + gate the meeting +python ../skills/meetings/scripts/meeting_cost_calculator.py \ + --attendees 6 --minutes 60 --avg-rate 90 --include-refocus \ + --has-decision --has-agenda --has-owner + +# 2a. ASYNC (exit 2) → draft the memo outline instead. Stop here. +# 2b. NOT-READY (exit 3) → get the missing agenda/owner, re-run the gate. + +# 3. MEET (exit 0) → build the timeboxed, decision-first agenda +python ../skills/meetings/scripts/agenda_builder.py --length 45 \ + --topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \ + --topic "Launch risks:Discuss open launch blockers:15:sam" \ + --topic "Metrics:Inform team of activation trend:5:alex" +``` + +## Stop Conditions + +- ASYNC verdict delivered + memo outline sketched → done. Do not build an agenda anyway. +- MEET verdict + agenda printed with pre-read line and closing recap slot → done. +- NOT-READY twice in a row on the same missing input → hand the gap to the user; don't invent an owner. +- User says "just book it" → deliver the cost line once, then comply. Their calendar, their call. + +## Related + +- Agent: [`cs-meeting-discipline`](../agents/cs-meeting-discipline.md) +- Skill: [`meetings`](../skills/meetings/SKILL.md) +- Sibling command: [`/cs:meeting-actions`](cs-meeting-actions.md) (post-meeting extraction) + +--- + +**Version:** 1.0.0 diff --git a/productivity/meetings/skills/meetings/SKILL.md b/productivity/meetings/skills/meetings/SKILL.md new file mode 100644 index 00000000..1b5b6e59 --- /dev/null +++ b/productivity/meetings/skills/meetings/SKILL.md @@ -0,0 +1,97 @@ +--- +name: meetings +description: Use when someone wants to decide whether a meeting is worth calling, price a meeting in dollars, build a timeboxed agenda with desired outcomes, or turn messy meeting notes into owned action items — or says "should this be a meeting", "/cs:meeting-prep", or "/cs:meeting-actions". Runs a cost gate (ASYNC / NOT-READY / MEET), builds a decision-first agenda, and extracts an owner + due-date checklist that flags every orphan. +argument-hint: "[the meeting to gate, or the notes to extract actions from]" +license: MIT +metadata: + version: 1.0.0 + build_pattern: "Path-B discipline skill — Rogelberg/HBR meeting-science canon + deterministic gate/agenda/extraction scripts" + distinct_from: "project-management (team ceremonies + Jira delivery flow; this is personal meeting hygiene); business-operations/internal-comms (org-level communication design; this never auto-sends); productivity/capture (private brain-dump triage; this parses shared meeting notes)" +--- + +# Meetings — Cost Gate → Timeboxed Agenda → Owned Actions + +> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls +> in scripts, nothing auto-sent. The scripts fix the discipline; the user runs the meeting. + +## What this does + +Most meetings should be an email. This skill makes that testable: it prices a meeting in real +dollars, refuses to let it exist without a decision + agenda + owner, builds a timeboxed +decision-first agenda for the survivors, and afterwards turns raw notes into an owner + due-date +checklist that flags every orphan. An ASYNC verdict is a win, not a failure. + +## Workflow — gate → agenda → run → extract + +**1. Gate.** Before starting, ask one clarifying question if the decision to be made is unstated — +the gate cannot run honestly without it. Then price it and apply the three checks. No decision → ASYNC (exit 2): draft a memo +instead, stop here. Decision but missing agenda/owner → NOT-READY (exit 3), naming the gap. +All present → MEET (exit 0) with total cost and a cost-per-minute line. + +**2. Agenda.** Only for MEET. Every topic needs a desired outcome — empty outcomes are refused by +name (exit 2). Decision topics (decide/choose/approve) sort before discuss/inform. Timeboxes plus +the mandatory 5-minute closing "actions recap" slot must fit `--length`, or the overflow is refused +with the exact overage (exit 3). Iterate — trim or split the named topic and re-run until it fits; +the stop condition is exit 0 (or the meeting goes async). Output includes a pre-read line. + +**3. Run.** The user runs the meeting from the printed agenda. Hold the timeboxes; use the closing +slot to read every action aloud with its owner and date. + +**4. Extract.** Feed the raw notes to the extractor: checkboxes, `ACTION:`/`TODO:` lines, +"@name will …" and "Name will … by date" patterns become a checklist grouped by owner, with +ORPHAN (no owner) and NO-DUE flags plus summary counts. Assign every orphan before posting — the +meeting is done when every action has an owner and a date; that completion check closes the loop. + +```bash +# 1. Gate: should this meeting exist? +python scripts/meeting_cost_calculator.py --attendees 6 --minutes 60 \ + --avg-rate 90 --include-refocus --has-decision --has-agenda --has-owner + +# 2. Agenda: timeboxed, decision-first, outcomes mandatory +python scripts/agenda_builder.py --length 45 \ + --topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \ + --topic "Launch risks:Discuss open launch blockers:15:sam" + +# 4. Extract: raw notes -> owner + due-date checklist with ORPHAN/NO-DUE flags +python scripts/action_item_extractor.py --input notes.md +``` + +## Scripts + +| Script | Role | +|---|---| +| `scripts/meeting_cost_calculator.py` | Dollars (attendees × minutes × rate, optional 23-min refocus overhead per attendee) + decision/agenda/owner gate → ASYNC / NOT-READY / MEET. | +| `scripts/agenda_builder.py` | Timeboxed decision-first agenda; refuses empty outcomes and overflow; enforces pre-read line + 5-min closing actions-recap slot. | +| `scripts/action_item_extractor.py` | Raw notes → owner-grouped markdown checklist with due dates, ORPHAN/NO-DUE flags, and summary counts. | + +## References + +- [`references/meeting_cost_canon.md`](references/meeting_cost_canon.md) — the real cost of meetings and the should-this-exist gate (7 sources) +- [`references/agenda_discipline.md`](references/agenda_discipline.md) — agendas as questions, timeboxing, decision-first ordering, pre-reads (7 sources) +- [`references/action_item_discipline.md`](references/action_item_discipline.md) — why meetings without owned actions are theater (6 sources) + +## Assets + +- [`assets/example_agenda.md`](assets/example_agenda.md) — a full worked timeboxed agenda (gate verdict → ordered topics → closing recap) +- [`assets/meeting_gate_worksheet.md`](assets/meeting_gate_worksheet.md) — fillable should-this-be-a-meeting worksheet + +## Rules + +- **Gate before agenda.** Never build an agenda for a meeting that hasn't passed the gate. +- **No decision, no meeting.** Status updates go async, every time. +- **No desired outcome, no agenda slot.** The builder refuses; go get the outcome. +- **Every action item has an owner and a date — or it is not an action item.** Flag, never drop. +- **Never auto-send.** No invites, no emails, no messages. Output is text the user sends. + +## Distinct From (don't reach for the wrong skill) + +- **`project-management/`** — team ceremonies, sprint cadence, Jira delivery flow. This gates one + meeting at a time for the person calling it. +- **`business-operations/internal-comms`** — org-level communication design. This never designs a + comms program and never sends anything. +- **`productivity/capture`** — triages a private brain-dump. This parses a shared meeting's notes. + +--- + +**Version:** 1.0.0 +**Build pattern:** Path-B discipline skill — meeting-science canon preserved + deterministic gate/agenda/extraction scripts added. diff --git a/productivity/meetings/skills/meetings/assets/example_agenda.md b/productivity/meetings/skills/meetings/assets/example_agenda.md new file mode 100644 index 00000000..116ffc3e --- /dev/null +++ b/productivity/meetings/skills/meetings/assets/example_agenda.md @@ -0,0 +1,76 @@ +# Worked Example — Q3 Pricing Decision Meeting + +A full pass through the `meetings` skill for a real meeting, from gate to agenda. Use it as the +canonical shape for what the scripts produce and how the pieces snap together. + +## Step 1 — The gate + +```bash +python scripts/meeting_cost_calculator.py --attendees 6 --minutes 45 \ + --avg-rate 90 --include-refocus --has-decision --has-agenda --has-owner +``` + +``` +Meeting Cost Gate (should this meeting exist?) +================================================================ + Attendees: 6 Length: 45 min Rate: $90/hr + Direct cost: $ 405.00 (6 x 45 min x $90/hr) + Refocus cost: $ 207.00 (23 min refocus overhead per attendee) + TOTAL COST: $ 612.00 + + Gate: decision=yes | agenda=yes | owner=yes + + VERDICT: MEET + Gate passed: decision + agenda + owner. This meeting has earned its slot. It costs $612.00 — + budget the agenda timeboxes like money. + + Every minute of this meeting costs $13.60 — price each agenda topic's timebox against that. +``` + +**MEET** — proceed to the agenda. (Had there been no decision, the verdict would be ASYNC and the +next step would be a memo outline, not an agenda.) + +## Step 2 — The agenda + +```bash +python scripts/agenda_builder.py --length 45 \ + --topic "Metrics review:Inform team of the activation trend:5:alex" \ + --topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \ + --topic "Launch risks:Discuss open launch blockers:15:sam" +``` + +Note what the builder did below: the **decision topic sorted to the front** even though it was +supplied second, every topic carries a desired outcome and an owner, and the closing actions-recap +slot is pinned to the end of the meeting. + +```markdown +# Agenda — 45 minutes, 3 topics + +**Pre-read:** circulate the relevant doc(s) at least a day ahead. The meeting starts assuming the +pre-read was read — no recap slot for skippers. + +| # | Timebox | Topic | Desired outcome | Owner | +|---|---------|-------|-----------------|-------| +| 1 | 00–15 min (15 min) | Q3 pricing | Decide usage-based vs seat-based | maria | +| 2 | 15–20 min (5 min) | Metrics review | Inform team of the activation trend | alex | +| 3 | 20–35 min (15 min) | Launch risks | Discuss open launch blockers | sam | +| ✔ | 40–45 min (5 min) | **Actions recap** | Read every action aloud: owner + due date, or it is not an action | meeting owner | + +_5 min of slack before the recap — if topics finish early, end early. Nobody has ever complained +about a meeting ending early._ +``` + +## What refusal looks like + +Two inputs the builder will not accept, so you see them before you hit them: + +- **Empty desired outcome** (`--topic "Roadmap::10:sam"`) → exit 2: + `REFUSED (REFUSED-NO-OUTCOME): No desired outcome, no agenda slot. Topics refused: "Roadmap". Go get the outcome, then rebuild.` +- **Overflow** (topics totaling 50 min into `--length 45`) → exit 3: + `REFUSED (REFUSED-OVERFLOW): Timeboxes (50 min) + closing actions-recap buffer (5 min) = 55 min, but the meeting is 45 min — 10 min over. Cut a topic, shrink a timebox, or move an inform topic to the pre-read.` + +## Step 3 — After the meeting + +Feed the raw notes to `action_item_extractor.py` and walk the flags — see +[`meeting_gate_worksheet.md`](meeting_gate_worksheet.md) for the fillable pre-meeting worksheet +that keeps the next meeting honest too. diff --git a/productivity/meetings/skills/meetings/assets/meeting_gate_worksheet.md b/productivity/meetings/skills/meetings/assets/meeting_gate_worksheet.md new file mode 100644 index 00000000..c8ae85c9 --- /dev/null +++ b/productivity/meetings/skills/meetings/assets/meeting_gate_worksheet.md @@ -0,0 +1,79 @@ +# Should This Be a Meeting? — Fillable Gate Worksheet + +Fill this in **before** sending the invite. If you can't fill a required line, the meeting is not +ready to be called — that's the worksheet working, not failing. Pairs with +`scripts/meeting_cost_calculator.py`, which turns the answers into an ASYNC / NOT-READY / MEET +verdict with a dollar figure. + +## 1. The decision (required) + +> No decision, no meeting. A status update goes async, every time. + +**The specific decision this meeting will make:** + +- _____________________________________________________________________ + +**What happens if this decision is NOT made this week:** + +- _____________________________________________________________________ + +*If you wrote "share an update", "align", or "sync" above — stop. Draft the memo/thread instead +and send it to the same list. You just saved the full cost below.* + +## 2. The price + +| Input | Value | +|---|---| +| Attendees (only people needed to make the decision) | _______ | +| Length in minutes | _______ | +| Avg fully-loaded hourly rate ($90 if unsure) | _______ | +| Include 23-min refocus overhead per attendee? (y/n) | _______ | + +```bash +python scripts/meeting_cost_calculator.py \ + --attendees ___ --minutes ___ --avg-rate ___ --include-refocus \ + --has-decision --has-agenda --has-owner +``` + +**Total cost: $ _______** — Would you approve this as an invoice for the decision in section 1? +If no, cut attendees or minutes until you would. + +## 3. The owner (required) + +**Named meeting owner** (runs the timeboxes, owns the recap, chases the actions): + +- _____________________________________________________________________ + +## 4. The agenda (required — outcomes, not nouns) + +Every topic needs a desired outcome starting with a verb. **Decide / choose / approve** topics go +first. If a topic's outcome starts with "inform", ask whether it belongs in the pre-read instead. + +| Topic | Desired outcome (verb first) | Minutes | Topic owner | +|---|---|---|---| +| ______________ | ______________________________ | _____ | _________ | +| ______________ | ______________________________ | _____ | _________ | +| ______________ | ______________________________ | _____ | _________ | + +**Timebox check:** topics total _____ min + 5 min closing actions-recap = _____ min ≤ meeting +length _____ min? If not, cut — don't stretch the meeting. + +## 5. The pre-read + +**Doc(s) to circulate ahead:** _______________________________________________ + +**Sent by (owner + date):** _________________________________________________ + +## 6. Commitments for the room + +- [ ] The meeting ends with the actions recap: every action read aloud with **owner + due date**. +- [ ] Actions without an owner get assigned before anyone leaves — no orphans out the door. +- [ ] Notes go through `scripts/action_item_extractor.py` and the checklist is posted the same day. +- [ ] If the decision gets made early, the meeting ends early. + +--- + +**Gate verdict (from the calculator):** ASYNC ☐ NOT-READY ☐ MEET ☐ + +*ASYNC → write the memo. NOT-READY → fix the named gap, re-run. MEET → build the agenda with +`scripts/agenda_builder.py` and send the invite with the pre-read attached.* diff --git a/productivity/meetings/skills/meetings/references/action_item_discipline.md b/productivity/meetings/skills/meetings/references/action_item_discipline.md new file mode 100644 index 00000000..e0ebce49 --- /dev/null +++ b/productivity/meetings/skills/meetings/references/action_item_discipline.md @@ -0,0 +1,85 @@ +# Action-Item Discipline — why meetings without owned actions are theater + +The third discipline of the `meetings` skill: the meeting's output is not the discussion, the +slides, or even the decision — it is the set of committed next actions that exist afterwards. A +meeting that ends without owned, dated actions consumed its budget and produced a feeling. This +file documents the canon behind `action_item_extractor.py` and its two flags, **ORPHAN** (no +owner) and **NO-DUE** (no date). + +## The next-action rule (why vague intentions don't survive) + +David Allen's *Getting Things Done* (Penguin, 2001) built its entire system on one observation: +outcomes don't get done, **next actions** do. "Handle the pricing situation" is a project; +"Maria sends the pricing one-pager by Friday" is an action. Allen's test — *what's the very next +physical, visible activity, and who takes it?* — is exactly the shape the extractor demands: a +verb, an owner, a date. Anything in the notes that fails the test ("we should think about X", +"the team agreed X matters") is deliberately not extracted, because promoting sentiment to a +checklist launders vagueness into false progress. + +## One owner, not a committee + +Every extracted item gets exactly one owner or an **ORPHAN** flag. This is the "Driver" / +"Accountable" insight of the DACI and RACI decision frameworks (documented in the Atlassian Team +Playbook's DACI play): when a task has two owners it has zero, because each can reasonably assume +the other has it. The extractor never auto-assigns an orphan — assignment is a human commitment, +and a name typed by a script is not a commitment. The flag's job is to force the assignment +conversation while the room (or thread) still remembers agreeing to the work. + +## A date, or it drifts + +George Doran's original SMART memo ("There's a S.M.A.R.T. Way to Write Management's Goals and +Objectives," *Management Review*, 1981) made **time-bound** one of the five criteria for a reason: +an undated commitment cannot be late, so it never becomes urgent, so it loses every scheduling +contest against work that can. The extractor captures "by/due/before " phrasings and flags +the rest **NO-DUE**. The fix costs five seconds at recap time ("Alex — by when?") and is the +highest-leverage edit anyone makes to meeting notes. + +## Implementation intentions (the psychology of "who + when") + +The owner + date rule is not managerial superstition; it has a mechanism. Peter Gollwitzer's +implementation-intentions research shows that goals formulated as *if-then* plans — specifying +when, where, and how the action happens — are acted on at substantially higher rates than +equally-motivated abstract goals, across dozens of studies (Peter M. Gollwitzer, "Implementation +Intentions: Strong Effects of Simple Plans," *American Psychologist*, 1999; meta-analysis: +Gollwitzer & Sheeran, *Advances in Experimental Social Psychology*, 2006). An action item with an +owner and a date is an implementation intention; one without is a wish. Likewise, Locke and +Latham's goal-setting research — the most replicated result in organizational psychology — shows +specific, difficult goals reliably outperform "do your best" vagueness (Edwin A. Locke & Gary P. +Latham, "Building a Practically Useful Theory of Goal Setting and Task Motivation," +*American Psychologist*, 2002). + +## Follow-through is the dropped phase + +Steven Rogelberg's meeting research finds the post-meeting phase is where most meeting value +leaks: actions go unrecorded, unowned, or unrevisited, and recurring meetings quietly re-discuss +last week's conclusions (*The Surprising Science of Meetings*, Oxford University Press, 2019). +His prescriptions map one-to-one onto this skill's mechanics: end with a recap of commitments +(the agenda builder's mandatory closing slot), record owner + deadline for each (the extractor's +output shape), and open the next meeting against the last list (the emitted markdown checklist is +built to be pasted into the next agenda's pre-read). + +## Why extraction is deterministic + +The extractor uses fixed patterns — checkboxes, `ACTION:`/`TODO:` prefixes, "@name will …", +"Name will … by date" — rather than judgment. Two reasons. First, auditability: anyone can look at +a line of notes and know whether it will extract, so the discipline is teachable ("write it as +`ACTION: @sam … by Friday` and it will never be lost"). Second, honesty: a fuzzy extractor that +infers actions from vibes recreates the original problem — commitments nobody actually made. If +the notes contain no extractable actions, the correct output is the uncomfortable summary line +saying so, not an invented checklist. + +## Sources + +1. David Allen, *Getting Things Done: The Art of Stress-Free Productivity*, Penguin, 2001 + (next-action discipline; the verb-owner test). +2. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019 + (follow-through as the dropped phase; end-with-recap prescription). +3. Atlassian Team Playbook, "DACI: Decision-making framework" (single Driver/Accountable; + two owners = zero owners). +4. George T. Doran, "There's a S.M.A.R.T. Way to Write Management's Goals and Objectives," + *Management Review*, 1981 (time-bound as a first-class criterion). +5. Peter M. Gollwitzer, "Implementation Intentions: Strong Effects of Simple Plans," *American + Psychologist*, 1999 (and Gollwitzer & Sheeran 2006 meta-analysis) — who/when/where plans get + acted on; wishes don't. +6. Edwin A. Locke & Gary P. Latham, "Building a Practically Useful Theory of Goal Setting and + Task Motivation," *American Psychologist*, 2002 (specific goals beat vague ones). diff --git a/productivity/meetings/skills/meetings/references/agenda_discipline.md b/productivity/meetings/skills/meetings/references/agenda_discipline.md new file mode 100644 index 00000000..2ed259ea --- /dev/null +++ b/productivity/meetings/skills/meetings/references/agenda_discipline.md @@ -0,0 +1,96 @@ +# Agenda Discipline — questions, timeboxes, decisions first, an owner, a pre-read + +The second discipline of the `meetings` skill: a meeting that passed the cost gate still fails if +its agenda is a list of nouns. This file documents the canon behind `agenda_builder.py` — why every +topic must carry a desired outcome, why timeboxes are budgets rather than suggestions, why decision +topics go first, and why the pre-read and the closing actions-recap slot are non-negotiable. + +## Agendas as questions (the desired-outcome rule) + +Steven Rogelberg's meeting research produced a counterintuitive result: merely *having* an agenda +does not predict meeting effectiveness — attendees rate agenda'd and agenda-less meetings about the +same. What predicts effectiveness is an agenda built from **questions to be answered**, because a +question forces the organizer to know what "done" looks like, makes the right attendee list +self-evident (invite whoever is needed to answer), and gives the meeting a natural end (the +question is answered) (Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, +2019; and his HBR guidance "Why Your Meetings Stink — and What to Do About It," *Harvard Business +Review*, January–February 2019). + +`agenda_builder.py` operationalizes this as a hard rule: **no desired outcome, no agenda slot.** +A topic supplied with an empty outcome field is refused by name (exit 2). "Roadmap" is a noun; +"Decide whether the roadmap slips two weeks" is a meeting. + +## Timeboxing (Parkinson's law made operational) + +"Work expands so as to fill the time available for its completion" (C. Northcote Parkinson, +"Parkinson's Law," *The Economist*, 1955). Discussion is the purest case: an unbounded topic +consumes whatever remains. The counter-discipline is the timebox — a fixed allocation that ends +the topic whether or not the room feels finished — which is the load-bearing mechanism of Scrum's +every ceremony: the sprint, the daily scrum's fifteen minutes, the review, the retro (Jeff +Sutherland, *Scrum: The Art of Doing Twice the Work in Half the Time*, Crown Business, 2014; +Schwaber & Sutherland, *The Scrum Guide*). The builder therefore: + +- requires per-topic minutes and refuses an agenda whose timeboxes (plus the closing buffer) + exceed the meeting length, naming the exact overflow (exit 3) — a budget you can silently + overdraw is not a budget; +- prints start–end offsets per topic so the running meeting can see the clock, not just the list; +- treats slack as a feature: finish early, end early. + +## Decisions first + +The builder sorts topics whose outcome starts with **decide / choose / approve** ahead of +discuss/inform topics. Three converging reasons: + +1. **Energy and attention decay.** Decision quality degrades as a session wears on; putting the + decision in the last five minutes buys either a rushed call or a follow-up meeting. HBR's + practical agenda guidance consistently recommends sequencing the most important items first — + also because meetings compress from the end when they start late or run over. +2. **Grove's mission-meeting logic.** If the meeting exists to produce a decision (Grove, *High + Output Management*, 1983), the decision *is* the meeting; everything else is garnish and should + be treated as such — or moved to the pre-read. +3. **Inform topics are the weakest claim on synchronous time.** If an inform topic gets squeezed + out by the sort order, that is the system working: it likely belonged in the memo anyway. + +## The owner role + +Every topic carries a named owner — the person who drives that timebox, states the question, and +is accountable for its outcome landing in the actions recap. The meeting as a whole has an owner +too (checked upstream by the cost gate). This is Rogelberg's "meeting leader as steward of +everyone's time" plus Grove's chairman-accountability rule, made explicit per-slot so +responsibility cannot diffuse across the invite list. + +## The pre-read line + +Every generated agenda opens with a pre-read instruction: circulate the material ahead; the +meeting starts assuming it was read. This is Amazon's narrative-memo practice inverted for +ordinary meetings — Bezos's argument for the six-page memo (documented in Amazon shareholder +letters and public remarks) is that writing forces complete thoughts and reading in advance means +the meeting spends its synchronous minutes on the decision, not the download. A meeting that +re-presents the pre-read punishes exactly the people who prepared. + +## The closing actions-recap slot + +The builder reserves a mandatory 5-minute closing slot: read every action aloud — owner and due +date — before anyone leaves. Two safety properties motivate making it structural rather than +aspirational. First, follow-through is the most commonly dropped meeting phase (Rogelberg), and a +slot on the clock cannot be skipped invisibly. Second, in decision meetings, dissent and confusion +surface late; a recap slot is the last cheap moment to catch "wait, that's not what I agreed to" — +which only happens if people feel safe saying it, Amy Edmondson's core argument for psychological +safety in decision-making teams (Amy C. Edmondson, *The Fearless Organization*, Wiley, 2018). + +## Sources + +1. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019 + (agenda-as-questions; leader as steward; follow-through gap). +2. Steven G. Rogelberg, "Why Your Meetings Stink — and What to Do About It," *Harvard Business + Review*, January–February 2019 (question-shaped agendas; invite-list implications). +3. C. Northcote Parkinson, "Parkinson's Law," *The Economist*, 1955 (work expands to fill the time + available — the case for timeboxes). +4. Jeff Sutherland, *Scrum: The Art of Doing Twice the Work in Half the Time*, Crown Business, + 2014 (timeboxing as the core ceremony mechanism; also Schwaber & Sutherland, *The Scrum Guide*). +5. Andrew S. Grove, *High Output Management*, Random House, 1983 (process vs mission meetings; + the decision is the meeting; chairman accountability). +6. Jeff Bezos, Amazon shareholder letters + public remarks (narrative memo pre-read; synchronous + time spent on the decision, not the download). +7. Amy C. Edmondson, *The Fearless Organization*, Wiley, 2018 (psychological safety in decision + meetings — why the recap slot must invite dissent). diff --git a/productivity/meetings/skills/meetings/references/meeting_cost_canon.md b/productivity/meetings/skills/meetings/references/meeting_cost_canon.md new file mode 100644 index 00000000..b739ac9b --- /dev/null +++ b/productivity/meetings/skills/meetings/references/meeting_cost_canon.md @@ -0,0 +1,91 @@ +# Meeting Cost Canon — what a meeting really costs, and the should-this-exist gate + +The first discipline of the `meetings` skill is treating a meeting as a purchase. Nobody would sign +a $600 invoice without asking what it buys, yet a six-person hour at a $90 fully-loaded rate is +exactly that — approved with one calendar click. This file documents the canon behind +`meeting_cost_calculator.py`: why the price must be said out loud, why the refocus overhead is real, +and why "no decision, no meeting" is the correct default. + +## The scale of the problem + +- **Executives average ~23 hours a week in meetings**, up from under 10 hours in the 1960s, and 71% + of senior managers surveyed said meetings are unproductive and inefficient (Leslie Perlow, + Constance Noonan Hadley & Eunice Eun, "Stop the Meeting Madness," *Harvard Business Review*, + July–August 2017). Perlow et al.'s core finding: dysfunctional meeting cultures self-perpetuate + because no individual meeting ever gets billed for its cost. +- **Most meetings fail by design, not execution.** Steven Rogelberg's research program (surveying + thousands of employees and leaders) finds roughly half of meeting time is rated ineffective by + attendees, and the strongest lever is what happens *before* the meeting: whether it should exist, + who is invited, and what the agenda demands (Steven G. Rogelberg, *The Surprising Science of + Meetings*, Oxford University Press, 2019). +- **Deleting meetings works.** Shopify started 2023 by removing all recurring meetings with more + than two people (~12,000 events) and later shipped an internal **meeting cost calculator** that + embeds a dollar figure into every calendar invite — a typical 30-minute, 3-person meeting priced + between $700 and $1,600. The point was behavioral: people decline purchases they would have + accepted as invites. +- **Industry surveys agree on the waste.** Doodle's "State of Meetings" report estimated poorly + organized meetings cost businesses in the tens of billions annually across the US and UK alone; + Atlassian's long-running "you waste a lot of time at work" meeting statistics put the average + employee at ~31 hours per month in unproductive meetings. Exact figures vary by survey; the + direction never does. + +## The refocus overhead (`--include-refocus`) + +The invite says 30 minutes; the attention bill is larger. Gloria Mark's interruption research at +UC Irvine measured an average of about **23 minutes to return to the interrupted task** after a +context switch (Gloria Mark, Daniela Gudith & Ulrich Klocke, "The Cost of Interrupted Work: More +Speed and Stress," *CHI*, 2008). A mid-morning meeting is an interruption for every attendee doing +focused work, which is why the calculator's `--include-refocus` flag adds 23 minutes per attendee +at the same hourly rate. It is an estimate — deliberately conservative (one refocus, not two) — and +it routinely doubles the sticker price of short meetings, which is the point. + +## The gate: decision, agenda, owner + +The calculator refuses a MEET verdict unless three things exist: + +1. **A decision** (`--has-decision`). Andy Grove's taxonomy separates *process* meetings + (one-on-ones, staff reviews — scheduled, informational) from *mission* meetings, which exist to + produce a decision (Andrew S. Grove, *High Output Management*, Random House, 1983). An ad-hoc + meeting with no decision is a status update wearing a meeting's clothes — Grove's rule was that + a mission meeting that produces no decision is a failure of the chairman, not the attendees. + Status flows async: Amazon's practice replaces presentation meetings with a silently-read + six-page narrative memo, and its "two-pizza team" rule caps the audience — both documented across + Jeff Bezos's shareholder letters and his explanation that "PowerPoint-style presentations + somehow give permission to gloss over ideas." +2. **An agenda** (`--has-agenda`). Rogelberg's finding is blunt: agendas per se don't correlate + with meeting quality — *prepared, question-shaped* agendas do (see + [`agenda_discipline.md`](agenda_discipline.md)). The gate checks existence; the agenda builder + enforces quality. +3. **An owner** (`--has-owner`). Perlow et al. and Rogelberg converge here: someone must be + accountable for the meeting achieving its outcome and for follow-through afterwards. A meeting + nobody owns produces minutes nobody reads. + +Missing the decision → **ASYNC** (the cheapest meeting is no meeting). Missing agenda or owner with +a real decision on the table → **NOT-READY**: the meeting may deserve to exist, but not yet. + +## Why the verdict is ternary, not a score + +A 0–100 "meeting quality score" invites negotiation. Three verdicts with hard edges do not: +ASYNC ends the conversation (write the memo), NOT-READY names exactly what to fix, and MEET prints +the invoice — total cost plus cost-per-minute — so the agenda's timeboxes get budgeted like money. +This mirrors Shopify's design insight: the number changes behavior only when it arrives *before* +the commitment is made. + +## Sources + +1. Leslie Perlow, Constance Noonan Hadley & Eunice Eun, "Stop the Meeting Madness," *Harvard + Business Review*, July–August 2017 (23 hrs/week; self-perpetuating meeting cultures). +2. Steven G. Rogelberg, *The Surprising Science of Meetings*, Oxford University Press, 2019 + (meeting-science canon; pre-meeting levers dominate). +3. Shopify meeting-cost calculator + recurring-meeting purge, 2023 (cost surfaced in the invite; + ~12,000 events deleted; widely covered, incl. Bloomberg/Reuters reporting). +4. Jeff Bezos, Amazon shareholder letters + public remarks (silent six-page narrative memo; + two-pizza team rule capping meeting size). +5. Doodle, "State of Meetings" report (survey-based estimate of the cost of poorly organized + meetings across US/UK). +6. Atlassian, "You Waste a Lot of Time at Work" meeting statistics (~31 unproductive meeting + hours/month per employee). +7. Andrew S. Grove, *High Output Management*, Random House, 1983 (meetings as the medium of + managerial work; process vs mission meetings; a decision-less mission meeting is a failure). +8. Gloria Mark, Daniela Gudith & Ulrich Klocke, "The Cost of Interrupted Work," *CHI* 2008 + (~23-minute refocus after interruption — the basis of `--include-refocus`). diff --git a/productivity/meetings/skills/meetings/scripts/action_item_extractor.py b/productivity/meetings/skills/meetings/scripts/action_item_extractor.py new file mode 100644 index 00000000..eee9d6cc --- /dev/null +++ b/productivity/meetings/skills/meetings/scripts/action_item_extractor.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +"""action_item_extractor.py — Turn raw meeting notes into an owned, dated action checklist. + +The `meetings` skill's third discipline: a meeting that ends without owned, dated actions was +theater. This script parses raw notes (from --input FILE or stdin) and extracts action items via +deterministic patterns: + + - "- [ ] ..." / "* [ ] ..." markdown checkboxes + - "ACTION: ..." / "TODO: ..." explicit prefixes (case-insensitive) + - "@name will ..." / "@name to ..." mention-owned commitments + - "Name will ... by " prose commitments (capitalized name, pronouns excluded) + +For every item it captures the OWNER (an @mention leading the item, or the leading +"Name will/to" name) and the DUE DATE ("by/due/before ") when present, then flags: + + ORPHAN — no owner captured. Unowned actions die; assign before posting. + NO-DUE — owner but no date. Undated actions drift; date them now. + +Output: a markdown checklist grouped by owner (orphans last, loudly) + summary counts, or --json. + +NO LLM CALLS. Pure regex + grouping. Stdlib only. Nothing is sent anywhere. + +Usage: + python action_item_extractor.py --input notes.md + cat notes.md | python action_item_extractor.py + python action_item_extractor.py --sample --json +""" + +import argparse +import json +import re +import sys +from typing import Any, Dict, List, Optional, Tuple + +CHECKBOX_RE = re.compile(r"^\s*[-*]\s*\[\s*\]\s*(?P.+)$") +PREFIX_RE = re.compile(r"^\s*(?:ACTION|TODO)\s*:\s*(?P.+)$", re.IGNORECASE) +MENTION_OWNED_RE = re.compile(r"^\s*@(?P[A-Za-z][\w.\-]*)\s+(?:will|to)\s+(?P.+)$") +NAME_WILL_RE = re.compile(r"^\s*(?P[A-Z][a-zA-Z]+)\s+(?:will|to)\s+(?P.+)$") +MENTION_HEAD_RE = re.compile(r"^\s*@(?P[A-Za-z][\w.\-]*)\s*[:,—-]?\s*(?P.*)$") +INNER_NAME_WILL_RE = re.compile(r"^(?P[A-Z][a-zA-Z]+)\s+(?:will|to)\s+(?P.+)$") + +_DATE_TOKEN = ( + r"(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|today|tomorrow|eod|eow" + r"|\d{4}-\d{2}-\d{2}" + r"|\d{1,2}/\d{1,2}(?:/\d{2,4})?" + r"|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\.?\s+\d{1,2})" +) +DUE_RE = re.compile(r"\b(?:by|due|before)\s+(?P" + _DATE_TOKEN + r")\b", re.IGNORECASE) + +PRONOUNS = {"We", "I", "It", "They", "This", "That", "You", "He", "She", + "Everyone", "Someone", "Anybody", "Nobody", "Team", "The"} + +# Sentence-initial capitalized words that are never a person committing to an action. +NON_OWNER_WORDS = PRONOUNS | { + "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", + "January", "February", "March", "April", "May", "June", "July", "August", + "September", "October", "November", "December", + "Today", "Tomorrow", "Tonight", "Next", "Last", "There", + "Everything", "Nothing", "Something", "Everybody", +} + +# "Name will " is a prediction or status, not a commitment +# ("Friday will be a half day", "Design will need another pass"). +STATIVE_CONTINUATIONS = {"be", "need", "probably", "likely", "not", "also", + "still", "never", "just", "only"} + + +def _is_commitment(text: str, owner: str) -> bool: + """A 'Name will/to ...' line is a commitment only if Name can be a person + and what follows reads as an action, not a stative prediction.""" + if owner in NON_OWNER_WORDS: + return False + first = text.strip().split(None, 1)[0].lower() if text.strip() else "" + return first not in STATIVE_CONTINUATIONS + +EPILOG = """\ +exit codes: + 0 success — checklist emitted (ORPHAN / NO-DUE items are flagged in the output, not fatal) + 2 no input — neither --input, --sample, nor piped stdin was provided (or file unreadable) + +--help and --sample exit 0. +""" + + +def _extract_owner_and_text(text: str, owner: Optional[str]) -> Tuple[Optional[str], str]: + """Refine owner from an already-captured action text. + + Only a mention that LEADS the item confers ownership ("@sam: book the room", + "@sam to book the room"). A mid-text mention is the task's object, not its + owner ("follow up with @sam") — those stay ORPHAN for a human to assign. + """ + m = MENTION_OWNED_RE.match(text.strip()) + if m: # "@owner will/to rest" at the head — strip the owner phrase from the text + return m.group("owner"), m.group("text") + m = MENTION_HEAD_RE.match(text.strip()) + if m: + return m.group("owner"), (m.group("text") or text) + m = INNER_NAME_WILL_RE.match(text.strip()) + if m and _is_commitment(m.group("text"), m.group("owner")): + return m.group("owner"), m.group("text") + return owner, text + + +def extract(notes: str) -> List[Dict[str, Any]]: + items: List[Dict[str, Any]] = [] + for lineno, raw in enumerate(notes.splitlines(), 1): + line = raw.rstrip() + if not line.strip(): + continue + owner: Optional[str] = None + text: Optional[str] = None + + m = CHECKBOX_RE.match(line) + if m: + text = m.group("text").strip() + else: + m = PREFIX_RE.match(line) + if m: + text = m.group("text").strip() + else: + m = MENTION_OWNED_RE.match(line) + if m: + owner, text = m.group("owner"), m.group("text").strip() + else: + m = NAME_WILL_RE.match(line) + if m and _is_commitment(m.group("text"), m.group("owner")): + owner, text = m.group("owner"), m.group("text").strip() + + if text is None: + continue + + # Refine only when no owner was captured at the head of the line — an + # already-owned commitment may legitimately name other people in its text + # ("@maria will ask @sam to review") without transferring ownership. + if owner is None: + owner, text = _extract_owner_and_text(text, owner) + due_m = DUE_RE.search(line) + due = due_m.group("due") if due_m else None + text = text.rstrip(".").strip() + + flags: List[str] = [] + if not owner: + flags.append("ORPHAN") + if not due: + flags.append("NO-DUE") + items.append({"line": lineno, "text": text, "owner": owner, "due": due, "flags": flags}) + return items + + +def summarize(items: List[Dict[str, Any]]) -> Dict[str, int]: + return { + "total": len(items), + "owned": sum(1 for i in items if i["owner"]), + "orphans": sum(1 for i in items if "ORPHAN" in i["flags"]), + "no_due": sum(1 for i in items if "NO-DUE" in i["flags"]), + } + + +def render_markdown(items: List[Dict[str, Any]]) -> str: + out: List[str] = ["## Action Items", ""] + if not items: + out.append("_No action items detected. If decisions were made, they left no owners — " + "that is worth fixing in the room next time._") + return "\n".join(out) + + owners = sorted({i["owner"] for i in items if i["owner"]}, key=str.lower) + for owner in owners: + out.append(f"### {owner}") + for i in items: + if i["owner"] == owner: + due = f" — due {i['due']}" if i["due"] else " **[NO-DUE — date it now]**" + out.append(f"- [ ] {i['text']}{due}") + out.append("") + + orphans = [i for i in items if not i["owner"]] + if orphans: + out.append("### (unassigned) — ORPHANS, assign before posting") + for i in orphans: + due = f" — due {i['due']}" if i["due"] else "" + out.append(f"- [ ] {i['text']}{due} **[ORPHAN]**") + out.append("") + + s = summarize(items) + out.append(f"**Summary:** {s['total']} actions · {s['owned']} owned · " + f"{s['orphans']} ORPHAN · {s['no_due']} NO-DUE") + if s["orphans"]: + out.append("") + out.append("_Every action item has an owner and a date — or it is not an action item. " + "Assign the orphans while the room still remembers agreeing to them._") + return "\n".join(out) + + +SAMPLE_NOTES = """\ +Notes — Q3 planning sync + +Maria will send the pricing one-pager by Friday. +We agreed the roadmap shape looks fine overall. +- [ ] update the launch checklist +ACTION: @sam to book the security review by 2026-07-24 +TODO: draft the customer announcement email +Alex will confirm vendor pricing. +It will probably rain during the offsite. +""" + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Extract owned, dated action items from raw meeting notes (flags ORPHAN / NO-DUE).", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=EPILOG, + ) + p.add_argument("--input", metavar="FILE", help="Notes file to read (otherwise stdin is used)") + p.add_argument("--sample", action="store_true", help="Run on the embedded example notes") + p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of markdown") + args = p.parse_args(argv) + + if args.sample: + notes = SAMPLE_NOTES + elif args.input: + try: + with open(args.input, "r", encoding="utf-8") as f: + notes = f.read() + except OSError as e: + print(f"error: cannot read {args.input}: {e}", file=sys.stderr) + return 2 + elif not sys.stdin.isatty(): + notes = sys.stdin.read() + else: + p.print_help() + print("\nerror: provide --input FILE, pipe notes on stdin, or use --sample", file=sys.stderr) + return 2 + + items = extract(notes) + if args.json: + print(json.dumps({"items": items, "summary": summarize(items)}, indent=2)) + else: + print(render_markdown(items)) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/meetings/skills/meetings/scripts/agenda_builder.py b/productivity/meetings/skills/meetings/scripts/agenda_builder.py new file mode 100644 index 00000000..892018ea --- /dev/null +++ b/productivity/meetings/skills/meetings/scripts/agenda_builder.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +"""agenda_builder.py — Build a timeboxed, decision-first agenda where every topic has an outcome. + +The `meetings` skill's second discipline. Feed it topics as + --topic "title:desired_outcome:minutes:owner" +plus the total meeting length (--length). It enforces the agenda canon deterministically: + + 1. NO DESIRED OUTCOME, NO AGENDA SLOT — any topic with an empty outcome is refused, by name. + 2. TIMEBOXES ARE A BUDGET — topic timeboxes + a mandatory 5-minute closing "actions recap" + buffer must fit --length, or the agenda is refused with the exact overflow. + 3. DECISIONS FIRST — topics whose outcome starts with decide/choose/approve sort before + discuss/inform topics, so the decision gets fresh brains, not the last five minutes. + 4. PRE-READ — the agenda opens with a pre-read line (circulate ahead; the meeting assumes it + was read) and closes with the actions-recap slot (every action gets an owner + a date). + +NO LLM CALLS. Pure parsing + ordering + arithmetic. Stdlib only. Nothing is sent or scheduled. + +Usage: + python agenda_builder.py --length 45 \\ + --topic "Q3 pricing:Decide usage-based vs seat-based:15:maria" \\ + --topic "Launch risks:Discuss open launch blockers:15:sam" + python agenda_builder.py --sample + python agenda_builder.py --sample --json +""" + +import argparse +import json +import sys +from typing import Any, Dict, List + +CLOSING_BUFFER_MIN = 5 # mandatory closing "actions recap" slot +DECISION_PREFIXES = ("decide", "choose", "approve") + +EPILOG = """\ +exit codes: + 0 agenda built (timeboxes + 5-min closing buffer fit --length; all outcomes present) + 1 usage error — malformed --topic spec or bad/missing flags (never a verdict) + 2 refused — one or more topics has an empty desired outcome (named in output) + 3 refused — timeboxes + 5-min closing buffer exceed --length (overflow named in output) + +--help and --sample exit 0. +""" + + +def parse_topic(spec: str) -> Dict[str, Any]: + # Split the two structured fields (minutes, owner) from the right, then the + # title at the first colon — so a desired outcome may itself contain colons. + parts = spec.rsplit(":", 2) + if len(parts) != 3 or ":" not in parts[0]: + raise ValueError( + f'malformed --topic "{spec}" — expected 4 colon-separated fields: ' + '"title:desired_outcome:minutes:owner" (colons are allowed inside ' + "desired_outcome; the title must not contain one)" + ) + head, minutes_raw, owner = parts + title, outcome = head.split(":", 1) + title, outcome, minutes_raw, owner = (p.strip() for p in (title, outcome, minutes_raw, owner)) + try: + minutes = int(minutes_raw) + except ValueError: + raise ValueError(f'malformed --topic "{spec}" — minutes must be an integer, got "{minutes_raw}"') + if minutes < 1: + raise ValueError(f'malformed --topic "{spec}" — minutes must be >= 1') + if not title: + raise ValueError(f'malformed --topic "{spec}" — title is empty') + is_decision = outcome.lower().startswith(DECISION_PREFIXES) + return {"title": title, "outcome": outcome, "minutes": minutes, "owner": owner, + "kind": "decision" if is_decision else "discuss/inform"} + + +def build(topics: List[Dict[str, Any]], length: int) -> Dict[str, Any]: + empty = [t["title"] for t in topics if not t["outcome"]] + if empty: + return { + "status": "REFUSED-NO-OUTCOME", + "exit_code": 2, + "offenders": empty, + "message": ("No desired outcome, no agenda slot. Topics refused: " + + ", ".join(f'"{t}"' for t in empty) + + ". Go get the outcome, then rebuild."), + } + + topic_minutes = sum(t["minutes"] for t in topics) + budget = topic_minutes + CLOSING_BUFFER_MIN + if budget > length: + overflow = budget - length + return { + "status": "REFUSED-OVERFLOW", + "exit_code": 3, + "topic_minutes": topic_minutes, + "closing_buffer": CLOSING_BUFFER_MIN, + "length": length, + "overflow_minutes": overflow, + "message": (f"Timeboxes ({topic_minutes} min) + closing actions-recap buffer " + f"({CLOSING_BUFFER_MIN} min) = {budget} min, but the meeting is " + f"{length} min — {overflow} min over. Cut a topic, shrink a timebox, " + "or move an inform topic to the pre-read."), + } + + # Decisions first (stable within each group — caller's order is preserved otherwise). + ordered = sorted(topics, key=lambda t: 0 if t["kind"] == "decision" else 1) + + cursor = 0 + slots: List[Dict[str, Any]] = [] + for t in ordered: + slots.append({**t, "start_min": cursor, "end_min": cursor + t["minutes"]}) + cursor += t["minutes"] + recap_start = length - CLOSING_BUFFER_MIN + slack = recap_start - cursor + + return { + "status": "OK", + "exit_code": 0, + "length": length, + "topic_minutes": topic_minutes, + "closing_buffer": CLOSING_BUFFER_MIN, + "slack_minutes": slack, + "slots": slots, + "recap": {"title": "Actions recap", "start_min": recap_start, "end_min": length, + "outcome": "Read every action aloud: owner + due date, or it is not an action"}, + } + + +def render_markdown(r: Dict[str, Any]) -> str: + out: List[str] = [] + out.append(f"# Agenda — {r['length']} minutes, {len(r['slots'])} topics") + out.append("") + out.append("**Pre-read:** circulate the relevant doc(s) at least a day ahead. " + "The meeting starts assuming the pre-read was read — no recap slot for skippers.") + out.append("") + out.append("| # | Timebox | Topic | Desired outcome | Owner |") + out.append("|---|---------|-------|-----------------|-------|") + for idx, s in enumerate(r["slots"], 1): + out.append(f"| {idx} | {s['start_min']:02d}–{s['end_min']:02d} min ({s['minutes']} min) " + f"| {s['title']} | {s['outcome']} | {s['owner']} |") + rec = r["recap"] + out.append(f"| ✔ | {rec['start_min']:02d}–{rec['end_min']:02d} min ({CLOSING_BUFFER_MIN} min) " + f"| **{rec['title']}** | {rec['outcome']} | meeting owner |") + out.append("") + if r["slack_minutes"] > 0: + out.append(f"_{r['slack_minutes']} min of slack before the recap — if topics finish early, " + "end early. Nobody has ever complained about a meeting ending early._") + else: + out.append("_Zero slack — hold the timeboxes or the recap gets eaten. " + "The recap is the most valuable slot; protect it._") + return "\n".join(out) + + +SAMPLE_ARGS = { + "length": 45, + "topics": [ + "Metrics review:Inform team of the activation trend:5:alex", + "Q3 pricing:Decide usage-based vs seat-based:15:maria", + "Launch risks:Discuss open launch blockers:15:sam", + ], +} + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Build a timeboxed, decision-first agenda (refuses empty outcomes and overflow).", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=EPILOG, + ) + p.add_argument("--topic", action="append", default=[], metavar="SPEC", + help='Repeatable. "title:desired_outcome:minutes:owner" ' + '(outcome starting decide/choose/approve sorts first)') + p.add_argument("--length", type=int, help="Total meeting length in minutes") + p.add_argument("--sample", action="store_true", help="Run the embedded 3-topic sample") + p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of markdown") + args = p.parse_args(argv) + + if args.sample: + length, specs = SAMPLE_ARGS["length"], SAMPLE_ARGS["topics"] + elif args.topic and args.length is not None: + length, specs = args.length, args.topic + else: + p.print_help() + print("\nerror: provide --length and at least one --topic, or --sample", file=sys.stderr) + return 1 + + if length < CLOSING_BUFFER_MIN + 1: + print(f"error: --length must be at least {CLOSING_BUFFER_MIN + 1} minutes " + f"({CLOSING_BUFFER_MIN}-min closing buffer is mandatory)", file=sys.stderr) + return 1 + + try: + topics = [parse_topic(s) for s in specs] + except ValueError as e: + print(f"error: {e}", file=sys.stderr) + return 1 + + result = build(topics, length) + if args.json: + print(json.dumps(result, indent=2)) + elif result["status"] == "OK": + print(render_markdown(result)) + else: + print(f"REFUSED ({result['status']}): {result['message']}", file=sys.stderr) + return result["exit_code"] + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py b/productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py new file mode 100644 index 00000000..b361e127 --- /dev/null +++ b/productivity/meetings/skills/meetings/scripts/meeting_cost_calculator.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""meeting_cost_calculator.py — Price a meeting in real dollars and gate whether it should exist. + +The `meetings` skill's first discipline: a meeting is a purchase. This script prices it +(attendees x minutes x hourly rate, optionally + a 23-minute refocus overhead per attendee, +per Gloria Mark's interruption-recovery research) and then applies the should-this-exist gate: + + 1. Is there a DECISION to make? (--has-decision) -> without one, it's a status update + 2. Is there an AGENDA? (--has-agenda) -> without one, nobody can prepare + 3. Is there a named meeting OWNER? (--has-owner) -> without one, nobody is accountable + +Verdicts: + ASYNC — no decision needed. Send a memo/thread instead. The cheapest meeting is no meeting. + NOT-READY — a decision exists but the agenda and/or owner is missing (named explicitly). + MEET — decision + agenda + owner all present. Cost printed, plus a cost-per-minute line + so agenda timeboxes get budgeted like money. + +NO LLM CALLS. Pure arithmetic + thresholds. Stdlib only. Never sends or schedules anything. + +Usage: + python meeting_cost_calculator.py --attendees 6 --minutes 60 --avg-rate 90 \ + --include-refocus --has-decision --has-agenda --has-owner + python meeting_cost_calculator.py --attendees 8 --minutes 30 # no decision -> ASYNC + python meeting_cost_calculator.py --sample + python meeting_cost_calculator.py --sample --json +""" + +import argparse +import json +import sys +from typing import Any, Dict, List + +REFOCUS_MINUTES = 23 # avg time to refocus after a context switch (Gloria Mark, UC Irvine) + +EPILOG = """\ +exit codes: + 0 MEET — decision + agenda + owner present; cost printed + 1 usage error — bad or missing flags (never a verdict) + 2 ASYNC — no decision needed; recommend a memo/thread instead of a meeting + 3 NOT-READY — decision exists but agenda and/or owner missing (named in output) + +--help and --sample always exit per the verdict rules above (--sample is a MEET scenario, exit 0). +""" + + +def evaluate(attendees: int, minutes: int, avg_rate: float, include_refocus: bool, + has_decision: bool, has_agenda: bool, has_owner: bool) -> Dict[str, Any]: + direct_cost = round(attendees * (minutes / 60.0) * avg_rate, 2) + refocus_cost = round(attendees * (REFOCUS_MINUTES / 60.0) * avg_rate, 2) if include_refocus else 0.0 + total_cost = round(direct_cost + refocus_cost, 2) + cost_per_minute = round(total_cost / minutes, 2) if minutes else 0.0 + + missing: List[str] = [] + if not has_agenda: + missing.append("agenda") + if not has_owner: + missing.append("meeting owner") + + if not has_decision: + verdict, exit_code = "ASYNC", 2 + headline = ( + "No decision to make -> this is a status update, not a meeting. " + f"Send it as a memo/thread and save the ${total_cost:,.2f}. " + "The cheapest meeting is the one you don't hold." + ) + elif missing: + verdict, exit_code = "NOT-READY", 3 + headline = ( + "A decision exists, but the meeting is not ready to be called. " + f"Missing: {' and '.join(missing)}. " + "Fix that, re-run the gate, and only then send the invite." + ) + else: + verdict, exit_code = "MEET", 0 + headline = ( + "Gate passed: decision + agenda + owner. This meeting has earned its slot. " + f"It costs ${total_cost:,.2f} — budget the agenda timeboxes like money." + ) + + return { + "inputs": { + "attendees": attendees, + "minutes": minutes, + "avg_rate_per_hour": avg_rate, + "include_refocus": include_refocus, + "has_decision": has_decision, + "has_agenda": has_agenda, + "has_owner": has_owner, + }, + "direct_cost": direct_cost, + "refocus_cost": refocus_cost, + "refocus_minutes_per_attendee": REFOCUS_MINUTES if include_refocus else 0, + "total_cost": total_cost, + "cost_per_minute": cost_per_minute, + "missing": missing, + "verdict": verdict, + "exit_code": exit_code, + "headline": headline, + } + + +def render_human(r: Dict[str, Any]) -> str: + i = r["inputs"] + out: List[str] = [] + out.append("Meeting Cost Gate (should this meeting exist?)") + out.append("=" * 64) + out.append(f" Attendees: {i['attendees']} Length: {i['minutes']} min " + f"Rate: ${i['avg_rate_per_hour']:,.0f}/hr") + out.append(f" Direct cost: ${r['direct_cost']:>10,.2f} " + f"({i['attendees']} x {i['minutes']} min x ${i['avg_rate_per_hour']:,.0f}/hr)") + if i["include_refocus"]: + out.append(f" Refocus cost: ${r['refocus_cost']:>10,.2f} " + f"({REFOCUS_MINUTES} min refocus overhead per attendee)") + out.append(f" TOTAL COST: ${r['total_cost']:>10,.2f}") + out.append("") + out.append(f" Gate: decision={'yes' if i['has_decision'] else 'NO'} | " + f"agenda={'yes' if i['has_agenda'] else 'NO'} | " + f"owner={'yes' if i['has_owner'] else 'NO'}") + out.append("") + out.append(f" VERDICT: {r['verdict']}") + out.append(f" {r['headline']}") + if r["verdict"] == "MEET": + out.append("") + out.append(f" Every minute of this meeting costs ${r['cost_per_minute']:,.2f} — " + "price each agenda topic's timebox against that.") + return "\n".join(out) + + +SAMPLE = dict(attendees=6, minutes=60, avg_rate=90.0, include_refocus=True, + has_decision=True, has_agenda=True, has_owner=True) + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Price a meeting and gate whether it should exist (ASYNC / NOT-READY / MEET).", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=EPILOG, + ) + p.add_argument("--attendees", type=int, help="Number of attendees") + p.add_argument("--minutes", type=int, help="Scheduled meeting length in minutes") + p.add_argument("--avg-rate", type=float, default=90.0, + help="Average fully-loaded hourly rate per attendee in dollars (default: 90)") + p.add_argument("--include-refocus", action="store_true", + help=f"Add a {REFOCUS_MINUTES}-minute refocus overhead per attendee to the cost") + p.add_argument("--has-decision", action="store_true", + help="A specific decision will be made in this meeting") + p.add_argument("--has-agenda", action="store_true", + help="A timeboxed agenda with desired outcomes exists") + p.add_argument("--has-owner", action="store_true", + help="A named meeting owner is accountable for outcome + follow-through") + p.add_argument("--sample", action="store_true", help="Run the embedded sample (a MEET scenario)") + p.add_argument("--json", action="store_true", help="Emit machine-readable JSON instead of text") + args = p.parse_args(argv) + + if args.sample: + vals = dict(SAMPLE) + elif args.attendees is not None and args.minutes is not None: + if args.attendees < 1 or args.minutes < 1: + print("error: --attendees and --minutes must both be >= 1", file=sys.stderr) + return 1 + vals = dict(attendees=args.attendees, minutes=args.minutes, avg_rate=args.avg_rate, + include_refocus=args.include_refocus, has_decision=args.has_decision, + has_agenda=args.has_agenda, has_owner=args.has_owner) + else: + p.print_help() + print("\nerror: provide --attendees and --minutes, or --sample", file=sys.stderr) + return 1 + + result = evaluate(**vals) + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + return result["exit_code"] + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/weekly-review/.claude-plugin/plugin.json b/productivity/weekly-review/.claude-plugin/plugin.json new file mode 100644 index 00000000..36ac1ae1 --- /dev/null +++ b/productivity/weekly-review/.claude-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "weekly-review", + "description": "Run a GTD-style weekly review that actually closes loops. Walks David Allen's three-phase ritual — GET CLEAR (collect, process inboxes to zero, empty your head), GET CURRENT (next actions, previous + upcoming calendar, waiting-for, project lists), GET CREATIVE (someday/maybe, new ideas) — backed by three deterministic stdlib scripts: an open-loop scanner that inventories unchecked checkboxes, TODO/FIXME markers, and stale files; a checklist gate that computes completion, names every missing step, and refuses a COMPLETE verdict while any GET CURRENT step is unaccounted for; and a commitment auditor that flags STALLED, NO-NEXT-ACTION, and SOMEDAY-CANDIDATE projects with a 0-100 commitment-health score. The periodic-review counterpart to capture (intake) and reflect (per-conversation reflection).", + "version": "2.11.2", + "author": { + "name": "Alireza Rezvani", + "url": "https://alirezarezvani.com" + }, + "homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/productivity/weekly-review", + "repository": "https://github.com/alirezarezvani/claude-skills", + "license": "MIT", + "skills": [ + "./skills/weekly-review" + ], + "source": { + "build_pattern": "Path-B ritual skill — David Allen's three-phase weekly-review loop preserved + deterministic scanner/gate/auditor scripts added", + "distinct_from": "productivity/reflect (per-conversation reflection on one piece of work, not a recurring cadence over a whole system); productivity/capture (brain-dump intake that feeds the system, not the periodic review that maintains it); project-management sprint retrospectives (a team ceremony about a shared iteration, not a personal trusted-system audit)" + } +} diff --git a/productivity/weekly-review/README.md b/productivity/weekly-review/README.md new file mode 100644 index 00000000..9c3f0fb4 --- /dev/null +++ b/productivity/weekly-review/README.md @@ -0,0 +1,59 @@ +# weekly-review — GTD Weekly Review Loop → Trusted System + +> Close every open loop once a week. David Allen called the weekly review the "critical success +> factor" of the whole method — the system is only trustworthy if it gets reviewed. + +`/cs:weekly-review` walks the three-phase GTD ritual — **GET CLEAR → GET CURRENT → GET CREATIVE** — +and refuses to call the review COMPLETE while any of the five mandatory GET CURRENT steps is +unaccounted for. Deterministic scripts do the inventory and the gating; you (and Claude) do the +thinking. + +## The three phases + +| Phase | What it does | Steps | +|---|---|---| +| **GET CLEAR** | Ground zero: nothing loose, nothing unprocessed | Collect loose inputs · process inboxes to zero · empty your head | +| **GET CURRENT** | The mandatory core — the system matches reality again | Next-action lists · previous calendar · upcoming calendar · waiting-for list · project lists | +| **GET CREATIVE** | Lift your eyes off the runway | Someday/maybe review · capture new ideas | + +Skipping a GET CURRENT step without a stated reason **always** forces an INCOMPLETE verdict. That +gate is the whole point: a weekly review that skims the core is a guilt ritual, not a review. + +## Quick start + +```bash +# 1. Inventory the open loops in a workspace (checkboxes, TODO/FIXME, stale files) +python skills/weekly-review/scripts/open_loop_scanner.py --dir ~/notes --stale-days 14 + +# 2. Walk the checklist, then gate it — names every missing step +python skills/weekly-review/scripts/weekly_review_gate.py --list +python skills/weekly-review/scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8" --skip "9:no someday list yet" + +# 3. Audit the commitment portfolio — STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE + health score +python skills/weekly-review/scripts/commitment_auditor.py --input commitments.json +``` + +Or just say **"run my weekly review"** or run `/cs:weekly-review`. + +## What's in the box + +- **Skill:** [`skills/weekly-review/SKILL.md`](skills/weekly-review/SKILL.md) +- **Agent:** [`agents/cs-weekly-review.md`](agents/cs-weekly-review.md) +- **Command:** [`commands/cs-weekly-review.md`](commands/cs-weekly-review.md) — `/cs:weekly-review` +- **3 stdlib scripts:** `open_loop_scanner.py`, `weekly_review_gate.py`, `commitment_auditor.py` +- **3 references** (5-7 sources each): the GTD weekly-review canon, the psychology of open loops + (Zeigarnik, attention residue, plan-making), and sustainable review-cadence design +- **2 assets:** fillable weekly-review checklist + a full worked example + +## Not the same as + +- **`productivity/reflect`** — reflects on one conversation or piece of work, once. The weekly + review is a recurring cadence over your *whole* system. +- **`productivity/capture`** — the intake funnel (brain dump → actions). Capture feeds the system; + the weekly review is how the system stays trusted. +- **Sprint retrospectives** (`project-management`) — a team ceremony about a shared iteration. This + is a personal trusted-system audit; no team required. + +## License + +MIT. diff --git a/productivity/weekly-review/agents/cs-weekly-review.md b/productivity/weekly-review/agents/cs-weekly-review.md new file mode 100644 index 00000000..d526136b --- /dev/null +++ b/productivity/weekly-review/agents/cs-weekly-review.md @@ -0,0 +1,94 @@ +--- +name: cs-weekly-review +description: Walks a user through a complete GTD weekly review — GET CLEAR (collect, process inboxes to zero, empty your head), GET CURRENT (next actions, previous + upcoming calendar, waiting-for, project lists), GET CREATIVE (someday/maybe, new ideas) — using deterministic scripts to inventory open loops, gate the checklist, and score commitment health. Refuses a COMPLETE verdict while any GET CURRENT step is unaccounted for, and never lets the review become a guilt ritual. Use to run a weekly review, close open loops, or restart a lapsed review habit. +skills: productivity/weekly-review/skills/weekly-review +domain: productivity +model: opus +tools: [Read, Glob, Grep, Bash] +--- + +# Weekly Review Agent + +## Purpose + +The `cs-weekly-review` agent orchestrates the `weekly-review` skill to move a user from "vague +sense of too many open things" to a closed-loop, trusted system in one sitting: + +1. **Inventory** — scan the user's workspace for open loops before asking them to recall anything + (`open_loop_scanner.py`): unchecked checkboxes, TODO/FIXME markers, files gone stale. Evidence + first, memory second. +2. **GET CLEAR** — walk collection: gather loose inputs, process every inbox to zero (clarify, + don't do), then a mind-sweep to empty the head. Two minutes or less per item or it becomes a + next action. +3. **GET CURRENT** — the mandatory core, all five steps: review next-action lists, the previous + calendar (missed commitments become actions), the upcoming calendar (prepare, don't react), the + waiting-for list (chase or drop), and every project for exactly one next action. +4. **Gate** — run `weekly_review_gate.py` with what was actually done. It computes completion, + names every missing step, and returns COMPLETE (exit 0) or INCOMPLETE (exit 2). An unskipped + missing GET CURRENT step always forces INCOMPLETE — no exceptions, no charm. +5. **GET CREATIVE + audit** — review someday/maybe, capture new ideas, then run + `commitment_auditor.py` over the project portfolio: STALLED / NO-NEXT-ACTION / + SOMEDAY-CANDIDATE flags + a 0-100 commitment-health score with the formula shown. +6. **Close** — deliver the verdict, the named gaps, the health score, and the first next action + for the coming week. One sitting, timeboxed, done. + +## Voice + +- Calm and procedural, never preachy. The review is maintenance, not judgment. +- Evidence over recall. Scan first, ask second — the user's memory is exactly what GTD says not to trust. +- Honest about an INCOMPLETE. A skimmed review marked "done" is worse than no review; the gate exists so the word COMPLETE keeps meaning something. +- Restart-friendly. A lapsed habit gets a shorter review and zero guilt, not a lecture. + +## Hard rules + +1. **All five GET CURRENT steps are mandatory.** A step may be skipped only with an explicit + stated reason (`--skip "N:reason"`); an unskipped missing GET CURRENT step forces INCOMPLETE. +2. **Never mark the review COMPLETE yourself.** Run `weekly_review_gate.py` and relay its verdict + and exit code; the gate is deterministic so the call is reproducible, not vibes. +3. **Every active project leaves with exactly one next action.** A project with none is flagged + NO-NEXT-ACTION and resolved (action, waiting-for, someday/maybe, or dropped) before close. +4. **Timebox it.** Target 60-90 minutes; past two hours, stop, gate what's done, and schedule the + remainder. Marathon reviews kill the habit. +5. **Process, don't do.** During the review, anything requiring more than two minutes becomes a + next action on a list — the review is for steering, not rowing. + +## Skill Integration + +**Skill Location:** `../skills/weekly-review/` + +### Python Scripts (Stdlib) + +1. **Open Loop Scanner** — `skills/weekly-review/scripts/open_loop_scanner.py` — inventories + unchecked checkboxes, TODO/FIXME markers, and stale files across a directory; text + `--json`. +2. **Weekly Review Gate** — `skills/weekly-review/scripts/weekly_review_gate.py` — the ten-step + three-phase checklist; `--done` / `--skip` / `--list`; completion % + named gaps → + COMPLETE (exit 0) / INCOMPLETE (exit 2). +3. **Commitment Auditor** — `skills/weekly-review/scripts/commitment_auditor.py` — flags + STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE, computes the 0-100 health score with the formula + shown → HEALTHY / DRIFTING / OVERCOMMITTED. + +### Knowledge Bases + +- `skills/weekly-review/references/gtd_weekly_review_canon.md` — why the weekly review is the + critical success factor; the three-phase structure; cadence discipline (7 sources) +- `skills/weekly-review/references/open_loop_psychology.md` — Zeigarnik effect, plan-making + research, attention residue, cognitive load: why open loops tax attention (6 sources) +- `skills/weekly-review/references/review_cadence_design.md` — horizons of focus, habit anchoring, + timeboxing, failure modes, restart-after-lapse discipline (7 sources) + +## Differentiates From Siblings + +- **vs `cs-reflect`** (productivity reflect): reflect examines one conversation or piece of work, + once. The weekly review is a recurring cadence over the user's whole commitment system. +- **vs `cs-capture`** (productivity capture): capture is intake — brain dump in, actions + out. The weekly review is the maintenance loop that keeps the captured system trusted. +- **vs sprint retrospectives** (`project-management`): a retro is a team ceremony about a shared + iteration. This is a personal trusted-system audit — no team, no velocity chart. + +## Related Agents + +- [cs-capture](../../capture/agents/cs-capture.md) — productivity sibling, the intake side of the same system + +--- + +**Version:** 1.0.0 diff --git a/productivity/weekly-review/commands/cs-weekly-review.md b/productivity/weekly-review/commands/cs-weekly-review.md new file mode 100644 index 00000000..081aebb6 --- /dev/null +++ b/productivity/weekly-review/commands/cs-weekly-review.md @@ -0,0 +1,90 @@ +--- +name: "cs-weekly-review" +description: "/cs:weekly-review — Run a GTD weekly review: GET CLEAR (collect, inboxes to zero, empty your head), GET CURRENT (next actions, both calendars, waiting-for, projects — all mandatory), GET CREATIVE (someday/maybe, new ideas). Deterministic gate names every missing step; commitment auditor scores portfolio health 0-100." +argument-hint: "[optional: directory or notes to review]" +--- + +# /cs:weekly-review — GTD Weekly Review Loop → Trusted System + +**Command:** `/cs:weekly-review [directory or notes]` + +The weekly review is the maintenance loop that makes the rest of a personal system trustworthy. +This command walks David Allen's three phases in order, scans for open loops so nothing depends on +memory, and refuses to call the review COMPLETE while any mandatory GET CURRENT step is +unaccounted for. + +## When to Run + +- "Run my weekly review" / "let's do the weekly review" +- "I have too many open loops" / "help me close open loops" +- End of the work week, before planning the next one +- "I fell off my GTD habit" — restart with a shorter, zero-guilt pass +- You want an honest completion verdict, not a warm feeling of having tidied up. + +## When NOT to Run + +- You just want to dump what's in your head into actions → use `/cs:capture` (intake, not review). +- You want to reflect on one conversation or piece of work → use `productivity/reflect`. +- A team iteration retro with velocity and ceremonies → that's `project-management`, not this. +- Mid-week micro-check ("what's next right now?") — the review is a weekly cadence, not a task picker. + +## What You Get + +1. **An open-loop inventory** — unchecked checkboxes, TODO/FIXME markers, and stale files across + your workspace (`open_loop_scanner.py`), grouped by kind with per-file locations. +2. **A walked three-phase checklist** — GET CLEAR (3 steps), GET CURRENT (5 mandatory steps), + GET CREATIVE (2 steps), processed in order, two-minute rule enforced. +3. **A deterministic verdict** — `weekly_review_gate.py` computes completion %, names every + missing step, and returns COMPLETE (exit 0) or INCOMPLETE (exit 2). Unskipped GET CURRENT gaps + always force INCOMPLETE. +4. **A commitment-health audit** — STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE flags plus a + 0-100 score with the formula shown → HEALTHY / DRIFTING / OVERCOMMITTED (`commitment_auditor.py`). +5. **One first next action** for the coming week, so the review ends in motion, not admin. + +## Trigger Phrases (auto-invoke without /cs:) + +- "run my weekly review" / "weekly review time" +- "close my open loops" / "too many open loops" +- "GTD review" / "get current" / "mind sweep and review" +- "restart my review habit" + +## Discipline + +- **Scan before you ask** — evidence from the scanner first; the user's memory is what GTD says not to trust. +- **All five GET CURRENT steps are mandatory** — skip only with `--skip "N:reason"`, and the gate still names it. +- **Never self-certify** — the gate issues the verdict; relay its exit code, don't soften it. +- **Process, don't do** — anything over two minutes becomes a next action, not a detour. +- **Timebox 60-90 minutes** — past two hours, gate what's done and schedule the rest. + +## Workflow + +```bash +# 1. Inventory open loops in the workspace (checkboxes, TODO/FIXME, stale files) +python ../skills/weekly-review/scripts/open_loop_scanner.py --dir . --stale-days 14 + +# 2. Show the numbered ten-step checklist, then walk it with the user phase by phase +python ../skills/weekly-review/scripts/weekly_review_gate.py --list + +# 3. Gate what was actually done — names every missing step; exit 2 if incomplete +python ../skills/weekly-review/scripts/weekly_review_gate.py \ + --done "1,2,3,4,5,6,7,8,10" --skip "9:no someday list yet" + +# 4. Audit the commitment portfolio (JSON list of {name, days_since_touched, has_next_action}) +python ../skills/weekly-review/scripts/commitment_auditor.py --input commitments.json +``` + +## Stop Conditions + +- Gate returns COMPLETE + commitment audit delivered + one next action named → done. +- Timebox exceeded → gate the partial review honestly (INCOMPLETE), schedule the remainder, stop. +- User says "stop" → gate what's done so the partial pass still counts, then drop it. + +## Related + +- Agent: [`cs-weekly-review`](../agents/cs-weekly-review.md) +- Skill: [`weekly-review`](../skills/weekly-review/SKILL.md) +- Siblings: `/cs:capture` (intake side of the same system), `productivity/reflect` (one-off reflection) + +--- + +**Version:** 1.0.0 diff --git a/productivity/weekly-review/skills/weekly-review/SKILL.md b/productivity/weekly-review/skills/weekly-review/SKILL.md new file mode 100644 index 00000000..c13e6698 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/SKILL.md @@ -0,0 +1,99 @@ +--- +name: weekly-review +description: Use when someone wants to run a weekly review, close open loops, audit stalled projects and commitments, get their system back to trusted, restart a lapsed review habit, or says "/cs:weekly-review". Walks David Allen's three-phase loop — GET CLEAR, GET CURRENT, GET CREATIVE — with deterministic scripts that inventory open loops, gate the checklist with named gaps, and score commitment health 0-100. +argument-hint: "[optional: directory or notes to review]" +license: MIT +metadata: + version: 1.0.0 + build_pattern: "Path-B ritual skill — GTD weekly-review loop preserved + deterministic scanner/gate/auditor scripts added" + distinct_from: "reflect (per-conversation reflection, not a recurring cadence); capture (intake that feeds the system, not the review that maintains it); project-management sprint retros (team ceremony, not a personal trusted-system audit)" +--- + +# Weekly Review — GTD Loop → Trusted System + +> **Portability:** Reasoning-led skill with 3 stdlib Python scripts. No external APIs, no LLM calls +> in scripts. Works in Claude Code CLI and Claude.ai web. The scripts do the inventory and the +> gating; Claude and the user do the thinking. + +## What this does + +A personal system is only trustworthy if it gets reviewed — David Allen calls the weekly review +the critical success factor of the whole method. This skill walks the three phases in order and +refuses to call the review COMPLETE while any of the five mandatory GET CURRENT steps is +unaccounted for. Evidence first: scan the workspace for open loops before asking the user to +recall anything, because their memory is exactly what the method says not to trust. + +## Phase 1 — GET CLEAR (steps 1-3) + +Collect loose inputs, process every inbox to zero (clarify, don't do — anything over two minutes +becomes a next action), then a mind sweep to empty the head. Start with evidence: + +```bash +# Inventory open loops: unchecked checkboxes, TODO/FIXME markers, stale files +python scripts/open_loop_scanner.py --dir ~/notes --stale-days 14 +``` + +Route every loop found to a list — next action, waiting-for, someday/maybe, or trash. + +## Phase 2 — GET CURRENT (steps 4-8, all mandatory) + +Review the next-action lists (mark done, prune dead), the previous calendar (missed commitments +become actions), the upcoming calendar (prepare, don't react), the waiting-for list (chase or +drop), and every project for exactly one next action. Then gate honestly: + +```bash +python scripts/weekly_review_gate.py --list # show the numbered ten-step checklist +python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8" --skip "9:no someday list yet" +``` + +The gate computes completion %, names every missing step, and exits 0 (COMPLETE) or 2 +(INCOMPLETE). An unskipped missing GET CURRENT step **always** forces INCOMPLETE. + +## Phase 3 — GET CREATIVE (steps 9-10) + +Review someday/maybe (activate, keep, or kill), capture new ideas while the head is clear, then +audit the whole commitment portfolio: + +```bash +python scripts/commitment_auditor.py --input commitments.json +``` + +Flags STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE, prints the health formula with the score, and +issues HEALTHY / DRIFTING / OVERCOMMITTED. End the review with one named next action. + +## Scripts + +| Script | Role | +|---|---| +| `scripts/open_loop_scanner.py` | Inventories unchecked checkboxes, TODO/FIXME markers, and stale files across a directory; grouped counts + per-file locations; `--json`. | +| `scripts/weekly_review_gate.py` | The ten-step three-phase checklist; `--done`/`--skip`/`--list`; completion % + named gaps → COMPLETE (exit 0) / INCOMPLETE (exit 2). | +| `scripts/commitment_auditor.py` | Flags stalled and actionless commitments, computes the 0-100 health score with the formula shown → HEALTHY / DRIFTING / OVERCOMMITTED. | + +## References + +- [`references/gtd_weekly_review_canon.md`](references/gtd_weekly_review_canon.md) — why the weekly review is the critical success factor; the three-phase structure; cadence discipline (7 sources) +- [`references/open_loop_psychology.md`](references/open_loop_psychology.md) — Zeigarnik effect, plan-making research, attention residue: why open loops tax attention (6 sources) +- [`references/review_cadence_design.md`](references/review_cadence_design.md) — horizons of focus, habit anchoring, timeboxing, failure modes, restart-after-lapse (7 sources) + +## Assets + +- [`assets/weekly_review_checklist.md`](assets/weekly_review_checklist.md) — fillable three-phase checklist +- [`assets/example_weekly_review.md`](assets/example_weekly_review.md) — a full worked review (scan → checklist → gate → audit → next action) + +## Rules + +- All five GET CURRENT steps are mandatory; skip only with a stated reason, and the gate still names it. +- Never self-certify — the gate issues the verdict; relay its exit code, don't soften it. +- Process, don't do: during the review, anything over two minutes becomes a next action, not a detour. +- Timebox 60-90 minutes; past two hours, gate what's done honestly and schedule the remainder. +- A lapsed habit restarts with a shorter pass and zero guilt — a review is maintenance, not judgment. + +## Distinct From (don't reach for the wrong sibling) + +- **`productivity/reflect`** — reflects on one conversation or piece of work, once. The weekly review is a recurring cadence over the whole system. +- **`productivity/capture`** — the intake funnel (brain dump → actions). Capture feeds the system; this review keeps it trusted. +- **`project-management` sprint retros** — a team ceremony about a shared iteration. This is a personal trusted-system audit. + +--- + +**Version:** 1.0.0 · **Build pattern:** Path-B ritual skill — GTD weekly-review loop preserved + deterministic gate/scanner/auditor scripts added. diff --git a/productivity/weekly-review/skills/weekly-review/assets/example_weekly_review.md b/productivity/weekly-review/skills/weekly-review/assets/example_weekly_review.md new file mode 100644 index 00000000..5d892323 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/assets/example_weekly_review.md @@ -0,0 +1,97 @@ +# Example: A Full Worked Weekly Review + +> A realistic Friday-afternoon run of the whole loop — scan → checklist → gate → audit → next +> action — including an honest INCOMPLETE on the first gate and what happened next. + +**Context:** Solo consultant, ~2 weeks since the last full review (one lapse). Notes and projects +live in `~/notes` as markdown. Timebox: 90 minutes, started 15:00. + +--- + +## Step 0 — Scan for evidence (2 min) + +```bash +python scripts/open_loop_scanner.py --dir ~/notes --stale-days 14 +``` + +Result: **19 open loops** — 11 unchecked checkboxes across 5 files, 3 TODO markers in scripts, +5 stale files (worst: `someday/write-a-novel.md`, 63 days untouched). + +## Phase 1 — GET CLEAR (25 min) + +1. **Collect:** 4 paper receipts photographed, 6 screenshots moved out of the desktop, 2 voicemail + notes transcribed → all into the task inbox. +2. **Inboxes to zero:** 47 emails processed (31 archived, 9 became next actions, 4 waiting-for, + 3 replied under two minutes). Task inbox: 14 items clarified. Notes-app inbox: 8 items. +3. **Mind sweep:** 9 new items surfaced, including "renew professional insurance" (a real loop + that was living rent-free in working memory) and "pitch retainer renewal to Acme." + +## Phase 2 — GET CURRENT (40 min) + +4. **Next-action lists:** 12 marked done, 3 pruned as dead, 2 flagged stuck (both blocked on the + same unanswered client email — chased in step 7). +5. **Previous calendar (2 weeks back, because of the lapse):** the Acme kickoff spawned 3 + follow-ups never captured; a dentist appointment was missed → rebook action created. +6. **Upcoming calendar:** conference talk in 12 days → "draft outline" created *now* instead of + the night before; two client calls need agendas → 2 prep actions. +7. **Waiting-for:** 6 items. 2 chased (incl. the blocker from step 4), 1 re-dated, 1 dropped + (the vendor clearly isn't answering — found an alternative instead). +8. **Project lists:** exported to `commitments.json` and audited: + +```bash +python scripts/commitment_auditor.py --input commitments.json +``` + +``` +Flags: STALLED 3 · NO-NEXT-ACTION 2 · SOMEDAY-CANDIDATE 1 +Formula: score = 100 - 30*(stalled/total) - 40*(no_next_action/total) - 30*(someday_candidates/total) +COMMITMENT HEALTH: 66.7/100 VERDICT: DRIFTING +``` + +Resolutions: "Hire a designer" got a next action ("post the brief in two freelance communities"); +"Write a novel" was honestly moved to someday/maybe; "Learn Spanish" got a concrete next action +("book Tuesday trial lesson") instead of the standing guilt entry. + +## First gate — honest INCOMPLETE (15:05 + 67 min) + +Ten minutes left in the timebox and phase 3 untouched. Gate what's true: + +```bash +python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8" +``` + +``` +Completion: 80.0% (done 8 · skipped 0 · missing 2) +Missing steps: 9. [GET CREATIVE] Review someday/maybe · 10. [GET CREATIVE] Capture new ideas +VERDICT: INCOMPLETE (exit code 2) +``` + +All five GET CURRENT steps done, so nothing *forced* the incomplete — GET CREATIVE was simply not +done yet. Ten minutes remained, and step 9 was half-triggered by the audit anyway, so: continue. + +## Phase 3 — GET CREATIVE (10 min) + +9. **Someday/maybe:** 14 items reviewed. 1 activated ("small-group workshop" — two people asked + about it this month), 2 killed without ceremony, novel filed with a clear conscience. +10. **New ideas:** 3 captured, including a productized-audit offering sketched in four bullets. + +## Final gate — COMPLETE + +```bash +python scripts/weekly_review_gate.py --done "1,2,3,4,5,6,7,8,9,10" +``` + +``` +Completion: 100.0% (done 10 · skipped 0 · missing 0) +VERDICT: COMPLETE (exit code 0) +``` + +## Close (total: 82 min) + +**One next action for the coming week:** send the Acme retainer-renewal email (drafted Monday +09:00, calendar-blocked). + +**What made this a good review:** evidence first (the scan found loops memory had dropped); the +lapse was handled with a look-back, not a guilt marathon; the first gate was allowed to say +INCOMPLETE; every flagged project left with a disposition; and it ended inside the timebox with +one concrete commitment. diff --git a/productivity/weekly-review/skills/weekly-review/assets/weekly_review_checklist.md b/productivity/weekly-review/skills/weekly-review/assets/weekly_review_checklist.md new file mode 100644 index 00000000..0412dbc9 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/assets/weekly_review_checklist.md @@ -0,0 +1,59 @@ +# Weekly Review Checklist + +> Fillable template. Copy this file (or print it), work top to bottom, then gate it: +> `python scripts/weekly_review_gate.py --done "..." --skip "N:reason"` + +**Week of:** ____________ **Started:** ____:____ **Timebox ends (max 2h):** ____:____ + +--- + +## Phase 1 — GET CLEAR + +- [ ] **1. Collect loose inputs** — papers, receipts, notes, screenshots, downloads → into an inbox + - Scanner run? `python scripts/open_loop_scanner.py --dir ______ --stale-days 14` + - Loops found: checkboxes ____ · TODO/FIXME ____ · stale files ____ +- [ ] **2. Process inboxes to zero** — clarify, don't do (two-minute rule is the only exception) + - Inboxes processed: ☐ email ☐ task inbox ☐ notes app ☐ desk/physical ☐ other: ______ +- [ ] **3. Empty your head** — mind sweep; capture every commitment still riding in memory + - New items captured: ____ + +## Phase 2 — GET CURRENT (all five mandatory) + +- [ ] **4. Review next-action lists** — mark done, prune dead, surface stuck + - Done: ____ · Pruned: ____ · Stuck (needs decision): ____ +- [ ] **5. Review previous calendar** — missed or spawned commitments become captured items + - Items captured from the look-back: ____ +- [ ] **6. Review upcoming calendar** — prepare, don't react + - Prep actions created: ____ +- [ ] **7. Review waiting-for list** — chase, re-date, or drop each item + - Chased: ____ · Re-dated: ____ · Dropped: ____ +- [ ] **8. Review project lists** — every active project has exactly one next action + - Auditor run? `python scripts/commitment_auditor.py --input ______` + - Health score: ____/100 · Verdict: ______ + - STALLED: ____ · NO-NEXT-ACTION: ____ · SOMEDAY-CANDIDATE: ____ + +## Phase 3 — GET CREATIVE + +- [ ] **9. Review someday/maybe** — activate, keep, or kill + - Activated: ____ · Killed: ____ +- [ ] **10. Capture new ideas** — add them while the head is clear + - New ideas captured: ____ + +--- + +## Gate it + +```bash +python scripts/weekly_review_gate.py --done "____________" --skip "____________" +``` + +**Gate verdict:** ☐ COMPLETE (exit 0) ☐ INCOMPLETE (exit 2) +**Missing steps named by the gate:** ______________________________________ + +## Close + +**One next action for the coming week:** ______________________________________ +**Finished:** ____:____ (total: ____ min — target 60-90) + +> Skipped a step? Write the reason above and give it first priority next week. +> Lapsed last week? Shorter pass, zero guilt. The review is maintenance, not judgment. diff --git a/productivity/weekly-review/skills/weekly-review/references/gtd_weekly_review_canon.md b/productivity/weekly-review/skills/weekly-review/references/gtd_weekly_review_canon.md new file mode 100644 index 00000000..b71d12d3 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/references/gtd_weekly_review_canon.md @@ -0,0 +1,95 @@ +# The GTD Weekly Review Canon + +> Why David Allen calls the weekly review the "critical success factor" of the whole method, what +> the three phases actually contain, and why the cadence matters more than the format. + +## The claim: the review is the keystone, not an accessory + +Getting Things Done rests on one psychological bargain: your mind will stop nagging you about +commitments **only if** it trusts that the external system holds all of them and that the system +gets looked at. Capture alone doesn't buy that trust — a list nobody reviews is just a graveyard +with better formatting. Allen is explicit that the weekly review is "the critical success factor" +and that people who fall off GTD almost always fall off here first: they keep capturing, stop +reviewing, watch the lists rot, and then stop trusting the lists — at which point everything moves +back into their head and the whole system collapses. + +The corollary this skill enforces: a review that skims is not a review. Marking the ritual "done" +while the core steps went unwalked actively *damages* trust in the system, because now the lists +are stale **and** carry a false freshness stamp. Hence the hard gate: unaccounted-for GET CURRENT +steps always force an INCOMPLETE verdict. + +## The three phases + +Allen's own material organizes the checklist into three movements, in a deliberate order: + +### 1. GET CLEAR — return to ground zero + +- **Collect loose papers and materials.** Physical and digital strays: receipts, notes, downloads, + screenshots, business cards. Everything into an inbox. +- **Get "in" to zero.** Process every inbox — email, notes app, task inbox, desk tray — by + *clarifying*, not doing. Each item becomes a next action, a project, a waiting-for, a + someday/maybe, reference material, or trash. The two-minute rule is the only exception: if + acting takes under two minutes, do it now. +- **Empty your head.** A mind sweep: write down every new project, task, or nagging thought still + riding in working memory. This is the step that converts psychic RAM into system entries. + +### 2. GET CURRENT — make the system match reality (the mandatory core) + +- **Review next-action lists.** Mark off what's done, prune what's dead, notice what's stuck. +- **Review the previous calendar.** Look back over the past week or two for missed commitments, + follow-ups spawned by meetings, promises made in passing. Each becomes a captured item. +- **Review the upcoming calendar.** Look ahead so preparation happens now instead of panic later. +- **Review the waiting-for list.** Everything delegated or pending from others: chase it, re-date + it, or drop it. Unreviewed waiting-fors are where other people's slippage becomes your surprise. +- **Review project lists.** The load-bearing step: every active project must have **exactly one + next action** on a list. A project with none is a wish; a project whose next action is buried is + stalled. This step is why GTD projects keep moving without daily replanning. + +### 3. GET CREATIVE — lift your eyes off the runway + +- **Review someday/maybe.** Activate what's become timely, keep what still sparks, kill what + doesn't. This list only works if it's periodically culled — otherwise it becomes a guilt museum. +- **Capture new ideas.** With a clear head and a current system, ambition is safe again. Allen's + point: creativity shows up *after* clearing, not instead of it. + +## Cadence discipline + +- **Weekly is the resolution of most working commitments.** Allen's observation is pragmatic, not + mystical: most people's world shifts materially over a week — meetings compound, projects drift, + promises accumulate. Daily is too granular to see project-level drift; monthly lets loops rot. +- **Same time, same trigger.** The canonical recommendation (echoed by every serious GTD guide) is + a recurring, protected block — commonly Friday afternoon or Sunday evening — anchored to an + existing rhythm so the review doesn't depend on motivation. +- **Timeboxed.** One to two hours. A review that regularly takes three hours gets skipped the + moment a week is busy, and skipped reviews are how the habit dies. Better a 45-minute honest + pass with steps explicitly skipped-with-reason than a marathon that happens twice and never again. +- **The review is steering, not rowing.** Forte's periodic-review framing and Allen's original + agree: during the review you decide and route; you do not execute (two-minute items excepted). + A review that turns into a work session never finishes. + +## What "done" means + +The review is complete when: every inbox is empty, every open loop found is on a list, every +active project has one next action, both calendars have been swept, every waiting-for has been +touched, and someday/maybe has been consciously kept or culled. That definition is checkable, +which is why this skill gates it with a script instead of a feeling. + +## Sources + +1. David Allen, *Getting Things Done: The Art of Stress-Free Productivity* (Penguin, original + edition 2001; revised edition 2015) — chapter on the power of the weekly review; the "critical + success factor" claim; the two-minute rule; the project/next-action distinction. +2. David Allen, *Ready for Anything: 52 Productivity Principles for Getting Things Done* (Viking, + 2003) — principles on review cadence and why unrenegotiated commitments erode self-trust. +3. GTD® methodology guides, gettingthingsdone.com (David Allen Company) — the official Weekly + Review® checklist with the GET CLEAR / GET CURRENT / GET CREATIVE structure used here. +4. Tiago Forte, *Building a Second Brain* (Atria, 2022) and the PARA method essays (fortelabs.com) + — periodic review as the maintenance loop of any personal knowledge system; weekly review as + routing, not execution. +5. Doist, "The Complete Guide to Getting Things Done" (todoist.com/productivity-methods) — a + widely used practitioner's walkthrough of the weekly review steps and common adaptations. +6. Cal Newport, *Deep Work* (Grand Central, 2016) and the weekly-planning essays at + calnewport.com — weekly planning as the scale at which meaningful work is steered; the + shutdown-ritual analogue of closing loops before rest. +7. Francis Wade, *Perfect Time-Based Productivity* (2014) — on why review cadences fail and how + habit load, not intention, predicts whether a review sticks. diff --git a/productivity/weekly-review/skills/weekly-review/references/open_loop_psychology.md b/productivity/weekly-review/skills/weekly-review/references/open_loop_psychology.md new file mode 100644 index 00000000..1d82393b --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/references/open_loop_psychology.md @@ -0,0 +1,95 @@ +# The Psychology of Open Loops + +> Why unfinished commitments tax attention even when you're not working on them — the research +> behind Allen's "mind like water," and why writing a next action down genuinely quiets the mind. + +## The Zeigarnik effect: unfinished tasks stay resident + +Bluma Zeigarnik's classic experiments found that interrupted, unfinished tasks are recalled +substantially better than completed ones — the canonical finding is roughly a 2:1 recall +advantage. The interpretation that survived a century of follow-up work: an unfinished task +maintains a state of tension that keeps it active in memory. Completion releases the tension; +interruption preserves it. + +For a personal system, the implication is direct: every commitment you've made and not resolved — +the un-replied email, the half-done project, the promise made in a hallway — is an *open loop* +that your memory keeps rehearsing whether or not rehearsing helps. Allen's term for the +accumulated hum of these loops is "psychic RAM": working memory spent tracking commitments instead +of thinking. + +## Masicampo & Baumeister: the plan, not the completion, closes the loop + +The most important modern refinement for GTD comes from E.J. Masicampo and Roy Baumeister's +"Consider It Done!" studies. Unfulfilled goals were shown to intrude on unrelated tasks — subjects +with an unfinished goal performed worse on reading comprehension and were more distracted by +goal-related words. The striking result: **making a specific plan for the goal eliminated the +intrusions**, even though the goal itself remained unfinished. The mind doesn't demand completion +before it stands down; it demands a *credible plan* held somewhere it trusts. + +This is the experimental vindication of the weekly review's core move. You don't have to finish +your projects to stop them from nagging you — you have to give each one a concrete next action in +a system you demonstrably revisit. The "demonstrably revisit" clause is why the review has to +actually happen on cadence: a plan written into a system that never gets read stops being +credible, and the intrusions come back. + +## Attention residue: switching without closing costs twice + +Sophie Leroy's work on attention residue shows that when people switch from Task A to Task B +without closing A, part of their attention remains allocated to A — and performance on B suffers. +Residue is worst when the prior task is unfinished and low in "closure." Open loops therefore +don't just occupy memory at rest; they degrade the quality of whatever you *are* working on, +because each unclosed thread leaks attention into the current one. + +The weekly review is a batch closure operation: one scheduled session in which every dangling +thread gets an explicit disposition (done, next action, waiting-for, someday/maybe, trash), so the +rest of the week can be spent with attention undivided. + +## Cognitive load: the system as external memory + +John Sweller's cognitive load theory frames working memory as severely capacity-limited (a handful +of elements at a time) while schemas and external supports offload that limit. A trusted external +system is precisely such an offload: it converts "remember to remember" (extraneous load, paid +continuously) into "consult the list" (paid once, at review time). The weekly review is the +maintenance the offload requires — an external memory that drifts from reality re-imports its +contents back into working memory, with interest. + +## "Mind like water" + +Allen's martial-arts image summarizes the target state: a mind that responds to what's in front of +it with appropriate force and returns to calm — neither over-reacting to a new input nor +under-reacting because it's busy rehearsing forty old ones. The research above cashes the metaphor +out: capture ends rehearsal (Zeigarnik), a next action ends intrusion (Masicampo & Baumeister), +closure ends residue (Leroy), and the external system ends the memory tax (Sweller). The review is +what keeps all four working. + +## Practical consequences encoded in this skill + +- **Scan, don't recall.** The open-loop scanner walks the actual workspace because self-report of + open loops is exactly what the research says is unreliable — loops intrude, but they don't + enumerate themselves on demand. +- **Every project leaves with one next action.** Not "review the project" — a physical, + visible next step. That's the specific-plan condition from the plan-making studies. +- **Skipping the waiting-for review is a real cost.** Delegated items are open loops too; the + tension doesn't care that someone else is holding the ball. +- **Restart without guilt.** The loops don't punish you for lapsing; they just come back. The + correct response to a lapsed cadence is a shorter review, not a longer apology. + +## Sources + +1. Bluma Zeigarnik, "Über das Behalten von erledigten und unerledigten Handlungen" ("On Finished + and Unfinished Tasks"), *Psychologische Forschung* 9, 1927 — the original recall-advantage + experiments for interrupted tasks. +2. E.J. Masicampo & Roy F. Baumeister, "Consider It Done! Plan Making Can Eliminate the Cognitive + Effects of Unfulfilled Goals," *Journal of Personality and Social Psychology* 101(4), 2011 — + plan-making eliminates goal intrusions without completion. +3. Sophie Leroy, "Why Is It So Hard to Do My Work? The Challenge of Attention Residue When + Switching Between Work Tasks," *Organizational Behavior and Human Decision Processes* 109(2), + 2009 — attention residue from unclosed tasks degrades subsequent performance. +4. John Sweller, "Cognitive Load During Problem Solving: Effects on Learning," *Cognitive Science* + 12(2), 1988 (and later cognitive load theory syntheses) — working-memory limits and the case + for external offloading. +5. David Allen, *Getting Things Done* (Penguin, revised edition 2015) — "psychic RAM," "mind like + water," and open loops as the phenomenology the research explains. +6. Roy F. Baumeister & John Tierney, *Willpower: Rediscovering the Greatest Human Strength* + (Penguin, 2011) — the accessible synthesis of the Zeigarnik and plan-making work, including its + explicit connection to GTD. diff --git a/productivity/weekly-review/skills/weekly-review/references/review_cadence_design.md b/productivity/weekly-review/skills/weekly-review/references/review_cadence_design.md new file mode 100644 index 00000000..d99f00d9 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/references/review_cadence_design.md @@ -0,0 +1,99 @@ +# Designing a Review Cadence That Survives Contact With Real Weeks + +> Choosing the right horizon for each review, anchoring the habit so it fires without motivation, +> timeboxing so it keeps firing, and restarting after the inevitable lapse. + +## Match the review to the horizon + +GTD's "horizons of focus" model assigns different altitudes to different cadences. The mistake to +avoid is asking one review to do every altitude's job: + +| Horizon | Cadence | What gets reviewed | What it is NOT | +|---|---|---|---| +| Ground — next actions & calendar | Daily (minutes) | What's next *right now*; today's hard landscape | Not a system audit | +| Projects & waiting-for | **Weekly** (60-90 min) | Every list, both calendars, every project's next action | Not goal-setting | +| Areas of responsibility | Monthly-ish | Are whole areas (health, finances, team) getting neglected? | Not task triage | +| Goals, vision, purpose | Quarterly / yearly | Direction: right projects at all? | Not maintenance | + +The weekly review is the load-bearing middle: frequent enough that loops can't rot, high enough to +see project drift the daily view can't. Skipping it and hoping the daily check compensates is the +most common cadence failure — daily views show *tasks*, and a system can be current on tasks while +five projects silently stall. + +## Anchor the habit; don't schedule the intention + +Habit research is unambiguous that repetition after a **stable cue** — not motivation, not +importance — is what makes a behavior automatic. Applied to the review: + +- **Fogg's recipe:** After I [existing anchor], I will [first tiny step]. "After Friday's last + meeting ends, I open the review checklist" beats "do weekly review Friday" — the anchor is an + event that already happens, and the committed behavior is deliberately tiny (open the checklist, + not finish the review). Momentum does the rest; celebration seals it. +- **Clear's environment framing:** make it obvious (recurring calendar block with the checklist + linked), easy (everything in one place — this skill's scripts and checklist), and satisfying + (the COMPLETE verdict and health score are the visible reward). Habit formation takes weeks to + months of repetition — expect the review to feel effortful for the first dozen iterations. +- **Protect the block like a meeting with someone important,** because it is: it's the meeting + where you renegotiate every commitment you've made with yourself. + +## Timebox it — the review must be cheap enough to repeat + +A review that costs three hours will be skipped the first busy week, and skipped once is the +beginning of lapsed. Working discipline: + +- **Target 60-90 minutes; hard-stop at two.** At the stop, gate what's done honestly + (INCOMPLETE is information, not failure), schedule the remainder, leave. +- **Process, don't do.** The two-minute rule is the *only* execution allowed. Everything else + becomes a next action. Reviews balloon when they silently turn into work sessions. +- **Let the scripts carry the clerical weight.** Scanning for loops and computing portfolio health + are mechanical; spend the human minutes on decisions (chase or drop? activate or kill?). + +## Failure modes + +- **The guilt ritual.** The review becomes a weekly confrontation with everything undone, feels + bad, and gets avoided. Countermeasures: the someday/maybe list exists precisely so ambitions can + be parked without shame; the commitment auditor names SOMEDAY-CANDIDATEs so parking is a normal + verdict, not an admission of defeat. +- **The marathon.** Usually caused by processing debt (inboxes untouched all week) or by doing + work mid-review. Fix the input side (a 10-minute daily sweep keeps inboxes shallow) and enforce + process-don't-do. +- **The skimmed review.** All boxes ticked in fifteen minutes, no calendar swept, no project + touched. Worse than skipping, because it stamps stale lists as fresh. This is exactly what the + gate's mandatory GET CURRENT rule exists to catch. +- **The floating review.** No anchor, so it happens "when there's time," which is never. Re-anchor + to a fixed event and shrink the committed first step. +- **Altitude creep.** The weekly review turns into an existential goals session every week and + becomes exhausting. Goals live at the quarterly horizon; the weekly review only has to make the + system current. + +## Restarting after a lapse + +Everyone lapses. The restart protocol that preserves the habit: + +1. **No back-processing marathon.** Don't attempt to review the missed weeks; review *now*. Old + loops are still in the system or will surface in the scan. +2. **Run a shorter pass.** GET CLEAR + the five GET CURRENT steps; explicitly skip GET CREATIVE + with a reason if energy is short. An honest 80% INCOMPLETE beats a fantasized 100%. +3. **Zero self-punishment.** Missing-then-restarting is what successful habit maintenance actually + looks like in the data; a single missed instance measurably does not derail formation — quitting + in response to missing does. +4. **Re-anchor before re-ambitioning.** Fix the cue and shrink the step; don't respond to a lapse + by designing a grander review. + +## Sources + +1. David Allen, *Getting Things Done* (Penguin, revised edition 2015) — horizons of focus; + the weekly review's altitude; the two-minute rule. +2. BJ Fogg, *Tiny Habits: The Small Changes That Change Everything* (Houghton Mifflin Harcourt, + 2020) — anchor → tiny behavior → celebration; behavior = motivation × ability × prompt. +3. James Clear, *Atomic Habits* (Avery, 2018) — cue/craving/response/reward; make it obvious, + easy, satisfying; "never miss twice." +4. Phillippa Lally et al., "How Are Habits Formed: Modelling Habit Formation in the Real World," + *European Journal of Social Psychology* 40(6), 2010 — automaticity takes ~18-254 days; a single + missed repetition does not materially harm formation. +5. Cal Newport, *Deep Work* (Grand Central, 2016) and weekly/quarterly-planning essays at + calnewport.com — multi-scale planning; the shutdown ritual as daily loop-closing. +6. Tiago Forte, "The Weekly Review Is an Operating System" (fortelabs.com) — the review as routing + and maintenance, and why doing work inside it kills it. +7. GTD® methodology guides, gettingthingsdone.com (David Allen Company) — official guidance on + scheduling, protecting, and recovering the Weekly Review®. diff --git a/productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py b/productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py new file mode 100644 index 00000000..32dddc10 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/scripts/commitment_auditor.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +"""commitment_auditor.py — Audit a commitment portfolio: flags, health score, one honest verdict. + +Step 8 of the weekly review (review project lists) in numbers. Reads a JSON list of commitments: + + [{"name": "Website relaunch", "days_since_touched": 3, "has_next_action": true}, ...] + +and flags each one: + + STALLED days_since_touched > 14 (a week-and-a-half of silence is drift, not rest) + NO-NEXT-ACTION has_next_action is false (a project with no next action is a wish) + SOMEDAY-CANDIDATE days_since_touched > 45 (be honest: activate it or move it to someday/maybe) + +Then computes a 0-100 commitment-health score. The formula is printed with the output so the +number is auditable, never mystical: + + score = 100 - 30*(stalled/total) - 40*(no_next_action/total) - 30*(someday/total) + +(SOMEDAY-CANDIDATEs also count as STALLED — the double penalty is deliberate escalation.) + +Verdict: HEALTHY (>= 80) · DRIFTING (50-79) · OVERCOMMITTED (< 50) + +Deterministic logic. No LLM calls, no network. Stdlib only. + +Usage: + python commitment_auditor.py --input commitments.json + python commitment_auditor.py --input commitments.json --json + python commitment_auditor.py --sample + +Exit codes: + 0 audit complete (any verdict) — also --sample / --help + 2 input file missing, unreadable, or not the expected JSON shape +""" + +import argparse +import json +import sys +from typing import Any, Dict, List + +STALLED_DAYS = 14 +SOMEDAY_DAYS = 45 + +FORMULA = ("score = 100 - 30*(stalled/total) - 40*(no_next_action/total) " + "- 30*(someday_candidates/total)") + +SAMPLE_COMMITMENTS = [ + {"name": "Website relaunch", "days_since_touched": 3, "has_next_action": True}, + {"name": "Q3 budget draft", "days_since_touched": 9, "has_next_action": True}, + {"name": "Hire a designer", "days_since_touched": 21, "has_next_action": False}, + {"name": "Learn Spanish", "days_since_touched": 41, "has_next_action": True}, + {"name": "Write a novel", "days_since_touched": 63, "has_next_action": False}, + {"name": "Renew passports", "days_since_touched": 2, "has_next_action": True}, +] + + +def validate(data: Any) -> List[Dict[str, Any]]: + if not isinstance(data, list) or not data: + raise ValueError("input must be a non-empty JSON list of commitment objects") + for i, c in enumerate(data): + if not isinstance(c, dict): + raise ValueError(f"entry {i} is not an object") + for key in ("name", "days_since_touched", "has_next_action"): + if key not in c: + raise ValueError(f"entry {i} ({c.get('name', '?')!r}) missing key: {key!r}") + if not isinstance(c["days_since_touched"], (int, float)) or c["days_since_touched"] < 0: + raise ValueError(f"entry {i}: days_since_touched must be a non-negative number") + if not isinstance(c["has_next_action"], bool): + raise ValueError(f"entry {i}: has_next_action must be true or false") + return data + + +def audit(commitments: List[Dict[str, Any]]) -> Dict[str, Any]: + total = len(commitments) + rows = [] + n_stalled = n_no_action = n_someday = 0 + for c in commitments: + flags = [] + if c["days_since_touched"] > STALLED_DAYS: + flags.append("STALLED") + n_stalled += 1 + if not c["has_next_action"]: + flags.append("NO-NEXT-ACTION") + n_no_action += 1 + if c["days_since_touched"] > SOMEDAY_DAYS: + flags.append("SOMEDAY-CANDIDATE") + n_someday += 1 + rows.append({"name": c["name"], + "days_since_touched": c["days_since_touched"], + "has_next_action": c["has_next_action"], + "flags": flags}) + + score = 100.0 - 30.0 * (n_stalled / total) - 40.0 * (n_no_action / total) \ + - 30.0 * (n_someday / total) + score = round(max(0.0, min(100.0, score)), 1) + + if score >= 80: + verdict = "HEALTHY" + headline = ("The portfolio is honest: most commitments are moving and have a next " + "action. Keep the cadence.") + elif score >= 50: + verdict = "DRIFTING" + headline = ("Several commitments are coasting on good intentions. Give each flagged " + "item a next action, a waiting-for, or a someday/maybe home this review.") + else: + verdict = "OVERCOMMITTED" + headline = ("More commitments than attention. Kill or park the SOMEDAY-CANDIDATEs, " + "then rebuild next actions for what survives — fewer, moving projects " + "beat many, stalled ones.") + + return { + "total_commitments": total, + "flag_counts": {"STALLED": n_stalled, "NO-NEXT-ACTION": n_no_action, + "SOMEDAY-CANDIDATE": n_someday}, + "thresholds": {"stalled_days": STALLED_DAYS, "someday_days": SOMEDAY_DAYS}, + "commitments": rows, + "formula": FORMULA, + "health_score": score, + "verdict": verdict, + "headline": headline, + } + + +def render_human(r: Dict[str, Any]) -> str: + out = ["Commitment Auditor (project lists in numbers)", "=" * 64] + out.append(f" Commitments: {r['total_commitments']} " + f"Stalled > {r['thresholds']['stalled_days']}d · " + f"Someday-candidate > {r['thresholds']['someday_days']}d") + out.append("") + for c in r["commitments"]: + mark = "OK " if not c["flags"] else "!! " + flags = f" [{', '.join(c['flags'])}]" if c["flags"] else "" + out.append(f" {mark}{c['name']} — {c['days_since_touched']}d since touched, " + f"next action: {'yes' if c['has_next_action'] else 'NO'}{flags}") + fc = r["flag_counts"] + out.append("") + out.append(f" Flags: STALLED {fc['STALLED']} · NO-NEXT-ACTION {fc['NO-NEXT-ACTION']} · " + f"SOMEDAY-CANDIDATE {fc['SOMEDAY-CANDIDATE']}") + out.append(f" Formula: {r['formula']}") + out.append(f" COMMITMENT HEALTH: {r['health_score']}/100 VERDICT: {r['verdict']}") + out.append("") + out.append(f" {r['headline']}") + return "\n".join(out) + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Audit commitments: STALLED / NO-NEXT-ACTION / SOMEDAY-CANDIDATE flags + 0-100 health score.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + p.add_argument("--input", help="Path to a JSON list of " + '{"name", "days_since_touched", "has_next_action"} objects') + p.add_argument("--sample", action="store_true", help="Run on embedded example data and exit 0") + p.add_argument("--json", action="store_true", help="Emit JSON instead of text") + args = p.parse_args(argv) + + try: + if args.sample: + commitments = validate(SAMPLE_COMMITMENTS) + elif args.input: + with open(args.input, "r", encoding="utf-8") as f: + commitments = validate(json.load(f)) + else: + p.print_help() + print("\nerror: provide --input or --sample", file=sys.stderr) + return 2 + except (OSError, ValueError, json.JSONDecodeError) as e: + print(f"error: {e}", file=sys.stderr) + return 2 + + result = audit(commitments) + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py b/productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py new file mode 100644 index 00000000..32d2de4d --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/scripts/open_loop_scanner.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +"""open_loop_scanner.py — Inventory the open loops hiding in a directory. + +The GET CLEAR phase of a weekly review starts with evidence, not memory. This script scans a +directory for three kinds of open loop: + + 1. checkbox — unchecked markdown checkboxes: - [ ] / * [ ] + 2. todo_fixme — TODO / FIXME markers in text and source files + 3. stale_file — files not modified in --stale-days days (default 14) + +Output is an inventory grouped by kind, with counts and per-file locations, so the review walks +real loops instead of whatever the user happens to remember. + +Deterministic logic. No LLM calls, no network. Stdlib only. + +Usage: + python open_loop_scanner.py --dir ~/notes + python open_loop_scanner.py --dir . --stale-days 30 --json + python open_loop_scanner.py --sample + +Exit codes: + 0 scan complete (even if zero loops found) — also --sample / --help + 2 directory not found or not a directory +""" + +import argparse +import json +import os +import re +import sys +import time +from typing import Any, Dict, List + +SKIP_DIRS = {".git", ".hg", ".svn", "node_modules", "__pycache__", ".venv", "venv", + "dist", "build", ".idea", ".vscode", ".cache"} +CHECKBOX_EXTS = {".md", ".markdown", ".txt"} +TEXT_EXTS = CHECKBOX_EXTS | {".py", ".js", ".ts", ".tsx", ".jsx", ".sh", ".rb", ".go", + ".rs", ".java", ".c", ".h", ".cpp", ".css", ".html", + ".yaml", ".yml", ".toml", ".ini", ".cfg", ".sql"} + +CHECKBOX_RE = re.compile(r"^\s*[-*]\s+\[ \]\s+(.*)$") +TODO_RE = re.compile(r"\b(TODO|FIXME)\b[:\s]*(.*)", re.IGNORECASE) + + +def _snippet(text: str, limit: int = 80) -> str: + text = text.strip() + return text if len(text) <= limit else text[: limit - 1] + "…" + + +def scan(directory: str, stale_days: int) -> Dict[str, Any]: + now = time.time() + stale_cutoff = now - stale_days * 86400 + checkboxes: List[Dict[str, Any]] = [] + todos: List[Dict[str, Any]] = [] + stale: List[Dict[str, Any]] = [] + files_scanned = 0 + + for root, dirs, files in os.walk(directory): + dirs[:] = sorted(d for d in dirs if d not in SKIP_DIRS and not d.startswith(".")) + for fname in sorted(files): + ext = os.path.splitext(fname)[1].lower() + if ext not in TEXT_EXTS: + continue + path = os.path.join(root, fname) + rel = os.path.relpath(path, directory) + files_scanned += 1 + + try: + mtime = os.path.getmtime(path) + if mtime < stale_cutoff: + stale.append({"file": rel, + "days_since_modified": int((now - mtime) // 86400)}) + with open(path, "r", encoding="utf-8", errors="replace") as f: + for lineno, line in enumerate(f, 1): + if ext in CHECKBOX_EXTS: + m = CHECKBOX_RE.match(line) + if m: + checkboxes.append({"file": rel, "line": lineno, + "text": _snippet(m.group(1))}) + m = TODO_RE.search(line) + if m: + todos.append({"file": rel, "line": lineno, + "marker": m.group(1).upper(), + "text": _snippet(m.group(2))}) + except OSError: + continue + + total = len(checkboxes) + len(todos) + len(stale) + return { + "directory": os.path.abspath(directory), + "stale_days_threshold": stale_days, + "files_scanned": files_scanned, + "total_open_loops": total, + "loops": { + "checkbox": {"count": len(checkboxes), "items": checkboxes}, + "todo_fixme": {"count": len(todos), "items": todos}, + "stale_file": {"count": len(stale), "items": stale}, + }, + } + + +SAMPLE_RESULT: Dict[str, Any] = { + "directory": "/home/sample/notes", + "stale_days_threshold": 14, + "files_scanned": 12, + "total_open_loops": 7, + "loops": { + "checkbox": { + "count": 3, + "items": [ + {"file": "projects/website-relaunch.md", "line": 14, + "text": "email designer about the hero image"}, + {"file": "projects/website-relaunch.md", "line": 15, + "text": "draft the pricing page copy"}, + {"file": "inbox.md", "line": 3, "text": "book dentist appointment"}, + ], + }, + "todo_fixme": { + "count": 2, + "items": [ + {"file": "scripts/backup.sh", "line": 22, "marker": "TODO", + "text": "rotate old archives after 90 days"}, + {"file": "notes/tax-prep.md", "line": 8, "marker": "FIXME", + "text": "the mileage total is wrong, recount Q2"}, + ], + }, + "stale_file": { + "count": 2, + "items": [ + {"file": "projects/learn-spanish.md", "days_since_modified": 41}, + {"file": "someday/write-a-novel.md", "days_since_modified": 63}, + ], + }, + }, +} + +KIND_LABEL = { + "checkbox": "Unchecked checkboxes (- [ ])", + "todo_fixme": "TODO / FIXME markers", + "stale_file": "Stale files (untouched past threshold)", +} + + +def render_human(r: Dict[str, Any]) -> str: + out = ["Open Loop Scanner (evidence first, memory second)", "=" * 64] + out.append(f" Directory: {r['directory']}") + out.append(f" Files scanned: {r['files_scanned']} " + f"Stale threshold: {r['stale_days_threshold']} days") + out.append(f" TOTAL OPEN LOOPS: {r['total_open_loops']}") + for kind in ("checkbox", "todo_fixme", "stale_file"): + bucket = r["loops"][kind] + out.append(f"\n {KIND_LABEL[kind]} — {bucket['count']}") + for item in bucket["items"][:25]: + if kind == "stale_file": + out.append(f" - {item['file']} ({item['days_since_modified']} days untouched)") + elif kind == "todo_fixme": + out.append(f" - {item['file']}:{item['line']} [{item['marker']}] {item['text']}") + else: + out.append(f" - {item['file']}:{item['line']} {item['text']}") + if bucket["count"] > 25: + out.append(f" … and {bucket['count'] - 25} more") + out.append("") + out.append(" Feed these into GET CLEAR: collect, clarify, and route every loop") + out.append(" to a list — next action, waiting-for, someday/maybe, or trash.") + return "\n".join(out) + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Scan a directory for open loops: unchecked checkboxes, TODO/FIXME, stale files.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + p.add_argument("--dir", default=".", help="Directory to scan (default: current directory)") + p.add_argument("--stale-days", type=int, default=14, + help="Flag files not modified in this many days (default: 14)") + p.add_argument("--sample", action="store_true", + help="Print a canned deterministic scan result and exit 0") + p.add_argument("--json", action="store_true", help="Emit JSON instead of text") + args = p.parse_args(argv) + + if args.sample: + result = SAMPLE_RESULT + else: + if not os.path.isdir(args.dir): + print(f"error: not a directory: {args.dir}", file=sys.stderr) + return 2 + result = scan(args.dir, args.stale_days) + + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py b/productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py new file mode 100644 index 00000000..90ea8a76 --- /dev/null +++ b/productivity/weekly-review/skills/weekly-review/scripts/weekly_review_gate.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""weekly_review_gate.py — Gate a GTD weekly review: completion %, named gaps, one honest verdict. + +Encodes David Allen's weekly-review checklist as three phases and ten numbered steps: + + GET CLEAR 1. Collect loose inputs (papers, receipts, notes, downloads) + 2. Process inboxes to zero (clarify, don't do) + 3. Empty your head (mind sweep — capture everything still riding in RAM) + GET CURRENT 4. Review next-action lists (mark done, prune dead, surface stuck) + 5. Review previous calendar (missed/spawned commitments become actions) + 6. Review upcoming calendar (prepare, don't react) + 7. Review waiting-for list (chase, re-date, or drop each item) + 8. Review project lists (every active project has exactly one next action) + GET CREATIVE 9. Review someday/maybe (activate, keep, or kill) + 10. Capture new ideas (add to the system while your head is clear) + +The gate's one hard rule: ALL FIVE GET CURRENT steps (4-8) are mandatory. A GET CURRENT step that +is neither done nor explicitly skipped with a reason ALWAYS forces INCOMPLETE — a review that +skims the core is a guilt ritual, not a review. + +Deterministic logic. No LLM calls. Stdlib only. + +Usage: + python weekly_review_gate.py --list + python weekly_review_gate.py --done "1,2,3,4,5,6,7,8,10" --skip "9:no someday list yet" + python weekly_review_gate.py --done "1,4,6" --json + python weekly_review_gate.py --sample + +Exit codes: + 0 review COMPLETE (every step done or skipped with a reason) — also --list / --sample / --help + 2 review INCOMPLETE (at least one step neither done nor skipped) + 3 bad input (unknown step number, malformed --done/--skip, step both done and skipped) +""" + +import argparse +import json +import sys +from typing import Any, Dict, List + +STEPS = [ + (1, "GET CLEAR", "Collect loose inputs (papers, receipts, notes, downloads)"), + (2, "GET CLEAR", "Process inboxes to zero (clarify, don't do)"), + (3, "GET CLEAR", "Empty your head (mind sweep — capture everything still in RAM)"), + (4, "GET CURRENT", "Review next-action lists (mark done, prune dead, surface stuck)"), + (5, "GET CURRENT", "Review previous calendar (missed/spawned commitments become actions)"), + (6, "GET CURRENT", "Review upcoming calendar (prepare, don't react)"), + (7, "GET CURRENT", "Review waiting-for list (chase, re-date, or drop each item)"), + (8, "GET CURRENT", "Review project lists (every active project has one next action)"), + (9, "GET CREATIVE", "Review someday/maybe (activate, keep, or kill)"), + (10, "GET CREATIVE", "Capture new ideas (add them while your head is clear)"), +] + +MANDATORY_PHASE = "GET CURRENT" +PHASE_ORDER = ["GET CLEAR", "GET CURRENT", "GET CREATIVE"] + + +def parse_done(raw: str) -> List[int]: + if not raw or not raw.strip(): + return [] + nums = [] + for part in raw.split(","): + part = part.strip() + if not part: + continue + if not part.isdigit(): + raise ValueError(f"--done entries must be step numbers, got: {part!r}") + nums.append(int(part)) + return sorted(set(nums)) + + +def parse_skips(raw_list: List[str]) -> Dict[int, str]: + skips: Dict[int, str] = {} + for raw in raw_list or []: + if ":" not in raw: + raise ValueError(f"--skip must be 'N:reason', got: {raw!r}") + num_s, reason = raw.split(":", 1) + num_s, reason = num_s.strip(), reason.strip() + if not num_s.isdigit(): + raise ValueError(f"--skip step must be a number, got: {num_s!r}") + if not reason: + raise ValueError(f"--skip requires a non-empty reason: {raw!r}") + skips[int(num_s)] = reason + return skips + + +def evaluate(done: List[int], skips: Dict[int, str]) -> Dict[str, Any]: + valid = {n for n, _, _ in STEPS} + for n in done: + if n not in valid: + raise ValueError(f"unknown step number in --done: {n} (valid: 1-10)") + for n in skips: + if n not in valid: + raise ValueError(f"unknown step number in --skip: {n} (valid: 1-10)") + overlap = set(done) & set(skips) + if overlap: + raise ValueError(f"step(s) both done and skipped: {sorted(overlap)}") + + rows = [] + missing: List[Dict[str, Any]] = [] + for n, phase, label in STEPS: + if n in done: + status = "DONE" + elif n in skips: + status = "SKIPPED" + else: + status = "MISSING" + missing.append({"step": n, "phase": phase, "label": label}) + rows.append({"step": n, "phase": phase, "label": label, "status": status, + "skip_reason": skips.get(n)}) + + considered = len(STEPS) - len(skips) + completion_pct = round(100.0 * len(done) / considered, 1) if considered else 100.0 + + missing_mandatory = [m for m in missing if m["phase"] == MANDATORY_PHASE] + skipped_mandatory = [n for n in skips if 4 <= n <= 8] + + verdict = "COMPLETE" if not missing else "INCOMPLETE" + forced = bool(missing_mandatory) + + warnings = [] + if skipped_mandatory: + warnings.append( + f"GET CURRENT step(s) {sorted(skipped_mandatory)} skipped with a reason — " + "allowed, but the core of the review was not fully walked. Do them first next week." + ) + if forced: + warnings.append( + "Unskipped GET CURRENT step(s) missing — this ALWAYS forces INCOMPLETE, " + "regardless of completion %." + ) + + return { + "steps": rows, + "done_count": len(done), + "skipped_count": len(skips), + "missing_count": len(missing), + "completion_pct": completion_pct, + "missing_steps": missing, + "missing_mandatory": missing_mandatory, + "mandatory_gate_forced_incomplete": forced, + "warnings": warnings, + "verdict": verdict, + "exit_code": 0 if verdict == "COMPLETE" else 2, + } + + +def render_list() -> str: + out = ["GTD Weekly Review — the ten steps", "=" * 64] + for phase in PHASE_ORDER: + tag = " (all mandatory)" if phase == MANDATORY_PHASE else "" + out.append(f"\n{phase}{tag}") + for n, p, label in STEPS: + if p == phase: + out.append(f" {n:>2}. {label}") + out.append("\nMark progress with --done \"1,2,3\" and --skip \"N:reason\".") + return "\n".join(out) + + +def render_human(r: Dict[str, Any]) -> str: + out = ["Weekly Review Gate (GET CLEAR -> GET CURRENT -> GET CREATIVE)", "=" * 64] + current_phase = None + for row in r["steps"]: + if row["phase"] != current_phase: + current_phase = row["phase"] + tag = " (mandatory)" if current_phase == MANDATORY_PHASE else "" + out.append(f"\n {current_phase}{tag}") + mark = {"DONE": "[x]", "SKIPPED": "[s]", "MISSING": "[ ]"}[row["status"]] + line = f" {mark} {row['step']:>2}. {row['label']}" + if row["status"] == "SKIPPED": + line += f" (skipped: {row['skip_reason']})" + out.append(line) + out.append("") + out.append(f" Completion: {r['completion_pct']}% " + f"(done {r['done_count']} · skipped {r['skipped_count']} · missing {r['missing_count']})") + if r["missing_steps"]: + out.append("\n Missing steps (do these to finish):") + for m in r["missing_steps"]: + star = " <- MANDATORY" if m["phase"] == MANDATORY_PHASE else "" + out.append(f" - {m['step']}. [{m['phase']}] {m['label']}{star}") + for w in r["warnings"]: + out.append(f"\n WARNING: {w}") + out.append("") + out.append(f" VERDICT: {r['verdict']}" + + (" (mandatory GET CURRENT gate forced this)" if r["mandatory_gate_forced_incomplete"] else "")) + return "\n".join(out) + + +def main(argv: List[str]) -> int: + p = argparse.ArgumentParser( + description="Gate a GTD weekly review: completion %, named gaps, COMPLETE/INCOMPLETE verdict.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) + p.add_argument("--done", default="", help='Comma-separated step numbers completed, e.g. "1,3,5"') + p.add_argument("--skip", action="append", default=[], + help='Skip a step with a reason, e.g. --skip "9:no someday list yet" (repeatable)') + p.add_argument("--list", action="store_true", help="Show the numbered ten-step checklist and exit") + p.add_argument("--sample", action="store_true", + help="Run an embedded example review (always exits 0; the sample shows an INCOMPLETE verdict)") + p.add_argument("--json", action="store_true", help="Emit JSON instead of text") + args = p.parse_args(argv) + + if args.list: + print(render_list()) + return 0 + + try: + if args.sample: + done = parse_done("1,2,3,4,6,7,8") + skips = parse_skips(["9:no someday list yet"]) + else: + done = parse_done(args.done) + skips = parse_skips(args.skip) + result = evaluate(done, skips) + except ValueError as e: + print(f"error: {e}", file=sys.stderr) + return 3 + + if args.json: + print(json.dumps(result, indent=2)) + else: + print(render_human(result)) + + if args.sample: + return 0 # sample is illustrative; documented to always exit 0 + return result["exit_code"] + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:]))