prizmkit 1.1.159 → 1.1.160
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/bundled/VERSION.json +3 -3
- package/bundled/adapters/codebuddy/settings-adapter.js +76 -25
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +9 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +42 -15
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +1 -1
- package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +6 -6
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +4 -3
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +2 -2
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +2 -2
- package/bundled/dev-pipeline/tests/test_runtime_commit.py +68 -13
- package/bundled/dev-pipeline/tests/test_unified_cli.py +10 -6
- package/bundled/rules/prizm/prizm-commit-workflow.md +7 -7
- package/bundled/rules/prizm/prizm-documentation.md +24 -65
- package/bundled/rules/prizm/prizm-progressive-loading.md +9 -8
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/prizmkit-code-review/SKILL.md +1 -1
- package/bundled/skills/prizmkit-committer/SKILL.md +48 -41
- package/bundled/skills/prizmkit-deploy/SKILL.md +172 -198
- package/bundled/skills/prizmkit-deploy/references/capability-contract.md +137 -0
- package/bundled/skills/prizmkit-deploy/references/deploy-record-schema.json +1053 -0
- package/bundled/skills/prizmkit-deploy/references/deployment-declaration-schema.json +291 -0
- package/bundled/skills/prizmkit-deploy/references/legacy-migration.md +90 -0
- package/bundled/skills/prizmkit-deploy/references/policy-and-execution.md +170 -0
- package/bundled/skills/prizmkit-deploy/references/real-resource-test-policy.md +73 -0
- package/bundled/skills/prizmkit-deploy/references/verification-and-recovery.md +164 -0
- package/bundled/skills/prizmkit-deploy/scripts/validate-deploy-artifact.py +371 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +2 -2
- package/bundled/skills/prizmkit-init/SKILL.md +17 -17
- package/bundled/skills/prizmkit-init/references/update-supplement.md +5 -5
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +17 -11
- package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +95 -155
- package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +6 -5
- package/bundled/skills/prizmkit-prizm-docs/references/op-rebuild.md +4 -2
- package/bundled/skills/prizmkit-prizm-docs/references/op-status.md +9 -10
- package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +6 -5
- package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +13 -12
- package/bundled/skills/prizmkit-retrospective/SKILL.md +3 -3
- package/bundled/skills/prizmkit-test/SKILL.md +26 -13
- package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +8 -4
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +7 -3
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +33 -10
- package/bundled/skills/prizmkit-test/references/test-report-template.md +33 -11
- package/bundled/skills/prizmkit-workflow/SKILL.md +5 -7
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +5 -6
- package/bundled/templates/hooks/commit-intent-status.py +9 -41
- package/bundled/templates/hooks/commit-intent.json +3 -3
- package/bundled/templates/hooks/diff-prizm-docs.py +3 -1
- package/bundled/templates/hooks/prizm-pre-commit.py +32 -6
- package/bundled/templates/hooks/run-python-hook.cjs +44 -0
- package/bundled/templates/hooks/validate-prizm-docs.py +399 -173
- package/bundled/templates/project-memory-template.md +19 -19
- package/package.json +1 -1
- package/src/clean.js +6 -7
- package/src/gitignore-template.js +8 -12
- package/src/scaffold.js +125 -71
- package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +0 -89
- package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +0 -145
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +0 -109
- package/bundled/skills/prizmkit-deploy/references/data-safety-examples.md +0 -120
- package/bundled/skills/prizmkit-deploy/references/database-setup.md +0 -65
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +0 -150
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +0 -78
- package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +0 -49
- package/bundled/skills/prizmkit-deploy/references/deployment-modes.md +0 -50
- package/bundled/skills/prizmkit-deploy/references/direct-upload.md +0 -26
- package/bundled/skills/prizmkit-deploy/references/dns-setup.md +0 -42
- package/bundled/skills/prizmkit-deploy/references/docker-deploy.md +0 -31
- package/bundled/skills/prizmkit-deploy/references/firewall-setup.md +0 -37
- package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +0 -21
- package/bundled/skills/prizmkit-deploy/references/nginx-blue-green.md +0 -59
- package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +0 -164
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +0 -221
- package/bundled/skills/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
- package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
- package/bundled/skills/prizmkit-deploy/references/ssh-takeover.md +0 -20
- package/bundled/skills/prizmkit-deploy/references/ssl-setup.md +0 -56
|
@@ -4,30 +4,30 @@
|
|
|
4
4
|
This project uses PrizmKit with the Prizm documentation system for AI-optimized progressive context loading.
|
|
5
5
|
|
|
6
6
|
### Progressive Loading Protocol
|
|
7
|
-
- ON SESSION START:
|
|
8
|
-
- ON
|
|
9
|
-
- ON
|
|
10
|
-
-
|
|
11
|
-
- NEVER load all
|
|
12
|
-
|
|
13
|
-
###
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
7
|
+
- ON SESSION START: Read exact `.prizmkit/prizm-docs/root.prizm` first when it exists; it alone is L0.
|
|
8
|
+
- ON TASK: Read only relevant direct-child L1 documents (`.prizmkit/prizm-docs/<module>.prizm`) selected through MODULE_INDEX or MODULE_GROUPS pointers.
|
|
9
|
+
- ON FILE EDIT: Read the complete relevant nested L2 and complete parent/child documents needed to resolve its SUBDIRS or DETAILS pointer. Grep-only fragments are insufficient modification context.
|
|
10
|
+
- IF L2 IS ABSENT: Inspect bounded target source and narrowly implicated contracts without creating a placeholder; documentation creation belongs to Retrospective or explicit repair.
|
|
11
|
+
- NEVER load all `.prizm` docs at once, load unrelated modules, or create L3.
|
|
12
|
+
|
|
13
|
+
### Framework Directory and Maintenance Protocol
|
|
14
|
+
- `.prizmkit/**` is the PrizmKit framework directory; owning capabilities may read/write their exact managed artifacts.
|
|
15
|
+
- The project alone controls whether framework paths are ignored, untracked, or tracked. Do not add, remove, force-add, stage, commit, or interpret `.gitignore` entries as documentation maintenance.
|
|
16
|
+
- Run `/prizmkit-retrospective` after validated changes when structure, interfaces, dependencies, behavior, or durable knowledge changed; it never owns staging/commit.
|
|
17
|
+
- Use `/prizmkit-committer` for an exact commit operation. A Git-visible `.prizmkit/**` intended path receives ordinary exact-manifest and Secret checks; ignored paths remain absent.
|
|
17
18
|
|
|
18
19
|
### Doc Format Rules
|
|
19
20
|
- All `.prizm` files use KEY: value format, not prose
|
|
20
|
-
-
|
|
21
|
+
- Path levels and raw UTF-8 limits: exact `root.prizm` = L0/4096B; other direct `.prizm` children = L1/4096B; nested `.prizm` documents = L2/5120B
|
|
21
22
|
- Arrow notation (->) indicates load pointers to other .prizm docs
|
|
22
23
|
- Memory files must not contain CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, or `.prizmkit/specs` / `.prizmkit/dev-pipeline` artifact paths
|
|
23
|
-
- Update durable sections in place;
|
|
24
|
-
- No date/time fields
|
|
25
|
-
|
|
26
|
-
###
|
|
27
|
-
- When
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
3. Add a pointer in the parent L1 doc's SUBDIRS section
|
|
24
|
+
- Update durable sections in place; temporal and workflow history stays outside Prizm memory
|
|
25
|
+
- No date/time fields and no Git-history requirement
|
|
26
|
+
|
|
27
|
+
### Missing L2 Fallback
|
|
28
|
+
- When relevant L2 is absent, inspect only bounded source needed for the current modification.
|
|
29
|
+
- Do not create a placeholder merely because source was read or changed.
|
|
30
|
+
- Retrospective or explicit documentation repair may later write a complete Value-Gate-qualified L2 and resolving parent pointer after identity, ownership, capacity, and format checks pass.
|
|
31
31
|
|
|
32
32
|
### Available Commands
|
|
33
33
|
Run `/prizmkit` to see all available PrizmKit commands.
|
package/package.json
CHANGED
package/src/clean.js
CHANGED
|
@@ -5,13 +5,12 @@ import { loadMetadata, loadRulesMetadata } from './metadata.js';
|
|
|
5
5
|
import { readManifest } from './manifest.js';
|
|
6
6
|
import { privateProjectMemoryFile, projectMemoryFile } from './platforms.js';
|
|
7
7
|
|
|
8
|
+
// Cleanup owns only deterministic private host-memory entries. Existing
|
|
9
|
+
// `.prizmkit/**` ignore policy is project-owned and must remain untouched.
|
|
8
10
|
const PRIZMKIT_GITIGNORE_HEADERS = new Set([
|
|
9
|
-
'# PrizmKit',
|
|
10
|
-
'# PrizmKit 运行时全部产物(state/、prizm-docs/、.env、logs 等)',
|
|
11
|
+
'# PrizmKit private host memory',
|
|
11
12
|
]);
|
|
12
13
|
const PRIZMKIT_GITIGNORE_ENTRIES = new Set([
|
|
13
|
-
'.prizmkit/',
|
|
14
|
-
'.prizmkit/*',
|
|
15
14
|
'CLAUDE.private.md',
|
|
16
15
|
'AGENTS.private.md',
|
|
17
16
|
'CODEBUDDY.private.md',
|
|
@@ -298,7 +297,7 @@ export async function runClean(directory, options = {}) {
|
|
|
298
297
|
results.push(await cleanMarkedProjectMemory(projectRoot, privateMemoryFile, dryRun));
|
|
299
298
|
}
|
|
300
299
|
|
|
301
|
-
// ── .gitignore cleanup
|
|
300
|
+
// ── Private host-memory .gitignore cleanup (never `.prizmkit/**`) ─────────
|
|
302
301
|
const gitignoreResult = await cleanGitignore(projectRoot, dryRun);
|
|
303
302
|
|
|
304
303
|
// ── Global team (optional) ────────────────────────────────────────────────
|
|
@@ -326,9 +325,9 @@ export async function runClean(directory, options = {}) {
|
|
|
326
325
|
|
|
327
326
|
if (gitignoreResult.updated) {
|
|
328
327
|
if (gitignoreResult.reason === 'dry_run') {
|
|
329
|
-
console.log(chalk.gray(' [dry-run] 将更新 .gitignore
|
|
328
|
+
console.log(chalk.gray(' [dry-run] 将更新 .gitignore(仅移除私有主机记忆条目)'));
|
|
330
329
|
} else {
|
|
331
|
-
console.log(chalk.green(' ✓ 已更新 .gitignore
|
|
330
|
+
console.log(chalk.green(' ✓ 已更新 .gitignore(仅移除私有主机记忆条目)'));
|
|
332
331
|
}
|
|
333
332
|
}
|
|
334
333
|
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* .
|
|
3
|
-
*
|
|
2
|
+
* Generate only explicit private host-memory ignores.
|
|
3
|
+
*
|
|
4
|
+
* `.prizmkit/**` tracking policy is project-controlled: this module never adds,
|
|
5
|
+
* removes, or interprets framework-directory ignore entries.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param {Object} options - { pipeline: boolean }
|
|
9
|
+
* @param {Object} options - { privateMemoryFiles?: string[] }
|
|
9
10
|
* @returns {string}
|
|
10
11
|
*/
|
|
11
12
|
export function generateGitignore(options = {}) {
|
|
12
13
|
const privateMemoryFiles = Array.isArray(options.privateMemoryFiles)
|
|
13
|
-
? options.privateMemoryFiles.filter(Boolean)
|
|
14
|
+
? [...new Set(options.privateMemoryFiles.filter(Boolean))]
|
|
14
15
|
: [];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'.prizmkit/*',
|
|
18
|
-
...privateMemoryFiles,
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
return [...new Set(lines)].join('\n');
|
|
16
|
+
if (privateMemoryFiles.length === 0) return '';
|
|
17
|
+
return ['# PrizmKit private host memory', ...privateMemoryFiles].join('\n');
|
|
22
18
|
}
|
package/src/scaffold.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 全部使用 Copy 模式(不使用 symlink),项目完全独立于框架仓库。
|
|
5
5
|
* 通过 adapters 转换 core/ 资源为平台特定格式。
|
|
6
6
|
* 不安装框架开发文件(docs、tests、scripts 等)。
|
|
7
|
-
*
|
|
7
|
+
* 仅按显式平台需要合并私有记忆文件 ignore;`.prizmkit/**` Git 策略由项目控制。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import chalk from 'chalk';
|
|
@@ -381,6 +381,57 @@ export async function installSkills(platform, skills, projectRoot, dryRun, runti
|
|
|
381
381
|
* Skill-owned references now provide inline reviewer/implementation subagent contracts.
|
|
382
382
|
*/
|
|
383
383
|
|
|
384
|
+
const MANAGED_HOOK_MARKERS = [
|
|
385
|
+
'.prizmkit/scripts/commit-intent-status.py',
|
|
386
|
+
'.prizmkit/scripts/post-command-prizm-drift.py',
|
|
387
|
+
'PRIZMKIT_DOC_UPDATE_REQUIRED',
|
|
388
|
+
'PrizmKit: .prizmkit/prizm-docs found',
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
function isManagedHookEntry(entry) {
|
|
392
|
+
let serialized = '';
|
|
393
|
+
try {
|
|
394
|
+
serialized = JSON.stringify(entry);
|
|
395
|
+
} catch {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
return MANAGED_HOOK_MARKERS.some(marker => serialized.includes(marker));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function mergeManagedHooks(existingHooks = {}, managedHooks = {}) {
|
|
402
|
+
if (!existingHooks || typeof existingHooks !== 'object' || Array.isArray(existingHooks)) {
|
|
403
|
+
throw new Error('Cannot preserve malformed user hooks: hooks must be an object');
|
|
404
|
+
}
|
|
405
|
+
const merged = {};
|
|
406
|
+
for (const [event, entries] of Object.entries(existingHooks)) {
|
|
407
|
+
if (!Array.isArray(entries)) {
|
|
408
|
+
throw new Error(`Cannot preserve malformed user hooks: ${event} must be an array`);
|
|
409
|
+
}
|
|
410
|
+
merged[event] = entries.filter(entry => !isManagedHookEntry(entry));
|
|
411
|
+
}
|
|
412
|
+
for (const [event, entries] of Object.entries(managedHooks || {})) {
|
|
413
|
+
if (!Array.isArray(entries)) {
|
|
414
|
+
throw new Error(`Managed hook template is invalid: ${event} must be an array`);
|
|
415
|
+
}
|
|
416
|
+
const preserved = Array.isArray(merged[event]) ? merged[event] : [];
|
|
417
|
+
merged[event] = [...preserved, ...entries];
|
|
418
|
+
}
|
|
419
|
+
return merged;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function readUserSettings(settingsPath) {
|
|
423
|
+
if (!await fs.pathExists(settingsPath)) return {};
|
|
424
|
+
try {
|
|
425
|
+
const value = await fs.readJSON(settingsPath);
|
|
426
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
427
|
+
throw new Error('settings root must be an object');
|
|
428
|
+
}
|
|
429
|
+
return value;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
throw new Error(`Cannot preserve malformed user settings at ${settingsPath}: ${error.message}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
384
435
|
/**
|
|
385
436
|
* 安装平台配置文件(settings/hooks/rules)
|
|
386
437
|
*/
|
|
@@ -412,11 +463,16 @@ export async function installSettings(platform, projectRoot, options, dryRun, ru
|
|
|
412
463
|
|
|
413
464
|
await fs.ensureDir(path.dirname(settingsPath));
|
|
414
465
|
|
|
415
|
-
//
|
|
466
|
+
// Replace only deterministic managed hook entries and preserve user settings/hooks.
|
|
416
467
|
const templatesDir = getTemplatesDir();
|
|
417
468
|
const hookTemplate = await fs.readJSON(path.join(templatesDir, 'hooks', 'commit-intent.json'));
|
|
469
|
+
const existingSettings = await readUserSettings(settingsPath);
|
|
470
|
+
const settings = {
|
|
471
|
+
...existingSettings,
|
|
472
|
+
hooks: mergeManagedHooks(existingSettings.hooks, hookTemplate.hooks),
|
|
473
|
+
};
|
|
418
474
|
|
|
419
|
-
await fs.writeFile(settingsPath, JSON.stringify(
|
|
475
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
420
476
|
console.log(chalk.green(` ✓ .codebuddy/settings.json`));
|
|
421
477
|
|
|
422
478
|
// Install rules for CodeBuddy
|
|
@@ -466,26 +522,37 @@ export async function installSettings(platform, projectRoot, options, dryRun, ru
|
|
|
466
522
|
'Bash(jq *)',
|
|
467
523
|
];
|
|
468
524
|
|
|
525
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
526
|
+
const existingSettings = await readUserSettings(settingsPath);
|
|
527
|
+
const existingPermissions = existingSettings.permissions && typeof existingSettings.permissions === 'object'
|
|
528
|
+
? existingSettings.permissions
|
|
529
|
+
: {};
|
|
530
|
+
const mergedPermissions = [...new Set([
|
|
531
|
+
...(Array.isArray(existingPermissions.allow) ? existingPermissions.allow : []),
|
|
532
|
+
...permissions,
|
|
533
|
+
])];
|
|
534
|
+
const managedHooks = {
|
|
535
|
+
...hookTemplate.hooks,
|
|
536
|
+
SessionStart: [
|
|
537
|
+
{
|
|
538
|
+
matcher: 'startup',
|
|
539
|
+
hooks: [
|
|
540
|
+
{
|
|
541
|
+
type: 'command',
|
|
542
|
+
command: 'test -f .prizmkit/prizm-docs/root.prizm && echo "PrizmKit: .prizmkit/prizm-docs found. Read exact root.prizm for project context." || echo "PrizmKit: No .prizmkit/prizm-docs found. Run /prizmkit-init to bootstrap."',
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
};
|
|
469
548
|
const settings = {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
SessionStart: [
|
|
474
|
-
{
|
|
475
|
-
matcher: 'startup',
|
|
476
|
-
hooks: [
|
|
477
|
-
{
|
|
478
|
-
type: 'command',
|
|
479
|
-
command: 'test -f .prizmkit/prizm-docs/root.prizm && echo "PrizmKit: .prizmkit/prizm-docs found. Read root.prizm for project context." || echo "PrizmKit: No .prizmkit/prizm-docs found. Run /prizmkit-init to bootstrap."',
|
|
480
|
-
},
|
|
481
|
-
],
|
|
482
|
-
},
|
|
483
|
-
],
|
|
484
|
-
},
|
|
549
|
+
...existingSettings,
|
|
550
|
+
permissions: { ...existingPermissions, allow: mergedPermissions },
|
|
551
|
+
hooks: mergeManagedHooks(existingSettings.hooks, managedHooks),
|
|
485
552
|
};
|
|
486
553
|
|
|
487
554
|
await fs.writeFile(
|
|
488
|
-
|
|
555
|
+
settingsPath,
|
|
489
556
|
JSON.stringify(settings, null, 2)
|
|
490
557
|
);
|
|
491
558
|
console.log(chalk.green(` ✓ .claude/settings.json`));
|
|
@@ -553,59 +620,33 @@ function escapeRegExp(value) {
|
|
|
553
620
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
554
621
|
}
|
|
555
622
|
|
|
556
|
-
function ensureTrailingNewline(value) {
|
|
557
|
-
return value.endsWith('\n') ? value : `${value}\n`;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function buildStandalonePrizmHook(templateContent) {
|
|
561
|
-
const content = templateContent.trimEnd();
|
|
562
|
-
const firstNewline = content.indexOf('\n');
|
|
563
|
-
|
|
564
|
-
if (content.startsWith('#!') && firstNewline !== -1) {
|
|
565
|
-
return [
|
|
566
|
-
content.slice(0, firstNewline),
|
|
567
|
-
PRIZMKIT_HOOK_MARKER_START,
|
|
568
|
-
content.slice(firstNewline + 1),
|
|
569
|
-
PRIZMKIT_HOOK_MARKER_END,
|
|
570
|
-
'',
|
|
571
|
-
].join('\n');
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
return [
|
|
575
|
-
PRIZMKIT_HOOK_MARKER_START,
|
|
576
|
-
content,
|
|
577
|
-
PRIZMKIT_HOOK_MARKER_END,
|
|
578
|
-
'',
|
|
579
|
-
].join('\n');
|
|
580
|
-
}
|
|
581
|
-
|
|
582
623
|
function buildShellPythonLauncherHook() {
|
|
583
624
|
return [
|
|
584
625
|
PRIZMKIT_HOOK_MARKER_START,
|
|
585
|
-
'# PrizmKit
|
|
586
|
-
'PRIZMKIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) ||
|
|
587
|
-
'
|
|
588
|
-
'
|
|
589
|
-
'
|
|
590
|
-
'
|
|
591
|
-
'
|
|
592
|
-
'
|
|
593
|
-
'
|
|
594
|
-
'
|
|
595
|
-
'
|
|
596
|
-
' py -3 "$PRIZMKIT_VALIDATE" --staged || exit $?',
|
|
597
|
-
' else',
|
|
598
|
-
' echo "PrizmKit: Python 3 is required for .prizm docs validation." >&2',
|
|
599
|
-
' exit 1',
|
|
600
|
-
' fi',
|
|
601
|
-
' fi',
|
|
626
|
+
'# PrizmKit host-neutral staged documentation validation',
|
|
627
|
+
'PRIZMKIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0',
|
|
628
|
+
'PRIZMKIT_RUNNER="$PRIZMKIT_ROOT/.prizmkit/scripts/run-python-hook.cjs"',
|
|
629
|
+
'PRIZMKIT_PRE_COMMIT="$PRIZMKIT_ROOT/.prizmkit/scripts/prizm-pre-commit.py"',
|
|
630
|
+
'if [ -f "$PRIZMKIT_RUNNER" ] && [ -f "$PRIZMKIT_PRE_COMMIT" ] && command -v node >/dev/null 2>&1; then',
|
|
631
|
+
' node "$PRIZMKIT_RUNNER" "$PRIZMKIT_PRE_COMMIT" || exit $?',
|
|
632
|
+
'elif git diff --cached --quiet -- .prizmkit/prizm-docs; then',
|
|
633
|
+
' :',
|
|
634
|
+
'else',
|
|
635
|
+
' echo "PrizmKit: staged Prizm documents require the installed host-neutral validator launcher." >&2',
|
|
636
|
+
' exit 1',
|
|
602
637
|
'fi',
|
|
603
638
|
PRIZMKIT_HOOK_MARKER_END,
|
|
604
639
|
'',
|
|
605
640
|
].join('\n');
|
|
606
641
|
}
|
|
607
642
|
|
|
643
|
+
function buildStandalonePrizmHook(templateContent) {
|
|
644
|
+
void templateContent;
|
|
645
|
+
return `#!/bin/sh\n${buildShellPythonLauncherHook()}`;
|
|
646
|
+
}
|
|
647
|
+
|
|
608
648
|
function buildAppendedPrizmHook(templateContent, userHookContent) {
|
|
649
|
+
void templateContent;
|
|
609
650
|
if (/^#!.*\bpython[0-9.]*\b/i.test(userHookContent)) {
|
|
610
651
|
return [
|
|
611
652
|
PRIZMKIT_HOOK_MARKER_START,
|
|
@@ -613,13 +654,15 @@ function buildAppendedPrizmHook(templateContent, userHookContent) {
|
|
|
613
654
|
'prizmkit_root_result = subprocess.run(["git", "rev-parse", "--show-toplevel"], text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False)',
|
|
614
655
|
'if prizmkit_root_result.returncode == 0:',
|
|
615
656
|
' prizmkit_root = pathlib.Path(prizmkit_root_result.stdout.strip())',
|
|
616
|
-
'
|
|
617
|
-
'
|
|
618
|
-
'
|
|
619
|
-
'
|
|
620
|
-
' prizmkit_result = subprocess.run([sys.executable, str(prizmkit_validate), "--staged"], cwd=prizmkit_root, check=False)',
|
|
657
|
+
' prizmkit_pre_commit = prizmkit_root / ".prizmkit/scripts/prizm-pre-commit.py"',
|
|
658
|
+
' prizmkit_diff = subprocess.run(["git", "diff", "--cached", "--quiet", "--", ".prizmkit/prizm-docs"], cwd=prizmkit_root, check=False)',
|
|
659
|
+
' if prizmkit_pre_commit.is_file():',
|
|
660
|
+
' prizmkit_result = subprocess.run([sys.executable, str(prizmkit_pre_commit)], cwd=prizmkit_root, check=False)',
|
|
621
661
|
' if prizmkit_result.returncode != 0:',
|
|
622
662
|
' raise SystemExit(prizmkit_result.returncode)',
|
|
663
|
+
' elif prizmkit_diff.returncode != 0:',
|
|
664
|
+
' print("PrizmKit: staged Prizm documents require the installed validator.", file=sys.stderr)',
|
|
665
|
+
' raise SystemExit(1)',
|
|
623
666
|
PRIZMKIT_HOOK_MARKER_END,
|
|
624
667
|
'',
|
|
625
668
|
].join('\n');
|
|
@@ -630,7 +673,10 @@ function buildAppendedPrizmHook(templateContent, userHookContent) {
|
|
|
630
673
|
|
|
631
674
|
function insertPrizmHookIntoUserHook(userHookContent, prizmHookBlock) {
|
|
632
675
|
const content = userHookContent.trimEnd();
|
|
633
|
-
const
|
|
676
|
+
const isPython = /^#!.*\bpython[0-9.]*\b/i.test(content);
|
|
677
|
+
const terminalSuccessExitPattern = isPython
|
|
678
|
+
? /(^|\n)([ \t]*(?:sys\.exit\(0\)|raise[ \t]+SystemExit\(0\)|exit\(0\))[ \t]*(?:#.*)?)[ \t]*$/
|
|
679
|
+
: /(^|\n)([ \t]*exit[ \t]+0[ \t]*(?:#.*)?)[ \t]*$/;
|
|
634
680
|
const match = content.match(terminalSuccessExitPattern);
|
|
635
681
|
|
|
636
682
|
if (!match) {
|
|
@@ -760,9 +806,11 @@ export async function installPrizmkitScripts(projectRoot, dryRun, runtime = 'pyt
|
|
|
760
806
|
|
|
761
807
|
const scripts = [
|
|
762
808
|
'validate-prizm-docs.py',
|
|
809
|
+
'prizm-pre-commit.py',
|
|
763
810
|
'diff-prizm-docs.py',
|
|
764
811
|
'commit-intent-status.py',
|
|
765
812
|
'post-command-prizm-drift.py',
|
|
813
|
+
'run-python-hook.cjs',
|
|
766
814
|
];
|
|
767
815
|
const legacyManagedScripts = [
|
|
768
816
|
'validate-prizm-docs.sh',
|
|
@@ -1277,8 +1325,13 @@ export async function installGitignore(projectRoot, options = {}, dryRun) {
|
|
|
1277
1325
|
privateMemoryFiles: options.privateMemoryFiles || [],
|
|
1278
1326
|
});
|
|
1279
1327
|
|
|
1328
|
+
if (!templateContent) {
|
|
1329
|
+
if (dryRun) console.log(chalk.gray(' [dry-run] .gitignore (no managed private-memory entries)'));
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1280
1333
|
if (dryRun) {
|
|
1281
|
-
console.log(chalk.gray(` [dry-run] .gitignore`));
|
|
1334
|
+
console.log(chalk.gray(` [dry-run] .gitignore (private host memory only)`));
|
|
1282
1335
|
return;
|
|
1283
1336
|
}
|
|
1284
1337
|
|
|
@@ -1291,14 +1344,15 @@ export async function installGitignore(projectRoot, options = {}, dryRun) {
|
|
|
1291
1344
|
|
|
1292
1345
|
if (missing.length > 0) {
|
|
1293
1346
|
const separator = existing.endsWith('\n') ? '' : '\n';
|
|
1294
|
-
const
|
|
1347
|
+
const managedHeader = '# PrizmKit private host memory';
|
|
1348
|
+
const header = existingLines.has(managedHeader) ? '' : `${managedHeader}\n`;
|
|
1295
1349
|
await fs.appendFile(targetPath, separator + '\n' + header + missing.join('\n') + '\n');
|
|
1296
1350
|
console.log(chalk.green(` ✓ .gitignore (added ${missing.length} entries)`));
|
|
1297
1351
|
} else {
|
|
1298
1352
|
console.log(chalk.green(' ✓ .gitignore (up-to-date)'));
|
|
1299
1353
|
}
|
|
1300
1354
|
} else {
|
|
1301
|
-
await fs.writeFile(targetPath, templateContent);
|
|
1355
|
+
await fs.writeFile(targetPath, `${templateContent}\n`);
|
|
1302
1356
|
console.log(chalk.green(' ✓ .gitignore'));
|
|
1303
1357
|
}
|
|
1304
1358
|
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# Deployment Document Template
|
|
2
|
-
|
|
3
|
-
Render `.prizmkit/deploy/deploy.md` with the exact top-level headings below. Replace placeholders with established values, use `not configured` or `not applicable` when truthful, and never write raw secret values. Update sections in place; do not append duplicate headings or turn this document into deployment history. Immutable operation events belong in `deploy-history/`.
|
|
4
|
-
|
|
5
|
-
```markdown
|
|
6
|
-
# Deployment
|
|
7
|
-
|
|
8
|
-
## Target
|
|
9
|
-
|
|
10
|
-
- Project: <project name>
|
|
11
|
-
- Project type: <detected project type>
|
|
12
|
-
- Environment: <dev | test | production>
|
|
13
|
-
- Target: <SSH host alias | cloud platform | Docker | unsupported target>
|
|
14
|
-
- Adapter: <ssh | cloud-guided | docker-guided | documented-fallback>
|
|
15
|
-
- Strategy: <push | pull | direct-upload | platform-native | container-native | manual>
|
|
16
|
-
- Public URL: <URL | not configured>
|
|
17
|
-
|
|
18
|
-
## Prerequisites
|
|
19
|
-
|
|
20
|
-
- <required runtime/tool/account/connectivity prerequisite>
|
|
21
|
-
|
|
22
|
-
## Required Environment Variables
|
|
23
|
-
|
|
24
|
-
Record names, purpose, source, and presence only. Never record values.
|
|
25
|
-
|
|
26
|
-
| Name | Required | Purpose | Secret Source | Present |
|
|
27
|
-
|---|---|---|---|---|
|
|
28
|
-
| `<VARIABLE_NAME>` | yes | <purpose> | <platform secret store or ignored local env file> | yes/no |
|
|
29
|
-
|
|
30
|
-
## Build and Release
|
|
31
|
-
|
|
32
|
-
- Install command: `<command | not required>`
|
|
33
|
-
- Build command: `<command | not required>`
|
|
34
|
-
- Start command: `<command>`
|
|
35
|
-
- Release layout: <path/layout or platform-native>
|
|
36
|
-
- Artifact source: <repository checkout | uploaded archive | container image | platform build>
|
|
37
|
-
|
|
38
|
-
## Runtime
|
|
39
|
-
|
|
40
|
-
- Process manager: <manager | platform-native>
|
|
41
|
-
- Runtime user: <user | platform-managed>
|
|
42
|
-
- Internal port: <port | platform-managed>
|
|
43
|
-
- Public routing: <Nginx/proxy/platform route>
|
|
44
|
-
- Persistent storage: <mounts/volumes | none>
|
|
45
|
-
|
|
46
|
-
## Health Checks
|
|
47
|
-
|
|
48
|
-
| Name | URL or Command | Expected | Last Verified Result |
|
|
49
|
-
|---|---|---|---|
|
|
50
|
-
| <check> | <path/command> | <status/output> | <pass/fail/not run> |
|
|
51
|
-
|
|
52
|
-
## DNS and TLS
|
|
53
|
-
|
|
54
|
-
- Domain: <domain | not configured>
|
|
55
|
-
- DNS status: <configured/pending/not applicable>
|
|
56
|
-
- TLS issuer: <issuer | platform-managed | not configured>
|
|
57
|
-
- Renewal: <automatic/manual/not applicable>
|
|
58
|
-
|
|
59
|
-
## Rollback
|
|
60
|
-
|
|
61
|
-
- Strategy: <previous release/platform rollback/image rollback/manual>
|
|
62
|
-
- Command or procedure: <safe rollback command/procedure>
|
|
63
|
-
- Readiness: <verified/not verified/not applicable>
|
|
64
|
-
|
|
65
|
-
## Operations
|
|
66
|
-
|
|
67
|
-
- Status: `<command or platform action>`
|
|
68
|
-
- Logs: `<command or platform action>`
|
|
69
|
-
- Restart: `<command or platform action>`
|
|
70
|
-
- Health: `<command or platform action>`
|
|
71
|
-
- Rollback: `<command or platform action>`
|
|
72
|
-
|
|
73
|
-
## Current Validation
|
|
74
|
-
|
|
75
|
-
- Configuration: <pass/fail/not run>
|
|
76
|
-
- Connectivity: <pass/fail/not run>
|
|
77
|
-
- Build: <pass/fail/not run>
|
|
78
|
-
- Staged health: <pass/fail/not run>
|
|
79
|
-
- Live health: <pass/fail/not run>
|
|
80
|
-
- Latest history record: `.prizmkit/deploy/deploy-history/<event-id>.json` or `none`
|
|
81
|
-
|
|
82
|
-
## Manual Actions
|
|
83
|
-
|
|
84
|
-
- <remaining user-owned action, or `None`>
|
|
85
|
-
|
|
86
|
-
## Notes
|
|
87
|
-
|
|
88
|
-
- <stable deployment constraint, adapter gap, or operational warning>
|
|
89
|
-
```
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# CI/CD Workflow Templates
|
|
2
|
-
|
|
3
|
-
Read this file when `deployStrategy` is `ci-cd-push` or `ci-cd-pull`.
|
|
4
|
-
|
|
5
|
-
## Shared Configuration
|
|
6
|
-
|
|
7
|
-
**Secrets** (add to GitHub repository Settings → Secrets and variables → Actions):
|
|
8
|
-
- `SSH_HOST` — server IP/hostname
|
|
9
|
-
- `SSH_USER` — runtime user (e.g., `deploy`)
|
|
10
|
-
- `SSH_KEY` — SSH private key
|
|
11
|
-
- `SSH_PORT` — SSH port (default 22)
|
|
12
|
-
|
|
13
|
-
**Shared trigger:**
|
|
14
|
-
```yaml
|
|
15
|
-
on:
|
|
16
|
-
push:
|
|
17
|
-
branches: [<branch>]
|
|
18
|
-
paths-ignore:
|
|
19
|
-
- '.prizmkit/**'
|
|
20
|
-
- 'docs/**'
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Push Mode Workflow (`ci-cd-push`)
|
|
26
|
-
|
|
27
|
-
Build happens on GitHub Actions runner. Only built artifacts are transferred to the server.
|
|
28
|
-
|
|
29
|
-
```yaml
|
|
30
|
-
name: Deploy to Production (Push)
|
|
31
|
-
|
|
32
|
-
on:
|
|
33
|
-
push:
|
|
34
|
-
branches: [<branch>]
|
|
35
|
-
|
|
36
|
-
jobs:
|
|
37
|
-
deploy:
|
|
38
|
-
runs-on: ubuntu-latest
|
|
39
|
-
steps:
|
|
40
|
-
- uses: actions/checkout@v4
|
|
41
|
-
|
|
42
|
-
- uses: actions/setup-node@v4
|
|
43
|
-
with:
|
|
44
|
-
node-version: 20
|
|
45
|
-
|
|
46
|
-
- name: Allocate Release Identity
|
|
47
|
-
run: echo "ARCHIVE_ID=$(date -u +%Y%m%dT%H%M%SZ)-$(git rev-parse --short=12 HEAD)" >> "$GITHUB_ENV"
|
|
48
|
-
|
|
49
|
-
- name: Install & Build
|
|
50
|
-
run: |
|
|
51
|
-
npm ci
|
|
52
|
-
npm run build
|
|
53
|
-
|
|
54
|
-
- name: Package & Transfer
|
|
55
|
-
run: |
|
|
56
|
-
tar czf deploy-$ARCHIVE_ID.tar.gz \
|
|
57
|
-
<build-output-dir>/ node_modules/ package.json package-lock.json
|
|
58
|
-
scp -P ${{ secrets.SSH_PORT }} deploy-$ARCHIVE_ID.tar.gz \
|
|
59
|
-
${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/var/www/<project>/releases/
|
|
60
|
-
|
|
61
|
-
- name: Deploy on Server
|
|
62
|
-
uses: appleboy/ssh-action@v1
|
|
63
|
-
env:
|
|
64
|
-
ARCHIVE_ID: ${{ env.ARCHIVE_ID }}
|
|
65
|
-
with:
|
|
66
|
-
host: ${{ secrets.SSH_HOST }}
|
|
67
|
-
username: ${{ secrets.SSH_USER }}
|
|
68
|
-
key: ${{ secrets.SSH_KEY }}
|
|
69
|
-
port: ${{ secrets.SSH_PORT }}
|
|
70
|
-
envs: ARCHIVE_ID
|
|
71
|
-
script: |
|
|
72
|
-
set -eu
|
|
73
|
-
cd /var/www/<project>
|
|
74
|
-
RELEASE_ID="$ARCHIVE_ID"
|
|
75
|
-
SUFFIX=1
|
|
76
|
-
while [ -e "releases/$RELEASE_ID" ]; do
|
|
77
|
-
[ "$SUFFIX" -le 99 ] || { echo "release identity suffixes exhausted" >&2; exit 1; }
|
|
78
|
-
RELEASE_ID="$ARCHIVE_ID-$(printf '%02d' "$SUFFIX")"
|
|
79
|
-
SUFFIX=$((SUFFIX + 1))
|
|
80
|
-
done
|
|
81
|
-
mkdir "releases/$RELEASE_ID"
|
|
82
|
-
tar xzf "releases/deploy-$ARCHIVE_ID.tar.gz" -C "releases/$RELEASE_ID"
|
|
83
|
-
rm "releases/deploy-$ARCHIVE_ID.tar.gz"
|
|
84
|
-
# PM2 start, health check, Nginx switch, history, and metadata use RELEASE_ID
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
**Key difference from Pull:** the runner checks out code, installs, builds, and only transmits the result. The server doesn't need Git or build tools — just Node.js runtime and PM2.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Pull Mode Workflow (`ci-cd-pull`)
|
|
92
|
-
|
|
93
|
-
GitHub Actions runner only triggers the server. The server does all the work.
|
|
94
|
-
|
|
95
|
-
```yaml
|
|
96
|
-
name: Deploy to Production (Pull)
|
|
97
|
-
|
|
98
|
-
on:
|
|
99
|
-
push:
|
|
100
|
-
branches: [<branch>]
|
|
101
|
-
|
|
102
|
-
jobs:
|
|
103
|
-
deploy:
|
|
104
|
-
runs-on: ubuntu-latest
|
|
105
|
-
steps:
|
|
106
|
-
- uses: actions/checkout@v4
|
|
107
|
-
|
|
108
|
-
- name: Allocate Release Identity
|
|
109
|
-
run: |
|
|
110
|
-
echo "RELEASE_BASE=$(date -u +%Y%m%dT%H%M%SZ)-$(git rev-parse --short=12 HEAD)" >> "$GITHUB_ENV"
|
|
111
|
-
echo "DEPLOY_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
|
|
112
|
-
|
|
113
|
-
- name: Trigger Server Deploy
|
|
114
|
-
uses: appleboy/ssh-action@v1
|
|
115
|
-
env:
|
|
116
|
-
RELEASE_BASE: ${{ env.RELEASE_BASE }}
|
|
117
|
-
DEPLOY_SHA: ${{ env.DEPLOY_SHA }}
|
|
118
|
-
with:
|
|
119
|
-
host: ${{ secrets.SSH_HOST }}
|
|
120
|
-
username: ${{ secrets.SSH_USER }}
|
|
121
|
-
key: ${{ secrets.SSH_KEY }}
|
|
122
|
-
port: ${{ secrets.SSH_PORT }}
|
|
123
|
-
envs: RELEASE_BASE,DEPLOY_SHA
|
|
124
|
-
script: |
|
|
125
|
-
set -eu
|
|
126
|
-
cd /var/www/<project>
|
|
127
|
-
RELEASE_ID="$RELEASE_BASE"
|
|
128
|
-
SUFFIX=1
|
|
129
|
-
while [ -e "releases/$RELEASE_ID" ]; do
|
|
130
|
-
[ "$SUFFIX" -le 99 ] || { echo "release identity suffixes exhausted" >&2; exit 1; }
|
|
131
|
-
RELEASE_ID="$RELEASE_BASE-$(printf '%02d' "$SUFFIX")"
|
|
132
|
-
SUFFIX=$((SUFFIX + 1))
|
|
133
|
-
done
|
|
134
|
-
git clone --no-checkout --branch <branch> <repoUrl> "releases/$RELEASE_ID"
|
|
135
|
-
git -C "releases/$RELEASE_ID" checkout --detach "$DEPLOY_SHA"
|
|
136
|
-
cd "releases/$RELEASE_ID"
|
|
137
|
-
npm ci
|
|
138
|
-
cp ../../shared/.env.production .
|
|
139
|
-
npm run build
|
|
140
|
-
# PM2 start, health check, Nginx switch, history, and metadata use RELEASE_ID
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
**Key difference from Push:** the workflow is simpler (one step: SSH + run script), but the server needs Git, full build toolchain, and must be able to reach the repo.
|
|
144
|
-
|
|
145
|
-
After generating the workflow, verify: the first `git push` to the configured branch will trigger the first deployment. Monitor the GitHub Actions run and report results.
|