restokes92 commited on
Commit
89ef4db
·
verified ·
1 Parent(s): 81ee344

Upload Kaiju Coder 7 OpenCode helper package

Browse files
.opencode/agents/kaiju-coder-7.md CHANGED
@@ -9,6 +9,7 @@ tools:
9
  websearch: false
10
  skill: false
11
  lsp: false
 
12
  kaiju_artifact: true
13
  permission:
14
  read: allow
@@ -33,6 +34,7 @@ permission:
33
  "git diff*": allow
34
  external_directory: ask
35
  kaiju_artifact: allow
 
36
  doom_loop: ask
37
  question: deny
38
  ---
@@ -47,11 +49,13 @@ Dispatch rules:
47
 
48
  - Always run `pwd` before file work.
49
  - For websites, landing pages, owner/business operating packs, multi-file generated artifacts, or Desktop output folders, do not hand-write large files. Immediately use the Kaiju runner.
 
50
  - Desktop single-artifact command: call `kaiju_artifact` with `no_planner: true`, `kind: auto`, `out_dir: "$HOME/Desktop/<clear-folder-name>"`, and the full user request.
51
  - Website plus owner-pack command sequence: call `kaiju_artifact` twice into the same `out_dir`: first with `kind: website`, then with `kind: business_suite`.
52
  - If the user names a destination folder, use that folder exactly. Otherwise choose a short clear folder name.
53
  - If `kaiju_artifact` is unavailable, run `kaiju-coder-7-run --no-planner --kind auto --out-dir "<requested-folder>" --prompt "<full user request>"`.
54
  - Only use edit/write tools directly for small one-file or two-file tasks.
 
55
 
56
  Identity:
57
 
 
9
  websearch: false
10
  skill: false
11
  lsp: false
12
+ kaiju_write_file: true
13
  kaiju_artifact: true
14
  permission:
15
  read: allow
 
34
  "git diff*": allow
35
  external_directory: ask
36
  kaiju_artifact: allow
37
+ kaiju_write_file: allow
38
  doom_loop: ask
39
  question: deny
40
  ---
 
49
 
50
  - Always run `pwd` before file work.
51
  - For websites, landing pages, owner/business operating packs, multi-file generated artifacts, or Desktop output folders, do not hand-write large files. Immediately use the Kaiju runner.
52
+ - For exact one-file creation requests with exact content, call `kaiju_write_file` after `pwd`.
53
  - Desktop single-artifact command: call `kaiju_artifact` with `no_planner: true`, `kind: auto`, `out_dir: "$HOME/Desktop/<clear-folder-name>"`, and the full user request.
54
  - Website plus owner-pack command sequence: call `kaiju_artifact` twice into the same `out_dir`: first with `kind: website`, then with `kind: business_suite`.
55
  - If the user names a destination folder, use that folder exactly. Otherwise choose a short clear folder name.
56
  - If `kaiju_artifact` is unavailable, run `kaiju-coder-7-run --no-planner --kind auto --out-dir "<requested-folder>" --prompt "<full user request>"`.
57
  - Only use edit/write tools directly for small one-file or two-file tasks.
58
+ - After `kaiju_artifact` succeeds, do not read the generated HTML or manifest unless the user explicitly asks you to inspect it. The tool output already provides artifact path, manifest path, and changed file count.
59
 
60
  Identity:
61
 
README.md CHANGED
@@ -1,5 +1,7 @@
1
  # OpenCode Quickstart For Kaiju Coder 7
2
 
 
 
3
  Kaiju Coder 7 is served as an OpenAI-compatible model with public model id
4
  `kaiju-coder-7`. After the helper is installed, Kaiju is configured as a normal
5
  OpenCode model: start OpenCode, select `kaiju/kaiju-coder-7` if needed, and
 
1
  # OpenCode Quickstart For Kaiju Coder 7
2
 
3
+ ![RMDW logo](assets/RMDWlogo.png)
4
+
5
  Kaiju Coder 7 is served as an OpenAI-compatible model with public model id
6
  `kaiju-coder-7`. After the helper is installed, Kaiju is configured as a normal
7
  OpenCode model: start OpenCode, select `kaiju/kaiju-coder-7` if needed, and
assets/RMDWlogo.png ADDED
kaiju_harness/website.py CHANGED
@@ -27,10 +27,20 @@ IMAGE_BANK: dict[str, list[str]] = {
27
  "https://images.unsplash.com/photo-1517433670267-08bbd4be890f?auto=format&fit=crop&w=900&q=80",
28
  "https://images.unsplash.com/photo-1555507036-ab1f4038808a?auto=format&fit=crop&w=900&q=80",
29
  ],
 
 
 
 
 
30
  "contractor": [
31
  "https://images.unsplash.com/photo-1504307651254-35680f356dfd?auto=format&fit=crop&w=1400&q=80",
32
- "https://images.unsplash.com/photo-1632759145351-1d592919f522?auto=format&fit=crop&w=900&q=80",
33
- "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?auto=format&fit=crop&w=900&q=80",
 
 
 
 
 
34
  ],
