@hanzlaa/rcode 3.4.4 → 3.4.6
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/AGENTS.md +1 -1
- package/CONTRIBUTING.md +63 -1
- package/README.md +9 -4
- package/cli/generate-command-skills.cjs +21 -9
- package/cli/index.js +0 -0
- package/cli/install.js +126 -7
- package/cli/lib/manifest.cjs +1 -1
- package/cli/uninstall.js +8 -0
- package/dist/rcode.js +1279 -2004
- package/package.json +16 -17
- package/rihal/agents/rihal-ahmed.md +2 -1
- package/rihal/agents/rihal-code-fixer.md +46 -0
- package/rihal/agents/rihal-code-reviewer.md +46 -1
- package/rihal/agents/rihal-deviation-analyzer.md +1 -0
- package/rihal/agents/rihal-docs-auditor.md +106 -1
- package/rihal/agents/rihal-edge-case-hunter.md +47 -1
- package/rihal/agents/rihal-executor.md +1 -1
- package/rihal/agents/rihal-khalid.md +40 -1
- package/rihal/agents/rihal-layla.md +2 -1
- package/rihal/agents/rihal-nasser.md +2 -1
- package/rihal/agents/rihal-noor.md +3 -2
- package/rihal/agents/rihal-nyquist-auditor.md +1 -1
- package/rihal/agents/rihal-phase-researcher.md +46 -1
- package/rihal/agents/rihal-planner.md +1 -1
- package/rihal/agents/rihal-profiler.md +45 -2
- package/rihal/agents/rihal-project-researcher.md +47 -0
- package/rihal/agents/rihal-remediation-planner.md +45 -0
- package/rihal/agents/rihal-roadmapper.md +46 -0
- package/rihal/agents/rihal-security-adversary.md +46 -1
- package/rihal/agents/rihal-security-auditor.md +45 -1
- package/rihal/agents/rihal-ui-auditor.md +44 -1
- package/rihal/agents/rihal-ux-designer.md +41 -1
- package/rihal/agents/rihal-zahra.md +2 -1
- package/rihal/agents/rihal-zayd.md +2 -1
- package/rihal/bin/lib/config.cjs +13 -1
- package/rihal/bin/lib/council-panel.cjs +185 -23
- package/rihal/bin/lib/roadmap.cjs +27 -2
- package/rihal/bin/rihal-tools.cjs +1837 -99
- package/rihal/commands/audit.md +2 -2
- package/rihal/commands/capture.md +12 -0
- package/rihal/commands/diagnose-issues.md +18 -0
- package/rihal/commands/discuss-phase-power.md +18 -0
- package/rihal/commands/feature-drift.md +18 -0
- package/rihal/commands/karpathy-audit.md +18 -0
- package/rihal/commands/lens-audit.md +70 -0
- package/rihal/commands/new-project-research.md +18 -0
- package/rihal/commands/new-project-roadmap.md +18 -0
- package/rihal/commands/phase.md +11 -0
- package/rihal/references/continuation-format.md +3 -3
- package/rihal/references/output-format.md +79 -0
- package/rihal/references/revision-loop.md +1 -1
- package/rihal/references/verb-dictionary.md +85 -28
- package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +1 -1
- package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/SKILL.md +12 -2
- package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/steps/step-04-final-validation.md +12 -0
- package/rihal/skills/actions/2-plan/rihal-create-prd/SKILL.md +12 -2
- package/rihal/skills/actions/2-plan/rihal-create-story/SKILL.md +12 -2
- package/rihal/skills/actions/4-implementation/rihal-browser-verify/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-code-review/SKILL.md +16 -4
- package/rihal/skills/actions/4-implementation/rihal-debug/SKILL.md +14 -1
- package/rihal/skills/actions/4-implementation/rihal-git-flow/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-incremental/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-perf/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-prove-it/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-scaffold-project/steps/step-01-target.md +6 -0
- package/rihal/skills/actions/4-implementation/rihal-source-truth/SKILL.md +1 -1
- package/rihal/skills/actions/4-implementation/rihal-sprint-planning/SKILL.md +14 -3
- package/rihal/skills/actions/4-implementation/rihal-trim/SKILL.md +1 -1
- package/rihal/skills/agents/ahmed-hassani-director/SKILL.md +15 -1
- package/rihal/skills/agents/dalil-scout/SKILL.md +14 -2
- package/rihal/skills/agents/fatima-qa/SKILL.md +16 -1
- package/rihal/skills/agents/haitham-frontend/SKILL.md +13 -1
- package/rihal/skills/agents/hanzla-engineer/SKILL.md +13 -1
- package/rihal/skills/agents/hussain-pm/SKILL.md +16 -1
- package/rihal/skills/agents/hussain-sm/SKILL.md +14 -1
- package/rihal/skills/agents/layla-designer/SKILL.md +13 -1
- package/rihal/skills/agents/majlis-council/SKILL.md +16 -1
- package/rihal/skills/agents/mariam-marketing/SKILL.md +14 -1
- package/rihal/skills/agents/nasser-eng-manager/SKILL.md +16 -1
- package/rihal/skills/agents/noor-writer/SKILL.md +15 -1
- package/rihal/skills/agents/raees-orchestrator/SKILL.md +15 -1
- package/rihal/skills/agents/rihal-cross-platform-auditor/SKILL.md +162 -0
- package/rihal/skills/agents/rihal-dep-auditor/SKILL.md +151 -0
- package/rihal/skills/agents/rihal-deviation-analyzer/SKILL.md +78 -0
- package/rihal/skills/agents/rihal-i18n-auditor/SKILL.md +152 -0
- package/rihal/skills/agents/rihal-observability-auditor/SKILL.md +156 -0
- package/rihal/skills/agents/sadiq-analyst/SKILL.md +12 -2
- package/rihal/skills/agents/waleed-architect/SKILL.md +12 -2
- package/rihal/skills/agents/yousef-backend/SKILL.md +12 -2
- package/rihal/skills/agents/zahra-branding/SKILL.md +15 -1
- package/rihal/skills/agents/zayd-ml/SKILL.md +13 -1
- package/rihal/skills/core/rihal-advanced-elicitation/SKILL.md +2 -2
- package/rihal/skills/core/rihal-auth-audit/SKILL.md +1 -1
- package/rihal/skills/core/rihal-brainstorming/SKILL.md +13 -2
- package/rihal/skills/core/rihal-client-gate/SKILL.md +1 -1
- package/rihal/skills/core/rihal-clone-website/SKILL.md +11 -1
- package/rihal/skills/core/rihal-deploy-unify/SKILL.md +1 -1
- package/rihal/skills/core/rihal-distillator/SKILL.md +2 -2
- package/rihal/skills/core/rihal-editorial-review-prose/SKILL.md +1 -1
- package/rihal/skills/core/rihal-editorial-review-structure/SKILL.md +2 -2
- package/rihal/skills/core/rihal-help/SKILL.md +18 -1
- package/rihal/skills/core/rihal-incident-record/SKILL.md +1 -1
- package/rihal/skills/core/rihal-index-docs/SKILL.md +1 -1
- package/rihal/skills/core/rihal-memory-audit/SKILL.md +18 -1
- package/rihal/skills/core/rihal-memory-init/SKILL.md +13 -1
- package/rihal/skills/core/rihal-memory-update/SKILL.md +13 -1
- package/rihal/skills/core/rihal-mvp-graduate/SKILL.md +1 -1
- package/rihal/skills/core/rihal-ocr-consistency/SKILL.md +1 -1
- package/rihal/skills/core/rihal-rebrand/SKILL.md +1 -1
- package/rihal/skills/core/rihal-review-adversarial-general/SKILL.md +1 -1
- package/rihal/skills/core/rihal-review-edge-case-hunter/SKILL.md +17 -1
- package/rihal/skills/core/rihal-shard-doc/SKILL.md +1 -1
- package/rihal/skills/core/rihal-theme-system/SKILL.md +1 -1
- package/rihal/team.yaml +0 -7
- package/rihal/templates/RESEARCH.md +84 -0
- package/rihal/templates/VALIDATION.md +45 -0
- package/rihal/templates/memory/INDEX.md +1 -0
- package/rihal/templates/memory/project/design-system.md +128 -0
- package/rihal/templates/summary.md +33 -3
- package/rihal/workflows/add-tests.md +1 -1
- package/rihal/workflows/add-todo.md +6 -0
- package/rihal/workflows/analyze-dependencies.md +6 -0
- package/rihal/workflows/audit-fix.md +12 -0
- package/rihal/workflows/audit-milestone.md +2 -2
- package/rihal/workflows/audit.md +23 -14
- package/rihal/workflows/autonomous-smart-discuss.md +247 -0
- package/rihal/workflows/autonomous.md +54 -267
- package/rihal/workflows/capture.md +60 -0
- package/rihal/workflows/chain.md +1 -1
- package/rihal/workflows/code-review-fix.md +6 -3
- package/rihal/workflows/code-review.md +34 -10
- package/rihal/workflows/complete-milestone.md +17 -8
- package/rihal/workflows/correct-course.md +6 -0
- package/rihal/workflows/council.md +37 -23
- package/rihal/workflows/create-architecture.md +31 -0
- package/rihal/workflows/create-epics-and-stories.md +7 -1
- package/rihal/workflows/create-prd.md +25 -0
- package/rihal/workflows/dashboard.md +1 -1
- package/rihal/workflows/debug.md +8 -0
- package/rihal/workflows/decisions.md +1 -1
- package/rihal/workflows/diff.md +6 -0
- package/rihal/workflows/discuss-phase-discuss-areas.md +271 -0
- package/rihal/workflows/discuss-phase.md +27 -266
- package/rihal/workflows/do.md +51 -12
- package/rihal/workflows/docs-update.md +3 -0
- package/rihal/workflows/document-project.md +7 -1
- package/rihal/workflows/edit-prd.md +31 -0
- package/rihal/workflows/enable-hooks.md +1 -1
- package/rihal/workflows/execute-regression-gates.md +131 -0
- package/rihal/workflows/execute-sprint.md +31 -2
- package/rihal/workflows/execute-verify-phase-goal.md +136 -0
- package/rihal/workflows/execute-waves.md +404 -0
- package/rihal/workflows/execute.md +101 -642
- package/rihal/workflows/feature-drift.md +243 -0
- package/rihal/workflows/forensics.md +10 -2
- package/rihal/workflows/health.md +65 -16
- package/rihal/workflows/help.md +36 -9
- package/rihal/workflows/import.md +17 -3
- package/rihal/workflows/init.md +20 -10
- package/rihal/workflows/install.md +2 -10
- package/rihal/workflows/lens-audit.md +689 -0
- package/rihal/workflows/map-codebase.md +7 -1
- package/rihal/workflows/memory-audit.md +67 -5
- package/rihal/workflows/memory-distill.md +10 -0
- package/rihal/workflows/memory-init.md +4 -0
- package/rihal/workflows/memory-update.md +4 -0
- package/rihal/workflows/new-milestone.md +7 -1
- package/rihal/workflows/new-project-create-roadmap.md +176 -0
- package/rihal/workflows/new-project-define-requirements.md +160 -0
- package/rihal/workflows/new-project-research-decision.md +247 -0
- package/rihal/workflows/new-project.md +3 -557
- package/rihal/workflows/note.md +1 -1
- package/rihal/workflows/phase.md +54 -0
- package/rihal/workflows/plan-milestone-gaps.md +1 -1
- package/rihal/workflows/plan-prd-express.md +108 -0
- package/rihal/workflows/plan-research-validation.md +313 -0
- package/rihal/workflows/plan-spawn-planner.md +204 -0
- package/rihal/workflows/plan.md +91 -532
- package/rihal/workflows/plant-seed.md +1 -1
- package/rihal/workflows/pr-branch.md +1 -1
- package/rihal/workflows/profile-user.md +1 -1
- package/rihal/workflows/quick.md +3 -3
- package/rihal/workflows/remove-phase.md +6 -1
- package/rihal/workflows/remove-workspace.md +6 -0
- package/rihal/workflows/rerun.md +1 -1
- package/rihal/workflows/research-phase.md +4 -2
- package/rihal/workflows/resume-work.md +8 -3
- package/rihal/workflows/retrospective.md +31 -0
- package/rihal/workflows/review-adversarial.md +12 -0
- package/rihal/workflows/review.md +6 -0
- package/rihal/workflows/scaffold-project.md +31 -0
- package/rihal/workflows/scan.md +10 -0
- package/rihal/workflows/secure-phase.md +15 -2
- package/rihal/workflows/session-report.md +32 -7
- package/rihal/workflows/ship.md +7 -2
- package/rihal/workflows/show.md +6 -0
- package/rihal/workflows/sprint-status.md +4 -4
- package/rihal/workflows/status.md +2 -2
- package/rihal/workflows/ui-phase.md +1 -1
- package/rihal/workflows/undo.md +2 -3
- package/rihal/workflows/update.md +2 -2
- package/rihal/workflows/validate-phase.md +1 -1
- package/rihal/workflows/validate-prd.md +31 -0
- package/rihal/workflows/verify-phase.md +38 -5
- package/rihal/workflows/verify-work.md +25 -11
- package/rihal/workflows/workstream.md +20 -8
- package/server/lib/html/client.js +13 -63
- package/server/lib/html/shell.js +0 -1
- package/server/lib/scanner.js +33 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Council panel selection — pure function that picks the right
|
|
3
|
-
* for a given
|
|
2
|
+
* Council panel selection — pure function that picks the right agents
|
|
3
|
+
* for a given question.
|
|
4
4
|
*
|
|
5
5
|
* This is the v2 version of the scorer, installed alongside rihal-tools.cjs
|
|
6
6
|
* at {project-root}/.rihal/bin/lib/council-panel.cjs. The helper binary
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* - Auditable: `explainSelection()` returns per-agent scores so users
|
|
17
17
|
* running with `--explain` can see why each agent was picked.
|
|
18
18
|
* - Cheap: zero LLM calls before the council starts.
|
|
19
|
+
* - Intent-matched: domain-specific panels. FE question → Haitham leads.
|
|
20
|
+
* BE question → Yousef leads. No strategic padding for technical work.
|
|
19
21
|
* - Optional team.yaml: If team.yaml exists at project root, reads
|
|
20
22
|
* domain_keywords from it. Fallback: hardcoded keywords below.
|
|
21
23
|
*
|
|
@@ -25,16 +27,17 @@
|
|
|
25
27
|
* else use hardcoded KEYWORDS below.
|
|
26
28
|
* 2. Normalize the question (lowercase, strip punctuation).
|
|
27
29
|
* 3. For each agent, sum the weight of every keyword that appears.
|
|
28
|
-
* 4. Apply priority boosts (Sadiq for strategic triggers,
|
|
29
|
-
* for
|
|
30
|
+
* 4. Apply priority boosts (Sadiq for strategic triggers, domain
|
|
31
|
+
* boosts for FE/BE/ML/deploy questions).
|
|
30
32
|
* 5. Named-agent mentions get +20 (overrides topic score).
|
|
31
|
-
* 6.
|
|
32
|
-
*
|
|
33
|
-
* 7.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* 9. If opts.
|
|
33
|
+
* 6. Detect domain from top-scoring agents: fe / be / ml / deploy /
|
|
34
|
+
* quality / strategic / market.
|
|
35
|
+
* 7. Sort by score desc. Tiebreaker: domain-specific padding order.
|
|
36
|
+
* 8. maxPanel=3 for technical domains (fe/be/ml/deploy/quality),
|
|
37
|
+
* maxPanel=4 for strategic/market. minPanel=1 always — no forced
|
|
38
|
+
* lame padding for specific technical questions.
|
|
39
|
+
* 9. If opts.full, return AGENT_IDS (canonical order).
|
|
40
|
+
* 10. If opts.agents, return that exact list (validated).
|
|
38
41
|
*
|
|
39
42
|
* The orchestrator is responsible for filtering the result to installed
|
|
40
43
|
* agents. This module returns the "ideal" panel; the workflow validates
|
|
@@ -307,6 +310,75 @@ const STRATEGIC_PADDING_ORDER = [
|
|
|
307
310
|
'zahra', 'zayd', 'mariam', 'noor',
|
|
308
311
|
];
|
|
309
312
|
|
|
313
|
+
// Domain-specific padding orders — used when a technical domain is clearly detected.
|
|
314
|
+
// These put the right specialists first and keep PM/strategy out unless asked.
|
|
315
|
+
const FRONTEND_PADDING_ORDER = [
|
|
316
|
+
'haitham', 'layla', 'zahra', 'yousef', 'waleed',
|
|
317
|
+
'fatima', 'sadiq', 'hussain-pm', 'zayd', 'khalid',
|
|
318
|
+
'nasser', 'ahmed-hassani', 'mariam', 'noor',
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
const BACKEND_PADDING_ORDER = [
|
|
322
|
+
'yousef', 'waleed', 'khalid', 'fatima', 'haitham',
|
|
323
|
+
'zayd', 'ahmed-hassani', 'sadiq', 'hussain-pm', 'layla',
|
|
324
|
+
'nasser', 'zahra', 'mariam', 'noor',
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
const ML_PADDING_ORDER = [
|
|
328
|
+
'zayd', 'yousef', 'waleed', 'fatima', 'khalid',
|
|
329
|
+
'haitham', 'sadiq', 'hussain-pm', 'ahmed-hassani', 'layla',
|
|
330
|
+
'nasser', 'zahra', 'mariam', 'noor',
|
|
331
|
+
];
|
|
332
|
+
|
|
333
|
+
const DEPLOY_PADDING_ORDER = [
|
|
334
|
+
'khalid', 'waleed', 'yousef', 'ahmed-hassani', 'fatima',
|
|
335
|
+
'sadiq', 'haitham', 'zayd', 'hussain-pm', 'nasser',
|
|
336
|
+
'layla', 'zahra', 'mariam', 'noor',
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
const QUALITY_PADDING_ORDER = [
|
|
340
|
+
'fatima', 'waleed', 'yousef', 'haitham', 'khalid',
|
|
341
|
+
'sadiq', 'zayd', 'ahmed-hassani', 'hussain-pm', 'layla',
|
|
342
|
+
'nasser', 'zahra', 'mariam', 'noor',
|
|
343
|
+
];
|
|
344
|
+
|
|
345
|
+
// Domain trigger arrays — when these fire, the question is clearly technical
|
|
346
|
+
// and should NOT be padded with PM/strategy agents.
|
|
347
|
+
const FE_TRIGGERS = [
|
|
348
|
+
'react', 'component', 'frontend', 'front-end', 'next.js', 'nextjs',
|
|
349
|
+
'tailwind', 'css', 'html', 'tsx', 'jsx', 'rtl', 'a11y', 'accessibility',
|
|
350
|
+
'ui ', 'ux ', 'layout', 'responsive', 'animation', 'hydration',
|
|
351
|
+
'bundle size', 'lighthouse', 'cls', 'lcp', 'tbt',
|
|
352
|
+
// Roman Urdu FE signals
|
|
353
|
+
'fe ', 'front end', 'button', 'page ', 'screen ', 'form ',
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
const BE_TRIGGERS = [
|
|
357
|
+
'backend', 'back-end', 'api', 'endpoint', 'server', 'prisma', 'database',
|
|
358
|
+
'query', 'schema', 'migration', 'queue', 'webhook', 'rest', 'graphql',
|
|
359
|
+
'n+1', 'index', 'latency', 'timeout', 'caching', 'redis', 'postgres',
|
|
360
|
+
'mysql', 'mongodb', 'bullmq', 'celery', 'worker', 'job', 'cron',
|
|
361
|
+
// Roman Urdu BE signals
|
|
362
|
+
'be ', 'db ', 'api call', 'server side',
|
|
363
|
+
];
|
|
364
|
+
|
|
365
|
+
const ML_TRIGGERS = [
|
|
366
|
+
'llm', 'model', 'embedding', 'rag', 'retrieval', 'vector', 'ocr',
|
|
367
|
+
'prompt', 'inference', 'fine-tun', 'dataset', 'eval', 'nlp',
|
|
368
|
+
'openai', 'anthropic', 'gemini', 'gpt', 'claude', 'mistral',
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
const DEPLOY_TRIGGERS = [
|
|
372
|
+
'deploy', 'docker', 'kubernetes', 'k8s', 'ci/cd', 'cicd', 'pipeline',
|
|
373
|
+
'rollback', 'incident', 'outage', 'monitoring', 'alert', 'sre',
|
|
374
|
+
'infra', 'cloud', 'aws', 'gcp', 'azure', 'vps',
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
const QUALITY_TRIGGERS = [
|
|
378
|
+
'test coverage', 'qa ', 'regression', 'flaky', 'production ready',
|
|
379
|
+
'ready to ship', 'release ready', 'perf test', 'load test', 'benchmark',
|
|
380
|
+
];
|
|
381
|
+
|
|
310
382
|
const AGENT_NAMES = {
|
|
311
383
|
sadiq: ['sadiq'],
|
|
312
384
|
'hussain-pm': ['hussain', 'hussain-pm', 'hussain pm'],
|
|
@@ -357,9 +429,64 @@ function applyPriorityBoosts(scores, normalizedQuestion) {
|
|
|
357
429
|
scores.mariam = (scores.mariam || 0) + 6; // Mariam leads market questions
|
|
358
430
|
scores['hussain-pm'] = (scores['hussain-pm'] || 0) + 3; // PM follows for scoping
|
|
359
431
|
}
|
|
432
|
+
// Domain boosts — lift the right technical expert when signal is clear
|
|
433
|
+
if (FE_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
|
|
434
|
+
scores.haitham = (scores.haitham || 0) + 4;
|
|
435
|
+
}
|
|
436
|
+
if (BE_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
|
|
437
|
+
scores.yousef = (scores.yousef || 0) + 4;
|
|
438
|
+
}
|
|
439
|
+
if (ML_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
|
|
440
|
+
scores.zayd = (scores.zayd || 0) + 4;
|
|
441
|
+
}
|
|
442
|
+
if (DEPLOY_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
|
|
443
|
+
scores.khalid = (scores.khalid || 0) + 4;
|
|
444
|
+
}
|
|
445
|
+
if (QUALITY_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
|
|
446
|
+
scores.fatima = (scores.fatima || 0) + 4;
|
|
447
|
+
}
|
|
360
448
|
return scores;
|
|
361
449
|
}
|
|
362
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Detect the primary domain of a question from its normalized text and scores.
|
|
453
|
+
* Returns: 'fe' | 'be' | 'ml' | 'deploy' | 'quality' | 'market' | 'strategic' | 'general'
|
|
454
|
+
*/
|
|
455
|
+
function detectDomain(normalizedQuestion, scores) {
|
|
456
|
+
const isMarket = MARKET_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
457
|
+
if (isMarket) return 'market';
|
|
458
|
+
|
|
459
|
+
const isStrategic = SADIQ_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
460
|
+
|
|
461
|
+
const feTrigger = FE_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
462
|
+
const beTrigger = BE_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
463
|
+
const mlTrigger = ML_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
464
|
+
const deployTrigger = DEPLOY_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
465
|
+
const qualityTrigger = QUALITY_TRIGGERS.some((t) => normalizedQuestion.includes(t));
|
|
466
|
+
|
|
467
|
+
// Multiple technical domains present — fall back to top-scoring agent
|
|
468
|
+
const technicalCount = [feTrigger, beTrigger, mlTrigger, deployTrigger, qualityTrigger].filter(Boolean).length;
|
|
469
|
+
if (technicalCount >= 2) {
|
|
470
|
+
// Resolve by whichever technical expert scored highest
|
|
471
|
+
const techLeaders = [
|
|
472
|
+
{ domain: 'fe', agent: 'haitham', score: scores.haitham || 0 },
|
|
473
|
+
{ domain: 'be', agent: 'yousef', score: scores.yousef || 0 },
|
|
474
|
+
{ domain: 'ml', agent: 'zayd', score: scores.zayd || 0 },
|
|
475
|
+
{ domain: 'deploy', agent: 'khalid', score: scores.khalid || 0 },
|
|
476
|
+
{ domain: 'quality', agent: 'fatima', score: scores.fatima || 0 },
|
|
477
|
+
].sort((a, b) => b.score - a.score);
|
|
478
|
+
if (techLeaders[0].score > 0) return techLeaders[0].domain;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (feTrigger) return 'fe';
|
|
482
|
+
if (beTrigger) return 'be';
|
|
483
|
+
if (mlTrigger) return 'ml';
|
|
484
|
+
if (deployTrigger) return 'deploy';
|
|
485
|
+
if (qualityTrigger) return 'quality';
|
|
486
|
+
if (isStrategic) return 'strategic';
|
|
487
|
+
return 'general';
|
|
488
|
+
}
|
|
489
|
+
|
|
363
490
|
function validateAgents(agents) {
|
|
364
491
|
const bad = agents.filter((id) => !AGENT_IDS.includes(id));
|
|
365
492
|
if (bad.length > 0) {
|
|
@@ -368,34 +495,59 @@ function validateAgents(agents) {
|
|
|
368
495
|
return agents;
|
|
369
496
|
}
|
|
370
497
|
|
|
498
|
+
const DOMAIN_PADDING = {
|
|
499
|
+
fe: FRONTEND_PADDING_ORDER,
|
|
500
|
+
be: BACKEND_PADDING_ORDER,
|
|
501
|
+
ml: ML_PADDING_ORDER,
|
|
502
|
+
deploy: DEPLOY_PADDING_ORDER,
|
|
503
|
+
quality: QUALITY_PADDING_ORDER,
|
|
504
|
+
market: MARKET_PADDING_ORDER,
|
|
505
|
+
strategic: STRATEGIC_PADDING_ORDER,
|
|
506
|
+
general: STRATEGIC_PADDING_ORDER,
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// Technical domains keep panels small — 1 right expert beats 3 wrong ones.
|
|
510
|
+
const DOMAIN_MAX_PANEL = {
|
|
511
|
+
fe: 3, be: 3, ml: 3, deploy: 3, quality: 3,
|
|
512
|
+
market: 4, strategic: 4, general: 3,
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// minPanel=1: never force-pad with irrelevant agents.
|
|
516
|
+
const DOMAIN_MIN_PANEL = {
|
|
517
|
+
fe: 1, be: 1, ml: 1, deploy: 1, quality: 1,
|
|
518
|
+
market: 2, strategic: 2, general: 1,
|
|
519
|
+
};
|
|
520
|
+
|
|
371
521
|
function selectPanel(question, opts = {}) {
|
|
372
522
|
if (opts.full) return [...AGENT_IDS];
|
|
373
523
|
if (opts.agents && opts.agents.length > 0) return validateAgents(opts.agents);
|
|
374
524
|
|
|
375
|
-
const maxPanel = opts.maxPanel || 5;
|
|
376
|
-
const minPanel = opts.minPanel || 3;
|
|
377
525
|
const normalized = normalize(question);
|
|
378
|
-
if (!normalized) return STRATEGIC_PADDING_ORDER.slice(0,
|
|
526
|
+
if (!normalized) return STRATEGIC_PADDING_ORDER.slice(0, 2);
|
|
379
527
|
|
|
380
528
|
const scores = {};
|
|
381
529
|
for (const agentId of AGENT_IDS) scores[agentId] = scoreAgent(agentId, normalized);
|
|
382
530
|
applyPriorityBoosts(scores, normalized);
|
|
383
531
|
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
const
|
|
532
|
+
const domain = detectDomain(normalized, scores);
|
|
533
|
+
const maxPanel = opts.maxPanel || DOMAIN_MAX_PANEL[domain];
|
|
534
|
+
const minPanel = opts.minPanel || DOMAIN_MIN_PANEL[domain];
|
|
535
|
+
const paddingPool = DOMAIN_PADDING[domain];
|
|
536
|
+
|
|
387
537
|
const ranked = [...AGENT_IDS]
|
|
388
538
|
.map((id) => ({ id, score: scores[id] }))
|
|
389
539
|
.sort((a, b) => {
|
|
390
540
|
if (b.score !== a.score) return b.score - a.score;
|
|
391
|
-
return
|
|
541
|
+
return paddingPool.indexOf(a.id) - paddingPool.indexOf(b.id);
|
|
392
542
|
});
|
|
393
543
|
|
|
394
544
|
const scored = ranked.filter((a) => a.score > 0).slice(0, maxPanel);
|
|
545
|
+
|
|
546
|
+
// If we have at least minPanel scored agents, return them — no padding needed.
|
|
395
547
|
if (scored.length >= minPanel) return scored.map((a) => a.id);
|
|
396
548
|
|
|
549
|
+
// Pad only up to minPanel using domain-appropriate agents.
|
|
397
550
|
const alreadyPicked = new Set(scored.map((a) => a.id));
|
|
398
|
-
const paddingPool = isMarket ? MARKET_PADDING_ORDER : isStrategic ? STRATEGIC_PADDING_ORDER : AGENT_IDS;
|
|
399
551
|
const padding = [];
|
|
400
552
|
for (const id of paddingPool) {
|
|
401
553
|
if (alreadyPicked.has(id)) continue;
|
|
@@ -410,11 +562,16 @@ function explainSelection(question, opts = {}) {
|
|
|
410
562
|
const scores = {};
|
|
411
563
|
for (const agentId of AGENT_IDS) scores[agentId] = scoreAgent(agentId, normalized);
|
|
412
564
|
applyPriorityBoosts(scores, normalized);
|
|
565
|
+
const domain = detectDomain(normalized, scores);
|
|
413
566
|
const panel = selectPanel(question, opts);
|
|
414
567
|
return {
|
|
415
|
-
question, normalized, scores, panel,
|
|
568
|
+
question, normalized, scores, panel, domain,
|
|
416
569
|
sadiq_triggered: SADIQ_TRIGGERS.some((t) => normalized.includes(t)),
|
|
417
570
|
pm_triggered: PM_TRIGGERS.some((t) => normalized.includes(t)),
|
|
571
|
+
fe_triggered: FE_TRIGGERS.some((t) => normalized.includes(t)),
|
|
572
|
+
be_triggered: BE_TRIGGERS.some((t) => normalized.includes(t)),
|
|
573
|
+
ml_triggered: ML_TRIGGERS.some((t) => normalized.includes(t)),
|
|
574
|
+
deploy_triggered: DEPLOY_TRIGGERS.some((t) => normalized.includes(t)),
|
|
418
575
|
};
|
|
419
576
|
}
|
|
420
577
|
|
|
@@ -494,8 +651,13 @@ function loadTeamConfig(projectRoot) {
|
|
|
494
651
|
}
|
|
495
652
|
|
|
496
653
|
module.exports = {
|
|
497
|
-
AGENT_IDS, KEYWORDS,
|
|
654
|
+
AGENT_IDS, KEYWORDS, AGENT_NAMES,
|
|
655
|
+
SADIQ_TRIGGERS, PM_TRIGGERS, MARKET_TRIGGERS,
|
|
656
|
+
FE_TRIGGERS, BE_TRIGGERS, ML_TRIGGERS, DEPLOY_TRIGGERS, QUALITY_TRIGGERS,
|
|
498
657
|
STRATEGIC_PADDING_ORDER, MARKET_PADDING_ORDER,
|
|
499
|
-
|
|
500
|
-
|
|
658
|
+
FRONTEND_PADDING_ORDER, BACKEND_PADDING_ORDER, ML_PADDING_ORDER,
|
|
659
|
+
DEPLOY_PADDING_ORDER, QUALITY_PADDING_ORDER,
|
|
660
|
+
DOMAIN_PADDING, DOMAIN_MAX_PANEL, DOMAIN_MIN_PANEL,
|
|
661
|
+
normalize, scoreAgent, applyPriorityBoosts, detectDomain,
|
|
662
|
+
selectPanel, explainSelection, loadTeamConfig,
|
|
501
663
|
};
|
|
@@ -19,7 +19,11 @@ function roadmapPathFor(projectRoot) {
|
|
|
19
19
|
* number, name, goal, section (raw markdown slice), headerIndex, sectionEnd
|
|
20
20
|
*/
|
|
21
21
|
function extractPhases(content) {
|
|
22
|
-
|
|
22
|
+
// Accept any of: ":", "—" (em-dash), "-" (hyphen) between phase number and name.
|
|
23
|
+
// Pre-#464 the regex required ":" only, which silently rejected heading-style
|
|
24
|
+
// ROADMAP using em-dash ("## Phase 6 — Name") and broke roadmap list-phases
|
|
25
|
+
// and roadmap get-phase. Same drift family as #455.
|
|
26
|
+
const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*[—\-:]\s*([^\n]+)/gi;
|
|
23
27
|
const hits = [];
|
|
24
28
|
let m;
|
|
25
29
|
while ((m = phasePattern.exec(content)) !== null) {
|
|
@@ -32,10 +36,26 @@ function extractPhases(content) {
|
|
|
32
36
|
const section = content.slice(h.headerIndex, end).trim();
|
|
33
37
|
const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
34
38
|
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
39
|
+
|
|
40
|
+
// Status parsing (Phase 10 / #466 / closes secondary part of #464).
|
|
41
|
+
// Maps the literal **Status:** line to a canonical enum.
|
|
42
|
+
const statusMatch = section.match(/\*\*Status(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
43
|
+
const statusRaw = statusMatch ? statusMatch[1].trim() : null;
|
|
44
|
+
let status = 'unknown';
|
|
45
|
+
if (statusRaw) {
|
|
46
|
+
const s = statusRaw.toLowerCase();
|
|
47
|
+
if (s.startsWith('complete')) status = 'complete';
|
|
48
|
+
else if (s.startsWith('active') || s.startsWith('in progress') || s.includes('sprint')) status = 'active';
|
|
49
|
+
else if (s.startsWith('planned')) status = 'planned';
|
|
50
|
+
else if (s.startsWith('closed')) status = 'closed';
|
|
51
|
+
}
|
|
52
|
+
|
|
35
53
|
phases.push({
|
|
36
54
|
number: h.number,
|
|
37
55
|
name: h.name,
|
|
38
56
|
goal,
|
|
57
|
+
status,
|
|
58
|
+
status_raw: statusRaw,
|
|
39
59
|
section,
|
|
40
60
|
headerIndex: h.headerIndex,
|
|
41
61
|
sectionEnd: end,
|
|
@@ -129,10 +149,15 @@ function cmdListPhases(projectRoot) {
|
|
|
129
149
|
const rp = roadmapPathFor(projectRoot);
|
|
130
150
|
if (!fs.existsSync(rp)) return [];
|
|
131
151
|
const content = fs.readFileSync(rp, 'utf8');
|
|
152
|
+
// Phase 10 / #466 — prefer the parsed Status field from extractPhases over
|
|
153
|
+
// the legacy phaseStatus() heuristic, which only matched literal "completed"
|
|
154
|
+
// in the header and missed our **Status:** Complete convention. Fall back to
|
|
155
|
+
// phaseStatus() only when extractPhases couldn't parse a Status line.
|
|
132
156
|
return extractPhases(content).map((p) => ({
|
|
133
157
|
number: p.number,
|
|
134
158
|
name: p.name,
|
|
135
|
-
status: phaseStatus(p.section),
|
|
159
|
+
status: p.status === 'unknown' ? phaseStatus(p.section) : p.status,
|
|
160
|
+
status_raw: p.status_raw,
|
|
136
161
|
}));
|
|
137
162
|
}
|
|
138
163
|
|