class-ai-agent 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/README.md +33 -0
- package/.agent/SESSION.md +54 -0
- package/.agent/SESSION.template.md +46 -0
- package/.claude/CLAUDE.md +21 -6
- package/.claude/commands/build.md +5 -4
- package/.claude/commands/debug.md +2 -1
- package/.claude/commands/handoff.md +94 -0
- package/.claude/commands/plan.md +1 -0
- package/.claude/commands/publish-npm.md +119 -0
- package/.claude/commands/resume.md +107 -0
- package/.claude/commands/spec.md +2 -1
- package/.claude/references/agent-continuity.md +42 -0
- package/.claude/references/codegraph.md +50 -0
- package/.claude/rules/agent-continuity.md +39 -0
- package/.claude/skills/agent-continuity/SKILL.md +70 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.cursor/CURSOR.md +37 -5
- package/.cursor/commands/build.md +5 -4
- package/.cursor/commands/debug.md +2 -1
- package/.cursor/commands/handoff.md +94 -0
- package/.cursor/commands/plan.md +1 -0
- package/.cursor/commands/publish-npm.md +119 -0
- package/.cursor/commands/resume.md +107 -0
- package/.cursor/commands/spec.md +2 -1
- package/.cursor/mcp.json +15 -0
- package/.cursor/references/agent-continuity.md +42 -0
- package/.cursor/references/codegraph.md +87 -0
- package/.cursor/rules/agent-continuity.mdc +44 -0
- package/.cursor/rules/codegraph.mdc +47 -0
- package/.cursor/rules/cursor-overview.mdc +10 -3
- package/.cursor/skills/agent-continuity/SKILL.md +70 -0
- package/.cursor/skills/ui-ux-pro-max/SKILL.md +288 -0
- package/.cursor/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.cursor/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.cursor/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.cursor/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.cursor/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.cursor/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.cursor/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.cursor/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.cursor/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.cursor/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.cursor/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.cursor/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.cursor/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.cursor/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.cursor/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.kiro/KIRO.md +146 -0
- package/.kiro/agents/backend.md +395 -0
- package/.kiro/agents/code-reviewer.md +110 -0
- package/.kiro/agents/copywriter-seo.md +236 -0
- package/.kiro/agents/frontend.md +384 -0
- package/.kiro/agents/project-manager.md +201 -0
- package/.kiro/agents/qa.md +221 -0
- package/.kiro/agents/security-auditor.md +143 -0
- package/.kiro/agents/systems-architect.md +211 -0
- package/.kiro/agents/test-engineer.md +123 -0
- package/.kiro/agents/ui-ux-designer.md +210 -0
- package/.kiro/commands/build.md +133 -0
- package/.kiro/commands/debug.md +243 -0
- package/.kiro/commands/deploy.md +40 -0
- package/.kiro/commands/fix-issue.md +42 -0
- package/.kiro/commands/handoff.md +94 -0
- package/.kiro/commands/plan.md +126 -0
- package/.kiro/commands/publish-npm.md +119 -0
- package/.kiro/commands/resume.md +107 -0
- package/.kiro/commands/review.md +50 -0
- package/.kiro/commands/simplify.md +222 -0
- package/.kiro/commands/spec.md +96 -0
- package/.kiro/commands/test.md +214 -0
- package/.kiro/references/accessibility-checklist.md +174 -0
- package/.kiro/references/agent-continuity.md +42 -0
- package/.kiro/references/codegraph.md +86 -0
- package/.kiro/references/performance-checklist.md +150 -0
- package/.kiro/references/security-checklist.md +94 -0
- package/.kiro/references/testing-patterns.md +183 -0
- package/.kiro/settings/mcp.json +15 -0
- package/.kiro/settings.json +8 -0
- package/.kiro/skills/agent-continuity/SKILL.md +70 -0
- package/.kiro/skills/code-review/SKILL.md +208 -0
- package/.kiro/skills/deploy/SKILL.md +68 -0
- package/.kiro/skills/deploy/deploy.md +735 -0
- package/.kiro/skills/incremental-implementation/SKILL.md +210 -0
- package/.kiro/skills/security-review/SKILL.md +71 -0
- package/.kiro/skills/tdd/SKILL.md +217 -0
- package/.kiro/skills/ui-ux-pro-max/SKILL.md +288 -0
- package/.kiro/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.kiro/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.kiro/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.kiro/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.kiro/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.kiro/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.kiro/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.kiro/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.kiro/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.kiro/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.kiro/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.kiro/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.kiro/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.kiro/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.kiro/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.kiro/steering/agent-continuity.md +44 -0
- package/.kiro/steering/api-conventions.md +85 -0
- package/.kiro/steering/clean-code.md +211 -0
- package/.kiro/steering/code-style.md +92 -0
- package/.kiro/steering/codegraph.md +47 -0
- package/.kiro/steering/database.md +66 -0
- package/.kiro/steering/error-handling.md +98 -0
- package/.kiro/steering/git-workflow.md +83 -0
- package/.kiro/steering/kiro-overview.md +38 -0
- package/.kiro/steering/monitoring.md +317 -0
- package/.kiro/steering/naming-conventions.md +266 -0
- package/.kiro/steering/project-structure.md +71 -0
- package/.kiro/steering/security.md +95 -0
- package/.kiro/steering/system-design.md +168 -0
- package/.kiro/steering/tech-stack.md +462 -0
- package/.kiro/steering/testing.md +110 -0
- package/AGENTS.md +13 -7
- package/README.md +126 -18
- package/bin/class-ai-agent.cjs +165 -11
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI Agent Project
|
|
2
2
|
|
|
3
|
-
**Production-grade configuration for [Claude Code](https://code.claude.com/docs)
|
|
3
|
+
**Production-grade configuration for [Claude Code](https://code.claude.com/docs), [Cursor](https://cursor.com), and [Kiro](https://kiro.dev)** — shared rules, specialized agents, workflow prompts, and checklists you can drop into any repository.
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ Open-source AI agent scaffolding by **Royal Solution** — use it in your own pr
|
|
|
18
18
|
<a href="https://www.npmjs.com/package/class-ai-agent"><img src="https://img.shields.io/npm/v/class-ai-agent?label=npm&logo=npm&style=flat-square" alt="npm version" /></a>
|
|
19
19
|
<img src="https://img.shields.io/badge/node-%3E%3D16.7-339933?logo=node.js&logoColor=white&style=flat-square" alt="Node.js 16.7+" />
|
|
20
20
|
<img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License MIT" />
|
|
21
|
-
<img src="https://img.shields.io/badge/version-1.
|
|
21
|
+
<img src="https://img.shields.io/badge/version-1.3.0-blue?style=flat-square" alt="Version" />
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
24
|
</div>
|
|
@@ -29,6 +29,8 @@ Open-source AI agent scaffolding by **Royal Solution** — use it in your own pr
|
|
|
29
29
|
|
|
30
30
|
- [Why use this](#why-use-this)
|
|
31
31
|
- [Install (quick)](#install-quick)
|
|
32
|
+
- [CodeGraph (code intelligence)](#codegraph-code-intelligence)
|
|
33
|
+
- [Agent continuity (cross-tool)](#agent-continuity-cross-tool)
|
|
32
34
|
- [Overview](#overview)
|
|
33
35
|
- [Development workflow](#development-workflow)
|
|
34
36
|
- [Project structure](#project-structure)
|
|
@@ -38,6 +40,7 @@ Open-source AI agent scaffolding by **Royal Solution** — use it in your own pr
|
|
|
38
40
|
- [Using commands & agents](#using-commands--agents)
|
|
39
41
|
- [Key concepts](#key-concepts)
|
|
40
42
|
- [Security](#security)
|
|
43
|
+
- [Release notes](#release-notes)
|
|
41
44
|
- [Contributing](#contributing)
|
|
42
45
|
- [Publishing to npm (maintainers)](#publishing-to-npm-maintainers)
|
|
43
46
|
- [Credits](#credits)
|
|
@@ -48,13 +51,32 @@ Open-source AI agent scaffolding by **Royal Solution** — use it in your own pr
|
|
|
48
51
|
|
|
49
52
|
| You get | Details |
|
|
50
53
|
|--------|---------|
|
|
51
|
-
| **
|
|
54
|
+
| **Three layouts** | **`.claude/`** (Claude Code), **`.cursor/`** (Cursor), **`.kiro/`** (Kiro steering + MCP) |
|
|
52
55
|
| **One workflow** | Spec → Plan → Build → Test → Review → Ship |
|
|
53
56
|
| **10 agent personas** | Frontend, backend, architect, review, QA, security, PM, UX, SEO, test engineer |
|
|
54
57
|
| **13 topic rules** | Code style, security, API, DB, testing, git, and more (same ideas in both trees) |
|
|
55
58
|
| **`npx` installer** | Copies the folders into your project in one command |
|
|
59
|
+
| **CodeGraph** | MCP + usage rules for Cursor and Kiro; local index via [CodeGraph](https://github.com/colbymchenry/codegraph) |
|
|
60
|
+
| **Agent continuity** | Committed **`.agent/SESSION.md`** — `/resume` and `/handoff` across Cursor, Claude Code, and Kiro |
|
|
56
61
|
|
|
57
|
-
Root **`AGENTS.md`**
|
|
62
|
+
Root **`AGENTS.md`** links hubs: **`.cursor/CURSOR.md`**, **`.kiro/KIRO.md`**, **`.claude/CLAUDE.md`**.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Agent continuity (cross-tool)
|
|
67
|
+
|
|
68
|
+
When one agent stops and another starts (new chat, different IDE, or teammate), **`npx class-ai-agent`** installs **`.agent/SESSION.md`** — a committed handoff file shared by all tools.
|
|
69
|
+
|
|
70
|
+
| Command | When |
|
|
71
|
+
|---------|------|
|
|
72
|
+
| **`/resume`** | Session start — read SESSION, `tasks/todo.md`, linked spec; summarize and continue |
|
|
73
|
+
| **`/handoff`** | Session end — update SESSION, sync tasks, note blockers |
|
|
74
|
+
|
|
75
|
+
**Read order:** `.agent/SESSION.md` → `tasks/todo.md` → `SPEC.md` (from SESSION pointers).
|
|
76
|
+
|
|
77
|
+
Rules: `.cursor/rules/agent-continuity.mdc`, `.claude/rules/agent-continuity.md`, `.kiro/steering/agent-continuity.md`. Reference: `.cursor/references/agent-continuity.md`.
|
|
78
|
+
|
|
79
|
+
Do **not** put secrets or PII in `SESSION.md`. See [`.agent/README.md`](.agent/README.md).
|
|
58
80
|
|
|
59
81
|
---
|
|
60
82
|
|
|
@@ -66,12 +88,21 @@ Requires **Node.js [16.7+](https://nodejs.org/)**.
|
|
|
66
88
|
npx class-ai-agent
|
|
67
89
|
```
|
|
68
90
|
|
|
69
|
-
|
|
91
|
+
The installer also runs **`npx @colbymchenry/codegraph init -i`** in the target directory (builds `.codegraph/` and adds it to `.gitignore`). **Node 20+** is recommended for CodeGraph. First run may take a minute while the index builds.
|
|
92
|
+
|
|
93
|
+
Skip indexing (copy-only, e.g. CI):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
CODEGRAPH_SKIP_INIT=1 npx class-ai-agent
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Install into another directory, or only one agent target:
|
|
70
100
|
|
|
71
101
|
```bash
|
|
72
102
|
npx class-ai-agent --dir /path/to/your/project
|
|
73
103
|
npx class-ai-agent --claude
|
|
74
104
|
npx class-ai-agent --cursor
|
|
105
|
+
npx class-ai-agent --kiro
|
|
75
106
|
npx class-ai-agent --force # overwrite existing
|
|
76
107
|
npx class-ai-agent --help
|
|
77
108
|
```
|
|
@@ -85,29 +116,48 @@ npm exec -- class-ai-agent --dir /path/to/your/project
|
|
|
85
116
|
# or: node bin/class-ai-agent.cjs --dir /path/to/your/project
|
|
86
117
|
```
|
|
87
118
|
|
|
88
|
-
**Manual copy:** copy **`.claude/`**, **`.cursor/`**, and **`AGENTS.md`** into your project root.
|
|
119
|
+
**Manual copy:** copy **`.agent/`**, **`.claude/`**, **`.cursor/`**, **`.kiro/`**, and **`AGENTS.md`** into your project root. Copy `.agent/SESSION.template.md` → `.agent/SESSION.md` if needed. Then run `npx @colbymchenry/codegraph init -i` and reload Cursor / restart Kiro.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## CodeGraph (code intelligence)
|
|
124
|
+
|
|
125
|
+
[class-ai-agent](https://www.npmjs.com/package/class-ai-agent) integrates **[CodeGraph](https://github.com/colbymchenry/codegraph)** — a local knowledge graph of symbols, call edges, and routes — so agents can answer structural questions with fewer grep/read tool calls.
|
|
126
|
+
|
|
127
|
+
| Topic | Details |
|
|
128
|
+
|-------|---------|
|
|
129
|
+
| **What you get** | Cursor + Kiro MCP config, usage rules, auto `init -i`, `.codegraph/` gitignored |
|
|
130
|
+
| **Cursor** | `.cursor/mcp.json`, `.cursor/rules/codegraph.mdc` — reload window after install |
|
|
131
|
+
| **Kiro** | `.kiro/settings/mcp.json`, `.kiro/steering/codegraph.md` — restart Kiro after install |
|
|
132
|
+
| **Claude Code** | Not wired by this package — `codegraph install --target=claude` (see `.claude/references/codegraph.md`) |
|
|
133
|
+
| **Manual index** | `npx @colbymchenry/codegraph init -i` if auto-init failed |
|
|
134
|
+
| **Opt-out** | `CODEGRAPH_SKIP_INIT=1` when running `npx class-ai-agent` |
|
|
135
|
+
| **Troubleshooting** | [CodeGraph README](https://github.com/colbymchenry/codegraph#troubleshooting) · `.cursor/references/codegraph.md` |
|
|
89
136
|
|
|
90
137
|
---
|
|
91
138
|
|
|
92
139
|
## Overview
|
|
93
140
|
|
|
94
|
-
This repo ships **
|
|
141
|
+
This repo ships **three parallel trees** so you can use the same habits in Claude Code, Cursor, and Kiro:
|
|
95
142
|
|
|
96
143
|
| Layout | Tool | What you use |
|
|
97
144
|
|--------|------|----------------|
|
|
98
145
|
| **`.claude/`** | Claude Code | Slash commands, **`CLAUDE.md`**, rules as **`.md`** |
|
|
99
146
|
| **`.cursor/`** | Cursor | Project rules as **`.mdc`**, hub **`CURSOR.md`**, **`@`** file mentions |
|
|
147
|
+
| **`.kiro/`** | Kiro | **Steering** as **`.md`** in `.kiro/steering/`, hub **`KIRO.md`**, **`.kiro/settings/mcp.json`** |
|
|
100
148
|
|
|
101
149
|
What is inside:
|
|
102
150
|
|
|
103
151
|
- **Structured workflow** (Spec → Plan → Build → Test → Review → Ship)
|
|
104
152
|
- **10 specialized agents** (markdown personas under `agents/`)
|
|
105
153
|
- **13 mandatory topic rules** (plus **`cursor-overview.mdc`** under `.cursor/rules/`)
|
|
106
|
-
- **
|
|
107
|
-
- **
|
|
108
|
-
- **
|
|
154
|
+
- **11 workflow prompts** under `commands/` (includes `handoff`, `resume`)
|
|
155
|
+
- **7 skills** (TDD, code review, incremental implementation, deploy, security review, agent continuity, UI/UX Pro Max)
|
|
156
|
+
- **6 reference docs** (security, testing, performance, accessibility, codegraph, agent-continuity)
|
|
157
|
+
- **`.agent/SESSION.md`** for cross-tool session handoff
|
|
158
|
+
- **CodeGraph MCP** for Cursor and Kiro (`.cursor/mcp.json`, `.kiro/settings/mcp.json`)
|
|
109
159
|
|
|
110
|
-
Keep **`.claude
|
|
160
|
+
Keep **`.claude/`**, **`.cursor/`**, and **`.kiro/`** in sync when you change standards. After editing `.cursor/`, run **`npm run sync:kiro`** (or `node scripts/sync-kiro-from-cursor.mjs`) to refresh `.kiro/`.
|
|
111
161
|
|
|
112
162
|
---
|
|
113
163
|
|
|
@@ -129,22 +179,33 @@ Keep **`.claude/`** and **`.cursor/`** in sync when you change standards.
|
|
|
129
179
|
| **Review** | `/review` | Five-axis code review |
|
|
130
180
|
| **Ship** | `/deploy` | Build, test, deploy |
|
|
131
181
|
|
|
132
|
-
**Supporting:** `/debug`, `/simplify`, `/fix-issue` (see `commands/`).
|
|
182
|
+
**Supporting:** `/debug`, `/simplify`, `/fix-issue`, `/handoff`, `/resume` (see `commands/`).
|
|
183
|
+
|
|
184
|
+
At session boundaries, use **`/handoff`** before switching tools and **`/resume`** when picking work back up.
|
|
133
185
|
|
|
134
186
|
---
|
|
135
187
|
|
|
136
188
|
## Project Structure
|
|
137
189
|
|
|
190
|
+
### `.agent/` (cross-tool)
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
.agent/
|
|
194
|
+
├── README.md
|
|
195
|
+
├── SESSION.md # Live handoff (committed; seeded on install)
|
|
196
|
+
└── SESSION.template.md # Schema reference
|
|
197
|
+
```
|
|
198
|
+
|
|
138
199
|
### `.claude/` (Claude Code)
|
|
139
200
|
|
|
140
201
|
```
|
|
141
202
|
.claude/
|
|
142
203
|
├── CLAUDE.md # Main configuration
|
|
143
|
-
├── commands/ #
|
|
204
|
+
├── commands/ # 11 workflow prompts (spec, plan, build, handoff, resume, …)
|
|
144
205
|
├── agents/ # 10 personas
|
|
145
|
-
├── rules/ #
|
|
206
|
+
├── rules/ # 14 mandatory topic rules (.md)
|
|
146
207
|
├── skills/ # TDD, review, deploy, …
|
|
147
|
-
├── references/ # Checklists
|
|
208
|
+
├── references/ # Checklists + codegraph.md
|
|
148
209
|
└── settings.json
|
|
149
210
|
```
|
|
150
211
|
|
|
@@ -155,21 +216,55 @@ Same ideas as `.claude/` for `commands/`, `agents/`, `skills/`, `references/`. D
|
|
|
155
216
|
| Item | Role |
|
|
156
217
|
|------|------|
|
|
157
218
|
| **`CURSOR.md`** | Hub (like `CLAUDE.md`, Cursor-focused) |
|
|
158
|
-
| **`rules/*.mdc`** | Project rules + YAML; **`security.mdc
|
|
219
|
+
| **`rules/*.mdc`** | Project rules + YAML; **`security.mdc`**, **`cursor-overview.mdc`**, **`codegraph.mdc`** are central |
|
|
220
|
+
| **`mcp.json`** | MCP servers (CodeGraph) |
|
|
159
221
|
| **`settings.json`** | Path hints (mirrors Claude layout) |
|
|
160
222
|
| **`AGENTS.md`** (repo root) | Entry pointer for Cursor |
|
|
161
223
|
|
|
162
224
|
```
|
|
163
225
|
.cursor/
|
|
164
226
|
├── CURSOR.md
|
|
227
|
+
├── mcp.json # CodeGraph MCP (npx @colbymchenry/codegraph)
|
|
228
|
+
├── settings.json
|
|
229
|
+
├── commands/
|
|
230
|
+
├── agents/
|
|
231
|
+
├── rules/ # 16 × .mdc (13 topics + cursor-overview + codegraph + agent-continuity)
|
|
232
|
+
├── skills/
|
|
233
|
+
└── references/ # includes codegraph.md
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
After install, **`.codegraph/`** (generated index) lives at the project root and should stay gitignored.
|
|
237
|
+
|
|
238
|
+
### `.kiro/` (Kiro)
|
|
239
|
+
|
|
240
|
+
Mirrors `.cursor/` for `commands/`, `agents/`, `skills/`, `references/`. Kiro uses **steering** instead of `.mdc` rules:
|
|
241
|
+
|
|
242
|
+
| Item | Role |
|
|
243
|
+
|------|------|
|
|
244
|
+
| **`KIRO.md`** | Hub (like `CURSOR.md`) |
|
|
245
|
+
| **`steering/*.md`** | Project standards (`inclusion: always` / `fileMatch` / `manual`) |
|
|
246
|
+
| **`settings/mcp.json`** | MCP servers (CodeGraph) |
|
|
247
|
+
| **`settings.json`** | Path hints |
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
.kiro/
|
|
251
|
+
├── KIRO.md
|
|
252
|
+
├── settings/
|
|
253
|
+
│ └── mcp.json # CodeGraph MCP
|
|
165
254
|
├── settings.json
|
|
255
|
+
├── steering/ # kiro-overview.md, security.md, codegraph.md, …
|
|
166
256
|
├── commands/
|
|
167
257
|
├── agents/
|
|
168
|
-
├── rules/ # 14 × .mdc (13 topics + cursor-overview)
|
|
169
258
|
├── skills/
|
|
170
259
|
└── references/
|
|
171
260
|
```
|
|
172
261
|
|
|
262
|
+
Regenerate from `.cursor/` after rule changes: `npm run sync:kiro`.
|
|
263
|
+
|
|
264
|
+
### Skill notes
|
|
265
|
+
|
|
266
|
+
- **`ui-ux-pro-max/`**: A UI/UX-oriented skill bundle (includes datasets under `data/` and helper scripts under `scripts/`) to support design-system decisions and UX guidelines.
|
|
267
|
+
|
|
173
268
|
---
|
|
174
269
|
|
|
175
270
|
## Specialized Agents
|
|
@@ -267,13 +362,25 @@ Ship thin end-to-end slices (DB + API + UI), not “all models first, then all r
|
|
|
267
362
|
|
|
268
363
|
---
|
|
269
364
|
|
|
365
|
+
## Release notes
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
### 1.3.0 — 2026-06-02
|
|
370
|
+
|
|
371
|
+
- Add **Kiro** support (steering, commands, MCP) with `npm run sync:kiro` to keep `.kiro/` aligned with `.cursor/`
|
|
372
|
+
- Ship **CodeGraph** rules, references, and MCP config for Cursor, Claude Code, and Kiro
|
|
373
|
+
- Add **agent continuity**: `.agent/SESSION.md` template, `/resume` and `/handoff` commands, and cross-tool rules
|
|
374
|
+
- Document `codegraph_context` (`task`) vs `codegraph_search` (`query`) so agents avoid MCP parameter errors
|
|
375
|
+
- Include **UI/UX Pro Max** skill and expanded production skills, commands, and specialized agents
|
|
376
|
+
|
|
270
377
|
## Contributing
|
|
271
378
|
|
|
272
379
|
1. Follow the workflow (`/spec` → `/plan` → `/build`, or the same prompts under `.cursor/commands/`).
|
|
273
380
|
2. Keep tests green.
|
|
274
381
|
3. Run **`/review`** before opening a PR.
|
|
275
382
|
4. Use [conventional commits](https://www.conventionalcommits.org/).
|
|
276
|
-
5. Update
|
|
383
|
+
5. Update **`.claude/`**, **`.cursor/`**, and **`.kiro/`** when rules or workflows change (run `npm run sync:kiro` after `.cursor/` edits).
|
|
277
384
|
|
|
278
385
|
---
|
|
279
386
|
|
|
@@ -314,3 +421,4 @@ That advances **`patch`** (e.g. `1.2.0` → `1.2.1`). Use **`npm version minor`*
|
|
|
314
421
|
|
|
315
422
|
- **[Royal Solution](https://codewebkhongkho.com/portfolios)** — project and scaffolding.
|
|
316
423
|
- Upstream inspiration: [fdhhhdjd/Class-AI-Agent](https://github.com/fdhhhdjd/Class-AI-Agent), [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills).
|
|
424
|
+
- **CodeGraph:** [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph) (MIT).
|
package/bin/class-ai-agent.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
6
7
|
|
|
7
8
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
8
9
|
|
|
@@ -12,7 +13,7 @@ function readPkg() {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function printHelp() {
|
|
15
|
-
console.log(`class-ai-agent — Install Claude Code &
|
|
16
|
+
console.log(`class-ai-agent — Install Claude Code, Cursor & Kiro AI agent scaffolding
|
|
16
17
|
|
|
17
18
|
Usage:
|
|
18
19
|
npx class-ai-agent [init] [options]
|
|
@@ -20,15 +21,27 @@ Usage:
|
|
|
20
21
|
Options:
|
|
21
22
|
-d, --dir <path> Target directory (default: current working directory)
|
|
22
23
|
--claude Install only .claude/
|
|
23
|
-
--cursor Install only .cursor/
|
|
24
|
+
--cursor Install only .cursor/
|
|
25
|
+
--kiro Install only .kiro/
|
|
24
26
|
-f, --force Overwrite existing files or directories
|
|
25
27
|
-h, --help Show help
|
|
26
28
|
-v, --version Print version
|
|
27
29
|
|
|
30
|
+
AGENTS.md is installed with --cursor, --kiro, or a full install (no --*-only flags).
|
|
31
|
+
|
|
32
|
+
.agent/:
|
|
33
|
+
Cross-tool session handoff (.agent/SESSION.md). Seeded from template on install;
|
|
34
|
+
existing SESSION.md is never overwritten unless you use --force on .agent/.
|
|
35
|
+
|
|
36
|
+
CodeGraph:
|
|
37
|
+
After install, runs "npx @colbymchenry/codegraph init -i" in the target directory
|
|
38
|
+
(Node 20+ recommended). Set CODEGRAPH_SKIP_INIT=1 to skip indexing.
|
|
39
|
+
|
|
28
40
|
Examples:
|
|
29
41
|
npx class-ai-agent
|
|
30
42
|
npx class-ai-agent --dir ./my-app
|
|
31
|
-
npx class-ai-agent --
|
|
43
|
+
npx class-ai-agent --kiro --force
|
|
44
|
+
CODEGRAPH_SKIP_INIT=1 npx class-ai-agent
|
|
32
45
|
`);
|
|
33
46
|
}
|
|
34
47
|
|
|
@@ -38,6 +51,7 @@ function parseArgs(argv) {
|
|
|
38
51
|
dir: process.cwd(),
|
|
39
52
|
claudeOnly: false,
|
|
40
53
|
cursorOnly: false,
|
|
54
|
+
kiroOnly: false,
|
|
41
55
|
force: false,
|
|
42
56
|
help: false,
|
|
43
57
|
version: false,
|
|
@@ -70,6 +84,10 @@ function parseArgs(argv) {
|
|
|
70
84
|
opts.cursorOnly = true;
|
|
71
85
|
continue;
|
|
72
86
|
}
|
|
87
|
+
if (a === '--kiro') {
|
|
88
|
+
opts.kiroOnly = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
73
91
|
if (a === '-d' || a === '--dir') {
|
|
74
92
|
const v = argv[++i];
|
|
75
93
|
if (!v) {
|
|
@@ -120,14 +138,126 @@ function copyFile(src, dest, { force }) {
|
|
|
120
138
|
fs.copyFileSync(src, dest);
|
|
121
139
|
}
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
141
|
+
const CODEGRAPH_GITIGNORE_ENTRY = '.codegraph/';
|
|
142
|
+
|
|
143
|
+
function ensureCodegraphGitignore(targetDir) {
|
|
144
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
145
|
+
const entry = CODEGRAPH_GITIGNORE_ENTRY;
|
|
146
|
+
|
|
147
|
+
if (fs.existsSync(gitignorePath)) {
|
|
148
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
149
|
+
if (content.includes(entry) || content.includes('.codegraph')) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const suffix = content.endsWith('\n') ? '' : '\n';
|
|
153
|
+
fs.appendFileSync(gitignorePath, `${suffix}${entry}\n`);
|
|
154
|
+
console.log(`Updated: ${gitignorePath} (${entry})`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fs.writeFileSync(gitignorePath, `${entry}\n`);
|
|
159
|
+
console.log(`Created: ${gitignorePath} (${entry})`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function runCodegraphInit(targetDir) {
|
|
163
|
+
if (process.env.CODEGRAPH_SKIP_INIT === '1') {
|
|
164
|
+
console.log('\nCodeGraph: skipped (CODEGRAPH_SKIP_INIT=1).');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log('\nCodeGraph: building local index (npx @colbymchenry/codegraph init -i)...');
|
|
169
|
+
const result = spawnSync(
|
|
170
|
+
'npx',
|
|
171
|
+
['-y', '@colbymchenry/codegraph@latest', 'init', '-i'],
|
|
172
|
+
{
|
|
173
|
+
cwd: targetDir,
|
|
174
|
+
stdio: 'inherit',
|
|
175
|
+
shell: process.platform === 'win32',
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (result.status === 0) {
|
|
180
|
+
console.log(`CodeGraph: index ready under ${path.join(targetDir, '.codegraph')}`);
|
|
181
|
+
console.log(
|
|
182
|
+
'CodeGraph: reload Cursor (.cursor/mcp.json) and/or restart Kiro (.kiro/settings/mcp.json).'
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.warn(
|
|
188
|
+
'\nCodeGraph: init failed (network, Node version, or permissions). Scaffolding was installed.'
|
|
189
|
+
);
|
|
190
|
+
console.warn(
|
|
191
|
+
' Retry: npx @colbymchenry/codegraph init -i (Node 20+ recommended)'
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function shouldInstallTarget(target, opts) {
|
|
196
|
+
const only = {
|
|
197
|
+
claude: opts.claudeOnly,
|
|
198
|
+
cursor: opts.cursorOnly,
|
|
199
|
+
kiro: opts.kiroOnly,
|
|
200
|
+
};
|
|
201
|
+
const anyOnly = only.claude || only.cursor || only.kiro;
|
|
202
|
+
if (!anyOnly) return true;
|
|
203
|
+
return only[target];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function shouldInstallAgentDir(opts) {
|
|
207
|
+
const anyOnly = opts.claudeOnly || opts.cursorOnly || opts.kiroOnly;
|
|
208
|
+
return !anyOnly;
|
|
209
|
+
}
|
|
127
210
|
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
211
|
+
function installAgentContinuity(targetDir, { force }) {
|
|
212
|
+
const srcDir = path.join(PKG_ROOT, '.agent');
|
|
213
|
+
const destDir = path.join(targetDir, '.agent');
|
|
214
|
+
const templateSrc = path.join(srcDir, 'SESSION.template.md');
|
|
215
|
+
const sessionDest = path.join(destDir, 'SESSION.md');
|
|
216
|
+
|
|
217
|
+
if (!fs.existsSync(srcDir)) {
|
|
218
|
+
console.error(`Error: template missing from package: ${srcDir}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (fs.existsSync(destDir)) {
|
|
223
|
+
if (force) {
|
|
224
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
225
|
+
fs.cpSync(srcDir, destDir, { recursive: true });
|
|
226
|
+
console.log(`Installed: ${destDir} (overwritten)`);
|
|
227
|
+
} else {
|
|
228
|
+
const readmeSrc = path.join(srcDir, 'README.md');
|
|
229
|
+
const readmeDest = path.join(destDir, 'README.md');
|
|
230
|
+
if (fs.existsSync(readmeSrc) && (!fs.existsSync(readmeDest) || force)) {
|
|
231
|
+
ensureParentDir(readmeDest);
|
|
232
|
+
fs.copyFileSync(readmeSrc, readmeDest);
|
|
233
|
+
console.log(`Updated: ${readmeDest}`);
|
|
234
|
+
}
|
|
235
|
+
const templateDest = path.join(destDir, 'SESSION.template.md');
|
|
236
|
+
if (fs.existsSync(templateSrc) && (!fs.existsSync(templateDest) || force)) {
|
|
237
|
+
ensureParentDir(templateDest);
|
|
238
|
+
fs.copyFileSync(templateSrc, templateDest);
|
|
239
|
+
console.log(`Updated: ${templateDest}`);
|
|
240
|
+
}
|
|
241
|
+
console.log(`Skipped overwrite: ${destDir} (existing; use --force to replace)`);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
fs.mkdirSync(path.dirname(destDir), { recursive: true });
|
|
245
|
+
fs.cpSync(srcDir, destDir, { recursive: true });
|
|
246
|
+
console.log(`Installed: ${destDir}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!fs.existsSync(sessionDest) && fs.existsSync(templateSrc)) {
|
|
250
|
+
ensureParentDir(sessionDest);
|
|
251
|
+
fs.copyFileSync(templateSrc, sessionDest);
|
|
252
|
+
console.log(`Created: ${sessionDest} (from template)`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function run(opts) {
|
|
257
|
+
const installClaude = shouldInstallTarget('claude', opts);
|
|
258
|
+
const installCursor = shouldInstallTarget('cursor', opts);
|
|
259
|
+
const installKiro = shouldInstallTarget('kiro', opts);
|
|
260
|
+
const installAgents = installCursor || installKiro;
|
|
131
261
|
|
|
132
262
|
fs.mkdirSync(opts.dir, { recursive: true });
|
|
133
263
|
|
|
@@ -147,6 +277,13 @@ function run(opts) {
|
|
|
147
277
|
console.log(`Installed: ${dest}`);
|
|
148
278
|
}
|
|
149
279
|
|
|
280
|
+
if (installKiro) {
|
|
281
|
+
const src = path.join(PKG_ROOT, '.kiro');
|
|
282
|
+
const dest = path.join(opts.dir, '.kiro');
|
|
283
|
+
copyDir(src, dest, copyOpts);
|
|
284
|
+
console.log(`Installed: ${dest}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
150
287
|
if (installAgents) {
|
|
151
288
|
const src = path.join(PKG_ROOT, 'AGENTS.md');
|
|
152
289
|
const dest = path.join(opts.dir, 'AGENTS.md');
|
|
@@ -154,10 +291,27 @@ function run(opts) {
|
|
|
154
291
|
console.log(`Installed: ${dest}`);
|
|
155
292
|
}
|
|
156
293
|
|
|
294
|
+
if (shouldInstallAgentDir(opts)) {
|
|
295
|
+
installAgentContinuity(opts.dir, copyOpts);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
ensureCodegraphGitignore(opts.dir);
|
|
299
|
+
runCodegraphInit(opts.dir);
|
|
300
|
+
|
|
157
301
|
const hints = [];
|
|
158
302
|
if (installClaude) hints.push('.claude/CLAUDE.md');
|
|
159
303
|
if (installCursor) hints.push('.cursor/CURSOR.md');
|
|
160
|
-
|
|
304
|
+
if (installKiro) hints.push('.kiro/KIRO.md');
|
|
305
|
+
console.log(`\nDone. Next steps: read ${hints.join(', ')}.`);
|
|
306
|
+
if (installCursor) {
|
|
307
|
+
console.log(' Cursor: reload the window so CodeGraph MCP loads (.cursor/mcp.json).');
|
|
308
|
+
}
|
|
309
|
+
if (installKiro) {
|
|
310
|
+
console.log(' Kiro: restart IDE/CLI so CodeGraph MCP loads (.kiro/settings/mcp.json).');
|
|
311
|
+
}
|
|
312
|
+
if (shouldInstallAgentDir(opts)) {
|
|
313
|
+
console.log(' Continuity: edit .agent/SESSION.md at session end (/handoff); read at start (/resume).');
|
|
314
|
+
}
|
|
161
315
|
}
|
|
162
316
|
|
|
163
317
|
const argv = process.argv.slice(2);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "class-ai-agent",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Production-grade AI agent configuration for Claude Code &
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Production-grade AI agent configuration for Claude Code, Cursor & Kiro",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"claude",
|
|
16
16
|
"claude-code",
|
|
17
17
|
"cursor",
|
|
18
|
+
"kiro",
|
|
19
|
+
"codegraph",
|
|
18
20
|
"ai",
|
|
19
21
|
"agent",
|
|
20
22
|
"scaffold"
|
|
@@ -27,13 +29,17 @@
|
|
|
27
29
|
},
|
|
28
30
|
"files": [
|
|
29
31
|
"bin",
|
|
32
|
+
".agent",
|
|
30
33
|
".claude",
|
|
31
34
|
".cursor",
|
|
35
|
+
".kiro",
|
|
32
36
|
"AGENTS.md",
|
|
33
37
|
"royal-solution-logo-v2.svg"
|
|
34
38
|
],
|
|
35
39
|
"scripts": {
|
|
36
|
-
"prepack": "node -e \"const fs=require('fs');['.claude','.cursor','AGENTS.md'].forEach(p=>{if(!fs.existsSync(p))process.exit(1)})\"",
|
|
37
|
-
"
|
|
40
|
+
"prepack": "node -e \"const fs=require('fs');['.agent','.claude','.cursor','.kiro','AGENTS.md'].forEach(p=>{if(!fs.existsSync(p))process.exit(1)})\"",
|
|
41
|
+
"sync:kiro": "node scripts/sync-kiro-from-cursor.mjs",
|
|
42
|
+
"release:readme": "node scripts/update-readme-release.mjs",
|
|
43
|
+
"test:cli": "node bin/class-ai-agent.cjs --help && node bin/class-ai-agent.cjs --version && node -e \"const fs=require('fs');const c=JSON.parse(fs.readFileSync('.cursor/mcp.json','utf8'));const k=JSON.parse(fs.readFileSync('.kiro/settings/mcp.json','utf8'));if(!c.mcpServers?.codegraph||!k.mcpServers?.codegraph)throw new Error('missing codegraph MCP');if(!fs.existsSync('.cursor/rules/codegraph.mdc'))throw new Error('missing codegraph.mdc');if(!fs.existsSync('.kiro/steering/codegraph.md'))throw new Error('missing kiro codegraph steering');if(!fs.existsSync('.agent/SESSION.template.md'))throw new Error('missing SESSION.template');if(!fs.existsSync('.cursor/rules/agent-continuity.mdc'))throw new Error('missing agent-continuity.mdc');if(!fs.existsSync('.kiro/steering/agent-continuity.md'))throw new Error('missing kiro agent-continuity steering');if(!fs.existsSync('.cursor/commands/handoff.md')||!fs.existsSync('.cursor/commands/resume.md'))throw new Error('missing handoff/resume commands');\""
|
|
38
44
|
}
|
|
39
45
|
}
|