Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/docs/src/content/docs/en/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Inspired by [ultracite](https://github.com/haydenbleasel/ultracite) and NaverPay

## Quick Start

Run from the root of any project that has a `package.json`:
Run from the root of your project. A `package.json` is optional: without one (Gradle, pubspec or Cargo-only projects) `init` offers only the AGENTS.md block and its guides, and `update` and `doctor` work as usual:

<CodeGroup titles={["bun", "pnpm", "npm"]}>

Expand Down Expand Up @@ -48,7 +48,7 @@ pleaseai-code-style init --yes # non-interactive, accept defaults

### `update`

Refresh **only** the `AGENTS.md` rules block without touching packages or other config files. Use this after bumping `@pleaseai/code-style` to pull in the latest rules.
Refresh the `AGENTS.md` rules block and the guides in `.agents/code-style/` without touching packages or other config files. Use this after bumping `@pleaseai/code-style` to pull in the latest rules.

```bash
pleaseai-code-style update
Expand Down Expand Up @@ -122,9 +122,9 @@ These rules are managed by `@pleaseai/code-style`. Run
My own content stays here untouched.
```

The CLI **only** owns the content between the two marker comments — everything else in `AGENTS.md` is preserved verbatim. Running `update` twice in a row is a no-op; the block never duplicates.
In `AGENTS.md`, the CLI **only** owns the content between the two marker comments — everything else is preserved verbatim. Running `update` twice in a row is a no-op; the block never duplicates.

The full canonical rules are shipped per language for AI assistants that prefer to read the long version: start with `node_modules/@pleaseai/code-style/rules/general.md` (every language), then the language guide listed in `node_modules/@pleaseai/code-style/rules.md` (for example, `node_modules/@pleaseai/code-style/rules/typescript.md` for TypeScript and JavaScript).
The full canonical rules are copied into your project per language for AI assistants that prefer to read the long version, so they resolve without `node_modules`: start with `.agents/code-style/general.md` (every language), then the guide of your language (for example, `.agents/code-style/typescript.md` for TypeScript and JavaScript). `NOTICE.md` and `LICENSE-Apache-2.0.txt` in the same directory carry the attribution for the adapted guides. Commit `.agents/code-style/`: `init` and `update` rewrite it, overwrite its files and remove the guide of a language that is no longer selected (other files there are never touched). `doctor` checks that every guide the block links exists.

## Options

Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/content/docs/ko/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ sidebar:

## 빠른 시작

`package.json`이 있는 프로젝트 루트에서 실행하세요:
프로젝트 루트에서 실행하세요. `package.json`은 필수가 아닙니다. 없는 프로젝트(Gradle, pubspec, Cargo 전용)에서는 `init`이 AGENTS.md 블록과 가이드만 제공하며, `update`와 `doctor`는 그대로 동작합니다:

<CodeGroup titles={["bun", "pnpm", "npm"]}>

Expand Down Expand Up @@ -48,7 +48,7 @@ pleaseai-code-style init --yes # 비대화형, 기본값 수락

### `update`

패키지나 다른 설정 파일을 건드리지 않고 `AGENTS.md` 규칙 블록 **만** 새로 고칩니다. `@pleaseai/code-style`을 업그레이드한 뒤 최신 규칙을 적용할 때 사용하세요.
패키지나 다른 설정 파일을 건드리지 않고 `AGENTS.md` 규칙 블록과 `.agents/code-style/`의 가이드를 새로 고칩니다. `@pleaseai/code-style`을 업그레이드한 뒤 최신 규칙을 적용할 때 사용하세요.

```bash
pleaseai-code-style update
Expand Down Expand Up @@ -122,9 +122,9 @@ CLI는 `AGENTS.md`에 마커로 구분된 블록을 작성하므로, 주변에
내 콘텐츠는 그대로 유지됩니다.
```

CLI는 두 마커 주석 사이의 내용 **만** 관리하며 — `AGENTS.md`의 나머지 내용은 그대로 보존됩니다. `update`를 연속으로 두 번 실행해도 no-op이며, 블록이 중복되지 않습니다.
`AGENTS.md`에서 CLI는 두 마커 주석 사이의 내용 **만** 관리하며 — 나머지 내용은 그대로 보존됩니다. `update`를 연속으로 두 번 실행해도 no-op이며, 블록이 중복되지 않습니다.

전체 원본 규칙은 AI 어시스턴트가 긴 버전을 읽기 원할 때 참고할 수 있도록 언어별로 나뉘어 함께 배포됩니다. `node_modules/@pleaseai/code-style/rules/general.md`(모든 언어)를 먼저 읽고, `node_modules/@pleaseai/code-style/rules.md`에 나열된 언어별 가이드를 읽으세요(예: TypeScript·JavaScript는 `node_modules/@pleaseai/code-style/rules/typescript.md`).
전체 원본 규칙은 AI 어시스턴트가 긴 버전을 읽기 원할 때 참고할 수 있도록 언어별로 나뉘어 프로젝트에 복사되므로 `node_modules` 없이도 읽을 수 있습니다. `.agents/code-style/general.md`(모든 언어)를 먼저 읽고, 사용하는 언어의 가이드를 읽으세요(예: TypeScript·JavaScript는 `.agents/code-style/typescript.md`). 같은 디렉터리의 `NOTICE.md`와 `LICENSE-Apache-2.0.txt`에 각색한 가이드의 출처 표기가 있습니다. `.agents/code-style/`은 커밋하세요. `init`과 `update`가 이 디렉터리를 다시 쓰며 파일을 덮어쓰고, 더 이상 선택하지 않은 언어의 가이드는 삭제합니다(디렉터리의 다른 파일은 건드리지 않습니다). `doctor`는 블록이 가리키는 가이드가 모두 있는지 확인합니다.
Comment thread
amondnet marked this conversation as resolved.

## 옵션

Expand Down
19 changes: 12 additions & 7 deletions packages/cli/README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ eslint/prettier/editorconfig/ast-grep 패키지를 설치하고, AI 코딩 어

## 사용법

`package.json`이 있는 프로젝트 루트에서 실행하세요:
프로젝트 루트에서 실행하세요. `package.json`은 필수가 아닙니다. 없는 프로젝트(Gradle,
pubspec, Cargo 전용)에서는 `init`이 AGENTS.md 블록과 가이드만 제공합니다:

```bash
bunx @pleaseai/code-style # `init`과 동일
bunx @pleaseai/code-style init # 대화형 설정
bunx @pleaseai/code-style update # AGENTS.md 규칙 블록만 다시 적용
bunx @pleaseai/code-style update # AGENTS.md 규칙 블록과 가이드 다시 적용
bunx @pleaseai/code-style doctor # 현재 프로젝트 상태 확인
bunx @pleaseai/code-style check # 파일 이름과 테스트 경로 검사 (ADR-0022)
```
Expand Down Expand Up @@ -200,7 +201,7 @@ bunx @pleaseai/code-style check --strict # 발견 사항이 하나라도 있

## `AGENTS.md` 블록

CLI는 아래 마커 사이의 내용만 관리합니다 — `AGENTS.md`의 나머지 내용은
`AGENTS.md`에서 CLI는 아래 마커 사이의 내용만 관리합니다 — 나머지 내용은
사용자의 것이며 그대로 보존됩니다:

```md
Expand All @@ -210,10 +211,14 @@ CLI는 아래 마커 사이의 내용만 관리합니다 — `AGENTS.md`의 나
```

`@pleaseai/code-style`을 업그레이드한 뒤에는 `pleaseai-code-style update`를
다시 실행하면 이 블록만 새로고침할 수 있습니다. 전체 규칙은 참고용으로
언어별로 나뉘어 함께 배포됩니다. `node_modules/@pleaseai/code-style/rules/general.md`(모든 언어)를
먼저 읽고, `node_modules/@pleaseai/code-style/rules.md`에 나열된 언어별 가이드를 읽으세요
(예: TypeScript·JavaScript는 `node_modules/@pleaseai/code-style/rules/typescript.md`).
다시 실행하면 이 블록과 블록이 가리키는 가이드를 새로고침할 수 있습니다. 전체 규칙은 언어별로 나뉘어 프로젝트에
복사되므로 `node_modules` 없이도 어떤 프로젝트에서나 읽을 수 있습니다.
Comment thread
amondnet marked this conversation as resolved.
`.agents/code-style/general.md`(모든 언어)를 먼저 읽고, 사용하는 언어의 가이드를 읽으세요
(예: TypeScript·JavaScript는 `.agents/code-style/typescript.md`). 각색한 가이드의 출처 표기는
`NOTICE.md`와 `LICENSE-Apache-2.0.txt`에 있습니다. `.agents/code-style/`은 커밋하세요.
`init`과 `update`가 이 디렉터리를 다시 쓰며 파일을 덮어쓰고, 더 이상 선택하지 않은 언어의
가이드는 삭제합니다(디렉터리의 다른 파일은 건드리지 않습니다). `doctor`는 블록이 가리키는
가이드가 모두 있는지 확인합니다.

## 옵션

Expand Down
24 changes: 14 additions & 10 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ NaverPay's [`@naverpay/code-style-cli`](https://github.com/NaverPayDev/code-styl

## Usage

From the root of any project that has a `package.json`:
From the root of your project. A `package.json` is optional: without one (Gradle,
pubspec or Cargo-only projects) `init` offers only the AGENTS.md block and its guides:

```bash
bunx @pleaseai/code-style # same as `init`
bunx @pleaseai/code-style init # interactive setup
bunx @pleaseai/code-style update # re-apply the AGENTS.md rules block only
bunx @pleaseai/code-style update # re-apply the AGENTS.md rules block and its guides
bunx @pleaseai/code-style doctor # check current project status
bunx @pleaseai/code-style check # check file names and test paths (ADR-0022)
```
Expand Down Expand Up @@ -382,8 +383,8 @@ does not catch lost tests.

## `AGENTS.md` block

The CLI owns only the content between these markers — everything else in
`AGENTS.md` is your content and is preserved verbatim:
In `AGENTS.md`, the CLI owns only the content between these markers — everything
else is your content and is preserved verbatim:

```md
<!-- pleaseai-code-style:start -->
Expand All @@ -392,12 +393,15 @@ The CLI owns only the content between these markers — everything else in
```

Re-run `pleaseai-code-style update` any time you upgrade `@pleaseai/code-style`
to refresh just this block. The full rules are shipped per language for
reference: start with `node_modules/@pleaseai/code-style/rules/general.md`
(every language), then the language guide listed in
`node_modules/@pleaseai/code-style/rules.md` (for example
`node_modules/@pleaseai/code-style/rules/typescript.md` for TypeScript and
JavaScript).
to refresh this block and the guides it links. The full rules are copied into your project, per
language, so they resolve in every project (no `node_modules` needed): start
with `.agents/code-style/general.md` (every language), then the guide of your
language (for example `.agents/code-style/typescript.md` for TypeScript and
JavaScript), with `NOTICE.md` and `LICENSE-Apache-2.0.txt` for the attribution
of the adapted guides. Commit `.agents/code-style/`; `init` and `update`
rewrite it, overwrite the files and remove the guide of a language that is no
longer selected (other files in the directory are never touched). `doctor`
checks that every guide the block links exists.

## Options

Expand Down
5 changes: 4 additions & 1 deletion packages/cli/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ apply to every language, then the guide for the language you are writing:
- [`rules/html-css.md`](./rules/html-css.md) — HTML and CSS

`pleaseai-code-style init` and `update` point your `AGENTS.md` block at the
general guide plus the guide of each selected language. Languages are detected
general guide plus the guide of each selected language, and copy those guides
(with `NOTICE.md` and `LICENSE-Apache-2.0.txt`) into `.agents/code-style/` in
your project, so the links work without `node_modules`. Commit that directory;
`update` rewrites it. Languages are detected
from marker files in the project root, or set with `--lang <list>`; `update`
keeps the languages the existing block links unless `--lang` is given.
9 changes: 7 additions & 2 deletions packages/cli/src/appliers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {
upsertMarkerBlock,
writePackageJson,
} from './fs-utils.js'
import { guidePath, syncGuides } from './guides.js'
import { t } from './i18n.js'
import { detectLanguages, languagesFromAgentsBlock, RULES_PREFIX } from './languages.js'
import { detectLanguages, languagesFromAgentsBlock } from './languages.js'

async function confirmOverwrite(file: string, auto: boolean): Promise<boolean> {
if (auto) {
Expand Down Expand Up @@ -182,7 +183,7 @@ function wantsCodeOrganization(ctx: ToolContext): boolean {
}

function agentsBody(ctx: ToolContext, languages: Language[]): string {
const guides = ['general', ...languages].map(id => `\`${RULES_PREFIX}/${id}.md\``)
const guides = ['general', ...languages].map(id => `\`${guidePath(id)}\``)
const pointer = guides.length > 1
? `${guides.slice(0, -1).join(', ')} and ${guides.at(-1)}`
: guides[0]
Expand All @@ -200,6 +201,10 @@ export async function applyAgentsMd(ctx: ToolContext): Promise<ToolApplyResult>
const languages = ctx.languages ?? languagesFromAgentsBlock(existing) ?? detectLanguages(ctx.cwd)
const next = upsertMarkerBlock(existing, agentsBody(ctx, languages))

const guides = syncGuides(ctx.cwd, languages)
result.created.push(...guides.created)
result.updated.push(...guides.updated)

if (existing == null) {
writeFileSync(target, next)
result.created.push(label)
Expand Down
81 changes: 44 additions & 37 deletions packages/cli/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,21 @@
detectPackageManager,
installCommand,
isPackageInstalled,
readFileOrNull,
readPackageJson,
} from './fs-utils.js'
import { assertGuidesWritable } from './guides.js'
import { t } from './i18n.js'
import { detectLanguages, LANGUAGES } from './languages.js'
import { detectLanguages, guidePathsFromAgentsBlock, LANGUAGES } from './languages.js'

export interface CommandOptions {
cwd: string
autoAccept: boolean
languages?: Language[]
}

function assertProjectRoot(cwd: string): void {
if (!existsSync(resolve(cwd, 'package.json'))) {
log.error(t('noPackageJson'))
process.exit(1)
}
function hasPackageJson(cwd: string): boolean {
return existsSync(resolve(cwd, 'package.json'))

Check warning on line 27 in packages/cli/src/commands.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/commands.ts#L27

Found existsSync from package "node:fs" with non literal argument at index 0
}

function mergeResults(results: ToolApplyResult[]): ToolApplyResult {
Expand Down Expand Up @@ -53,27 +52,30 @@
// --- init -------------------------------------------------------------------

export async function runInit(opts: CommandOptions): Promise<void> {
assertProjectRoot(opts.cwd)
intro(t('appTitle'))

const pm = detectPackageManager(opts.cwd)
log.info(t('detectedPm')(pm))

const pkg = readPackageJson(opts.cwd)
const withPackages = hasPackageJson(opts.cwd)
const pm = withPackages ? detectPackageManager(opts.cwd) : undefined
const pkg = withPackages ? readPackageJson(opts.cwd) : undefined
if (pm != null) {
log.info(t('detectedPm')(pm))
}

const options = TOOLS.map((tool) => {
const installed = tool.packages.length > 0
const available = withPackages ? TOOLS : TOOLS.filter(tool => tool.packages.length === 0)
const options = available.map((tool) => {
const installed = pkg != null
&& tool.packages.length > 0

Check warning on line 67 in packages/cli/src/commands.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The non-empty check is useless as `Array#some()` returns `false` for an empty array.

See more on https://sonarcloud.io/project/issues?id=pleaseai_code-style&issues=AaFo5FiMuObywQ7Cat6g&open=AaFo5FiMuObywQ7Cat6g&pullRequest=70
&& tool.packages.some(p => isPackageInstalled(pkg, p))
return {
value: tool.id,
label: installed ? `${tool.label} ${t('alreadyInstalled')}` : tool.label,
}
})

const initialValues = opts.autoAccept ? TOOLS.map(tool => tool.id) : undefined
const initialValues = opts.autoAccept ? available.map(tool => tool.id) : undefined

const selectedIdsRaw = opts.autoAccept
? TOOLS.map(tool => tool.id)
? available.map(tool => tool.id)
: await multiselect({
message: t('selectTools'),
options,
Expand All @@ -94,7 +96,7 @@
}

const selectedTools: Tool[] = selectedIds
.map(id => TOOLS.find(tool => tool.id === id))
.map(id => available.find(tool => tool.id === id))
.filter((tool): tool is Tool => tool != null)

let languages = opts.languages
Expand All @@ -117,13 +119,17 @@
languages = chosen
}
}
if (selectedTools.some(tool => tool.id === 'agents-md')) {
// Fail before installing anything rather than leave a half-configured project.
assertGuidesWritable(opts.cwd, languages ?? [])
}

// 1. Install packages (de-duped) for selected tools.
const packagesToInstall = Array.from(
new Set(selectedTools.flatMap(tool => tool.packages)),
)

if (packagesToInstall.length > 0) {
if (pm != null && packagesToInstall.length > 0) {
const [cmd, args] = installCommand(pm, packagesToInstall)
const spin = spinner()
spin.start(t('installing'))
Expand Down Expand Up @@ -152,7 +158,6 @@
// --- update -----------------------------------------------------------------

export async function runUpdate(opts: CommandOptions): Promise<void> {
assertProjectRoot(opts.cwd)
intro(t('appTitle'))
const result = await applyAgentsMd({ cwd: opts.cwd, autoAccept: true, selectedToolIds: [], languages: opts.languages })
reportResults(result)
Expand All @@ -162,22 +167,22 @@

// --- doctor -----------------------------------------------------------------

export function runDoctor(opts: CommandOptions): void {

Check failure on line 170 in packages/cli/src/commands.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=pleaseai_code-style&issues=AaFo5FiMuObywQ7Cat6h&open=AaFo5FiMuObywQ7Cat6h&pullRequest=70
assertProjectRoot(opts.cwd)
const pkg = readPackageJson(opts.cwd)
const pm = detectPackageManager(opts.cwd)

log.info(`${t('doctorHeading')}:`)
log.info(` ${t('detectedPm')(pm)}`)

// Package-based tools only — file-only tools (agents-md) are reported below.
for (const tool of TOOLS) {
if (tool.packages.length === 0) {
continue
const packageBased = hasPackageJson(opts.cwd)
if (packageBased) {
const pkg = readPackageJson(opts.cwd)
log.info(` ${t('detectedPm')(detectPackageManager(opts.cwd))}`)

for (const tool of TOOLS) {
if (tool.packages.length === 0) {
continue
}
const installed = tool.packages.every(p => isPackageInstalled(pkg, p))
const suffix = installed ? t('doctorOk') : t('doctorMissing')
log.info(` ${tool.label} — ${suffix}`)
}
const installed = tool.packages.every(p => isPackageInstalled(pkg, p))
const suffix = installed ? t('doctorOk') : t('doctorMissing')
log.info(` ${tool.label} — ${suffix}`)
}

if (opts.languages != null) {
Expand All @@ -188,15 +193,17 @@
log.info(` ${languages.length > 0 ? t('doctorLanguages')(languages.join(', ')) : t('doctorNoLanguages')}`)
}

// File presence checks.
const files = [
'eslint.config.mjs',
'.editorconfig',
'AGENTS.md',
'sgconfig.yml',
]
// File presence checks. Without a package.json, init offers only the AGENTS.md block.
const files = packageBased
? ['eslint.config.mjs', '.editorconfig', 'AGENTS.md', 'sgconfig.yml']
: ['AGENTS.md']
for (const f of files) {
const exists = existsSync(resolve(opts.cwd, f))
log.info(` ${f} — ${exists ? t('doctorOk') : t('doctorMissing')}`)
}

for (const guide of guidePathsFromAgentsBlock(readFileOrNull(resolve(opts.cwd, 'AGENTS.md'))) ?? []) {
const exists = existsSync(resolve(opts.cwd, guide))

Check warning on line 206 in packages/cli/src/commands.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/commands.ts#L206

Found existsSync from package "node:fs" with non literal argument at index 0
log.info(` ${guide} — ${exists ? t('doctorOk') : t('doctorMissing')}`)
}
}
Loading
Loading