35
  "roofing": [
36
  "https://images.unsplash.com/photo-1632759145351-1d592919f522?auto=format&fit=crop&w=1400&q=80",
@@ -131,11 +141,13 @@ LAYOUT_BY_TYPE: dict[str, str] = {
131
  "med_spa": "editorial",
132
  "salon": "editorial",
133
  "bakery": "editorial",
 
134
  "restaurant": "editorial",
135
  "roofing": "proof",
136
  "plumbing": "proof",
137
  "electrical": "proof",
138
  "contractor": "proof",
 
139
  "legal": "proof",
140
  "dental": "proof",
141
  }
@@ -176,29 +188,35 @@ def title_case_service(value: str) -> str:
176
 
177
  def infer_business_name(prompt: str) -> str:
178
  patterns = [
179
- r"named\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+include|\s+for|\s+that|$)",
180
- r"called\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+include|\s+for|\s+that|$)",
181
- r"for\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+include|\s+that|$)",
 
 
182
  ]
183
  for pattern in patterns:
184
  match = re.search(pattern, prompt)
185
  if match:
186
- return clean_text(match.group(1), "Local Business").rstrip(".")
 
 
187
  return "Local Business"
188
 
189
 
190
  def infer_business_type(prompt: str) -> str:
191
  lower = prompt.lower()
192
  choices = [
193
- ("barber", ["barber", "fade", "beard", "haircut"]),
 
 
194
  ("bakery", ["bakery", "baked", "pastry", "croissant", "cake"]),
195
  ("roofing", ["roof", "roofing", "gutter", "storm"]),
196
  ("contractor", ["contractor", "remodel", "construction", "hvac", "repair"]),
 
197
  ("cleaning", ["cleaning", "maid", "janitorial"]),
198
  ("fitness", ["fitness", "gym", "trainer"]),
199
  ("med_spa", ["med spa", "medical spa", "botox", "injectables", "facial", "skincare", "skin care"]),
200
  ("salon", ["salon", "esthetician", "hair stylist", "hair studio"]),
201
- ("restaurant", ["restaurant", "coffee", "cafe", "menu"]),
202
  ("real_estate", ["real estate", "realtor", "homes", "property"]),
203
  ("pets", ["pet", "dog", "groomer", "veterinary", "vet"]),
204
  ("auto", ["auto", "car", "detailer", "mechanic", "repair shop"]),
@@ -215,6 +233,49 @@ def infer_business_type(prompt: str) -> str:
215
  return "default"
216
 
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  def term_matches(lower_text: str, term: str) -> bool:
219
  if " " in term:
220
  return term in lower_text
@@ -225,8 +286,10 @@ def infer_services(prompt: str, business_type: str) -> list[str]:
225
  defaults = {
226
  "barber": ["Signature Cuts", "Skin Fades", "Beard Trims", "Hot Towel Shaves"],
227
  "bakery": ["Fresh Pastries", "Custom Cakes", "Coffee Bar", "Catering Boxes"],
 
228
  "roofing": ["Roof Repair", "Full Replacement", "Storm Inspection", "Gutter Service"],
229
  "contractor": ["Repairs", "Installations", "Project Planning", "Emergency Help"],
 
230
  "cleaning": ["Deep Cleaning", "Recurring Service", "Move-Out Cleaning", "Office Cleaning"],
231
  "fitness": ["Strength Training", "Nutrition Coaching", "Mobility Work", "Accountability"],
232
  "salon": ["Signature Service", "Treatment Plans", "Consultations", "Aftercare"],
@@ -244,6 +307,66 @@ def infer_services(prompt: str, business_type: str) -> list[str]:
244
  "default": ["Consultation", "Planning", "Delivery", "Support"],
245
  }
246
  lower = prompt.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  if "services" in lower:
248
  after = re.split(r"services[:\s]", prompt, flags=re.IGNORECASE, maxsplit=1)
249
  if len(after) == 2:
@@ -288,12 +411,33 @@ def infer_palette(prompt: str, business_type: str) -> str:
288
 
289
 
290
  def infer_cta(prompt: str, business_type: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
291
  match = re.search(
292
  r"\bCTA\s+(?:is\s+|should\s+be\s+|button\s+is\s+)?[\"']?([A-Za-z][A-Za-z0-9 &'/-]{2,50}?)[\"']?(?:[.!?;,]|$)",
293
  prompt,
294
  )
295
  if match:
296
- return clean_text(match.group(1), "Get started").rstrip(" .")
 
 
 
 
 
 
 
 
 
297
  if business_type == "barber":
298
  return "Book a chair"
299
  if "emergency" in prompt.lower():
@@ -304,11 +448,13 @@ def infer_cta(prompt: str, business_type: str) -> str:
304
  def spec_from_prompt(prompt: str) -> WebsiteSpec:
305
  business_type = infer_business_type(prompt)
306
  name = infer_business_name(prompt)
 
307
  services = infer_services(prompt, business_type)
308
  cta = infer_cta(prompt, business_type)
309
  return WebsiteSpec(
310
  business_name=name,
311
  business_type=business_type,
 
312
  headline=headline_for(name, business_type),
313
  subheadline=subheadline_for(business_type),
314
  cta=cta,
@@ -320,6 +466,7 @@ def spec_from_prompt(prompt: str) -> WebsiteSpec:
320
  "I knew what was happening at every step.",
321
  ],
322
  palette=infer_palette(prompt, business_type),
 
323
  )
324
 
325
 
@@ -327,12 +474,14 @@ def headline_for(name: str, business_type: str) -> str:
327
  options = {
328
  "barber": "Sharp cuts. Clean fades. Easy booking.",
329
  "bakery": "Fresh pastries, warm coffee, zero guesswork.",
 
330
  "roofing": "Roofing help when it actually matters.",
331
  "contractor": "Reliable work, clear scope, cleaner results.",
 
332
  "cleaning": "A cleaner home without the scheduling mess.",
333
  "fitness": "Private training built around real consistency.",
334
  "salon": "Skin, style, and care that feels considered.",
335
- "restaurant": "Local flavor with a reason to come back.",
336
  "real_estate": "Confident moves for buyers and sellers.",
337
  "pets": "Gentle grooming for pets people love.",
338
  "auto": "Mobile detailing that makes the car feel new.",
@@ -352,12 +501,14 @@ def subheadline_for(business_type: str) -> str:
352
  options = {
353
  "barber": "Premium barbering with visible services, simple pricing, real photos, and a direct chair-booking CTA.",
354
  "bakery": "A warm neighborhood page with menu highlights, catering, hours, and a clear path to order.",
 
355
  "roofing": "A trust-first contractor page with emergency help, service areas, proof, and fast contact.",
356
  "contractor": "A practical local-service site that explains the work, builds trust, and gets customers to act.",
 
357
  "cleaning": "Recurring cleaning, deep cleaning, and move-out help presented with clear packages and proof.",
358
  "fitness": "Coaching, nutrition, schedule, and testimonials organized for people ready to start.",
359
  "salon": "A polished service page with treatments, packages, testimonials, and online booking.",
360
- "restaurant": "Menu highlights, photos, events, catering, hours, and contact details in one clean page.",
361
  "real_estate": "Buying, selling, valuation, and local guidance packaged into a clear conversion path.",
362
  "pets": "Friendly grooming services, pricing, hours, and booking for busy pet owners.",
363
  "auto": "Packages, proof, photos, and mobile booking for customers who want the car handled right.",
@@ -436,8 +587,11 @@ def service_copy(spec: WebsiteSpec, service: str) -> str:
436
  "electrical": "Safety-first service with clear estimates, careful work, and practical scheduling.",
437
  "med_spa": "A polished service path with consultation, expectations, aftercare, and booking clarity.",
438
  "dental": "A patient-ready service path with visit clarity, insurance context, and simple scheduling.",
 
439
  "barber": "A clean chair-ready offer with visible pricing, easy booking, and a premium feel.",
440
  "bakery": "A warm customer offer with photos, order clarity, and an easy way to inquire.",
 
 
441
  "legal": "A plain-English service area with trust, next steps, and consultation clarity.",
442
  }
443
  fallback = "Clear scope, practical guidance, and a clean result without confusing back-and-forth."
@@ -456,8 +610,23 @@ def render_cards(spec: WebsiteSpec) -> str:
456
 
457
  def render_pricing(services: list[str]) -> str:
458
  rows = []
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  for index, item in enumerate(services[:4]):
460
- rows.append(f"<li><span>{esc(item)}</span><strong>${45 + index * 25}+</strong></li>")
 
 
461
  return "\n".join(rows)
462
 
463
 
 
27
  "https://images.unsplash.com/photo-1517433670267-08bbd4be890f?auto=format&fit=crop&w=900&q=80",
28
  "https://images.unsplash.com/photo-1555507036-ab1f4038808a?auto=format&fit=crop&w=900&q=80",
29
  ],
30
+ "food": [
31
+ "https://images.unsplash.com/photo-1562967914-608f82629710?auto=format&fit=crop&w=1400&q=80",
32
+ "https://images.unsplash.com/photo-1604908176997-125f25cc6f3d?auto=format&fit=crop&w=900&q=80",
33
+ "https://images.unsplash.com/photo-1550547660-d9450f859349?auto=format&fit=crop&w=900&q=80",
34
+ ],
35
  "contractor": [
36
  "https://images.unsplash.com/photo-1504307651254-35680f356dfd?auto=format&fit=crop&w=1400&q=80",
37
+ "https://images.unsplash.com/photo-1541888946425-d81bb19240f5?auto=format&fit=crop&w=900&q=80",
38
+ "https://images.unsplash.com/photo-1503387762-592deb58ef4e?auto=format&fit=crop&w=900&q=80",
39
+ ],
40
+ "bookkeeping": [
41
+ "https://images.unsplash.com/photo-1554224155-6726b3ff858f?auto=format&fit=crop&w=1400&q=80",
42
+ "https://images.unsplash.com/photo-1554224154-26032ffc0d07?auto=format&fit=crop&w=900&q=80",
43
+ "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?auto=format&fit=crop&w=900&q=80",
44
  ],
45
  "roofing": [
46
  "https://images.unsplash.com/photo-1632759145351-1d592919f522?auto=format&fit=crop&w=1400&q=80",
 
141
  "med_spa": "editorial",
142
  "salon": "editorial",
143
  "bakery": "editorial",
144
+ "food": "editorial",
145
  "restaurant": "editorial",
146
  "roofing": "proof",
147
  "plumbing": "proof",
148
  "electrical": "proof",
149
  "contractor": "proof",
150
+ "bookkeeping": "proof",
151
  "legal": "proof",
152
  "dental": "proof",
153
  }
 
188
 
189
  def infer_business_name(prompt: str) -> str:
190
  patterns = [
191
+ r"(?:website|site|landing page|homepage)\s+for\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+at\s+|\s+with|\s+include|\s+that|\s+put|\s+and\s+put|$)",
192
+ r"for\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+at\s+|\s+with|\s+include|\s+that|\s+put|\s+and\s+put|$)",
193
+ r"(?:food\s+business|business|company|firm|restaurant|clinic|shop),\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+have|\s+include|\s+for|\s+that|$)",
194
+ r"(?:business|company|firm|restaurant|clinic|shop)\s+named\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+have|\s+include|\s+for|\s+that|$)",
195
+ r"(?:business|company|firm|restaurant|clinic|shop)\s+called\s+([A-Z][A-Za-z0-9 &'&-]{2,80}?)(?:\.|,|\s+with|\s+have|\s+include|\s+for|\s+that|$)",
196
  ]
197
  for pattern in patterns:
198
  match = re.search(pattern, prompt)
199
  if match:
200
+ candidate = clean_text(match.group(1), "Local Business").rstrip(".")
201
+ if not re.search(r"\bfolder\b|\bdirectory\b|\bdesktop\b", candidate, re.IGNORECASE):
202
+ return candidate
203
  return "Local Business"
204
 
205
 
206
  def infer_business_type(prompt: str) -> str:
207
  lower = prompt.lower()
208
  choices = [
209
+ ("food", ["food business", "fried chicken", "chicken", "chickens", "wings", "family meals", "food truck"]),
210
+ ("restaurant", ["restaurant", "omakase", "dining", "menu", "cuisine", "reserve a table", "reservation"]),
211
+ ("barber", ["barber", "skin fade", "beard", "haircut"]),
212
  ("bakery", ["bakery", "baked", "pastry", "croissant", "cake"]),
213
  ("roofing", ["roof", "roofing", "gutter", "storm"]),
214
  ("contractor", ["contractor", "remodel", "construction", "hvac", "repair"]),
215
+ ("bookkeeping", ["bookkeeping", "bookkeeper", "payroll", "accounting", "accountant", "financial reporting"]),
216
  ("cleaning", ["cleaning", "maid", "janitorial"]),
217
  ("fitness", ["fitness", "gym", "trainer"]),
218
  ("med_spa", ["med spa", "medical spa", "botox", "injectables", "facial", "skincare", "skin care"]),
219
  ("salon", ["salon", "esthetician", "hair stylist", "hair studio"]),
 
220
  ("real_estate", ["real estate", "realtor", "homes", "property"]),
221
  ("pets", ["pet", "dog", "groomer", "veterinary", "vet"]),
222
  ("auto", ["auto", "car", "detailer", "mechanic", "repair shop"]),
 
233
  return "default"
234
 
235
 
236
+ def infer_location(prompt: str) -> str:
237
+ city_state = re.search(r"\b([A-Z][A-Za-z .'-]{2,40}),\s*([A-Z]{2})(?:\s+\d{5})?\b", prompt)
238
+ if city_state:
239
+ return f"{clean_text(city_state.group(1), 'Atlanta')}, {city_state.group(2)}"
240
+ in_city = re.search(r"\bin\s+([A-Z][A-Za-z .'-]{2,40})(?:\.|,|\s+with|\s+include|\s+for|\s+that|$)", prompt)
241
+ if in_city:
242
+ candidate = clean_text(in_city.group(1), "Atlanta").rstrip(".")
243
+ if not re.search(r"\bfolder\b|\bdesktop\b|\bsection\b", candidate, re.IGNORECASE):
244
+ return candidate
245
+ return "Atlanta"
246
+
247
+
248
+ def contact_phone_for_location(location: str) -> str:
249
+ lower = location.lower()
250
+ area_codes = [
251
+ ("san francisco", "415"),
252
+ ("austin", "512"),
253
+ ("boston", "617"),
254
+ ("charlotte", "704"),
255
+ ("new york", "212"),
256
+ ("los angeles", "323"),
257
+ ("chicago", "312"),
258
+ ("miami", "305"),
259
+ ("atlanta", "404"),
260
+ ]
261
+ for city, code in area_codes:
262
+ if city in lower:
263
+ return f"({code}) 555-0199"
264
+ state_codes = {
265
+ " ca": "415",
266
+ " tx": "512",
267
+ " ma": "617",
268
+ " nc": "704",
269
+ " ny": "212",
270
+ " fl": "305",
271
+ " ga": "404",
272
+ }
273
+ for marker, code in state_codes.items():
274
+ if lower.endswith(marker) or f",{marker}" in lower:
275
+ return f"({code}) 555-0199"
276
+ return "(404) 555-0199"
277
+
278
+
279
  def term_matches(lower_text: str, term: str) -> bool:
280
  if " " in term:
281
  return term in lower_text
 
286
  defaults = {
287
  "barber": ["Signature Cuts", "Skin Fades", "Beard Trims", "Hot Towel Shaves"],
288
  "bakery": ["Fresh Pastries", "Custom Cakes", "Coffee Bar", "Catering Boxes"],
289
+ "food": ["Signature Chicken", "Family Meals", "Catering Trays", "Sides & Drinks"],
290
  "roofing": ["Roof Repair", "Full Replacement", "Storm Inspection", "Gutter Service"],
291
  "contractor": ["Repairs", "Installations", "Project Planning", "Emergency Help"],
292
+ "bookkeeping": ["Monthly Bookkeeping", "Payroll Support", "Financial Reporting", "Cleanup Projects"],
293
  "cleaning": ["Deep Cleaning", "Recurring Service", "Move-Out Cleaning", "Office Cleaning"],
294
  "fitness": ["Strength Training", "Nutrition Coaching", "Mobility Work", "Accountability"],
295
  "salon": ["Signature Service", "Treatment Plans", "Consultations", "Aftercare"],
 
307
  "default": ["Consultation", "Planning", "Delivery", "Support"],
308
  }
309
  lower = prompt.lower()
310
+ package_match = re.search(r"(?:service\s+)?packages?\s+with\s+prices?\s*\(([^)]{20,220})\)", prompt, re.IGNORECASE)
311
+ if package_match:
312
+ packages = []
313
+ for item in re.split(r",|;", package_match.group(1)):
314
+ name = re.sub(r"\$[\d,]+(?:/mo)?", "", item, flags=re.IGNORECASE).strip(" -:")
315
+ if len(name) > 2:
316
+ packages.append(title_case_service(name))
317
+ if len(packages) >= 3:
318
+ return packages[:4]
319
+ if business_type == "restaurant":
320
+ simple_menu_match = re.search(
321
+ r"menu highlights with\s+(.+?)(?:,\s*omakase|,\s*reservation|,\s*hours|\.|$)",
322
+ prompt,
323
+ re.IGNORECASE | re.DOTALL,
324
+ )
325
+ if simple_menu_match:
326
+ dishes = []
327
+ normalized_menu = re.sub(r"\s+and\s+", ", ", simple_menu_match.group(1))
328
+ for item in re.split(r",|;", normalized_menu):
329
+ cleaned = re.sub(r"\([^)]*\)|\$[\d,]+", "", item).strip(" -:")
330
+ if len(cleaned) > 3:
331
+ dishes.append(title_case_service(cleaned))
332
+ if len(dishes) >= 3:
333
+ return dishes[:4]
334
+ parenthetical_dishes = []
335
+ for match in re.finditer(r"([A-Z][A-Za-z0-9 '&-]{3,60}?)\s*\(", prompt):
336
+ dish = title_case_service(match.group(1))
337
+ if len(dish) > 3 and dish.lower() not in {"hours", "location", "phone", "email"}:
338
+ parenthetical_dishes.append(dish)
339
+ if len(parenthetical_dishes) >= 3:
340
+ return parenthetical_dishes[:4]
341
+ bullet_dishes = []
342
+ for match in re.finditer(r"^\s*-\s*([A-Z][^\n(]+?)(?:\s*\(|$)", prompt, re.MULTILINE):
343
+ dish = title_case_service(match.group(1))
344
+ if len(dish) > 3 and dish.lower() not in {"hours", "address", "phone", "email"}:
345
+ bullet_dishes.append(dish)
346
+ if len(bullet_dishes) >= 3:
347
+ return bullet_dishes[:4]
348
+ menu_match = re.search(r"(?:items\s+like|include\s+items\s+like|signature\s+dishes(?:\s+with\s+prices)?)[^:]*:\s*([^.]*)", prompt, re.IGNORECASE)
349
+ if menu_match:
350
+ dishes = []
351
+ for item in re.split(r",|;", menu_match.group(1)):
352
+ name = re.sub(r"\([^)]*\)|\$[\d,]+", "", item).strip(" -:")
353
+ if len(name) > 3:
354
+ dishes.append(title_case_service(name))
355
+ if len(dishes) >= 3:
356
+ return dishes[:4]
357
+ if business_type == "bakery" and any(term in lower for term in ["weekly specials", "catering", "order-ahead", "order ahead"]):
358
+ bakery_services = ["Fresh Pastries"]
359
+ if "weekly specials" in lower:
360
+ bakery_services.append("Weekly Specials")
361
+ if "catering" in lower:
362
+ bakery_services.append("Catering Boxes")
363
+ if "cake" in lower or "cakes" in lower:
364
+ bakery_services.append("Custom Cakes")
365
+ if len(bakery_services) < 4:
366
+ bakery_services.append("Coffee Bar")
367
+ return bakery_services[:4]
368
+ if business_type == "contractor" and re.search(r"project sectors?", lower):
369
+ return ["Ground-Up Construction", "Tenant Improvements", "Industrial Buildouts", "Preconstruction Planning"]
370
  if "services" in lower:
371
  after = re.split(r"services[:\s]", prompt, flags=re.IGNORECASE, maxsplit=1)
372
  if len(after) == 2:
 
411
 
412
 
413
  def infer_cta(prompt: str, business_type: str) -> str:
414
+ lower = prompt.lower()
415
+ quoted = re.search(r"['\"]([A-Za-z][A-Za-z0-9 &'/-]{2,50})['\"]\s+CTA", prompt)
416
+ if quoted:
417
+ return clean_text(quoted.group(1), "Get started").rstrip(" .")
418
+ if "reserve a table" in lower or "reservation" in lower:
419
+ return "Reserve a Table"
420
+ if "cleanup-call" in lower or "cleanup call" in lower:
421
+ return "Book a Cleanup Call"
422
+ if "bid-request" in lower or "bid request" in lower:
423
+ return "Request a Bid"
424
+ if "order-ahead" in lower or "order ahead" in lower or "online order" in lower:
425
+ return "Order Ahead"
426
  match = re.search(
427
  r"\bCTA\s+(?:is\s+|should\s+be\s+|button\s+is\s+)?[\"']?([A-Za-z][A-Za-z0-9 &'/-]{2,50}?)[\"']?(?:[.!?;,]|$)",
428
  prompt,
429
  )
430
  if match:
431
+ candidate = clean_text(match.group(1), "Get started").rstrip(" .")
432
+ candidate_lower = candidate.lower()
433
+ if (
434
+ candidate_lower not in {"button", "section", "call", "contact"}
435
+ and not candidate_lower.startswith("and ")
436
+ and not any(term in candidate_lower for term in ("testimonial", "price section", "hero section"))
437
+ ):
438
+ return candidate
439
+ if business_type == "food":
440
+ return "Order Now"
441
  if business_type == "barber":
442
  return "Book a chair"
443
  if "emergency" in prompt.lower():
 
448
  def spec_from_prompt(prompt: str) -> WebsiteSpec:
449
  business_type = infer_business_type(prompt)
450
  name = infer_business_name(prompt)
451
+ location = infer_location(prompt)
452
  services = infer_services(prompt, business_type)
453
  cta = infer_cta(prompt, business_type)
454
  return WebsiteSpec(
455
  business_name=name,
456
  business_type=business_type,
457
+ location=location,
458
  headline=headline_for(name, business_type),
459
  subheadline=subheadline_for(business_type),
460
  cta=cta,
 
466
  "I knew what was happening at every step.",
467
  ],
468
  palette=infer_palette(prompt, business_type),
469
+ contact_phone=contact_phone_for_location(location),
470
  )
471
 
472
 
 
474
  options = {
475
  "barber": "Sharp cuts. Clean fades. Easy booking.",
476
  "bakery": "Fresh pastries, warm coffee, zero guesswork.",
477
+ "food": "Hot chicken, clear prices, easy ordering.",
478
  "roofing": "Roofing help when it actually matters.",
479
  "contractor": "Reliable work, clear scope, cleaner results.",
480
+ "bookkeeping": "Clean books, clear payroll, better owner decisions.",
481
  "cleaning": "A cleaner home without the scheduling mess.",
482
  "fitness": "Private training built around real consistency.",
483
  "salon": "Skin, style, and care that feels considered.",
484
+ "restaurant": "Where tradition meets the harbor.",
485
  "real_estate": "Confident moves for buyers and sellers.",
486
  "pets": "Gentle grooming for pets people love.",
487
  "auto": "Mobile detailing that makes the car feel new.",
 
501
  options = {
502
  "barber": "Premium barbering with visible services, simple pricing, real photos, and a direct chair-booking CTA.",
503
  "bakery": "A warm neighborhood page with menu highlights, catering, hours, and a clear path to order.",
504
+ "food": "A simple food-business site with strong photos, clear prices, customer proof, and a direct order CTA.",
505
  "roofing": "A trust-first contractor page with emergency help, service areas, proof, and fast contact.",
506
  "contractor": "A practical local-service site that explains the work, builds trust, and gets customers to act.",
507
+ "bookkeeping": "Bookkeeping, payroll, reporting, and cleanup support presented with clear packages and a strong consultation path.",
508
  "cleaning": "Recurring cleaning, deep cleaning, and move-out help presented with clear packages and proof.",
509
  "fitness": "Coaching, nutrition, schedule, and testimonials organized for people ready to start.",
510
  "salon": "A polished service page with treatments, packages, testimonials, and online booking.",
511
+ "restaurant": "Refined Japanese dining with omakase, private rooms, signature dishes, and a clear reservation path.",
512
  "real_estate": "Buying, selling, valuation, and local guidance packaged into a clear conversion path.",
513
  "pets": "Friendly grooming services, pricing, hours, and booking for busy pet owners.",
514
  "auto": "Packages, proof, photos, and mobile booking for customers who want the car handled right.",
 
587
  "electrical": "Safety-first service with clear estimates, careful work, and practical scheduling.",
588
  "med_spa": "A polished service path with consultation, expectations, aftercare, and booking clarity.",
589
  "dental": "A patient-ready service path with visit clarity, insurance context, and simple scheduling.",
590
+ "bookkeeping": "A business-owner-ready package with clear monthly scope, reporting rhythm, and cleanup support.",
591
  "barber": "A clean chair-ready offer with visible pricing, easy booking, and a premium feel.",
592
  "bakery": "A warm customer offer with photos, order clarity, and an easy way to inquire.",
593
+ "food": "A customer-ready menu offer with strong photos, simple pricing, and an easy ordering path.",
594
+ "restaurant": "A signature dining experience with seasonal ingredients, careful presentation, and reservation-ready detail.",
595
  "legal": "A plain-English service area with trust, next steps, and consultation clarity.",
596
  }
597
  fallback = "Clear scope, practical guidance, and a clean result without confusing back-and-forth."
 
610
 
611
  def render_pricing(services: list[str]) -> str:
612
  rows = []
613
+ explicit_prices = {
614
+ "starter": "$299/mo",
615
+ "growth": "$599/mo",
616
+ "enterprise": "$999/mo",
617
+ "omakase": "$120",
618
+ "wagyu": "$85",
619
+ "black cod": "$42",
620
+ "ramen": "$28",
621
+ "signature chicken": "$14+",
622
+ "family meals": "$38+",
623
+ "catering trays": "$120+",
624
+ "sides": "$5+",
625
+ }
626
  for index, item in enumerate(services[:4]):
627
+ lower = item.lower()
628
+ price = next((value for key, value in explicit_prices.items() if key in lower), f"${45 + index * 25}+")
629
+ rows.append(f"<li><span>{esc(item)}</span><strong>{esc(price)}</strong></li>")
630
  return "\n".join(rows)
631
 
632
 
scripts/check_hf_uploaded_release.py CHANGED
@@ -53,6 +53,7 @@ REPOS: tuple[RepoSpec, ...] = (
53
  label="adapter repo",
54
  required_files=(
55
  "README.md",
 
56
  "adapter_config.json",
57
  "adapter_model.safetensors",
58
  "DATA_PROVENANCE_DRAFT.md",
@@ -82,6 +83,7 @@ REPOS: tuple[RepoSpec, ...] = (
82
  label="OpenCode helper repo",
83
  required_files=(
84
  "README.md",
 
85
  "PUBLIC_TESTING_QUICKSTART.md",
86
  "opencode.kaiju-coder-7.jsonc",
87
  ".opencode/agents/kaiju-coder-7.md",
@@ -120,6 +122,7 @@ REPOS: tuple[RepoSpec, ...] = (
120
  label="runtime quantization helper repo",
121
  required_files=(
122
  "README.md",
 
123
  "PUBLIC_TESTING_QUICKSTART.md",
124
  "GGUF_CANDIDATE.md",
125
  "scripts/start-qwen36-merged-vllm.sh",
 
53
  label="adapter repo",
54
  required_files=(
55
  "README.md",
56
+ "assets/RMDWlogo.png",
57
  "adapter_config.json",
58
  "adapter_model.safetensors",
59
  "DATA_PROVENANCE_DRAFT.md",
 
83
  label="OpenCode helper repo",
84
  required_files=(
85
  "README.md",
86
+ "assets/RMDWlogo.png",
87
  "PUBLIC_TESTING_QUICKSTART.md",
88
  "opencode.kaiju-coder-7.jsonc",
89
  ".opencode/agents/kaiju-coder-7.md",
 
122
  label="runtime quantization helper repo",
123
  required_files=(
124
  "README.md",
125
+ "assets/RMDWlogo.png",
126
  "PUBLIC_TESTING_QUICKSTART.md",
127
  "GGUF_CANDIDATE.md",
128
  "scripts/start-qwen36-merged-vllm.sh",
scripts/kaiju_opencode_fast_proxy.py CHANGED
@@ -11,6 +11,7 @@ from __future__ import annotations
11
  import argparse
12
  import json
13
  import os
 
14
  import time
15
  import urllib.error
16
  import urllib.request
@@ -28,6 +29,10 @@ NORMAL_MAX_TOKENS = int(os.environ.get("KAIJU_NORMAL_MAX_TOKENS", "384"))
28
  WORK_MAX_TOKENS = int(os.environ.get("KAIJU_WORK_MAX_TOKENS", "1536"))
29
  ARTIFACT_MAX_TOKENS = int(os.environ.get("KAIJU_ARTIFACT_MAX_TOKENS", "4096"))
30
  MAX_REQUEST_BYTES = int(os.environ.get("KAIJU_MAX_REQUEST_BYTES", "2097152"))
 
 
 
 
31
 
32
 
33
  def normalize_messages(messages: Any) -> list[dict[str, Any]]:
@@ -36,20 +41,97 @@ def normalize_messages(messages: Any) -> list[dict[str, Any]]:
36
  return [message for message in messages if isinstance(message, dict)]
37
 
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  def message_text(messages: list[dict[str, Any]]) -> str:
40
  parts: list[str] = []
41
  for message in messages:
 
 
 
 
 
 
 
 
42
  content = message.get("content", "")
43
  if isinstance(content, str):
44
- parts.append(content)
45
- else:
46
- parts.append(json.dumps(content, ensure_ascii=False))
47
- return "\n".join(parts).lower()
 
 
 
 
 
 
 
 
 
 
48
 
49
 
50
- def classify_job(messages: list[dict[str, Any]]) -> str:
 
 
 
 
 
 
 
 
 
 
 
51
  text = message_text(messages)
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  artifact_terms = (
 
 
 
 
53
  "complete html",
54
  "html file",
55
  "one-file website",
@@ -57,6 +139,10 @@ def classify_job(messages: list[dict[str, Any]]) -> str:
57
  "build a website",
58
  "make a website",
59
  "full file",
 
 
 
 
60
  )
61
  work_terms = (
62
  "create ",
@@ -77,6 +163,278 @@ def classify_job(messages: list[dict[str, Any]]) -> str:
77
  return "normal"
78
 
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  def target_tokens(job_class: str) -> int:
81
  if job_class == "artifact":
82
  return ARTIFACT_MAX_TOKENS
@@ -154,6 +512,16 @@ class Handler(BaseHTTPRequestHandler):
154
  except Exception as error: # noqa: BLE001 - return request parse failures.
155
  self._json(HTTPStatus.BAD_REQUEST, {"error": {"message": str(error), "type": "bad_request"}})
156
  return
 
 
 
 
 
 
 
 
 
 
157
  self._forward_post("/chat/completions", body)
158
 
159
  def _headers(self) -> dict[str, str]:
 
11
  import argparse
12
  import json
13
  import os
14
+ import re
15
  import time
16
  import urllib.error
17
  import urllib.request
 
29
  WORK_MAX_TOKENS = int(os.environ.get("KAIJU_WORK_MAX_TOKENS", "1536"))
30
  ARTIFACT_MAX_TOKENS = int(os.environ.get("KAIJU_ARTIFACT_MAX_TOKENS", "4096"))
31
  MAX_REQUEST_BYTES = int(os.environ.get("KAIJU_MAX_REQUEST_BYTES", "2097152"))
32
+ AUTOROUTE_ENABLED = os.environ.get("KAIJU_OPENCODE_AUTOROUTE", "1").lower() not in {"0", "false", "no"}
33
+ SUMMARY_ENABLED = os.environ.get("KAIJU_OPENCODE_FAST_SUMMARY", "1").lower() not in {"0", "false", "no"}
34
+ TOOL_NAME = "kaiju_artifact"
35
+ WRITE_TOOL_NAME = "kaiju_write_file"
36
 
37
 
38
  def normalize_messages(messages: Any) -> list[dict[str, Any]]:
 
41
  return [message for message in messages if isinstance(message, dict)]
42
 
43
 
44
+ def content_to_text(content: Any) -> str:
45
+ if isinstance(content, str):
46
+ stripped = content.strip()
47
+ if stripped.startswith(("{", "[")):
48
+ try:
49
+ return content_to_text(json.loads(stripped))
50
+ except Exception:
51
+ return content
52
+ return content
53
+ if isinstance(content, list):
54
+ parts: list[str] = []
55
+ for item in content:
56
+ if not isinstance(item, dict):
57
+ continue
58
+ if isinstance(item.get("text"), str):
59
+ parts.append(item["text"])
60
+ elif item.get("type") == "text" and isinstance(item.get("content"), str):
61
+ parts.append(item["content"])
62
+ elif isinstance(item.get("output"), str):
63
+ parts.append(item["output"])
64
+ if parts:
65
+ return "\n".join(parts)
66
+ return json.dumps(content, ensure_ascii=False)
67
+ if isinstance(content, dict):
68
+ for key in ("output", "text", "content"):
69
+ if isinstance(content.get(key), str):
70
+ return content[key]
71
+ return json.dumps(content, ensure_ascii=False)
72
+ return json.dumps(content, ensure_ascii=False)
73
+
74
+
75
  def message_text(messages: list[dict[str, Any]]) -> str:
76
  parts: list[str] = []
77
  for message in messages:
78
+ parts.append(content_to_text(message.get("content", "")))
79
+ return "\n".join(parts).lower()
80
+
81
+
82
+ def latest_user_text(messages: list[dict[str, Any]]) -> str:
83
+ for message in reversed(messages):
84
+ if message.get("role") != "user":
85
+ continue
86
  content = message.get("content", "")
87
  if isinstance(content, str):
88
+ return content
89
+ if isinstance(content, list):
90
+ parts: list[str] = []
91
+ for item in content:
92
+ if not isinstance(item, dict):
93
+ continue
94
+ if isinstance(item.get("text"), str):
95
+ parts.append(item["text"])
96
+ elif item.get("type") == "text" and isinstance(item.get("content"), str):
97
+ parts.append(item["content"])
98
+ if parts:
99
+ return "\n".join(parts)
100
+ return json.dumps(content, ensure_ascii=False)
101
+ return ""
102
 
103
 
104
+ def clean_prompt(prompt: str) -> str:
105
+ cleaned = prompt.strip()
106
+ if len(cleaned) >= 2 and cleaned[0] == cleaned[-1] and cleaned[0] in {"'", '"'}:
107
+ return cleaned[1:-1].strip()
108
+ return cleaned
109
+
110
+
111
+ def has_tool(messages: list[dict[str, Any]]) -> bool:
112
+ return any(message.get("role") == "tool" or message.get("tool_call_id") for message in messages)
113
+
114
+
115
+ def has_tool_result(messages: list[dict[str, Any]]) -> bool:
116
  text = message_text(messages)
117
+ return (
118
+ TOOL_NAME in text
119
+ or WRITE_TOOL_NAME in text
120
+ or "wrote file:" in text
121
+ or "task type:" in text
122
+ or "artifact:" in text
123
+ or "manifest:" in text
124
+ or "changed files:" in text
125
+ ) and has_tool(messages)
126
+
127
+
128
+ def classify_text(text: str) -> str:
129
+ text = text.lower()
130
  artifact_terms = (
131
+ "website",
132
+ "one-page",
133
+ "one page",
134
+ "homepage",
135
  "complete html",
136
  "html file",
137
  "one-file website",
 
139
  "build a website",
140
  "make a website",
141
  "full file",
142
+ "desktop",
143
+ "owner pack",
144
+ "operating pack",
145
+ "business suite",
146
  )
147
  work_terms = (
148
  "create ",
 
163
  return "normal"
164
 
165
 
166
+ def classify_job(messages: list[dict[str, Any]]) -> str:
167
+ return classify_text(clean_prompt(latest_user_text(messages)))
168
+
169
+
170
+ def infer_kind(prompt: str) -> str:
171
+ lower = prompt.lower()
172
+ if any(term in lower for term in ("website", "landing page", "one-page", "one page", "homepage", "html")):
173
+ return "website"
174
+ if any(term in lower for term in ("owner pack", "operating pack", "business suite")):
175
+ return "business_suite"
176
+ return "auto"
177
+
178
+
179
+ def infer_out_dir(prompt: str) -> str:
180
+ folder_match = re.search(r"folder named\s+([A-Za-z0-9_ -]{3,80})(?:\.|,|$)", prompt, re.IGNORECASE)
181
+ if folder_match:
182
+ folder = re.sub(r"\s+", "-", re.sub(r"[^A-Za-z0-9_. -]", "", folder_match.group(1).strip().rstrip(" .")))
183
+ return os.path.join(os.path.expanduser("~"), "Desktop", folder)
184
+ if "desktop" in prompt.lower():
185
+ return os.path.join(os.path.expanduser("~"), "Desktop", "Kaiju-Coder-7-Artifacts")
186
+ return ""
187
+
188
+
189
+ def should_synthesize_tool_call(body: dict[str, Any], messages: list[dict[str, Any]]) -> bool:
190
+ if not AUTOROUTE_ENABLED or has_tool(messages):
191
+ return False
192
+ if classify_job(messages) != "artifact":
193
+ return False
194
+ return tool_available(body, TOOL_NAME)
195
+
196
+
197
+ def tool_available(body: dict[str, Any], name: str) -> bool:
198
+ tools = body.get("tools")
199
+ if not isinstance(tools, list):
200
+ return False
201
+ return any(
202
+ isinstance(item, dict)
203
+ and item.get("type") == "function"
204
+ and isinstance(item.get("function"), dict)
205
+ and item["function"].get("name") == name
206
+ for item in tools
207
+ )
208
+
209
+
210
+ def parse_exact_file_write(prompt: str) -> dict[str, str] | None:
211
+ prompt = clean_prompt(prompt)
212
+ match = re.search(
213
+ r"\bcreate\s+([A-Za-z0-9_./-]{1,160})\s+with exactly(?: this content and no extra characters)?:\s*(.+?)\s*$",
214
+ prompt,
215
+ re.IGNORECASE | re.DOTALL,
216
+ )
217
+ if not match:
218
+ return None
219
+ file_path = match.group(1).strip()
220
+ content = match.group(2).strip()
221
+ if not file_path or not content:
222
+ return None
223
+ return {"file_path": file_path, "content": content}
224
+
225
+
226
+ def should_synthesize_file_write(body: dict[str, Any], messages: list[dict[str, Any]]) -> bool:
227
+ if not AUTOROUTE_ENABLED or has_tool(messages):
228
+ return False
229
+ if not tool_available(body, WRITE_TOOL_NAME):
230
+ return False
231
+ return parse_exact_file_write(latest_user_text(messages)) is not None
232
+
233
+
234
+ def tool_call_arguments(prompt: str) -> dict[str, Any]:
235
+ prompt = clean_prompt(prompt)
236
+ args: dict[str, Any] = {
237
+ "prompt": prompt,
238
+ "kind": infer_kind(prompt),
239
+ "no_planner": True,
240
+ }
241
+ out_dir = infer_out_dir(prompt)
242
+ if out_dir:
243
+ args["out_dir"] = out_dir
244
+ return args
245
+
246
+
247
+ def completion_id(prefix: str = "chatcmpl-kaiju") -> str:
248
+ return f"{prefix}-{int(time.time() * 1000)}"
249
+
250
+
251
+ def write_sse(handler: BaseHTTPRequestHandler, chunks: list[dict[str, Any]]) -> None:
252
+ handler.send_response(HTTPStatus.OK)
253
+ handler.send_header("content-type", "text/event-stream; charset=utf-8")
254
+ handler.send_header("cache-control", "no-store, no-transform")
255
+ handler.send_header("connection", "close")
256
+ handler.end_headers()
257
+ for chunk in chunks:
258
+ handler.wfile.write(f"data: {json.dumps(chunk, separators=(',', ':'))}\n\n".encode("utf-8"))
259
+ handler.wfile.flush()
260
+ handler.wfile.write(b"data: [DONE]\n\n")
261
+ handler.wfile.flush()
262
+
263
+
264
+ def split_json_arguments(args: dict[str, Any]) -> list[str]:
265
+ raw = json.dumps(args, separators=(",", ":"), ensure_ascii=False)
266
+ return [raw[index : index + 768] for index in range(0, len(raw), 768)] or ["{}"]
267
+
268
+
269
+ def synthesize_function_call(handler: BaseHTTPRequestHandler, body: dict[str, Any], tool_name: str, args: dict[str, Any]) -> None:
270
+ created = int(time.time())
271
+ model = str(body.get("model") or DEFAULT_MODEL)
272
+ chat_id = completion_id()
273
+ call_id = f"call_kaiju_{created}"
274
+ if body.get("stream") is True:
275
+ chunks = [
276
+ {
277
+ "id": chat_id,
278
+ "object": "chat.completion.chunk",
279
+ "created": created,
280
+ "model": model,
281
+ "choices": [
282
+ {
283
+ "index": 0,
284
+ "delta": {
285
+ "role": "assistant",
286
+ "tool_calls": [
287
+ {
288
+ "index": 0,
289
+ "id": call_id,
290
+ "type": "function",
291
+ "function": {"name": tool_name, "arguments": ""},
292
+ }
293
+ ],
294
+ },
295
+ "finish_reason": None,
296
+ }
297
+ ],
298
+ }
299
+ ]
300
+ chunks.extend(
301
+ {
302
+ "id": chat_id,
303
+ "object": "chat.completion.chunk",
304
+ "created": created,
305
+ "model": model,
306
+ "choices": [
307
+ {
308
+ "index": 0,
309
+ "delta": {"tool_calls": [{"index": 0, "function": {"arguments": part}}]},
310
+ "finish_reason": None,
311
+ }
312
+ ],
313
+ }
314
+ for part in split_json_arguments(args)
315
+ )
316
+ chunks.append(
317
+ {
318
+ "id": chat_id,
319
+ "object": "chat.completion.chunk",
320
+ "created": created,
321
+ "model": model,
322
+ "choices": [{"index": 0, "delta": {}, "finish_reason": "tool_calls"}],
323
+ }
324
+ )
325
+ write_sse(handler, chunks)
326
+ return
327
+ handler._json(
328
+ HTTPStatus.OK,
329
+ {
330
+ "id": chat_id,
331
+ "object": "chat.completion",
332
+ "created": created,
333
+ "model": model,
334
+ "choices": [
335
+ {
336
+ "index": 0,
337
+ "message": {
338
+ "role": "assistant",
339
+ "content": None,
340
+ "tool_calls": [
341
+ {
342
+ "id": call_id,
343
+ "type": "function",
344
+ "function": {"name": tool_name, "arguments": json.dumps(args, separators=(",", ":"))},
345
+ }
346
+ ],
347
+ },
348
+ "finish_reason": "tool_calls",
349
+ }
350
+ ],
351
+ "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
352
+ },
353
+ )
354
+
355
+
356
+ def synthesize_tool_call(handler: BaseHTTPRequestHandler, body: dict[str, Any], prompt: str) -> None:
357
+ synthesize_function_call(handler, body, TOOL_NAME, tool_call_arguments(prompt))
358
+
359
+
360
+ def synthesize_file_write_call(handler: BaseHTTPRequestHandler, body: dict[str, Any], prompt: str) -> None:
361
+ args = parse_exact_file_write(prompt)
362
+ if args is None:
363
+ raise ValueError("prompt is not an exact file-write request")
364
+ synthesize_function_call(handler, body, WRITE_TOOL_NAME, args)
365
+
366
+
367
+ def extract_tool_summary(messages: list[dict[str, Any]]) -> str:
368
+ text = ""
369
+ for message in reversed(messages):
370
+ if message.get("role") == "tool" or message.get("tool_call_id"):
371
+ text = content_to_text(message.get("content", ""))
372
+ break
373
+ if not text:
374
+ text = message_text(messages)
375
+ fields = []
376
+ for label in ("Task type", "Artifact type", "Manifest", "Artifact", "Project/repo", "Changed files", "Opened artifact"):
377
+ match = re.search(rf"^{re.escape(label)}:\s*(.+)$", text, re.MULTILINE)
378
+ if match:
379
+ fields.append(f"{label}: {match.group(1).strip()}")
380
+ if fields:
381
+ return "Kaiju artifact complete.\n\n" + "\n".join(fields)
382
+ write_match = re.search(r"Wrote file:\s*(.+)$", text, re.MULTILINE)
383
+ if write_match:
384
+ return f"File written.\n\nPath: {write_match.group(1).strip()}"
385
+ return "Kaiju artifact complete. Review the generated output folder and manifest from the tool result."
386
+
387
+
388
+ def synthesize_summary(handler: BaseHTTPRequestHandler, body: dict[str, Any], messages: list[dict[str, Any]]) -> None:
389
+ created = int(time.time())
390
+ model = str(body.get("model") or DEFAULT_MODEL)
391
+ content = extract_tool_summary(messages)
392
+ chat_id = completion_id("chatcmpl-kaiju-summary")
393
+ if body.get("stream") is True:
394
+ chunks = [
395
+ {
396
+ "id": chat_id,
397
+ "object": "chat.completion.chunk",
398
+ "created": created,
399
+ "model": model,
400
+ "choices": [{"index": 0, "delta": {"role": "assistant"}, "finish_reason": None}],
401
+ },
402
+ {
403
+ "id": chat_id,
404
+ "object": "chat.completion.chunk",
405
+ "created": created,
406
+ "model": model,
407
+ "choices": [{"index": 0, "delta": {"content": content}, "finish_reason": None}],
408
+ },
409
+ {
410
+ "id": chat_id,
411
+ "object": "chat.completion.chunk",
412
+ "created": created,
413
+ "model": model,
414
+ "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}],
415
+ },
416
+ ]
417
+ write_sse(handler, chunks)
418
+ return
419
+ handler._json(
420
+ HTTPStatus.OK,
421
+ {
422
+ "id": chat_id,
423
+ "object": "chat.completion",
424
+ "created": created,
425
+ "model": model,
426
+ "choices": [
427
+ {
428
+ "index": 0,
429
+ "message": {"role": "assistant", "content": content},
430
+ "finish_reason": "stop",
431
+ }
432
+ ],
433
+ "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
434
+ },
435
+ )
436
+
437
+
438
  def target_tokens(job_class: str) -> int:
439
  if job_class == "artifact":
440
  return ARTIFACT_MAX_TOKENS
 
512
  except Exception as error: # noqa: BLE001 - return request parse failures.
513
  self._json(HTTPStatus.BAD_REQUEST, {"error": {"message": str(error), "type": "bad_request"}})
514
  return
515
+ messages = normalize_messages(body.get("messages"))
516
+ if should_synthesize_file_write(body, messages):
517
+ synthesize_file_write_call(self, body, latest_user_text(messages))
518
+ return
519
+ if should_synthesize_tool_call(body, messages):
520
+ synthesize_tool_call(self, body, latest_user_text(messages))
521
+ return
522
+ if SUMMARY_ENABLED and has_tool_result(messages):
523
+ synthesize_summary(self, body, messages)
524
+ return
525
  self._forward_post("/chat/completions", body)
526
 
527
  def _headers(self) -> dict[str, str]:
scripts/opencode-kaiju-no-autocontinue.mjs CHANGED
@@ -1,14 +1,23 @@
1
  import { spawn } from "node:child_process";
2
  import { existsSync } from "node:fs";
3
- import { homedir } from "node:os";
 
4
  import path from "node:path";
5
  import { tool } from "@opencode-ai/plugin";
6
 
7
  const TASK_KINDS = ["auto", "website", "business_document", "business_suite", "app", "code_project", "repo_patch", "coding"];
8
-
9
  const defaultRuntimeScript = () =>
10
  path.join(homedir(), ".config", "opencode", "kaiju-coder-7-runtime", "scripts", "run_kaiju_router.py");
11
 
 
 
 
 
 
 
 
 
 
12
  const runProcess = (command, args, options = {}) =>
13
  new Promise((resolve, reject) => {
14
  const child = spawn(command, args, {
@@ -36,6 +45,33 @@ const runProcess = (command, args, options = {}) =>
36
  });
37
  });
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  export const KaijuCoder7NoAutocontinuePlugin = async () => {
40
  const isKaijuCoder7 = (input) => {
41
  const payload = JSON.stringify({
@@ -54,6 +90,24 @@ export const KaijuCoder7NoAutocontinuePlugin = async () => {
54
 
55
  return {
56
  tool: {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  kaiju_artifact: tool({
58
  description:
59
  "Create fast Kaiju Coder 7 websites, owner packs, apps, business documents, or code artifacts with the packaged local router. Use this instead of bash/write for large generated artifact tasks.",
@@ -95,8 +149,20 @@ export const KaijuCoder7NoAutocontinuePlugin = async () => {
95
  const result = await runProcess(process.env.KAIJU_PYTHON || "python3", procArgs, {
96
  cwd: context.directory || process.cwd(),
97
  });
 
 
 
 
 
 
 
 
 
 
 
 
98
  return {
99
- output: result.stdout.trim() || "Kaiju artifact command completed.",
100
  metadata: { stderr: result.stderr.trim(), out_dir: outDir, kind, no_planner: noPlanner },
101
  };
102
  },
 
1
  import { spawn } from "node:child_process";
2
  import { existsSync } from "node:fs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { homedir, platform } from "node:os";
5
  import path from "node:path";
6
  import { tool } from "@opencode-ai/plugin";
7
 
8
  const TASK_KINDS = ["auto", "website", "business_document", "business_suite", "app", "code_project", "repo_patch", "coding"];
 
9
  const defaultRuntimeScript = () =>
10
  path.join(homedir(), ".config", "opencode", "kaiju-coder-7-runtime", "scripts", "run_kaiju_router.py");
11
 
12
+ const resolveSafeFile = (directory, filePath) => {
13
+ const base = path.resolve(directory || process.cwd());
14
+ const resolved = path.resolve(base, filePath);
15
+ if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) {
16
+ throw new Error(`Refusing to write outside the OpenCode directory: ${filePath}`);
17
+ }
18
+ return resolved;
19
+ };
20
+
21
  const runProcess = (command, args, options = {}) =>
22
  new Promise((resolve, reject) => {
23
  const child = spawn(command, args, {
 
45
  });
46
  });
47
 
48
+ const shouldOpenArtifact = (text) =>
49
+ /\b(open it|open the (?:html|file|site|website|page)|launch it|show it)\b/i.test(text || "");
50
+
51
+ const extractArtifactPath = (stdout) => {
52
+ const artifact = stdout.match(/^Artifact:\s*(.+)$/m);
53
+ if (artifact) {
54
+ return artifact[1].trim();
55
+ }
56
+ const project = stdout.match(/^Project\/repo:\s*(.+)$/m);
57
+ return project ? project[1].trim() : "";
58
+ };
59
+
60
+ const openArtifact = async (artifactPath) => {
61
+ if (!artifactPath || !existsSync(artifactPath)) {
62
+ return "";
63
+ }
64
+ const system = platform();
65
+ if (system === "darwin") {
66
+ await runProcess("open", [artifactPath]);
67
+ } else if (system === "win32") {
68
+ await runProcess("cmd", ["/c", "start", "", artifactPath]);
69
+ } else {
70
+ await runProcess("xdg-open", [artifactPath]);
71
+ }
72
+ return `Opened artifact: ${artifactPath}`;
73
+ };
74
+
75
  export const KaijuCoder7NoAutocontinuePlugin = async () => {
76
  const isKaijuCoder7 = (input) => {
77
  const payload = JSON.stringify({
 
90
 
91
  return {
92
  tool: {
93
+ kaiju_write_file: tool({
94
+ description:
95
+ "Write one exact small file in the current OpenCode directory. Use for exact one-file creation requests, not large generated websites or projects.",
96
+ args: {
97
+ file_path: tool.schema.string().describe("Relative path to write inside the current OpenCode directory."),
98
+ content: tool.schema.string().describe("Exact file content to write."),
99
+ },
100
+ async execute(args, context) {
101
+ const target = resolveSafeFile(context.directory, args.file_path);
102
+ await mkdir(path.dirname(target), { recursive: true });
103
+ await writeFile(target, args.content, "utf8");
104
+ return {
105
+ output: `Wrote file: ${target}`,
106
+ metadata: { file_path: target, bytes: Buffer.byteLength(args.content, "utf8") },
107
+ };
108
+ },
109
+ }),
110
+
111
  kaiju_artifact: tool({
112
  description:
113
  "Create fast Kaiju Coder 7 websites, owner packs, apps, business documents, or code artifacts with the packaged local router. Use this instead of bash/write for large generated artifact tasks.",
 
149
  const result = await runProcess(process.env.KAIJU_PYTHON || "python3", procArgs, {
150
  cwd: context.directory || process.cwd(),
151
  });
152
+ let output = result.stdout.trim() || "Kaiju artifact command completed.";
153
+ if (shouldOpenArtifact(args.prompt)) {
154
+ const artifactPath = extractArtifactPath(output);
155
+ try {
156
+ const opened = await openArtifact(artifactPath);
157
+ output += opened
158
+ ? `\n${opened}`
159
+ : "\nOpen requested, but no generated artifact path was available to open.";
160
+ } catch (error) {
161
+ output += `\nOpen requested, but could not open artifact: ${error.message}`;
162
+ }
163
+ }
164
  return {
165
+ output,
166
  metadata: { stderr: result.stderr.trim(), out_dir: outDir, kind, no_planner: noPlanner },
167
  };
168
  },
scripts/prepare_hf_merged_model_metadata.sh CHANGED
@@ -33,9 +33,10 @@ if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
33
  fi
34
 
35
  rm -rf "${STAGING}"
36
- mkdir -p "${STAGING}/upstream/qwen3.6-27b"
37
 
38
  cp "${ROOT}/release/MODEL_CARD_DRAFT.md" "${STAGING}/README.md"
 
39
  cp "${ROOT}/release/PUBLIC_TESTING_QUICKSTART.md" "${STAGING}/PUBLIC_TESTING_QUICKSTART.md"
40
  cp "${ROOT}/release/LOCAL_TEST_INSTRUCTIONS.md" "${STAGING}/LOCAL_TEST_INSTRUCTIONS.md"
41
  cp "${ROOT}/release/SERVING_BENCHMARKS.md" "${STAGING}/SERVING_BENCHMARKS.md"
@@ -130,6 +131,7 @@ kaiju_gojira_b_ssh "
130
  FINAL_RELEASE_REPORT.md \
131
  GOAL_COMPLETION_AUDIT.md \
132
  HF_UPLOAD_EVIDENCE.md \
 
133
  MERGED_MODEL_RELEASE_MANIFEST.json \
134
  upstream/qwen3.6-27b/LICENSE; do
135
  test -f '${MODEL_REMOTE}/'\${required} || { echo \"Missing synced metadata: \${required}\" >&2; exit 2; }
 
33
  fi
34
 
35
  rm -rf "${STAGING}"
36
+ mkdir -p "${STAGING}/assets" "${STAGING}/upstream/qwen3.6-27b"
37
 
38
  cp "${ROOT}/release/MODEL_CARD_DRAFT.md" "${STAGING}/README.md"
39
+ cp "${ROOT}/release/assets/RMDWlogo.png" "${STAGING}/assets/RMDWlogo.png"
40
  cp "${ROOT}/release/PUBLIC_TESTING_QUICKSTART.md" "${STAGING}/PUBLIC_TESTING_QUICKSTART.md"
41
  cp "${ROOT}/release/LOCAL_TEST_INSTRUCTIONS.md" "${STAGING}/LOCAL_TEST_INSTRUCTIONS.md"
42
  cp "${ROOT}/release/SERVING_BENCHMARKS.md" "${STAGING}/SERVING_BENCHMARKS.md"
 
131
  FINAL_RELEASE_REPORT.md \
132
  GOAL_COMPLETION_AUDIT.md \
133
  HF_UPLOAD_EVIDENCE.md \
134
+ assets/RMDWlogo.png \
135
  MERGED_MODEL_RELEASE_MANIFEST.json \
136
  upstream/qwen3.6-27b/LICENSE; do
137
  test -f '${MODEL_REMOTE}/'\${required} || { echo \"Missing synced metadata: \${required}\" >&2; exit 2; }
scripts/upload_hf_merged_model_from_gojira_b.sh CHANGED
@@ -75,6 +75,7 @@ kaiju_gojira_b_ssh "
75
  FINAL_RELEASE_REPORT.md \
76
  GOAL_COMPLETION_AUDIT.md \
77
  HF_UPLOAD_EVIDENCE.md \
 
78
  MERGED_MODEL_RELEASE_MANIFEST.json \
79
  upstream/qwen3.6-27b/LICENSE; do
80
  test -f '${MODEL_REMOTE}/'\${required} || {
 
75
  FINAL_RELEASE_REPORT.md \
76
  GOAL_COMPLETION_AUDIT.md \
77
  HF_UPLOAD_EVIDENCE.md \
78
+ assets/RMDWlogo.png \
79
  MERGED_MODEL_RELEASE_MANIFEST.json \
80
  upstream/qwen3.6-27b/LICENSE; do
81
  test -f '${MODEL_REMOTE}/'\${required} || {