@agentsmd/cli 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +56 -2
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +62 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +98 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/format.d.ts +9 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +17 -0
- package/dist/format.js.map +1 -0
- package/dist/git-hooks.d.ts +9 -0
- package/dist/git-hooks.d.ts.map +1 -0
- package/dist/git-hooks.js +26 -0
- package/dist/git-hooks.js.map +1 -0
- package/dist/hook-permissions.d.ts +10 -0
- package/dist/hook-permissions.d.ts.map +1 -0
- package/dist/hook-permissions.js +31 -0
- package/dist/hook-permissions.js.map +1 -0
- package/dist/io.d.ts +16 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +2 -0
- package/dist/io.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +9 -0
- package/dist/main.js.map +1 -0
- package/dist/presets.d.ts +9 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +12 -0
- package/dist/presets.js.map +1 -0
- package/dist/run.d.ts +5 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +47 -0
- package/dist/run.js.map +1 -0
- package/package.json +41 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 이태경
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @agentsmd/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AI 코딩 에이전트용 작업 지침(`AGENTS.md`)을 설치하고, 지침이 저장소와 어긋나지 않았는지 검사하는 CLI입니다.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# 지침 파일을 설치하고 Git 훅을 연결한다 (공통 Git 규칙이 함께 설치된다)
|
|
7
|
+
npx @agentsmd/cli init --preset typescript # 또는 --preset java
|
|
8
|
+
|
|
9
|
+
# 문서 지도가 저장소와 맞는지 검사한다 (문제가 있으면 종료 코드 1)
|
|
10
|
+
npx @agentsmd/cli check
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
전역으로 설치하면 `agentsmd` 명령으로 씁니다.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @agentsmd/cli
|
|
17
|
+
agentsmd check
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 왜 필요한가
|
|
21
|
+
|
|
22
|
+
`AGENTS.md`를 "어떤 문서를 언제 읽을지" 안내하는 문서 지도로 쓰면, 문서가 바뀔 때 쉽게 깨집니다.
|
|
23
|
+
|
|
24
|
+
- 옮기거나 지운 문서를 지도가 계속 가리키면 에이전트는 **추측으로 작업**합니다.
|
|
25
|
+
- 새 문서를 지도에 올리지 않으면 에이전트는 그 문서를 읽지 않아 **규칙이 없는 것과 같습니다**.
|
|
26
|
+
- `CLAUDE.md`가 `AGENTS.md`를 불러오지 않으면 **Claude Code만 지침을 읽지 못합니다**.
|
|
27
|
+
|
|
28
|
+
`agentsmd check`는 이 세 가지를 `파일:줄` 형식으로 알려 주고, CI에 넣으면 머지 전에 막습니다.
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
$ agentsmd check
|
|
32
|
+
AGENTS.md:12 [broken-link] 링크 대상이 없습니다: docs/api.md
|
|
33
|
+
CLAUDE.md [claude-import] @AGENTS.md 로 문서 지도를 불러오지 않습니다
|
|
34
|
+
docs/requirements.md [unlisted-doc] 문서 지도(AGENTS.md)에서 안내하지 않는 문서입니다
|
|
35
|
+
|
|
36
|
+
문제 3건
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 프리셋
|
|
40
|
+
|
|
41
|
+
| 프리셋 | 내용 |
|
|
42
|
+
| ------------ | --------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `typescript` | TypeScript·React 프론트엔드: 필수 tsconfig 옵션, 타입·구조·접근성·테스트·보안 규칙, Prettier 커밋 검사, pnpm CI |
|
|
44
|
+
| `java` | Java·Gradle: 코드 규칙, palantir-java-format(Spotless) 커밋 검사, Gradle CI |
|
|
45
|
+
|
|
46
|
+
어느 프리셋이든 공통 Git 규칙(이슈 → 브랜치 → PR → merge 커밋, 커밋 형식·파일 수 훅, PR 규칙 검사, 이슈·PR 템플릿)이 함께 설치됩니다.
|
|
47
|
+
이미 있는 파일은 덮어쓰지 않습니다.
|
|
48
|
+
|
|
49
|
+
## 종료 코드
|
|
50
|
+
|
|
51
|
+
| 코드 | 의미 |
|
|
52
|
+
| ---- | ---------------------------------- |
|
|
53
|
+
| `0` | 문제 없음 |
|
|
54
|
+
| `1` | 규칙 위반이 있음 |
|
|
55
|
+
| `2` | 옵션이 잘못됐거나 문서 지도가 없음 |
|
|
56
|
+
|
|
57
|
+
명령 옵션, 규칙 설명, 구조는 [저장소](https://github.com/xoruddl/agentsmd-kit)를 참고하세요. Node.js 22 이상이 필요합니다.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ExitCode, Io } from "../io.js";
|
|
2
|
+
export declare const CHECK_USAGE = "\uC0AC\uC6A9\uBC95: agentsmd check [\uC635\uC158]\n\nAGENTS.md \uBB38\uC11C \uC9C0\uB3C4\uAC00 \uC800\uC7A5\uC18C\uC640 \uB9DE\uB294\uC9C0 \uAC80\uC0AC\uD569\uB2C8\uB2E4.\n\n\uAC80\uC0AC \uADDC\uCE59:\n broken-link \uC9C0\uB3C4\uC758 \uB9C1\uD06C\uAC00 \uC5C6\uB294 \uD30C\uC77C\u00B7\uB514\uB809\uD130\uB9AC\uB97C \uAC00\uB9AC\uD0B4\n unlisted-doc \uBB38\uC11C \uB514\uB809\uD130\uB9AC\uC5D0 \uC788\uC9C0\uB9CC \uC9C0\uB3C4\uC5D0\uC11C \uC548\uB0B4\uD558\uC9C0 \uC54A\uB294 \uBB38\uC11C\n claude-import CLAUDE.md\uAC00 @AGENTS.md\uB85C \uC9C0\uB3C4\uB97C \uBD88\uB7EC\uC624\uC9C0 \uC54A\uC74C\n\n\uC635\uC158:\n --root <\uACBD\uB85C> \uAC80\uC0AC\uD560 \uC800\uC7A5\uC18C \uB8E8\uD2B8 (\uAE30\uBCF8\uAC12: \uD604\uC7AC \uB514\uB809\uD130\uB9AC)\n --agents <\uACBD\uB85C> \uBB38\uC11C \uC9C0\uB3C4 \uD30C\uC77C, \uB8E8\uD2B8 \uAE30\uC900 (\uAE30\uBCF8\uAC12: AGENTS.md)\n --docs <\uACBD\uB85C> \uC9C0\uB3C4\uC5D0 \uBAA8\uB450 \uC788\uC5B4\uC57C \uD558\uB294 \uBB38\uC11C \uB514\uB809\uD130\uB9AC, \uB8E8\uD2B8 \uAE30\uC900 (\uAE30\uBCF8\uAC12: docs)\n --claude <\uACBD\uB85C> Claude Code \uC9C0\uCE68 \uD30C\uC77C, \uB8E8\uD2B8 \uAE30\uC900 (\uAE30\uBCF8\uAC12: CLAUDE.md)\n -h, --help \uC774 \uB3C4\uC6C0\uB9D0\n\n\uC885\uB8CC \uCF54\uB4DC:\n 0 \uBB38\uC81C \uC5C6\uC74C\n 1 \uADDC\uCE59 \uC704\uBC18\uC774 \uC788\uC74C\n 2 \uC635\uC158\uC774 \uC798\uBABB\uB410\uAC70\uB098 \uBB38\uC11C \uC9C0\uB3C4\uAC00 \uC5C6\uC74C\n";
|
|
3
|
+
/** `agentsmd check` 명령. 문제가 있으면 1을 돌려줘 CI가 실패하게 한다. */
|
|
4
|
+
export declare function runCheck(args: readonly string[], io: Io): Promise<ExitCode>;
|
|
5
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,WAAW,47CAoBvB,CAAC;AAEF,uDAAuD;AACvD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmCjF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { checkRepository, DocMapNotFoundError } from "@agentsmd/core";
|
|
4
|
+
import { formatFindings } from "../format.js";
|
|
5
|
+
export const CHECK_USAGE = `사용법: agentsmd check [옵션]
|
|
6
|
+
|
|
7
|
+
AGENTS.md 문서 지도가 저장소와 맞는지 검사합니다.
|
|
8
|
+
|
|
9
|
+
검사 규칙:
|
|
10
|
+
broken-link 지도의 링크가 없는 파일·디렉터리를 가리킴
|
|
11
|
+
unlisted-doc 문서 디렉터리에 있지만 지도에서 안내하지 않는 문서
|
|
12
|
+
claude-import CLAUDE.md가 @AGENTS.md로 지도를 불러오지 않음
|
|
13
|
+
|
|
14
|
+
옵션:
|
|
15
|
+
--root <경로> 검사할 저장소 루트 (기본값: 현재 디렉터리)
|
|
16
|
+
--agents <경로> 문서 지도 파일, 루트 기준 (기본값: AGENTS.md)
|
|
17
|
+
--docs <경로> 지도에 모두 있어야 하는 문서 디렉터리, 루트 기준 (기본값: docs)
|
|
18
|
+
--claude <경로> Claude Code 지침 파일, 루트 기준 (기본값: CLAUDE.md)
|
|
19
|
+
-h, --help 이 도움말
|
|
20
|
+
|
|
21
|
+
종료 코드:
|
|
22
|
+
0 문제 없음
|
|
23
|
+
1 규칙 위반이 있음
|
|
24
|
+
2 옵션이 잘못됐거나 문서 지도가 없음
|
|
25
|
+
`;
|
|
26
|
+
/** `agentsmd check` 명령. 문제가 있으면 1을 돌려줘 CI가 실패하게 한다. */
|
|
27
|
+
export async function runCheck(args, io) {
|
|
28
|
+
const { values } = parseArgs({
|
|
29
|
+
args: [...args],
|
|
30
|
+
strict: true,
|
|
31
|
+
allowPositionals: false,
|
|
32
|
+
options: {
|
|
33
|
+
root: { type: "string" },
|
|
34
|
+
agents: { type: "string" },
|
|
35
|
+
docs: { type: "string" },
|
|
36
|
+
claude: { type: "string" },
|
|
37
|
+
help: { type: "boolean", short: "h" },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
if (values.help === true) {
|
|
41
|
+
io.stdout(CHECK_USAGE);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const findings = await checkRepository({
|
|
46
|
+
root: resolve(io.cwd, values.root ?? "."),
|
|
47
|
+
...(values.agents === undefined ? {} : { agentsFile: values.agents }),
|
|
48
|
+
...(values.docs === undefined ? {} : { docsDir: values.docs }),
|
|
49
|
+
...(values.claude === undefined ? {} : { claudeFile: values.claude }),
|
|
50
|
+
});
|
|
51
|
+
io.stdout(formatFindings(findings));
|
|
52
|
+
return findings.length === 0 ? 0 : 1;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof DocMapNotFoundError) {
|
|
56
|
+
io.stderr(`${error.message}\n--root 나 --agents 옵션으로 위치를 지정할 수 있습니다.\n`);
|
|
57
|
+
return 2;
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;CAoB1B,CAAC;AAEF,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAuB,EAAE,EAAM;IAC5D,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,MAAM,EAAE,IAAI;QACZ,gBAAgB,EAAE,KAAK;QACvB,OAAO,EAAE;YACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACvB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC;YACrC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;YACzC,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;YACrE,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9D,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;SACtE,CAAC,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpC,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;YACzC,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,4CAA4C,CAAC,CAAC;YACxE,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ExitCode, Io } from "../io.js";
|
|
2
|
+
export declare const INIT_USAGE: string;
|
|
3
|
+
/** `agentsmd init` 명령. 파일 설치 → 훅 연결 → 남은 수작업 안내 순서로 진행한다. */
|
|
4
|
+
export declare function runInit(args: readonly string[], io: Io): Promise<ExitCode>;
|
|
5
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAO7C,eAAO,MAAM,UAAU,QActB,CAAC;AAEF,6DAA6D;AAC7D,wBAAsB,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6ChF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { installPreset, resolvePresets } from "@agentsmd/core";
|
|
4
|
+
import { configureHooksPath } from "../git-hooks.js";
|
|
5
|
+
import { makeHooksExecutable } from "../hook-permissions.js";
|
|
6
|
+
import { findPreset, PRESETS } from "../presets.js";
|
|
7
|
+
const presetList = PRESETS.map((preset) => ` ${preset.name.padEnd(8)}${preset.description}`).join("\n");
|
|
8
|
+
export const INIT_USAGE = `사용법: agentsmd init --preset <이름> [옵션]
|
|
9
|
+
|
|
10
|
+
AI 작업 지침(AGENTS.md, docs/, Git 훅, CI)을 저장소에 설치하고 훅을 연결합니다.
|
|
11
|
+
이미 있는 파일은 덮어쓰지 않고 건너뜁니다.
|
|
12
|
+
|
|
13
|
+
프리셋:
|
|
14
|
+
${presetList}
|
|
15
|
+
|
|
16
|
+
옵션:
|
|
17
|
+
--preset <이름> 설치할 프리셋 (필수)
|
|
18
|
+
--root <경로> 설치할 저장소 루트 (기본값: 현재 디렉터리)
|
|
19
|
+
--force 이미 있는 파일도 덮어씁니다
|
|
20
|
+
--skip-hooks core.hooksPath 를 설정하지 않습니다
|
|
21
|
+
-h, --help 이 도움말
|
|
22
|
+
`;
|
|
23
|
+
/** `agentsmd init` 명령. 파일 설치 → 훅 연결 → 남은 수작업 안내 순서로 진행한다. */
|
|
24
|
+
export async function runInit(args, io) {
|
|
25
|
+
const { values } = parseArgs({
|
|
26
|
+
args: [...args],
|
|
27
|
+
strict: true,
|
|
28
|
+
allowPositionals: false,
|
|
29
|
+
options: {
|
|
30
|
+
preset: { type: "string" },
|
|
31
|
+
root: { type: "string" },
|
|
32
|
+
force: { type: "boolean" },
|
|
33
|
+
"skip-hooks": { type: "boolean" },
|
|
34
|
+
help: { type: "boolean", short: "h" },
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
if (values.help === true) {
|
|
38
|
+
io.stdout(INIT_USAGE);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const preset = values.preset === undefined ? undefined : findPreset(values.preset);
|
|
42
|
+
if (preset === undefined) {
|
|
43
|
+
const reason = values.preset === undefined
|
|
44
|
+
? "--preset 을 지정해야 합니다"
|
|
45
|
+
: `알 수 없는 프리셋입니다: ${values.preset}`;
|
|
46
|
+
io.stderr(`${reason}\n\n프리셋:\n${presetList}\n`);
|
|
47
|
+
return 2;
|
|
48
|
+
}
|
|
49
|
+
const root = resolve(io.cwd, values.root ?? ".");
|
|
50
|
+
// 요구되는 프리셋(예: 공통 Git 프리셋)부터 설치한다.
|
|
51
|
+
const presets = resolvePresets(preset);
|
|
52
|
+
const result = await installAll(presets, root, values.force === true);
|
|
53
|
+
const hooksPath = presets.find((p) => p.hooksPath !== undefined)?.hooksPath;
|
|
54
|
+
if (hooksPath !== undefined) {
|
|
55
|
+
// npm 패키지에서 풀린 훅은 실행 권한이 없으므로, 훅 연결 여부와 상관없이 권한을 맞춘다.
|
|
56
|
+
await makeHooksExecutable(root, hooksPath);
|
|
57
|
+
}
|
|
58
|
+
const hooks = hooksPath === undefined || values["skip-hooks"] === true
|
|
59
|
+
? undefined
|
|
60
|
+
: await configureHooksPath(root, hooksPath);
|
|
61
|
+
io.stdout(formatReport(presets, result, hooksPath, hooks));
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
async function installAll(presets, root, force) {
|
|
65
|
+
const written = [];
|
|
66
|
+
const skipped = [];
|
|
67
|
+
for (const preset of presets) {
|
|
68
|
+
const result = await installPreset(preset, root, { force });
|
|
69
|
+
written.push(...result.written);
|
|
70
|
+
skipped.push(...result.skipped);
|
|
71
|
+
}
|
|
72
|
+
return { written, skipped };
|
|
73
|
+
}
|
|
74
|
+
/** 마지막 프리셋이 사용자가 고른 프리셋이고, 그 앞은 함께 설치된 프리셋이다. */
|
|
75
|
+
function formatReport(presets, result, hooksPath, hooks) {
|
|
76
|
+
const chosen = presets.at(-1)?.name ?? "";
|
|
77
|
+
const lines = [`${chosen} 프리셋: 파일 ${String(result.written.length)}개를 설치했습니다.`];
|
|
78
|
+
const included = presets.slice(0, -1).map((preset) => preset.name);
|
|
79
|
+
if (included.length > 0) {
|
|
80
|
+
lines.push(`함께 설치한 프리셋: ${included.join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
if (result.skipped.length > 0) {
|
|
83
|
+
lines.push(`이미 있어서 건너뛴 파일 ${String(result.skipped.length)}개 (덮어쓰려면 --force):`, ...result.skipped.map((file) => ` ${file}`));
|
|
84
|
+
}
|
|
85
|
+
if (hooks === "configured") {
|
|
86
|
+
lines.push(`Git 훅을 연결했습니다: core.hooksPath=${hooksPath ?? ""}`);
|
|
87
|
+
}
|
|
88
|
+
else if (hooks === "not-a-repo") {
|
|
89
|
+
lines.push("Git 저장소가 아니어서 훅을 연결하지 않았습니다. git init 후 다시 실행하세요.");
|
|
90
|
+
}
|
|
91
|
+
const steps = [
|
|
92
|
+
...presets.flatMap((preset) => preset.nextSteps ?? []),
|
|
93
|
+
"agentsmd check 로 문서 지도를 검사하세요.",
|
|
94
|
+
];
|
|
95
|
+
lines.push("", "다음 단계:", ...steps.map((step, index) => ` ${String(index + 1)}. ${step}`));
|
|
96
|
+
return `${lines.join("\n")}\n`;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Finding } from "@agentsmd/core";
|
|
2
|
+
/**
|
|
3
|
+
* 검사 결과를 사람이 읽는 텍스트로 만든다.
|
|
4
|
+
*
|
|
5
|
+
* 한 줄에 문제 하나를 `파일:줄 [규칙] 메시지` 형식으로 적는다.
|
|
6
|
+
* 편집기와 터미널이 `파일:줄`을 링크로 인식하므로 바로 그 위치로 이동할 수 있다.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatFindings(findings: readonly Finding[]): string;
|
|
9
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAQnE"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 검사 결과를 사람이 읽는 텍스트로 만든다.
|
|
3
|
+
*
|
|
4
|
+
* 한 줄에 문제 하나를 `파일:줄 [규칙] 메시지` 형식으로 적는다.
|
|
5
|
+
* 편집기와 터미널이 `파일:줄`을 링크로 인식하므로 바로 그 위치로 이동할 수 있다.
|
|
6
|
+
*/
|
|
7
|
+
export function formatFindings(findings) {
|
|
8
|
+
if (findings.length === 0) {
|
|
9
|
+
return "문제 없음\n";
|
|
10
|
+
}
|
|
11
|
+
const lines = findings.map((finding) => `${location(finding)} [${finding.ruleId}] ${finding.message}`);
|
|
12
|
+
return `${lines.join("\n")}\n\n문제 ${String(findings.length)}건\n`;
|
|
13
|
+
}
|
|
14
|
+
function location(finding) {
|
|
15
|
+
return finding.line === undefined ? finding.file : `${finding.file}:${String(finding.line)}`;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CACxB,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC,OAAO,EAAE,CAC7E,CAAC;IACF,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC;AACnE,CAAC;AAED,SAAS,QAAQ,CAAC,OAAgB;IAChC,OAAO,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type HooksResult = "configured" | "not-a-repo";
|
|
2
|
+
/**
|
|
3
|
+
* 저장소의 `core.hooksPath`를 설치한 훅 디렉터리로 설정한다.
|
|
4
|
+
*
|
|
5
|
+
* 훅 파일만 복사하면 Git이 실행하지 않으므로, 템플릿을 쓸 때 사람이 따로 하던 설정을 대신한다.
|
|
6
|
+
* Git 저장소가 아니면 설정하지 않고 알려 주기만 한다. 나중에 `git init` 한 뒤 다시 실행하면 된다.
|
|
7
|
+
*/
|
|
8
|
+
export declare function configureHooksPath(dir: string, hooksPath: string): Promise<HooksResult>;
|
|
9
|
+
//# sourceMappingURL=git-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-hooks.d.ts","sourceRoot":"","sources":["../src/git-hooks.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;AAEtD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAM7F"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const run = promisify(execFile);
|
|
4
|
+
/**
|
|
5
|
+
* 저장소의 `core.hooksPath`를 설치한 훅 디렉터리로 설정한다.
|
|
6
|
+
*
|
|
7
|
+
* 훅 파일만 복사하면 Git이 실행하지 않으므로, 템플릿을 쓸 때 사람이 따로 하던 설정을 대신한다.
|
|
8
|
+
* Git 저장소가 아니면 설정하지 않고 알려 주기만 한다. 나중에 `git init` 한 뒤 다시 실행하면 된다.
|
|
9
|
+
*/
|
|
10
|
+
export async function configureHooksPath(dir, hooksPath) {
|
|
11
|
+
if (!(await isGitRepository(dir))) {
|
|
12
|
+
return "not-a-repo";
|
|
13
|
+
}
|
|
14
|
+
await run("git", ["-C", dir, "config", "core.hooksPath", hooksPath]);
|
|
15
|
+
return "configured";
|
|
16
|
+
}
|
|
17
|
+
async function isGitRepository(dir) {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await run("git", ["-C", dir, "rev-parse", "--is-inside-work-tree"]);
|
|
20
|
+
return stdout.trim() === "true";
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=git-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-hooks.js","sourceRoot":"","sources":["../src/git-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAIhC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW,EAAE,SAAiB;IACrE,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAC;IACrE,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,uBAAuB,CAAC,CAAC,CAAC;QACvF,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 훅 디렉터리 아래의 모든 파일에 실행 권한을 준다. 하위 디렉터리(`pre-commit.d/` 등)도 포함한다.
|
|
3
|
+
*
|
|
4
|
+
* npm 패키지로 배포하면 파일의 실행 권한이 사라지고, Git은 실행 권한이 없는 훅을 오류 없이 건너뛴다.
|
|
5
|
+
* 그래서 설치한 뒤 권한을 직접 맞춘다. 훅 디렉터리가 없으면 아무것도 하지 않는다.
|
|
6
|
+
*
|
|
7
|
+
* @returns 권한을 준 파일 수
|
|
8
|
+
*/
|
|
9
|
+
export declare function makeHooksExecutable(root: string, hooksPath: string): Promise<number>;
|
|
10
|
+
//# sourceMappingURL=hook-permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-permissions.d.ts","sourceRoot":"","sources":["../src/hook-permissions.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW1F"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { chmod, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* 훅 디렉터리 아래의 모든 파일에 실행 권한을 준다. 하위 디렉터리(`pre-commit.d/` 등)도 포함한다.
|
|
5
|
+
*
|
|
6
|
+
* npm 패키지로 배포하면 파일의 실행 권한이 사라지고, Git은 실행 권한이 없는 훅을 오류 없이 건너뛴다.
|
|
7
|
+
* 그래서 설치한 뒤 권한을 직접 맞춘다. 훅 디렉터리가 없으면 아무것도 하지 않는다.
|
|
8
|
+
*
|
|
9
|
+
* @returns 권한을 준 파일 수
|
|
10
|
+
*/
|
|
11
|
+
export async function makeHooksExecutable(root, hooksPath) {
|
|
12
|
+
const dir = join(root, hooksPath);
|
|
13
|
+
if (!(await isDirectory(dir))) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const entries = await readdir(dir, { recursive: true, withFileTypes: true });
|
|
17
|
+
const files = entries.filter((entry) => entry.isFile());
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
await chmod(join(file.parentPath, file.name), 0o755);
|
|
20
|
+
}
|
|
21
|
+
return files.length;
|
|
22
|
+
}
|
|
23
|
+
async function isDirectory(path) {
|
|
24
|
+
try {
|
|
25
|
+
return (await stat(path)).isDirectory();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=hook-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-permissions.js","sourceRoot":"","sources":["../src/hook-permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,SAAiB;IACvE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,CAAC,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 명령이 바깥 세계와 주고받는 통로.
|
|
3
|
+
*
|
|
4
|
+
* 명령이 process를 직접 쓰지 않게 해서, 테스트에서는 출력을 모으고 실행 위치를 바꿔 끼울 수 있게 한다.
|
|
5
|
+
*/
|
|
6
|
+
export interface Io {
|
|
7
|
+
/** 상대 경로 옵션을 풀 때 기준이 되는 디렉터리. */
|
|
8
|
+
readonly cwd: string;
|
|
9
|
+
/** 결과 출력. 줄바꿈은 호출하는 쪽이 붙인다. */
|
|
10
|
+
readonly stdout: (text: string) => void;
|
|
11
|
+
/** 오류·안내 출력. 줄바꿈은 호출하는 쪽이 붙인다. */
|
|
12
|
+
readonly stderr: (text: string) => void;
|
|
13
|
+
}
|
|
14
|
+
/** 프로세스 종료 코드. 0: 문제 없음, 1: 규칙 위반, 2: 사용법 오류나 검사를 시작할 수 없음. */
|
|
15
|
+
export type ExitCode = 0 | 1 | 2;
|
|
16
|
+
//# sourceMappingURL=io.d.ts.map
|
package/dist/io.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,EAAE;IACjB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}
|
package/dist/io.js
ADDED
package/dist/io.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":""}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { run } from "./run.js";
|
|
3
|
+
// 실제 프로세스와 run을 잇는 얇은 진입점. 로직은 테스트할 수 있도록 run에 둔다.
|
|
4
|
+
process.exitCode = await run(process.argv.slice(2), {
|
|
5
|
+
cwd: process.cwd(),
|
|
6
|
+
stdout: (text) => process.stdout.write(text),
|
|
7
|
+
stderr: (text) => process.stderr.write(text),
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,mDAAmD;AACnD,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IAClD,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;IAClB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;IAC5C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;CAC7C,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Preset } from "@agentsmd/core";
|
|
2
|
+
/**
|
|
3
|
+
* `init --preset`으로 고를 수 있는 프리셋. 새 프리셋 패키지를 만들면 여기에 추가한다.
|
|
4
|
+
*
|
|
5
|
+
* 공통 Git 프리셋은 문서 지도가 없어 단독으로 고르지 않고, 언어 프리셋이 요구해서 함께 설치된다.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PRESETS: readonly Preset[];
|
|
8
|
+
export declare function findPreset(name: string): Preset | undefined;
|
|
9
|
+
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAI7C;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,SAAS,MAAM,EAAmC,CAAC;AAEzE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE3D"}
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { javaPreset } from "@agentsmd/preset-java";
|
|
2
|
+
import { typescriptPreset } from "@agentsmd/preset-typescript";
|
|
3
|
+
/**
|
|
4
|
+
* `init --preset`으로 고를 수 있는 프리셋. 새 프리셋 패키지를 만들면 여기에 추가한다.
|
|
5
|
+
*
|
|
6
|
+
* 공통 Git 프리셋은 문서 지도가 없어 단독으로 고르지 않고, 언어 프리셋이 요구해서 함께 설치된다.
|
|
7
|
+
*/
|
|
8
|
+
export const PRESETS = [javaPreset, typescriptPreset];
|
|
9
|
+
export function findPreset(name) {
|
|
10
|
+
return PRESETS.find((preset) => preset.name === name);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../src/presets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAsB,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AAEzE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACxD,CAAC"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ExitCode, Io } from "./io.js";
|
|
2
|
+
export declare const USAGE = "\uC0AC\uC6A9\uBC95: agentsmd <\uBA85\uB839> [\uC635\uC158]\n\nAI \uCF54\uB529 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uC791\uC5C5 \uC9C0\uCE68(AGENTS.md)\uC744 \uC124\uCE58\uD558\uACE0 \uAC80\uC0AC\uD569\uB2C8\uB2E4.\n\n\uBA85\uB839:\n init \uD504\uB9AC\uC14B\uC73C\uB85C \uC9C0\uCE68 \uD30C\uC77C\uC744 \uC124\uCE58\uD558\uACE0 Git \uD6C5\uC744 \uC5F0\uACB0\uD569\uB2C8\uB2E4\n check AGENTS.md \uBB38\uC11C \uC9C0\uB3C4\uAC00 \uC800\uC7A5\uC18C\uC640 \uB9DE\uB294\uC9C0 \uAC80\uC0AC\uD569\uB2C8\uB2E4\n\n\uBA85\uB839\uBCC4 \uC635\uC158: agentsmd <\uBA85\uB839> --help\n";
|
|
3
|
+
/** 첫 인자로 명령을 고르고 나머지 인자를 넘긴다. 옵션 파싱 오류는 사용법 오류(2)로 바꾼다. */
|
|
4
|
+
export declare function run(argv: readonly string[], io: Io): Promise<ExitCode>;
|
|
5
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAS5C,eAAO,MAAM,KAAK,8jBASjB,CAAC;AAEF,2DAA2D;AAC3D,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuB5E"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { runCheck } from "./commands/check.js";
|
|
2
|
+
import { runInit } from "./commands/init.js";
|
|
3
|
+
const COMMANDS = {
|
|
4
|
+
check: runCheck,
|
|
5
|
+
init: runInit,
|
|
6
|
+
};
|
|
7
|
+
export const USAGE = `사용법: agentsmd <명령> [옵션]
|
|
8
|
+
|
|
9
|
+
AI 코딩 에이전트용 작업 지침(AGENTS.md)을 설치하고 검사합니다.
|
|
10
|
+
|
|
11
|
+
명령:
|
|
12
|
+
init 프리셋으로 지침 파일을 설치하고 Git 훅을 연결합니다
|
|
13
|
+
check AGENTS.md 문서 지도가 저장소와 맞는지 검사합니다
|
|
14
|
+
|
|
15
|
+
명령별 옵션: agentsmd <명령> --help
|
|
16
|
+
`;
|
|
17
|
+
/** 첫 인자로 명령을 고르고 나머지 인자를 넘긴다. 옵션 파싱 오류는 사용법 오류(2)로 바꾼다. */
|
|
18
|
+
export async function run(argv, io) {
|
|
19
|
+
const [name, ...args] = argv;
|
|
20
|
+
if (name === undefined || name === "-h" || name === "--help") {
|
|
21
|
+
io.stdout(USAGE);
|
|
22
|
+
return name === undefined ? 2 : 0;
|
|
23
|
+
}
|
|
24
|
+
const command = COMMANDS[name];
|
|
25
|
+
if (command === undefined) {
|
|
26
|
+
io.stderr(`알 수 없는 명령입니다: ${name}\n\n${USAGE}`);
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return await command(args, io);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (isParseArgsError(error)) {
|
|
34
|
+
io.stderr(`${error.message}\n자세한 옵션: agentsmd ${name} --help\n`);
|
|
35
|
+
return 2;
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** node:util parseArgs가 던지는 오류는 ERR_PARSE_ARGS_ 로 시작하는 code를 가진다. */
|
|
41
|
+
function isParseArgsError(error) {
|
|
42
|
+
return (error instanceof Error &&
|
|
43
|
+
"code" in error &&
|
|
44
|
+
typeof error.code === "string" &&
|
|
45
|
+
error.code.startsWith("ERR_PARSE_ARGS_"));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAK7C,MAAM,QAAQ,GAAsC;IAClD,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,OAAO;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;CASpB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAuB,EAAE,EAAM;IACvD,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAE7B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7D,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,EAAE,CAAC,MAAM,CAAC,iBAAiB,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,sBAAsB,IAAI,WAAW,CAAC,CAAC;YACjE,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,CACzC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentsmd/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI 코딩 에이전트용 작업 지침(AGENTS.md)을 설치하고 검사하는 CLI",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agents.md",
|
|
7
|
+
"ai-agent",
|
|
8
|
+
"claude-code",
|
|
9
|
+
"cli",
|
|
10
|
+
"git-hooks"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/xoruddl/agentsmd-kit#readme",
|
|
13
|
+
"bugs": "https://github.com/xoruddl/agentsmd-kit/issues",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/xoruddl/agentsmd-kit.git",
|
|
17
|
+
"directory": "packages/cli"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "이태경 (https://github.com/xoruddl)",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"agentsmd": "./dist/main.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@agentsmd/core": "0.1.0",
|
|
36
|
+
"@agentsmd/preset-java": "0.1.0",
|
|
37
|
+
"@agentsmd/preset-typescript": "0.1.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc -p tsconfig.build.json",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
42
|
+
"test": "vitest run"
|
|
43
|
+
}
|
|
6
44
|
}
|