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
27 changes: 27 additions & 0 deletions .claude/skills/agent-shared-context/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@ node tools/agent-context-validate.mjs
node tools/benchmark.mjs # synthetic 5/50/500, writes BENCHMARK.md
```

## Commands (슬래시 별칭 → 단일 Bash 호출)

스킬 커맨드는 `node tools/ac.mjs` 디스패처 하나로 통합 — 서브에이전트 불필요, 메인 에이전트가 직접 실행.

| 슬래시 별칭 | 실제 명령 | 동작 |
|---|---|---|
| `/ac-export` | `node tools/ac.mjs export --session S --task "..." --done "a;b" --next "c"` | 세션 내보내기 (핸드오프 저장 + CURRENT.md 갱신) |
| `/ac-import` | `node tools/ac.mjs import [file]` | 세션 불러오기 (복원 브리프 ~280 tok) |
| `/ac-current` | `node tools/ac.mjs current` | 현재 포인터 보기 |
| `/ac-history` | `node tools/ac.mjs history "query" --limit 3` | 히스토리·지식 검색 (계층, 0 LLM) |
| `/ac-issue` | `node tools/ac.mjs issue --title "..." [--feature F] [--refs "p1,p2"]` | 이슈 작성 |
| `/ac-learning` | `node tools/ac.mjs learning --title "..." --cause C --fix F2 --lesson L` | 교훈 기록 |
| `/ac-idea` | `node tools/ac.mjs idea --title "..."` | 아이디어 기록 |
| `/ac-note` / `/ac-todo` / `/ac-decision` | `node tools/ac.mjs note|todo|decision --title "..."` | 기타 타입 기록 |

## 결과 중심 기록 (원칙)

기록에는 **도구 호출 로그를 남기지 않는다**. 도구 사용 과정·출력 전문은 토큰 낭비:

```
❌ "Grep으로 검색하고 Read로 3개 읽었더니..."
✅ "JWT race → 전역 mutex 해결. 검증: src/auth/refresh.ts:42"
```

- 결론 + `refs`(검증 링크)만 저장. 다음 에이전트는 결론을 쓰거나 refs로 직접 확인
- 버그는 `repro`에 재현 레시피만 (이것도 과정 로그가 아니라 레시피)

## References

- Concepts from `Coral-Protocol/AgentRadio` (Apache 2.0) and contemporary session collaboration patterns — file-based adaptation. See `docs/radio.md` `docs/sessions.md` `docs/hierarchy.md` `REFERENCES.md`.
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ cp -r skills/agent-shared-context ~/.codex/skills/
- **`skills/agent-shared-context/SKILL.md`** — OpenCode/Codex generic skill (same, standard frontmatter)
- **네이티브 유니버설**: 주력 `Claude Code` `Codex` `OpenCode` 모두 `Node` 네이티브 지원, 추가 언어·의존성 0. 상세는 `docs/skill.md` (또는 skill 파일 자체).

## 명령어 — 스킬 커맨드 1줄 호출

| 별칭 | 명령 |
|---|---|
| /ac-export | `node tools/ac.mjs export --session S --task "..." --next "..."` — 세션 내보내기 |
| /ac-import | `node tools/ac.mjs import` — 세션 불러오기 (~280 tok) |
| /ac-history | `node tools/ac.mjs history "query"` — 계층 검색 (0 LLM) |
| /ac-issue / /ac-learning / /ac-idea ... | `node tools/ac.mjs <type> --title "..." [--refs "p1,p2"]` |

**결과 중심 기록 원칙**: 도구 호출 로그 저장 금지 — 결론 + refs(검증 링크)만. 토큰 낭비 제거.

## 검증

```bash
Expand Down
15 changes: 14 additions & 1 deletion agent-context/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@
},
"level": {
"type": "string",
"enum": ["post-it", "memo", "diary", "bookshelf", "library"],
"enum": [
"post-it",
"memo",
"diary",
"bookshelf",
"library"
],
"description": "계층 — post-it(10tok, L1 cache) | memo(50tok, HBM) | diary(200tok, DRAM) | bookshelf(1000tok, SSD) | library(5000tok, cold) — 검색엔진(&AI) 계층, 가벼운 AI가 능동 배정"
},
"title": {
Expand Down Expand Up @@ -161,6 +167,13 @@
}
}
}
},
"refs": {
"type": "array",
"items": {
"type": "string"
},
"description": "결과 중심 기록 — 검증 링크(문서·코드 경로). 도구 실행 로그 대신 결론+링크만 저장"
}
}
}
15 changes: 15 additions & 0 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,18 @@ Glob pattern="agent-context/diary/2026-08-*.md"

- `tools/agent-context-index.mjs --check` 로 drift 감지 (CI에서 필수)
- `agent-context/README.md` 동일한 3단계

## 결과 중심 기록 (Outcome-based logging)

기록에는 **도구 호출 로그를 남기지 않는다**. 도구 사용 여부·실행 과정·출력 전문은 토큰 낭비다. 대신:

