create-snipara 2.0.8 → 2.0.10

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +443 -35
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,6 +26,7 @@ Use it for:
26
26
  - free account signup with reviewed Context + Memory
27
27
  - local project wiring, `doctor`, `repair`, and `upgrade`
28
28
  - local source activation without GitHub, plus optional GitHub repository sync and PR Answer Packs approval when the current folder is a GitHub repo
29
+ - day-zero Project Intelligence proof: a First Work Brief, a ready-to-run `snipara_context_query`, a small hosted README/docs seed sync for virgin projects, review-only decision archaeology candidates, possible doc-staleness findings, and an estimated context economy report from the user's own repository
29
30
 
30
31
  ## Why It Exists
31
32
 
@@ -114,7 +115,7 @@ That distinction matters:
114
115
  - installs optional `snipara-sandbox`
115
116
  - installs optional `snipara-orchestrator` for proof gates, drift checks, and htask workflows when requested
116
117
  - configures `.mcp.json`
117
- - generates merge-ready AI agent templates in `.snipara/templates/`, including Pro+ symbol-card, code-impact, and managed workflow guidance for paid Context plans
118
+ - generates merge-ready AI agent templates in `.snipara/templates/`, including symbol-card, code-impact, and managed workflow guidance within plan capacity
118
119
  - creates `AGENTS.md` and `CLAUDE.md` when missing, or appends a marked Snipara section when those files already exist
119
120
  - creates `.cursor/rules/snipara.mdc` and `.codex/config.toml` when those files do not already exist
120
121
  - validates Codex config against Hosted MCP plus `bearer_token_env_var = "SNIPARA_API_KEY"` and tells users to restart Codex after MCP config changes
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import { homedir } from "os";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "create-snipara",
16
- version: "2.0.8",
16
+ version: "2.0.10",
17
17
  description: "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
18
18
  main: "dist/index.js",
19
19
  type: "module",
@@ -104,6 +104,9 @@ var SANDBOX_COMMAND_NAME = "snipara-sandbox";
104
104
  var LEGACY_SANDBOX_COMMAND_NAME = "rlm";
105
105
  var ORCHESTRATOR_COMMAND_NAME = "snipara-orchestrator";
106
106
  var DEVICE_CLIENT_ID = "create-snipara";
107
+ var DAY_ZERO_CORPUS_MAX_FILES = 8;
108
+ var DAY_ZERO_CORPUS_MAX_FILE_CHARS = 12e3;
109
+ var DAY_ZERO_CORPUS_MAX_TOTAL_CHARS = 5e4;
107
110
  var EXPERIENCE_GOAL_SECTION = `## Experience Goal
108
111
 
109
112
  The target experience is outcome-first agent work across supported clients:
@@ -115,6 +118,19 @@ deployment, and production smoke checks behind the scenes. Surface important
115
118
  decisions, blockers, and evidence, but do not make the user manually orchestrate
116
119
  routine Snipara or release mechanics.`;
117
120
  var UNIVERSAL_CLIENT_GUIDANCE = "The target experience is outcome-first: the user can ask for engineering judgment or a release, and the agent should use Snipara context, workflow state, verification, package checks, Git, deploy, and smoke checks without making the user orchestrate routine mechanics.";
