@forwardimpact/pathway 0.25.12 → 0.25.20
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/bin/fit-pathway.js +62 -54
- package/package.json +3 -4
- package/src/commands/agent-io.js +120 -0
- package/src/commands/agent.js +266 -349
- package/src/commands/init.js +2 -2
- package/src/commands/job.js +237 -183
- package/src/components/comparison-radar.js +118 -103
- package/src/components/progression-table.js +244 -208
- package/src/formatters/interview/markdown.js +100 -88
- package/src/formatters/job/description.js +76 -75
- package/src/formatters/job/dom.js +113 -97
- package/src/formatters/level/dom.js +87 -102
- package/src/formatters/questions/markdown.js +37 -33
- package/src/formatters/questions/shared.js +142 -75
- package/src/formatters/skill/dom.js +102 -93
- package/src/lib/comparison-radar-chart.js +256 -0
- package/src/lib/radar-utils.js +199 -0
- package/src/lib/radar.js +25 -662
- package/src/pages/agent-builder-download.js +170 -0
- package/src/pages/agent-builder-preview.js +344 -0
- package/src/pages/agent-builder.js +6 -550
- package/src/pages/progress-comparison.js +110 -0
- package/src/pages/progress.js +11 -111
- package/src/pages/self-assessment-steps.js +494 -0
- package/src/pages/self-assessment.js +54 -504
|
@@ -11,39 +11,25 @@ import {
|
|
|
11
11
|
div,
|
|
12
12
|
h1,
|
|
13
13
|
h2,
|
|
14
|
-
h3,
|
|
15
14
|
p,
|
|
16
|
-
span,
|
|
17
15
|
label,
|
|
18
|
-
section,
|
|
19
16
|
select,
|
|
20
17
|
option,
|
|
21
18
|
} from "../lib/render.js";
|
|
22
19
|
import { getState } from "../lib/state.js";
|
|
23
20
|
import { loadAgentDataBrowser } from "../lib/yaml-loader.js";
|
|
24
|
-
import {
|
|
25
|
-
generateStageAgentProfile,
|
|
26
|
-
deriveStageAgent,
|
|
27
|
-
generateSkillMarkdown,
|
|
28
|
-
deriveAgentSkills,
|
|
29
|
-
deriveReferenceLevel,
|
|
30
|
-
deriveToolkit,
|
|
31
|
-
} from "@forwardimpact/libskill";
|
|
21
|
+
import { deriveReferenceLevel } from "@forwardimpact/libskill";
|
|
32
22
|
import {
|
|
33
23
|
createSelectWithValue,
|
|
34
24
|
createDisciplineSelect,
|
|
35
25
|
} from "../lib/form-controls.js";
|
|
36
26
|
import { createReactive } from "../lib/reactive.js";
|
|
37
27
|
import { getStageEmoji } from "../formatters/stage/shared.js";
|
|
38
|
-
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
39
28
|
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} from "
|
|
44
|
-
import { createFileCard } from "../components/file-card.js";
|
|
45
|
-
import { createToolkitTable } from "../formatters/toolkit/dom.js";
|
|
46
|
-
import { createDetailSection } from "../components/detail.js";
|
|
29
|
+
createAllStagesPreview,
|
|
30
|
+
createSingleStagePreview,
|
|
31
|
+
createHelpSection,
|
|
32
|
+
} from "./agent-builder-preview.js";
|
|
47
33
|
|
|
48
34
|
/** All stages option value */
|
|
49
35
|
const ALL_STAGES_VALUE = "all";
|
|
@@ -193,6 +179,7 @@ export async function renderAgentBuilder() {
|
|
|
193
179
|
// Supports: /agent/discipline, /agent/discipline/track, /agent/discipline/track/stage
|
|
194
180
|
const hash = window.location.hash;
|
|
195
181
|
const pathMatch = hash.match(
|
|
182
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- negated char classes prevent backtracking; parses internal URL hash
|
|
196
183
|
/#\/agent\/([^/]+)(?:\/([^/]+))?(?:\/([^/?]+))?/,
|
|
197
184
|
);
|
|
198
185
|
const initialDiscipline = pathMatch ? pathMatch[1] : "";
|
|
@@ -426,534 +413,3 @@ function createEmptyState(disciplineCount, trackCount) {
|
|
|
426
413
|
),
|
|
427
414
|
);
|
|
428
415
|
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Create preview for all stages
|
|
432
|
-
* Shows cards for each stage agent and all skills
|
|
433
|
-
* @param {Object} context - Generation context
|
|
434
|
-
* @returns {HTMLElement}
|
|
435
|
-
*/
|
|
436
|
-
function createAllStagesPreview(context) {
|
|
437
|
-
const {
|
|
438
|
-
humanDiscipline,
|
|
439
|
-
humanTrack,
|
|
440
|
-
agentDiscipline,
|
|
441
|
-
agentTrack,
|
|
442
|
-
level,
|
|
443
|
-
stages,
|
|
444
|
-
skills,
|
|
445
|
-
behaviours,
|
|
446
|
-
agentBehaviours,
|
|
447
|
-
claudeCodeSettings,
|
|
448
|
-
templates,
|
|
449
|
-
} = context;
|
|
450
|
-
|
|
451
|
-
// Generate all stage agents
|
|
452
|
-
const stageAgents = stages.map((stage) => {
|
|
453
|
-
const derived = deriveStageAgent({
|
|
454
|
-
discipline: humanDiscipline,
|
|
455
|
-
track: humanTrack,
|
|
456
|
-
stage,
|
|
457
|
-
level,
|
|
458
|
-
skills,
|
|
459
|
-
behaviours,
|
|
460
|
-
agentBehaviours,
|
|
461
|
-
agentDiscipline,
|
|
462
|
-
agentTrack,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
const profile = generateStageAgentProfile({
|
|
466
|
-
discipline: humanDiscipline,
|
|
467
|
-
track: humanTrack,
|
|
468
|
-
stage,
|
|
469
|
-
level,
|
|
470
|
-
skills,
|
|
471
|
-
behaviours,
|
|
472
|
-
agentBehaviours,
|
|
473
|
-
agentDiscipline,
|
|
474
|
-
agentTrack,
|
|
475
|
-
stages,
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
return { stage, derived, profile };
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
// Get derived skills for skill cards
|
|
482
|
-
const derivedSkills = deriveAgentSkills({
|
|
483
|
-
discipline: humanDiscipline,
|
|
484
|
-
track: humanTrack,
|
|
485
|
-
level,
|
|
486
|
-
skills,
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
// Generate skill files
|
|
490
|
-
const skillFiles = derivedSkills
|
|
491
|
-
.map((derived) => skills.find((s) => s.id === derived.skillId))
|
|
492
|
-
.filter((skill) => skill?.agent)
|
|
493
|
-
.map((skill) => generateSkillMarkdown(skill, stages));
|
|
494
|
-
|
|
495
|
-
// Derive toolkit from agent skills
|
|
496
|
-
const toolkit = deriveToolkit({
|
|
497
|
-
skillMatrix: derivedSkills,
|
|
498
|
-
skills,
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
return div(
|
|
502
|
-
{ className: "agent-deployment" },
|
|
503
|
-
|
|
504
|
-
// Download all button
|
|
505
|
-
createDownloadAllButton(
|
|
506
|
-
stageAgents,
|
|
507
|
-
skillFiles,
|
|
508
|
-
claudeCodeSettings,
|
|
509
|
-
context,
|
|
510
|
-
),
|
|
511
|
-
|
|
512
|
-
// Agents section
|
|
513
|
-
section(
|
|
514
|
-
{ className: "agent-section" },
|
|
515
|
-
h2({}, `Agents (${stageAgents.length})`),
|
|
516
|
-
p(
|
|
517
|
-
{ className: "text-muted" },
|
|
518
|
-
"Stage-specific agents with skills, constraints, and stage transitions.",
|
|
519
|
-
),
|
|
520
|
-
div(
|
|
521
|
-
{ className: "agent-cards-grid" },
|
|
522
|
-
...stageAgents.map(({ stage, profile }) => {
|
|
523
|
-
const content = formatAgentProfile(profile, templates.agent);
|
|
524
|
-
const stageEmoji = getStageEmoji(stages, stage.id);
|
|
525
|
-
return createFileCard({
|
|
526
|
-
header: [
|
|
527
|
-
span({ className: "file-card-emoji" }, stageEmoji),
|
|
528
|
-
h3({}, `${stage.name} Agent`),
|
|
529
|
-
],
|
|
530
|
-
files: [
|
|
531
|
-
{
|
|
532
|
-
filename: profile.filename,
|
|
533
|
-
content,
|
|
534
|
-
language: "markdown",
|
|
535
|
-
},
|
|
536
|
-
],
|
|
537
|
-
maxHeight: 400,
|
|
538
|
-
});
|
|
539
|
-
}),
|
|
540
|
-
),
|
|
541
|
-
),
|
|
542
|
-
|
|
543
|
-
// Skills section
|
|
544
|
-
section(
|
|
545
|
-
{ className: "agent-section" },
|
|
546
|
-
h2({}, `Skills (${skillFiles.length})`),
|
|
547
|
-
skillFiles.length > 0
|
|
548
|
-
? div(
|
|
549
|
-
{ className: "skill-cards-grid" },
|
|
550
|
-
...skillFiles.map((skill) => buildSkillFileCard(skill, templates)),
|
|
551
|
-
)
|
|
552
|
-
: p(
|
|
553
|
-
{ className: "text-muted" },
|
|
554
|
-
"No skills with agent sections found for this discipline.",
|
|
555
|
-
),
|
|
556
|
-
),
|
|
557
|
-
|
|
558
|
-
// Tool Kit section
|
|
559
|
-
toolkit.length > 0
|
|
560
|
-
? createDetailSection({
|
|
561
|
-
title: `Tool Kit (${toolkit.length})`,
|
|
562
|
-
content: createToolkitTable(toolkit),
|
|
563
|
-
})
|
|
564
|
-
: null,
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Create preview for a single stage
|
|
570
|
-
* @param {Object} context - Generation context
|
|
571
|
-
* @param {Object} stage - Selected stage
|
|
572
|
-
* @returns {HTMLElement}
|
|
573
|
-
*/
|
|
574
|
-
function createSingleStagePreview(context, stage) {
|
|
575
|
-
const {
|
|
576
|
-
humanDiscipline,
|
|
577
|
-
humanTrack,
|
|
578
|
-
agentDiscipline,
|
|
579
|
-
agentTrack,
|
|
580
|
-
level,
|
|
581
|
-
skills,
|
|
582
|
-
behaviours,
|
|
583
|
-
agentBehaviours,
|
|
584
|
-
claudeCodeSettings,
|
|
585
|
-
stages,
|
|
586
|
-
templates,
|
|
587
|
-
} = context;
|
|
588
|
-
|
|
589
|
-
const profile = generateStageAgentProfile({
|
|
590
|
-
discipline: humanDiscipline,
|
|
591
|
-
track: humanTrack,
|
|
592
|
-
stage,
|
|
593
|
-
level,
|
|
594
|
-
skills,
|
|
595
|
-
behaviours,
|
|
596
|
-
agentBehaviours,
|
|
597
|
-
agentDiscipline,
|
|
598
|
-
agentTrack,
|
|
599
|
-
stages,
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
// Get skills for this stage (using full derived skills)
|
|
603
|
-
const derivedSkills = deriveAgentSkills({
|
|
604
|
-
discipline: humanDiscipline,
|
|
605
|
-
track: humanTrack,
|
|
606
|
-
level,
|
|
607
|
-
skills,
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
const skillFiles = derivedSkills
|
|
611
|
-
.map((d) => skills.find((s) => s.id === d.skillId))
|
|
612
|
-
.filter((skill) => skill?.agent)
|
|
613
|
-
.map((skill) => generateSkillMarkdown(skill, stages));
|
|
614
|
-
|
|
615
|
-
// Derive toolkit from agent skills
|
|
616
|
-
const toolkit = deriveToolkit({
|
|
617
|
-
skillMatrix: derivedSkills,
|
|
618
|
-
skills,
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
return div(
|
|
622
|
-
{ className: "agent-deployment" },
|
|
623
|
-
|
|
624
|
-
// Download button for single stage
|
|
625
|
-
createDownloadSingleButton(
|
|
626
|
-
profile,
|
|
627
|
-
skillFiles,
|
|
628
|
-
claudeCodeSettings,
|
|
629
|
-
templates,
|
|
630
|
-
),
|
|
631
|
-
|
|
632
|
-
// Agents section (single card)
|
|
633
|
-
section(
|
|
634
|
-
{ className: "agent-section" },
|
|
635
|
-
h2({}, "Agent"),
|
|
636
|
-
div(
|
|
637
|
-
{ className: "agent-cards-grid single" },
|
|
638
|
-
(() => {
|
|
639
|
-
const content = formatAgentProfile(profile, templates.agent);
|
|
640
|
-
const stageEmoji = getStageEmoji(stages, stage.id);
|
|
641
|
-
return createFileCard({
|
|
642
|
-
header: [
|
|
643
|
-
span({ className: "file-card-emoji" }, stageEmoji),
|
|
644
|
-
h3({}, `${stage.name} Agent`),
|
|
645
|
-
],
|
|
646
|
-
files: [
|
|
647
|
-
{
|
|
648
|
-
filename: profile.filename,
|
|
649
|
-
content,
|
|
650
|
-
language: "markdown",
|
|
651
|
-
},
|
|
652
|
-
],
|
|
653
|
-
maxHeight: 400,
|
|
654
|
-
});
|
|
655
|
-
})(),
|
|
656
|
-
),
|
|
657
|
-
),
|
|
658
|
-
|
|
659
|
-
// Skills section
|
|
660
|
-
section(
|
|
661
|
-
{ className: "agent-section" },
|
|
662
|
-
h2({}, `Skills (${skillFiles.length})`),
|
|
663
|
-
skillFiles.length > 0
|
|
664
|
-
? div(
|
|
665
|
-
{ className: "skill-cards-grid" },
|
|
666
|
-
...skillFiles.map((skill) => buildSkillFileCard(skill, templates)),
|
|
667
|
-
)
|
|
668
|
-
: p(
|
|
669
|
-
{ className: "text-muted" },
|
|
670
|
-
"No skills with agent sections found for this discipline.",
|
|
671
|
-
),
|
|
672
|
-
),
|
|
673
|
-
|
|
674
|
-
// Tool Kit section
|
|
675
|
-
toolkit.length > 0
|
|
676
|
-
? createDetailSection({
|
|
677
|
-
title: `Tool Kit (${toolkit.length})`,
|
|
678
|
-
content: createToolkitTable(toolkit),
|
|
679
|
-
})
|
|
680
|
-
: null,
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Build a file card for a skill with 1–3 file panes (accordion).
|
|
686
|
-
* @param {Object} skill - Skill with frontmatter and body
|
|
687
|
-
* @param {{skill: string, install: string, reference: string}} templates - Mustache templates
|
|
688
|
-
* @returns {HTMLElement}
|
|
689
|
-
*/
|
|
690
|
-
function buildSkillFileCard(skill, templates) {
|
|
691
|
-
const content = formatAgentSkill(skill, templates.skill);
|
|
692
|
-
|
|
693
|
-
/** @type {import('../components/file-card.js').FileDescriptor[]} */
|
|
694
|
-
const files = [
|
|
695
|
-
{
|
|
696
|
-
filename: `${skill.dirname}/SKILL.md`,
|
|
697
|
-
content,
|
|
698
|
-
language: "markdown",
|
|
699
|
-
},
|
|
700
|
-
];
|
|
701
|
-
|
|
702
|
-
if (skill.installScript) {
|
|
703
|
-
files.push({
|
|
704
|
-
filename: `${skill.dirname}/scripts/install.sh`,
|
|
705
|
-
content: formatInstallScript(skill, templates.install),
|
|
706
|
-
language: "bash",
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
if (skill.implementationReference) {
|
|
711
|
-
files.push({
|
|
712
|
-
filename: `${skill.dirname}/references/REFERENCE.md`,
|
|
713
|
-
content: formatReference(skill, templates.reference),
|
|
714
|
-
language: "markdown",
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const headerChildren = [
|
|
719
|
-
span({ className: "file-card-name" }, skill.frontmatter.name),
|
|
720
|
-
];
|
|
721
|
-
if (files.length > 1) {
|
|
722
|
-
headerChildren.push(
|
|
723
|
-
span({ className: "file-card-badge" }, `${files.length} files`),
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
return createFileCard({
|
|
728
|
-
header: headerChildren,
|
|
729
|
-
files,
|
|
730
|
-
maxHeight: 300,
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Create download all button for all stages
|
|
736
|
-
* @param {Array} stageAgents - Array of {stage, derived, profile}
|
|
737
|
-
* @param {Array} skillFiles - Array of skill file objects
|
|
738
|
-
* @param {Object} claudeCodeSettings - Claude Code settings
|
|
739
|
-
* @param {Object} context - Context with discipline/track info and templates
|
|
740
|
-
* @returns {HTMLElement}
|
|
741
|
-
*/
|
|
742
|
-
function createDownloadAllButton(
|
|
743
|
-
stageAgents,
|
|
744
|
-
skillFiles,
|
|
745
|
-
claudeCodeSettings,
|
|
746
|
-
context,
|
|
747
|
-
) {
|
|
748
|
-
const { humanDiscipline, humanTrack, templates } = context;
|
|
749
|
-
const agentName = `${humanDiscipline.id}-${humanTrack.id}`.replace(/_/g, "-");
|
|
750
|
-
|
|
751
|
-
const btn = document.createElement("button");
|
|
752
|
-
btn.className = "btn btn-primary download-all-btn";
|
|
753
|
-
btn.textContent = "📦 Download All (.zip)";
|
|
754
|
-
|
|
755
|
-
btn.addEventListener("click", async () => {
|
|
756
|
-
btn.disabled = true;
|
|
757
|
-
btn.textContent = "Generating...";
|
|
758
|
-
|
|
759
|
-
try {
|
|
760
|
-
const JSZip = await importJSZip();
|
|
761
|
-
const zip = new JSZip();
|
|
762
|
-
|
|
763
|
-
// Add all stage agent profiles to .claude/agents/
|
|
764
|
-
for (const { profile } of stageAgents) {
|
|
765
|
-
const content = formatAgentProfile(profile, templates.agent);
|
|
766
|
-
zip.file(`.claude/agents/${profile.filename}`, content);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Add skills (SKILL.md + optional install script + optional reference)
|
|
770
|
-
for (const skill of skillFiles) {
|
|
771
|
-
const content = formatAgentSkill(skill, templates.skill);
|
|
772
|
-
zip.file(`.claude/skills/${skill.dirname}/SKILL.md`, content);
|
|
773
|
-
|
|
774
|
-
if (skill.installScript) {
|
|
775
|
-
const installContent = formatInstallScript(skill, templates.install);
|
|
776
|
-
zip.file(
|
|
777
|
-
`.claude/skills/${skill.dirname}/scripts/install.sh`,
|
|
778
|
-
installContent,
|
|
779
|
-
{ unixPermissions: "755" },
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
if (skill.implementationReference) {
|
|
784
|
-
const refContent = formatReference(skill, templates.reference);
|
|
785
|
-
zip.file(
|
|
786
|
-
`.claude/skills/${skill.dirname}/references/REFERENCE.md`,
|
|
787
|
-
refContent,
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// Add Claude Code settings
|
|
793
|
-
if (Object.keys(claudeCodeSettings).length > 0) {
|
|
794
|
-
zip.file(
|
|
795
|
-
".claude/settings.json",
|
|
796
|
-
JSON.stringify(claudeCodeSettings, null, 2) + "\n",
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// Generate and download
|
|
801
|
-
const blob = await zip.generateAsync({ type: "blob" });
|
|
802
|
-
const url = URL.createObjectURL(blob);
|
|
803
|
-
|
|
804
|
-
const link = document.createElement("a");
|
|
805
|
-
link.href = url;
|
|
806
|
-
link.download = `${agentName}-agents.zip`;
|
|
807
|
-
document.body.appendChild(link);
|
|
808
|
-
link.click();
|
|
809
|
-
document.body.removeChild(link);
|
|
810
|
-
|
|
811
|
-
URL.revokeObjectURL(url);
|
|
812
|
-
} finally {
|
|
813
|
-
btn.disabled = false;
|
|
814
|
-
btn.textContent = "📦 Download All (.zip)";
|
|
815
|
-
}
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
return btn;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* Create download button for single stage
|
|
823
|
-
* @param {Object} profile - Agent profile
|
|
824
|
-
* @param {Array} skillFiles - Skill files
|
|
825
|
-
* @param {Object} claudeCodeSettings - Claude Code settings
|
|
826
|
-
* @param {{agent: string, skill: string}} templates - Mustache templates
|
|
827
|
-
* @returns {HTMLElement}
|
|
828
|
-
*/
|
|
829
|
-
function createDownloadSingleButton(
|
|
830
|
-
profile,
|
|
831
|
-
skillFiles,
|
|
832
|
-
claudeCodeSettings,
|
|
833
|
-
templates,
|
|
834
|
-
) {
|
|
835
|
-
const btn = document.createElement("button");
|
|
836
|
-
btn.className = "btn btn-primary download-all-btn";
|
|
837
|
-
btn.textContent = "📥 Download Agent (.zip)";
|
|
838
|
-
|
|
839
|
-
btn.addEventListener("click", async () => {
|
|
840
|
-
btn.disabled = true;
|
|
841
|
-
btn.textContent = "Generating...";
|
|
842
|
-
|
|
843
|
-
try {
|
|
844
|
-
const JSZip = await importJSZip();
|
|
845
|
-
const zip = new JSZip();
|
|
846
|
-
|
|
847
|
-
// Add profile to .claude/agents/
|
|
848
|
-
const content = formatAgentProfile(profile, templates.agent);
|
|
849
|
-
zip.file(`.claude/agents/${profile.filename}`, content);
|
|
850
|
-
|
|
851
|
-
// Add skills (SKILL.md + optional install script + optional reference)
|
|
852
|
-
for (const skill of skillFiles) {
|
|
853
|
-
const skillContent = formatAgentSkill(skill, templates.skill);
|
|
854
|
-
zip.file(`.claude/skills/${skill.dirname}/SKILL.md`, skillContent);
|
|
855
|
-
|
|
856
|
-
if (skill.installScript) {
|
|
857
|
-
const installContent = formatInstallScript(skill, templates.install);
|
|
858
|
-
zip.file(
|
|
859
|
-
`.claude/skills/${skill.dirname}/scripts/install.sh`,
|
|
860
|
-
installContent,
|
|
861
|
-
{ unixPermissions: "755" },
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (skill.implementationReference) {
|
|
866
|
-
const refContent = formatReference(skill, templates.reference);
|
|
867
|
-
zip.file(
|
|
868
|
-
`.claude/skills/${skill.dirname}/references/REFERENCE.md`,
|
|
869
|
-
refContent,
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Add Claude Code settings
|
|
875
|
-
if (Object.keys(claudeCodeSettings).length > 0) {
|
|
876
|
-
zip.file(
|
|
877
|
-
".claude/settings.json",
|
|
878
|
-
JSON.stringify(claudeCodeSettings, null, 2) + "\n",
|
|
879
|
-
);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// Generate and download
|
|
883
|
-
const blob = await zip.generateAsync({ type: "blob" });
|
|
884
|
-
const url = URL.createObjectURL(blob);
|
|
885
|
-
|
|
886
|
-
const link = document.createElement("a");
|
|
887
|
-
link.href = url;
|
|
888
|
-
link.download = `${profile.frontmatter.name}-agent.zip`;
|
|
889
|
-
document.body.appendChild(link);
|
|
890
|
-
link.click();
|
|
891
|
-
document.body.removeChild(link);
|
|
892
|
-
|
|
893
|
-
URL.revokeObjectURL(url);
|
|
894
|
-
} finally {
|
|
895
|
-
btn.disabled = false;
|
|
896
|
-
btn.textContent = "📥 Download Agent (.zip)";
|
|
897
|
-
}
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
return btn;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/**
|
|
904
|
-
* Dynamically import JSZip from CDN
|
|
905
|
-
* @returns {Promise<typeof JSZip>}
|
|
906
|
-
*/
|
|
907
|
-
async function importJSZip() {
|
|
908
|
-
const module = await import("https://cdn.jsdelivr.net/npm/jszip@3.10.1/+esm");
|
|
909
|
-
return module.default;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Create help section explaining how agent builder works
|
|
914
|
-
* @returns {HTMLElement}
|
|
915
|
-
*/
|
|
916
|
-
function createHelpSection() {
|
|
917
|
-
return section(
|
|
918
|
-
{ className: "section section-detail" },
|
|
919
|
-
h2({ className: "section-title" }, "How It Works"),
|
|
920
|
-
div(
|
|
921
|
-
{ className: "auto-grid-md" },
|
|
922
|
-
div(
|
|
923
|
-
{ className: "detail-item" },
|
|
924
|
-
div({ className: "detail-item-label" }, "Stages"),
|
|
925
|
-
p(
|
|
926
|
-
{},
|
|
927
|
-
"Agents are generated for each stage: Plan (research), Code (implement), and Review (verify). " +
|
|
928
|
-
"Each stage has specific skills, constraints, and stage transitions.",
|
|
929
|
-
),
|
|
930
|
-
),
|
|
931
|
-
div(
|
|
932
|
-
{ className: "detail-item" },
|
|
933
|
-
div({ className: "detail-item-label" }, "Agent Profiles"),
|
|
934
|
-
p(
|
|
935
|
-
{},
|
|
936
|
-
"The .md files contain the agent's identity, skills, and constraints. " +
|
|
937
|
-
"Place them in .claude/agents/ for Claude Code to discover.",
|
|
938
|
-
),
|
|
939
|
-
),
|
|
940
|
-
div(
|
|
941
|
-
{ className: "detail-item" },
|
|
942
|
-
div({ className: "detail-item-label" }, "Skills"),
|
|
943
|
-
p(
|
|
944
|
-
{},
|
|
945
|
-
"SKILL.md files provide specialized knowledge that agents can use. " +
|
|
946
|
-
"Place them in .claude/skills/{skill-name}/ directories.",
|
|
947
|
-
),
|
|
948
|
-
),
|
|
949
|
-
div(
|
|
950
|
-
{ className: "detail-item" },
|
|
951
|
-
div({ className: "detail-item-label" }, "All Stages"),
|
|
952
|
-
p(
|
|
953
|
-
{},
|
|
954
|
-
"Select 'All Stages' to download a complete agent deployment with all stage agents and skills in one zip file.",
|
|
955
|
-
),
|
|
956
|
-
),
|
|
957
|
-
),
|
|
958
|
-
);
|
|
959
|
-
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comparison result builder for progress page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { div, h2, a, section } from "../lib/render.js";
|
|
6
|
+
import { createStatCard } from "../components/card.js";
|
|
7
|
+
import {
|
|
8
|
+
createComparisonSkillRadar,
|
|
9
|
+
createComparisonBehaviourRadar,
|
|
10
|
+
} from "../components/comparison-radar.js";
|
|
11
|
+
import { createProgressionTable } from "../components/progression-table.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Build the comparison result DOM from a progression view
|
|
15
|
+
* @param {Object} progressionView
|
|
16
|
+
* @param {Object} currentJobView
|
|
17
|
+
* @param {Object} currentLevel
|
|
18
|
+
* @param {Object} targetLevel
|
|
19
|
+
* @param {Object|null} targetTrack
|
|
20
|
+
* @param {Object} targetDiscipline
|
|
21
|
+
* @param {Object} data
|
|
22
|
+
* @returns {HTMLElement}
|
|
23
|
+
*/
|
|
24
|
+
export function buildComparisonResult(
|
|
25
|
+
progressionView,
|
|
26
|
+
currentJobView,
|
|
27
|
+
currentLevel,
|
|
28
|
+
targetLevel,
|
|
29
|
+
targetTrack,
|
|
30
|
+
targetDiscipline,
|
|
31
|
+
data,
|
|
32
|
+
) {
|
|
33
|
+
const { skillChanges, behaviourChanges, summary, target } = progressionView;
|
|
34
|
+
|
|
35
|
+
return div(
|
|
36
|
+
{ className: "comparison-result" },
|
|
37
|
+
div(
|
|
38
|
+
{ className: "grid grid-6" },
|
|
39
|
+
summary.skillsGained > 0
|
|
40
|
+
? createStatCard({ value: summary.skillsGained, label: "New Skills" })
|
|
41
|
+
: null,
|
|
42
|
+
createStatCard({ value: summary.skillsUp, label: "Skills to Grow" }),
|
|
43
|
+
summary.skillsDown > 0
|
|
44
|
+
? createStatCard({
|
|
45
|
+
value: summary.skillsDown,
|
|
46
|
+
label: "Skills Decrease",
|
|
47
|
+
})
|
|
48
|
+
: null,
|
|
49
|
+
summary.skillsLost > 0
|
|
50
|
+
? createStatCard({ value: summary.skillsLost, label: "Skills Removed" })
|
|
51
|
+
: null,
|
|
52
|
+
createStatCard({
|
|
53
|
+
value: summary.behavioursUp,
|
|
54
|
+
label: "Behaviours to Mature",
|
|
55
|
+
}),
|
|
56
|
+
summary.behavioursDown > 0
|
|
57
|
+
? createStatCard({
|
|
58
|
+
value: summary.behavioursDown,
|
|
59
|
+
label: "Behaviours Decrease",
|
|
60
|
+
})
|
|
61
|
+
: null,
|
|
62
|
+
),
|
|
63
|
+
div(
|
|
64
|
+
{ className: "section auto-grid-lg" },
|
|
65
|
+
createComparisonSkillRadar(
|
|
66
|
+
currentJobView.skillMatrix,
|
|
67
|
+
target.skillMatrix,
|
|
68
|
+
{
|
|
69
|
+
title: "Skills Comparison",
|
|
70
|
+
currentLabel: `Current (${currentLevel.id})`,
|
|
71
|
+
targetLabel: `Target (${targetLevel.id})`,
|
|
72
|
+
size: 400,
|
|
73
|
+
capabilities: data.capabilities,
|
|
74
|
+
},
|
|
75
|
+
),
|
|
76
|
+
createComparisonBehaviourRadar(
|
|
77
|
+
currentJobView.behaviourProfile,
|
|
78
|
+
target.behaviourProfile,
|
|
79
|
+
{
|
|
80
|
+
title: "Behaviours Comparison",
|
|
81
|
+
currentLabel: `Current (${currentLevel.id})`,
|
|
82
|
+
targetLabel: `Target (${targetLevel.id})`,
|
|
83
|
+
size: 400,
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
section(
|
|
88
|
+
{ className: "section section-detail" },
|
|
89
|
+
h2({ className: "section-title" }, "Skill Changes"),
|
|
90
|
+
createProgressionTable(skillChanges, "skill"),
|
|
91
|
+
),
|
|
92
|
+
section(
|
|
93
|
+
{ className: "section section-detail" },
|
|
94
|
+
h2({ className: "section-title" }, "Behaviour Changes"),
|
|
95
|
+
createProgressionTable(behaviourChanges, "behaviour"),
|
|
96
|
+
),
|
|
97
|
+
div(
|
|
98
|
+
{ className: "page-actions" },
|
|
99
|
+
a(
|
|
100
|
+
{
|
|
101
|
+
href: targetTrack
|
|
102
|
+
? `#/job/${targetDiscipline.id}/${targetLevel.id}/${targetTrack.id}`
|
|
103
|
+
: `#/job/${targetDiscipline.id}/${targetLevel.id}`,
|
|
104
|
+
className: "btn btn-secondary",
|
|
105
|
+
},
|
|
106
|
+
`View ${targetLevel.id}${targetTrack ? ` ${targetTrack.name}` : ""} Job Definition →`,
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
}
|