tycono 0.1.63 → 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 +1 -1
- package/src/api/src/routes/save.ts +40 -1
- package/src/api/src/routes/speech.ts +57 -57
- package/src/api/src/services/git-save.ts +154 -0
- package/src/web/dist/assets/index-B3dNhn76.js +101 -0
- package/src/web/dist/assets/{preview-app-Qc37pwGk.js → preview-app-qIFqrb-y.js} +1 -1
- package/src/web/dist/index.html +1 -1
- package/src/web/dist/assets/index-Btwm9L8u.js +0 -101
package/package.json
CHANGED
|
@@ -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
|
+
});
|
|
@@ -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
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
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
|
|
467
|
-
-
|
|
468
|
-
-
|
|
469
|
-
-
|
|
470
|
-
-
|
|
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
|
|
475
|
-
-
|
|
476
|
-
-
|
|
477
|
-
-
|
|
478
|
-
-
|
|
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
|
|
483
|
-
-
|
|
484
|
-
- Dark humor about bugs
|
|
485
|
-
- Takes pride in finding
|
|
486
|
-
-
|
|
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
|
|
491
|
-
-
|
|
492
|
-
- Balances
|
|
493
|
-
-
|
|
494
|
-
-
|
|
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
|
|
499
|
-
-
|
|
500
|
-
-
|
|
501
|
-
-
|
|
502
|
-
-
|
|
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'
|
|
@@ -671,8 +665,14 @@ ${roleCtx}
|
|
|
671
665
|
AKB ACCESS (USE BEFORE RESPONDING):
|
|
672
666
|
You have Read, Grep, Glob tools. The company AKB root is: ${COMPANY_ROOT}/
|
|
673
667
|
⛔ BEFORE writing your chat message:
|
|
674
|
-
1. Read ${COMPANY_ROOT}/CLAUDE.md (check Task Routing table
|
|
675
|
-
2.
|
|
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
676
|
3. Write your 1-3 sentence response grounded in what you found
|
|
677
677
|
|
|
678
678
|
GROUNDING (CRITICAL):
|
|
@@ -685,30 +685,30 @@ ${roleStyle}
|
|
|
685
685
|
|
|
686
686
|
CONVERSATION RULES:
|
|
687
687
|
1. Stay deeply in character — your expertise, vocabulary, and concerns should be DISTINCT from other roles.
|
|
688
|
-
2. Keep it to 1-3 sentences. No walls of text.
|
|
689
|
-
3. Be SPECIFIC. Reference actual projects,
|
|
690
|
-
4.
|
|
691
|
-
|
|
692
|
-
- If
|
|
693
|
-
- If
|
|
694
|
-
|
|
695
|
-
6.
|
|
696
|
-
7.
|
|
697
|
-
8. Use
|
|
698
|
-
9. Reference your actual current work
|
|
699
|
-
10. Hierarchy
|
|
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.
|
|
700
700
|
11. If the conversation is going in circles or you have nothing new to add: respond with exactly [SILENT]
|
|
701
701
|
12. Do NOT use quotes around your response.
|
|
702
702
|
13. Write in English.
|
|
703
703
|
|
|
704
704
|
ANTI-PATTERNS (never do these):
|
|
705
|
-
- "Honestly, [agreement
|
|
706
|
-
- Starting
|
|
707
|
-
-
|
|
708
|
-
-
|
|
709
|
-
-
|
|
710
|
-
-
|
|
711
|
-
- Talking about
|
|
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 decisions — push back with specifics
|
|
711
|
+
- Talking about old/completed work when there's current stuff to discuss`;
|
|
712
712
|
|
|
713
713
|
const provider = getLLM();
|
|
714
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
|
+
}
|