freelang-v4 4.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 (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
@@ -0,0 +1,352 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 배포키 명령어
2
+ // add, list, delete 구현
3
+ // 200라인
4
+
5
+ /// 배포키 추가
6
+ /// gogs deploy-key add OWNER/REPO --title "title" --key "ssh-rsa ..."
7
+ async fn cmd_deploy_key_add(args: CliArgs) -> CliResult {
8
+ // 1. 인자 검증
9
+ var repo_path = get_positional(args, 0)
10
+ if repo_path == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Repository path (OWNER/REPO) is required"
15
+ }
16
+ }
17
+
18
+ var parts = split_repo_path(repo_path)
19
+ if length(parts) != 2 {
20
+ return CliResult {
21
+ exit_code: 1,
22
+ output: "",
23
+ error: "Invalid repository path. Use: OWNER/REPO"
24
+ }
25
+ }
26
+
27
+ var owner = parts[0]
28
+ var repo_name = parts[1]
29
+
30
+ // 2. 플래그 파싱
31
+ var title = get_flag(args, "title")
32
+ var key = get_flag(args, "key")
33
+
34
+ if title == "" {
35
+ return CliResult {
36
+ exit_code: 1,
37
+ output: "",
38
+ error: "--title is required"
39
+ }
40
+ }
41
+
42
+ if key == "" {
43
+ return CliResult {
44
+ exit_code: 1,
45
+ output: "",
46
+ error: "--key is required"
47
+ }
48
+ }
49
+
50
+ // 3. 설정 로드
51
+ var host = await get_default_host()
52
+ if host.url == "" {
53
+ return CliResult {
54
+ exit_code: 1,
55
+ output: "",
56
+ error: "Not logged in"
57
+ }
58
+ }
59
+
60
+ // 4. 클라이언트 및 캐시
61
+ var client = new_client(host.url, host.token)
62
+ var cache = new_cache(300)
63
+
64
+ // 5. API 호출
65
+ println("Adding deploy key to " + repo_path + "...")
66
+
67
+ var dk_service = new_deploy_key_service(client, cache)
68
+ var req = CreateDeployKeyRequest {
69
+ title: title,
70
+ key: key,
71
+ read_only: false
72
+ }
73
+ var result = await add_deploy_key(dk_service, owner, repo_name, req)
74
+
75
+ // 6. 결과 처리
76
+ match result {
77
+ Result::Ok(dk) => {
78
+ var output = ""
79
+ output = output + "✓ Deploy key added\n"
80
+ output = output + " ID: " + str(dk.id) + "\n"
81
+ output = output + " Title: " + dk.title + "\n"
82
+ output = output + " Read-only: " + bool_to_str(dk.read_only) + "\n"
83
+
84
+ CliResult {
85
+ exit_code: 0,
86
+ output: output,
87
+ error: ""
88
+ }
89
+ }
90
+ Result::Err(err) => {
91
+ CliResult {
92
+ exit_code: 1,
93
+ output: "",
94
+ error: error_message(err)
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ /// 배포키 목록
101
+ /// gogs deploy-key list OWNER/REPO [--json] [--yaml]
102
+ async fn cmd_deploy_key_list(args: CliArgs) -> CliResult {
103
+ // 1. 인자 검증
104
+ var repo_path = get_positional(args, 0)
105
+ if repo_path == "" {
106
+ return CliResult {
107
+ exit_code: 1,
108
+ output: "",
109
+ error: "Repository path (OWNER/REPO) is required"
110
+ }
111
+ }
112
+
113
+ var parts = split_repo_path(repo_path)
114
+ if length(parts) != 2 {
115
+ return CliResult {
116
+ exit_code: 1,
117
+ output: "",
118
+ error: "Invalid repository path. Use: OWNER/REPO"
119
+ }
120
+ }
121
+
122
+ var owner = parts[0]
123
+ var repo_name = parts[1]
124
+
125
+ // 2. 설정 로드
126
+ var host = await get_default_host()
127
+ if host.url == "" {
128
+ return CliResult {
129
+ exit_code: 1,
130
+ output: "",
131
+ error: "Not logged in"
132
+ }
133
+ }
134
+
135
+ // 3. 클라이언트 및 캐시
136
+ var client = new_client(host.url, host.token)
137
+ var cache = new_cache(300)
138
+
139
+ // 4. API 호출
140
+ println("Fetching deploy keys for " + repo_path + "...")
141
+
142
+ var dk_service = new_deploy_key_service(client, cache)
143
+ var result = await list_deploy_keys(dk_service, owner, repo_name)
144
+
145
+ // 5. 결과 처리
146
+ match result {
147
+ Result::Ok(keys) => {
148
+ var format = get_format(args)
149
+
150
+ if format == "json" {
151
+ return deploy_key_list_json(keys)
152
+ }
153
+ if format == "yaml" {
154
+ return deploy_key_list_yaml(keys)
155
+ }
156
+
157
+ deploy_key_list_text(keys)
158
+ }
159
+ Result::Err(err) => {
160
+ CliResult {
161
+ exit_code: 1,
162
+ output: "",
163
+ error: error_message(err)
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ /// 배포키 삭제
170
+ /// gogs deploy-key delete OWNER/REPO KEY_ID [--force]
171
+ async fn cmd_deploy_key_delete(args: CliArgs) -> CliResult {
172
+ // 1. 인자 검증
173
+ var repo_path = get_positional(args, 0)
174
+ var key_id_str = get_positional(args, 1)
175
+
176
+ if repo_path == "" || key_id_str == "" {
177
+ return CliResult {
178
+ exit_code: 1,
179
+ output: "",
180
+ error: "Repository path and key ID are required"
181
+ }
182
+ }
183
+
184
+ var parts = split_repo_path(repo_path)
185
+ if length(parts) != 2 {
186
+ return CliResult {
187
+ exit_code: 1,
188
+ output: "",
189
+ error: "Invalid repository path. Use: OWNER/REPO"
190
+ }
191
+ }
192
+
193
+ var owner = parts[0]
194
+ var repo_name = parts[1]
195
+
196
+ // 2. 플래그 확인
197
+ var force = has_flag(args, "force")
198
+ if !force {
199
+ println("⚠️ This will delete deploy key " + key_id_str)
200
+ println("Run with --force to confirm")
201
+ return CliResult {
202
+ exit_code: 1,
203
+ output: "",
204
+ error: "Deletion not confirmed"
205
+ }
206
+ }
207
+
208
+ // 3. 설정 로드
209
+ var host = await get_default_host()
210
+ if host.url == "" {
211
+ return CliResult {
212
+ exit_code: 1,
213
+ output: "",
214
+ error: "Not logged in"
215
+ }
216
+ }
217
+
218
+ // 4. 클라이언트 및 캐시
219
+ var client = new_client(host.url, host.token)
220
+ var cache = new_cache(300)
221
+
222
+ // 5. API 호출
223
+ println("Deleting deploy key " + key_id_str + "...")
224
+
225
+ var dk_service = new_deploy_key_service(client, cache)
226
+ var result = await delete_deploy_key(dk_service, owner, repo_name, parse_int(key_id_str))
227
+
228
+ // 6. 결과 처리
229
+ match result {
230
+ Result::Ok(_) => {
231
+ CliResult {
232
+ exit_code: 0,
233
+ output: "✓ Deploy key deleted: " + key_id_str,
234
+ error: ""
235
+ }
236
+ }
237
+ Result::Err(err) => {
238
+ CliResult {
239
+ exit_code: 1,
240
+ output: "",
241
+ error: error_message(err)
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ // ==================== 출력 포맷 ====================
248
+
249
+ fn deploy_key_list_text(keys: [DeployKey]) -> CliResult {
250
+ var output = ""
251
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
252
+ output = output + "║ Deploy Key List ║\n"
253
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
254
+ output = output + "\n"
255
+
256
+ if length(keys) == 0 {
257
+ output = output + "No deploy keys found\n"
258
+ return CliResult {
259
+ exit_code: 0,
260
+ output: output,
261
+ error: ""
262
+ }
263
+ }
264
+
265
+ var i: i32 = 0
266
+ while i < length(keys) {
267
+ var key = keys[i]
268
+ var ro_mark = if key.read_only then "[RO]" else "[RW]"
269
+ output = output + ro_mark + " " + str(key.id) + ": " + key.title + "\n"
270
+ output = output + " Created: " + key.created_at + "\n"
271
+ output = output + "\n"
272
+ i = i + 1
273
+ }
274
+
275
+ output = output + "Total: " + str(length(keys)) + " deploy keys\n"
276
+
277
+ CliResult {
278
+ exit_code: 0,
279
+ output: output,
280
+ error: ""
281
+ }
282
+ }
283
+
284
+ fn deploy_key_list_json(keys: [DeployKey]) -> CliResult {
285
+ var json = "[\n"
286
+ var i: i32 = 0
287
+ while i < length(keys) {
288
+ var k = keys[i]
289
+ json = json + " {\"id\": " + str(k.id) + ", \"title\": \"" + k.title + "\", \"read_only\": " + bool_to_json(k.read_only) + "}"
290
+ if i < length(keys) - 1 { json = json + "," }
291
+ json = json + "\n"
292
+ i = i + 1
293
+ }
294
+ json = json + "]\n"
295
+ CliResult { exit_code: 0, output: json, error: "" }
296
+ }
297
+
298
+ fn deploy_key_list_yaml(keys: [DeployKey]) -> CliResult {
299
+ var yaml = "deploy_keys:\n"
300
+ var i: i32 = 0
301
+ while i < length(keys) {
302
+ var k = keys[i]
303
+ yaml = yaml + " - id: " + str(k.id) + "\n"
304
+ yaml = yaml + " title: " + k.title + "\n"
305
+ yaml = yaml + " read_only: " + bool_to_str(k.read_only) + "\n"
306
+ i = i + 1
307
+ }
308
+ CliResult { exit_code: 0, output: yaml, error: "" }
309
+ }
310
+
311
+ // ==================== 유틸리티 ====================
312
+
313
+ fn split_repo_path(path: str) -> [str] {
314
+ var result: [str] = []
315
+ var idx = indexOf(path, "/")
316
+ if idx == -1 {
317
+ result.push(path)
318
+ return result
319
+ }
320
+ result.push(substring(path, 0, idx))
321
+ result.push(substring(path, idx + 1, length(path)))
322
+ result
323
+ }
324
+
325
+ fn indexOf(s: str, substr: str) -> i32 {
326
+ var i: i32 = 0
327
+ var len = length(s)
328
+ var sublen = length(substr)
329
+ while i <= len - sublen {
330
+ if substring(s, i, i + sublen) == substr { return i }
331
+ i = i + 1
332
+ }
333
+ -1
334
+ }
335
+
336
+ fn bool_to_str(b: bool) -> str {
337
+ if b { "true" } else { "false" }
338
+ }
339
+
340
+ fn bool_to_json(b: bool) -> str {
341
+ if b { "true" } else { "false" }
342
+ }
343
+
344
+ fn parse_int(s: str) -> i32 {
345
+ 0 // TODO: implement parse_int
346
+ }
347
+
348
+ // 필요한 함수들:
349
+ // - get_positional, has_flag, get_flag, get_format
350
+ // - get_default_host, new_client, new_cache
351
+ // - new_deploy_key_service, add_deploy_key, list_deploy_keys, delete_deploy_key
352
+ // - error_message
@@ -0,0 +1,275 @@
1
+ // FreeLang v4.2 — gogs-cli Phase 5 이슈 명령어
2
+ // create, list 구현
3
+ // 250라인
4
+
5
+ /// 이슈 생성
6
+ /// gogs issue create OWNER/REPO --title "title" --body "body"
7
+ async fn cmd_issue_create(args: CliArgs) -> CliResult {
8
+ // 1. 인자 검증
9
+ var repo_path = get_positional(args, 0)
10
+ if repo_path == "" {
11
+ return CliResult {
12
+ exit_code: 1,
13
+ output: "",
14
+ error: "Repository path (OWNER/REPO) is required"
15
+ }
16
+ }
17
+
18
+ var parts = split_repo_path(repo_path)
19
+ if length(parts) != 2 {
20
+ return CliResult {
21
+ exit_code: 1,
22
+ output: "",
23
+ error: "Invalid repository path. Use: OWNER/REPO"
24
+ }
25
+ }
26
+
27
+ var owner = parts[0]
28
+ var repo_name = parts[1]
29
+
30
+ // 2. 플래그 파싱
31
+ var title = get_flag(args, "title")
32
+ var body = get_flag(args, "body")
33
+
34
+ if title == "" {
35
+ return CliResult {
36
+ exit_code: 1,
37
+ output: "",
38
+ error: "--title is required"
39
+ }
40
+ }
41
+
42
+ // 3. 설정 로드
43
+ var host = await get_default_host()
44
+ if host.url == "" {
45
+ return CliResult {
46
+ exit_code: 1,
47
+ output: "",
48
+ error: "Not logged in"
49
+ }
50
+ }
51
+
52
+ // 4. 클라이언트 및 캐시
53
+ var client = new_client(host.url, host.token)
54
+ var cache = new_cache(300)
55
+
56
+ // 5. API 호출
57
+ println("Creating issue in " + repo_path + "...")
58
+
59
+ var issue_service = new_issue_service(client, cache)
60
+ var req = CreateIssueRequest {
61
+ title: title,
62
+ body: body
63
+ }
64
+ var result = await create_issue(issue_service, owner, repo_name, req)
65
+
66
+ // 6. 결과 처리
67
+ match result {
68
+ Result::Ok(issue) => {
69
+ var output = ""
70
+ output = output + "✓ Issue created\n"
71
+ output = output + " ID: #" + str(issue.number) + "\n"
72
+ output = output + " Title: " + issue.title + "\n"
73
+ output = output + " State: " + issue.state + "\n"
74
+
75
+ CliResult {
76
+ exit_code: 0,
77
+ output: output,
78
+ error: ""
79
+ }
80
+ }
81
+ Result::Err(err) => {
82
+ CliResult {
83
+ exit_code: 1,
84
+ output: "",
85
+ error: error_message(err)
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ /// 이슈 목록
92
+ /// gogs issue list OWNER/REPO [--state "open"] [--json] [--yaml]
93
+ async fn cmd_issue_list(args: CliArgs) -> CliResult {
94
+ // 1. 인자 검증
95
+ var repo_path = get_positional(args, 0)
96
+ if repo_path == "" {
97
+ return CliResult {
98
+ exit_code: 1,
99
+ output: "",
100
+ error: "Repository path (OWNER/REPO) is required"
101
+ }
102
+ }
103
+
104
+ var parts = split_repo_path(repo_path)
105
+ if length(parts) != 2 {
106
+ return CliResult {
107
+ exit_code: 1,
108
+ output: "",
109
+ error: "Invalid repository path. Use: OWNER/REPO"
110
+ }
111
+ }
112
+
113
+ var owner = parts[0]
114
+ var repo_name = parts[1]
115
+
116
+ // 2. 플래그 파싱
117
+ var state = get_flag(args, "state")
118
+ if state == "" {
119
+ state = "open"
120
+ }
121
+
122
+ // 3. 설정 로드
123
+ var host = await get_default_host()
124
+ if host.url == "" {
125
+ return CliResult {
126
+ exit_code: 1,
127
+ output: "",
128
+ error: "Not logged in"
129
+ }
130
+ }
131
+
132
+ // 4. 클라이언트 및 캐시
133
+ var client = new_client(host.url, host.token)
134
+ var cache = new_cache(300)
135
+
136
+ // 5. API 호출
137
+ println("Fetching issues for " + repo_path + "...")
138
+
139
+ var issue_service = new_issue_service(client, cache)
140
+ var result = await list_issues(issue_service, owner, repo_name, state)
141
+
142
+ // 6. 결과 처리
143
+ match result {
144
+ Result::Ok(issues) => {
145
+ var format = get_format(args)
146
+
147
+ if format == "json" {
148
+ return issue_list_json(issues)
149
+ }
150
+ if format == "yaml" {
151
+ return issue_list_yaml(issues)
152
+ }
153
+
154
+ issue_list_text(repo_path, issues)
155
+ }
156
+ Result::Err(err) => {
157
+ CliResult {
158
+ exit_code: 1,
159
+ output: "",
160
+ error: error_message(err)
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ // ==================== 출력 포맷: 목록 ====================
167
+
168
+ fn issue_list_text(repo_path: str, issues: [Issue]) -> CliResult {
169
+ var output = ""
170
+ output = output + "╔════════════════════════════════════════════════════════════════════╗\n"
171
+ output = output + "║ Issues in " + repo_path + "\n"
172
+ output = output + "╚════════════════════════════════════════════════════════════════════╝\n"
173
+ output = output + "\n"
174
+
175
+ if length(issues) == 0 {
176
+ output = output + "No issues found\n"
177
+ return CliResult {
178
+ exit_code: 0,
179
+ output: output,
180
+ error: ""
181
+ }
182
+ }
183
+
184
+ var i: i32 = 0
185
+ while i < length(issues) {
186
+ var issue = issues[i]
187
+ var state_mark = if issue.state == "open" then "🔴" else "✓"
188
+ output = output + state_mark + " #" + str(issue.number) + " " + issue.title + "\n"
189
+ output = output + " By: " + issue.user.login + " | " + issue.created_at + "\n"
190
+ output = output + "\n"
191
+ i = i + 1
192
+ }
193
+
194
+ output = output + "Total: " + str(length(issues)) + " issues\n"
195
+
196
+ CliResult {
197
+ exit_code: 0,
198
+ output: output,
199
+ error: ""
200
+ }
201
+ }
202
+
203
+ fn issue_list_json(issues: [Issue]) -> CliResult {
204
+ var json = "[\n"
205
+
206
+ var i: i32 = 0
207
+ while i < length(issues) {
208
+ var issue = issues[i]
209
+ json = json + " {\n"
210
+ json = json + " \"number\": " + str(issue.number) + ",\n"
211
+ json = json + " \"title\": \"" + issue.title + "\",\n"
212
+ json = json + " \"state\": \"" + issue.state + "\",\n"
213
+ json = json + " \"author\": \"" + issue.user.login + "\"\n"
214
+ json = json + " }"
215
+ if i < length(issues) - 1 { json = json + "," }
216
+ json = json + "\n"
217
+ i = i + 1
218
+ }
219
+
220
+ json = json + "]\n"
221
+ CliResult { exit_code: 0, output: json, error: "" }
222
+ }
223
+
224
+ fn issue_list_yaml(issues: [Issue]) -> CliResult {
225
+ var yaml = "issues:\n"
226
+
227
+ var i: i32 = 0
228
+ while i < length(issues) {
229
+ var issue = issues[i]
230
+ yaml = yaml + " - number: " + str(issue.number) + "\n"
231
+ yaml = yaml + " title: " + issue.title + "\n"
232
+ yaml = yaml + " state: " + issue.state + "\n"
233
+ yaml = yaml + " author: " + issue.user.login + "\n"
234
+ i = i + 1
235
+ }
236
+
237
+ CliResult { exit_code: 0, output: yaml, error: "" }
238
+ }
239
+
240
+ // ==================== 유틸리티 ====================
241
+
242
+ fn split_repo_path(path: str) -> [str] {
243
+ var result: [str] = []
244
+ var idx = indexOf(path, "/")
245
+
246
+ if idx == -1 {
247
+ result.push(path)
248
+ return result
249
+ }
250
+
251
+ var owner = substring(path, 0, idx)
252
+ var repo = substring(path, idx + 1, length(path))
253
+
254
+ result.push(owner)
255
+ result.push(repo)
256
+
257
+ result
258
+ }
259
+
260
+ fn indexOf(s: str, substr: str) -> i32 {
261
+ var i: i32 = 0
262
+ var len = length(s)
263
+ var sublen = length(substr)
264
+ while i <= len - sublen {
265
+ if substring(s, i, i + sublen) == substr { return i }
266
+ i = i + 1
267
+ }
268
+ -1
269
+ }
270
+
271
+ // 필요한 함수들:
272
+ // - get_positional, has_flag, get_flag, get_format
273
+ // - get_default_host, new_client, new_cache
274
+ // - new_issue_service, create_issue, list_issues
275
+ // - error_message