@neobiotechlabs/neobiotech-dev-agent-codex 0.1.40 → 0.1.41

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/dist/index.js CHANGED
@@ -185945,7 +185945,7 @@ var xrayClientSecret = process.env.XRAY_CLIENT_SECRET || "";
185945
185945
  var xray = xrayClientId && xrayClientSecret ? new XrayClient(xrayClientId, xrayClientSecret, jiraUrl, jiraEmail, jiraToken) : null;
185946
185946
  var server = new McpServer({
185947
185947
  name: "jira-confluence",
185948
- version: "0.1.40"
185948
+ version: "0.1.41"
185949
185949
  });
185950
185950
  var ctx = { server, jira, confluence, risk, xray };
185951
185951
  registerJiraCrudTools(ctx);
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { fileURLToPath as __f2p } from "node:url";
4
+ import { dirname as __dn } from "node:path";
5
+ const require = createRequire(import.meta.url);
6
+ const __filename = __f2p(import.meta.url);
7
+ const __dirname = __dn(__filename);
8
+
9
+ // src/cli/setup-antigravity.ts
10
+ import { spawnSync } from "child_process";
11
+ import path from "path";
12
+ import { fileURLToPath } from "url";
13
+ function runSetupAntigravityCli(installer = (command, args, options) => spawnSync(command, args, options)) {
14
+ const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
15
+ const result = installer("agy", ["plugin", "install", pluginRoot], { stdio: "inherit" });
16
+ if (result.error) {
17
+ process.stderr.write(`Antigravity plugin install failed: ${result.error.message}
18
+ `);
19
+ return 1;
20
+ }
21
+ if (result.status !== 0) return result.status ?? 1;
22
+ process.stdout.write(
23
+ [
24
+ "Antigravity plugin installed: neobiotech-dev-agent",
25
+ "Required environment variables: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN",
26
+ "Restart Antigravity, then verify /mcp and /agents.",
27
+ "Run /gate-parallel-review for a full Gate review."
28
+ ].join("\n") + "\n"
29
+ );
30
+ return 0;
31
+ }
32
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
33
+ process.exitCode = runSetupAntigravityCli();
34
+ }
35
+ export {
36
+ runSetupAntigravityCli
37
+ };
38
+ //# sourceMappingURL=setup-antigravity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/setup-antigravity.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\ntype InstallerResult = {\n error?: Error;\n status: number | null;\n};\n\ntype Installer = (\n command: string,\n args: string[],\n options: { stdio: 'inherit' },\n) => InstallerResult;\n\nexport function runSetupAntigravityCli(\n installer: Installer = (command, args, options) => spawnSync(command, args, options),\n) {\n const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');\n const result = installer('agy', ['plugin', 'install', pluginRoot], { stdio: 'inherit' });\n if (result.error) {\n process.stderr.write(`Antigravity plugin install failed: ${result.error.message}\\n`);\n return 1;\n }\n if (result.status !== 0) return result.status ?? 1;\n process.stdout.write(\n [\n 'Antigravity plugin installed: neobiotech-dev-agent',\n 'Required environment variables: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN',\n 'Restart Antigravity, then verify /mcp and /agents.',\n 'Run /gate-parallel-review for a full Gate review.',\n ].join('\\n') + '\\n',\n );\n return 0;\n}\n\nif (process.argv[1] === fileURLToPath(import.meta.url)) {\n process.exitCode = runSetupAntigravityCli();\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAavB,SAAS,uBACd,YAAuB,CAAC,SAAS,MAAM,YAAY,UAAU,SAAS,MAAM,OAAO,GACnF;AACA,QAAM,aAAa,KAAK,QAAQ,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,IAAI;AAClF,QAAM,SAAS,UAAU,OAAO,CAAC,UAAU,WAAW,UAAU,GAAG,EAAE,OAAO,UAAU,CAAC;AACvF,MAAI,OAAO,OAAO;AAChB,YAAQ,OAAO,MAAM,sCAAsC,OAAO,MAAM,OAAO;AAAA,CAAI;AACnF,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,EAAG,QAAO,OAAO,UAAU;AACjD,UAAQ,OAAO;AAAA,IACb;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI,IAAI;AAAA,EACjB;AACA,SAAO;AACT;AAEA,IAAI,QAAQ,KAAK,CAAC,MAAM,cAAc,YAAY,GAAG,GAAG;AACtD,UAAQ,WAAW,uBAAuB;AAC5C;","names":[]}
@@ -11,12 +11,20 @@ import path2 from "path";
11
11
  import { fileURLToPath } from "url";