```
❌ 나쁜 예: "Grep으로 검색했고, Read로 3개 파일을 읽었고, 벤치마크를 돌렸더니..."
✅ 좋은 예: "JWT race가 원인 → 전역 mutex로 해결. 검증: src/auth/refresh.ts:42"
```

- frontmatter의 `refs` 필드(또는 본문 하단 **검증 링크**)에 확인 경로만 남긴다
- 다음 에이전트는 결론을 믿거나, 의심되면 refs로 직접 확인한다 — 중간 과정은 필요 없음
- 단, **재현 가능성**이 필요한 버그는 `repro`(재현 절차)에 최소 정보를 담는다 — 이것도 과정 로그가 아니라 재현 레시피다

명령어: `node tools/ac.mjs learning --title "..." --cause C --fix F --lesson L --refs "p1,p2"` 가 이 형식을 강제한다.
20 changes: 20 additions & 0 deletions skills/agent-shared-context/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ node tools/agent-search-lite.mjs "<query>"

**서브에이전트 불필요**: 모든 도구는 단일 Bash 호출. 메인 에이전트가 직접 검색, Node 없으면 Grep 폴백(`^level: post-it` → `^priority: [45]`). See `docs/session-continuity.md`.

## Commands (슬래시 별칭 → 단일 Bash 호출)

| 별칭 | 명령 | 동작 |
|---|---|---|
| `/ac-export` | `node tools/ac.mjs export --session S --task "..." --next "..."` | 세션 내보내기 |
| `/ac-import` | `node tools/ac.mjs import [file]` | 세션 불러오기 |
| `/ac-history` | `node tools/ac.mjs history "query"` | 히스토리 검색 (계층, 0 LLM) |
| `/ac-issue` | `node tools/ac.mjs issue --title "..." [--refs "p1,p2"]` | 이슈 작성 |
| `/ac-learning` | `node tools/ac.mjs learning --title "..." --cause C --fix F --lesson L` | 교훈 기록 |
| `/ac-idea` / `/ac-note` / `/ac-todo` / `/ac-decision` | `node tools/ac.mjs <type> --title "..."` | 기타 기록 |

## 결과 중심 기록 (원칙)

도구 호출 로그를 남기지 않는다 — **결론 + `refs`(검증 링크)**만 저장:

```
❌ "Grep으로 검색하고 Read로 3개 읽었더니..."
✅ "JWT race → 전역 mutex 해결. 검증: src/auth/refresh.ts:42"
```

## References

