spr-ai-native 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 +181 -0
- package/bin/cli.js +119 -0
- package/package.json +41 -0
- package/preset/common/base-rules.md +57 -0
- package/preset/common/commands/engineer.md +22 -0
- package/preset/common/commands/planner.md +23 -0
- package/preset/common/commands/pm.md +170 -0
- package/preset/common/commands/qa.md +21 -0
- package/preset/common/project-doc.md +90 -0
- package/preset/common/roles/engineer.md +75 -0
- package/preset/common/roles/planner.md +125 -0
- package/preset/common/roles/qa.md +78 -0
- package/src/index.js +55 -0
- package/src/lib/preset.js +61 -0
- package/src/lib/write.js +45 -0
- package/src/targets/claude.js +77 -0
- package/src/targets/codex.js +81 -0
- package/src/targets/cursor.js +74 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { COMMANDS, ROLES, baseRules, loadCommand, loadRole, projectDoc } from '../lib/preset.js';
|
|
3
|
+
import { frontmatter, quoted } from '../lib/write.js';
|
|
4
|
+
|
|
5
|
+
export const label = 'Codex CLI';
|
|
6
|
+
export const projectDocPath = 'AGENTS.md';
|
|
7
|
+
export const globalDocPath = '~/.codex/AGENTS.md';
|
|
8
|
+
|
|
9
|
+
const MODEL_COMMENT = `# model을 지정하지 않으면 부모 세션의 모델을 상속합니다.
|
|
10
|
+
# 고정하려면 model = "..." / model_reasoning_effort = "..." 를 추가하세요.`;
|
|
11
|
+
|
|
12
|
+
const DELEGATE_HOWTO = `Codex 멀티 에이전트 기능으로 서브에이전트를 spawn 합니다. \`.codex/agents/\`에 정의된 \`planner\` / \`engineer\` / \`qa\` 를 이름으로 지정해 호출하세요.
|
|
13
|
+
|
|
14
|
+
- 서브에이전트는 이 대화의 컨텍스트를 **보지 못합니다.** task_id, 파일 경로, 회차를 프롬프트에 반드시 포함하세요.
|
|
15
|
+
- 한 번에 하나씩 순서대로 spawn 합니다 (planner → engineer → qa). 각 단계가 앞 단계 산출물에 의존하므로 병렬 실행하지 마세요.
|
|
16
|
+
- 멀티 에이전트가 비활성화되어 있어 spawn이 불가능하면, 임의로 직접 구현하지 말고 사용자에게 알리고 중단하세요.`;
|
|
17
|
+
|
|
18
|
+
const vars = {
|
|
19
|
+
PROJECT_DOC: projectDocPath,
|
|
20
|
+
ARGS: '사용자가 이 스킬을 호출할 때 함께 입력한 내용',
|
|
21
|
+
DELEGATE_HINT: '(멀티 에이전트 기능으로 해당 이름의 서브에이전트를 spawn 합니다.)',
|
|
22
|
+
DELEGATE_HOWTO,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function tomlLiteral(body, role) {
|
|
26
|
+
if (body.includes("'''")) {
|
|
27
|
+
throw new Error(`${role} 본문에 ''' 가 있어 TOML 리터럴 문자열로 변환할 수 없습니다.`);
|
|
28
|
+
}
|
|
29
|
+
return `'''\n${body}\n'''`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function apply({ writer, cwd, home, useGlobal }) {
|
|
33
|
+
const notes = [];
|
|
34
|
+
|
|
35
|
+
writer.write(
|
|
36
|
+
join(cwd, projectDocPath),
|
|
37
|
+
projectDoc({ withBaseRules: !useGlobal, globalPath: globalDocPath })
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
for (const role of ROLES) {
|
|
41
|
+
const { meta, body } = loadRole(role, vars);
|
|
42
|
+
const toml = [
|
|
43
|
+
MODEL_COMMENT,
|
|
44
|
+
`name = ${quoted(meta.name)}`,
|
|
45
|
+
`description = ${quoted(meta.description)}`,
|
|
46
|
+
`developer_instructions = ${tomlLiteral(body, role)}`,
|
|
47
|
+
].join('\n');
|
|
48
|
+
writer.write(join(cwd, '.codex', 'agents', `${role}.toml`), `${toml}\n`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const command of COMMANDS) {
|
|
52
|
+
const { meta, body } = loadCommand(command, vars);
|
|
53
|
+
const head = frontmatter([
|
|
54
|
+
['name', meta.name],
|
|
55
|
+
['description', quoted(meta.description)],
|
|
56
|
+
]);
|
|
57
|
+
writer.write(join(cwd, '.codex', 'skills', command, 'SKILL.md'), `${head}\n${body}\n`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (useGlobal) {
|
|
61
|
+
const status = writer.write(
|
|
62
|
+
join(home, '.codex', 'AGENTS.md'),
|
|
63
|
+
`# 공통 행동 지침\n\n${baseRules()}\n`
|
|
64
|
+
);
|
|
65
|
+
if (status === 'skipped') {
|
|
66
|
+
notes.push(
|
|
67
|
+
`${globalDocPath}가 이미 있어 건너뛰었습니다. 공통 행동 지침이 전역에 없을 수 있으니 ${projectDocPath} §9를 확인하세요.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
notes.push(
|
|
72
|
+
`공통 행동 지침을 전역(${globalDocPath})에도 설치하려면 \`--global\`을 붙여 다시 실행하세요.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
notes.push('`$pm` 스킬로 워크플로우를 시작합니다. (예: `$pm PROJ-582 로그인 API 구현`)');
|
|
77
|
+
notes.push(
|
|
78
|
+
'`.codex/config.toml`은 수정하지 않았습니다. 멀티 에이전트가 꺼져 있으면 `[agents] enabled = true` 를 직접 확인하세요.'
|
|
79
|
+
);
|
|
80
|
+
return notes;
|
|
81
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { COMMANDS, ROLES, baseRules, loadCommand, loadRole, readPreset } from '../lib/preset.js';
|
|
3
|
+
import { frontmatter, quoted } from '../lib/write.js';
|
|
4
|
+
|
|
5
|
+
export const label = 'Cursor';
|
|
6
|
+
export const projectDocPath = '.cursor/rules/10-project.mdc';
|
|
7
|
+
export const globalDocPath = null; // Cursor 전역 규칙은 파일이 아니라 Settings > Rules > User Rules
|
|
8
|
+
|
|
9
|
+
// qa는 코드 수정이 금지되므로 readonly. planner/engineer는 works/ 및 소스에 써야 한다.
|
|
10
|
+
const READONLY = { planner: false, engineer: false, qa: true };
|
|
11
|
+
|
|
12
|
+
const MODEL_COMMENT =
|
|
13
|
+
'# model: 기본값 inherit(부모 세션 모델 상속). 특정 모델 ID로 변경할 수 있습니다.';
|
|
14
|
+
|
|
15
|
+
const DELEGATE_HOWTO = `\`.cursor/agents/\`에 정의된 \`planner\` / \`engineer\` / \`qa\` 서브에이전트에게 위임합니다. 이름을 명시해 호출하세요.
|
|
16
|
+
|
|
17
|
+
- 서브에이전트는 이 대화의 컨텍스트를 **보지 못합니다.** task_id, 파일 경로, 회차를 프롬프트에 반드시 포함하세요.
|
|
18
|
+
- 한 번에 하나씩 순서대로 위임합니다 (planner → engineer → qa). 각 단계가 앞 단계 산출물에 의존하므로 병렬 실행하지 마세요.`;
|
|
19
|
+
|
|
20
|
+
const vars = {
|
|
21
|
+
PROJECT_DOC: projectDocPath,
|
|
22
|
+
ARGS: '사용자가 이 커맨드와 함께 입력한 내용',
|
|
23
|
+
DELEGATE_HINT: '(서브에이전트 이름을 명시해 위임합니다.)',
|
|
24
|
+
DELEGATE_HOWTO,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function apply({ writer, cwd, useGlobal }) {
|
|
28
|
+
const notes = [];
|
|
29
|
+
|
|
30
|
+
writer.write(
|
|
31
|
+
join(cwd, '.cursor', 'rules', '00-base.mdc'),
|
|
32
|
+
`${frontmatter([
|
|
33
|
+
['description', quoted('공통 개발 행동 지침')],
|
|
34
|
+
['alwaysApply', 'true'],
|
|
35
|
+
])}\n# 공통 행동 지침\n\n${baseRules()}\n`
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
writer.write(
|
|
39
|
+
join(cwd, '.cursor', 'rules', '10-project.mdc'),
|
|
40
|
+
`${frontmatter([
|
|
41
|
+
['description', quoted('프로젝트 개요 · 기술 스택 · 검증 명령 · 산출물 규약')],
|
|
42
|
+
['alwaysApply', 'true'],
|
|
43
|
+
])}\n${readPreset('project-doc.md').trim()}\n`
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
for (const role of ROLES) {
|
|
47
|
+
const { meta, body } = loadRole(role, vars);
|
|
48
|
+
const head = frontmatter([
|
|
49
|
+
['name', meta.name],
|
|
50
|
+
['description', quoted(meta.description)],
|
|
51
|
+
MODEL_COMMENT,
|
|
52
|
+
['model', 'inherit'],
|
|
53
|
+
['readonly', String(READONLY[role])],
|
|
54
|
+
]);
|
|
55
|
+
writer.write(join(cwd, '.cursor', 'agents', `${role}.md`), `${head}\n${body}\n`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const command of COMMANDS) {
|
|
59
|
+
const { body } = loadCommand(command, vars);
|
|
60
|
+
writer.write(join(cwd, '.cursor', 'commands', `${command}.md`), `${body}\n`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (useGlobal) {
|
|
64
|
+
notes.push(
|
|
65
|
+
'Cursor는 전역 규칙을 파일로 두지 않습니다(Settings > Rules > User Rules). `--global`은 무시했습니다.'
|
|
66
|
+
);
|
|
67
|
+
notes.push(
|
|
68
|
+
'전역으로 쓰려면 `.cursor/rules/00-base.mdc`의 frontmatter 아래 본문을 User Rules에 붙여넣으세요.'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
notes.push('`/pm <task_id> <작업 지시>` 로 워크플로우를 시작합니다.');
|
|
73
|
+
return notes;
|
|
74
|
+
}
|