create-snipara 2.0.15 → 2.0.17
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/README.md +46 -1
- package/dist/index.js +577 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Use it for:
|
|
|
29
29
|
- free account signup with reviewed Context + Memory
|
|
30
30
|
- local project wiring, `doctor`, `repair`, and `upgrade`
|
|
31
31
|
- local source activation without GitHub, plus optional GitHub repository sync and PR Answer Packs approval when the current folder is a GitHub repo
|
|
32
|
-
- day-zero proof with a ready-to-ask grounded question
|
|
32
|
+
- day-zero proof with a ready-to-ask grounded question, a small hosted README/docs seed sync, and an evidence-linked Project Bootstrap for new projects
|
|
33
33
|
|
|
34
34
|
## Why It Exists
|
|
35
35
|
|
|
@@ -137,6 +137,47 @@ That distinction matters:
|
|
|
137
137
|
- optionally installs Claude Code agent hooks and companion local code overlay Git hooks
|
|
138
138
|
- supports `doctor`, `repair`, `upgrade`, and `print-config`
|
|
139
139
|
|
|
140
|
+
## Project Bootstrap: Cold-Start Intelligence
|
|
141
|
+
|
|
142
|
+
During setup, `create-snipara` builds a reviewable picture of the repository
|
|
143
|
+
before the first agent task. It writes both a machine-readable artifact and a
|
|
144
|
+
human-readable report:
|
|
145
|
+
|
|
146
|
+
- `.snipara/activation/project-bootstrap.json`
|
|
147
|
+
- `.snipara/activation/project-bootstrap.md`
|
|
148
|
+
|
|
149
|
+
The report summarizes eight dynamic sections. Counts come from the current
|
|
150
|
+
repository; they are not fixed demo numbers:
|
|
151
|
+
|
|
152
|
+
- architecture patterns
|
|
153
|
+
- conventions
|
|
154
|
+
- decision candidates
|
|
155
|
+
- procedures
|
|
156
|
+
- skills and candidate workflows
|
|
157
|
+
- recurring failure candidates
|
|
158
|
+
- critical code symbols
|
|
159
|
+
- verification patterns
|
|
160
|
+
|
|
161
|
+
The local pass uses bounded evidence from Git history, `AGENTS.md`,
|
|
162
|
+
`CLAUDE.md`, `README`, `docs/`, CI configuration, package scripts, test
|
|
163
|
+
patterns, and release history. Every item links back to its evidence. Directly
|
|
164
|
+
detected facts can be marked `observed`; inferred decisions, workflows, and
|
|
165
|
+
failure patterns remain `needs_review` and are not promoted to durable project
|
|
166
|
+
memory automatically.
|
|
167
|
+
|
|
168
|
+
The artifact also includes a coverage ledger. PR and issue history are marked
|
|
169
|
+
`unavailable` until the user explicitly connects a source provider. Critical
|
|
170
|
+
symbols are marked `deferred` until Code Graph indexing is available. Snipara
|
|
171
|
+
does not invent candidates to make these sections look complete.
|
|
172
|
+
|
|
173
|
+
After setup, start here:
|
|
174
|
+
|
|
175
|
+
> Review what Snipara learned about your project.
|
|
176
|
+
|
|
177
|
+
The Project Bootstrap summary is also carried into the First Work Brief so the
|
|
178
|
+
agent begins with the repository's inherited experience and its remaining
|
|
179
|
+
evidence gaps in view.
|
|
180
|
+
|
|
140
181
|
## Product Boundary
|
|
141
182
|
|
|
142
183
|
Keep the package roles simple:
|
|
@@ -461,6 +502,10 @@ After setup, the project gets a local pack at `.snipara/companion/`:
|
|
|
461
502
|
- `commands.json`: starter commands for the current project
|
|
462
503
|
- `doctor.json`: most recent validation report
|
|
463
504
|
|
|
505
|
+
Activation evidence is kept separately under `.snipara/activation/`, including
|
|
506
|
+
the Project Bootstrap JSON/Markdown pair, First Work Brief, handoff, and memory
|
|
507
|
+
candidate review artifacts.
|
|
508
|
+
|
|
464
509
|
This pack is meant to be the local source of truth for machine-level setup. When
|
|
465
510
|
`snipara-companion` is installed, it includes business-folder onboarding commands
|
|
466
511
|
such as:
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,8 @@ import { homedir } from "os";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "create-snipara",
|
|
16
|
-
version: "2.0.
|
|
17
|
-
description: "Onboard Snipara Hosted MCP Context + Memory with
|
|
16
|
+
version: "2.0.17",
|
|
17
|
+
description: "Onboard Snipara Hosted MCP Context + Memory with project bootstrap intelligence and optional local workflows",
|
|
18
18
|
main: "dist/index.js",
|
|
19
19
|
type: "module",
|
|
20
20
|
bin: {
|
|
@@ -107,8 +107,8 @@ var DEVICE_CLIENT_ID = "create-snipara";
|
|
|
107
107
|
var DAY_ZERO_CORPUS_MAX_FILES = 8;
|
|
108
108
|
var DAY_ZERO_CORPUS_MAX_FILE_CHARS = 12e3;
|
|
109
109
|
var DAY_ZERO_CORPUS_MAX_TOTAL_CHARS = 5e4;
|
|
110
|
-
var
|
|
111
|
-
var
|
|
110
|
+
var PROJECT_BOOTSTRAP_MAX_WALKED_FILES = 2500;
|
|
111
|
+
var PROJECT_BOOTSTRAP_MAX_MATCHED_FILES = 80;
|
|
112
112
|
var EXPERIENCE_GOAL_SECTION = `## Experience Goal
|
|
113
113
|
|
|
114
114
|
The target experience is outcome-first agent work across supported clients:
|
|
@@ -154,6 +154,12 @@ function getFirstWorkBriefPath() {
|
|
|
154
154
|
function getMemoryCandidatesPath() {
|
|
155
155
|
return path.join(getActivationDir(), "memory-candidates.json");
|
|
156
156
|
}
|
|
157
|
+
function getProjectBootstrapJsonPath() {
|
|
158
|
+
return path.join(getActivationDir(), "project-bootstrap.json");
|
|
159
|
+
}
|
|
160
|
+
function getProjectBootstrapMarkdownPath() {
|
|
161
|
+
return path.join(getActivationDir(), "project-bootstrap.md");
|
|
162
|
+
}
|
|
157
163
|
function writeActivationManifest(manifest) {
|
|
158
164
|
fs.mkdirSync(getActivationDir(), { recursive: true });
|
|
159
165
|
const payload = JSON.stringify(manifest, null, 2) + "\n";
|
|
@@ -561,6 +567,23 @@ function listFilesUnder(relativeDir, maxFiles) {
|
|
|
561
567
|
visit(root);
|
|
562
568
|
return results;
|
|
563
569
|
}
|
|
570
|
+
function isCreateSniparaGeneratedInstructionSource(relativePath) {
|
|
571
|
+
if (!["AGENTS.md", "CLAUDE.md", ".cursor/rules/snipara.mdc"].includes(relativePath)) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
const prefix = fs.readFileSync(path.join(process.cwd(), relativePath), "utf8").slice(0, 4096);
|
|
576
|
+
return prefix.includes(
|
|
577
|
+
"## Snipara Context Workflow\n\nThis project uses Snipara Hosted MCP for project context and reviewed memory."
|
|
578
|
+
) || prefix.includes(
|
|
579
|
+
"Canonical project context is in `AGENTS.md`. This file exists for Claude Code compatibility."
|
|
580
|
+
) || prefix.includes(
|
|
581
|
+
"description: Use Snipara Hosted MCP for project context and reviewed memory."
|
|
582
|
+
);
|
|
583
|
+
} catch {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
564
587
|
function findDecisionArchaeologySources(cwd) {
|
|
565
588
|
const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
|
|
566
589
|
const directSources = [
|
|
@@ -578,7 +601,527 @@ function findDecisionArchaeologySources(cwd) {
|
|
|
578
601
|
...listFilesUnder("docs/decisions", 20),
|
|
579
602
|
...listFilesUnder(".cursor/rules", 20)
|
|
580
603
|
];
|
|
581
|
-
return [.../* @__PURE__ */ new Set([...directSources, ...archaeologySources])].slice(0, 80);
|
|
604
|
+
return [.../* @__PURE__ */ new Set([...directSources, ...archaeologySources])].filter((source) => !isCreateSniparaGeneratedInstructionSource(source)).slice(0, 80);
|
|
605
|
+
}
|
|
606
|
+
function findBoundedProjectFiles(predicate, maxMatches = PROJECT_BOOTSTRAP_MAX_MATCHED_FILES) {
|
|
607
|
+
const ignoredDirectories = /* @__PURE__ */ new Set([
|
|
608
|
+
".git",
|
|
609
|
+
".next",
|
|
610
|
+
".snipara",
|
|
611
|
+
".turbo",
|
|
612
|
+
".venv",
|
|
613
|
+
"build",
|
|
614
|
+
"coverage",
|
|
615
|
+
"dist",
|
|
616
|
+
"node_modules",
|
|
617
|
+
"target",
|
|
618
|
+
"vendor"
|
|
619
|
+
]);
|
|
620
|
+
const results = [];
|
|
621
|
+
let walkedFiles = 0;
|
|
622
|
+
const visit = (directory) => {
|
|
623
|
+
if (walkedFiles >= PROJECT_BOOTSTRAP_MAX_WALKED_FILES || results.length >= maxMatches) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
let entries;
|
|
627
|
+
try {
|
|
628
|
+
entries = fs.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
629
|
+
} catch {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
for (const entry of entries) {
|
|
633
|
+
if (entry.isSymbolicLink()) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const absolutePath = path.join(directory, entry.name);
|
|
637
|
+
if (entry.isDirectory()) {
|
|
638
|
+
if (!ignoredDirectories.has(entry.name)) {
|
|
639
|
+
visit(absolutePath);
|
|
640
|
+
}
|
|
641
|
+
} else if (entry.isFile()) {
|
|
642
|
+
walkedFiles += 1;
|
|
643
|
+
const relativePath = path.relative(process.cwd(), absolutePath).replace(/\\/g, "/");
|
|
644
|
+
if (predicate(relativePath)) {
|
|
645
|
+
results.push(relativePath);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (walkedFiles >= PROJECT_BOOTSTRAP_MAX_WALKED_FILES || results.length >= maxMatches) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
visit(process.cwd());
|
|
654
|
+
return results;
|
|
655
|
+
}
|
|
656
|
+
function readProjectPackageMetadata() {
|
|
657
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
658
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
659
|
+
return { scripts: {}, dependencies: [], workspaces: false };
|
|
660
|
+
}
|
|
661
|
+
try {
|
|
662
|
+
const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
663
|
+
const scripts = Object.fromEntries(
|
|
664
|
+
Object.entries(parsed.scripts ?? {}).filter(
|
|
665
|
+
(entry) => typeof entry[1] === "string"
|
|
666
|
+
)
|
|
667
|
+
);
|
|
668
|
+
const dependencies = [
|
|
669
|
+
...Object.keys(parsed.dependencies ?? {}),
|
|
670
|
+
...Object.keys(parsed.devDependencies ?? {}),
|
|
671
|
+
...Object.keys(parsed.peerDependencies ?? {})
|
|
672
|
+
];
|
|
673
|
+
return {
|
|
674
|
+
scripts,
|
|
675
|
+
dependencies: [...new Set(dependencies)].sort(),
|
|
676
|
+
workspaces: Boolean(parsed.workspaces)
|
|
677
|
+
};
|
|
678
|
+
} catch {
|
|
679
|
+
return { scripts: {}, dependencies: [], workspaces: false };
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function readBootstrapGitSubjects(maxCount) {
|
|
683
|
+
try {
|
|
684
|
+
const output = execSync(`git log --pretty=format:%h%x20%s --max-count=${maxCount}`, {
|
|
685
|
+
cwd: process.cwd(),
|
|
686
|
+
encoding: "utf-8",
|
|
687
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
688
|
+
timeout: 1500
|
|
689
|
+
}).trim();
|
|
690
|
+
return output ? output.split(/\r?\n/).map((line) => line.trim()) : [];
|
|
691
|
+
} catch {
|
|
692
|
+
return [];
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function readBootstrapReleaseTags() {
|
|
696
|
+
try {
|
|
697
|
+
const output = execSync("git tag --sort=-creatordate", {
|
|
698
|
+
cwd: process.cwd(),
|
|
699
|
+
encoding: "utf-8",
|
|
700
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
701
|
+
timeout: 1e3
|
|
702
|
+
}).trim();
|
|
703
|
+
return output ? output.split(/\r?\n/).slice(0, 20) : [];
|
|
704
|
+
} catch {
|
|
705
|
+
return [];
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function createBootstrapItem(section, title, status, detail, evidence) {
|
|
709
|
+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 56);
|
|
710
|
+
return {
|
|
711
|
+
id: `${section}:${slug || "candidate"}`,
|
|
712
|
+
title,
|
|
713
|
+
status,
|
|
714
|
+
detail,
|
|
715
|
+
evidence
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function buildProjectBootstrap(scan) {
|
|
719
|
+
const cwd = process.cwd();
|
|
720
|
+
const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
|
|
721
|
+
const packageMetadata = readProjectPackageMetadata();
|
|
722
|
+
const allDetectedFiles = findBoundedProjectFiles(() => true, PROJECT_BOOTSTRAP_MAX_WALKED_FILES);
|
|
723
|
+
const testFiles = allDetectedFiles.filter(
|
|
724
|
+
(relativePath) => /(^|\/)(?:__tests__|tests?|spec)(\/|$)|(?:^|\/)[^/]+\.(?:test|spec)\.[^/]+$|(?:^|\/)test_[^/]+\.py$/i.test(
|
|
725
|
+
relativePath
|
|
726
|
+
)
|
|
727
|
+
);
|
|
728
|
+
const skillFiles = allDetectedFiles.filter(
|
|
729
|
+
(relativePath) => /(^|\/)SKILL\.md$/i.test(relativePath)
|
|
730
|
+
);
|
|
731
|
+
const ciFiles = [
|
|
732
|
+
...allDetectedFiles.filter(
|
|
733
|
+
(relativePath) => /^\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath)
|
|
734
|
+
),
|
|
735
|
+
...[".gitlab-ci.yml", "azure-pipelines.yml", "Jenkinsfile", ".circleci/config.yml"].filter(
|
|
736
|
+
exists
|
|
737
|
+
)
|
|
738
|
+
].filter((value, index, values) => values.indexOf(value) === index);
|
|
739
|
+
const instructionSources = scan.files.candidateDocs.filter(
|
|
740
|
+
(source) => classifyDecisionSource(source) === "agent_instruction"
|
|
741
|
+
);
|
|
742
|
+
const documentationSources = [
|
|
743
|
+
...["README.md", "CHANGELOG.md", "MEMORY.md"].filter(exists),
|
|
744
|
+
...listFilesUnder("docs", 40)
|
|
745
|
+
].filter((value, index, values) => values.indexOf(value) === index);
|
|
746
|
+
const releaseTags = scan.git.detected ? readBootstrapReleaseTags() : [];
|
|
747
|
+
const gitSubjects = scan.git.detected ? readBootstrapGitSubjects(120) : [];
|
|
748
|
+
const architecture = [];
|
|
749
|
+
if (scan.languages.length > 0) {
|
|
750
|
+
architecture.push(
|
|
751
|
+
createBootstrapItem(
|
|
752
|
+
"architecture",
|
|
753
|
+
`Languages: ${scan.languages.join(", ")}`,
|
|
754
|
+
"observed",
|
|
755
|
+
"Detected from root manifests and source extensions.",
|
|
756
|
+
scan.files.hasPackageJson ? [{ source: "package.json", kind: "manifest" }] : [{ source: ".", kind: "filesystem_scan" }]
|
|
757
|
+
)
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
if (scan.packageManagers.length > 0) {
|
|
761
|
+
architecture.push(
|
|
762
|
+
createBootstrapItem(
|
|
763
|
+
"architecture",
|
|
764
|
+
`Package manager: ${scan.packageManagers.join(", ")}`,
|
|
765
|
+
"observed",
|
|
766
|
+
"Detected from lockfiles.",
|
|
767
|
+
scan.files.lockfiles.map((source) => ({ source, kind: "lockfile" }))
|
|
768
|
+
)
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
const workspaceEvidence = ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json"].filter(
|
|
772
|
+
exists
|
|
773
|
+
);
|
|
774
|
+
if (packageMetadata.workspaces || workspaceEvidence.length > 0) {
|
|
775
|
+
architecture.push(
|
|
776
|
+
createBootstrapItem(
|
|
777
|
+
"architecture",
|
|
778
|
+
"Workspace or monorepo layout",
|
|
779
|
+
"observed",
|
|
780
|
+
"A workspace declaration or workspace configuration file is present.",
|
|
781
|
+
(workspaceEvidence.length > 0 ? workspaceEvidence : ["package.json"]).map((source) => ({
|
|
782
|
+
source,
|
|
783
|
+
kind: "config"
|
|
784
|
+
}))
|
|
785
|
+
)
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
for (const directory of ["apps", "packages", "services", "libs"].filter(exists)) {
|
|
789
|
+
architecture.push(
|
|
790
|
+
createBootstrapItem(
|
|
791
|
+
"architecture",
|
|
792
|
+
`${directory}/ project boundary`,
|
|
793
|
+
"observed",
|
|
794
|
+
"Top-level project organization detected; no architectural meaning was inferred beyond its presence.",
|
|
795
|
+
[{ source: `${directory}/`, kind: "directory" }]
|
|
796
|
+
)
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
const frameworks = [
|
|
800
|
+
["next", "Next.js"],
|
|
801
|
+
["react", "React"],
|
|
802
|
+
["@nestjs/core", "NestJS"],
|
|
803
|
+
["express", "Express"],
|
|
804
|
+
["fastify", "Fastify"],
|
|
805
|
+
["vitest", "Vitest"],
|
|
806
|
+
["jest", "Jest"]
|
|
807
|
+
];
|
|
808
|
+
for (const [dependency, label] of frameworks) {
|
|
809
|
+
if (packageMetadata.dependencies.includes(dependency)) {
|
|
810
|
+
architecture.push(
|
|
811
|
+
createBootstrapItem(
|
|
812
|
+
"architecture",
|
|
813
|
+
`${label} dependency`,
|
|
814
|
+
"observed",
|
|
815
|
+
`The root manifest declares ${dependency}; its role still needs code-level confirmation.`,
|
|
816
|
+
[{ source: "package.json", kind: "manifest" }]
|
|
817
|
+
)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (scan.files.hasDockerfile) {
|
|
822
|
+
architecture.push(
|
|
823
|
+
createBootstrapItem(
|
|
824
|
+
"architecture",
|
|
825
|
+
"Container build surface",
|
|
826
|
+
"observed",
|
|
827
|
+
"A root Dockerfile is present.",
|
|
828
|
+
[{ source: "Dockerfile", kind: "config" }]
|
|
829
|
+
)
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
const candidates = buildMemoryCandidatePreviews(scan);
|
|
833
|
+
const conventions = candidates.filter(
|
|
834
|
+
(candidate) => candidate.evidence === "agent_instruction" || candidate.evidence === "project_document"
|
|
835
|
+
).slice(0, 16).map(
|
|
836
|
+
(candidate) => createBootstrapItem(
|
|
837
|
+
"conventions",
|
|
838
|
+
candidate.text.slice(0, 100),
|
|
839
|
+
"needs_review",
|
|
840
|
+
candidate.reason,
|
|
841
|
+
[{ source: candidate.source, kind: candidate.evidence }]
|
|
842
|
+
)
|
|
843
|
+
);
|
|
844
|
+
const decisions = candidates.filter(
|
|
845
|
+
(candidate) => ["git_commit_subject", "adr_or_decision_doc", "changelog"].includes(candidate.evidence)
|
|
846
|
+
).slice(0, 12).map(
|
|
847
|
+
(candidate) => createBootstrapItem(
|
|
848
|
+
"decisions",
|
|
849
|
+
candidate.text.slice(0, 100),
|
|
850
|
+
"needs_review",
|
|
851
|
+
candidate.reason,
|
|
852
|
+
[{ source: candidate.source, kind: candidate.evidence }]
|
|
853
|
+
)
|
|
854
|
+
);
|
|
855
|
+
const procedureScriptPattern = /(^|:)(build|dev|start|lint|test|type-?check|check|verify|deploy|release|migrate|seed|smoke|e2e)(:|$)/i;
|
|
856
|
+
const procedures = Object.keys(packageMetadata.scripts).filter((name) => procedureScriptPattern.test(name)).slice(0, 20).map(
|
|
857
|
+
(name) => createBootstrapItem(
|
|
858
|
+
"procedures",
|
|
859
|
+
`package script: ${name}`,
|
|
860
|
+
"observed",
|
|
861
|
+
"A root package script is configured; its exact command remains in package.json.",
|
|
862
|
+
[{ source: "package.json", kind: "package_script" }]
|
|
863
|
+
)
|
|
864
|
+
);
|
|
865
|
+
for (const source of ciFiles.slice(0, 8)) {
|
|
866
|
+
procedures.push(
|
|
867
|
+
createBootstrapItem(
|
|
868
|
+
"procedures",
|
|
869
|
+
`CI workflow: ${path.basename(source)}`,
|
|
870
|
+
"observed",
|
|
871
|
+
"Automation configuration detected; individual jobs were not interpreted during the bounded scan.",
|
|
872
|
+
[{ source, kind: "ci_workflow" }]
|
|
873
|
+
)
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
const skills = skillFiles.slice(0, 12).map(
|
|
877
|
+
(source) => createBootstrapItem(
|
|
878
|
+
"skills",
|
|
879
|
+
`Existing skill: ${path.basename(path.dirname(source)) || "root"}`,
|
|
880
|
+
"observed",
|
|
881
|
+
"An explicit SKILL.md workflow was found.",
|
|
882
|
+
[{ source, kind: "skill_definition" }]
|
|
883
|
+
)
|
|
884
|
+
);
|
|
885
|
+
const verificationScriptNames = Object.keys(packageMetadata.scripts).filter(
|
|
886
|
+
(name) => /test|lint|type|check|verify|smoke|e2e|build/i.test(name)
|
|
887
|
+
);
|
|
888
|
+
if (verificationScriptNames.length >= 2 && skills.length < 12) {
|
|
889
|
+
skills.push(
|
|
890
|
+
createBootstrapItem(
|
|
891
|
+
"skills",
|
|
892
|
+
"Verification workflow candidate",
|
|
893
|
+
"needs_review",
|
|
894
|
+
`Related scripts detected: ${verificationScriptNames.slice(0, 8).join(", ")}. Review their intended order before promoting this to a skill.`,
|
|
895
|
+
[{ source: "package.json", kind: "package_scripts" }]
|
|
896
|
+
)
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
const failureTerms = [
|
|
900
|
+
["rollback or revert", /\b(?:rollback|revert)\b/i],
|
|
901
|
+
["hotfix", /\bhotfix\b/i],
|
|
902
|
+
["regression", /\bregression\b/i],
|
|
903
|
+
["flaky verification", /\b(?:flaky|flakiness)\b/i],
|
|
904
|
+
["broken or failing behavior", /\b(?:broken|failing|failure)\b/i]
|
|
905
|
+
];
|
|
906
|
+
const recurringFailures = failureTerms.flatMap(([label, matcher]) => {
|
|
907
|
+
const matches = gitSubjects.filter((subject) => matcher.test(subject)).slice(0, 6);
|
|
908
|
+
if (matches.length < 2) {
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
return [
|
|
912
|
+
createBootstrapItem(
|
|
913
|
+
"recurringFailures",
|
|
914
|
+
`${label} signal (${matches.length} commits)`,
|
|
915
|
+
"needs_review",
|
|
916
|
+
"Repeated commit-language is a signal, not proof of a common root cause. Review the linked commits before accepting this anti-pattern.",
|
|
917
|
+
matches.map((source) => ({ source, kind: "git_commit_subject" }))
|
|
918
|
+
)
|
|
919
|
+
];
|
|
920
|
+
});
|
|
921
|
+
const verificationPatterns = verificationScriptNames.slice(0, 16).map(
|
|
922
|
+
(name) => createBootstrapItem(
|
|
923
|
+
"verificationPatterns",
|
|
924
|
+
`Verification script: ${name}`,
|
|
925
|
+
"observed",
|
|
926
|
+
"A root verification script is configured; its exact command remains in package.json.",
|
|
927
|
+
[{ source: "package.json", kind: "package_script" }]
|
|
928
|
+
)
|
|
929
|
+
);
|
|
930
|
+
const testPatternGroups = [
|
|
931
|
+
["*.test.*", /\.test\.[^/]+$/i],
|
|
932
|
+
["*.spec.*", /\.spec\.[^/]+$/i],
|
|
933
|
+
["__tests__/", /(^|\/)__tests__\//i],
|
|
934
|
+
["test_*.py", /(^|\/)test_[^/]+\.py$/i]
|
|
935
|
+
];
|
|
936
|
+
for (const [label, matcher] of testPatternGroups) {
|
|
937
|
+
const matches = testFiles.filter((source) => matcher.test(source));
|
|
938
|
+
if (matches.length === 0) {
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
verificationPatterns.push(
|
|
942
|
+
createBootstrapItem(
|
|
943
|
+
"verificationPatterns",
|
|
944
|
+
`Test pattern: ${label}`,
|
|
945
|
+
"observed",
|
|
946
|
+
`${matches.length} matching file(s) found in the bounded scan.`,
|
|
947
|
+
matches.slice(0, 6).map((source) => ({ source, kind: "test_file" }))
|
|
948
|
+
)
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
const sections = {
|
|
952
|
+
architecture: {
|
|
953
|
+
title: "Architecture",
|
|
954
|
+
status: architecture.length > 0 ? "observed" : "deferred",
|
|
955
|
+
detail: "Manifest, filesystem, dependency, and configuration patterns only.",
|
|
956
|
+
items: architecture.slice(0, 20)
|
|
957
|
+
},
|
|
958
|
+
conventions: {
|
|
959
|
+
title: "Conventions",
|
|
960
|
+
status: conventions.length > 0 ? "needs_review" : "deferred",
|
|
961
|
+
detail: "Guidance-language candidates require human review before durable import.",
|
|
962
|
+
items: conventions
|
|
963
|
+
},
|
|
964
|
+
decisions: {
|
|
965
|
+
title: "Decisions",
|
|
966
|
+
status: decisions.length > 0 ? "needs_review" : "deferred",
|
|
967
|
+
detail: "Commit and decision-document candidates are never auto-approved.",
|
|
968
|
+
items: decisions
|
|
969
|
+
},
|
|
970
|
+
procedures: {
|
|
971
|
+
title: "Procedures",
|
|
972
|
+
status: procedures.length > 0 ? "observed" : "deferred",
|
|
973
|
+
detail: "Configured scripts and CI files; execution order is not inferred.",
|
|
974
|
+
items: procedures.slice(0, 24)
|
|
975
|
+
},
|
|
976
|
+
skills: {
|
|
977
|
+
title: "Skills",
|
|
978
|
+
status: skills.some((item) => item.status === "needs_review") ? "needs_review" : skills.length > 0 ? "observed" : "deferred",
|
|
979
|
+
detail: "Explicit skill definitions plus reviewable workflow candidates.",
|
|
980
|
+
items: skills
|
|
981
|
+
},
|
|
982
|
+
recurringFailures: {
|
|
983
|
+
title: "Recurring failures",
|
|
984
|
+
status: recurringFailures.length > 0 ? "needs_review" : "deferred",
|
|
985
|
+
detail: "Only repeated failure-language signals are shown; no root cause is inferred.",
|
|
986
|
+
items: recurringFailures
|
|
987
|
+
},
|
|
988
|
+
criticalCode: {
|
|
989
|
+
title: "Critical code",
|
|
990
|
+
status: "deferred",
|
|
991
|
+
detail: "Critical symbols require Code Graph indexing; the local bootstrap does not guess them.",
|
|
992
|
+
items: []
|
|
993
|
+
},
|
|
994
|
+
verificationPatterns: {
|
|
995
|
+
title: "Verification patterns",
|
|
996
|
+
status: verificationPatterns.length > 0 ? "observed" : "deferred",
|
|
997
|
+
detail: "Configured checks and test naming patterns from the bounded local scan.",
|
|
998
|
+
items: verificationPatterns.slice(0, 24)
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
const summary = Object.fromEntries(
|
|
1002
|
+
Object.entries(sections).map(([key, section]) => [key, section.items.length])
|
|
1003
|
+
);
|
|
1004
|
+
const coverage = {
|
|
1005
|
+
gitHistory: createBootstrapCoverage(
|
|
1006
|
+
scan.git.detected ? "partial" : "unavailable",
|
|
1007
|
+
scan.git.detected ? "Inspected up to 120 recent commit subjects; commit bodies and diffs were not interpreted." : "No local Git repository was detected.",
|
|
1008
|
+
scan.git.detected ? ["git log"] : []
|
|
1009
|
+
),
|
|
1010
|
+
pullRequests: createBootstrapCoverage(
|
|
1011
|
+
"unavailable",
|
|
1012
|
+
"Pull request history requires an explicitly connected provider.",
|
|
1013
|
+
[]
|
|
1014
|
+
),
|
|
1015
|
+
issues: createBootstrapCoverage(
|
|
1016
|
+
"unavailable",
|
|
1017
|
+
"Issue history requires an explicitly connected provider.",
|
|
1018
|
+
[]
|
|
1019
|
+
),
|
|
1020
|
+
instructions: createBootstrapCoverage(
|
|
1021
|
+
instructionSources.length > 0 ? "observed" : "unavailable",
|
|
1022
|
+
instructionSources.length > 0 ? "Project instruction files were included in the bounded local scan." : "No AGENTS.md, CLAUDE.md, or Copilot instruction file was found.",
|
|
1023
|
+
instructionSources
|
|
1024
|
+
),
|
|
1025
|
+
documentation: createBootstrapCoverage(
|
|
1026
|
+
documentationSources.length > 0 ? "partial" : "unavailable",
|
|
1027
|
+
documentationSources.length > 0 ? `Inspected a bounded set of ${documentationSources.length} README, changelog, memory, and docs files.` : "No README, changelog, memory, or docs files were found.",
|
|
1028
|
+
documentationSources
|
|
1029
|
+
),
|
|
1030
|
+
ci: createBootstrapCoverage(
|
|
1031
|
+
ciFiles.length > 0 ? "observed" : "unavailable",
|
|
1032
|
+
ciFiles.length > 0 ? "CI configuration files were detected." : "No standard CI configuration was found.",
|
|
1033
|
+
ciFiles
|
|
1034
|
+
),
|
|
1035
|
+
packageScripts: createBootstrapCoverage(
|
|
1036
|
+
Object.keys(packageMetadata.scripts).length > 0 ? "observed" : "unavailable",
|
|
1037
|
+
Object.keys(packageMetadata.scripts).length > 0 ? "Root package scripts were inspected." : "No root package scripts were available.",
|
|
1038
|
+
Object.keys(packageMetadata.scripts).length > 0 ? ["package.json"] : []
|
|
1039
|
+
),
|
|
1040
|
+
testPatterns: createBootstrapCoverage(
|
|
1041
|
+
testFiles.length > 0 ? "partial" : "unavailable",
|
|
1042
|
+
testFiles.length > 0 ? `Found ${testFiles.length} test-like files within a ${PROJECT_BOOTSTRAP_MAX_WALKED_FILES}-file scan cap.` : `No test-like files were found within the ${PROJECT_BOOTSTRAP_MAX_WALKED_FILES}-file scan cap.`,
|
|
1043
|
+
testFiles.slice(0, 20)
|
|
1044
|
+
),
|
|
1045
|
+
releaseHistory: createBootstrapCoverage(
|
|
1046
|
+
releaseTags.length > 0 || exists("CHANGELOG.md") ? "partial" : "unavailable",
|
|
1047
|
+
releaseTags.length > 0 || exists("CHANGELOG.md") ? `Detected ${releaseTags.length} recent tag(s)${exists("CHANGELOG.md") ? " and CHANGELOG.md" : ""}.` : "No Git tags or root CHANGELOG.md were detected.",
|
|
1048
|
+
[
|
|
1049
|
+
...releaseTags.map((tag) => `git tag ${tag}`),
|
|
1050
|
+
...exists("CHANGELOG.md") ? ["CHANGELOG.md"] : []
|
|
1051
|
+
]
|
|
1052
|
+
),
|
|
1053
|
+
codeGraph: createBootstrapCoverage(
|
|
1054
|
+
"deferred",
|
|
1055
|
+
"Critical symbols and structural impact require Code Graph indexing after activation.",
|
|
1056
|
+
[]
|
|
1057
|
+
)
|
|
1058
|
+
};
|
|
1059
|
+
return {
|
|
1060
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1061
|
+
repoPath: cwd,
|
|
1062
|
+
reviewPrompt: "Review what Snipara learned about your project.",
|
|
1063
|
+
scanLimits: {
|
|
1064
|
+
walkedFiles: PROJECT_BOOTSTRAP_MAX_WALKED_FILES,
|
|
1065
|
+
matchedFilesPerQuery: PROJECT_BOOTSTRAP_MAX_MATCHED_FILES,
|
|
1066
|
+
gitCommitSubjects: 120
|
|
1067
|
+
},
|
|
1068
|
+
summary,
|
|
1069
|
+
coverage,
|
|
1070
|
+
sections
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
function createBootstrapCoverage(status, detail, sources) {
|
|
1074
|
+
return { status, detail, sources };
|
|
1075
|
+
}
|
|
1076
|
+
function writeProjectBootstrap(report) {
|
|
1077
|
+
fs.mkdirSync(getActivationDir(), { recursive: true });
|
|
1078
|
+
fs.writeFileSync(getProjectBootstrapJsonPath(), JSON.stringify(report, null, 2) + "\n", "utf8");
|
|
1079
|
+
const lines = [
|
|
1080
|
+
"# PROJECT BOOTSTRAP",
|
|
1081
|
+
"",
|
|
1082
|
+
`Generated: ${report.generatedAt}`,
|
|
1083
|
+
`Repository: ${report.repoPath}`,
|
|
1084
|
+
"",
|
|
1085
|
+
"## Coverage",
|
|
1086
|
+
"",
|
|
1087
|
+
...Object.entries(report.coverage).map(
|
|
1088
|
+
([name, entry]) => `- ${name}: ${entry.status} \u2014 ${entry.detail}`
|
|
1089
|
+
),
|
|
1090
|
+
"",
|
|
1091
|
+
...Object.values(report.sections).flatMap((section) => [
|
|
1092
|
+
`## ${section.title} \u2014 ${section.items.length}`,
|
|
1093
|
+
"",
|
|
1094
|
+
section.detail,
|
|
1095
|
+
"",
|
|
1096
|
+
...section.items.length > 0 ? section.items.flatMap((item) => [
|
|
1097
|
+
`- [${item.status}] ${item.title}`,
|
|
1098
|
+
` ${item.detail}`,
|
|
1099
|
+
...item.evidence.length > 0 ? [
|
|
1100
|
+
` Evidence: ${item.evidence.map((entry) => entry.source).join(", ")}`
|
|
1101
|
+
] : []
|
|
1102
|
+
]) : ["- No evidence-backed item available yet."],
|
|
1103
|
+
""
|
|
1104
|
+
]),
|
|
1105
|
+
`**${report.reviewPrompt}**`,
|
|
1106
|
+
""
|
|
1107
|
+
];
|
|
1108
|
+
fs.writeFileSync(getProjectBootstrapMarkdownPath(), lines.join("\n"), "utf8");
|
|
1109
|
+
}
|
|
1110
|
+
function addProjectBootstrapToFirstWorkBrief(manifest, report) {
|
|
1111
|
+
manifest.firstBrief.sections.splice(1, 0, {
|
|
1112
|
+
title: "Project Bootstrap",
|
|
1113
|
+
lines: [
|
|
1114
|
+
`Architecture: ${report.summary.architecture} pattern(s).`,
|
|
1115
|
+
`Conventions: ${report.summary.conventions} candidate(s).`,
|
|
1116
|
+
`Decisions: ${report.summary.decisions} inferred candidate(s).`,
|
|
1117
|
+
`Procedures: ${report.summary.procedures} observed workflow(s).`,
|
|
1118
|
+
`Skills: ${report.summary.skills} existing or candidate workflow(s).`,
|
|
1119
|
+
`Recurring failures: ${report.summary.recurringFailures} review candidate(s).`,
|
|
1120
|
+
`Critical code: ${report.summary.criticalCode}; deferred until Code Graph indexes symbols.`,
|
|
1121
|
+
`Verification patterns: ${report.summary.verificationPatterns} observed pattern(s).`,
|
|
1122
|
+
report.reviewPrompt
|
|
1123
|
+
]
|
|
1124
|
+
});
|
|
582
1125
|
}
|
|
583
1126
|
function createInitialFirstWorkBrief(answers, detection, scan) {
|
|
584
1127
|
const surfaceContract = buildActivationSurfaceContract(answers.aiClient);
|
|
@@ -651,8 +1194,6 @@ function createActivationManifest(answers, detection, mode = "default", scan) {
|
|
|
651
1194
|
const dayZeroQuery = "What is this project, what is currently in flight, and what should an agent know before making the first edit?";
|
|
652
1195
|
const surfaceContract = buildActivationSurfaceContract(answers.aiClient);
|
|
653
1196
|
return {
|
|
654
|
-
version: "create-snipara-activation-v1",
|
|
655
|
-
schemaVersion: ACTIVATION_MANIFEST_SCHEMA_VERSION,
|
|
656
1197
|
activationId: buildActivationId(),
|
|
657
1198
|
mode,
|
|
658
1199
|
surfaceContract,
|
|
@@ -706,6 +1247,10 @@ function createActivationManifest(answers, detection, mode = "default", scan) {
|
|
|
706
1247
|
hasCompanion ? "queued" : "skipped",
|
|
707
1248
|
hasCompanion ? "Free Code Graph indexing starts from local source overlay within capacity caps." : "Free Code Graph is available after companion or hosted provider sync."
|
|
708
1249
|
),
|
|
1250
|
+
projectBootstrap: createActivationLane(
|
|
1251
|
+
"queued",
|
|
1252
|
+
"Bounded local cold-start analysis will prepare an evidence-linked Project Bootstrap."
|
|
1253
|
+
),
|
|
709
1254
|
memoryCandidates: createActivationLane(
|
|
710
1255
|
hasCompanion ? "needs_review" : "skipped",
|
|
711
1256
|
hasCompanion ? "Candidate decisions stay pending review; no durable memory is auto-approved." : "Install companion later to extract reviewable memory candidates."
|
|
@@ -728,7 +1273,9 @@ function createActivationManifest(answers, detection, mode = "default", scan) {
|
|
|
728
1273
|
legacyManifestPath: path.relative(process.cwd(), getActivationManifestPath()),
|
|
729
1274
|
firstBriefPath: null,
|
|
730
1275
|
handoffPath: null,
|
|
731
|
-
memoryCandidatesPath: null
|
|
1276
|
+
memoryCandidatesPath: null,
|
|
1277
|
+
projectBootstrapJsonPath: null,
|
|
1278
|
+
projectBootstrapMarkdownPath: null
|
|
732
1279
|
},
|
|
733
1280
|
nextActions: buildActivationNextActions(surfaceContract, answers),
|
|
734
1281
|
firstBrief: createInitialFirstWorkBrief(answers, detection, localScan),
|
|
@@ -918,6 +1465,22 @@ async function runHostedDayZeroCorpusSync(answers, manifest, enabled) {
|
|
|
918
1465
|
updateActivationManifest(manifest);
|
|
919
1466
|
}
|
|
920
1467
|
function createActivationArtifacts(answers, manifest) {
|
|
1468
|
+
const projectBootstrap = buildProjectBootstrap(manifest.localScan);
|
|
1469
|
+
writeProjectBootstrap(projectBootstrap);
|
|
1470
|
+
manifest.artifacts.projectBootstrapJsonPath = path.relative(
|
|
1471
|
+
process.cwd(),
|
|
1472
|
+
getProjectBootstrapJsonPath()
|
|
1473
|
+
);
|
|
1474
|
+
manifest.artifacts.projectBootstrapMarkdownPath = path.relative(
|
|
1475
|
+
process.cwd(),
|
|
1476
|
+
getProjectBootstrapMarkdownPath()
|
|
1477
|
+
);
|
|
1478
|
+
manifest.lanes.projectBootstrap = createActivationLane(
|
|
1479
|
+
"needs_review",
|
|
1480
|
+
"Evidence-linked Project Bootstrap generated; inferred candidates require review.",
|
|
1481
|
+
manifest.artifacts.projectBootstrapMarkdownPath
|
|
1482
|
+
);
|
|
1483
|
+
addProjectBootstrapToFirstWorkBrief(manifest, projectBootstrap);
|
|
921
1484
|
const candidates = buildMemoryCandidatePreviews(manifest.localScan);
|
|
922
1485
|
writeMemoryCandidates(candidates);
|
|
923
1486
|
manifest.artifacts.memoryCandidatesPath = path.relative(process.cwd(), getMemoryCandidatesPath());
|
|
@@ -1258,8 +1821,6 @@ function buildActivationJsonSummary(manifest, validation) {
|
|
|
1258
1821
|
ok: true,
|
|
1259
1822
|
activationStatus,
|
|
1260
1823
|
activationId: manifest.activationId,
|
|
1261
|
-
schemaVersion: manifest.schemaVersion,
|
|
1262
|
-
consumerContractVersion: manifest.consumerContract.version,
|
|
1263
1824
|
projectSlug: manifest.projectSlug,
|
|
1264
1825
|
aiClient: manifest.aiClient,
|
|
1265
1826
|
profile: validation.profile,
|
|
@@ -3524,22 +4085,24 @@ function buildActivationSurfaceContract(client) {
|
|
|
3524
4085
|
function buildEditorActivationConsumerContract(client) {
|
|
3525
4086
|
const secretRule = client === "vscode" ? "Use VS Code SecretStorage for SNIPARA_API_KEY and pass it to create-snipara through the child-process environment; never write raw secrets into extension settings or logs." : "Pass SNIPARA_API_KEY through the process environment or client secret store; never parse, log, or persist raw secrets from terminal output.";
|
|
3526
4087
|
return {
|
|
3527
|
-
version: ACTIVATION_CONSUMER_CONTRACT_VERSION,
|
|
3528
4088
|
audience: "editor_extension",
|
|
3529
4089
|
stableFiles: {
|
|
3530
4090
|
activationManifest: ".snipara/activation/activation-manifest.json",
|
|
3531
4091
|
firstBrief: ".snipara/activation/first-brief.md",
|
|
3532
4092
|
handoff: ".snipara/activation/handoff.md",
|
|
3533
|
-
memoryCandidates: ".snipara/activation/memory-candidates.json"
|
|
4093
|
+
memoryCandidates: ".snipara/activation/memory-candidates.json",
|
|
4094
|
+
projectBootstrapJson: ".snipara/activation/project-bootstrap.json",
|
|
4095
|
+
projectBootstrapMarkdown: ".snipara/activation/project-bootstrap.md"
|
|
3534
4096
|
},
|
|
3535
4097
|
requiredManifestFields: [
|
|
3536
|
-
"schemaVersion",
|
|
3537
|
-
"consumerContract.version",
|
|
3538
4098
|
"surfaceContract",
|
|
3539
4099
|
"artifacts.activationManifestPath",
|
|
3540
4100
|
"artifacts.firstBriefPath",
|
|
3541
4101
|
"artifacts.handoffPath",
|
|
4102
|
+
"artifacts.projectBootstrapJsonPath",
|
|
4103
|
+
"artifacts.projectBootstrapMarkdownPath",
|
|
3542
4104
|
"lanes.connection.status",
|
|
4105
|
+
"lanes.projectBootstrap.status",
|
|
3543
4106
|
"lanes.firstBrief.status",
|
|
3544
4107
|
"nextActions"
|
|
3545
4108
|
],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-snipara",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Onboard Snipara Hosted MCP Context + Memory with
|
|
3
|
+
"version": "2.0.17",
|
|
4
|
+
"description": "Onboard Snipara Hosted MCP Context + Memory with project bootstrap intelligence and optional local workflows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|