Class: Putpaws::Ai::Guide

Inherits:
Object
  • Object
show all
Defined in:
lib/putpaws/ai/guide.rb

Overview

putpaws away: put paws away — take your paws off and let AI take over. Installs a guide for AI agents (Claude Code etc.) into the host project: a skill file plus a pointer block in CLAUDE.md / AGENTS.md. Re-runnable: everything is regenerated in place.

Constant Summary collapse

SKILL_PATH =
File.join('.claude', 'skills', 'putpaws', 'SKILL.md')
BEGIN_MARK =
'<!-- putpaws:begin -->'
END_MARK =
'<!-- putpaws:end -->'

Class Method Summary collapse

Class Method Details

.install!(root: '.', services: nil) ⇒ Object



15
16
17
18
19
20
21
22
23
# File 'lib/putpaws/ai/guide.rb', line 15

def self.install!(root: '.', services: nil)
  services ||= Putpaws::ApplicationConfig.all.map(&:name)
  skill_path = File.join(root, SKILL_PATH)
  FileUtils.mkdir_p(File.dirname(skill_path))
  File.write(skill_path, skill_content(services: services))
  pointer_path = pointer_target(root)
  update_pointer!(pointer_path)
  [skill_path, pointer_path]
end

.pointer_blockObject



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/putpaws/ai/guide.rb', line 43

def self.pointer_block
  "    \#{BEGIN_MARK}\n    ## putpaws (AWS operations)\n\n    This project uses the putpaws gem for AWS/ECS operations (logs, deploy, migrations, one-off tasks).\n    Read `.claude/skills/putpaws/SKILL.md` for commands and safety notes before running AWS operations.\n    Quick reference: services are the top-level keys of `.putpaws/application.json`;\n    `bundle exec putpaws <service> info` shows resolved settings without touching AWS.\n    \#{END_MARK}\n  MD\nend\n"

.pointer_target(root) ⇒ Object

Prefer an existing CLAUDE.md; otherwise use AGENTS.md (created if absent).



26
27
28
29
30
# File 'lib/putpaws/ai/guide.rb', line 26

def self.pointer_target(root)
  claude = File.join(root, 'CLAUDE.md')
  return claude if File.exist?(claude)
  File.join(root, 'AGENTS.md')
end

.skill_content(services: []) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/putpaws/ai/guide.rb', line 56

def self.skill_content(services: [])
  services_line = services.empty? ? '(none configured yet — check .putpaws/application.json)' : services.join(', ')
  "    ---\n    name: putpaws\n    description: AWS operations for this project (CloudWatch logs, ECS deploy, db:migrate and one-off tasks on Fargate, CodeBuild CI) via the putpaws CLI. Use when asked to investigate logs, deploy, run rake tasks on AWS, or inspect ECS services.\n    ---\n\n    # putpaws\n\n    Capistrano-style AWS operation commands. General form:\n\n        bundle exec putpaws <service> <command> key=value\n\n    Services in this project: \#{services_line}\n    (services are the top-level keys of `.putpaws/application.json`)\n\n    Discover commands with `bundle exec putpaws -T` (or `-D` for full descriptions).\n\n    ## Read-only commands (safe to run without asking)\n\n    - `bundle exec putpaws <service> info` \u2014 resolved settings, no AWS access\n    - `bundle exec putpaws <service> log:tail since=2h` \u2014 CloudWatch logs (units: s/m/h/d/w, add `for=1h` to bound the range)\n    - `bundle exec putpaws <service> log:tailf` \u2014 follow logs (long-running; prefer `log:tail` in automation)\n    - `bundle exec putpaws ahead` \u2014 dry-run of provisioning (CREATE/UPDATE/SKIP)\n\n    ## Mutating commands (confirm with the user before running)\n\n    - `bundle exec putpaws <service> ecs:run cmd='bundle exec rake db:migrate' wait=true`\n      \u2014 runs a one-off command on a temporary Fargate task and exits non-zero on failure.\n      The best choice for migrations and rake tasks. Fully non-interactive.\n    - `bundle exec putpaws <service> ecs:deploy wait=true` \u2014 redeploy the service (force new deployment)\n    - `bundle exec putpaws <service> code_build:build branch=main` \u2014 start the CI build (build + push + deploy per buildspec)\n    - `bundle exec putpaws <service> scheduler:deploy` \u2014 deploy schedules from `.putpaws/schedule.json`\n\n    ## Interactive commands (avoid in non-TTY sessions; meant for humans)\n\n    - `ecs:attach`, `ecs:shell`, `ecs:forward` \u2014 open live sessions into containers\n    - `ready`, `steady`, `up`, `iam:grant` \u2014 provisioning flows with confirmation prompts\n\n    ## Config files (source of truth, safe to read)\n\n    - `.putpaws/application.json` \u2014 services and their cluster/log/build settings\n    - `.putpaws/infra.json` \u2014 network (subnets/security groups) and target (cluster/taskdef) definitions\n    - `.putpaws/schedule.json` \u2014 named schedules, `.putpaws/operators.json` \u2014 operator permission profiles\n    - `.putpaws/provisioning/<service>/` \u2014 provisioning inputs (provision.json), state, IAM role drafts\n\n    ## Suggested permission allowlist (.claude/settings.json)\n\n        \"permissions\": {\n          \"allow\": [\n            \"Bash(bundle exec putpaws -T*)\",\n            \"Bash(bundle exec putpaws * info)\",\n            \"Bash(bundle exec putpaws * log:*)\"\n          ]\n        }\n\n    ---\n    Generated by putpaws \#{Putpaws::VERSION}. Refresh with `bundle exec putpaws away` after updating the gem.\n  MD\nend\n"

.update_pointer!(path) ⇒ Object



32
33
34
35
36
37
38
39
40
41
# File 'lib/putpaws/ai/guide.rb', line 32

def self.update_pointer!(path)
  block = pointer_block
  content = File.exist?(path) ? File.read(path) : ''
  if content.include?(BEGIN_MARK)
    content = content.sub(/#{Regexp.escape(BEGIN_MARK)}.*#{Regexp.escape(END_MARK)}/m, block.strip)
  else
    content = content.empty? ? block : content.rstrip + "\n\n" + block
  end
  File.write(path, content)
end