@rch4com/agent-setup 1.0.0 → 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.
Files changed (56) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +151 -3
  3. package/install.mjs +102 -33
  4. package/lib/args.mjs +104 -94
  5. package/lib/bootstrap/adapter.mjs +96 -95
  6. package/lib/bootstrap/apply.mjs +380 -269
  7. package/lib/bootstrap/flow.mjs +73 -54
  8. package/lib/bootstrap/record.mjs +154 -0
  9. package/lib/bootstrap/templates.mjs +148 -148
  10. package/lib/bootstrap/text.mjs +19 -0
  11. package/lib/catalog.mjs +213 -145
  12. package/lib/claude-plugins.mjs +64 -64
  13. package/lib/clis.mjs +102 -102
  14. package/lib/context.mjs +66 -65
  15. package/lib/deps.mjs +5 -7
  16. package/lib/design-md/catalog.json +454 -454
  17. package/lib/design-md/catalog.mjs +266 -268
  18. package/lib/design-md/flow.mjs +181 -166
  19. package/lib/design-md/open.mjs +51 -50
  20. package/lib/design-md/providers/awesome-design-md.mjs +117 -114
  21. package/lib/design-md/providers/index.mjs +9 -9
  22. package/lib/design-md/scan.mjs +14 -10
  23. package/lib/engine.mjs +48 -44
  24. package/lib/gitignore.mjs +18 -18
  25. package/lib/i18n/catalog/en.mjs +369 -0
  26. package/lib/i18n/catalog/ko.mjs +359 -0
  27. package/lib/i18n/detect.mjs +39 -0
  28. package/lib/i18n/index.mjs +93 -0
  29. package/lib/items/mcp.codebase-memory.mjs +6 -6
  30. package/lib/items/mcp.graphify.mjs +8 -0
  31. package/lib/items/mcp.headroom.mjs +8 -0
  32. package/lib/items/mcp.notion.mjs +6 -6
  33. package/lib/items/mcp.supabase.mjs +6 -6
  34. package/lib/items/mcp.vercel.mjs +6 -6
  35. package/lib/items/plugin.bkit.mjs +7 -7
  36. package/lib/items/plugin.ecc.mjs +11 -0
  37. package/lib/items/plugin.impeccable.mjs +14 -0
  38. package/lib/items/plugin.mattpocock-skills.mjs +2 -2
  39. package/lib/items/plugin.ponytail.mjs +104 -0
  40. package/lib/items/plugin.superpowers.mjs +7 -7
  41. package/lib/items/plugin.understand-anything.mjs +10 -0
  42. package/lib/items/skill.caveman.mjs +7 -0
  43. package/lib/items/skill.gsd.mjs +25 -24
  44. package/lib/items/skill.gstack.mjs +40 -39
  45. package/lib/items/skill.karpathy.mjs +7 -0
  46. package/lib/items/skill.taste.mjs +9 -0
  47. package/lib/jsonfile.mjs +40 -40
  48. package/lib/status.mjs +93 -0
  49. package/lib/tomlfile.mjs +60 -60
  50. package/lib/tui/render.mjs +41 -63
  51. package/lib/tui/rows.mjs +278 -206
  52. package/lib/tui/run.mjs +64 -28
  53. package/lib/update.mjs +85 -0
  54. package/lib/width.mjs +57 -0
  55. package/package.json +36 -36
  56. package/lib/items/.omc/state/sessions/a859aff7-59d9-4ff6-afc6-8cafe4471dc4/pre-tool-advisory-throttle.json +0 -14