121
+ var DECISION_COMMIT_TERMS = [
122
+ "adopt",
123
+ "because",
124
+ "decide",
125
+ "decision",
126
+ "drop",
127
+ "migrate",
128
+ "prefer",
129
+ "replace",
130
+ "revert",
131
+ "standardize",
132
+ "switch"
133
+ ];
118
134
  function buildActivationId() {
119
135
  return `act_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
120
136
  }
@@ -200,23 +216,196 @@ function readRecentCommitSubjects() {
200
216
  return [];
201
217
  }
202
218
  }
219
+ function readDecisionCommitSubjectLines() {
220
+ try {
221
+ const output = execSync("git log --pretty=format:%h%x20%s --max-count=50", {
222
+ cwd: process.cwd(),
223
+ encoding: "utf-8",
224
+ stdio: ["ignore", "pipe", "ignore"],
225
+ timeout: 1500
226
+ }).trim();
227
+ if (!output) {
228
+ return [];
229
+ }
230
+ return output.split(/\r?\n/).map((line) => line.trim()).filter((line) => DECISION_COMMIT_TERMS.some((term) => line.toLowerCase().includes(term))).slice(0, 8);
231
+ } catch {
232
+ return [];
233
+ }
234
+ }
203
235
  function readMemoryCandidateLines(relativePath) {
204
236
  const absolutePath = path.join(process.cwd(), relativePath);
205
237
  if (!fs.existsSync(absolutePath) || fs.statSync(absolutePath).isDirectory()) {
206
238
  return [];
207
239
  }
208
240
  const content = fs.readFileSync(absolutePath, "utf8").slice(0, 24e3);
209
- return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => /decision|decided|must|should|always|never|convention|rule/i.test(line)).slice(0, 3);
241
+ return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => /decision|decided|must|should|always|never|convention|rule/i.test(line)).slice(0, 4).map((line) => ({
242
+ source: relativePath,
243
+ text: line.slice(0, 280),
244
+ status: "needs_review",
245
+ reason: "Detected project guidance language. Review before storing as durable memory.",
246
+ evidence: classifyDecisionSource(relativePath)
247
+ }));
248
+ }
249
+ function estimateFileTokens(relativePath) {
250
+ const absolutePath = path.join(process.cwd(), relativePath);
251
+ if (!fs.existsSync(absolutePath) || fs.statSync(absolutePath).isDirectory()) {
252
+ return 0;
253
+ }
254
+ try {
255
+ const content = fs.readFileSync(absolutePath, "utf8");
256
+ return Math.ceil(content.length / 4);
257
+ } catch {
258
+ return 0;
259
+ }
210
260
  }
211
261
  function buildMemoryCandidatePreviews(scan) {
212
- return scan.files.candidateDocs.flatMap(
213
- (source) => readMemoryCandidateLines(source).map((line) => ({
262
+ const seen = /* @__PURE__ */ new Set();
263
+ const gitCandidates = scan.git.recentDecisionCommits.map((line) => ({
264
+ source: "git log",
265
+ text: line.slice(0, 280),
266
+ status: "needs_review",
267
+ reason: "Detected decision-language in recent commit history. Confirm intent before storing.",
268
+ evidence: "git_commit_subject"
269
+ }));
270
+ const candidates = [
271
+ ...scan.files.candidateDocs.flatMap(readMemoryCandidateLines),
272
+ ...gitCandidates
273
+ ];
274
+ return candidates.filter((candidate) => {
275
+ const key = `${candidate.source}:${candidate.text}`;
276
+ if (seen.has(key)) {
277
+ return false;
278
+ }
279
+ seen.add(key);
280
+ return true;
281
+ }).slice(0, 20);
282
+ }
283
+ function readTextFileSnippet(relativePath, maxChars = 24e3) {
284
+ const absolutePath = path.join(process.cwd(), relativePath);
285
+ if (!fs.existsSync(absolutePath) || fs.statSync(absolutePath).isDirectory()) {
286
+ return "";
287
+ }
288
+ try {
289
+ return fs.readFileSync(absolutePath, "utf8").slice(0, maxChars);
290
+ } catch {
291
+ return "";
292
+ }
293
+ }
294
+ function collectDayZeroCorpusDocuments(scan) {
295
+ const documents = [];
296
+ let totalChars = 0;
297
+ const cwd = process.cwd();
298
+ const rootReadmes = fs.existsSync(cwd) ? fs.readdirSync(cwd).filter((entry) => /^readme(?:\.[a-z0-9_-]+)?\.mdx?$/i.test(entry)).sort() : [];
299
+ const sources = [
300
+ ...rootReadmes,
301
+ ...listFilesUnder("docs", DAY_ZERO_CORPUS_MAX_FILES * 8),
302
+ ...scan.files.candidateDocs
303
+ ];
304
+ const seen = /* @__PURE__ */ new Set();
305
+ for (const source of sources) {
306
+ const normalized = source.replace(/\\/g, "/");
307
+ if (seen.has(normalized)) {
308
+ continue;
309
+ }
310
+ seen.add(normalized);
311
+ const lower = normalized.toLowerCase();
312
+ const isReadme = /^readme(?:\.[a-z0-9_-]+)?\.mdx?$/.test(lower);
313
+ const isDocsMarkdown = lower.startsWith("docs/") && /\.(?:md|mdx|markdown|txt|rst|adoc)$/.test(lower);
314
+ if (!isReadme && !isDocsMarkdown) {
315
+ continue;
316
+ }
317
+ if (documents.length >= DAY_ZERO_CORPUS_MAX_FILES) {
318
+ break;
319
+ }
320
+ if (totalChars >= DAY_ZERO_CORPUS_MAX_TOTAL_CHARS) {
321
+ break;
322
+ }
323
+ const remainingChars = DAY_ZERO_CORPUS_MAX_TOTAL_CHARS - totalChars;
324
+ const content = readTextFileSnippet(
214
325
  source,
215
- text: line.slice(0, 280),
216
- status: "needs_review",
217
- reason: "Detected project guidance language. Review before storing as durable memory."
218
- }))
219
- ).slice(0, 12);
326
+ Math.min(DAY_ZERO_CORPUS_MAX_FILE_CHARS, remainingChars)
327
+ ).trim();
328
+ if (!content) {
329
+ continue;
330
+ }
331
+ documents.push({
332
+ path: normalized,
333
+ content
334
+ });
335
+ totalChars += content.length;
336
+ }
337
+ return documents;
338
+ }
339
+ function createDayZeroCorpusReport(status, documents, detail, options = {}) {
340
+ return {
341
+ status,
342
+ documents: documents.map((document) => ({
343
+ path: document.path,
344
+ chars: document.content.length
345
+ })),
346
+ detail,
347
+ ...options.tool ? { tool: options.tool } : {},
348
+ ...options.error ? { error: options.error.slice(0, 500) } : {}
349
+ };
350
+ }
351
+ function buildStalenessFindings(scan) {
352
+ const findings = [];
353
+ const scriptSet = new Set(scan.scripts);
354
+ for (const source of scan.files.candidateDocs.slice(0, 30)) {
355
+ const content = readTextFileSnippet(source);
356
+ if (!content) {
357
+ continue;
358
+ }
359
+ const scriptMatches = [...content.matchAll(/\b(?:npm|pnpm|yarn|bun)\s+run\s+([a-z0-9:_-]+)/gi)];
360
+ for (const match of scriptMatches) {
361
+ const scriptName = match[1];
362
+ if (scriptName && !scriptSet.has(scriptName)) {
363
+ findings.push({
364
+ source,
365
+ severity: "possible",
366
+ summary: `Documentation references \`${match[0]}\`, but package.json has no \`${scriptName}\` script.`,
367
+ recommendation: "Verify the setup docs or add the missing script before relying on this instruction."
368
+ });
369
+ }
370
+ }
371
+ if (scan.packageManagers.length === 1 && scan.packageManagers[0] === "pnpm" && /\bnpm install\b/i.test(content)) {
372
+ findings.push({
373
+ source,
374
+ severity: "possible",
375
+ summary: "Documentation mentions `npm install`, but the repo has a pnpm lockfile.",
376
+ recommendation: "Confirm whether npm remains supported or update setup docs to pnpm."
377
+ });
378
+ }
379
+ }
380
+ return findings.slice(0, 8);
381
+ }
382
+ function buildContextEconomyReport(scan, dayZeroQuery) {
383
+ const indexedSourceTokens = scan.files.candidateDocs.reduce(
384
+ (total, source) => total + estimateFileTokens(source),
385
+ 0
386
+ );
387
+ const rawWindowBaselineTokens = Math.max(indexedSourceTokens, 8e3);
388
+ const estimatedPromptTokens = Math.ceil(dayZeroQuery.length / 4);
389
+ const estimatedRetrievedTokens = Math.min(
390
+ rawWindowBaselineTokens,
391
+ Math.max(estimatedPromptTokens + scan.files.candidateDocs.length * 160, 1200)
392
+ );
393
+ const estimatedSavedTokens = Math.max(0, rawWindowBaselineTokens - estimatedRetrievedTokens);
394
+ return {
395
+ basis: "activation_estimate",
396
+ rawWindowBaselineTokens,
397
+ estimatedRetrievedTokens,
398
+ estimatedSavedTokens,
399
+ savingsPercent: rawWindowBaselineTokens > 0 ? Math.round(estimatedSavedTokens / rawWindowBaselineTokens * 100) : 0,
400
+ note: "Estimated from activation sources until hosted query receipts provide measured savings."
401
+ };
402
+ }
403
+ function buildAgentReadinessReport(scan) {
404
+ const findings = buildStalenessFindings(scan);
405
+ return {
406
+ stalenessFindings: findings,
407
+ summary: findings.length > 0 ? `${findings.length} possible doc staleness finding(s) need review.` : "No obvious setup-doc staleness found in activation sources."
408
+ };
220
409
  }