12
12
 
13
13
  // src/cli/codex-project-config.ts
14
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
14
+ import {
15
+ existsSync,
16
+ mkdirSync,
17
+ readFileSync,
18
+ readdirSync,
19
+ unlinkSync,
20
+ writeFileSync
21
+ } from "fs";
15
22
  import path from "path";
16
23
  var FALLBACK_KEY = "project_doc_fallback_filenames";
17
24
  var MAX_BYTES_KEY = "project_doc_max_bytes";
18
25
  var FALLBACK_LINE = `${FALLBACK_KEY} = ["CLAUDE.md"]`;
19
26
  var MAX_BYTES_LINE = `${MAX_BYTES_KEY} = 65536`;
27
+ var GENERATED_AGENT_MARKER = "# Generated by neobiotech-dev-agent. Do not edit directly.";
20
28
  function parseStringArray(value) {
21
29
  try {
22
30
  const parsed = JSON.parse(value);
@@ -116,7 +124,72 @@ function planCodexProjectSetup({
116
124
  };
117
125
  }
118
126
  }
119
- function setupCodexProject({ projectDir }) {
127
+ function syncCodexAgents({
128
+ projectDir,
129
+ templatesDir
130
+ }) {
131
+ const emptyReport = {
132
+ created: [],
133
+ updated: [],
134
+ unchanged: [],
135
+ removed: [],
136
+ conflicts: []
137
+ };
138
+ if (!templatesDir || !existsSync(templatesDir)) {
139
+ return {
140
+ ...emptyReport,
141
+ status: "skip",
142
+ messages: ["Codex custom agent \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC9C0 \uBABB\uD574 \uB3D9\uAE30\uD654\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4."]
143
+ };
144
+ }
145
+ const templateNames = readdirSync(templatesDir).filter((name) => name.endsWith(".toml")).sort();
146
+ const targetDir = path.join(projectDir, ".codex", "agents");
147
+ const report = {
148
+ ...emptyReport,
149
+ status: "noop",
150
+ messages: []
151
+ };
152
+ for (const name of templateNames) {
153
+ const source = readFileSync(path.join(templatesDir, name), "utf8");
154
+ const targetPath = path.join(targetDir, name);
155
+ if (!existsSync(targetPath)) {
156
+ mkdirSync(targetDir, { recursive: true });
157
+ writeFileSync(targetPath, source);
158
+ report.created.push(name);
159
+ continue;
160
+ }
161
+ const current = readFileSync(targetPath, "utf8");
162
+ if (current === source) {
163
+ report.unchanged.push(name);
164
+ } else if (current.startsWith(GENERATED_AGENT_MARKER)) {
165
+ writeFileSync(targetPath, source);
166
+ report.updated.push(name);
167
+ } else {
168
+ report.conflicts.push(name);
169
+ }
170
+ }
171
+ if (existsSync(targetDir)) {
172
+ for (const name of readdirSync(targetDir).filter((entry) => entry.endsWith(".toml")).sort()) {
173
+ if (templateNames.includes(name)) continue;
174
+ const targetPath = path.join(targetDir, name);
175
+ if (readFileSync(targetPath, "utf8").startsWith(GENERATED_AGENT_MARKER)) {
176
+ unlinkSync(targetPath);
177
+ report.removed.push(name);
178
+ }
179
+ }
180
+ }
181
+ if (report.conflicts.length > 0) report.status = "conflict";
182
+ else if (report.updated.length > 0 || report.removed.length > 0) report.status = "update";
183
+ else if (report.created.length > 0) report.status = "create";
184
+ report.messages.push(
185
+ report.status === "conflict" ? `\uC0AC\uC6A9\uC790 \uAD00\uB9AC custom agent\uC640 \uCDA9\uB3CC\uD588\uC2B5\uB2C8\uB2E4: ${report.conflicts.join(", ")}` : `Codex custom agent \uB3D9\uAE30\uD654: \uC0DD\uC131 ${report.created.length}, \uAC31\uC2E0 ${report.updated.length}, \uC720\uC9C0 ${report.unchanged.length}, \uC81C\uAC70 ${report.removed.length}`
186
+ );
187
+ return report;
188
+ }
189
+ function setupCodexProject({
190
+ projectDir,
191
+ agentTemplatesDir
192
+ }) {
120
193
  const claudePath = path.join(projectDir, "CLAUDE.md");
121
194
  const agentsPath = path.join(projectDir, "AGENTS.md");
122
195
  const configPath = path.join(projectDir, ".codex", "config.toml");
@@ -129,7 +202,10 @@ function setupCodexProject({ projectDir }) {
129
202
  mkdirSync(path.dirname(configPath), { recursive: true });
130
203
  writeFileSync(configPath, plan.configText);
131
204
  }
132
- return plan;
205
+ return {
206
+ ...plan,
207
+ agents: syncCodexAgents({ projectDir, templatesDir: agentTemplatesDir })
208
+ };
133
209
  }
134
210
 
135
211
  // src/cli/setup-codex.ts
@@ -141,10 +217,16 @@ function parseProjectPath(args) {
141
217
  }
142
218
  function runSetupCodexCli(args) {
143
219
  const projectDir = parseProjectPath(args);
144
- const result = setupCodexProject({ projectDir });
220
+ const pluginRoot = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..");
221
+ const result = setupCodexProject({
222
+ projectDir,
223
+ agentTemplatesDir: path2.join(pluginRoot, "codex-agents")
224
+ });
145
225
  const output = [
146
226
  `project instruction status: ${result.status}`,
147
227
  ...result.messages,
228
+ `custom agent status: ${result.agents?.status ?? "skip"}`,
229
+ ...result.agents?.messages ?? [],
148
230
  "Marketplace registration: codex plugin marketplace add <path-to-neobiotech-marketplace>",
149
231
  "Plugin installation: install neobiotech-dev-agent from the NeoBiotech Marketplace.",
150
232
  "Required environment variables: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN",
@@ -153,7 +235,7 @@ function runSetupCodexCli(args) {
153
235
  ];
154
236
  process.stdout.write(`${output.join("\n")}
155
237
  `);
156
- return result.status === "conflict" ? 2 : 0;
238
+ return result.status === "conflict" || result.agents?.status === "conflict" ? 2 : 0;
157
239
  }
158
240
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
159
241
  process.exitCode = runSetupCodexCli(process.argv.slice(2));
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/setup-codex.ts","../src/cli/codex-project-config.ts"],"sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { setupCodexProject } from './codex-project-config.js';\n\nfunction parseProjectPath(args: string[]) {\n const index = args.indexOf('--project');\n if (index === -1) return process.cwd();\n if (!args[index + 1]) throw new Error('--project requires a path');\n return path.resolve(args[index + 1]);\n}\n\nexport function runSetupCodexCli(args: string[]) {\n const projectDir = parseProjectPath(args);\n const result = setupCodexProject({ projectDir });\n const output = [\n `project instruction status: ${result.status}`,\n ...result.messages,\n 'Marketplace registration: codex plugin marketplace add <path-to-neobiotech-marketplace>',\n 'Plugin installation: install neobiotech-dev-agent from the NeoBiotech Marketplace.',\n 'Required environment variables: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN',\n 'Optional environment variables: XRAY_CLIENT_ID, XRAY_CLIENT_SECRET, RISK_PLUGIN_ENDPOINT, RISK_PLUGIN_AUTH_TOKEN, RISK_PLUGIN_AUTHOR',\n 'Restart Codex after Marketplace or environment changes.',\n ];\n process.stdout.write(`${output.join('\\n')}\\n`);\n return result.status === 'conflict' ? 2 : 0;\n}\n\nif (process.argv[1] === fileURLToPath(import.meta.url)) {\n process.exitCode = runSetupCodexCli(process.argv.slice(2));\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\n\nconst FALLBACK_KEY = 'project_doc_fallback_filenames';\nconst MAX_BYTES_KEY = 'project_doc_max_bytes';\nconst FALLBACK_LINE = `${FALLBACK_KEY} = [\"CLAUDE.md\"]`;\nconst MAX_BYTES_LINE = `${MAX_BYTES_KEY} = 65536`;\n\nexport interface SetupInput {\n hasClaude: boolean;\n hasAgents: boolean;\n configText: string;\n}\n\nexport interface SetupPlan {\n status: 'create' | 'update' | 'noop' | 'conflict' | 'skip';\n messages: string[];\n configText?: string;\n}\n\nfunction parseStringArray(value: string): string[] | null {\n try {\n const parsed: unknown = JSON.parse(value);\n return Array.isArray(parsed) && parsed.every((item) => typeof item === 'string')\n ? parsed\n : null;\n } catch {\n return null;\n }\n}\n\nfunction formatStringArray(values: string[]) {\n return `[${values.map((value) => JSON.stringify(value)).join(', ')}]`;\n}\n\nexport function mergeCodexConfig(configText: string): { changed: boolean; content: string } {\n const newline = configText.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const hadTrailingNewline = configText.endsWith('\\n');\n const lines = configText ? configText.replace(/\\r\\n/g, '\\n').split('\\n') : [];\n if (hadTrailingNewline) lines.pop();\n\n let inSection = false;\n let fallbackIndex = -1;\n let maxBytesIndex = -1;\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n if (/^\\s*\\[/.test(line)) inSection = true;\n if (inSection) continue;\n if (new RegExp(`^\\\\s*${FALLBACK_KEY}\\\\s*=`).test(line)) {\n if (fallbackIndex !== -1) throw new Error(`Duplicate ${FALLBACK_KEY}`);\n fallbackIndex = index;\n }\n if (new RegExp(`^\\\\s*${MAX_BYTES_KEY}\\\\s*=`).test(line)) {\n if (maxBytesIndex !== -1) throw new Error(`Duplicate ${MAX_BYTES_KEY}`);\n maxBytesIndex = index;\n }\n }\n\n if (fallbackIndex !== -1) {\n const match = lines[fallbackIndex].match(\n new RegExp(`^(\\\\s*)${FALLBACK_KEY}\\\\s*=\\\\s*(\\\\[[^#]*\\\\])(\\\\s+#.*)?$`),\n );\n if (!match) throw new Error(`Cannot safely parse ${FALLBACK_KEY}`);\n const values = parseStringArray(match[2]);\n if (!values) throw new Error(`Cannot safely parse ${FALLBACK_KEY}`);\n if (!values.includes('CLAUDE.md')) {\n lines[fallbackIndex] = `${match[1]}${FALLBACK_KEY} = ${formatStringArray([\n ...values,\n 'CLAUDE.md',\n ])}${match[3] ?? ''}`;\n }\n }\n\n if (maxBytesIndex !== -1) {\n const match = lines[maxBytesIndex].match(\n new RegExp(`^(\\\\s*)${MAX_BYTES_KEY}\\\\s*=\\\\s*(\\\\d+)(\\\\s+#.*)?$`),\n );\n if (!match) throw new Error(`Cannot safely parse ${MAX_BYTES_KEY}`);\n if (Number(match[2]) < 65536) {\n lines[maxBytesIndex] = `${match[1]}${MAX_BYTES_LINE}${match[3] ?? ''}`;\n }\n }\n\n const additions = [];\n if (fallbackIndex === -1) additions.push(FALLBACK_LINE);\n if (maxBytesIndex === -1) additions.push(MAX_BYTES_LINE);\n if (additions.length > 0) {\n if (lines.length > 0 && lines[0] !== '') additions.push('');\n lines.unshift(...additions);\n }\n\n const content = `${lines.join(newline)}${newline}`;\n const normalizedOriginal = configText\n ? `${configText\n .replace(/\\r\\n|\\r|\\n/g, '\\n')\n .replace(/\\n$/, '')\n .replace(/\\n/g, newline)}${newline}`\n : '';\n return { changed: content !== normalizedOriginal, content };\n}\n\nexport function planCodexProjectSetup({\n hasClaude,\n hasAgents,\n configText,\n}: SetupInput): SetupPlan {\n if (hasClaude && hasAgents) {\n return {\n status: 'conflict',\n messages: ['CLAUDE.md와 AGENTS.md가 동시에 존재합니다. 자동 변경하지 않습니다.'],\n };\n }\n if (hasAgents) return { status: 'skip', messages: ['기존 AGENTS.md를 유지합니다.'] };\n if (!hasClaude) {\n return { status: 'skip', messages: ['CLAUDE.md가 없어 fallback을 생성하지 않습니다.'] };\n }\n\n try {\n const merged = mergeCodexConfig(configText);\n return {\n status: configText ? (merged.changed ? 'update' : 'noop') : 'create',\n messages: [\n merged.changed\n ? 'Codex fallback 설정을 준비했습니다.'\n : 'Codex fallback 설정이 이미 최신입니다.',\n ],\n configText: merged.content,\n };\n } catch (error) {\n return {\n status: 'conflict',\n messages: [\n `기존 Codex 설정을 안전하게 병합할 수 없습니다: ${\n error instanceof Error ? error.message : String(error)\n }`,\n ],\n };\n }\n}\n\nexport function setupCodexProject({ projectDir }: { projectDir: string }): SetupPlan {\n const claudePath = path.join(projectDir, 'CLAUDE.md');\n const agentsPath = path.join(projectDir, 'AGENTS.md');\n const configPath = path.join(projectDir, '.codex', 'config.toml');\n const plan = planCodexProjectSetup({\n hasClaude: existsSync(claudePath),\n hasAgents: existsSync(agentsPath),\n configText: existsSync(configPath) ? readFileSync(configPath, 'utf8') : '',\n });\n if ((plan.status === 'create' || plan.status === 'update') && plan.configText) {\n mkdirSync(path.dirname(configPath), { recursive: true });\n writeFileSync(configPath, plan.configText);\n }\n return plan;\n}\n"],"mappings":"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","names":["path","path"]}
1
+ {"version":3,"sources":["../src/cli/setup-codex.ts","../src/cli/codex-project-config.ts"],"sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { setupCodexProject } from './codex-project-config.js';\n\nfunction parseProjectPath(args: string[]) {\n const index = args.indexOf('--project');\n if (index === -1) return process.cwd();\n if (!args[index + 1]) throw new Error('--project requires a path');\n return path.resolve(args[index + 1]);\n}\n\nexport function runSetupCodexCli(args: string[]) {\n const projectDir = parseProjectPath(args);\n const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');\n const result = setupCodexProject({\n projectDir,\n agentTemplatesDir: path.join(pluginRoot, 'codex-agents'),\n });\n const output = [\n `project instruction status: ${result.status}`,\n ...result.messages,\n `custom agent status: ${result.agents?.status ?? 'skip'}`,\n ...(result.agents?.messages ?? []),\n 'Marketplace registration: codex plugin marketplace add <path-to-neobiotech-marketplace>',\n 'Plugin installation: install neobiotech-dev-agent from the NeoBiotech Marketplace.',\n 'Required environment variables: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN',\n 'Optional environment variables: XRAY_CLIENT_ID, XRAY_CLIENT_SECRET, RISK_PLUGIN_ENDPOINT, RISK_PLUGIN_AUTH_TOKEN, RISK_PLUGIN_AUTHOR',\n 'Restart Codex after Marketplace or environment changes.',\n ];\n process.stdout.write(`${output.join('\\n')}\\n`);\n return result.status === 'conflict' || result.agents?.status === 'conflict' ? 2 : 0;\n}\n\nif (process.argv[1] === fileURLToPath(import.meta.url)) {\n process.exitCode = runSetupCodexCli(process.argv.slice(2));\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport path from 'node:path';\n\nconst FALLBACK_KEY = 'project_doc_fallback_filenames';\nconst MAX_BYTES_KEY = 'project_doc_max_bytes';\nconst FALLBACK_LINE = `${FALLBACK_KEY} = [\"CLAUDE.md\"]`;\nconst MAX_BYTES_LINE = `${MAX_BYTES_KEY} = 65536`;\nconst GENERATED_AGENT_MARKER = '# Generated by neobiotech-dev-agent. Do not edit directly.';\n\nexport interface SetupInput {\n hasClaude: boolean;\n hasAgents: boolean;\n configText: string;\n}\n\nexport interface SetupPlan {\n status: 'create' | 'update' | 'noop' | 'conflict' | 'skip';\n messages: string[];\n configText?: string;\n agents?: AgentSyncReport;\n}\n\nexport interface AgentSyncReport {\n status: 'create' | 'update' | 'noop' | 'conflict' | 'skip';\n created: string[];\n updated: string[];\n unchanged: string[];\n removed: string[];\n conflicts: string[];\n messages: string[];\n}\n\nfunction parseStringArray(value: string): string[] | null {\n try {\n const parsed: unknown = JSON.parse(value);\n return Array.isArray(parsed) && parsed.every((item) => typeof item === 'string')\n ? parsed\n : null;\n } catch {\n return null;\n }\n}\n\nfunction formatStringArray(values: string[]) {\n return `[${values.map((value) => JSON.stringify(value)).join(', ')}]`;\n}\n\nexport function mergeCodexConfig(configText: string): { changed: boolean; content: string } {\n const newline = configText.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const hadTrailingNewline = configText.endsWith('\\n');\n const lines = configText ? configText.replace(/\\r\\n/g, '\\n').split('\\n') : [];\n if (hadTrailingNewline) lines.pop();\n\n let inSection = false;\n let fallbackIndex = -1;\n let maxBytesIndex = -1;\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n if (/^\\s*\\[/.test(line)) inSection = true;\n if (inSection) continue;\n if (new RegExp(`^\\\\s*${FALLBACK_KEY}\\\\s*=`).test(line)) {\n if (fallbackIndex !== -1) throw new Error(`Duplicate ${FALLBACK_KEY}`);\n fallbackIndex = index;\n }\n if (new RegExp(`^\\\\s*${MAX_BYTES_KEY}\\\\s*=`).test(line)) {\n if (maxBytesIndex !== -1) throw new Error(`Duplicate ${MAX_BYTES_KEY}`);\n maxBytesIndex = index;\n }\n }\n\n if (fallbackIndex !== -1) {\n const match = lines[fallbackIndex].match(\n new RegExp(`^(\\\\s*)${FALLBACK_KEY}\\\\s*=\\\\s*(\\\\[[^#]*\\\\])(\\\\s+#.*)?$`),\n );\n if (!match) throw new Error(`Cannot safely parse ${FALLBACK_KEY}`);\n const values = parseStringArray(match[2]);\n if (!values) throw new Error(`Cannot safely parse ${FALLBACK_KEY}`);\n if (!values.includes('CLAUDE.md')) {\n lines[fallbackIndex] = `${match[1]}${FALLBACK_KEY} = ${formatStringArray([\n ...values,\n 'CLAUDE.md',\n ])}${match[3] ?? ''}`;\n }\n }\n\n if (maxBytesIndex !== -1) {\n const match = lines[maxBytesIndex].match(\n new RegExp(`^(\\\\s*)${MAX_BYTES_KEY}\\\\s*=\\\\s*(\\\\d+)(\\\\s+#.*)?$`),\n );\n if (!match) throw new Error(`Cannot safely parse ${MAX_BYTES_KEY}`);\n if (Number(match[2]) < 65536) {\n lines[maxBytesIndex] = `${match[1]}${MAX_BYTES_LINE}${match[3] ?? ''}`;\n }\n }\n\n const additions = [];\n if (fallbackIndex === -1) additions.push(FALLBACK_LINE);\n if (maxBytesIndex === -1) additions.push(MAX_BYTES_LINE);\n if (additions.length > 0) {\n if (lines.length > 0 && lines[0] !== '') additions.push('');\n lines.unshift(...additions);\n }\n\n const content = `${lines.join(newline)}${newline}`;\n const normalizedOriginal = configText\n ? `${configText\n .replace(/\\r\\n|\\r|\\n/g, '\\n')\n .replace(/\\n$/, '')\n .replace(/\\n/g, newline)}${newline}`\n : '';\n return { changed: content !== normalizedOriginal, content };\n}\n\nexport function planCodexProjectSetup({\n hasClaude,\n hasAgents,\n configText,\n}: SetupInput): SetupPlan {\n if (hasClaude && hasAgents) {\n return {\n status: 'conflict',\n messages: ['CLAUDE.md와 AGENTS.md가 동시에 존재합니다. 자동 변경하지 않습니다.'],\n };\n }\n if (hasAgents) return { status: 'skip', messages: ['기존 AGENTS.md를 유지합니다.'] };\n if (!hasClaude) {\n return { status: 'skip', messages: ['CLAUDE.md가 없어 fallback을 생성하지 않습니다.'] };\n }\n\n try {\n const merged = mergeCodexConfig(configText);\n return {\n status: configText ? (merged.changed ? 'update' : 'noop') : 'create',\n messages: [\n merged.changed\n ? 'Codex fallback 설정을 준비했습니다.'\n : 'Codex fallback 설정이 이미 최신입니다.',\n ],\n configText: merged.content,\n };\n } catch (error) {\n return {\n status: 'conflict',\n messages: [\n `기존 Codex 설정을 안전하게 병합할 수 없습니다: ${\n error instanceof Error ? error.message : String(error)\n }`,\n ],\n };\n }\n}\n\nexport function syncCodexAgents({\n projectDir,\n templatesDir,\n}: {\n projectDir: string;\n templatesDir?: string;\n}): AgentSyncReport {\n const emptyReport = {\n created: [],\n updated: [],\n unchanged: [],\n removed: [],\n conflicts: [],\n };\n if (!templatesDir || !existsSync(templatesDir)) {\n return {\n ...emptyReport,\n status: 'skip',\n messages: ['Codex custom agent 템플릿을 찾지 못해 동기화를 건너뜁니다.'],\n };\n }\n\n const templateNames = readdirSync(templatesDir)\n .filter((name) => name.endsWith('.toml'))\n .sort();\n const targetDir = path.join(projectDir, '.codex', 'agents');\n const report: AgentSyncReport = {\n ...emptyReport,\n status: 'noop',\n messages: [],\n };\n\n for (const name of templateNames) {\n const source = readFileSync(path.join(templatesDir, name), 'utf8');\n const targetPath = path.join(targetDir, name);\n if (!existsSync(targetPath)) {\n mkdirSync(targetDir, { recursive: true });\n writeFileSync(targetPath, source);\n report.created.push(name);\n continue;\n }\n\n const current = readFileSync(targetPath, 'utf8');\n if (current === source) {\n report.unchanged.push(name);\n } else if (current.startsWith(GENERATED_AGENT_MARKER)) {\n writeFileSync(targetPath, source);\n report.updated.push(name);\n } else {\n report.conflicts.push(name);\n }\n }\n\n if (existsSync(targetDir)) {\n for (const name of readdirSync(targetDir).filter((entry) => entry.endsWith('.toml')).sort()) {\n if (templateNames.includes(name)) continue;\n const targetPath = path.join(targetDir, name);\n if (readFileSync(targetPath, 'utf8').startsWith(GENERATED_AGENT_MARKER)) {\n unlinkSync(targetPath);\n report.removed.push(name);\n }\n }\n }\n\n if (report.conflicts.length > 0) report.status = 'conflict';\n else if (report.updated.length > 0 || report.removed.length > 0) report.status = 'update';\n else if (report.created.length > 0) report.status = 'create';\n report.messages.push(\n report.status === 'conflict'\n ? `사용자 관리 custom agent와 충돌했습니다: ${report.conflicts.join(', ')}`\n : `Codex custom agent 동기화: 생성 ${report.created.length}, 갱신 ${report.updated.length}, 유지 ${report.unchanged.length}, 제거 ${report.removed.length}`,\n );\n return report;\n}\n\nexport function setupCodexProject({\n projectDir,\n agentTemplatesDir,\n}: {\n projectDir: string;\n agentTemplatesDir?: string;\n}): SetupPlan {\n const claudePath = path.join(projectDir, 'CLAUDE.md');\n const agentsPath = path.join(projectDir, 'AGENTS.md');\n const configPath = path.join(projectDir, '.codex', 'config.toml');\n const plan = planCodexProjectSetup({\n hasClaude: existsSync(claudePath),\n hasAgents: existsSync(agentsPath),\n configText: existsSync(configPath) ? readFileSync(configPath, 'utf8') : '',\n });\n if ((plan.status === 'create' || plan.status === 'update') && plan.configText) {\n mkdirSync(path.dirname(configPath), { recursive: true });\n writeFileSync(configPath, plan.configText);\n }\n return {\n ...plan,\n agents: syncCodexAgents({ projectDir, templatesDir: agentTemplatesDir }),\n };\n}\n"],"mappings":"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","names":["path","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neobiotechlabs/neobiotech-dev-agent-codex",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "description": "Jira & Confluence MCP Server plugin for Codex",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: gate-parallel-review
3
+ description: PA/EA Gate 전체를 Codex subagent로 병렬 검토하고 hard/soft verdict를 통합한다. Gate 승인 전 다학제 규제·보안·아키텍처·문서·추적성 리뷰에 사용한다.
4
+ ---
5
+
6
+ # Gate Parallel Review
7
+
8
+ PA 또는 EA Gate 전체를 검토할 때만 사용한다. 단일 전문 분야 리뷰는 기존 `role-*` skill을 직접 사용한다.
9
+
10
+ ## 실행 원칙
11
+
12
+ 1. Gate key와 검토 범위를 먼저 확정한다.
13
+ 2. 다음 다섯 역할을 서로 독립된 subagent thread로 실행한다.
14
+ - `mdr-regulatory-architect`
15
+ - `mdr-cybersecurity-reviewer`
16
+ - `sw-architect`
17
+ - `document-content-reviewer`
18
+ - `requirement-coverage-tracker`
19
+ 3. 프로젝트에 설치된 동일 이름 custom agent를 우선 사용한다.
20
+ 4. 이름 있는 custom agent를 사용할 수 없으면 일반 subagent를 생성하고 대응하는 `role-*` skill을 명시적으로 로드한다.
21
+ 5. Jira·Confluence 부하를 제한하기 위해 동시에 최대 3개 reviewer만 실행한다.
22
+ 6. 모든 reviewer는 읽기 전용이다. 티켓 생성·수정·전이·링크·첨부·게재·삭제를 수행하지 않는다.
23
+ 7. 각 finding은 ticket key, field 또는 문서 위치, 근거 발췌를 포함해야 한다.
24
+
25
+ ## Verdict 통합
26
+
27
+ - Hard verdict: `mdr-regulatory-architect`, `mdr-cybersecurity-reviewer`, `sw-architect`
28
+ - Soft verdict: `document-content-reviewer`, `requirement-coverage-tracker`
29
+ - Hard reviewer의 critical FAIL이 하나라도 있으면 최종 상태는 `BLOCK`이다.
30
+ - Hard reviewer가 실행 실패하거나 근거 부족이면 `READY`로 판정하지 않는다.
31
+ - Soft verdict는 hard verdict를 뒤집지 않는다. 보완 작업과 잔여 불확실성을 제시한다.
32
+ - reviewer 간 결론이 충돌하면 숨기지 말고 양쪽 근거와 판단 차이를 함께 기록한다.
33
+
34
+ ## 부분 실패 처리
35
+
36
+ - 실패한 reviewer를 한 번만 재시도한다.
37
+ - 재시도 후에도 실패하면 해당 역할을 `NOT_RUN`으로 표시한다.
38
+ - Hard reviewer가 `NOT_RUN`이면 최종 상태는 `CONDITIONAL` 또는 `BLOCK`이어야 한다.
39
+
40
+ ## 최종 출력
41
+
42
+ 다음 구조로 하나의 통합 보고서를 반환한다.
43
+
44
+ 1. `Gate Parallel Review`
45
+ 2. Gate key와 검토 범위
46
+ 3. 최종 상태: `BLOCK`, `CONDITIONAL`, `READY`
47
+ 4. 역할별 실행 상태와 verdict 요약
48
+ 5. 통합 findings: severity 순서, 중복 병합, 원 역할 표시
49
+ 6. reviewer 간 충돌과 미해결 불확실성
50
+ 7. 우선순위별 다음 조치와 재검토 조건
51
+