@@ -1,54 +1,73 @@
1
- // 부트스트랩 진입점 — 순서와 보고만 담당한다.
2
- // 무엇을 만들지는 manifest.mjs가, 어떻게 만들지는 apply.mjs·adapter.mjs가 안다.
3
- import { MANIFEST } from './manifest.mjs'
4
- import { ensureBlocks, ensureDirs, ensureFiles, ensureIgnore, ensureJsonKeys } from './apply.mjs'
5
- import { configureAdapter } from './adapter.mjs'
6
-
7
- const SKILL_MODES = ['auto', 'link', 'copy']
8
-
9
- export function runBootstrap(root, opts = {}) {
10
- const { dryRun = false, skillMode = 'auto', log = console.log, manifest = MANIFEST } = opts
11
-
12
- if (!SKILL_MODES.includes(skillMode)) {
13
- throw new Error(`--skill-mode는 ${SKILL_MODES.join(', ')} 중 하나여야 합니다: ${skillMode}`)
14
- }
15
-
16
- const say = (message) => log(`[agent-setup] ${message}`)
17
- const ctx = { dryRun, log: say }
18
-
19
- say(`저장소 루트: ${root}`)
20
- say('글로벌 설정 경로는 읽거나 수정하지 않습니다.')
21
-
22
- const results = [
23
- ...ensureDirs(root, manifest.dirs, ctx),
24
- ...ensureFiles(root, manifest.files, ctx),
25
- ...ensureJsonKeys(root, manifest.settings ?? [], ctx),
26
- ...ensureBlocks(root, manifest.blocks, ctx),
27
- ]
28
-
29
- // 어댑터는 항목별로 실패를 격리한다 — 하나가 실패해도 나머지를 계속한다.
30
- for (const entry of manifest.adapters) {
31
- try {
32
- results.push(configureAdapter(root, entry, { ...ctx, skillMode }))
33
- } catch (err) {
34
- results.push({ ok: false, action: 'link', path: entry.path, message: err.message })
35
- }
36
- }
37
-
38
- results.push(...ensureIgnore(root, manifest.ignore, ctx))
39
-
40
- const failed = results.filter((r) => !r.ok)
41
- log('')
42
- if (failed.length > 0) {
43
- say(`실패 ${failed.length}건:`)
44
- for (const f of failed) say(` ✖ ${f.path} — ${f.message}`)
45
- }
46
- say('완료되었습니다.')
47
- say('공통 지침: AGENTS.md')
48
- say('공통 스킬: .agents/skills/')
49
- say(`적용 도구: ${manifest.tools.join(', ')}`)
50
- say('도구별 설정은 모두 현재 저장소 안에만 생성되었습니다.')
51
- say('기존 설정 파일은 덮어쓰지 않았습니다.')
52
-
53
- return { results, failed }
54
- }
1
+ // 부트스트랩 진입점 — 순서와 보고만 담당한다.
2
+ // 무엇을 만들지는 manifest.mjs가, 어떻게 만들지는 apply.mjs·adapter.mjs가 안다.
3
+ import { MANIFEST } from './manifest.mjs'
4
+ import {
5
+ configureAdapterSafe, ensureBlocks, ensureDirs, ensureFiles, ensureIgnore, ensureJsonKeys,
6
+ } from './apply.mjs'
7
+ import { RECORD_REL, collectManaged, emptyRecord, readRecord, writeRecord } from './record.mjs'
8
+ import { createT, LocalizedError, toText } from '../i18n/index.mjs'
9
+
10
+ const SKILL_MODES = ['auto', 'link', 'copy']
11
+
12
+ export function runBootstrap(root, opts = {}) {
13
+ const { dryRun = false, skillMode = 'auto', adopt = false, log = console.log, manifest = MANIFEST, t = createT('en') } = opts
14
+
15
+ if (!SKILL_MODES.includes(skillMode)) {
16
+ throw new LocalizedError('error.badSkillModeRuntime', { list: SKILL_MODES.join(', '), value: skillMode })
17
+ }
18
+
19
+ const say = (message) => log(`[agent-setup] ${message}`)
20
+ const ctx = { dryRun, log: say, t }
21
+
22
+ say(t('log.repoRoot', { path: root }))
23
+ say(t('log.noGlobalWrites'))
24
+
25
+ // --adopt는 이미 있는 저장소를 기록 체계로 끌어오는 용도라 파일을 만들지
26
+ // 않는다. 설치기를 복사해 쓰던 저장소가 여기로 들어온다.
27
+ const results = adopt ? [] : [
28
+ ...ensureDirs(root, manifest.dirs, ctx),
29
+ ...ensureFiles(root, manifest.files, ctx),
30
+ ...ensureJsonKeys(root, manifest.settings ?? [], ctx),
31
+ ...ensureBlocks(root, manifest.blocks, ctx),
32
+ ]
33
+
34
+ if (!adopt) {
35
+ // 어댑터는 항목별로 실패를 격리한다 — 하나가 실패해도 나머지를 계속한다.
36
+ // update.mjs도 같은 격리를 써야 하므로 apply.mjs가 단일 출처다.
37
+ for (const entry of manifest.adapters) {
38
+ results.push(configureAdapterSafe(root, entry, { ...ctx, skillMode }))
39
+ }
40
+
41
+ results.push(...ensureIgnore(root, manifest.ignore, ctx))
42
+ }
43
+
44
+ // 기존 기록의 items·design은 보존한다 — 부트스트랩은 배선만 다루므로
45
+ // 사용자가 고른 설치 항목을 지울 권한이 없다. dry-run에서는 읽지 않는다:
46
+ // 깨진 기록이 "무엇이 바뀔지 확인"까지 막을 이유가 없다.
47
+ const previous = dryRun ? null : readRecord(root)
48
+ const record = {
49
+ ...emptyRecord({ skillMode }),
50
+ // 부트스트랩은 배선만 다룬다. 사용자가 고른 언어·항목을 지울 권한이 없다.
51
+ lang: previous?.lang ?? null,
52
+ items: previous?.items ?? [],
53
+ design: previous?.design ?? [],
54
+ managed: collectManaged(root, manifest),
55
+ }
56
+ results.push(writeRecord(root, record, ctx))
57
+
58
+ const failed = results.filter((r) => !r.ok)
59
+ log('')
60
+ if (failed.length > 0) {
61
+ say(t('bootstrap.failures', { count: failed.length }))
62
+ for (const f of failed) say(` ✖ ${f.path} — ${toText(t, f.message)}`)
63
+ }
64
+ say(t('bootstrap.done'))
65
+ say(t('bootstrap.sharedGuide'))
66
+ say(t('bootstrap.sharedSkills'))
67
+ say(t('bootstrap.tools', { list: manifest.tools.join(', ') }))
68
+ say(t('bootstrap.repoOnly'))
69
+ say(t('bootstrap.noOverwrite'))
70
+ say(t('bootstrap.record', { path: RECORD_REL }))
71
+
72
+ return { results, failed, record }
73
+ }
@@ -0,0 +1,154 @@
1
+ // 저장소에 커밋되는 설치 기록.
2
+ //
3
+ // 이 파일은 "의도"다 — 실제 상태의 근거는 여전히 스캔이다. 기록이 더하는
4
+ // 것은 재현성(팀원이 같은 결과를 얻는다)과 버전 고정, 그리고 "우리가 쓴
5
+ // 그대로인가"를 판정할 해시다.
6
+ //
7
+ // 설치기 안의 manifest.mjs(무엇을 생성할지 선언)와 이름이 겹치지 않게
8
+ // 저장소 쪽 파일은 '설치 기록'이라 부른다.
9
+ //
10
+ // 부트스트랩 그래프에 속하므로 node: 내장 모듈만 쓴다.
11
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
12
+ import { dirname } from 'node:path'
13
+ import { repoPath, repoPathStrict } from '../context.mjs'
14
+ import { LOCALES, LocalizedError } from '../i18n/index.mjs'
15
+ import { hashBody, normalizeBody } from './text.mjs'
16
+
17
+ export const RECORD_REL = '.agent-kit/agent-setup.json'
18
+ export const FORMAT_VERSION = 1
19
+
20
+ // 발행된 패키지에도 package.json은 항상 들어간다(npm이 무조건 포함한다).
21
+ // createRequire 대신 URL로 읽어 의존성 0을 유지한다.
22
+ let cachedVersion
23
+ export function toolVersion() {
24
+ if (!cachedVersion) {
25
+ const url = new URL('../../package.json', import.meta.url)
26
+ cachedVersion = JSON.parse(readFileSync(url, 'utf8')).version
27
+ }
28
+ return cachedVersion
29
+ }
30
+
31
+ export function emptyRecord({ skillMode = 'auto', lang = null } = {}) {
32
+ return {
33
+ formatVersion: FORMAT_VERSION,
34
+ pinnedVersion: toolVersion(),
35
+ skillMode,
36
+ lang,
37
+ items: [],
38
+ design: [],
39
+ managed: {},
40
+ }
41
+ }
42
+
43
+ // 없으면 null. 있으면 필드를 채워 돌려준다 — 손으로 편집해 필드가 빠져도
44
+ // 죽지 않아야 한다. 형식 버전이 다르면 추측하지 않고 던진다.
45
+ export function readRecord(root) {
46
+ const target = repoPath(root, RECORD_REL)
47
+ let text
48
+ try {
49
+ text = readFileSync(target, 'utf8')
50
+ } catch (err) {
51
+ if (err.code === 'ENOENT') return null
52
+ throw new LocalizedError('error.recordUnreadable', { path: RECORD_REL, code: err.code ?? err.message })
53
+ }
54
+
55
+ let parsed
56
+ try {
57
+ parsed = JSON.parse(text)
58
+ } catch (err) {
59
+ throw new LocalizedError('error.recordNotJson', { path: RECORD_REL, message: err.message })
60
+ }
61
+
62
+ if (parsed.formatVersion !== FORMAT_VERSION) {
63
+ throw new LocalizedError('error.recordVersion', { path: RECORD_REL, found: parsed.formatVersion, expected: FORMAT_VERSION })
64
+ }
65
+
66
+ return {
67
+ formatVersion: parsed.formatVersion,
68
+ pinnedVersion: parsed.pinnedVersion ?? null,
69
+ skillMode: parsed.skillMode ?? 'auto',
70
+ // 손으로 편집된 값 때문에 도구가 죽으면 안 된다. 모르는 값은 없는 것으로 본다.
71
+ lang: LOCALES.includes(parsed.lang) ? parsed.lang : null,
72
+ items: Array.isArray(parsed.items) ? parsed.items : [],
73
+ design: Array.isArray(parsed.design) ? parsed.design : [],
74
+ managed: parsed.managed && typeof parsed.managed === 'object' ? parsed.managed : {},
75
+ }
76
+ }
77
+
78
+ // pinnedVersion은 여기서 실행 중 버전으로 맞춘다 — 기록을 쓰는 명령만
79
+ // 버전을 옮길 수 있어야 고정이 거짓말하지 않는다.
80
+ //
81
+ // log를 주면 t도 반드시 줘야 한다. 영어 리터럴 폴백을 두면 t를 빠뜨린
82
+ // 호출부가 한국어 출력 한가운데에 영어 한 줄을 흘리면서도 아무 신호를 내지
83
+ // 않는다 — update.mjs가 실제로 그렇게 새어 나갔다.
84
+ export function writeRecord(root, record, { dryRun = false, log, t } = {}) {
85
+ const target = repoPathStrict(root, RECORD_REL)
86
+ const body = `${JSON.stringify({ ...record, pinnedVersion: toolVersion() }, null, 2)}\n`
87
+ log?.(t('log.record.write', { path: RECORD_REL }))
88
+ if (!dryRun) {
89
+ mkdirSync(dirname(target), { recursive: true })
90
+ writeFileSync(target, body, { encoding: 'utf8' })
91
+ }
92
+ return { ok: true, action: dryRun ? 'skip' : 'write', path: RECORD_REL }
93
+ }
94
+
95
+ // 언어만 바꾼다. 기록이 없으면 만든다 — 부트스트랩 전에 언어를 골라도
96
+ // 유지되어야 하기 때문이다. 나머지 필드는 있는 그대로 옮긴다.
97
+ export function writeLang(root, lang, { dryRun = false, log, t } = {}) {
98
+ const previous = readRecord(root)
99
+ return writeRecord(root, { ...(previous ?? emptyRecord()), lang }, { dryRun, log, t })
100
+ }
101
+
102
+ // 마커는 apply.mjs의 ensureBlocks(생성)와 updateBlocks(갱신), 그리고 여기의
103
+ // extractBlock(판정)이 함께 쓴다. 정의가 둘로 갈리면 한쪽만 고치는 회귀가
104
+ // 생기므로 이 모듈이 단일 출처가 되고 apply.mjs가 import한다.
105
+ export const BEGIN_MARKER = '<!-- agent-kit:begin -->'
106
+ export const END_MARKER = '<!-- agent-kit:end -->'
107
+
108
+ // 블록은 파일 전체가 아니라 마커 사이 본문만 관리 대상이다. 파일 전체 해시와
109
+ // 섞이지 않게 키에 접미사를 붙인다.
110
+ export const BLOCK_SUFFIX = '#agent-kit'
111
+
112
+ export function managedKey(rel, isBlock) {
113
+ return isBlock ? `${rel}${BLOCK_SUFFIX}` : rel
114
+ }
115
+
116
+ // 마커 사이 본문을 정규화해 돌려준다. 마커가 없거나 순서가 뒤집혀 있으면 null.
117
+ export function extractBlock(text) {
118
+ const begin = text.indexOf(BEGIN_MARKER)
119
+ if (begin === -1) return null
120
+ const end = text.indexOf(END_MARKER, begin + BEGIN_MARKER.length)
121
+ if (end === -1) return null
122
+ return normalizeBody(text.slice(begin + BEGIN_MARKER.length, end))
123
+ }
124
+
125
+ function readOrNull(root, rel) {
126
+ try {
127
+ return readFileSync(repoPath(root, rel), 'utf8')
128
+ } catch {
129
+ return null
130
+ }
131
+ }
132
+
133
+ // 채택 규칙: 현재 내용이 이 버전의 템플릿과 정규화 후 일치하는 것만 해시를
134
+ // 남긴다. 그러지 않으면 사용자가 이미 고쳐 둔 파일이 "우리가 쓴 그대로"로
135
+ // 위장되어 다음 update에 날아간다. 값이 null인 키는 update가 절대 덮어쓰지
136
+ // 않으며, 없는 파일도 키를 남겨 update의 생성 분기가 집어간다.
137
+ export function collectManaged(root, manifest) {
138
+ const managed = {}
139
+
140
+ for (const { path: rel, template } of manifest.files ?? []) {
141
+ const text = readOrNull(root, rel)
142
+ const wanted = hashBody(template)
143
+ managed[rel] = text !== null && hashBody(text) === wanted ? wanted : null
144
+ }
145
+
146
+ for (const { path: rel, block } of manifest.blocks ?? []) {
147
+ const text = readOrNull(root, rel)
148
+ const wanted = hashBody(extractBlock(block) ?? block)
149
+ const current = text === null ? null : extractBlock(text)
150
+ managed[managedKey(rel, true)] = current !== null && hashBody(current) === wanted ? wanted : null
151
+ }
152
+
153
+ return managed
154
+ }
@@ -1,148 +1,148 @@
1
- // setup-agents.ps1의 here-string 내용을 옮긴 단일 출처.
2
- // 여기만 고치면 모든 OS가 같은 파일을 만든다.
3
-
4
- export const AGENTS_TEMPLATE = `# Repository Instructions
5
-
6
- ## Scope
7
-
8
- These instructions apply only to this repository.
9
-
10
- ## Required workflow
11
-
12
- 1. Inspect the relevant existing implementation before editing.
13
- 2. Make the smallest change that satisfies the requirement.
14
- 3. Add or update tests for behavior changes.
15
- 4. Run focused verification first, then the full repository checks.
16
- 5. Report anything that could not be verified.
17
-
18
- ## Repository commands
19
-
20
- Document the actual commands used by this repository:
21
-
22
- - Install:
23
- - Build:
24
- - Test:
25
- - Lint:
26
- - Full verification:
27
-
28
- ## Rules
29
-
30
- - Do not modify files outside this repository.
31
- - Do not add production dependencies without explicit approval.
32
- - Do not edit generated files directly.
33
- - Never commit credentials, access tokens, or connection strings.`
34
-
35
- export const CLAUDE_BLOCK = `<!-- agent-kit:begin -->
36
- @AGENTS.md
37
- <!-- agent-kit:end -->`
38
-
39
- export const GEMINI_BLOCK = `<!-- agent-kit:begin -->
40
- @./AGENTS.md
41
- <!-- agent-kit:end -->`
42
-
43
- export const SKILL_README = `# Shared repository skills
44
-
45
- Create each shared skill in:
46
-
47
- \`.agents/skills/<skill-name>/SKILL.md\`
48
-
49
- Use portable Agent Skills frontmatter:
50
-
51
- \`\`\`yaml
52
- ---
53
- name: example-skill
54
- description: Explain when this skill should be used.
55
- ---
56
- \`\`\`
57
-
58
- Claude Code, Kiro, and Grok Build receive these skills through project-local
59
- adapters at \`.claude/skills\`, \`.kiro/skills\`, and \`.grok/skills\`. Codex,
60
- Gemini CLI, OpenCode, Kilo Code, Kimi Code, Antigravity, GitHub Copilot CLI,
61
- and VS Code Copilot discover \`.agents/skills\` directly.`
62
-
63
- export const EXAMPLE_SKILL = `---
64
- name: repository-check
65
- description: Verify repository changes by reviewing the diff and running the documented build, test, and lint commands. Use before reporting implementation work as complete.
66
- ---
67
-
68
- # Repository check
69
-
70
- 1. Read \`AGENTS.md\`.
71
- 2. Review the complete working-tree diff.
72
- 3. Run the smallest relevant checks first.
73
- 4. Run the repository's documented full verification command.
74
- 5. Report failures, skipped checks, and remaining risks.`
75
-
76
- export const AGENT_KIT_README = `# Repository-local agent setup
77
-
78
- This directory documents files generated or used by the bootstrap scripts.
79
-
80
- The installer bootstrap:
81
-
82
- - finds the current Git repository root;
83
- - refuses to write outside that repository;
84
- - creates only project-scoped configuration;
85
- - never writes to user-home or system-wide agent configuration;
86
- - preserves existing configuration files;
87
- - adds a small managed import block to \`CLAUDE.md\` and \`GEMINI.md\`;
88
- - exposes \`.agents/skills\` to Claude Code, Kiro, and Grok Build through
89
- local adapters;
90
- - adds \`chat.useAgentsMdFile\` to \`.vscode/settings.json\` only when the key
91
- is absent, so VS Code Copilot reads the shared \`AGENTS.md\`;
92
- - relies on the native support of Kilo Code, Kimi Code, Antigravity, GitHub
93
- Copilot CLI, and VS Code Copilot for \`AGENTS.md\` and \`.agents/skills\`.`
94
-
95
- export const CLAUDE_SETTINGS = `{}`
96
-
97
- export const CODEX_CONFIG = `# Repository-local Codex configuration.
98
- # Personal or machine-wide defaults belong outside this repository.
99
- project_doc_max_bytes = 65536`
100
-
101
- export const GEMINI_SETTINGS = `{
102
- "skills": {
103
- "enabled": true
104
- },
105
- "security": {
106
- "folderTrust": {
107
- "enabled": true
108
- },
109
- "disableAlwaysAllow": true,
110
- "enablePermanentToolApproval": false
111
- }
112
- }`
113
-
114
- export const GROK_CONFIG = `# Repository-local Grok Build configuration.
115
- # Project scope supports mcp_servers, plugins, and permission rules only.
116
- # Personal or machine-wide defaults belong in ~/.grok/config.toml.
117
- # Grok Build reads the root AGENTS.md natively.`
118
-
119
- export const OPENCODE_CONFIG = `{
120
- "$schema": "https://opencode.ai/config.json"
121
- }`
122
-
123
- export const KILO_CONFIG = `{
124
- // Project-local Kilo Code configuration.
125
- // Kilo Code automatically loads ./AGENTS.md and ./.agents/skills/.
126
- }`
127
-
128
- export const KIRO_MCP_CONFIG = `{
129
- "mcpServers": {}
130
- }`
131
-
132
- export const KIMI_MCP_CONFIG = `{
133
- "mcpServers": {}
134
- }`
135
-
136
- export const COPILOT_MCP_CONFIG = `{
137
- "mcpServers": {}
138
- }`
139
-
140
- // 모델·추론 강도·컨텍스트 티어를 저장소 단위로 고정하는 공유 설정 자리.
141
- // 구체적인 값은 팀이 정한다. 개인 오버라이드는 settings.local.json이며
142
- // .gitignore 대상이다.
143
- export const COPILOT_SETTINGS = `{}`
144
-
145
- // VS Code는 최상위 키가 servers다 (Copilot CLI의 mcpServers와 다르다).
146
- export const VSCODE_MCP_CONFIG = `{
147
- "servers": {}
148
- }`
1
+ // setup-agents.ps1의 here-string 내용을 옮긴 단일 출처.
2
+ // 여기만 고치면 모든 OS가 같은 파일을 만든다.
3
+
4
+ export const AGENTS_TEMPLATE = `# Repository Instructions
5
+
6
+ ## Scope
7
+
8
+ These instructions apply only to this repository.
9
+
10
+ ## Required workflow
11
+
12
+ 1. Inspect the relevant existing implementation before editing.
13
+ 2. Make the smallest change that satisfies the requirement.
14
+ 3. Add or update tests for behavior changes.
15
+ 4. Run focused verification first, then the full repository checks.
16
+ 5. Report anything that could not be verified.
17
+
18
+ ## Repository commands
19
+
20
+ Document the actual commands used by this repository:
21
+
22
+ - Install:
23
+ - Build:
24
+ - Test:
25
+ - Lint:
26
+ - Full verification:
27
+
28
+ ## Rules
29
+
30
+ - Do not modify files outside this repository.
31
+ - Do not add production dependencies without explicit approval.
32
+ - Do not edit generated files directly.
33
+ - Never commit credentials, access tokens, or connection strings.`
34
+
35
+ export const CLAUDE_BLOCK = `<!-- agent-kit:begin -->
36
+ @AGENTS.md
37
+ <!-- agent-kit:end -->`
38
+
39
+ export const GEMINI_BLOCK = `<!-- agent-kit:begin -->
40
+ @./AGENTS.md
41
+ <!-- agent-kit:end -->`
42
+
43
+ export const SKILL_README = `# Shared repository skills
44
+
45
+ Create each shared skill in:
46
+
47
+ \`.agents/skills/<skill-name>/SKILL.md\`
48
+
49
+ Use portable Agent Skills frontmatter:
50
+
51
+ \`\`\`yaml
52
+ ---
53
+ name: example-skill
54
+ description: Explain when this skill should be used.
55
+ ---
56
+ \`\`\`
57
+
58
+ Claude Code, Kiro, and Grok Build receive these skills through project-local
59
+ adapters at \`.claude/skills\`, \`.kiro/skills\`, and \`.grok/skills\`. Codex,
60
+ Gemini CLI, OpenCode, Kilo Code, Kimi Code, Antigravity, GitHub Copilot CLI,
61
+ and VS Code Copilot discover \`.agents/skills\` directly.`
62
+
63
+ export const EXAMPLE_SKILL = `---
64
+ name: repository-check
65
+ description: Verify repository changes by reviewing the diff and running the documented build, test, and lint commands. Use before reporting implementation work as complete.
66
+ ---
67
+
68
+ # Repository check
69
+
70
+ 1. Read \`AGENTS.md\`.
71
+ 2. Review the complete working-tree diff.
72
+ 3. Run the smallest relevant checks first.
73
+ 4. Run the repository's documented full verification command.
74
+ 5. Report failures, skipped checks, and remaining risks.`
75
+
76
+ export const AGENT_KIT_README = `# Repository-local agent setup
77
+
78
+ This directory documents files generated or used by the bootstrap scripts.
79
+
80
+ The installer bootstrap:
81
+
82
+ - finds the current Git repository root;
83
+ - refuses to write outside that repository;
84
+ - creates only project-scoped configuration;
85
+ - never writes to user-home or system-wide agent configuration;
86
+ - preserves existing configuration files;
87
+ - adds a small managed import block to \`CLAUDE.md\` and \`GEMINI.md\`;
88
+ - exposes \`.agents/skills\` to Claude Code, Kiro, and Grok Build through
89
+ local adapters;
90
+ - adds \`chat.useAgentsMdFile\` to \`.vscode/settings.json\` only when the key
91
+ is absent, so VS Code Copilot reads the shared \`AGENTS.md\`;
92
+ - relies on the native support of Kilo Code, Kimi Code, Antigravity, GitHub
93
+ Copilot CLI, and VS Code Copilot for \`AGENTS.md\` and \`.agents/skills\`.`
94
+
95
+ export const CLAUDE_SETTINGS = `{}`
96
+
97
+ export const CODEX_CONFIG = `# Repository-local Codex configuration.
98
+ # Personal or machine-wide defaults belong outside this repository.
99
+ project_doc_max_bytes = 65536`
100
+
101
+ export const GEMINI_SETTINGS = `{
102
+ "skills": {
103
+ "enabled": true
104
+ },
105
+ "security": {
106
+ "folderTrust": {
107
+ "enabled": true
108
+ },
109
+ "disableAlwaysAllow": true,
110
+ "enablePermanentToolApproval": false
111
+ }
112
+ }`
113
+
114
+ export const GROK_CONFIG = `# Repository-local Grok Build configuration.
115
+ # Project scope supports mcp_servers, plugins, and permission rules only.
116
+ # Personal or machine-wide defaults belong in ~/.grok/config.toml.
117
+ # Grok Build reads the root AGENTS.md natively.`
118
+
119
+ export const OPENCODE_CONFIG = `{
120
+ "$schema": "https://opencode.ai/config.json"
121
+ }`
122
+
123
+ export const KILO_CONFIG = `{
124
+ // Project-local Kilo Code configuration.
125
+ // Kilo Code automatically loads ./AGENTS.md and ./.agents/skills/.
126
+ }`
127
+
128
+ export const KIRO_MCP_CONFIG = `{
129
+ "mcpServers": {}
130
+ }`
131
+
132
+ export const KIMI_MCP_CONFIG = `{
133
+ "mcpServers": {}
134
+ }`
135
+
136
+ export const COPILOT_MCP_CONFIG = `{
137
+ "mcpServers": {}
138
+ }`
139
+
140
+ // 모델·추론 강도·컨텍스트 티어를 저장소 단위로 고정하는 공유 설정 자리.
141
+ // 구체적인 값은 팀이 정한다. 개인 오버라이드는 settings.local.json이며
142
+ // .gitignore 대상이다.
143
+ export const COPILOT_SETTINGS = `{}`
144
+
145
+ // VS Code는 최상위 키가 servers다 (Copilot CLI의 mcpServers와 다르다).
146
+ export const VSCODE_MCP_CONFIG = `{
147
+ "servers": {}
148
+ }`
@@ -0,0 +1,19 @@
1
+ // 정규화와 해시의 단일 출처.
2
+ //
3
+ // 파일을 쓸 때와 "우리가 쓴 그대로인가"를 판정할 때 같은 정규화를 써야 한다.
4
+ // 둘이 갈리면 갓 쓴 파일조차 드리프트로 보고된다. 그래서 이 모듈을 최하위에
5
+ // 두고 apply.mjs(쓰기)와 record.mjs(판정)가 함께 import한다 — 한쪽에만
6
+ // 정규화 규칙을 두면 두 모듈이 서로를 import하는 순환이 생긴다.
7
+ //
8
+ // 부트스트랩 그래프에 속하므로 node: 내장 모듈만 쓴다.
9
+ import { createHash } from 'node:crypto'
10
+
11
+ // 두 OS가 같은 파일을 만들도록 항상 LF + 끝 개행 1개로 정규화한다.
12
+ export function normalizeBody(text) {
13
+ return text.replace(/\r\n/g, '\n').trim() + '\n'
14
+ }
15
+
16
+ // 정규화한 내용의 해시. 워킹트리 줄바꿈이 CRLF든 LF든 같은 값이 나온다.
17
+ export function hashBody(text) {
18
+ return `sha256:${createHash('sha256').update(normalizeBody(text), 'utf8').digest('hex')}`
19
+ }