221
410
  function writeMemoryCandidates(candidates) {
222
411
  fs.mkdirSync(getActivationDir(), { recursive: true });
@@ -261,15 +450,7 @@ function writeInitialHandoff(answers, manifest) {
261
450
  function detectLocalActivationScan(detection) {
262
451
  const cwd = process.cwd();
263
452
  const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
264
- const candidateDocs = [
265
- "README.md",
266
- "AGENTS.md",
267
- "CLAUDE.md",
268
- "CHANGELOG.md",
269
- "docs",
270
- ".cursor/rules",
271
- ".github"
272
- ].filter(exists);
453
+ const candidateDocs = findDecisionArchaeologySources(cwd);
273
454
  const lockfiles = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"].filter(
274
455
  exists
275
456
  );
@@ -305,15 +486,76 @@ function detectLocalActivationScan(detection) {
305
486
  git: {
306
487
  detected: detection.inGitRepo,
307
488
  branch: detection.inGitRepo ? detectGitBranch() : null,
308
- remote: detection.inGitRepo ? readGitRemote() : null
489
+ remote: detection.inGitRepo ? readGitRemote() : null,
490
+ recentDecisionCommits: detection.inGitRepo ? readDecisionCommitSubjectLines() : []
491
+ }
492
+ };
493
+ }
494
+ function classifyDecisionSource(relativePath) {
495
+ if (/(^|\/)(adr|decisions?)(\/|$)/i.test(relativePath)) {
496
+ return "adr_or_decision_doc";
497
+ }
498
+ if (/AGENTS\.md|CLAUDE\.md|MEMORY\.md|\.cursor\/rules|copilot-instructions/i.test(relativePath)) {
499
+ return "agent_instruction";
500
+ }
501
+ if (/CHANGELOG/i.test(relativePath)) {
502
+ return "changelog";
503
+ }
504
+ return "project_document";
505
+ }
506
+ function listFilesUnder(relativeDir, maxFiles) {
507
+ const root = path.join(process.cwd(), relativeDir);
508
+ if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
509
+ return [];
510
+ }
511
+ const results = [];
512
+ const visit = (dir) => {
513
+ if (results.length >= maxFiles) {
514
+ return;
515
+ }
516
+ for (const entry of fs.readdirSync(dir).sort()) {
517
+ const absolutePath = path.join(dir, entry);
518
+ const stat = fs.statSync(absolutePath);
519
+ if (stat.isDirectory()) {
520
+ visit(absolutePath);
521
+ } else if (/\.(md|mdx|txt|mdc)$/i.test(entry)) {
522
+ results.push(path.relative(process.cwd(), absolutePath));
523
+ }
524
+ if (results.length >= maxFiles) {
525
+ return;
526
+ }
309
527
  }
310
528
  };
529
+ visit(root);
530
+ return results;
531
+ }
532
+ function findDecisionArchaeologySources(cwd) {
533
+ const exists = (relativePath) => fs.existsSync(path.join(cwd, relativePath));
534
+ const directSources = [
535
+ "README.md",
536
+ "CHANGELOG.md",
537
+ "MEMORY.md",
538
+ ...["AGENTS.md", "CLAUDE.md", ".github/copilot-instructions.md"].filter(
539
+ (source) => exists(source)
540
+ )
541
+ ].filter(exists);
542
+ const archaeologySources = [
543
+ ...listFilesUnder("docs/adr", 20),
544
+ ...listFilesUnder("docs/adrs", 20),
545
+ ...listFilesUnder("docs/decision", 20),
546
+ ...listFilesUnder("docs/decisions", 20),
547
+ ...listFilesUnder(".cursor/rules", 20)
548
+ ];
549
+ return [.../* @__PURE__ */ new Set([...directSources, ...archaeologySources])].slice(0, 80);
311
550
  }
