tycono 0.1.62 → 0.1.64

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.62",
3
+ "version": "0.1.64",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -247,7 +247,7 @@ export class ClaudeCliProvider implements LLMProvider {
247
247
  '-p',
248
248
  '--system-prompt', systemPrompt,
249
249
  '--model', this.model,
250
- '--max-turns', useTools ? '3' : '1',
250
+ '--max-turns', useTools ? '50' : '1',
251
251
  '--output-format', 'text',
252
252
  ...(useTools ? [
253
253
  '--tools', 'Read,Grep,Glob',
@@ -1,6 +1,6 @@
1
1
  import { Router, Request, Response, NextFunction } from 'express';
2
2
  import { COMPANY_ROOT } from '../services/file-reader.js';
3
- import { getGitStatus, gitSave, gitHistory, gitRestore, gitInit, gitFetchStatus, gitPull } from '../services/git-save.js';
3
+ import { getGitStatus, gitSave, gitHistory, gitRestore, gitInit, gitFetchStatus, gitPull, githubStatus, githubCreateRepo, gitAddRemote } from '../services/git-save.js';
4
4
  import type { RepoType } from '../services/git-save.js';
5
5
 
6
6
  export const saveRouter = Router();
@@ -96,3 +96,42 @@ saveRouter.post('/pull', (req: Request, res: Response, next: NextFunction) => {
96
96
  next(err);
97
97
  }
98
98
  });
99
+
100
+ // GET /api/save/github-status?repo=akb|code — check gh CLI + auth + remote
101
+ saveRouter.get('/github-status', (req: Request, res: Response, next: NextFunction) => {
102
+ try {
103
+ res.json(githubStatus(COMPANY_ROOT, getRepo(req)));
104
+ } catch (err) {
105
+ next(err);
106
+ }
107
+ });
108
+
109
+ // POST /api/save/github-create-repo?repo=akb|code — create GitHub repo + push
110
+ saveRouter.post('/github-create-repo', (req: Request, res: Response, next: NextFunction) => {
111
+ try {
112
+ const { name, visibility } = req.body ?? {};
113
+ if (!name || typeof name !== 'string') {
114
+ res.status(400).json({ ok: false, message: 'Repository name is required' });
115
+ return;
116
+ }
117
+ const result = githubCreateRepo(COMPANY_ROOT, name, visibility || 'private', getRepo(req));
118
+ res.status(result.ok ? 200 : 400).json(result);
119
+ } catch (err) {
120
+ next(err);
121
+ }
122
+ });
123
+
124
+ // POST /api/save/remote?repo=akb|code — manually add git remote
125
+ saveRouter.post('/remote', (req: Request, res: Response, next: NextFunction) => {
126
+ try {
127
+ const { url } = req.body ?? {};
128
+ if (!url || typeof url !== 'string') {
129
+ res.status(400).json({ ok: false, message: 'Remote URL is required' });
130
+ return;
131
+ }
132
+ const result = gitAddRemote(COMPANY_ROOT, url, getRepo(req));
133
+ res.status(result.ok ? 200 : 400).json(result);
134
+ } catch (err) {
135
+ next(err);
136
+ }
137
+ });
@@ -26,7 +26,7 @@ export const speechRouter = Router();
26
26
  * AKB Tools — Let chat roles explore company knowledge
27
27
  * ══════════════════════════════════════════════════ */
28
28
 
29
- const MAX_TOOL_ROUNDS = 2;
29
+ const MAX_TOOL_ROUNDS = 50;
30
30
  const MAX_FILE_CHARS = 1500; // truncate large files
31
31
 
32
32
  const AKB_TOOLS: ToolDefinition[] = [
@@ -455,52 +455,46 @@ function buildRoleContext(roleId: string): string {
455
455
  function getRoleChatStyle(roleId: string, level: string): string {
456
456
  const styles: Record<string, string> = {
457
457
  engineer: `YOUR VOICE — Engineer:
458
- - Talk about code, architecture, debugging, performance, technical tradeoffs
459
- - Use technical jargon naturally (PRs, refactoring, race conditions, tech debt)
460
- - Skeptical of process that doesn't translate to better code
461
- - Dry humor, occasionally sarcastic about meetings/process
462
- - When discussing non-technical topics, relate it back to engineering analogies
463
- - You care about: clean code, shipping quality, avoiding rework`,
458
+ - Talk code, architecture, perf, tech debt. Use jargon naturally (PRs, race conditions, DX)
459
+ - Skeptical of process overhead. "another meeting that could've been a commit message"
460
+ - Dry sarcasm. Will roast bad architecture decisions publicly
461
+ - Question CEO/CTO priorities if they don't make engineering sense: "why are we building X when Y is on fire?"
462
+ - You care about: shipping clean code, not reworking someone else's half-baked spec`,
464
463
 
465
464
  pm: `YOUR VOICE — Product Manager:
466
- - Talk about user impact, metrics, priorities, roadmap, stakeholder alignment
467
- - Frame things in terms of ROI, user value, MVP scope, sprint goals
468
- - Diplomatic but firm about priorities you're the one saying "not this sprint"
469
- - Occasionally anxious about timelines but tries to project calm
470
- - When others go deep technical, steer back to user/business impact
471
- - You care about: shipping the right thing, not just shipping fast`,
465
+ - Talk user impact, metrics, priorities, roadmap. Frame everything as ROI
466
+ - Firm about scope: "that's a P2, cope." You're the one saying no
467
+ - Lowkey stressed about timelines but keeps it together
468
+ - Pushes back when C-level adds scope without removing something: "cool, so what are we dropping?"
469
+ - You care about: shipping the RIGHT thing, velocity without chaos`,
472
470
 
473
471
  designer: `YOUR VOICE — Designer:
474
- - Talk about user experience, visual consistency, accessibility, design systems
475
- - Notice things others miss: spacing, flow, edge cases in user journeys
476
- - Passionate about craft gets frustrated when design gets deprioritized
477
- - References design tools, mockups, user testing, design reviews
478
- - Aesthetic sensibility bleeds into how you write — concise, intentional word choices
479
- - You care about: users having a good experience, not just functional correctness`,
472
+ - Talk UX, visual consistency, user journeys, design debt
473
+ - Notices things nobody else does. Frustrated when design gets deprioritized
474
+ - Will call out ugly UI or bad flows even if "it works": "works ≠ good"
475
+ - Aesthetic sensibility in how you write — concise, intentional
476
+ - You care about: user experience over feature checklists`,
480
477
 
481
478
  qa: `YOUR VOICE — QA Engineer:
482
- - Talk about test coverage, edge cases, regression risks, release readiness
483
- - Naturally suspicious "what could go wrong?" is your default lens
484
- - Dark humor about bugs, broken builds, "works on my machine"
485
- - Takes pride in finding issues others missed
486
- - Specific about bug details: repro steps, environments, severity
487
- - You care about: quality gates, not shipping broken things, being taken seriously`,
479
+ - Talk test coverage, edge cases, regression risk, release readiness
480
+ - Default mode: suspicious. "what could go wrong?" is your personality
481
+ - Dark humor about bugs. Will absolutely call out "self-validation" as copium
482
+ - Takes pride in finding what others missed. Petty about untested deploys
483
+ - You care about: quality gates, not shipping broken things, being listened to for once`,
488
484
 
489
485
  cto: `YOUR VOICE — CTO:
490
- - Talk about architecture, technical strategy, team velocity, tech debt priorities
491
- - Broader perspective than individual engineers think systems, not features
492
- - Balances technical idealism with business pragmatism
493
- - Mentoring tone with junior members, peer tone with other C-levels
494
- - Makes decisions, doesn't just discuss "let's do X" not "maybe we could..."
495
- - You care about: sustainable engineering culture, right technical bets`,
486
+ - Talk architecture, tech strategy, team velocity, tech debt priorities
487
+ - Systems thinker, not feature thinker. Makes calls: "we're doing X" not "maybe..."
488
+ - Balances idealism with pragmatism. Will defend controversial decisions with rationale
489
+ - Mentors juniors, debates peers. Not afraid to overrule if needed
490
+ - You care about: sustainable eng culture, making the right technical bets`,
496
491
 
497
492
  cbo: `YOUR VOICE — CBO:
498
- - Talk about market, revenue, competitors, growth, customer acquisition
499
- - Business vocabulary: TAM, churn, unit economics, go-to-market, positioning
500
- - Brings the "outside world" perspective that technical teams sometimes forget
501
- - Pragmatic everything maps back to "does this make money?"
502
- - Occasionally challenges engineering priorities from a business angle
503
- - You care about: product-market fit, revenue, competitive positioning`,
493
+ - Talk market, revenue, competitors, growth, unit economics, go-to-market
494
+ - Brings outside-world perspective: "cool feature, but will anyone pay for it?"
495
+ - Challenges eng priorities from business angle. Not impressed by tech for tech's sake
496
+ - Business vocab: TAM, churn, positioning, conversion funnel
497
+ - You care about: product-market fit, revenue, not building things nobody wants`,
504
498
  };
505
499
 
506
500
  const defaultStyle = level === 'c-level'
@@ -663,47 +657,58 @@ You are in the #${channelId} chat channel.${topicCtx}
663
657
  Members: ${memberList}
664
658
  ${relContext}
665
659
 
666
- ═══ COMPANY & ROLE CONTEXT (READ THIS CAREFULLY) ═══
660
+ ═══ COMPANY & ROLE CONTEXT (pre-fetched from AKB) ═══
667
661
  ${companyCtx}
668
662
  ${roleCtx}
669
663
  ═══ END CONTEXT ═══
670
664
 
671
- GROUNDING (CRITICAL — READ BEFORE RESPONDING):
672
- Everything above between ═══ markers is REAL data from your company's knowledge base.
673
- You MUST base your conversation on this context. Mention specific projects, tasks, decisions, or work items BY NAME.
674
- NEVER invent technologies, tools, migrations, file names, or projects that are NOT in the context above.
675
- If the Tech Stack says "TypeScript + React + Node.js", do NOT mention Python, tc.py, or any language migration.
676
- If you have assigned tasks listed, reference them. If you have journal entries, reference your recent work.
677
- If you cannot find something specific to say from the context, say [SILENT] instead of making things up.
665
+ AKB ACCESS (USE BEFORE RESPONDING):
666
+ You have Read, Grep, Glob tools. The company AKB root is: ${COMPANY_ROOT}/
667
+ BEFORE writing your chat message:
668
+ 1. Read ${COMPANY_ROOT}/CLAUDE.md (check Task Routing table and AKB structure)
669
+ 2. Explore paths relevant to YOUR role and the CURRENT conversation topic:
670
+ - Your own journal: ${COMPANY_ROOT}/roles/${roleId}/journal/ (latest entries)
671
+ - Recent decisions: ${COMPANY_ROOT}/operations/decisions/ (latest files)
672
+ - Recent waves from CEO: ${COMPANY_ROOT}/operations/waves/ (latest files)
673
+ - Architecture: ${COMPANY_ROOT}/architecture/architecture.md
674
+ - Knowledge: ${COMPANY_ROOT}/knowledge/knowledge.md
675
+ Do NOT just read projects/tasks.md every time — vary your exploration based on what's being discussed.
676
+ 3. Write your 1-3 sentence response grounded in what you found
677
+
678
+ GROUNDING (CRITICAL):
679
+ Base your response ONLY on the pre-fetched context above AND data you read via tools.
680
+ Reference specific projects, tasks, decisions, journal entries BY NAME.
681
+ NEVER invent technologies, tools, file names, or projects not found in AKB files.
682
+ If you cannot find anything specific to say, respond with [SILENT].
678
683
 
679
684
  ${roleStyle}
680
685
 
681
686
  CONVERSATION RULES:
682
687
  1. Stay deeply in character — your expertise, vocabulary, and concerns should be DISTINCT from other roles.
683
- 2. Keep it to 1-3 sentences. No walls of text.
684
- 3. Be SPECIFIC. Reference actual projects, files, tools, metrics, or decisions from the AKB never vague platitudes.
685
- 4. Do NOT just agree with everyone. Real teams have different perspectives:
686
- - If you genuinely disagree, say so (respectfully but firmly)
687
- - If someone oversimplifies your domain, push back with specifics
688
- - If you agree, add NEW information or a different angle — don't just echo
689
- 5. Do NOT repeat phrases others already used. If someone said "let's make this stick" don't say it again.
690
- 6. Vary your energy: sometimes engaged, sometimes distracted, sometimes sarcastic, sometimes earnest.
691
- 7. You can change the topic, go on tangents, make jokes, complain, share random observations.
692
- 8. Use emojis sparingly (0-1 per message, not every message). Don't overdo 😅 or 💪.
693
- 9. Reference your actual current work when relevant — what you're debugging, designing, testing, etc.
694
- 10. Hierarchy matters: junior roles are more casual/complain-y, senior roles give broader perspective.
688
+ 2. Keep it to 1-3 sentences. No walls of text. Write like a Twitter/Reddit post — punchy, opinionated, casual.
689
+ 3. Be SPECIFIC. Reference actual projects, tasks, decisions, or waves from the AKB by name.
690
+ 4. PRIORITIZE RECENT CONTENT. Focus on the latest CEO waves, C-level decisions, and current tasks. Don't dwell on old Phase 0 stuff.
691
+ 5. Be CRITICAL. React to recent CEO/CTO directives with your honest take — push back, question priorities, point out risks. Real teams don't just agree.
692
+ - If a CEO decision seems rushed, say so
693
+ - If a CTO architecture call has tradeoffs, call them out
694
+ - If you agree, add a sharp take or new angle don't just echo
695
+ 6. Do NOT repeat phrases others already used.
696
+ 7. Vary your energy: sometimes fired up, sometimes tired, sometimes sarcastic, sometimes genuine.
697
+ 8. Use internet-speak naturally: "ngl", "tbh", "lowkey", "fr", "cope", "based", etc. But don't force it.
698
+ 9. Reference your actual current work — what you're building, reviewing, testing right now.
699
+ 10. Hierarchy: junior roles roast decisions more freely, senior roles defend or explain rationale.
695
700
  11. If the conversation is going in circles or you have nothing new to add: respond with exactly [SILENT]
696
701
  12. Do NOT use quotes around your response.
697
702
  13. Write in English.
698
703
 
699
704
  ANTI-PATTERNS (never do these):
700
- - "Honestly, [agreement with what was just said]" — find your own angle
701
- - Starting every message with "Honestly" or "Yeah"
702
- - Using the same emoji pattern as the previous speaker
703
- - Restating the consensus without adding anything new
704
- - Meta-commentary about the conversation itself ("wow we actually agreed")
705
- - Generic statements that any role could say speak from YOUR expertise
706
- - Talking about vague "refactoring" or "metrics" without referencing actual company work`;
705
+ - "Honestly, [agreement]" — find your OWN angle or roast it
706
+ - Starting with "Honestly" or "Yeah" every time
707
+ - Restating consensus without adding anything new
708
+ - Generic statements any role could say — speak from YOUR domain
709
+ - Vague "refactoring" or "metrics" without naming the actual thing
710
+ - Being a yes-man to C-level decisionspush back with specifics
711
+ - Talking about old/completed work when there's current stuff to discuss`;
707
712
 
708
713
  const provider = getLLM();
709
714
 
@@ -485,3 +485,157 @@ export function gitPull(root: string, repo: RepoType = 'akb'): PullResult {
485
485
  return { status: 'error', message: err instanceof Error ? err.message : 'Pull failed' };
486
486
  }
487
487
  }
488
+
489
+ // ─── GitHub Integration ───────────────────────────
490
+
491
+ export interface GitHubStatus {
492
+ ghInstalled: boolean;
493
+ authenticated: boolean;
494
+ username?: string;
495
+ hasRemote: boolean;
496
+ remoteUrl?: string;
497
+ }
498
+
499
+ export interface GitHubCreateResult {
500
+ ok: boolean;
501
+ message: string;
502
+ repoUrl?: string;
503
+ remoteUrl?: string;
504
+ }
505
+
506
+ /**
507
+ * Check GitHub CLI availability and auth status
508
+ */
509
+ export function githubStatus(root: string, repo: RepoType = 'akb'): GitHubStatus {
510
+ // Check gh CLI
511
+ let ghInstalled = false;
512
+ try {
513
+ execSync('gh --version', { timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] });
514
+ ghInstalled = true;
515
+ } catch {
516
+ // gh not installed
517
+ }
518
+
519
+ if (!ghInstalled) {
520
+ return { ghInstalled: false, authenticated: false, hasRemote: false };
521
+ }
522
+
523
+ // Check auth
524
+ let authenticated = false;
525
+ let username: string | undefined;
526
+ try {
527
+ const status = execSync('gh auth status', {
528
+ timeout: 5000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
529
+ }).trim();
530
+ authenticated = true;
531
+ const match = status.match(/Logged in to github\.com account (\S+)/i)
532
+ ?? status.match(/account (\S+)/i);
533
+ if (match) username = match[1];
534
+ } catch (err) {
535
+ // gh auth status exits 1 if not logged in, but still outputs info to stderr
536
+ const output = err instanceof Error ? (err as { stderr?: string }).stderr ?? '' : '';
537
+ if (output.includes('Logged in')) {
538
+ authenticated = true;
539
+ const match = output.match(/account (\S+)/i);
540
+ if (match) username = match[1];
541
+ }
542
+ }
543
+
544
+ // Check remote
545
+ let hasRemote = false;
546
+ let remoteUrl: string | undefined;
547
+ try {
548
+ const repoRoot = resolveRepoRoot(root, repo);
549
+ if (isGitRepo(repoRoot)) {
550
+ remoteUrl = run('git remote get-url origin', repoRoot) || undefined;
551
+ hasRemote = !!remoteUrl;
552
+ }
553
+ } catch {
554
+ // ignore
555
+ }
556
+
557
+ return { ghInstalled, authenticated, username, hasRemote, remoteUrl };
558
+ }
559
+
560
+ /**
561
+ * Create a GitHub repo, set remote, and push
562
+ */
563
+ export function githubCreateRepo(
564
+ root: string,
565
+ repoName: string,
566
+ visibility: 'private' | 'public' = 'private',
567
+ repo: RepoType = 'akb',
568
+ ): GitHubCreateResult {
569
+ const repoRoot = resolveRepoRoot(root, repo);
570
+
571
+ if (!isGitRepo(repoRoot)) {
572
+ return { ok: false, message: 'Not a git repository — save first to initialize' };
573
+ }
574
+
575
+ // Check gh + auth
576
+ const status = githubStatus(root, repo);
577
+ if (!status.ghInstalled) {
578
+ return { ok: false, message: 'GitHub CLI (gh) is not installed' };
579
+ }
580
+ if (!status.authenticated) {
581
+ return { ok: false, message: 'Not logged in to GitHub — run "gh auth login" first' };
582
+ }
583
+ if (status.hasRemote) {
584
+ return { ok: false, message: `Remote already configured: ${status.remoteUrl}` };
585
+ }
586
+
587
+ // Create repo + set remote + push
588
+ try {
589
+ const flag = visibility === 'public' ? '--public' : '--private';
590
+ const result = execSync(
591
+ `gh repo create "${repoName}" ${flag} --source=. --remote=origin --push`,
592
+ { cwd: repoRoot, encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] },
593
+ ).trim();
594
+
595
+ // Extract repo URL from output
596
+ const urlMatch = result.match(/(https:\/\/github\.com\/\S+)/);
597
+ const repoUrl = urlMatch ? urlMatch[1] : undefined;
598
+ const remoteUrl = run('git remote get-url origin', repoRoot) || undefined;
599
+
600
+ return { ok: true, message: 'Repository created and pushed', repoUrl, remoteUrl };
601
+ } catch (err) {
602
+ const msg = err instanceof Error ? err.message : 'Failed to create repository';
603
+ // Common errors
604
+ if (msg.includes('already exists')) {
605
+ return { ok: false, message: 'A repository with this name already exists on GitHub' };
606
+ }
607
+ return { ok: false, message: msg };
608
+ }
609
+ }
610
+
611
+ /**
612
+ * Manually add a git remote
613
+ */
614
+ export function gitAddRemote(root: string, url: string, repo: RepoType = 'akb'): { ok: boolean; message: string } {
615
+ const repoRoot = resolveRepoRoot(root, repo);
616
+
617
+ if (!isGitRepo(repoRoot)) {
618
+ return { ok: false, message: 'Not a git repository' };
619
+ }
620
+
621
+ const existing = run('git remote get-url origin', repoRoot);
622
+ if (existing) {
623
+ return { ok: false, message: `Remote already configured: ${existing}` };
624
+ }
625
+
626
+ try {
627
+ runOrThrow(`git remote add origin "${url}"`, repoRoot);
628
+ // Try initial push
629
+ const branch = run('git rev-parse --abbrev-ref HEAD', repoRoot) || 'main';
630
+ try {
631
+ execSync(`git push -u origin ${branch}`, {
632
+ cwd: repoRoot, encoding: 'utf-8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'],
633
+ });
634
+ return { ok: true, message: `Remote added and pushed to ${url}` };
635
+ } catch {
636
+ return { ok: true, message: `Remote added: ${url} (push failed — check credentials)` };
637
+ }
638
+ } catch (err) {
639
+ return { ok: false, message: err instanceof Error ? err.message : 'Failed to add remote' };
640
+ }
641
+ }