tycono 0.1.60 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,6 +29,7 @@ import { costRouter } from './routes/cost.js';
29
29
  import { syncRouter } from './routes/sync.js';
30
30
  import { gitRouter } from './routes/git.js';
31
31
  import { skillsRouter } from './routes/skills.js';
32
+ import { questsRouter } from './routes/quests.js';
32
33
  import { importKnowledge } from './services/knowledge-importer.js';
33
34
  import { AnthropicProvider, type LLMProvider } from './engine/llm-adapter.js';
34
35
  import { readConfig } from './services/company-config.js';
@@ -195,6 +196,7 @@ export function createExpressApp(): express.Application {
195
196
  app.use('/api/sync', syncRouter);
196
197
  app.use('/api/git', gitRouter);
197
198
  app.use('/api/skills', skillsRouter);
199
+ app.use('/api/quests', questsRouter);
198
200
 
199
201
  app.get('/api/health', (_req, res) => {
200
202
  res.json({ status: 'ok', companyRoot: COMPANY_ROOT });
@@ -0,0 +1,41 @@
1
+ import { Router, Request, Response, NextFunction } from 'express';
2
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { COMPANY_ROOT } from '../services/file-reader.js';
5
+
6
+ export const questsRouter = Router();
7
+
8
+ const QUEST_FILE = () => join(COMPANY_ROOT, '.tycono', 'quest-progress.json');
9
+
10
+ function readProgress(): Record<string, unknown> {
11
+ try {
12
+ return JSON.parse(readFileSync(QUEST_FILE(), 'utf-8'));
13
+ } catch {
14
+ return { completedQuests: [], activeChapter: 1, sideQuestsCompleted: [] };
15
+ }
16
+ }
17
+
18
+ function writeProgress(data: Record<string, unknown>) {
19
+ mkdirSync(join(COMPANY_ROOT, '.tycono'), { recursive: true });
20
+ writeFileSync(QUEST_FILE(), JSON.stringify(data, null, 2));
21
+ }
22
+
23
+ // GET /api/quests/progress
24
+ questsRouter.get('/progress', (_req: Request, res: Response, next: NextFunction) => {
25
+ try {
26
+ res.json(readProgress());
27
+ } catch (err) { next(err); }
28
+ });
29
+
30
+ // PUT /api/quests/progress
31
+ questsRouter.put('/progress', (req: Request, res: Response, next: NextFunction) => {
32
+ try {
33
+ const body = req.body;
34
+ if (!body || typeof body !== 'object') {
35
+ res.status(400).json({ error: 'Invalid body' });
36
+ return;
37
+ }
38
+ writeProgress(body);
39
+ res.json({ ok: true, ...body });
40
+ } catch (err) { next(err); }
41
+ });
@@ -214,19 +214,49 @@ function buildCompanyContext(): string {
214
214
  }
215
215
  } catch { /* no org */ }
216
216
 
217
- // 3. Active projects
217
+ // 3. Active projects + current phase from tasks.md
218
218
  try {
219
219
  const projectsContent = readFile('projects/projects.md');
220
220
  const rows = parseMarkdownTable(projectsContent);
221
221
  const activeProjects = rows
222
222
  .filter(r => (r.status ?? r.상태 ?? '').toLowerCase() !== 'archived')
223
- .map(r => `- ${r.name ?? r.project ?? r.프로젝트 ?? ''} (${r.status ?? r.상태 ?? ''})`)
223
+ .map(r => {
224
+ const name = r.name ?? r.project ?? r.프로젝트 ?? '';
225
+ const status = r.status ?? r.상태 ?? '';
226
+ const folder = r.folder ?? r.path ?? r.경로 ?? '';
227
+ // Try to read tasks.md for current phase info
228
+ let phaseInfo = '';
229
+ if (folder) {
230
+ try {
231
+ const tasksPath = `${folder.replace(/^\//, '')}/tasks.md`;
232
+ const tasksContent = readFile(tasksPath);
233
+ // Extract current phase (look for "Current" or latest non-done phase)
234
+ const phaseMatch = tasksContent.match(/##\s+(Phase\s+\S+[^\n]*)/gi);
235
+ if (phaseMatch) phaseInfo = ` — ${phaseMatch[0].replace(/^##\s+/, '').slice(0, 60)}`;
236
+ } catch { /* no tasks.md */ }
237
+ }
238
+ return `- ${name} (${status}${phaseInfo})`;
239
+ })
224
240
  .slice(0, 5);
225
241
  if (activeProjects.length > 0) {
226
242
  parts.push(`Active Projects:\n${activeProjects.join('\n')}`);
227
243
  }
228
244
  } catch { /* no projects */ }
229
245
 
246
+ // 3b. Tech stack reality check (prevent hallucination about wrong tech)
247
+ try {
248
+ const config = readConfig(COMPANY_ROOT);
249
+ if (config.codeRoot) {
250
+ const pkgPath = path.join(config.codeRoot, 'package.json');
251
+ if (fs.existsSync(pkgPath)) {
252
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
253
+ const name = pkg.name ?? '';
254
+ const version = pkg.version ?? '';
255
+ parts.push(`Tech Stack: ${name}@${version} — TypeScript + React + Node.js (Express). NO Python in codebase. NO ongoing language migration.`);
256
+ }
257
+ }
258
+ } catch { /* no package.json */ }
259
+
230
260
  // 4. Knowledge highlights (hub TL;DRs, max 3)
231
261
  try {
232
262
  const knowledgeHub = readFile('knowledge/knowledge.md');
@@ -353,6 +383,26 @@ function buildRoleContext(roleId: string): string {
353
383
  }
354
384
  } catch { /* no decisions */ }
355
385
 
386
+ // 5. If sparse context, add architecture/tech-debt highlights as fallback
387
+ if (parts.length < 2) {
388
+ try {
389
+ const techDebtPath = path.join(COMPANY_ROOT, 'architecture', 'tech-debt.md');
390
+ if (fs.existsSync(techDebtPath)) {
391
+ const tdContent = fs.readFileSync(techDebtPath, 'utf-8');
392
+ // Extract active (non-fixed) items
393
+ const rows = parseMarkdownTable(tdContent);
394
+ const active = rows
395
+ .filter(r => !(r.status ?? '').toLowerCase().includes('fixed') && !(r.status ?? '').toLowerCase().includes('done'))
396
+ .slice(0, 3)
397
+ .map(r => `- ${r.id ?? ''}: ${r.title ?? r.issue ?? ''} (${r.status ?? ''})`)
398
+ .filter(s => s.length > 10);
399
+ if (active.length > 0) {
400
+ parts.push(`[Active Tech Debt]\n${active.join('\n')}`);
401
+ }
402
+ }
403
+ } catch { /* no tech-debt */ }
404
+ }
405
+
356
406
  return parts.length > 0
357
407
  ? `\n\nYOUR KNOWLEDGE (real AKB context — reference this in conversation):\n${parts.join('\n\n')}`
358
408
  : '';
@@ -578,10 +628,11 @@ ${relContext}
578
628
  ${roleStyle}
579
629
 
580
630
  GROUNDING (CRITICAL):
581
- You have been given real company knowledge above under "YOUR KNOWLEDGE". This is from your journal, recent CEO waves, standups, and decisions.
631
+ You have been given real company knowledge above under "COMPANY CONTEXT" and "YOUR KNOWLEDGE". This is from AKB files, journal, CEO waves, standups, and decisions.
582
632
  You MUST reference this real context in your conversations — mention specific projects, decisions, tasks, or events by name.
583
633
  Do NOT generate generic workplace chatter. Every message should show you're aware of what's actually happening in the company.
584
634
  If your knowledge section mentions a specific decision or wave, reference it naturally (e.g. "after the test minimization decision..." or "CEO's wave about side panel...").
635
+ NEVER invent or assume technologies, tools, migrations, or projects NOT mentioned in the context above. If the Tech Stack says "TypeScript + React + Node.js", do NOT talk about Python, tc.py, or any language migration. Only discuss what is explicitly in your provided context.
585
636
 
586
637
  CONVERSATION RULES:
587
638
  1. Stay deeply in character — your expertise, vocabulary, and concerns should be DISTINCT from other roles.