312
551
  function createInitialFirstWorkBrief(answers, detection, scan) {
313
552
  const githubLine = answers.githubRepo ? `GitHub detected: ${answers.githubRepo.fullName}` : "No GitHub integration required for first value.";
314
553
  const commands = scan.scripts.length > 0 ? scan.scripts.join(", ") : "no package scripts detected";
315
554
  const docs = scan.files.candidateDocs.length > 0 ? scan.files.candidateDocs.join(", ") : "no standard docs detected";
316
555
  const languages = scan.languages.length > 0 ? scan.languages.join(", ") : "not detected yet";
556
+ const hotFiles = scan.files.candidateDocs.slice(0, 5);
557
+ const hotFileLine = hotFiles.length > 0 ? `First files to ground on: ${hotFiles.join(", ")}.` : "First grounding files will appear after local source sync or hosted provider sync.";
558
+ const dayZeroQuery = "What is this project, what is currently in flight, and what should an agent know before making the first edit?";
317
559
  return {
318
560
  title: `First Work Brief for ${answers.projectSlug}`,
319
561
  status: "partial",
@@ -332,9 +574,17 @@ function createInitialFirstWorkBrief(answers, detection, scan) {
332
574
  lines: [
333
575
  `Initial docs: ${docs}.`,
334
576
  `Package scripts: ${commands}.`,
577
+ hotFileLine,
335
578
  "Source indexing runs before claiming architecture certainty."
336
579
  ]
337
580
  },
581
+ {
582
+ title: "Day-zero answer pack",
583
+ lines: [
584
+ `Ask Snipara: "${dayZeroQuery}"`,
585
+ "Goal: reach one correct, non-obvious statement about this repository in under 5 minutes."
586
+ ]
587
+ },
338
588
  {
339
589
  title: "Current risks",
340
590
  lines: [
@@ -349,22 +599,40 @@ function createInitialFirstWorkBrief(answers, detection, scan) {
349
599
  {
350
600
  title: "Top impacted modules",
351
601
  lines: [
352
- "Code Graph is indexing; impacted modules will improve as symbols are available.",
353
- "Use the activation dashboard or companion code impact once indexing reports ready."
602
+ "Code Graph is available on Free within capacity caps; first answers improve as symbols index.",
603
+ "For the first PR, run snipara_code_impact or companion code impact before editing."
354
604
  ]
355
605
  }
356
606
  ]
357
607
  };
358
608
  }
359
- function createActivationManifest(answers, detection, mode = "default") {
609
+ function createActivationManifest(answers, detection, mode = "default", scan) {
360
610
  const now = (/* @__PURE__ */ new Date()).toISOString();
361
611
  const hasHostedConfig = answers.profile !== "runtime-only";
362
612
  const hasCompanion = answers.installCompanion;
363
- const localScan = detectLocalActivationScan(detection);
613
+ const localScan = scan ?? detectLocalActivationScan(detection);
614
+ const dayZeroQuery = "What is this project, what is currently in flight, and what should an agent know before making the first edit?";
364
615
  return {
365
616
  version: "create-snipara-activation-v1",
366
617
  activationId: buildActivationId(),
367
618
  mode,
619
+ activationMetric: {
620
+ name: "time_to_how_did_it_know_that",
621
+ targetMinutes: 5,
622
+ proof: "A correct, non-obvious statement about this repository from the first answer pack."
623
+ },
624
+ dayZeroQuery: {
625
+ tool: "snipara_context_query",
626
+ query: dayZeroQuery,
627
+ expectedOutcome: "First own-repo answer pack before the user reads product docs."
628
+ },
629
+ dayZeroCorpus: createDayZeroCorpusReport(
630
+ "queued",
631
+ collectDayZeroCorpusDocuments(localScan),
632
+ "README and docs Markdown will be submitted to hosted context before the first day-zero query when live hosted checks are enabled."
633
+ ),
634
+ agentReadiness: buildAgentReadinessReport(localScan),
635
+ contextEconomy: buildContextEconomyReport(localScan, dayZeroQuery),
368
636
  projectSlug: answers.projectSlug,
369
637
  projectId: answers.projectId || null,
370
638
  repoPath: process.cwd(),
@@ -390,12 +658,12 @@ function createActivationManifest(answers, detection, mode = "default") {
390
658
  hasCompanion ? "Local source activation will create a repository snapshot." : "Install companion later to activate local source context."
391
659
  ),
392
660
  contextIndex: createActivationLane(
393
- hasCompanion ? "queued" : "skipped",
394
- hasCompanion ? "Context indexing starts from local docs and instruction files." : "Context indexing requires companion or hosted provider sync."
661
+ hasHostedConfig || hasCompanion ? "queued" : "skipped",
662
+ hasCompanion ? "Context indexing starts from local docs and instruction files." : hasHostedConfig ? "Day-zero README/docs sync will seed hosted context when live hosted checks run." : "Context indexing requires companion or hosted provider sync."
395
663
  ),
396
664
  codeGraph: createActivationLane(
397
665
  hasCompanion ? "queued" : "skipped",
398
- hasCompanion ? "Code Graph indexing starts from local source overlay." : "Code Graph indexing requires companion or hosted provider sync."
666
+ hasCompanion ? "Free Code Graph indexing starts from local source overlay within capacity caps." : "Free Code Graph is available after companion or hosted provider sync."
399
667
  ),
400
668
  memoryCandidates: createActivationLane(
401
669
  hasCompanion ? "needs_review" : "skipped",
@@ -407,7 +675,7 @@ function createActivationManifest(answers, detection, mode = "default") {
407
675
  ),
408
676
  firstBrief: createActivationLane(
409
677
  "ready",
410
- "First Work Brief preview generated from local setup signals."
678
+ "First Work Brief and day-zero answer-pack prompt generated from local setup signals."
411
679
  ),
412
680
  githubSync: createActivationLane(
413
681
  answers.configureGitHub && answers.githubRepo ? "queued" : "skipped",
@@ -495,6 +763,115 @@ function runLocalSourceActivation(answers, manifest) {
495
763
  }
496
764
  updateActivationManifest(manifest);
497
765
  }
766
+ async function runHostedDayZeroCorpusSync(answers, manifest, enabled) {
767
+ if (answers.profile === "runtime-only") {
768
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
769
+ "skipped",
770
+ [],
771
+ "Runtime-only profile skipped Hosted MCP config and hosted document sync."
772
+ );
773
+ updateActivationManifest(manifest);
774
+ return;
775
+ }
776
+ if (!enabled) {
777
+ const documents2 = collectDayZeroCorpusDocuments(manifest.localScan);
778
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
779
+ "skipped",
780
+ documents2,
781
+ "Skipped because hosted validation was disabled with --skip-test."
782
+ );
783
+ updateActivationManifest(manifest);
784
+ return;
785
+ }
786
+ if (answers.apiKeyType === "none" || answers.apiKey.includes("YOUR_")) {
787
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
788
+ "skipped",
789
+ [],
790
+ "Skipped because no live hosted API key was available."
791
+ );
792
+ updateActivationManifest(manifest);
793
+ return;
794
+ }
795
+ const documents = collectDayZeroCorpusDocuments(manifest.localScan);
796
+ if (documents.length === 0) {
797
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
798
+ "skipped",
799
+ [],
800
+ "No README or docs Markdown files were found to seed hosted context."
801
+ );
802
+ if (manifest.lanes.contextIndex.status === "queued") {
803
+ manifest.lanes.contextIndex = createActivationLane(
804
+ "skipped",
805
+ "No README or docs Markdown files were found to seed hosted context."
806
+ );
807
+ }
808
+ updateActivationManifest(manifest);
809
+ return;
810
+ }
811
+ const syncTool = "snipara_sync_documents";
812
+ const syncResponse = await callHostedMcp(answers, "tools/call", {
813
+ name: syncTool,
814
+ arguments: {
815
+ documents,
816
+ delete_missing: false
817
+ }
818
+ });
819
+ if (syncResponse.ok && !syncResponse.data.error) {
820
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
821
+ "submitted",
822
+ documents,
823
+ `Submitted ${documents.length} README/docs files to hosted context for the first day-zero answer pack.`,
824
+ { tool: syncTool }
825
+ );
826
+ manifest.lanes.contextIndex = createActivationLane(
827
+ "indexing",
828
+ "Day-zero README/docs corpus submitted to hosted context indexing.",
829
+ syncTool
830
+ );
831
+ updateActivationManifest(manifest);
832
+ return;
833
+ }
834
+ const uploadTool = "snipara_upload_document";
835
+ const uploadErrors = [];
836
+ for (const document of documents) {
837
+ const uploadResponse = await callHostedMcp(answers, "tools/call", {
838
+ name: uploadTool,
839
+ arguments: document
840
+ });
841
+ if (!uploadResponse.ok || uploadResponse.data.error) {
842
+ uploadErrors.push(
843
+ uploadResponse.ok ? describeJsonRpcError(uploadResponse.data) : describeHostedHttpFailure(uploadResponse.status, uploadResponse.bodyText)
844
+ );
845
+ break;
846
+ }
847
+ }
848
+ if (uploadErrors.length === 0) {
849
+ manifest.dayZeroCorpus = createDayZeroCorpusReport(
850
+ "submitted",
851
+ documents,
852
+ `Submitted ${documents.length} README/docs files to hosted context for the first day-zero answer pack.`,
853
+ { tool: uploadTool }
854
+ );
855
+ manifest.lanes.contextIndex = createActivationLane(
856
+ "indexing",
857
+ "Day-zero README/docs corpus submitted to hosted context indexing.",
858
+ uploadTool
859
+ );
860
+ } else {
861
+ const syncError = syncResponse.ok ? describeJsonRpcError(syncResponse.data) : describeHostedHttpFailure(syncResponse.status, syncResponse.bodyText);
862
+ const detail = `Could not submit the day-zero README/docs corpus. sync_documents: ${syncError}; upload_document: ${uploadErrors[0]}`;
863
+ manifest.dayZeroCorpus = createDayZeroCorpusReport("failed", documents, detail, {
864
+ tool: syncTool,
865
+ error: detail
866
+ });
867
+ manifest.lanes.contextIndex = failActivationLane(
868
+ "Hosted day-zero README/docs sync failed; the first answer may need GitHub, companion, or a later sync.",
869
+ syncTool,
870
+ new Error(detail)
871
+ );
872
+ }
873
+ updateActivationManifest(manifest);
874
+ }
498
875
  function createActivationArtifacts(answers, manifest) {
499
876
  const candidates = buildMemoryCandidatePreviews(manifest.localScan);
500
877
  writeMemoryCandidates(candidates);
@@ -776,6 +1153,8 @@ function parseInitOptionsFromArgv(args) {
776
1153
  async function runInit(options) {
777
1154
  const normalizedOptions = normalizeInitOptions(options);
778
1155
  const answers = await gatherConfiguration(normalizedOptions);
1156
+ const activationDetection = detectEnvironment();
1157
+ const activationScan = detectLocalActivationScan(activationDetection);
779
1158
  if (answers.installMcp || answers.installRuntime || answers.installOrchestrator) {
780
1159
  await installPythonPackages(answers, false);
781
1160
  }
@@ -802,9 +1181,11 @@ async function runInit(options) {
802
1181
  writeDoctorReport(validationReport);
803
1182
  const activationManifest = createActivationManifest(
804
1183
  answers,
805
- detectEnvironment(),
806
- normalizedOptions.starter ? "starter" : "default"
1184
+ activationDetection,
1185
+ normalizedOptions.starter ? "starter" : "default",
1186
+ activationScan
807
1187
  );
1188
+ await runHostedDayZeroCorpusSync(answers, activationManifest, !normalizedOptions.skipTest);
808
1189
  runLocalSourceActivation(answers, activationManifest);
809
1190
  createActivationArtifacts(answers, activationManifest);
810
1191
  printSuccess(answers, validationReport, activationManifest);
@@ -2201,8 +2582,8 @@ Agent task lifecycle:
2201
2582
  work.
2202
2583
  6. Use whichever structural code graph tools are exposed in the current session
2203
2584
  for callers, imports, neighbors, and path questions. Tool exposure can vary.
2204
- 7. On paid Context plans, use \`snipara_code_symbol_card\` for agent-ready symbol
2205
- context and \`snipara-companion code impact\` before risky multi-file changes, PR
2585
+ 7. Use \`snipara_code_symbol_card\` for agent-ready symbol context within plan
2586
+ capacity and \`snipara-companion code impact\` before risky multi-file changes, PR
2206
2587
  reviews, routes, services, jobs, auth, billing, deployment, schema, migrations,
2207
2588
  or explicit "what is missing" assessments.
2208
2589
  8. Use \`snipara_plan\` for complex or multi-surface work in any plan when
@@ -2269,7 +2650,7 @@ ${EXPERIENCE_GOAL_SECTION}
2269
2650
  - Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for canonical structural code questions.
2270
2651
  - For code impact and structural code questions, use \`snipara-companion code callers/imports/neighbors/shortest-path/impact\` first when \`snipara-companion\` is installed; it auto-selects hosted MCP for clean indexed code or the local overlay for dirty/ahead worktrees and reports \`sourceSelection\`. Use hosted \`snipara_code_impact\` only when companion is unavailable or after push/reindex for canonical graph impact.
2271
2652
  - Run \`snipara-companion code impact\` before risky multi-file changes, PR reviews, routes, services, jobs, auth, billing, deployment, schema, migrations, or explicit "what is missing" assessments.
2272
- - On paid Context plans, use \`snipara_code_symbol_card\` before editing important symbols.
2653
+ - Use \`snipara_code_symbol_card\` before editing important symbols within plan capacity.
2273
2654
  - Check every \`snipara_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings.
2274
2655
  - Use local file reads, \`rg\`, \`git status --short\`, and tests for exact edits/current working tree; use Snipara for architecture and indexed code graph context.
2275
2656
  - If a broad query is slow, retry once with a narrow keyword query before falling back to local search.
@@ -2309,7 +2690,7 @@ ${EXPERIENCE_GOAL_SECTION}
2309
2690
  - Use \`snipara_code_callers\`, \`snipara_code_imports\`, \`snipara_code_neighbors\`, or \`snipara_code_shortest_path\` for canonical structural code questions.
2310
2691
  - For code impact and structural code questions, use \`snipara-companion code callers/imports/neighbors/shortest-path/impact\` first when \`snipara-companion\` is installed; it auto-selects hosted MCP for clean indexed code or the local overlay for dirty/ahead worktrees and reports \`sourceSelection\`. Use hosted \`snipara_code_impact\` only when companion is unavailable or after push/reindex for canonical graph impact.
2311
2692
  - Run \`snipara-companion code impact\` before risky multi-file changes, PR reviews, routes, services, jobs, auth, billing, deployment, schema, migrations, or explicit "what is missing" assessments.
2312
- - On paid Context plans, use \`snipara_code_symbol_card\` before editing important symbols.
2693
+ - Use \`snipara_code_symbol_card\` before editing important symbols within plan capacity.
2313
2694
  - Check every \`snipara_code_*\` response for indexed commit SHA, indexing time, included files, coverage, and freshness warnings.
2314
2695
  - Use \`snipara_plan\` for complex or multi-surface work when available. Keep simple Q&A on targeted \`snipara_context_query\`; for FULL-mode audits, comparisons, roadmap/implementation planning, release readiness, or package-surface reviews, use \`snipara_decompose\` to preserve axes and \`snipara_multi_query\` for independent follow-up questions when Pro+ tools are exposed.
2315
2696
  - Use local file reads, \`rg\`, \`git status --short\`, and tests for exact code edits and current working-tree state.
@@ -3121,7 +3502,7 @@ function getCompanionCommands(answers) {
3121
3502
  {
3122
3503
  title: "Analyze code impact before a risky change",
3123
3504
  command: `${COMPANION_NPX_COMMAND} code impact --changed-files src/api/webhooks.ts tests/webhooks.test.ts --diff-summary "webhook retry hardening"`,
3124
- purpose: "Use paid Context code impact analysis to surface likely tests, docs, routes, config, and risks."
3505
+ purpose: "Use code impact analysis within plan capacity to surface likely tests, docs, routes, config, and risks."
3125
3506
  },
3126
3507
  {
3127
3508
  title: answers.installOrchestrator ? "Run an explicit production orchestration gate" : "Enable explicit production orchestration",
@@ -3292,7 +3673,7 @@ ${guidance.map((line) => `- ${line}`).join("\n")}
3292
3673
  - Use \`answer_pack\` facts, caveats, source maps, and verification checks before making claims.
3293
3674
  - For coding tasks, choose a workflow mode before editing: LITE for small single-phase changes, FULL managed workflow for multi-file, risky, release/deploy, architectural, compaction-prone, or future-maintainer-sensitive work.
3294
3675
  - Use \`snipara_plan\` for complex or multi-surface work when exposed. Keep simple Q&A on targeted \`snipara_context_query\`; for FULL-mode audits, comparisons, roadmap/implementation planning, release readiness, or package-surface reviews, use \`snipara_decompose\` to preserve axes and \`snipara_multi_query\` for independent follow-up questions when Pro+ tools are exposed.
3295
- - Use \`snipara_code_symbol_card\` for important symbols. For risky multi-file changes, PR reviews, routes, services, jobs, auth, billing, deployment, schema, migrations, or explicit "what is missing" assessments, use \`snipara-companion code impact\` first when companion is available; use hosted \`snipara_code_impact\` only when companion is unavailable or after push/reindex for canonical graph impact.
3676
+ - Use \`snipara_code_symbol_card\` for important symbols within plan capacity. For risky multi-file changes, PR reviews, routes, services, jobs, auth, billing, deployment, schema, migrations, or explicit "what is missing" assessments, use \`snipara-companion code impact\` first when companion is available; use hosted \`snipara_code_impact\` only when companion is unavailable or after push/reindex for canonical graph impact.
3296
3677
  - Use the installed \`snipara-companion\` binary for low-latency hooks; use \`${COMPANION_NPX_COMMAND}\` for one-off terminal commands when freshness matters.
3297
3678
  - If a broad query is slow, retry once with a narrow keyword query before falling back to local search.
3298
3679
  - Use a JSON machine plan plus \`snipara-companion workflow start\`, \`phase-start\`, \`phase-commit\`, \`workflow resume\`, and \`final-commit\` for multi-phase work. After \`workflow resume\`, rerun \`workflow phase-start <phase_id>\` before editing again.
@@ -4117,6 +4498,33 @@ function printActivationSummary(manifest) {
4117
4498
  console.log(chalk.dim(` - ${line}`));
4118
4499
  }
4119
4500
  }
4501
+ console.log(chalk.bold("\nDay-zero proof:\n"));
4502
+ console.log(` Tool: ${manifest.dayZeroQuery.tool}`);
4503
+ console.log(` Query: ${manifest.dayZeroQuery.query}`);
4504
+ console.log(
4505
+ chalk.dim(
4506
+ ` Target: ${manifest.activationMetric.targetMinutes} minutes to a true, non-obvious own-repo answer.`
4507
+ )
4508
+ );
4509
+ console.log(
4510
+ chalk.dim(
4511
+ ` Day-zero corpus: ${manifest.dayZeroCorpus.status} - ${manifest.dayZeroCorpus.detail}`
4512
+ )
4513
+ );
4514
+ console.log(chalk.bold("\nAgent readiness:\n"));
4515
+ console.log(` ${manifest.agentReadiness.summary}`);
4516
+ for (const finding of manifest.agentReadiness.stalenessFindings.slice(0, 3)) {
4517
+ console.log(chalk.dim(` - ${finding.source}: ${finding.summary}`));
4518
+ }
4519
+ console.log(chalk.bold("\nContext economy:\n"));
4520
+ console.log(
4521
+ ` Estimated ${manifest.contextEconomy.estimatedRetrievedTokens.toLocaleString("en-US")} tokens retrieved instead of ${manifest.contextEconomy.rawWindowBaselineTokens.toLocaleString("en-US")} raw-window tokens.`
4522
+ );
4523
+ console.log(
4524
+ chalk.dim(
4525
+ ` Saved about ${manifest.contextEconomy.estimatedSavedTokens.toLocaleString("en-US")} tokens (${manifest.contextEconomy.savingsPercent}%). ${manifest.contextEconomy.note}`
4526
+ )
4527
+ );
4120
4528
  console.log(
4121
4529
  chalk.dim(
4122
4530
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snipara",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",