persona-harness 0.3.0-alpha.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 (176) hide show
  1. package/.persona/harness.jsonc +9 -0
  2. package/.persona/rules/backend/gradle-bootstrap.md +27 -0
  3. package/.persona/rules/backend/java-backend-bootstrap.md +28 -0
  4. package/.persona/rules/backend/java-common.md +22 -0
  5. package/.persona/rules/backend/layered-architecture.md +19 -0
  6. package/.persona/rules/backend/package-structure.md +21 -0
  7. package/.persona/rules/backend/spring-controller.md +19 -0
  8. package/.persona/rules/backend/spring-dto.md +21 -0
  9. package/.persona/rules/backend/spring-entity.md +22 -0
  10. package/.persona/rules/backend/spring-repository.md +22 -0
  11. package/.persona/rules/backend/spring-service.md +23 -0
  12. package/.persona/rules/backend/spring-test.md +32 -0
  13. package/.persona/rules/backend/step1-api-contract.md +29 -0
  14. package/.persona/rules/backend/step2-3-api-contract.md +24 -0
  15. package/.persona/rules/backend/validation-exception.md +18 -0
  16. package/.persona/rules/clean-code/abstraction.md +18 -0
  17. package/.persona/rules/clean-code/common.md +21 -0
  18. package/.persona/rules/clean-code/method-design.md +20 -0
  19. package/.persona/rules/clean-code/naming.md +19 -0
  20. package/.persona/rules/clean-code/oop.md +18 -0
  21. package/.persona/rules/clean-code/testability.md +19 -0
  22. package/CHANGELOG.md +40 -0
  23. package/LICENSE +201 -0
  24. package/README.ja.md +95 -0
  25. package/README.ko.md +95 -0
  26. package/README.md +125 -0
  27. package/README.zh-cn.md +95 -0
  28. package/dist/cli/bearshell.d.ts +12 -0
  29. package/dist/cli/bearshell.js +189 -0
  30. package/dist/cli/bearshell.js.map +1 -0
  31. package/dist/cli/history.d.ts +15 -0
  32. package/dist/cli/history.js +156 -0
  33. package/dist/cli/history.js.map +1 -0
  34. package/dist/cli/index.d.ts +10 -0
  35. package/dist/cli/index.js +117 -0
  36. package/dist/cli/index.js.map +1 -0
  37. package/dist/cli/init.d.ts +21 -0
  38. package/dist/cli/init.js +194 -0
  39. package/dist/cli/init.js.map +1 -0
  40. package/dist/cli/intake-profile.d.ts +52 -0
  41. package/dist/cli/intake-profile.js +167 -0
  42. package/dist/cli/intake-profile.js.map +1 -0
  43. package/dist/cli/intake.d.ts +14 -0
  44. package/dist/cli/intake.js +209 -0
  45. package/dist/cli/intake.js.map +1 -0
  46. package/dist/cli/plan-command.d.ts +4 -0
  47. package/dist/cli/plan-command.js +130 -0
  48. package/dist/cli/plan-command.js.map +1 -0
  49. package/dist/cli/plan-status.d.ts +11 -0
  50. package/dist/cli/plan-status.js +41 -0
  51. package/dist/cli/plan-status.js.map +1 -0
  52. package/dist/cli/plan.d.ts +10 -0
  53. package/dist/cli/plan.js +199 -0
  54. package/dist/cli/plan.js.map +1 -0
  55. package/dist/cli/policy.d.ts +7 -0
  56. package/dist/cli/policy.js +139 -0
  57. package/dist/cli/policy.js.map +1 -0
  58. package/dist/cli/report-status.d.ts +13 -0
  59. package/dist/cli/report-status.js +40 -0
  60. package/dist/cli/report-status.js.map +1 -0
  61. package/dist/index.d.ts +5 -0
  62. package/dist/index.js +9 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/phase0/drift-detector.d.ts +24 -0
  65. package/dist/phase0/drift-detector.js +334 -0
  66. package/dist/phase0/drift-detector.js.map +1 -0
  67. package/dist/phase0/evidence.d.ts +9 -0
  68. package/dist/phase0/evidence.js +36 -0
  69. package/dist/phase0/evidence.js.map +1 -0
  70. package/dist/phase0/file-role.d.ts +5 -0
  71. package/dist/phase0/file-role.js +55 -0
  72. package/dist/phase0/file-role.js.map +1 -0
  73. package/dist/phase0/harness-config.d.ts +12 -0
  74. package/dist/phase0/harness-config.js +115 -0
  75. package/dist/phase0/harness-config.js.map +1 -0
  76. package/dist/phase0/hooks.d.ts +8 -0
  77. package/dist/phase0/hooks.js +127 -0
  78. package/dist/phase0/hooks.js.map +1 -0
  79. package/dist/phase0/injection.d.ts +2 -0
  80. package/dist/phase0/injection.js +77 -0
  81. package/dist/phase0/injection.js.map +1 -0
  82. package/dist/phase0/java-role-discovery.d.ts +6 -0
  83. package/dist/phase0/java-role-discovery.js +90 -0
  84. package/dist/phase0/java-role-discovery.js.map +1 -0
  85. package/dist/phase0/messages.d.ts +2 -0
  86. package/dist/phase0/messages.js +30 -0
  87. package/dist/phase0/messages.js.map +1 -0
  88. package/dist/phase0/policy-overlay.d.ts +12 -0
  89. package/dist/phase0/policy-overlay.js +171 -0
  90. package/dist/phase0/policy-overlay.js.map +1 -0
  91. package/dist/phase0/project-profile.d.ts +1 -0
  92. package/dist/phase0/project-profile.js +148 -0
  93. package/dist/phase0/project-profile.js.map +1 -0
  94. package/dist/phase0/rule-catalog.d.ts +14 -0
  95. package/dist/phase0/rule-catalog.js +77 -0
  96. package/dist/phase0/rule-catalog.js.map +1 -0
  97. package/dist/phase0/rule-diagnostics-report.d.ts +17 -0
  98. package/dist/phase0/rule-diagnostics-report.js +51 -0
  99. package/dist/phase0/rule-diagnostics-report.js.map +1 -0
  100. package/dist/phase0/rule-frontmatter-diagnostics.d.ts +19 -0
  101. package/dist/phase0/rule-frontmatter-diagnostics.js +65 -0
  102. package/dist/phase0/rule-frontmatter-diagnostics.js.map +1 -0
  103. package/dist/phase0/rule-frontmatter.d.ts +23 -0
  104. package/dist/phase0/rule-frontmatter.js +172 -0
  105. package/dist/phase0/rule-frontmatter.js.map +1 -0
  106. package/dist/phase0/rule-glob.d.ts +2 -0
  107. package/dist/phase0/rule-glob.js +38 -0
  108. package/dist/phase0/rule-glob.js.map +1 -0
  109. package/dist/phase0/rule-loader.d.ts +13 -0
  110. package/dist/phase0/rule-loader.js +143 -0
  111. package/dist/phase0/rule-loader.js.map +1 -0
  112. package/dist/phase0/shared-skill-router.d.ts +14 -0
  113. package/dist/phase0/shared-skill-router.js +85 -0
  114. package/dist/phase0/shared-skill-router.js.map +1 -0
  115. package/dist/phase0/store.d.ts +6 -0
  116. package/dist/phase0/store.js +14 -0
  117. package/dist/phase0/store.js.map +1 -0
  118. package/dist/phase0/target-file.d.ts +2 -0
  119. package/dist/phase0/target-file.js +41 -0
  120. package/dist/phase0/target-file.js.map +1 -0
  121. package/dist/phase0/types.d.ts +38 -0
  122. package/dist/phase0/types.js +2 -0
  123. package/dist/phase0/types.js.map +1 -0
  124. package/dist/phase1/observer/controller-repository-observer.d.ts +17 -0
  125. package/dist/phase1/observer/controller-repository-observer.js +210 -0
  126. package/dist/phase1/observer/controller-repository-observer.js.map +1 -0
  127. package/dist/phase1/observer/controller-sql-observer.d.ts +20 -0
  128. package/dist/phase1/observer/controller-sql-observer.js +148 -0
  129. package/dist/phase1/observer/controller-sql-observer.js.map +1 -0
  130. package/dist/phase1/observer/java-source.d.ts +9 -0
  131. package/dist/phase1/observer/java-source.js +88 -0
  132. package/dist/phase1/observer/java-source.js.map +1 -0
  133. package/dist/phase1/observer/report.d.ts +52 -0
  134. package/dist/phase1/observer/report.js +173 -0
  135. package/dist/phase1/observer/report.js.map +1 -0
  136. package/dist/phase1/observer/service-storage-observer.d.ts +20 -0
  137. package/dist/phase1/observer/service-storage-observer.js +155 -0
  138. package/dist/phase1/observer/service-storage-observer.js.map +1 -0
  139. package/dist/phase1/observer/test-contract-anchor-matchers.d.ts +10 -0
  140. package/dist/phase1/observer/test-contract-anchor-matchers.js +198 -0
  141. package/dist/phase1/observer/test-contract-anchor-matchers.js.map +1 -0
  142. package/dist/phase1/observer/test-contract-observer.d.ts +21 -0
  143. package/dist/phase1/observer/test-contract-observer.js +70 -0
  144. package/dist/phase1/observer/test-contract-observer.js.map +1 -0
  145. package/docs/README.md +34 -0
  146. package/docs/current/README.md +35 -0
  147. package/docs/current/java-backend-mvp-install-guide.md +160 -0
  148. package/docs/current/persona-harness-detailed-usage.md +782 -0
  149. package/docs/current/release/README.md +6 -0
  150. package/docs/current/release/release-checklist.md +113 -0
  151. package/docs/current/release/release-notes-template.md +68 -0
  152. package/docs/current/v0.3.0-alpha-publish-readiness.md +185 -0
  153. package/docs/current/v0.3.0-external-tester-feedback-template.md +214 -0
  154. package/docs/current/v0.3.0-external-tester-guide.md +217 -0
  155. package/docs/current/v0.3.0-gradle-spring-build-guidance.md +100 -0
  156. package/package.json +87 -0
  157. package/packages/shared-skills/skills/programming/SKILL.md +490 -0
  158. package/packages/shared-skills/skills/programming/references/java/README.md +193 -0
  159. package/packages/shared-skills/skills/programming/references/java/anti-patterns.md +85 -0
  160. package/packages/shared-skills/skills/programming/references/java/application-layer.md +200 -0
  161. package/packages/shared-skills/skills/programming/references/java/architecture.md +152 -0
  162. package/packages/shared-skills/skills/programming/references/java/backend.md +152 -0
  163. package/packages/shared-skills/skills/programming/references/java/code-quality-refactoring.md +146 -0
  164. package/packages/shared-skills/skills/programming/references/java/concurrency-discipline.md +98 -0
  165. package/packages/shared-skills/skills/programming/references/java/domain-model.md +214 -0
  166. package/packages/shared-skills/skills/programming/references/java/error-handling.md +179 -0
  167. package/packages/shared-skills/skills/programming/references/java/foundations.md +49 -0
  168. package/packages/shared-skills/skills/programming/references/java/java-idioms.md +108 -0
  169. package/packages/shared-skills/skills/programming/references/java/language-core.md +96 -0
  170. package/packages/shared-skills/skills/programming/references/java/method-and-naming.md +164 -0
  171. package/packages/shared-skills/skills/programming/references/java/object-collaboration.md +140 -0
  172. package/packages/shared-skills/skills/programming/references/java/performance-discipline.md +115 -0
  173. package/packages/shared-skills/skills/programming/references/java/repository-pattern.md +181 -0
  174. package/packages/shared-skills/skills/programming/references/java/technology-seams.md +92 -0
  175. package/packages/shared-skills/skills/programming/references/java/test-design.md +149 -0
  176. package/packages/shared-skills/skills/programming/references/java/testing.md +188 -0
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ class PersonaInitError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "PersonaInitError";
10
+ }
11
+ }
12
+ const OPENCODE_CONFIG_PATH = ".opencode/opencode.json";
13
+ function stripJsonComments(input) {
14
+ let output = "";
15
+ let index = 0;
16
+ let inString = false;
17
+ let escaped = false;
18
+ while (index < input.length) {
19
+ const current = input[index];
20
+ const next = input[index + 1];
21
+ if (inString) {
22
+ output += current;
23
+ if (escaped) {
24
+ escaped = false;
25
+ }
26
+ else if (current === "\\") {
27
+ escaped = true;
28
+ }
29
+ else if (current === "\"") {
30
+ inString = false;
31
+ }
32
+ index += 1;
33
+ continue;
34
+ }
35
+ if (current === "\"") {
36
+ inString = true;
37
+ output += current;
38
+ index += 1;
39
+ continue;
40
+ }
41
+ if (current === "/" && next === "/") {
42
+ while (index < input.length && input[index] !== "\n") {
43
+ index += 1;
44
+ }
45
+ continue;
46
+ }
47
+ if (current === "/" && next === "*") {
48
+ index += 2;
49
+ while (index < input.length && !(input[index] === "*" && input[index + 1] === "/")) {
50
+ index += 1;
51
+ }
52
+ index += 2;
53
+ continue;
54
+ }
55
+ output += current;
56
+ index += 1;
57
+ }
58
+ return output;
59
+ }
60
+ function isRecord(value) {
61
+ return typeof value === "object" && value !== null && !Array.isArray(value);
62
+ }
63
+ function defaultPackageRoot() {
64
+ return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
65
+ }
66
+ function readOpencodeConfig(configPath) {
67
+ if (!existsSync(configPath)) {
68
+ return {};
69
+ }
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(stripJsonComments(readFileSync(configPath, "utf8")));
73
+ }
74
+ catch (error) {
75
+ if (error instanceof SyntaxError) {
76
+ throw new PersonaInitError(`Failed to parse ${OPENCODE_CONFIG_PATH}. Fix the JSON/JSONC syntax and run init again.`);
77
+ }
78
+ throw error;
79
+ }
80
+ if (!isRecord(parsed)) {
81
+ throw new PersonaInitError(`${OPENCODE_CONFIG_PATH} must contain a JSON object.`);
82
+ }
83
+ return { ...parsed };
84
+ }
85
+ function mergePluginPath(config, pluginPath) {
86
+ const plugin = config.plugin;
87
+ const existingPlugins = typeof plugin === "string"
88
+ ? [plugin]
89
+ : Array.isArray(plugin)
90
+ ? plugin.filter((entry) => typeof entry === "string")
91
+ : [];
92
+ return {
93
+ ...config,
94
+ plugin: existingPlugins.includes(pluginPath) ? existingPlugins : [...existingPlugins, pluginPath],
95
+ };
96
+ }
97
+ function backupAmbiguousOpencodeConfig(configPath, config) {
98
+ const plugin = config.plugin;
99
+ if (plugin === undefined || typeof plugin === "string" || Array.isArray(plugin)) {
100
+ return undefined;
101
+ }
102
+ const backupPath = `${configPath}.bak`;
103
+ renameSync(configPath, backupPath);
104
+ return backupPath;
105
+ }
106
+ function relativePath(projectDir, filePath) {
107
+ const normalized = filePath.startsWith(projectDir) ? filePath.slice(projectDir.length + 1) : filePath;
108
+ return normalized.replace(/\\/g, "/");
109
+ }
110
+ function writeOpencodeConfig(projectDir, pluginPath) {
111
+ const opencodeDir = join(projectDir, ".opencode");
112
+ const configPath = join(opencodeDir, "opencode.json");
113
+ mkdirSync(opencodeDir, { recursive: true });
114
+ const config = readOpencodeConfig(configPath);
115
+ const backupPath = existsSync(configPath) ? backupAmbiguousOpencodeConfig(configPath, config) : undefined;
116
+ const merged = mergePluginPath(backupPath === undefined ? config : {}, pluginPath);
117
+ writeFileSync(configPath, `${JSON.stringify(merged, null, 2)}\n`);
118
+ return backupPath === undefined ? [] : [relativePath(projectDir, backupPath)];
119
+ }
120
+ export function initializePersonaHarness(options = {}) {
121
+ const projectDir = resolve(options.projectDir ?? process.cwd());
122
+ const packageRoot = resolve(options.packageRoot ?? defaultPackageRoot());
123
+ const personaDir = join(projectDir, ".persona");
124
+ const sourceHarnessConfig = join(packageRoot, ".persona", "harness.jsonc");
125
+ const sourceRulesDir = join(packageRoot, ".persona", "rules");
126
+ const targetHarnessConfig = join(personaDir, "harness.jsonc");
127
+ const targetRulesDir = join(personaDir, "rules");
128
+ const pluginPath = join(packageRoot, "dist", "index.js");
129
+ if (!existsSync(sourceHarnessConfig)) {
130
+ throw new PersonaInitError(`Missing template: ${sourceHarnessConfig}`);
131
+ }
132
+ if (!existsSync(sourceRulesDir)) {
133
+ throw new PersonaInitError(`Missing template: ${sourceRulesDir}`);
134
+ }
135
+ mkdirSync(personaDir, { recursive: true });
136
+ cpSync(sourceHarnessConfig, targetHarnessConfig);
137
+ cpSync(sourceRulesDir, targetRulesDir, { recursive: true });
138
+ const backups = writeOpencodeConfig(projectDir, pluginPath);
139
+ return {
140
+ projectDir,
141
+ packageRoot,
142
+ pluginPath: isAbsolute(pluginPath) ? pluginPath : resolve(pluginPath),
143
+ installed: [".persona/harness.jsonc", ".persona/rules/", ".opencode/opencode.json"],
144
+ backups,
145
+ evidenceCopied: false,
146
+ };
147
+ }
148
+ export function formatInitResult(result) {
149
+ const backupLines = result.backups.length > 0 ? ["", "Backups:", ...result.backups.map((backup) => `- ${backup}`)] : [];
150
+ return [
151
+ "Persona Harness initialized.",
152
+ "",
153
+ "Installed:",
154
+ ...result.installed.map((item) => `- ${item}`),
155
+ ...backupLines,
156
+ "",
157
+ "Next:",
158
+ 'opencode run --dir . --model openai/gpt-5.4-mini-fast "README.md를 끝까지 읽고, 요구사항 전체를 Gradle 기반 Spring 백엔드로 구현해줘."',
159
+ "",
160
+ "Scope:",
161
+ "- Java/Spring backend Clean Code injection",
162
+ "- Gradle-first backend product code shape guidance",
163
+ "",
164
+ "Not guaranteed:",
165
+ "- generated app product quality",
166
+ "- test quality",
167
+ "- rule enforcement",
168
+ "- frontend/infra/multi-domain productization",
169
+ "",
170
+ "Evidence:",
171
+ "- .persona/evidence/",
172
+ "- .persona/evidence/ was not copied from the template; it is created only when hooks run.",
173
+ ].join("\n");
174
+ }
175
+ export function runInitCommand(options = {}) {
176
+ try {
177
+ return {
178
+ status: 0,
179
+ stdout: `${formatInitResult(initializePersonaHarness(options))}\n`,
180
+ stderr: "",
181
+ };
182
+ }
183
+ catch (error) {
184
+ if (error instanceof PersonaInitError) {
185
+ return {
186
+ status: 1,
187
+ stdout: "",
188
+ stderr: `${error.message}\n`,
189
+ };
190
+ }
191
+ throw error;
192
+ }
193
+ }
194
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ export type ProjectProfileQuestion = {
2
+ readonly id: string;
3
+ readonly prompt: string;
4
+ readonly choices: readonly string[];
5
+ readonly answer: string | null;
6
+ };
7
+ export type ProjectProfile = {
8
+ readonly schema: "persona.project-profile.v1";
9
+ readonly status: "draft";
10
+ readonly scope: {
11
+ readonly role: "backend";
12
+ readonly mvp: "java-spring-clean-code";
13
+ readonly productized: false;
14
+ };
15
+ readonly defaults: {
16
+ readonly language: "java";
17
+ readonly framework: "spring";
18
+ readonly buildTool: "gradle";
19
+ readonly testPolicy: "deferred";
20
+ };
21
+ readonly questions: readonly ProjectProfileQuestion[];
22
+ readonly notes: {
23
+ readonly project: string | null;
24
+ };
25
+ readonly philosophy: {
26
+ readonly company: null;
27
+ readonly personal: null;
28
+ readonly project: null;
29
+ readonly priority: readonly [
30
+ "project-specific",
31
+ "company/team",
32
+ "personal",
33
+ "clean-code-baseline",
34
+ "framework-default"
35
+ ];
36
+ };
37
+ readonly next: readonly string[];
38
+ };
39
+ type IntakeChoice = {
40
+ readonly value: string;
41
+ readonly label: string;
42
+ };
43
+ export type IntakeQuestionDefinition = {
44
+ readonly id: string;
45
+ readonly prompt: string;
46
+ readonly choices: readonly IntakeChoice[];
47
+ readonly recommended: string;
48
+ };
49
+ export declare const PROFILE_PATH = ".persona/project-profile.jsonc";
50
+ export declare const INTAKE_QUESTIONS: readonly IntakeQuestionDefinition[];
51
+ export declare function createBackendProfile(answers?: ReadonlyMap<string, string>, projectNote?: string | null): ProjectProfile;
52
+ export {};
@@ -0,0 +1,167 @@
1
+ export const PROFILE_PATH = ".persona/project-profile.jsonc";
2
+ export const INTAKE_QUESTIONS = [
3
+ {
4
+ id: "project-context",
5
+ prompt: "이 프로젝트는 어떤 맥락에 가깝나요?",
6
+ choices: [
7
+ { value: "solo", label: "개인 프로젝트" },
8
+ { value: "team", label: "팀 프로젝트" },
9
+ { value: "company", label: "회사/조직 프로젝트" },
10
+ { value: "open-source", label: "오픈소스 프로젝트" },
11
+ { value: "learning", label: "학습 목적" },
12
+ { value: "recommend", label: "추천값 사용" },
13
+ ],
14
+ recommended: "solo",
15
+ },
16
+ {
17
+ id: "project-goal",
18
+ prompt: "이 프로젝트의 목적은 어느 쪽에 가깝나요?",
19
+ choices: [
20
+ { value: "prototype", label: "빠른 검증" },
21
+ { value: "production-service", label: "실제 운영 서비스" },
22
+ { value: "study", label: "학습/미션" },
23
+ { value: "internal-tool", label: "내부 도구" },
24
+ { value: "portfolio", label: "포트폴리오" },
25
+ { value: "recommend", label: "추천값 사용" },
26
+ ],
27
+ recommended: "production-service",
28
+ },
29
+ {
30
+ id: "project-scale",
31
+ prompt: "프로젝트 규모와 생명주기는 어느 쪽인가요?",
32
+ choices: [
33
+ { value: "throwaway", label: "버려도 되는 짧은 실험" },
34
+ { value: "small", label: "작은 서비스" },
35
+ { value: "medium", label: "중간 규모 이상으로 확장 가능" },
36
+ { value: "long-lived", label: "오래 유지보수할 서비스" },
37
+ { value: "recommend", label: "추천값 사용" },
38
+ ],
39
+ recommended: "small",
40
+ },
41
+ {
42
+ id: "application-type",
43
+ prompt: "애플리케이션 형태는 어떤 쪽인가요?",
44
+ choices: [
45
+ { value: "rest-api", label: "REST API" },
46
+ { value: "mvc-web", label: "Spring MVC 웹 애플리케이션" },
47
+ { value: "batch", label: "batch/job 중심" },
48
+ { value: "library", label: "라이브러리/모듈" },
49
+ { value: "mixed", label: "여러 형태 혼합" },
50
+ { value: "recommend", label: "추천값 사용" },
51
+ ],
52
+ recommended: "rest-api",
53
+ },
54
+ {
55
+ id: "storage",
56
+ prompt: "데이터 저장 방식은 어떤 쪽인가요?",
57
+ choices: [
58
+ { value: "none", label: "저장소 없음" },
59
+ { value: "in-memory", label: "서버 실행 중만 유지" },
60
+ { value: "file", label: "파일 기반 저장" },
61
+ { value: "database", label: "DB 사용" },
62
+ { value: "external-api", label: "외부 API/서비스가 source of truth" },
63
+ { value: "mixed", label: "여러 저장 방식 혼합" },
64
+ { value: "recommend", label: "추천값 사용" },
65
+ ],
66
+ recommended: "database",
67
+ },
68
+ {
69
+ id: "persistence-technology",
70
+ prompt: "영속성 기술은 어떤 쪽을 원하나요?",
71
+ choices: [
72
+ { value: "not-needed", label: "현재 필요 없음" },
73
+ { value: "jdbc-template", label: "JdbcTemplate" },
74
+ { value: "jpa", label: "JPA" },
75
+ { value: "mybatis", label: "MyBatis" },
76
+ { value: "custom", label: "직접 구현/특수 저장소" },
77
+ { value: "recommend", label: "추천값 사용" },
78
+ ],
79
+ recommended: "jdbc-template",
80
+ },
81
+ {
82
+ id: "migration-style",
83
+ prompt: "DB schema/migration 방식은 어떤 쪽인가요?",
84
+ choices: [
85
+ { value: "schema.sql", label: "Spring Boot schema.sql" },
86
+ { value: "flyway", label: "Flyway" },
87
+ { value: "liquibase", label: "Liquibase" },
88
+ { value: "none", label: "DB는 쓰지만 migration 도구는 안 씀" },
89
+ { value: "not-needed", label: "DB/persistence 자체가 필요 없음" },
90
+ { value: "recommend", label: "추천값 사용" },
91
+ ],
92
+ recommended: "flyway",
93
+ },
94
+ {
95
+ id: "package-style",
96
+ prompt: "패키지를 무엇 기준으로 나눌까요?",
97
+ choices: [
98
+ { value: "simple-mvc", label: "작은 프로젝트용 Controller/Service/Repository 중심" },
99
+ { value: "layer-first", label: "presentation/application/domain/infrastructure를 상위 layer로 분리" },
100
+ { value: "domain-first", label: "domain/module 아래 presentation/application/domain/infrastructure" },
101
+ { value: "feature-first", label: "feature 단위로 묶고 내부에서 필요한 계층 분리" },
102
+ { value: "recommend", label: "추천값 사용" },
103
+ ],
104
+ recommended: "domain-first",
105
+ },
106
+ {
107
+ id: "architecture-style",
108
+ prompt: "선택한 패키지 구조 안에서 내부 계층은 어떻게 나눌까요?",
109
+ choices: [
110
+ { value: "simple-layered", label: "단순 layer 구조" },
111
+ { value: "clean-architecture-light", label: "가벼운 Clean Architecture" },
112
+ { value: "hexagonal-light", label: "가벼운 port/adapter 구조" },
113
+ { value: "strict-clean-architecture", label: "엄격한 Clean Architecture" },
114
+ { value: "recommend", label: "추천값 사용" },
115
+ ],
116
+ recommended: "clean-architecture-light",
117
+ },
118
+ {
119
+ id: "boundary-strictness",
120
+ prompt: "계층/DTO/검증 경계는 어느 정도로 엄격하게 가져갈까요?",
121
+ choices: [
122
+ { value: "lightweight", label: "작은 프로젝트 기준으로 최소 경계만" },
123
+ { value: "pragmatic", label: "외부 request/response는 분리하고 내부는 필요할 때 분리" },
124
+ { value: "strict", label: "request/response, command/result, validation, layer boundary를 엄격히 분리" },
125
+ { value: "recommend", label: "추천값 사용" },
126
+ ],
127
+ recommended: "strict",
128
+ },
129
+ ];
130
+ export function createBackendProfile(answers = new Map(), projectNote = null) {
131
+ return {
132
+ schema: "persona.project-profile.v1",
133
+ status: "draft",
134
+ scope: {
135
+ role: "backend",
136
+ mvp: "java-spring-clean-code",
137
+ productized: false,
138
+ },
139
+ defaults: {
140
+ language: "java",
141
+ framework: "spring",
142
+ buildTool: "gradle",
143
+ testPolicy: "deferred",
144
+ },
145
+ questions: INTAKE_QUESTIONS.map((question) => ({
146
+ id: question.id,
147
+ prompt: question.prompt,
148
+ choices: question.choices.map((choice) => choice.value),
149
+ answer: answers.get(question.id) ?? null,
150
+ })),
151
+ notes: {
152
+ project: projectNote,
153
+ },
154
+ philosophy: {
155
+ company: null,
156
+ personal: null,
157
+ project: null,
158
+ priority: ["project-specific", "company/team", "personal", "clean-code-baseline", "framework-default"],
159
+ },
160
+ next: [
161
+ "Run npx ph plan.",
162
+ "Review .persona/workflow/plan.md before implementation.",
163
+ "Ask the agent to implement only after the plan is accepted.",
164
+ ],
165
+ };
166
+ }
167
+ //# sourceMappingURL=intake-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake-profile.js","sourceRoot":"","sources":["../../src/cli/intake-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { CliRunResult } from "./bearshell.js";
2
+ type IntakeOptions = {
3
+ readonly projectDir?: string;
4
+ };
5
+ export type InteractiveIntakeOptions = IntakeOptions & {
6
+ readonly isTty: boolean;
7
+ readonly write: (text: string) => void;
8
+ readonly readLine: (prompt: string) => Promise<string>;
9
+ };
10
+ export declare function intakeUsage(invocation?: string): string;
11
+ export declare function initializeProjectIntake(options?: IntakeOptions, force?: boolean): string;
12
+ export declare function runInteractiveIntakeCommand(args: readonly string[], options: InteractiveIntakeOptions, invocationName?: string): Promise<CliRunResult>;
13
+ export declare function runIntakeCommand(args: readonly string[], options?: IntakeOptions, invocationName?: string): CliRunResult;
14
+ export {};
@@ -0,0 +1,209 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import process from "node:process";
4
+ import { INTAKE_QUESTIONS, PROFILE_PATH, createBackendProfile } from "./intake-profile.js";
5
+ export function intakeUsage(invocation = "ph") {
6
+ return [
7
+ `Usage: ${invocation} intake [--force | --interactive]`,
8
+ "",
9
+ "Creates a draft backend project profile for the v0.3.0 intake workflow.",
10
+ "",
11
+ "Output:",
12
+ `- ${PROFILE_PATH}`,
13
+ "",
14
+ "Scope:",
15
+ "- Java/Spring backend Clean Code planning surface",
16
+ "- No rule enforcement",
17
+ "- No frontend/infra productization",
18
+ "- No generated app product-quality certification",
19
+ ].join("\n");
20
+ }
21
+ function parseIntakeArgs(args) {
22
+ let force = false;
23
+ let interactive = false;
24
+ for (const arg of args) {
25
+ if (arg === "--help" || arg === "-h") {
26
+ return { kind: "help" };
27
+ }
28
+ if (arg === "--force") {
29
+ force = true;
30
+ continue;
31
+ }
32
+ if (arg === "--interactive") {
33
+ interactive = true;
34
+ continue;
35
+ }
36
+ return { kind: "invalid", message: `Unknown option: ${arg}` };
37
+ }
38
+ return { kind: "run", force, interactive };
39
+ }
40
+ function resolveProfilePath(options) {
41
+ const projectDir = resolve(options.projectDir ?? process.cwd());
42
+ return join(projectDir, PROFILE_PATH);
43
+ }
44
+ function ensureWritableProfile(options, force) {
45
+ const profilePath = resolveProfilePath(options);
46
+ if (existsSync(profilePath) && !force) {
47
+ throw new Error(`${PROFILE_PATH} already exists. Re-run with --force to replace the draft.`);
48
+ }
49
+ return profilePath;
50
+ }
51
+ function writeProfile(profilePath, answers, projectNote) {
52
+ mkdirSync(dirname(profilePath), { recursive: true });
53
+ writeFileSync(profilePath, `${JSON.stringify(createBackendProfile(answers, projectNote), null, 2)}\n`);
54
+ }
55
+ export function initializeProjectIntake(options = {}, force = false) {
56
+ const profilePath = ensureWritableProfile(options, force);
57
+ writeProfile(profilePath, new Map(), null);
58
+ return profilePath;
59
+ }
60
+ function introText() {
61
+ return [
62
+ "Persona Harness backend project intake",
63
+ "",
64
+ "답변 방식:",
65
+ "- 번호를 입력하면 해당 선택지를 사용합니다.",
66
+ "- Enter, 추천, recommend는 추천값을 사용합니다.",
67
+ "- 미정은 undecided로 저장합니다.",
68
+ "- 완료 전 종료하면 profile을 저장하지 않습니다.",
69
+ "",
70
+ ].join("\n");
71
+ }
72
+ function shouldSkipMigration(answers) {
73
+ return answers.get("storage") === "none" || answers.get("persistence-technology") === "not-needed";
74
+ }
75
+ function questionText(index, question) {
76
+ const choices = question.choices.map((choice, choiceIndex) => `${choiceIndex + 1}) ${choice.value} - ${choice.label}`);
77
+ return [`${index}. ${question.prompt}`, ...choices].join("\n") + "\n";
78
+ }
79
+ function resolveInteractiveAnswer(input, question) {
80
+ const normalized = input.trim();
81
+ if (normalized === "" || normalized === "추천" || normalized === "recommend") {
82
+ return question.recommended;
83
+ }
84
+ if (normalized === "미정" || normalized === "undecided") {
85
+ return "undecided";
86
+ }
87
+ if (!/^\d+$/.test(normalized)) {
88
+ return undefined;
89
+ }
90
+ const choiceIndex = Number.parseInt(normalized, 10) - 1;
91
+ const selected = question.choices[choiceIndex];
92
+ if (selected === undefined) {
93
+ return undefined;
94
+ }
95
+ return selected.value === "recommend" ? question.recommended : selected.value;
96
+ }
97
+ async function askQuestion(io, index, question) {
98
+ io.write(questionText(index, question));
99
+ while (true) {
100
+ const input = await io.readLine(`선택 [추천: ${question.recommended}]: `);
101
+ const answer = resolveInteractiveAnswer(input, question);
102
+ if (answer !== undefined) {
103
+ io.write("\n");
104
+ return answer;
105
+ }
106
+ io.write("다시 입력해 주세요.\n");
107
+ }
108
+ }
109
+ async function collectInteractiveAnswers(io) {
110
+ const answers = new Map();
111
+ let displayIndex = 1;
112
+ for (const question of INTAKE_QUESTIONS) {
113
+ if (question.id === "migration-style" && shouldSkipMigration(answers)) {
114
+ answers.set("migration-style", "not-needed");
115
+ continue;
116
+ }
117
+ answers.set(question.id, await askQuestion(io, displayIndex, question));
118
+ displayIndex += 1;
119
+ }
120
+ io.write(`${displayIndex}. 추가로 agent가 계획 전에 꼭 알아야 할 프로젝트 조건이 있나요? 없으면 Enter를 누르세요.\n`);
121
+ const noteInput = await io.readLine("입력: ");
122
+ const projectNote = noteInput.trim().length > 0 ? noteInput.trim() : null;
123
+ return { answers, projectNote };
124
+ }
125
+ export async function runInteractiveIntakeCommand(args, options, invocationName = "ph") {
126
+ const parsed = parseIntakeArgs(args);
127
+ if (parsed.kind === "help") {
128
+ return { status: 0, stdout: `${intakeUsage(invocationName)}\n`, stderr: "" };
129
+ }
130
+ if (parsed.kind === "invalid") {
131
+ return { status: 1, stdout: "", stderr: `${parsed.message}\n\n${intakeUsage(invocationName)}\n` };
132
+ }
133
+ if (!parsed.interactive) {
134
+ return runIntakeCommand(args, options, invocationName);
135
+ }
136
+ if (!options.isTty) {
137
+ return { status: 1, stdout: "", stderr: "Interactive intake requires a TTY. Use `npx ph intake` to create an editable draft.\n" };
138
+ }
139
+ const profilePath = ensureWritableProfile(options, parsed.force);
140
+ options.write(introText());
141
+ let result;
142
+ try {
143
+ result = await collectInteractiveAnswers(options);
144
+ }
145
+ catch (error) {
146
+ if (error instanceof Error) {
147
+ return { status: 1, stdout: "", stderr: "Interactive intake aborted. No project profile was written.\n" };
148
+ }
149
+ throw error;
150
+ }
151
+ writeProfile(profilePath, result.answers, result.projectNote);
152
+ options.write([
153
+ "",
154
+ "Persona Harness project intake profile created.",
155
+ "",
156
+ `Profile: ${profilePath}`,
157
+ "",
158
+ "Next:",
159
+ "- Run npx ph plan.",
160
+ "- Review .persona/workflow/plan.md before implementation.",
161
+ "- Ask the agent to implement only after the plan is accepted.",
162
+ "",
163
+ ].join("\n"));
164
+ return { status: 0, stdout: "", stderr: "" };
165
+ }
166
+ export function runIntakeCommand(args, options = {}, invocationName = "ph") {
167
+ const parsed = parseIntakeArgs(args);
168
+ if (parsed.kind === "help") {
169
+ return { status: 0, stdout: `${intakeUsage(invocationName)}\n`, stderr: "" };
170
+ }
171
+ if (parsed.kind === "invalid") {
172
+ return { status: 1, stdout: "", stderr: `${parsed.message}\n\n${intakeUsage(invocationName)}\n` };
173
+ }
174
+ if (parsed.interactive) {
175
+ return {
176
+ status: 1,
177
+ stdout: "",
178
+ stderr: "Interactive intake requires a TTY. Use `npx ph intake` to create an editable draft.\n",
179
+ };
180
+ }
181
+ try {
182
+ const profilePath = initializeProjectIntake(options, parsed.force);
183
+ return {
184
+ status: 0,
185
+ stdout: [
186
+ "Persona Harness project intake draft created.",
187
+ "",
188
+ `Profile: ${profilePath}`,
189
+ "",
190
+ "Next:",
191
+ `- Fill ${PROFILE_PATH} questions[].answer values.`,
192
+ "- Ask the agent to propose an architecture/technology plan before implementation.",
193
+ "",
194
+ "Scope:",
195
+ "- Java/Spring backend Clean Code planning surface",
196
+ "- Philosophy/policy overlays are deferred to a separate future surface",
197
+ ].join("\n") + "\n",
198
+ stderr: "",
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ status: 1,
204
+ stdout: "",
205
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
206
+ };
207
+ }
208
+ }
209
+ //# sourceMappingURL=intake.js.map