- Concepts from `Coral-Protocol/AgentRadio` (Apache 2.0) and contemporary session collaboration patterns — file-based adaptation. See `docs/radio.md` `docs/sessions.md` `docs/hierarchy.md` `REFERENCES.md`.
Expand Down
167 changes: 167 additions & 0 deletions tools/ac.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env node
// Path: tools/ac.mjs
// Single dispatcher for all agent-shared-context commands (skill command layer).
// Slash-style commands in SKILL.md map 1:1 to these subcommands — one Bash call.
//
// Outcome-based logging principle: entries record CONCLUSIONS + pointers,
// not tool-call transcripts. "used tool X" is noise; "result was Y, verify at Z"
// is signal. refs[] holds verification links.
import { spawnSync } from 'node:child_process';
import { existsSync, readdirSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';

const TOOLS = new URL('.', import.meta.url).pathname;
const args = process.argv.slice(2);
const cmd = args[0];
const rest = args.slice(1);

function run(tool, passthrough) {
const r = spawnSync(process.execPath, [join(TOOLS, tool), ...passthrough], { stdio: 'inherit' });
process.exit(r.status ?? 0);
}
function getFlag(name) {
const i = rest.indexOf(name);
return i !== -1 ? rest[i + 1] : null;
}
function csvFlag(name) {
const v = getFlag(name);
return v ? v.split(',').map(s => s.trim()).filter(Boolean) : [];
}
function today() { return new Date().toISOString().slice(0, 10); }
function slug(s) {
return String(s).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'entry';
}
function resolveRoot() {
const cwd = process.cwd();
if (existsSync(join(cwd, 'agent-context.config.json')) || existsSync(join(cwd, 'agent-context')))
return join(cwd, 'agent-context');
return new URL('../agent-context', import.meta.url).pathname; // source-repo fallback
}
function pluralDir(type) {
const map = { issue: 'bugs', bug: 'bugs', learning: 'learnings', idea: 'ideas', note: 'notes',
decision: 'decisions', diary: 'diary', todo: 'todos', 'code-history': 'code-history',
memo: 'notes', 'work-history': 'code-history', 'overall-flow': 'notes' };
return map[type] || 'notes';
}
function nextId(type) {
return `${type}-${today().replace(/-/g, '')}-${Math.random().toString(16).slice(2, 10)}`;
}

// Generic entry creator — outcome-based template with refs field
function createEntry(opts) {
const { type, title, feature = 'global', agent = 'system', status = 'done',
priority = 3, summary, body = '', refs = [] } = opts;
const root = resolveRoot();
const dir = pluralDir(type);
const targetDir = join(root, dir);
mkdirSync(targetDir, { recursive: true });
let fname;
if (type === 'decision') {
let n = 1;
try { n = readdirSync(targetDir).filter(f => /^\d{4}-/.test(f)).length + 1; } catch {}
fname = `${String(n).padStart(4, '0')}-${slug(title)}.md`;
} else if (type === 'diary') {
fname = `${today()}.md`;
} else {
fname = `${today()}-${slug(title)}--${agent}.md`;
}
const path = join(targetDir, fname);
if (existsSync(path) && type === 'diary') {
// diary append-only: append a section instead of failing
const prev = readdirSync(targetDir).includes(fname)
? require('fs').readFileSync(path, 'utf8') : '';
writeFileSync(path, prev + `\n## ${new Date().toTimeString().slice(0,5)} ${agent} — ${title}\n- ${summary}\n`, 'utf8');
} else {
const lines = [
`<!-- Path: agent-context/${dir}/${fname} -->`,
'---',
`id: ${nextId(type)}`,
`type: ${type}`,
`title: "${String(title).slice(0, 80)}"`,
`tags: [${type}]`,
`feature: ${feature}`,
`level: ""`,
`scope: global`,
`agent: ${agent}`,
`created: ${new Date().toISOString()}`,
`updated: ${new Date().toISOString()}`,
`status: ${status}`,
`priority: ${priority}`,
`summary: "${String(summary || title).slice(0, 180)}"`,
];
if (refs.length) {
lines.push('refs:');
refs.forEach(r => lines.push(` - "${r}"`));
}
lines.push('---', '', body || '## 결과\n\n(도구 호출 로그 아님 — 결론만 기록. 검증은 refs 링크로)\n');
writeFileSync(path, lines.join('\n') + '\n', 'utf8');
}
// regenerate index so level auto-assign runs
spawnSync(process.execPath, [join(TOOLS, 'agent-context-index.mjs')], { stdio: 'inherit' });
console.log(`created: ${path}`);
}

const HELP = `Usage: node tools/ac.mjs <command> [args]

세션 명령:
export --session NAME --task "..." [--done "a;b"] [--next "..."] 세션 내보내기
import [file] 세션 불러오기
current 현재 포인터

기록 명령 (결과 중심 — 도구 로그 아님):
issue --title "..." [--feature F] [--agent A] [--summary "..."] [--refs a,b]
learning --title "..." [--cause C] [--fix F2] [--lesson L] [동일 옵션]
idea --title "..." [--summary "..."]
note --title "..." [--summary "..."]
todo --title "..." [--status open]
decision --title "..." [--summary "..."]
history "query" [--limit N] [--level L]

옵션 공통: --feature F --agent A(claude|codex|opencode|human|system) --priority N --refs "p1,p2"

원칙: 기록에는 '결론 + 확인 링크(refs)'만 남긴다. 도구 실행 과정·출력 전문은 저장하지 않는다.`;

switch (cmd) {
case 'export': run('agent-handoff.mjs', ['save', ...rest]); break;
case 'import': run('agent-handoff.mjs', ['load', ...rest]); break;
case 'current': run('agent-handoff.mjs', ['current']); break;
case 'history':
case 'search': run('agent-search-lite.mjs', rest); break;
case 'index': run('agent-context-index.mjs', rest); break;
case 'validate':run('agent-context-validate.mjs', rest); break;
case 'issue':
case 'learning':
case 'idea':
case 'note':
case 'todo':
case 'decision': {
const title = getFlag('--title');
if (!title) { console.error(`${cmd} requires --title "..."`); process.exit(1); }
createEntry({
type: cmd === 'issue' ? 'issue' : cmd,
title,
feature: getFlag('--feature') || 'global',
agent: getFlag('--agent') || 'system',
status: cmd === 'todo' ? (getFlag('--status') || 'open')
: cmd === 'idea' ? (getFlag('--status') || 'proposed')
: (getFlag('--status') || 'done'),
priority: Number(getFlag('--priority') || (cmd === 'issue' ? 4 : 3)),
summary: getFlag('--summary'),
refs: csvFlag('--refs'),
body: [
cmd === 'learning' && getFlag('--cause') ? `## 원인\n${getFlag('--cause')}` : null,
cmd === 'learning' && getFlag('--fix') ? `## 해결\n${getFlag('--fix')}` : null,
cmd === 'learning' && getFlag('--lesson') ? `## 교훈\n${getFlag('--lesson')}` : null,
getFlag('--body') ? `## 결과\n${getFlag('--body')}` : null,
refsNote(csvFlag('--refs')),
].filter(Boolean).join('\n\n'),
});
break;
}
case '--help': case '-h': default:
console.log(HELP);
}
function refsNote(refs) {
if (!refs.length) return '';
return `---\n**검증 링크**: ${refs.map(r => `\`${r}\``).join(' · ')}`;
}
Loading